没想到吧,我过年也在整理笔记。大家新年快乐,万事如心意!!!
1.非父子组件的通信
主要两种方式:
- Provide/Inject
- Mitt全局事件总线
1.1.Provide和Inject
一些深度嵌套的组件,下层组件要获得上层组件的一些信息
在这种情况下,如果我们仍然将props沿着组件链逐级传递下 去,就会非常的麻烦
对于这种情况下,我们可以使用 Provide 和 Inject
- 上层组件有一个 provide 选项来给所有下层组件提供数据
- 下层组件有一个 inject 选项来开始使用这些数据
- 上层下层互相都不需要知道我给谁提供数据,我用了谁的数据
我们基于这个结构演示使用
上面如果我们如果Provide中提供的一些数据是来自data,我们通过this来获取会报错哦
因为this指向会出问题,我们可以通过Provide函数返回对应数据给解决这个this指向之后数据又会丧失响应式
所以最终代码是:
1.2.全局事件总线mitt库
Vue3从实例中移除了 off 和 $once 方法
所以我们如果希望继续使用全局事件总线,要通过第三方的库
Vue3官方有推荐一些库,例如
mitt或tiny-emitter我们来说mitt的使用
安装:
npm install mitt
我们可以封装一个工具eventbus.js
使用事件总线通信
取消mitt事件监听
2.插槽
让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===> 子组件
插槽也是为了让组件有更强的灵活和通用性,比如一个页面的导航都是左中右的布局结构,但是每个页面又会有所细小的区别,这时候我认为就需要考虑插槽了
分类:默认插槽、具名插槽、作用域插槽
2.1.插槽基本使用
有一个组件MySlotCpn.vue,该组件中有一个插槽,我们可以在插槽中放入需要显示的内容
在使用到该组件地方,我们可以插入普通的内容、html元素、组件元素,这些元素写在组件的内部,最终会替换掉子组件的slot
2.2.默认插槽
有时候我们在使用需要插槽的组件时没有传入对应的结构
我们可以默认配置一个没有插入内容时显示的内容
多个插槽效果
如果我们想对应不同内容插入不同插槽,可能就得具名插槽了
2.3.具名插槽
一个不带 name 的slot,会带有隐含的名字 default
插槽取名使用name属性,插入对应的内容外层template加一个v-slot:对应的name名
v-slot:的缩写是#,所以也可简写成#name名,例如#left
2.4.动态插槽名
之前使用的插槽名都是固定的,比如#left,#center等
我们可以通过 v-slot:[dynamicSlotName]方式动态绑定一个名称,简写可以为#[dynamicSlotName]
2.5.渲染作用域
父级模板里的所有内容都是在父级作用域中编译的
子模板里的所有内容都是在子作用域中编译的
2.6.作用域插槽
我们希望插槽可以访问到子组件中的内容
我的理解是子向父传递数据,插槽来传递数据给使用的组件决定如何插入展示
1.我们在App.vue定义数据,通过props传递给ShowNames组件
2.ShowNames组件中遍历names数据,定义插槽的prop
3.通过v-slot:default或v-slot的方式获取到slot的props,使用slotProps中的item和index
所以作用域插槽首先得我们传递一个数据过去,然后组件内通过插槽prop再回传给父组件,最后由父组件拿到数据插入结构后再放到对应的<slot>当中
如果只有默认插槽,我们就可以将 v-slot直接用在组件上
如果出现多个插槽,要为插槽使用完整的template写法
3.Vue2的全局事件总线和插槽
3.1.全局事件总线(GlobalEventBus)
-
一种组件间通信的方式,适用于任意组件间通信。
-
安装全局事件总线:
new Vue({ ...... beforeCreate() { Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm }, ...... }) -
使用事件总线:
-
接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。
methods(){ demo(data){......} } ...... mounted() { this.$bus.$on('xxxx',this.demo) } -
提供数据:
this.$bus.$emit('xxxx',数据)
-
-
最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
3.2.消息订阅与发布(pubsub)
-
一种组件间通信的方式,适用于任意组件间通信。
-
使用步骤:
-
安装pubsub:
npm i pubsub-js -
引入:
import pubsub from 'pubsub-js' -
接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
methods(){ demo(data){......} } ...... mounted() { this.pid = pubsub.subscribe('xxx', this.demo) //订阅消息 } -
提供数据:
pubsub.publish('xxx',数据) -
最好在beforeDestroy钩子中,用
PubSub.unsubscribe(pid)去取消订阅。
-
3.3.插槽
使用方式:
-
默认插槽:
父组件中: <Category> <div>html结构1</div> </Category> 子组件中: <template> <div> <!-- 定义插槽 --> <slot>插槽默认内容...</slot> </div> </template> -
具名插槽:
父组件中: <Category> <template slot="center"> <div>html结构1</div> </template> <template v-slot:footer> <div>html结构2</div> </template> </Category> 子组件中: <template> <div> <!-- 定义插槽 --> <slot name="center">插槽默认内容...</slot> <slot name="footer">插槽默认内容...</slot> </div> </template> -
作用域插槽:
-
理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)
-
具体编码:
父组件中: <Category> <template scope="scopeData"> <!-- 生成的是ul列表 --> <ul> <li v-for="g in scopeData.games" :key="g">{{g}}</li> </ul> </template> </Category> <Category> <template slot-scope="scopeData"> <!-- 生成的是h4标题 --> <h4 v-for="g in scopeData.games" :key="g">{{g}}</h4> </template> </Category> 子组件中: <template> <div> <slot :games="games"></slot> </div> </template> <script> export default { name:'Category', props:['title'], //数据在子组件自身 data() { return { games:['红色警戒','穿越火线','劲舞团','超级玛丽'] } }, } </script>
-