1. 項目實現介紹
vue
項目搭建參考《Webpack4 搭建 Vue 項目》
文檔使用 vuepress
, 官方文檔 https://vuepress.vuejs.org
發佈文檔 github pages
+ gh-page
項目地址 https://github.com/zxpsuper/vui-vue<img src="https://img.shields.io/github...;>
文檔地址 https://zxpsuper.github.io/vui-vue
組件地址 https://zxpsuper.github.io/vui-vue/components/throttle.html
處於自我摸索階段,期待留下您的寶貴意見!
2. Throttle 組件的實現
- 首先,寫一個防抖節流的通用函數
/**
* @param {function} func 執行函數
* @param {number} time 防抖節流時間
* @param {boolean} isDebounce 是否爲防抖組件
* @param {this} ctx this 的指向
*/
const debounce = (func, time, isDebounce, ctx) => {
var timer, lastCall, rtn;
if (isDebounce) {
rtn = (...params) => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
func.apply(ctx, params);
}, time);
};
} else {
rtn = (...params) => {
const now = new Date().getTime();
if (now - lastCall < time && lastCall) return;
lastCall = now;
func.apply(ctx, params);
};
}
return rtn;
};
- 使用抽象組件
export default {
name: 'Throttle',
abstract: true,
props: {
time: {
type: Number,
default: 800,
},
events: {
type: String,
default: 'click',
},
isDebounce: {
type: Boolean,
default: false,
},
},
created() {
this.eventKeys = this.events.split(','); // 分隔事件
this.originMap = {}; // 儲存事件,用於重新render時與子事件的對比
this.debouncedMap = {}; // 儲存防抖節流事件
},
render() {
const vnode = this.$slots.default[0];
this.eventKeys.forEach(key => {
const target = vnode.data.on[key];
if (target === this.originMap[key] && this.debouncedMap[key]) {
vnode.data.on[key] = this.debouncedMap[key];
} else if (target) {
this.originMap[key] = target;
this.debouncedMap[key] = debounce(
target,
this.time,
this.isDebounce,
vnode
);
vnode.data.on[key] = this.debouncedMap[key]; // 重寫子組件的事件
}
});
return vnode;
},
};
3. 使用組件
需全局或者組件內註冊一下,這裏只展示全局註冊代碼:
Vue.component("Throttle", Throttle);
使用方法:
<Throttle :time="5000" isDebounce>
<span @click="decounceFunction">
<Button color="purple" light>防抖按鈕</Button>
</span>
</Throttle>
4. 存在問題與解決方法
當頁面元素在防抖節流時間內發生了更新(渲染)(可以用定時器修改頁面,如頁面倒計時),那麼此組件會重新執行一遍
this.debouncedMap[key] = debounce(target,this.time,this.isDebounce,vnode);
導致防抖節流失效,目前的解決方法是在此組件的子元素添加 v-once
,如下:
<Throttle :time="5000" isDebounce>
<span @click="decounceFunction" v-once>
<Button color="purple" light>防抖按鈕</Button>
</span>
</Throttle>
可解決此問題,有更好的方法請在評論區留言
5. 完整代碼
/*
* @descript: 防抖節流組件,前提是頁面在等待時間內無其他渲染方可使用,重新渲染導致 debounce() 函數不斷重置
* @Author: super
* @Date: 2019-04-09 14:21:18
* @Last Modified by: super
* @Last Modified time: 2019-10-24 16:37:43
*/
const debounce = (func, time, isDebounce, ctx) => {
var timer, lastCall, rtn;
if (isDebounce) {
rtn = (...params) => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
func.apply(ctx, params);
}, time);
};
} else {
rtn = (...params) => {
const now = new Date().getTime();
if (now - lastCall < time && lastCall) return;
lastCall = now;
func.apply(ctx, params);
};
}
return rtn;
};
export default {
name: 'Throttle',
abstract: true,
props: {
time: {
type: Number,
default: 800,
},
events: {
type: String,
default: 'click',
},
isDebounce: {
type: Boolean,
default: false,
},
},
created() {
this.eventKeys = this.events.split(',');
this.originMap = {};
this.debouncedMap = {};
},
render() {
const vnode = this.$slots.default[0];
this.eventKeys.forEach(key => {
const target = vnode.data.on[key];
if (target === this.originMap[key] && this.debouncedMap[key]) {
vnode.data.on[key] = this.debouncedMap[key];
} else if (target) {
this.originMap[key] = target;
this.debouncedMap[key] = debounce(
target,
this.time,
this.isDebounce,
vnode
);
vnode.data.on[key] = this.debouncedMap[key];
}
});
return vnode;
},
};
總結
本文是對render及抽象組件的使用總結,若有錯誤,望指出共同進步。
更多推薦
Canvas 進階(二)寫一個生成帶logo的二維碼npm插件
Canvas 進階(三)ts + canvas 重寫”辨色“小遊戲
<h1>最後祝大家1024節日快樂哈哈😍</h1>