关于Vue代码可读性的几点建议

目录
  • 一、善用组件让代码更有条理性
    • 1、 提取UI组件
    • 2、按模块提取业务组件
    • 3、按功能提取功能组件
  • 二、利用v-bind使组件的属性更具有可读性
  • 三、利用attrs与attrs与listeners来封装第三方组件
    • 1、$attrs
    • 2、$listeners

前言:

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

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

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

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

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

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组件

2、按模块提取业务组件

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

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与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" 传入内部组件

v<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组件自定义的事件。

到此这篇关于关于Vue代码可读性的几点建议的文章就介绍到这了,更多相关Vue代码可读性内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2021-09-13

五分钟教你使用vue-cli3创建项目(新手入门)

目录 一.搭建vue环境 二.Vue脚手架工具 三.创建项目 四.选择manually select (enter键确认,并进入下一步) 五.选择完成之后回车 这里我们选择3.x的 六.完成之后回车 出现以下界面 七.回车 出现以下界面 八.回车出现以下界面 九.回车出现以下界面 十.回车出现以下界面 十一.回车出现以下界面 十二.根据提示,启动项目 一.搭建vue环境 安装Nodejs 官网下载Nodejs,如果希望稳定的开发环境则下LTS(Long Time Support)长期支持版,稳定

Vue.js 实现tab切换并变色操作讲解

在实现这个功能时借鉴的原博主的方法没有实现切换变色,琢磨了好久终于知道了怎么切换变色(小菜鸟的咆哮)!!!记录下来以供参考,以下是vue的完整tab页切换并变色的代码框架. ​<template> <div > //tab页切换按钮部分 <ul > <li v-for="(item,index) in navList" :class = "{active:!(index- menuIndex)}" @click = 'men

Vue组件通信方法案例总结

目录 一.父组件向子组件传值(props) 二.子组件向父组件传值($emit) 三.兄弟组件传值(EventBus) 1. 初始化(new Vue()) 2. 发送事件($emit()) 3. 接收事件($on()) 4. 移除事件监听者 四.Vuex 一.父组件向子组件传值(props) 步骤: 在父组件中通过 v-bind 将数据传给子组件 在子组件中通过 props 接收父组件传递过来的数据 <div id="app"> <!-- 1.通过 v-bind 将数

Vue.js slot插槽的作用域插槽用法详解

目录 没有插槽的情况 Vue2.x 插槽 有插槽的情况 具名插槽 没有slot属性 插槽简单实例应用 作用域插槽 ( 2.1.0 新增 ) Vue3.x 插槽 插槽 作用域插槽 没有插槽的情况 <div id="app"> <child> <span>1111</span> </child> </div> <script> // 注册子组件 Vue.component("child"

vue项目中less的一些使用小技巧

目录 前言 一.样式穿透 1.  什么是样式穿透? 2.  如何使用? 二.混入 1.  什么是混入? 2.  如何使用? 三. less自动化导入 1. 自动化导入好处 2.  如何实现? 总结 前言 我们所能看到的美观的网页都是经过UI精心设计后,由前端攻城狮搭建的.网页想要有炫酷的样式,就需要用到css来处理,其中不乏会出现大量重复.冗余的代码,这时,像less.sass.scss等样式预处理器就出现了,极大地精简了css代码,提高了开发效率.今天跟着本文一起看看在vue项目中使用less

如何在Vue项目中应用TypeScript类

目录 一.前言 二.使用 1.@Component 2.compued.data.methods 3.@props 4.@watch 5.@emit 三 .总结 一.前言 TypeScript是基于vue-class-component库而来,这个库vue官方推出的一个支持使用class方式来开发vue单文件组件的库 主要的功能如下: methods 可以直接声明为类的成员方法 计算属性可以被声明为类的属性访问器 初始化的 data 可以被声明为类属性 data.render 以及所有的 Vue

vue中点击切换按钮功能之点启用后按钮变为禁用

实现方法分位三步: 在template中设置2个按钮,通过v-if ,v-show来控制: data中设置按钮的默认值: methods中控制点击按钮切换效果. <template> <el-table :data="tableData" border style="width: 100%"> <el-table-column fixed prop="date" label="日期" width=

用Vue封装导航栏组件

前言:把一个功能模块使用组件化的思想充分封装,如导航栏,这无论对我们的开发思想还是效率都有许多好处,在开发中,我们要尽量多得运用组件化的开发思想,不要把所有代码都写在同一个.vue文件中,这样能大大提高代码的可读性. 封装导航栏 主要思路:把红色的部分当成一个个组件,而他们只是图片和文字不同,所以我们可以把他们封装成同一个组件,然后向组件里传入图片信息和文字信息即可(可以用插槽). //TabBarItem.vue <template> <div class="tabBarIt

Vuex总体案例详解

目录 一.简介 二.优点 三.使用步骤 1. 安装Vuex 2. 引用Vuex 3. 创建仓库Store 四.包含模块 1. State 2. Getters 3. Mutations 4. Action 5. Modules 五.Vuex最最简单的项目实例 1. 存储数据 2. 获取数据 3. store文件目录结构 index.js state.js mutations.js actions.js getters.js 4. 使用store 一.简介 我们来看看对 Vuex 比较专业的介绍:

AngularJS日程表案例详解

功能:添加事件/完成事件/删除事件 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> *{ margin: 0; padding: 0; } .note{ margin:0 auto; background: orange; color: ora

BootStrap的JS插件之轮播效果案例详解

Bootstrap 是一个用于快速开发 Web 应用程序和网站的前端框架.Bootstrap 是基于 HTML.CSS.JAVASCRIPT 的. 案例 下面展示的就是此插件和相关组件制作的轮播案例. <div id="carousel-example-generic" class="carousel slide" data-ride="carousel"> <!-- Indicators --> <ol class

Vue 过渡(动画)transition组件案例详解

Vue过度(动画),本质走的是CSS3:transtion,animation. 控制器div显示/隐藏,代码如下: <div id="box"> <input type="button" value="按钮" @click="toggle"> <div id="div1" v-show="isShow"></div> </div&g

vue.js+boostrap项目实践(案例详解)

一.为什么要写这篇文章 最近忙里偷闲学了一下vue.js,同时也复习了一下boostrap,发现这两种东西如果同时运用到一起,可以发挥很强大的作用,boostrap优雅的样式和丰富的组件使得页面开发变得更美观和更容易,同时vue.js又是可以绑定model和view(这个相当于MVC中的,M和V之间的关系),使得对数据变换的操作变得更加的简易,简化了很多的逻辑代码. 二.学习这篇文章需要具备的知识 1.需要有vue.js的知识 2.需要有一定的HTML.CSS.JavaScript的基础知识 3

Apache 文件上传与文件下载案例详解

写一个Apache文件上传与文件下载的案例:以供今后学习 web.xml配置如下: <span style="font-family:SimSun;font-size:14px;"><?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns=&

jQuery 跨域访问解决原理案例详解

浏览器端跨域访问一直是个问题,多数研发人员对待js的态度都是好了伤疤忘了疼,所以病发的时候,时不时地都要疼上一疼.记得很久以前使用iframe 加script domain 声明.yahoo js util 的方式解决二级域名跨域访问的问题. 时间过得好快,又被拉回js战场时, 跨域问题这个伤疤又开疼了.好在,有jQuery帮忙,跨域问题似乎没那么难缠了.这次也借此机会对跨域问题来给刨根问底,结合实际的开发项目,查阅了相关资料,算是解决了跨域问题...有必要记下来备忘, 跨域的安全限制都是指浏览

Android开发之对话框案例详解(五种对话框)

下面通过实例代码给大家分享5种android对话框,具体内容详情如下所示: 1 弹出普通对话框 --- 系统更新 2 自定义对话框-- 用户登录 3 时间选择对话框 -- 时间对话框 4 进度条对话框 -- 信息加载.. 5 popuWindow对话框 1 弹出普通对话框 --- 系统更新  //弹出普通对话框 public void showNormalDialog(View v) { AlertDialog.Builder builder = new Builder(this); //设置D

MongoDB模糊查询操作案例详解(类关系型数据库的 like 和 not like)

1.作用与语法描述 作用: 正则表达式是使用指定字符串来描述.匹配一系列符合某个句法规则的字符串.许多程序设计语言都支持利用正则表达式进行字符串操作.MongoDB 使用 $regex 操作符来设置匹配字符串的正则表达式. 语法一 { <field>: { $regex: /pattern/, $options: '<options>' } } { <field>: { $regex: 'pattern', $options: '<options>' } }

spring boot 结合jsp案例详解

这篇文章主要介绍了spring boot 结合jsp案例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- jstl是⼀

我用Python抓取了7000 多本电子书案例详解

安装 安装很简单,只要执行: pip install requests-html 就可以了. 分析页面结构 通过浏览器审查元素可以发现这个电子书网站是用 WordPress 搭建的,首页列表元素很简单,很规整 所以我们可以查找 .entry-title > a 获取所有图书详情页的链接,接着我们进入详情页,来寻找下载链接,由下图 可以发现 .download-links > a 里的链接就是该书的下载链接,回到列表页可以发现该站一共 700 多页,由此我们便可以循环列表获取所有的下载链接. R