webpack学习笔记之优化缓存、合并、懒加载


Posted in Javascript onAugust 24, 2017

除了的webpack基本配置,还可以进一步添加配置,优化合并文件,加快编译速度。下面是生产环境配置文件webpack.production.js,与wenbpack.config.js相比其不需要一些dev-tools,dev-server和jshint校验等,将与开发相关的东西删掉。下面的介绍均以此代码配置作参考。

/*生成环境配置文件:不需要一些dev-tools,dev-server和jshint校验等。和开发有关的东西删掉*/
var webpack = require('webpack');
var path = require('path');
var node_modules = path.resolve(__dirname, 'node_modules');
var pathToReact = path.resolve(node_modules, 'react/dist/react.min.js');
var pathToReactDOM = path.resolve(node_modules, 'react-dom/dist/react-dom.min.js');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
//具体作用及缺点见plugins中的描述
//var WebpackMd5Hash = require('webpack-md5-hash');
// 该插件是对“webpack-md5-hash”的改进:在主文件中获取到各异步模块的hash值,然后将这些hash值与主文件的代码内容一同作为计算hash的参数,这样就能保证主文件的hash值会跟随异步模块的修改而修改。
var WebpackSplitHash = require('webpack-split-hash');

var config = {
  entry:{
    app:path.resolve(__dirname, 'src/js/entry.js'),
    mobile: path.resolve(__dirname, 'src/js/mobile.js'),
    //添加要打包在vendors.js里面的库
    vendors:['react','react-dom']
  },
  resolve:{
    alias: {
      'react.js': pathToReact, //alias:别名,
      'react-dom.js': pathToReactDOM
    },
    fallback: path.join(__dirname, "node_modules")
  },
  resolveLoader: { 
    fallback: path.join(__dirname, "node_modules") 
  },
  output:{
    path:path.resolve(__dirname, 'dist'),
    publicPath:'../',//生成的html里的引用路径用 publicPath
    //以文件内容的MD5生成Hash名称的script来防止缓存
    filename: 'js/[name].[chunkhash:8].js',
    //异步加载的模块是要以文件形式加载,生成的文件名是以chunkFilename配置的
    chunkFilename: 'js/[name].[chunkhash:8].js'
  },
  module:{
    loaders:[{
      test: /\.jsx?$/,
      //这里(node_modules文件夹)再也不需通过任何第三方来加载
      exclude:path.resolve(__dirname, 'node_modules'),
      loader: 'babel',
      query:{
        presets:['es2015', 'react']
      }
    },
    {
      test:/\.css$/,
      //loader:'style!css'
      loader: ExtractTextPlugin.extract("style", "css")
    },
    {
      test:/\.scss$/,
      loader:'style!css!sass'
    },
    //url-loader:图片、字体图标加载器,是对file-loader的上层封装,支持base64编码。传入的size(也有的写limit) 参数是告诉它图片如果不大于 25KB 的话要自动在它从属的 css 文件中转成 BASE64 字符串。
    {
      test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/,
      loader: 'url?limit=25000&name=[name].[ext]'
    }]
  },
  plugins:[
    //提取公共代码的插件,用于提取多个入口文件的公共脚本部分,然后生成一个vendors.js。注意HTML代码中要加载这个公共文件
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendors',
      filename: 'js/vendors.js'
    }),
    //在文件开头插入banner
    new webpack.BannerPlugin("The file is creted by yangmin.--"+ new Date()),
    //压缩js文件
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      }
    }),
    /*插件:单独使用style标签加载css文件.contenthash代表的是文本文件内容的hash值,也就是只有style文件的hash值*/
    new ExtractTextPlugin("css/[name].[contenthash:8].css"),//设置其路径(路径相对于path)
    /*插件:动态生成html,在webpack完成前端资源打包以后,自动将打包后的资源路径和版本号写入HTML中,达到自动化的效果。*/
    new HtmlWebpackPlugin({
      filename:'view/index.html',  //生成的html存放路径,相对于 path
      template:'src/view/index.html',  //html模板路径
      inject:true,  //允许插件修改哪些内容,true/'head'/'body'/false,
      chunks:['vendors','app'],//加载指定模块中的文件,否则页面会加载所有文件
      hash:false,  //为静态资源生成hash值
      minify:{  //压缩HTML文件
        removeComments:false,  //移除HTML中的注释
        collapseWhitespace:false  //删除空白符与换行符
      }    
    }),
    new HtmlWebpackPlugin({
      filename:'view/mobile.html',  //生成的html存放路径,相对于 path
      template:'src/view/mobile.html',  //html模板路径
      inject:true,  //允许插件修改哪些内容,true/'head'/'body'/false,
      chunks:['vendors','mobile'],//加载指定模块中的文件,否则页面会加载所欲文件
      hash:false,  //为静态资源生成hash值
      minify:{  //压缩HTML文件
        removeComments:false,  //移除HTML中的注释
        collapseWhitespace:false  //删除空白符与换行符
      }
    }),
    /*作用:生成具有独立hash值的css和js文件,即css和js文件hash值解耦.
     *缺点:webpack-md5-hash插件对chunk-hash钩子进行捕获并重新计算chunkhash,它的计算方法是只计算模块本身的当前内容(包括同步模块)。这种计算方式把异步模块的内容忽略掉了,会造成一个问题:异步模块的修改并未影响主文件的hash值。
     */
    //new WebpackMd5Hash()
    new WebpackSplitHash()
  ]
}
 module.exports = config;

一、在开发环境中使用压缩文件

例如ReactJS项目中为了不让 Webpack 去遍历 React JS 及其所有依赖,你可以在webpack.config.js中重写它的行为。

config.alias: 每当 "react" 在代码中被引入,它会使用压缩后的 React JS 文件。

noParse: 阻止Webpack 去解析那个压缩后的文件。

当加载多个压缩文件时,下述方法更优雅简便,webpack.production.js:

var webpack = require("webpack");
...
var HtmlWebpackPlugin = require('html-webpack-plugin');

var deps = [
  'react/dist/react.min.js',
  'react-dom/dist/react-dom.min.js'
];
var config = {
  ...
  resolve:{
    alias:{},
    fallback:path.join(__dirname, "node_modules")
  },
  ...
  module:{
     ...
     noParse:[]  
   }  
}
/*当加载多个压缩文件时,下述方法更优雅简便*/
deps.forEach(function(dep){  
  var depPath = path.resolve(node_modules, dep);
  //path.dep是路径分隔符。
  config.resolve.alias[dep.split(path.dep)[0]] = depPath;  
  config.module.noParse.push(depPath);

});

module.exports = config;

二、分离应用和第三方文件

当你的应用依赖其他库尤其是像 React JS 这种大型库的时候,需要考虑把这些依赖分离出去,这样就能够让用户在你更新应用之后不需要再次下载第三方文件。上述配置文件中的entry里添加了第三方包vendors,其值为要分离打包的文件。运行配置后会在dist/js下生成三个独立文件:app.js、mobile.js、vendors.js。注意在页面中药引入vendors.js

<script src="../dist/vendors.js"></script>
<script src="../dist/app.js"></script>

三、多重入口

当应用有多个页面, 页面之间虽然有共享代码,但是不想在页面中加载所有代码时可以定义多重入口。例如配置文件中的app.js针对pc端页面,mobile.js仅针对移动端页面,output的filename:'js/[name].[chunkhash:8].js',采用了文件名变量,这样在dist/js中可生成与源文件同名的文件。

四、优化缓存及懒加载

在生产环境中,将输出文件名添加hash值,目的是在文件更改时强制客户端重新加载这个文件,而未改变的文件继续使用缓存文件。常用的有hash和chunkhash。配置文件中的[chunkhash:8]即截取8位chunkhash值。

webpack的编译理念:webpack将style视为js的一部分,所以在计算chunkhash时,会把所有的js代码和style代码混合在一起计算。比如entry.js引用了main.css:

import 'main.css'; 
alert('I am main.js');

webpack计算chunkhash时,以entry.js文件为编译入口,整个chunk的内容会将main.css的内容也计算在内。所以,不论是修改了js代码还是css代码,整个chunk的内容都改变了,计算所得的chunkhash随之改变。但理想情况下是想css或js内容改变时仅影响自身文件的chunkhash,这样客户端只需更新一部分文件。解决此问题首先要将css单独编译输出文件,因为正常情况下webpack会把js文件中引入的css文件编译输出到html页面的<style></style>标签中。

1.使用extract-text-webpack-plugin单独编译输出css文件

安装extract-text-webpack-plugin,

npm install extract-text-webpack-plugin --save-dev

然后在配置文件中引入插件,

//webpack.production.config.js
var ExtractTextPlugin = require('extract-text-webpack-plugin');

该插件除了chunkhash还提供了另外一种hash值contenthash。顾名思义,contenthash代表的是文本文件内容的hash值,也就是只有style文件的hash值。此hash是可解决上述问题的关键所在。上述配置文件使用了contenthash:

//webpack.production.config.js
new ExtractTextPlugin("css/[name].[contenthash:8].css"),//设置其路径(路径相对于path)

2.使用使用webpack-md5-hash解耦css和js文件hash值

再考虑以下情况,只修改了main.css文件,未修改entry.js文件,编译输出的js文件hash是否改变?答案是改变了,因为上文提到的webpack的编译理念,webpack将style视为js的一部分,所以在计算chunkhash时,会把所有的js代码和style代码混合在一起计算。解决办法是使用webpack-md5-hash插件:

//webpack.production.config.js

var WebpackMd5Hash = require('webpack-md5-hash');
...
new WebpackMd5Hash();

它的作用是生成具有独立hash值的css和js文件,即css和js文件hash值解耦。webpack-md5-hash插件对chunk-hash钩子进行捕获并重新计算chunkhash,它的计算方法是只计算模块本身的当前内容(包括同步模块)。

3.主文件使用hash代替chunkhash解决异步加载模块改变时主文件hash不改变

假如文件中引入了异步模块,异步模块修改后会影响编译输出的js文件的chunkhash吗?现在入口文件中引入异步模块a.js,a.js文件又异步引入b.js,b.js同步引入c模块

//entry.js

'use strict';

import './saveCarInfo.js';

window.onload = function(){//懒加载
  require.ensure(['./a.js'],function(require){
    var moduleA = require('./a.js');
  },'a');
};
//a.js

'use strict'

console.log("a");

setTimeout(function(){
  require.ensure([],function(require){
    require('./b.js');
  },'b');
},10000);

module.exports = "moduleA";
//b.js
import fn_c from './c.js';

console.log('b');
module.exports = 'moduleB';
//c.js

console.log("c");
module.exports = "moduleC";

运行npm run deploy,编译输出如下,我们看到除了入口文件、css文件、html文件被输出外,异步加载的模块a.js、b.js也被当做独立模块输出。

webpack学习笔记之优化缓存、合并、懒加载

此时修改a.js文件中的代码,经编译后,a.[chunkhash].js的chunkhash会改变,而生成的主文件app.[chunkhash].js的chunkhash值并没有改变。原因是webpack-md5-hash的这种计算方式把异步模块的内容忽略掉了,这会造成一个问题:异步模块的修改并未影响主文件的chunkhash值。解决办法是将输出的主文件采用[hash],而非[chunkhash]

output:{
  path:path.resolve(__dirname, 'dist'),
  publicPath:'../',//生成的html里的引用路径用 publicPath
  filename: 'js/[name].[hash:8].js',
  //异步加载的模块是要以文件形式加载,生成的文件名是以chunkFilename配置的
  chunkFilename: 'js/[name].[chunkhash:8].js'
},

这种做法也存在缺陷,如果项目中存在不止一个主js文件,修改任意js代码会影响所有最终主文件的[hash]值。例如上面的项目配置中会生成两个带[hash]的主文件:app.[hash].js, mobile.[hash].js。无论是修改entry.js代码还是异步模块a.js,或b.js的代码,app.[hash].js和mobile.[hash].js的[hash]都会改变。

补充:npm提供了webpack-split-hash插件代替webpack-md5-hash,该插件可以获取到各异步模块的hash值,然后将这些hash值与主文件的代码内容一同作为计算hash的参数,这样就能保证主文件的hash值会跟随异步模块的修改而修改。但我验证后没有实现。。。

4.使用html-webpack-plugin动态生成html

配置文件中的输出文件都带了[chunkhash]作为版本号,在style或js文件改变时,其值都会随之改变。利用html-webpack-plugin在webpack完成前端资源打包以后,自动将打包后的资源路径和版本号写入HTML中,达到自动化的效果。

//webpack.production.config.js

var HtmlWebpackPlugin = require('html-webpack-plugin');
...
var config = {
...
plugins:[
... 
  new HtmlWebpackPlugin({
    filename:'view/index.html',  //生成的html存放路径,相对于 path
    template:'src/view/index.html',  //html模板路径
    inject:true,  //允许插件修改哪些内容,true/'head'/'body'/false,
    chunks:['vendors','app'],//加载指定模块中的文件,否则页面会加载所有文件
    hash:false,  //为静态资源生成hash值
    minify:{  //压缩HTML文件
      removeComments:false,  //移除HTML中的注释
      collapseWhitespace:false  //删除空白符与换行符
     }    
  }),
  new HtmlWebpackPlugin({
    filename:'view/mobile.html',  //生成的html存放路径,相对于 path
    template:'src/view/mobile.html',  //html模板路径
    inject:true,  //允许插件修改哪些内容,true/'head'/'body'/false,
    chunks:['vendors','mobile'],//加载指定模块中的文件,否则页面会加载所有文件
    hash:false,  //为静态资源生成hash值
    minify:{  //压缩HTML文件
      removeComments:false,  //移除HTML中的注释
      collapseWhitespace:false  //删除空白符与换行符
     }    
  })
]}

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

Javascript 相关文章推荐
ExtJS PropertyGrid中使用Combobox选择值问题
Jun 13 Javascript
Javascript图像处理—平滑处理实现原理
Dec 28 Javascript
js拦截alert对话框另类应用
Jan 16 Javascript
采用自执行的匿名函数解决for循环使用闭包的问题
Sep 11 Javascript
jQuery实现类似老虎机滚动抽奖效果
Aug 06 Javascript
jquery实现隐藏在左侧的弹性弹出菜单效果
Sep 18 Javascript
讲解JavaScript的Backbone.js框架的MVC结构设计理念
Feb 14 Javascript
leaflet的开发入门教程
Nov 17 Javascript
Javascript 两种刷新方法以及区别和适用范围
Jan 17 Javascript
详解Chai.js断言库API中文文档
Jan 31 Javascript
详解Vue的异步更新实现原理
Dec 22 Vue.js
Axios代理配置及封装响应拦截处理方式
Apr 07 Vue.js
基于JavaScript实现新增内容滚动播放效果附完整代码
Aug 24 #Javascript
快速理解 JavaScript 中的 LHS 和 RHS 查询的用法
Aug 24 #Javascript
vue不通过路由直接获取url中参数的方法示例
Aug 24 #Javascript
VueJS 集成 Medium Editor的示例代码 (自定义编辑器按钮)
Aug 24 #Javascript
JS实现电商放大镜效果
Aug 24 #Javascript
weui框架实现上传、预览和删除图片功能代码
Aug 24 #Javascript
jQuery Datatable 多个查询条件自定义提交事件(推荐)
Aug 24 #jQuery
You might like
PHP中在数据库中保存Checkbox数据(1)
2006/10/09 PHP
PHP5新特性: 更加面向对象化的PHP
2006/11/18 PHP
PHP 第二节 数据类型之字符串类型
2012/04/28 PHP
php中判断数组是一维,二维,还是多维的解决方法
2013/05/04 PHP
php 邮件发送问题解决
2014/03/22 PHP
PHP定义字符串的四种方式详解
2018/02/06 PHP
Yii 使用intervention/image拓展实现图像处理功能
2019/06/22 PHP
javascript数组组合成字符串的脚本
2021/01/06 Javascript
jQuery LigerUI 使用教程入门篇
2012/01/18 Javascript
javascript获得网页窗口实际大小的示例代码
2013/09/21 Javascript
js菜单点击显示或隐藏效果的简单实例
2014/01/13 Javascript
javascript框架设计读书笔记之种子模块
2014/12/02 Javascript
webapp框架AngularUI的demo改造之路
2014/12/21 Javascript
javascript简单实现类似QQ头像弹出效果的方法
2015/08/03 Javascript
jQuery+css3实现Ajax点击后动态删除功能的方法
2015/08/10 Javascript
js时间戳转为日期格式的方法
2015/12/28 Javascript
JQuery form表单提交前验证单选框是否选中、删除记录时验证经验总结(整理)
2017/06/09 jQuery
vue.js根据代码运行环境选择baseurl的方法
2018/02/28 Javascript
详解Vue之计算属性
2020/06/20 Javascript
原生js+css实现tab切换功能
2020/09/17 Javascript
Python查看多台服务器进程的脚本分享
2014/06/11 Python
Python中__name__的使用实例
2015/04/14 Python
Python对多属性的重复数据去重实例
2018/04/18 Python
详解Python函数式编程—高阶函数
2019/03/29 Python
python实现趣味图片字符化
2019/04/30 Python
python线程中的同步问题及解决方法
2019/08/29 Python
Python3 ID3决策树判断申请贷款是否成功的实现代码
2020/05/21 Python
百思买美国官网:Best Buy
2016/07/28 全球购物
美国在线眼镜店:GlassesShop
2018/11/15 全球购物
专科文秘应届生求职信
2013/11/18 职场文书
三月学雷锋月活动总结
2014/04/28 职场文书
党员自我剖析材料范文
2014/10/06 职场文书
部队个人年终总结
2015/03/02 职场文书
Golang 使用Map实现去重与set的功能操作
2021/04/29 Golang
Python实现Hash算法
2022/03/18 Python
Redis主从复制操作和配置详情
2022/09/23 Redis