执行npm run build,将打包代码部署上线后访问项目,会发现表现很糟糕,页面会出现长时间的空白等待,这是无法忍受的性能问题,迫切需要解决。
原来的路由引入组件
import Index from @/views/index.vue;
{
path: '/'
name: 'index'
component: Index
}
现在引入路由(vue异步组件)
import Vue from 'vue';
import Router from 'vue-router';
Vue.use(Router)
{
path: "/",
name: "index",
component: resolve => require(["@/views/Index"], resolve)
}
ES 提出的import方法(常用)
const HelloWorld = ()=>import(‘需要加载的模块地址’)
import Vue from 'vue';
import Router from 'vue-router';
Vue.use(Router)
const HelloWorld = ()=>import("@/components/HelloWorld")
export default new Router({
routes: [
{
path: '/',
name: 'HelloWorld',
component:HelloWorld
}
]
})
原来组件中的写法:
<template>
<div>
<span></span>
<One-com></One-com>
</div>
</template>
<script>
import One from './one';
export default{
components:{
"One-com": One,
},
data(){
return{
num: 0
}
}
}
</script>
const 方法
<template>
<div class="hello">
<One-com></One-com>
1111
</div>
</template>
<script>
const One = ()=>import("./one");
export default {
components:{
"One-com":One
},
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
}
}
</script>
异步方法:
<template>
<div class="hello">
<One-com></One-com>
1111
</div>
</template>
<script>
export default {
components:{
"One-com":resolve=>(['./one'],resolve)
},
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
}
}
</script>
第1步 先在vue-config.js中配置开启gzip。
// 安装插件
npm i --save-dev compression-webpack-plugin
// 在vue-config.js 中加入
const CompressionWebpackPlugin = require('compression-webpack-plugin');
const productionGzipExtensions = ['js', 'css'];
const isProduction = process.env.NODE_ENV === 'production';
.....
module.exports = {
....
configureWebpack: config => {
if (isProduction) {
config.plugins.push(new CompressionWebpackPlugin({
algorithm: 'gzip',
test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
threshold: 10240,
minRatio: 0.8
}))
}
}
}
第2步 WEB服务器开启gzip。这里以我的nginx为例。
gzip on;
gzip_types text/plain application/x-javascript application/javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
用Gzip已把文件的大小减少了三分之二了。如果还不能满足,把那些不太可能改动的代码或者库分离出来,继续减小单个chunk-vendors,然后通过CDN进行加速加载资源。
(对vue、vuex、vue-router、vue-moment采用cdn引入,此外客户端还可以引入highlight的cdn,但管理端不需要。因此,需要两份不同的模板html,配置分别如下:)
(1)、vue.config.js 在上一步的基础上继续加入
config.externals的配置,如下:
configureWebpack: config => {
if (isProduction) {
config.plugins.push(
new CompressionWebpackPlugin({
algorithm: "gzip",
test: new RegExp("\\.(" + productionGzipExtensions.join("|") + ")$"),
threshold: 10240,
minRatio: 0.8
})
);
config.externals = {
vue: "Vue",
"vue-router": "VueRouter"
};
}
}
(2)、在原来所有引入vue、vue-router的地方,全部屏蔽或删除。
//原来对vue、vue-router的引用全部注释掉。
//涉及所有引用的文件,只要有引入就注释掉。
// import Vue from "vue";
// import Router from "vue-router";
//vue-router的非cdn引用
//Vue.use(Router);
(3)、在代码中用vue-router的时候,用VueRouter。
//cdn 引入vue-router时,源码抛出的变量是:VueRouter
console.log(VueRouter);
Vue.use(VueRouter);
//Vue.use(Router); 继续像原来一样用Router就会报错。
以上方案解决的问题是缩小js资源体积以便加快加载速度,这样的方案在网络良好的情况下首屏渲染的速度已经够快了,但是终归讲,渲染依赖于js加载完成后的执行逻辑,而且这样的方式不利于SEO。那么进一步提高首屏加载的方案还有两个,一个是预渲染,一个是SSR,即服务端渲染,后者的方案较为复杂,我会在以后的文章中进行分析,服务端渲染的方式相比预渲染,最主要的优势是可以动态拼接数据,作为文档的一部分返回,从而实现更友好的SEO和动态分享等功能。
预渲染依赖一个prerender-spa-plugin插件,首先要在webpack.prod.conf.js中引入该插件,如下
const PrerenderSPAPlugin = require('prerender-spa-plugin')
然后,在plugins中添加以下插件配置:
new PrerenderSPAPlugin(
path.join(__dirname, '../nginx/blog'),
['/'],
{
//在一定时间后再捕获页面信息,使得页面数据信息加载完成
captureAfterTime: 50000,
//忽略打包错误
ignoreJSErrors: true,
phantomOptions: '--web-security=false',
maxAttempts: 10,
}
)
经过这样配置以后,打包生成的index.html就包含了预渲染的dom结构,因此首屏渲染速度会得到更大提升。但是这里要注意一个问题,异步加载的路由打包后的chunk文件被插入了head标签中,并且带有一个async属性,如下:
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Blog - SilentPort</title>
<link href="/static/css/client.6b5a982c3673872dbaa1a8891279d36d.css" rel="stylesheet">
<script type="text/javascript" charset="utf-8" async="" src="/static/js/3.c925dfc72043d1d1d5ac.js"></script>
</head>
而运行时的manifest文件则位于body的底部,由于async会导致加载和渲染后续文档元素的过程和当前script脚本的加载与执行并行进行(异步),因此会导致该script脚本先于manifest执行,这会产生一个webpackJsonp is not defined错误。因此在部署之前这里需要手动将async改成defer,后者在加载后续文档元素的过程中也会和当前script脚本的加载并行进行(异步),但是当前script脚本的执行要在所有元素解析完成之后,DOMContentLoaded事件触发之前完成,这样就保证了脚本后于manifest执行。
参考文章:https://www.jianshu.com/p/da46f410156a?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation