模块标准: CommonJS、AMD、CMD、ES6 Module
CommonJS是Node.js原生支持的模块标准. 使用module.exports和require()
函数.
AMD和CMD比较相似, AMD的实现有require.js, CMD的实现有Sea.js.
ES6 Module引入import
和export
两个关键字, 是Webpack推荐的模块标准.
export 与 import
export
首先module.exports = ...
和export
关键字不能混用, 在Wbpack中优先使用export
关键字.
export
对一个Module对象进行操作, 而不是普通的js对象, 最后module.exports
赋值为该对象以导出.
使用ES6语法导出的是一个Module对象, 根据default字段, 有"默认导入"和"具名导出"两种说法.
let value = 88;
let value2 = 66;
function printValue() {
console.log(value);
}
// 导出"默认"变量作为一个{}对象
export default {
value2: value, // 导出变量的同时重命名
printValue,
};
// export default value; // 也可以将"默认"变量导出为一个变量或函数, 但不能多次导出"默认"变量
// export default value2 = value; // 重命名
// 具名导出
export { value as value2, value2 as value }; // 导出多个非默认变量, 同时重命名别名
// export { value: value2, value2: value }; // 语法错误
export var foo = printValue; // 导出单个非默认变量, 同时重命名
console.log({ foo }); // 现在 foo 是合法变量
import
import会判断对应模块入口文件(通常是/index.js
)中module.exports
是普通js对象还是Module
对象, 从而实现兼容:
如果是
Module
对象, 则有默认导出和其它导出(具名导出)之分
如果是普通js对象, 那么module.exports就是默认导出
import * as A from './test'; // 等价于C, 这种语法通常在使用import关键字引用浏览器和服务器通用模块时使用, 因为这些模块不导出Module对象
import B from './test'; // 默认导入
const C = require('./test'); // Node.js原始require语法, 可理解为伪代码: C = test.module.exports;
console.log({ A });
console.log({ B });
console.log({ C });
// 更新
import { default as D } from './test'; // 等价于B
高阶用法
集线器
可以将一个js文件作为路由集线器, 导入其它js的非默认同时导出:
export { DefaultLoadingManager, LoadingManager } from './loaders/LoadingManager.js';
我们将之前的文件命名为test2.js
, 在test1.js
中路由:
export { default } from './test2'; // 导出default
export { foo } from './test2'; // 导出非默认变量foo
export * from './test2'; // 导出全部非默认变量. 也就是说导出不会包含含default字段
// 也就是说, 导出test2全部可导成员需要以下两步
export * from './test2';
export { default } from './test2';
// 有时我们需要导入默认导出, 又需要导入普通的CommonJS导出, 可以这样写:
import React, { Component } from 'react';
// 那么之前的导出全部成员可以这么写吗? (疑问)
export *, { default } from './test2';
// 导入这样写可以吗? (疑问) 不可以!!!
import * as React, { Component } from 'react';
构建通用包时, 建议不使用ES6模块
以下四种React的引入是一样的,显然react包没有使用ES6模块:
import ReactA, { createElement as e, default as ReactB } from 'react';
import * as ReactC from 'react';
const ReactD = require('react');
console.log(ReactA === ReactB); // true
console.log(ReactA === ReactC);
console.log(ReactA === ReactD);
import一个目录
当我们导入一个目录时, 会尝试执行该目录下的index.js
文件. 并且目录优先于同名的js文件, 除非导入时显式指定.js
后缀.
在Windows下文件名不区分大小写, 所以Index.js
也可以编译, 但是在Linux下就完蛋了...!这也算是一个坑吧
异步import
function(string loader_and_file): Promise
动态加载模块。调用 import()
之处被视为分割点,意思着被请求的模块和它引用的所有子模块,会分割到一个单独的 chunk 中。
异步import, 模块的处理过程将作为一个分割点单独打包为独立模块, 再通过ajax请求, 请求路径是相对于引用脚本的URL, 所以对于单页应用来说没有问题.
// "."表示当前js文件, 使用raw-loader异步加载当前js文件的文本内容
import('!raw-loader!.').then(({ default: text }) => {
this.setState({ code: text });
});
非入口点文件
通过异步import打包后的文件称为非入口点文件, 文件名可以通过Webpack配置中的config.output.chunkFilename确定:
output: {
filename: '[name].js',
path: DIR_DIST,
chunkFilename: 'async/[id].js', // 此选项确定非入口块文件的名称
},
Magic Comments可以通过js注释的方式控制每一个异步import.
官方文档
https://webpack.docschina.org/api/module-methods/
异步import及其魔法注释
https://webpack.js.org/api/module-methods/#magic-comments