当前位置: 首页 > news >正文

深圳罗湖做网站公司哪家好盗网站asp源码

深圳罗湖做网站公司哪家好,盗网站asp源码,潍坊兆通网站建设,wordpress 个人说明模块之于程序#xff0c;就如同细胞之于生物体#xff0c;是具有特定功能的组成单元。不同的模块负责不同的工作#xff0c;它们以某种方式联系在一起#xff0c;共同保证程序的正常运转。介绍Webpack如何对其进行打包以及合并#xff0c;主要内容如下#xff1a; 不同模…模块之于程序就如同细胞之于生物体是具有特定功能的组成单元。不同的模块负责不同的工作它们以某种方式联系在一起共同保证程序的正常运转。介绍Webpack如何对其进行打包以及合并主要内容如下 不同模块的标准以及它们之间的区别如何编写模块模块打包的原理。 随着JavaScript语言的发展社区中产生了很多模块标准。在认识这些标准的同时也要了解其背后的思想。例如它为什么会有这个特性或者为什么要这样去实现。这对我们自己编写模块也会有所帮助。 1、CommonJS CommonJS是由JavaScript社区于2009年提出的包含模块、文件、IO、控制台在内的一系列标准。Node.js的实现中采用了CommonJS标准的一部分并在其基础上进行了一些调整。我们所说的CommonJS模块和Node.js中的实现并不完全一样现在一般谈到CommonJS其实是Node.js中的版本而非它的原始定义。 CommonJS最初只为服务端而设计直到有了Browserify——一个运行在Node.js环境下的模块打包工具它可以将CommonJS模块打包为浏览器可以运行的单个文件。这意味着客户端的代码也可以遵循CommonJS标准来编写了。 不仅如此借助Node.js的包管理器npm开发者还可以获取他人的代码库或者把自己的代码发布上去供他人使用。这种可共享的传播方式使CommonJS在前端开发领域逐渐流行起来。 1.1、模块 CommonJS中规定每个文件是一个模块。将一个JavaScript文件直接通过script标签插入页面中与封装成CommonJS模块最大的不同在于前者的顶层作用域是全局作用域在进行变量及函数声明时会污染全局环境而后者会形成一个属于模块自身的作用域所有的变量及函数只有自己能访问对外是不可见的。请看下面的例子 // calculator.js var name calculator.js;// index.js var name index.js; require(./calculator.js); console.log(name); // index.js这里有两个文件在index.js中我们通过CommonJS的require函数加载calculator.js。运行之后控制台结果是“index.js”说明calculator.js中的变量声明并不会影响index.js可见每个模块是拥有各自的作用域的。 1.2、导出 导出是一个模块向外暴露自身的唯一方式。在CommonJS中通过module.exports可以导出模块中的内容如 module.exports {name: calculater,add: function(a, b) {return a b;} };CommonJS模块内部会用一个module对象存放当前模块的信息可以理解成在每个模块的最开始定义了以下对象 var module {...}; // 模块自身逻辑 module.exports {...};module.exports用来指定该模块要对外暴露哪些内容在上面的代码中我们导出了一个对象包含name和add两个属性。为了书写方便CommonJS也支持另一种简化的导出方式——直接使用exports。 exports.name calculater; exports.add function(a, b) {return a b; };在实现效果上这段代码和上面的module.exports没有任何不同。其内在机制是将exports指向module.exports而module.exports在初始化时是一个空对象。我们可以简单地理解为CommonJS在每个模块的首部默认添加了以下代码 var module {exports: {}, }; var exports module.exports;因此为exports.add赋值相当于在module.exports对象上添加一个属性。 在使用exports时要注意一个问题即不要直接给exports赋值否则会导致其失效。如 exports {name: calculater };以上代码中由于对exports进行了赋值操作使其指向了新的对象而module.exports却仍然指向原来的空对象因此name属性并不会被导出。 另一个在导出时容易犯的错误是不恰当地把module.exports与exports混用。 exports.add function(a, b) {return a b; }; module.exports {name: calculater };上面的代码先通过exports导出了add属性然后将module.exports重新赋值为另外一个对象。这会导致原本拥有add属性的对象丢失了最后导出的只有name。 另外要注意导出语句不代表模块的末尾在module.exports或exports后面的代码依旧会照常执行。比如下面的console会在控制台上打出“end” module.exports {name: calculater }; console.log(end);1.3、导入 在CommonJS中使用require语法进行模块导入。如 // calculator.js module.exports {add: function(a, b) {return a b;} }; // index.js const calculator require(./calculator.js); const sum calculator.add(2, 3); console.log(sum); // 5我们在index.js中导入了calculator模块并调用了它的add函数。 当我们使用require导入一个模块时会有两种情况 该模块未曾被加载过。这时会首先执行该模块然后获取到该模块最终导出的内容。该模块已经被加载过。这时该模块的代码不会再次执行而是直接获取该模块上一次导出的内容。 请看下面的例子 // calculator.js console.log(running calculator.js); module.exports {name: calculator,add: function(a, b) {return a b;} };// index.js const add require(./calculator.js).add; const sum add(2, 3); console.log(sum:, sum); const moduleName require(./calculator.js).name; console.log(end);控制台的输出结果如下 running calculator.js sum: 5 end从结果可以看到尽管我们有两个地方使用require导入了calculator.js但其内部代码只执行了一遍。 我们前面提到模块会有一个module对象用来存放其信息这个对象中有一个属性loaded用于记录该模块是否被加载过。loaded的值默认为false在模块第一次被加载和执行过后会置为true后面再次加载时检查到module.loaded为true则不会再次执行模块代码。 有时我们加载一个模块不需要获取其导出的内容只是想要通过执行它而产生某种作用比如把它的接口挂在全局对象上此时直接使用require即可 require(./task.js);另外require函数可以接收表达式借助这个特性我们可以动态地指定模块加载路径。 const moduleNames [foo.js, bar.js]; moduleNames.forEach(name {require(./ name); });2、ES6 Module JavaScript之父Brendan Eich在最初设计这门语言时并没有包含模块的概念。基于越来越多的工程需求为了使用模块化进行开发JavaScript社区涌现出了多种模块标准其中也包括CommonJS。一直到2015年6月由TC39标准委员会正式发布了ES6ECMAScript 6.0自此JavaScript语言才具备了模块这一特性。 2.1、模块 请看下面的例子我们使用ES6的方式改写前面的calculator.js和index.js。 // calculator.js export default {name: calculator,add: function(a, b) {return a b;} }; // index.js import calculator from ./calculator.js; const sum calculator.add(2, 3); console.log(sum); // 5ES6 Module也是将每个文件作为一个模块每个模块拥有自身的作用域不同的是导入、导出语句。ES6版本将import和export作为保留关键字加入了进来CommonJS中的module并不属于关键字。 ES6 Module会自动采用严格模式该模式在ES5ECMAScript 5.0中只是一个可选项。也就是说以前我们可以通过选择是否在文件开始时加上use strict来控制严格模式而在ES6 Module中不管开头是否有use strict都会采用严格模式。所以在将原本是CommonJS的模块或任何未开启严格模式的代码改写为ES6 Module时要注意这一点。 1.2、导出 在ES6 Module中使用export命令来导出模块。export有两种形式 命名导出默认导出 一个模块可以有多个命名导出。它有两种不同的写法 // 写法1 export const name calculator; export const add function(a, b) { return a b; }; // 写法2 const name calculator; const add function(a, b) { return a b; }; export { name, add };第1种写法是将变量的声明和导出写在一行第2种则是先进行变量声明再用同一个export语句导出。两种写法的效果是一样的。 在使用命名导出时可以通过as关键字对变量重命名。如 const name calculator; const add function(a, b) { return a b; }; export { name, add as getSum }; // 在导入时即为 name 和 getSum与命名导出不同模块的默认导出只能有一个。如 export default {name: calculator,add: function(a, b) {return a b;} };我们可以将export default理解为对外输出了一个名为default的变量因此不需要像命名导出一样进行变量声明直接导出值即可。 // 导出字符串 export default This is calculator.js; // 导出 class export default class {...} // 导出匿名函数 export default function() {...}2.3、导入 ES6 Module中使用import语法导入模块。首先我们来看如何加载带有命名导出的模块请看下面的例子 // calculator.js const name calculator; const add function(a, b) { return a b; }; export { name, add };// index.js import { name, add } from ./calculator.js; add(2, 3);加载带有命名导出的模块时import后面要跟一对大括号来将导入的变量名包裹起来并且这些变量名应该与导出的变量名完全一致。导入变量的效果相当于在当前作用域下声明了这些变量name和add并且不可对其进行更改也就是所有导入的变量都是只读的。 与命名导出类似我们可以通过as关键字对导入的变量重命名。如 import { name, add as calculateSum } from ./calculator.js; calculateSum(2, 3);在导入多个变量时我们还可以采用整体导入的方式。如 import * as calculator from ./calculator.js; console.log(calculator.add(2, 3)); console.log(calculator.name);使用import * as myModule可以把所有导入的变量作为属性值添加到myModule对象中从而减少对当前作用域的影响。 接下来处理默认导出请看下面这个例子 // calculator.js export default {name: calculator,add: function(a, b) { return a b; } };// index.js import myCalculator from ./calculator.js; calculator.add(2, 3);对于默认导出来说import后面直接跟变量名并且这个名字可以自由指定比如这里是myCalculator它指代了calculator.js中默认导出的值。从原理上可以这样去理解 import { default as myCalculator } from ./calculator.js;最后看一个两种导入方式混合起来的例子 // index.js import React, { Component } from react;这里的React对应的是该模块的默认导出而Component则是其命名导出中的一个变量。 **注意**这里的React必须写在大括号前面不能颠倒顺序否则会提示语法错误。 2.4、复合写法 在工程中有时需要把某一个模块导入之后立即导出比如专门用来集合所有页面或组件的入口文件。此时可以采用复合写法 export { name, add } from ./calculator.js;复合写法目前只支持被导入模块这里的calculator.js通过命名导出的方式暴露出来的变量默认导出则没有对应的复合形式只能将导入和导出拆开写。 import calculator from ./calculator.js ; export default calculator;3、CommonJS与ES6 Module的区别 分别介绍了CommonJS和ES6 Module两种形式的模块定义在实际开发过程中我们经常会将二者混用因此这里有必要对比一下它们各自的特性。 3.1、动态与静态 CommonJS与ES6 Module最本质的区别在于前者对模块依赖的解决是“动态的”而后者是“静态的”。在这里“动态”的含义是模块依赖关系的建立发生在代码运行阶段而“静态”则表示模块依赖关系的建立发生在代码编译阶段。 让我们先看一个CommonJS的例子 // calculator.js module.exports { name: calculator }; // index.js const name require(./calculator.js).name;在上面介绍CommonJS的部分时我们提到过模块A在加载模块B时在上面的例子中是index.js加载calculator.js会执行B中的代码并将其module.exports对象作为require函数的返回值返回。require的模块路径可以动态指定支持传入一个表达式甚至可以通过if语句判断是否加载某个模块。因此在CommonJS模块被执行前我们并没有办法确定明确的依赖关系模块的导入、导出发生在代码的运行阶段。 针对同样的例子我们再对比看下ES6 Module的写法 // calculator.js export const name calculator; // index.js import { name } from ./calculator.js;ES6 Module的导入、导出语句都是声明式的它不支持将表达式作为导入路径并且导入、导出语句必须位于模块的顶层作用域比如不能放在if语句中。因此我们说ES6 Module是一种静态的模块结构在ES6代码的编译阶段就可以分析出模块的依赖关系。它相比CommonJS来说具备以下几点优势 死代码检测和排除。我们可以用静态分析工具检测出哪些模块没有被调用过。比如在引入工具类库时工程中往往只用到了其中一部分组件或接口但有可能会将其代码完整地加载进来。未被调用到的模块代码永远不会被执行也就成了死代码。通过静态分析可以在打包时去掉这些未曾使用过的模块以减小打包资源体积。模块变量类型检查。JavaScript属于动态类型语言不会在代码执行前检查类型错误比如对一个字符串类型的值进行函数调用。ES6 Module的静态模块结构有助于确保模块之间传递的值或接口类型是正确的。编译器优化。在CommonJS等动态模块系统中无论采用哪种方式本质上导入的都是一个对象而ES6 Module支持直接导入变量减少了引用层级程序效率更高。 3.2、值复制与动态映射 在导入一个模块时对于CommonJS来说获取的是一份导出值的副本而在ES6 Module中则是值的动态映射并且这个映射是只读的。 上面的话可能有些难以理解我们先来看一个例子了解一下什么是CommonJS中的值复制。 // calculator.js var count 0; module.exports {count: count,add: function(a, b) {count 1;return a b;} };// index.js var count require(./calculator.js).count; var add require(./calculator.js).add;console.log(count); // 0这里的count是calculator.js中count值的副本 add(2, 3); console.log(count); // 0calculator.js中变量值的改变不会对这里的副本造成影响count 1; console.log(count); // 1副本的值可以更改index.js中的count是calculator.js中count的一份副本因此在调用add函数时虽然更改了原本calculator.js中count的值但是并不会对index.js中导入时创建的副本造成影响。 另一方面在CommonJS中允许对导入的值进行更改。我们可以在index.js中更改count和add将其赋予新值。同样由于是值的副本这些操作不会影响calculator.js本身。 下面我们使用ES6 Module对上面的例子进行改写 // calculator.js let count 0; const add function(a, b) {count 1;return a b; }; export { count, add };// index.js import { count, add } from ./calculator.js; console.log(count); // 0对 calculator.js 中 count 值的映射 add(2, 3); console.log(count); // 1实时反映calculator.js 中 count值的变化// count 1; // 不可更改会抛出SyntaxError: count is read-only上面的例子展示了ES6 Module中导入的变量其实是对原有值的动态映射。index.js中的count是对calculator.js中count值的实时反映当我们通过调用add函数更改了calculator.js中的count值时index.js中count的值也随之变化。并且ES6Module规定不能对导入的变量进行修改当我们尝试去修改时它会抛出该变量只读的错误。 3.3、循环依赖 循环依赖是指模块A依赖于模块B同时模块B依赖于模块A。比如下面这个例子 // a.js import { foo } from ./b.js; foo();// b.js import { bar } from ./a.js; bar();一般来说工程中应该尽量避免循环依赖的产生因为从软件设计的角度来说单向的依赖关系更加清晰循环依赖则会带来一定的复杂度。但在实际开发中循环依赖有时会在我们不经意间产生因为当工程的复杂度上升到足够大时就容易出现隐藏的循环依赖关系。 简单来说A和B两个模块之间是否存在直接的循环依赖关系是很容易发现的。但实际情况往往是A依赖于BB依赖于CC依赖于D最后绕了一大圈D又依赖于A。当中间模块太多时我们就很难发现A和B之间存在隐式的循环依赖了。 因此如何处理循环依赖是开发者必须要面对的问题。我们首先看一下在CommonJS中循环依赖的例子 // foo.js const bar require(./bar.js); console.log(value of bar:, bar); module.exports This is foo.js;// bar.js const foo require(./foo.js); console.log(value of foo:, foo); module.exports This is bar.js;// index.js require(./foo.js);在这里index.js是执行入口它加载了foo.jsfoo.js和bar.js之间存在循环依赖。我们观察一下foo.js和bar.js中的代码理想状态下我们希望二者都能导入正确的值并在控制台上输出 value of foo: This is foo.js value of bar: This is bar.js而当我们运行上面的代码时实际输出却是 value of foo: {} value of bar: This is bar.js为什么foo的值会是一个空对象呢让我们从头梳理一下代码的实际执行顺序: 1index.js导入了foo.js此时开始执行foo.js中的代码。 2foo.js的第1句导入了bar.js这时foo.js不会继续向下执行而是会进入bar.js内部。 3在bar.js中又对foo.js进行了导入这里产生了循环依赖。需要注意的是执行权并不会再交回foo.js而是直接取其导出值也就是module.exports。但由于foo.js未执行完毕导出值在这时为默认的空对象因此当bar.js执行到打印语句时我们看到控制台中的value of foo就是一个空对象。 4bar.js执行完毕将执行权交回foo.js。5foo.js从require语句继续向下执行在控制台打印出value of bar这个值是正确的整个流程结束。 由上面可以看出尽管循环依赖的模块均被执行了但模块导入的值并不是我们想要的。因此如果在CommonJS中遇到循环依赖我们将没有办法得到预想中的结果。 我们再从Webpack的实现角度来看将上面的例子打包后bundle中有这样一段非常重要的代码 // The require function function __webpack_require__(moduleId) {if(installedModules[moduleId]) {return installedModules[moduleId].exports;}// Create a new module (and put it into the cache)var module installedModules[moduleId] {i: moduleId,l: false,exports: {}};... }当index.js引用了foo.js之后相当于执行了这个__webpack_require__函数初始化了一个module对象并放入installedModules中。当bar.js再次引用foo.js时又执行了该函数但这次是直接从installedModules里面取值此时它的module.exports是一个空对象。这就解释了上面在第3步看到的现象。 接下来我们使用ES6 Module的方式重写上面的例子 // foo.js import bar from ./bar.js; console.log(value of bar:, bar); export default This is foo.js;// bar.js import foo from ./foo.js; console.log(value of foo:, foo); export default This is bar.js;// index.js import foo from ./foo.js;执行结果如下 value of foo: undefined foo.js:3 value of bar: This is bar.js很遗憾在bar.js中同样无法得到foo.js正确的导出值只不过和CommonJS默认导出一个空对象不同这里获取到的是undefined。 上面我们谈到在导入一个模块时CommonJS获取到的是值的副本ES6 Module则是动态映射那么我们能否利用ES6Module的特性使其支持循环依赖呢请看下面这个例子 //index.js import foo from ./foo.js; foo(index.js);// foo.js import bar from ./bar.js; function foo(invoker) {console.log(invoker invokes foo.js);bar(foo.js); } export default foo;// bar.js import foo from ./foo.js; let invoked false; function bar(invoker) {if(!invoked) {invoked true;console.log(invoker invokes bar.js);foo(bar.js);} } export default bar;上面代码的执行结果如下 index.js invokes foo.js foo.js invokes bar.js bar.js invokes foo.js可以看到foo.js和bar.js这一对循环依赖的模块均获取到了正确的导出值。下面让我们分析一下代码的执行过程 1index.js作为入口导入了foo.js此时开始执行foo.js中的代码。 2从foo.js导入bar.js执行权交给bar.js。 3在bar.js中一直执行到结束完成bar函数的定义。注意此时由于foo.js还没执行完foo的值现在仍然是undefined。 4执行权回到foo.js继续执行直到结束完成foo函数的定义。由于ES6 Module动态映射的特性此时在bar.js中foo的值已经从undefined成为我们定义的函数这是与CommonJS在解决循环依赖时的本质区别CommonJS中导入的是值的副本不会随着模块中原有值的变化而变化。 5执行权回到index.js并调用foo函数此时会依次执行foo→bar→foo并在控制台输出正确的值。 由上面的例子可以看出ES6 Module的特性使其可以更好地支持循环依赖只是需要由开发者来保证当导入的值被使用时已经设置好正确的导出值。 4、加载其他类型的模块 前面我们介绍的主要是CommonJS和ES6 Module除此之外在开发中我们还有可能遇到其他类型的模块目前AMD、UMD等模块使用的场景已经不多但当遇到这类模块时我们仍然需要知道如何处理。 4.1、非模块化文件 非模块化文件指的是并不遵循任何一种模块标准的文件。如果你维护的是一个几年前的项目那么里面极有可能有非模块化文件最常见的就是在script标签中引入的jQuery及其各种插件。 如何使用Webpack打包这类文件呢其实只要直接引入即可如 import ./jquery.min.js;这句代码会直接执行jquery.min.js。一般来说jQuery这类库都是将其接口绑定在全局因此无论是从script标签引入还是使用Webpack打包的方式引入其最终效果是一样的。 但假如我们引入的非模块化文件是以隐式全局变量声明的方式暴露其接口的则会发生问题。如 // 通过在顶层作用域声明变量的方式暴露接口 var calculator {// ... }由于Webpack在打包时会为每一个文件包装一层函数作用域来避免全局污染上面的代码将无法把calculator对象挂在全局因此需要格外注意这种隐式全局变量声明。 4.2、AMD AMDAsynchronous Module Definition异步模块定义是由JavaScript社区提出的专注于支持浏览器端模块化的标准。从名字就可以看出它与CommonJS和ES6 Module最大的区别在于它加载模块的方式是异步的。下面的例子展示了如何定义一个AMD模块 define(getSum, [calculator], function(math) {return function(a, b) {console.log(sum: calculator.add(a, b));} });在AMD中使用define函数来定义模块它可以接收3个参数。第1个参数是当前模块的id相当于模块名第2个参数是当前模块的依赖比如上面我们定义的getSum模块需要引入calculator模块作为依赖第3个参数用来描述模块的导出值可以是函数或对象。如果是函数则导出的是函数的返回值如果是对象则直接导出对象本身。 和CommonJS类似AMD也使用require函数来加载模块只不过采用异步的形式 require([getSum], function(getSum) {getSum(2, 3); });require的第1个参数指定了加载的模块第2个参数是当加载完成后执行的回调函数。 通过AMD这种形式定义模块的好处在于其模块加载是非阻塞性的当执行到require函数时并不会停下来去执行被加载的模块而是继续执行require后面的代码使得模块加载操作并不会阻塞浏览器。 尽管AMD的设计理念很好但与同步加载的模块标准相比其语法要更加冗长。另外其异步加载的方式没有同步清晰并且容易造成回调地狱callback hell。目前AMD在实际中已经用得越来越少大多数开发者还是会选择CommonJS或ES6Module的形式。 4.3、UMD 我们已经介绍了很多模块形式如CommonJS、ES6Module、AMD及非模块化文件在很多时候工程中会用到其中两种形式甚至更多。有时对于一个库或者框架的开发者来说如果面向的使用群体足够庞大就需要考虑支持各种模块形式。 严格来说UMD并不是一种模块标准而是一组模块形式的集合。UMD的全称是Universal Module Definition也就是通用模块标准它的目标是使一个模块能运行在各种环境下不论是CommonJS、AMD还是非模块化的环境当时ES6Module还未被提出。 请看下面的例子 // calculator.js (function (global, main) {// 根据当前环境采取不同的导出方式if (typeof define function define.amd) {// AMDdefine(...);} else if (typeof exports object) {// CommonJSmodule.exports ...;} else {// 非模块化环境global.add ...;} }(this, function () {// 定义模块主体return {...} }));可以看出UMD其实就是根据当前全局对象中的值判断目前处于哪种模块环境。当前环境是AMD就以AMD的形式导出当前环境是CommonJS就以CommonJS的形式导出。 需要注意的是UMD模块一般都最先判断AMD环境也就是全局下是否有define函数而通过AMD定义的模块是无法使用CommonJS或ES6 Module的形式正确引入的。在Webpack中由于它同时支持AMD及CommonJS也许工程中的所有模块都是CommonJS而UMD标准却发现当前有AMD环境并使用了AMD方式导出这会使得模块导入时出错。当需要这样做时我们可以更改UMD模块中判断的顺序使其以CommonJS的形式导出。 4.4、加载npm模块 与Java、C、Python等语言相比JavaScript是一种缺乏标准库的语言。当开发者需要解决URL处理、日期解析这类很常见的问题时很多时候只能自己动手来封装工具接口。而npm提供了这样一种方式可以让开发者在其平台上找到由他人所开发和发布的库并安装到项目中从而快速地解决问题这就是npm作为包管理器为开发者带来的便利。 很多语言都有包管理器比如Java的MavenRuby的gem。目前JavaScript最主流的包管理器有两个——npm和yarn。两者的仓库是共通的只是在使用上有所区别。截至目前npm平台上已经有几十万个模块也可称为包并且这个数字每天都在增加各种主流的框架类库都可以在npm平台上找到。作为开发者每个人也都可以自己封装模块并上传到npm通过这种方式来与他人共享代码。 那么如何从我们的本地工程安装和加载一个外部的npm模块呢首先我们需要初始化一个npm工程并通过npm来获取模块。下面以lodash这个库为例 # 项目初始化 npm init –y # 安装lodash npm install lodash执行了上面的命令之后npm会将lodash安装在工程的node_modules目录下并将对该模块的依赖信息记录在package.json中。 在使用时加载一个npm模块的方式很简单只需要引入包的名字即可 // index.js import _ from lodash;Webpack在解析到这条语句时会自动去node_modules中寻找名为lodash的模块而不需要我们写出从源文件index.js到node_modules中lodash的路径。 现在我们知道在导入一个npm模块时只要写明它的名字就可以了。那么在实际打包的过程中具体加载的是npm模块中的哪个JS文件呢 每一个npm模块都有一个入口。当我们加载一个模块时实际上就是加载该模块的入口文件。这个入口被维护在模块内部package.json文件的main字段中。 比如对于前面的lodash模块来说它的package.json内容如下 // ./node_modules/underscore/package.json {name: lodash,……main: lodash.js }当加载该模块时实际上加载的是node_modules/lodash/lodash.js。 除了直接加载模块以外我们也可以通过package_name/path的形式单独加载模块内部的某个JS文件。如 import all from lodash/fp/all.js; console.log(all, all);这样Webpack最终只会打包node_modules/lodash/fp/all.js这个文件而不会打包全部的lodash库进而减小打包资源的体积。 5、模块打包原理 面对工程中成百上千个模块Webpack究竟是如何将它们有序地组织在一起并按照我们预想的顺序运行在浏览器上的呢下面我们将从原理上进行探究。 还是使用前面calculator的例子 // index.js const calculator require(./calculator.js); const sum calculator.add(2, 3); console.log(sum, sum);// calculator.js module.exports {add: function(a, b) {return a b;} };上面的代码经过Webpack打包后将会成为如下形式为了易读性这里只展示代码的大体结构 // 立即执行匿名函数 (function(modules) {//模块缓存var installedModules {};// 实现requirefunction __webpack_require__(moduleId) {...}// 执行入口模块的加载return __webpack_require__(__webpack_require__.s 0); })({// modules以key-value的形式存储所有被打包的模块0: function(module, exports, __webpack_require__) {// 打包入口module.exports __webpack_require__(3qiv);},3qiv: function(module, exports, __webpack_require__) {// index.js内容},jkzz: function(module, exports) {// calculator.js 内容} });这是一个最简单的Webpack打包结果bundle但已经可以清晰地展示出它是如何将具有依赖关系的模块串联在一起的。上面的bundle分为以下几个部分 最外层匿名函数。它用来包裹整个bundle并构成自身的作用域。installedModules对象。每个模块只在第一次被加载的时候执行之后其导出值就被存储到这个对象里面当再次被加载的时候webpack会直接从这里取值而不会重新执行该模块。webpack_require__函数。对模块加载的实现在浏览器中可以通过调用__webpack_require(module_id)来完成模块导入。modules对象。工程中所有产生了依赖关系的模块都会以key-value的形式放在这里。key可以理解为一个模块的id由数字或者一个很短的hash字符串构成value则是由一个匿名函数包裹的模块实体匿名函数的参数赋予了每个模块导出和导入的能力。 接下来让我们看看bundle是如何在浏览器中执行的 1在最外层匿名函数中初始化浏览器执行环境包括定义installedModules对象、webpack_require__函数等为模块的加载和执行做一些准备工作。 2加载入口模块。每个bundle都有且只有一个入口模块在上面的示例中index.js是入口模块在浏览器中会从它开始执行。 3执行模块代码。如果执行到了module.exports则记录下模块的导出值如果中间遇到require函数准确地说是__webpack_require则会暂时交出执行权进入__webpack_require__函数体内进行加载其他模块的逻辑。 4在__webpack_require__中判断即将加载的模块是否存在于installedModules中。如果存在则直接取值否则回到第3步执行该模块的代码来获取导出值。 5所有依赖的模块都已执行完毕最后执行权又回到入口模块。当入口模块的代码执行完毕也就意味着整个bundle运行结束。 不难看出第3步和第4步是一个递归的过程。Webpack为每个模块创造了一个可以导出和导入模块的环境但本质上并没有修改代码的执行逻辑因此代码执行的顺序与模块加载的顺序是完全一致的这也是Webpack模块打包的奥秘。 6、总结 介绍了JavaScript模块包括主流模块标准的定义以及在Webpack中是如何进行模块打包的。 CommonJS和ES6 Module是目前使用较为广泛的模块标准。它们的主要区别在于前者是在运行时建立模块依赖关系后者是在编译时建立在模块导入方面CommonJS导入的是值副本ES6 Module导入的是只读的变量映射ES6 Module通过其静态特性可以进行编译过程中的优化并且具备处理循环依赖的能力。
http://www.huolong8.cn/news/339175/

相关文章:

  • 网站建设背景资料做网站一般几个步骤
  • 色彩 导航网站微帮推广平台怎么加入
  • 济南 网站 建设seo建站外贸
  • 网站app怎么做广州app开发外包
  • 泰安服装网站建设手机设置管理网站
  • 如何查看一个网站流量哪里有营销型网站最新报价
  • 合肥建网站公司开发板的作用
  • 天河建设网站平台iis怎么查看网站的域名
  • 海珠高端网站建设桂林论坛天涯社区
  • 网站开发快递文件网站广告怎么做
  • 网站建设中扁平化结构广州免费设计网站建设
  • 无锡建设机械网站制作网站制作内联框
  • 关于协会网站建设的几点思考优化关键词规则
  • 网站 内容 制作网站建设会销
  • 南昌网站建设加王道下拉江西核工业建设有限公司网站
  • 有一个做ppt的网站公司网站后台打不开
  • 企业网站备案是什么意思商丘做网站哪个好
  • 个人网站设计作品html空间设计公司
  • 企业网站建设犇类建筑网站建设分金手指排名四
  • 2015网站建设小程序自己开发难吗
  • 哪有专做飞织鞋面的网站seo与sem的关系
  • 招商门户网站建设方案平和县建设局网站
  • 网站单页生成器wordpress两步验证
  • 做的好的ppt下载网站巴中住房建设部网站
  • 网站设计公司飞沐wordpress+v4.1
  • 建站运营新闻wordpress去分类
  • 部门网站建设管理办法青岛网红打卡景点
  • 响应式科技公司网站模板下载网站做用户记录
  • 自己的网站可以做淘客吗网站开发部职责
  • 网站开发前端后端书籍网络营销代运营外包公司