持久化缓存实施,webpack打包经验

webpack 持久化缓存实施

2018/01/15 · JavaScript
· webpack,
缓存

原稿出处: happylindz   

前言

ProvidePlugin

  • 语法:

    module.export = {

    plugins: [
         new webpack.ProvidePlugin({
             $: 'jquery',
             jQuery: 'jquery',
             'window.jQuery': 'jquery',
             'window.$': 'jquery',
        }),
    ]
    

    }

  • 作用:

rovidePlugin的体制是:当webpack加载到某些js模块里,出现了未定义且名称相符(字符串完全合作)配置中key的变量时,会自动require配置中value所钦点的js模块

利用ProvidePlugin还有个便宜,正是,你本人写的代码里,再!也!不!用!require!jQuery!啦!

延伸:

{
  test: require.resolve(‘jquery’), //
此loader配置项的对象是NPM中的jquery
  loader: ‘expose?$!expose?jQuery’, //
先把jQuery对象证明成为全局变量`jQuery`,再通过管道进一步又声称成为全局变量`$`
},

有了ProvidePlugin为嘛还需求expose-loader?

假诺你持有的jQuery插件都以用webpack来加载的话,的确用ProvidePlugin就够用了;

然则总有那个供给是不得不用<script>来加载的

 

前言

近年对三个比较老的市廛项目做了3回优化,管理的关键是webpack打包文件体积过大的标题。

此地就写一下对此webpack打包优化的局地经验。

第一分为以下多少个方面:

  1. 去掉开荒意况下的配备
  2. ExtractTextPlugin:提取样式到css文件
  3. webpack-bundle-analyzer:webpack打包文件体量和依据关系的可视化
  4. 康芒斯ChunkPlugin:提取通用模块文件
  5. 领到manifest:让提取的公家js的hash值不要改变
  6. 压缩js,css,图片
  7. react-router 肆 在此之前的按需加载
  8. react-router 四 的按需加载

本篇博客用到的webpack插件怎么样布置都可以去查看自身写的那篇博客:

【Webpack的使用指南
0二】Webpack的常用化解方案

那边就不细讲这一个安插了。

前言

不久前在看 webpack
咋办持久化缓存的始末,开掘其间依旧有局地坑点的,正好有时光就将它们整理总括一下,读完本文你大约能够知道:

  1. 怎么是持久化缓存,为何做持久化缓存?
  2. webpack 如何做持久化缓存?
  3. webpack 做缓存的壹对注意点。

多年来在看 webpack
如何是好持久化缓存的始末,开采内部照旧有局地坑点的,正好有时间就将它们整理总计一下,读完本文你差不离能够领悟:

webpack.optimize.CommonsChunkPlugin

  • 语法:

    new webpack.optimize.CommonsChunkPlugin({

    name: 'commons/commons',      
    filename: '[name]/bundle.js',
    minChunks: 4,
    

    }),

  • 作用:

收取出全部通用的部分,参数:

  1. name: ‘commons/commons’
    : 给那么些包蕴公共代码的chunk命个名(唯一标志)
  2. chunks: 表示要求在什么样chunk(也能够了然为webpack配置中entry的每一项)里研究国有代码举办包装。不设置此参数则暗中认可提取范围为具备的chunk
  3. filename: ‘[name]/bundle.js’
    :怎么着命名打包后生产的js文件,也是能够用上[name]、[hash]、[chunkhash]那些变量的,
     例子正是’commons/commons/bundle.js’了 (最后生成文书的路径是依附webpack配置中的ouput.path和下边CommonsChunkPlugin的filename参数来拼的)
  4. minChunks: 四,
    : 公共代码的度量楷模:有些js模块被有个别个chunk加载了才好不轻易集体代码

 

去掉开拓意况下的配备

持久化缓存实施,webpack打包经验。比如webpack中的devtool改为false,不供给热加载那类只用于支付景况的事物。

那一个不到底优化,而好不轻巧错误了。

对此在付出意况下才有用的事物在包装到生产条件时通通去掉。

持久化缓存

首先大家要求去解释一下,什么是持久化缓存,在明天光景端分离的利用大行其道的背景下,前端
html,css,js
往往是以1种静态财富文件的款式存在于服务器,通过接口来获取数据来显示动态内容。这就提到到厂家怎么样去安顿前端代码的难题,所以就事关到2个更新配备的主题材料,是先布置页面,还是先布置能源?

  1. 先安插页面,再布局能源:在双边陈设的时日距离内,借使有用户访问页面,就会在新的页面结构中加载旧的财富,并且把那个旧版本能源作为新本子缓存起来,其结果正是:用户访问到1个体裁错乱的页面,除非手动去刷新,不然在能源缓存过期在此之前,页面会一向处在混乱的意况。
  2. 先配备财富,再配置页面:在布局时间间隔内,有旧版本的能源本地缓存的用户访问网站,由于请求的页面是旧版本,资源引用未有改动,浏览器将直接行使本地缓存,那样属于常规意况,但没有本地缓存或许缓存过期的用户在走访网址的时候,就会现出旧版本页面加载新本子财富的意况,导致页面施行错误。

之所以大家要求壹种配备战术来担保在立异大家线上的代码的时候,线上用户也能平滑地接通并且精确张开我们的网址。

推荐先看这么些答复:大商号里怎么开采和安排前端代码?

当你读完上边的回应,大约就会清楚,今后比较早熟的持久化缓存方案就是在静态能源的名字背后加
hash 值,因为老是修改文件生成的 hash
值不等同,那样做的裨益在于增量式宣布文件,防止覆盖掉在此之前文件从而形成线上的用户访问失效。

因为借使达成每一回发布的静态能源(css, js,
img)的称号都以全世界无双的,那么本身就能够:

  • 针对 html 文件:不开启缓存,把 html
    放到自身的服务器上,关闭服务器的缓存,自个儿的服务器只提供 html
    文件和数据接口
  • 针对静态的 js,css,图片等文件:开启 cdn 和缓存,将静态财富上传到
    cdn
    服务商,大家能够对财富开启长时间缓存,因为每一个能源的路径都以无比的,所以不会促成财富被遮住,保险线上用户访问的心潮澎湃。
  • 老是发表更新的时候,先将静态财富(js, css, img) 传到 cdn
    服务上,然后再上传 html
    文件,那样既保险了老用户能还是不能够健康访问,又能让新用户观望新的页面。

上边大概介绍了下主流的前端持久化缓存方案,那么大家为啥须求做持久化缓存呢?

  1. 用户选用浏览器第二回访问大家的站点时,该页面引进了美妙绝伦的静态能源,如若我们能到位持久化缓存的话,能够在
    http 响应头加上 Cache-control 或 Expires
    字段来设置缓存,浏览器能够将那么些财富一1缓存到地头。
  2. 用户在延续访问的时候,假如急需再一次恳请同样的静态能源,且静态财富未有过期,那么浏览器能够一贯走地面缓存而不用再通过互联网请求能源。
  1. 什么是持久化缓存,为啥做持久化缓存?
  2. webpack 咋办持久化缓存?
  3. webpack 做缓存的部分注意点。

ExtractTextPlugin

  • 语法:

    new ExtractTextPlugin(‘[name]/styles.css’),

  • 作用:

抽取出chunk的css , 

ExtractTextPlugin的开端化参数不多,唯1的必填项是filename参数,也正是怎么来定名生成的CSS文件。跟webpack配置里的output.filename参数近似,那ExtractTextPlugin的filename参数也同意选拔变量,包涵[id]、[name]和[contenthash];理论上的话即便唯有贰个chunk,那么不用这几个变量,写死2个文本名也是能够的,但由于大家要做的是多页应用,必然存在多个chunk(至少每种entry都对应二个chunk啦)

在那里配置的[name]相应的是chunk的name,在webpack配置中把各类entry的name都按index/index、index/login那样的情势来安装了,那么最终css的路径就会像这么:build/index/index/styles.css,跟chunk的js文件放1块了(js文件的门径形如build/index/index/entry.js)

备注: 还要在css-loader , less-loader ,
postcss-loader 等关于体制的loader 配置里做相应的更换

{
  test: /\.css$/,
  include: /bootstrap/,
  use: ExtractTextPlugin.extract([{
    loader: 'css-loader',
  }]),
}

 

 

 

ExtractTextPlugin:提取样式到css文件

将样式提取到独门的css文件,而不是内嵌到打包的js文件中。

这么拉动的功利时分离出来的css和js是能够相互下载的,那样可以更加快地加载样式和本子。

消除方案:

安装ExtractTextPlugin

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

接下来修改webpack.config.js为:

const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
  // ...
  plugins: [
    // ...
    new ExtractTextPlugin({ filename: '[name].[contenthash].css', allChunks: false }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        exclude: /node_modules/,
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: ['css-loader?modules', 'postcss-loader'],
        }),
      }, {
        test: /\.css$/,
        include: /node_modules/,
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: ['css-loader', 'postcss-loader'],
        }),
      },
      {
        test: /\.less$/,
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: ['css-loader?modules', 'less-loader', 'postcss-loader'],
        }),
      },
    ],
  },
}

卷入后生成文件如下:

美高梅开户网址 1

webpack 如何是好持久化缓存

地点简要介绍完持久化缓存,下边这么些才是首要,那么我们应当怎么在 webpack
中展开持久化缓存的吗,大家必要完毕以下两点:

  1. 保险 hash 值的唯1性,即为各类打包后的能源转移二个独一无二的 hash
    值,只要打包内容不1致,那么 hash 值就不1致。
  2. 担保 hash
    值的安定,大家需求产生修改某些模块的时候,唯有受影响的打包后文件
    hash 值改换,与该模块毫无干系的打包文件 hash 值不改变。

hash 文件名是贯彻持久化缓存的首先步,近来 webpack 有二种总结 hash
的法子([hash] 和 [chunkhash])

  • hash 代表每趟 webpack 在编写翻译的长河中会生成唯一的 hash
    值,在类型中此外二个文书退换后就会被再度创造,然后 webpack 总括新的
    hash 值。
  • chunkhash 是依靠模块计算出来的 hash
    值,所以有些文件的改造只会潜移默化它本身的 hash 值,不会影响其余文件。

就此1旦您只是一味地将具有内容打包成同叁个文本,那么 hash
就可见满意你了,假如你的门类事关到拆包,分模块实行加载等等,那么你须要用
chunkhash,来保险每便换代之后唯有相关的文本 hash 值发生变动。

因此我们在1份具备持久化缓存的 webpack 配置相应长这么:

module.exports = { entry: __dirname + ‘/src/index.js’, output: { path:
__dirname + ‘/dist’, filename: ‘[name].[chunkhash:8].js’, } }

1
2
3
4
5
6
7
module.exports = {
  entry: __dirname + ‘/src/index.js’,
  output: {
    path: __dirname + ‘/dist’,
    filename: ‘[name].[chunkhash:8].js’,
  }
}

上面代码的意思便是:以 index.js
为进口,将有着的代码全部打包成多个文本取名字为 index.xxxx.js 并放置 dist
目录下,以后大家可以在每一遍换代项指标时候做到生成新命名的文书了。

假定是应付轻松的气象,那样做就够了,可是在巨型多页面使用中,大家一再供给对页面举行品质优化:

  1. 告辞业务代码和第二方的代码:之所以将事情代码和第3方代码分离出来,是因为作业代码更新频率高,而第3方代码更新迭代速度慢,所以大家将第一方代码(库,框架)实行抽离,这样可以充足利用浏览器的缓存来加载第1方库。
  2. 按需加载:例如在动用 React-Router
    的时候,当用户需求拜访到某些路由的时候再去加载对应的机件,那么用户大可不必在1上马的时候就将持有的路由组件下载到本地。
  3. 在多页面使用中,大家反复可以将集人体模型块实行抽离,举例 header, footer
    等等,那样页面在张开跳转的时候那个公共模块因为存在于缓存里,就足以一贯开始展览加载了,而不是再举行网络请求了。

这正是说怎么着进展拆包,分模块实行加载,那就要求 webpack
内置插件:CommonsChunkPlugin,下边小编将通过3个例子,来批注 webpack
该怎么着开始展览安插。

正文的代码放在自家的 Github 上,风乐趣的能够下载来看看:

git clone cd
blog/code/multiple-page-webpack-demo npm install

1
2
3
git clone https://github.com/happylindz/blog.git
cd blog/code/multiple-page-webpack-demo
npm install

开卷上边包车型客车始末前边笔者强烈提出你看下作者前边的篇章:深刻领会 webpack
文件打包机制,理解 webpack
文件的打包的编写制定促进你更加好地促成持久化缓存。

事例大致是如此讲述的:它由七个页面组成 pageA 和 pageB

// src/pageA.js import componentA from ‘./common/componentA’; // 使用到
jquery 第1方库,要求抽离,幸免业务打包文件过大 import $ from ‘jquery’;
// 加载 css 文件,1部分为集体样式,1部分为独有体制,须求抽离 import
‘./css/common.css’ import ‘./css/pageA.css’; console.log(componentA);
console.log($.trim(‘ do something ‘)); // src/pageB.js // 页面 A 和 B
都用到了公共模块 componentA,要求抽离,防止重新加载 import componentA
from ‘./common/componentA’; import componentB from
‘./common/componentB’; import ‘./css/common.css’ import
‘./css/pageB.css’; console.log(componentA); console.log(componentB); //
用到异步加载模块 asyncComponent,须要抽离,加载首屏速度
document.getElementById(‘xxxxx’).add伊夫ntListener(‘click’, () => {
import( /* webpackChunkName: “async” */
‘./common/asyncComponent.js’).then((async) => { async(); }) }) //
公共模块基本长那样 export default “component X”;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// src/pageA.js
import componentA from ‘./common/componentA’;
 
// 使用到 jquery 第三方库,需要抽离,避免业务打包文件过大
import $ from ‘jquery’;
 
// 加载 css 文件,一部分为公共样式,一部分为独有样式,需要抽离
import ‘./css/common.css’
import ‘./css/pageA.css’;
 
console.log(componentA);
console.log($.trim(‘    do something   ‘));
 
// src/pageB.js
// 页面 A 和 B 都用到了公共模块 componentA,需要抽离,避免重复加载
import componentA from ‘./common/componentA’;
import componentB from ‘./common/componentB’;
import ‘./css/common.css’
import ‘./css/pageB.css’;
 
console.log(componentA);
console.log(componentB);
 
// 用到异步加载模块 asyncComponent,需要抽离,加载首屏速度
document.getElementById(‘xxxxx’).addEventListener(‘click’, () => {
  import( /* webpackChunkName: "async" */
    ‘./common/asyncComponent.js’).then((async) => {
      async();
  })
})
 
// 公共模块基本长这样
export default "component X";

地点的页面内容主导总结关联到了大家拆分模块的三种形式:拆分公共库,按需加载和拆分公共模块。那么接下去要来配置
webpack:

const path = require(‘path’); const webpack = require(‘webpack’); const
ExtractTextPlugin = require(‘extract-text-webpack-plugin’);
module.exports = { entry: { pageA: [path.resolve(__dirname,
‘./src/pageA.js’)], pageB: path.resolve(__dirname, ‘./src/pageB.js’),
}, output: { path: path.resolve(__dirname, ‘./dist’), filename:
‘js/[name].[chunkhash:8].js’, chunkFilename:
‘js/[name].[chunkhash:8].js’ }, module: { rules: [ { //
用正则去相称要用该 loader 转变的 CSS 文件 test: /.css$/, use:
ExtractTextPlugin.extract({ fallback: “style-loader”, use:
[“css-loader”] }) } ] }, plugins: [ new
webpack.optimize.CommonsChunkPlugin({ name: ‘common’, minChunks: 2, }),
new webpack.optimize.CommonsChunkPlugin({ name: ‘vendor’, minChunks: ({
resource }) => ( resource && resource.indexOf(‘node_modules’) >=
0 && resource.match(/.js$/) ) }), new ExtractTextPlugin({ filename:
`css/[name].[chunkhash:8].css`, }), ] }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
const path = require(‘path’);
const webpack = require(‘webpack’);
const ExtractTextPlugin = require(‘extract-text-webpack-plugin’);
module.exports = {
  entry: {
    pageA: [path.resolve(__dirname, ‘./src/pageA.js’)],
    pageB: path.resolve(__dirname, ‘./src/pageB.js’),
  },
  output: {
    path: path.resolve(__dirname, ‘./dist’),
    filename: ‘js/[name].[chunkhash:8].js’,
    chunkFilename: ‘js/[name].[chunkhash:8].js’
  },
  module: {
    rules: [
      {
        // 用正则去匹配要用该 loader 转换的 CSS 文件
        test: /.css$/,
        use: ExtractTextPlugin.extract({
          fallback: "style-loader",
          use: ["css-loader"]
        })  
      }
    ]
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: ‘common’,
      minChunks: 2,
    }),
    new webpack.optimize.CommonsChunkPlugin({
      name: ‘vendor’,
      minChunks: ({ resource }) => (
        resource && resource.indexOf(‘node_modules’) >= 0 && resource.match(/.js$/)
      )
    }),
    new ExtractTextPlugin({
      filename: `css/[name].[chunkhash:8].css`,
    }),
  ]
}

首先个 康芒斯ChunkPlugin 用于抽离公共模块,也正是是说 webpack
大佬,即使您看到有个别模块被加载一次即以上,那么请您帮小编移到 common chunk
里面,那里 minChunks 为
2,粒度拆解最细,你能够依靠自身的莫过于境况,看选择是用有个别次模块才将它们抽离。

第三个 CommonsChunkPlugin
用来提取第1方代码,将它们实行抽离,判定财富是或不是来自
node_modules,假如是,则证实是第2方模块,那就将它们抽离。也便是是告诉
webpack 大佬,即使您瞧瞧某个模块是缘于 node_modules 目录的,并且名字是
.js 结尾的话,麻烦把他们都移到 vendor chunk 里去,如若 vendor chunk
不设有的话,就创办贰个新的。

这么布置有何利润,随着业务的增高,大家赖以的第1方库代码很只怕会愈增添,假若我们特地配备3个进口来存放第1方代码,那时候大家的
webpack.config.js 就会化为:

// 不便利开展 module.exports = { entry: { app: ‘./src/main.js’, vendor:
[ ‘vue’, ‘axio’, ‘vue-router’, ‘vuex’, // more ], }, }

1
2
3
4
5
6
7
8
9
10
11
12
13
// 不利于拓展
module.exports = {
  entry: {
    app: ‘./src/main.js’,
    vendor: [
      ‘vue’,
      ‘axio’,
      ‘vue-router’,
      ‘vuex’,
      // more
    ],
  },
}

其多个 ExtractTextPlugin 插件用于将 css 从打包好的 js
文件中抽离,生成独立的 css
文件,想象一下,当你只是修改了下样式,并不曾更换页面包车型大巴意义逻辑,你料定不期望你的
js 文件 hash 值变化,你一定是意在 css 和 js 能够相互分开,且互不影响。

运作 webpack 后方可知见打包之后的效用:

├── css │   ├── common.2beb7387.css │   ├── pageA.d178426d.css │   └──
pageB.33931188.css └── js ├── async.03f28faf.js ├── common.2beb7387.js
├── pageA.d178426d.js ├── pageB.33931188.js └── vendor.22a1d956.js

1
2
3
4
5
6
7
8
9
10
11
├── css
│   ├── common.2beb7387.css
│   ├── pageA.d178426d.css
│   └── pageB.33931188.css
└── js
    ├── async.03f28faf.js
    ├── common.2beb7387.js
    ├── pageA.d178426d.js
    ├── pageB.33931188.js
    └── vendor.22a1d956.js
 

能够看到 css 和 js 已经分离,并且大家对模块实行了拆分,保险了模块 chunk
的唯1性,当您每一趟换代代码的时候,会转移不等同的 hash 值。

唯1性有了,那么大家要求确认保障 hash
值的牢固,试想下这么的情景,你早晚不指望您改改某有个其他代码(模块,css)导致了文件的
hash 值全变了,那么显然是不明智的,那么大家去完结 hash 值变化最小化呢?

换句话说,大家将要搜索 webpack
编译中会导致缓存失效的成分,想艺术去化解或优化它?

潜移默化 chunkhash 值变化首要由以下四个部分引起的:

  1. 含蓄模块的源代码
  2. webpack 用于运转运作的 runtime 代码
  3. webpack 生成的模块 moduleid(包罗富含模块 id 和被引述的信赖性模块 id)
  4. chunkID

那肆局地只要有私行部分爆发变化,生成的分块文件就不平等了,缓存也就会失灵,下边就从多个部分每种介绍:

持久化缓存

HtmlWebpackPlugin

  • 语法:

    var glob = require(‘glob’);
    var path = require(‘path’);
    var options = {
    cwd: ‘./src/pages’, // 在pages目录里找
    sync: true, // 那里无法异步,只可以同步
    };
    var globInstance = new glob.Glob(‘!()*/!()*’, options); // 思索到八个页面共用HTML等财富的情形,跳过以’_’伊始的目录
    var pageArr = globInstance.found; // 一个数组,形如[‘index/index’, ‘index/login’, ‘alert/index’]
    var configPlugins = [];
    pageArr.forEach((page) => {
    const htmlPlugin = new HtmlWebpackPlugin({

    filename: `${page}/page.html`,
    template: path.resolve(dirVars.pagesDir, `./${page}/html.js`),
    // 意思是加载 page 下面的js , 和加载 commons/commons 目录下的js
    chunks: [page, 'commons/commons'],
    hash: true, // 为静态资源生成hash值
    xhtml: true,
    

    });
    configPlugins.push(htmlPlugin);
    });

  • 作用:

生成html,参数:

  1. filename  `${page}/page.html`, : 生成的公文名字,多页面就会有四个 HtmlWebpackPlugin ,日常使用循环生成一个数组
  2. template : path.resolve(dirVars.pagesDir, `./${page}/html.js`),  
    生成的html 基于的沙盘
  3. chunks : [ page, ‘commons/commons’] : 意思是加载 变量page 和
     commons/commons 目录下的js
  4. hash: true : 为静态财富生成hash值

 

webpack-bundle-analyzer:webpack打包文件体量和依据关系的可视化

本条东西不到底优化,而是让咱们能够清楚得看看各样包的出口文件体量与互动关系。

安装:

npm install --save-dev webpack-bundle-analyzer

下一场修改webpack.config.js:

const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

module.exports = merge(common, {
  // ...
  plugins: [
    new BundleAnalyzerPlugin({ analyzerPort: 8919 })
  ],
});

装进后会自动出现三个端口为891玖的站点,站点内容如下:

美高梅开户网址 2

能够见见大家打包后的main.js中的代码1部分源于node_modules文件夹中的模块,1部分来源自身写的代码,也正是src文件夹中的代码。

为了未来描述方便,那一个图大家直接翻译过来就叫webpack打包分析图。

一、源代码变化:

明确性不用多说,缓存必要求刷新,不然就有标题了

先是大家需求去解释一下,什么是持久化缓存,在今后光景端分离的选择大行其道的背景下,前端
html,css,js
往往是以一种静态财富文件的情势存在于服务器,通过接口来获取数据来体现动态内容。这就涉嫌到商铺怎么着去安排前端代码的主题材料,所以就涉及到3个更新配备的标题,是先安顿页面,依然先配备财富?

CommonsChunkPlugin:提取通用模块文件

所谓通用模块,正是如react,react-dom,redux,axios差不多各种页面都会动用到的js模块。

将这么些js模块提收取来放到贰个文书中,不仅能够裁减主文件的尺寸,在第3遍下载的时候能并行下载,进步加载作用,更要紧的是那个文件的代码大概不会转移,那么每一趟打包发布后,如故会沿用缓存,从而加强了加载功效。

而对于那多少个多文件输入的采纳越来越使得,因为在加载不一样的页面时,那部分代码是国有的,直接能够从缓存中央银行使。

这么些东西不供给设置,间接退换webpack的配备文件就能够:

const webpack = require('webpack');

module.exports = {
  entry: {
    main: ['babel-polyfill', './src/app.js'],
    vendor: [
      'react',
      'react-dom',
      'redux',
      'react-router-dom',
      'react-redux',
      'redux-actions',
      'axios'
    ]
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      names: ['vendor'],
      minChunks: Infinity,
      filename: 'common.bundle.[chunkhash].js',
    })
  ]
}

卷入后的webpack打包分析图为:

美高梅开户网址 3

能够很扎眼看到react那么些模块都被打包进了common.js中。

贰、webpack 运维运作的 runtime 代码:

看过本身在此之前的篇章:深入领悟 webpack
文件打包机制 就会明白,在
webpack 运营的时候须求实行一些运维代码。

(function(modules) { window[“webpackJsonp”] = function
webpackJsonpCallback(chunkIds, moreModules) { // … }; function
__webpack_require__(moduleId) { // … } __webpack_require__.e
= function requireEnsure(chunkId, callback) { // … script.src =
__webpack_require__.p + “” + chunkId + “.” +
({“0″:”pageA”,”1″:”pageB”,”3″:”vendor”}[chunkId]||chunkId) + “.” +
{“0″:”e72ce7d4″,”1″:”69f6bbe3″,”2″:”9adbbaa0″,”3″:”53fa02a7”}[chunkId]

  • “.js”; }; })([]);
1
2
3
4
5
6
7
8
9
10
11
12
(function(modules) {
  window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules) {
    // …
  };
  function __webpack_require__(moduleId) {
    // …
  }
  __webpack_require__.e = function requireEnsure(chunkId, callback) {
    // …
    script.src = __webpack_require__.p + "" + chunkId + "." + ({"0":"pageA","1":"pageB","3":"vendor"}[chunkId]||chunkId) + "." + {"0":"e72ce7d4","1":"69f6bbe3","2":"9adbbaa0","3":"53fa02a7"}[chunkId] + ".js";
  };
})([]);

大概内容像上边那样,它们是 webpack
的部分初始代码,它们是局地函数,告诉浏览器如何加载 webpack 定义的模块。

在那之中有一行代码每一回换代都会更改的,因为运维代码须要驾驭地领悟 chunkid 和
chunkhash 值得对应涉及,那样在异步加载的时候才具科学地拼接出异步 js
文件的门径。

那正是说那有的代码最后放在哪个文件呢?因为大家刚刚配置的时候最后生成的
common chunk
模块,那么那部分运转时期码会被直接内置在里面,这就形成了,大家每一遍换代我们工作代码(pageA,
pageB, 模块)的时候, common chunkhash
会一贯变化,不过那明摆着不适合我们的设想,因为大家只是要用 common chunk
用来存放在公共模块(那里指的是 componentA),那么本人 componentA
都没去修改,凭啥 chunkhash 必要变了。

因此咱们需求将那部分 runtime 代码抽离成单身文件。

module.exports = { // … plugins: [ // … // 放到任何的
CommonsChunkPlugin 前面 new webpack.optimize.CommonsChunkPlugin({ name:
‘runtime’, minChunks: Infinity, }), ] }

1
2
3
4
5
6
7
8
9
10
11
module.exports = {
  // …
  plugins: [
    // …
    // 放到其他的 CommonsChunkPlugin 后面
    new webpack.optimize.CommonsChunkPlugin({
      name: ‘runtime’,
      minChunks: Infinity,
    }),
  ]
}

这一定于是告诉 webpack 帮我把运营时期码抽离,放到单独的文本中。

├── css │   ├── common.4cc08e4d.css │   ├── pageA.d178426d.css │   └──
pageB.33931188.css └── js ├── async.03f28faf.js ├── common.4cc08e4d.js
├── pageA.d178426d.js ├── pageB.33931188.js ├── runtime.8c79fdcd.js └──
vendor.cef44292.js

1
2
3
4
5
6
7
8
9
10
11
12
├── css
│   ├── common.4cc08e4d.css
│   ├── pageA.d178426d.css
│   └── pageB.33931188.css
└── js
    ├── async.03f28faf.js
    ├── common.4cc08e4d.js
    ├── pageA.d178426d.js
    ├── pageB.33931188.js
    ├── runtime.8c79fdcd.js
    └── vendor.cef44292.js
 

多生成了三个 runtime.xxxx.js,未来您在转移业务代码的时候,common chunk
的 hash 值就不会变了,替代它的是 runtime chunk hash
值会变,既然这有个别代码是动态的,能够经过 chunk-manifest-webpack-plugin
将他们 inline 到 html 中,减弱三遍网络请求。

先配备页面,再配置财富:在2者布署的小运距离内,假若有用户访问页面,就会在新的页面结构中加载旧的财富,并且把那些旧版本资源作为新本子缓存起来,其结果正是:用户访问到一个样式错乱的页面,除非手动去刷新,不然在资源缓存过期在此之前,页面会平昔处在混乱的意况。

领到manifest:让提取的集体js的hash值不要改造

当大家精晓webpack中的hash值时,一般都汇合到[hash]和[chunkhash]两种hash值的配置。

内部hash根据每一趟编写翻译的内容总结获得,所以每编写翻译2遍具有文件都会变卦一个新的hash,也就完全无法利用缓存。

据此大家那边用了[chunkhash],chunkhash是基于剧情来变化的,所以只要剧情不变,那么生成的hash值就不会改换。

chunkhash适用于一般的景色,可是,对于大家上述的图景是不适用的。

本人去退换主文件代码,然后生成的八个国有js代码的chunkhash值却改造了,它们并未行使到主文件。

于是乎小编用文件相比较工具,相比较了它们的代码,开掘唯有1行代码是有差异的:

美高梅开户网址 4

那是因为webpack在实行时会有贰个带有模块标记的运维时期码。

当大家不领取vendor包的时候那段代码会被打包到main.js文件中。

当大家领到vendor到common.js时,那段脚本会被注入到common.js里面,而main.js中尚无那段脚本了了.

当大家将库文件分为三个包提收取来,分别为common1.js和common二.js,开掘这段脚本只现出在2个common一.js中,并且
这段标志代码产生了:

u.src=t.p+""+e+"."+{0:"9237ad6420af10443d7f",1:"be5ff93ec752c5169d4c"}

下一场开掘别的包的首部都会有个这么的代码:

webpackJsonp([1],{2:functio

以此运转时脚本的代码正好和任何包起来的那段代码中的数字相对应。

咱俩得以将那1部分代码提取到二个独自的js中,那样打包的国有js就不会遭到震慑。

我们能够进行如下配置:

 plugins: [
   new webpack.optimize.CommonsChunkPlugin({
     names: ['vendor'],
     minChunks: Infinity,
     filename: 'common.bundle.[chunkhash].js',
   }),
   new webpack.optimize.CommonsChunkPlugin({
     names: ['manifest'],
     filename: 'manifest.bundle.[chunkhash].js',
   }),
   new webpack.HashedModuleIdsPlugin()
 ]

对于names来说,假若chunk已经在entry中定义了,那么就会基于entry中的入口提取chunk文件。假诺没有概念,比方mainifest,那么就会扭转叁个空的chunk文件,来领取别的全部chunk的共用代码。

而作者辈那段代码的意趣正是将webpack注入到包中的那段公共代码提收取来。

包装后的文书:

美高梅开户网址 5

webpack打包分析图:

美高梅开户网址 6

看到图绿深橙的格外块了吧?

可怜东西就是包裹后的manifest文件。

诸如此类管理后,当我们再修改主文件中的代码时,生成的公共js的chunkhash是不会改造的,改造的是可怜单独提抽出来的manifest.bundle.[chunkhash].js的chunkhash。

3、webpack 生成的模块 moduleid

在 webpack二 中私下认可加载 OccurrenceOrderPlugin
那么些插件,OccurrenceOrderPlugin
插件会按引进次数最多的模块举办排序,引进次数的模块的 moduleId
越小,可是那依旧是不稳固的,随着你代码量的增添,即便代码引用次数的模块
moduleId 越小,越不易于生成,然则免不了依旧不分明的。

私下认可情形下,模块的 id
是那么些模块在模块数组中的索引。OccurenceOrderPlugin
会将引用次数多的模块放在眼下,在历次编写翻译时模块的依次都以一模一样的,如若您改改代码时新扩张或删除了有个别模块,那将可能会影响到独具模块的
id。

最棒试行方案是经过 HashedModuleIdsPlugin
那几个插件,那个插件会依照模块的相对路线生成3个长度唯有四人的字符串作为模块的
id,既隐藏了模块的门道消息,又收缩了模块 id 的长度。

那样壹来,改换 moduleId
的不二诀窍就唯有文件路线的变动了,只要您的文件路径值不改变,生成四位的字符串就不改变,hash
值也不改变。扩充或删除业务代码模块不会对 moduleid 发生别的影响。

module.exports = { plugins: [ new webpack.HashedModuleIdsPlugin(), //
放在最前面 // … ] }

1
2
3
4
5
6
7
module.exports = {
  plugins: [
    new webpack.HashedModuleIdsPlugin(),
    // 放在最前面
    // …
  ]
}

先配备财富,再计划页面:在安立即间间隔内,有旧版本的能源本地缓存的用户访问网址,由于请求的页面是旧版本,财富引用未有更换,浏览器将直接采取本地缓存,那样属于常规状态,但尚未地方缓存恐怕缓存过期的用户在造访网址的时候,就会见世旧版本页面加载新本子能源的状态,导致页面实施错误。

压缩js,css,图片

本条实际上不筹算记录进入,因为那一个相似品种应当都有所了,但是那里照旧顺带提一句吧。

压缩js和css一步就能够:

webpack -p

图片的压缩:

image-webpack-loader

具体的利用请查看
Webpack的常用化解方案
的第16点。

四、chunkID

事实上情状中分块的个数的逐条在多次编写翻译之间基本上都以固定的,
不太轻便发生变化。

那边涉及的只是比较基础的模块拆分,还有壹部分任何情况未有设想到,比方异步加载组件中带有公共模块,能够重新将集人体模型块举办抽离。产生异步公共
chunk 模块。有想深远学习的能够看那篇小说:Webpack 大法之 Code
Splitting

由此大家需求1种配备战略来保险在革新大家线上的代码的时候,线上用户也能平滑地接通并且精确展开大家的网址。

react-router 4 以前的按需加载

即便接纳过Ant Design
一般都精通有二个计划按需加载的功用,就是在终极打包的时候只把用到的零部件代码打包。

而对此一般的react组件其实也有二个使用react-router完结按需加载的游戏的方法。

对于每三个路由来说,别的路由的代码实际上并不是必须的,所以当切换成某1个路由后,即便只加载这些路由的代码,那么首屏加载的快慢将大大提高。

首先在webpack的output中配置

output: {
  // ...
  chunkFilename: '[name].[chunkhash:5].chunk.js',
},

下一场需求将react-router的加载改为按需加载,比方对于上边那样的代码:

import React from 'react';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import ReactDOM from 'react-dom';
import { HashRouter as Router, Route } from 'react-router-dom';
import PageMain from './components/pageMain';
import PageSearch from './components/pageSearch';
import PageReader from './components/pageReader';
import reducer from './reducers';

const store = createStore(reducer);
const App = () => (
  <Provider store={store}>
    <Router>
      <div>
        <Route exact path="/" component={PageMain} />
        <Route path="/search" component={PageSearch} />
        <Route path="/reader/:bookid/:link" component={PageReader} />
      </div>
    </Router>
  </Provider>
);

应该改为:

import React from 'react';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import ReactDOM from 'react-dom';
import { HashRouter as Router, Route } from 'react-router-dom';
import reducer from './reducers';

const store = createStore(reducer);

const PageMain = (location, callback) => {
  require.ensure([], require => {
    callback(null, require('./components/pageMain').default);
  }, 'PageMain');
};

const PageSearch = (location, callback) => {
  require.ensure([], require => {
    callback(null, require('./components/pageSearch').default);
  }, 'PageSearch');
};

const PageReader = (location, callback) => {
  require.ensure([], require => {
    callback(null, require('./components/pageReader').default);
  }, 'PageReader');
};

const App = () => (
  <Provider store={store}>
    <Router>
      <div>
        <Route exact path="/" getComponent={PageMain} />
        <Route path="/search" getComponent={PageSearch} />
        <Route path="/reader/:bookid/:link" getComponent={PageReader} />
      </div>
    </Router>
  </Provider>
);

webpack 做缓存的有的留意点

  1. CSS 文件 hash 值失效的难题
  2. 不提议线上宣布使用 DllPlugin 插件

引入先看那几个回答:大厂商里怎么开垦和配置前端代码?

react-router 四 的按需加载

地点那种情势运用到react-router
四上是不行的,因为getComponent方法已经被移除了。

接下来作者参考了合法教程的办法

在此处大家须要用到webpack, babel-plugin-syntax-dynamic-import和
react-loadable。

webpack内建了动态加载,不过大家因为用到了babel,所以须要去用babel-plugin-syntax-dynamic-import避免做一些格外的改造。

由此率先要求

npm i babel-plugin-syntax-dynamic-import  --save-dev

接下来在.babelrc参与配置:

"plugins": [
  "syntax-dynamic-import"
]

接下去大家须要用到react-loadable,它是2个用以动态加载组件的高阶组件。
那是官英特网的1个例证

import Loadable from 'react-loadable';
import Loading from './my-loading-component';

const LoadableComponent = Loadable({
  loader: () => import('./my-component'),
  loading: Loading,
});

export default class App extends React.Component {
  render() {
    return <LoadableComponent/>;
  }
}

行使起来并简单,Loadable函数会传出三个参数对象,重返二个渲染到界面上的零部件。
本条参数对象的loader属性正是内需动态加载的机件,而loading这脾个性传入的是1个显得加载状态的零件,当还并未有加载出动态组件时,显示在分界面上的就是以此loading组件。

运用那种艺术相对于原来的章程优势很醒目,大家不光是在路由上能够张开动态加载了,我们动态加载的零部件粒度能够更加细,比方一个石英钟组件,而不是像以前那样频仍是3个页面。

透过灵活去行使动态加载能够周密调控加载的js的分寸,从而使首屏加载时间和别的页面加载时控到二个针锋相对平衡的度。

那边有个点需求小心,正是一般大家在选择loading组件时平时会产出的难题:闪烁现象。

那种气象的原委是,在加载真正的机件前,会并发loading页面,然则组件加载高效,就会招致loading页面出现的时光非常的短,从而导致闪烁。

杀鸡取蛋的办法正是加个属性delay

const LoadableComponent = Loadable({
  loader: () => import('./my-component'),
  loading: Loading,
  delay: 200
});

只有当加载时间超越200ms时loading组件才会冒出。

再有越多的关于react-loadable的游戏的方法:

那么今后看下大家的打包文件:

美高梅开户网址 7

美高梅开户网址 ,webpack打包分析图:

美高梅开户网址 8

注意看看下边的打包文件名字,开采经过那种措施实行按需加载的多少个公文都是循序渐进数字命名,而尚未如约大家希望的机件名命名。

自家在那么些项目标github上边找了须臾间,发掘它提供的按组件命名的章程供给利用服务端渲染,然后就不曾继续下去了。

反正那一个东西也不是很重大,所以就不曾进一步商讨,假设有园友对那个主题材料有好的点子,也可望能在评价里证实。

CSS 文件 hash 值失效的标题:

ExtractTextPlugin
有个相比较严重的标题,那便是它生成文书名所用的[chunkhash]是一向取自于引用该
css 代码段的 js chunk ;换句话说,假设自身只是修改 css 代码段,而不动 js
代码,那么最后生成出来的 css 文件名还是未有成形。

由此大家供给将 ExtractTextPlugin 中的 chunkhash 改为
contenthash,顾名思义,contenthash 代表的是文本文件内容的 hash
值,也正是唯有 style 文件的 hash 值。那样编译出来的 js 和 css
文件就有独立的 hash 值了。

module.exports = { plugins: [ // … new ExtractTextPlugin({ filename:
`css/[name].[contenthash:8].css`, }), ] }

1
2
3
4
5
6
7
8
module.exports = {
  plugins: [
    // …
    new ExtractTextPlugin({
      filename: `css/[name].[contenthash:8].css`,
    }),
  ]
}

要是您使用的是 webpack二,webpack三,那么恭喜你,那样就够用了,js 文件和
css 文件修改都不会潜移默化到互相的 hash 值。那假使你使用的是
webpack一,那么就会并发难题。

具体来讲正是 webpack一 和 webpack 在测算 chunkhash 值得不相同:

webpack一 在论及的时候并从未思虑像 ExtractTextPlugin
会将模块内容抽离的主题材料,所以它在计算 chunkhash
的时候是经过包装在此之前模块内容去总括的,也正是说在测算的时候 css
内容也暗含在内,之后才将 css 内容抽离成单身的公文,

那么就会现出:固然只修改了 css 文件,未修改引用的 js
文件,那么编译输出的 js 文件的 hash 值也会变动。

对此,webpack二 做了改正,它是依附打包后文件内容来估测计算 hash
值的,所以是在 ExtractTextPlugin 抽离 css
代码之后,所以就不存在上述如此的标题。假如不幸的您还在运用
webpack1,那么推荐你使用 md5-hash-webpack-plugin 插件来改动 webpack 总括hash 的国策。

当你读完上边包车型地铁答应,大概就会知晓,未来相比较成熟的持久化缓存方案就是在静态能源的名字背后加
hash 值,因为每一趟修改文件生成的 hash
值不一样样,那样做的便宜在于增量式发表文件,幸免覆盖掉此前文件从而致使线上的用户访问失效。

总结

总的来讲,通过以上步骤应该是能够缓慢解决超越5/十装进文件容量过大的难题。

本来,因为文中webpack版本和插件版本的距离,在布署和玩法上会有局地见仁见智,不过下面描述的那一个动向都是未有毛病的,并且相信在每家每户版本下都足以找到呼应的缓慢解决方案。

文中如有疑误,请不吝赐教。

不建议线上颁发使用 DllPlugin 插件

为啥那样说吧?因为近日有对象来问笔者,他们 leader 不让在线上用 DllPlugin
插件,来问我何以?

DllPlugin 自个儿有多少个毛病:

  1. 先是你要求卓殊多配备1份 webpack 配置,扩张职业量。
  2. 里面三个页面用到了二个体量相当大的第壹方信赖库而其余页面根本不必要选用,但若一向将它包裹在
    dll.js 里很不值得,每一遍页面张开都要去加载那段无用的代码,不可能利用到
    webpack二 的 Code Splitting 作用。
  3. 先是次打开的时候必要下载 dll
    文件,因为您把数不胜数库全体打在联名了,导致 dll
    文件不小,第三回进入页面加载速度极慢。

就算你能够打包成 dll
文件,然后让浏览器去读取缓存,那样下次就绝不再去恳求,比方您用 lodash
在那之中三个函数,而你用dll会将全部 lodash
文件打进去,那就会产生您加载无用代码过多,不便于首屏渲染时间。

笔者感觉的精确的姿势是:

  1. 像 React、Vue 那样全体性偏强的库,能够生成 vendor
    第3方库来去做缓存,因为你相似才具系统是定位的,三个站点里面基本上都会用到统一技巧系统,所以生成
    vendor 库用于缓存。
  2. 像 antd、lodash 这种成效性组件库,能够透过 tree shaking
    来进行破除,只保留有用的代码,千万不要向来打到 vendor
    第2方Curry,否则你将大量实施无用的代码。

因为借使成功每便公布的静态财富(css, js,
img)的称号都以绝无仅有的,那么本人就可以:

结语

好了,感到本身又扯了累累,方今在看 webpack
确实获得累累,希望我们能从文章中也能抱有收获。其它推荐再度推荐一下自己事先写的篇章,能够更好地帮您明白文件缓存机制:深远掌握webpack 文件打包机制

  1. 本着 html 文件:不开启缓存,把 html
    放到本人的服务器上,关闭服务器的缓存,自个儿的服务器只提供 html
    文件和数量接口
  2. 本着静态的 js,css,图片等公事:开启 cdn 和缓存,将静态能源上传到
    cdn
    服务商,我们得以对财富开启长时间缓存,因为各种能源的门道都以无可比拟的,所以不会招致资源被掩盖,保障线上用户访问的地西泮团结。
  3. 每趟发布更新的时候,先将静态能源(js, css, img) 传到 cdn
    服务上,然后再上传 html
    文件,这样既保证了老用户能还是没办法平常访问,又能让新用户看到新的页面。

参照链接:

  • Webpack中hash与chunkhash的区分,以及js与css的hash指纹解耦方案
  • webpack多页应用架构体系(十6):善用浏览器缓存,该去则去,该留则留
  • 用 webpack
    实现持久化缓存
  • Webpack
    真正的持久缓存完结

    2 赞 2 收藏
    评论

美高梅开户网址 9

地点差不多介绍了下主流的前端持久化缓存方案,那么我们为什么须求做持久化缓存呢?

用户选择浏览器第一遍访问大家的站点时,该页面引进了美妙绝伦的静态财富,假如大家能成就持久化缓存的话,能够在
http 响应头加上 Cache-control 或 Expires
字段来设置缓存,浏览器能够将那几个财富1一缓存到本地。

用户在此起彼伏访问的时候,如若须要重新请求一样的静态财富,且静态财富未有过期,那么浏览器能够直接走地面缓存而不用再经过互连网请求财富。

webpack 如何是好持久化缓存

上面简要介绍完持久化缓存,上边那些才是第一,那么咱们相应怎样在 webpack
中开展持久化缓存的吗,大家须要做到以下两点:

  1. 担保 hash 值的唯壹性,即为每一种打包后的财富转移叁个满世界无双的 hash
    值,只要打包内容不等同,那么 hash 值就不等同。
  2. 有限支撑 hash
    值的牢固,大家必要达成修改某些模块的时候,只有受影响的打包后文件
    hash 值更换,与该模块毫不相关的打包文件 hash 值不变。

hash 文件名是完结持久化缓存的率先步,方今 webpack 有三种总括 hash
的艺术([hash] 和 [chunkhash])

  1. hash 代表每回 webpack 在编写翻译的进度中会生成唯一的 hash
    值,在类型中任何叁个文本改造后就会被重复创建,然后 webpack 总结新的
    hash 值。
  2. chunkhash 是根据模块计算出来的 hash
    值,所以有些文件的更换只会潜移默化它自个儿的 hash 值,不会影响别的文件。

从而假诺您只是可是地将持有剧情打包成同二个文书,那么 hash
就能够满足你了,假使您的项目事关到拆包,分模块实行加载等等,那么你须求用
chunkhash,来保障每回换代之后唯有相关的文本 hash 值发生变动。

故而我们在一份具备持久化缓存的 webpack 配置相应长这么:

module.exports = {
 entry: __dirname + '/src/index.js',
 output: {
 path: __dirname + '/dist',
 filename: '[name].[chunkhash:8].js',
 }
}

地点代码的意义正是:以 index.js
为输入,将兼具的代码全体打包成一个文书取名字为 index.xxxx.js 并内置 dist
目录下,未来大家能够在历次换代项目标时候做到生成新命名的文件了。

只借使敷衍简单的景观,这样做就够了,可是在大型多页面使用中,大家往往须求对页面进行质量优化:

  1. 分手业务代码和第二方的代码:之所以将事情代码和第2方代码分离出来,是因为业务代码更新频率高,而第一方代码更新迭代速度慢,所以我们将第三方代码(库,框架)实行抽离,那样可以丰硕利用浏览器的缓存来加载第2方库。
  2. 按需加载:举个例子在采取 React-Router
    的时候,当用户须求拜访到有个别路由的时候再去加载对应的零件,那么用户并完全没有要求在一从头的时候就将有所的路由组件下载到本地。
  3. 在多页面使用中,大家反复能够将集人体模型块进行抽离,比方 header, footer
    等等,那样页面在展开跳转的时候那个集人体模型块因为存在于缓存里,就能够直接进行加载了,而不是再开始展览互联网请求了。

那么哪些进展拆包,分模块实行加载,那就要求 webpack
内置插件:CommonsChunkPlugin,上边小编将通过1个事例,来讲明 webpack
该怎么着进展布局。

正文的代码放在笔者的 Github 上,风乐趣的能够下载来看看:

git clone https://github.com/happylindz/blog.git
cd blog/code/multiple-page-webpack-demo
npm install

读书下边包车型地铁内容前边自个儿强烈提议你看下笔者事先的篇章:长远明白 webpack
文件打包机制,了解 webpack
文件的打包的体制推进你更加好地贯彻持久化缓存。

事例大约是那样讲述的:它由多少个页面组成 pageA 和 pageB

// src/pageA.js
import componentA from './common/componentA';
// 使用到 jquery 第三方库,需要抽离,避免业务打包文件过大
import $ from 'jquery';
// 加载 css 文件,一部分为公共样式,一部分为独有样式,需要抽离
import './css/common.css'
import './css/pageA.css';
console.log(componentA);
console.log($.trim(' do something '));

// src/pageB.js
// 页面 A 和 B 都用到了公共模块 componentA,需要抽离,避免重复加载
import componentA from './common/componentA';
import componentB from './common/componentB';
import './css/common.css'
import './css/pageB.css';
console.log(componentA);
console.log(componentB);
// 用到异步加载模块 asyncComponent,需要抽离,加载首屏速度
document.getElementById('xxxxx').addEventListener('click', () => {
 import( /* webpackChunkName: "async" */
 './common/asyncComponent.js').then((async) => {
  async();
 })
})
// 公共模块基本长这样
export default "component X";

下面的页面内容主导回顾关联到了大家拆分模块的三种格局:拆分公共库,按需加载和拆分公共模块。那么接下去要来配置
webpack:

const path = require('path');

const webpack = require('webpack');

const ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {

 entry: {

 pageA: [path.resolve(__dirname, './src/pageA.js')],

 pageB: path.resolve(__dirname, './src/pageB.js'),

 },

 output: {

 path: path.resolve(__dirname, './dist'),

 filename: 'js/[name].[chunkhash:8].js',

 chunkFilename: 'js/[name].[chunkhash:8].js'

 },

 module: {

 rules: [

  {

  // 用正则去匹配要用该 loader 转换的 CSS 文件

  test: /.css$/,

  use: ExtractTextPlugin.extract({

   fallback: "style-loader",

   use: ["css-loader"]

  }) 

  }

 ]

 },

 plugins: [

 new webpack.optimize.CommonsChunkPlugin({

  name: 'common',

  minChunks: 2,

 }),

 new webpack.optimize.CommonsChunkPlugin({

  name: 'vendor',

  minChunks: ({ resource }) => (

  resource && resource.indexOf('node_modules') >= 0 && resource.match(/.js$/)

  )
 }),
 new ExtractTextPlugin({
  filename: `css/[name].[chunkhash:8].css`,
 }),
 ]
}

首先个 CommonsChunkPlugin 用于抽离公共模块,约等于是说 webpack
大佬,借使您看到有些模块被加载三次即以上,那么请你帮自个儿移到 common chunk
里面,这里 minChunks 为
2,粒度拆解最细,你能够依照自身的其实际处情形,看选拔是用多少次模块才将它们抽离。

其次个 CommonsChunkPlugin
用来领取第三方代码,将它们实行抽离,剖断能源是不是来自
node_modules,即便是,则表明是第三方模块,那就将它们抽离。相当于是告诉
webpack 大佬,假设您瞧瞧某个模块是来源于 node_modules 目录的,并且名字是
.js 结尾的话,麻烦把他们都移到 vendor chunk 里去,假使 vendor chunk
不设有的话,就创办二个新的。

那样布置有啥利润,随着业务的滋长,我们借助的第二方库代码十分大概会越多,假如大家专门安插三个输入来存放在第一方代码,那时候大家的
webpack.config.js 就会形成:

// 不利于拓展
module.exports = {
 entry: {
 app: './src/main.js',
 vendor: [
  'vue',
  'axio',
  'vue-router',
  'vuex',
  // more
 ],
 },
}

 第多个 ExtractTextPlugin 插件用于将 css 从打包好的 js
文件中抽离,生成独立的 css
文件,想象一下,当你只是修改了下样式,并不曾更换页面的效果逻辑,你肯定不期望你的
js 文件 hash 值变化,你一定是期待 css 和 js 能够相互分开,且互不影响。

运作 webpack 后方可看来打包之后的效率:

├── css

│ ├── common.2beb7387.css

│ ├── pageA.d178426d.css

│ └── pageB.33931188.css

└── js

 ├── async.03f28faf.js

 ├── common.2beb7387.js

 ├── pageA.d178426d.js

 ├── pageB.33931188.js

 └── vendor.22a1d956.js

能够见到 css 和 js 已经分别,并且我们对模块举行了拆分,保险了模块 chunk
的唯1性,当你每回换代代码的时候,会扭转不均等的 hash 值。

唯1性有了,那么大家需求保障 hash
值的七台河久安,试想下那样的风貌,你一定不期望你改改某部分的代码(模块,css)导致了文本的
hash 值全变了,那么明显是不明智的,那么我们去完结 hash 值变化最小化呢?

换句话说,大家将在搜索 webpack
编写翻译中会导致缓存失效的因素,想方法去消除或优化它?

潜移默化 chunkhash 值变化注重由以下八个部分引起的:

  1. 包蕴模块的源代码
  2. webpack 用于运维运作的 runtime 代码
  3. webpack 生成的模块 moduleid(包蕴富含模块 id 和被引用的依赖模块 id)
  4. chunkID

那四部分只要有自由部分爆发变化,生成的分块文件就不均等了,缓存也就会失灵,上边就从多少个部分各个介绍:

1、源代码变化:

无人不晓不用多说,缓存供给求刷新,不然就有标题了

二、webpack 运行运作的 runtime 代码:

看过本人事先的小说:深远掌握 webpack 文件打包机制 就会明白,在 webpack
运营的时候需求实施一些起动代码。

(function(modules) {

 window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules) {

 // ...

 };

 function __webpack_require__(moduleId) {

 // ...

 }

 __webpack_require__.e = function requireEnsure(chunkId, callback) {

 // ...

 script.src = __webpack_require__.p + "" + chunkId + "." + ({"0":"pageA","1":"pageB","3":"vendor"}[chunkId]||chunkId) + "." + {"0":"e72ce7d4","1":"69f6bbe3","2":"9adbbaa0","3":"53fa02a7"}[chunkId] + ".js";

 };

})([]);

大概内容像上边那样,它们是 webpack
的局地起步代码,它们是有的函数,告诉浏览器怎么着加载 webpack 定义的模块。

内部有一行代码每趟换代都会变动的,因为运营代码需求理解地掌握 chunkid 和
chunkhash 值得对应提到,那样在异步加载的时候技术科学地拼接出异步 js
文件的路径。

那正是说那某个代码最终放在哪个文件呢?因为大家刚刚配置的时候最后生成的
common chunk
模块,那么这一部分运营时期码会被平昔内置在里面,那就导致了,大家每趟换代大家专业代码(pageA,
pageB, 模块)的时候, common chunkhash
会从来变化,但是那眼看不符合大家的设想,因为我们只是要用 common chunk
用来存放在公共模块(那里指的是 componentA),那么自身 componentA
都没去修改,凭啥 chunkhash 必要变了。

由此我们须求将那有个别 runtime 代码抽离成单身文件。

module.exports = {

 // ...

 plugins: [

 // ...

 // 放到其他的 CommonsChunkPlugin 后面

 new webpack.optimize.CommonsChunkPlugin({

  name: 'runtime',

  minChunks: Infinity,
 }),
 ]
}

这一定于是告诉 webpack 帮笔者把运维时期码抽离,放到单独的文书中。

├── css

│ ├── common.4cc08e4d.css

│ ├── pageA.d178426d.css

│ └── pageB.33931188.css

└── js

 ├── async.03f28faf.js

 ├── common.4cc08e4d.js

 ├── pageA.d178426d.js

 ├── pageB.33931188.js

 ├── runtime.8c79fdcd.js

 └── vendor.cef44292.js

多生成了3个 runtime.xxxx.js,未来你在改造业务代码的时候,common chunk
的 hash 值就不会变了,取代他的是 runtime chunk hash
值会变,既然那部分代码是动态的,能够经过 chunk-manifest-webpack-plugin
将她们 inline 到 html 中,收缩2回网络请求。

3、webpack 生成的模块 moduleid

在 webpack二 中默许加载 OccurrenceOrderPlugin
这么些插件,OccurrenceOrderPlugin
插件会按引进次数最多的模块举办排序,引进次数的模块的 moduleId
越小,不过那依然是不平稳的,随着你代码量的增多,就算代码引用次数的模块
moduleId 越小,越不便于变化,不过免不了依然不明确的。

暗许意况下,模块的 id 是其一模块在模块数组中的索引。OccurenceOrderPlugin
会将引用次数多的模块放在前边,在每便编写翻译时模块的逐1未有不一致的,如若你改改代码时新增添或删除了有些模块,那将或者会影响到持有模块的
id。

至上实施方案是透过 HashedModuleIdsPlugin
那几个插件,这一个插件会基于模块的周旋路线生成3个长度唯有几个人的字符串作为模块的
id,既隐藏了模块的路线新闻,又减弱了模块 id 的长短。

那样一来,改造 moduleId
的措施就唯有文件路线的改造了,只要您的文书路径值不变,生成3个人的字符串就不改变,hash
值也不改变。扩充或删除业务代码模块不会对 moduleid 发生任何影响。

module.exports = {

 plugins: [

 new webpack.HashedModuleIdsPlugin(),

 // 放在最前面

 // ...

 ]

}

四、chunkID

其实况形中分块的个数的次第在频仍编译之间基本上都以一定的,
不太轻巧发生变化。

此间提到的只是比较基础的模块拆分,还有一些此外处境并未有思考到,比如异步加载组件中含有公共模块,能够另行将公共模块进行抽离。产生异步公共
chunk 模块。有想深切学习的能够看这篇文章:Webpack 大法之 Code Splitting

webpack 做缓存的片段在意点

  1. CSS 文件 hash 值失效的主题材料
  2. 不提出线上发布使用 DllPlugin 插件

CSS 文件 hash 值失效的主题素材:

ExtractTextPlugin
有个比较严重的主题材料,那正是它生成文书名所用的[chunkhash]是一直取自于引用该
css 代码段的 js chunk ;换句话说,要是自个儿只是修改 css 代码段,而不动 js
代码,那么最后生成出来的 css 文件名如故未有变化。

于是大家要求将 ExtractTextPlugin 中的 chunkhash 改为
contenthash,顾名思义,contenthash 代表的是文本文件内容的 hash
值,也正是唯有 style 文件的 hash 值。那样编写翻译出来的 js 和 css
文件就有独立的 hash 值了。

module.exports = {

 plugins: [

 // ...

 new ExtractTextPlugin({

  filename: `css/[name].[contenthash:8].css`,

 }),

 ]

}

举例您使用的是 webpack二,webpack三,那么恭喜你,那样就足足了,js 文件和
css 文件修改都不会影响到相互的 hash 值。那假如您利用的是
webpack一,那么就会冒出难题。

具体来讲正是 webpack1 和 webpack 在测算 chunkhash 值得差别:

webpack壹 在关系的时候并不曾考虑像 ExtractTextPlugin
会将模块内容抽离的难题,所以它在测算 chunkhash
的时候是经过包装之前模块内容去总计的,也正是说在企图的时候 css
内容也带有在内,之后才将 css 内容抽离成单身的公文,

那么就会并发:假使只修改了 css 文件,未修改引用的 js
文件,那么编写翻译输出的 js 文件的 hash 值也会改动。

对此,webpack贰 做了校订,它是依据打包后文件内容来计算 hash
值的,所以是在 ExtractTextPlugin 抽离 css
代码之后,所以就不设有上述如此的难题。如若不幸的您还在选拔webpack一,那么推荐您利用 md5-hash-webpack-plugin 插件来改造 webpack 总结hash 的政策。

不提议线上颁发使用 DllPlugin 插件

缘何如此说吗?因为近年来有情侣来问小编,他们 leader 不让在线上用 DllPlugin
插件,来问笔者干吗?

DllPlugin 本人有多少个毛病:

  1. 先是你供给额外多配备一份 webpack 配置,扩大职业量。
  2. 中间一个页面用到了二个容量极大的第3方依赖库而任何页面根本不必要运用,但若直接将它包裹在
    dll.js 里很不值得,每一趟页面张开都要去加载那段无用的代码,不可能运用到
    webpack2 的 Code Splitting 作用。
  3. 先是次张开的时候需求下载 dll
    文件,因为你把许多库全体打在一块儿了,导致 dll
    文件非常大,第二遍进入页面加载速度一点也不快。

固然如此您能够打包成 dll
文件,然后让浏览器去读取缓存,那样下次就绝不再去伏乞,举例你用 lodash
当中三个函数,而你用dll会将壹切 lodash
文件打进去,那就会导致你加载无用代码过多,不便宜首屏渲染时间。

自家感到的不错的架势是:

  1. 像 React、Vue 那样全部性偏强的库,能够生成 vendor
    第三方库来去做缓存,因为你相似本领类别是永久的,3个站点里面多数都会用到统一技巧系统,所以生成
    vendor 库用于缓存。
  2. 像 antd、lodash 那种成效性组件库,能够经过 tree shaking
    来开始展览铲除,只保留有用的代码,千万不要直接打到 vendor
    第三方Curry,不然你将大气实行无用的代码。

结语

好了,感到小编又扯了过多,近年来在看 webpack
确实获得累累,希望我们能从小说中也能享有收获。其余推荐再一次推荐一下自家在此以前写的小说,能够越来越好地帮你驾驭文件缓存机制:深切驾驭webpack 文件打包机制

以上就是本文的全体内容,希望对大家的读书抱有支持,也愿意大家多多援救脚本之家。

您只怕感兴趣的篇章:

  • webpack学习笔记之优化缓存、合并、懒加载
  • webpack进阶——缓存与单身包装的用法
  • webpack独立包装和缓存管理详解

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图