🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] >[success] # Bus **Vuex** 适合 **大型复杂项目** 中使用,进行 **状态管理**,我们的项目如果不是很复杂可以使用 **Bus** 来满足需求。 >[success] ## 父子组件传值 **父传子**:*通过在使用子组件的标签上定义自定义属性* 。 **子传父** : *在子组件中使用this.$emit传给父组件,然后在父组件中通过自定义事件接收子组件传过来的值* 。 **vue** 的 **父子组件传值** 是通过 **单项数据流** ,[官方不推荐在子组件中修改父组件传过来的props的值](https://cn.vuejs.org/v2/guide/components-props.html?#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81),**避免这个参数多个子组件引用,无法找到造成数据不正常的原因** 。 1. **子组件** 写法: **AInput.vue** ~~~ <template> <input @input="handleInput" :value="value"> </template> <script> export default { name: 'AInput', props: { value: { type: [String, Number], default: '' } }, methods: { handleInput(event){ const value = event.target.value // 获取input的值 this.$emit('input', value) // 把value传给父组件 } } } </script> ~~~ 2. **父组件** 写法 **store.vue** ~~~ <template> <div> <!-- 习惯性使用这种方式 x-xxxxx --> <a-input v-model="inputValue"/> <p>{{ inputValue }}</p> </div> </template> <script> import AInput from '_c/AInput' // _c对应的是src/components文件夹,是vue.config.js中配置的 export default { name: 'store', data(){ return{ inputValue: '' } }, components: { // AInput: AInput, // 啰嗦方式 AInput // 简写方式 } } </script> ~~~ 上面代码 **父组件** 中的 **v-model** 实际上是一个 **语法糖** ,跟下面这种写法一样: ~~~ <template> <div> <a-input :value="inputValue" @input="handleInput"/> <p>{{ inputValue }}</p> </div> </template> <script> import AInput from '_c/AInput' export default { name: 'store', data(){ return{ inputValue: '' } }, components: { AInput }, methods:{ handleInput(val){ this.inputValue = val } } } </script> ~~~ >[success] ## 兄弟组件传值 接下来是 **子组件1** 给 **子组件2** 传值,这两个组件是 **兄弟级组件(同级组件)** 。 1. **父组件** 写法: **store.vue** ~~~ <template> <div> <!-- 子组件1 --> <a-input v-model="inputValue"/> <!-- 子组件2 --> <a-show :content="inputValue"/> </div> </template> <script> import AInput from '_c/AInput' import AShow from '_c/AShow' export default { name: 'store', data(){ return{ inputValue: '' } }, components: { AInput, AShow } } </script> ~~~ 或者这样写: ~~~ <template> <div> <!-- 子组件1 --> <a-input @input="handleInput"/> <!-- 子组件2 --> <a-show :content="inputValue"/> </div> </template> <script> import AInput from '_c/AInput' import AShow from '_c/AShow' export default { name: 'store', data(){ return{ inputValue: '' } }, components: { AInput, AShow }, methods:{ handleInput(val){ this.inputValue = val } } } </script> ~~~ 2. **子组件1** 写法: **AInput.vue** ~~~ <template> <input @input="handleInput" :value="value"> </template> <script> export default { name: 'AInput', props: { value: { type: [String, Number], default: '' } }, methods: { handleInput(event){ const value = event.target.value // 获取input的值 this.$emit('input', value) // 把value传给父组件 } } } </script> ~~~ 3. **子组件2** 写法: **AShow.vue** ~~~ <template> <div> <p> AShow:{{ content }} </p> </div> </template> <script> export default { name: 'AShow', props: { content: { type: [String, Number], default: '' } } } </script> ~~~ >[success] ## Vue Bus方式传值(跨文件的组件传值) 下面用到的[$on、$emit、$once、$off使用方法](https://juejin.im/post/6844904062417109005) 1. 首先创建在 **src目录** 下的 **lib文件夹** 并且在其中创建 **bus.js** **src/lib/bus.js** ~~~ import Vue from 'vue' const Bus = new Vue() export default Bus ~~~ 2. 在 **main.js** 中 **引入全局Bus** ,其实这种方式有点跟在 **window**上挂载 **方法或变量** 有点相似。 **main.js** ~~~ import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store' import './plugins/element.js' import Bus from './lib/bus' // 引入Bus Vue.config.productionTip = false Vue.prototype.$bus = Bus // 挂载Bus到Vue原型链(全局挂载Bus) new Vue({ router, store, render: h => h(App) }).$mount('#app') ~~~ 3. 在 **email.vue** 中 **set** 一个值 **email.vue** ~~~ <template> <div> <button @click="handleClick">点击我跳转tel页面</button> </div> </template> <script> export default { methods: { handleClick() { this.$router.push({ path: "/tel" }); } }, beforeDestroy(){ // 离开页面时 this.$bus.$emit('on-click', '把我传过去') // 使用bus Set值 } } </script> ~~~ 4. 在 **tel.vue** 中 **get** 这个值,并且把值显示到页面上 **tel.vue** ~~~ <template> <div>我是tel页面</div> </template> <script> export default{ created(){ this.$bus.$on('on-click', mes => { // 监听on-click事件get值 console.log(mes) // 把我传过去 }) }, beforeDestroy(){ // 页面离开时 this.$bus.$off('on-click') // 销毁监听事件(监听事件不会自动销毁),如果不销毁,事件会多次执行 } } </script> ~~~ 5. **原理** : **$emit方法** 会 **触发当前实例上的一些事件** , **$on** 给 **指定事件** 绑定一个 **事件监听** ,同样也可以在自己的页面监听自定义事件,如下: **store.vue** ~~~ <template> <div> <button @click="handleClick">按我</button> </div> </template> <script> export default { methods: { handleClick(){ this.$emit('on-click', '111') // 自定义事件 } }, mounted(){ this.$on('on-click', mes => { // 监听自定义事件 console.log(mes) // 111 }) } } </script> ~~~ 6. **Bus** 必须要在 **beforeDestroy** 中执行 **Set** 操作。如果在 **点击方法** 中 **set** 值后跳转到 **get** 值的页面,**get** 值的页面 **未渲染完成** 就会 **监听不到Bus** ,[解决方案](https://segmentfault.com/a/1190000021053767) 。 >[warning] ## 后期补充(该项后期删除) 1. **v-model** 的 **语法糖** ,如果子组件传给父组件多个值怎么办