Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
总之一句话:有问题,查官网。本笔记只适合学习复习使用。如果学习话,请自行查阅Vue官网。
本笔记来源于:自学过程,参考Vue官方文档。——大娃
https://cn.vuejs.org/v2/guide/installation.html 相关版本介绍
1. 直接引用
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
2. CDN
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
3. 模块化构建
<script type="module">
import Vue from 'https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.esm.browser.js'
</script>
声明式渲染
<div id="app">
{{ message }}
</div>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
//声明式渲染
条件与循环
<div id="app-2">
<span v-bind:title="message">
鼠标悬停几秒钟查看此处动态绑定的提示信息!
</span>
</div>
var app2 = new Vue({
el: '#app-2',
data: {
message: '页面加载于 ' + new Date().toLocaleString()
}
})
//逻辑判断,相关指令
处理用户输入
<div id="app-6">
<p>{{ message }}</p>
<input v-model="message">
</div>
var app6 = new Vue({
el: '#app-6',
data: {
message: 'Hello Vue!'
}
})
//事件绑定,自动渲染
组件化应用构建
Vue.component('todo-item', {
// todo-item 组件现在接受一个
// "prop",类似于一个自定义 attribute。
// 这个 prop 名为 todo。
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
})
<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>
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: '随便其它什么人吃的东西' }
]
}
})
//组件化应用构建-自定义
从上述简单的案例,让用户明白Vue能够用来干什么。虽然这些作者都已经说了,这些都只是Vue的皮毛的体现,但是就这些内容,已经颠覆了我对前端的认知了。
api:https://vuejs.org/v2/api/#Options-Data
var vm = new Vue({
// 选项
})
当创建一个 Vue 实例时,你可以传入一个选项对象( {} 这个就是选项对象。)。这篇教程主要描述的就是如何使用这些选项来创建你想要的行为。
目前你只需要明白所有的 Vue 组件都是 Vue 实例,并且接受相同的选项对象 (一些根实例特有的选项除外)
api: https://vuejs.org/v2/api/#Instance-Properties
注意:当这些数据改变时,视图会进行重渲染。值得注意的是只有当实例被创建时就已经存在于
data
中的属性才是响应式的
这里唯一的例外是使用
Object.freeze()
,这会阻止修改现有的属性,也意味着响应系统无法再追踪变化。Object.freeze(obj) 方法可以冻结一个对象。
var obj = {
foo: 'bar'
}
Object.freeze(obj)
new Vue({
el: '#app',
data: obj
})
<div id="app">
<p>{{ foo }}</p>
<!-- 这里的 `foo` 不会更新! -->
<button v-on:click="foo = 'baz'">Change it</button>
</div>
除了数据属性,Vue 实例还暴露了一些有用的实例属性与方法。它们都有前缀
$
,以便与用户定义的属性区分开来。
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 也就是Vue提供了能够直接访问 Vue实例对象的参数的。
api: https://vuejs.org/v2/guide/instance.html#Instance-Lifecycle-Hooks
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。
注意:不要在选项属性或回调上使用箭头函数,比如
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
之类的错误。
官方提示:下图展示了实例的生命周期。你不需要立马弄明白所有的东西,不过随着你的不断学习和使用,它的参考价值会越来越高。
自己的理解:就是在生命周期的各个阶段,调用对应的回调函数,能够进行相关的操作。给用户提供了很多操作底层的机会。生命周期图中(标红部分)每个生命周期阶段都对应的一个点,也就是相同名字的对应回调函数。
Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。所有 Vue.js 的模板都是合法的 HTML ,所以能被遵循规范的浏览器和 HTML 解析器解析。
在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。
文本 (Mustache 语法 {{}}
)
数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值
<span>Message: {{ msg }}</span>
原始HTML(v-html
指令)
<p>Using v-html directive: <span v-html="rawHtml"></span></p>
注意:你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。
Attribute (v-bind
指令)
<div v-bind:id="dynamicId"></div>
<button v-bind:disabled="isButtonDisabled">Button</button>
对于布尔 attribute (它们只要存在就意味着值为
true
),v-bind
工作起来略有不同,在这个例子中.如果
isButtonDisabled
的值是null
、undefined
或false
,则disabled
attribute 甚至不会被包含在渲染出来的 `` 元素中。
使用-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
。你不应该在模板表达式中试图访问用户定义的全局变量。
指令 (Directives) 是带有
v-
前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 (v-for
是例外情况,稍后我们再讨论)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
参数
一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,v-bind
指令可以用于响应式地更新 HTML attribute。
动态参数
从 2.6.0 开始,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:
<!--
注意,参数表达式的写法存在一些约束,如之后的“对动态参数表达式的约束”章节所述。
-->
<a v-bind:[attributeName]="url"> ... </a>
<!--
在 DOM 中使用模板时这段代码会被转换为 `v-bind:[someattr]`。
除非在实例中有一个名为“someattr”的 property,否则代码不会工作。
-->
<a v-bind:[someAttr]="value"> ... </a>
修饰符
修饰符 (modifier) 是以半角句号
.
指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent
修饰符告诉v-on
指令对于触发的事件调用event.preventDefault()
:
<form v-on:submit.prevent="onSubmit">...</form>
v-bind缩写
<!-- 完整语法 -->
<a v-bind:href="url">...</a>
<!-- 缩写 -->
<a :href="url">...</a>
<!-- 动态参数的缩写 (2.6.0+) -->
<a :[key]="url"> ... </a>
v-on缩写
<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>
<!-- 缩写 -->
<a @click="doSomething">...</a>
<!-- 动态参数的缩写 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>
对于任何复杂逻辑,你都应当使用计算属性。
基础例子
<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: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
结果:
Original message: "大娃测试"
Computed reversed message: "试测娃大"
这里我们声明了一个计算属性 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 函数是没有副作用 (side effect) 的,这使它更易于测试和理解。
大娃理解:computed:这个VM的系统属性就是定义计算属性的。里面的方法自定义。实现了动态绑定data,还实现了缓存。调用属性名,就等于调用这个计算方法的getter()方法。
计算属性缓存VS方法
注意:我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要
message
还没有发生改变,多次访问reversedMessage
计算属性会立即返回之前的计算结果,而不必再次执行函数。相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。
我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A 。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。
计算属性VS侦听属性
我现在还不知道侦听属性 watch 有什么用。不过能看到计算属性的缓存优势
var vm = new Vue({
el: '#demo',
data: {
firstName: 'Foo',
lastName: 'Bar'
},
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
}
})
计算属性的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
也会相应地被更新。
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过
watch
选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
watch: {
// 如果 `question` 发生改变,这个函数就会运行
question: function (newQuestion, oldQuestion) {
this.answer = 'Waiting for you to stop typing...'
this.debouncedGetAnswer()
}
}
具体案例,查询官网给出的案例
大娃理解:监听器,功能也如字面意思,监听。当监听的属性发生变化的时候,做出指定的操作。
这里监听的属性可以是计算属性。
补充概念:Truthy(真值) https://developer.mozilla.org/zh-CN/docs/Glossary/Truthy
在 JavaScript 中,truthy(真值)指的是在布尔值上下文中,转换后的值为真的值。所有值都是真值,除非它们被定义为 假值(即除
false
、0
、""
、null
、undefined
和NaN
以外皆为真值)。
对象语法
可以直接定义在模板中
可以:一个对象
<div v-bind:class="{ active: isActive }"></div>
或者:两个对象
<div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>
data: {
isActive: true,
hasError: false
}
或者:也可以不定义在模板中
<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'
}
}
数组语法
我们可以把一个数组传给 v-bind:class
//方式1:
<div v-bind:class="[activeClass, errorClass]"></div>
//方式2:三元表达式+数组
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
//方式3:JS模板+数组
<div v-bind:class="[{ active: isActive }, errorClass]"></div>
用在组件上(需要对Vue组件有了解)
其实就是在说:自定义的模板组件,你定义时候带的class样式,默认是替换不掉的
你定义的模板:
Vue.component('my-component', {
template: '<p class="foo bar">Hi</p>'
})
然后你用的时候再加class的话
<my-component class="baz boo"></my-component>
会被渲染成这样,累加class的形式(class数据动态绑定形式的也是这样)
<p class="foo bar baz boo">Hi</p>
对象语法
<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'
}
}
数组语法
<div v-bind:style="[baseStyles, overridingStyles]"></div>
自动添加前缀
当
v-bind:style
使用需要添加浏览器引擎前缀的 CSS 属性时,如transform
,Vue.js 会自动侦测并添加相应的前缀。很nice的配置。 如下所示,自动添加浏览器前缀
-webkit-transition: all 4s ease; -moz-transition: all 4s ease; -ms-transition: all 4s ease; -o-transition: all 4s ease; transition: all 4s ease;
多重值
从 2.3.0 起你可以为
style
绑定中的属性提供一个包含多个值的数组,常用于提供多个带前缀的值,例如:
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
这样写只会渲染数组中最后一个被浏览器支持的值。在本例中,如果浏览器支持不带浏览器前缀的 flexbox,那么就只会渲染
display: flex
。
v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no </h1>
在template
元素上使用-v-if-条件渲染分组
PS:因为
v-if
是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个<template>
元素当做不可见的包裹元素,并在上面使用v-if。
最终的渲染结果将不包含<template>
元素。
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
v-else
v-else
元素必须紧跟在带v-if
或者v-else-if
的元素的后面,否则它将不会被识别。
<div v-if="Math.random() > 0.5">
Now you see me
</div>
<div v-else>
Now you don't
</div>
v-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
的元素之后。
key
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>
问题:上述代码在切换的时候,输入框中的内容不会变。因为两个模板使用了相同的元素,
input
不会被替换掉——仅仅是替换了它的placeholder
。
<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>
只需添加一个具有唯一值的
key
属性即可保证渲染的时候重新渲染数据。现在,每次切换时,输入框都将被重新渲染。
另一个用于根据条件展示元素的选项是 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
和v-for
。请查阅风格指南以获取更多信息。当
v-if
与v-for
一起使用时,v-for
具有比v-if
更高的优先级。请查阅列表渲染指南 以获取详细信息。
v-for
把一个数组对应为一组元素我们可以用
v-for
指令基于一个数组来渲染一个列表。v-for
指令需要使用item in items
形式的特殊语法,其中items
是源数据数组,而item
则是被迭代的数组元素的别名。
<div id="toDoList">
<ul>
<li v-for="item in toDoList">my name is {{item.name}},my age is {{item.age}}</li>
</ul>
</div>
<script>
var vm = new Vue({
el:'#toDoList',
data:{
toDoList:[
{name:'dawa',age:23},
{name:'erwa',age:24}
]
}
});
</script>
在
v-for
块中,我们可以访问所有父作用域的属性。v-for
还支持一个可选的第二个参数,即当前项的索引。
<li v-for="(item,inde) in toDoList">my name is {{item.name}},my age is {{item.age}},address is {{address}},my index is {{inde+1}}</li>
你也可以用
of
替代in
作为分隔符,因为它更接近 JavaScript 迭代器的语法:
<div v-for="item of items"></div>
你也可以用
v-for
来遍历一个对象的属性。对于这么一个object
new Vue({
el: '#v-for-object',
data: {
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
}
}
})
第一个参数:默认取值
<ul id="v-for-object" class="demo">
<li v-for="value in object">
{{ value }}
</li>
</ul>
第二个参数:默认为属性名
<div v-for="(value, name) in object">
{{ name }}: {{ value }}
</div>
title: How to do lists in Vue
author: Jane Doe
publishedAt: 2016-04-10
第三个参数:默认为索引值
<div v-for="(value, name, index) in object">
{{ index }}. {{ name }}: {{ value }}
</div>
0. title: How to do lists in Vue
1. author: Jane Doe
2. publishedAt: 2016-04-10
当 Vue 正在更新使用
v-for
渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。这个类似 Vue 1.x 的track-by="$index"
。这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一
key
属性:<div v-for="item in items" v-bind:key="item.id"> <!-- 内容 --> </div>
建议尽可能在使用
v-for
时提供key
attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。因为它是 Vue 识别节点的一个通用机制,
key
并不仅与v-for
特别关联。后面我们将在指南中看到,它还具有其它用途。不要使用对象或数组之类的非基本类型值作为
v-for
的key
。请用字符串或数值类型的值。更多
key
attribute 的细节用法请移步至key
的 API 文档。
此处,个人觉得,讲述的是比较底层的内容,但是也需要理解。相关key的引用和底层的渲染。
变异方法 (mutation method)
所谓的变异,就是会改变调用该方法的原有数组
Vue 将被侦听的数组的变异方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
替换数组
变异方法,顾名思义,会改变调用了这些方法的原始数组。相比之下,也有非变异 (non-mutating method) 方法,例如
filter()
、concat()
和slice()
。它们不会改变原始数组,而总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
你可能认为这将导致 Vue 丢弃现有 DOM 并重新渲染整个列表。幸运的是,事实并非如此。Vue 为了使得 DOM 元素得到最大范围的重用而实现了一些智能的启发式方法,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。
注意事项
由于 JavaScript 的限制,Vue 不能检测以下数组的变动:
var vm = new Vue({
data: {
items: ['a', 'b', 'c']
}
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的
为了解决第一类问题,以下两种方式都可以实现和
vm.items[indexOfItem] = newValue
相同的效果,同时也将在响应式系统内触发状态更新:// Vue.set Vue.set(vm.items, indexOfItem, newValue) // Array.prototype.splice vm.items.splice(indexOfItem, 1, newValue)
你也可以使用
vm.$set
实例方法,该方法是全局方法Vue.set
的一个别名:vm.$set(vm.items, indexOfItem, newValue)
为了解决第二类问题,你可以使用
splice
:vm.items.splice(newLength)
这一块,官网解释字面意思都清楚,但是由于没有实际操作过,理解的不深刻。
还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除:
var vm = new Vue({ data: { a: 1 } }) // `vm.a` 现在是响应式的 vm.b = 2 // `vm.b` 不是响应式的
对于已经创建的实例,Vue 不允许动态添加根级别的响应式属性。但是,可以使用
Vue.set(object, propertyName, value)
方法向嵌套对象添加响应式属性。例如,对于:var vm = new Vue({ data: { userProfile: { name: 'Anika' } } })
你可以添加一个新的
age
属性到嵌套的userProfile
对象:Vue.set(vm.userProfile, 'age', 27)
你还可以使用
vm.$set
实例方法,它只是全局Vue.set
的别名:vm.$set(vm.userProfile, 'age', 27)
有时你可能需要为已有对象赋值多个新属性,比如使用
Object.assign()
或_.extend()
。在这种情况下,你应该用两个对象的属性创建一个新的对象。所以,如果你想添加新的响应式属性,不要像这样:Object.assign(vm.userProfile, { age: 27, favoriteColor: 'Vue Green' })
你应该这样做:
vm.userProfile = Object.assign({}, vm.userProfile, { age: 27, favoriteColor: 'Vue Green' })
有时,我们想要显示一个数组经过过滤或排序后的版本,而不实际改变或重置原始数据。在这种情况下,可以创建一个计算属性,来返回过滤或排序后的数组。
例如:
<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 }) } }
在计算属性不适用的情况下 (例如,在嵌套
v-for
循环中) 你可以使用一个方法:<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 }) } }
v-for
也可以接受整数。在这种情况下,它会把模板重复对应次数。
<div>
<span v-for="n in 10">{{ n }} </span>
</div>
1 2 3 4 5 6 7 8 9 10
类似于
v-if
,你也可以利用带有v-for
的 ` 来循环渲染一段包含多个元素的内容。比如:<ul> <template v-for="item in items"> <li>{{ item.msg }}</li> <li class="divider" role="presentation"></li> </template> </ul>
注意我们不推荐在同一元素上使用
v-if
和v-for
。更多细节可查阅风格指南。当它们处于同一节点,
v-for
的优先级比v-if
更高,这意味着v-if
将分别重复运行于每个v-for
循环中。当你只想为部分项渲染节点时,这种优先级的机制会十分有用,如下:<li v-for="todo in todos" v-if="!todo.isComplete"> {{ todo }} </li>
上面的代码将只渲染未完成的 todo。
而如果你的目的是有条件地跳过循环的执行,那么可以将
v-if
置于外层元素 (或 `)上。如:<ul v-if="todos.length"> <li v-for="todo in todos"> {{ todo }} </li> </ul> <p v-else>No todos left!</p>
这部分内容假定你已经了解组件相关知识。你也完全可以先跳过它,以后再回来查看。
https://cn.vuejs.org/v2/guide/components.html
在自定义组件上,你可以像在任何普通元素上一样使用
v-for
。<my-component v-for="item in items" :key="item.id"></my-component>
可以用
v-on
指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。示例:
<div id="example-1"> <button v-on:click="counter += 1">Add 1</button> <p>The button above has been clicked {{ counter }} times.</p> </div> var example1 = new Vue({ el: '#example-1', data: { counter: 0 } })
然而许多事件处理逻辑会更为复杂,所以直接把 JavaScript 代码写在
v-on
指令中是不可行的。因此v-on
还可以接收一个需要调用的方法名称。示例:
<div id="example-2"> <!-- `greet` 是在下面定义的方法名 --> <button v-on:click="greet">Greet</button> </div> var example2 = new Vue({ el: '#example-2', data: { name: 'Vue.js' }, // 在 `methods` 对象中定义方法 methods: { greet: function (event) { // `this` 在方法里指向当前 Vue 实例 alert('Hello ' + this.name + '!') // `event` 是原生 DOM 事件 if (event) { alert(event.target.tagName) } } } }) // 也可以用 JavaScript 直接调用方法 example2.greet() // => 'Hello Vue.js!'
除了直接绑定到一个方法,也可以在内联 JavaScript 语句中调用方法:
<div id="example-3"> <button v-on:click="say('hi')">Say hi</button> <button v-on:click="say('what')">Say what</button> </div> new Vue({ el: '#example-3', methods: { say: function (message) { alert(message) } } })
有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量
$event
把它传入方法:$event:
<button v-on:click="warn('Form cannot be submitted yet.', $event)"> Submit </button> // ... methods: { warn: function (message, event) { // 现在我们可以访问原生事件对象 if (event) { event.preventDefault() } alert(message) } }
在事件处理程序中调用
event.preventDefault()
或event.stopPropagation()
是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。为了解决这个问题,Vue.js 为
v-on
提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用
v-on:click.prevent.self
会阻止所有的点击,而v-on:click.self.prevent
只会阻止对元素自身的点击。<!-- 点击事件将只会触发一次 --> <a v-on:click.once="doThis"></a>
不像其它只能对原生的 DOM 事件起作用的修饰符,
.once
修饰符还能被用到自定义的组件事件上。如果你还没有阅读关于组件的文档,现在大可不必担心。
在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为
v-on
在监听键盘事件时添加按键修饰符:<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` --> <input v-on:keyup.enter="submit">
你可以直接将
KeyboardEvent.key
暴露的任意有效按键名转换为 kebab-case 来作为修饰符。<input v-on:keyup.page-down="onPageDown">
在上述示例中,处理函数只会在
$event.key
等于PageDown
时被调用。
可以用如下修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器。
.ctrl
.alt
.shift
.meta
你可能注意到这种事件监听的方式违背了关注点分离 (separation of concern) 这个长期以来的优良传统。但不必担心,因为所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的 ViewModel 上,它不会导致任何维护上的困难。实际上,使用
v-on
有几个好处:
你可以用
v-model
指令在表单、
及 `` 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但v-model
本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
v-model
会忽略所有表单元素的value
、checked
、selected
attribute 的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的data
选项中声明初始值。
v-model
在内部为不同的输入元素使用不同的属性并抛出不同的事件:
- text 和 textarea 元素使用
value
属性和input
事件;- checkbox 和 radio 使用
checked
属性和change
事件;- select 字段将
value
作为 prop 并将change
作为事件。对于需要使用输入法 (如中文、日文、韩文等) 的语言,你会发现
v-model
不会在输入法组合文字过程中得到更新。如果你也想处理这个过程,请使用input
事件。
文本
<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>
多行文本
在文本区域插值 (
{{text}}
) 并不会生效,应用v-model
来代替。
<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<br>
<textarea v-model="message" placeholder="add multiple lines"></textarea>
复选框
//单个复选框,绑定到布尔值:
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
//多个复选框,绑定到同一个数组
<div id='example-3'>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
</div>
new Vue({
el: '#example-3',
data: {
checkedNames: []
}
})
单选按钮
<div id="example-4">
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>Picked: {{ picked }}</span>
</div>
new Vue({
el: '#example-4',
data: {
picked: ''
}
})
选择框
单选时
<div id="example-5">
<select v-model="selected">
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: {{ selected }}</span>
</div>
new Vue({
el: '...',
data: {
selected: ''
}
})
如果
v-model
表达式的初始值未能匹配任何选项,`` 元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。因此,更推荐像上面这样提供一个值为空的禁用选项
多选时
<div id="example-6">
<select v-model="selected" multiple style="width: 50px;">
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<br>
<span>Selected: {{ selected }}</span>
</div>
new Vue({
el: '#example-6',
data: {
selected: []
}
})
用v-for实现
<select v-model="selected">
<option v-for="option in options" v-bind:value="option.value">
{{ option.text }}
</option>
</select>
<span>Selected: {{ selected }}</span>
new Vue({
el: '...',
data: {
selected: 'A',
options: [
{ text: 'One', value: 'A' },
{ text: 'Two', value: 'B' },
{ text: 'Three', value: 'C' }
]
}
})
对于单选按钮,复选框及选择框的选项,
v-model
绑定的值通常是静态字符串 (对于复选框也可以是布尔值):<!-- 当选中时,`picked` 为字符串 "a" --> <input type="radio" v-model="picked" value="a"> <!-- `toggle` 为 true 或 false --> <input type="checkbox" v-model="toggle"> <!-- 当选中第一个选项时,`selected` 为字符串 "abc" --> <select v-model="selected"> <option value="abc">ABC</option> </select>
但是有时我们可能想把值绑定到 Vue 实例的一个动态属性上,这时可以用
v-bind
实现,并且这个属性的值可以不是字符串。
复选框
<input
type="checkbox"
v-model="toggle"
true-value="yes"
false-value="no"
>
// 当选中时
vm.toggle === 'yes'
// 当没有选中时
vm.toggle === 'no'
这里的
true-value
和false-value
attribute 并不会影响输入控件的value
attribute,因为浏览器在提交表单时并不会包含未被选中的复选框。如果要确保表单中这两个值中的一个能够被提交,(比如“yes”或“no”),请换用单选按钮。
单选按钮
<input type="radio" v-model="pick" v-bind:value="a">
// 当选中时
vm.pick === vm.a
下拉框的选项
<select v-model="selected">
<!-- 内联对象字面量 -->
<option v-bind:value="{ number: 123 }">123</option>
</select>
// 当选中时
typeof vm.selected // => 'object'
vm.selected.number // => 123
.lazy
在默认情况下,
v-model
在每次input
事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加lazy
修饰符,从而转变为使用change
事件进行同步:<!-- 在“change”时而非“input”时更新 --> <input v-model.lazy="msg" >
.number
如果想自动将用户的输入值转为数值类型,可以给
v-model
添加number
修饰符:<input v-model.number="age" type="number">
这通常很有用,因为即使在
type="number"
时,HTML 输入元素的值也总会返回字符串。如果这个值无法被parseFloat()
解析,则会返回原始的值。
.trim
如果要自动过滤用户输入的首尾空白字符,可以给
v-model
添加trim
修饰符:<input v-model.trim="msg"
如果你还不熟悉 Vue 的组件,可以暂且跳过这里。
HTML 原生的输入元素类型并不总能满足需求。幸好,Vue 的组件系统允许你创建具有完全自定义行为且可复用的输入组件。这些输入组件甚至可以和 v-model
一起使用!要了解更多,请参阅组件指南中的自定义输入组件。
这里有一个 Vue 组件的示例:
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
组件是可复用的 Vue 实例,且带有一个名字:在这个例子中是button-counter
。我们可以在一个通过 new Vue
创建的 Vue 根实例中,把这个组件作为自定义元素来使用:
<div id="components-demo">
<button-counter></button-counter>
</div>
new Vue({ el: '#components-demo' })
因为组件是可复用的 Vue 实例,所以它们与
new Vue
接收相同的选项,例如data
、computed
、watch
、methods
以及生命周期钩子等。仅有的例外是像el
这样根实例特有的选项。
你可以将组件进行任意次数的复用:
<div id="components-demo">
<button-counter></button-counter>
<button-counter></button-counter>
<button-counter></button-counter>
</div>
注意当点击按钮时,每个组件都会各自独立维护它的 count。因为你每用一次组件,就会有一个它的新实例被创建。
注意:data必须是一个函数
当我们定义这个
components-demo
组件时,你可能会发现它的data
并不是像这样直接提供一个对象:data: { count: 0 }
取而代之的是,一个组件的
data
选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:data: function () { return { count: 0 } }
如果 Vue 没有这条规则,点击一个按钮就可能会像如下代码一样影响到其它所有实例。
大娃个人理解:正式因为方法,所以参数和变量都是局部变量,由每一个单独的实例来维护。
通常一个应用会以一棵嵌套的组件树的形式来组织:
例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。
为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册和局部注册。至此,我们的组件都只是通过
Vue.component
全局注册的:Vue.component('my-component-name', { // ... options ... })
全局注册的组件可以用在其被注册之后的任何 (通过
new Vue
) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。到目前为止,关于组件注册你需要了解的就这些了,如果你阅读完本页内容并掌握了它的内容,我们会推荐你再回来把组件注册读完。
早些时候,我们提到了创建一个博文组件的事情。问题是如果你不能向这个组件传递某一篇博文的标题或内容之类的我们想展示的数据的话,它是没有办法使用的。这也正是 prop 的由来。
Prop 是你可以在组件上注册的一些自定义 attribute。当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个属性。为了给博文组件传递一个标题,我们可以用一个
props
选项将其包含在该组件可接受的 prop 列表中:Vue.component('blog-post', { props: ['title'], template: '<h3>{{ title }}</h3>' })
一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。在上述模板中,你会发现我们能够在组件实例中访问这个值,就像访问
data
中的值一样。一个 prop 被注册之后,你就可以像这样把数据作为一个自定义 attribute 传递进来:
<blog-post title="My journey with Vue"></blog-post> <blog-post title="Blogging with Vue"></blog-post> <blog-post title="Why Vue is so fun"></blog-post>
大娃理解:动态传值
然而在一个典型的应用中,你可能在
data
里有一个博文的数组:new Vue({ el: '#blog-post-demo', data: { posts: [ { id: 1, title: 'My journey with Vue' }, { id: 2, title: 'Blogging with Vue' }, { id: 3, title: 'Why Vue is so fun' } ] } })
并想要为每篇博文渲染一个组件:
<blog-post v-for="post in posts" v-bind:key="post.id" v-bind:title="post.title" ></blog-post>
如上所示,你会发现我们可以使用
v-bind
来动态传递 prop。这在你一开始不清楚要渲染的具体内容,比如从一个 API 获取博文列表的时候,是非常有用的。到目前为止,关于 prop 你需要了解的大概就这些了,如果你阅读完本页内容并掌握了它的内容,我们会推荐你再回来把 prop 读完。
demo:https://jsfiddle.net/chrisvfritz/sbLgr0ad
Vue.component('blog-post', { props: ['title'], template: '<h3>{{ title }}</h3>' }) new Vue({ el: '#blog-post-demo', data: { posts: [] }, created: function () { // Alias the component instance as `vm`, so that we // can access it inside the promise function var vm = this // Fetch our array of posts from an API fetch('https://jsonplaceholder.typicode.com/posts') .then(function (response) { return response.json() }) .then(function (data) { vm.posts = data }) } }) <div id="blog-post-demo" class="demo"> <blog-post v-for="post in posts" v-bind:key="post.id" v-bind:title="post.title" ></blog-post> </div>https://jsfiddle.net/chrisvfritz/sbLgr0ad#
Ok,暂时理解了
当构建一个
<blog-post>
组件时,你的模板最终会包含的东西远不止一个标题:<h3>{{ title }}</h3>
最最起码,你会包含这篇博文的正文:
<h3>{{ title }}</h3> <div v-html="content"></div>
然而如果你在模板中尝试这样写,Vue 会显示一个错误,并解释道 every component must have a single root element (每个组件必须只有一个根元素)。你可以将模板的内容包裹在一个父元素内,来修复这个问题,例如:
<div class="blog-post"> <h3>{{ title }}</h3> <div v-html="content"></div> </div>
所以是时候重构一下这个 `` 组件了,让它变成接受一个单独的
post
prop:<blog-post v-for="post in posts" v-bind:key="post.id" v-bind:post="post" ></blog-post> Vue.component('blog-post', { props: ['post'], template: ` <div class="blog-post"> <h3>{{ post.title }}</h3> <div v-html="post.content"></div> </div> ` })
上述的这个和一些接下来的示例使用了 JavaScript 的模板字符串来让多行的模板更易读。它们在 IE 下并没有被支持,所以如果你需要在不 (经过 Babel 或 TypeScript 之类的工具) 编译的情况下支持 IE,请使用折行转义字符取而代之。
现在,不论何时为
post
对象添加一个新的属性,它都会自动地在<blog-post>
内可用。
OK:理解了,自己写了一个Demo.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Document</title> <script src="../js/vue.js"></script> </head> <body> <div id="blog-post"> <blog-post v-for="post in posts" v-bind:key="post.id" v-bind:post="post" ></blog-post> </div> </body> <script> Vue.component("blog-post", { props: ["post"], template: ` <div class="blog-post"> <h3>{{ post.title }}</h3> <div v-html="post.content"></div> </div> ` }); new Vue({ el: "#blog-post", data: { posts: [ { id:1, title: "title", content: "content" }, { id:2, title: "title2", content: "content2" } ] } }); </script> </html>
关于实例方法.事件 vm.$emit, vm.$on 这种的,还是理解的有点不到位。
https://cn.vuejs.org/v2/api/#vm-emit , 看完这个官网案例,理解了。
和 HTML 元素一样,我们经常需要向一个组件传递内容,像这样:
<alert-box> Something bad happened. </alert-box>
可能会渲染出这样的东西:
幸好,Vue 自定义的
slot
元素让这变得非常简单:Vue.component('alert-box', { template: ` <div class="demo-alert-box"> <strong>Error!</strong> <slot></slot> </div> ` })
如你所见,我们只要在需要的地方加入插槽就行了——就这么简单!
到目前为止,关于插槽你需要了解的大概就这些了,如果你阅读完本页内容并掌握了它的内容,我们会推荐你再回来把插槽读完。
大娃:理解slot了。
Demo: https://jsfiddle.net/chrisvfritz/o3nycadu/
有的时候,在不同组件之间进行动态切换是非常有用的,比如在一个多标签的界面里:
上述内容可以通过 Vue 的
component
元素加一个特殊的is
attribute 来实现:<!-- 组件会在 `currentTabComponent` 改变时改变 --> <component v-bind:is="currentTabComponent"></component>
在上述示例中,
currentTabComponent
可以包括
- 已注册组件的名字,或
- 一个组件的选项对象
你可以在这里查阅并体验完整的代码,或在这个版本了解绑定组件选项对象,而不是已注册组件名的示例。
请留意,这个 attribute 可以用于常规 HTML 元素,但这些元素将被视为组件,这意味着所有的 attribute 都会作为 DOM attribute 被绑定。对于像
value
这样的 property,若想让其如预期般工作,你需要使用.prop
修饰器。到目前为止,关于动态组件你需要了解的大概就这些了,如果你阅读完本页内容并掌握了它的内容,我们会推荐你再回来把动态和异步组件读完。
有些 HTML 元素,诸如
、
、和
,对于哪些元素可以出现在其内部是有严格限制的。而有些元素,诸如、 和
,只能出现在其它某些特定的元素内部。这会导致我们使用这些有约束条件的元素时遇到一些问题。例如:
<table> <blog-post-row></blog-post-row> </table>
这个自定义组件 `` 会被作为无效的内容提升到外部,并导致最终渲染结果出错。幸好这个特殊的
is
attribute 给了我们一个变通的办法:<table> <tr is="blog-post-row"></tr> </table>
需要注意的是如果我们从以下来源使用模板的话,这条限制是*不存在*的:
- 字符串 (例如:
template: '...'
)- 单文件组件 (
.vue
)<script type='text/x-template>'
到这里,你需要了解的解析 DOM 模板时的注意事项——实际上也是 Vue 的全部必要内容,大概就是这些了。恭喜你!接下来还有很多东西要去学习,不过首先,我们推荐你先休息一下,试用一下 Vue,自己随意做些好玩的东西。
如果你感觉已经掌握了这些知识,我们推荐你再回来把完整的组件指南,包括侧边栏中组件深入章节的所有页面读完。
目前到这里可以暂时做为一个节点。只是把Vue的基础了解的部分了解了一下,但是也对Vue有了整体的认知,接下来会持续深入学习。OK。我尝试去GitHub上寻找了部分的Vue项目,发现了都.vue扩展名结尾,正好在基础部分的最后,文档中提到了单文件组件。让我对Vue的看法又确定了一下,我的学习路线是没有错的。
手机扫一扫
移动阅读更方便
你可能感兴趣的文章