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 相关文章推荐
Iframe thickbox2.0使用的方法
Mar 05 Javascript
jquery dialog键盘事件代码
Aug 01 Javascript
JS 按钮点击触发(兼容IE、火狐)
Aug 07 Javascript
使用AngularJS制作一个简单的RSS阅读器的教程
Jun 18 Javascript
jquery实现具有嵌套功能的选项卡
Feb 12 Javascript
分享自己用JS做的扫雷小游戏
Feb 17 Javascript
正则表达式(语法篇推荐)
Jun 24 Javascript
Html5 js实现手风琴效果
Apr 17 Javascript
bootstrap选项卡扩展功能详解
Jun 14 Javascript
vue 实现LED数字时钟效果(开箱即用)
Dec 08 Javascript
js+canvas实现图片格式webp/png/jpeg在线转换
Aug 22 Javascript
JS指定音频audio在某个时间点进行播放
Nov 28 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 显示指定路径下的图片
2009/10/29 PHP
php中preg_match的isU代表什么意思
2015/10/01 PHP
PHP常用的小程序代码段
2015/11/14 PHP
Zend Framework入门教程之Zend_Mail用法示例
2016/12/08 PHP
基于PHP实现邮箱验证激活过程详解
2020/10/28 PHP
dropdownlist之间的互相联动实现(显示与隐藏)
2009/11/24 Javascript
javascript 事件处理程序介绍
2012/06/27 Javascript
javascript与jquery中跳出循环的区别总结
2013/11/04 Javascript
javascript四舍五入函数代码分享(保留后几位)
2013/12/10 Javascript
jQuery移除元素自动解绑事件实现思路及代码
2014/05/31 Javascript
jQuery中nextUntil()方法用法实例
2015/01/07 Javascript
jQuery插件slicebox实现3D动画图片轮播切换特效
2015/04/12 Javascript
JavaScript中标识符提升问题
2015/06/11 Javascript
Backbone.js框架中Model与Collection的使用实例
2016/05/07 Javascript
简单分析javascript中的函数
2016/09/10 Javascript
js实现可输入可选择的select下拉框
2016/12/21 Javascript
简单实现js选项卡切换效果
2017/02/09 Javascript
layer弹出层框架alert与msg详解
2017/03/14 Javascript
详解JavaScript对象的深浅复制
2017/03/30 Javascript
详解Angular2 关于*ngFor 嵌套循环
2017/05/22 Javascript
JS简单实现动态添加HTML标记的方法示例
2018/04/08 Javascript
JavaScript求一个数组中重复出现次数最多的元素及其下标位置示例
2018/07/23 Javascript
微信小程序自定义头部导航栏和导航栏背景图片 navigationStyle问题
2019/07/26 Javascript
jquery中attr、prop、data区别与用法分析
2019/09/25 jQuery
学习python (1)
2006/10/31 Python
浅谈python日志的配置文件路径问题
2018/04/28 Python
python实现蒙特卡罗方法教程
2019/01/28 Python
Python OpenCV 调用摄像头并截图保存功能的实现代码
2019/07/02 Python
Xadmin+rules实现多选行权限方式(级联效果)
2020/04/07 Python
如何在sublime编辑器中安装python
2020/05/20 Python
html5 拖拽上传图片实例演示
2013/04/01 HTML / CSS
座谈会主持词
2014/03/20 职场文书
大学毕业生推荐信
2014/07/09 职场文书
2014年重阳节老干部座谈会上的讲话稿
2014/09/25 职场文书
个人年终总结开头
2015/03/06 职场文书
创业计划书之婴幼儿游泳馆
2019/09/11 职场文书