Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲

2023-05-29 0 1,040

下栽の地止:https://www.itwangzi.cn/4372.html

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲Vue关键技术Vue+Vue-Router

前几日子公司有两个较为关键的组件从 vue2 升级换代到 vue3,升级换代后辨认出 element-plus table 的操控性较之 vue2 版上升十分轻微

自订列全数键入的情景下(20 行 x 180 列),条目中的控制器转换,费时从原本的 400-500 微秒上升到 7-8 秒,严重影响使用者新体验,历经较长的操控性试验、debug,找出了两处较为核心理念的强化点。

先上看呵呵 20 行 x 180 列情景下各强化点的操控性原始统计数据,为须建随机性,每一情景单厢测 3 次。

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲

大体强化文本如下

修正 table 源代码,将 data 与 columns 从 ref 改成 shallowRef。修正 table 源代码,getColspanRealWidth 表达式中积极响应式统计数据强化。销售业务强化:拿掉 el-tooltip disabled 特性,改成 if。

预备组织工作

首先初始化两个 vue3 项目,引入 element-plus,并使用 el-table 实现两个 20 行 * 180 条目格。

20 行 + 180 列:2 个固定列(两个文本、两个 switch),178 个通过 for 循环创建的自订列两个显示/隐藏 table 的 switch 控制器,用于试验 table 从隐藏到显示,渲染费时自订列中有两个 el-tooltip + disabled 逻辑

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲1-table-base.png

最小化销售业务 demo 创建

核心理念 table 代码代码如下表所示,完整代码参见:table-base | table-performance-demo[1]

<el-tablev-if=“showTable”:data=“tableData”style=“width: 100%; height: 500px; overflow: scroll”><el-table-columnprop=“info”label=“信息”width=“80”fixed /><el-table-columnprop=“status”label=“状态”width=“80”fixed><template #default=“scope”><el-switchv-model=“scope.row.status” @change=“statusChange” /></template></el-table-column><el-table-columnv-for=“item in customColumns”:key=“item.prop”:prop=“item.prop”:label=“item.label” ><template #default=“scope”><el-tooltipplacement=“top-start”:disabled=“!(item.prop === column1 && scope.row[item.prop])” ><template #content><span>{{ “tooltip显示” + scope.row[item.prop] }}</span></template><span>{{ scope.row[item.prop] }}</span></el-tooltip></template></el-table-column></el-table><scriptlang=“ts”setup>// 假统计数据逻辑const customColCount = 178; // 自订列数const rowCount = 20; // 行数onBeforeMount(() => {// 初始化自订列统计数据let temp = [];for (let i = 0; i < customColCount; i++) { temp.push({ prop: `column${i + 1}`, label: `第${i + 1}列` }); } customColumns.value = temp;// 初始化表格统计数据letdataTemp = [];for (let i = 0; i < rowCount; i++) {let row: any = { info: `第${i + 1}行`, status: true }; i === 0&& (row.status =false);for (let j = 0; j < customColCount + 2; j++) { row[`column${j + 1}`] = `第${i + 1}${j + 1}列`; } dataTemp.push(row); } tableData.value = dataTemp;});</script>

渲染费时计算逻辑

渲染费时计算逻辑如下表所示,利用 script 阻塞,来计算渲染费时

/*

当前显示:{{ `${rowCount}行${customColCount + 2}列` }}, 显示/隐藏 table:

….

*/
// 显示/隐藏 table,计算 table 渲染费时const switchTableShow = () => {// 先展示 loading showLoading.value = true;// 200ms 后再修正 table 是否显示,防止和 loading 合并到两个渲染周期,导致 loading 不显示 setTimeout(() => {let startTime = +newDate();showTable.value = !showTable.value;// 修正 table 显示,会形成 script 阻塞 showLoading.value = false; // 这里的 loading 关闭,会在 table 阻塞完成后渲染关闭 dom// 创建两个宏任务,等上面阻塞的微任务执行完成后,再显示计算费时 setTimeout(() => {let endTime = +newDate(); ElMessage.success(`渲染费时:${(endTime – startTime)

/ 1000}s`);

}, 0);

}, 200);

};

操控性统计数据,与 performance 费时对比

table 渲染、switch 转换试验费时如下表所示

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲table-base-duration.png

table 隐藏到显示 gif 图

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲

table-base-6-8-s.gif

switch 从关到开 gif 图

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲

table-base-switch-3-8-s.gif

为了验证我们自己写的费时原始统计数据的准确性,这里在 switch 控制器时,打开了 performance 录制,具体如下表所示图

页面显示渲染费时:4.524s,performance 中两个 Long Task:2.29s + 2.17,加上非 Long Task 部分,统计数据基本一致,因此我们自己写的费时计算逻辑是基本准确的

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲

table-base-switch-performance.gif

另外,开启 performance 录制时,比不录制时要稍微慢点。下面来开始强化吧!

ref 改 shallowRef

理论依据与可行性分析

条目中的控制器转换时,table 虽然只是两个节点发生了变化,但依旧触发了完整的 vue patch 比对更新逻辑,费时较久。

上看两个官方的解释:渲染机制 | Vue.js

(https://cn.vuejs.org/guide/extras/rendering-mechanism.html#compiler-informed-virtual-dom”)

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲vue-render-logic.png

理论上,减少积极响应式统计数据依赖,就可以提升操控性。

shallowRef() 是 ref() 的浅层作用形式。仅当 xx.value 发生变更时,才触发积极响应更新,减少深层次的积极响应依赖,可以提升 patch 比对操控性。

参考:指南 – 减少大型不可变结构的积极响应性开销

(https://cn.vuejs.org/guide/best-practices/performance.html#reduce-reactivity-overhead-for-large-immutable-structures )

const state = shallowRef({ count: 1 })// shallowRef 不会触发更改,如果 state 为 ref 时,是可以触发更新的。state.value.count = 2// shallowRef 会触发更改state.value = { count: 2 }

这里主要修正两种统计数据从 ref 到 shallowRef

// src/table/src/store/watcher.tsfunctionuseWatcher<T>() {const data: Ref<T[]> = shallowRef([]); // table data 统计数据constcolumns: Ref<TableColumnCtx<T>[]> = shallowRef([]);// 列统计数据// …}

这里有个问题,把 data、columns 改成 shallowRef 对功能会不会有影响?

首选,每次条目数据更新,我们销售业务逻辑单厢去请求条目,设置 list.value = xxx 可以触发 shallowRef 更新。历经试验,就算是 switch 控制器 v-model 绑定的 scope.row.status 变更也可以正常更新。手动点击试验选中、排序、分页等均未辨认出异常。

基于以上三点,在我们销售业务中,这个修正是可行的。提醒:如果想在你自己的项目中使用该强化,需要先做好试验。

下面上看具体修正细节

拷贝 element-plus table 源代码到当前项目

当前最新的版是 2.2.8,打开 element-plus/releases ( https://github.com/element-plus/element-plus/releases),下载最新版代码,将 table 目录(element-plus-2.2.28/packages/components/table) copy 到项目中的 src/table 下,删除目中无用的__test__ 试验目录

新开两个路由,/new 指定到两个新增的 table 组件内,较之原本 table 组件,只增加一行代码,当前组件内使用我们自订修正的 table。

完整代码参见:2-table-use-source | table-performance-demo

(https://github.com/zuoxiaobai/table-performance-demo/tree/2-table-use-source)

import ElTable from“@/table/src/table.vue”;

引入后报错 [plugin:vite:import-analysis] Failed to resolve import “@element-plus/directives” from “src\table\src\table.vue”. Does the file exist?

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲element-table-error.png

做一些修正,让代码可以在我们自己的项目中跑起来,方便修正、调试源代码

在 table 目录中搜索 \@element-plus 相关关键字,并进行批量替换

// @element-plus/directives => element-plus/es/directives/index// @element-plus/hooks => element-plus/es/hooks/index// @element-plus/utils => element-plus/es/utils/index

搜索 `@element-plus/components` 改成直接从 element-plus 引入

// 比如:import ElCheckbox from@element-plus/components/checkbox// 改成import { ElCheckbox } fromelement-plus// 注意:资源类的可以不用改,比如 import “@element-plus/components/base/style/css”;

修正源代码 – ref 改 shallowRef

在 src/table/src/store/watcher.ts 中,将 data 和 columns 统计数据从 ref 改成 shallowRef,具体代码参:table-ref-shallowRef | table-performance-demo

(“https://github.com/zuoxiaobai/table-performance-demo/tree/table-ref-shallowRef”)

// src/table/src/store/watcher.tsfunctionuseWatcher<T>() {constdata: Ref<T[]> = shallowRef([]);const _data: Ref<T[]> = shallowRef([]);const_columns: Ref<TableColumnCtx<T>[]> = shallowRef([]);const columns: Ref<TableColumnCtx<T>[]> = shallowRef([]);// …}

另外在 中 表格前面增加下面一行,标记调用的是我们修正的 table 组件

<!– src/table/src/table.vue 表格顶部增加下面一行 —><pstyle=“color: red”>来自 table 源代码</p><!– 内部逻辑 –><div:class=“ns.e(inner-wrapper)”:style=“tableInnerStyle”><!– … –></div>

操控性统计数据(费时减少17-20%)

table 渲染、switch 转换试验费时如下表所示

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲table-ref-shallow-ref-duration.png

table 隐藏到显示 gif 图

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲

table-ref-shallowRef.gif

switch 从关到开 gif 图

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲

table-ref-shallowRef-switch.gif

getColspanRealWidth 强化

当页面卡顿时,可以通过 performance 试验性能。下图是点击 switch 控制器后的操控性统计数据。可以看到

有两个 Scripting 阻塞 longTask,1.89s + 1.73s,整体费时 3.62s (performance开启时,会变慢一点)主要有两种费时任务:紫色小块是 render 渲染费时、绿色小块是 patch 比对费时,一般 patch 是 vue 内部逻辑,较为难强化通过查看 render 相关费时,找出 getColspanRealWidth 费时 212.2ms,这里有强化的空间

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲switch-performance-test.png

我们来查看这个表达式费时的原因,主要是在 tr 渲染时调用该表达式,计算每列的宽度

// src\table\src\table-body\render-helper.tscolumns.value.map((column, cellIndex) => {// …columnData.realWidth = getColspanRealWidth( columns.value, colspan, cellIndex );// …})

具体实现如下表所示,只用到了 realWidth, width 特性,且 columns.value 是积极响应式依赖,可以修正为非积极响应式统计数据,看是否能减少费时。

// src\table\src\table-body\styles-helper.tsconstgetColspanRealWidth = ( columns: TableColumnCtx<T>[],colspan: number,index: number): number => {if(colspan <1) {return columns[index].realWidth }const widthArr = columns .map(({ realWidth, width }) =>realWidth || width) .slice(index, index + colspan)returnNumber( widthArr.reduce((acc, width) =>Number(acc) + Number(width), -1) )}

这里我们新建 optimizeColumns 变量,存储表达式中使用的 realWidth 和 width,将这个非积极响应式统计数据传入到 getColspanRealWidth 函数内部使用,

完整代码参见 getColspanRealWidth-optimize | table-performance-demo

( “https://github.com/zuoxiaobai/table-performance-demo/tree/getColspanRealWidth-optimize”)

// src\table\src\table-body\render-helper.ts

const optimizeColumns = columns.value.map((item) => {

return { realWidth: item.realWidth, width: item.width };

});

columns.value.map((column, cellIndex) => {

// …

columnData.realWidth = getColspanRealWidth(

optimizeColumns, // 传入表达式内部时,使用非积极响应式统计数据

colspan,

cellIndex

);

// …

})

费时从 200ms 上升到 0.7ms

修正好后再次试验操控性,惊喜的辨认出,这个表达式的费时从 200ms+ 上升到 1ms 内,render 操控性明显提升。1.54s + 1.45s = 2.99s

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲getColspanRealWidth-optimize.png

操控性统计数据(费时减少7-20%)

table 渲染、switch 转换试验费时如下表所示

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲get-width-optimize-perf.png

table 隐藏到显示 gif 图

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲

get-width-optimize-table.gif

switch 从关到开 gif 图

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲

get-width-optimize-switch.gif

销售业务强化 tooltip disabled 改 if

历经上面的强化后,我们意识到,即使是很细微的积极响应式统计数据强化,也会对操控性带来较大影响。那销售业务逻辑中是否也存在这样的统计数据呢?

于是采用注释 + 将 el-table-column 插槽换成静态节点 <span>123</span>的方法,试验具体是哪里费时较长,然后针对性强化

历经试验,辨认出将自订列中的 el-tooltip 换成静态节点后,操控性有极大提升。

<el-table-columnv-for=“item in customColumns”:key=“item.prop”:prop=“item.prop”:label=“item.label”><template #default=“scope”>

placement=”top-start”

:disabled=”!(item.prop === column1 && scope.row[item.prop])”

>

{{ “tooltip显示” + scope.row[item.prop] }}

{{ scope.row[item.prop] }}

</el-tooltip> –>
<span>123</span></template></el-table-column>

如下表所示图,switch 控制器转换费时从 2.7s 左右减少到 0.5s 左右。performance 面板可以看到 patch 基本没有了,应该是模板编译时静态节点标记后,更新时就不用比对了。

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲tooltip-static-node-test.png

基于这个思路,el-tooltip 组件会成倍的增加 patch 比对费时,减少这个节点数量即可增强操控性。

为了少些一些代码,el-tooltip 使用 disabled 特性,用于在特定情景下隐藏 tooltip,这一部分统计数据可以不使用 el-tooltip 节点,改动如下表所示,使用 v-if 替换 disabled 特性功能,这样虽然会有重复代码,但可以减少节点数。

<template #default=“scope”>

placement=”top-start”

:disabled=”!(item.prop === column1 && scope.row[item.prop])”

>

{{ “tooltip显示” + scope.row[item.prop] }}

{{ scope.row[item.prop] }}

–>
<spanv-if=“!(item.prop === column1 && scope.row[item.prop])”> {{ scope.row[item.prop] }}</span><el-tooltipv-elseplacement=“top-start”><template #content><span>{{ “tooltip显示” + scope.row[item.prop] }}</span></template><span>{{ scope.row[item.prop] }}</span></el-tooltip></template>

再次试验操控性,可以看到操控性并没有上升多少,switch 控制器转换可以做到 0.5s 左右刷新

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲tooltip-optimize.png

操控性统计数据(费时减少80%)

table 渲染、switch 转换试验费时如下表所示

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲tooltip-optimize-pref.png

table 隐藏到显示 gif 图

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲

tooltip-optimize-table.gif

switch 从关到开 gif 图

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲

tooltip-optimize-switch.gif

总结

如下表所示图,通过 3 个小的细节改动,将 table 渲染费时从 6.88s 减少到 1s 左右,平均减少 85% 渲染费时,使用者新体验基本达到预期。

完整 demo github 地址:

https://github.com/zuoxiaobai/table-performance-demo

Vue核心技术Vue+Vue-Router+Vuex+SSR实战精讲pref-summary.png

在 vue3 项目中,积极响应式统计数据这块要特别注意。当遇到较为慢的情景时,建议采用如下表所示方法进行操控性强化

使用 performance 分析操控性瓶颈,或者自己写两个操控性费时逻辑,这样在做操控性强化时有统计数据参考。针对销售业务代码较多情景,采用注释 + 替换成静态节点方法排查费时较长的逻辑,针对性强化。另外,可以使用 Vue devtools 调试工具,查看组件更新渲染费时,排查积极响应式统计数据问题。
举报/反馈

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务