Vue系列功能轮子
输入框防抖轮子
// 1.设置v-throttle自定义指令
Vue.directive('throttle', {
bind: (el, binding) => {
let throttleTime = binding.value; // 防抖时间
if (!throttleTime) { // 用户若不设置防抖时间,则默认2s
throttleTime = 2000;
}
let cbFun;
el.addEventListener('click', event => {
if (!cbFun) { // 第一次执行
cbFun = setTimeout(() => {
cbFun = null;
}, throttleTime);
} else {
event && event.stopImmediatePropagation();
}
}, true);
},
});
// 2.为button标签设置v-throttle自定义指令
<button @click="sayHello" v-throttle>提交</button>
图片懒加载轮子
const LazyLoad = {
// install方法
install(Vue,options){
// 代替图片的loading图
let defaultSrc = options.default;
Vue.directive('lazy',{
bind(el,binding){
LazyLoad.init(el,binding.value,defaultSrc);
},
inserted(el){
// 兼容处理
if('IntersectionObserver' in window){
LazyLoad.observe(el);
}else{
LazyLoad.listenerScroll(el);
}
},
})
},
// 初始化
init(el,val,def){
// data-src 储存真实src
el.setAttribute('data-src',val);
// 设置src为loading图
el.setAttribute('src',def);
},
// 利用IntersectionObserver监听el
observe(el){
let io = new IntersectionObserver(entries => {
let realSrc = el.dataset.src;
if(entries[0].isIntersecting){
if(realSrc){
el.src = realSrc;
el.removeAttribute('data-src');
}
}
});
io.observe(el);
},
// 监听scroll事件
listenerScroll(el){
let handler = LazyLoad.throttle(LazyLoad.load,300);
LazyLoad.load(el);
window.addEventListener('scroll',() => {
handler(el);
});
},
// 加载真实图片
load(el){
let windowHeight = document.documentElement.clientHeight
let elTop = el.getBoundingClientRect().top;
let elBtm = el.getBoundingClientRect().bottom;
let realSrc = el.dataset.src;
if(elTop - windowHeight<0&&elBtm > 0){
if(realSrc){
el.src = realSrc;
el.removeAttribute('data-src');
}
}
},
// 节流
throttle(fn,delay){
let timer;
let prevTime;
return function(...args){
let currTime = Date.now();
let context = this;
if(!prevTime) prevTime = currTime;
clearTimeout(timer);
if(currTime - prevTime > delay){
prevTime = currTime;
fn.apply(context,args);
clearTimeout(timer);
return;
}
timer = setTimeout(function(){
prevTime = Date.now();
timer = null;
fn.apply(context,args);
},delay);
}
}
}
export default LazyLoad;
计算时差工具轮子
/* getLengthedNumberStr(9,3) => 009 */
function getLengthedNumberStr(num, len) {
let ret = "";
for (let i = 0; i < len - (num + "").length; i++)
{ ret += "0"; }
ret += num;
return ret; }
export function getTimeDiffer(d1, d2) {
const differMillis = d2 - d1;
const hours = parseInt(differMillis / (3600 * 1000));
const minutes = parseInt((differMillis % (3600 * 1000)) / (60 * 1000));
const seconds = parseInt((differMillis % (60 * 1000)) / 1000);
return { hours: getLengthedNumberStr(hours, 2),
minutes: getLengthedNumberStr(minutes, 2),
seconds: getLengthedNumberStr(seconds, 2),
};
}
封装倒计时自定义hook
import { reactive, toRefs } from "vue";
import { onMounted, onBeforeUnmount, onUnmounted } from "vue";
import { getTimeDiffer } from "@/utils/datetimeUtil";
function useCountDown(targetDate) {
/* 构建响应式数据 */
const { hoursLeft, minutesLeft, secondsLeft } = toRefs(
reactive({
hoursLeft: "00",
minutesLeft: "00",
secondsLeft: "00",
})
);
/* 让响应式数据自动变化 */
let timer;
/* 组件一挂载即创建定时器开始倒计时 动态修改hoursLeft,minutesLeft,secondsLeft */
onMounted(() => {
console.log("onMounted");
timer = setInterval(() => {
const { hours, minutes, seconds } = getTimeDiffer(new Date(), targetDate);
/* 更新响应式数据 */
hoursLeft.value = hours;
minutesLeft.value = minutes;
secondsLeft.value = seconds;
}, 1000);
});
/* 组件卸载前 清除倒计时定时器 */
onBeforeUnmount(() => {
console.log("onBeforeUnmount");
clearInterval(timer);
console.log("定时器已清除");
});
/* 将响应式数据丢还调用者 */
return { hoursLeft, minutesLeft, secondsLeft }; }
export default useCountDown;
使用方法:
<script setup>
import useCountDown from "@/hooks/useCountDown";
const { hoursLeft, minutesLeft, secondsLeft } = useCountDown(
new Date(2022, 5, 10, 18)
);
</script>
axios经典三层封装
第一层/实例层:配置请求实例与拦截器,导出请求实例
const instance = axios.create({
// baseURL: "http://localhost:9000/api",
baseURL: '/api',
timeout: 3000
})
// 添加请求拦截器
instance.interceptors.request.use(
function(config) {
// 在发送请求之前做些什么
config.headers['Authorization'] = `Bearer ${getToken()}`
return config },
function(error) {
// 对请求错误做些什么
return Promise.reject(error) })
// 添加响应拦截器
instance.interceptors.response.use( function(response) {
// 对响应数据做点什么
return response.data }, function(error) {
// 对响应错误做点什么
return Promise.reject(error)
})
第二层/操作层:封装并导出通用增删改查方法
export async function doPost(url, data, conf) {
try { const ret = await instance.post(url, data, conf)
return ret
} catch (error) {
handleErr(`doPost@${url}`, error)
} }
export function doDelete(url,conf) {
return instance.delete(url, conf)
}
export function doPut(url, data, conf) {
return instance.put(url, data, conf)
}
export async function doGet(url, conf) {
try { const ret = await instance.get(url, conf)
return ret
} catch (error) {
handleErr(`doGet@${url}`, error)
} }
第三层/业务层:业务逻辑请求封装
import { doDelete, doGet, doPost, doPut } from "../network/request";
export async function addGoods(goods) {
return await doPost("/goods/0", goods);
}
export async function deleteGoods(goods) {
return await doDelete(`/goods/${goods._id}`);
}
export async function updateGoods(goods) {
console.log("updateGoods", goods);
let gid = goods._id;
delete goods._id;
return await doPut(`/goods/${gid}`, goods);
}
export async function getGoodsByPage(page) {
const data = await doGet(`/goods/list/${page % 2 === 1 ? 1 : 2}`);
return new Promise((resolve, reject) => {
setTimeout(() => {
// return data;
resolve(data);
}, 2000);
});
}