如何写好一个vue组件,老夫的一年经验全在这了

栏目: JavaScript · 发布时间: 6年前

内容简介:如果该组件有一个参数的时候,笔者习惯使用组件具有自身状态,当没有相关 porps 传入时,使用自身状态完成渲染和交互逻辑;当该组件被调用时,如果有相关 props 传入,那么将会交出控制权,由父组件控制其行为比如当一个组件有诸多配置项,且当没有传入配置项取用组件内部默认项的时候,我们原先的父组件写法:

如果该组件有一个参数的时候,笔者习惯使用 v-model 将该参数传入组件,减少记忆成本(毕竟 vue 官方的语法糖,不用白不用)

组件具有自身状态,当没有相关 porps 传入时,使用自身状态完成渲染和交互逻辑;当该组件被调用时,如果有相关 props 传入,那么将会交出控制权,由父组件控制其行为

<my-component v-model="text" />
复制代码

很多值需要传入

比如当一个组件有诸多配置项,且当没有传入配置项取用组件内部默认项的时候,我们原先的父组件写法:

<child-component :prop1="var1" :prop2="var2" :prop="var3" ... />
复制代码

并且要在子组件里面判断每一个传入的值是否为空,不为空替代默认的配置项

这种情况,不妨使用一个对象将配置收集到一起

<child-component v-model="text" :setting="{color:'bule'}" />

// 子组件内部读取配置,通过扩展运算符替换掉默认配置
const setting ={
  ...defaultSetting,
  ...this.setting
}
复制代码

computed 属性

vue 的 computed 属性默认是只读的,你可以提供一个 setter 。它可以优化我写组件的逻辑,适用于父组件处理的值和子组件处理的值是同一个的情况

<template>
  <el-select v-model="email">
    <el-option
      v-for="item in adminUserOptions"
      :key="item.email"
      :label="item.email"
      :value="item.email"
    />
  </el-select>
</template>
复制代码
export default {
  props: {
    value: {}
  },
  computed: {
    email: {
      get() {
        return this.value
      },
      set(val) {
        this.$emit('input', val)
        this.$emit('change', val)
      }
    }
  }
}
复制代码

灵活的 prop

我们常看到一些优秀的组件库,传入的值既可以是一个 String/Number,也可以是一个函数。

比如 ElementUITable 组件,当你想要显示树形数据的时候,必须传入 row-key 。看它的介绍就知道是有多灵活:

row-key 的作用:行数据的 Key ,用来优化 Table 的渲染;在使用 reserve-selection 功能与显示树形数据时,该属性是必填的。类型为 String 时,支持多层访问: user.info.id ,但不支持 user.info[0].id,此种情况请使用 Function

处理 rowKey 生成 RowIdentity 的函数源码:

//https://github.com/ElemeFE/element/blob/dev/packages/table/src/util.js
export const getRowIdentity = (row, rowKey) => {
  if (!row) throw new Error('row is required when get row identity')
  // 行数据的key
  if (typeof rowKey === 'string') {
    if (rowKey.indexOf('.') < 0) {
      return row[rowKey]
    }
    // 支持多层访问:user.info.id
    let key = rowKey.split('.')
    let current = row
    for (let i = 0; i < key.length; i++) {
      current = current[key[i]]
    }
    return current
    // 通过函数自定义
    // 我处理过父和子id可能相同的情况,只好通过Function自定义
    // 不可以通过时间或者随机字符串生成ID
  } else if (typeof rowKey === 'function') {
    return rowKey.call(null, row)
  }
}
复制代码

组件的设计者很难考虑完全,不妨设计灵活的 prop,由开发者自行定义

事件

emit/on

读者肯定知道 emit/on 如何使用,我就简单说一下 vue 的 v-modelsync 的语法糖,我们可以利用这些语法糖,帮助我们写出简洁的代码(父组件可以少写监听子组件的事件,比如你不用写@input)

v-model

看一下下面的代码示例,就能懂这句话了。v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值

<input v-model="searchText" />

<input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value"
/>

// 当把v-model用在组件上

<custom-input
  v-bind:value="searchText"
  v-on:input="searchText = $event"
></custom-input>
复制代码

为了让它正常工作,这个组件内的 必须:将其 value 特性绑定到一个名叫 value 的 prop 上在其 input 事件被触发时,将新的值通过自定义的 input 事件抛出,即 this.$emit('input',changedValue)

自定义 v-model

为啥要自定义组件的 v-model 呢,因为数据不符合要求呗。你的输入值不可能总是 value ,你的事件不可能总是 input,具体详见文档

sync(双向绑定语法糖)

vue 真的是方便了开发者很多,站在开发者的角度考虑,可以很大的提升开发效率

以  update:myPropName  的模式触发事件取代双向绑定 this.$emit('update:title', newTitle) ,具体详见文档

Function 通过 prop 传入

本来想放在 prop 部分的,但是个人觉得其实它和 emit/on 更有关系一点

有读者可能会问,为什么不能把子组件里面的事件 emit 出来,通过父组件处理?然后传入一个控制子组件的 prop 属性。

我想说的是,可以,但是这样真的很麻烦,子组件内部的状态却要依赖父组件传值。

该组件内部的状态,我们需要把它暴露出来嘛?我觉得不需要,组件内部的状态就让它处于组件内部

但是可以通过传入 function(你可以理解为一个钩子),参与组件状态变更的行为。比如很好用的拖拽库, Vue.Draggable 控制元素是否被拖动的行为。

Vue.Draggable 可以传入一个 move 方法,我们看一下它如何处理的。

onDragMove(evt, originalEvent) {
      const onMove = this.move;
      // 如果没有传入move,那么返回true,可以移动
      if (!onMove || !this.realList) {
        return true;
      }

      const relatedContext = this.getRelatedContextFromMoveEvent(evt);
      const draggedContext = this.context;
      const futureIndex = this.computeFutureIndex(relatedContext, evt);
      Object.assign(draggedContext, { futureIndex });
      const sendEvt = Object.assign({}, evt, {
        relatedContext,
        draggedContext
      });
      // 组件行为由传入的move函数控制
      return onMove(sendEvt, originalEvent);
}
复制代码

这样做的好处,就是组件内部自由一套运行逻辑,但是我可以通过传入 function 来干预。我没有直接修改组件内部状态,而是通过函数(你可以称它为钩子)去触发,方便调试组件,使得组件行为具有可预测性

父组件直接操作子组件

很少有这样的骚操作,但是由于数据和操作的复杂性,当数据结构复杂,嵌套过深的情况下,父组件很难对于子组件的数据的精细控制

因此,如果不得已而为之,请在文档里,把子组件可以调用的方法暴露出来,供使用者使用。使用这种组件比较麻烦,得去看文档,没有文档的只好去看源码

ElementUI tree 组件 提供了很多方法,用于父组件去操作子组件。

eg: this.$refs.tree.setCheckedKeys([]);

插槽

能用默认插槽就不要使用具名插槽(我真的不想使用你这个组件的时候还去翻看你的插槽叫什么名字)

之前我司一个网页模板 三个插槽,header,body,footer,我用的是真的难受,每次都记不得,看似三个单词都挺熟悉的,但是其实 head,content,foot 这些单词也都行啊,谁知道用啥(可能我老了吧,组件如果不是必要尽量不要让人有记忆成本)。

组件命名

这里推荐遵循vue 官方指南,值得一看

我们构建组件的时候通常会将其入口命名为 index.vue ,引入的时候,直接引入该组件的文件夹即可。

但是这样做会有一个问题,当你编辑多个组件的时候,所有的组件入口都叫做 index.vue ,容易糊涂

vscode 显然意识到了这个问题,所以当文件名相同的文件被打开时,它会在文件名旁边显示文件夹名

如何解决呢,我们可以把 index.js 当作一个单纯的入口,不承担任何逻辑。仅仅负责引入 component-name-container 以及 export default component-name-container

my-app
└── src
        └── components
                └── component-name
                    ├── component-name.css
                    ├── component-name-container.vue
                    └── index.js
复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Web Applications (Hacking Exposed)

Web Applications (Hacking Exposed)

Joel Scambray、Mike Shema / McGraw-Hill Osborne Media / 2002-06-19 / USD 49.99

Get in-depth coverage of Web application platforms and their vulnerabilities, presented the same popular format as the international bestseller, Hacking Exposed. Covering hacking scenarios across diff......一起来看看 《Web Applications (Hacking Exposed)》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具