同步操作将从 tHero/VUE 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
#VUE
组件系统是 Vue
的另一个重要概念,因为它是一种抽象,允许我们使用小型、自包含和通常可复用的组件构建大型应用。仔细想想,几乎任意类型的应用界面都可以抽象为一个组件树:
在 Vue
里,一个组件本质上是一个拥有预定义选项的一个 Vue
实例,在 Vue
中注册组件很简单:
1、 定义一个组件
Vue.component('todo-item',{
props: ['todo'],
template:'<li>{{todo.text}}</li>'
});
2、在body
中定义一个标签
<ol>
<todo-item v-for="item in groceryList" v-bind:todo="item"></todo-item>
</ol>
3、定义VUE
对象
var app1 =new Vue({
el:'#app-1',
data:{
message:'hello vue',
groceryList:[{
text:'蔬菜'
},{
text:'奶酪'
},{
text:'苹果'
}]
}
});
在上述的代码中我们可以总结下面的规律
new Vue({})
可以进行创建一个vue
的对象,在{}
中定义的data
,可以在该对象中的message
可以进行双向的绑定,在该对象 下可以定义数组,对象,字符串,整型等类型;v-for
表示循环当前的dom
元素内容,其中item
表示循环体,groceryList
表示要循环的对象;- 注意下图中的描述问题
1、在图中1
标注的位置,表示第一个参数为dom
标签,vue
会根据该标签进行渲染元素,将元素替换为template
定义的值
还需要注意的是,两个红色选中的部分的内容是一致的; 注意,如果component
第一个参数为驼峰命名时,dom
则需要使用-
进行连接
2、在图中2
标注的位置,表示todo
可以访问到vue定义的数组对象,todo
是在VUE
中props
定义的属性,通过配置在图中5位置
中v-bind:todo
来标记属性,个人理解为钩子,连接js
与dom
直接对象传递的桥梁,todo
是dom
向js
传递的钩子;
3、 在图中3
标注的位置,表示VUE
的作用域,e1
表示绑定的dom
元素;
4、在图中5
标注的位置,表示循环体,获得到vue
内容data
定义的对象,并通过for
标签的形式来循环
component
中的template
进行渲染的页面;html
元素将会被替换,就是说<todo-item>
将会被template
元素替换component
的时候,在dom
的外层必须有一个标签包裹,下面有代码解释一下 Vue.component('user-list',{
props:['user'],
template:'<td>用户名</td><td>{{user.userName}}</td><td>性别</td><td>{{user.sex}}</td>'
})
如果我们使用了上述的代码进行设置template
模板将会报下图中出现的错误,提示必须添加一个根部的元素,加上<tr>
将会解决问题
其实这个很好理解,
VUE
将页面进行组件化了,他相当于一个树形的结构,为了满足树形结构,就必须 存在着根组织节点,通过他来完成页面的拼接
component
第一个参数为驼峰命名时,dom
则需要使用-
进行连接 Vue.component('todoItem',{
props: ['todo'],
template:'<li>{{todo.text}}</li>'
});
<todo-item v-for="item in groceryList" v-bind:todo="item"></todo-item>
如果为
<todoItem v-for="item in groceryList" v-bind:todo="item"></todoItem>
则不会显示循环
template
中输入传递的是一个对象,例如下图所示,我们在user
对象中定义了families对象,在template
中进行了输出,VUE
会将整个对象按照JSON
的格式进行输出,我们也可以直接通过对象的方式进行进一步的访问每个 Vue.js
应用都是通过构造函数 Vue
创建一个 Vue
的根实例 启动的:
var vm = new Vue({
// 选项
})
虽然没有完全遵循 MVVM
模式, Vue
的设计无疑受到了它的启发。因此在文档中经常会使用 vm
(ViewModel
的简称) 这个变量名表示 Vue
实例。
在实例化 Vue
时,需要传入一个选项对象,它可以包含数据、模板、挂载元素、方法、生命周期钩子等选项。全部的选项可以在 API
文档中查看。
可以扩展 Vue
构造器,从而用预定义选项创建可复用的组件构造器:
var MyComponent = Vue.extend({
// 扩展选项
})
// 所有的 `MyComponent` 实例都将以预定义的扩展选项被创建
var myComponentInstance = new MyComponent()
尽管可以命令式地创建扩展实例,不过在多数情况下建议将组件构造器注册为一个自定义元素,然后声明式地用在模板中。我们将在后面详细说明组件系统。
现在你只需知道所有的 Vue.js
组件其实都是被扩展的 Vue
实例。
每个 Vue
实例都会代理其 data
对象里所有的属性:
var data = { a: 1 }
var vm = new Vue({
data: data
})
vm.a === data.a // -> true
// 设置属性也会影响到原始数据
vm.a = 2
data.a // -> 2
// ... 反之亦然
data.a = 3
vm.a // -> 3
注意只有这些被代理的属性是响应的
。如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。我们将在后面详细讨论响应系统。
注意只有这些被代理的属性是响应的。如果在实例创建之后添加新的属性到实例上,它不会触发视图更新。我们将在后面详细讨论响应系统。
除了 data
属性, Vue
实例暴露了一些有用的实例属性与方法。这些属性与方法都有前缀 $
,以便与代理的 data
属性区分。例如:
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 (newVal, oldVal) {
// 这个回调将在 `vm.a` 改变后调用
})
Vue
实例暴露了一些有用的实例属性与方法。这些属性与方法都有前缀 $
,如下图所示;
我们自己的定义的方法,如message
、user
都是不含有$
符合的如下图
下面我们尝试使用控制台的形式来操作一下对象,观察页面元素,可以通过app1来访问对象性中的属性
如果我们修改其中的值页面是否会跟随其改变呢?通过在控制台处理的对象中的属性,我们看到页面也跟随其 改变
注意,不要在实例属性或者回调函数中(如
vm.$watch('a', newVal => this.myMethod())
)使用箭头函数。因为箭头函数绑定父上下文, 所以this
不会像预想的一样是Vue
实例,而是this.myMethod
未被定义。
每个 Vue
实例在被创建之前都要经过一系列的初始化过程。例如,实例需要配置数据观测(data observer)
、
编译模版、挂载实例到 DOM
,然后在数据变化时更新 DOM
。在这个过程中,实例也会调用一些
生命周期钩子 ,这就给我们提供了执行自定义逻辑的机会。例如,created
这个钩子在实例被创建之后被
调用:
var vm = new Vue({
data: {
a: 1
},
created: function () {
// `this` 指向 vm 实例
console.log('a is: ' + this.a)
}
})
// -> "a is: 1"
也有一些其它的钩子,在实例生命周期的不同阶段调用,如 mounted
、 updated
、destroyed
。
钩子的 this
指向调用它的 Vue
实例。一些用户可能会问 Vue.js
是否有“控制器”的概念?答案是,
没有。组件的自定义逻辑可以分布在这些钩子中。
如何更好的理解钩子呢?下面的图展示的是js与DOM直接的钩子,进行的参数绑定,来完成页面
的渲染,有时候我们还componet
与VUE
、DOM
之间同样存在着类似的钩子,我们看下是如
何控制与管理的
查看下面的图可以观察一下componet
与VUE
、DOM
之间的交互
下图说明了实例的生命周期。你不需要立马弄明白所有的东西,不过以后它会有帮助。
根据我们之前学习的东西,我们可以简单的对生命周期进行测试
var app1 =new Vue({
el:'#app-1',
data: {
message: 'hello vue',
},
created: function () {
console.log("执行created")
},
beforeCreate:function(){
console.log("执行beforeCreate")
},
beforeMount:function () {
console.log("执行beforeMount");
},
mounted:function () {
console.log("执行mounted");
},
beforeUpdate:function () {
console.log("执行beforeUpdate");
},
updated:function () {
console.log("执行updated");
},
beforeDestory:function () {
console.log("beforeDestory");
},
destroyed:function () {
console.log("beforeDestory");
}
});
<div id="app-1">
{{message}}
</div>
先看下官方的图
根据上面的代码我们测试获得生命周期为
beforeCreate
、create
、beforeMount
、mounted
,从图中标注的1
可以看出执行属性beforeUpdate
、Update
并没有被执行;如图控制台的执行顺序可以观察出message
的值,在图中2
步骤,执行后,图中3
被执行了,输出了beforeUpdate
、Update
由此我们可以得知,如果想处理页面绑定中的逻辑时,可以在该方法中进行处理Vue.js
使用了基于 HTML
的模版语法,允许开发者声明式地将 DOM
绑定至底层 Vue
实例的数据。所有Vue.js
的模板都是合法的 HTML
,所以能被遵循规范的浏览器和HTML
解析器解析。
在底层的实现上, Vue
将模板编译成虚拟 DOM
渲染函数。结合响应系统,在应用状态改变时, Vue
能够智能地计算出重新渲染组件的最小代价并应用到 DOM
操作上。
如果你熟悉虚拟 DOM
并且偏爱 JavaScript
的原始力量,你也可以不用模板,直接写渲染(render)
函数,使用可选的 JSX
语法。
这里描述了一个概念即虚拟DOM
,这里简单的描述一下这个概念
1.为什么需要虚拟DOM
DOM
是很慢的,其元素非常庞大,页面的性能问题鲜有由JS
引起的,大部分都是由DOM
操作引起的。
如果对前端工作进行抽象的话,主要就是维护状态和更新视图;而更新视图和维护状态都需要DOM
操作。
其实近年来,前端的框架主要发展方向就是解放DOM
操作的复杂性。
2.理解虚拟DOM
虚拟的DOM
的核心思想是:对复杂的文档DOM
结构,提供一种方便的工具,进行最小化地DOM
操作。这句话,也许过于抽象,
却基本概况了虚拟DOM
的设计思想
具体的内容可以查看http://www.ituring.com.cn/article/211352进行学习
数据绑定最常见的形式就是使用“Mustache”
语法(双大括号)的文本插值:
<span>Message: {{ msg }}</span>
Mustache
标签将会被替代为对应数据对象上 msg
属性的值。无论何时,绑定的数据对象上 msg
属性发生了改变,插值处的内容都会更新。
通过使用v-once
指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上所有的数据绑定:
<span v-once>This will never change: {{ msg }}</span>
VUE
data
中没有进行定义那么会报错,如下图所示这里还需要注意一个问题,当我们在控制台进行更改某一个变量的值时,上面的错误仍会抛出!
被标记为v-once
的元素,在控制台进行修改该值得时候,是无法进行修改的,需要特别注意,
该标签下的所有被绑定的属性,都只能被绑定一次
双大括号会将数据解释为纯文本,而非 HTML 。为了输出真正的 HTML ,你需要使用 v-html 指令:
<div v-html="rawHtml"></div>
被插入的内容都会被当做 HTML
—— 数据绑定会被忽略。注意,你不能使用 v-html
来复合局部模板,因为 Vue
不是基于字符串的模板引擎。
组件更适合担任 UI
重用与复合的基本单元。
你的站点上动态渲染的任意 HTML
可能会非常危险,因为它很容易导致 XSS
攻击。请只对可信内容使用 HTML
插值,
绝不要对用户提供的内容插值。
我在mustacheHtml.html
页面中测试了html绑定的内容;
v-html
可以向页面中输出dom
元素,但是在dom
中绑定的属性,是不能被渲染的,如上图所示;v-hmtl
进行大量的DOM
渲染,也就是不能使用复合布局排版,如果想使用的话,推荐使用component
的方法Mustache
不能在 HTML
属性中使用,应使用v-bind
指令:
<div v-bind:id="dynamicId"></div>
这对布尔值的属性也有效 —— 如果条件被求值为 false
的话该属性会被移除:
<button v-bind:disabled="someDynamicCondition">Button</button>
下面的代码很好的演示了,使用{{}}
的形式来绑定属性是报出的错误
v-bind:id="dynamicId"
在属性的前面添加v-bind:
进行标注,表示要绑定的属性,双引号直接则表示在VUE
data
定义的属性名称
迄今为止,在我们的模板中,我们一直都只绑定简单的属性键值。但实际上,对于所有的数据绑定, 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 } }}
模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 Math
和 Date
。你不应该在模板表达式中试图访问用户定义
的全局变量。
譬如
<p>{{Math.random()}}</p>
是否可以访问到我们自定仪的全局变量呢?
通过下面图我们可以看出,在{{}}
直接我们是可以直接访问到Math
对象的,但是我们定义在页面中的全局变量topName
是不能访问到的;
{{}}
不支持定义变量,不支持含有大括号
指令(Directives)
是带有v-
前缀的特殊属性。指令属性的值预期是单一 JavaScript
表达式(除了 v-for
,之后再讨论)。指令
的职责就是当其表达式的值改变时相应地将某些行为应用到DOM
上。让我们回顾一下在介绍里的例子:
<p v-if="seen">Now you see me</p>
这里, v-if
指令将根据表达式seen
的值的真假来移除/插入<p>
元素。
v-if
不仅仅可以可以支持单独的校验,还支持使用计算;如下图中的number+1==2
一些指令能接受一个“参数”,在指令后以冒号指明。例如, v-bind
指令被用来响应地更新HTML
属性:
<a v-bind:href="url"></a>
在这里 href
是参数,告知 v-bind
指令将该元素的 href
属性与表达式 url
的值绑定。
另一个例子是v-on
指令,它用于监听DOM
事件:
<a v-on:click="doSomething">
在这里参数是监听的事件名。我们也会更详细地讨论事件处理。
这里要说明一个问题,在上面我们使用了v-for
的形式来进行遍历数据,在我们就是使用了参数这种形式
修饰符(Modifiers)
是以半角句号 . 指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent
修饰符告诉 v-on
指令对于触发的事件调用 event.preventDefault():
<form v-on:submit.prevent="onSubmit"></form>
之后当我们更深入地了解 v-on
与 v-model
时,会看到更多修饰符的使用。
Vue.js
允许你自定义过滤器,可被用作一些常见的文本格式化。过滤器可以用在两个地方:mustache
插值和 v-bind
表达式。
过滤器应该被添加在 JavaScript
表达式的尾部,由“管道”符指示:
<!-- in mustaches -->
{{ message | capitalize }}
<!-- in v-bind -->
<div v-bind:id="rawId | formatId"></div>
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 }}
lightColor
3
个属性,分别表示着红绿灯的颜色,如图中1
标签;vue
中定义了一个方法,过滤方法,通过该方法我们可以过滤字段,这里传入一个参数,在图中3标志,
通过该value可以获得属性值,然后在进行过滤{{}}
中定义了|
后面的表示过滤方法,我们定义的是filterColor
{{}}
是支持多重过滤的,我们可以定义多个过滤方法,来过滤相关属性由于{{}}
是支持多重过滤的,所以我们要注意一下每一层过滤后的值是否发生了改变?从上面的图中,我们
为lightColor
中添加了两个过滤器,分别为filterColor
、filterPass
,在经过第一个过滤器filterColor
后,进入到filterPass
中的Value
发生了变化,有1
变成了黄灯
上述的例子展示出了{{}}
支持v-bind
指令
过滤器是 JavaScript
函数,因此可以接受参数:
{{ message | filterA('arg1', arg2) }}
这里,字符串 'arg1'
将传给过滤器作为第二个参数, arg2
表达式的值将被求值然后传给过滤器作为第三个参数。
从上面的图中我们可以看出,在html
中定义的个参数,filterhttp('12','32')
进入filter
方法后,将会变为第2和第3个参数
如果在程序中没有定义filter
方法,将会报出下面的错误
v-
前缀在模板中是作为一个标示 Vue
特殊属性的明显标识。当你使用Vue.js
为现有的标记添加动态行为时,它会很有用,
但对于一些经常使用的指令来说有点繁琐。同时,当搭建 Vue.js
管理所有模板的 SPA
时,v-
前缀也变得没那么重要了。因此,
Vue.js
为两个最为常用的指令提供了特别的缩写:
v-bind
缩写
<!-- 完整语法 -->
<a v-bind:href="url"></a>
<!-- 缩写 -->
<a :href="url"></a>
v-on
缩写
<!-- 完整语法 -->
<a v-on:click="doSomething"></a>
<!-- 缩写 -->
<a @click="doSomething"></a>
<a v-bind:href="url" v-bind:title="url | filterHttp('12','32')" >百度么?</a>
<a :href="url" :title="url | filterHttp('12','32')" >缩写绑定v-bind</a>
它们看起来可能与普通的 HTML
略有不同,但 :
与 @
对于属性名来说都是合法字符,在所有支持 Vue.js
的浏览器都能被正确地解析。而且,它们不会出现在最终渲染的标记。缩写语法是完全可选的,但随着你更深入地了
解它们的作用,你会庆幸拥有它们。
模板内的表达式是非常便利的,但是它们实际上只用于简单的运算。在模板中放入太多的逻辑会让模板过重且难以维护。例如:
<div id="example">
{{ message.split('').reverse().join('') }}
</div>
在这种情况下,模板不再简单和清晰。在意识到这是反向显示 message
之前,你不得不再次确认第二遍。当你想要在模板中多次反向显示
message
的时候,问题会变得更糟糕。这就是对于任何复杂逻辑,你都应当使用计算属性的原因。
<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
是没有副作用,这使得它易于测试和推理。
computed
方法中定义的reversedMessage
,这个方法中的this
只向的是vm
从图中可以看出
我们在学习filter的时候,官方介绍提供的是一个方法,可以添加参数,那么computed
中是否也支持方法
传递参数呢 ?通过下图我们可以看出,我们添加了方法,但是控制台报错了!
同样我们去掉{{}}
中的方法中的参数仍然报错
由此我们可以得出一个结论,在
computed
定义的方法,虽然在结构上可以看出他是个方法,但是,是不能通过方法的形式来调用的 其最终返回的时候一个属性
我们这里使用了2
中方法来进行输出message
的值,当我们在控制台调用reverMessage
的时候,发现抛出不是个function
的错误
你可能已经注意到我们可以通过调用表达式中的 method
来达到同样的效果:
<p>Reversed message: "{{ reversedMessage() }}"</p>
// in component
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
我们可以将同一函数定义为一个 method
而不是一个计算属性。对于最终的结果,两种方式确实是相同的。然而,不同的是计算属性
是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要 message
还没有发生改变,
多次访问 reversedMessage
计算属性会立即返回之前的计算结果,而不必再次执行函数。
这也同样意味着下面的计算属性将不再更新,因为 Date.now()
不是响应式依赖:
computed: {
now: function () {
return Date.now()
}
}
相比而言,只要发生重新渲染,method
调用总会执行该函数。
我们为什么需要缓存?假设我们有一个性能开销比较大的的计算属性 A
,它需要遍历一个极大的数组和做大量的计算。然后我们可能有
其他的计算属性依赖于A
。如果没有缓存,我们将不可避免的多次执行 A
的 getter!
如果你不希望有缓存,请用method
替代。
通过项目中的computed.html
我们可以看出上面的结论,当页面初始化时
定义在computed reverMessage
和methods reverMessage1
都被执行了
下面通过一些步骤来测试
message
进行赋值时,值不发生改变,如下图中的2
computed reverMessage
和methods reverMessage1
都没有被执行;messsage
的值进行修改时,computed reverMessage
和methods reverMessage1
均被执行了;如图中第3
步methods reverMessage1
的方法是,computed reverMessage
没有被执行computed reverMessage
中定义的值是存在缓存的,当值没有发生变化的时候,定义在computed reverMessage
将不会发生改变;methods reverMessage1
只要调用就会被执行;Vue
确实提供了一种更通用的方式来观察和响应Vue
实例上的数据变动:watch
属性。当你有一些数据需要随着其它数据变动而变动时,
你很容易滥用 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: function (val) {
this.fullName = val + ' ' + this.lastName
},
lastName: function (val) {
this.fullName = this.firstName + ' ' + val
}
}
})
上面代码是命令式的和重复的。将它与 computed
属性的版本进行比较:
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
下面我们通过实例来研究一下问题,watch
方法,提供两个参数,在图中5
所示的位置,val
表示原始数据;b
表示更改
后的数据;
在图1
中,如果我们修改了lastName
的值;lastName
的watch
与fullName
的watch
都会被执行;原则是,当oldValue
与newValue
不同时,对应的watch
都会被执行;个人认为官方提供的watch
替换为computed
的例子有些特殊;当属性没有
相互关联时,就可以使用watch
方法
。当你想要在数据变化响应时,执行异步操作或开销较大的操作,这是很有用的。
计算属性默认只有 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
也相应地会被更新。
set
方法中定义的内容可以放到get
中可以达到同样的效果Class
与Style
绑定数据绑定一个常见需求是操作元素的 class
列表和它的内联样式。因为它们都是属性 ,我们可以用v-bind
处理它们:只需要计算出表达式最
终的字符串。不过,字符串拼接麻烦又易错。因此,在 v-bind
用于 class
和 style
时,Vue.js
专门增强了它。表达式的结果类型除了字
符串之外,还可以是对象或数组。
我们可以传给 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"。
1
页面初始化的时候,页面进行加载起初为红色 ;2
当修改app.isActivie = false
的时候;{ active: isActive, 'text-danger': hasError }
没有任何一个class被显示出来;3
与4
表明了data
中的数据是可以动态的进行修改;5
上面的格式类似于javascript
中对象的定义,为true
的将会被返回,返回css
样式,页面会被加载你也可以直接绑定数据里的一个对象:
<div v-bind:class="classObject"></div>
<div v-bind:class="classObject"></div>
data: {
classObject: {
active: true,
'text-danger': false
}
}
渲染的结果和上面一样。
classObject
在app
中没有定义,将会抛出下面的错误vue.js:435 [Vue warn]: Property or method "classObject" is not defined
on the instance but referenced during render. Make sure to declare reactive
data properties in the data option.
(found in <Root>)
class
的定义出现语法错误的时候,譬如: <div v-bind:class="{active:isActive , greenColor:}">active</div>
,将会抛出下面的错误
class
可以理解为对象;
那么在data
定义的东西我们是否可以写成函数,然后返回对象呢? var app = new Vue({
el:'#app-1',
data:{
isActive:true,
hasPass:false,
classObject:{
active:true,
greenColor:function () {
return false;
}
}
}
});
通过上面的代码我们发现,greenColor
可以被浏览器正确的解析,但是有个问题,
如果我们在控制台直接访问greenColor
的时候是需要通过函数式的方式;譬如:
app.classObject.greenColor()
,尝试在greenColor
定义一个有参的数据譬如:
greenColor:function (a) {
if(a=="123"){
a = true;
}else{
a = false;
}
console.log(a);
return a;
}
在控制台进行进行测试,发现虽然a
的值发生了改变,但是却不能引起页面的渲染
我们也可以在这里绑定返回对象的计算属性。这是一个常用且强大的模式:
<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',
}
}
}
computedClassObject
来修改他的值得时候,页面并没有发生
变化如下图我们可以把一个数组传给 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]">
我们可以通过修改app
定义的activeClass
或errorClass
属性来修改class
样式
app
中定义一个数组,如下图的arrayObject
,我们还可以通过动态的处理数组内容
来控制 css
样式active
的时候,我们通过控制台添加相同的class
class
会被
重复性的添加当你在一个定制的组件上用到 class 属性的时候,这些类将被添加到根元素上面,这个元素上已经存在的类不会被覆盖。 例如,如果你声明了这个组件:
Vue.component('my-component', {
template: '<p class="foo bar">Hi</p>'
})
然后在使用它的时候添加一些 class:
<my-component class="baz boo"></my-component>
HTML 最终将被渲染成为:
<p class="foo bar baz boo">Hi</p>
同样的适用于绑定HTML class
:
<my-component v-bind:class="{ active: isActive }"></my-component>
当 isActive
为 true
的时候,HTML
将被渲染成为:
<p class="foo bar active">Hi</p>
v-bind
是不能够起到作用的,譬如,控制台将会报错;错误内容为如下;但是却可以添加到DOM
中vue.js:435 [Vue warn]: Property or method "arrayObject" is not defined on the instance
but referenced during render.
Make sure to declare reactive data properties in the data option.
found in
<MyComponent>
<Root>
v-bind:style
的对象语法十分直观——看着非常像 CSS
,其实它是一个 JavaScript
对象。 CSS
属性名可以用驼峰式(camelCase)
或短横分隔命名(kebab-case)
:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {
activeColor: 'red',
fontSize: 30
}
sytle
的绑定同时也是支持动态修改的,我们在控制台修改active
的
样式时,页面的样式会被修改
直接绑定到一个样式对象通常更好,让模板更清晰:
<div v-bind:style="styleObject"></div>
data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
同样的,对象语法常常结合返回对象的计算属性使用。
<div v-bind:style="[fontSizes]">数组形式样式</div>
1
的位置,我们在data
中顶一个font-size
是个字符串,但是该字符串并不能应用到html
中2
的位置, styleObject
定义的就是对象,可以进行页面的渲染;自动添加前缀
当 v-bind:style
使用需要特定前缀的CSS
属性时,如 transform
,Vue.js
会自动侦测并添加相应的前缀。
多重值
2.3.0+
从 2.3 开始你可以为style
绑定中的属性提供一个包含多个值的数组,常用于提供多个带前缀的值:
在字符串模板中,如 Handlebars ,我们得像这样写一个条件块:
{{#if ok}}
<h1>Yes</h1>
{{/if}}
在 Vue.js ,我们使用 v-if 指令实现同样的功能:
<h1 v-if="ok">Yes</h1>
也可以用 v-else 添加一个 “else” 块:
<h1 v-if="ok">Yes</h1>
<h1 v-else>No</h1>
v-else
没有v-if
将会报错,如下:vue.js:435 [Vue warn]: Error compiling template:
<div id="app-1">
<h1 v-else="">NO</h1>
</div>
- v-else used on element <h1> without corresponding v-if.
(found in <Root>)
v-if
中动态修改data
中的值,是可以动态显示与隐藏的<template>
中 v-if
条件组因为 v-if
是一个指令,需要将它添加到一个元素上。但是如果我们想切换多个元素呢?此时我们可以把一个
<template>
元素当做包装元素,并在上面使用 v-if
。最终的渲染结果不会包含<template>
元素。
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
template
没有被渲染到页面上
v-else-if
2.1.0 新增
v-else-if
,顾名思义,充当v-if
的“else-if
块”。可以链式地使用多次:
<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>
Not A/B/C
</div>
类似于 v-else
,v-else-if
必须紧跟在 v-if
或者 v-else-if
元素之后。
[Vue warn]: Error compiling template:
- v-else-if=" type === 'C' " used on element <div> without corresponding v-if.
- v-else used on element <div> without corresponding v-if.
(found in <Root>)
Vue
会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做,除了使 Vue
变得非常快之外,还有一些有用的好处。
例如,如果你允许用户在不同的登录方式之间切换:
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address">
</template>
那么在上面的代码中切换 loginType
将不会清除用户已经输入的内容。因为两个模版使用了相同的元素,<input>
不会被替换掉——仅仅是替换了它的的 placeholder
。
自己动手试一试,在输入框中输入一些文本,然后按下切换按钮:
这样也不总是符合实际需求,所以Vue
为你提供了一种方式来声明“这两个元素是完全独立的——不要复用它们”。只需添加一个具有唯一值的
key
属性即可:
<template v-if="loginType === 'username'">
<label>Username</label>
<input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
<label>Email</label>
<input placeholder="Enter your email address" key="email-input">
</template>
现在,每次切换时,输入框都将被重新渲染。你自己看:
注意, <label>
元素仍然会被高效地复用,因为它们没有添加key
属性。
VUE
会最大程度上的利用元素,如果是这样,我们在第一个input中输入一个数据,如果他复用了
那么input表单中的值将不会消失,如下图:同时仔细观察页面中的dom
,
VUE
只是修改了label
中的标签和input
中的name
下面我们添加上了key
,观察下面的Gif
动图,页面中定义的<p>这个内容
没有发生任何的变化,同时input
的表单
也发生了改变
另一个用于根据条件展示元素的选项是 v-show
指令。用法大致一样:
<h1 v-show="ok">Hello!</h1>
不同的是带有 v-show
的元素始终会被渲染并保留在 DOM
中。v-show
是简单地切换元素的 CSS
属性 display
。
注意, v-show
不支持 <template>
语法,也不支持 v-else
。
v-if
是“真正的”条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下, v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS
进行切换。
一般来说, v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,
则使用 v-show
较好;如果在运行时条件不太可能改变,则使用v-if
较好。
这里我们可以理解为,如果是类似于数据字典类型的显示与隐藏使用v-if如果div或者模块显示与隐藏使用show
ok
为false
template
中间的元素不会被隐藏 <template v-show="ok">
<p>hhh</p>
<p>hhh2</p>
</template>
我们用 v-for
指令根据一组数组的选项列表进行渲染。 v-for
指令需要以item in items
形式的特殊语法,
items
是源数据数组并且 item
是数组元素迭代的别名。
基本用法
<ul id="example-1">
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
var example1 = new Vue({
el: '#example-1',
data: {
items: [
{message: 'Foo' },
{message: 'Bar' }
]
}
})
结果:
Foo
Bar
item in items
其中的item
表示循环的变量,代表着每一个元素;可以据此来获得message
值在v-for
块中,我们拥有对父作用域属性的完全访问权限。v-for
还支持一个可选的第二个参数为当前项的索引。
<ul id="example-2">
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
</ul>
var example2 = new Vue({
el: '#example-2',
data: {
parentMessage: 'Parent',
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})
结果:
Parent - 0 - Foo
Parent - 1 - Bar
1
处:{{item.message}}
获得定义在数组中过的对象;2
处:{{index}}
获得索引的下标,这里要注意,索引的下标是可以直接访问的,不需要item
来访问;3
处:(item,index)
定义两个参数,第一个item
循环变量;index
索引下标,该处的in
可以更改为of
也可以完成循环你也可以用 of
替代 in
作为分隔符,因为它是最接近 JavaScript
迭代器的语法:
<div v-for="item of items"></div>
如同v-if
模板,你也可以用带有v-for
的<template>
标签来渲染多个元素块。例如:
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider"></li>
</template>
</ul>
你也可以用 v-for
通过一个对象的属性来迭代。
<ul id="repeat-object" class="demo">
<li v-for="value in object">
{{ value }}
</li>
</ul>
new Vue({
el: '#repeat-object',
data: {
object: {
FirstName: 'John',
LastName: 'Doe',
Age: 30
}
}
})
结果:
John
Doe
30
在遍历对象时,是按Object.keys()
的结果遍历,但是不能保证它的结果在不同的 JavaScript
引擎下是一致的。
3
处:单个对象遍历属性的方法; value in Object
可以进行遍历对象属性;直接通过{{value}}
获得值如果获得属性的名称?
你也可以提供第二个的参数为键名:
<div v-for="(value, key) in object">
{{ key }} : {{ value }}
</div>
第三个参数为索引:
<div v-for="(value, key, index) in object">
{{ index }}. {{ key }} : {{ value }}
</div>
1
处:v-for
定义中 (value,index)
可以进行访问值,和索引下标;2
处:v-for
定义中还可以接收3
个参数,value
,key
index
分别代码值,键,索引v-for
定义的参数,是和顺序有关系,和名称无关;譬如:v-for
v-for
也可以取整数。在这种情况下,它将重复多次模板。
<div>
<span v-for="n in 10">{{ n }}</span>
</div>
v-for with v-if
当它们处于同一节点, v-for
的优先级比 v-if
更高,这意味着 v-if
将分别重复运行于每个 v-if
循环中。当你想为仅有的 一些
项渲染节点时,这种优先级的机制会十分有用,如下:
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo }}
</li>
上面的代码只传递了未complete
的todos
。
而如果你的目的是有条件地跳过循环的执行,那么将 v-if 置于包装元素 (或 <template>
)上。如:
<ul v-if="shouldRenderTodos">
<li v-for="todo in todos">
{{ todo }}
</li>
</ul>
v-if
的判断,来输出内容,上图的代码中,只有status
为2
的被展示出来了而如果你的目的是有条件地跳过循环的执行,那么将 v-if 置于包装元素 (或 <template>)上
。如:
<ul v-if="shouldRenderTodos">
<li v-for="todo in todos">
{{ todo }}
</li>
</ul>
当 Vue.js
用 v-for
正在更新已渲染过的元素列表时,它默认用 “就地复用” 策略。如果数据项的顺序被改变,Vue
将不是移动 DOM
元素
来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。这个类似 Vue 1.x
的
track-by="$index"
。这个默认的模式是有效的,但是只适用于不依赖子组件状态或临时 DOM 状态(例如:表单输入值)的列表渲染输出。
为了给Vue
一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一key
属性。理想的 key
值是每项都有唯一 id
。这个特殊的属性相当于Vue 1.x
的 track-by
,但它的工作方式类似于一个属性,所以你需要用 v-bind
来绑定动态值
(在这里使用简写):
<div v-for="item in items" :key="item.id">
<!-- 内容 -->
</div>
建议尽可能使用 v-for
来提供 key
,除非迭代 DOM
内容足够简单,或者你是故意要依赖于默认行为来获得性能提升。
因为它是Vue
识别节点的一个通用机制,key
并不特别与v-for
关联,key
还具有其他用途,我们将在后面的指南中看到其他用途。
key
,但是在html
页面中,并没有将id
渲染到对应的元素上!数组更新检测
变异方法
Vue
包含一组观察数组的变异方法,所以它们也将会触发视图更新。这些方法如下:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
你打开控制台,然后用前面例子的 items
数组调用变异方法:example1.items.push({ message: 'Baz' })
。
重塑数组变异方法(mutation method)
,顾名思义,会改变被这些方法调用的原始数组。相比之下,也有非变异(non-mutating method)
方法,
例如: filter()
, concat()
, slice()
。这些不会改变原始数组,但总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组:
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
filter
,页面中的DOM
元素并没有发生改变;上图中1
2
表述了这个现象,究其原因,是因为filter
返回的
是一个新的数组,并没有影响到原有数组内容;使用上面的代码可以进行替换旧数组你可能认为这将导致 Vue
丢弃现有DOM
并重新渲染整个列表。幸运的是,事实并非如此。 Vue
实现了一些智能启发式方法来最大化 DOM
元素重用,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。
注意事项
由于 JavaScript
的限制, Vue
不能检测以下变动的数组:
当你利用索引直接设置一个项时,例如: vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如: vm.items.length = newLength
为了解决第一类问题,以下两种方式都可以实现和vm.items[indexOfItem] = newValue
相同的效果, 同时也将触发状态更新:
app.todos[0].todoName='拜见女方家长'
通过数组的方式直接访问到元素,并修改它的属性,页面进行了渲染;app.todos[1] ={}
通过直接修改一个数组的元素;页面没有发生任何的改变;app.todos
发现数据发生了改变,但是页面并没有渲染;通过下面的方法可以修改数组中的值;并进行渲染页面,这个方法会替换原来indexOfItem
对应的元素
// Vue.set
Vue.set(example1.items, indexOfItem, newValue)
// Array.prototype.splice`
example1.items.splice(indexOfItem, 1, newValue)
为了解决第二类问题,你也同样可以使用 splice
:
example1.items.splice(newLength)
显示过滤/排序结果
有时,我们想要显示一个数组的过滤或排序副本,而不实际改变或重置原始数据。在这种情况下,可以创建返回过滤或排序数组的计算属性。 例如:
<li v-for="n in evenNumbers">{{ n }}</li>
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
computed: {
evenNumbers: function () {
return this.numbers.filter(function (number) {
return number % 2 === 0
})
}
}
evenNumber
的过滤或者,你也可以在计算属性不适用的情况下 (例如,在嵌套 v-for 循环中) 使用 method 方法:
<li v-for="n in even(numbers)">{{ n }}</li>
data: {
numbers: [ 1, 2, 3, 4, 5 ]
},
methods: {
even: function (numbers) {
return numbers.filter(function (number) {
return number % 2 === 0
})
}
}
VUE
中定义的方法;even
传入数组,然后在过滤,该方法的复用率高;DOM
处是没有添加()
的,即v-on:click=green
此时传递到greet
中的方法event
则是一个事件对象,如下图;如果我们在DOM
处是添加()
的并传递一个参数,那么:event
将不存在,被替换为传入的参数;
如何既获得参数,又获得
event
对象呢?
event
,需要特别注意的是
如果在DOM
进行传递参数就必须使用$event
的方式来传递特殊的对象;事件修饰符问题;
官方文档有一个对按钮添加一次监听的功能;
<a v-on:click.once="doThis"></a>`
需要注意的是,该按钮确实只能被点击一次,下次激活的时候,需要刷新页面才能激活 从下图我们可以看出,不刷新页面的情况下,该按钮不起作用
props
的方式进行传递参数;model
的方法在子元素内部定义slot=AAA
名称与组件中的template slot name='AAA'
一致时,将会替换
template
中的元素
作用域插槽
slot
定义了3个,template
中的内容都会被渲染进去;
也就是说,在页面中渲染了3组html
如图中所示;scope="props"
props.text
引入的属性会读取slot
定义的属性,如果没有值
页面也会渲染,span
也会输出;scope
是父组件应用子组件中的slot
中的属性;上面的两种方式 ,实现了替换子组件中的slot
内容,已经循环插入值得功能;
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。