关注 逆锋起笔,回复“加群”

加入我们一起学习,天天进步

作者:红尘炼心

https://juejin.cn/post/7005751368937897991

前言

近来入坑了一个Vue项目,感觉掉进了祖传屎山中,可读性极差,更别说可维护性了。故借此专栏提几点关于Vue代码可读性的建议,觉得有用的点个赞,觉得建议不合理的发表评论批评一下,有更好的建议欢迎发表评论补充一下。

一、善用组件让代码更有条理性

千万不要把一个页面的实现代码都梭哈在一个.vue文件中,除非这个页面非常简单,不然这个.vue文件中的代码会又长又臭。

Vue提供组件的目的不仅仅是为了复用,也可以用来分割代码,甚至善用组件可以优化页面的渲染更新速度。这是因为Vue页面渲染更新时不会去更新页面中的组件,除非组件的props或者slot所引用的数据发生变化。

可以按以下步骤来将一个Vue页面分割成一个个组件让代码更有条理性

1.1、提取UI组件

如何定义UI组件呢?个人建议按有无处理服务端数据来区分UI组件和业务组件。例如加载弹窗、二次确认弹窗、消息提示框等等属于UI交互组件。

将UI组件提取出来后,可以把UI交互的代码和业务交互的代码剥离开来。切记不能UI组件中写业务代码,这样UI组件将无法复用。

举一个反例,在二次确认弹窗中添加二次确认后要处理的业务代码,导致UI组件将无法复用。我们可以模仿ElementUI中二次确认弹窗的调用来实现一个二次确认弹窗组件。

this.$confirm(message, title, options).then(res =>{}).catch(err =>{})

这样业务代码可以写在then的回调函数中,组件的核心实现代码如下所示:

//confirm.vue
<template><div v-show="show">//...<div @click="ok"></div><div @click="cancel"></div></div>
</template>
<script>
export default {data() {return {show: false,}},methods: {ok() {this.show = false;this.resolve();},cancel() {this.show = false;this.resolve();},}
}
</script>
//index.js
import Vue from 'vue';
import options from './confirm.vue';
const Confirm = Vue.extend(options);
let confirm = undefined;
const ConfirmInit = (options = {}) => {return new Promise((resolve, reject) => {options.resolve = resolve;options.reject = reject;confirm = new Confirm({el: document.createElement('div'),data: options})document.body.appendChild(confirm.$el);Vue.nextTick(() => {if (confirm) confirm.show = true;})return confirm;})
}
Vue.prototype.$confirm = ConfirmInit;
//main.js
import 'components/confirm/index.js';//全局注册二次确认弹窗confirm组件

1.2、按模块提取业务组件

一个页面可以分为多个区域,比如头部、底部、侧边栏、商品列表、成员列表等等,每个区域可以当作一个模块来提取业务组件。

1.3、按功能提取功能组件

按模块提取完业务组件,此时业务组件有可能还是很庞大的,故要按功能在进一步地提取功能组件。

功能有大有小,提取要注意把握几个原则:

  • 过于简单的功能不提取

    例如一个收藏的功能,只要请求一个接口就完成,类似这样的功能不要提取。要有一定复杂度的逻辑操作的功能才提取。

  • 功能要单一,一个功能组件只处理一项业务。

    例如一个文件阅读器组件,有一个需求,要求打开文件后自动收藏该文件,那么收藏逻辑代码要写在哪里呢?

    或许你想都没想就在组件中监听文件成功打开的方法中写下收藏逻辑代码,过一段时间后,需求改为要先添加到阅读记录中再点击收藏按钮收藏,去组件中修改代码时发现另一个页面也引用了这个组件,故在组件中要额外加个参数做业务场景区分,随着需求的变化导致业务场景的叠加,组件的代码中会添加各种判断逻辑,久而久之变得又长又臭,显然这种做法是不可去。

    正确的做法是在组件标签上自定义一个事件on-fileOpen-success,用handleFileOpenSuccess函数来监听这个事件。

    <fileReader @on-fileOpen-success="handleFileOpenSuccess"
    >
    </fileReader>

    在组件中监听文件成功打开的方法中执行this.$emit('on-fileOpen-success',data)触发这个事件,其中data可以把文件信息传递出去,在handleFileOpenSuccess函数去处理收藏或者添加历史记录再收藏等业务交互。这种做法使文件阅读器组件具有单一性。

  • 功能组件尽量少包含UI部分,UI部分用slot插槽传入,这样使组件更纯粹,更具有复用性。

    例如上传组件的上传图标,不可能随着UI设计稿的变动就往里面添加一个上传图标,此时可以利用slot插槽把上传图标传入。

    //upload.vue
    <template><div><slot name="icon"></slot></div>
    </template>
    //index.vue
    <template><div><upload><template #icon>//上传图标</template></upload></div>
    </template>

二、利用v-bind使组件的属性更具有可读性

如果想要将一个对象的所有属性都作为prop传入组件componentA,可以使用不带参数的v-bind。例如,对于一个给定的对象params

params: {id: 1,name: 'vue'
}

优化前

<componentA :id="params.id" :name="params.name"></componentA>

优化后

<componentA v-bind="params"></componentA>

三、利用attrs与attrs与attrs与listeners来封装第三方组件

1、$attrs

在封装第三方组件中,经常会遇到一个问题,如何通过封装的组件去使用第三方组件自身的属性和事件。

比如封装一个elementUi组件中的Input输入框组件myInput,当输入错误的内容在输入框下面显示错误的提示。

myInput组件代码如下所示:

<template><div><el-input v-model="input"></el-input><div>{{errorTip}}</div></div>
</template>
<script>
export default {props: {value: {type: String,default: '',},errorTip: {type: String,default: '',}},data() {return {}},computed: {input: {get() {return this.value},set(val) {this.$emit('input', val)}}}
}
</script>

这样调用myInput组件,其中errorTip为输入框输入错误的提示。

<myInput v-model="input" :errorTip="errorTip"></myInput>

如果要在myInput组件上添加一个disabled属性来禁用输入框,要如何实现呢?一般同学会这么做

<template><div><el-input v-model="input":disabled="disabled"></el-input><div>{{errorTip}}</div></div>
</template>
<script>
export default {props: {//...disabled: {type: Boolean,default: false}},//...
}
</script>

过一段时间后又要在myInput组件上添加el-input组件其它的属性,el-input组件总共有27个多,那该怎么呢,难道一个个用prop传进去,这样做不仅可读性差而且繁琐,可以用$attrs一步到位,先来看一下attrs的定义。

$attrs: 包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件

<template><div><el-input v-model="input"v-bind="$attrs"></el-input><div>{{errorTip}}</div></div>
</template>

这还不够,还得把inheritAttrs选项设置为false,为什么呢,来看一下inheritAttrs选项的定义就明白了。

默认情况下父作用域的不被认作 props 的 attribute 绑定 (attribute bindings) 将会“回退”且作为普通的 HTML attribute 应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。通过设置 inheritAttrs 为 false,这些默认行为将会被去掉。而通过 $attrs 可以让这些 attribute 生效,且可以通过 v-bind 显性的绑定到非根元素上。注意:这个选项不影响 class 和 style 绑定。

简单来说,把inheritAttrs设置为falsev-bind="$attrs" 才生效。

<template><div><el-input v-model="input"v-bind="$attrs"></el-input><div>{{errorTip}}</div></div>
</template>
<script>
export default {inheritAttrs: false,props: {value: {type: String,default: '',},errorTip: {type: String,default: '',}},data() {return {}},computed: {input: {get() {return this.value},set(val) {this.$emit('input', val)}}}
}
</script>

这样就可以很清楚的把el-input组件的属性和myinput组件的属性区分开来了,组件的props选项的可读性大大提高。

2、$listeners

那么如何实现在myIpput组件上使用el-input组件上自定义的事件呢,可能你的第一反应是this.$emit

<template><div><el-input v-model="input"v-bind="$attrs"@blur="blur"></el-input><div>{{errorTip}}</div></div>
</template>
<script>
export default {//...methods: {blur() {this.$emit('blur')}}
}
</script>
<myInput v-model="input":errorTip="errorTip"@blur="handleBlur">
</myInput>

el-input组件有4个自定义事件,还不算多,假如遇到自定义事件更多的第三方组件,要怎么办,难道一个一个添加进去,不仅会增加一堆非必要的methods,而且可读性差很容易和myInput自身的methods混在一起。其实可以用$listeners一步到位,先来看一下$listeners的定义。

$listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件。

<template><div><el-input v-model="input"v-bind="$attrs"v-on="$listeners"></el-input><div>{{errorTip}}</div></div>
</template>
<script>
export default {//...
}
</script>
<myInput v-model="input":errorTip="errorTip"@blur="handleBlur">
</myInput>

在myInput组件中只要在el-input组件上添加v-on="$listeners",就可以在myInput组件上使用el-input组件自定义的事件。

逆锋起笔是一个专注于程序员圈子的技术平台,你可以收获最新技术动态最新内测资格BAT等大厂的经验精品学习资料职业路线副业思维,微信搜索逆锋起笔关注!

Vue 项目前端多语言方案

推荐 9 个 VUE3 UI 框架

Vue 实现数组四级联动

Vue 项目打包部署总结

Vue3.0 七大亮点是什么??

点个在看支持我吧,转发就更好了

3 个简单的技巧让你的 vue.js 代码更优雅!相关推荐

  1. js倒计时代码最简单的_10个简单的技巧让你的 vue.js 代码更优雅

    作为深度代码洁癖,我们都希望能写出简单高效的代码,让我们的代码看起来更加优雅,让我们抛弃繁杂的代码,一起开启简单的旅程~~ slots 新语法向 3.0 看齐 ❝使用带有"#"的新 ...

  2. python写出的程序如何给别人使用-涨姿势!这些小技巧让小白也可以写出更优雅的Python代码!...

    原标题:涨姿势!这些小技巧让小白也可以写出更优雅的Python代码! 一.前言 我前两天回答了两个Python相关的问题,收到了很多赞,从答案被收藏的情况来看,确实对不少人都很有帮助,所以我也很开心. ...

  3. filter vue 循环_详解在Vue.js编写更好的v-for循环的6种技巧

    在vuejs中,v-for循环是每个项目都会使用的东西,它允许您在模板代码中编写for循环. 在最基本的用法中,它们的用法如下. {{ product.name }} 但是,在本文中,我将介绍六种方法 ...

  4. web前端学习基础教程,简单的图片旋转木马自动轮播js代码

    一款简单的图片旋转木马自动轮播js代码,图片叠加轮播切换效果,支持点击左右箭头按钮控制切换. 案例效果图 案例源码: <!DOCTYPE html> <html lang=" ...

  5. 日历签到html,简单的手机移动端日历签到js代码

    特效详情: 一款简单的手机移动端日历签到js代码,挺不错的手机签到页面,可自定义已签到日期,连续几天签到代码. js代码 var isSign = false; var myday = new Arr ...

  6. 10个实用技巧让你的 Vue 代码更优雅

    点击上方[全栈开发者社区]→右上角[...]→[设为星标⭐] 作者:johnYu https://juejin.im/post/6854573215969181703 前言 作为深度代码洁癖,我们都希 ...

  7. ☆ 10个小技巧,让你的 Python 代码更加优雅~ ☆

    10个小技巧改进的 Python 代码,让你的代码更加简洁.更加 Python 化. 1. 用enumerate代替range 如果你需要遍历一个列表,并且需要同时获取索引和元素,大多数情况可能会使用 ...

  8. 这些小技巧,让你的前端编程更优雅

    > 这几年,很多工作 1-3 年的前端小伙伴,经常问我一个问题:"怎么样才算一个合格的前端程序员?" > > 这些小伙伴们,在日常工作中,也都能熟练使用各种框架. ...

  9. 一个简单的步骤让你的 Python 代码更干净

    说起来容易做起来难,我们都知道代码可读性非常重要,但是写的时候总是随心所欲,不考虑类型提示.import 排序.PEP8 规范.今天分享一个小技巧,通过一个简单的步骤就可以让你的 Python 代码更 ...

最新文章

  1. 关于Sqlite的一个demo
  2. Beta阶段事后分析
  3. Python脚本后台运行的五种方式
  4. Springboot中使用Google 的Kaptcha工具实现验证码校验
  5. 网站程序IIS布署问题备忘录
  6. 马云狂炸近百亿,你的借呗额度涨了吗?
  7. AIgorand区块链中VRF随机函数的应用
  8. 用c语言随机获区100个整数,用rand() 函数去100 个随机的整数的有关问题
  9. linux管理员清理主目录,在Linux上如何清理垃圾系统管理员
  10. 5在ios上无法选取文件_无法在 Ubuntu 20.04 上安装 Deb 文件?这是你需要做的! | Linux 中国...
  11. 爬虫之异步爬虫asyncio
  12. 群发邮件避免被识别为垃圾邮件
  13. linux系统如何修复分区工具,如何恢復LINUX的硬盘分区表
  14. 新浪微博批量取消关注
  15. 加拿大滑铁卢大学计算机世界排名,滑铁卢大学世界排名
  16. 柏拉图团队-手把手教你发代币(二)remix入门
  17. 什么是低代码-甲骨文对低代码的定义
  18. 华为鸿蒙系统应用开发工具介绍 DevEco Studio
  19. CC2 条理分明-----独立思考
  20. 2.12-3.20上周的习惯坚持下来了✌️精诚所至金石为开,加油兄弟

热门文章

  1. using b tree mysql_浅析MysQL B-Tree 索引
  2. 脑电数据的实验范式及EEGLAB分析预处理
  3. 树莓派开发笔记(九):基于CSI口的摄像头拍照程序(同样适用USB摄像头)
  4. 网站备案其实是服务器备案,国内服务器为什么需要备案?国外服务器备案吗?
  5. 基于SSM(Spring+SpringMVC+MyBatic)的停车场管理系统
  6. python学习笔记(一)数据处理
  7. 在计算机网络中ln代表的是,数学中e和ln的关系?
  8. 数据库笔记--常见sql操作
  9. 什么是 “奋斗逼”?
  10. 金额大小写转化、阿拉伯数字转大写数字,大写数字转阿拉伯数字