响应式的 switchboard:让又大又慢的Vue/AIpine 页面爆快
我的提示: AIpine 是一个js 库,官网口号是 “一个新的轻量极javascript框架”,其实我之前也没接触过,翻译这篇文章时才注意到
官方地址: [AIpine.js]https://alpinejs.dev
下面开始是译文:
小提示: 在这篇文章中我将使用Vue/AIpine 术语 ,但是我认为此模式可以应用于更多不同的语言框架
前段时间我就碰到了数千行的超大表格。每一行都是单独的 AIpine 组件, 你可以通过点击激活它(css会显示高亮)。如果你点击了另一行,那么前一行激活状态就会处理非激活状态,新行则是激活状态。
问题就是:激活某一个单行居然差不多要耗时整整一秒钟
此类性能问题几乎让这整个效果无法使用,特别是在用键盘操作导航至单元格时。
所以 ,我用一个加载了1万行的页面去测试寻找以找到尽可能的提高性能方法。 不久, 我想出了一个简洁的模式让页面可以立即更新状态。我称它为: Switchboard 模式
下面展示展示…
在此例子中,我不打算用AIpine 。取而代之的是AIpine 底层用到的 vue 提供的一些通用响应式方法。 如果你对 “watchEffect” 和 “ref” 不太熟悉,通过后面的代码片断你应该能凭直觉就知道它们的用法,如果还是不知道,那么 api 文档就在这里查看
假定给我们一个拥有1万行的表格,下面简单的代码会在页面加载时高亮当前激活的 activeRow
let activeRow = 12
document.querySelectorAll('tr').forEach((row) => {
if (row.id === activeRow) {
row.classList.add('active')
} else {
row.classList.remove('active')
}
})
现在,当不同行被点击时,我们可以给行添加点击事件来设置新的高亮行
let activeRow = 12
document.querySelectorAll('tr').forEach((row) => {
row.addEventListener('click', () => {
activeRow = row.id
})
if (row.id === activeRow) {
row.classList.add('active')
} else {
row.classList.remove('active')
}
})
以上代码的问题是,当一个行被点击,当前激活行会更新,但在视觉上我们看不到任何变化。
下面展示了我们可以使用 “reactivity” 让当activeRow 发生变化时,所有行自己触发自身的更新:
import { ref, watchEffect } from 'vue'
let activeRow = ref(12)
document.querySelectorAll('tr').forEach((row) => {
row.addEventListener('click', () => {
activeRow.value = row.id
})
watchEffect(() => {
if (row.id === activeRow.value) {
row.classList.add('active')
} else {
row.classList.remove('active')
}
})
})
上面的代码片断做了这么几件事
- 用ref 包裹 activeRow 变量,从而使得它可以被响应依赖追踪
- 循环1万行,添加点击事件,用于改变响应式的 actievRow 变量
- 注册一个响应式副作用 watchEffect 它会在任意响应依赖变更时重新运行(此处是 activeRow )
这是为何AIpine ( 或Vue 也类似,以我的知识范围内的理解来讲 )能在底层成功工作的原理,如果你要渲染1万行组件,它们全部依赖一个响应式状态比如: “activerRow”
现在,当某个用户点击某行,那么被点击行将变成 active 其它行自动变成 deactivated
问题是: 页面更新超级变
为什么 ?因为每当activeRow变量发生变化时, 1万个watchEffect 回调会被执行。
大多数 app中, 声明一个状态,然后它被子组件,这不成问题。 然而,如果你你正在创建非常多的组件(或“效果”),除了被activeRow状态影响的相关的两行外,其它9998 完全不需要关心状态变更, 这非常低效。
解决方案: 一个响应式的switchboard
响应式 switchboard 这术语是我现在为这个概念创造的。 非常有可能这个模式也许已经有了其它的名称,但是,管它呢…
在当前设置中,我们有单个一个状态,和1万个依赖于此状态的地方。
假如换成一个单独状态,和1万个不同预存的值(和上面一样),我们拥有了1万个不同的状态,每个状态是一个布尔值,代表了每个预设值。举个栗子:
// Before
let activeRow = ref(4)
// After
let rowStates = {
1: ref(false),
2: ref(false),
3: ref(false),
4: ref(true),
5: ref(false),
...
}
让我们稍变动一下上面例子的代码来使用此模式:
import { ref, watchEffect } from 'vue'
let rowStates = {}
document.querySelectorAll('tr').forEach((row) => {
rowStates[row.id] = ref(false)
row.addEventListener('click', () => {
rowStates[row.id].value = true
})
watchEffect(() => {
if (rowStates[row.id].value) {
row.classList.add('active')
} else {
row.classList.remove('active')
}
})
})
好了,现在你能看到, 不同于activeRow存储单一的row ID, 我们使用 rowStates 来存储1万条数据,每条key就是row ID, 每条数据值就是一个响应式的布尔值,代表了当前行是否处于激活状态
这行的通且超级快,现在,由于只点击一行,只有被点击的当前行状态会变更状态(不会影响到其它9999行)
不过还有一个问题 之前 因为 activeRow 只包含引用一个值,相同时间当前只有一个行被允许激活。 前一个行会自动变更为非激活态,因为每行都会自动重新计算。
在这个例子中,“非激活过程”没有触发。 为了让行拥有非激活态,我们需要在rowStates里找到它并标记它的值为false
让我们添加一丢丢代码来实现它:
import { ref, watchEffect } from 'vue'
let rowStates = {}
document.querySelectorAll('tr').forEach((row) => {
rowStates[row.id] = ref(false)
row.addEventListener('click', () => {
// Deactivate the old row...
for (id in rowStates) {
if (rowStates[id].value === true) {
rowStates[id].value = false
return
}
}
rowStates[row.id].value = true
})
watchEffect(() => {
if (rowStates[row.id].value) {
row.classList.add('active')
} else {
row.classList.remove('active')
}
})
})
正如你所看到的,我们添加了一丢丢代码在点击事件内,循环全部的行并设置为非激活态
现在我们加上了非激活态功能 ,但我们的代码依然不高效,每次行被点击,就需要循环rowStates对象的1万项
结果是我们回头在之前优化中使用过的,通过添加一点数据来存储当前激活的行ID。 它类似于基础的暂存,使得我们无需再使用循环了:
import { ref, watchEffect } from 'vue'
let rowStates = {}
let activeRow
document.querySelectorAll('tr').forEach((row) => {
rowStates[row.id] = ref(false)
row.addEventListener('click', () => {
if (activeRow) rowStates[activeRow].value = false
activeRow = row.id
rowStates[row.id].value = true
})
watchEffect(() => {
if (rowStates[row.id].value) {
row.classList.add('active')
} else {
row.classList.remove('active')
}
})
})
得了,现在我们添加 了activeRow 变量,我们搞定了完美高效更新
接近完美,但感觉还差点意思,如果我们能简单抽象一下让我们少做一些跑腿的活。
小的函数 switchboard 它包含了一个值,并返回一些通用函数用于访问和变更这个值
import { watchEffect } from 'vue'
import { switchboard } from 'reactive-switchboard' // Heads up: this isn't on NPM
let { set: activate, is: isActive } = switchboard(12)
document.querySelectorAll('tr').forEach((row) => {
row.addEventListener('click', () => {
activate(row.id)
})
watchEffect(() => {
if (isActive(row.id)) {
row.classList.add('active')
} else {
row.classList.remove('active')
}
})
})
现在通过小小的switchboard 函数, 我们拥有了和之前一样洁净的代码,并且拥有超高效的性能
这里是全部的 switchboard API
import { switchboard } from 'reactive-switchboard' // Heads up: this isn't on NPM
let { get, set, is } = switchboard(12)
// get() returns "12" in this case (non-reactively)
// set(10) sets the internal value to 10
// is(10) runs a reactive comparison to the underlying value
这对于追踪类似于active激活态超级有用,因为只有一个激活状态值了
我也找到了类似的需求,在追踪 ‘selected’ 状态时,这需要多个状态
对于这些需求,我新建了一个通用方法 switchboardSet 它拥有类似 Set 对象的API (可能有更好的名字,但管它呢…)
import { switchboardSet } from 'reactive-switchboard' // Heads up: this isn't on NPM
let { get, add, remove, has, clear } = switchboardSet([12])
// get() returns [12] (non-reactively)
// add(10) sets the internal array to [12, 10]
// remove(10) sets the array back to [12]
// has(12) returns a reactive boolean
// clear() reactively clears the internal array: []
老弟你行了,发现问题,找到解决方法,并抽象它。
我把switchboard源码放到 github上了
自取!
英文原文链接
https://calebporzio.com/reactive-switchboard
转载入注明博客园 王二狗Sheldon
Email: willian12345@126.com
https://github.com/willian12345
响应式的 switchboard:让又大又慢的Vue/AIpine 页面爆快的更多相关文章
- 响应式WEB设计的基本原则大总结
响 应式Web设计对于解决多类型屏幕问题来说是个不错方案,但从印刷的角度来看,其却存在着很多的困难.没有固定的页面尺寸.没有毫米或英寸,没有任何物理 限制,让人感到无从下手.随着建立网站可用的各种小工 ...
- 手持式停车收费管理系统全套案例,支持车牌识别,包含了android版app,微信小程序查询,响应式管理后台,云端大数据存储
先展示几个app效果图片吧,使用起来非常方便,关联了机器的快捷键操作,操作速度提高了不少,摄像头车牌自动识别,车牌识别无网络情况下离线也可以使用 再来一张后台截图,停车场信息完整显示,今日数据实时 ...
- 前端必读:Vue响应式系统大PK
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 原文参考:https://www.sitepoint.com/vue-3-reactivity-system ...
- CSS3与页面布局学习笔记(四)——页面布局大全(负边距、双飞翼、多栏、弹性、流式、瀑布流、响应式布局)
一.负边距与浮动布局 1.1.负边距 所谓的负边距就是margin取负值的情况,如margin:-100px,margin:-100%.当一个元素与另一个元素margin取负值时将拉近距离.常见的功能 ...
- 什么是响应式Web设计?怎样进行?
http://beforweb.com/node/6/page/0/3 开始第一篇.老规矩,先无聊的谈论天气一类的话题.十一长假,天气也终于开始有些秋天的味道,坐在屋里甚至觉得需要热咖啡.话说两年前也 ...
- 响应式Web设计 H5和CSS3实战(第二版)随笔
第一章 响应式设计基础 1.弹性布局 <meta name = "viewport" content = "width = device-width"&g ...
- Web移动前端开发-——bootstarp响应式框架
移动端WEB开发之响应式布局 1.0 响应式开发原理 1.1 响应式开发原理 就是使用媒体查询针对不同宽度的设备进行布局和样式的设置,从而适配不同设备的目的. 设备的划分情况: 小于768的为超小屏幕 ...
- 使用 Responsive Elements 快速构建响应式网站
Responsive Elements 可以使任何元素来适应和应对他们所占据的区域.这是一个轻量的 JavaScript 库,你可以轻松嵌入到你的项目.元素会更具自己的宽度,自动响应和适应空间的增加或 ...
- Bootstrap页面布局5 - 响应式布局(格式)
旨在优化不同上网设备中页面显示的优化 响应式布局:就是根据浏览窗口的尺寸,改变页面的变化 原理:利用css的media-queries判断浏览窗口的尺寸,在CSS样式表中设置一些规则! 例如: 在&l ...
- Vue 响应式总结
有些时候,不得不想添加.修改数组和对象的值,但是直接添加.修改后getter.setter又失去了. 由于 JavaScript 的限制, Vue 不能检测以下变动的数组: 当你利用索引直接设置一个项 ...
随机推荐
- consul 服务注册与更新
服务注册与发现是微服务架构中不可或缺的重要组件. 起初服务都是单节点的,不保障高可用性,也不考虑服务的压力承载,服务之间调用单纯的通过接口访问.直到后来出现了多个节点的分布式架构,起初的解决手段是在服 ...
- app 好用的手机端开发调试工具插件 vconsole vue3
效果:可以在手机上看控制台的内容 $ npm install vconsole 在main.js里引入 // 添加移动端log查看调试器 import Vconsole from 'vconsole' ...
- 记一次FusionCompute安装springboot应用过程
客户给了一个地址,登录后发现是FusionCompute 的一个虚拟机,第一次使用,于是过了一下在线帮助文档 ,大概明白了. 因为客户已经创建完了裸机虚拟机,前面的过程我也不会太多关注. 在概要里,可 ...
- node.js缓冲区类与node-red向串口发数据
遇到的问题是使用node-red的串口模块向串口发送16进制数据,控制LED灯. 初学者经常想当然的认为只要msg的payload里放上对应的数就行了.其实不是. Node-red是node.js环境 ...
- centos7下安装Node.js MongoDB Nginx
一.Node.js 方法1(笔者采用).如果对Node.js环境有比较高的要求,建议选择源码安装的方式进行安装,通过wget命令下载Node.js官网上的tar.gz文件包到centos服务器上,进 ...
- 生产环境Java应用服务内存泄漏分析与解决
有个生产环境CRM业务应用服务,情况有些奇怪,监控数据显示内存异常.内存使用率99.%多.通过生产监控看板发现,CRM内存超配或内存泄漏的现象,下面分析一下这个问题过程记录. 服务器配置情况: 生产服 ...
- CSP2022-S游寄
游寄游寄,顾名思义,边游边寄 11.00AM 起床 复习了一下各种终端命令,然后又复习了一下对拍 虽然都没用到 然后接着睡. 有点小紧张,毕竟一年没搞OI 12.00AM 今天吃河虾 还行,只是有点扎 ...
- Java面试——Java基础
更多内容,移步IT-BLOG 一.JAVA中的几种基本数据类型 Java语言中一共提供了8种原始的数据类型(byte,short,int,long,float,double,char,boolean) ...
- 搭建react的架手架
1.回顾 cnpm i @vue/cli -g ----- 4的脚手架 ------ webpack 4 cnpm i @vue/cli@3 -g ----- 3的脚手架 ------ webpack ...
- TS 基础及在 Vue 中的实践:TypeScript 都发布 5.0 版本啦,现在不学更待何时!
大家好,我是 Kagol,OpenTiny 开源社区运营,TinyVue 跨端.跨框架组件库核心贡献者,专注于前端组件库建设和开源社区运营. 微软于3月16日发布了 TypeScript 5.0 版本 ...