前端MVC Vue2学习总结(三)——模板语法、过滤器、计算属性、观察者、Class 与 Style 绑定

时间:2022-02-25 00:31:35

Vue.js 使用了基于 HTML 的模版语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。所有 Vue.js 的模板都是合法的 HTML ,所以能被遵循规范的浏览器和 HTML 解析器解析。

在底层的实现上, Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,在应用状态改变时, Vue 能够智能地计算出重新渲染组件的最小代价并应用到 DOM 操作上。

如果你熟悉虚拟 DOM 并且偏爱 JavaScript 的原始力量,你也可以不用模板,直接写渲染(render)函数,使用可选的 JSX 语法。

一、模板语法

1.1、插值

1.1.1、文本

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

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

Mustache 标签将会被替代为对应数据对象上 msg 属性的值。无论何时,绑定的数据对象上 msg 属性发生了改变,插值处的内容都会更新

通过使用 v-once 指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上所有的数据绑定:

<span v-once>This will never change: {{ msg }}</span>

v-text指令:更新元素的 textContent。如果要更新部分的 textContent ,需要使用 {{ Mustache }} 插值。

<span v-text="msg"></span>
<!-- 和下面的一样 -->
<span>{{msg}}</span>

示例:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Vue2模板语法</title>
</head>
<body>
<div id="app1">
<input v-model="msg" />
<span v-once>{{msg}}</span>
<h3 v-text="msg"></h3>
<h3>{{msg}}</h3>
</div>
<script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var app1 = new Vue({
el: "#app1",
data:{
msg:"vue"
}
});
</script>
</body> </html>

结果:

前端MVC Vue2学习总结(三)——模板语法、过滤器、计算属性、观察者、Class 与 Style 绑定

1.1.2、纯HTML

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

<div v-html="rawHtml"></div>

被插入的内容都会被当做 HTML —— 数据绑定会被忽略。注意,你不能使用 v-html 来复合局部模板,因为 Vue 不是基于字符串的模板引擎。组件更适合担任 UI 重用与复合的基本单元

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

示例:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Vue2模板语法</title>
</head>
<body>
<div id="app1">
<textarea v-model="msg" rows="3" cols="30"></textarea>
<p>{{msg}}</p>
<p v-html="msg"></p>
</div>
<script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var app1 = new Vue({
el: "#app1",
data:{
msg:"<span style='color:blue;font-weight:bold'>zhangguo</span>"
}
});
</script>
</body> </html>

结果:

前端MVC Vue2学习总结(三)——模板语法、过滤器、计算属性、观察者、Class 与 Style 绑定

1.1.3、属性

Mustache 不能在 HTML 属性中使用,应使用 v-bind 指令:

<div v-bind:id="dynamicId"></div>

这对布尔值的属性也有效 —— 如果条件被求值为 false 的话该属性会被移除:

<button v-bind:disabled="someDynamicCondition">Button</button>

someDynamicCondition为true是disabled出现,为false时移除disabled

示例:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Vue2模板语法</title>
</head>
<body>
<div id="app1">
<button type="button" v-on:click="disabled=!disabled">反转{{disabled}}</button>
<button type="button" v-bind:disabled="disabled">提交</button>
</div>
<script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var app1 = new Vue({
el: "#app1",
data:{
disabled:true
}
});
</script>
</body> </html>

结果:

前端MVC Vue2学习总结(三)——模板语法、过滤器、计算属性、观察者、Class 与 Style 绑定

修改disabled的值为false

前端MVC Vue2学习总结(三)——模板语法、过滤器、计算属性、观察者、Class 与 Style 绑定

1.1.4、javascript表达式

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

{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>

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

{{ var a = 1 }}     <!-- 这是语句,不是表达式 -->
{{ if (ok) { return message } }} <!-- 流控制也不会生效,请使用三元表达式 -->

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

1.2、指令

指令(Directives)是带有 v- 前缀的特殊属性。指令属性的值预期是单一 JavaScript 表达式。指令的职责就是当其表达式的值改变时相应地将某些行为应用到 DOM 上

<p v-if="seen">Now you see me</p>

这里, v-if 指令将根据表达式 seen 的值的真假来移除/插入元素

常用的指令有:

示例:

<!DOCTYPE html>
<html> <head>
<meta charset="UTF-8">
<title>Vue2模板语法</title>
</head> <body>
<div id="app1">
<button type="button" v-on:click="isShow=!isShow">反转{{isShow}}</button>
<button type="button" v-if="isShow">提交v-if</button>
<button type="button" v-show="isShow">提交v-show</button>
<br/><br/>
<div>
等级A-C:<input v-model="type" />
</div>
<div v-if="type === 'A'">
A优秀
</div>
<div v-else-if="type === 'B'">
B良好
</div>
<div v-else-if="type === 'C'">
C及格
</div>
<div v-else>
D其它
</div>
</div>
<script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var app1 = new Vue({
el: "#app1",
data: {
isShow: true,
type: 'D'
}
});
</script>
</body> </html>

结果:

前端MVC Vue2学习总结(三)——模板语法、过滤器、计算属性、观察者、Class 与 Style 绑定

修改:

前端MVC Vue2学习总结(三)——模板语法、过滤器、计算属性、观察者、Class 与 Style 绑定

事件处理指令非常强大,说明如下:

v-on指令

  • 缩写:@

  • 预期:Function | Inline Statement | Object

  • 参数:event

  • 修饰符:

    • .stop - 调用 event.stopPropagation()
    • .prevent - 调用 event.preventDefault()
    • .capture - 添加事件侦听器时使用 capture 模式。
    • .self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
    • .{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
    • .native - 监听组件根元素的原生事件。
    • .once - 只触发一次回调。
    • .left - (2.2.0) 只当点击鼠标左键时触发。
    • .right - (2.2.0) 只当点击鼠标右键时触发。
    • .middle - (2.2.0) 只当点击鼠标中键时触发。
    • .passive - (2.3.0) 以 { passive: true } 模式添加侦听器
  • 用法:

    绑定事件监听器。事件类型由参数指定。表达式可以是一个方法的名字或一个内联语句,如果没有修饰符也可以省略。

    从 2.4.0 开始,v-on 同样支持不带参数绑定一个事件/监听器键值对的对象。注意当使用对象语法时,是不支持任何修饰器的。

    用在普通元素上时,只能监听 原生 DOM 事件。用在自定义元素组件上时,也可以监听子组件触发的自定义事件。

    在监听原生 DOM 事件时,方法以事件为唯一的参数。如果使用内联语句,语句可以访问一个 $event 属性:v-on:click="handle('ok', $event)"

示例:

<!-- 方法处理器 -->
<button v-on:click="doThis"></button> <!-- 对象语法 (2.4.0+) -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button> <!-- 内联语句 -->
<button v-on:click="doThat('hello', $event)"></button> <!-- 缩写 -->
<button @click="doThis"></button> <!-- 停止冒泡 -->
<button @click.stop="doThis"></button> <!-- 阻止默认行为 -->
<button @click.prevent="doThis"></button> <!-- 阻止默认行为,没有表达式 -->
<form @submit.prevent></form> <!-- 串联修饰符 -->
<button @click.stop.prevent="doThis"></button> <!-- 键修饰符,键别名 -->
<input @keyup.enter="onEnter"> <!-- 键修饰符,键代码 -->
<input @keyup.13="onEnter"> <!-- 点击回调只会触发一次 -->
<button v-on:click.once="doThis"></button>
  • 在子组件上监听自定义事件 (当子组件触发“my-event”时将调用事件处理器):

<my-component @my-event="handleThis"></my-component>

<!-- 内联语句 -->
<my-component @my-event="handleThis(123, $event)"></my-component> <!-- 组件中的原生事件 -->
<my-component @click.native="onClick"></my-component>

示例:

<!DOCTYPE html>
<html> <head>
<meta charset="UTF-8">
<title>Vue2模板语法</title>
</head> <body>
<div id="app1">
<div>
等级A-C:<input v-model="type" @keyup.13="submit_click($event)" />
</div>
</div>
<script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var app1 = new Vue({
el: "#app1",
data: {
isShow: true,
type: 'D'
},
methods: {
submit_click: function(e) {
if(confirm("确证要提交吗?")) {
console.log(e);
}
}
}
});
</script>
</body> </html>

结果:

前端MVC Vue2学习总结(三)——模板语法、过滤器、计算属性、观察者、Class 与 Style 绑定

示例:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Vue2 Demo</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app1" v-on:click="++n">
<div v-on:click="++n">
<a href="http://best.cnblogs.com" @click.stop.prevent.ctrl="go_click($event)">{{n}}</a>
</div>
<textarea cols="30" rows="5" @keyup.enter.ctrl="show" v-model="msg"></textarea>
<p>
{{msg}}
</p>
</div>
<script src="../../js/vue/vue.min.js"></script>
<script>
var vm = new Vue({
el: "#app1",
data: {
isShow: true,
students: ["tom", "mark", "rose"],
level: "A",
n: 1,
msg:""
},
methods: {
go_click: function (event) {
console.log(event);
//阻止事件冒泡
//event.stopPropagation();
//阻止默认事件
//event.preventDefault();
alert("准备进入博客园!");
},
show:function () {
console.log(this.msg);
}
}
});
</script>
</body>
</html>

运行结果:

前端MVC Vue2学习总结(三)——模板语法、过滤器、计算属性、观察者、Class 与 Style 绑定

1.2.1、参数

一些指令能接受一个“参数”,在指令后以冒号指明。例如, v-bind 指令被用来响应地更新 HTML 属性:

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

在这里 href 是参数,告知 v-bind 指令将该元素的 href 属性与表达式 url 的值绑定

v-on 指令用于监听 DOM 事件:

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

1.2.2、修饰符

修饰符(Modifiers)是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定:

<form v-on:submit.prevent="onSubmit"></form>
  • .stop - 调用 event.stopPropagation()。
  • .prevent - 调用 event.preventDefault()。
  • .capture - 添加事件侦听器时使用 capture 模式。
  • .self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
  • .{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
  • .native - 监听组件根元素的原生事件。
  • .once - 只触发一次回调。
  • .left - (2.2.0) 只当点击鼠标左键时触发。
  • .right - (2.2.0) 只当点击鼠标右键时触发。
  • .middle - (2.2.0) 只当点击鼠标中键时触发。
  • .passive - (2.3.0) 以 { passive: true } 模式添加侦听器

使用方法:

<!-- 方法处理器 -->
<button v-on:click="doThis"></button> <!-- 对象语法 (2.4.0+) -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button> <!-- 内联语句 -->
<button v-on:click="doThat('hello', $event)"></button> <!-- 缩写 -->
<button @click="doThis"></button> <!-- 停止冒泡 -->
<button @click.stop="doThis"></button> <!-- 阻止默认行为 -->
<button @click.prevent="doThis"></button> <!-- 阻止默认行为,没有表达式 -->
<form @submit.prevent></form> <!-- 串联修饰符 -->
<button @click.stop.prevent="doThis"></button> <!-- 键修饰符,键别名 -->
<input @keyup.enter="onEnter"> <!-- 键修饰符,键代码 -->
<input @keyup.13="onEnter"> <!-- 点击回调只会触发一次 -->
<button v-on:click.once="doThis"></button>

1.3、过滤器

Vue.js 允许你自定义过滤器,可被用作一些常见的文本格式化。过滤器可以用在两个地方:mustache 插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符指示:

{{ message | capitalize }}     <!-- in mustaches -->
<div v-bind:id="rawId | formatId"></div> <!-- in v-bind -->

Vue 2.x 中,过滤器只能在 mustache 绑定和 v-bind 表达式(从 2.1.0 开始支持)中使用,因为过滤器设计目的就是用于文本转换。为了在其他指令中实现更复杂的数据变换,你应该使用计算属性

过滤器函数总接受表达式的值作为第一个参数

new Vue({
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
})

过滤器可以串联:

{{ message | filterA | filterB }}

过滤器是 JavaScript 函数,因此可以接受参数:

{{ message | filterA('arg1', arg2) }}

这里,字符串 'arg1' 将传给过滤器作为第二个参数, arg2 表达式的值将被求值然后传给过滤器作为第三个参数

在vue1中有一些内置的过滤器,而vue2中需要自定义。

示例:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Vue2模板语法</title>
</head>
<body>
<div id="app1">
<span>{{msg | toUpper | toLower(" zhuhai")}}</span>
</div>
<div id="app2">
<span v-bind:title=" 'Hello Filter!' | toUpper">{{msg | toUpper | toLower}}</span>
</div>
<script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
//全局过滤器
Vue.filter("toUpper",function(value){
return value.toUpperCase();
}); var app1 = new Vue({
el: "#app1",
data:{
msg:"China"
},
filters:{ //带参数的当前实例过滤器
toLower:function(value,add){
return value.toLowerCase()+add;
}
}
}); //可以使用全局过滤器
var app2 = new Vue({
el: "#app2",
data:{
msg:"Vue"
}
});
</script>
</body>
</html> 

结果:

前端MVC Vue2学习总结(三)——模板语法、过滤器、计算属性、观察者、Class 与 Style 绑定

1.4、缩写

v- 前缀在模板中是作为一个标示 Vue 特殊属性的明显标识。当你使用 Vue.js 为现有的标记添加动态行为时,它会很有用,但对于一些经常使用的指令来说有点繁琐。同时,当搭建 Vue.js 管理所有模板的 SPA 时,v- 前缀也变得没那么重要了。因此,Vue.js 为两个最为常用的指令提供了特别的缩写:

1.4.1、v-bind 缩写

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

1.4.2、v-on 缩写

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

它们看起来可能与普通的 HTML 略有不同,但 : 与 @ 对于属性名来说都是合法字符,在所有支持 Vue.js 的浏览器都能被正确地解析。而且,它们不会出现在最终渲染的标记。缩写语法是完全可选的。

官方原文

二、计算属性和观察者

2.1、计算属性

在模板中绑定表达式是非常便利的,但是它们实际上只用于简单的操作。在模板中放入太多的逻辑会让模板过重且难以维护。例如:

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

在这种情况下,模板不再简单和清晰。在实现反向显示 message 之前,你应该确认它。这个问题在你不止一次反向显示 message 的时候变得更加糟糕。

这就是为什么任何复杂逻辑,你都应当使用计算属性。

2.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: {
// a computed getter
reversedMessage: function () {
// `this` points to the vm instance
return this.message.split('').reverse().join('')
}
}
})

结果:

Original message: "Hello"

Computed reversed message: "olleH"

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

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

你可以打开浏览器的控制台,修改 vm 。 vm.reversedMessage 的值始终取决于vm.message 的值。

你可以像绑定普通属性一样在模板中绑定计算属性。 Vue 知道 vm.reversedMessage 依赖于 vm.message ,因此当 vm.message 发生改变时,依赖于 vm.reversedMessage 的绑定也会更新。而且最妙的是我们是声明式地创建这种依赖关系:计算属性的 getter 是干净无副作用的,因此也是易于测试和理解的。

2.1.2、计算缓存 vs Methods

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

<p>Reversed message: "{{ reverseMessage() }}"</p>
// in component
methods: {
reverseMessage: function () {
return this.message.split('').reverse().join('')
}
}

不经过计算属性,我们可以在 method 中定义一个相同的函数来替代它。对于最终的结果,两种方式确实是相同的。然而,不同的是计算属性是基于它的依赖缓存。计算属性只有在它的相关依赖发生改变时才会重新取值。这就意味着只要 message 没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。

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

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

相比而言,每当重新渲染的时候,method 调用总会执行函数。

我们为什么需要缓存?假设我们有一个重要的计算属性 A ,这个计算属性需要一个巨大的数组遍历和做大量的计算。然后我们可能有其他的计算属性依赖于 A 。如果没有缓存,我们将不可避免的多次执行 A 的 getter !如果你不希望有缓存,请用 method 替代。

区别:

计算属性只能当作属性用,不能带参数,有缓存,效率高

方法可以直接调用,可带参数,没有缓存,每次调用都会执行,效率不如计算属性高。

2.1.3、计算属性 vs Watched Property

Vue.js 提供了一个方法 $watch ,它用于观察 Vue 实例上的数据变动。当一些数据需要根据其它数据变化时, $watch 很诱人 —— 特别是如果你来自 AngularJS 。不过,通常更好的办法是使用计算属性而不是一个命令式的 $watch 回调。思考下面例子:

<div id="demo">{{ fullName }}</div>
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar',
fullName: 'Foo Bar'
},
watch: {
firstName: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})

上面代码是命令式的和重复的。跟计算属性对比:

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

这样更好,不是吗?

2.1.4、计算 setter

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

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

现在在运行 vm.fullName = 'John Doe' 时, setter 会被调用, vm.firstName 和vm.lastName 也会被对应更新。

示例:

<!DOCTYPE html>
<html> <head>
<meta charset="UTF-8">
<title>计算属性和观察者</title>
</head> <body>
<div id="app1">
姓:<input v-model="x" /> 名:
<input v-model="m" /> 姓名:
<input v-model="xm" />
</div>
<script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var app1 = new Vue({
el: "#app1",
data: {
x: "",
m: ""
},
computed:{
xm:{
get:function(){
return this.x+this.m;
},
set:function(value){
this.x=value.substring(0,1);
this.m=value.substring(1);
}
}
}
});
app1.xm="张果";
</script>
</body> </html>

结果:

前端MVC Vue2学习总结(三)——模板语法、过滤器、计算属性、观察者、Class 与 Style 绑定

2.1.5、methods、watch与computed的区别

示例:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Vue2 Demo</title>
</head>
<body>
<div id="app1">
<p>
n3 <input v-model="n3"/>
</p>
<p>
方法 n1<input v-model="n1"/>+n2<input v-model="n2"/>={{add()}}
</p>
<p>
属性 n1 <input v-model="n1"/>+n2<input v-model="n2"/>=<input v-model="total"/>
</p>
<p>
监视 n1 <input v-model="n1"/>+n2<input v-model="n2"/>=<input v-model="sum"/>
</p>
</div>
<script src="../../js/vue/vue.min.js"></script>
<script> var vm = new Vue({
el: "#app1",
data: {
msg: "merry christmas!",
n1: 1000,
n2: 1000,
n3: 2000,
sum:0
},
//方法
methods: {
add: function () {
console.log("方法");
return parseInt(this.n1) + parseInt(this.n2);
}
},
//监视值的变化
watch: {
n1: {
handler:function (newV, oldV) {
console.log(newV, oldV);
this.sum=parseInt(this.n1) + parseInt(this.n2);
},
deep:true, //是否深度监视
immediate:true //首次也调用
},
n2: function (newV, oldV) {
console.log(newV, oldV);
this.sum=parseInt(this.n1) + parseInt(this.n2);
}
},
//计算属性
computed: {
total: {
get: function () {
return parseInt(this.n1) + parseInt(this.n2);
},
set: function (value) {
this.n1 = parseInt(value) / 2;
this.n2 = parseInt(value) / 2;
}
}
}
});
</script>
</body>
</html>

结果:

前端MVC Vue2学习总结(三)——模板语法、过滤器、计算属性、观察者、Class 与 Style 绑定

三者的区别:

计算属性computed:计算属性只能当作属性用,不能带参数,有缓存,效率高,,可以直接与v-model绑定。

方法methods:方法可以直接调用,可带参数,没有缓存,每次调用都会执行,效率不如计算属性高,不可与v-model绑定。

监视watch:不能显式调用(被监视的对象变化时被动调用),可以对变化的控制更加具体,但应用复杂,可以间接与v-model绑定。

2.2、观察 Watchers

虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的 watcher 。这是为什么 Vue 提供一个更通用的方法通过 watch 选项,来响应数据的变化。当你想要在数据变化响应时,执行异步操作或昂贵操作时,这是很有用的。

例如:

<div id="watch-example">
<p>
Ask a yes/no question:
<input v-model="question">
</p>
<p>{{ answer }}</p>
</div>
<!-- Since there is already a rich ecosystem of ajax libraries    -->
<!-- and collections of general-purpose utility methods, Vue core -->
<!-- is able to remain small by not reinventing them. This also -->
<!-- gives you the freedom to just use what you're familiar with. -->
<script src="https://unpkg.com/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://unpkg.com/lodash@4.13.1/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: function (newQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.getAnswer()
}
},
methods: {
// _.debounce 是一个通过 lodash 限制操作频率的函数。
// 在这个例子中,我们希望限制访问yesno.wtf/api的频率
// ajax请求直到用户输入完毕才会发出
// 学习更多关于 _.debounce function (and its cousin
// _.throttle), 参考: https://lodash.com/docs#debounce
getAnswer: _.debounce(
function () {
var vm = this
if (this.question.indexOf('?') === -1) {
vm.answer = 'Questions usually contain a question mark. ;-)'
return
}
vm.answer = 'Thinking...'
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
})
},
// 这是我们为用户停止输入等待的毫秒数
500
)
}
})
</script>

结果:

Ask a yes/no question:

I cannot give you an answer until you ask a question!

在这个示例中,使用 watch 选项允许我们执行异步操作(访问一个 API),限制我们执行该操作的频率,并直到我们得到最终结果时,才设置中间状态。这是计算属性无法做到的。

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

官方帮助: http://vuejs.org/guide/computed.html

示例:

<!DOCTYPE html>
<html> <head>
<meta charset="UTF-8">
<title>南方机器人</title>
</head> <body>
<div id="app1">
<p>
请输入您的问题:
<input v-model="question">
</p>
<p>{{ answer }}</p>
<p>
<img v-bind:src="src"/>
</p>
</div>
<script src="https://unpkg.com/lodash@4.13.1/lodash.min.js"></script>
<script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
<script src="https://unpkg.com/axios@0.12.0/dist/axios.min.js"></script>
<script type="text/javascript">
var watchExampleVM = new Vue({
el: '#app1',
data: {
question: '',
answer: '您问我一个问题我会给您一个答案!',
url:''
},
watch: {
// 如果 question 发生改变,这个函数就会运行
question: function(newQuestion) {
this.answer = '等待您的输入...'
this.getAnswer()
}
},
methods: {
// _.debounce 是一个通过 lodash 限制操作频率的函数。
// 在这个例子中,我们希望限制访问yesno.wtf/api的频率
// ajax请求直到用户输入完毕才会发出
// 学习更多关于 _.debounce function (and its cousin
// _.throttle), 参考: https://lodash.com/docs#debounce
getAnswer: _.debounce(
function() {
var vm = this
if(this.question.indexOf('?') === -1) {
vm.answer = '问题请以问号(?)结束'
return
}
vm.answer = '让我想想...'
axios.get('https://yesno.wtf/api')
.then(function(response) {
vm.answer = _.capitalize(response.data.answer);
vm.src=response.data.image;
console.log(response);
})
.catch(function(error) {
vm.answer = '发生了错误,不能调用该应用程序接口' + error
})
},
// 这是我们为用户停止输入等待的毫秒数
500
)
}
})
</script>
</body> </html>=

结果:

前端MVC Vue2学习总结(三)——模板语法、过滤器、计算属性、观察者、Class 与 Style 绑定

三、Class 与 Style 绑定

数据绑定一个常见需求是操作元素的 class 列表和它的内联样式。因为它们都是属性 ,我们可以用v-bind 处理它们:只需要计算出表达式最终的字符串。不过,字符串拼接麻烦又易错。因此,在 v-bind 用于 class 和 style 时, Vue.js 专门增强了它。表达式的结果类型除了字符串之外,还可以是对象或数组。

3.1、绑定 HTML Class

3.1.1、对象语法

我们可以传给 v-bind:class 一个对象,以动态地切换 class 。

<div v-bind:class="{ active: isActive }"></div>

上面的语法表示 classactive 的更新将取决于数据属性 isActive 是否为 真值 。

我们也可以在对象中传入更多属性用来动态切换多个 class 。此外, v-bind:class 指令可以与普通的 class 属性共存。如下模板:

<div class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>

如下 data:

data: {
isActive: true,
hasError: false
}

渲染为:

<div class="static active"></div>

当 isActive 或者 hasError 变化时,class 列表将相应地更新。例如,如果 hasError的值为 true , class列表将变为 "static active text-danger"

你也可以直接绑定数据里的一个对象:

<div v-bind:class="classObject"></div>
data: {
classObject: {
active: true,
'text-danger': false
}
}

渲染的结果和上面一样。我们也可以在这里绑定返回对象的 计算属性。这是一个常用且强大的模式:

<div v-bind:class="classObject"></div>
data: {
isActive: true,
error: null
},
computed: {
classObject: function () {
return {
active: this.isActive && !this.error,
'text-danger': this.error && this.error.type === 'fatal',
}
}
}

示例:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Class 与 Style 绑定</title>
<style>
.valide{
display: none;
}
.priceRequired{
display: inline;
}
.priceRange{
display: inline;
}
</style>
</head>
<body>
<div id="app1">
<span class="bgGreen" v-bind:class="{bgRed:isShow,bgBlue:isActive}">span1</span> <span class="bgGreen" v-bind:class="classObj1">span2</span> <p>
<label>价格:</label>
<input v-model="price" /><span class="valide" v-bind:class="priceValide1">必填</span><span class="valide" v-bind:class="priceValide2">介于0.1-99999之间</span>
</p>
</div> <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var app1 = new Vue({
el: "#app1",
data:{
isShow:true,
isActive:false,
classObj1:{
isHide:true,
isRight:false
},
price:0
},
computed:
{
priceValide1:function(){
return {
priceRequired:this.price===''
}
},
priceValide2:function(){
return {
priceRange:this.price!=''&&(this.price<0.1||this.price>99999)
}
}
}
}); </script>
</body>
</html>

结果:

前端MVC Vue2学习总结(三)——模板语法、过滤器、计算属性、观察者、Class 与 Style 绑定

3.1.2、数组语法

我们可以把一个数组传给 v-bind:class ,以应用一个 class 列表:

<div v-bind:class="[activeClass, errorClass]">
data: {
activeClass: 'active',
errorClass: 'text-danger'
}

渲染为:

<div class="active text-danger"></div>

如果你也想根据条件切换列表中的 class ,可以用三元表达式:

<div v-bind:class="[isActive ? activeClass : '', errorClass]">

此例始终添加 errorClass ,但是只有在 isActive 是 true 时添加 activeClass 。

不过,当有多个条件 class 时这样写有些繁琐。可以在数组语法中使用对象语法:

<div v-bind:class="[{ active: isActive }, errorClass]">

示例:

<!DOCTYPE html>
<html> <head>
<meta charset="UTF-8">
<title>Class 与 Style 绑定</title>
</head> <body>
<div id="app1">
<span class="bgGreen" v-bind:class="[isHide,isRight]">span3</span>
<span class="bgGreen" v-bind:class="[(isShow?'bg3':''),isRight,bg4]">span4</span>
<span class="bgGreen" v-bind:class="[{bg3:isShow},isRight,bg4]">span5</span>
</div> <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var app1 = new Vue({
el: "#app1",
data: {
isShow: true,
isHide: 'bg1',
isRight: 'bg2',
price: 0
} });
</script>
</body> </html>

结果:

前端MVC Vue2学习总结(三)——模板语法、过滤器、计算属性、观察者、Class 与 Style 绑定

3.1.3、With Components

This section assumes knowledge of  Vue Components. Feel free to skip it and come back later.

When you use the class attribute on a custom component, those classes will be added to the component’s root element. Existing classes on this element will not be overwritten.

For example, if you declare this component:

Vue.component('my-component', {
template: '<p class="foo bar">Hi</p>'
})

Then add some classes when using it:

<my-component class="baz boo"></my-component>

The rendered HTML will be:

<p class="foo bar baz boo">Hi</p>

The same is true for class bindings:

<my-component v-bind:class="{ active: isActive }"></my-component>

When isActive is truthy, the rendered HTML will be:

<div class="foo bar active"></div>

3.2、绑定内联样式

3.2.1、对象语法

v-bind:style 的对象语法十分直观——看着非常像 CSS ,其实它是一个 JavaScript 对象。 CSS 属性名可以用驼峰式(camelCase)或短横分隔命名(kebab-case):

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {
activeColor: 'red',
fontSize: 30
}

直接绑定到一个样式对象通常更好,让模板更清晰:

<div v-bind:style="styleObject"></div>
data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}

同样的,对象语法常常结合返回对象的计算属性使用。

3.2.2、数组语法

v-bind:style 的数组语法可以将多个样式对象应用到一个元素上:

<div v-bind:style="[baseStyles, overridingStyles]">

3.2.3、自动添加前缀

当 v-bind:style 使用需要特定前缀的 CSS 属性时,如 transform ,Vue.js 会自动侦测并添加相应的前缀。

官方帮助: http://vuejs.org/guide/class-and-style.html

四、示例下载

https://git.coding.net/zhangguo5/vue2.git

五、视频

https://www.bilibili.com/video/av17503637/

六、作业

6.1、请把v-on指令的每一种“修饰符”与调用方式都应用一遍。

<!-- 方法处理器 -->
<button v-on:click="doThis"></button>
<!-- 对象语法 (2.4.0+) -->
<button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
<!-- 内联语句 -->
<button v-on:click="doThat('hello', $event)"></button>
<!-- 缩写 -->
<button @click="doThis"></button>
<!-- 停止冒泡 -->
<button @click.stop="doThis"></button>
<!-- 阻止默认行为 -->
<button @click.prevent="doThis"></button>
<!-- 阻止默认行为,没有表达式 -->
<form @submit.prevent></form>
<!-- 串联修饰符 -->
<button @click.stop.prevent="doThis"></button>
<!-- 键修饰符,键别名 -->
<input @keyup.enter="onEnter">
<!-- 键修饰符,键代码 -->
<input @keyup.13="onEnter">
<!-- 点击回调只会触发一次 -->
<button v-on:click.once="doThis"></button>

6.2、请完成一个商品管理模块,要求如下:

  • 使用bootstrap+vue2技术实现
  • 添加与修改功能使用模式窗口
  • 支持全选与反选,隔行换色与光棒效果
  • 详细是点击时在弹出层中显示当前商品的所有信息
  • 尝试分页(选做)

前端MVC Vue2学习总结(三)——模板语法、过滤器、计算属性、观察者、Class 与 Style 绑定

6.3、增强商品管理

  • 数据从后台加载,请注意跨域(axios)
  • 实现搜索功能(Lodash)
  • 有验证、有错误提示
  • 增加删除与添加时的动画效果(选作)
  • 了解UIKit(选作)

6.4、复现所有上课示例

6.5、请完成一个智能机器人,重点练习使用vue-cli+app界面(任意框架)

接口:http://www.itpk.cn/robot.php、http://www.haoservice.com/docs/119

打包后在手机端根据提问给出答案,app界面