总结:在vue中的传值形式

日常搬砖,项目有值传递是很普遍的行为, 我罗列了vue中涉及到值传递的使用场景,总结下在vue中的必使用的传值形式。

组件传值

父子、子父组件传值

  • props
// 父组件
<i-a msg="燕国使者荆轲向秦王献图了"></i-a>
// 子组件
props: ['msg']
复制代码
  • on/emit
// 父组件
<!--自定义组件使用on监听派发出的事件-->
<i-a @on="gx"></i-a>
// 子组件
this.$emit('gx') // 使用emit派发自定义事件: gx
复制代码
  • $parent/$children
this.$parent 得到父组件实例
this.$children 得到子组件实例,可能有多个,且不保证顺序
复制代码
  • ref
<template>
    <div id="app">
        <!--自定义组件-->
        <i-a ref="ia" />
        <!--普通标签-->
        <p ref="p1">111</p>
    </div>
</template>
<script>
export default {
    methods: {
        rename() {
            this.$refs.ia // 获取到的是自定义组件实例,可以通过它调用子组件上的方法或是数据。
            this.$refs.p1 // 获取的就是p标签,普通dom节点
        },
    },
};
</script>
复制代码

子孙后代组件传值

  • provoid/inject 兄弟两需要一起使用。
  • provoid 对象 | 返回一个对象的函数。
  • inject 字符串数组 | 对象。
案例1

先决条件:有A、B、C、D 4个组件,A嵌套B,B嵌套C,C嵌套D。在App.vue中引入A组件。

// App.vue
<template>
    <div id="app">
        <i-a></i-a>
    </div>
</template>
<script>
import A from "./components/A";
export default {
    name: "App",
    components: {
        "i-a": A,
    },
    provide: {
        name: "大秦",
    }
};
</script>
// A.vue B、C、D组件同理
<template>
    <div class="hello">
        A: {{ name }}
        <div>
            <i-b></i-b>
        </div>
    </div>
</template>
<script>
import B from "./B";
export default {
    name: "A",
    inject: ["name"],
    components: {
        "i-b": B,
    },
};
</script>
复制代码

image.png name的结果就被所有后待继承了。

案例2

你应该注意到了,provide中的name是写死的字符,正常情况下,这个值多数使用data中获取的。想要访问到this,provide就必须是个函数,return 出一个对象。

// App.vue
provide() {
    return {
        name: this.name
    }
},
data: function () {
    return {
        name: "大秦帝国",
    };
},
复制代码

image.png

案例3

既然是后代,对于app来说,a、b、c、d都是后代,对于a来说,b、c、d都是后代...,那么在A组件中使用provide,其后台是否也能获取嘞?答案是:裤裆着火啦-裆燃(当然)。

// A.vue
provide: {
    wuchenghou: "武成侯-王翦",
},
// B.vue \ C.vue \ D.vue
inject: ["name", "wuchenghou"],
复制代码

image.png

案例4

如果共同的父级都有同样的值,那我应该听谁的?答案是:谁离你近就听谁的!

// App.vue
provide: {
    shoudu: "咸阳",
}
// A.vue
provide: {
    wuchenghou: "武成侯-王翦",
    shoudu: "长安",
},
// ABCD组件
inject: ["name", "wuchenghou", "shoudu"]
<!--在页面使用-->
<p>首都: {{ shoudu }}</p>
复制代码

image.png

案例5

前面提到,inject处了支持字符串数组外,还支持对象。作为对象时,key就是本地的绑定名,value如果是。

  • 在可用的注入内容中搜索用的 key(字符串或 Symbol),或
  • 一个对象,该对象的:
    • from property 是在可用的注入内容中搜索用的 key (字符串或 Symbol)
    • default property 是降级情况下使用的 value

把B组件的inject改写下:

扫描二维码关注公众号,回复: 13299224 查看本文章
inject: {
    name: "name",
    wuchenghou: {
        from: "wuchenghou",
        default: "--",
    },
    shoudu: "shoudu",
},
复制代码
案例6

你有没有想过,provide在使用异步数据的时候还好不好使?假设我们需要在特定的情况下修改provide中的值,会不会与预期一样?

// App.vue
<template>
    <button @click="rename">秦王政</button>
    {{ this.name }}
    <hr />
</template>
// 添加一个事件
provide() {
    return {
        name: this.name,
        shoudu: "咸阳",
    };
},
methods: {
    rename() {
        this.name = "大秦帝国: 始皇帝嬴政";
    },
},
复制代码

image.png

然后点击"秦王政"按钮

image.png

app.vue 中的name已经发生变化了,但是他的后代门却没有改变。 provide 和 inject 绑定并不是可响应的。如果传入了一个可监听的对象,那么其对象的 property 还是可响应的。 就是说,如果你希望后代可以响应到异步得到的值,那么provide中与key对应的value值必须是个引用类型。

// App.vue
provide() {
    return {
        name: this.name,
        shoudu: "咸阳",
        qin: this.qin,
    };
},
data: function () {
    return {
        name: "大秦帝国",
        qin: {
            ge: "秦王政",
        },
    };
},
methods: {
    rename() {
        this.name = "大秦帝国: 始皇帝嬴政";
        this.qin.ge = "始皇帝嬴政";
    },
},
// A.vue
inject: ["name", "shoudu", "qin"]
<!--使用-->
<p>异步数据: {{ qin.ge }}</p>
复制代码

image.png

这时候传递下去的异步数据就是:秦王政。点击秦王政按钮,动态更改为:始皇帝嬴政。

image.png

查看演示效果

同级/兄弟组件传值

  • vuex
  • observable
  • EventBus

同级/兄弟组件,它们没有直接的隶属关系,对于这类的组件传值,通常会使用vuex、EventBus、observable。

这里主要搞下EventBus,也叫事件总线或者中央事件总线。说到底,无非就是利用Vue本身自带的 $on、$emit进行。

先决条件:存在两个同级组件A、B,有一个共同的跟组件(通常是$root或是共同的父级组件)。

// App.vue
<template>
    <div id="app">
        <i-a />
        <i-b />
    </div>
</template>
<script>
import A from "./components/A";
import B from "./components/B";
export default {
    name: "App",
    components: {
        "i-a": A,
        "i-b": B,
    },
};
</script>
复制代码

在main.js中搞一个新的Vue实例,目的就是确保所有组件都可以访问到它。

// 新的实例挂载到了Vue的原型上, 组件中既可以使用this.$bus访问到
Vue.prototype.$bus = new Vue()
// A.vue
created() {
    // 开始自定义事件的监听
    this.$bus.$on("amsg", (msg) => {
        this.msg = msg;
    });
},
methods: {
    setMsg() {
        // 也可以提交一个自定义的事件
        this.$bus.$emit("bmsg", "A-> 汉王:刘邦是也");
    },
},
// B.vue
created() {
    // 开始自定义事件的监听
    this.$bus.$on("bmsg", (msg) => {
        this.msg = msg;
    });
},
methods: {
    setMsg() {
        // 也可以提交一个自定义的事件
        this.$bus.$emit("amsg", "B-> 西楚霸王:项羽是也");
    },
},
复制代码

点击对应的按钮,就可以把自己的信息传递到其他组件。 image.png 其实,这个方法也不是仅仅局限与同级/兄弟组件之间,只要保证任意两个组件之间存在一个共同的父级组件或者跟组件,就可以。

需要注意的是:销毁组件时记得移除$on绑定的事件,避免造成重复监听。

beforeDestroy() {
    this.$bus.$off("bmsg")
    // this.$bus.$off() 如果不指定移除的事件名称,表示移除所有绑定的事件。
}
复制代码

查看演示效果

路由传值

操作url实现的

  • location
  • ue-route: query
  • vue-route: params
  • 命名路由
// location
location.href = 'https://三达不溜.不坑你坑谁.com/?msg=霸王过江了吗'

// vue-route: query
this.$routes.push({name: 'xx', query: {msg: '霸王过江了吗'}})

// vue-route: params
this.$routes.push({name: 'xx', params: {msg: '霸王过江了吗'}})

// 命名路由,可以使用路由的params导向。
{
  path: '/bw/:name',
  name: 'Bw'
}
this.$routes.push({name: 'Bw', params: {name: '楚霸王'}})
this.$routes.push({name: 'Bw', params: {name: '项羽'}})
复制代码

前端存储实现的

  • localStorage
  • sessionStorage
  • cookie
  • indexedDB

窗口传值

这玩意比较特殊,vue基本都是单页面应用,极少有同时打开两个窗口,还需要传值处理的情况。但是,这玩意要是嵌入到app中使用,就另说了。

犹豫一些特殊的使用场景,嵌入app的路由跳转一律拦截,统一使用location跳转。没打开一个页面,就是一个新的实例被创建,vuex这些内部的东西统统无效。

  • localStorage 通过监听storage来更新值 (ios端webview的问题导致事件不能触发放弃)
// A组件
mounted() {
    window.addEventListener('storage', this.setValFun, false)
}
methods: {
    setValFun(e) {
        if (e.storageArea?.val === '1') {
             //...
             localStorage.removeItem('val')
        }
    }
}
// B组件
localStorage.setIetm('val', '1')
复制代码
  • 轮询函数查询存储的值
// A组件
mounted() {
    this.setValFun()
}
methods: {
    setValFun() {
        if (getCookie('val') === '1') {
             //...
             delCookie('val')
        }
        setTimeout(() => {
          this.setValFun()
        }, 1000)
    }
}
// B组件
setCookie('val', '1', '1d')
复制代码

番外

极简封装observable

// store/index.js
const store = {
  state: Vue.observable({
    name: ''
  }),
  commit: {
    setName(n) {
        store.state.name = n
    },
  }
}
export default {
  install: function() {
    Vue.prototype.$store = store
  }
}

// main.js
import store from './store'
Vue.use(store)
复制代码

猜你喜欢

转载自juejin.im/post/7033318688979681317