[Vue]组件编写小结

前言

随着js编程进入工程化纪元,代码模块化,组件化,成为工程的具体落地方法。javascript

最近使用vue全家桶作了一个相似于iconfont的网站,在作的过程当中关于组件有了一些思考和总结,为了巩固,写个小结。css

一 组件类型

根据我已知的组件的编写方式,有四种介绍给你们:html

1.1 基本组件

最基本的组件由一个.vue文件构成,其中包含了基本的tempplate,script, style三大元素。前端

能够经过props接收参数,$emit事件bus向父传递参数。vue

比较懒得我通常会在dev阶段在components文件夹里放置一个Demo.vue。写好基本的元素以及组件注释。java

import后在组件的compontents中声明就能够在模板里标签调用了。git

<template>
  <!-- 指南中推荐组件名应该始终为多个单词的,根组件App,<transition>、<component>之类除外 -->
  <Header></Header>
  <shop-cart></shop-cart>
</template>
components: { Header, ShopCart }复制代码

这里比较注意的是组件命名规范应该严格参考vue风格指南。让驼峰和短横线在该使用的地方使用。github

1.2 构造组件

构造类组件主要是用在想要经过js动态调用组件的场景里,由一个该组件文件夹好比notice,中包含Notice.vue, index.js两个文件组成。vuex

vue文件就是编写基本的vue文件编程

index.js中

  • 使用vue的extend方法来返回一个Notice.vue的构造器对象,在对象原型上添加一些经常使用方法好比close
  • 编写Notice函数对象,在函数中将构造器对象进行实例化(最基本的:创造一个div元素挂在el属性上,你也能够根据场景需求为其配置上store,router,经过.$store获取)
  • 将该实例的el属性上的元素append到body(或者你想要的添加的元素)中
  • 将函数传来的参数数据处理后对实例中数据进行赋值(等于props传值,同时此处至关于作了每次打开组件时的周期。)
  • 将这个函数对象做为default返回

/** 项目添加模态框 */
import Vue from 'vue'
import proModal from './ProModal.vue'
import store from '@/store/index'
import router from '@/router/index'
let proModalInstance
export default {
  open (modalType, packageInfoDatail) {
    if (!proModalInstance) {
      proModalInstance = new ProModalCreater({
        el: document.createElement('div'),
        store: store,
        router: router
      })
      document.body.appendChild(proModalInstance.$el)
    }
    proModalInstance.$data.modalType = modalType
    proModalInstance.$data.options4 = []
    proModalInstance.visible = true
    modalType === 'modify'
      ? (() => {
        proModalInstance.$data.packageInfoDetailTemp = packageInfoDatail
        proModalInstance.$data.headerName = '修改项目'
        proModalInstance.$data.value9 = []
        packageInfoDatail.coUsers.map((item) => {
          proModalInstance.$data.value9.push(item)
          proModalInstance.$data.options4.push(item)
        })
      })()
      : proModalInstance.init()
    if (modalType === 'create' && packageInfoDatail) {
      proModalInstance.fromPath = packageInfoDatail
    }
    // proModalInstance.$data.callback = callback
  }
}复制代码

调用:

  • 能够经过在main.js中调用而后将其挂载在Vue原型上再去组件this中调用方式调用

Vue.prototype._message = obj => {
  obj.duration = messageDurationTime
  ElementUI.Message(obj)
}

this._message.info('why so serious?')复制代码

  • 也能够在不一样组件中引入而后再去调用

import ProModal from '@/components/common/proModal'
methods: {
  createPro () {
    // 打开模态框    
    ProModal.open('create')  
  },
}复制代码

1.3 全局组件

全局组件主要是用在一些通用组件想要在各处组件里使用标签调用的场景,组成与构建组件相同。

vue文件就是编写基本的vue文件(该注意的点仍是多注意下,全局对通用,颗粒的要求更高一些

index.js中

  • 引入组件ButtonComponent而且编写一个组件对象用于导出,该组件对象中有一个install方法
  • install方法中核心:Vue.component("Button", ButtonComponent);
  • 返回该组件对象

import NoContentComponent from './NoContent.vue'
// 添加install方法 (插件方法)
const NoContent = {
  install: function (Vue) {
    Vue.component('NoContent', NoContentComponent)
  }
}
// 导出Buttonexport 
default NoContent复制代码

在全局main.js中引入组件对象使用Vue.use()注册该组件

Vue.use(NoContent)复制代码

在各个组件中经过标签直接调用

<no-content v-if="!Array.isArray(iconLibNowDetail.icons)">
  <template slot="default"> <span>尚未上传图标哦</span> <upload-btn :toPath="'/upload?type=iconLib&id=' + libId"></upload-btn> </template> </no-content>复制代码

1.4 指令类组件

指令类主要是使用Vue.directive方法注册一个指令,在指令中bind的方法中去实例组件并添加到响应dom处。这里很少说,由于我没用到。(想看怎么用的去看参考Vue组件的三种调用方式

其结构相似于全局组件,只是调用是经过在某个元素上的指令属性进行调用。

二 组件设计原则

好了,知道怎么写一个基本的组件了,路程刚刚开始,那么如何写好一个组件呢。若是你写的组件是为了目的而写不考虑设计原则,相信很快就会被你本身所抛弃,而后重构组件。(小伙伴说我这一节写的步骤有些乱不太好,后面会改进)

先说下个人组件设计思路:

  1. 思考组件类型:是基础组件,仍是业务组件
  2. 思考组件核心:该组件核心是什么,核心表明着极低的变更,甚至不变
  3. 思考组件颗粒度(复用性): 该组件是基础组件会被多处复用 ?设计时尽可能最小化,减小ui数据绑定使其灵活轻巧 : 业务组件则能够适当ui和数据绑定,只经过一部分传参进行update
  4. 思考组件插槽:插槽的存放位置,后备内容,数据等
  5. 思考组件数据传递(可配置性): 是否须要数据传递 ?是否会深层传参 ?采用vuex :注意props数据不要被组件内直接改变,$emit向上传递数据 :无数据传递,仅做为ui层复用
    组件通讯这里能够再更多考虑,局部组件处嵌套通讯是否可使用bus来通讯,深层传参是否使用inject来传入,包括是否使用this.$refs.parent.xxData来获取数据。这些都须要去考虑。
  6. 思考组件数据健康(内存管理,watcher下尽可能扁平数据存放,动态请求刷新)
  7. 思考组件性能(优化,提取)

三 复杂组件设计

这一节起的有点节题党,这里只是分享一点点点总结,不敢谈设计,后续有好的总结会更新上来,大佬们轻拍哈哈

组件在一些场景中会变得复杂,其复杂性分为组件自己的复杂和被应用场景的复杂。

自己比较复杂的组件如dataTimerPicker,Form之类的拥有复杂的配置项属性以及methods,events,而且其中会包含子组件,子组件的slot,attributes,methods也会不少。

这里仅仅分享三个场景:

1. 我在作一个IconList,当时想要一次性在组件中所有作好,就不想再多抽出子组件,结果发现会写的很冗余。因而很快就抽出了icon组件,IconList接受icons数组数据,在v-for中将item对象再传入icon组件,icon中经过props拿到iconData,最终用来渲染出最终显示的icon。

固然还能够再对icon进行子组件抽取的,我目前仍是采用配置参数iconModalType方式+v-if来区分项目中和图标库内在不一样权限下,三种modal层的显示。后面能够继续使用mixIn(IconModal.vue中使用)+调用时<icon-modal :modalType="icon.modalType">方式来加载。

组合大于继承,不用一次性去写一个庞然大物,这样会在复用的时候束手束脚失去灵活性。

2. 我在作一个FilterGroup,肯定了其核心是InputSearch结果到了后面发现仍是会遇到核心不肯定场景。因而这个时候FilterGroup只为ui服务,子元素能够经过slot来加载,slot中经过name将子组件放置到对应位置。下面是个demo,我当时就将InputSearch和Sort只做为了FilterGroup中的元素来处理了,致使了后续须要经过很长的配置项在group上去肯定groupElement上的属性,使Group自己属性被混淆减弱了自身含义,增长了复用时理解难度。


<!-- group合理设计应该为使用slot插入须要的groupElement而不是直接去集成div,即便是很是固有化的元素,如其中的两个元素:排序和搜索元素 -->
<btn-group inputPlaceVal="请搜索图标" :useSort="true" @sort="proSort" :sortOptions="options" @search="searchIcon" >
   <template slot="body">
       <div class="radius-btn" v-if="_isProManager() >= 2" @click="createPro">
           <img :src="iconAddLib" alt="">
       </div>
   </template>
</btn-group>复制代码


正确的是应该去考衡抽出的必要性,假如该元素不复杂不须要传递数据自身无复杂方法,固定为ui项,就能够经过配置属性来取决其是否显示,好比element的input的icon,使用prefix-iconsuffix-icon 属性在 input 组件首部和尾部增长显示图标,固然在句尾说清了:也能够经过 slot 来放置图标 。由于slot伴随着子组件的编写,也是有代价的,因此对于一些默认最小项,我的认为能够内置编写好,经过配置属性来显示。

固然,能够看到,slot对于一个组件是多么的重要,一个复杂的子组件,一定不是一次性在组件内写好的,而是通过提取优化思考获得的一个组合结果,而且经得起复用和灵活扩展。

3. 我在作一个BatchOperate,批量操做组件时发现本身须要经过得到兄弟组件iconlist中被批量选择的icon的id数据,而且将这些icon加入ShopCart,这个时候对于数据以及状态的考量就来了。

最后选用了vuex加LocalStorage方式来解决了这个数据通讯的问题。(有点晚了,先不细说...)

这几个场景事最想表达的是:

在模块化思想的今天,这种抽出组合的思惟是很灵活的,能够在写组件的时候多去思考下模块化,包括公共方法,公共样式,vue的mixIn,scss的mixIn,公共变量仓库store集中处理,组件最下化,让重复性代码模块化抽出,再经过配置灵活组合。这种作法可让代码在后续的维护中很是清爽,一键修改专制各类公共改动,不再用跑到各个地方去作一下下的修改,而且避免了耦合带来的深层代码缠绕,能够说真的很好用了。

今天先写到这,第三节总结的不是很好,只是先罗列出本身以前想到的一些问题和处理,具体的优化想法会和组内小伙伴讨论后再更新的。



参考

Vue组件实现tips的总结

[译] 前端组件设计原则

前端工程化、模块化、组件化看法

Vue组件的三种调用方式

vue 组件的三种使用方式教程

element

相关文章
相关标签/搜索