一、props $emit 父子组件之间传值
缺点:如果组件嵌套层次多的话,数据传递比较繁琐
1)父组件向子组件传值 props
<body>
<div id="app">
<compa :propa='username' :propb='true' :propc="[1,2,3]" :propd='{name:"dny"}' :prope='abc' :propf='["apple","banana"]' proph='默认父组件传递'></compa>
</div>
<script>
const compa={
props:{
propa:String,
propb:{
type:Boolean,
required:true//必传
},
propc:{
type:Array
},
propd:{
type:Object,
required:true
},
prope:{
type:Function,
required:true
},
propf:{
validator:function(value){
if(value.indexOf('banana')>-1){
return true
}else{
return false
}
},
required:true
},
//设置默认值 如果父组件没有传 就走默认值
proph:{
default(){
return '设置默认值'
}
}
},
template:`<h1>{{proph}}</h1>`,
}
var vm=new Vue({
el:"#app",
data:{
username:'dny'
},
components:{
compa
},
methods:{
abc(){
}
}
})
</script>
</body>
2)子组件向父组件传值 —$emit
<body>
<div id="app">
{{countF}}
<gp-18 @counterchanger='handlerChange'></gp-18>
</div>
<script type="text/template" id="gp-18Temp">
<div>
<h1>gp-counter</h1>
<button @click='decrement(1)'>-</button>
{{count}}
<button @click='increment(1)'>+</button>
</div>
</script>
<script>
//全局组件
Vue.component('gp-18',{
template:'#gp-18Temp',
data(){
return{
count:0
}
},
methods:{
increment(num){
this.count++
this.$emit('counterchanger',this.count)
},
decrement(num){
this.count--
this.$emit('counterchanger',this.count)
}
}
})
var vm=new Vue({
el:'#app',
data:{
countF:0
},
methods:{
//子组件的值将作为第一个参数
handlerChange(num){
this.countF=num
}
}
})
</script>
</body>
二、边界 this.$root — this.$parent— this.$refs
缺点:$refs 只会在组件渲染完成之后生效,并且它们不是响应式的。这仅作为一个用于直接操作子组件的“逃生舱”——你应该避免在模板或计算属性中访问 $refs。
- 在每个 new Vue 实例的子组件中,其根实例可以通过 $root 属性进行访问
- 和 parent 属性可以用来从一个子组件访问父组件的实例
- 尽管存在 prop 和事件,有的时候你仍可能需要在 JavaScript 里直接访问一个子组件。为了达到这个目的,可以通过 ref 这个 attribute 为子组件赋予一个 ID 引用,如(< compb ref=‘refcomb’ >< /compb >),然后通过this.$refs.refcomb来访问这个子组件实例
<body>
<div id="app">
<input type="text" ref='myinput'>
<compa ></compa>
</div>
<script>
const compb={
data(){
return{
title:"孙子组件的数据"
}
},
template:'<h2>compb-{{$root.username}}</h2>',
mounted(){
//获取根组件的内容
console.log(this.$root.username)
//获取父组件的内容
console.log(this.$parent.title)
console.log(this.$parent.start())
}
}
const compa={
props:['user'],
data(){
return{
title:'父组件的数据'
}
},
template:`
<h1>
compa
<compb ref='refcomb'></compb>
</h1>`,
components:{
compb
},
methods:{
start(){
console.log('父组件的方法');
}
},
mounted(){
console.log(this.$refs.refcomb.title);
}
}
var vm=new Vue({
el:"#app",
data:{
username:'dny'
},
components:{
compa
},
mounted(){
console.log(this.$refs.myinput);
}
})
</script>
</body>
三、provide inject (依赖注入)
缺点:依赖注入将应用程序中的组件与它们当前的组织方式耦合起来,使重构变得更加困难。同时所提供的属性是非响应式的
<body>
<div id="app">
<input type="text" ref='myinput'>
<compa></compa>
</div>
<!-- 数据不是响应式的 -->
<script>
const compb = {
inject: ['username'],
template: '<h2>compb-{{username}}</h2>',
mounted(){
console.log(this.foo()) // dny
}
}
const compa = {
template: `
<h1>
compa
<compb ></compb>
</h1>`,
components: {
compb
},
}
var vm = new Vue({
el: "#app",
data: {
username: 'dny'
},
//返回内容在子组件和孙子组件都可以访问到
provide: function () {
return {
username: this.username,
foo:()=>{
return this.username
}
}
},
})
</script>
</body>
四、event-bus 事件总线
不支持响应式
<body>
<div id="app">
<compa></compa>
</div>
<script>
var eventbus=new Vue()
const compb = {
template: '<h2>compb-</h2>',
mounted(){
eventbus.$on('message',function(msg){
console.log(msg);
})
}
}
const compa = {
template: `
<h1>
compa
<compb ></compb>
</h1>`,
components: {
compb
},
}
var vm = new Vue({
el: "#app",
data: {
username: 'dny'
},
components: {
compa
},
methods:{
handleback(){
eventbus.$emit('message','hello world')
}
}
})
</script>
</body>
五、slot插槽
六、.sync修饰符
在有些情况下,我们可能需要对一个 prop 进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以变更父组件,且在父组件和子组件都没有明显的变更来源。这也是为什么我们推荐以 update:myPropName 的模式触发事件取而代之
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../vue.js"></script>
</head>
<body>
<div id="app">
<parent-component></parent-component>
</div>
<script>
let ChildComponent={
props:['title'],
template:`
<h1>-Child------</h1>
`,
mounted(){
this.$emit('update:title',"def")
}
}
let ParentComponent={
template:`
<div>
<h1>parent-component-----{{title}}</h1>
<child-component v-bind:title.sync='title'></child-component>
</div>
`,
data(){
return{
title:"abc"
}
},
components:{
ChildComponent
}
}
var vm= new Vue({
el:"#app",
components:{
ParentComponent
}
})
</script>
</body>
</html>
七、vuex
支持响应式,但是数据的读取和修改需要按照流程来操作,不适合小型项目
<body>
<div id="app">
<counter-btn type='decrement'></counter-btn>
<counter-span></counter-span>
<counter-btn type='increment'></counter-btn>
</div>
<script>
Vue.component('counter-btn',{
props:['type'],
template:`<button @click='handleClick'>{{btntext}}</button>`,
computed:{
btntext(){
return this.type==='decrement'?'-':'+'
}
},
methods:{
handleClick(){
if(this.type==='increment'){
// this.$store.state.count++
//调用mutations的方法
// this.$store.commit('increment',{num:3})
store.dispatch('add',{num:3})
}else{
// this.$store.state.count--
this.$store.commit('decrement',{num:2})
}
}
}
})
Vue.component('counter-span',{
template:`<span>{{$store.state.count}}</span>`
})
var store=new Vuex.Store({
state:{
count:0
},
//同步数据修改
mutations:{
increment(state,obj){
state.count+=obj.num
},
decrement(state,obj){
state.count-=obj.num
}
},
//异步数据修改
actions:{
add(context,obj){
setTimeout(()=>{
context.commit('increment',obj)
},3000)
}
}
})
var em=new Vue({
el:"#app",
store
})
</script>
</body>