vue笔记6
15.组件传值
-
父组件向子组件传值:props
- 父组件可以在调用子组件时,在标签上绑定属性,子组件在自身的props中注册该属性之后,就能将该属性作为数据使用。
- 注意点:属性的用法和data中的数据用法一样,在子组件中只能使用该属性的值,不能进行修改(即使在子组件中修改了该属性的值,重新渲染时,依旧会被父组件传入的属性值重新覆盖),该属性名不要和data中的属性重名,否则会被data 中的属性覆盖。
//父组件 <template> <div id="app"> <Box :title1="msg"></Box> <Box :title1="msg2"></Box> <Box3 :title="b3" :title2="b4" title3="父组件传的值" :title4="n"></Box3> <SinaBox :data="arr[0]"></SinaBox> </div> </template> <script> import Box from "./Box.vue" import Box2 from "./Box2.vue" import Box3 from "./Box3.vue" export default { data(){ return { arr:[{text:"xxx",created:"123123"}], msg:"父组件的数据", msg2:"父组件的数据2", b3:"100",//"hello", b4:100,//"hello", n:4//3//"hello66666" } }, components:{ Box, Box2, Box3 } } </script> //子组件 <template> <div> <h1>{{title}}--{{title2}}--{{title3}}--{{title4}}</h1> </div> </template> <script> export default { //属性的类型验证 props:{ title:String, title2:[String,Number], title3:{ type:String, default:"默认属性的值" }, title4:{ validator:(v)=>{ return isNaN(v)?false:v%2==0 } } } } </script>
-
props有简洁和详细两种写法:
-
props:[“msg1”,“msg2”];
-
props: { propA: Number, // 基础的类型检查 (`null` 匹配任何类型) propB: [String, Number], // 多个可能的类型 propC: { type: String, required: true // 使用该子组件时propC为必传的属性,且类型为String }, propD: { type: Number, default: 100 // 带有默认值的数字 }, propE: { type: Object, // 带有默认值的对象 default: function () { // 对象或数组默认值必须从一个工厂函数获取 return { message: 'hello' } } } }
-
-
子组件向父组件传值:
$emit
-
在子组件中的生命周期或者方法中通过
this.$emit("父组件的事件名fn1",传入父组件的参数)
调用父组件中绑定的事件,传入参数,达到子组件向父组件传值的目的 -
父组件中定义对应的方法:
@父组件的事件名fn1="父组件自身的方法"
,父组件定义的方法中就可以接收到子组件传来的值//子组件 <template> <div> <h1>box----{{n}}</h1> <button @click="change1">box---修改</button> </div> </template> <script> export default { props:["n"], methods:{ change1(){ this.$emit("changebaba",this.n+1) } } } </script> //父组件 <template> <div id="app"> <h1>app---{{count}}</h1> <Box @changebaba="fn" v-on:xx="eventmyself" :n="count"></Box> //changebaba是子组件的自定义方法,会通过this.$emit()调用 //fn是定义的父组件自身的方法 <button @click="change1">app--修改count</button> </div> </template> <script> import Box from "./Box.vue" export default { data() { return { count: 20 } }, methods:{ change1(){ this.count+=1 }, fn(arg){ this.count=arg } }, components: { Box } } </script>
-
-
在父子组件上使用v-model双向绑定:
- 在父组件中使用
v-model="msg"
,绑定传入子组件的数据 - 在子组件中注册value属性(只能是value属性),然后可以直接使用该属性
- 本质上也是一个语法糖
//父组件 <template> <div id="app"> <h1>app---{{count}}</h1> <Box v-model="count"></Box> </div> </template> <script> import Box from './Box.vue' export default { data(){ return { count:20 } }, components: { Box }, methods:{ input1(v){ console.log(v); this.count=v; }, }, } </script> //子组件 <template> <div > <h1>box---{{value}}</h1> <button type="button" @click='fn'>修改父组件</button> </div> </template> <script> export default { props:['value'], methods:{ fn(){ this.$emit('input',this.value+1) } } } </script>
- 在父组件中使用
-
子组件添加原生事件:修饰符:
.native
- 在父组件中使用子组件时,在原生方法上加上该修饰符,可以将事件绑定到子组件的根标签上(只能用于原生事件,click等)
<template> <div> <Box @click.native="fn"></Box> </div> </template> <script> import Box from "./Box.vue" export default { methods:{ fn(){ console.log(66666) } }, components:{ Box } } </script>
-
通过
.sync
实现父子组件的双向绑定- 在父组件调用子组件时,在绑定的属性后加上修饰符
.sync
- 在子组件中使用
this.$emit("update:属性名",传递的值)
来实现子组件向父组件传值
//子组件 <template> <div> <h1>box----{{n}}</h1> <button @click="fn">儿子修改爸爸</button> </div> </template> <script> export default { props:["n"], methods:{ fn(){ this.$emit("update:n",this.n+1)//n为子组件中注册的属性名 } } } </script> //父组件 <template> <div> <h1>app----{{count}}</h1> <Box :n.sync="count"></Box> </div> </template> <script> import Box from "./Box.vue" export default { data() { return { count: 20 } }, components:{ Box } } </script>
- 在父组件调用子组件时,在绑定的属性后加上修饰符
-
多层组件传值:
$listeners/$attrs
-
$listeners
:事件传递 -
$attrs
:属性传递 -
在中间层绑定
v-on="$listeners"
和v-bind="$attrs"
可以将上一层组件的数据和事件往下一层传递,也可以将下一层的this.$emit()
往上一层传递//第一层 App层 <template> <div> <h1>app----{{n}}</h1> <Two @appchange1="change1" :count="n"></Two> </div> </template> <script> import Two from "./Two.vue" export default { data() { return { n: 20 } }, components:{ Two }, methods:{ change1(v){ this.n=v } } } </script> //第二层 中间层 <template> <div> <Three v-bind="$attrs" v-on="$listeners"></Three> </div> </template> <script> import Three from "./Three.vue" export default { components:{ Three }, } </script> //第三层 孙子层 <template> <div> <h1>three----{{count}}</h1> <button @click="change1">three修改数据</button> </div> </template> <script> export default { props:["count"], methods:{ change1(){ this.$emit("appchange1",this.count+1) } } } </script>
-
-
使用
$parent
/$root
/$children
/$refs
实现传值这些功能都有一定的缺陷,官方建议尽量避免使用
-
$root
//$root:能够访问根组件vm对象,所有的实例都可以将这个实例对象作为一个全局变量来访问和使用 <template> <div> <h1>three-----{{$root.name1}}</h1> <button @click="change2">three-给根组件的name1设置数据</button> </div> </template> <script> export default { methods:{ change2(){ this.$root.name1="three修改了跟组件的name1数据" } } } </script>
-
$parent
//$parent:访问父组件对象,可以直接操作到父组件的data数据和方法,但是容易造成数据混乱 //根组件的父组件是undefined //子组件 <template> <div> <p>two---{{msg}}</p> <button @click="getf">two--</button> </div> </template> <script> export default { props:["msg"], data() { return { sonmsg: "儿子的数据" } }, methods:{ getf(){ this.$parent.msg="two直接访问到app组件了然后直接改了数据" } } } </script> //父组件 <template> <div> <h1>app---{{msg}}</h1> <Two :msg="msg"></Two> <Two msg="9999"></Two> <button @click="look">lookSon</button> </div> </template> <script> import Two from "./Two.vue" export default { data() { return { msg:"666666" } }, methods:{ look(){ console.log(this.$children) } }, components:{ Two }, } </script>
-
$children
//$children:能够访问子组件对象数组,但是不能保证顺序,也不是响应式的。其返回的值是当前组件的所有直接子组件的数组。 <template> <div> <Two msg="9999"></Two> <button @click="look">lookSon</button> </div> </template> <script> import Two from "./Two.vue" export default { data() { return { msg:"666666" } }, methods:{ look(){ console.log(this.$children) } }, components:{ Two }, } </script>
-
$refs
$refs:只会在组件渲染完成之后生效,并且它们不是响应式的。你应该避免在模板或计算属性中访问 $refs。 //在组件或者原生元素绑定ref属性(类似于id): <myinput ref="myInput1"></myinput> <input ref="myInput2"></input> //在父组件中可以通过 this.$refs访问到它: methods: { focus: function () { this.$refs.myInput2.focus() } }
-
-
vue依赖注入 - provide/inject
-
vue在2.2.0之后,提供了provide/inject来实现多层组件传值
//爷爷 <template> <div> <p>{{ title }}</p> <son></son> </div> </template> <script> import Son from "./son" export default { name: 'Father', components: { Son }, // provide选项提供变量 provide: { message: 'provided by father' }, data () { return { title: '父组件' } }, methods: { ... } } </script> //爸爸 <template> <div> <p>{{ title }}</p> <grand-son></grand-son> </div> </template> <script> import grandSon from "./grandSon " export default { name: "Son", components: { grandSon }, data () { return { title: '子组件' } }, }; </script> //孙子 <template> <div> <p>message:{{ message }}</p> </div> </template> <script> export default { name: "GrandSon", inject: [ "message" ], data () { return { title: '孙组件' } }, methods: { ... } }; </script>
-
-
中央事件总线:
-
创建一个新的vm对象,统一注册所有事件,将其放在Vue的原型上,使所有组件都可以调用访问。
-
vue提供的绑定事件的方式:
this.$on
,移除事件的方式:this.$off
//vue-bus.js文件 单独创建一个vm对象,创建on/off方法 import Vue from "vue" let bus=new Vue({ methods:{ on(eventname,cb){ this.$on(eventname,cb) }, emit(eventname){ this.$emit(eventname,...arguments) }, off(eventname){ this.$off(eventname) } } }) export default bus; ################ Vue.prototype.$bus=Bus;//由于这个新的vm放在与界面绑定的那个vm的原型上,因此页面上的所有组件都能通过this.$bus访问这个新vm对象 //main.js文件 //import VueBus from './vue-bus' //Vue.use(VueBus); import Vue from 'vue' import App from './App.vue' import $bus from "./bus.js" Vue.prototype.$bus=$bus //绑定到原型上 // Vue.use($bus) // new Vue({ // methods:{ // on(eventname,cb){ // this.$on(eventname,cb) // }, // emit(eventname){ // this.$emit(eventname,...arguments) // }, // off(eventname){ // this.$off(eventname) // } // } // }) var vm=new Vue({ render: h => h(App), }).$mount('#app') //组件文件中: 任意业务中都可以通过调用来绑定事件,触发事件并传值,和销毁事件 this.$bus.on(event,callback) this.$bus.off(event,callback) this.$bus.emit(event, ...args) 示例: 组件1: this.$bus.on('changedFormObject',(val) =>{ //接受并处理传过来的值:val this.msg = val; }); 组件2: this.$bus.emit('changedFormObject',this.inputValue);//把组件2的data中的给inputValue值传给组件1
-
面试题
1.什么是单向数据流?
答:指的是父组件通过属性传值给子组件传入的数据是data中的变量,如果子组件修改了属性值,是不会修改data容器中的变量的,因此,数据在整个工程中,由父级组件通过props向子级组件流动,并且当父组件中的数据发生改变时,子组件会自动接收到改变后的数据,并且更新页面的内容。