Vue使用教程(持续更新)

前端框架Vue

Vue介绍

挂载点 模板 实例化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<body>
<!--挂载点,模板,实例-->
<div id="root">{{msg}}111
</div>
<!--template会取代div里边的内容-->
<script>
var app = new Vue({
el: "#root",
template: '<h1>hello {{msg}}</h1>',
data: {
msg: "Hello world"
}
})
setTimeout(function(){
app.$data.msg = 'bye world'
},2000)
</script>
</body>

插值表达式- v-text和v-html

1
2
3
4
5
6
7
8
9
10
11
12
<body>
<!--挂载点,模板,实例-->
<div id="root">{{number}}</div>
<script>
new Vue({
el: "#root",
data: {
number: 123
}
})
</script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
<div id="root">
<div v-html="number"></div> =><h1>123</h1>
<div v-text="number"></div> => # 123
</div>
<script>
new Vue({
el: "#root",
data: {
number: "<h1>123</h1>"
}
})
</script>
</body>
</html>

v-text不会转译,v-html会转译

插值表达式和v-text作用一致

绑定事件 v-on

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<div id="root">
<div v-on:click="handleclick">{{content}}</div>
<div @click="handleclick">{{content}}</div>
</div>
<script>
new Vue({
el: "#root",
data: {
content: "hello"
},
methods: {
handleclick: function(){
this.content = "world"
}
}
})
</script>
</body>

v-on:可以省略为@

vue特点:不用对DOM操作,而直接对数据操作

属性绑定 JS表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
<body>
<div id="root">
<div v-bind:title="'这里是个表达式'+title">hello world</div>
</div>
<script>
new Vue({
el: "#root",
data: {
title: "this is hellow world"
}
})
</script>
</body>

v-bind:可以省略为:

v-bind:title=“ ” 引号里边是一个表达式而不单单是字符串

双向绑定 v-model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<body>
<div id="root">
<input v-model="content"/>
<div>{{content}}</div>
</div>
<script>
new Vue({
el: "#root",
data: {
content: "this is content"
}
})
</script>
</body>

v-model双向数据绑定,input的值变化,content会跟着和变化,div也会跟着变化。

计算属性 computed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<div id="root">
<input v-model="firstname" type="text">
<input v-model="lastname" type="text">
<div>{{fullname}}</div>
</div>
<script>
new Vue({
el: "#root",
data: {
firstname: '',
lastname: '',
},
computed: {
fullname:function(){
return this.firstname + ' ' + this.lastname
}
}
})
</script>
</body>

缓存机制:computed里边的的fullname只有当firstname和lastname的值改变的时候才会去计算,否则就用缓存的值也就是上一次的值去显示。因此代码比较高效。

侦听器 watch

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="root">
<input v-model="firstname" type="text">
<input v-model="lastname" type="text">
<div>{{fullname}}</div>
<div>{{count}}</div>
</div>
<script>
new Vue({
el: "#root",
data: {
firstname: '',
lastname: '',
count: 0,
},
computed: {
fullname:function(){
return this.firstname + ' ' + this.lastname
}
},
watch: {
firstname: function () {
this.count ++
},
lastname: function () {
this.count ++
}
}
})
</script>
</body>

每当firstname和lastname改变,侦听器就会收到反应,进行一些逻辑运算。

条件渲染

v-if
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<div id="root">
<div v-if="show">hello world</div>
<button @click="handleclick">toggle</button>
</div>
<script>
new Vue({
el: "#root",
data: {
show: true
},
methods: {
handleclick: function(){
this.show = !this.show;
}
}
})
</script>
</body>

v-if的数据项为false时候,会直接在dom将这个标签移除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<body>
<div id="root">
<div v-if="show">hello world</div>
<div v-else="show">Bye world</div>
</div>
<script>
new Vue({
el: "#root",
data: {
show: true
}
})
</script>
</body>

v-if为true显示hello world,为false时显示Bye world

v-show
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<div id="root">
<div v-show="show">hello world</div>
<button @click="handleclick">toggle</button>
</div>
<script>
new Vue({
el: "#root",
data: {
show: true
},
methods: {
handleclick: function(){
this.show = !this.show;
}
}
})
</script>
</body>

v-show的数据项为false时候,会将这个标签隐藏。性能更高

列表渲染(数组方式)

v-for
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
<div id="root">
<ul>
<li v-for="item of list" >{{item}}</li>
</ul>
</div>
<script>
new Vue({
el: "#root",
data: {
list: [1,3,5]
}
})
</script>
</body>

作用:循环展示

更安全的方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<body>
<div id="root">
<ul>
<li v-for="(item,index) of list" :key="index">{{item}}</li>
</ul>
</div>
<script>
new Vue({
el: "#root",
data: {
list: [1,2,3]
}
})
</script>
</body>

index是数组的下标。key值不能重复,而list的值可能会重复,所以使用index来避免key值重复问题

但是,如果在list数组push一个元素,元素多对应的下标均被改变,所有的dom都是重新渲染,浪费资源

更高效的方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<body>
<div id="root">
<ul>
<li v-for="(item,index) of list" :key="item.id">{{item.name}}</li>
</ul>
</div>
<script>
new Vue({
el: "#root",
data: {
list: [{
id: "00001",
neme: "Hello"
}, {
id: "00002",
neme: "World"
}, {
id: "00003",
neme: "Dotown"
}]
}
})
</script>
</body>

一般而言,在后端返回数据时,携带的有唯一标识符id,用id作为key值更为高效。

注意!

如果直接在数组添加一项,页面是不会改变的。eg:list[4]={id:”00004”,name: “Do”}

通过vue官方提供的改变数组的方式,页面才会自动改变。(三种方法)

1.push、pop、shift、unshift、splice、sort、reverse

2.改变list的引用(js)。即重新修改list的内容。eg: list=[….]

列表循环(对象方式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<body>
<div id="root">
<div v-for = "(item, key, index) of userInfo"
>{{item}}-{{key}}--{{index}}</div>
</div>
<script>
var vm = new Vue({
el: "#root",
data: {
userInfo: {
name: "Dell",
age: 28,
gender: "male",
salary: "secret"
}
}

})

</script>
</body>

输出为:

1
2
3
4
Dell-name--0
28-age--1
male-gender--2
secret-salary--3

同样的,如果直接在对象添加一项,页面是不会改变的。

改变list的引用(js)。即重新修改list的内容。eg: list={….}

set方法

对象:通过set方法向对象里边增加值,就会刷新页面。

eg:

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

**数组**:通过set方法向数组里边增加值,就会刷新页面。

eg:```Vue.set(vm.userInfo,1,5)```或者```vm.$set(vm.userInfo,1,5)```//将下标为1的数据改为5

### Todolist-组件拆分

```html
<body>
<div id="root">
<div>
<input v-model="inputValue"/>
<button @click="handleclick">提交</button>
</div>
<ul>
<li v-for="(item,index) of list" :key="index">{{item}}</li>
</ul>
</div>
<script>
new Vue({
el: "#root",
data: {
inputValue: "",
list: []
},
methods: {
handleclick: function(){
this.list.push(this.inputValue)
this.inputValue= ""
}
}
})
</script>
</body>

全局组件

由于list的内容可以无限的大,因此为了便于维护,将ul标签单独拆分下来。

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="root">
<div>
<input v-model="inputValue"/>
<button @click="handleclick">提交</button>
</div>
<ul>
<todo-item v-for="(item,index) of list" :key="index"></todo-item>
</ul>
</div>
<script>
<!-- component来定义全局组件-->
Vue.component('todo-item', {
template: '<li>item</li>'
})
<!-- component定义全局组件结束-->
new Vue({
el: "#root",
data: {
inputValue: "",
list: []
},
methods: {
handleclick: function(){
this.list.push(this.inputValue)
this.inputValue= ""
}
}
})
</script>
</body>

标签的字母全小写且必须包含一个连字符可以避免和当前以及未来的 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
32
33
34
35
36
<body>
<div id="root">
<div>
<input v-model="inputValue"/>
<button @click="handleclick">提交</button>
</div>
<ul>
<todo-item></todo-item>
</ul>
</div>
<script>
<!-- 局部组件的定义-->
var TodoItem = {
template: '<li>item</li>'
}
<!-- 局部组件的定义-->
new Vue({
el: "#root",
<!-- 将标签和局部组件对应-->
components: {
'todo-item': TodoItem
},
<!-- 将标签和局部组件对应-->
data: {
inputValue: "",
list: []
},
methods: {
handleclick: function(){
this.list.push(this.inputValue)
this.inputValue= ""
}
}
})
</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="root">
<div>
<input v-model="inputValue"/>
<button @click="handleclick">提交</button>
</div>
<ul>
<todoitem
v-for="(item,index) of list"
:key="index"
:content="item"
>
</todoitem>
</ul>
</div>
<script>
Vue.component('todoitem', {
props: ['content'],
template: '<li>{{content}}</li>'
}),
new Vue({
el: "#root",
data: {
inputValue: "",
list: []
},
methods: {
handleclick: function(){
this.list.push(this.inputValue)
this.inputValue= ""
}
}
})
</script>
</body>

props数组装的是多个属性的名称,比如我要把index也当成参数传递给组件,:index=”index”,这个时候就应该写props: [‘content’, ‘index’]

整个流程就是先创建出来todoitem,然后标签item的值返回给content,通过content将模板赋值,template通用显示

每一个组建都是一个vue实例,每一个vue实例也是一个组建

父组件向子组件传值
  • 子组件在props中创建一个属性,用以接收父组件传过来的值
  • 父组件中注册子组件
  • 在子组件标签中添加子组件props中创建的属性
  • 把需要传给子组件的值赋给该属性

注意:子组件需要修改这个值时,最好在自己的data中复制一份修改,这样不会影响父组件的值。

TodoList删除功能-this.$emit

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
-this<body>
<div id="root">
<div>
<input v-model="inputValue"/>
<button @click="handleclick">提交</button>
</div>
<ul>
<todo-item
v-for="(item,index) of list"
:key="index"
:content="item"
:index="index"
@delete="handledelete"
>
</todo-item>
</ul>
</div>
<script>
Vue.component('todoitem', {
props: ['content','index'],
template: '<li @click="handleclick">{{content}}</li>',
methods: {
handleclick: function(){
this.$emit('delete',this.index)
}
}
}),
new Vue({
el: "#root",
data: {
inputValue: "",
list: []
},
methods: {
handleclick: function(){
this.list.push(this.inputValue)
this.inputValue= ""
},
handledelete: function(index){
this.list.splice(index,1)
}
}
})
</script>
</body>

只要list变化,页面中的标签机就会变化。

父组件为todo-item,子组件为li

设置子组件被点击,响应handleclick方法。向外触发事件delete并传值index,而父组件监听delete这个事件,因此出发handledelete方法。在父组件中删除数据list数组下标为index的元素即可完成对子组件的删除。

父组通过属性控制子组件,子组件通过方法反馈父组件。

Vue实例

设计模式

MVP设计模式:(eg:jQuery)

model 数据层,Presenter 控制层,View 视图层

MVV设计模式:

model 数据层,ViewModel Vue实现,View 视图层

只需要关注M层的变化,V层会通过Vue自动改变

ps

app.$data 或者app.$app的$符号表示Vue实例所拥有的属性或者方法,以便与用户定义的属性区分开来。

vm.$data === data // => true
vm.$el === document.getElementById(‘app’) // => true

生命周期

生命周期函数就是vue实例在某个时间点会自动执行的函数

image

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
<script>
var vm = new Vue({
el: '#app',
beforeCreate: function (){
console.log('beforeCreate');
},
created: function (){
console.log('Create');
},
beforeMount: function(){
console.log('beforeMount');
},
mounted: function(){
console.log('mounted');
},
beforeDestroy: function(){
console.log('beforeDestroy');
},
destroyed: function(){
console.log('destroyed');
},
beforeUpdate: function(){
console.log('beforeUpdate');
},
updated: function(){
console.log('updated');
},
})
</script>

生命周期函数不需要放在methods

Ps:不要在选项属性或者回调上使用箭头函数,eg:created: () => console.log(this.a)

set和get

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
<body>
<div id="root">
<input v-model="firstname" type="text">
<input v-model="lastname" type="text">
<div>{{fullname}}</div>
</div>
<script>
var vm = new Vue({
el: "#root",
data: {
firstname: '',
lastname: '',
},
computed: {
fullname: {
get: function(){
return this.firstname + ' ' + this.lastname
},
set: function(value){
console.log('1');
var arr = value.split(" ");
this.firstname = arr[0];
this.lastname = arr[1];
}
}
}
})
</script>
</body>

set 设置值时触发 eg:控制台调试

get 得到值时触发 eg:值被其他函数调用

样式绑定

实现点击hello变红色,再点击一次变为黑色

calss的对象绑定
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
    <style>
.activated{
color: red;
}
</style>
<body>
<div id="root">
<div
@click="handleDivClick"
:class="{activated: isActivated}"
>hello</div>
</div>
<script>
var vm = new Vue({
el: "#root",
data: {
isActivated: false
},
methods: {
handleDivClick: function(){
this.isActivated = !this.isActivated;
}
}
})
</script>
</body>

当isActivated为true时,

: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

##### class的数组绑定

```html
<style>
.activated{
color: red;
}
</style>
<body>
<div id="root">
<div
@click="handleDivClick"
:class="[Activated,Activatedone]"
>hello</div>
</div>
<script>
var vm = new Vue({
el: "#root",
data: {
isActivated: "",
Activatedone: "act-one" //=>class = "act-one"
},
methods: {
handleDivClick: function(){
this.isActivated = this.isActivated === "activated" ? "" : "activated"
}
}
})
</script>
</body>

当Activated和Activatedone的值不为空时,

: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

##### style的对象样式

```html
<body>
<div id="root">
<div
@click= "handleonclick"
:style="styleObj"
>hello</div>
</div>
<script>
var vm = new Vue({
el: "#root",
data: {
styleObj: {
color: "black"
}
},
methods: {
handleonclick: function(){
this.styleObj.color = this.styleObj.color === "black" ? "red" : "black"
}
}
})
</script>

注意:这里:style=”styleObj” 等号右边使用引号。显示为:

style
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

##### style的数组样式

```html
<body>
<div id="root">
<div
@click= "handleonclick"
:style="[styleObj,{fontSize: '20px'}]"
>hello</div>
</div>
<script>
var vm = new Vue({
el: "#root",
data: {
styleObj: {
color: "black"
}
},
methods: {
handleonclick: function(){
this.styleObj.color = this.styleObj.color === "black" ? "red" : "black"
}
}
})
</script>
</body>

显示为:

style
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

#### 模板占位符

用template标签可以省去div的占用,即在页面上不会显示。但是可以传递数据给子div

#### 组建参数校验与非Props特性



### 组件使用的细节

#### is属性

```html
<body>
<div id="root">
<table>
<tbody>
<tr><td>row</td></tr>
<tr><td>row</td></tr>
<tr><td>row</td></tr>
</tbody>
</table>
</div>
</body>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<div id="root">
<table>
<tbody>
<row></row>
<row></row>
<row></row>
</tbody>
</table>
</div>
<script>
Vue.component('row',{
template: '<tr><td>this is a row</td></tr>'
})
var vm = new Vue({
el: "#root",
})
</script>
</body>

理论上,这两个在浏览器的显示应该一致。但是在浏览器上显示为

image

即,table标签偏置到了tr标签的下边。原因是 tbody标签内部只能放tr标签而产生的错误(H5编码规范)。

则应使用下列方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<body>
<div id="root">
<table>
<tbody>
<tr is="row"></tr>
<tr is="row"></tr>
<tr is="row"></tr>
</tbody>
</table>
</div>
<script>
Vue.component('row',{
template: '<tr><td>this is a row</td></tr>'
})
var vm = new Vue({
el: "#root",
})
</script>
</body>

同样的,在某些浏览器ul标签下必须使用li标签;select标签内必须用option标签

利用is属性解决模板标签的问题

子组件的data只能是一个对象

正常情况,上个例子中的代码也可以通过下列方式去实现,即用data中的content代替模板中的数值。

1
2
3
4
5
6
7
8
9
10
11
<script>
Vue.component('row',{
data:{
content: 'this is a row'
}
template: '<tr><td>{{content}}</td></tr>'
})
var vm = new Vue({
el: "#root",
})
</script>

但是,实际结果并不显示。

这是因为子组件的data必须是个对象,而且需要通过函数返回。即:

1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
Vue.component('row',{
data: function() {
return {
content: 'this is a row'
}
},
template: '<tr><td>{{content}}</td></tr>'
})
var vm = new Vue({
el: "#root",
})
</script>

$ref的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<body>
<div id="root">
<div ref='hello' @click="handleclick">
hello world
</div>
</div>
<script>
var vm = new Vue({
el: "#root",
methods: {
handleclick: function() {
alert(this.$refs.hello.innerHTML)
}
}
})
</script>
</body>

由上可知当在div标签时,ref获取的内容是标签的dom元素。

当在组件时,ref获取的是counter子组件(当对于大的div而言)的引用。eg:

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="root">
<counter ref="one" @comp= "change"></counter>
<counter ref="two" @comp= "change"></counter>
<div>{{total}}</div>
</div>
<script>
Vue.component('counter', {
template: '<div @click= "handleclick">{{number}}</div>',
data: function() {
return {
number: 0
}
},
methods: {
handleclick: function() {
this.number ++;
this.$emit('comp')
}
}
})
var vm = new Vue({
el: "#root",
data: {
total: 0
},
methods: {
change: function() {
this.total = this.$refs.one.number + this.$refs.two.number
}
}

})
</script>
</body>
0%