实现全局事件总线
-
创建一个eventBus.js文件,代码如下
import Vue from "vue";
const listencers = {};
Vue.prototype.$bus = {
//监听事件
$on(eventName, handle) {
//监听对象里面没有事件,加到对象里面
if (!listencers[eventName]) {
listencers[eventName] = new Set();
}
listencers[eventName].add(handle);
},
//取消监听
$off(eventName, handle) {
if (!listencers[eventName]) {
return;
}
listencers[eventName].delete(handle);
},
//触发事件
$emit(eventName, ...args) {
if (!listencers[eventName]) {
return;
}
for (const handle of listencers[eventName]) {
//调用注册的方法
handle(...args);
}
},
};
// const app = new Vue({});
// Vue.prototype.$bus = app;
export default Vue.prototype.$bus;
/**
* 事件名:mainScroll
* 含义:主区域滚动条变化后触发
* 参数:
* -滚动的dom元素,如果时undefined,则dom元素已经不存在
*
* 事件名:setMainScroll
* 含义:当需要设置主区域滚条位置时触发
*
*/
-
在main.js中配置事件总线
import "./eventBus";
使用mixins的方式方便多个页面监听滚动事件,处理后续操作。
-
创建一个mixins文件夹,文件夹下面mainScroll.js
export default function (refValue) {
return {
mounted() {
//监听setMainScroll
this.$bus.$on("setMainScroll", this.handleSetMainScroll);
//监听滚动事件,滚动了就触发事件总线中的mainScroll事件
this.$refs[refValue].addEventListener("scroll", this.handleMainScroll);
},
beforeDestroy() {
this.$bus.$off("mainScroll", this.handleMainScroll);
this.$bus.$off("setMainScroll", this.handleSetMainScroll);
this.$refs[refValue].removeEventListener("scroll", this.handleMainScroll);
},
methods: {
handleSetMainScroll(scrollTop) {
this.$refs[refValue].scrollTop = scrollTop;
},
handleMainScroll() {
//事件总线中触发mainScroll事件
this.$bus.$emit("mainScroll", this.$refs[refValue]);
},
},
};
}
封装v-lazy指令
-
创建lazy.js文件
import eventBus from "../eventBus";
import { debounce } from "@/utils";
import defaultImg from "@/assets/loading.gif";
//调用该函数,就可以设置哪些合适的图片
let images = [];
/**
* 处理单个图片
* @param {} img
*/
function setImage(img) {
//真实图片没加载之前的图片
img.dom.src = defaultImg;
//处理图片是否在视口内
const rect = img.dom.getBoundingClientRect();
const clientHeight = document.documentElement.clientHeight;
const height = rect.height || 100;
if (rect.top >= -height || rect.top <= clientHeight) {
//在视口范围内
const tempImg = new Image();
tempImg.onload = function () {
//当真实图片加载的时候,把真实的dom绑定服务器图片地址
img.dom.src = img.src;
};
tempImg.src = img.src;
//将加载好的图片移出数组
images = images.filter((item) => item !== img);
}
}
function setImages() {
for (const image of images) {
//处理图片
setImage(image);
}
}
function handleScroll() {
setImages();
}
//监听事件总线名为mainScroll事件
eventBus.$on("mainScroll", debounce(handleScroll, 50));
export default {
inserted(el, bindings) {
images.push({
dom: el,
src: bindings.value,
});
setImages();
},
unbind(el) {
images = images.filter((item) => item.dom !== el);
},
};
其中debounce防抖函数如下:
export default function (fn, duration = 100) {
let timeId = null;
return (...args) => {
clearTimeout(timeId);
// 将该函数的this传递到fn
timeId = setTimeout(() => {
fn(...args);
}, duration);
};
}
-
在main.js中注册自制指令
//入口文件
import "./mock";
import Vue from "vue";
import App from "./App.vue";
import "./style/global.less";
import "./eventBus";
import router from "./router";
import store from "./store";
//注册自制指令
import vLoading from "@/directive/loading";
import vLazy from "@/directive/lazy";
Vue.directive("loading", vLoading);
Vue.directive("lazy", vLazy);
new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#app");