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>来加载的

 

前言

不久前对1个相比老的集团项目做了三次优化,管理的显假使webpack打包文件容量过大的标题。

那里就写一下对此webpack打包优化的壹对经历。

重在分为以下多少个地点:

  1. 去掉开拓条件下的布局
  2. ExtractTextPlugin:提取样式到css文件
  3. webpack-bundle-analyzer:webpack打包文件容量和依赖关系的可视化
  4. CommonsChunkPlugin:提取通用模块文件
  5. 领到manifest:让提取的公共js的hash值不要改造
  6. 压缩js,css,图片
  7. react-router 四 以前的按需加载
  8. react-router 四 的按需加载

本篇博客用到的webpack插件怎么样安排都得以去查看自身写的那篇博客:

【Webpack的使用指南
0贰】Webpack的常用消除方案

这边就不细讲这么些配置了。

前言

近来在看 webpack
如何是好持久化缓存的内容,开采个中依旧有1对坑点的,正好有时光就将它们整理计算一下,读完本文你大致能够知道:

  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的每1项)里搜寻国有代码进行李包裹装。不设置此参数则暗中同意提取范围为全体的chunk
  3. filename: ‘[name]/bundle.js’
    :如何命名打包后生育的js文件,也是足以用上[name]、[hash]、[chunkhash]那一个变量的,
     例子正是’commons/commons/bundle.js’了 (最后生成文书的门道是依附webpack配置中的ouput.path和上边CommonsChunkPlugin的filename参数来拼的)
  4. minChunks: 四,
    : 公共代码的剖断标准:有些js模块被有些个chunk加载了才好不轻便公共代码

 

去掉开垦条件下的配置

举例webpack中的devtool改为false,不供给热加载那类只用于开垦条件的东西。

这么些不算是优化,而好不轻松错误了。

对于在支付条件下才有用的事物在包装到生育碰着时通通去掉。

持久化缓存

先是我们必要去解释一下,什么是持久化缓存,在后天内外端分离的施用大行其道的背景下,前端
html,css,js
往往是以1种静态资源文件的款式存在于服务器,通过接口来获取数据来显示动态内容。那就涉及到市肆怎么着去布置前端代码的标题,所以就关乎到一个立异配备的主题素材,是先布置页面,仍旧先安顿财富?

  1. 先安插页面,再布局财富:在两者铺排的年华距离内,即使有用户访问页面,就会在新的页面结构中加载旧的能源,并且把这一个旧版本财富作为新本子缓存起来,其结果就是:用户访问到贰个体制错乱的页面,除非手动去刷新,不然在能源缓存过期在此之前,页面会一直处在混乱的意况。
  2. 先安插能源,再配备页面:在配备时间距离内,有旧版本的能源本地缓存的用户访问网址,由于请求的页面是旧版本,能源引用未有变动,浏览器将直接采取本地缓存,那样属于常规景况,但尚未本地缓存或许缓存过期的用户在做客网址的时候,就会现出旧版本页面加载新本子财富的情形,导致页面实行错误。

就此我们要求壹种配备攻略来确定保证在立异我们线上的代码的时候,线上用户也能平滑地连接并且准确张开我们的网址。

引入先看这几个答复:大市廛里什么开拓和布局前端代码?

当你读完上边的回复,大约就会精通,未来比较成熟的持久化缓存方案正是在静态财富的名字背后加
hash 值,因为每趟修改文件生成的 hash
值不雷同,那样做的便宜在于增量式发布文件,幸免覆盖掉从前文件从而形成线上的用户访问失效。

因为1旦形成每一次宣布的静态财富(css, js,
img)的称呼都以无比的,那么自身就能够:

  • 本着 html 文件:不开启缓存,把 html
    放到本人的服务器上,关闭服务器的缓存,本人的服务器只提供 html
    文件和多少接口
  • 本着静态的 js,css,图片等公事:开启 cdn 和缓存,将静态能源上传到
    cdn
    服务商,我们得以对能源开启长时间缓存,因为各个财富的门道都是无可比拟的,所以不会招致财富被掩盖,有限支撑线上用户访问的安澜。
  • 历次公布更新的时候,先将静态能源(js, css, img) 传到 cdn
    服务上,然后再上传 html
    文件,那样既保证了老用户能或不能够健康访问,又能让新用户观察新的页面。

地点大概介绍了下主流的前端持久化缓存方案,那么咱们怎么供给做持久化缓存呢?

  1. 用户接纳浏览器第三次访问大家的站点时,该页面引进了五花8门的静态财富,假诺大家能不辱任务持久化缓存的话,能够在
    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,那么不用这几个变量,写死一个文本名也是足以的,但出于大家要做的是多页应用,必然存在多少个chunk(至少各个entry都对应1个chunk啦)

在此处配置的[name]相应的是chunk的name,在webpack配置中把各样entry的name都按index/index、index/login这样的样式来安装了,那么最终css的路线就会像这么:build/index/index/styles.css,跟chunk的js文件放一块了(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'],
        }),
      },
    ],
  },
}

包裹后生成文件如下:

金沙注册送58 1

webpack 如何做持久化缓存

上边简介完持久化缓存,上边那几个才是第二,那么大家相应什么在 webpack
中进行持久化缓存的呢,大家须要产生以下两点:

  1. 担保 hash 值的唯一性,即为每种打包后的能源转移二个无比的 hash
    值,只要打包内容分歧等,那么 hash 值就分裂等。
  2. 有限支撑 hash
    值的稳固,我们供给完结修改某些模块的时候,只有受影响的打包后文件
    hash 值改动,与该模块无关的打包文件 hash 值不变。

hash 文件名是落成持久化缓存的第3步,近日 webpack 有两种总结 hash
的办法([hash] 和 [chunkhash])

  • hash 代表每便 webpack 在编写翻译的长河中会生成唯1的 hash
    值,在档案的次序中其它一个文本改造后就会被再一次创制,然后 webpack 总结新的
    hash 值。
  • chunkhash 是基于模块总计出来的 hash
    值,所以有些文件的变动只会潜移默化它自身的 hash 值,不会影响别的文件。

因此只要您只是唯有地将享有内容打包成同二个文书,那么 hash
就可见满意你了,假诺你的品类涉及到拆包,分模块进行加载等等,那么您必要用
chunkhash,来确认保障每一回换代之后唯有连带的公文 hash 值产生转移。

故而大家在一份具备持久化缓存的 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
为进口,将享有的代码全部打包成1个文书取名称叫 index.xxxx.js 并置于 dist
目录下,现在大家得以在每一次换代项目标时候做到生成新命名的公文了。

假诺是假意周旋轻易的气象,那样做就够了,不过在巨型多页面使用中,大家壹再要求对页面进行品质优化:

  1. 分开张营业务代码和第一方的代码:之所以将业务代码和第三方代码分离出来,是因为作业代码更新频率高,而第2方代码更新迭代速度慢,所以大家将第三方代码(库,框架)举行抽离,那样能够充足利用浏览器的缓存来加载第二方库。
  2. 按需加载:例如在利用 React-Router
    的时候,当用户需求拜访到有个别路由的时候再去加载对应的机件,那么用户完全没须要在一发端的时候就将具备的路由组件下载到本地。
  3. 在多页面使用中,我们往往能够将公共模块举行抽离,举个例子 header, footer
    等等,这样页面在拓展跳转的时候那个公共模块因为存在于缓存里,就足以平素开始展览加载了,而不是再实行互连网请求了。

那么如何举行拆包,分模块进行加载,那就须要 webpack
内置插件:CommonsChunkPlugin,上边小编将经过一个例子,来疏解 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 第一方库,必要抽离,制止业务打包文件过大 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`,
    }),
  ]
}

先是个 CommonsChunkPlugin 用于抽离公共模块,相当于是说 webpack
大佬,就算您看到某些模块被加载三次即以上,那么请您帮作者移到 common chunk
里面,那里 minChunks 为
二,粒度拆解最细,你能够依赖自身的莫过于境况,看选用是用有个别次模块才将它们抽离。

其次个 CommonsChunkPlugin
用来领取第1方代码,将它们进行抽离,判别能源是还是不是来自
node_modules,假使是,则表明是第1方模块,这就将它们抽离。也便是是告诉
webpack 大佬,如果您瞧瞧某个模块是发源 node_modules 目录的,并且名字是
.js 结尾的话,麻烦把她们都移到 vendor chunk 里去,假如 vendor chunk
不存在的话,就成立三个新的。

诸如此类布署有哪些便宜,随着专门的学问的加强,我们依赖的第一方库代码很恐怕会越加多,如若我们特地配备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
的唯一性,当您每一趟换代代码的时候,会变卦分裂的 hash 值。

唯壹性有了,那么我们须要有限支撑 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 })
  ],
});

卷入后会自动出现3个端口为891玖的站点,站点内容如下:

金沙注册送58 2

能够看到我们打包后的main.js中的代码一部分源于node_modules文件夹中的模块,1部分源点自个儿写的代码,也正是src文件夹中的代码。

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

壹、源代码变化:

通晓不用多说,缓存必须要刷新,不然就不平日了

先是咱们需求去解释一下,什么是持久化缓存,在现行光景端分离的利用大行其道的背景下,前端
html,css,js
往往是以1种静态资源文件的样式存在于服务器,通过接口来获取数据来展现动态内容。那就涉及到铺子如何去布署前端代码的难题,所以就涉嫌到三个更新配备的标题,是先配备页面,照旧先计划财富?

CommonsChunkPlugin:提取通用模块文件

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

将这个js模块提抽取来放到贰个文书中,不仅能够减弱主文件的尺寸,在首先次下载的时候能并行下载,进步加载功能,更珍视的是那些文件的代码差不离不会变动,那么每趟打阎罗包老布后,依旧会沿用缓存,从而抓好了加载功能。

而对于那几个多文本输入的利用越发使得,因为在加载区别的页面时,那部分代码是国有的,直接能够从缓存中动用。

那么些事物不供给设置,直接改换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打包分析图为:

金沙注册送58 3

可以很显眼看到react那一个模块都被打包进了common.js中。

2、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 定义的模块。

中间有1行代码每一次换代都会更动的,因为运维代码供给领悟地知道 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 中,收缩一回网络请求。

先配备页面,再布置能源:在二者陈设的年华距离内,假设有用户访问页面,就会在新的页面结构中加载旧的财富,并且把那几个旧版本财富作为新本子缓存起来,其结果正是:用户访问到三个体裁错乱的页面,除非手动去刷新,不然在能源缓存过期事先,页面会一向处于混乱的境况。

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

当我们询问webpack中的hash值时,一般都会看到[hash]和[chunkhash]三种hash值的布局。

里面hash依据每一遍编写翻译的始末总计获得,所以每编写翻译一遍具备文件都会转换1个新的hash,也就全盘不可能选取缓存。

所以大家这里用了[chunkhash],chunkhash是依据剧情来扭转的,所以借使剧情不改动,那么生成的hash值就不会转移。

chunkhash适用于一般的意况,不过,对于我们上述的情景是不适用的。

自己去改换主文件代码,然后生成的四个公共js代码的chunkhash值却退换了,它们并从未应用到主文件。

于是本身用文件相比较工具,相比了它们的代码,发掘唯有1行代码是有出入的:

金沙注册送58 4

那是因为webpack在施行时会有3个包含模块标志的运维时期码。

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

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

当大家将库文件分为八个包提收取来,分别为common一.js和common贰.js,发现那段脚本只出现在3个common壹.js中,并且
那段标记代码形成了:

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

下一场发掘其它包的首部都会有个这么的代码:

webpackJsonp([1],{2:functio

其一运转时脚本的代码正好和别的包起来的那段代码中的数字绝对应。

咱俩得以将那一部分代码提取到三个单独的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注入到包中的那段公共代码提抽出来。

打包后的公文:

金沙注册送58 5

webpack打包分析图:

金沙注册送58 6

探望图洋蓟绿色的不行块了吗?

非凡东西就是包装后的manifest文件。

那样管理后,当我们再修改主文件中的代码时,生成的公家js的chunkhash是不会转移的,改动的是那么些单独提收取来的manifest.bundle.[chunkhash].js的chunkhash。

叁、webpack 生成的模块 moduleid

在 webpack二 中默许加载 OccurrenceOrderPlugin
那么些插件,OccurrenceOrderPlugin
插件会按引进次数最多的模块举办排序,引进次数的模块的 moduleId
越小,不过那依旧是不安静的,随着你代码量的扩充,就算代码引用次数的模块
moduleId 越小,越不便于生成,不过免不了如故不明确的。

暗中认可意况下,模块的 id
是其壹模块在模块数组中的索引。OccurenceOrderPlugin
会将引用次数多的模块放在日前,在每便编写翻译时模块的各样都以均等的,要是您改改代码时新添或删除了一些模块,那将只怕会潜移默化到全体模块的
id。

至上试行方案是经过 HashedModuleIdsPlugin
那些插件,这一个插件会依靠模块的相对路线生成八个长短唯有几人的字符串作为模块的
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

之所以大家须要一种配备计谋来保障在立异我们线上的代码的时候,线上用户也能平滑地联网并且准确展开大家的网址。

react-router 四 以前的按需加载

只要利用过Ant Design
一般都掌握有一个安排按需加载的效能,正是在终极打包的时候只把用到的机件代码打包。

而对此一般的react组件其实也有1个用到react-router达成按需加载的玩的方法。

对于每多少个路由来讲,别的路由的代码实际上并不是必须的,所以当切换成某2个路由后,如若只加载那几个路由的代码,那么首屏加载的速度将大大升高。

首先在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,它是3个用于动态加载组件的高阶组件。
这是官方网站络的一个例子

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函数会传来三个参数对象,再次回到3个渲染到分界面上的机件。
其一参数对象的loader属性正是内需动态加载的组件,而loading那一个脾性传入的是三个显得加载状态的机件,当还没有加载出动态组件时,体未来分界面上的正是以此loading组件。

使用那种情势相对于原来的方法优势很明显,大家不仅是在路由上得以拓展动态加载了,大家动态加载的零件粒度能够更加细,比方3个机械钟组件,而不是像在此之前那么往往是多个页面。

通过灵活去行使动态加载能够圆满调节加载的js的尺寸,从而使首屏加载时间和任何页面加载时间决定到一个相对平衡的度。

此处有个点供给注意,就是普通大家在行使loading组件时平时会合世的标题:闪烁现象。

那种景况的缘故是,在加载真正的组件前,会出现loading页面,不过组件加载高效,就会造成loading页面出现的命宫非常的短,从而导致闪烁。

涸泽而渔的法子就是加个属性delay

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

唯有当加载时间跨越200ms时loading组件才会产出。

还有愈多的有关react-loadable的玩的方法:

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

金沙注册送58 7

webpack打包分析图:

金沙注册送58 8

只顾看看上边的打包文件名字,发掘经过那种措施开始展览按需加载的多少个文件都以遵守数字命名,而尚未服从大家目的在于的零部件名命名。

自个儿在那个类型的github上面找了1晃,开采它提供的按组件命名的措施供给使用服务端渲染,然后就未有继续下去了。

反正那么些事物也不是很珍视,所以就不曾进一步商讨,要是有园友对这几个难点有好的章程,也希望能在评价里证实。

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二,webpack3,那么恭喜你,那样就够用了,js 文件和
css 文件修改都不会潜移默化到互相的 hash 值。那要是你使用的是
webpack一,那么就会冒出难点。

具体来讲就是 webpack1 和 webpack 在总括 chunkhash 值得不相同:

webpack一 在论及的时候并未设想像 ExtractTextPlugin
会将模块内容抽离的标题,所以它在企图 chunkhash
的时候是透过包装在此以前模块内容去总括的,也正是说在计算的时候 css
内容也包含在内,之后才将 css 内容抽离成独立的文书,

那便是说就会现出:假设只修改了 css 文件,未修改引用的 js
文件,那么编译输出的 js 文件的 hash 值也会退换。

对此,webpack2 做了考订,它是依据打包后文件内容来计量 hash
值的,所以是在 ExtractTextPlugin 抽离 css
代码之后,所以就不存在上述如此的主题素材。假如不幸的您还在动用
webpack一,那么推荐您采用 md5-hash-webpack-plugin 插件来改动 webpack 计算hash 的政策。

当您读完下面的对答,大概就会清楚,未来可比早熟的持久化缓存方案正是在静态财富的名字背后加
hash 值,因为老是修改文件生成的 hash
值差异,这样做的益处在于增量式发表文件,幸免覆盖掉此前文件从而变成线上的用户访问失效。

总结

总的来讲,通过上述步骤应该是能够缓慢解决大多数封装文件体量过大的主题素材。

自然,因为文中webpack版本和插件版本的差距,在配置和游戏的方法上会有局地比不上,不过上边描述的这么些方向都以绝非难点的,并且信任在逐一版本下都足以找到相应的缓和方案。

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

不提议线上宣布使用 DllPlugin 插件

为什么那样说啊?因为近期有意中人来问笔者,他们 leader 不让在线上用 DllPlugin
插件,来问我干什么?

DllPlugin 自身有多少个缺陷:

  1. 先是你须要额外多安顿一份 webpack 配置,扩展工作量。
  2. 内部3个页面用到了二个体量十分的大的第叁方正视库而其余页面根本不须求使用,但若一贯将它包裹在
    dll.js 里很不值得,每便页面展开都要去加载那段无用的代码,不能利用到
    webpack贰 的 Code Splitting 功用。
  3. 率先次张开的时候必要下载 dll
    文件,因为你把繁多库全体打在协同了,导致 dll
    文件不小,第3遍跻身页面加载速度比很慢。

虽说您能够打包成 dll
文件,然后让浏览器去读取缓存,那样下次就不要再去央浼,举例你用 lodash
个中1个函数,而你用dll会将全体 lodash
文件打进去,那就会促成您加载无用代码过多,不便于首屏渲染时间。

我以为的准确的姿态是:

  1. 像 React、Vue 那样全体性偏强的库,能够生成 vendor
    第二方库来去做缓存,因为您相似本事系列是固定的,一个站点里面多数都会用到联合技巧体系,所以生成
    vendor 库用于缓存。
  2. 像 antd、lodash 那种作用性组件库,能够通过 tree shaking
    来张开清除,只保留有用的代码,千万不要一直打到 vendor
    第1方Curry,不然你将大气施行无用的代码。

因为借使达成每趟公布的静态能源(css, js,
img)的名称都以全世界无双的,那么笔者就足以:

结语

好了,感到自个儿又扯了成都百货上千,近来在看 webpack
确实赢得良多,希望我们能从小说中也能具备收获。其余推荐再一次推荐一下自身事先写的稿子,能够越来越好地帮您驾驭文件缓存机制:长远明白webpack 文件打包机制

  1. 本着 html 文件:不开启缓存,把 html
    放到本人的服务器上,关闭服务器的缓存,本人的服务器只提供 html
    文件和数量接口
  2. 本着静态的 js,css,图片等公事:开启 cdn 和缓存,将静态能源上传到
    cdn
    服务商,我们得以对能源开启长期缓存,因为各样能源的门径都是独一无2的,所以不会产生能源被覆盖,保障线上用户访问的平静。
  3. 老是公布更新的时候,先将静态财富(js, css, img) 传到 cdn
    服务上,然后再上传 html
    文件,那样既保障了老用户能无法平时访问,又能让新用户看到新的页面。

参照链接:

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

    2 赞 2 收藏
    评论

金沙注册送58 9

地点差不离介绍了下主流的前端持久化缓存方案,那么大家为何要求做持久化缓存呢?

用户使用浏览器第二回访问大家的站点时,该页面引进了丰富多彩的静态能源,假设大家能不辱职责持久化缓存的话,能够在
http 响应头加上 Cache-control 或 Expires
字段来设置缓存,浏览器能够将这几个能源壹一缓存到地头。

用户在持续访问的时候,倘若急需重新伸手同样的静态财富,且静态财富未有过期,那么浏览器能够一贯走地面缓存而不用再经过互连网请求财富。

webpack 咋办持久化缓存

上面简介完持久化缓存,上边这些才是珍视,那么大家应该怎么样在 webpack
中开始展览持久化缓存的啊,大家要求做到以下两点:

  1. 保障 hash 值的唯壹性,即为各类打包后的财富转移1个独一无贰的 hash
    值,只要打包内容不平等,那么 hash 值就不均等。
  2. 确定保障 hash
    值的安静,我们须求做到修改有些模块的时候,唯有受影响的打包后文件
    hash 值改换,与该模块非亲非故的打包文件 hash 值不改变。

hash 文件名是贯彻持久化缓存的率先步,方今 webpack 有二种计算 hash
的方法([hash] 和 [chunkhash])

  1. hash 代表每便 webpack 在编写翻译的历程中会生成唯一的 hash
    值,在档案的次序中任何3个文本改变后就会被再次创制,然后 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. 按需加载:比方在采纳 React-Router
    的时候,当用户必要拜访到有个别路由的时候再去加载对应的机件,那么用户并大可不必在一同先的时候就将富有的路由组件下载到本地。
  3. 在多页面使用中,大家往往能够将国有模块进行抽离,例如 header, footer
    等等,那样页面在拓展跳转的时候那些集人体模型块因为存在于缓存里,就能够直接开始展览加载了,而不是再进行网络请求了。

那么哪些进展拆包,分模块进行加载,那就须求 webpack
内置插件:CommonsChunkPlugin,上面小编将经过一个例子,来讲授 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
不设有的话,就成立二个新的。

那般陈设有如何便宜,随着专门的学业的增高,大家借助的第一方库代码很恐怕会愈多,借使我们特地配备3个输入来存放第贰方代码,那时候我们的
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
的唯壹性,当你每一趟换代代码的时候,会调换不相同的 hash 值。

唯壹性有了,那么我们须求确定保证 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 须求变了。

金沙注册送58 ,故此大家需求将那壹部分 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

多生成了1个 runtime.xxxx.js,今后你在退换业务代码的时候,common chunk
的 hash 值就不会变了,替代它的是 runtime chunk hash
值会变,既然那部分代码是动态的,能够通过 chunk-manifest-webpack-plugin
将她们 inline 到 html 中,收缩一遍互连网请求。

3、webpack 生成的模块 moduleid

在 webpack2 中暗许加载 OccurrenceOrderPlugin
那些插件,OccurrenceOrderPlugin
插件会按引入次数最多的模块进行排序,引进次数的模块的 moduleId
越小,可是那如故是不安宁的,随着你代码量的加码,固然代码引用次数的模块
moduleId 越小,越不易于变化,可是免不了依然不分明的。

暗许情状下,模块的 id 是其1模块在模块数组中的索引。OccurenceOrderPlugin
会将引用次数多的模块放在前方,在历次编写翻译时模块的次第都以如出壹辙的,如果你改改代码时新扩充或删除了部分模块,那将大概会影响到具备模块的
id。

超级施行方案是经过 HashedModuleIdsPlugin
这一个插件,那一个插件会依附模块的相持路线生成一个长短只有二位的字符串作为模块的
id,既隐藏了模块的不二等秘书技音讯,又缩短了模块 id 的长短。

那样一来,改动 moduleId
的主意就唯有文件路线的退换了,只要你的文件路线值不改变,生成四个人的字符串就不改变,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`,

 }),

 ]

}

1经您使用的是 webpack2,webpack3,那么恭喜你,那样就够用了,js 文件和
css 文件修改都不会潜移默化到相互的 hash 值。这假诺你使用的是
webpack一,那么就会冒出问题。

具体来讲正是 webpack一 和 webpack 在测算 chunkhash 值得分裂:

webpack一 在关乎的时候并未思索像 ExtractTextPlugin
会将模块内容抽离的标题,所以它在图谋 chunkhash
的时候是因此包装此前模块内容去总括的,也正是说在企图的时候 css
内容也富含在内,之后才将 css 内容抽离成独立的文本,

那正是说就会油不过生:假如只修改了 css 文件,未修改引用的 js
文件,那么编写翻译输出的 js 文件的 hash 值也会变动。

对此,webpack二 做了革新,它是依靠打包后文件内容来测算 hash
值的,所以是在 ExtractTextPlugin 抽离 css
代码之后,所以就不存在上述如此的标题。假使不幸的您还在运用
webpack一,那么推荐你采纳 md五-hash-webpack-plugin 插件来退换 webpack 总结hash 的国策。

不提出线上公布使用 DllPlugin 插件

缘何如此说呢?因为目前有意中人来问作者,他们 leader 不让在线上用 DllPlugin
插件,来问小编怎么?

DllPlugin 本身有多少个毛病:

  1. 率先你须要相当多配备壹份 webpack 配置,增加工作量。
  2. 在那之中四个页面用到了叁个体积相当的大的第三方信赖库而其余页面根本不必要运用,但若一贯将它包裹在
    dll.js 里很不值得,每一趟页面张开都要去加载那段无用的代码,不可能使用到
    webpack2 的 Code Splitting 功效。
  3. 先是次张开的时候须求下载 dll
    文件,因为你把无数库整体打在壹块儿了,导致 dll
    文件一点都不小,第一遍进入页面加载速度异常的慢。

即便你能够打包成 dll
文件,然后让浏览器去读取缓存,那样下次就不用再去央浼,比如您用 lodash
当中二个函数,而你用dll会将总体 lodash
文件打进去,那就会招致您加载无用代码过多,不方便人民群众首屏渲染时间。

自己感到的不易的架子是:

  1. 像 React、Vue 那样全体性偏强的库,能够生成 vendor
    第一方库来去做缓存,因为你相似技术种类是恒久的,2个站点里面许多都会用到统一技能种类,所以生成
    vendor 库用于缓存。
  2. 像 antd、lodash 那种功效性组件库,可以经过 tree shaking
    来拓展割除,只保留有用的代码,千万不要一直打到 vendor
    第壹方库里,不然你将大量举行无用的代码。

结语

好了,感到小编又扯了繁多,近日在看 webpack
确实赚取广大,希望大家能从文章中也能有所收获。其它推荐再一次推荐一下本人前边写的小说,能够越来越好地帮你领会文件缓存机制:深远驾驭webpack 文件打包机制

以上就是本文的全体内容,希望对我们的求学抱有帮助,也目的在于我们多多协理脚本之家。

你恐怕感兴趣的篇章:

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

相关文章

网站地图xml地图