当前位置: 首页 > Web前端 > HTML

前端实训:Vue3的自定义指令

时间:2023-03-28 00:47:14 HTML

背景众所周知,Vue.js的核心思想是数据驱动+组件化。通常,开发一个页面的过程就是编写一些组件,通过修改数据来驱动组件的重新安装。使成为。在这个过程中,我们不需要手动操作DOM。但是,在某些场景下,我们仍然无法避免对DOM进行操作。前端培训由于Vue.js框架接管了创建和更新DOM元素的过程,它可以在DOM元素的生命周期中注入用户代码,因此Vue.js设计并提供了自定义指令,允许用户执行一些低级培训。DOM操作。举一个实际的例子——图片的延迟加载。图片的延迟加载是一种常见的性能优化方法。因为它只加载可见区域的图片,所以可以减少很多不必要的请求,大大提高用户体验。图片懒加载的实现原理也很简单。当图片没有进入可见区域时,我们只需要让img标签的src属性指向一张默认图片即可。进入可见区域后,将其src指向真实图片的地址即可。如果我们要在Vue.js项目中实现图片懒加载,那么使用自定义指令是完美的,那我就带大家用Vue3实现一个图片延迟加载的自定义指令v-lazy。插件为了使这个指令易于用于多个项目,我们将它做成一个插件:constlazyPlugin={install(app,options){app.directive('lazy',{//directiveobject})}}exportdefaultlazyPlugin然后在项目中引用它:import{createApp}from'vue'importAppfrom'./App.vue'importlazyPluginfrom'vue3-lazy'createApp(App).use(lazyPlugin,{//添加一些配置parameter})通常一个Vue3插件会暴露install函数,当app实例使用该插件时会执行该函数。在install函数中,通过app.directive注册一个全局指令,以便它们可以在组件中使用。指令的实现接下来我们需要做的是实现指令对象。一个指令定义对象可以提供挂载、更新、卸载等多个钩子函数,我们可以在相应的钩子函数中编写相应的代码来满足需求。在写代码之前,我们不妨想一下实现图片懒加载的几个关键步骤。图片管理管理图片的DOM、真实的src、预加载的url、加载状态、图片的加载情况。可见区域判断判断图片是否进入可见区域。关于图片的管理,我们设计了ImageManager类:constState={loading:0,loaded:1,error:2}exportclassImageManager{constructor(options){this.el=options.elthis.src=options.srcthis.state=State.loadingthis.loading=options.loadingthis.error=options.errorthis.render(this.loading)}render(){this.el.setAttribute('src',src)}load(next){if(this.state>State.loading){return}this.renderSrc(next)}renderSrc(next){loadImage(this.src).then(()=>{this.state=State.loadedthis.render(this.src)next&&next()}).catch((e)=>{this.state=State.errorthis.render(this.error)console.warn(loadfailedwithsrcimage(${this.src})和错误msgis${e.message})next&&next()})}}exportdefaultfunctionloadImage(src){returnnewPromise((resolve,reject)=>{constimage=newImage()image.onload=function(){resolve()dispose()}image.onerror=function(e){reject(e)dispose()}image.src=srcfunctiondispose(){image.onload=image.onerror=null}})}首先,对于图片,它有三种状态,loading、loadingcompleted和loadingfailed。ImageManager在实例化时,除了会初始化一些数据外,还会在其对应的img标签的src中执行图片加载,默认是相当Imageloaded。当执行ImageManager对象的load方法时,会判断图片的状态。如果它还在加载,它的真实src将被加载。这里使用loadImage图片预加载技术来请求src图片,然后在src成功后替换img标签,修改状态,从而完成图片真实地址的加载。有了图片管理器,接下来我们需要实现可见区域的判断和多个图片管理器的管理,设计Lazy类:constDEFAULT_URL='data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAAIBRAA7'exportdefaultclassLazy{构造函数(选项){this.managerQueue=[]this.initIntersectionObserver()this.loading=options.loading||DEFAULT_URLthis.error=options.error||DEFAULT_URL}add(el,binding){constsrc=binding.valueconstmanager=newImageManager({el,src,loading:this.loading,error:this.error})this.managerQueue.push(manager)this.observer。观察(el)}initIntersectionObserver(){这个。observer=newIntersectionObserver((entries)=>{entries.forEach((entry)=>{if(entry.isIntersecting){constmanager=this.managerQueue.find((manager)=>{returnmanager.el===entry.target})if(manager){if(manager.state===State.loaded){this.removeManager(manager)return}manager.load()}}})},{rootMargin:'0px',阈值:0})}removeManager(manager){constindex=this.managerQueue.indexOf(manager)if(index>-1){this.managerQueue.splice(index,1)}if(this.observer){this.observer.unobserve(manager.el)}}}constlazyPlugin={install(app,options){constlazy=newLazy(options)app.directive('lazy',{mounted:lazy.add.bind(lazy)})}}这样,每当图像元素绑定到v-lazycommand,而在挂载的钩子函数中执行时,会执行Lazy对象的add方法,其中第一个参数el对应图片对应的DOM元素对象,第二个参数binding为command绑定的值对象,例如:其中item.pic对应命令绑定的值,所以可以通过绑定获取图片的真实地址。value有了图片的DOM元素对象和真实的图片地址后,可以基于它们创建一个图片管理器对象,加入managerQueue,观察图片DOM的可见区域同时元素。对于图片进入可见区域的判断,主要使用IntersectionObserverAPI,其对应的回调函数的参数入口为一个IntersectionObserverEntry对象数组。Web前端训练当观察到的元素可见比例超过指定阈值时,会执行回调函数遍历entry,获取每一个entry,然后判断entry.isIntersecting是否为true。如果是,则表明入口对象对应的DOM元素进入了视口。然后根据DOM元素的比较,从managerQueue中找到对应的manager,判断其对应图片的加载状态。如果图片处于loading状态,此时执行manager.load函数,完成真实图片的加载;如果处于loaded状态,则直接从managerQueue中移除其对应的manager,停止观察图片DOM元素。目前我们已经实现了图片元素挂载到页面后延迟加载的一系列流程。但是,当元素从页面卸载时,还需要进行一些清理操作:==el})if(manager){this.removeManager(manager)}}}constlazyPlugin={install(app,options){constlazy=newLazy(options)app.directive('lazy',{mounted:lazy.add.bind(lazy),remove:lazy.remove.bind(lazy)})}}当元素被卸载时,其对应的图片管理器也会从managerQueue中移除,图片DOM元素的观察也会停止。另外,如果v-lazy指令绑定的值是动态修改的,即真实图片的请求地址,则需要在指令内部进行相应的修改:exportdefaultclassImageManager{update(src){constcurrentSrc=this.srcif(src!==currentSrc){this.src=srcthis.state=State.loading}}}exportdefaultclassLazy{update(el,binding){constsrc=binding.valueconstmanager=this.managerQueue.find((manager)=>{returnmanager.el===el})if(manager){manager.update(src)}}}constlazyPlugin={install(app,options){constlazy=newLazy(options)app.directive('lazy',{mounted:lazy.add.bind(lazy),remove:lazy.remove.bind(lazy),update:lazy.update.bind(lazy)})}}到目前为止,我们已经实现了一个简单的Image懒加载指令,在此基础上,我们还可以做一些优化吗?指令优化在加载图片真实url的过程中,我们使用了loadImage来预加载图片。显然,对于具有相同url的多张图片,预加载只需要进行一次。为了实现上述需求,我们可以在Lazy模块内部创建一个cache缓存:exportdefaultclassLazy{constructor(options){//...this.cache=newSet()}}并且在创建ImageManager实例时,把缓存传入:constmanager=newImageManager({el,src,loading:this.loading,error:this.error,cache:this.cache})然后修改ImageManager如下:exportdefaultclassImageManager{load(next){if(this.state>State.loading){return}if(this.cache.has(this.src)){this.state=State.loadedthis.render(this.src)return}this.renderSrc(next)}renderSrc(next){loadImage(this.src).then(()=>{this.state=State.loadedthis.render(this.src)next&&next()}).catch((e)=>{this.state=State.errorthis.cache.add(this.src)this.render(this.error)console.warn(loadfailedwithsrcimage(${this.src})和错误信息is${e.message})next&&next()})}}在每次执行load之前判断缓存中是否存在,执行完再更新缓存ngloadImage成功预加载图像。通过这种以空间换时间的方式,避免了一些重复的url请求,达到了优化性能的目的。总结懒加载图片指令的完整指令实现,可以在vue3-lazy中查看。懒加载图片命令的核心是通过IntersectionObserverAPI判断图片是否进入可见区域。现代浏览器支持该功能,IE浏览器不支持。这时候可以监听图片的可滚动父元素的一些事件。比如滚动,调整大小等,然后通过一些DOM计算来判断图片元素是否进入可见区域。但是Vue3显然已经不支持IE了,所以只用IntersectionObserverAPI就可以了。除了懒加载图片的自定义指令中使用的钩子函数,Vue3的自定义指令还提供了一些其他的钩子函数。以后开发自定义指令时,可以参考它的文档,找到合适的钩子函数。编写相应的代码逻辑。文章来自Vue中文社区