响应式的 switchboard:让又大又慢的Vue/AIpine 页面爆快

这篇具有很好参考价值的文章主要介绍了响应式的 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文章来源地址https://www.toymoban.com/news/detail-429939.html


到了这里,关于响应式的 switchboard:让又大又慢的Vue/AIpine 页面爆快的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 第45讲:Python生成式的概念以及列表生成式的应用案例

    Python中针对列表、元组、集合、字典等可迭代对象提供了一种快速创建序列的简介语法。这个简介的语法就是生成式,也叫作推导式。 生成式一般由:生成序列中元素的表达式+for循环+if条件组成。 生成式主要是利用循环遍历一个可迭代对象,然后根据指定的生成元素表达式

    2024年02月06日
    浏览(45)
  • 矩阵的顺序主子式的理解

    矩阵的顺序主子式(principal minors)是矩阵的特定类型子矩阵,它们可以帮助我们分析矩阵的性质,如正定性和特征值。顺序主子式是通过从矩阵的主对角线上选取连续的行和列,然后构成子矩阵来定义的。这些子矩阵的行数和列数相同,且与原矩阵的行数和列数有关。它们通

    2024年02月05日
    浏览(21)
  • 15.一种坍缩式的简单——组合模式详解

    当曾经的孩子们慢慢步入社会才知道,那年味渐淡的春节就像是疾驰在人生路上的暂停键。 它允许你在隆隆的鞭炮声中静下心来,瞻前顾后,怅然若失。 也允许你在寂静的街道上屏气凝神,倾听自己胸腔里的那团人声鼎沸。 孩子们会明白的,就像他们步入大学校园时候渐渐

    2024年02月21日
    浏览(37)
  • 线性代数|余子式和代数余子式的性质

    前置知识: 【定义】n阶行列式 行列式的性质 阶梯形行列式的性质 【定义】余子式和代数余子式 引理1 设 D = ∣ a 11 ⋯ a 1 k ⋮ ⋮ 0 a k 1 ⋯ a k k c 11 ⋯ c 1 k b 11 ⋯ b 1 n ⋮ ⋮ ⋮ ⋮ c n 1 ⋯ c n k b n 1 ⋯ b n n ∣ D = begin{vmatrix} a_{11} cdots a_{1k} \\\\ vdots vdots 0 \\\\ a_{k1} cdots a_{kk} \\\\ c

    2024年02月07日
    浏览(46)
  • 一款MAC下集成式的WEB开发利器——ServBay

    ServBay是一个集成式、图形化的本地化Web开发环境。 开发者通过ServBay几分钟就能部署一个本地化的开发环境。解决了Web开发者(比如PHP、Nodejs)、测试工程师、小型团队安装和维护开发测试环境的问题,同时可以快速的进行环境的升级以及维护。 ServBay还将Web服务器,数据库

    2024年03月13日
    浏览(31)
  • 一款强大的浏览器翻译插件 - 沉浸式的翻译

    前一段时间谷歌翻译宣布跑路,不再对大陆用户提供服务。听闻这一噩耗我不由得心里一惊,燕子!啊不是,谷歌没有你我可咋活呀! 对于没太大工作需求,顶多遇上几个不认识单词或需要翻译网页的我来说,Chrome 自带的谷歌翻译可以说是我最常用的翻译工具,这一下就没

    2024年02月06日
    浏览(54)
  • Java 网络编程 —— 实现非阻塞式的客户端

    客户程序一般不需要同时建立与服务器的多个连接,因此用一个线程,按照阻塞模式运行就能满足需求 对于客户与服务器之间的通信,按照它们收发数据的协调程度来区分,可分为同步通信和异步通信 同步通信指甲方向乙方发送了一批数据后,必须等接收到了乙方的响应数

    2024年02月05日
    浏览(48)
  • Java 网络编程 —— 实现非阻塞式的服务器

    当 ServerSocketChannel 与 SockelChannel 采用默认的阻塞模式时,为了同时处理多个客户的连接,必须使用多线程 在非阻塞模式下, EchoServer 只需要启动一个主线程,就能同时处理三件事: 接收客户的连接 接收客户发送的数据 向客户发回响应数据 EchoServer 委托 Selector 来负责监控接

    2024年02月05日
    浏览(55)
  • Spring Boot Maven package时显式的跳过test内容

    在pom.xml的编译插件部分显式的增加一段内容: maven package时可以跳过Spring Boot项目中的各种TEST代码,避免一些打包中的错误。

    2024年02月13日
    浏览(39)
  • 街电入局两轮电动车充电,讲出了“跃迁”式的新故事?

    以共享单车为起点,中国的共享经济正蓬勃发展。国家信息中心披露的数据显示,2022年,中国共享经济市场交易规模约38320亿元,同比增长约3.9%。 虽然同属共享经济,但与部分共享单车企业嫁入“豪门”全身而退不同,目前不少共享充电宝企业似乎困在了“漫长的季节”。

    2024年02月05日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包