- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
我的提示: 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')
}
})
})
上面的代码片断做了这么几件事 。
这是为何AIpine ( 或Vue 也类似,以我的知识范围内的理解来讲 )能在底层成功工作的原理,如果你要渲染1万行组件,它们全部依赖一个响应式状态比如: “activerRow” 。
现在,当某个用户点击某行,那么被点击行将变成 active 其它行自动变成 deactivated 。
问题是: 页面更新超级变 。
为什么 ?因为每当activeRow变量发生变化时, 1万个watchEffect 回调会被执行.
大多数 app中, 声明一个状态,然后它被子组件,这不成问题。 然而,如果你你正在创建非常多的组件(或“效果”),除了被activeRow状态影响的相关的两行外,其它9998 完全不需要关心状态变更, 这非常低效.
响应式 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页面爆快的文章就讲到这里了,如果你想了解更多关于响应式的switchboard:让又大又慢的Vue/AIpine页面爆快的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我刚刚更新了 Ruby,现在我在尝试启动 compass 时遇到以下错误: Encoding::CompatibilityError on line ["28"] of /usr/local/Cell
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 6 年前。
关闭。这个问题需要debugging details .它目前不接受答案。 编辑问题以包含 desired behavior, a specific problem or error, and th
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我正在尝试在我的 iOS 应用程序中开发可折叠/ Accordion 式的功能。这将是您可以在网站上找到的典型 FAQ 类型功能。我想点击标题,然后显示详细信息。 因为这是帮助部分,只有几个项目,我认
我正在尝试设计一个基于 REST 的 Web 服务来与我正在开发的农场动物管理系统进行交互。 为了详细说明问题,我收藏了动物 属于一个农场。每只动物都有自己的信息——例如姓名、身份证号、品种年龄等。因
我有 3 种不同的表单,其中复选框数量不同,每个部分基本上代表一个表单,因此当用户选择该部分中的复选框时,它会显示他们在该部分的总金额中 checkout 了多少 HTML
我有一份 32 页的 PDF 版家谱。与其将家谱全部放在一个非常大的 PDF 页面上(这是我想要的),不如将其格式化为一组 8 个单独的美国信纸大小的页面应该在整个宽度上缝合; 4 行这样就完成了树。
指SASS implementation for Java? : 在 Maven 目标编译包中自动编译 compass-style.org 样式表的最佳方法是什么? 我不想发送太多的自编译库,也不想通
鉴于以下 XAML... 我正在寻找一种绑定(bind) ComboBox、Button 和 Command 的方法,以便当 ComboBox 的值更改时,在 Command 上调用 CanExe
在玩具应用程序中,我有一个显示所有帖子标题的“帖子”模板。当您单击每个标题时,我不想直接进入“显示” View ,而是直接内联展开该帖子的其余内容。 我考虑过让 postRoute 重用 postsR
我需要一些使用 Twitter Bootstrap 或其他响应式框架的自定义 Swagger-UI 实现。需要在我的移动设备上使用这样的 UI 测试我的 API,但 swagger-ui 不能很好地扩
我正在做一个项目,我真的在尝试编写面向对象的 JavaScript 代码。我刚刚开始阅读Douglas Crockford's JavaScript: The Good Parts我很快开始意识到用
在 C# 中,我通过执行以下操作来加密文本数据(请注意我正在以 block ( block )的形式加密数据): public string EncryptData(string pu
我正在构建一个社交网站,该网站将向全世界公开 REST API (WCF WebAPI),以便任何开发人员都能够为该网站创建客户端应用程序、将其与其他服务集成等。 我想为 API 实现 Faceboo
我是一名优秀的程序员,十分优秀!