Vue深入-13【手寫lazyload-2與函式呼叫元件】
阿新 • • 發佈:2020-12-12
(1).lazyload
export default (Vue) =>{ class ReactiveListener{ constructor({el,src,elRenderer,options}){ this.el = el, this.src = src, this.elRenderer = elRenderer, this.options = options; this.state = { loading:false } } checkInView(){ const { top } = this.el.getBoundingClientRect(); return top < window.innerHeight * this.options.preLoad; } load(){ this.elRenderer(this,'loading'); loadImageAsync(this.src,()=>{ this.state.loading = true; this.elRenderer(this,'success') },()=>{ this.elRenderer(this,'error') }) } } function loadImageAsync(src,resolve,reject){ let image = new Image(); image.src = src; image.onload = resolve; image.onerror = reject; } return class LazyClass{ constructor(options){ this.options = options;//通過new例項化傳遞過來的 this.isHandlerBind = false;//是否繫結過滾動事件 this.listenerQueue = [];//元素列表 this.lazyLoadHandler = () =>{ let catIn = false;//當前img元素是否在區域內,預設不在 this.listenerQueue.forEach(listener=>{ if(listener.state.loading) return catIn = listener.checkInView(); catIn && listener.load(); }) } } //被繫結元素會執行 add(el,bindinds){ //要拿到父節點ul必須要nextTick Vue.nextTick(()=>{ function findScrollParent(){ let parent = el.parentNode; while(parent){ if(/scroll/.test(getComputedStyle(parent)['overflow'])){ return parent } parent = parent.parentNode; } return parent } let parent = findScrollParent(); if(!this.isHandlerBind){ this.isHandlerBind = true; parent.addEventListener('scroll',this.lazyLoadHandler) } const src = bindinds.value; let listener = new ReactiveListener({ el, src, elRenderer:this.elRenderer.bind(this), options:this.options }) this.listenerQueue.push(listener) this.lazyLoadHandler(); }) } //根據條件新增 elRenderer(listener,state){ let { el } = listener; let src = ''; switch(state){ case'loading': src = listener.options.loading break; case'error': src = listener.options.error break; default: src = listener.src break; } el.setAttribute('src',src) } } }
lodash節流
npm i lodash import {throttle} from 'lodash' this.lazyLoadHandler = throttle(() =>{ let catIn = false;//當前img元素是否在區域內,預設不在 this.listenerQueue.forEach(listener=>{ if(listener.state.loading) return catIn = listener.checkInView(); catIn && listener.load(); }) },300);
(2).函式式呼叫元件實現提示框
toast.js
import Vue from 'vue'; import ToastComponent from '@/views/Toast.vue' //新建建構函式 const ToastConstructor = Vue.extend(ToastComponent); const Tost = (options) =>{ //建立掛載節點 const mountNode = document.createElement('div'); document.body.appendChild(mountNode); new ToastConstructor({ data:options,//這裡的引數就會合並 el:mountNode }) } const type = ['success','error','warning','alert']; //這樣可以處理.success的情況 type.forEach((item)=>{ Tost[item] = (options) =>{ options.type = item; Tost(options); } }) export default Tost
toast.vue
<template>
<div class="toast" :class="toastType">
{{message}} | {{type}}
</div>
</template>
<script>
export default {
data(){
return{
message:'',
type:'',
duration:1000
}
},
computed:{
toastType(){
return 'toast-'+this.type
}
},
mounted(){
setTimeout(()=>{
//定時後銷燬
this.$destroy();
this.$el.parentNode.removeChild(this.$el)
},this.duration)
}
}
</script>
<style>
.toast{
width: 200px;
height: 52px;
line-height: 52px;
text-align: center;
}
.toast-loading{
background-color: #424242;
color: #fff;
}
.toast-success{
background-color: #dff0d8;
color: #3c763d;
}
</style>
app.vue中呼叫
import Toast from '@/tost/tost'
showToast(){
Toast({message:'載入中',type:'loading'})
Toast.success({message:'成功'})
}
vue3
此處解除安裝和掛載就不一樣了
import {createApp} from 'vue';
import Toast from './Toast.vue'
function createToast(message,type,duration=1000){
const toastInstInstance = createApp(Toast,{
message,type
})
const mountNode = document.createElement('div');
document.body.appendChild(mountNode);
toastInstInstance .mount(mountNode);
setTimeout(()=>{
toastInstInstance.unmount(mountNode)
document.body.removeChild(mountNode)
},duration)
}
exprot {createToast}