当前位置: 首页 > 后端技术 > Node.js

Vue实战技巧合集

时间:2023-04-03 22:45:41 Node.js

前言本文纯属本人日常实践中的一些经验总结。本文不涉及稀有API的使用等,大部分内容都是基于Vue的一些实践。由于疑似投机取巧,可能会带来一些不符合规范的副作用,请按项目要求使用。多个页面使用的方法,放在vue.prototype上会很方便。刚开始接触vue的时候做了一件很蠢的事,因为它封装了一个异步请求接口post,放在post.js文件中,然后在每个需要使用异步请求的页面引入importfrom'./xxxx/xxxx/post'如果是这样的话,没别的,我们可以写一个页面,然后复制过来,这样可以保证每个页面都有上面的语句。但是如果每个文件的目录层次不同呢?//假设这是正常的导入端口from'../xxxx/xxxx/post'//目录加深一层,就变成了这个导入端口from'../../xxxx/xxxx/post'//再深化一下第一层好像是importportfrom'../../../xxxx/xxxx/post'当然这个时候我们可以使用别名@/xxxx/post,但是仍然有必要参考每一页。我们来看看,使用vue.prototype到底有多方便?首先你得在vue的入口文件中做如下设置(vue-cli生成的项目,默认是/src/main.js)importportfrom'./xxxx/xxxx/post'vue。prototype.$post=post这样我们就可以在所有的vue组件(页面)中使用this.post()方法,就像vue之子tip:在prototype上挂方法的时候,最好加一个$前缀,避免与其他变量冲突tilagain:不要给原型挂载太多的方法,只挂载一些使用频率非常高的需要响应的数据。获取界面数据时,首先设置是否经常遇到这两种情况,在循环列表的时候,我们需要给列表项一个属性来控制显示,比如是否可以删除,是否选中等,而后端接口一般不会返回这个字段,因为这是纯前端展示的,跟后端没有关系。比如后台给出的数据如下[{name:'abc',age:18},{name:'def',age:20},{name:'ghi',age:22},]我们不妨假设上面的数据是学生列表,然后我们需要渲染列表并在每个项目后面显示一个检查按钮。如果用户勾选按钮,按钮将是绿色的,默认情况下按钮是灰色的。这个时候,上面的表格是没有满足渲染条件的数据的,如果我们在用户勾选框的时候加上这个数据,正常的做法是无法及时响应的。如果我们在获取数据的时候先给数组的每一项加上一个刻度线,就可以解决这个问题。假设我们拿到的数据是res.listres.list.map(item=>{item.isTicked=false})这样做的原理是vue不能响应不存在的属性,所以当我们拿到数据的时候,我们先添加需要的属性,然后赋值给data,这样在接收数据的时候,发送数据的时候,这个属性已经存在了,所以才会响应。当然还有其他方法可以做到。不过,对于一个强迫症来说,我还是更喜欢这种方式来封装全局的基于promise的异步请求方法。看了很多项目的源码,发现大部分异步请求都是直接使用axios等方法,如下axios({method:'post',url:'/user/12345',data:{firstName:'Fred',lastName:'Flintstone'}}).then(function(response){console.log(response);}).catch(function(error){console.log(error);});如果有跨域,或者需要设置httpheaders,就需要增加更多的配置,而这些配置对于同一个项目来说基本是一样的,区别只是url和参数而已,何乐而不为呢我把它封装成一个方法?functionpost(url,param){returnaxios({method:'post',url:url,data:param...axios的其他配置})}tip:这里我多加了一层promise来包裹,就是简单的需求太多了,感觉掘金用户@日月为易。指出结合第一点,我们可以使用letparam={firstName:'Fred',lastName:'Flintstone'}this.post('/user/12345',param).then(.Is..)。catch(...)比原来的简单多了?如果你的项目支持异步等待,你也可以使用letparam={firstName:'Fred',lastName:'Flintstone'}letres=awaitthis.post('/user/12345',param)console.log(res)//res为异步返回的数据提示:async修饰的函数中必须使用await关键字。如果你觉得有时候,真的需要父子组件共享一个值,那为什么不试试给vue的父子传递一个引用类型,过去组件中传值的方式有很多,所以我这里就不一一列举了,但是今天我们要了解的是利用javascript的引用类型特性来达到传值的另一个目的。假设有这样一个需求,父组件需要传3个值给子组件,然后在子组件中修改,需要在父组件上立即响应。修改之后,我们一般都是通过this.$emit发出事件,然后在父组件中监听相应的事件,不过这样处理一两条数据还是可以的。如果数据太多,会很累。我们不妨将要传递的数据包装在一个对象/数组中,然后传递给子组件data(){return{subData:{filed1:'field1',filed2:'field2',filed3:'field3',filed4:'field4',filed5:'field5',}}}这样,如果我们在子组件中改变subData的内容,父组件可以直接响应如果没有this.$emit或者vuex,如果有其他的兄弟组件,只要兄弟组件也绑定了这个subData,那么兄弟组件中的subData也能及时响应。数据不多的话就乖乖用this.$emit吧。其次,这个数据需要满足特定的条件才能构建,并不适用于所有情况。将异步请求的参数构造在数据中,包裹在一个对象中,这对很多从事过类ERP系统的同学来说会很方便。他们一定遇到过这样的场景,一个列表,N个过滤条件。这时候通常我们这样绑定....data(){return{field1:'value1',field2:'value2',field3:'value3',...fieldn:'valuen'}}然后像这样提交数据:varparam={backend_field1:this.field1,backend_field2:this.field2,backend_field3:this.field3,...backend_fieldn:this.fieldn}this.post(url,param)比如可以看到,每次提交接口都要构造参数,很容易遗漏。我们这样做:先去接口文档中查看后端需要的字段名,然后....```javascript数据(){返回{queryParam:{backend_field1:'value1'backend_field2:'value2'backend_field3:'value3'...backend_fieldn:'valuen'}}}```然后像这样提交数据:```javascriptthis.post(url,this.queryParam)```是的,这也是有限的。比如你在两个地方共享一个数据,比如前端组件绑定了一个数组,需要提交给后端的是两个字符串(比如:elementui控件的时间),但是一些特殊的问题稍微处理一下,比重建一个参数要容易一些,不是吗?当data里面有很多数据的时候,给每一个数据加个注释,以后回头看的时候就一目了然了。当data里的数据很多的时候,写的时候可能就清楚了。毕竟都是你自己写的,但是过了十天半个月,或者别人看了你的代码,相信我,不管是你自己还是别人,都是一头雾水(记忆力超常人除外)),所以我们不妨给每条数据添加一个注释data(){return{field1:'value1',//控制xxx显示field2:'value2',//页面加载状态field3:[],//用户列表...fieldn:'valuen'//XXXXXXXX}}逻辑复杂的内容,尝试将其拆分成组件假设我们有这样一个场景:

Name:{{user1.name}}
性别:{{user1.sex}}
年龄:{{user1.age}}
...这里省略了999个字段...
他邻居阿姨的小狗PetName:{{user1.petName}}
<--当然我们也不会傻到在显示中不使用v-for,我们假设v-for不能在显示中使用这种情况-->
surname姓名:{{user2.name}}
性别:{{user2.sex}}
年龄:{{user2.age}}
...此处省略999个字段...
他邻居阿姨的小狗的名字:{{user2.petName}}
这种情况下,我们不妨将[user]的代码提取到一个组件中:假设如下代码,在comUserInfo.vue然后原来的页面可以改成这样(省略import和注册组件,假设注册名称是comUserInfo):这样是不是更清楚了?不看评论也能猜到。这是两个用户信息模块。这样做的另一个好处是,当发生错误时,可以更容易地定位到错误的位置如果只在子组件中改变父组件的一个值,试试$emit('input'),它会直接改变v-model。我们正常的父子组件通信是父组件将props传给子组件,子组件通过this.$emit('eventName',value)通知父组件上绑定@eventName的方法做相应加工。但是这里有一个特例。Vue默认会监听组件的输入事件,会将子组件传过来的值赋值给当前绑定到v-model上的值。正常使用——父组件正常使用-subcomponent使用默认输入事件-父组件使用默认输入事件-子组件exportdefault{methods:{updateData1(newValue){this.$emit('update:field1',newValue)},updateData2(newValue){this.$emit('update:field2',newValue)}}}}该方法比较适合更新不能更新的数据bound在v-model的情况下,或者要双向传递的数据大于一个(也可以用一个,不过我个人比较推荐输入法,看个人喜好),但是这种情况不多。组件放在vue选项的最上面,不知道大家有没有这样的经历:导入组件,然后在其他页面使用,好吧,报错,为什么呢?忘记注册组件,为什么经常忘记注册组件?因为一个普通的vue实例的结构大概是这样的:importxxxform'xxx/xxx'exportdefault{name:'component-name',data(){return{//...根据业务逻辑的复杂度,这里省略了几行}},computed:{//...根据业务逻辑的复杂程度,这里省略了一些行},created(){//...根据业务逻辑的复杂程度,这里省略了几行},mounted(){//...根据业务逻辑的复杂程度,这里省略了一些行},methods(){//...根据业务逻辑的复杂程度,这里省略了一些行},}不知道大家正常是不是把components属性放在哪里。反正我之前放在最下面,结果就是经常犯上面的错误。后来我把组件调到第一个importxxxform'xxx/xxx'exportdefault{components:{xxx},//省略其他代码}从此,妈妈再也不用担心我忘记注册组件了,import注册都在一个地方,所以很难忘记。大多数情况下,生命周期中的代码行数不应该太多。可以封装成一个方法,然后调用很多代码,包括我之前自己的。生命周期里我滔滔不绝写了一两百行代码,比如:页面加载的时候,所有要做的事情都写在created里,导致整个代码难以阅读。我不知道你在加载页面时做了什么。这时候我们不妨把那些逻辑封装成方法,然后在生命周期中直接调用:created(){//获取用户信息this.getUserInfo()//获取系统信息this.getSystemInfo()//获取配置this.getConfigInfo()},methods:{//获取用户信息getUserInfo(){...},//获取系统信息getSystemInfo(){...},//获取配置getConfigInfo(){...},}是不是一眼就能看出来?页面加载时它做了什么?tip:这个应该算是习惯规范了,不过我觉得自己看多了就这样写了,而且我初学的时候也是这样写的,所以写出来,希望新同学可以避免这个问题,使用watchless,如果你认为需要在很多地方使用watch,很可能是你对vueAPI了解不够。Vue本身是一个数据驱动的框架。数据变化可以实时反馈到视图。如果你想根据数据来控制视图,一般情况下,配合计算应用可以解决大部分问题。至于视图的变化,我们通常可以通过监听输入变化等事件来实现实时监控,所以使用的必要性不大。说到watch,至少我最近做的十几个项目里,没有用过watch。当然,也不是说手表就没用。vue提供这个API肯定是有原因的,有些需求确实需要用到,但我觉得应该很少用到。如果你觉得到处都需要用到它,那我觉得你应该多熟悉一下computed和vue的其他API。最后附上本文的github地址欢迎star、follow、非自愿问题。另外github上还有其他关于前端的教程和组件。有兴趣的童鞋可以看看。您的支持是我最大的动力。