# 介绍
# 为什么有且只能有一个根元素
官方回应是取决于 diff算法
的编写方式,树状的数据结构需要通过根元素进行遍历对比。
<body>
<div id="app"></div>
</body>
2
3
new Vue({
el: '#app'
})
2
3
<template>
<div />
</template>
2
3
多个根元素会导致难以指定vue实例的根节点,vue在body里定义一个元素,将其绑定处理成程序的入口。template下也一样,通过唯一的根节点,递归遍历整个vue树下的所有节点并处理为vdom,渲染成真正的HTML后直接替换原来位置。
# vue的优缺点
优点:
- 渐进式框架:轻量高效,简单易用。
- MVVM的架构:数据驱动视图,方便测试。
- 组件化:组件可复用,易维护,便于协同开发,提高开发效率。
- 虚拟DOM:减少浏览器资源损耗,渲染更快
- 前后端分离:服务器相对压力小。
缺点:
- 不支持IE9以下浏览器
- 首次加载耗时比较多。
- SEO问题,不利于百度,360等搜索引擎收录。
# data
# data 为什么是一个函数
export default
导出的是个类,注册组件其实并不产生新的组件类,但会产生一个可以用来实例化的新方式,使用组件才是真正创建一个组件实例。
每个组件进行实例化时执行data,当data为对象时每个组件data共享,导致数据可能被另外的组件修改,而data为方法时,data就会存在在闭包当中,每个实例的data属性是独立的,不会相互影响。这是因为js本身的特性带来的,跟vue本身设计无关。
错误: 使用原型链定义data,如果两个实例同时引用一个对象,那么当你修改其中一个属性的时候,另外一个实例也会跟着改。
var MyComponent = function() {}
MyComponent.prototype.data = {
b: 1,
}
var component1 = new MyComponent()
var component2 = new MyComponent()
component1.data.b = 5
console.log(component2.data.b) // 5
2
3
4
5
6
7
8
正确: 形成闭包,数据不再互相影响
var MyComponent = function() {
this.data = this.data()
}
MyComponent.prototype.data = function() {
return {
b: 1,
}
}
var component1 = new MyComponent()
var component2 = new MyComponent()
component1.data.b = 5
console.log(component2.data.b) // 1
2
3
4
5
6
7
8
9
10
11
12
# 修改数据未更新
vue监听不到深层次的对象属性或者数组值的改变,除了通过数组的变异方法触发视图更新,还可以使用替换的方法去改变对象存储的地址而不是单单改变数值。
# 数组
变异方法:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
替换数组:
filter()
concat()
slice()
this.arr[0] = 'a' + new Date().getTime()
this.arr.length = 5
2
$set
方法
this.$set(this.arr, 0, 'a' + new Date().getTime())
- 使用变异方法
splice
等
this.$set(this.arr, 0, 'a' + new Date().getTime())
// 该方法改变长度只能减短不能加长
this.arr.splice(1)
2
3
$forceUpdate
强制更新(通用但暴力)迫使 Vue 实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。
this.arr[0] = 'a' + new Date().getTime()
this.arr.length = 5
this.$forceUpdate()
2
3
# 对象
vue在进行初始化实例时进行数据双向绑定,使用 Object.defineProperty()
对属性遍历添加 getter
/ setter
方法,所以属性必须在 data
对象上存在时才能进行上述过程!
vue初始化实例后,再去给实例对象添加属性时并没有添加 getter
和 setter
的方法,所以改变属性值和添加属性值时只可以看到数据改变,页面数据并没有更新! 这时用 $set
属性就可以使页面数据的更新!
this.obj.name = 'qietuniu' + new Date().getTime()
$set
方法
this.$set(this.obj, name, 'qietuniu' + new Date().getTime())
Object.assign
this.obj = Object.assign({}, this.obj, {
name: 'qietuniu' + new Date().getTime()
})
2
3
- ...扩展运算符
this.obj = {
...this.obj,
name: 'qietuniu' + new Date().getTime()
}
2
3
4
$forceUpdate
强制更新
# name
# 命名规范
建议name首字母为大写
- 当
name:'List'
时,组件可书写成<List />
或者<list />
- 当
name:'list'
时,组件只能书写成<list />
组件使用时命名:
- kebab-case:字母全小写且必须包含一个连字符
name:'ListTree'
=> <list-tree />
- PascalCase:每一个单字的首字母都采用大写字母
name:'ListTree'
=> <ListTree />
# 作用
- 组件模板递归调用自身
<ul>
<li v-for="(item, index) of list" :key="index">
{{item.title}}
<div v-if="item.children">
<List :list="item.children" />
</div>
</li>
</ul>
2
3
4
5
6
7
8
name: 'List',
props: {
list: {
type: Array,
default: function() {
return [{
title: '1',
children: [{
title: '1-1'
},
{
title: '1-2'
},
{
title: '1-3'
}
]
},
{
title: '2',
children: [{
title: '2-1',
children: [{
title: '2-1-1'
},
{
title: '2-1-2'
},
{
title: '2-1-3'
}
]
},
{
title: '2-2'
},
{
title: '2-3'
}
]
}
]
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
- 便于调试
vue-devtools中有名字的组件有更友好的警告信息,未命名组件将显示成 <AnonymousComponent>
- 作为keep-alive 的 include 和 exclude 的参数
详情请见keep-alive
# props
# props的写法与属性
HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名。
type
可以是下列原生构造函数中的一种:
String
、Number
、Boolean
、Array
、Object
、Date
、Function
、Symbol
、任何自定义构造函数、或上述内容组成的数组。会检查一个 prop 是否是给定的类型,否则抛出警告。default
:any
为该 prop 指定一个默认值。如果该
prop
没有被传入,则换做用这个值。对象或数组的默认值必须从一个工厂函数返回。required
:Boolean
定义该 prop 是否是必填项。在非生产环境中,如果这个值为 truthy 且该 prop 没有被传入的,则一个控制台警告将会被抛出。
truthy(真值)指的是在布尔值上下文中,转换后的值为真的值
validator
:Function
自定义验证函数会将该
prop
的值作为唯一的参数代入。在非生产环境下,如果该函数返回一个 falsy 的值 (也就是验证失败),一个控制台警告将会被抛出。你可以在这里查阅更多 prop 验证的相关信息。
Vue.component('my-component', {
props: {
// 基础的类型检查 ( `null` 和 `undefined` 会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function() {
return {
message: 'hello'
}
}
},
// 自定义验证函数
propF: {
validator: function(value) {
// 这个值必须匹配下列字符串中的一个
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# props验证
基础的类型检查 ( null
和 undefined
会通过任何类型验证), type 可以是下列原生构造函数中的一个:
String
Number
Boolean
Array
Object
Date
Function
Symbol
额外的,type 还可以是一个自定义的构造函数,并且通过 instanceof 来进行检查确认。例如,给定下列现成的构造函数来验证 author prop 的值是否是通过 new Person 创建的。:
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
list: Object,
callback: Function,
contactsPromise: Promise, // or any other constructor
age: {
type: Number,
default: 0,
required: true,
validator: function(value) {
return value >= 0
}
},
author: Person
}
function Person(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
这里有两种常见的试图改变一个 prop 的情形:
# prop 用来传递一个初始值
子组件希望将prop作为本地数据来使用。在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:
props: ['initialCounter'],
data: function() {
return {
counter: this.initialCounter
}
}
2
3
4
5
6
# prop 以一种原始的值传入且需转换
在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
props: ['txt'],
computed: {
normalizedTxt: function() {
return this.txt.trim().toLowerCase()
}
}
2
3
4
5
6
# class与style绑定
# class
在一个自定义组件上用到 class 属性的时候,这些类将被添加到根元素上面,这个元素上已经存在的类不会被覆盖。
- 对象语法
:class
指令也可以与普通的 class
属性共存。
<li class="bold" :class="{ blue: true }">class对象语法</li>
- 数组语法
<li :class="['bold', {'blue': isActive}]">class数组语法</li>
- 三元运算语法
<li :class="[true?'blue':'bold']">class三元运算语法</li>
# style
Vue会自动侦测需要添加浏览器引擎前缀的 CSS 属性,并添加相应的前缀,如 transform、flex等
- 对象语法
data() {
return {
isActive: true,
activeColor: {
color: '#08d'
},
activeFont: {
fontSize: '18px',
fontWeight: 'bold'
}
}
}
2
3
4
5
6
7
8
9
10
11
12
<li :style="activeColor">style数组语法</li>
- 数组语法
可以使用data属性也可以使用计算属性
<li :style="[activeColor, activeFont]">style数组语法</li>
- 三元运算语法
<li :style="{color: isActive?'#08d':''}">style三元运算语法</li>