利用vue脚手架:vue_cli,通过复数个.App单文件组件的组合形成界面
单文件组件结构
分为template
、script
、style
三个组成部分。
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() { 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中插槽的使用方法