白屏优化
-
路由懒加载
{ path: '/login', component: () => import('@aicc/components/login/index') } -
使用v-if代替v-show
好处是可以减少首屏渲染dom数量(除非是一些会频繁变换显隐的元素,这样的元素应该用v-show)
-
不直接显示的组件做懒加载
常见的场景是:
el-tabs组件内从第二个tab开始渲染的组件都可以懒加载,目的是减少根据此路由完成首屏渲染时需加载的代码量比如
vue项目内的import pageComponent from './pageComponent' export default { name: 'Recharge', components: { pageComponent, }, ...改成
export default { name: 'Recharge', components: { pageComponent: () => import('./pageComponent'), }, ... -
使用
resolutions统一依赖版本假设主项目依赖包
A@1.1.0,主项目依赖的包B依赖A@1.1.1,此时yarn会在node_modules根路径和node_modules/B/node_modules分别安装A的两个版本,并都会打包到最终bundle。此时可以在package.json中使用resolutions强行统一A的版本"resolutions": { "a": "1.1.1" } -
组件库按需加载
包括项目使用的第三方组件库、公司内部组件库、项目
components目录内的组件 这些都可以使用babel-plugin-import插件实现,babel.config.js如下:const babelPluginImportForComponents = [ 'import', { libraryName: '@/components', libraryDirectory: '', camel2DashComponentName: false, customName: name => { return `@/components/${name}` } } ] module.exports = { ... 'plugins': [ ... babelPluginImportForComponents ] } -
资源预加载
场景比如:使用
element的项目打包出来的css内会通过@font-face去加载字体,也就是说字体会等css加载完并开始解析了再去加载,这样我们可以预加载字体,无需等待css这个一般是处理字体资源:
<link rel="preload" href="/ai-engine/static/fonts/element-icons.woff" as="font" type="font/woff" crossorigin="anonymous">或者其他任何首屏必须加载,却要等待其他资源先加载的资源 -
defer
一些对渲染影响不大的js资源可以在
script上加defer,这样这些js不会阻塞渲染也不影响加载,比如iconfont的彩色字体js文件 -
CDN
部分静态资源可以放
CDN上,好处是请求这些资源时不会带本网站的cookie,减少流量。一些优秀的CDN也会加快资源加载速度,比如七牛、ali -
减少css加载
原因是css的加载会阻塞dom的渲染,因为浏览器去渲染dom会先构建两棵树:dom树和css规则树,然后再做渲染。 所以应该只加载首屏需要的css,并且最好先通过
<style>内置到index.html内 -
gzip
打包的时候就打成
gzip包,不需要服务器再去做压缩 -
静态文件缓存
目前前端资源一般通过
hash区分版本,如果hash没变,可以一直缓存,在服务端设置相应的缓存策略 -
服务端渲染/预渲染
这个可以说是终极杀器,因为访问url直接就请求下来一段html和样式,可以直接渲染出来,其他的资源非阻塞式加载就好了。可以说使用此方案会减少很多优化工作量
首屏优化
其实上面的方案也都是首屏优化方案,这里重在强调白屏结束后的处理
-
骨架屏/loading动效
假如首屏上有些内容要通过接口请求数据再渲染,可以先渲染出骨架屏或者loading效果,让用户觉得页面是在加载的
-
后端数据层缓存
一些频繁查询的数据可以设置缓存,比如淘宝首页商品列表
-
图片懒加载(可以配合骨架屏方案)
-
图片压缩
-
去除或缓存重复请求
-------------------22/4/7更新---------------------
最近发现一种方式,虚拟代理。对于一些占用系统资源较多或者加载时间较长的对象比如echart,可以给这些对象提供一个虚拟代理。在真实对象创建成功之前虚拟代理扮演真实对象的替身,而当真实对象创建之后,虚拟代理将用户的请求转发给真实对象,比如echart懒加载:
// 用于处理echart懒加载
let handler
let echartFactory
const echart = {
init: (...args) => {
let container = {
// initFlag标志着echart是否已初始化,false代表未初始化,true代表已初始化
initFlag: false,
// 记录在未初始化完之前,主函数中要执行的方法都会存放在fns里面
fns: [],
// 待初始化的实例,在加载后所有方法都会在在echart中实现
echart: undefined
}
echartFactory.create(args).then(echart => {
container.echart = echart
container.fns.forEach(({ fn, args }) => {
if (typeof echart[fn] === 'function') echart[fn].apply(echart, args)
})
container.initFlag = true
})
return new Proxy(container, handler)
}
}
handler = {
get: function(target, key) {
if (!target.initFlag) {
// container中的echart未加载时,收集要执行的程序的名称和参数以对象的形式存放在fns中
return (...args) => {
target.fns.push({
fn: key,
args
})
}
} else {
// 当echarts初始化完成后,直接返回方法
if (key in target.echart) {
return target.echart[key]
} else {
return undefined
}
}
}
}
echartFactory = {
// 存放从外部加载的echarts模块
_echart: undefined,
create: async function(args) {
if (!this._echart) {
import('@aicc/utils/macarons.js')
this._echart = await import(/* webpackChunkName: "echarts" */'echarts')
}
return this._echart.init.apply(undefined, args)
}
}
export default echart
-------------------22/4/28更新---------------------
请求合并
如果是通过nginx启动服务,可以使用nginx-http-concat模块做请求合并;
如果是通过node起的服务,可以通过识别路由参数后,动态获取文件,拼接内容后返回的方式合并请求