webpack原理与实现
webpack官方定义
At its core, webpack is a static module bundler for modern JavaScript applications.
百度翻译:webpack的核心是一个用于现代JavaScript应用程序的静态模块绑定器。
什么意思呢?
简单来说就是一个模块打包的工具。
模块打包带出几个问题思考:
1.对于前端什么是模块?如何定义模块?
对于一些新人可能对javascript模块历史不熟悉的可以跳转这里了解熟悉javascript模块。
2.webpack是如何实现模块打包?
webpack打包原理
1.webpack把前端应用的代码和资源看作是一个个的模块,然后处理这些模块之间依赖的关系最后输出bundle.js文件。
2.对于任意方式定义的模块,webpack都能兼容处理,它是通过loader机制进行转换实现的。
一切都是模块:
每个js文件是一个模块,每个css、html 和image也可以看作是一个个模块,只要它们本身被依赖,例如require(‘cssFile.css’)。
模块加载:
webpack可以将所有模块编译打包输出庞大的bundle.js,这种加载模块方式对于体验是极其不好的,所以webpack可使用许多特性来分割代码生成多个较小的bundle.js文件,通过异步加载的方式请求资源。
webpack文件管理
webpack打包通过webpack.config.js描述的相关信息执行,webpack本身是一个node的模块,所以webpack.config.js是以commonjs形式书写的。
// webpack.config.js
module.exports = {
entry:'./a.js',
output:{
filename:'bundle.js'
}
};
1.entry属性是webpack执行打包过程加载文件的入口,属性值被称作是入口文件。
2.output属性是webpack执行打包后输出的文件。
3.webpack会把入口文件进行分析,入口文件代码中所依赖(即require)的资源全部打包在一起,一个资源多次引用的话,也只会打包一份。
4.对于多个入口的情况,其实就是分别独立的执行单个入口情况,每个入口文件都互不相干独立的执行各自的打包程序(可用CommonsChunkPlugin优化)
代码分析
模块a
// a.js
var b = require('./b.js');// a模块依赖b
console.log('我是a模块');
b.fn(); //执行b模块的b1方法
模块b
// b.js
exports.fn = function () {
console.log('我是B模块方法')
};
打包输出的bundle.js文件
// bundle.js
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
var b = __webpack_require__(1);
console.log('我是a模块');
b.fn();
/***/ },
/* 1 */
/***/ function(module, exports) {
exports.fn = function () {
console.log('我是B模块方法')
};
/***/ }
/******/ ]);
1.bundle.js 是一个匿名的自执行函数,参数则为一个数组。数组的每一项都为个function。function的内容则为每个模块的内容,并按照require的顺序排列。
2._webpack_require是模块加载函数,接收的参数是模块id(每个模块都会有一个独一无二的id,也就是模块数组的索引)
3.有一个模块加载缓存机制,若无加载过得模块则会创建一个模块对象实例,并赋值给installedModules 数组中。
var module = installedModules[moduleId] = {
exports: {},
id: moduleId,
loaded: false
};
4._webpack_require加载函数会用call函数去执行每个模块的代码内容,执行对象是上面新创建的模块对象实例的exports属性引用的对象。
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
5.a模块依赖b模块,所以在a模块中调用了webpack加载模块的函数
var b = __webpack_require__(1);
6.webpack初始化执行第一个模块
return __webpack_require__(0);
总结
遗留思考:
1.上面提到的loader的实现原理是什么?
2.如何实现一个简单的webpack,有哪几个步骤?
3.webpack和其它打包工具如grunt/gulp对比的优劣?