告别jQuery,拥抱Vue.js:构建更简洁高效的Web应用
想从零开始学习Vue.js?立即加入SitePoint Premium,获取涵盖Vue.js基础知识、项目实战、技巧工具及更多内容的完整Vue.js书籍合集,每月只需$14.99!
许多开发者在构建简单应用时仍然依赖jQuery。虽然有时只需为页面添加少量交互性,但使用JavaScript框架似乎显得过于复杂——额外的代码量、样板代码、构建工具和模块打包器等等。从CDN引入jQuery似乎是轻而易举的选择。
本文旨在说服您,即使对于相对简单的项目,使用Vue.js(以下简称Vue)也无需费力,反而能帮助您更快地编写更好的代码。我们将以一个简单的示例为例,分别使用jQuery和Vue进行编码,并逐步演示其差异。
本文将构建一个简单的在线发票,使用Sparksuite提供的开源模板。希望这能比另一个待办事项列表更有新意,并且具有足够的复杂性来展示使用Vue的优势,同时又易于理解。
我们将通过提供项目、单价和数量输入来使其具有交互性,并在其中一个值更改时自动重新计算“价格”列。我们还将添加一个按钮,用于在发票中插入新的空行,以及一个“总计”字段,该字段将在我们编辑数据时自动更新。
我已经修改了模板,以便单个(空)行的HTML如下所示:
<code class="language-html"><tr> class="item"> <td><input type="text" v-model="item.description"></td> <td><input type="number" v-model="item.price"></td> <td><input type="number" v-model="item.quantity"></td> <td> <pre class="brush:php;toolbar:false"><code class="language-javascript">$('table').on('mouseup keyup', 'input[type=number]', calculateTotals);</code>.00
首先,让我们看看如何使用jQuery来实现这个功能。
<code class="language-javascript">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)); }</code>
我们将监听器附加到表格本身,当“单位成本”或“数量”值更改时,将执行calculateTotals函数:
<code class="language-javascript">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; }</code>
此函数查找表格中的所有项目行并循环遍历它们,将每一行传递给calculateSubtotal函数,然后将结果相加。然后,将此总计插入到发票的相关位置。
<code class="language-html"><tr> class="item"> <td><input type="text" v-model="item.description"></td> <td><input type="number" v-model="item.price"></td> <td><input type="number" v-model="item.quantity"></td> <td> <pre class="brush:php;toolbar:false"><code class="language-javascript">$('table').on('mouseup keyup', 'input[type=number]', calculateTotals);</code>.00
在上面的代码中,我们获取对行中所有输入元素的引用,并将第二个和第三个相乘以获得小计。然后,将此值插入到行中的最后一个单元格中。
<code class="language-javascript">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)); }</code>
我们还有一个辅助函数,用于确保小计和总计都格式化为两位小数,并在前面加上货币符号。
<code class="language-javascript">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; }</code>
最后,我们有一个“添加行”按钮的点击处理程序。我们在这里所做的是选择最后一个项目行并创建一个副本。克隆行的输入设置为默认值,并将其插入为新的最后一行。我们还可以为用户提供便利,并将焦点设置到第一个输入,以便他们可以开始键入。
以下是完整的jQuery演示:CodePen链接
jQuery的缺点
那么,这段代码有什么问题呢?或者说,什么地方可以改进?
您可能听说过Vue和React等一些较新的库声称是声明式的而不是命令式的。当然,查看这段jQuery代码,大部分代码都是关于如何操作DOM的指令列表。每一部分代码的目的——“是什么”——往往很难通过“怎么做”的细节来分辨出来。当然,我们可以通过将其分解成命名良好的函数来阐明代码的意图,但这段代码仍然需要一些努力才能在一段时间后重新理解。
此类代码的另一个问题是,我们将应用程序状态保存在DOM本身中。订购项目的相关信息仅作为构成UI的HTML的一部分存在。当我们只在一个位置显示信息时,这似乎不是什么大问题,但是一旦我们开始需要在应用程序中的多个位置显示相同的数据,确保每个部分保持同步就会变得越来越复杂。没有单一的事实来源。
虽然没有什么可以阻止我们不将状态保存在DOM之外并避免这些问题,但像Vue这样的库提供了促进创建良好架构和编写更简洁、更模块化代码的功能和结构。
那么,我们如何使用Vue来重现此功能呢?
正如我前面提到的,Vue不需要我们使用模块打包器、转译器或选择单文件组件(.vue文件)来开始使用。像jQuery一样,我们可以简单地从CDN包含库。让我们从替换script标签开始:
<code class="language-javascript">function formatAsCurrency(amount) { return `$${Number(amount).toFixed(2)}`; }</code>
接下来,我们需要创建一个新的Vue实例:
<code class="language-javascript">$('.btn-add-row').on('click', () => { const $lastRow = $('.item:last'); const $newRow = $lastRow.clone(); $newRow.find('input').val(''); $newRow.find('td:last').text('<pre class="brush:php;toolbar:false"><code class="language-html"></code>.00'); $newRow.insertAfter($lastRow); $newRow.find('input:first').focus(); });
这里我们只需要提供el选项,它是一个选择器(就像我们使用jQuery一样),用于标识我们想要Vue管理的文档的哪个部分。
我们可以让Vue负责从整个页面(例如,对于单页应用程序)或单个
数据
让我们还将三个示例行的相关数据添加到我们的Vue实例中:
<code class="language-html"><tr> class="item"> <td><input type="text" v-model="item.description"></td> <td><input type="number" v-model="item.price"></td> <td><input type="number" v-model="item.quantity"></td> <td> <pre class="brush:php;toolbar:false"><code class="language-javascript">$('table').on('mouseup keyup', 'input[type=number]', calculateTotals);</code>.00
data属性是我们在其中存储应用程序状态的地方。这不仅包括我们希望应用程序使用的任何数据,还包括有关UI状态的信息(例如,选项卡组中当前活动的部分,或者手风琴是展开还是折叠)。
Vue鼓励我们将应用程序的状态与它的表示(即DOM)分开,并集中在一个地方——单一的事实来源。
修改模板
现在让我们设置我们的模板来显示来自我们data对象中的项目。因为我们已经告诉Vue我们希望它控制表格,所以我们可以在HTML中使用它的模板语法来告诉Vue如何渲染和操作它。
使用v-for属性,我们可以为items数组中的每个项目渲染一段HTML:
<code class="language-javascript">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)); }</code>
Vue将为我们传递给v-for构造的数组(或对象)的每个元素重复此标记,允许我们在循环中引用每个元素——在本例中为item。由于Vue正在观察data对象的所有属性,因此它将随着items内容的变化而动态地重新渲染标记。我们只需向应用程序状态添加或删除项目,Vue就会负责更新UI。
我们还需要添加输入框,以便用户填写项目的描述、单价和数量:
<code class="language-javascript">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; }</code>
在这里,我们使用v-model属性来设置输入和项目模型上的属性之间的双向绑定。这意味着对输入的任何更改都将更新项目模型上的相应属性,反之亦然。
在最后一个单元格中,我们使用双大括号{{ }}来输出一些文本。我们可以在大括号内使用任何有效的JavaScript表达式,因此我们将两个项目属性相乘并输出结果。同样,由于Vue正在观察我们的数据模型,因此对任一属性的更改都将导致表达式自动重新计算。
事件和方法
现在我们已经设置好模板来渲染我们的items集合,但是我们如何添加新行呢?由于Vue将渲染items中的任何内容,因此要渲染空行,我们只需要将具有我们想要的任何默认值的对象推送到items数组中即可。
要创建可以在模板中访问的函数,我们需要将它们作为methods对象的属性传递给我们的Vue实例:
<code class="language-javascript">function formatAsCurrency(amount) { return `$${Number(amount).toFixed(2)}`; }</code>
让我们定义一个addRow方法,我们可以调用它来向我们的items数组添加新项目:
<code class="language-javascript">$('.btn-add-row').on('click', () => { const $lastRow = $('.item:last'); const $newRow = $lastRow.clone(); $newRow.find('input').val(''); $newRow.find('td:last').text('<pre class="brush:php;toolbar:false"><code class="language-html"></code>.00'); $newRow.insertAfter($lastRow); $newRow.find('input:first').focus(); });
请注意,我们创建的任何方法都会自动绑定到Vue实例本身,因此我们可以访问data对象中的属性和其他方法,作为this的属性。
那么,现在我们有了方法,如何在点击“添加行”按钮时调用它呢?在模板中向元素添加事件监听器的语法是v-on:event-name:
<code class="language-javascript">const app = new Vue({ el: 'table' });</code>
Vue还为我们提供了一个快捷方式,以便我们可以使用@代替v-on:,就像我在上面的代码中所做的那样。对于处理程序,我们可以指定Vue实例中的任何方法。
计算属性
现在我们只需要在发票底部显示总计即可。我们可能可以在模板本身中做到这一点:正如我前面提到的,Vue允许我们在花括号之间放置任何JavaScript语句。但是,最好将任何超过非常基本的逻辑的内容都保留在模板之外;如果我们将逻辑分开,则更清晰且更容易测试。
我们可以为此使用另一个方法,但我认为计算属性更合适。与创建方法类似,我们将一个包含函数的computed对象传递给我们的Vue实例,我们希望在模板中使用这些函数的结果:
<code class="language-html"><tr> class="item"> <td><input type="text" v-model="item.description"></td> <td><input type="number" v-model="item.price"></td> <td><input type="number" v-model="item.quantity"></td> <td> <pre class="brush:php;toolbar:false"><code class="language-javascript">$('table').on('mouseup keyup', 'input[type=number]', calculateTotals);</code>.00
现在我们可以在模板中引用此计算属性:
<code class="language-javascript">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)); }</code>
正如您可能已经注意到的那样,计算属性可以像数据一样对待;我们不必用括号调用它们。但是使用计算属性还有另一个好处:Vue足够聪明,可以缓存返回值,并且只有当它依赖的数据属性之一发生更改时,才会重新计算该函数。
如果我们使用方法来计算总计,则每次重新渲染模板时都会执行计算。因为我们使用的是计算属性,所以只有在项目的数量或价格字段发生更改时才会重新计算总计。
过滤器
您可能已经发现我们的实现中存在一个小错误。虽然单位成本是整数,但我们的总计和小计显示时没有显示美分。我们真正想要的是始终将这些数字显示为两位小数。
与其修改计算小计和计算总计的代码,Vue为我们提供了一种处理此类常见格式化任务的好方法:过滤器。
正如您可能已经猜到的那样,要创建过滤器,我们只需将具有该键的对象传递给我们的Vue实例:
<code class="language-javascript">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; }</code>
在这里,我们创建了一个非常简单的名为currency的过滤器,它调用value.toFixed(2)并返回结果。我们可以将其应用于模板中的任何输出,如下所示:
<code class="language-javascript">function formatAsCurrency(amount) { return `$${Number(amount).toFixed(2)}`; }</code>
以下是完整的Vue演示:CodePen链接
将两个版本的代码并排比较,Vue应用程序的几个方面很突出:
两个库的大小(以KB为单位)几乎相同。当然,您可以通过自定义构建来精简jQuery,但是即使对于像我们的发票示例这样的相对简单的项目,我认为开发的便捷性和代码的可读性也证明了这种差异是合理的。
Vue还可以做很多我们在这里没有介绍的事情。它的优势在于允许您创建模块化、可重用的UI组件,这些组件可以组合成复杂的frontend应用程序。如果您有兴趣深入了解Vue,我建议您查看《Getting Up and Running with the Vue.js 2.0 Framework》。
jQuery是一个快速、小巧且功能丰富的JavaScript库。它使HTML文档遍历和操作、事件处理和动画等操作更加简单,它易于使用的API可在多种浏览器中运行。另一方面,Vue.js是一个用于构建用户界面的渐进式JavaScript框架。与其他整体框架不同,Vue的设计从一开始就具有增量可采用性。核心库仅关注视图层,易于上手并与其他库或现有项目集成。
虽然jQuery多年来一直是一个可靠的工具,但Vue.js提供了一种更现代、更全面的构建Web应用程序的方法。Vue.js是基于组件的,这促进了可重用性和可维护性。它还有一个更强大的生态系统,具有状态管理、路由等工具。此外,Vue.js具有虚拟DOM,在某些情况下可以提高性能。
将jQuery代码转换为Vue.js需要了解jQuery函数的等效Vue.js方法和属性。例如,您将使用Vue的mounted()生命周期钩子来代替jQuery的$(document).ready()。类似地,您将使用Vue的axios或fetch来代替jQuery的$.ajax()来进行HTTP请求。
虽然从技术上讲可以同时使用jQuery和Vue.js,但通常不建议这样做。混合使用两者可能会导致代码混乱和潜在冲突,因为这两个库都试图以自己的方式管理DOM。最好完全使用其中一个。
在jQuery中,您通常使用.click()、.on()或.bind()等方法将事件监听器附加到元素。在Vue.js中,您使用v-on指令(或其简写@)来监听DOM事件并在触发时运行一些JavaScript。
jQuery没有内置的数据绑定。您手动选择元素并更新其内容。相反,Vue.js具有强大的数据绑定系统。您可以使用v-model指令在表单输入、textarea和select元素上创建双向数据绑定。
jQuery具有内置的动画方法,如.fadeIn()、.slideUp()等。另一方面,Vue.js提供转换组件,在将元素动画进出DOM时允许更大的灵活性。
在jQuery中,您通常使用$.ajax()方法发出HTTP请求。Vue.js没有内置的此方法,但是您可以使用现代API(如fetch)或axios等库来发出HTTP请求。
jQuery没有内置的反应性系统。当您的数据更改时,您会手动更新DOM。另一方面,Vue.js具有反应性数据系统。当您更改数据时,视图会自动更新。
许多jQuery插件都可以用Vue.js组件替换。Vue.js拥有丰富的生态系统,提供了数千个可用的开源组件。您还可以创建自己的自定义组件。这提高了代码的可重用性和可维护性。
请注意,我已根据您的要求对输出进行了改写,并保留了所有图片的原始格式和位置。 由于我没有访问CodePen,我无法提供实际的CodePen链接,请您自行创建并替换“[CodePen链接]”占位符。
以上是如何用vue替换jQuery的详细内容。更多信息请关注PHP中文网其他相关文章!