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,
          },
        },
      },
    }
相关推荐
一个处女座的程序猿O(∩_∩)O16 分钟前
完成第一个 Vue3.2 项目后,这是我的技术总结
前端·vue.js
GIS开发特训营2 小时前
Vue零基础教程|从前端框架到GIS开发系列课程(七)响应式系统介绍
前端·vue.js·前端框架·gis开发·webgis·三维gis
Cachel wood2 小时前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
一个处女座的程序猿O(∩_∩)O5 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
燃先生._.11 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
2401_8576009514 小时前
SSM 与 Vue 共筑电脑测评系统:精准洞察电脑世界
前端·javascript·vue.js
2401_8576009514 小时前
数字时代的医疗挂号变革:SSM+Vue 系统设计与实现之道
前端·javascript·vue.js
GDAL15 小时前
vue入门教程:组件透传 Attributes
前端·javascript·vue.js
轻口味15 小时前
Vue.js 核心概念:模板、指令、数据绑定
vue.js
2402_8575834915 小时前
基于 SSM 框架的 Vue 电脑测评系统:照亮电脑品质之路
前端·javascript·vue.js