好用的自定义轮子

77 阅读2分钟

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);
});
}