如何写好一个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,也可以是一个函数。
比如 ElementUI
的 Table
组件,当你想要显示树形数据的时候,必须传入 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-model
和 sync
的语法糖,我们可以利用这些语法糖,帮助我们写出简洁的代码(父组件可以少写监听子组件的事件,比如你不用写@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
复制代码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- vue组件通信--注意事项及经验总结
- 【开发经验】Flutter组件的事件传递与数据控制
- 前端手札——vue组件vue-tinymce开发经验分享
- 如何设计一个vue组件,老夫的一年经验全在这了
- SaaS管理系统开发经验------Dva(Redux)实战经验分享
- 20年程序员分享经验:20条编程经验,一定要看完
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
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)》 这本书的介绍吧!