三、其它技巧
3.1.1 适时使用provide和inject
provide 和 inject 主要为高阶插件/组件库提供用例,并不推荐直接用于应用程序代码中;并且这对选项需要一起使用以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,在上下游关系成立的时间里始终生效。
- provide:
Object | () => Object
- inject:
Array | { [key: string]: string | Symbol | Object }
//父组件: provide: { // provide 提供一个属性和方法 foo: '这是 foo', fooMethod: () => { console.log('父组件 fooMethod 被调用') } }, // 子或者孙子组件 inject: ['foo', 'fooMethod'], //数组或者对象,注入到子组件 mounted() { this.fooMethod() console.log(this.foo) } //在父组件下面所有的子组件都可以利用inject
provide 和 inject 绑定并不是可响应的。这是官方刻意为之的。
然而,如果你传入了一个可监听的对象,那么其对象的属性还是可响应的,因为对象是引用类型
//父组件: provide: { foo: '这是 foo' }, mounted(){ this.foo = '这是新的 foo' } // 子或者孙子组件 inject: ['foo'], mounted() { console.log(this.foo) //子组件打印的还是'这是 foo' } //父组件: provide() { return { foo: this.foo, nameList: this.nameList, personalInfo: this.personalInfo } }, data() { return { foo: '这是foo', nameList: ['张三', '李四'], personalInfo: { age: 18 } } }, mounted(){ this.foo = '这是新的 foo' this.nameList.push('王五') // 改变堆内存中的值 // this.nameList = ['王五', '赵六'] // 放开后子组件仍打印 ['张三', '李四', '王五'], this.personalInfo.age = 16 // 改变堆内存中的值 this.personalInfo = {age: 12} // 将当前对象的指针指向新的堆内存地址 } // 子或者孙子组件 <template> <div>{{foo}}</div> <div>{{nameList}}</div> <div>{{personalInfo && personalInfo.age}}</div> </template> <script> export default{ inject: ['foo', 'nameList', 'personalInfo'], mounted() { setTimeout(() => { console.log(this.foo) // 这是foo -视图不会更新 console.log(this.nameList) // ['张三', '李四', '王五'] -视图会更新 console.log(this.personalInfo.age) // 16 foo -视图会更新 }, 1000) } } </script>
3.1.2 利用key值解决vue就地复用策略的问题
已知:throttle是封装过的节流指令,在规定时间仅第一次执行,后续点击不再执行,目前设置的时间是2秒,即每2秒内的重复点击只执行第一次。
// 节流指令 Vue.directive('throttle', { inserted: (el, binding) => { const throttleTime = binding.value || 2000 // 节流时间 el.addEventListener('click', event => { if (el.nodeName === 'BUTTON' || el.type === 'button') { if (!el.disabled) { el.disabled = true setTimeout(() => { el.disabled = false }, throttleTime) } } else { // 合理使用样式穿透使元素本身及其子元素鼠标事件失效 el.style.pointerEvents = 'none' el.style.color = 'red' el.setAttribute('data-flag', '0') // 只是为了打印 setTimeout(() => { if (el && el.style) { el.style.pointerEvents = 'auto' el.style.color = '#000' el.setAttribute('data-flag', '1') // 只是为了打印 } }, throttleTime) } }, false) } })
通过以下代码可以发现点击完接受工作票按钮后不到2秒的时间内由于vue的就地复用策略导致工作许可按钮复用了原来的元素,继承了之前的样式和自定义属性,从而导致工作许可按钮在上述2s内的点击不会生效,且颜色也发生了短暂的改变。(利用项目加浏览器调试来演示说明)
<template> <view> <!-- 待接收 --> <button v-if="stateNo == '10'" class="btn-test" @click="ticketReceive" v-throttle>接收工作票</button> <!-- 待许可 --> <button v-if="stateNo == '12'" class="btn-test" @click="permitTicket" v-throttle>工作许可</button> </view> </template> <script> export default{ data() { return { stateNo: '10' } }, methods: { // 接收工作票 ticketReceive($event) { console.log('$event', $event) this.stateNo = '12' this.$nextTick(() => { console.log('视图已完成更新', '工作许可按钮已显示') setTimeout(() => { let dom = document.querySelector('.btn-test') console.log('data-flag', dom.getAttribute('data-flag')) // 0 }, 1000) setTimeout(() => { let dom = document.querySelector('.btn-test') console.log('data-flag', dom.getAttribute('data-flag')) // 1 }, 2600) }) }, permitTicket() { console.log('点击了工作许可') }, } } </script>
解决办法:给两个使用v-if的标签添加不同的key值,如:
<template> <view> <!-- 待接受 --> <button v-if="stateNo == '10'" class="btn-test" @click="ticketReceive" v-throttle key="1">接受工作票</button> <!-- 待许可 --> <button v-if="stateNo == '12'" class="btn-test" @click="permitTicket" v-throttle key="2">工作许可</button> </view> </template>
‘0’) // 只是为了打印
setTimeout(() => {
if (el && el.style) {
el.style.pointerEvents = ‘auto’
el.style.color = ‘#000’
el.setAttribute(‘data-flag’, ‘1’) // 只是为了打印
}
}, throttleTime)
}
}, false)
}
})
通过以下代码可以发现点击完接受工作票按钮后不到2秒的时间内由于vue的就地复用策略导致工作许可按钮复用了原来的元素,继承了之前的样式和自定义属性,从而导致工作许可按钮在上述2s内的点击不会生效,且颜色也发生了短暂的改变。(利用项目加浏览器调试来演示说明) ```jsx <template> <view> <!-- 待接收 --> <button v-if="stateNo == '10'" class="btn-test" @click="ticketReceive" v-throttle>接收工作票</button> <!-- 待许可 --> <button v-if="stateNo == '12'" class="btn-test" @click="permitTicket" v-throttle>工作许可</button> </view> </template> <script> export default{ data() { return { stateNo: '10' } }, methods: { // 接收工作票 ticketReceive($event) { console.log('$event', $event) this.stateNo = '12' this.$nextTick(() => { console.log('视图已完成更新', '工作许可按钮已显示') setTimeout(() => { let dom = document.querySelector('.btn-test') console.log('data-flag', dom.getAttribute('data-flag')) // 0 }, 1000) setTimeout(() => { let dom = document.querySelector('.btn-test') console.log('data-flag', dom.getAttribute('data-flag')) // 1 }, 2600) }) }, permitTicket() { console.log('点击了工作许可') }, } } </script>
解决办法:给两个使用v-if的标签添加不同的key值,如:
<template> <view> <!-- 待接受 --> <button v-if="stateNo == '10'" class="btn-test" @click="ticketReceive" v-throttle key="1">接受工作票</button> <!-- 待许可 --> <button v-if="stateNo == '12'" class="btn-test" @click="permitTicket" v-throttle key="2">工作许可</button> </view> </template>