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 相关文章推荐
利用js对象弹出一个层
Mar 26 Javascript
JQuery获取元素文档大小、偏移和位置和滚动条位置的方法集合
Jan 12 Javascript
JQery jstree 大数据量问题解决方法
Mar 09 Javascript
jquery创建并行对象或者合并对象的实现代码
Oct 10 Javascript
javascript计算用户打开网页的停留时间
Jan 09 Javascript
js函数调用的方式
May 06 Javascript
Jquery为DIV添加click事件的简单实例
Jun 02 Javascript
json实现添加、遍历与删除属性的方法
Jun 17 Javascript
浅谈Vue.js应用的四种AJAX请求数据模式
Aug 30 Javascript
vue实现双向绑定和依赖收集遇到的坑
Nov 29 Javascript
最简单的JS实现json转csv的方法
Jan 10 Javascript
详解vue组件之间的通信
Aug 30 Javascript
基于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网站判断用户是否是手机访问的方法
2013/11/01 PHP
PHP中filter函数校验数据的方法详解
2015/07/31 PHP
Javascript 表单之间的数据传递代码
2008/12/04 Javascript
firefox和IE系列的相关区别整理 以备后用
2009/12/28 Javascript
Jquery 实现Tab效果 思路是js思路
2010/03/02 Javascript
基于jquery的一个图片hover的插件
2010/04/24 Javascript
jQuery 1.5 源码解读 面向中高阶JSER
2011/04/05 Javascript
简单的jquery拖拽排序效果实现代码
2011/09/20 Javascript
jQuery中Ajax的load方法详解
2015/01/14 Javascript
jQuery设置指定网页元素宽度和高度的方法
2015/03/25 Javascript
jQuery图片特效插件Revealing实现拉伸放大
2015/04/22 Javascript
jQuery树形下拉菜单特效代码分享
2015/08/15 Javascript
JavaScript中循环遍历Array与Map的方法小结
2016/03/12 Javascript
js 基础篇必看(点击事件轮播图的简单实现)
2016/08/20 Javascript
jQuery插件Echarts实现的双轴图效果示例【附demo源码下载】
2017/03/04 Javascript
详解vue 模拟后台数据(加载本地json文件)调试
2017/08/25 Javascript
jQuery选择器之基本过滤选择器用法实例分析
2019/02/19 jQuery
js实现简单的无缝轮播效果
2020/09/05 Javascript
[00:20]TI9不朽观赛名额抽取
2019/08/05 DOTA
python计算列表内各元素的个数实例
2018/06/29 Python
python实现人机猜拳小游戏
2020/02/03 Python
python-xpath获取html文档的部分内容
2020/03/06 Python
浅谈keras 模型用于预测时的注意事项
2020/06/27 Python
美国开幕式潮店:Opening Ceremony
2018/02/10 全球购物
abstract是什么意思
2012/02/12 面试题
销售业务实习自我鉴定
2013/09/23 职场文书
工商管理实习生自我鉴定范文
2013/12/18 职场文书
成语的广告词
2014/03/19 职场文书
会计演讲稿范文
2014/05/23 职场文书
门面房租房协议书
2014/08/20 职场文书
《多彩的民间艺术》教学反思
2016/02/16 职场文书
公司晚会主持词
2019/04/17 职场文书
幼儿园教师辞职信
2019/06/21 职场文书
html实现弹窗的实例
2021/06/09 HTML / CSS
浅谈自定义校验注解ConstraintValidator
2021/06/30 Java/Android
springmvc直接不经过controller访问WEB-INF中的页面问题
2022/02/24 Java/Android