全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

详解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, 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;

/***/ },

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


# webpack  # 分包  # 异步加载  # webpack2  # webpack中splitChunks分包策略的实现  # 使用Webpack进行高效分包优化的实现  # 加载  # 都是  # 完后  # 是在  # 为我  # 会将  # 配置文件  # 就可以  # 的是  # 很好  # 好了  # 看了  # 其他的  # 它是  # 第二个  # 再来  # 还有一个  # 用过  # 就看  # 回调 


相关文章: 重庆市网站制作公司,重庆招聘网站哪个好?  桂林网站制作公司有哪些,桂林马拉松怎么报名?  广州建站公司哪家好?十大优质服务商推荐  定制建站流程解析:需求评估与SEO优化功能开发指南  建站之星后台密码如何安全设置与找回?  手机怎么制作网站教程步骤,手机怎么做自己的网页链接?  微课制作网站有哪些,微课网怎么进?  如何用PHP快速搭建CMS系统?  如何高效搭建专业期货交易平台网站?  如何在IIS7上新建站点并设置安全权限?  怎么用手机制作网站链接,dw怎么把手机适应页面变成网页?  如何通过VPS搭建网站快速盈利?  网站制作公司哪里好做,成都网站制作公司哪家做得比较好,更正规?  建站之星CMS五站合一模板配置与SEO优化指南  创业网站制作流程,创业网站可靠吗?  小建面朝正北,A点实际方位是否存在偏差?  佛山企业网站制作公司有哪些,沟通100网上服务官网?  购物网站制作公司有哪些,哪个购物网站比较好?  建站之星安装路径如何正确选择及配置?  c++怎么使用类型萃取type_traits_c++ 模板元编程类型判断【方法】  建站主机选购指南与交易推荐:核心配置解析  建设网站制作价格,怎样建立自己的公司网站?  公司门户网站制作流程,华为官网怎么做?  常州自助建站工具推荐:低成本搭建与模板选择技巧  建站主机是什么?如何选择适合的建站主机?  微信小程序制作网站有哪些,微信小程序需要做网站吗?  清单制作人网站有哪些,近日“兴风作浪的姑奶奶”引起很多人的关注这是什么事情?  网站制作网站,深圳做网站哪家比较好?  成都网站制作价格表,现在成都广电的单独网络宽带有多少的,资费是什么情况呢?  GML (Geography Markup Language)是什么,它如何用XML来表示地理空间信息?  宝华建站服务条款解析:五站合一功能与SEO优化设置指南  建站之星安装后如何配置SEO及设计样式?  如何选择最佳自助建站系统?快速指南解析优劣  如何配置IIS站点权限与局域网访问?  制作无缝贴图网站有哪些,3dmax无缝贴图怎么调?  台州网站建设制作公司,浙江手机无犯罪记录证明怎么开?  如何快速搭建高效香港服务器网站?  保定网站制作方案定制,保定招聘的渠道有哪些?找工作的人一般都去哪里看招聘信息?  学校为何禁止电信移动建设网站?  在线制作视频网站免费,都有哪些好的动漫网站?  如何基于PHP生成高效IDC网络公司建站源码?  建站之星后台搭建步骤解析:模板选择与产品管理实操指南  如何快速搭建虚拟主机网站?新手必看指南  韩国服务器如何优化跨境访问实现高效连接?  如何通过.red域名打造高辨识度品牌网站?  百度网页制作网站有哪些,谁能告诉我百度网站是怎么联系?  如何在阿里云高效完成企业建站全流程?  制作农业网站的软件,比较好的农业网站推荐一下?  如何在云指建站中生成FTP站点?  c++如何打印函数堆栈信息_c++ backtrace函数与符号名解析【方法】 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。