Bootstrap

学习:Vue.js官方文档----介绍、Vue实例、模板语法、计算属性与侦听器

2. 介绍

  • v-bind 绑定属性(attribute)
  • v-on 绑定事件(添加事件监听器)调用方法

2.3 声明式渲染【v-bind:title / 简写为 :title】

Vue.js 的核心是一个允许采用 简洁的模板语法 来声明式地 将数据渲染进 DOM 的系统:

// HTML
<div id="demo">
	<h1> Hello,{{ name.toUpperCase() }},{{ address }} </h1>
</div>
// JS 
var app = new Vue({
  // id选择器
  el: '#app',  // el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串。
  data:{ 
	name:'atguigu',
	address:'北京'
  },
})

lzl:这其实也是模块化的思想,div中的变量放在 Vue实例对象中,实现隔离。
为了让div中的变量知道从哪里读取变量,所以需要 el: '#app' 建立连接。

现在 数据DOM 已经被建立了关联,所有东西都是 响应式的。我们要怎么确认呢?打开你的浏览器的 JavaScript 控制台 (就在这个页面打开),并修改 app.nameapp.address 的值,你将看到上例相应地更新

lzl:app 是对象名,message 是对象的属性,所以app.nameapp.address能读取和修改值。这是 this 对象:
在这里插入图片描述

注意我们不再和 HTML 直接交互了。一个 Vue 应用会将其挂载到一个 DOM 元素上 (对于这个例子是 #app) 然后对其进行完全控制。
——————
除了文本插值,我们还可以像这样来 绑定元素 attribute

<div id="app-2">
  <!-- :title 的功能-->
  <span v-bind:title="message">
    鼠标悬停几秒钟查看此处动态绑定的提示信息!
  </span>
</div>
var app2 = new Vue({
  el: '#app-2',
  data: {
    message: '页面加载于 ' + new Date().toLocaleString()
  }
})

在这里插入图片描述
你看到的 v-bind attribute 被称为 指令。指令带有前缀 v-,以表示它们是 Vue 提供的特殊 attribute。

如果你再次打开浏览器的 JavaScript 控制台,输入 app2.message = '新消息',就会再一次看到这个绑定了 title attribute 的 HTML 已经进行了更新。


2.4 条件与循环【v-if 、v-for=“todo in todos”】

控制切换一个元素是否显示 也相当简单:

<div id="app-3">
  <p v-if="seen">现在你看到我了</p>
</div>
var app3 = new Vue({
  el: '#app-3',
  data: {
    seen: true  // 
  }
})

在控制台输入 app3.seen = false,你会发现之前显示的消息消失了。

这个例子演示了我们不仅可以 把数据 绑定到 DOM 文本或 attribute,还可以 绑定到 DOM 结构

此外,Vue 也提供一个强大的过渡效果系统,可以在 Vue 插入/更新/移除元素时 自动应用过渡效果
——————
还有其它很多指令,每个都有特殊的功能。例如,v-for 指令可以绑定数组的数据来渲染一个项目列表:

<div id="app-4">
  <ol>
    <li v-for="todo in todos">
      {{ todo.text }}
    </li>
  </ol>
</div>
var app4 = new Vue({
  el: '#app-4',
  data: {
    todos: [
      { text: '学习 JavaScript' },
      { text: '学习 Vue' },
      { text: '整个牛项目' }
    ]
  }
})

在控制台里,输入 app4.todos.push({ text: '新项目' }),你会发现列表最后添加了一个新项目。


2.5 处理用户输入【v-on:click=“xxx”、v-model=“…”】

为了让用户和你的应用进行交互,我们可以用 v-on 指令添加一个 事件监听器,通过它 调用在 Vue 实例中定义的方法

<div id="app-5">
  <p>{{ message }}</p>
  
  <!-- v-on添加一个事件监听器,调用方法 reverseMessage,
       不是调用方法返回的结果 reverseMessage()。
       调用方法是为了执行某个动作。-->
  <button v-on:click="reverseMessage">反转消息</button>
</div>
var app5 = new Vue({
  el: '#app-5',
  data: {
    message: 'Hello Vue.js!'
  },
  methods: {
    reverseMessage() {
      this.message = this.message.split('').reverse().join('')
    }
  }
})

在这里插入图片描述

注意在 reverseMessage 方法中,我们更新了应用的状态,但没有触碰 DOM——所有的 DOM 操作都由 Vue 来处理,你编写的代码只需要关注逻辑层面即可。

——————
Vue 还提供了 v-model 指令,它能轻松实现 表单输入和应用状态 之间的双向绑定。

<div id="app-6">
  <p>{{ message }}</p>
  <input v-model="message">
</div>
var app6 = new Vue({
  el: '#app-6',
  data: {
    message: 'Hello Vue!'
  }
})

在这里插入图片描述


2.6 组件化应用构建

组件系统是 Vue 的另一个重要概念,因为它是一种抽象,允许我们使用 小型、独立和通常可复用的组件 构建大型应用。仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树:
在这里插入图片描述
在 Vue 里,一个组件本质上是一个拥有预定义选项的一个 Vue 实例。在 Vue 中注册组件很简单:

// 定义名为 todo-item 的新组件
Vue.component('todo-item', {
  template: '<li>这是个待办项</li>'
})

var app = new Vue(...)

现在你可以用它构建另一个组件模板:

<ol>
  <!-- 创建一个 todo-item 组件的实例 -->
  <todo-item></todo-item>
</ol>

——————
但是这样会 为每个待办项渲染同样的文本,这看起来并不炫酷。我们应该能从父作用域将数据传到子组件才对。让我们来修改一下组件的定义,使之能够接受一个 prop:

Vue.component('todo-item', {
  // todo-item 组件现在接受一个
  // "prop",类似于一个自定义 attribute。
  // 这个 prop 名为 todo。
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})

现在,我们可以使用 v-bind 指令将待办项传到循环输出的每个组件中:

<!--html-->
<div id="app-7">
  <ol>
    <!--
      现在我们为每个 todo-item 提供 todo 对象
      todo 对象是变量,即其内容可以是动态的。
      我们也需要为每个组件提供一个“key”,稍后再作详细解释。
    -->
    <todo-item
      v-for="item in groceryList"
      v-bind:todo="item"
      v-bind:key="item.id"
    ></todo-item>
  </ol>
</div>
// JS
Vue.component('todo-item', {
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})

var app7 = new Vue({
  el: '#app-7',
  data: {
    groceryList: [
      { id: 0, text: '蔬菜' },
      { id: 1, text: '奶酪' },
      { id: 2, text: '随便其它什么人吃的东西' }
    ]
  }
})

尽管这只是一个刻意设计的例子,但是我们已经 设法将应用分割成了两个更小的单元子单元通过 prop 接口与父单元进行了良好的解耦

我们现在可以 进一步改进 <todo-item> 组件,提供更为复杂的模板和逻辑,而不会影响到父单元。

在一个大型应用中,有必要将整个应用程序划分为组件,以使开发更易管理。在后续教程中我们将详述组件,不过这里有一个 (假想的) 例子,以展示使用了组件的应用模板是什么样的:

<div id="app">
  <app-nav></app-nav>
  <app-view>
    <app-sidebar></app-sidebar>
    <app-content></app-content>
  </app-view>
</div>

3. Vue实例

3.1 创建一个 Vue 实例

每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例 开始的:

var vm = new Vue({
  // 选项
})


3.2 数据与方法

– data所有property加入到vm中

当一个 Vue 实例被创建时,它将 data 对象中的所有的 property 加入到 Vue 的响应式系统中。当这些 property 的值发生改变时,视图将会产生"响应",即匹配更新为新的值。

// 我们的数据对象
var data = { a: 1 }

// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
  data: data  // 将data对象中的属性与属性值 放到vm身上,所以有vm.a
})

// 获得这个实例上的 property
// 返回源数据中对应的字段
vm.a == data.a // => true

// 设置 property 也会影响到原始数据
vm.a = 2
data.a // => 2

// ……反之亦然,即都指向同一个堆地址
data.a = 3
vm.a // => 3

——————

– 当实例被创建时,就已经存在于 data 中的 property 才是响应式的。

当这些数据改变时,视图会进行重渲染。值得注意的是,只有当实例被创建时,就已经存在于 data 中的 property 才是响应式的。 也就是说如果你添加一个新的 property,比如:

vm.b = 'hi'

那么对 b 的改动将不会触发任何视图的更新。如果你知道你会在晚些时候需要一个 property,但是一开始它为空或不存在,那么你仅需要设置一些初始值。比如:

data: {
  // 提前在data中配置好属性,并设定初始值,这样这些属性才是响应式的!
  newTodoText: '',
  visitCount: 0,
  hideCompletedTodos: false,
  todos: [],
  error: null
}

——————

– Object.freeze() 阻止修改现有的 property

这里唯一的例外是使用 Object.freeze(),这会阻止修改现有的 property,也意味着响应系统无法再追踪变化。

var obj = {
  foo: 'bar'
}

Object.freeze(obj)

new Vue({
  el: '#app',
  data: obj  // 把obj存储的堆地址,传递给data
})
<div id="app">
  <p>{{ foo }}</p>
  <!-- 这里的 `foo` 不会更新! -->
  <button v-on:click="foo = 'baz'">Change it</button>
</div>

——————

– vm.$data 与用户定义的 property(属性) 区分开来

除了数据 property,Vue 实例还暴露了一些有用的实例 property 与方法。它们都有前缀 $,以便 与用户定义的 property(属性) 区分开来

// vm.$data、vm.$el、vm.$watch 都是自带的
var data = { a: 1 }
var vm = new Vue({
  el: '#example',
  data: data
})

vm.$data === data // => true
vm.$el === document.getElementById('example') // => true

// $watch 是一个实例方法
vm.$watch('a', function (newValue, oldValue) {
  // 这个回调将在 `vm.a` 改变后调用
})

可以在 API 参考中查阅到完整的实例 property 和方法的列表。


3.3 实例生命周期钩子

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置 数据监听编译模板将实例挂载到 DOM , 并在数据变化时更新 DOM 等。

同时在这个过程中也会运行一些叫做 生命周期钩子函数,这给了用户在不同阶段 添加自己的代码的机会。

比如 created 钩子可以用来 在一个实例被创建之后执行代码

new Vue({
  data: {
    a: 1
  },
  created() {  // 生命周期钩子都是些函数,放在vm对象中,与data同级
    // `this` 指向 vm 实例
    console.log('a is: ' + this.a)
  }
})
// => "a is: 1"

也有一些其它的钩子,在实例生命周期的不同阶段被调用,如 mountedupdateddestroyed。生命周期钩子的 this 上下文指向 调用它的 Vue 实例

  • 不要在选项 property 或回调上使用 箭头函数,比如 created: () => console.log(this.a)vm.$watch('a', newValue => this.myMethod())

  • 因为箭头函数并没有 this,this 会作为变量一直向上级词法作用域查找,直至找到为止,经常导致 Uncaught TypeError: Cannot read property of undefined Uncaught TypeError: this.myMethod is not a function 之类的错误。


3.4 生命周期图示

下图展示了实例的生命周期。随着你的不断学习和使用,它的参考价值会越来越高。
实例的生命周期图


4. 模板语法

4.1 插值

– 4.1.1 文本插值 {{ msg}}

数据绑定 最常见的形式就是使用 “Mustache”语法 (双大括号) 文本插值

<span>Message: {{ msg }}</span>

Mustache 标签将会 被替代为 对应数据对象上 msg property(属性) 的值
无论何时,绑定的数据对象上 msg property 发生了改变,插值处的内容都会更新。
——————
通过使用 v-once 指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上的其它数据绑定:

<span v-once> 这个将不会改变: {{ msg }} </span>

– 4.1.2 原始 HTML

双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html 指令:

<p> Using mustaches: {{ rawHtml }} </p>
                            <!--就是说-->
<p> Using v-html directive: <span v-html="rawHtml"></span> </p>

在这里插入图片描述
这个 span 的内容将会被替换成为 property 值 rawHtml,直接作为 HTML——会忽略解析 property 值中的数据绑定。

注意,你不能使用 v-html 来复合局部模板,因为 Vue 不是基于字符串的模板引擎。 反之,对于用户界面 (UI),组件更适合作为可重用和可组合的基本单位。

你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值


– 4.1.3 Attribute【v-bind 指令】

Mustache 语法(双大括号)不能作用在 HTML attribute 上,遇到这种情况应该使用 v-bind 指令:

<div v-bind:id="dynamicId"></div>
<button v-bind:disabled="isButtonDisabled">Button</button>

如果 isButtonDisabled 的值是 nullundefinedfalse,则 disabled attribute 甚至不会被包含在渲染出来的 <button> 元素中。


– 4.1.4 使用 JavaScript 表达式

迄今为止,在我们的模板中,我们一直都只绑定简单的 property 键值。但实际上,对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式 支持。

{{ number + 1 }}  // 表达式

{{ ok ? 'YES' : 'NO' }}  // 三元表达式

{{ message.split('').reverse().join('') }}  // 表达式

<div v-bind:id="'list-' + id"></div>

这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含 单个表达式,所以下面的例子都不会生效。

<!-- 这是语句,不是表达式 -->
{{ var a = 1 }}

<!-- 流控制也不会生效,要使用三元表达式 -->
{{ if (ok) { return message } }}

模板表达式都被放在沙盒中,只能访问 全局变量的一个白名单,如 MathDate你不应该在模板表达式中试图访问 用户定义的全局变量。


4.2 指令【v-xxx】

指令 (Directives) 是带有 v- 前缀的特殊 attribute。 指令 attribute 的值预期是 单个 JavaScript 表达式 (v-for 是例外情况,稍后我们再讨论)。

指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。

– 4.2.1 指令的 参数【v-bind、v-on】

一些指令能够接收一个 参数”,在指令名称之后以冒号表示。例如,此处是 href,v-bind 指令可以用于响应式地更新 HTML attribute:

<a v-bind:href="url">...</a>

在这里 href 是参数,告知 v-bind 指令将该元素的 href attribute 与 表达式 url 的值绑定。
——————
另一个例子是 v-on 指令,它 用于监听 DOM 事件

<a v-on:click="doSomething">...</a>

在这里 参数 是监听的事件名 click


– 4.2.2 指令的 动态参数

2.6.0 新增

从 2.6.0 开始,可以用 方括号括起来的 JavaScript 表达式 作为一个指令的参数:

<!--
注意,参数表达式的写法存在一些约束,如之后的 “对动态参数表达式的约束” 章节所述。
-->
<a v-bind:[attributeName]="url"> ... </a>

这里的 attributeName 会被作为一个 JavaScript 表达式 进行动态求值,求得的值将会作为最终的参数来使用。例如,如果你的 Vue 实例有一个 data property attributeName,其值为 "href",那么这个绑定将等价于 v-bind:href
——————
同样地,你可以使用动态参数为一个 动态的事件名 绑定处理函数:

<a v-on:[eventName]="doSomething"> ... </a>

在这个示例中,当 eventName 的值为 "focus" 时,v-on:[eventName] 将等价于 v-on:focus。(不同动态事件绑定同一个处理函数)


---- 对动态参数的值的约束

动态参数预期会求出一个 字符串,异常情况下值为 null。这个特殊的 null 值可以被显性地用于移除绑定。任何其它非字符串类型的值都将会触发一个警告。

---- 对动态参数表达式的约束:attribute名 全部小写

动态参数表达式有一些语法约束,因为某些字符,如空格和引号,放在 HTML attribute 名里是无效的。例如:

<!-- 这会触发一个编译警告 -->
<a v-bind:['foo' + bar]="value"> ... </a>

变通的办法是使用 没有空格或引号的表达式,或用 计算属性 替代这种复杂表达式。

在 DOM 中使用模板时 (直接在一个 HTML 文件里撰写模板),还需要避免使用大写字符来命名键名,因为浏览器 会把 attribute 名全部强制转为小写

<!--
在 DOM 中使用模板时这段代码会被转换为 `v-bind:[someattr]`。
除非在实例中有一个名为“someattr”的 property,否则代码不会工作。
-->
<a v-bind:[someAttr]="value"> ... </a>

– 4.2.3 指令的 修饰符【v-on:submit.prevent=“xxx”】

修饰符 (modifier) 是以半角句号.指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault()

<form v-on:submit.prevent="onSubmit">...</form>

在接下来对 v-onv-for 等功能的探索中,你会看到修饰符的其它例子。


4.3 缩写

v- 前缀作为一种视觉提示,用来识别模板中 Vue 特定的 attribute。

4.3.1 v-bind 缩写

<!-- 完整语法 -->
<a v-bind:href="url">...</a>

<!-- 缩写  href是指令的参数 -->
<a :href="url">...</a>

<!-- 动态参数的缩写 (2.6.0+) -->
<a :[key]="url"> ... </a>

4.3.2 v-on 缩写

<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>

<!-- 缩写 -->
<a @click="doSomething">...</a>

<!-- 动态参数的缩写 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>

它们不会出现在最终渲染的标记中。


5. 计算属性computed 和 侦听器watch

5.1 计算属性

模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入 太多的逻辑 会让模板过重且难以维护 。例如:

<div id="example">
  {{ message.split('').reverse().join('') }}
</div>

对于任何复杂逻辑,你都应当使用计算属性。


– 5.1.1 计算属性的基础例子

<div id="example">
  <p>Original message: "{{ message }}"</p>
  <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    // 计算属性的 getter
    reversedMessage() {
      return this.message.split('').reverse().join('')
    }
  }
})

这里我们声明了一个计算属性 reversedMessage。我们提供的函数将 用作 property vm.reversedMessage 的 getter 函数:

console.log(vm.reversedMessage) // => 'olleH'
vm.message = 'Goodbye'
console.log(vm.reversedMessage) // => 'eybdooG'

Vue 知道 vm.reversedMessage 依赖于 vm.message,因此当 vm.message 发生改变时,所有依赖 vm.reversedMessage 的绑定也会更新。

而且最妙的是我们已经以声明的方式创建了这种依赖关系:计算属性的 getter 函数是没有副作用 (side effect) 的,这使它更易于测试和理解。


– 5.1.2 ☆☆ 计算属性缓存 vs 方法

你可能已经注意到我们可以通过 在表达式中调用方法来达到同样的效果:

<p>Reversed message: "{{ reversedMessage() }}"</p>
// 在组件中
methods: {
  reversedMessage: function () {
    return this.message.split('').reverse().join('')
  }
}

我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。

然而,不同的是计算属性是基于它们的 响应式依赖 进行缓存的。

  • 1、只在相关响应式依赖发生改变时,它们才会重新求值。 ---- 【响应式】

  • 这就意味着:2、只要 message 还没有发生改变,多次访问 reversedMessage 计算属性 会立即返回之前的计算结果,而不必再次执行函数。 ---- 【提高内存时间效率】

这也同样意味着下面的计算属性将不再更新,因为 Date.now() 不是响应式依赖:

computed: {
  now() {
    return Date.now()
  }
}

——
相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。

<!--尚硅谷08_计算属性--methods实现-->
<script type="text/javascript" src="../js/vue.js"></script>

<div id="root">
	姓:<input type="text" v-model="firstName"> <br/><br/>
	名:<input type="text" v-model="lastName"> <br/><br/>
	全名:<span>{{ fullName() }}</span>
</div>
new Vue({
	el:'#root',
	data:{
		firstName:'张',
		lastName:'三'
	},
	methods: {
		fullName(){
			return this.firstName + '-' + this.lastName
		}  // 每当firstName、lastName在输入框被改变时,
	},     // 页面就会重新渲染,再次调用methods中的函数计算值
})

在这里插入图片描述
————
我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。


– 5.1.3 ☆☆ 计算属性 vs 侦听属性

Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch ——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性computed,而不是命令式的 watch 回调。

细想一下这个例子:

<div id="demo">{{ fullName }}</div>
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    // 监听 firstName、lastName有变化时,更新this.fullName
    firstName(val) {  
      this.fullName = val + ' ' + this.lastName
    },
    lastName(val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
})

上面代码是命令式且重复的。将它与计算属性的版本进行比较:

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName() {
      return this.firstName + ' ' + this.lastName
    }
  }
})

好得多了,不是吗?


– 5.1.4 计算属性的 setter

计算属性 默认只有 getter,不过在需要时你也可以提供一个 setter:

如果计算属性要被修改,那必须写 set 函数去响应修改,且 set 中要引起计算时依赖的数据发生改变。

// ...
computed: {
  fullName: {
    // getter
    get() {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set(newValue) {  // vm.fullName='John Doe' 会启动setter
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}
// ...

现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstNamevm.lastName 也会相应地被更新。


5.2 侦听器

const vm = new Vue({
	// ...
	watch:{  
	    //【对象的形式、函数的形式】
		//当isHot发生改变时, handler调用
		isHot:{  
			immediate:true, // 初始化时让handler调用一下
			deep:true,   // 监视多级结构中所有属性的变化
			handler(newValue, oldValue){
				console.log('isHot被修改了', newValue, oldValue)
			}
		},
		//简写:官方写法
		isHot(newValue, oldValue){
			console.log('isHot被修改了',newValue,oldValue,this)
		} 
		
		// 深度监听
	    'numbers.a':{
			handler(){
				console.log('a被改变了')
			}
		},
		/* numbers:{
			deep:true,
			handler(){
				console.log('numbers改变了')
			}
		} */
	} 
})
const vm = new Vue({
//...
})

vm.$watch('isHot',{
	immediate:true, 
	handler(newValue, oldValue){
		console.log('isHot被修改了',newValue, oldValue)
	}
})

//简写
vm.$watch('isHot',(newValue, oldValue)=>{
	console.log('isHot被修改了', newValue, oldValue, this)
}) 

虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。

这就是为什么 Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时,执行异步或开销较大的操作时,这个方式是最有用的。

例如:

<div id="watch-example">
  <p>
    Ask a yes/no question:
    <input v-model="question">
  </p>
  <p>{{ answer }}</p>
</div>
<!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 -->
<!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
<script>
var watchExampleVM = new Vue({
  el: '#watch-example',
  data: {
    question: '',
    answer: 'I cannot give you an answer until you ask a question!'
  },
  watch: {
    // 如果 `question` 发生改变,这个函数就会运行
    question(newQuestion, oldQuestion) {
      this.answer = 'Waiting for you to stop typing...'
      this.debouncedGetAnswer()
    }
  },
  created() {
    // `_.debounce` 是一个通过 Lodash 限制操作频率的函数。
    // 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率
    // AJAX 请求直到用户输入完毕才会发出。想要了解更多关于
    // `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识,
    // 请参考:https://lodash.com/docs#debounce
    this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
  },
  methods: {
    getAnswer: function () {
      if (this.question.indexOf('?') === -1) {
        this.answer = 'Questions usually contain a question mark. ;-)'
        return
      }
      this.answer = 'Thinking...'
      var vm = this
      axios.get('https://yesno.wtf/api')
        .then(function (response) {
          vm.answer = _.capitalize(response.data.answer)
        })
        .catch(function (error) {
          vm.answer = 'Error! Could not reach the API. ' + error
        })
    }
  }
})
</script>

除了 watch 选项之外,您还可以使用命令式的 vm.$watch API。

Vue官网:侦听器


END

;