1. 程式人生 > 其它 >Vue深入-13【手寫lazyload-2與函式呼叫元件】

Vue深入-13【手寫lazyload-2與函式呼叫元件】

技術標籤:Vuevue

(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}