1、webpack的模块化原理
webpack 本身维护了一套模块系统,这套模块系统兼容了所有前端历史进程下的模块规范,包括 amd commonjs es6 等
(function(modules) {
function __webpack_require__(moduleId) {
var module = {
i: moduleId,
l: false,
exports: {}
};
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
return module.exports;
}
return __webpack_require__(0);
})([(function (module, __webpack_exports__, __webpack_require__) {
// 模块的逻辑代码
...
})]
原理的关键有两点:1、所有的模块都被封装成function (module, webpack_exports, webpack_require){}的形式,2、webpack_require函数,该函数内部有一个module对象,该对象有两个重要属性:i代表模块id,exports代表要暴露给外面的对象。每次调用webpack_require时都会传入一个数字作为模块id,这样不同模块就能被区分,另外该函数将module.exports作为返回值。每个模块的导出值都会被记录在module.exports对象里,其他依赖该模块的模块就能取到对应数据
2、webpack2是如何支持import和require两种引入模块的方式
首先webpack是基于node的,node的模块规范是commonjs,该规范就是使用require和module.exports来引入和导出模块的;其次,es6 module是静态的依赖,所以可以在运行前进行代码转换。import的本质其实就是require,webpack在进行静态分析时会将其转为require。这也说明了在webpack中可以混用import和require,因为二者本质上都是require
3、如何使用webpack2的tree-shaking技术
webpack2的tree-shaking基于ES6的模块静态依赖机制,babel也是可以将ES6模块转换为commenjs模块的,但是你一旦这样做了就会失去tree-shaking技术。所以在使用babel转换ES6时一般会如下配置,即只使用bable的ES6语法转换能力,不使用它的模块处理,而是使用webpack2自己的模块处理。
presets: [['babel-preset-es2015', {modules: false}]]
需要说明的是,即使在 引入模块时使用了 es6 ,但是引入的那个模块却是使用 commonjs 进行输出,这也无法使用tree-shaking。
而第三方库大多是遵循 commonjs 规范的,这也造成了引入第三方库无法减少不必要的引入。
所以对于未来来说第三方库要同时发布 commonjs 格式和 es6 格式的模块。es6 模块的入口由 package.json 的字段 module 指定。而 commonjs 则还是在 main 字段指定。
4、import及export转换为require和module.exports的规则是啥样的
- es6 的 export default 都会被转换成 exports.default
- export 的所有输出都会添加到module.exports对象上
- 使用require去引用ES6模块的export default输出时,注意用require('**').default
5、babel如何转换ES6模块语法
- 导出
// ES6
export default 123;
export const a = 123;
const b = 3;
const c = 4;
export { b, c };
// 转换后
exports.default = 123;
exports.a = 123;
exports.b = 3;
exports.c = 4;
exports.__esModule = true;
babel 转换 es6 的模块输出逻辑非常简单,即将所有输出都赋值给 exports,并带上一个标志 __esModule 表明这是个由 es6 转换来的 commonjs 输出。
- 导入
// 1、普通导入
import a from './a.js' //引入一个 es6 模块中的 default 输出
// babel会这么转
function _interopRequireDefault(obj) {
return obj && obj.__esModule
? obj
: { 'default': obj };
}
var _a = require('./a.js');
var _a2 = _interopRequireDefault(_a);
var a = _a2['default'];
// 2、通配符引入
import * as a from './a.js' //将 es6 模块的所有命名输出以及defalut输出打包成一个对象赋值给a变量
// Babel内部这么转
function _interopRequireWildcard(obj) {
if (obj && obj.__esModule) {
return obj;
}
else {
var newObj = {}; // (A)
if (obj != null) {
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key))
newObj[key] = obj[key];
}
}
newObj.default = obj;
return newObj;
}
}
// 3、具名引入
import { a } from './a.js' // 将 es6 模块的a引入
//babel这样转
require('./a.js').a
网友评论