vue1

Adding vue

总的来说,前端框架的目标是解决前端网页开发中的以下问题:

  • 开发周期长
  • 修复 Bug 和更新困难
  • 页面渲染速度慢

在本课中,我们将介绍 Vue 所具备的一些功能,这些功能正是用来解决上述问题的。本课将带你初步体验在 Vue 中进行编程的感觉。所有讲解的内容在后续课程中都会更深入讲解,所以如果你现在觉得还没完全理解,不用担心!

要开始使用任何前端框架,第一步就是将该框架添加到你的项目中。你可以通过在项目的 HTML 文件的 <head> 标签中添加一段 <script> 脚本来引入 Vue:

js 复制代码
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js" defer></script>

上面的代码是将当前版本的 Vue(由 Vue 团队托管)直接加载到你的项目中。我们在 <script> 标签中使用了 defer 属性,是为了确保页面内容加载完成并准备好与 Vue 绑定之后再加载 Vue。

即使是在这个最基础的步骤中,Vue 团队也在努力为你节省开发时间。许多前端框架,比如 React 和 Angular,都有复杂的安装过程,这可能会让项目起步变得很麻烦。而 Vue 团队意识到,许多复杂的前端功能在前期学习阶段(甚至是整个学习过程中)其实并不需要。因此,他们提供了这个简单的替代方案,让开发者能够快速、轻松地使用 Vue 的大部分功能。

创建vue应用

现在我们的项目已经可以使用 Vue 库了。这意味着我们可以访问所有用于构建 Vue 应用(也就是使用 Vue 构建的网页前端)的代码,但这并不代表我们已经创建了一个 Vue 应用。接下来我们需要编写代码来实际创建一个 Vue 应用。

Vue 提供了一个名为 Vue 的类来让我们轻松创建新应用。就像使用其他 JavaScript 类一样,我们使用 new 关键字来创建这个类的实例。每一个 Vue 实例就是一个完整可用的 Vue 应用。我们来看一个例子:

js 复制代码
// app.js
const app = new Vue({});

通过使用 new 关键字调用 Vue 类的构造函数,我们创建了一个新的 Vue 类实例,并将其命名为 app。在调用时,我们可以为这个 Vue 应用设置许多属性。不过,不同于许多其他的构造函数,Vue 并不会把每个属性作为单独的参数传入。

Vue 应用通常需要很多配置信息------而这些信息在不同应用之间差异很大。为了适应这种情况,Vue 构造函数不会尝试将每个参数都单独列出。如果那样做,开发者就得严格记住参数的顺序,这会让添加或修改属性变得非常困难。

相反,Vue 构造函数只接收一个参数:选项对象(options object) 。Vue 应用运行所需的每一项信息,都会作为键值对的形式添加到这个对象中。这样,开发者只需在选项对象中查找相应的键名,就可以轻松地更新或添加信息。

在接下来的练习中,我们将通过不断为选项对象添加信息,来实际体验这一过程。

vue的设计理念是一个应用对应一个 Vue 实例」,然后通过这个实例去管理整个页面或某个主视图(比如 <div id="app">),其余的页面结构可以通过组件来组织。

el

在创建一个新的 Vue 应用时,我们就可以使用 Vue 提供的所有强大功能了。不过,我们并不一定希望整个 HTML 页面都具备这些功能。因此,我们需要告诉 Vue 应用:我们希望哪些 HTML 区域可以使用 Vue 的逻辑功能

我们可以通过在 Vue 应用的选项对象(options object)中添加一个键值对来实现这个目的。这个键名是 el,代表 HTML 的 element,它的值是一个 CSS 选择器字符串,用来指定我们想要赋予 Vue 功能的 HTML 元素。

js 复制代码
// app.js
const app = new Vue({
  el: '#app'
});

在上面的例子中,我们希望 ID 为 app 的 HTML 元素能够使用 Vue 应用的功能。因此,我们在选项对象中添加了一个名为 el 的键,并将其值设置为 '#app',这是一个 CSS 选择器,它会选中一个 ID 是 app 的元素。

接下来,我们需要把这个 JavaScript 文件引入到 HTML 文件中,这样 Vue 应用才能找到我们指定的 HTML 元素,并将其变成一个 Vue 应用。

html 复制代码
<!-- index.html -->
<head>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js" defer></script>
  <script src="./js/app.js" defer></script>
</head>
<body>
  <div id="app">
    <!-- Vue 应用的 HTML 内容 -->
  </div>
</body>

在这个示例中,我们先写好了 HTML 内容,它会被 Vue 应用接管。然后,在 <head> 中引入了之前写的 JavaScript 文件,这个文件会将 ID 为 app<div> 元素转化为 Vue 应用。

**注意:**我们必须在引入 Vue 应用的代码之前先引入 Vue.js 库。否则在 app.js 文件中就无法访问 Vue 类,也就没法创建 Vue 应用了。这也是我们为什么要在两个 <script> 标签中都加上 defer ------ 这样可以确保当我们创建 Vue 应用时,Vue 库已经加载完毕。

简单理解,el: '#app' 的意思是:"我这个 Vue 应用要控制哪一块 HTML?"

例如:

html 复制代码
<body>
  <h1>欢迎</h1>
  <div id="app">
    <p>{{ message }}</p>
  </div>
  <footer>版权所有</footer>
</body>

你只想让 Vue 来处理 <div id="app"> 这一块,而不是整个 <body>,那你就告诉 Vue:

js 复制代码
new Vue({
  el: '#app'
});
  • el 是 Vue 构造函数的一个选项,用来指定 Vue 应用的「挂载点」。

  • 它的值是一个 CSS 选择器(比如 #app, .main, body 等)。

  • Vue 会把这个元素里面的内容,变成它自己的模板进行渲染

  • 一旦 Vue 接管了那个元素,里面原来的东西就会被 Vue 的模板替换掉(除非你用的内容和数据一致)。

Data

大多数网页上的数据可能会随时改变,比如一篇文章的点赞数或要展示的文章集合。我们把这种不断变化的数据称为动态数据。为了能够在 HTML 中使用这些数据,即使我们无法知道它们在任何时刻的具体值,我们需要一个地方来存储这些动态数据值。在 Vue 中,有很多不同的方法来实现这一点。

所有前端框架的核心功能之一就是:渲染和更新动态数据

比如,社交媒体上一条动态的点赞数,随时可能发生变化。

前端框架必须让我们 轻松显示这些变化的数据 ,并且一旦数据变化,自动更新到页面上

最简单的方法是使用 Vue 实例选项对象中的 data 属性。data 的值是一个对象。该对象中的每一个键值对都对应着一个要在模板中展示的数据。键是数据的名称,值是在模板渲染时显示的内容。

然后,我们使用花括号语法(mustache 语法)在 HTML 模板中展示这些数据。

为了实现这个功能,我们需要两个东西:

  1. 一个地方来存储这些动态数据
  2. 一种语法来在页面上显示这些数据

数据存储在哪

就像上一个小节说的:

Vue 应用需要的所有信息,都要通过 options object(配置对象) 提供。

所以,我们的所有动态数据都应该放在这个配置对象里的 data 属性中。

来看个例子:

js 复制代码
const app = new Vue({
  el: '#app',
  data: {
    username: 'Michael'
  }
});

我们在这个 Vue 应用中加入了 data 属性。

应用可能需要展示很多不同的数据,所以 data 本身是一个对象

我们把每一条数据都用 键 - 值 的形式加进去。上面例子里我们加了一个叫 username 的字段,它的值是 'Michael'

js 复制代码
const app = new Vue({
  el: '#app',
  data: {
    language: 'Spanish',
    hoursStudied: 274
  }
});
html 复制代码
<div id="app">
  <p>You have been studying {{ language }} for {{ hoursStudied }} hours</p>
</div>

通过这个例子,我们可以看到,两个动态数据项 languagehoursStudied 被添加到了 Vue 实例的 data 中,分别对应值 'Spanish'274。这些值接着使用花括号语法在模板中展示,展示的文本是 "You have been studying Spanish for 274 hours"。如果这些值在之后的某个时刻被应用程序修改,比如语言被改变或学习的小时数增加,模板会自动显示更新后的数据给用户。

在真实项目中,这些数据一般是从数据库里获取的(比如用户名会从登录状态里拿到)。

不过在这个课程里,为了方便,我们先写死这些初始数据

模板

回顾一下,我们要在 Vue 应用中展示动态信息,需要两样东西:

  1. 存储数据的地方 (上节讲过,就是 data 属性)
  2. 一种语法,用来显示这些信息

我们已经知道,动态信息是存在 .data 对象里的,那现在的问题是:

这些数据可能会变,那我们该怎么把它展示在页面上?

vue的模板语法(Template)

Vue 使用了一种叫做 模板(template) 的机制。

也就是说,我们在 HTML 中写的有些内容不是"字面上要显示的文字",

而是要被 Vue 替换成真正的数据

你可以用 两个大括号包住变量名,像这样:

html 复制代码
<div id="app">
  <h2>Hello, {{ username }}</h2>
</div>

这里的 {{ username }} 并不会直接显示这段字面文本。

它会被 Vue 自动替换成 .data 中对应字段的值,比如 'Michael'

如果你在 JavaScript 中把 username 改成了 'Yeye',页面上的内容也会自动变成 'Hello, Yeye'

这种方式写出来的 HTML,我们称之为:模板

模板包含了网站的固定内容,也指定了"哪一块需要动态渲染"。

相比传统的 JavaScript 操作 DOM(用 JS 找元素、改 innerText),

Vue 的模板语法更:

  • 简洁
  • 不容易出错

指令(Directives)

指令 是 Vue 内置的"自定义 HTML 属性"。

它们可以用极少的代码,实现非常复杂、常见的前端操作。

条件渲染(显示/隐藏)

前端经常会遇到这种需求:

"如果用户已经登录,就显示'退出'按钮;否则显示'登录'按钮。"

在 Vue 中,这种逻辑只需要写一行代码:

js 复制代码
<button v-if="userIsLoggedIn">Log Out</button>
<button v-if="!userIsLoggedIn">Log In</button>

解释:

  • v-if="userIsLoggedIn" 就像 JavaScript 中的 if 判断语句。
  • .data.userIsLoggedIntrue 时,第一个按钮才会显示。
  • 当它为 false 时,第二个按钮才会显示。

循环渲染数组

另一个常见的前端需求是:遍历数组,渲染多个相似的元素

Vue 提供了 v-for 来实现:

html 复制代码
<ul>
  <li v-for="todo in todoList">{{ todo }}</li>
</ul>
  • v-for="todo in todoList":会把 todoList 数组的每一项拿出来。
  • 然后用 todo 这个变量,渲染一个 <li> 元素。
  • 就这样,列表里的每一项都会被渲染出来!

如果你向数组添加新元素,页面会 自动更新,不需要手动操作 DOM!

表单绑定 v-model

v-model 是用在表单上的超级好用的指令。

它可以把 HTML 表单元素(比如 <input>)和 Vue 的 data 数据绑定起来。

html 复制代码
<input v-model="username" />

解释:

  • 输入框默认会显示 .data.username 的值。
  • 用户只要修改输入框的内容,.data.username 的值就会自动更新!

你不需要写任何事件监听器(如 oninput),Vue 全部帮你做好了。

  • 所有 Vue 内置的指令,都以 v- 开头。

  • 我们这节只学了一部分,Vue 还有很多其他内置指令,你可以随时查阅官方文档。

  • 如果你需要的功能没有内置指令?没关系,你甚至可以自己写指令!

按钮绑定

v-on:click 的值是一段 JavaScript 代码。当这个元素被点击时,Vue 会运行提供的代码,并且可以使用 Vue 应用中的数据。

给 "Add Tweet" 的 <button> 元素添加一个 v-on:click 指令,值为:tweets.push(newTweet)

v-on:click 既可以直接写一段表达式 ,也可以绑定一个方法名 ;而 onclick 只能绑定一个函数 (要么是函数名,要么是 function() {} 这种完整函数)。

组件(Components)

在前端开发中,复用复杂元素是非常常见的操作。比如说:Instagram 或 Facebook 上的每一条帖子看起来都差不多,但内容不同。这些"模块"不仅要在很多不同页面上重复使用,还要在不同设备上都保持一致的外观和行为。

为了解决这个问题,Vue 提供了一种方式,允许我们创建自定义、可复用的 HTML 元素 ,这就是 组件(components)

创建组件时你需要做的事情:

  • 提供一个模板(template) ,告诉 Vue 每次使用这个组件时应该显示什么结构。

  • 定义 props ------ 这些是组件可以"接收"的动态数据,就像参数一样。

当你在 HTML 中使用组件时,可以像写普通 HTML 属性一样给它传递 props,比如:

html 复制代码
<my-post username="Michael" />

使用组件的好处:

  • 在整个站点中像使用普通 HTML 元素一样复用组件。

  • 不用复制粘贴重复的 HTML 代码。

  • 修改组件只需要改一处,所有使用该组件的地方都会自动更新。

  • 避免了样式混乱、结构错误等问题。

了解什么时候该用组件、怎么组织组件 属于稍微进阶一点的内容。这节课我们不会深入讲解这些细节,但我们可以通过一些示例来体验组件的强大功能

在将来,我们很可能需要在网站的多个不同区域显示推文(tweets)。为了让这个任务更简单、出错更少,我们已经创建了一个 tweet 组件(component) ,可以在整个网站中复用。

Tweet.js 文件中,你可以看到用来创建 tweet 组件的代码。这个组件接收一个名为 message 的 prop,并通过提供的模板来展示它。

就像创建 Vue 应用一样,组件的所有信息也是通过一个 options 对象 提供的。

你会注意到模板中的用户名(username)现在还是写死的 "CoderInTraining"。我们现在来改一下,使得这个用户名也可以作为一个 prop 传进组件。

  1. 'author' 添加到 props 数组中,
    这样这个组件就可以通过 prop 接收推文作者(用户名)了。
js 复制代码
const Tweet = Vue.component('tweet', {
 props: ['message','author'],
 template: '<div class="tweet"><h3>CoderInTraining</h3><p>{{ message }}</p></div>'
});
  1. 接下来,我们需要更新组件的模板,使用传入的 author 值。

将组件模板中的 CoderInTraining 替换为一个 mustache 表达式 ,该表达式会被评估为 author 的值。

js 复制代码
const Tweet = Vue.component('tweet', {
 props: ['message','author'],
 template: '<div class="tweet"><h3>{{ author }}</h3><p>{{ message }}</p></div>'
});
  1. 最后,我们需要使用 v-bind 指令将 data 中的 username 值传递给组件的 author 属性。

v-bind 从 Vue 应用的数据对象中获取一个值,并将其用作指定组件属性的值

你可以在 index.html 的第27行看到一个例子。我们使用 v-bindtweet 的 Vue 应用数据值设置为 tweet 组件实例 <tweet>message 属性。

v-bind 后面的 : 之后的是接收该值的属性名。v-bind(引号中的名称)是我们将用来设置该属性值的数据值。

index.html 中,为 <tweet> 元素添加一个 v-bind 指令,将 data 中的 username 值传递给 author 属性。这个指令应该与元素上其他的 v-bind 表达式非常相似。

js 复制代码
<div class="tweets">
  <tweet v-for="tweet in tweets" 
         v-bind:message="tweet"></tweet>
</div>

v-bind和{{}}都是vue中用于绑定数据的方式,但它们的作用和使用场景不同

  • {{}}(Mustache语法)

这是 Vue 的插值语法 ,用来在模板中插入动态数据。当 Vue 渲染组件时,{{}} 会把 Vue 应用的数据(比如 data 对象中的属性值)动态地插入到 HTML 内容中。

js 复制代码
<div>{{ username }}</div>

这里,username 会被替换成 Vue 应用中的 username 数据的值。

  • v-bind:

v-bind 是用于绑定HTML 属性 或组件的props的指令。它允许你将 Vue 应用的数据绑定到一个 HTML 属性或组件的属性上,进而动态更新它们。

比如,当你需要将数据绑定到 HTML 的 classhrefsrc 等属性时,或者你想将数据传递给一个组件的 prop,你就需要使用 v-bind

例如:

html 复制代码
<img v-bind:src="imageUrl" />

这里,v-bind:src 会将 Vue 应用中 imageUrl 的值绑定到 src 属性上。如果 imageUrl 发生变化,src 属性会自动更新。

  • 绑定属性和组件的 prop{{}} 只能用于插值,不适合直接绑定属性或传递给组件的 props。v-bind 是专门设计来处理这些情况的。

  • 动态更新 HTML 属性 :如果你需要动态地更改一个元素的属性值(例如 classstylehref 等),v-bind 会确保属性在数据变化时自动更新,而不需要手动修改 HTML。

为什么在组件中使用 v-bind

你在组件 <tweet> 中使用 v-bind 是为了将 Vue 数据(比如 username)传递给组件的 props (像是 author 这种属性)。这样,组件就可以接收到外部传入的数据并渲染出不同的信息。

举个例子:

html 复制代码
<tweet v-bind:author="username" />

这行代码表示将 username 的值传递给 tweet 组件中的 author 属性。这和插值语法 {{ username }} 不同,因为你是将数据绑定到组件的属性,而不仅仅是显示在 HTML 上。

总的来说:

  • {{}} 是用来显示数据的。

  • v-bind 是用来绑定数据到属性或组件的 prop 上的。

虚拟dom

到目前为止,我们在 Vue 中学习的内容主要是语法。像插值模板、指令和组件这样的语法特性,极大地提高了代码编写的速度和可读性。然而,这些特性并不一定能提高页面的速度,这是所有前端框架需要解决的一个关键问题。

在幕后,Vue 使用了一种非常酷的数据结构,叫做虚拟 DOM,来大幅提高 Vue 应用的速度和响应能力。

  • 已知dom代表文档对象模型,是由用户浏览器创建并存储的网页的表示。浏览器获取网站的html,将其转换为dom,然后将dom绘制到用户屏幕上,使网站对用户可见。dom存储为树状结构,其中每个html元素对应树中的一个节点,让我们看看浏览器如何获取html文件并将其转换为dom树

假设用户单击Ruby列表项旁边的删除按钮,我们希望删除此列表项作为响应,该节点及其下面的节点都将从dom中删除,每当浏览器检测到dom更改时,它都会使用新版本的dom重新绘制整个页面,但是等等,我们真的需要重新绘制整个页面吗,网站是否只有一部分发生了变化,例如标题,没有任何不同,我们为什么要重新绘制。

与dom相比,确定哪些部分发生了变化非常耗时,因此,每当用户与网站交互时,浏览器只需要重新绘制整个页面,而不是是否确定哪些dom发生了变化,但是,如果有一种快速的方法来进行这些比较,这就是虚拟dom的作用所在。在一些前端框架(vue和react中),它们会以js对象的形式创建自己的dom表示,每当要对dom进行更改时,框架都会复制此js对象,对该副本进行更改,并且比较这两个js对象以查看更改的内容,然后将这些更改通知浏览器,并且只重新绘制更改的那部分dom,对js对象进行更改并进行比较,比尝试对dom执行相同操作要快得多。因此,使用JS对象,我们可以快速确定要重新绘制的内容,而仅靠浏览器则无法做到这一点,由于dom这个副本作为js对象存储在内存中,因此它被称为虚拟dom,使用虚拟dom框架可以识别用户交互何时根本不需要重新绘制。

总结

我们将在未来的 Vue 课程中深入教授这些技能(以及其他更多内容)。让我们总结一下 Vue 能做到的一些酷功能:

  • 让前端代码编写更快速:通过一行代码加载 Vue,使用花括号模板(mustache templates)、内置指令和可重用组件,Vue 让代码更易读、更易写。
  • 让前端代码更易修改和修复:通过可读的花括号模板、使用内置的经过充分测试的指令,以及将重复的代码集中到组件中,Vue 使得代码更少出错,也更容易修复。
  • 让前端应用更快速和响应更好:Vue 使用虚拟 DOM 使得网站更新只在需要时发生,并且在更新时极其快速。

你可能会问,Vue 适合我吗?我应该学习 Angular 还是 React 呢?答案是:没有标准答案!这些前端框架都非常好,并且每年都在变得更强大。每个框架有自己独特的语法,但你学到的很多技能是可以在多个框架之间迁移的。React 也使用虚拟 DOM,Angular 也使用指令。两者都使用类似花括号的模板和组件。学你最感兴趣的框架才是最重要的。只要确保你在学习过程中永远不会停滞不前。

开始使用vue

欢迎来到 Vue 数据篇!在《Vue 简介》中,我们简要介绍了 Vue 的一些令人兴奋的功能。在本课中,我们将从复习如何创建 Vue 应用并显示动态数据开始,然后深入了解 Vue 可以存储和显示的不同类型的数据。

开始任何 Vue 项目的第一步是导入 Vue 库 。对于简单项目,我们只需在 HTML 文件的 <head> 部分添加以下 <script> 标签即可:

html 复制代码
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js" defer></script>

vue应用实例化

现在我们已经成功引入了 Vue 库,接下来需要创建 Vue 应用实例 。所有的 Vue 应用本质上都是通过引入的 Vue 库中的 Vue 类 创建的实例。

就像使用其他类一样,我们必须使用 new 关键字来创建这个类的实例,比如这样:

js 复制代码
const app = new Vue({});

如上所示,Vue 构造函数只接受一个参数------一个对象 ,这个对象被称为选项对象(options object) ,它包含了 Vue 应用运行所需要的所有信息(也就是各种"选项")。

我们将在接下来的练习中学习这个选项对象中可以添加的各种配置内容。

计算属性

我们可以使用 data 对象中的值与花括号模板语法来在 HTML 中展示信息。这样做需要将命名的数据存储在 data 对象中,但有些数据可以基于已经存储的信息进行计算,并且不需要在 data 对象中单独存储它的键值对

例如,如果我知道一个月份,假设是四月,我可以根据这个月份计算出季节,例如春天。如果我们试图将这些值分别存储在 data 中,就需要每次其中一个值变化时都更新另一个值,如果不小心的话,可能会导致它们之间的数据不同步。Vue 允许我们将可以根据 data 对象中的值计算得出的数据存储在一个叫做 computed 的单独属性中。

我们不再将计算出的数据存储为 data 对象中的键值对,而是将键值对存储在单独的 computed 对象中。computed 对象中的每个键都是属性的名称,而值是一个函数,用来生成一个值,而不是一个具体的值。

js 复制代码
const app = new Vue({
  el: '#app',
  data: {
    hoursStudied: 274
  },
  computed: {
    languageLevel: function() {
      if (this.hoursStudied < 100) {
        return 'Beginner';
      } else if (this.hoursStudied < 1000) {
        return 'Intermediate';
      } else {
        return 'Expert';
      }
    }
  }
});
html 复制代码
<div id="app">
  <p>You have studied for {{ hoursStudied }} hours. You have {{ languageLevel }}-level mastery.</p>
</div>

在这个例子中,我们需要知道用户学习了多少小时,以便确定他们的语言掌握程度。学习的小时数是已知的,因此我们将它存储在 data 中。然而,我们需要使用 hoursStudied 来计算 languageLevel,所以 languageLevel 必须存储在 computed 中。

Vue 应用会使用提供的函数来确定 languageLevel 的值。在这个例子中,hoursStudied 是 274,所以 languageLevel 会是 'Intermediate'。模板将显示 "You have studied for 274 hours. You have Intermediate-level mastery."。如果 hoursStudied 发生变化,languageLevel 也会自动重新计算。

为了在这个函数中引用 data 中的值,我们使用 this. 来表示这个值,后面跟着数据的名称------在我们的例子中就是 this.hoursStudied

最后,为了在模板中显示计算属性的值,我们像显示 data 属性一样,使用花括号将计算属性的名称括起来。

计算属性的setter

能够根据数据生成计算属性非常有用。但为什么要仅仅限制数据流只能单向流动呢?

Vue 允许我们不仅根据数据值来确定计算值,还能在计算值发生变化时更新相应的数据值 !这使得我们的用户可以在前端编辑计算值,并且相应的数据属性会根据计算值变化,从而重新计算计算属性,使其符合用户选择的值。

示例:

假设后端数据库存储的是标准的 ISO 8601 格式日期(如:2025-04-25T00:00:00Z,即 UTC 时间),但你希望在前端展示给用户的是本地时间格式,例如:YYYY年MM月DD日(中文格式)。而当用户在表单中编辑生日时,系统需要将用户输入的本地日期转换回数据库可存储的 UTC 时间。

js 复制代码
const app = new Vue({
  el: '#app',
  data: {
    // 假设后端存储的生日是 UTC 时间
    birthDateUTC: '2025-04-25T00:00:00Z'
  },
  computed: {
    // 计算属性,用来格式化并返回本地时间的生日
    formattedBirthDate: {
      get: function() {
        const date = new Date(this.birthDateUTC);
        // 将 UTC 时间转换为本地时间,格式化为 YYYY年MM月DD日
        return date.toLocaleDateString('zh-CN', {
          year: 'numeric',
          month: 'numeric',
          day: 'numeric'
        });
      },
      set: function(newDate) {
        // 用户输入的生日是本地时间,需要转换成 UTC 时间
        const localDate = new Date(newDate);  // 转换为本地时间的 Date 对象
        this.birthDateUTC = localDate.toISOString();  // 转换回 UTC 时间并更新
      }
    }
  }
});

html部分:

html 复制代码
<div id="app">
  <p>你的生日是:{{ formattedBirthDate }}</p>
  <label for="birthDate">修改生日:</label>
  <input type="date" v-model="formattedBirthDate">
</div>
  • get 方法:

    • 用户访问 formattedBirthDate 时,Vue 会调用 get 方法,将存储的 UTC 时间(birthDateUTC)转换为本地时间,并格式化为 YYYY年MM月DD日 格式返回。这确保了用户看到的是根据本地时区计算的生日。
  • set 方法:

    • 当用户通过表单编辑生日时(例如,选择了 2025-04-25),Vue 会调用 set 方法,将用户选择的本地日期转换为 UTC 时间,并更新 birthDateUTC。这确保了前端与后端数据的同步------后端始终存储的是 UTC 时间。

在这个应用场景中,getset 方法的结合实现了

  • 格式化展示:用户看到的是本地化的生日格式,符合他们的阅读习惯。
  • 统一数据存储:尽管用户输入的是本地时间,数据最终会被转换回标准的 UTC 时间存储,确保系统的时间一致性。

为什么需要 getset

  • get 使得系统能根据存储的 UTC 时间动态计算并展示本地时间,而不需要每次手动进行转换。
  • set 使得用户编辑的本地时间能被转换回存储的 UTC 格式,而不需要在数据层面做额外处理。

观察器(Watchers)

到目前为止,我们已经了解到,data 用于存储已知的动态数据,computed 用于存储通过其他动态数据计算得出的动态数据。但这里有一个注意点:

一个计算属性(computed只有在其 getter 函数中使用的动态数据发生变化时才会重新计算 。例如,在我们之前的例子中,languageLevel 只会在 hoursStudied 变化时重新计算。但如果我们想在某个值改变时执行某些更新操作,而这些操作并不需要返回新的值,该怎么办?这时候我们就可以使用 watch 属性。

来看这个例子:

js 复制代码
const app = new Vue({
  el: '#app',
  data: {
    currentLanguage: 'Spanish',
    supportedLanguages: ['Spanish', 'Italian', 'Arabic'],
    hoursStudied: 274
  },
  watch: {
    currentLanguage: function (newCurrentLanguage, oldCurrentLanguage) {
      if (supportedLanguages.includes(newCurrentLanguage)) {
        this.hoursStudied = 0;
      } else {
        this.currentLanguage = oldCurrentLanguage;
      }
    }
  }
});

在这个例子中,我们希望:当用户切换到一个新的支持语言时,将学习时间 hoursStudied 重置为 0。如果用户选择了一个不受支持的语言,就把语言切换回原来的那个。

这段功能并不是计算属性 ,因为我们不需要持续计算一个新值;我们只是想在某个数据变化时,触发一些副作用行为(更新其他属性)。

watch 的值是一个对象,包含所有我们希望"观察"的属性。这个对象的 key 是要监听的属性名,value 是在该属性变化时要执行的函数。这个函数接收两个参数:该属性的新值和旧值。

js 复制代码
watch: {
    specialRequests: function(newRequests, oldRequests){
        if (newRequests.toLowerCase().includes('meet and greet') || newRequests.toLowerCase().includes('meet-and-greet')) {
        this.ticketType = 'vip';
     }
    }
}

当用户在 special requests (特殊请求)字段里输入 "meet and greet" 或 "meet-and-greet" 时,我们会自动把他们的票升级为 VIP

实例方法

在本课中,我们介绍了许多选项对象(options object)中的属性,它们可以用来存储和生成动态值 ,供模板中使用。

但如果我们需要一些**方法(functions)**来让应用程序运行,该把它们存在哪里呢?

你也许已经猜到了:在选项对象中,有一个叫做 methods 的属性。

methods 属性就是 Vue 应用用来存放**实例方法(instance methods)**的地方。

这些方法可以用于很多场景,比如:

  • 在其他方法中调用的辅助函数(helper functions)
  • 作为事件处理函数(event handlers),即响应特定用户交互而调用的函数

示例代码:

js 复制代码
const app = new Vue({
  el: "#app",
  data: {
    hoursStudied: 300
  },
  methods: {
    resetProgress: function () {
      this.hoursStudied = 0;
    }
  }
});
html 复制代码
<button v-on:click="resetProgress">Reset Progress</button>

在这个例子中,我们使用 methods 向 Vue 应用添加了一个名为 resetProgress 的实例方法。

这个方法的作用是:把 hoursStudied 的值重置为 0。

然后,我们把这个方法作为事件处理函数绑定到一个 <button> 上。

这样一来,每当用户点击这个按钮时,就会调用 resetProgress 方法。

(不用担心这里出现的 v-on:click 代码,本课不会详细讲解它------我们之后在学习 Vue 表单(Vue Forms)时会专门介绍。)

总结一下:
methods 的值是一个对象,其中:

  • 每一个键(key)是一个方法的名字,
  • 每一个值(value)是对应的方法(function)。

总结

在这一课中,我们学习了四种在 Vue 应用中展示和更新动态数据 的方法。

下面是本课介绍的 Vue 应用选项对象(options object)属性的简要回顾,以及它们各自的使用场景:

  • data ------ 用来存储已知的动态值

  • computed ------ 用来基于已知的动态值计算出新的动态值

    (此外,还可以通过同时指定 getset 来为计算属性添加一个 setter:

    • getter 用来读取计算出来的值
    • setter 在计算属性被修改时,用来更新其他动态值)
  • watch ------ 用来监听指定的动态值变化,并在变化时执行相应的功能逻辑。

  • methods ------ 用来存放整个应用中会用到的实例方法

如果你想了解每个属性的更多细节,可以查看 Vue.js 官方文档中的 Options / Data 章节。

恭喜你学会了这些新技巧!

现在可能感觉有点信息量大,但随着你在构建 Vue 应用的过程中不断练习,你会越来越擅长根据不同情况选择合适的技巧。

在完成 Vue Forms(Vue 表单)Styling Elements with Vue(使用 Vue 为元素添加样式) 之后,你将能全面理解如何使用这些数据部分,构建出功能完整的 Vue 表单应用。

vue表单

欢迎来到 Vue 表单(Vue Forms)

在之前的 Vue 数据(Vue Data) 部分中,你学习了如何在 Vue 实例上存储动态数据和
methods(方法),并使用花括号模板(mustache templates)来展示这些数据。

在这一课中,你将学习如何使用 HTML 表单来修改这些数据。

本课的前半部分将讲解如何把各种类型的 HTML 表单字段绑定到 Vue 实例中的数据

后半部分则会讲解如何清理(clean)、校验(validate)并提交这些数据,从而让你可以为用户创建完整、交互式的表单。

文本框(Text)、文本区域(Textarea)和下拉选择框(Select)绑定

Vue 数据(Vue Data) 部分中,我们学习了在 Vue 应用中存储动态数据的两个主要地方:

  • data ------ 用于存储已知的动态值
  • computed ------ 用于基于其他动态值计算出来的动态值

在网页开发中,添加表单以允许用户修改这些动态值 是非常常见的需求。

因此,Vue 实现了一个指令,叫做 v-model ,它可以自动将表单字段绑定到 Vue 应用中的动态值上

当一个表单字段被绑定后:

  • 当表单字段的值改变时,Vue 应用中的数据也会自动同步成相同的值。
  • 反过来,当 Vue 应用中的数据改变时,表单字段的显示值也会自动更新,反映新的数据。
html 复制代码
<input type="text" v-model="username" />
js 复制代码
const app = new Vue({ 
  el: '#app',
  data: { username: 'Michael' } 
});

在这个例子中,我们做了以下操作:

  • 在 Vue 应用中添加了一个名为 username 的动态数据。
  • <input> 元素上使用了 v-model,把这个输入框绑定到了 username 这个数据上。

因此,当这个网站加载时,<input> 输入框里会预先填好 'Michael' ,也就是 username 的初始值。

之后,当用户在输入框中修改内容时,Vue 应用中的 username 数据也会自动更新为用户输入的新值

在这个例子中,我们是把表单字段绑定到了 data 里的属性上。

但实际上,v-model 同样也可以绑定到 computed 计算属性上

v-model 可以应用在所有 HTML 表单字段元素上。

比如,像 <textarea><select> 这样的简单表单元素,也可以用完全相同的方式进行绑定:

只需要在元素的起始标签里加上 v-model="属性名" 就可以了。

单选按钮绑定

单选按钮是一个有趣的、稍微复杂一些的表单字段例子。
单选按钮 是一组按钮,用户一次只能选择其中一个。当选择了另一个按钮时,之前选中的按钮会自动取消选中。

在 HTML 中,每一个单选按钮都是一个独立的 <input> 元素

不过,它们都对应 Vue 应用中的同一份数据

因此,每个 <input> 都需要单独使用 v-model 指令

但注意:每个单选按钮的 v-model 属性值是相同的 ------ 也就是它们对应的那一份数据的名字。

html 复制代码
<legend>How was your experience?</legend>

<input type="radio" id="goodReview" value="good" v-model="experienceReview" />
<label for="goodReview">Good</label>

<input type="radio" id="neutralReview" value="neutral" v-model="experienceReview" />
<label for="neutralReview">Neutral</label>

<input type="radio" id="badReview" value="bad" v-model="experienceReview" />
<label for="badReview">Bad</label>
js 复制代码
const app = new Vue({ 
  el: '#app', 
  data: { experienceReview: '' } 
});

在这个例子中,我们有三个单选按钮 <input> 元素,都通过 v-model 绑定到了同一个数据 experienceReview

当其中一个按钮被选中时,它的 value 值就会被赋给 experienceReview

比如,如果用户选择了"Good",那么 experienceReview 的值就会变成 "good"

复选框绑定

另一个有趣的表单字段例子是复选框(checkbox)

复选框适用于用户可以为一个表单字段选择多个选项的情况。

与单选按钮不同的是,当用户选中新的复选框时,之前选中的复选框不会被取消

用户可以选择所有他们想要的选项。

因此,绑定到这类复选框的动态数据必须是一个数组(array)

这个数组用来存储所有被选中的复选框的值

html 复制代码
<legend>Which of the following features would you like to see added?</legend>

<input type="checkbox" id="search-bar" value="search" v-model="requestedFeatures">
<label for="search-bar">Search Bar</label>

<input type="checkbox" id="ads" value="ads" v-model="requestedFeatures">
<label for="ads">Ads</label>

<input type="checkbox" id="new-content" value="content" v-model="requestedFeatures">
<label for="new-content">New Content</label>
js 复制代码
const app = new Vue({ 
  el: '#app', 
  data: { requestedFeatures: [] } 
});

在这个例子中,我们有一组复选框,用户可以选择他们想要在网站上新增的功能。

每一个复选框 <input> 元素,都使用了相同的 v-model 绑定:requestedFeatures

所有被勾选的复选框的 value 值,会被加入到 requestedFeatures 这个数组里。

举个例子:

如果用户选择了"Search Bar"和"New Content"这两个复选框,

那么 requestedFeatures 的值就是 ['search', 'content']

单个复选框

你并不总是需要一组复选框(checkboxes)。有时候,你可能只需要单个复选框,用于表示用户是否选择了某一个选项。在这种情况下,绑定到这个复选框的 Vue 数据类型需要做出一些调整。

就像我们在之前的例子中看到的:

如果你使用的是一组带有不同值的复选框 ,那么它们需要绑定到一个数组 ,用来存储所有被选中的值。

而如果只是单个复选框,那可以直接用一个**布尔值(boolean)**来表示。

  • 如果复选框被选中,对应的值是 true
  • 如果未选中,则是 false

例如:

html 复制代码
<legend>你会把这个网站推荐给朋友吗?</legend>
<input type="checkbox" v-model="wouldRecommend" />
js 复制代码
const app = new Vue({ 
  el: '#app',
  data: { wouldRecommend: false } 
});

在这个例子中,我们给一个单独的复选框添加了 v-model 绑定。

  • 如果用户愿意推荐这个网站给朋友,他们就会勾选这个复选框,此时 wouldRecommend 的值变为 true
  • 如果用户取消勾选或者保持未勾选状态,wouldRecommend 的值就是 false

表单事件处理器

在你之前的学习过程中,你可能已经了解到:每一个网页应用的体验,都是一系列"事件"及其"响应"的过程

在网页应用中,所有的操作,比如用户点击按钮、数据库返回一条信息,都是被称为"事件"的东西。

就像你可以在 MDN 的事件列表中看到的那样,表单(form)有两个内置的事件需要我们处理:

  • submit 事件(当点击提交按钮时触发,提交整个表单)
  • reset 事件(当点击重置按钮时触发,表单恢复到初始状态)

在《Vue 入门》中我们简单提到过,Vue 使用 v-on 指令来添加事件处理器(Event Handlers)。

事件处理器的作用是:当某个指定事件发生时,自动调用一个指定的方法(method)来进行响应

我们可以在 <form> 元素上使用 v-on 指令来为表单添加事件处理功能,比如:

html 复制代码
<form v-on:reset="resetForm">
  ...
  <button type="reset">Reset</button>
</form>
js 复制代码
const app = new Vue({ 
  el: '#app', 
  methods: { 
    resetForm: function() { 
      // 这里写重置表单的逻辑 
    } 
  }
});

在这个例子中,我们给表单添加了一个 reset 事件处理器:

  • 使用 v-on: 后面跟着事件类型(这里是 reset
  • 把要调用的方法(resetForm)写成指令的值
  • 当用户点击了"Reset"按钮(它的 type="reset"),就会触发一个 reset 事件
  • 表单捕获到这个事件,就会调用 Vue 实例中定义的 resetForm 方法来处理。

注意

Vue 中有一种常见的简写形式,可以把 v-on: 省略为一个 @,像这样:

html 复制代码
<form @reset="resetForm">
</form>
  • <button type="reset"> 属于表单控件。
  • 当点击这种按钮时,浏览器自动帮你发出一个 reset 事件到**所属的 <form>**上。

所以在html中只要:

  • <button>type="reset"
  • 它属于某个 <form>

浏览器就自动触发这个 <form> 的 reset 事件不需要 Vue 或 JavaScript 额外处理

这就是浏览器内置行为 (叫做 default event dispatching)。

在vue中:

  • 你在 <form> 上加了 @reset="resetFields"

  • 当浏览器自动派发了 reset 事件,

  • Vue 捕获到了这个事件,

  • 然后调用了你的 resetFields 方法。

submit同理

表单事件修饰符(Form Event Modifiers)

如果你之前有前端开发经验,可能已经熟悉了常见的事件处理套路;如果没有,也不用担心 ------ Vue 会帮你搞定!

为了确保良好的网页体验,浏览器会针对常见的**事件(events)**设置默认的处理动作。

这样就算一个网页应用没有专门处理某些事件,浏览器也会做出合理的反应。

你在上一节练习中已经见到过了 ------ 当你提交表单时,页面自动刷新了,就是浏览器默认行为导致的。

在 JavaScript 中,事件对象(Event objects)有一些内置的方法可以修改这种默认行为,比如:

  • Event.preventDefault() ------ 阻止浏览器执行默认动作。
  • Event.stopPropagation() ------ 阻止事件继续传播(比如冒泡到其他元素)。

Vue 提供了一种更方便的方式来使用这些方法 ------

就是通过修饰符(modifiers)

修饰符是添加到指令上的"附加属性",可以改变指令的行为。

Vue 内置了很多常用操作的修饰符,尤其是跟事件处理(event handling)相关的。

比如下面这个例子:

js 复制代码
<form v-on:submit.prevent="submitForm">
  ...
</form>

在这里,我们在 submit 事件处理器后面加了 .prevent 修饰符。

这样做,相当于在事件触发时,Vue 会自动调用 Event.preventDefault(),从而阻止表单提交后页面刷新

同样,如果我们用了 .stop 修饰符,Vue 就会自动调用 Event.stopPropagation()阻止事件继续冒泡

输入符修饰

修饰符是非常有用的工具,能让我们快速在指令中添加基本的前端逻辑。

Vue 为很多指令都提供了修饰符,包括本节课的重点:v-model

没错,v-model 也可以配合修饰符使用,从而让表单字段变得更灵活!

Vue 为 v-model 提供了以下三种修饰符:

  • .number ------ 自动把表单字段中的值转换为数字类型。
  • .trim ------ 自动移除表单字段值开头和结尾的空白字符。
  • .lazy ------ 只有在触发 change 事件时才更新数据(通常是用户离开输入框时更新,而不是每敲一个字就更新)。

如果你想了解这些修饰符的更多细节,可以查看 Vue 官方文档。

html 复制代码
<input type="text" id="first-name" v-model.trim="firstName" />

<select id="ticket-quantity" v-model.number="ticketQuantity">

表单校验

要完成你在 Vue 中的表单知识,还有最后一个功能必须掌握 ------ 表单校验

表单校验 是指确保用户填写了所有必填信息,并且格式正确的过程。

比如我们不希望用户忘记填写重要的信息(像是姓氏)却没有被提示!

在 Vue 中,有很多种方式可以实现表单校验------我们这里讲的是一种常见的方法。

这个方法大量使用了 <button> 元素的 disabled 属性。简单来说:

  • 如果 disabled 存在(或者值为 true),那么按钮点击时不会触发任何操作;
  • 如果 disabled 不存在(或者值为 false),那么按钮可以正常使用。

你可以在 MDN 的 <button> 文档 里查看更多关于 disabled 属性的信息。

html 复制代码
<button type="submit" v-bind:disabled="!formIsValid">Submit</button>
js 复制代码
const app = new Vue({ 
  el: '#app', 
  computed: { 
    formIsValid: function() { 
      // 校验逻辑
      return this.firstName && this.lastName && this.email && this.purchaseAgreementSigned;
    } 
  }
});
  • 我们用 v-bind 指令,把按钮的 disabled 属性绑定到了 formIsValid 这个 计算属性 上;

  • formIsValid 里包含了检查 Vue 应用中数据的逻辑,会返回一个布尔值(truefalse)来表示表单是否有效;

  • 如果表单有效,formIsValid 返回 true,那么按钮不会被禁用;

  • 如果表单无效,formIsValid 返回 false,那么按钮就会被禁用,用户无法点击提交。

虽然这个方案一开始看起来有点复杂,但掌握表单校验在前端开发中非常重要,因此我们在这个阶段就希望你能先接触它。

v-model是一种双向绑定,v-bind是一种单向绑定

v-model:

  • 是一种双向绑定(Two-way binding)

  • 主要用在表单元素(input、select、textarea等)上。

  • 作用:用户输入会自动更新 Vue 的数据,Vue 的数据变化也能自动更新到界面上。

  • 常用于:表单控件(输入框、下拉框、单选框、复选框等)。

html 复制代码
<input v-model="username">

等同于:

  • 监听用户输入事件(input事件)
  • 同时把 input 的 value 绑定到 username

所以,当用户一打字,username 里的值就变了。

v-bind:

  • 是一种单向绑定(One-way binding)

  • 作用:把 Vue 中的数据绑定到 HTML 的属性上。

  • 常用于 :HTML元素的属性,比如:hrefsrcdisabledclassstyle 等。

html 复制代码
<button v-bind:disabled="isDisabled">Submit</button>
  • 如果 isDisabledtrue,按钮就被禁用了;
  • 如果 isDisabledfalse,按钮就可以点击。

注意:v-bind 只是把数据"赋值"到属性上,不会自动监听用户操作返回到数据里。
作为验证函数的isDisabled,什么时候会触发?

formIsValid 是一个 computed(计算属性),它的特点是:

  • 不是主动触发 ,而是被依赖触发
  • 当它用到的数据(依赖)变化时 ,它就会自动重新计算
  • 如果依赖的数据没有变,Vue 会缓存之前的计算结果,不会重新算。

formIsValid 在它依赖的数据变化时自动触发计算,完全不用自己手动去调用。

总结

恭喜你!你现在已经学会了如何在 Vue 中将 HTML 表单绑定到数据,以及如何监听事件了!

让我们来回顾一下本课的重点内容:

  • 可以使用 v-model 指令将表单字段绑定到 Vue 的数据 ------ 使用 v-model 的方式取决于它添加到的字段类型;
  • 可以通过 v-on:submitv-on:reset 添加表单的事件处理器;
  • 可以使用**修饰符(modifiers)**为指令添加额外功能 ------ 最重要的是使用 v-on:submit.prevent 防止表单提交时页面刷新,以及使用 .number.trim 来清理表单字段的值;
  • 可以通过使用 v-bind<button>disabled 属性绑定到一个计算属性的值,来实现表单验证;

使用 Vue 给元素添加样式

虽然课程名叫"给元素添加样式",但实际上,给 HTML 元素添加样式的方法仍然是通过 CSS。不过,现在我们已经会创建动态网页了,也需要让我们的 CSS 样式能随着数据动态变化。幸运的是,Vue 提供了简单易用的功能来实现这一需求。

在本课的前半部分,我们将学习如何根据 Vue 应用中的数据,动态地给 HTML 元素添加内联样式(inline styles)

而在后半部分,我们会学习如何对这部分工作进行优化,改为动态地添加类(class)

这节课篇幅不长,但它包含的信息对于打造专业的前端应用来说是非常重要的。请好好享受学习过程!

在这节课中,我们将通过添加动态 CSS 样式,完善我们的电子票务应用(e-ticketing app)。

我们将实现:

  • 当所有必填字段正确填写时,让"提交(Submit)"按钮看起来是启用状态;如果填写不完整,则显示为禁用状态;
  • 动态地给"电子邮件(Email)"输入框添加样式,以表现出表单字段的不同状态。

到本课结束时,你将掌握给其他表单字段添加类似动态样式的技能。

不过,如果给每个字段都重复添加样式,这会很枯燥且没有太多额外学习价值,因此本课不会让你做所有字段的样式。

未来的课程将教你如何避免这种重复劳动。

内联样式

我们将从讲解如何在 Vue 应用中**动态添加内联样式(inline styles)**开始这一课。

你可能还记得,在最早学习 CSS 的时候,内联样式指的是直接添加到 HTML 元素上的 CSS 样式规则。使用方式是给 HTML 元素添加 style 属性,比如:

html 复制代码
<h2 style="color:red">Breaking News</h2>

过去我们一直建议不要使用内联样式,因为它们会让 CSS 更难调试,同时也让 HTML 更难阅读。

但在前端框架中,由于可以把 CSS 样式局部封装在小而可复用的组件中,内联样式反而成为了一个强大的工具

在你后续学习 Vue 的过程中,我们会大量使用它。

下面是 Vue 中添加动态内联样式的语法:

html 复制代码
<h2 v-bind:style="{ color: breakingNewsColor, 'font-size': breakingNewsFontSize }">Breaking News</h2>
js 复制代码
const app = new Vue({ 
  data: { 
    breakingNewsColor: 'red',
    breakingNewsFontSize: '32px'
  }
});

在这个例子中,我们使用了 v-bind:style 指令,把两个内联样式的值绑定到了 Vue 应用中的数据属性上。

  • v-bind:style 的值是一个对象,对象的 key 是 CSS 样式的属性名,value 是 Vue 实例中的数据属性
  • 在这里,我们把 <h2> 元素的 color 属性绑定到了 breakingNewsColor('red'),
    font-size 属性绑定到了 breakingNewsFontSize('32px')。

注意:

如果要绑定 带连字符(-)的 CSS 属性名 ,比如 font-size,必须把属性名用 引号 包起来,

这样才能构造出合法的 JavaScript 对象。

另外,虽然这个例子中用的是 data 中的值,但实际上,computed 计算属性也可以用于 v-bind:style,和本课里其他指令的用法是一样的。

虽然在这个例子中,动态内联样式只是让 HTML 更易读了一点,

js 复制代码
submitButtonColor: function() {
    if (this.formIsValid) {
      return '#4c7ef3';
    } else {
      return 'gray';
    }
},
submitButtonCursor: function(){
  if (this.formIsValid) {
    return 'pointer';
  } else {
    return 'default';
  }
html 复制代码
<button type="submit" v-bind:disabled="!formIsValid" v-bind:style="{'background-color': submitButtonColor, cursor: submitButtonCursor}">Confirm Tickets</button>

或者:

js 复制代码
computed: {
    formIsValid: function() {
      return this.firstName && this.lastName && this.email && this.purchaseAgreementSigned;
    },
    submitButtonStyles: function(){
        if (this.formIsValid) {
          return {
            'background-color': '#4c7ef3',
            cursor: 'pointer'
          }
        } else {
          return {
            'background-color': 'gray',
            cursor: 'default'
          }
        }
    }
}
html 复制代码
<button type="reset" class="reset">Reset</button>
<button type="submit" v-bind:disabled="!formIsVali"
v-bind:style="submitButtonStyles">Confirm Tickets</button>

多个样式对象

v-bind:style 的另一个强大之处是,它也可以接受一个样式对象数组作为值。

js 复制代码
const app = new Vue({ 
  data: {
    newsHeaderStyles: { 
      'font-weight': 'bold', 
      color: 'grey'
    },
    breakingNewsStyles: { 
      color: 'red'
    }
  }
});
html 复制代码
<h2 v-bind:style="[newsHeaderStyles, breakingNewsStyles]">Breaking News</h2>

在这个例子中,我们为 Vue 应用添加了另一个属性 newsHeaderStyles。这是一个样式对象,主要用来给所有新闻标题设置样式。然后,使用 v-bind:style 的数组写法,我们把这两个样式对象都应用到了 "Breaking News" 这个元素上。

你可能注意到,这两个样式对象都定义了 color 属性。当这种情况发生时,数组中后添加的样式对象优先级更高 。所以,"Breaking News" 最终会显示为**加粗(bold)且红色(red)**的文字。灰色的 color 样式会被覆盖,不会生效。

多个样式对象

v-bind:style 的另一个强大之处是,它也可以接受一个样式对象数组作为值。

js 复制代码
const app = new Vue({ 
  data: {
    newsHeaderStyles: { 
      'font-weight': 'bold', 
      color: 'grey'
    },
    breakingNewsStyles: { 
      color: 'red'
    }
  }
});
html 复制代码
<h2 v-bind:style="[newsHeaderStyles, breakingNewsStyles]">Breaking News</h2>

在这个例子中,我们为 Vue 应用添加了另一个属性 newsHeaderStyles。这是一个样式对象,主要用来给所有新闻标题设置样式。然后,使用 v-bind:style 的数组写法,我们把这两个样式对象都应用到了 "Breaking News" 这个元素上。

你可能注意到,这两个样式对象都定义了 color 属性。当这种情况发生时,数组中后添加的样式对象优先级更高 。所以,"Breaking News" 最终会显示为**加粗(bold)且红色(red)**的文字。灰色的 color 样式会被覆盖,不会生效。

例子:


表单字段通常会有三种常见状态:未触碰(untouched)已触碰(touched)无效(invalid)

  • 未触碰(untouched)指的是用户还没有在字段中输入任何内容的状态。
  • 已触碰(touched)指的是字段中已经填写了信息的状态。
  • 无效(invalid)指的是字段中输入了无效信息的状态。

我们将为"Email"字段设计这三种状态的样式。

具体要求是:

  • 当字段未触碰时,边框应显示为浅灰色。
  • 当字段被触碰后,边框颜色应变得稍微深一些。
  • 如果输入了无效的电子邮件 (为了简化,只检测是否包含 @ 符号),边框颜色应变为红色。

首先,让我们添加一个计算属性,用于根据是否输入了内容来返回相应的样式对象。

app.js 中,添加一个名为 touchedEmailStyles 的计算属性,内容如下:

js 复制代码
function() {
  if (this.email) {
    return {
      'border-color': '#bdbcbc',
      'border-width': '2px'
    }
  } else {
    return {
      'border-color': '#e0e0e0',
      'border-width': '2px'
    }
  }
}

这个函数会根据"Email"字段是否有输入内容返回不同的样式对象:

  • 如果还没有输入(未触碰),则返回浅灰色的边框。
  • 如果已经输入了内容(已触碰),则返回稍深灰色的边框。

此外,为了强调这是一个必填字段,在两种状态下,我们都将 border-width 设置为稍粗一点(2px)。

接下来,让我们添加一个计算属性,用于在"Email"字段填写错误时应用样式。

(我们已经添加了一个单独的计算属性 emailIsValid,它负责执行邮箱的验证逻辑。)

app.js 中,添加一个名为 invalidEmailStyles 的计算属性,内容如下:

js 复制代码
computed: {

    emailIsValid: function() {
      return this.email.includes('@');
    },
    
    isvalidEmailStyles: function(){
      if (this.email && !this.emailIsValid) {
        return {
          'background-color': '#ffeded',
          'border-color': '#da5252'
        }    
    }
}

这个函数会在"Email"字段填写了无效邮箱时,返回一个样式对象:

  • 背景色设置为浅红色#ffeded
  • 边框颜色设置为深红色#da5252

现在,让我们使用前面创建的样式对象,来为"Email"字段的不同状态设置样式。

index.html 中,为"Email"字段添加一个 v-bind:style 指令,同时绑定 touchedEmailStylesinvalidEmailStyles 这两个样式对象。

请注意,一定要先添加 touchedEmailStyles ,这样当填写了无效邮箱时,invalidEmailStyles 中的边框颜色才能正确覆盖 touchedEmailStyles 的边框颜色。

完成代码后,测试一下这个字段:

  • "Email"字段的边框应该比其他字段略微粗一点;
  • 一开始边框是浅灰色;
  • 输入无效内容后(没有 @ 符号),背景颜色和边框颜色都会变成红色;
  • 输入 @ 符号后,背景会恢复成白色,边框变为深灰色。
html 复制代码
<input type="text" id="email" v-model.trim="email" v-bind:style="[touchedEmailStyles,invalidEmailStyles]"/>

CSS类

就像之前提到的,等你后面学习了组件的用法后,你会更频繁地使用内联样式(inline styles)。

不过目前,我们所有的 Vue 应用程序都写在一个文件里。如果在这个文件中塞入大量的样式规则,代码很快就会变得难以阅读。

因此,在这种情况下,最好还是使用 CSS 类(classes),把大部分样式信息放在独立的 CSS 文件中。

接下来我们看看,如何动态地添加 CSS 类,而不是使用内联样式:

html 复制代码
<span v-bind:class="{ unread: hasNotifications }">Notifications</span>
css 复制代码
.unread {
  background-color: blue;
}
js 复制代码
const app = new Vue({
  data: {
    notifications: [ ... ]
  },
  computed: {
    hasNotifications: function() {
      return notifications.length > 0;
    }
  }
})

在这个例子中,我们使用 v-bind:class 指令,根据计算属性 hasNotifications 的返回结果,动态地给"Notifications"这个 <span> 元素添加 unread 类。

v-bind:class 的值是一个对象:

  • 对象的 键(key) 是 CSS 类名。

  • 对象的 值(value) 是 Vue 应用中的属性。

    • 如果值为 真值(truthy) ,就会把对应的类添加到元素上。
    • 如果值为 假值(falsy) ,就不会添加这个类。

在上面的例子中,如果 notifications 数组中有内容,hasNotifications 会返回 true,那么 unread 类就会被加到"Notifications"元素上,使它的背景色变成蓝色。

和之前 v-bind:style 的用法类似,v-bind:class 的值也可以是一个 Vue 应用中的属性,而不是直接在 HTML 里写对象。

示例:

  1. 在css文件新增:
css 复制代码
button.active {  
  cursor: pointer;  
  background-color: #4c7ef3;  
}
html 复制代码
<button type="submit" v-bind:disabled="!formIsValid" v-bind:class="{ active: formIsValid }">Confirm Tickets</button>

index.html 中,使用 v-bind:class,在 formIsValidtrue 时,有条件地 给"Confirm Tickets"按钮添加 active 类。

Class数组

这个数组(指 v-bind:class 的值可以是数组)可以包含类对象 ,并会按照数组中类对象的顺序 将对应的类应用到元素上。不过,这个数组也可以包含另一种类型的元素

虽然类对象 很适合有条件地添加类 ,但有时候,我们也需要不管任何条件,直接添加一个类 到元素上。这种情况下,你可以直接把类名加到数组里,它就会一直应用在元素上。这些类名需要作为 Vue 应用的属性存在。

让我们用一个例子来说明:

html 复制代码
<span v-bind:class="[{ unread: hasNotifications }, menuItemClass]">Notifications</span>
js 复制代码
const app = new Vue({
  data: { 
    notifications: [ ... ],
    menuItemClass: 'menu-item'
  },
  computed: {
    hasNotifications: function() {
      return notifications.length > 0;
    }
  }
})
css 复制代码
.menu-item {
  font-size: 12px;
}

.unread {
  background-color: blue;
}

在这段代码中,我们对之前的例子做了修改,把 v-bind:class 的值改成了一个数组

  • 数组的开头是一个对象{ unread: hasNotifications } ------ 它会根据 hasNotifications 是否为 true,来有条件地添加 unread 类。
  • 数组的后面加了一个Vue 应用的属性 menuItemClass,它的值是 'menu-item' ------ 无条件地给元素添加 menu-item 类。

这样,"Notifications" 元素会:

  • 如果有未读通知,加上 unread 类,背景变蓝;
  • 无论是否有未读通知 ,总是加上 menu-item 类,使字体大小变成 12px。

使用数组作为 v-bind:class 的值,可以很方便地同时添加有条件的类和无条件的类 。而且,就像我们之前学过的那样,也可以通过数组添加多个类对象

示例:

  1. css中新增:
css 复制代码
input.required {
  border-width: 2px;
}

input.touched {
  border-color: #bdbcbc;
}

input.invalid {
  background-color: #ffeded;
  border-color: #da5252;
}
  1. 接下来,我们要在 Vue 应用中添加两个属性。

我们希望无论"Email"字段处于什么状态,都始终给它加上 required 这个类。因此,我们需要一个 Vue 应用的属性来存储这个类名,以便配合 v-bind:class 使用。

由于这个属性不需要经过计算 ,所以我们可以直接把它放在 data

app.js 中,给你的 Vue 应用添加一个 data 属性,名字叫 requiredFieldClass ,值为字符串 'required'(就是类名)。

js 复制代码
computed: {
    emailClasses: function() {
      return {
      touched: this.email.length !== 0,
      invalid: this.email && !this.emailIsValid
    }
}
  1. index.html 中,使用 v-bind:class,并用数组 的形式,把 requiredFieldClassemailClasses 这两个属性绑定到 <input> 类型为 "Email" 的字段上。
html 复制代码
<input type="text" id="email" v-model.trim="email" v-bind:class="[requiredFieldClass, emailClasses]"/>

总结

我们学习了如何使用 v-bind:style 搭配样式对象样式对象数组 ,来动态添加内联样式 。随后,我们又学习了如何使用 v-bind:class 搭配类对象类对象数组和类名字符串 ,来动态添加CSS类

你可能觉得,单靠其中任何一种技巧就足够在前端应用中实现动态样式了 ------ 这确实没错!但随着你继续深入学习 Vue,你会看到不同技巧各自的优势和适用场景。

在你学习过程中,最重要的一点是:要尽量选择能让你的代码最易读、且避免重复代码的技巧

相关推荐
夕水20 分钟前
这个提升效率宝藏级工具一定要收藏使用
前端·javascript·trae
会飞的鱼先生34 分钟前
vue3 内置组件KeepAlive的使用
前端·javascript·vue.js
苹果酱05671 小时前
【Azure Redis 缓存】在Azure Redis中,如何限制只允许Azure App Service访问?
java·vue.js·spring boot·mysql·课程设计
斯~内克1 小时前
前端浏览器窗口交互完全指南:从基础操作到高级控制
前端
Mike_jia1 小时前
Memos:知识工作者的理想开源笔记系统
前端
前端大白话1 小时前
前端崩溃瞬间救星!10 个 JavaScript 实战技巧大揭秘
前端·javascript
loveoobaby1 小时前
Shadertoy着色器移植到Three.js经验总结
前端
蓝易云1 小时前
在Linux、CentOS7中设置shell脚本开机自启动服务
前端·后端·centos
浩龙不eMo1 小时前
前端获取环境变量方式区分(Vite)
前端·vite
土豆骑士2 小时前
monorepo 实战练习
前端