1. 第一个VUE应用

Html 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="app">
<h1>标题:商品管理系统</h1>
<ul>
<li v-for="(item, i) in products">
商品名称:{{item.name}},商品库存:
<button @click="changeStock(item, item.stock - 1)">-</button>
{{item.stock?item.stock:'无货'}}
<button @click="changeStock(item, item.stock + 1)">+</button>
<button @click="remove(i)">删除</button>
</li>
</ul>
<p>总库存: {{total}}</p>
</div>

JavaScript 示例:

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
var vm = new Vue({
el: "#app",
// 页面中要使用的数据
data: {
products: [
{ name: "iphone", stock: 10 },
{ name: "xiaomi", stock: 9 },
{ name: "huawei", stock: 8 },
],
},
//计算属性
computed: {
total() {
return this.products.reduce((a, b) => a + b.stock, 0);
},
},
//方法
methods: {
changeStock(product, newStock) {
if (newStock < 0) {
newStock = 0;
}
product.stock = newStock; // 数据变化
},
remove(index) {
this.products.splice(index, 1);
},
},
});

2. VUE核心概念

2-1. 注入

示例图片

vue会将以下配置注入到vue实例:

  • data:和界面相关的数据
  • computed:通过已有数据计算得来的数据,将来详细讲解
  • methods:方法

模板中可以使用vue实例中的成员

2-2. 虚拟DOM树

直接操作真实的DOM会引发严重的效率问题,vue使用虚拟DOM(vnode)的方式来描述要渲染的内容

vnode是一个普通的JS对象,用于描述界面上应该有什么,比如:

1
2
3
4
5
6
7
// 示例:实际并不是这样
var vnode = {
tag: "h1",
children: [
{ tag: undefined, text: "第一个vue应用:Hello World"}
]
}

上面的对象描述了: 有一个标签名为h1的节点,它有一个子节点,该子节点是一个文本,内容为「第一个vue应用:Hello World」
vue模板并不是真实的DOM,它会被编译为虚拟DOM

1
2
3
4
<div id="app">
<h1>第一个vue应用:{{title}}</h1>
<p>作者:{{author}}</p>
</div>

上面的模板会被编译为类似下面结构的虚拟DOM

1
2
3
4
5
6
7
{
tag: "div",
children: [
{ tag: "h1", children: [ { text: "第一个vue应用:Hello World" } ] },
{ tag: "p", children: [ { text: "作者:袁" } ] }
]
}

虚拟DOM树会最终生成为真实的DOM树

示例图片

当数据变化后,将引发重新渲染,vue会比较新旧两棵vnode tree,找出差异,然后仅把差异部分应用到真实dom tree中

示例图片
可见,在vue中,要得到最终的界面,必须要生成一个vnode tree

vue通过以下逻辑生成vnode tree:
示例图片
注意:虚拟节点树必须是单根的

2-3. 挂载

将生成的真实DOM树,放置到某个元素位置,称之为挂载

  1. 通过el:"css选择器"进行配置
  2. 通过vue实例.$mount("css选择器")进行配置

2-4. 完整流程

示例图片

3. 组件

组件的出现是为了实现以下两个目标:

  1. 降低整体复杂度,提升代码的可读性和可维护性
  2. 提升局部代码的可复用性

绝大部分情况下,一个组件就是页面中某个区域,组件包含该区域的:

  • 功能(JS代码)
  • 内容(模板代码)
  • 样式(CSS代码)

    要在组件中包含样式,需要构建工具的支撑

3-1. 创建组件

组件是根据一个普通的配置对象创建的,所以要开发一个组件,只需要写一个配置对象即可

该配置对象和vue实例的配置是几乎一样

1
2
3
4
5
6
7
8
9
//组件配置对象
var myComp = {
data(){
return {
// ...
}
},
template: `....`
}

值得注意的是,组件配置对象和vue实例有以下几点差异:

  • el
  • data必须是一个函数,该函数返回的对象作为数据
  • 由于没有el配置,组件的虚拟DOM树必须定义在templaterender

3-2. 注册组件

注册组件分为两种方式,一种是全局注册,一种是局部注册

3-2-1. 全局注册

一旦全局注册了一个组件,整个应用中任何地方都可以使用该组件

示例图片

全局注册的方式是:

1
2
3
4
// 参数1:组件名称,将来在模板中使用组件时,会使用该名称
// 参数2:组件配置对象
// 该代码运行后,即可在模板中使用组件
Vue.component('my-comp', myComp)

在模板中,可以使用组件了

1
2
3
<my-comp />
<!-- 或 -->
<my-comp></my-comp>

但在一些工程化的大型项目中,很多组件都不需要全局使用。
比如一个登录组件,只有在登录的相关页面中使用,如果全局注册,将导致构建工具无法优化打包
因此,除非组件特别通用,否则不建议使用全局注册

3-2-2. 局部注册

局部注册就是哪里要用到组件,就在哪里注册

示例图片

局部注册的方式是,在要使用组件的组件或实例中加入一个配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 这是另一个要使用my-comp的组件
var otherComp = {
components:{
// 属性名为组件名称,模板中将使用该名称
// 属性值为组件配置对象
"my-comp": myComp
},
template: `
<div>
<!-- 该组件的其他内容 -->
<my-comp></my-comp>
</div>
`;
}

3-2-3. 应用组件

在模板中使用组件特别简单,把组件名当作HTML元素名使用即可。

但要注意以下几点:

  1. 组件必须有结束

组件可以自结束,也可以用结束标记结束,但必须要有结束

下面的组件使用是错误的:

1
<my-comp>

3-2-4. 组件的命名

无论你使用哪种方式注册组件,组件的命名需要遵循规范。

组件可以使用kebab-case 短横线命名法,也可以使用PascalCase 大驼峰命名法

下面两种命名均是可以的

1
2
3
4
5
6
var otherComp = {
components:{
"my-comp": myComp, // 方式1
MyComp: myComp //方式2
}
}

实际上,使用小驼峰命名法 camelCase也是可以识别的,只不过不符合官方要求的规范

使用PascalCase方式命名还有一个额外的好处,即可以在模板中使用两种组件名

1
2
3
4
5
var otherComp = {
components:{
MyComp: myComp
}
}

模板中:

1
2
3
<!-- 可用 -->
<my-comp />
<MyComp />

因此,在使用组件时,为了方便,往往使用以下代码:

1
2
3
4
5
6
7
8
9
var MyComp = {
//组件配置
}

var OtherComp = {
components:{
MyComp // ES6速写属性
}
}

3-2-5. 组件树

一个组件创建好后,往往会在各种地方使用它。它可能多次出现在vue实例中,也可能出现在其他组件中。

于是就形成了一个组件树
示例图片

3-2-6. 向组件传递数据

大部分组件要完成自身的功能,都需要一些额外的信息

比如一个头像组件,需要告诉它头像的地址,这就需要在使用组件时向组件传递数据

传递数据的方式有很多种,最常见的一种是使用组件属性 component props

首先在组件中申明可以接收哪些属性:

1
2
3
4
5
6
7
8
9
10
var MyComp = {
props:["p1", "p2", "p3"],
// 和vue实例一样,使用组件时也会创建组件的实例
// 而组件的属性会被提取到组件实例中,因此可以在模板中使用
template: `
<div>
{{p1}}, {{p2}}, {{p3}}
</div>
`
}

在使用组件时,向其传递属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
var OtherComp = {
components: {
MyComp
},
data(){
return {
a:1
}
},
template: `
<my-comp :p1="a" :p2="2" p3="3"/>
`
}

注意:在组件中,属性是只读的,绝不可以更改,这叫做单向数据流
示例图片

4. 基础知识

4-1. 文件引用

  1. 在style中用:~@表示src目录
  2. 在script中用:@表示src目录

__END__