2020你应该有一个自己的组件

自作多情 提交于 2021-02-16 06:03:50

公众号:前端微服务
GitHub:https://github.com/yongbolu
作  者:子奕

一、概述

组件化是长期开发过程中一个提炼精华的过程,目的主要是提高复用性、解耦、提升开发效率。本次主要以Vue.js为例来讲解前端组件开发的注意事项,并且带领大家封装自己的组件并发布到npm。

二、环境

  • window10 x64
  • node v10.16.3
  • npm v6.13.4
  • yarn

三、什么叫做组件化

Vue.js 有两大特性,一个是数据驱动,另一个就是组件化。接下来我就针对这两个问题一一解答,所谓组件化,就是把页面拆分成多个组件,每个组件依赖的 CSS、JS、模板、图片等资源放在一起开发和维护。 因为组件是资源独立的,所以组件在系统内部可复用,组件和组件之间可以嵌套,如果项目比较复杂,可以极大简化代码量,并且对后期的需求变更和维护也更加友好。

四、如何进行组件化开发

Vue.js 提供了 2 种组件的注册方式,全局注册和局部注册。

4.1 全局注册

在vue.js中我们可以使用Vue.component(tagName,options)进行全局注册。 例如:

Vue.component('my-component', {
// 选项
})

4.2 局部注册

Vue.js 也同样支持局部注册,我们可以在一个组件内部使用 components 选项做组件的局部注册。 例如:

import HelloWorld from './components/HelloWorld'
export default {
components: {
HelloWorld
}
}

区别:全局组件是挂载在 Vue.options.components 下,而局部组件是挂载在 vm.$options.components 下,这也是全局注册的组件能被任意使用的原因。

五、组件开发的要素

  • 组件的名称
  • 控制参数
  • 自定义内容
  • 自定义事件

六、封装一个Vue组件

6.1 开发vue组件要素

6.6.1 组件的名称name(必填)

js 中使用驼峰式命令,HTML 使用kebab-case命名。


<my-component/>
<my-component></my-component>

……
<script>
export default {
name: 'MyComponent',
props: {
prefixCls: {
type: String,
default: 'ant-editor-wang'
},
// eslint-disable-next-line
value: {
type: String
}
}
……
</script>

6.6.2 props参数

组件属性,用于父子组件通信,可通过this.msg访问。

父对子传参,就需要用到 props,通常的 props 是这样的:

props:['data','type']
// show: Boolean // 默认false //msg: [String, Boolean]  // 多种类型

但是通用组件的的应用场景比较复杂,对 props 传递的参数应该添加一些验证规则,常用格式如下:

props: {
// 基础类型检测 (`null` 意思是任何类型都可以)
propA: Number,
// 多种类型
propB: [String, Number],
// 必传且是字符串
propC: {
type: String,
required: true
},
// 数字,有默认值
propD: {
type: Number,
default: 100
},
// 数组/对象的默认值应当由一个工厂函数返回
propE: {
type: Object,
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
return value > 10
}
}
}

对于通过 props 传入的参数,不建议对其进行操作,因为会同时修改父组件里面的数据 // vue2.5已经针对 props 做出优化,这个问题已经不存在了 如果一定需要有这样的操作,可以这么写:

let copyData = JSON.parse(JSON.stringify(this.data))

6.6.3 computed:

处理data或者props中的属性,并返回一个新属性。(注:因为props,data和computed在编译阶段都会作为vm的属性合并,所以不可重名)

<template>
<div>{{newMsg}}</div>
</template>
<script>
export default {
computed: {
newMsg() {
return '前端微服务 ' + this.msg
}
},
……
}
</script>

6.6.4 render

用render函数描述template。

<my-component tag='button'>前端微服务</my-component>

<script type="text/javascript">
export default {
name: 'MyComponent',
props: {
tag: {
type: String,
default: 'div'
},
},
// h: createElement
render(h) {
return h(this.tag,
{class: 'demo'},
this.$slots.default)
}
}
</script>

注意: render 中的 h 其实就是 createElement,它接受三个参数,返回一个 vnode
h 参数解释:

  1. args1: stringFunction | Object用于提供DOM的html内容
  2. args2: {Object} 设置DOM样式、属性、绑定事件之类
  3. args3: {array} 用于设置分发的内容

注:vue编译顺序: template–> compile --> render --> vnode --> patch --> DOM

6.6.5 slot定制插槽

分发内容,有传入时显示,无传入 DOM 时显示默认,分为无名和具名两种,this.slots.default默认指向无名插槽,多个slot时用法this.slots.name,一个通用组件,往往不能够完美的适应所有应用场景 所以在封装组件的时候,只需要完成组件 80% 的功能,剩下的 20% 让父组件通过 solt 解决。

// 子组件
<div class="child-btn">
<!-- 具名插槽 -->
<slot name="button"></slot>
<!-- 匿名插槽(每个组件只能有一个) -->
<slot><slot>
</div>

// 父组件
<child>
<!-- 对应子组件中button的插槽 -->
<button slot="button">slot按钮</button>
</child>

组件封装

<my-component>
<div slot='header'>header</div>
<div class="body" slot='body'>
<input type="text">
</div>
<div slot='footer'>footer</div>

<button class='btn'>button</button>
</my-component>

<template>
<div>
<slot name='header'></slot>
<slot name='body'></slot>
<slot name='footer'></slot>
<slot></slot>
</div>
</template>

<script>
export default {
name: 'MyComponent',
mounted() {
this.$slots.header // 包含了slot="foo"的内容
this.$slots.default // 得到一个vnode,没有被包含在具名插槽中的节点,这里是button
}
}
</script>

6.6.6  定义子组件的类名class

// 父组件
<my-component round type='big'/>

// 子组件
<div :class="[
type ? 'my-component__' + type : '',
{'is-round': round},
]">控制</div>

//真实DOM
<div class='my-component__big is-round'>hello</div>

6.6.7 向子组件传递样式 style

// 父组件
<my-component :bodyStyle='{color: "red"}'/>


// 子组件
<template>
<div :style='bodyStyle'>hello world</div>
</template>

<script>
export default {
name: 'MyComponent',
props: {
bodyStyle: {},
},
}
</script>

6.2 其他属性

6.2.1 $attrs

v-bind="$attrs" 将除class和style外的属性添加到父组件上,如定义input:

<input v-bind="$attrs">

6.2.2 v-once

组件只渲染一次,后面即使数据发生变化也不会重新渲染。比如例子中val不会变成2020

<template>
<div>
<button @click="show = !show">button</button>
<button @click="val = '2020'">button</button>
<div v-once v-if="show">
<span>{{val}}</span>
</div>
</div>
</template>

<script>
export default {
data() {
return{
show: false,
val: '123'
}
},
};
</script>

6.2.3 mixins

// mixin.js
export default {
data() {
return{
msg: 'hello world'
}
},
methods: {
clickBtn() {
console.log(this.msg)
}
},
}

// index.vue
<button @click="clickBtn">button</button>

import actionMixin from "./mixin.js";
export default {
methods: {},
mixins: [actionMixin]
}

6.3  封装button组件

index.vue

<template>
<button>MyButton</button>
</template>

<script>
export default {
name: 'MyButton'
}
</script>

index.js

import MyButton from './src/index'

MyButton.install = (Vue) => {
Vue.component(MyButton.name, MyButton)
}

export default MyButton

其中 install 是 Vue.js 提供了一个公开方法,这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象。MyPlugin.install = function (Vue, options){}

七、发布到npm

7.1 登陆npm

$ npm login

7.2 发布

$ npm publish

八 、关注我们


本文分享自微信公众号 - 前端微服务(micro_frontend)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!