背景
在上一篇文章,介绍了利用webpack怎么建立长期有效的缓存机制,这篇文章将在此基础上,对vue代码进行分割的操作,这个是我们使用vue架构大型项目的时候,需要考虑的点。
一般使用vue开发项目的时候,我们把vue,vue-router,vuex,axios 等基础库代码打包成一个vendor.js文件,把业务代码打包成一个main.js类似名字的代码。库代码基本在较长时间内会一直保存不变,这样我们能够对这类代码进行长期缓存,减少带宽压力,减少http请求次数,最多增加304请求问题。
但是,随着项目越来越大,业务代码随之变大,如我在vipkid 参与的学习中心的业务代码已达到1.2MB,虽然服务器对代码进行了gzip压缩,在网络上的传输的体积也有将近300KB,当用户的网络环境比较差的时候,首屏加载变慢,有些情况下进入界面需要4s-5s 这对于用户来说,是不可忍受。在此情况下,我们需要降低首屏加载时间,希望能控制的1s-1.5s之内。
解决方案
对于上述问题,需要解决的问题是降低首屏加载时间,其实根本就是减少首屏代码体积。目前有两种解决方案,一种是服务端渲染 ,另一种是利用vue的异步组件,和webpack的代码分割功能结合,进行代码的懒加载。两种的侧重点不同,但是都能够减少首屏加载时间。
第一种采用服务端渲染,也能进行代码分割,主要针对是,1.解决搜索引擎的SEO问题,2.首屏减少了了API请求时间,能够使用户更快的看到想要看得内容。
第二种解决问题的核心就是异步加载代码。
在webpack 我们需要对chunk type的概览进行了解,上一篇文章,我提到了chunk type的概念,并详细进了说明和解读。在这篇文章我不进行说明了。webpack一共有三种chunk ,分别是Entry Chunk,Normal Chunk,Intial Chunk 。我们解决的问题就是要把首页用不到业务代码,也就是首页不需要的组件,在首页不加载,只有需要它的时候,才请求加载出来。 这样一看,思路估计就出来了,那什么时候,我们需要其他组件的代码,其实就是当我从A路由,转B路由的时候,可能会对应不同的组件,那么在切换路由的,会触发对应的事件,这个时候,建立一个script标签,请求需要的组件代码,然后执行该代码,(类似jsonp)原理大致就是这样。
Vue代码分割
vue代码分割,我们一般以路由为界限进行分割,我们结合Vue的异步组件和Webpack的代码分割功能。在webpack中(webpack2+支持)使用动态import语法来定义代码分割点。
// 这种写法会按照moudle的编号进行对分割的js命名,如0.js,或1.js
const Foo = () => import('./Test.vue');
代码demo地址为https://gitee.com/lmz2423/code-split
webpack配置如下
const path = require('path'); const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry:{ main:'./src/main.js', vendor:[ 'vue', 'vue-router' ] }, module:{ loaders:[ { test: /\.vue$/, loader:'vue-loader', options:{ extractCSS:false, esModule:false } },{ test:/\.js$/, loader:'babel-loader', exclude:/node_modules/ },{ test:/\.css$/, loader:'css-loader' }] }, plugins:[ newHtmlWebpackPlugin({ template:'./src/index.html', filename:'index.html' }), newwebpack.HashedModuleIdsPlugin(), newwebpack.optimize.CommonsChunkPlugin({ name:'vendor', }), newwebpack.optimize.CommonsChunkPlugin({ name:'manifest', chunks:['vendor'] }) ], output:{ path:path.resolve(__dirname,'dist'), filename:'[name]-[chunkhash].js', chunkFilename:'[name]-[chunkhash].bundle.js', publicPath:'/' } }
当我们没有进行代码分割之前
路由配置如下:
import a from '../componets/a.vue'; import b from '../componets/b.vue'; import c from '../componets/c.vue'; export default[ { path:'/', component:a }, { path:'/b', component:b }, { path:'/c', component:c } ]
首页的视图如下:
webpack打包的文件如下
这个时候我们没有对业务代码main进行了拆分,而是打包一个文件,现在我打算把代码按照路由组件进行分割。
//import a from '../componets/a.vue'; const a = ()=> import('../componets/c.vue'); //import b from '../componets/b.vue'; const b = ()=> import('../componets/b.vue'); //import c from '../componets/c.vue'; const c = ()=> import('../componets/c.vue'); export default[ { path:'/', component:a }, { path:'/b', component:b }, { path:'/c', component:c }]
打包文件如下
当我们点击按钮b时和按钮c时,对应的js才开始加载,然后缓存起来
我们看一下<head>添加的东西
其实就是类似jsonp的形式添加了回调函数。但是我们分割的代码是[id]-[chunkname].js的形式,如果我们想自己命名呢。可以利用chunkname来进行
import a from '../componets/a.vue'; //import b from '../componets/b.vue'; // const b = ()=> import('../componets/b.vue'); const b = ()=> import(/*webpackChunkName:'b' */'../componets/b.vue'); //import c from '../componets/c.vue'; // const c = ()=> import('../componets/c.vue'); const c = ()=> import(/*webpackChunkName:'c'*/'../componets/c.vue'); export default[ { path:'/', component:a }, { path:'/b', component:b }, { path:'/c', component:c }]
打包之后
上述就是以路由为分割点的懒加载。详细代码见https://gitee.com/lmz2423/code-split
参考文档
https://router.vuejs.org/zh-cn/advanced/lazy-loading.html
发表评论