Vue h5项目性能优化

掘金上有很多大佬分享过性能优化的经验贴,但有一些方式针对需要快速的迭代的h5不太适用,这里分享一些适用于h5开发(Vue)性能优化的方法

vue 自带了模块分析 可以打包命令后添加--report 就会生成一个report.html 直接运行即可看到各个模块占比

一.加载优化

1.路由懒加载:

使用路由懒加载,不做路由懒加载的话,用户打开首页,会一次性加载所有资源

css 复制代码
{
    path: '/loading',
    name: 'loading',
    component: () => import('@/views/loading/index.vue'),
},

原理:依托ES6动态加载模import(),调用 import() 之处,被作为分离的模块起点,意思是,被请求的模块和它引用的所有子模块,会分离到一个单独的 chunk 中,可以通过魔法注释设置模块名称,名称相同的将会被组合到相同的块中

2.组件懒加载:

通过import 异步引入对应组件资源,但这样可能导致请求增多

拆分原则:

1.该页面体积过大,导致加载缓慢,通过浏览器并行发起请求,提示下载速度

2.该组件不是一进入页面就展示,需要一定条件下才触发(比如弹框组件)

3.该组件复用性高,很多页面都有引入,利用组件懒加载抽离出该组件,一方面可以很好利用缓存,同时也可以减少页面的 JS 文件大小(比如表格组件、图形组件等)

javascript 复制代码
// vue 中实现异步加载组件
import { reactive, ref, onMounted, computed, watch,defineAsyncComponent } from 'vue'
const rule = defineAsyncComponent(() => (import('_c/rule')))
3.Tree shaking 优化

export default 导出的是一个对象,无法知道里面是否使用未导出的对象,但通过export 导出的函数就能利用tree-shaking 去除代码中没有使用的部分

4.骨架屏

插件:vue-skeleton-webpack-plugin 可以给不同页面设置骨架屏

白屏时间调试:谷歌的Performance 中FP/FCP(首屏渲染)

5.长列表虚拟滚动

如果使用长图去进行列表滚动,所需要的渲染时间会很长,滚动时还会造成页面卡顿,整体体验非常不好

虚拟滚动------指的是只渲染可视区域的列表项,非可见区域的不渲染,在滚动时动态更新可视区域,该方案在优化大量数据渲染时效果是很明显的,目前已有成熟的虚拟滚动插件

6.使用requestAnimationFrame 制作动画

(1)引擎层面

setTimeout/setInterval 属于 JS引擎,requestAnimationFrame 属于 GUI引擎

JS引擎与GUI引擎是互斥的,也就是说 GUI 引擎在渲染时会阻塞 JS 引擎的计算

(2)时间是否准确

requestAnimationFrame 刷新频率是固定且准确的,但 setTimeout/setInterval 是宏任务,根据事件轮询机制,其他任务会阻塞或延迟js任务的执行,会出现定时器不准的情况

7. 调整JS 的加载方式

js 常用的有6种加载方式,async、defer 是 script 标签的专属属性,对于网页中的其他资源,可以通过 link 的 preload、prefetch 属性来预加载

xml 复制代码
1.正常加载:会阻塞 dom 渲染,浏览器必须等待 index.js 加载和执行完成后才能去做其它事情
<script src="index.js"></script>
​
​
2.异步加载:JS 不会阻塞 DOM 的渲染,async 加载是无顺序的,当它加载结束,JS 会立即执行
  使用场景:使用场景:若该 JS 资源与 DOM 元素没有依赖关系,也不会产生其他资源所需要的数据时,可以使用async 模式,比如埋点统计
<script async src="index.js"></script>
​
3.defer 模式:defer 资源会在 DOMContentLoaded 执行之前,并且 defer 是有顺序的加载,如果有多个设置了 defer 的 script 标签存在,则会按照引入的前后顺序执行,即便是后面的 script 资源先返回
    
 <script defer src="index.js"></script>
 
​
4.module 模式:在主流的现代浏览器中,script 标签的属性可以加上 type="module",浏览器会对其内部的 import 引用发起 HTTP 请求,获取模块内容。这时 script 的行为会像是 defer 一样,在后台下载,并且等待 DOM 解析
Vite 就是利用浏览器支持原生的 es module 模块,开发时跳过打包的过程,提升编译效率
​
<script type="module">import { a } from './a.js'</script>
​
5.preload:预加载,用于提前加载一些需要的依赖,这些资源会优先加载(如下图红框),vue2中 会自动给首页的所需资源添加预加载
    5.1preload 加载的资源是在浏览器渲染机制之前进行处理的,并且不会阻塞 onload 事件;
    5.2preload 加载的 JS 脚本其加载和执行的过程是分离的,即 preload 会预加载相应的脚本代码,待到需要时自行调用;
<link rel="preload" as="script" href="index.js">
​
6.prefetch:利用浏览器的空闲时间,加载页面将来可能用到的资源的一种机制
 prefetch 特点:
    6.1 prefetch 加载的资源可以获取非当前页面所需要的资源,并且将其放入缓存至少5分钟(无论资源是否可以缓存)
    6.2当页面跳转时,未完成的 prefetch 请求不会被中断
8.图片优化

1.图片动态裁剪:

利用阿里云/腾讯云cdn提供的图片动态裁剪功能,只需在图片的url地址上动态添加参数,就可以得到你所需要的尺寸大小,针对不同分辨率使用合适的图片可以有效减少图片尺寸。

ruby 复制代码
// 具体形式 h高度 w宽度 e(0:按长边缩放,1:按短边缩放) c(0:)

<https://oss-console-img-demo-cn-hangzhou.oss-cn-hangzhou.aliyuncs.com/example.jpg@100h_100w_1e_1c>

2.图片懒加载:

对于一些图片量比较大的首页,用户打开页面后,只需要呈现出在屏幕可视区域内的图片,当用户滑动页面时,再去加载出现在屏幕内的图片,以优化图片的加载效果

图片懒加载实现原理:

由于浏览器会自动对页面中的 img 标签的 src 属性发送请求并下载图片,可以通过 html5 自定义属性 data-xxx 先暂存 src 的值,然后在图片出现在屏幕可视区域的时候,再将 data-xxx 的值重新赋值到 img 的 src 属性即可

二、图片渲染优化

css 复制代码
1.CSS写在头部,JS写在尾部并异步
​
2.避免img、iframe等的src为空:空src会重新加载当前页面,影响速度和效率
​
3.尽量避免重置图像大小:多次重置图像大小会引发图像的多次重绘,影响性能
​
4.大图像尽量避免使用DataURL:DataURL图像没有使用图像的压缩算法,文件会变大,并且要解码后再渲染,加载慢耗时长

三、dom渲染优化

css 复制代码
1.减少dom节点
​
2.优化动画
    1.尽量使用CSS3动画
    2.合理使用requestAnimationFrame动画代替setTimeout
    
3.GPU加速
    使用某些HTML5标签和CSS3属性会触发GPU渲染
    1.css 属性opacity、transform、transition
    2.HTML标签 video、canvas、webgl

四.样式优化

arduino 复制代码
1.避免在HTML中书写style
​
2.避免CSS表达式,CSS表达式的执行需跳出CSS树的渲染
​
3.正确使用display
    display:inline后不应该再使用float、margin、padding、width和height
    display:inline-block后不应该再使用float
    display:block后不应该再使用vertical-align
    display:table-*后不应该再使用float和margin
 
4.不声明过多的font-size
​
5.值为0时不需要任何单位

五.脚本优化

arduino 复制代码
1.减少重绘和回流
    避免不必要的DOM操作
    避免使用document.write
    减少drawImage
    尽量改变class而不是style,使用classList代替className
    用DOMFragment缓存批量化DOM操作
 
2.尽量避免操作Dom,可以先用
​
3.缓存.length的值:每次.length计算用一个变量保存值

六.构建优化

基于各类打包工具进行体积优化,

减少打包时间:缩减范围缓存副本定向搜索提前构建并行构建可视结构

减少打包体积:分割代码摇树优化动态垫片按需加载作用提升压缩资源

1.减少打包时间
javascript 复制代码
​
1.配置include/exclude 
    减少检索的范围,一般是再各loader中进行配置
export default {
    // ...
    module: {
        rules: [{
            exclude: /node_modules/,
            include: /src/,
            test: /.js$/,
            use: "babel-loader"
        }]
    }
};
​
​
2.缓存副本
  配置cache缓存Loader对文件的编译副本,大部分Loader/Plugin都会提供一个可使用编译缓存的选项,通常包含cache字眼。以babel-loader
export default {
    module: {
        rules: [{
            // ...
            test: /.js$/,
            use: [{
                loader: "babel-loader",
                options: { cacheDirectory: true }
            }]
        }]
    },
};
​
3.定向搜索
    配置resolve提高文件的搜索速度
export default {
    // ...
    resolve: {
        alias: {
            "#": '', // 根目录快捷方式
            "@": '', // src目录快捷方式
            swiper: "swiper/js/swiper.min.js"
        }, 
        // 模块导入快捷方式
        extensions: [".js", ".ts", ".jsx", ".tsx", ".json", ".vue"] // import路径时文件可省略后缀名
    }
};
​
4.并行构建
    配置Thread将Loader单进程转换为多进程,若项目文件不算多就不要使用,开启多个线程也会存在性能开销。
​
​
5.可视结构,寻找体积过大的包
import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
​
export default {
    // ...
    plugins: [
        // ...
        BundleAnalyzerPlugin()
    ]
};
2.减少打包体积
javascript 复制代码
1.分割代码
    webpack v4使用splitChunks替代CommonsChunksPlugin实现代码分割。
​
​
2.摇树
    删除项目中未被引用代码,只对ESM模块有效,webpack中将模式设置为production就可以开启摇树
    
3.动态垫片
    通过垫片服务根据UA返回当前浏览器代码垫片,好处是无需将繁重的代码垫片打包进去
    
​
4.按需加载
    webpack中配合import()实现按需加载
    // 魔术注解
    const Login = () => import( /* webpackChunkName: "login" */ "../../views/login");
    const Logon = () => import( /* webpackChunkName: "logon" */ "../../views/logon");
3.抽离第三方包
arduino 复制代码
configureWebpack: config => {
    // 再这里配置不需要进行打包的组件
    config.externals = {
      vant: 'vant',
      axios: 'axios',
    }
  },
4.开启gzip压缩

一般正式项目上了cdn后 自动开启gzip压缩模式,所以一般无需配置,如果有需要的话,可以开启(需要nginx 也配置相关的) 默认nginx是可以直接转换压缩的,只不过直接有gz文件可以省去压缩的时间

javascript 复制代码
// 开启gzip压缩
const CompressionPlugin = require('compression-webpack-plugin')
​
// vue.config.js
config.plugins.push(
      new CompressionPlugin({
        filename: '[path].gz[query]',
        test: /.js$|.css$|.html$|.ttf$|.eot$|.woff$/, // 匹配文件名
        threshold: 10240, // 对超过10k的数据压缩
        deleteOriginalAssets: false, // 不删除源文件
      })
    )
5.分包 splitChunks

项目中的第三方库默认会被打包到一个文件名含vendors的bundle中,splitChunks可以抽离出常用的第三方包,不整合到主包中

yaml 复制代码
// 分离第三方包
    config.optimization = {
      splitChunks: {
        chunks: 'all',
        // 入口和它的同步依赖最多能被拆分的数量,默认为3
        maxInitialRequests:5,
        cacheGroups: {
          axios: {
            name: 'axios',
            test: /[\/]node_modules[\/]axios[\/]/,
            priority: -10,
          },
          vant: {
            name: 'vant',
            test: /[\/]node_modules[\/]vant[\/]/,
            priority: -10,
          },
          'vue-router': {
            name: 'vue-router',
            test: /[\/]node_modules[\/]vue-router[\/]/,
            priority: -10,
          },
          pinia: {
            name: 'pinia',
            test: /[\/]node_modules[\/]pinia[\/]/,
            priority: -10,
          },
        },
      },
    }
相关推荐
neter.asia几秒前
vue中如何关闭eslint检测?
前端·javascript·vue.js
十一吖i19 分钟前
前端将后端返回的文件下载到本地
vue.js·elementplus
光影少年20 分钟前
vue2与vue3的全局通信插件,如何实现自定义的插件
前端·javascript·vue.js
熊的猫1 小时前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js
mosen8682 小时前
Uniapp去除顶部导航栏-小程序、H5、APP适用
vue.js·微信小程序·小程序·uni-app·uniapp
别拿曾经看以后~3 小时前
【el-form】记一例好用的el-input输入框回车调接口和el-button按钮防重点击
javascript·vue.js·elementui
Gavin_9154 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
Devil枫9 小时前
Vue 3 单元测试与E2E测试
前端·vue.js·单元测试
GIS程序媛—椰子10 小时前
【Vue 全家桶】6、vue-router 路由(更新中)
前端·vue.js
毕业设计制作和分享11 小时前
ssm《数据库系统原理》课程平台的设计与实现+vue
前端·数据库·vue.js·oracle·mybatis