​ 利用vue脚手架:vue_cli,通过复数个.App单文件组件的组合形成界面


单文件组件结构

​ 分为templatescriptstyle三个组成部分。

template :组件的html代码

script :支持组件运转的脚本script代码(javascript、Typescript)

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
28
<template>
<div class="demo">
<h2>学校名称:{{ schoolName }}</h2>
<h2>学校地址:{{ address }}</h2>
<h2>{{updatedMsg}}</h2>
</div>
</template>

<script>
//引入混合mixin
import {mixin} from '../mixin'
export default {
name: "SchoolVue",//定义了组件的名字
data() {
return {
schoolName: '河南大学',
address: '河南开封'
}
},
mixins:[mixin]
}
</script>

<style scoped>
.demo {
background-color: skyblue;
}
</style>

​ 要想在其他组件中使用该组件,须在components中写上组件名称,就能以同名自定义标签的形式在template中使用了。


组件间通信

父组件向子组件传值

​ 如下例所示,TodoList是MyFooter的父组件,想将定义的checkedNum变量值传给MyFooter。在template相应的子组件标签中写明要传递的参数即可。

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
<template>
<div class="todo-container">
<div class="todo-warp">
<MyFooter :checkedNum="checkedNum"></MyFooter>
</div>
</div>
</template>

<script>
import MyFooter from "./MyFooter.vue";
export default {
name: "TodoList",
data() {
return {
checkedNum: 1,
todos: [
{ id: 3, title: "吃饭", done: true ,isEdit:false},
{ id: 2, title: "睡觉", done: false ,isEdit:false},
{ id: 1, title: "打游戏", done: false ,isEdit:false},
],
todosNum: 3,
};
},
components: {
MyFooter,
}
};
</script>
<style>
</style>

​ 而在MyFooter中,只需用props方法引入对应的变量即可:

1
props:['checkedNum','allCheck','allMissCheck','todosNum','deleteChecked']
1
<span>{{this.checkedNum}}</span>

子组件向父组件传值

​ 父组件向子组件传递一个methods方法中的函数,子组件通过调用该函数修改父组件数据来达到传值的效果。如下所示,父组件TodoList向子组件Myheader传递一个receive函数:

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
<template>
<div class="todo-container">
<div class="todo-warp">
<MyHeader :receive="receive"></MyHeader>
</div>
</div>
</template>

<script>
import MyHeader from "./MyHeader.vue";
export default {
name: "TodoList",
data() {
return {
todos: [
{ id: 3, title: "吃饭", done: true ,isEdit:false},
{ id: 2, title: "睡觉", done: false ,isEdit:false},
{ id: 1, title: "打游戏", done: false ,isEdit:false},
],
todosNum: 3,
};
},
components: {
MyHeader,
},
methods: {
receive: function (x) {
x.id = this.todos.length + 1;
this.todos.unshift(x);
this.todosNum++;
}
};
</script>

​ 子组件MyHeader接收receive,并在addTodo方法中调用:

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
 <template>
<div class='todo-header'>
<input type="text" placeholder="请输入计划,按enter键确认" v-model="title" v-on:keyup.enter="addTodo" />
</div>
</template>

<script>
export default {
name:'MyHeader',
props:["receive"],
data(){
return {
title:''
}
},
methods: {
addTodo() {
if(!this.title.trim())return alert("输入不能为空")
const todoObj={id:0,title:this.title,done:false,isEdit:false};
this.receive(todoObj)
this.title='';
}
},
}
</script>

全局事件总线

全局事件总线功能比较强大,不局限于父传子或兄弟组件之间通信,它可以跨组件通信,通过事件总线传递的值,不管哪个组件都可以获取到。

main.js中加上如下代码:

1
2
3
beforeCreate() {
Vue.prototype.$bus = this//安装全局事件总线
}

​ 在信息发送端的组件中使用如下代码:

1
2
3
4
5
methods:{
sendMsg(){
this.$bus.$emit("msgName",msgData)
}
}

​ 在信息接收端的组件中使用如下代码:

1
2
3
4
5
6
7
8
9
mounted() {
//给$bus绑定hello事件
this.$bus.$on("msgName", (data) => {
console.log(data);//可以自定义
})
},
beforeDestroy() {
this.$bus.off("msgName")
}

​ 如果$on绑定的函数为临时自定义的非methods,则接收的值作为该函数的参数并执行;

​ 如果绑定的是已有的methods方法,则接收的值作为该方法的参数,并进行调用。


$nextTick

​ vue中的nextTick主要用于处理数据动态变化后,DOM还未及时更新的问题,用nextTick就可以获取数据更新后最新DOM的变化。

适用场景

第一种

​ 有时需要根据数据动态地为页面某些dom元素添加事件,这就要求在dom元素渲染完毕时去设置,但是created与mounted函数执行时一般dom并没有渲染完毕,所以就会出现获取不到,添加不了事件的问题,这回就要用到nextTick处理。

第二种

​ 在使用某个第三方插件时 ,希望在vue生成的某些dom动态发生变化时重新应用该插件,也会用到该方法,这时候就需要在 $nextTick 的回调函数中执行重新应用插件的方法,例如:应用滚动插件better-scroll时。

第三种

​ 数据改变后获取焦点时需要用到$nextTick,否则无法获取焦点

1
2
3
4
5
6
7
8
handleEdit(todo){
if(todo.hasOwnProperty.call("isEdit")){
todo.isEdit=true
}else{
this.$set(todo,"isEdit",true)
}
this.$nextTick(function(){this.$refs.inputTitle.focus()})
},

插槽

默认插槽

​ 父组件中:

1
2
3
<Category>
<div>html结构1</div>
</Category>

​ 子组件中:

1
2
3
4
5
6
<template>
<div>
<!-- 定义插槽 -->
<slot>插槽默认内容...</slot>
</div>
</template>

具名插槽

​ 父组件中:

1
2
3
4
5
6
7
8
<Category>
<template slot="center">
<div>html结构1</div>
</template>
<template v-slot:footer>
<div>html结构2</div>
</template>
</Category>

​ 子组件中:

1
2
3
4
5
6
7
<template>
<div>
<!-- 定义插槽 -->
<slot name="center">插槽默认内容...</slot>
<slot name="footer">插槽默认内容...</slot>
</div>
</template>

作用域插槽

​ 数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)

​ 父组件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<Category>
<template scope="scopeData">
<!-- 生成的是ul列表 -->
<ul>
<li v-for="g in scopeData.games" :key="g">{{g}}</li>
</ul>
</template>
</Category>

<Category>
<template slot-scope="scopeData">
<!-- 生成的是h4标题 -->
<h4 v-for="g in scopeData.games" :key="g">{{g}}</h4>
</template>
</Category>

​ 子组件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<div>
<slot :games="games"></slot>
</div>
</template>

<script>
export default {
name:'Category',
props:['title'],
//数据在子组件自身
data() {
return {
games:['红色警戒','穿越火线','劲舞团','超级玛丽']
}
},
}
</script>

附录

引用文章:

$nextTick的作用和使用场景 Vue中插槽的使用方法