详解webpack分包及异步加载套路

网友投稿 550 2023-05-04


详解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'),

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.

当页面的路径为:

http://localhost:8080/pages/register/#/path1时,会加载path1.js文件

http://localhost:8080/pages/register/#/path2时,会加载path2.js文件.

再来看看webpack打包后的文件:

其中在common.js中, webpack定义了一个全局函数webpackJsonp.这个全局函数在项目一启动后就定义好。

局部函数__webpack_require__用以在某一个模块中初始化或者调用其他的模块方法。同时这个函数还有一个静态方法__webpack_require__.e这个方法就是用来异步加载js文件的。

接下来一步一步的看:

//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.

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, fzNlbkCVunction () {

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;

/***/ },


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:solr在java中的使用实例代码
下一篇:Maven2 plugin开发教程详解
相关文章

 发表评论

暂时没有评论,来抢沙发吧~