摘要:一原理與環(huán)境不同,瀏覽器中不支持的主要原因是缺少了以下幾個環(huán)境變量換句話說,打包器的原理就是模擬這四個變量的行為。
本文首發(fā)于知乎專欄:
http://zhuanlan.zhihu.com/starkwang
CommonJS 是一個流行的前端模塊化規(guī)范,也是目前 NodeJS 以及其模塊托管倉庫 npm 使用的規(guī)范,但目前暫無瀏覽器支持 CommonJS 。要想讓瀏覽器用上這些模塊,必須轉(zhuǎn)換格式。
這個系列的文章,我們會一步步完成一個基于 CommonJS 的打包工具,類似于一個簡單版的 Browserify 或者 Webpack 。
一、原理與 NodeJS 環(huán)境不同,瀏覽器中不支持 CommonJS 的主要原因是缺少了以下幾個環(huán)境變量:
module
exports
require
global
換句話說,打包器的原理就是模擬這四個變量的行為。
比如我們有一個index.js文件,依賴了module1和module2兩個模塊,并且module1依賴module2:
//index.js var module1 = require("./module1"); var module2 = require("./module2"); module1.foo(); module2.foo(); function hello(){ console.log("Hello!"); } module.exports = hello;
//module1.js var module2 = require(module2); console.log("initialize module1"); console.log("this is module2.foo() in module1:"); module2.foo(); console.log(" ") module.exports = { foo: function(){ console.log("module1 foo !!!"); } };
//module2.js console.log("initialize module2"); module.exports = { foo: function(){ console.log("module2 foo !!!"); } };
把它放入一個匿名函數(shù)內(nèi),通過這個匿名函數(shù)注入 require、modules、export、global變量(我們暫時不實現(xiàn)global)
function(module, exports, require, global){ var module1 = require("./module1"); var module2 = require("./module2"); module1.foo(); module2.foo(); function hello(){ console.log("Hello!"); } module.exports = hello; }
現(xiàn)在我們用一個 modules 對象來存入這些匿名函數(shù):
//modules { "entry": function(module, exports, require, global){ //index.js var module1 = require("./module1"); var module2 = require("./module2"); module1.foo(); module2.foo(); function hello(){ console.log("Hello!"); } module.exports = hello; }, "./module1": function(module, exports, require, global){ var module2 = require("./module2"); console.log("initialize module1"); console.log("this is module2.foo() in module1:"); module2.foo(); console.log(" ") module.exports = { foo: function(){ console.log("module1 foo !!!"); } }; }, "./module2": function(module, exports, require, global){ console.log("initialize module2"); module.exports = { foo: function(){ console.log("module2 foo !!!"); } }; } }
下面我們實現(xiàn)一個簡單的 require 函數(shù):
//這個對象用于儲存已導入的模塊 var installedModules = {}; function require(moduleName) { //如果模塊已經(jīng)導入,那么直接返回它的exports if(installedModules[moduleName]){ return installedModules[moduleName].exports; } //模塊初始化 var module = installedModules[moduleName] = { exports: {}, name: moduleName, loaded: false }; //執(zhí)行模塊內(nèi)部的代碼,這里的 modules 變量即為我們在上面寫好的 modules 對象 modules[moduleName].call(module.exports, module, module.exports,require); //模塊導入完成 module.loaded = true; //將模塊的exports返回 return module.exports; }
最后只要把我們上面寫好的 modules 對象以立即執(zhí)行函數(shù)的形式傳入這個 require 函數(shù)就可以了,以下是完整的代碼:
(function(modules){ //這個對象用于儲存已導入的模塊 var installedModules = {}; function require(moduleName) { //如果模塊已經(jīng)導入,那么直接返回它的exports if(installedModules[moduleName]){ return installedModules[moduleName].exports; } //模塊初始化 var module = installedModules[moduleName] = { exports: {}, name: moduleName, loaded: false }; //執(zhí)行模塊內(nèi)部的代碼,這里的 modules 變量即為我們在上面寫好的 modules 對象 modules[moduleName].call(module.exports, module, module.exports,require); //模塊導入完成 module.loaded = true; //將模塊的exports返回 return module.exports; } //入口函數(shù) return require("entry"); })({ "entry": function(module, exports, require, global){ //index.js var module1 = require("./module1"); var module2 = require("./module2"); module1.foo(); module2.foo(); function hello(){ console.log("Hello!"); } module.exports = hello; }, "./module1": function(module, exports, require, global){ var module2 = require("./module2"); console.log("initialize module1"); console.log("this is module2.foo() in module1:"); module2.foo(); console.log(" ") module.exports = { foo: function(){ console.log("module1 foo !!!"); } }; }, "./module2": function(module, exports, require, global){ console.log("initialize module2"); module.exports = { foo: function(){ console.log("module2 foo !!!"); } }; } });
事實上,我們短短的這幾十行代碼模仿了 Webpack 的部分實現(xiàn)。但我們依然在使用諸如 "./module1" 這樣的字符串作為模塊的唯一識別碼,這是一個明顯的缺陷,存在多層級文件時,這個名稱很容易沖突。
在 Browserify 或 Webpack 這樣的生產(chǎn)級工具里,一般使用數(shù)字作為函數(shù)的唯一識別碼,例如它可能會把(以 Webpack 為例):
var module1 = require("./module1");
編譯成:
var module1 = __webpack_require__(1);二、小結
我們在這里實現(xiàn)了一個最簡單的 CommonJS 標準的執(zhí)行器,接下來的文章中我們會做以下事情:
1、實現(xiàn) global 變量
2、用 moduleID 替代 moduleName
3、寫一個命令行小工具
4、支持 node_modules 和多層級文件
文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/79155.html
摘要:感謝感謝和在推動模塊化發(fā)展方面做出的貢獻。與引用阮一峰老師的標準參考教程規(guī)范加載模塊是同步的,也就是說,只有加載完成,才能執(zhí)行后面的操作。規(guī)定了新的模塊加載方案。與引用阮一峰老師的入門它們有兩個重大差異。 前言 本篇我們重點介紹以下四種模塊加載規(guī)范: AMD CMD CommonJS ES6 模塊 最后再延伸講下 Babel 的編譯和 webpack 的打包原理。 require....
摘要:之前寫的文章急速全棧教程得到了不錯的閱讀量,霸屏掘金頭條天,點贊過千,閱讀近萬,甚至還有人在評論區(qū)打廣告,可見也是一個小小的生態(tài)了。今天看到的霸屏的,也是講全棧的,見參考文章接下來要寫的是模塊。全局命名污染和命名沖突依賴管理。 之前寫的文章急速Js全棧教程得到了不錯的閱讀量,霸屏掘金頭條3天,點贊過千,閱讀近萬,甚至還有人在評論區(qū)打廣告,可見也是一個小小的生態(tài)了;)。看來和JS全棧有關...
摘要:先說下我面試情況,我一共面試了家公司。篇在我面試的眾多公司里,只有同城的面問到相關問題,其他公司壓根沒問。我自己回答的是自己開發(fā)組件面臨的問題。完全不用擔心對方到時候打電話核對的問題。 2019的5月9號,離發(fā)工資還有1天的時候,我的領導親切把我叫到辦公室跟我說:阿郭,我們公司要倒閉了,錢是沒有的啦,為了不耽誤你,你趕緊出去找工作吧。聽到這話,我虎軀一震,這已經(jīng)是第2個月沒工資了。 公...
摘要:因此,你還是需要各種各樣雜七雜八的工具來轉(zhuǎn)換你的代碼噢,我可去你媽的吧,這些東西都是干嘛的我就是想用個模塊化,我到底該用啥子本文正旨在列出幾種可用的在生產(chǎn)環(huán)境中放心使用模塊化的方法,希望能幫到諸位后來者這方面的中文資源實在是忒少了。 原文發(fā)表在我的博客上。最近搗鼓了一下 ES6 的模塊化,分享一些經(jīng)驗 :) Python3 已經(jīng)發(fā)布了九年了,Python 社區(qū)卻還在用 Python 2...
摘要:所以,打包工具就出現(xiàn)了,它可以幫助做這些繁瑣的工作。打包工具介紹僅介紹款主流的打包工具,,,,以發(fā)布時間為順序。它定位是模塊打包器,而屬于構建工具。而且在其他的打包工具在處理非網(wǎng)頁文件比如等基本還是需要借助它來實現(xiàn)。 本文當時寫在本地,發(fā)現(xiàn)換電腦很不是方便,在這里記錄下。 前端的打包工具 打包工具可以更好的管理html,css,javascript,使用可以錦上添花,不使用也沒關系...
閱讀 1785·2023-04-26 00:47
閱讀 1543·2021-11-11 16:55
閱讀 2597·2021-09-27 14:04
閱讀 3548·2021-09-22 15:58
閱讀 3554·2021-07-26 23:38
閱讀 2129·2019-08-30 13:47
閱讀 1979·2019-08-30 13:15
閱讀 1142·2019-08-29 17:09