详解webpack分包及异步加载套路
最近一个小项目是用webpack来进行构建的。其中用到了webpack分包异步加载的功能。今天抽时间看了下webpack打包后的文件,大致弄明白了webpack分包及异步加载的套路。
由于这个小项目是用自己写的一个路由,路由定义好了不同路径对应下的模板及逻辑代码:
webpack配置文件:
var path = require('path'), DashboardPlugin = require('webpack-dashboard/plugin'), HtmlWebpackPlugin = require('html-webpack-plugin'), webpack = require('webpack'), ExtractTextPlugin = require('extract-text-webpack-plugin'); var PATHS = { app: path.join(__dirname, 'src'), dist: path.join(__dirname, 'dist') } var PKG = require('./package.json'); var TARGET = process.env.npm_lifecycle_event; //获取当前正在运行的脚本名称 var isProduction = function() { return process.env.NODE_ENV === 'production'; } module.exports ={ entry: { 'index': path.join(__dirname, 'src/index.js'), 'lib': ['./src/lib/js/index.js'], }, //filename是主入口文件的名称,即对应的entry //chunkFilename对应的是非主入口文件的名称,chunk output: { path: PATHS.dist, publicPath: '/static/taxi-driver/', //publicPath 的话是打包的时候生成的文件链接,如果是在生产环境是用服务器地址,如果是开发环境就是用本地静态服务器的地址 filename: 'js/register/[name].js', chunkFilename: 'js/register/[name].js', //TODO: build文件中加入hash值 }, //生成source-map文件 devtool: isProduction ? null : 'source-map', devServer: { proxy: { '/api/': { target: 'http://localhost:3000', secure: false } } }, module: { loaders: [ { test: /\.js$/, exclude: /node_modules|picker.min.js/, loader: 'babel' }, { test: /\.less$/, loader: ExtractTextPlugin.extract('style', 'css!less') }, { test: /\.html$/, loader: 'raw' }, { test: /\.css$/, loader: ExtractTextPlugin.extract('style', 'css') }, { test: /\.json$/, loader: 'json' } ] }, resolve: { alias: { src: path.join(__dirname, 'src'), modules: path.join(__dirname, 'src/modules'), lessLib: path.join(__dirname, 'src/lib/less'), jsLib: path.join(__dirname, 'src/lib/js'), ponents: path.join(__dirname, 'src/ponents') }, extensions: ['', '.js', '.less', '.html', '.json'], }, plugins: [ new HtmlWebpackPlugin({ title: '认证资料', template: './dist/assets/info.html', inject: 'body', filename: 'pages/register/index.html' //输出html文件的位置 }), new DashboardPlugin(), new ExtractTextPlugin('css/register/style.css'), //将引入的样式文件单独抽成style.css文件并插入到head标签当中,带有路径时,打包 new webpack.optimize.CommonsChunkPlugin({ name: 'mon', filename: 'js/register/mon.js', minChunks: 3 }) ] }
接下来是定义好的路由文件:
const Router = new Route(); Route .addRoute({ path: 'path1', viewBox: '.public-container', template: require('modules/path1/index.html'), pageInit() { //webpack提供的分包的API. require.ensure require.ensure([], () => { let controller = require('modules/path1/controller'); Router.registerCtrl('path1', new controller('.public-container')); }, 'path1'); } }) .addRoute({ path: 'path2', viewBox: '.public-container', template: require('modules/path2/index.html'), pageInit() { require.ensure([], () => { let controller = require('modules/path2/controller'); Router.registerCtrl('path2', new controller('.public-container')); }, 'path2'); } });
webpack会将这2个需要异步加载的模块,分别打包成path1.js和path2.js.
当页面的路径为:
http://localhost:8080/pages/register/#/path1时,会加载path1.js文件
http://localhost:8080/pages/register/#/path2时,会加载path2.js文件.
再来看看webpack打包后的文件:
其中在mon.js中, webpack定义了一个全局函数webpackJsonp.这个全局函数在项目一启动后就定义好。
局部函数__webpack_require__用以在某一个模块中初始化或者调用其他的模块方法。这个函数还有一个静态方法__webpack_require__.e这个方法就是用来异步加载js文件的。
接下来一步一步的看:
//mon.js (function(modules) { //modules用来保存所有的分包,它是一个数组,数组每个元素对应的都是callback,每个分包都是通过数字来进行标识的 //定义好的全局函数webpackJsonp //大家可以看看其他打包好的文件,例如index.js, path1.js和path2.js文件.都是webpackJsonp()这种的形式,大家用过JSONP应该会很好理解。在前端定义好函数,然后后端下发组装好的函数js文件,前端获取到这个文件后就可以立即进行执行了 var parentJsonpFunction = window["webpackJsonp"]; window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules) { var moduleId, chunkId, i = 0, callbacks = []; for(;i < chunkIds.length; i++) { chunkId = chunkIds[i]; if(installedChunks[chunkId]) callbacks.push.apply(callbacks, installedChunks[chunkId]); installedChunks[chunkId] = 0; } //这个全局函数会将各个分包缓存到modules for(moduleId in moreModules) { modules[moduleId] = moreModules[moduleId]; } if(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules); while(callbacks.length) callbacks.shift().call(null, __webpack_require__); //用以启动整个应用 if(moreModules[0]) { installedModules[0] = 0; return __webpack_require__(0); } }; })([]);
// The require function //通过数字标识的moduleId 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; } // This file contains only the entry chunk. // The chunk loading function for additional chunks //异步加载函数 __webpack_require__.e = function requireEnsure(chunkId, callback) { // "0" is the signal for "already loaded" if(installedChunks[chunkId] === 0) return callback.call(null, __webpack_require__); // an array means "currently loading". if(installedChunks[chunkId] !== undefined) { installedChunks[chunkId].push(callback); } else { //创建script表情,请求js文件 // start chunk loading installedChunks[chunkId] = [callback]; var head = document.getElementsByTagName('head')[0]; var script = document.createElement('script'); script.type = 'text/javascript'; script.charset = 'utf-8'; script.async = true; script.src = __webpack_require__.p + "js/register/" + ({"0":"index","1":"path1","2":"path2"}[chunkId]||chunkId) + ".js"; head.appendChild(script); } }; // expose the modules object (__webpack_modules__) __webpack_require__.m = modules; // expose the module cache __webpack_require__.c = installedModules; // __webpack_public_path__ //配置文件中定义的publicPath,build完后加载文件的路径 __webpack_require__.p = "/static/taxi-driver/"; })
在输出的index.html文件中加载的是这个mon.js文件,然后是入口文件index.js。因为这个实例代码里面没有很多共用文件,webpack自己提供的monChunkPlugin这个插件并没有起到作用,本来作为共用文件的xRoute.js也被打包进入了index.js.
webpackJsonp([0, 3], [ / 0 / // function(module, exports, __webpack_require__) { 'use strict'; __webpack_require__(1); __webpack_require__(8); // }, / 1 / / 2 / / 3 / //.... / 8 / ])
index.js文件在mon.js后加载,加载完后即开始执行.大家还记得webpackJsonp这个全局函数里面的倒数3行代码吧。就是用以调用这里:
/ 0 / function(module, exports, __webpack_require__) { 'use strict'; __webpack_require__(1); __webpack_require__(8); }
其中模块Id为1和8的内容请查看相应文件, 其中模块1为我定义的路由文件,在执行模块1的代码前,会加载模块2的内容,模块2的内容为我定义的路由库。
接下来就看下模块1中路由定义的具体内容
/ 1 / // function(module, exports, __webpack_require__) { 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); //加载路由库 var _index = __webpack_require__(2); //实例化一个路由 var Router = new _index.Route(); //定义好的路由规则 Router.home('path1').addRoute({ path: 'path1', viewBox: '.public-container', //模板文件,为模块4 template: __webpack_require__(4), pageInit: function pageInit() { //这个方法是在mon.js中__webpack_require__的静态方法,用来异步加载js。 //异步加载js的文件(即chunk)用来数字来标识,chunk的顺序从0开始. //这里path1.js的chunk num为1,大家可以回过头到mon.js的__webpack_require__.e方法里面看看,里面已经做好了chunk num和模块文件的映射, chunk 1对应的模块文件为path1.js,chunk 2对用的模块文件为path2.js //__webpack_require__.e()接收的第二个参数为异步加载模块后的回调. 当path1.js被加载完后,在modules里面进行了缓存.这时就可以通过模块id去获取这个模块。然后进行初始化等后续的操作 __webpack_require__.e/ nsure /(1, function () { var controller = __webpack_require__(6); Router.registerCtrl('path1', new controller('.public-container')); }); } }).addRoute({ path: 'path2', viewBox: '.public-container', //模板文件,为模块5 template: __webpack_require__(5), pageInit: function pageInit() { __webpack_require__.e/ nsure /(2, function () { var controller = __webpack_require__(7); Router.registerCtrl('path2', new controller('.public-container')); }); } }); Router.bootstrap(); exports.default = Router; // },
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持狼蚁SEO。
编程语言
- 如何快速学会编程 如何快速学会ug编程
- 免费学编程的app 推荐12个免费学编程的好网站
- 电脑怎么编程:电脑怎么编程网咯游戏菜单图标
- 如何写代码新手教学 如何写代码新手教学手机
- 基础编程入门教程视频 基础编程入门教程视频华
- 编程演示:编程演示浦丰投针过程
- 乐高编程加盟 乐高积木编程加盟
- 跟我学plc编程 plc编程自学入门视频教程
- ug编程成航林总 ug编程实战视频
- 孩子学编程的好处和坏处
- 初学者学编程该从哪里开始 新手学编程从哪里入
- 慢走丝编程 慢走丝编程难学吗
- 国内十强少儿编程机构 中国少儿编程机构十强有
- 成人计算机速成培训班 成人计算机速成培训班办
- 孩子学编程网上课程哪家好 儿童学编程比较好的
- 代码编程教学入门软件 代码编程教程