写在最前:自学笔记,往后逐步添加笔记内容
一、通过TodoList案例起步
1. 模板语法
A. 插值
a. 文本内容插值
数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值:
Message: {{ msg }}
或者使用 v-text
指令,绑定文本内容插值,若想插入html,可以使用 v-html
指令。
通过使用 v-once
指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。
b. 属性插值
我们通过v-bind 指令为属性赋值
对于用布尔值控制的属性而言,v-bind工作起来略有不同,他们只要存在就意味着值为true
c. JavaScript表达式
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式,所以下面的例子都不会生效。
{{ var a = 1 }}
{{ if (ok) { return message } }}
B. 指令
指令 (Directives) 是带有 v- 前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 (v-for 是例外情况)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
a. 参数
一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,v-bind
指令可以用于响应式地更新 HTML attribute
...
另一个例子是 v-on
指令,它用于监听 DOM 事件
...
b. 动态参数
从 2.6.0 开始,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:
...
这里的 attributeName 会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。例如,如果你的 Vue 实例有一个 data 属性 attributeName,其值为 "href",那么这个绑定将等价于 v-bind:href
。
c. 修饰符
修饰符 (modifier) 是以半角句号 .
指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent
修饰符告诉 v-on
指令对于触发的事件调用 event.preventDefault()
:
C. 缩写
a. v-bind 缩写
...
...
...
b. v-on 缩写
...
...
...
2. 计算属性
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如:
{{ message.split('').reverse().join('') }}
所以,对于任何复杂逻辑,你都应当使用计算属性。
A. 基础例子
Original message: "{{ message }}"
Computed reversed message: "{{ reversedMessage }}"
//js部分
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
B. 计算属性与方法的区别
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。相比之下计算属性更高效些。
C. 侦听器
虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。这就是为什么 Vue 通过 watch
选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
D. 计算属性的getter和setter
{{ fullName }}
//js部分
var vm = new Vue({
el: '#example',
data: {
firstName: 'neo',
lastName: 'Wang"
},
computed: {
fullName: {
get : function(){
return `${this.firstName} ${this.lastName}`
},
set : function(value){
let arr = value.split(" ");
this.firstName = arr[0];
this.lastName = arr[1];
}
}
}
})
计算属性所依赖的值发生了变化,就会重新计算,当set计算属性的时候,get函数也会重新计算。
3. Class 与 Style 绑定
A. 绑定HTML Class
a. 对象语法
我们可以传给 v-bind:class 一个对象,以动态地切换 class:
//js部分
data: {
isActive: true,
hasError: false
}
上面的语法表示 active
这个 class 存在与否将取决于数据属性 isActive
的 truthiness。结果渲染为:
绑定的数据对象不必内联定义在模板里:
//js部分
data: {
classObject: {
active: true,
'text-danger': false
}
}
渲染的结果和上面一样。我们也可以在这里绑定一个返回对象的计算属性。
b. 数组语法
我们可以把一个数组传给 v-bind:class,以应用一个 class 列表:
//js部分
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
渲染为:
c. 用在组件上
当在一个自定义组件上使用 class 属性时,这些 class 将被添加到该组件的根元素上面。这个元素上已经存在的 class 不会被覆盖。
//组件声明
Vue.component('my-component', {
template: '
'
})
HTML 将被渲染为:
对于带数据绑定 class 也同样适用
B. 绑定内联样式
a. 对象语法
v-bind:style
的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。CSS 属性名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名:
//js部分
data: {
activeColor: 'red',
fontSize: 30
}
同样的,对象语法常常结合返回对象的计算属性使用。
b. 数组语法
v-bind:style
的数组语法可以将多个样式对象应用到同一个元素上:
c. 自动添加加前缀
当 v-bind:style
使用需要添加浏览器引擎前缀的 CSS 属性时,如 transform
,Vue.js 会自动侦测并添加相应的前缀。
d. 多重值
从 2.3.0 起你可以为 style 绑定中的属性提供一个包含多个值的数组,常用于提供多个带前缀的值,这样写只会渲染数组中最后一个被浏览器支持的值。在本例中,如果浏览器支持不带浏览器前缀的 flexbox,那么就只会渲染 display: flex。例如:
4. 条件渲染
A. v-if v-else v-else-if
v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。也可以用 v-else
添加一个“else 块”,v-else-if
,顾名思义,充当 v-if 的“else-if 块”,可以连续使用v-else 。元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面,否则它将不会被识别。
Vue is awesome!
Vue is awesome!
Oh no
B. 在
元素上使用 v-if 条件渲染分组
因为 v-if 是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个 元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含 元素。
Title
Paragraph 1
Paragraph 2
C. 用key管理可复用的元素
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使 Vue 变得非常快之外,还有其它一些好处。这样也不总是符合实际需求,所以 Vue 为你提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 key 属性即可:
现在,每次切换时,输入框都将被重新渲染。
D. v-show
另一个用于根据条件展示元素的选项是 v-show 指令。用法大致一样:
Hello!
不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性 display。
注意,v-show 不支持 元素,也不支持
v-else
。
5. 列表渲染
A. v-for
我们可以用 v-for
指令基于一个数组来渲染一个列表。v-for
指令需要使用 item in items
形式的特殊语法,其中 items
是源数据数组,而 item
则是被迭代的数组元素的别名。
在 v-for
块中,我们可以访问所有父作用域的属性。v-for
还支持一个可选的第二个参数,即当前项的索引。
-
{{ parentMessage }} - {{ index }} - {{ item.message }}
//js部分
var example2 = new Vue({
el: '#example-2',
data: {
parentMessage: 'Parent',
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})
结果:
- Parent-0-Foo
- Parent-1-Bar
你也可以用of
替代in
作为分隔符,因为它更接近 JavaScript 迭代器的语法
B. 在v-for 中使用对象
与遍历数组相同,只出过第二个和第三个可选参数分别表示键名和索引值
注意,在遍历对象时,会按 Object.keys()
的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下都一致。
C. 数组检测更新
a. 变异方法
Vue 将被侦听的数组的变异方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
b. 替换数组
变异方法,顾名思义,会改变调用了这些方法的原始数组。相比之下,也有非变异 (non-mutating method) 方法,例如 filter()
、concat()
和 slice()
。它们不会改变原始数组,而总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组
注意,Vue 不能检测以下数组的变动:
- 当你利用索引直接设置一个数组项时,例如:
vm.items[indexOfItem] = newValue
- 当你修改数组的长度时,例如:
vm.items.length = newLength
为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue
相同的效果,同时也将在响应式系统内触发状态更新:
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
为了解决第二类问题,你可以使用 splice
:
vm.items.splice(newLength)
D. 对象检测更新
还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除:具体移步官方文档 对象响应式添加属性
E. 在 上使用 v-for
类似于 v-if
,你也可以利用带有v-for
的 来循环渲染一段包含多个元素的内容。
F. 在组件上使用 v-for
在自定义组件上,你可以像在任何普通元素上一样使用 v-for
。
注意,当在组件上使用 v-for
时,key
现在是必须的。
然而,任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要使用 prop:
注意这里的 is="todo-item"
属性。这种做法在使用 DOM 模板时是十分必要的,因为在
元素内只有 元素会被看作有效内容。这样做实现的效果与
相同,但是可以避开一些潜在的浏览器解析错误。
6. 事件处理
A. 监听事件
可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。
B. 事件处理方法
然而许多事件处理逻辑会更为复杂,所以直接把 JavaScript 代码写在 v-on
指令中是不可行的。因此 v-on
还可以接收一个需要调用的方法名称。
C. 内联处理器中的方法
除了直接绑定到一个方法,也可以在内联 JavaScript 语句中调用方法:
//js部分
new Vue({
el: '#example-3',
methods: {
say: function (message) {
alert(message)
}
}
})
有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法。
D. 事件修饰符
Vue.js 为 v-on
提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。
.stop
.prevent
.capture
.self
.once
.passive
详情请移步官方文档 事件修饰符
E. 按键修饰符
在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:
你可以直接将 KeyboardEvent.key
暴露的任意有效按键名转换为 kebab-case 来作为修饰符。
在上述示例中,处理函数只会在 $event.key
等于 PageDown
时被调用。
keyCode
的事件用法已经被废弃了并可能不会被最新的浏览器支持。
使用 keyCode attribute 也是允许的:
更多组合键内容请移步官方文档 系统修饰键
7. 表单输入绑定
A. 基本语法
你可以用 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
作为事件。
a. 文本示例
Message is: {{ message }}
注意,在文本区域插值 () 并不会生效,应用 v-model 来代替。
b. 复选框
单个复选框,绑定到布尔值:
多个复选框,绑定到同一个数组:
Checked names: {{ checkedNames }}
//js部分
new Vue({
el: '#example-3',
data: {
checkedNames: []
}
})
c. 单选按钮
Picked: {{ picked }}
//js部分
new Vue({
el: '#example-4',
data: {
picked: ''
}
})
d. 选择框
单选时:
Selected: {{ selected }}
//js部分
new Vue({
el: '...',
data: {
selected: ''
}
})
多选时 (绑定到一个数组),同复选框。
用 v-for 渲染的动态选项:
Selected: {{ selected }}
//js部分
new Vue({
el: '...',
data: {
selected: 'A',
options: [
{ text: 'One', value: 'A' },
{ text: 'Two', value: 'B' },
{ text: 'Three', value: 'C' }
]
}
})
B. 绑定值
对于单选按钮,复选框及选择框的选项,v-model
绑定的值通常是静态字符串 (对于复选框也可以是布尔值),但是有时我们可能想把值绑定到 Vue 实例的一个动态属性上,这时可以用 v-bind
实现,并且这个属性的值可以不是字符串。
a. 复选框
注意,这里的 true-value
和 false-value
attribute 并不会影响输入控件的 value
attribute,因为浏览器在提交表单时并不会包含未被选中的复选框。如果要确保表单中这两个值中的一个能够被提交,(比如“yes”或“no”),请换用单选按钮。
b. 单选框
c. 选择框的选项
C. 修饰符
a. .lazy
在默认情况下,v-model
在每次 input
事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy
修饰符,从而转变为使用 change
事件进行同步:
b. .number
如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:
c. .trim
如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:
D. 在组件上使用 v-model
Vue 的组件系统允许你创建具有完全自定义行为且可复用的输入组件。这些输入组件甚至可以和 v-model 一起使用!
8. 组件基础
A. 基本示例
//js部分
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: ''
})
new Vue({ el: '#components-demo' })
因为组件是可复用的 Vue 实例,所以它们与 new Vue
接收相同的选项,例如 data
、computed
、watch
、methods
以及生命周期钩子等。仅有的例外是像 el
这样根实例特有的选项。
B. 组件的复用
你可以将组件进行任意次数的复用:
注意,当点击按钮时,每个组件都会各自独立维护它的 count
。因为你每用一次组件,就会有一个它的新实例被创建。同时需要注意的是,当我们定义这个
组件时,你可能会发现它的 data
并不是像这样直接提供一个对象,取而代之的是,一个组件的 data
选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝,如果 Vue 没有这条规则,点击一个按钮就可能会影响到其它所有实例。
C. 组件的组织
组件必须先注册以便 Vue 能够识别,分为两种注册方式:全局注册和局部注册。至此,我们的组件都只是通过 Vue.component
全局注册的:
Vue.component('my-component-name', {
// ... options ...
})
全局注册的组件可以用在其被注册之后的任何 (通过 new Vue
) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。
D. 通过Prop向子组件传递数据
Prop 是你可以在组件上注册的一些自定义 attribute。当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个属性。为了给博文组件传递一个标题,我们可以用一个 props
选项将其包含在该组件可接受的 prop 列表中:
Vue.component('blog-post', {
props: ['title'],
template: '{{ title }}
'
})
一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。在上述模板中,你会发现我们能够在组件实例中访问这个值,就像访问 data
中的值一样。
一个 prop 被注册之后,你就可以像这样把数据作为一个自定义 attribute 传递进来:
E. 单个根元素
每个组件必须只有一个根元素,也就是说可以将模板的内容包裹在一个父元素内:
{{ title }}
F. 监听子组件事件
在首章节开始的TodoList案例中,我们需要单击 li
标签来删除该项。这个时候就需要监听子组件事件。
在组件中绑定单击事件(这里我们定义 handleItemClick
事件),当点击 li
时,我们需要告诉父级组件删除该文本。 Vue 实例提供了一个自定义事件的系统来解决这个问题(这里我们自定义 delete
事件),同时第二个参数可以向父级传值。父级组件可以像处理 native DOM 事件一样通过 v-on 监听子组件实例的任意事件。
G. 组件上的特殊属性ref
ref
被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs
对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例:
hello
当 v-for
用于元素或组件的时候,引用信息将是包含 DOM 节点或组件实例的数组。
关于 ref 注册时间的重要说明:因为 ref
本身是作为渲染结果被创建的,在初始渲染的时候你不能访问它们 - 它们还不存在!$refs
也不是响应式的,因此你不应该试图用它在模板中做数据绑定。
H. 父子组件传值
参考章节一开始的案例,我们就可以发现,父组件通过标签属性向子组件传值,同时父组件传入的值不应该在子组件中改变。子组件通过 this.$emit('自定义事件名', 待传入值)
向父组件传值。
I. 动态组件
有的时候,在不同组件之间进行动态切换是非常有用的,比如在一个多标签的界面里,上述内容可以通过 Vue 的
元素加一个特殊的 is
attribute 来实现:
在上述示例中,currentTabComponent
可以包括
- 已注册组件的名字,或
- 一个组件的选项对象
少部分内容没有详记了