目录
1. 属性传值
1.1 语法
1.2 属性和数据源同名
2. 反向传值
2.1 属性绑定+自定义事件
简单案例:
购物车算总价案例:
2.2 v-model 组件的双向数据绑定
3. 透传(多层组件传值)
3.1 类型透传
3.2 属性穿透 v-bind="$attrs"
3.3 方法穿透 v-on="$listeners"
4. 获取组件
5. 依赖provide注入indect
父组件中,其中msg是父组件的数据
// 静态传值 直接将静态数据hello传过去
// 动态传值 msg为app父组件的数据源
子组件中,当数据源中没有同名的数据时,可以通过this来访问这个属性
box1组件-----{{ title }}
//script标签中——注册
props:["title"],
methods: {
fn() {
console.log(this.title);
}
},
结果:
当属性和数据源中的data的数据同名时:vue2.x劫持的顺序不同 所有有优先级;vue3.x data中的同名数据不做代理(不生效)
子组件的模板中显示的是属性传值过来的内容,访问时,得到的是子组件本身数据源中的数据,并且会报警告。
子组件不能通过this直接修改这个同名变量(vue2.x可以直接修改同名变量的值,且子组件模板会刷新,但父组件数据不变;vue3.x不可以,会报错,title为只读)
原因: 单向数据流:数据只能由父级组件流向子组件(父级组件的数据源更新,会重新刷新模板,重新传新的属性,两个模板都会刷新)。反过来,数据不能由子级组件流向父级组件(会造成死循环),否则程序混乱且难以管理
注:vue2.0 一个template标签只能有一个根元素(简单说就是只能有一个div标签,其余的都在这个div中写);vue3.0解决了这个问题,但还是使用2.0的习惯吧
补充知识:函数声明在a作用域,在b作用域调用,那它就在声明作用域中运行
像上面可以知道,子组件不能修改父组件传过来的值,但在实际开发中还是需要通过子组件来修改父组件中的数据,那这个时候就需要通过反向传值,将子组件的值传给父组件。
子级组件想要修改父级组件的数据,需要绑定事件,将子级组件修改的数据通过事件的实参传给父级组件,然后再把父级组件的数据改为子级组件传过来的数据,从而刷新页面。
语法:
父组件:监听事件
Box子组件内部:触发事件 this.$emit("myevent","传参数")
//父组件
//方法,监听自定义事件myevent
methods: {
box1_change_msg(arg) {
this.msg = arg;
}
}
//box1子组件
box1组件-----{{ title }}
//方法:自定义事件myevent 触发事件 将要修改的数据传过去
methods: {
change_app_data() {
this.$emit("myevent","box1向app反向传过去的数据")
}
}
前面文章中有用html实现购物车的功能,现在将这个功能用Vue框架来实现,体现了反向传值(子组件的按钮点击了,不能直接修改父组件中的count,需要利用属性绑定和自定义事件来实现数据的反向传值)
父组件(App):
总价:{{ total }}元
子组件(Goodsbox):
商品名:{{ goods.title }}---
价格:{{ goods.price }}---
数量:
{{ goods.count }}
语法:
父组件:
vue2.0 v-model是语法糖: :value="msg" @input="(arg)=>{this.msg=arg}"
vue3.0 v-model:value是语法糖 :value="msg" @update:value=(arg)=>{ msg = arg}
子组件:注册属性,方法中使用this.$emit("update:"属性名","要传的数据")
简单案例:
// 父组件
//父组件数据源
data() {
return {
msg: "app组件中的数据",
key: "app中的key数据",
}
}
//子组件Box2
box2组件-----{{ value }}----{{ keywords }}
//注册属性
props: ["value","keywords"],
//方法 传值
methods: {
v_model_passValue() {
this.$emit("update:value", "box2传给app的value数据");
this.$emit("update:keywords","box2传给app的keywords数据")
}
}
语法:
父组件中设置了类名,子组件渲染时会自动加上父组件设置的类名;子组件也有类名时,会合并两个类名。
父级组件绑定属性 子组件中给孙组件绑定穿透属性:v-bind="$attrs" 事件透传 同理:v-on="$listeners"
Box3将属性传给下一级组件(Box4)时,自己Box3是不能注册该属性的,才能传给下一子级。
//父组件 app中 通过属性传值
//子组件 box3
box3组件----{{ count }}
//要传给自己,就不注册;不传,就注册来自己用
// props:["count"],
//孙组件 box4
box4组件----{{ count }}
//注册的属性名应该为父组件的属性名
props:["count"],
如果即要自己用,又想传给自己的子组件时,使用$attrs属性透传。$attrs中保存了父组件传给一级子组件的所有属性值,后代组件可以通过this.$attrs来访问。一级子组件Box3不注册,要透传属性给谁,就在这个标签上面添加v-bind:$attrs,但不能注册。使用时:this.$attrs.属性名
//父组件 app中 通过属性传值
//子组件 box3
box3组件----{{ this.$attrs.count }}
//孙组件 box4
box4组件----{{ this.$attrs.count }}
// 属性透传给box5
// 曾孙组件 box5
box5组件----{{ count }}
//注册的属性名应该为父组件的属性名
props:["count"],
vue2.x版本中:父组件app绑定的事件,想要通过孙组件Box4来触发该事件时,需要给子组件Box3添加v-on="$listeners",将事件穿透过去,然后在Box4中来触发该事件。
vue3.x版本中,直接通过v-bind="$attrs"就可以实现事件的穿透,$listenners弃用。
$parent/$root/$options——方便,但尽量不要用,会引起不必要的麻烦,不好解决
$refs(获取组件中节点或者组件)
box4组件----{{ count }}
box4中的p1标签
methods: {
box4_btn() {
// 上级元素
console.log(this.$parent);
// 根级元素
console.log(this.$root);
// 子级元素
console.log(this.$options.components);
// 获取组件中节点或者组件
console.log(this.$refs.p1);
}
}
父级组件提供数据,组件链上的子级组件注入并使用。
简单案例:
// 父组件 app
provide: {
msgGlobal: 'provided by app',
numGlobal: 999
}
// 子组件们 box2 box3均相同
// 使用
global---{{ msgGlobal }}
numGlobal----{{ numGlobal }}
// 注入
inject: ["msgGlobal", "numGlobal"],
虽然看起来很好用,但官方还是不建议使用,因为害怕使用者管理不好,容易出错。
1. 属性传值
父组件的数据源更新了就会让子组件接收新的属性值
父组件给子组件传的数据,可以通过子组件再传给它自己的子组件
2. 反向传值(子组件给父组件传值)重点
因为父组件属性传值给子组件后,子组件不能直接修改属性值(框架设计时单项数据流:数据只能由父级组件流给子级组件)。父组件监听事件(绑定函数),子组件触发事件(调用函数传数据)
语法:
Box内部:this.$emit("myevent","传参数")
组件的双向数据绑定:
Box内部:this.$emit("update:title","参数")
3 .透传(多层组件传值)
Box内部可以直接传给下一级组件
4. 获取组件 $parent $root $options.components
5. 提供provide注入inject
父级组件提供数据,子级组件注入并使用
6. 中央事件总线、仓库——兄弟组件间的传值