最近一個小項目是用webpack
來進行構建的。其中用到了webpack
分包異步加載的功能。今天抽時間看了下webpack
打包後的文件,大體弄明白了webpack
分包及異步加載的套路。javascript
因爲這個小項目是用本身寫的一個路由,路由定義好了不一樣路徑對應下的模板及邏輯代碼:css
webpack
配置文件:html
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'), components: path.join(__dirname, 'src/components') }, 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: 'common', filename: 'js/register/common.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
.java
當頁面的路徑爲:http://localhost:8080/pages/register/#/path1
時,會加載path1.js
文件http://localhost:8080/pages/register/#/path2
時,會加載path2.js
文件.node
再來看看webpack
打包後的文件:webpack
其中在common.js
中, webpack
定義了一個全局函數webpackJsonp
.這個全局函數在項目一啓動後就定義好。
局部函數__webpack_require__
用以在某一個模塊中初始化或者調用其餘的模塊方法。同時這個函數還有一個靜態方法__webpack_require__.e
這個方法就是用來異步加載js
文件的。web
接下來一步一步的看:npm
//common.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
文件中首先加載的是這個common.js
文件,而後是入口文件index.js
。由於這個實例代碼裏面沒有不少共用文件,所以webpack
本身提供的commonChunkPlugin
這個插件並無起到做用,原本做爲共用文件的xRoute.js
所以也被打包進入了index.js
.json
webpackJsonp([0, 3], [ /* 0 */ /***/ function(module, exports, __webpack_require__) { 'use strict'; __webpack_require__(1); __webpack_require__(8); /***/ }, /* 1 */ /* 2 */ /* 3 */ //.... /* 8 */ ])
index.js
文件在common.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() { //這個方法是在common.js中__webpack_require__的靜態方法,用來異步加載js。 //異步加載js的文件(即chunk)用來數字來標識,chunk的順序從0開始. //這裏path1.js的chunk num爲1,你們能夠回過頭到common.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; /***/ },