前端组件化:Vue组件设计思路与合规原则

模块组件设计

组件化的工作方式相信独立性、完整性和自由组合。目标是尽可能的将设计和开发中的元素分离,使其具有完整的局部功能,并通过自由组合构成整个产品。

从页面元素的复用性角度,我们将组件按照类型分为公共组件、容器组件和视图组件。

模块通常强调职责(分离、内聚),组件是可重用模块和相关依赖的包。

组件可以定义如下:

  • 可复用模块完成既定功能
  • 有明确的接口规定
  • 有上下文相关和外部相关的资源定义
  • 可以独立发布

为什么要组件化

组件化是实现的分层,是一种更有效的代码组合方式

组件化是对资源的重组和优化,使项目资源管理更加合理

组件化有利于单元测试

组件化对重构更友好

Vue组件库包含三种类型的组件(Component)、指令(Directive)和过滤器(Filter)。

当组件嵌套多层时,Modal 应该放在根组件中,然后由子组件中的事件触发。在具体的应用中,应该这样使用,这符合 Vue 提倡的“状态驱动”。

尽可能使用以下原则

  • 层次结构和 UML 类图
  • 扁平的、面向数据的状态/道具
  • 纯状态变化
  • 低耦合
  • 辅助代码分离
  • 提炼本质
  • 时间模块化
  • 集中/统一状态管理

单一职责组件拆分的目的:复用和隔离

隔离的类型,组件业务一定很繁重。这时候虽然组件要尽量简单,

复用型的通用性更强,所以功能单一,使用起来更方便。

注意:不要直接修改引用类型props的对象。虽然可以达到传输数据的目的,但是会有副作用。如果数据用于其他地方,可能会产生未知的影响。

Vue 组件之间的交互设计Vue 组件和 React 组件是完全不同的。模板的设计更倾向于 HTML,因此通常不太需要实现类似于 React 的高级组件。

高级组件的集成度太高。对于业务来说,当业务越来越复杂的时候,组件的内部逻辑就会难以拆分,这未必是好事,所以我们只讨论通用的组件设计。

组件设计就是考虑组件通信方式,主要分为以下几个方面:数据流(向下传值,上传值),伪双向绑定,方法调用。

简化和提取其他实现使用插件或mixins来实现

模块化封装(组件封装)思想

  1. 智能组件
  • 和一切数据打交道,发生各种请求。
  • 只接受父组件的参数。返回给父组件需要的值。
  1. 木偶组件
  • 不依赖父组件的实例,不受父组件影响(css)。
  • 接受父组件的一切,不返回任何值。
  • 渲染确定的结果。
    页面渲染通过智能组件。它们专门做数据相关的应用逻辑,和各种数据打交道、和 Ajax 打交道,然后把数据通过 props 传递给木偶组件,它们带领着 木偶组件组件完成了复杂的应用程序逻辑\

vue组件封装实例

需要对vue的指令有更生的理解:

extend:组件构造器;
directive:指令生成器;
slot:组件插槽;
style,class绑定;
复制代码

组件封装思想:model层,view层,control层

1. vue组件封装: message封装。

已经实现:自定义样式,自定义内容,以方法调用

model层实现

<template>
  <transition name="mei-message-fade">
    <div v-if="show" :class="[
        'mei-message',
        type? `mei-message-${ type }` : '']">
      <span class="mei-message-con">{{text}}</span>
    </div>
  </transition>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class MessageBox extends Vue {
  show: boolean = false;
  text: string = '';
  type: string = '';
}
</script>
<style>
  .mei-message {
  }

  .mei-message-success {
  }

  .mei-message-error {
  }

  .mei-message-warning {
  }

  .mei-message-icon {
  }

  .mei-message-con {
    line-height: 40px;
    height: 40px;
    display: inline-block;
    margin-left: 10px;
  }

  .mei-message-fade-enter-active {
    transition: all 0.3s linear;
  }

  .mei-message-fade-leave-active {
    transition: all 0.3s linear;
  }

  .mei-message-fade-enter, .mei-message-fade-leave-to
    /* .slide-fade-leave-active for below version 2.1.8 */
  {
    opacity: 0;
  }
</style>
复制代码

show: boolean = false; 控制组件的显示隐藏
text: string = ”; 组件的显示文字
type: string = ”;组件显示类型
这是个典型的木偶组件,依赖三个参数;它只负责页面的渲染;给什么渲染什么。

control层实现:

import Vue from 'vue';
import messageVue from '@/components/MessageBox.vue'; // 组件引入

interface Star {  ts接口声明
  show?: boolean;
  text?: string;
  duration?: string;
  type?: string;
}

export const messageBox = (options: Star) => {
  const defaults = {
    show: false,
    text: '',
    duration: '2000',
    type: ''
  };
  const messageVueConstructor = Vue.extend(messageVue);//  实现组件构造
  if (Vue.prototype.$isServer) {
    return;
  }
  options = Object.assign({}, defaults, options); // 配置参数
  const parent = document.body;
  const instance = new messageVueConstructor({ // 组件的实例
    el: document.createElement('div'),
    data: options
  });
  parent.appendChild(instance.$el);// 插入页面
  Vue.nextTick(() => {
    instance.show = true; // 修改显示和隐藏
    setTimeout(function () {
      // (<any>instance).show=false;
      instance.show = false;
    }, options.duration);
  });
  return instance;
};

export default {
  install: vue => {
    vue.prototype.$message = messageBox; // 将message组件暴露出去,并挂载在Vue的prototype上
  }
};
复制代码

首先需要我们引入组件,然后通过构造实例来形成组件,通过组件的实例来控制组件的显示和隐藏。
最后我们把实例的方法导出去;同时挂载导vue的原型;的在main.ts里面引入,通过use使用。这样我们就封装好来一个属于我们自己的$message

import message from './util/message';
Vue.use(message);
复制代码

最后我们通过vm.$message()就可以使用了;

view层实现

vm.$message({type:’success’,text:’xxx’,duration:3333})

2. vue指令封装 v-loading

可以实现:添加修饰符,样式修改,内容添加

model层

<template>
  <div v-show="visible" class="zh-loading-box" v-bind:class="{full:body}">
    <div class="flex-center">
      <div>
        <h1>加载</h1>
      </div>
      <p>{{ text }}</p>
    </div>
  </div>
</template>

<script lang='ts'>
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class Load extends Vue {
  text: string = '';
  body: boolean = true;
  visible: boolean = false;
}
</script>

<style scoped>
  .zh-loading-box {
    position: absolute;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.8);
    top: 0;
    bottom: 0;
    right: 0;
    left: 0;
  }
</style>
复制代码

这也是个木偶组件;对传入的参数进行显示;

control层

import Load from '@/components/Load.vue';

const toggleLoading = (el, binding) => {
  if (binding.modifiers.body) {
    el.instance.body = true;
  } else {
    el.instance.body = false;
  }
  if (binding.value) {
    el.instance.visible = true;
  } else {
    el.instance.visible = false;
  }
};

export default {
  install: vue => {
    vue.directive('loading', {
      bind: (el, binding) => {
        const defaults = {
          visible: false,
          body: false,
          text: el.getAttribute('loading-text')
        };
        const options = Object.assign({}, defaults);
        const LoadingCounstruct = vue.extend(Load);
        const loading = new LoadingCounstruct({
          el: document.createElement('div'),
          data: options
        });
        el.style.position = 'relative';
        el.appendChild(loading.$el);
        el.instance = loading; // el.instance是个Vue实例
        toggleLoading(el, binding);
      },
      update: (el, binding) => {
        // el.instance.setText(el.getAttribute('loading-text'));
        if (binding.oldValue !== binding.value) {
          toggleLoading(el, binding);
        }
      }
    });
  }
};
复制代码

指令的实现是通过 vue.directive来实现的

Vue.directive('my-directive', {
  bind: function () {},
  inserted: function () {},
  update: function () {},
  componentUpdated: function () {},
  unbind: function () {}
})
复制代码

这个是它的生命周期;钩子函数的参数 ( el、binding、vnode 和 oldVnode)。

bind只调用一次,指令第一次绑定到元素时调用。
update在指令的传入值更新的时候实现。

在bind的时候通过调用组件的实例让组件显示,同时获取绑定标签属性来设置显示的文字;和设置标签的样式让组件合理显示,在处理loading显示的时候通过获取修饰符binding.modifiers.body,来对显示元素实现不通的显示效果,

通过对update市设置,让loading隐藏

if (binding.value) {
el.instance.visible = true;
} else {
el.instance.visible = false;
}

最后export default 出去;
在main.ts里面

import loading from './util/loading';
Vue.use(loading);
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享