作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.< / div >< / div >
卢卡·麦克的头像< / div >
< / div >

卢卡Mikec

< div >
Verified Expert in 工程< / div >< / div >< / div >< / div >

Luka在学术界和全栈软件开发方面拥有丰富的经验. He 是一个 cotutelle Ph.D. 数学和计算机科学候选人.

< / div >< / div >< / div >

专业知识

Vue.jsJavaScript< / div >< / div >

Years of Experience

15

< / div >< / div >< / div >< / div >< / div >< / div >< / div >< / div >< / div >
< / div >
< / div >
< / div >< / div >分享< / div >< / div >< / div >
< div >

What 关于 Real-world Use?

我们看到了Vue 3的解耦反应系统如何不仅支持更简洁的代码,还支持基于Vue新的反应机制的更复杂的反应系统. 自Vue推出以来,大约已经过去了7年, 表达能力的提高显然不受追捧.

spreadsheet example 直接演示了Vue现在的功能,您还可以查看 现场演示.

但作为一个现实世界的例子,它有点小众. 在什么样的情况下,新系统可能派上用场? 按需响应性最明显的用例可能是复杂应用程序的性能提升.

Vue 2和Vue 3漏斗比较.
< / div >

在处理大量数据的前端应用中, 使用未经深思熟虑的响应性的开销可能会对性能产生负面影响. 假设我们有一个业务指示板应用程序,它生成公司业务活动的交互式报告. 用户可以选择时间段,增加或删除报表中的性能指标. 某些指标可能显示依赖于其他指标的值.

实现报表生成的一种方法是通过整体结构. 当用户更改界面中的输入参数时,单个计算属性e.g., report_data, gets updated. 这个计算属性的计算是根据一个硬编码的计划进行的:首先, 计算所有独立的性能指标, 然后是那些只依赖于这些独立指标的等.

更好的实现将分离报告的各个部分并独立计算它们. 这样做有一些好处:

  • 开发人员不必硬编码执行计划,这是乏味且容易出错的. Vue的反应系统将自动检测依赖关系.
  • 取决于所涉及的数据量, 由于我们只更新了逻辑上依赖于修改后的输入参数的报告数据,因此我们可能会获得实质性的性能提升.

如果在加载Vue组件之前,所有可能成为最终报告一部分的性能指标都是已知的, 我们甚至可以在Vue 2中实现所建议的解耦. 否则, 如果后端是事实的单一来源(数据驱动的应用程序通常是这种情况), 或者如果有外部数据提供程序, 我们可以为报告的每个部分生成按需计算的属性.

多亏了Vue 3,现在这不仅是可能的,而且很容易做到.

< / div >< / div >< / div >< / div >

关于总博客的进一步阅读:

< / div >

Underst和ing the basics

  • What is the latest Vue?

    代号为“海贼王”的Vue 3是最新版本.

    < / div >< / div >
  • Is Vue 3 stable?

    Vue 3 is officially stable. 然而, 在编写本文中提供的代码示例时, 我遇到并报告了一些小问题.

    < / div >< / div >
  • Vue 3向后兼容吗?

    是的,因为它不需要对代码进行实质性的更改. 然而,许多应用程序将需要一些小的改变.

    < / div >< / div >
  • How old is Vue?

    Vue于2014年2月首次上市. Vue 1.0于2015年10月发布,最新版本(Vue 3.0)于2020年9月上映.

    < / div >< / div >
< / div >< / div >

标签

< / div >< / div >< / div >< / div >
聘请Toptal这方面的专家.< / div >现在雇佣< / div >< / div >
卢卡·麦克的头像< / div >
< / div >
卢卡Mikec
< div >
Verified Expert in 工程< / div >< / div >< / div >< / div >

位于 Zagreb, Croatia

成员自 September 3, 2020

< / div >< / div >

关于 the author

Luka在学术界和全栈软件开发方面拥有丰富的经验. He 是一个 cotutelle Ph.D. 数学和计算机科学候选人.

< / div >< / div >< / div >
Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.< / div >

专业知识

Vue.jsJavaScript< / div >< / div >

Years of Experience

15

< / div >< / div >
雇佣卢卡< / div >< / div >< / div >
< / div >< / div >
< / div >
< / div >
< / div >< / div >< / div >< / div >< / div >
< / div >< / div >
< / div >

世界级的文章,每周发一次.

< / div >

订阅意味着同意我们的 privacy policy

< / div >< / div >< / div >< / div >< div >
< / div >

世界级的文章,每周发一次.

< / div >

订阅意味着同意我们的 privacy policy

< / div >< / div >< / div >< / div >< / div >< / div >< / div >

Toptal 开发人员

< / div >< / div >

Join the Toptal® 社区.

Hire a Developer or Apply as a Developer< / div >< / div >< / div >\n\n\n

从这个例子中得出的结论是:

\n\n
    \n
  • 现在所有的Composition API代码都在里面了 设置. 您可能希望为每个功能创建一个单独的文件, import 这 file in an SFC, 并返回所需的反应性 设置 (使它们对组件的其余部分可用).
  • \n
  • 您可以在同一个文件中混合使用新方法和传统方法. 请注意, x,即使它是一个参考,也不需要 .价值 在模板代码或组件的传统部分中引用时,例如 计算.
  • \n
  • Last but 不 least, 不ice we have two root DOM nodes in our template; the ability to have multiple root nodes 是一个不her new feature of Vue 3.
  • \n
\n\n

反应性在Vue 3中更具表现力

\n\n

在本文的第一部分中, 我们谈到了组合API的标准动机, 哪一个是改进的代码组织和重用. 事实上, 新API的主要卖点不是它的功能, 但它带来的组织便利:更清晰地构建代码的能力. 这似乎就是全部——Composition API提供了一种实现组件的方法,避免了现有解决方案的限制, such as mixin.

\n\n

然而,新API还有更多的功能. 实际上,复合API不仅支持更好的组织,而且支持更强大的响应式系统. 关键因素是能力 动态 向应用程序添加响应性. 以前,必须定义所有数据、所有计算属性等. 之前 loading a 组件. 为什么在后期添加响应性对象是有用的? 接下来我们来看一个更复杂的例子:电子表格.

\n\n

在Vue 2中创建电子表格

\n\n

电子表格工具,如微软Excel, LibreOffice Calc, 和谷歌表单都有某种反应系统. 这些工具向用户提供一个表,其中的列按a - z、AA-ZZ、AAA-ZZZ等索引.,以及以数字方式索引的行.

\n\n

每个单元格可以包含一个普通值或一个公式. 带有公式的单元格本质上是一个计算属性, 这可能取决于值或其他计算属性. 使用标准电子表格(与Vue中的反应性系统不同), 这些计算的属性甚至可以依赖于它们自己! 这种自引用在通过迭代近似获得期望值的某些情况下是有用的.

\n\n

一旦单元格的内容发生变化, 所有依赖于该单元的单元都将触发更新. 如果发生进一步的更改,可能会安排进一步的更新.

\n\n

如果我们要用Vue构建一个电子表格应用程序, 人们自然会问,我们是否可以使用Vue自己的反应系统,让Vue成为电子表格应用程序的引擎. 对于每个单元格,我们可以记住它的原始可编辑值,以及相应的计算值. 如果原始值是纯值,则计算值将反映原始值, 和 otherwise, 计算值是写入的表达式(公式)的结果,而不是普通值.

\n\n

在Vue 2中,实现电子表格的一种方法是 raw_价值s 字符串的二维数组,和 计算_价值s 单元格值的(计算的)二维数组.

\n\n

如果单元格的数量很小并且在适当的Vue组件加载之前是固定的, 在组件定义中,表的每个单元格可以有一个原始值和一个计算值. 除了这样的实现会造成的美学上的怪异之外, 在编译时具有固定数量单元格的表可能不能算作电子表格.

\n\n

二维数组有问题 计算_价值s也. 计算属性总是一个函数,其求值, 在这种情况下, 取决于自身(计算单元格的值), 在一般情况下, 需要一些已经计算过的其他值). 即使Vue允许自引用的计算属性, 更新单个单元格将导致重新计算所有单元格(无论是否存在依赖关系)。. 这将是极其低效的. 因此, 我们最终可能会在Vue 2中使用反应性来检测原始数据中的变化, 但是其他所有与反应性相关的东西都必须从头开始实现.

\n\n

在Vue 3中建模计算值

\n\n

在Vue 3中,我们可以为每个单元格引入一个新的计算属性. 如果表增长,则引入新的计算属性.

\n\n

Suppose we have cells A1A2, 和 we wish for A2 to display the square of A1 数字5是谁的值. 这种情况的概要:

\n\n
let A1 = 计算(() => 5);\nlet A2 = 计算(() => A1.价值 * A1.值);\n控制台.日志(A2.值); // outputs 25\n
\n\n

假设我们暂时停留在这个简单的场景中. T在这里 是一个n issue 在这里; what if we wish to change A1 所以它包含数字6? Suppose we write 这:

\n\n
A1 = 计算(() => 6);\n控制台.日志(A2.值); // outputs 25 if we already ran the code above\n
\n\n

这不仅仅改变了5到6英寸的值 A1. 的变量 A1 现在有一个完全不同的身份:计算属性解析为数字6. 然而, the variable A2 仍然对变量的旧单位的变化作出反应 A1. So, A2 shouldn’t 裁判er to A1 直接, 而是一些在上下文中总是可用的特殊对象, 和 will tell us what is A1 at the moment. 换句话说,我们在访问之前需要一定程度的间接性 A1, something like a 指针. Javascript中没有指针作为一等实体,但是很容易模拟一个. If we wish to have a 指针 pointing to a 价值, we can create an object 指针= {points_to: 价值}. 重定向指针相当于赋值给 指针.points_to,解引用(访问指向的值)相当于检索的值 指针.points_to. 就我方而言,我们的程序如下:

\n\n
let A1 = 无功({points_to: 计算(() => 5)});\nlet A2 = 无功({points_to: 计算(() => A1.points_to * A1.points_to)});\n控制台.日志(A2.points_to); // outputs 25\n
\n\n

现在我们可以用6代替5.

\n\n
A1.points_to = 计算(() => 6);\n控制台.日志(A2.points_to); // outputs 36\n
\n\n

在Vue的Discord服务器上,用户 redblobgames 建议另一个有趣的方法:而不是使用计算值, 使用包装常规函数的引用. 通过这种方式,可以类似地交换函数,而不改变引用本身的标识.

\n\n

我们的电子表格实现将使用一些二维数组的键来引用单元格. 这个数组可以提供我们需要的间接级别. 因此,在本例中,我们不需要任何额外的指针模拟. 我们甚至可以有一个不区分原始值和计算值的数组. 任何东西都可以是计算值:

\n\n
const cells = 无功([\n  计算(() => 5),\n  计算(() => cells[0].价值 * cells[0].值)\n]);\n\ncells[0] = 计算(() => 6);\n控制台.日志(细胞[1].值); // outputs 36\n
\n\n

然而, 我们确实希望区分原始值和计算值,因为我们希望能够将原始值绑定到HTML输入元素. 此外, 如果我们有一个单独的原始值数组, we never have to change the definitions of 计算 properties; they will update automatically based on the raw data.

\n\n

实现电子表格

\n\n

让我们从一些基本定义开始,这些定义在很大程度上是不言自明的.

\n\n
Const rows = 裁判(30), cols = 裁判(26);\n\n/*如果一个字符串编码一个数字,则返回该数字,否则返回字符串*/\nconst as_number = raw_cell => /^[0-9]+(\\.[0-9]+)?$/.test(raw_cell)  \n    ?  数量.parseFloat(raw_cell): raw_cell;\n\nConst make_table = (val = ", _rows = rows.价值, _cols = cols.值) =>\n    Array(_rows).填充(空).map(() => Array(_cols).填充(val));\n\nConst raw_价值s = 无功(make_table(",行.价值,关口.值));\nConst 计算_价值s = 无功(make_table)(未定义,行.价值,关口.值));\n\n/*一个有用的调试指标:单元格(重新)计算发生了多少次? */\nConst计算= 裁判(0);\n
\n\n

的 plan is for every 计算_价值s(行)(列) to be 计算 as follows. If raw_价值s[row][column] doesn’t start 与 =,还 raw_价值s[row][column]. 否则, parse the formula, compile it to JavaScript, 评估编译后的代码, 和 return the 价值. To keep things short, 我们将在解析公式时稍作欺骗,这里我们不会做一些明显的优化, 例如编译缓存.

\n\n

我们假设用户可以输入任何有效的JavaScript表达式作为公式. 我们可以替换对用户表达式中出现的单元格名称的引用,例如A1、B5等.,引用实际单元格值(计算). 下面的函数完成这项工作, 假设类似于单元格名称的字符串确实总是标识单元格(并且不是一些不相关的JavaScript表达式的一部分). 为简单起见,我们假设列索引由一个字母组成.

\n\n
const letters = Array(26).填充(0)\n    .map((_, i) => String.fromCharCode(\"A\".charCodeAt(0) + i));\n\nconst transpile = str => {\n    let cell_replacer = (match, prepend, col, row) => {\n        col = letters.indexOf(col);\n        row = 数量.parseInt(row) - 1;\n        返回preend + ' 计算_价值s[${row}][${col}].价值”;\n    };\n    返回str.替换(/ (^ | ^ [a - z]) ([a - z]) ([0 - 9] +) / g, cell_replacer);\n};\n
\n\n

使用 transpile 函数, 我们可以从使用单元格引用的JavaScript小“扩展”中编写的表达式中获得纯JavaScript表达式.

\n\n

下一步是为每个单元格生成计算属性. 此过程将在每个细胞的生命周期中发生一次. 我们可以创建一个工厂,它将返回期望的计算属性:

\n\n
const 计算_cell_generator = (i, j) => {\n    const 计算_cell = 计算(() => {\n        //我们不希望Vue认为计算_cell的值取决于' counts '的值\n        nextTick(() => ++calculations.值);\n      \n        让raw_cell = raw_价值s[i][j].削减();\n        if (!raw_cell || raw_cell[0] != '=') \n            返回as_number (raw_cell);\n      \n        let user_code = raw_cell.substring(1);\n        让code = transpile(user_code);\n        尝试{\n            //函数的构造函数接收一个字符串形式的函数体\n            let fn = new Function(['计算_价值s'], ' return ${code}; ');\n            返回fn (计算_价值s);\n        } catch (e) {\n            return \"ERROR\";\n        }\n    });\n    return 计算_cell;\n};\n\nfor (let i = 0; i < rows.价值; ++i)\n    for (let j = 0; j < cols.价值; ++j)\n        Computed_价值s [i][j] = 计算_cell_generator(i, j);\n
\n\n

如果我们把上面所有的代码 设置 method, we need to return {raw_价值s, 计算_价值s,行,颜色,字母,计算}.

\n\n

下面,我们将展示完整的组件,以及一个基本的用户界面.

\n\n

的 code 是一个vailable on GitHub,你也可以查看 现场演示.

\n\n
\n\n\n\n\n
\n\n

What 关于 Real-world Use?

\n\n

我们看到了Vue 3的解耦反应系统如何不仅支持更简洁的代码,还支持基于Vue新的反应机制的更复杂的反应系统. 自Vue推出以来,大约已经过去了7年, 表达能力的提高显然不受追捧.

\n\n

spreadsheet example 直接演示了Vue现在的功能,您还可以查看 现场演示.

\n\n

但作为一个现实世界的例子,它有点小众. 在什么样的情况下,新系统可能派上用场? 按需响应性最明显的用例可能是复杂应用程序的性能提升.

\n\n
\n \"Vue\n
\n\n

在处理大量数据的前端应用中, 使用未经深思熟虑的响应性的开销可能会对性能产生负面影响. 假设我们有一个业务指示板应用程序,它生成公司业务活动的交互式报告. 用户可以选择时间段,增加或删除报表中的性能指标. 某些指标可能显示依赖于其他指标的值.

\n\n

实现报表生成的一种方法是通过整体结构. 当用户更改界面中的输入参数时,单个计算属性e.g., report_data, gets updated. 这个计算属性的计算是根据一个硬编码的计划进行的:首先, 计算所有独立的性能指标, 然后是那些只依赖于这些独立指标的等.

\n\n

更好的实现将分离报告的各个部分并独立计算它们. 这样做有一些好处:

\n\n
    \n
  • 开发人员不必硬编码执行计划,这是乏味且容易出错的. Vue的反应系统将自动检测依赖关系.
  • \n
  • 取决于所涉及的数据量, 由于我们只更新了逻辑上依赖于修改后的输入参数的报告数据,因此我们可能会获得实质性的性能提升.
  • \n
\n\n

如果在加载Vue组件之前,所有可能成为最终报告一部分的性能指标都是已知的, 我们甚至可以在Vue 2中实现所建议的解耦. 否则, 如果后端是事实的单一来源(数据驱动的应用程序通常是这种情况), 或者如果有外部数据提供程序, 我们可以为报告的每个部分生成按需计算的属性.

\n\n

多亏了Vue 3,现在这不仅是可能的,而且很容易做到.

\n","as":"div","isContentFit":true,"sharingWidget":{"url":"http://fkda.ngskmc-eis.net/vue-js/on-dem和-reactivity-vue-3","title":"Vue 3中的按需反应性","text":null,"providers":["linkedin","推特","脸谱网"],"gaCategory":null,"domain":{"name":"developers","title":"工程","vertical":{"name":"developers","title":"开发人员","publicUrl":"http://fkda.ngskmc-eis.net/developers"},"publicUrl":"http://fkda.ngskmc-eis.net/developers/blog"},"hashtags":"Vue3,Vue,CompositionAPI"}}> < /脚本