跟着Vue-cli来'学'并'改'Webpack之 打包优化

首先,咱们要知道什么要用webpack来打包,这样打包有那些好处。咱们能够简单的列出如下几点:javascript

  • 单文件组件 (.vue文件)
  • 优化Vue构建过程 (alias等)
  • 浏览器缓存管理
  • 代码分离 (懒加载等)

这篇文章的重点讲的就是webpack打包之优化浏览器缓存管理,vue-cli生成的脚手架的配置中,已经作了不少对于打包,利用缓存的优化处理,本文未来学校其中知识,而且作出改动。css

了解浏览器的缓存原理

在此以前呢,咱们须要先了解浏览器缓存是怎么工做的,先抄了一张图。html

  • 浏览器: 我须要 test.js
  • 服务器:找到了给你,而且在259200秒(一个月)内别来找我
  • 浏览器:好的,那我缓存到磁盘里

过了一个星期,再次访问这个页面vue

  • 浏览器:我须要test.js,缓存期限还在,直接从磁盘读取
  • 服务器:没我卵事
  • 用户:哇塞打开页面好快

应产品经理需求更改了一个图标java

  • 浏览器:我须要test.js,缓存期限还在,直接从磁盘读取
  • 产品经理:发布了吗?你肯定?怎么没效果啊?
  • 服务器:吃瓜

弄清了原理,咱们就知道怎么去破坏缓存机制,让浏览器请求到新的文件。node

清楚缓存技术

ctrl+F5 强制刷新页面

手动强制刷新页面,可是用户不是程序员啊,他们怎么会知道须要强制刷新呢,因此这个方案给用户确定是不可行的。webpack

更改文件

  1. 修改文件的名字:test.js -> test.v2.js
  2. 修改文件的路径:/static/test.js -> /static/v2/test.js
  3. 加 query string : test.js -> test.js?v=qwer

咱们了解完了如何清楚缓存,再来看看Vue-cli模版中是如何进行配置的ios

Code Splitting(代码分割)

什么是代码分割

咱们直接生成一个Vue-cli的新项目,安装依赖后直接运行 npm run build命令,并打开/dist/js文件目录程序员

发现有3个js文件,这就是webpack将代码进行了分割。web

为何要进行代码分割

  1. 分离业务代码和第三方库( vendor )
  2. 按需加载(利用 import() 语法)

之因此把业务代码和第三方库代码分离出来,是由于产品经理的需求是源源不断的,所以业务代码更新频率大,相反第三方库代码更新迭代相对较慢且能够锁版本,因此能够充分利用浏览器的缓存来加载这些第三方库。

而按需加载的适用场景,好比说「访问某个路由的时候再去加载对应的组件」,用户不必定会访问全部的路由,因此不必把全部路由对应的组件都先在开始的加载完;更典型的例子是「某些用户他们的权限只能访问某些页面」,因此不必把他们没权限访问的页面的代码也加载

剖析 vue-cli 的 webpack Code Splitting

分离业务代码和第三方库( vendor )

vue-cli中使用了 CommonsChunkPlugin这个webpack插件来提取框架代码。 打开 webpack.prod.conf.js 文件,找到下面这段代码

new webpack.optimize.CommonsChunkPlugin({
  name: 'vendor',
  minChunks: function (module, count) {
    // any required modules inside node_modules are extracted to vendor
    return (
      module.resource &&
      /\.js$/.test(module.resource) &&
      module.resource.indexOf(
        path.join(__dirname, '../node_modules')
      ) === 0
    )
  }
}),
复制代码

这段代码在打包的时候把 node_modules 下面的 ,而且名字是 .js 结尾的,而且不是重复的模块提取到vender里面。

因此打包后应该会生成app.js(业务代码)、vender.js(框架代码)这个两个文件,细心的同窗可能会发现还有个 manifest.js,在后面咱们讲进行解读。

按需加载(利用 import() 语法)

若是咱们修改一下hello组件的加载方式改成路由懒加载(import()语法),在进行打包

// import Hello from '@/components/Hello'

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Hello',
      // component: Hello,
      component: () => import('@/components/Hello')
    }
  ]
})
复制代码

很明显的看到,打包后有4个js文件,仔细的同窗还发现,app.js文件的大小加上新多出文件的大小,正好等于没有分割打包的app的大小。 这样等于异步加载的组件,是单独打包成了一个js,在页面首次加载的时候不须要加载他,等到请求相应的页面的时候在去服务器请求它,减少了页面首屏加载的时间。

vue-cli /webpack.prod.conf.js 中配置 output.chunkFilename 规定了打包异步文件的格式

output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 
  },
复制代码

如何利用利用浏览器缓存

目标

假设咱们如今有不少不少的静态文件,而后每次须要更新不少不少的文件,那是否是要手动地一个一个地修改文件的名字呢?咱们的理想固然是:哪一个文件更新了,就自动地生成一个新的文件名。

另外,若是咱们打包出来的静态文件只有一个单独的 JavaScript 文件 app.js ,那么每次改动一点代码,app.js 的文件名确定都会变。但实际上,我只改动了某个模块的代码(其余模块并无修改),就破坏了其余模块的缓存,这显然没有充分利用到缓存啊。咱们的目标是:

哪一个模块更新了破坏他的缓存,没更新的模块继续利用缓存。

步骤1:增长hash值

上文中咱们提到清楚缓存的三种方式:修改文件名,修改路径,给url加参数,webpack的作法是修改文件名。 vue-cli /webpack.prod.conf.js 中 output.chunkFilename 规定了打包异步文件的格式

output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),   
    // 规定文件名为 js文件夹下 Chunk.name . hash值 .js 的文件
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
    // 规定文件名为 js文件夹下 module id. hash值 .js 的文件
  },
复制代码

这样的话给每一个文件加上了hash值,那个文件发生了变化hash值就会改变

步骤2:提取manifast文件

为何要提取manifast文件呢?

缘由是 vendor chunk 里面包含了 webpack 的 runtime 代码(用来解析和加载模块之类的运行时代码)

这样会致使:即便你没有更改引入模块(vendor的模块没有发生变更的状况下,你仅仅修改了其余代码) 也会致使 vendor 的chunkhash值发生变化,从而破坏了缓存,达不到预期效果

vue-cli /webpack.prod.conf.js 提取 manifast

new webpack.optimize.CommonsChunkPlugin({
      name: 'manifest',
      minChunks: Infinity
    }),
复制代码

步骤3:根据模块的相对路径生成一个四位数的hash做为模块id

webpack 里每一个模块都有一个 module id ,module id 是该模块在模块依赖关系图里按顺序分配的序号,若是这个 module id 发生了变化,那么他的 chunkhash 也会发生变化。

这样会致使:若是你引入一个新的模块,会致使 module id 总体发生改变,可能会致使全部文件的chunkhash发生变化,这显然不是咱们想要的

这里须要用 HashedModuleIdsPlugin ,根据模块的相对路径生成一个四位数的hash做为模块id,这样就算引入了新的模块,也不会影响 module id 的值,只要模块的路径不改变的话。

vue-cli /webpack.prod.conf.js

new webpack.HashedModuleIdsPlugin()
复制代码

完成目标

至此若是咱们改了某个模块的代码,是不会破坏其余模块的缓存,这就是咱们想要实现的持久性缓存。

改造vue-cli中的webpack提高首页加载速度

分析

咱们首先来看一个实际项目

运行 npm run build --report 能够查看打包分布图 咱们发现最大的文件仍是vendor,大部分框架代码都打包在这里面,而这些框架代码是不常变化的,也不须要每次进行打包。因此咱们能够想办法把他们提取出来,挂到cdn上面去。

具体步骤

以 vue, vue-router,element-ui为例

步骤1 index.html cdn引入框架

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>demo-vue-project</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/element-ui/2.0.8/theme-chalk/index.css">
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
    <script src="https://cdn.bootcss.com/vue/2.5.13/vue.min.js"></script>
    <script src="https://cdn.bootcss.com/vue-router/2.7.0/vue-router.min.js"></script>
    <script src="https://cdn.bootcss.com/element-ui/2.0.7/index.js"></script>
  </body>
</html>
复制代码

步骤2 修改 build/webpack.base.conf.js

module.exports = {
  ...
  externals: {
    'vue': 'Vue',
    'vue-router': 'VueRouter',
    'element-ui': 'ELEMENT'
  },
  ...
}
复制代码

步骤3 修改框架注册方式

修改 src/router/index.js

// import Vue from 'vue'
import VueRouter from 'vue-router'
// 注释掉
// Vue.use(VueRouter)
...
复制代码

修改 src/main.js

import Vue from 'vue'
import App from './App'
import router from './router'
import ELEMENT from 'element-ui'
// import 'element-ui/lib/theme-chalk/index.css'

Vue.config.productionTip = false

Vue.use(ELEMENT)
Vue.prototype.$http = axios

/* eslint-disable no-new */
new Vue({
  el: '#app',
  store,
  template: '<App/>',
  components: { App }
})
复制代码

打包

打包后咱们发现vendor体积大大减少,由于库代码都用cdn加载了。可是这样会致使请求资源增多也有响应的代价,这仅可算是一个思路。 首屏问题的最终解决方案仍是SSR

总结

至此 vue-cli中的打包配置,也有一些了解了。我的吐槽下webpack是真的复杂。观望和期待 parcel能来带不同的体验。

相关文章
相关标签/搜索