从jQuery迁移到Vue.js

本文的作者(我们今天将其翻译发表)认为,世界上还有更多的程序员在需要开发简单的Web应用程序时使用jQuery 。 通常,当某个页面需要具备简单的交互功能时会发生这种情况,但是为此使用某种JavaScript框架似乎有点过头了。 毕竟,这些都是千字节的不必要的代码,模板,用于构建项目的工具,用于包装模块的工具……同时,使用CDN资源连接到jQuery页面就像给梨子装壳一样容易。



本文将讨论如何将使用jQuery创建的项目转换为Vue.js。 该项目将在jQuery上创建,然后使用Vue重新设计。 该材料的作者希望向所有人证明,即使在相对较小的项目中,Vue的使用也不一定意味着此类项目的代码大小会过度增加,并给程序员带来巨大的额外负担。 相反,这与使用jQuery时几乎具有相同的辅助代码大小,从而使您能够提高生产力并提高应用程序质量。

项目概况


我们将基于Sparksuite的开源模板开发一个简单的电子帐户。

通常,此类案例研究使用各种待办事项清单。 我希望背离这种传统将使故事变得新鲜和有趣,同时,我们的任务将非常困难,无法证明使用Vue之类的东西来开发小型项目的好处。 该手册的设计使所有没有特殊困难的人都可以复制并掌握建议的工作方法。

这是我们要使用的帐户模板。


电子发票

我们将向其中添加交互式功能,使其能够选择产品,数量和单价。 更改值时,将重新计算商品总成本和单据总额。 另外,我们将在此处添加一个按钮,使您可以在帐户中插入新的空行。

我通过将空字符串的HTML代码转换为以下形式来修改模板:

<tr class="item">  <td><input value="" /></td>  <td>$<input type="number" value="0" /></td>  <td><input type="number" value="1" /></td>  <td>$0.00</td> </tr> 

jQuery项目开发


首先,让我们看一下如何使用jQuery解决问题。

 $('table').on('mouseup keyup', 'input[type=number]', calculateTotals); 

我们将事件侦听器连接到表。 当与产品的单位成本或其数量对应的值发生更改时,此侦听器将调用calculateTotals函数:

 function calculateTotals()  { const subtotals = $('.item').map((idx, val)  => calculateSubtotal(val)).get(); const total = subtotals.reduce((a, v)  => a + Number(v), 0); $('.total td:eq(1)').text(formatAsCurrency(total)); } 

该函数获取表中的行并遍历它们,将每一行传递给calculateSubtotal函数并汇总结果。 计算结果属于常数。 结果,凭证的总金额将替换到帐户的相应字段中。

 function calculateSubtotal(row) { const $row = $(row); const inputs = $row.find('input'); const subtotal = inputs[1].value * inputs[2].value; $row.find('td:last').text(formatAsCurrency(subtotal)); return subtotal; } 

在此代码中,我们引用了该行中存在的所有<input>字段,然后将存储在第二个和第三个字段中的值相乘以获得subtotal值。 然后,将此值插入到该行的最后一个单元格中。 我们使用formatAsCurrency函数格式化该值。 这是她的代码:

 function formatAsCurrency(amount) { return `$${Number(amount).toFixed(2)}`; } 

此外,我们使用了一个小的辅助功能,以确保字段的外观与货物和整个文档的数量相同。 即,我们需要在这些字段中的数字前面加上$符号,并且它们是带有两个小数位的十进制数字。

 $('.btn-add-row').on('click', () => { const $lastRow = $('.item:last'); const $newRow = $lastRow.clone(); $newRow.find('input').val(''); $newRow.find('td:last').text('$0.00'); $newRow.insertAfter($lastRow); $newRow.find('input:first').focus(); }); 

最后,我们有一个事件处理程序,用于单击“ Add row按钮,该事件处理程序用于向文档中添加新行。 在这里,我们获取表中包含产品数据的最后一行,并对其进行复制。 同时,我们将标准数据插入此新行的字段中。 此外,我们会照顾用户的便利,将注意力集中在新行的第一个字段上,这将使用户在添加新行后立即可以在其第一个字段中输入数据。

这是一个使用jQuery工具实现其交互功能的帐户的工作示例。

基于jQuery的解决方案的缺点


让我们问问自己,上述方法的缺点是什么,或者说,如何改进我们的项目。

您可能听说过Vue和React这样的新库,据说它们允许它们以声明式而不是命令式的方式工作。 如果您看一下上面的jQuery代码,那么很明显,它主要是作为一组描述操作DOM的指令阅读的。 这段代码每一部分的目的,即对它做什么问题的答案,通常很难通过查看其工作方式来弄清楚。 当然,您可以通过将代码分成具有经过深思熟虑选择的名称的函数来使使用的代码片段的意图更清晰。 但是,如果您将这段代码保留一会儿,然后再次返回它,事实证明,为了理解它,您仍然需要付出一些努力。

此代码的另一个问题是应用程序的状态存储在DOM中。 有关用户要订购的产品的信息仅作为用户界面中HTML标记的一部分存在。 如果我们只需要在一个地方输出数据,那么这个问题似乎就不会那么严重了。 但是,一旦有必要在应用程序的多个位置显示此数据,我们就面临着同步数据的任务。 在我们的案例中,如果没有一个可靠的数据源,那么很难在整个应用程序中保持它们的最新状态。

在使用jQuery时,没有什么可以阻止我们将应用程序的状态存储在DOM之外并避免上述问题,Vue之类的库提供了有助于构建具有良好架构的应用程序的功能。 这些库可帮助程序员编写更简洁,更结构化的代码。

Vue上的项目翻译


现在让我们谈谈如何在Vue上重新创建相同的功能。 为了利用Vue的功能,只需将此库连接到常规网页,就像使用jQuery一样。 也就是说,无需使用系统来打包模块或编译器;您无需将应用程序分解为组件并将其代码分解为.vue文件。

我们开始通过替换<script>的内容将项目转换为Vue:

 <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script> 

下一步是创建Vue的新实例:

 const app = new Vue({ el: 'table' }); 

传递给新Vue实例的构造函数的唯一参数是el 。 这是一个选择器(与jQuery中使用的选择器相同),用于标识我们要通过Vue控制的文档部分。

可以为Vue分配各种大小的任务-从管理整个页面(例如,在单页面应用程序的情况下)到包含在<div>的小片段。 在我们的示例中,Vue将负责处理HTML表。

资料


将文档三行的条件数据添加到Vue实例:

 const app = new Vue({ el: 'table', data: {   items: [     { description: 'Website design', quantity: 1, price: 300 },     { description: 'Hosting (3 months)', quantity: 1, price: 75 },     { description: 'Domain name (1 year)', quantity: 1, price: 10 },   ] } }); 

data属性是我们存储应用程序状态的位置。 状态不仅包括应用程序应使用的数据,还包括有关用户界面状态的信息(例如,应用程序中由多个选项卡组成的哪个部分当前处于活动状态,或者某个小部件是否已最小化,例如“手风琴”,或展开)。

Vue帮助将应用程序的状态与其表示形式(即与DOM)分开,并帮助将状态集中存储在一个位置,这是可靠数据的单一来源。

模板修改


现在配置模板,使其显示存储在data对象中的data 。 由于我们告诉框架我们要使用Vue实例处理表,因此我们可以在描述该表的HTML代码中使用Vue模板语法,以告诉Vue如何呈现表以及如何使用它工作。

使用v-for属性,我们可以从items数组中推断出每个元素的HTML代码块:

 <tr class="item" v-for="item in items"> </tr> 

Vue将对传递给v-for的数组(或对象)的每个元素重复此标记。 这使我们可以引用循环中的每个元素。 在这种情况下,此元素由item变量表示。 由于Vue会监视data对象的属性,因此,当items数组的内容更改时,框架将动态更新标记。 我们需要做的就是通过添加或删除元素来修改状态,Vue将自动更新用户界面。

另外,我们需要通过输入产品,单价,数量的描述来添加用户可以填写的输入字段:

 <td><input v-model="item.description" /></td> <td>$<input type="number" v-model="item.price" /></td> <td><input type="number" v-model="item.quantity" /></td> <td>${{ item.price * item.quantity }}</td> 

在这里,我们使用v-model属性来配置输入字段和数据模型中的属性之间的双向数据绑定。 这意味着更改输入字段中的数据将导致其更改模型,反之亦然。

在最后一个单元格中,我们使用花括号{{}} ,以显示文本。 您可以在括号中使用任何有效的JavaScript表达式。 在这种情况下,我们将元素的两个属性相乘并输出发生的情况。 再次,Vue监督数据模型;更改任何属性将自动重新计算表达式。

事件与方法


现在,我们已经准备好一个模板来显示items集合,并且面临着如何向表中添加新行的问题。 由于Vue会将items数组中的所有内容输出到页面,因此为了在页面上显示空字符串,只需将items数组中应该存在的任何值传递给Vue对象就足够了。

为了创建可以从模板访问的函数,您需要将此函数作为methods对象的属性传递给Vue实例:

 const app = new Vue({ // ... methods: {   myMethod() {} }, // ... }) 

addRow方法,可以调用addRow方法将新项目添加到items数组:

 methods: { addRow() {   this.items.push({ description: '', quantity: 1, price: 0 }); }, }, 

请注意,我们创建的任何方法都将自动绑定到Vue实例,这将使我们能够从这些方法访问data对象和其他方法的属性。

现在我们有了一个方法。 单击Add row按钮如何调用它? 要将事件侦听器添加到模板中的控件,Vue使用v-on:event-name语法。

 <button class="btn-add-row" @click="addRow">Add row</button> 

Vue还提供了v-on :构造的快捷方式,它类似于@ 。 在上面的代码片段中使用了它。 您可以使用Vue实例中的任何方法作为事件处理程序。

计算属性


现在,我们只需要在发票底部提取文件的总金额即可。 这可以在模板本身中完成。 如前所述,Vue允许您将所有有效的JS表达式都放在大括号中。 但是,最好坚持只在模板中仅存储非常简单的逻辑而不再存储其他任何内容的方法。 如果逻辑与模板分离,则代码将更简洁,更易于测试。

为此,我们可以使用通常的方法,但是我相信在这种情况下,所谓的计算属性最适合我们。 使用此类属性类似于上述使用方法的工作。 也就是说,要创建此类属性, computed对象传递给Vue computed ,该computed包含我们要在模板中使用其执行结果的函数:

 const app = new Vue({ // ... computed: {   total() {     return this.items.reduce((acc, item) => acc + (item.price * item.quantity), 0);   } } }); 

现在可以从模板引用计算的属性:

 <tr class="total"> <td colspan="3"></td> <td>Total: ${{ total }}</td> 

您可能已经注意到,模板中的计算属性就像数据一样工作。 在这种情况下,您无需“调用”模板中的任何内容。 使用计算的属性还有另一个优点。 Vue是一个相当智能的系统,它缓存返回的值并仅在更改了所计算的属性的值所依赖的属性时才对它们进行重新计数。

如果我们使用该方法来计算文档的总金额,则每次输出模板时都会执行计算。 但是,由于我们使用计算所得的属性,因此仅在更改表行中的quantityprice值时才重新计算总额。

筛选器


您可能已经注意到,使用Vue创建的项目的实施中存在一个小错误。 它包含以下事实:当单个商品的价格用整数表示时,行中总额的计算值和单据中总额的计算值将显示为整数美元值,不含美分。 我们希望像jQuery示例一样,这些数字始终包含两个小数位。

我们可以利用Vue便利的数据格式化功能,而无需修改负责计算行总价值的代码以及计算文档总金额的代码。 关于过滤器。

如您可能已经了解的那样,为了创建过滤器,只需将具有相应键的对象(在这种情况下为filters )传递给Vue实例即可:

 const app = new Vue({ // ... filters: {   currency(value) {     return value.toFixed(2);   } } }); 

在这里,我们创建了一个非常简单的名为currency过滤器。 它为传递给它的数值调用toFixed(2)函数,并返回结果。 您可以在模板中使用此过滤器,如下所示:

 <td>Total: ${{ total | currency }}</td> 

这是我们在Vue上的项目完成实施。

总结


如果比较项目的两个版本,一个使用jQuery创建,另一个使用Vue编写,则您会注意到基于Vue的应用程序的以下优点:

  • 用户界面,逻辑和控制界面的数据之间的清晰分隔。 代码更清晰,测试起来更容易。
  • 接口的说明性描述。 程序员只需要关心如何描述他想在屏幕上看到的内容,而不必关心如何访问DOM才能使应用程序页面具有所需的外观。

Vue和jQuery库的大小几乎相同(以千字节为单位)。 当然,可以通过使用我们自己的库程序集来减小jQuery的大小,但是即使在相对简单的项目中(例如我们的电子账户示例),我相信易于开发和代码易读性也证明应用程序大小略有增加是合理的。

此外,Vue的功能比我们在此描述的功能要广泛得多。 该框架的优势在于它允许您创建模块化的,可重复使用的用户界面组件,从中可以构建复杂的客户端应用程序。

如果您对在Vue上开发Web应用程序感兴趣,请阅读此资料 。 此外,如果您正在考虑将项目从纯JS迁移到Vue,这是我们最近发布的有关此主题的出版物

亲爱的读者们! 您使用jQuery吗? 而且,如果您使用它,是否打算将该库更改为其他内容,例如Vue?

Source: https://habr.com/ru/post/zh-CN414073/


All Articles