当前位置: 首页 > Web前端 > vue.js

Steps组件的设计与实现

时间:2023-04-01 12:15:27 vue.js

NutUI组件源码揭秘前言本文的主题是Steps组件的设计与实现。Steps组件是Steps步骤和Timeline组件的组合。在此之前,它们是两个不同的组件。NutUI在最新版本升级时,将它们合二为一。让我们看看组件是如何开发的。一步步实现组件的功能。说到NutUI,可能有些人不太了解,我们先简单介绍一下。NutUI是一个京东风格的移动端Vue组件库,开发和服务于移动Web界面的企业级前中后端产品。通过NutUI,您可以快速构建风格统一的页面,提高开发效率。目前已有50+组件,广泛应用于京东各项移动服务。之前它们要分开使用,但是很多功能是交叉的,不能满足步骤和时间同时出现的业务场景,所以合并了。我们先来看一下Steps组件的最终渲染,数据展示,以及一些程序逻辑。组件的作用:根据不同的场景,可以使用不同的布局方式来指定当前节点。它可以水平或垂直排列,并且可以动态响应数据的变化。一般来说,这个组件需要用在物流信息、流程信息等的展示上,可以像这样使用。组件打包的思路大多数组件都是单个组件使用起来非常简单,比如我们的NutUI组件库中的defaultstate、等。使用该方法就可以实现该组件,这个设计的组件非常优秀,用户使用起来非常方便简单。简洁优雅的组件设计方法适用于大多数功能简单的组件,但相对逻辑性较差的组件则不适合布局复杂复杂的组件。功能比较复杂的组件会使得组件非常不灵活,模板固定,使用自由度低。对于开发者来说,组件编码也会变得非常臃肿。因此在vue组件的开发过程中合理使用slot特性,使组件更加灵活开放。像下面这样:这里是第一页的内容这里是Tab2的内容这里是Tab3的内容这里是第4页的内容...有很多比较复杂的组件使用这种方式,既可以保证组件功能的完整性,又可以自由配置sub的内容-元素。组件的实现基于以上设计思路,可以开始实现组件了。本文的Steps组件包含两部分:外层的和内层的。我们一般这样设计<--nut-steps--><--nut-step-->外层组件控制整个组件的布局、激活状态等,子组件主要渲染内容,但是它们之间的关系就成了问题。子组件中的一些状态逻辑需要由父组件来控制,这意味着父子组件之间的属性或状态的通信。有两种方法可以解决这个问题。一种是在父组件中获取子组件信息,然后将子组件需要的父组件信息设置给子组件。二是在子组件中获取父组件的属性信息来渲染子组件。第一种方案:this.steps=this.$slots.default.filter((vnode)=>!!vnode.componentInstance).map((node)=>node.componentInstance);this.updateChildProps(true);首先通过this.$slots.default获取所有的子组件,然后遍历updateChildProps中的this.steps,根据父组件的属性信息更新子组件。运行起来验证一下,似乎达到了预期的效果!!!Prop动态更新但是在实际项目应用中,发现动态刷新存在很大的问题。比如当前状态发生变化,需要遍历使用到的子组件,性能低下,子组件的内容或者某个属性发生变化,更新组件会变得很麻烦,需要维护父组件并在一开始管理许多子组件的属性。比较笨拙的,将用于渲染子组件的列表传递给父组件,监听属性的变化重新渲染子组件。但是为了实现这个更新,增加了一个无意义的数据监控,需要进行深度监控,在某些场景下是没有必要的。反遍历渲染子组件也会造成性能消耗和低效。所以这种方法不适合,改用第二种方法。在子组件中访问父组件的属性,使用this.$parent来访问父组件的属性。//在创建步骤组件之前,将组件实例添加到父组件的步骤数组中beforeCreate(){this.$parent.steps.push(this);},data(){return{index:-1,};},methods:{getCurrentStatus(){//访问父组件的逻辑更新属性const{current,type,steps,timeForward}=this.$parent;//逻辑处理}},mounted(){//监听索引变化计算相关逻辑constunwatch=this.$watch('index',val=>{this.$watch('$parent.current',this.getCurrentStatus,{immediate:true});unwatch();});}在父组件中,接收子组件实例并设置索引属性data(){return{steps:[],};},watch:{steps(steps){steps.forEach((child,index)=>{child.index=index;//设置子组件的index属性,用于子组件的显示逻辑});}},通过下图看它的数据变化。子组件中的属性变化只依赖于子组件的属性。子组件内部的属性变化不需要触发父组件的更新,但是子组件个数的变化会触动父组件,子组件会按照创建的顺序重新排序。设置索引值,子组件会根据索引值的变化重新渲染。更多的逻辑交给了子组件,而父组件更多的是负责整个组件的功能逻辑。也不需要监听子组件的数据源来更新组件。但是在实现过程中有一个关键属性可能会导致bug。这是this.$parent。只有当子组件的parent为时访问的this.$parent才是准确的。如果不是直接的亲子类,肯定会有bug。在实际使用中,不仅是该组件,该类型的其他组件也会出现子组件的直接父级不是其对应父级的情况,从而导致bug。例如:组件作为component使用组件时,this.$parent不再指向。然后你可以在中添加一些技巧:letparent=this.$parent||这个。$parent.$parent;完成的。多层传递的神器——依赖注入现在主要需要解决的是让后代子组件可以访问父级组件实例上的属性或方法,不管中间有多少层。Vue依赖注入可以派上用场。vue实例有两个配置选项:provide:指定我们要提供给后代组件的数据/方法。inject:接收我们要添加到此实例的指定属性。这两个属性是vuev2.2.0新加入的。这两个选项需要一起使用才能让一个祖先组件向它的所有后代组件注入一个依赖,不管这个组件层次有多深,并且在它的上下游关系建立期间总是生效。如果您熟悉React,这与React的上下文功能非常相似。父组件使用provide提供可以注入子组件的属性。//父组件stepsprovide(){return{timeForward:this.timeForward,type:this.type,pushStep:this.pushStep,delStep:this.delStep,current:this.current,}},methods:{pushStep(step){this.steps.push(步骤);},delStep(step){conststeps=this.steps;constindex=steps.indexOf(step);如果(index>=0){steps.splice(index,1);}}},子组件通过inject读取父组件提供的属性。//后代组件stepinject:['timeForward','type','current','pushStep','delStep']//beforeCreate(){//this.$parent.steps.push(this);///this.pushStep(this);//},created(){this.pushStep(this);},子组件不再使用this.$parent获取父组件的数据。这里有一个细节。子组件更新父组件steps值的时机由beforeCreate改为created。这是因为inject的初始化是在beforeCreate之后执行的,所以在此之前无法访问inject中的属性。跨级嵌套的问题解决了,还有一个问题,就是监听父组件属性的变化。因为:提供和注入绑定没有响应。例如,可以动态更改当前属性。像上面的注入一样,后代组件将始终获得初始注入值,而不是最新的注入值。这个也好解决,用一个函数获取父组件注入依赖时的实时当前值即可。provide(){return{getCurrentIndex:()=>this.current,}},在子组件中:computed:{current(){returnthis.getCurrentIndex();}},mounted(){constunwatch=this.$watch('index',val=>{this.$watch('current',this.getCurrentStatus,{immediate:true});unwatch();});},this.$watch和watch方法中的监听是一样的效果,可以主动触发watch,this.$watch()返回一个cancelwatch函数,停止触发回调。这里,组件挂载后,监听索引的变化,索引的变化立即触发当前属性变化的监听。这样就可以实时获取到父组件的属性变化,实现数据监控刷新组件。至此,该组件的主要难点已经攻克。当然,这种方式只适用于父子层次较深的场景,同级的兄弟组件无法通过这种方式进行通信。另外,provide和inject主要是在开发高级组件或者组件库的时候使用,在普通的应用代码中最好不要使用。因为这可能会造成数据混乱,业务和逻辑混乱,项目变得难以维护。综上所述,在组件开发的过程中,为了保证组件的灵活性和完整性,很多组件都会存在这种嵌套问题,甚至深度嵌套导致的属性共享问题和数据监控问题,所以本文是主要根据Steps组件的开发经验提供解决方案,希望能对大家有所帮助或启发。