6-SCJ -》 CWL

认识Vue

Vue(读音似view)

Vue是一个渐进式的框架,响应式

Vue有很多特点和Web卡法中常见的高级功能:

  1. 解耦视图和数据
  2. 可复用的组件
  3. 前端路由技术
  4. 状态管理
  5. 虚拟DOM

引入Vue的CDN

1
2
3
4
5
<!--开发环境版本,包含了有帮助的命令行警告-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<!--生产环境版本,优化了尺寸和速度-->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>

初步使用

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
{{message}}
<h1>{{name}}</h1>
</div>
<div>
{{message}}
</div>

<script src="./js/vue.js"></script>

<script>
const app = new Vue({
el: '#app', //绑定元素
data: { //设置数据
message: 'Hello 黄泽彬',
name: '黄泽彬'
}
})
</script>
</body>
</html>

循环展示列表数据 v-for

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<div id="app">
<ul>
<li v-for="item in movies">
{{item}}
</li>
</ul>
</div>

<script src="./js/vue.js"></script>

<script>
const app = new Vue({
el: '#app',
data: {
name: 'HuangZebin',
movies: ['速度与激情', '西游记', '垃圾']
}
})
</script>
</body>

绑定事件 v-on

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<div id="app">
当前技术:{{counter}}
<button v-on:click="++counter">+</button>
<button v-on:click="--counter">-</button>
</div>

<script src="./js/vue.js"></script>

<script>
const app = new Vue({
el: '#app',
data: {
counter: 0
}
})
</script>
</body>
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
<body>
<div id="app">
当前技术:{{counter}}
<button @click="add">+</button>
<button @click="sub">-</button>
</div>

<script src="./js/vue.js"></script>

<script>
const app = new Vue({
el: '#app',
data: {
counter: 0
},
methods: {
add: function() {
++this.counter;
},
sub: function() {
--this.counter;
}
}
})
</script>
</body>

MVVM

MVVM (Model View View Model)

Vue实例传入的options

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
el
类型:string | HTMLElement
作用:决定之后Vue实例会管理哪一个DOM
data
类型:Object | Function【组件当中data必须是一个函数】
作用:Vue实例对应的数据对象
methods
类型:{ [key: string]: Function }
作用:定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用
filters
过滤器
components
组件
props
属性

Mustache语法

通过Mustache语法(双大括号),可以将data中的文本数据插入到HTML中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<body>
<!--Mustache语法不仅可以写变量,也可以写简单的表达式-->
<div id="app">
<h1>{{message}}</h1>
<h1>{{firstName + ' ' + lastName}}</h1>
<h1>{{firstName}} {{lastName}}</h1>
<h1>{{counter * 2}}</h1>
</div>

<script src="js/vue.js"></script>

<script>
const app = new Vue({
el: '#app',
data: {
message: 'Hello',
firstName: '黄',
lastName: '泽彬',
counter: 1
}
})
</script>
</body>

v-once语法

v-once指令不需要跟任何表达式,该指令表示元素和组件只渲染一次,不会随着数据的改变而改变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
<div id="app">
<h1>{{message}}</h1>
<h1 v-once>{{message}}</h1>
<!--在控制台修改message,虽是响应式的,但v-once那一句不会改变-->
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '黄泽彬'
}
})
</script>
</body>

v-html语法

v-html可以将数据解析为htm格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<div id="app">
<h1>{{url}}</h1>

<!--解析为html-->
<h1 v-html="url"></h1>
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
url: '<a href="http://hzebin.cn">黄泽彬个人主页</a>'
}
})
</script>
</body>

v-text语法

v-text作用和Mustache比较相似:都是用于将数据显示在界面中,v-text通常情况下,接收一个string类型

(不够灵活,后面的数据会被覆盖,没有双大括号好用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<div id="app">
<h1>{{message}}456</h1>

<!--456会被覆盖-->
<h1 v-text="message">456</h1>
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '123'
}
})
</script>
</body>

v-pre指令

v-pre用于跳过这个元素和它子元素的编译过程,用于显示原本的Mustache语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<div id="app">
<h1>{{message}}</h1>

<!--显示{{message}}-->
<h1 v-pre>{{message}}</h1>
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '黄泽彬'
}
})
</script>
</body>

[??] 13 v-cloak

v-bind指令

v-bind作用是动态绑定属性,缩写为【:】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<body>
<div id="app">
<!--将数据动态绑定给属性-->
<img v-bind:src="imageUrl" alt=""/>
<a v-bind:href="aHref">百度一下</a>

<!--语法糖的写法-->
<img :src="imageUrl" alt=""/>
<a :href="aHref">百度一下(语法糖)</a>
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
imageUrl: 'https://i0.hdslb.com/bfs/archive/a85460cd16061187d1eb88ef849008ac7e8f1f3a.jpg@336w_190h.webp',
aHref: 'http://baidu.com'
}
})
</script>
</body>

v-bind动态绑定class—-对象语法

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
36
37
38
39
<head>
<meta charset="UTF-8">
<title>Title</title>

<style>
.active {
color: red;
}
</style>
</head>
<body>
<div id="app">
<h1 class="active">你好</h1>

<!--<h1 :class="{key1: value1, key2: value2}"></h1>-->
<!--<h1 :class="{类名: boolean, 类名: value2}"></h1>-->

<!--class的数据会合并-->
<h1 class="t1" :class="{active: isActive, line: isLine}">{{message}}</h1>
<button @click="btnClick">按钮</button>
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
isActive: true,
isLine: true
},
methods: {
btnClick: function() {
this.isActive = !this.isActive;
}
}
})
</script>
</body>

v-bind动态绑定class—-数组语法

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
<body>
<div id="app">
<!-- 当成字符串解析-->
<h1 class="title" :class="['aa', 'bbb']">{{message}}</h1>
<!-- 没有单引号为变量-->
<h1 :class="['a', c]">{{message}}</h1>
<!--通过方法返回-->
<h1 :class="getClass()">{{message}}</h1>
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Hello',
c: 'ccc'
},
methods: {
getClass: function() {
return [this.c];
}
}
})
</script>
</body>

v-bind动态绑定style(一)

我们可以用v-bind:style来绑定一些CSS内联样式

在写CSS属性名时,比如font-size

  1. 我们可以使用驼峰式(camelCase)fontSize
  2. 或短横线分割(Kebab-case,记得用单引号括起来)’font-size’

绑定class有两种方式:

  1. 对象语法(属性值加了单引号为字符串,否则为变量)

  2. 数组语法

    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
    <body>
    <!--对象属性-->

    <div id="app">
    <!--<h1 :style="{key属性名: value属性值}"></h1>-->
    <h1 :style="">{{message}}</h1>
    <!--对象语法-->
    <h1 :style="{fontSize: '50px'}">{{message}}</h1>
    <h1 :style="{fontSize: finalSize}">{{message}}</h1><!--推荐-->
    <h1 :style="getStyle()">{{message}}123</h1>
    </div>

    <script src="js/vue.js"></script>
    <script>
    const app = new Vue({
    el: '#app',
    data: {
    message: 'Hello World',
    finalSize: '100px'
    },
    methods: {
    getStyle: function() {
    return {fontSize: this.finalSize};
    }
    }
    })
    </script>
    </body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<!--数组属性-->

<div id="app">
<h1 :style="[baseStyle1, baseStyle2]">{{message}}</h1>
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'hzb',
baseStyle1: {fontSize: '100px'}, //对象,键值对
baseStyle2: {backgroundColor: 'red'}
}
})
</script>
</body>

计算属性 computed

computed: {} 可以对需要的数据进行转化之后显示,或需要将多个数据结合起来显示。(只执行一次,有缓存)(相当于一个属性,用名词命名

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
<body>
<div id="app">
<h1>{{firstName + ' ' + lastName}}</h1>
<h1>{{firstName}} {{lastName}}</h1>

<h1>{{getFullName()}}</h1>

<!--计算属性,是一个属性,不用()-->
<h1>{{fullName}}</h1>
</div>

<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
firstName: 'Huang',
lastName: 'Zebin'
},
methods: {
getFullName: function() {
return this.firstName + ' ' + this.lastName;
}
},
computed: { //计算属性
fullName: function() {
return this.firstName + ' ' + this.lastName;
}
}
})
</script>
</body>
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
<body>
<div id="app">
<h1>总价为{{totalPrice}}</h1>
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
books: [
{id: '101', name: '操作系统', price: 100},
{id: '102', name: 'Linux操作系统', price: 150},
{id: '103', name: '高等数学', price: 200}
]
},
computed: {
totalPrice: function() {
let total = 0;
for (let i = 0; i < this.books.length; ++i) {
total += this.books[i].price;
}
return total;
}
}
})
</script>
</body>

完整写计算属性的get/set

计算属性一般没有set方法,为只读属性

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
<body>
<div id="app">
<h1>{{fullName}}</h1>
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
firstName: 'Huang',
lastName: 'Zebin'
},
computed: {
fullName: {
set: function(newValue) {
//在控制台改变fullName变量的值试试
console.log("==" + newValue + "==");
const names = newValue.split(' '); //空格隔开,返回数组
this.firstName = names[0];
this.lastName = names[1];
},
get: function() {
return this.firstName + ' ' + this.lastName;
}
}
}
})
</script>
</body>

计算属性和methods的对比

methods方法每次调用都会重新计算;而computed只要结果不变就只执行一次,它有缓存推荐computed

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
<body>
<div id="app">
<!--1.直接拼接,语言过于繁琐-->
<h1>{{firstName}} {{lastName}}</h1>
<!--2.通过methods方法-->
<h1>{{getFullName()}}</h1>
<!--3.通过计算属性,有缓存,只执行一次-->
<h1>{{fullName}}</h1>
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
firstName: 'Huang',
lastName: '泽彬'
},
methods: {
getFullName: function() {
return this.firstName + ' ' + this.lastName;
}
},
computed: {
fullName: function() {
return this.firstName + ' ' + this.lastName;
}
}
})
</script>
</body>

ES6补充

事实上var是JS的一个设计上的错误,let关键字是可以更完美代替var。let有块级作用域,而var在块外任然可以访问会有问题

const关键字:用const修饰的标识符不会被再次赋值,保证了数据的安全性,但是申明的时候需要初始化一个值。

1
2
3
4
const
1. 一旦给const修饰的标识符被赋之后,不能修改
2. 在使用const定义标识符,必须进行赋值
3. 常量的含义是指向的对象不能修改(内存地址不能改),但是可以改变对象的内部属性

【建议】在ES6开发中,优先使用const,只有需要改变某一个标识符的时候才使用let

增强写法

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<body>
<script>
// const obj = {} //这个大括号就是字面量

//字面量的一般写法
// const app = {
// name: 'Huang',
// age: 20,
// run: function() {
// console.log("我在奔跑");
// },
// eat: function() {
// console.log("我在吃东西");
// }
// }

//1. 属性的增强写法
const name = 'Huang Zebin';
const age = 20;
const height = 1.75;

//ES5的写法
// const obj = {
// name: name,
// age: age,
// height: height
// }
// console.log(obj);

//ES6的写法,可以直接写标识符进去
const obj = {
name,
age,
height
}
console.log(obj);

//==============================

//2. 函数的增强写法

//ES5写法
// const obj2 = {
// eat: function() {
//
// },
// run: function() {
//
// }
// }

//ES6写法
const obj2 = {
run() {

},
eat() {

}
}

//==============================

</script>
</body>
1
2
3
4
5
6
ES5
''单引号
""双引号
ES6
``【可以换行】
上面三种方式都可以申明字符串

v-on的基本使用和语法糖

1
2
3
指令:v-on
作用:绑定事件监听器
缩写:@
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<body>
<div id="app">
<h1>{{counter}}</h1>
<button v-on:click="add">+</button>
<button @click="--counter">-</button>
</div>

<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
counter: 0
},
methods: {
add() {
++this.counter;
}
}
})
</script>
</body>

v-on方法含有参数

当通过methods中定义方法,以供@click调用时,需要注意参数问题:

  1. 如果该方法不需要额外参数,那么方法后的()可以不添加

    但是注意:如果方法本身中有一个参数,那么会默认将原生事件event参数传递进去。

  2. 如果需要同时传入某个参数,同时需要event时,可以通过$event传入事件

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
36
37
38
<body>
<div id="app">
<!--事件调用的方法没有参数-->
<button @click="btn1Click">按钮1</button>
<button @click="btn1Click()">按钮1</button>

<!--含有一个参数-->
<button @click="btn2Click(888)">按钮2</button>
<button @click="btn2Click()">按钮2</button> <!--undefine-->
<button @click="btn2Click">按钮2</button> <!--event事件-->

<!--含有event参数,也有其他参数-->
<button @click="btn3Click($event, 1, 2, m1, 'm1')">按钮3</button>
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
m1: '11111'
},
methods: {
btn1Click() {
console.log("123", "----");
},
btn2Click(a) {
//如果调用的方法有(),没有传入参数值,则为undefine
//如果没有()则为event事件
console.log(a);
},
btn3Click(event, a, b, c, d) {
console.log(event, a, b, c, d);
}
}
})
</script>
</body>

v-on的修饰符

1
2
3
4
5
.stop修饰符,可以组织冒泡现象
.prevent修饰符,阻止默认行为;例如提交表单,会阻止浏览器自动提交表单,转到自己的方法
.{keyCode | keyAlias}监听键盘的按键
.native监听组件根元素的原生事件
.once只触发一次回调
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<body>

<div id="app">
<!--会冒泡现象,如果点击了按钮1,div1Click也会触发-->
<div @click="div1Click">
aaa
<button @click="btn1Click">按钮1</button>
</div>

<hr/>

<!--.stop修饰符,阻止了冒泡的现象-->
<div @click="div1Click">
aaa
<button @click.stop="btn1Click">按钮1</button>
</div>

<hr/>

<!--.prevent修饰符,阻止默认事件-->
<form action="baidu">
<input type="submit" value="提交" @click.prevent="subClick"/>
</form>

<!--监听键盘的某一个按键-->
<input type="text" @keyup="keyClick"/>

<hr/>
<!--监听回车按键被按-->
<input type="text" @keyup.enter="enterClick"/>

<!--.once修饰符,只执行一次-->
<button @click.once="btn2Click">按钮2</button>
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {

},
methods: {
btn1Click() {
console.log("btn1Click");
},
div1Click() {
console.log("div1Click");
},
subClick() {
console.log("subClick");
},
keyClick() {
console.log("keyClick");
},
enterClick() {
console.log("回车");
},
btn2Click() {
console.log("btn2Click");
}
}
})
</script>
</body>

v-if和v-else-if和v-else的使用

如果条件很多,推荐使用计算属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<div id="app">
<h1 v-if="score >= 90">优秀</h1>
<h1 v-else-if="score >= 80">良好</h1>
<h1 v-else-if="score >= 60">及格</h1>
<h1 v-else>不及格</h1>
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
score: 92
}
})
</script>
</body>

v-show

v-show指令决定一个元素是否渲染

v-if与v-show的比较:

  1. v-if当条件为false时,不会有对应的元素在DOM
  2. v-show当条件为false时,仅仅是将元素的display属性设置为none而已

怎么选择:

  1. 当需要在显示和隐藏之间切面很频繁是,使用v-show
  2. 当只有一次切换时,使用v-if

v-for遍历数组和对象

1
有下标值,先item后index

遍历数组

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
<body>
<div id="app">
<!--没有下标值-->
<ul>
<li v-for="item in names">{{item}}</li>
</ul>

<!--有下标值,先item后index-->
<ul>
<li v-for="(item, index) in names">{{index + 1}} {{item}}</li>
</ul>
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
names: ['黄泽彬', '熊君', '陈文略']
},
methods: {

}
})
</script>
</body>

遍历对象

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
36
37
38
39
<body>
<div id="app">
<!--没有使用循环-->
<ul>
<li>{{info.name}}</li>
<li>{{info.age}}</li>
<li>{{info.height}}</li>
</ul>

<!--(value) 值-->
<ul>
<li v-for="value in info">{{value}}</li>
</ul>

<!--(value, key) 值 键-->
<ul>
<li v-for="(value, key) in info">{{value}}--{{key}}</li>
</ul>

<!--(value, key, index) 值 键 序号-->
<ul>
<li v-for="(value, key, index) in info">{{value}}--{{key}}--{{index}}</li>
</ul>
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
info: {
name: 'HuangZebin',
age: 20,
height: 1.75
}
}
})
</script>
</body>

理解v-for绑定和非绑定key的区别

推荐在使用v-for的时候,给对应的元素或组件添加:key属性,这个:key值需要能与值对应(唯一性),比如value,但index可能会改变

为什么要key属性:Vue的虚拟DOM有关系,加key不让它复用原来的代码

数组中的哪些方法是响应式的

因为Vue是响应式的,所以当数据发生变化时,Vue会自动检测数据变化,视图会发生对应的更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
哪些方法是响应式的:
push()在数组的尾部添加元素【可以一次添加多个元素,逗号分隔】
pop()删除数组尾部的元素
shift()删除数组头部的元素
unshift()在数组头部添加元素【可以一次添加多个元素,逗号分隔】
splice()
删除元素:
splice(start, n) 从start下标开始,删除n个元素,如果省略n则后面全删
替换元素:
splice(start, n, ...value[])从start删n个,之后添加元素在那个位置
插入元素:
splice(start, 0, ...value[])在start位置开始插入元素
sort()排序,默认从小到大
reverse() 数组反转

Vue.set(要修改的对象, 索引值, 修改后的值)
例如Vue.set(this.arr, 0, 'bb');

=============================
通过索引修改元素不是响应式的
=============================

表单绑定v-model

Vue中使用v-model指令来实现表单元素和数据的双向绑定

原理:

  1. v-bind绑定一个value属性

  2. v-on指令给当前元素绑定input事件

  3. 等价于

    1
    2
    3
    <input type="text" v-model="message"/>
    ↑↓
    <input type="text" :value="message" :input="message = $event.target.value"/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<div id="app">
双向绑定
<input type="text" v-model="message"/>
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'Huang Zebin'
}
})
</script>
</body>

v-model与radio(单选框)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<body>
<div id="app">
<!-- v-model绑定了同一个sex之后,可以省略name-->
<label for="male"> <!--label的for可以在用户选中文字之后,按钮会自动选中-->
<input type="radio" id="male" name="sex" value="男" v-model="sex"/>
</label>
<label for="female">
<input type="radio" id="female" name="sex" value="女" v-model="sex"/>
</label>
<h1>你选择的性别是:{{sex}}</h1>
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
sex: '' //可以初始化 男-女
}
})
</script>
</body>

v-model与checkbox(复选框)

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
<body>
<div id="app">
<!--checkbox单选框-->
<label for="agree">
<input type="checkbox" id="agree" v-model="isAgree"/>同意协议
</label>
<br/>
<button :disabled="!isAgree">下一步</button>

<hr/>

<!--checkbox多选框-->
<input type="checkbox" value="足球" v-model="hobbies"/>足球
<input type="checkbox" value="台球" v-model="hobbies"/>台球
<input type="checkbox" value="乒乓球" v-model="hobbies"/>乒乓球
<input type="checkbox" value="蓝球" v-model="hobbies"/>篮球
<h1>选择的爱好为:{{hobbies}}</h1>
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
isAgree: false,
hobbies: []
}
})
</script>
</body>

单选框对应boolean,多选框对应数组

v-model和select结合使用

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
<body>
<div id="app">
<!--选择一个-->
<select v-model="fruit">
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="葡萄">葡萄</option>
<option value="桃子">桃子</option>
</select>
<h1>选了什么水果:{{fruit}}</h1>

<!--选择多个-->
<select v-model="fruits" multiple>
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="葡萄">葡萄</option>
<option value="桃子">桃子</option>
</select>
<h1>选了什么水果:{{fruits}}</h1>
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
fruit: '香蕉',
fruits: []
}
})
</script>
</body>

值绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<div id="app">
<label v-for="item in starts" :for="item">
<input type="checkbox" :id="item" :value="item" v-model="selectStarts"/>{{item}}
</label>
<h1>选择的明星为:{{selectStarts}}</h1>
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
//已选择
selectStarts: [],
//原数据
starts: ['赵丽颖', '吴亦凡', '邓超', '李现']
}
})
</script>
</body>

v-model修饰符

1
2
3
4
5
6
7
8
9
10
11
lazy修饰符
1. 默认情况下,v-model默认是在input事件中同步输入框的数据的
2. 也就是说,一旦有数据发生改变对应的data中的数据就会自动发生改变
3. lazy修饰符可以让数据在失去焦点或者回车时才会更新
number修饰符
1. 默认情况下,在输入框中无论我们输入的是字母还是数字,都会被当作【字符串】类型进行处理
2. 但是如果我们希望处理的数字类型,那么最好直接将内容当做数字处理
3. number修饰符可以让在输入框中输入的内容自动转成数字类型
trim修饰符
1. 如果输入的内容首尾有很多空格,通常我们希望将其去除
2. trim修饰符可以过滤内容左右边的空格
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
<body>
<div id="app">
<!-- lazy修饰符 -->
<input type="text" v-model.lazy="message"/>
{{message}}

<hr/>

<!-- number修饰符 -->
<input type="number" v-model.number="age"/>
类型{{typeof age}}

<hr/>

<!-- trim修饰符 -->
<input type="text" v-model.trim="name"/>
name={{name}}
</div>

<script src="js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '123',
age: 23,
name: ''
}
})
</script>
</body>

组件化初步认识

  • 我们将一个完整的页面分成很多个组件
  • 每个组件都用于实现页面的一个功能
  • 而每一个组件又可以进行细分
  • 组件化是Vue.js中的重要思想
    • 它提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用
    • 任何的应用都会被抽象成一棵组件树
  • 组件化思想的应用:
    • 有了组件化思想,我们在之后的开发中就要充分利用它
    • 进可能地将页面拆分成一个个小地、可复用地组件
    • 这样让我们的代码更加方便组织和管理,并且扩展性也更强

注册组件的基本步骤

  1. 创建组件构造器Vue.extend()
    1. 调用Vue.extend()创建的是一个组件构造器
    2. 通常在创建组件构造器是,传入template代表我们自定义组件的模板
    3. 该模板就是在使用到组件的地方,要显示的HTML代码
  2. 注册组件Vue.component()
    1. 调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称
    2. 需要传递两个参数:注册组件的标签名、组件构造器
  3. 使用组件
    1. 组件必须挂载到某个Vue实例下,否则它不会生效
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
<body>
<div id="app">
<!--3.使用-->
<my-cpn></my-cpn>
<my-cpn></my-cpn>
</div>

<script src="js/vue.js"></script>
<script>
//1.创建组件构造器对象
const cpnC = Vue.extend({
template: `
<div>
<h6>我是标题</h6>
<p>第一行内容</p>
<p>第二行内容</p>
</div>`
})

//2.注册组件 【这里是全局组件】
Vue.component('my-cpn', cpnC);

const app = new Vue({
el: '#app'
})
</script>
</body>

【!写组件的时候,要写在

!】

全局组件和局部组件

在开发中一般只有一个new Vue实例

全局组件:可以在多个Vue的实例下使用

局部组件:在new Vue里面定义的 components: {标签名: 组件名}

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<body>
<div id="app">
<cpn2></cpn2>
<cpn1></cpn1>
</div>

<div id="app2">
<cpn1></cpn1>
<cpn2></cpn2>
</div>

<script src="js/vue.js"></script>

<script>
//全局组件
const cpnC = Vue.extend({
template: `
<div>
<h1>我的标题</h1>
<p>哈哈哈这一行是废的</p>
</div>
`
})

Vue.component('cpn1', cpnC);
</script>
<script>
//局部组件
const cpnD = Vue.extend({
template: `
<div>
<h2>局部的组件模板</h2>
</div>
`
})
</script>

<script>
const app = new Vue({
el: '#app'
})

const app2 = new Vue({
el: '#app2',
//下面定义局部组件
components: {
cpn2: cpnD
}
})
</script>
</body>

父组件和子组件的区别

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
36
37
38
39
40
<body>
<div id="app">
<cpn></cpn>
</div>

<script src="js/vue.js"></script>
<script>
//子组件
const cpnC1 = Vue.extend({
template: `
<div>
<h1>组件1</h1>
</div>
`
})

//父组件
const cpnC2 = Vue.extend({
template: `
<div>
<h1>组件2</h1>
<cpn1></cpn1>
</div>
`,
//申明使用的子组件
components: {
cpn1: cpnC1
}
})

//root组件
const app = new Vue({
el: '#app',
//申明使用的父组件
components: {
cpn: cpnC2
}
})
</script>
</body>

组件的语法糖写法【组件标签名用小写】

查找组件时,先在自己内部找components,如果没有则找全局的,所以全局的组件可以不用在new Vue实例里面的components注册

组件的名字用小写的,因为HTML解析是不区分大小写,用全小写来查找组件标签!!

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
<body>
<div id="app">
<my-cpn></my-cpn>
<my-cpn2></my-cpn2>
</div>

<script src="js/vue.js"></script>
<script>
Vue.component('my-cpn', {
template: `
<div>
<h1>语法糖-全局组件</h1>
</div>
`
})
</script>
<script>
const app = new Vue({
el: '#app',
components: {
'my-cpn2': {
template: `
<div>
<h1>语法糖--局部组件</h1>
</div>
`
}
}
})
</script>
</body>

组件模板抽离的写法

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
36
37
38
39
<body>
<div id="app">
<my-cpn1></my-cpn1>
<my-cpn2></my-cpn2>
</div>

<!--组件分离的模板 全局变量 方式1-->
<script type="text/x-template" id="my-cpn1">
<div>
<h1>组件模板分离-全局变量 方式1</h1>
</div>
</script>

<!--组件分离的模板 全局变量 方式2-->
<template id="my-cpn2">
<div>
<h1>组件模板分离-全局变量 方式2</h1>
</div>
</template>

<script src="js/vue.js"></script>

<script>
Vue.component('my-cpn1', {
template: '#my-cpn1'
})
</script>
<script>
Vue.component('my-cpn2', {
template: '#my-cpn2'
})
</script>

<script>
const app = new Vue({
el: '#app'
})
</script>
</body>

组件data必须是函数

组件里面访问不了实例里的data数据

组件自己的数据存放在哪里呢:

  1. 组件对象也有一个data属性(也有methods等属性)
  2. 只是这个data属性必须是一个函数
  3. 而且这个函数返回一个对象,对象内部保存着数据

如果组件里的data不是函数,那么组件复用时,数据是共享的

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
36
37
38
39
40
41
<body>
<div id="app">
<my-cpn1></my-cpn1>
<my-cpn1></my-cpn1>
</div>

<template id="my-cpn1">
<div>
<h1>当前计数为:{{counter}}</h1>
<button @click="decrement">-</button>
<button @click="increment">+</button>
</div>
</template>

<script src="js/vue.js"></script>

<script>
Vue.component('my-cpn1', {
template: '#my-cpn1',
data() {
return {
counter: 0
}
},
methods: {
increment() {
++this.counter;
},
decrement() {
--this.counter;
}
}
})
</script>

<script>
const app = new Vue({
el: '#app'
})
</script>
</body>

父子组件的通信

在开发中,往往一些数据确实需要从上层(root)传递到下层(小组件)。

  • 比如在一个页面中,我们从服务器请求到了很多的数据
  • 其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。
  • 这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)

如何进行父子组件间的通信呢:

  • 通过props向子组件传递数据
  • 通过事件向父组件发送信息

props的值有两种方式:

  • 一:字符串数组,数组中的字符串就是传递时的名称

  • 二:对象,对象可以设置传递时的类型,也可以设置默认值等。可以对类型进行验证,验证支持的类型:String, Number, Boolean, Array, Object, Date, Function, Symbol; 如果有自定义构造函数时,验证也支持自定义的类型。还可以提供默认值,如果使用了模板标签而没有v-bind绑定属性的值的话,会使用默认值。还可以限制使用模板标签时是否必传值

    1
    2
    3
    4
    5
    对象名: {
    type: 类型,
    default: 默认值,
    required: true/false 如果为true时,使用模板标签必须赋值
    }
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
36
37
【父组件-》子组件  方式1】

<body>
<div id="app">
<my-cpn :cmessage="message" :cstarts="starts"></my-cpn>
</div>

<template id="my-cpn">
<div>
<h1>子组件</h1>
<ul>
<li v-for="item in cstarts">{{item}}</li>
</ul>
<p>{{cmessage}}</p>
</div>
</template>

<script src="js/vue.js"></script>

<script>
Vue.component('my-cpn', {
template: '#my-cpn',
//下面这里是变量
props: ['cmessage', 'cstarts']
})
</script>

<script>
const app = new Vue({
el: '#app',
data: {
message: '收到',
starts: ['邓超', '吴亦凡', '赵丽颖']
}
})
</script>
</body>
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
【父组件 -》 子组件  方式二】

<body>
<div id="app">
<my-cpn :cmessage="message" :cstarts="starts"></my-cpn>
</div>

<template id="my-cpn">
<div>
<h1>{{cmessage}}</h1>
<ul>
<li v-for="item in cstarts">{{item}}</li>
</ul>
</div>
</template>

<script src="js/vue.js"></script>

<script>
Vue.component('my-cpn', {
template: '#my-cpn',
// props: ['cmessage', 'cstarts'] 方式一:数组

//方式二:对象
// props: {
// //可以限制类型
// cmessage: String,
// cstarts: Array
// }

props: {
cmessage: {
//类型
type: String,
//默认值
default: 'aaa',
//true时,使用模板必须要传值
required: true
},
cstarts: {
type: Array,
//类型是对象或数组时,默认类型必须是函数
default() {
return [];
}
}
}
})
</script>

<script>
const app = new Vue({
el: '#app',
data: {
message: '明星展示',
starts: ['吴亦凡', '赵丽颖', '邓超', '邓紫棋']
}
})
</script>
</body>
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
【子组件 -》 父组件  自定义事件$smit】

<body>
<div id="app">
<!--接收子组件发射出来的信息--><!--cpnClick的参数不用写,默认传递-->
<my-cpn @categories-btn-click="cpnClick"></my-cpn>
</div>

<template id="my-cpn">
<div>
<button v-for="item in categories" @click="btnClick(item)">
{{item.name}}
</button>
</div>
</template>

<script src="js/vue.js"></script>

<script>
Vue.component('my-cpn', {
template: '#my-cpn',
data() {
return {
categories: [
{id: '0', name: '热门推荐'},
{id: '1', name: '手机数码'},
{id: '2', name: '家用家电'},
{id: '3', name: '电脑办公'}
]
}
},
methods: {
btnClick(item) {
//子组件 向 父组件 发射事件
//用-分隔!!
this.$emit('categories-btn-click', item, '测试');
}
}
})
</script>

<script>
const app = new Vue({
el: '#app',
methods: {
cpnClick(item, a) {
console.log(item, '123', a)
}
}
})
</script>
</body>

父访问子里面的东西–$children $refs

this.$children返回的是子组件数组

this.$refs返回对象类型,默认为空对象{};需要在使用标签那里加ref=””

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<body>
<div id="app">
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<my-cpn></my-cpn>
<button @click="btnClick">按钮</button>
</div>

<template id="my-cpn">
<div>
<h1>我是子组件</h1>
</div>
</template>

<script src="./js/vue.js"></script>

<script>
const app = new Vue({
el: '#app',
methods: {
btnClick() {
console.log(this.$children); //返回的是数组
// $children 需要下标值,不够灵活,如果在中间插新的组件,下标值会改变
// this.$children[0].showMessage();
// console.log(this.$children[0].name);
for (let c of this.$children) {
c.showMessage();
console.log(c.name);
}
}
},
components: {
'my-cpn': {
template: '#my-cpn',
methods: {
showMessage() {
console.log('Hello, 我是子组件里的方法')
}
},
data() {
return {
name: '我是子组件的数据name'
}
}
}
}
})
</script>
</body>
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
36
37
38
39
40
41
42
43
44
<body>
<div id="app">
<cpn ref="ccc"></cpn>
<cpn ref="bbb"></cpn>
<cpn ref="aaa"></cpn>
<cpn></cpn>
<button @click="btnClick">按钮</button>
</div>

<template id="cpn">
<div>
<h1>我是一个组件</h1>
</div>
</template>

<script src="js/vue.js"></script>

<script>
const app = new Vue({
el: '#app',
methods: {
btnClick() {
// this.$refs 默认为空对象{}
console.log(this.$refs.aaa.showMessage());
}
},
components: {
cpn: {
template: '#cpn',
data() {
return {
name: '子组件的name'
}
},
methods: {
showMessage() {
console.log("子组件的方法");
}
}
}
}
})
</script>
</body>

子访问父里面的东西–$parent $root

不推荐子访问父组件,这样子组件与父组件之间有耦合性,子组件不能独立

this.$parent访问父组件

this.$root访问根组件【直接访问到Vue实例】

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
<body>
<div id="app">
<cpn></cpn>
</div>

<template id="cpn">
<div>
<h1>我是子组件</h1>
<button @click="btnClick">按钮</button>
</div>
</template>

<script src="js/vue.js"></script>

<script>
const app = new Vue({
el: '#app',
data: {
message: '父组件message'
},
components: {
cpn: {
template: '#cpn',
methods: {
btnClick() {
//访问父组件 this.$parent
console.log(this.$parent.message);
}
}
}
}
})
</script>
</body>

slot插槽的基本使用

组件的插槽:

  • 组件的插槽也是为了让我们封装的组件更加具有扩展性
  • 让使用者决定组件内部的一些内容到底展示什么
  1. 插槽的基本使用
  2. 插槽的默认值默认值写在这里
  3. 如果有多个值同时放入到一个插槽进行替换时,一起作为替换元素
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
36
37
38
<body>
<div id="app">
<cpn></cpn>
<cpn>
<!--插入插槽-->
<span>嘻嘻嘻</span>
</cpn>
<cpn>
<i>我去跑步了</i>
<i>我去跑步了</i>
</cpn>
<cpn></cpn>
<cpn></cpn>
</div>

<template id="cpn">
<div>
<h1>我是组件</h1>
<p>哈哈哈哈哈哈</p>
<!--插槽标签,标签里面写的是默认值,如果使用时有新的则将默认值覆盖-->
<slot><button>按钮</button></slot>
</div>
</template>

<script src="js/vue.js"></script>

<script>
Vue.component('cpn', {
template: '#cpn'
})
</script>

<script>
const app = new Vue({
el: '#app'
})
</script>
</body>

slot具名插槽的使用

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
36
37
38
<body>
<div id="app">
<cpn>
<!--指定插槽-->
<button slot="left">返回</button>
<span slot="right">菜单</span>
<span slot="center">
<span>aaa</span>
<span>bbb</span>
</span>
</cpn>
<hr/>
<cpn><span slot="right">菜单</span></cpn>
</div>

<template id="cpn">
<div>
<!--给插槽起名字-->
<slot name="left"><span>左边</span></slot>
<slot name="center"><span>中间</span></slot>
<slot name="right"><span>右边</span></slot>
</div>
</template>

<script src="js/vue.js"></script>

<script>
Vue.component('cpn', {
template: '#cpn'
})
</script>

<script>
const app = new Vue({
el: '#app'
})
</script>
</body>

理解编译作用域的概念

判断是哪里的变量,看在哪一个模板,而不是在标签的那个组件

父组件模板的所有东西都会在父级作用域内编译,子组件模板的所有东西都会在子级作用域内编译

父组件替换插槽的标签,但是内容有子组件来提供

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
<body>
<div id="app">
<cpn v-show="isShow"></cpn> <!--使用了new Vue实例的isShow变量-->
</div>

<template id="cpn">
<div>
<h1>我是子组件,有两个isShow,看看是哪一个</h1>
</div>
</template>

<script src="js/vue.js"></script>

<script>
Vue.component('cpn', {
template: '#cpn',
data() {
return {
//这里有一个isShow变量
isShow: false
}
}
})
</script>

<script>
const app = new Vue({
el: '#app',
data: {
//这里有一个isShow变量
isShow: true
}
})
</script>
</body>

作用域插槽的使用

如果父组件对子组件展示的内容不满意,想以另外一种方式进行展示,就可以用到作用域插槽。

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<body>
<div id="app">
<cpn></cpn>

<cpn>
<!--目的是获取插槽的数据-->
<template slot-scope="s">
<!--pLanguage写不了驼峰,浏览器会识别为planguage-->
<span v-for="item in s.planguage">{{item}} - </span>
<br/>
<!--数组.join() 分隔数组-->
<span>{{s.planguage.join(' # ')}}</span>
<br/>
<span>{{s.abc}}</span>
</template>
</cpn>
</div>

<template id="cpn">
<div>
<!--插槽,设置:data数据,可以传到外面-->
<slot :planguage="pLanguage" :abc="abc">
<ul>
<li v-for="item in pLanguage">{{item}}</li>
</ul>
</slot>
</div>
</template>

<script src="js/vue.js"></script>

<script>
Vue.component('cpn', {
template: '#cpn',
data() {
return {
pLanguage: ['Java', 'C', 'C++', 'JavaScript', 'Python', 'Linux'],
abc: 'abc'
}
}
})
</script>

<script>
const app = new Vue({
el: '#app'
})
</script>
</body>

ES6模块化的导入和导出

export导出

import导入

什么是Webpack

从本质上将,webpack是一个现代的JavaScript应用的静态模块打包工具。

前端模块化】【打包

webpack其中一个核心就是让我们可能进行模块化开发,并且会帮助我们处理模块间的依赖关系,而且不仅仅是JavaScript文件,我们的CSS、图片、JSON文件等等在webpack中都可以被当做模块来使用。

grunt/gulp和we bpack有什么不同呢?

  1. grunt/gulp更加强调的是前端流程的自动化,模块化不是它的核心
  2. webpack更加强调模块化开发管理,而文件压缩合并、预处理等功能,是它附带的功能。

webpack运行需要依赖node环境

node环境为了可以正常执行很多代码,必须包含各种依赖的包;所以管理各种包需要一种管理工具:npm工具(node packages manager)

安装webpack首先要安装Node.js,Node.js自带了软件包管理工具npm

node -v 查看node版本

全局安装webpack(这里我先指定版本号3.6.0,因为vue cli2依赖该版本)

全局安装webpack:npm install webpack@3.6.0 -g

查看版本webpack –version

局部安装webpack(后续才需要)。–save-dev是开发时依赖,项目打包后不需要继续使用的

为什么全局安装后还需要局部安装的:

  1. 在终端直接执行webpack命令,使用的是全局安装的webpack
  2. 当在package.json中定义了scripts时,其中包含了webpack命令,那么使用的是局部webpack

====================================================

other:

数组.push()

数组.splice()

====

.toFixed(2) 保留两位小数

====

unfinish

44.JS高级函数和多张种方式的for

59.props的驼峰命名问题,先不用驼峰,用-分隔

65 watch

74 ES6导入导出

=====

typeof 变量名 -》获取变量的类型

========

Vue CLI3.X(构造Vue的项目)

Vue2.5.21

===================

不推荐子组件修改子组件的props数据,让父组件修改子组件的props数据

============