preface

Components are an integral part of the page, and designing components is the front-end student’s daily job.

so

A programmer’s career lasts about ten years, a tenth of a human lifespan. The front-end project is just a part of your life and work, but you are all of it. You are his soul. Stop playing games for long hours and playing games at work. Study more to accompany your project in the most perfect state!

The body of the

This article will think about the packaging design of components from the perspective of my cognition. If you have my opinion, way, or you have a better way, better design mode, you can discuss and think together in the comments section, communication is the only way to progress.

knowledge

  • How are components classified
  • Vue and React encapsulate the component pattern
  • What makes a good extensible, generic, robust component
  • Think and discuss and ask questions

How are components classified

  • The business component
  • Generic components (non-business components)
    • UI components

Whether it is a business component or a generic component, it has the three qualities of component nature: extensible, generic, and robust

  • Expansibility: on the basis of the original components can be twice packaged into new components in line with the design of open and close principle

  • Commonality: The commonality of a component is measured according to the parameters accepted by the component and the decoupling ratio between the component and the business. The component with a commonality ratio of 100% is not the best component, which needs to be analyzed according to different scenarios

  • Robustness: to avoid crashes and errors in component parameter processing and function execution that may lead to the program directly hanging up, single test is measured by doing a good job of boundary handling inside the component, and catching exceptions and errors

The business component

The components of service and business are called business components, and the components in the project are divided into paging level components and global level components

--- componentes --- pagesCopy the code

And the structure would look something like this

Components stored in componentes often have the current project in multiple scenarios reuse will be designed and encapsulated

Components in Vue

<template>
    ....
</template>
<script>
export default {
  props: {... }, data () { .... },methods: {... }}</script>
Copy the code

Components in React

import React, { Component } from 'react';
export default class Demo extends Component {
    state = {
    };

    componentDidMount(){... }render() {
      const{... } =this.props;
      return (
        <div>.</div>); }}Copy the code

This is currently the most basic component wrapper template for both frameworks.

Do you think about it when you package components

  • Maintainability of components?
  • Readability of components?
  • Scalability, robustness, versatility?
  • Does this component need to be packaged and removed?
  • Is the component strongly associated with the business?

Have you considered these questions before component encapsulation begins coding

As components continue to expand to improve their versatility, it will inevitably reduce the ease of use of components

The continuous enrichment of a component will also lead to its component code is too long, component mission is not a single, difficult to read, difficult to maintain

Like Vue and React, a component code length of 200-500 lines is recommended

Components in a business tend to differentiate

  • The container component is responsible for processingBusiness dependent logic, registering business dependent hooks, passing in the appropriate familiarity and slots, and so on
  • The view component is responsiblePresentation of data, implementation of interaction

Container components are often not reusable

View components decide whether or not to wrap based on their style and how often they interact with each other in the project

View and data decoupling and matching can greatly improve the readable and maintainability of components

Does this component need to be packaged and removed?

This may be a problem for new front-end students

Not all DOM structures need to be pulled out

You need to have an overall view of the UI direction of the project you’re working on, and if you’re not sure if you need to wrap it, don’t wrap it

The next time the business needs the same UI as the original view, encapsulate the design instead of fast Copy

Is the component strongly associated with the business?

Typically, a lot of data in a component comes from interface requests from the current component. With little or no dependence on external props and so on, it is called business strongly dependent components, and abandons the idea of component encapsulation.

What makes a good extensible, generic, robust component?

Take a look at Star High Ant Design and Element

Ant design of rc – switch

import * as React from 'react';
import classNames from 'classnames';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import KeyCode from 'rc-util/lib/KeyCode';

const Switch = React.forwardRef(
  (
    {
      prefixCls = 'rc-switch', className, checked, defaultChecked, disabled, loadingIcon, checkedChildren, unCheckedChildren, onClick, onChange, onKeyDown, ... restProps }, ref,) = > {
    const [innerChecked, setInnerChecked] = useMergedState<boolean>(false, {
      value: checked,
      defaultValue: defaultChecked,
    });

    function triggerChange(newChecked: boolean, event: React.MouseEvent
       
         | React.KeyboardEvent
        
         ,
        
       ) {
      let mergedChecked = innerChecked;

      if(! disabled) { mergedChecked = newChecked; setInnerChecked(mergedChecked); onChange? .(mergedChecked, event); }return mergedChecked;
    }

    function onInternalKeyDown(e) {
      if (e.which === KeyCode.LEFT) {
        triggerChange(false, e);
      } else if (e.which === KeyCode.RIGHT) {
        triggerChange(true, e); } onKeyDown? .(e); }function onInternalClick(e) {
      constret = triggerChange(! innerChecked, e);// [Legacy] trigger onClick with valueonClick? .(ret, e); }const switchClassName = classNames(prefixCls, className, {
      [`${prefixCls}-checked`]: innerChecked,
      [`${prefixCls}-disabled`]: disabled,
    });

    return (
      <button
        {. restProps}
        type="button"
        role="switch"
        aria-checked={innerChecked}
        disabled={disabled}
        className={switchClassName}
        ref={ref}
        onKeyDown={onInternalKeyDown}
        onClick={onInternalClick}
      >
        {loadingIcon}
        <span className={` ${prefixCls}-inner`} >
          {innerChecked ? checkedChildren : unCheckedChildren}
        </span>
      </button>); }); Switch.displayName ='Switch';

export default Switch;
Copy the code
  • Directly fromUI
  • Accept arguments and handle hooks

Ant Design is a secondary encapsulation of the API and UI

The React Components are extensible

Look again at

The Switch Element UI

<template>
  <div
    class="el-switch"
    :class="{ 'is-disabled': switchDisabled, 'is-checked': checked }"
    role="switch"
    :aria-checked="checked"
    :aria-disabled="switchDisabled"
    @click.prevent="switchValue"
  >
    <input
      class="el-switch__input"
      type="checkbox"
      @change="handleChange"
      ref="input"
      :id="id"
      :name="name"
      :true-value="activeValue"
      :false-value="inactiveValue"
      :disabled="switchDisabled"
      @keydown.enter="switchValue"
    >
    <span
      :class="['el-switch__label', 'el-switch__label--left', !checked ? 'is-active' : '']"
      v-if="inactiveIconClass || inactiveText">
      <i :class="[inactiveIconClass]" v-if="inactiveIconClass"></i>
      <span v-if=! "" inactiveIconClass && inactiveText" :aria-hidden="checked">{{ inactiveText }}</span>
    </span>
    <span class="el-switch__core" ref="core" :style="{ 'width': coreWidth + 'px' }">
    </span>
    <span
      :class="['el-switch__label', 'el-switch__label--right', checked ? 'is-active' : '']"
      v-if="activeIconClass || activeText">
      <i :class="[activeIconClass]" v-if="activeIconClass"></i>
      <span v-if=! "" activeIconClass && activeText" :aria-hidden=! "" checked">{{ activeText }}</span>
    </span>
  </div>
</template>
<script>
  import emitter from 'element-ui/src/mixins/emitter';
  import Focus from 'element-ui/src/mixins/focus';
  import Migrating from 'element-ui/src/mixins/migrating';
  export default {
    name: 'ElSwitch'.mixins: [Focus('input'), Migrating, emitter],
    inject: {
      elForm: {
        default: ' '}},props: {
      value: {
        type: [Boolean.String.Number].default: false
      },
      disabled: {
        type: Boolean.default: false
      },
      width: {
        type: Number.default: 40
      },
      activeIconClass: {
        type: String.default: ' '
      },
      inactiveIconClass: {
        type: String.default: ' '
      },
      activeText: String.inactiveText: String.activeColor: {
        type: String.default: ' '
      },
      inactiveColor: {
        type: String.default: ' '
      },
      activeValue: {
        type: [Boolean.String.Number].default: true
      },
      inactiveValue: {
        type: [Boolean.String.Number].default: false
      },
      name: {
        type: String.default: ' '
      },
      validateEvent: {
        type: Boolean.default: true
      },
      id: String
    },
    data() {
      return {
        coreWidth: this.width
      };
    },
    created() {
      if(! ~ [this.activeValue, this.inactiveValue].indexOf(this.value)) {
        this.$emit('input'.this.inactiveValue); }},computed: {
      checked() {
        return this.value === this.activeValue;
      },
      switchDisabled() {
        return this.disabled || (this.elForm || {}).disabled; }},watch: {
      checked() {
        this.$refs.input.checked = this.checked;
        if (this.activeColor || this.inactiveColor) {
          this.setBackgroundColor();
        }
        if (this.validateEvent) {
          this.dispatch('ElFormItem'.'el.form.change'[this.value]); }}},methods: {
      handleChange(event) {
        const val = this.checked ? this.inactiveValue : this.activeValue;
        this.$emit('input', val);
        this.$emit('change', val);
        this.$nextTick(() = > {
          // set input's checked property
          // in case parent refuses to change component's value
          this.$refs.input.checked = this.checked;
        });
      },
      setBackgroundColor() {
        let newColor = this.checked ? this.activeColor : this.inactiveColor;
        this.$refs.core.style.borderColor = newColor;
        this.$refs.core.style.backgroundColor = newColor;
      },
      switchValue(){!this.switchDisabled && this.handleChange();
      },
      getMigratingConfig() {
        return {
          props: {
            'on-color': 'on-color is renamed to active-color.'.'off-color': 'off-color is renamed to inactive-color.'.'on-text': 'on-text is renamed to active-text.'.'off-text': 'off-text is renamed to inactive-text.'.'on-value': 'on-value is renamed to active-value.'.'off-value': 'off-value is renamed to inactive-value.'.'on-icon-class': 'on-icon-class is renamed to active-icon-class.'.'off-icon-class': 'off-icon-class is renamed to inactive-icon-class.'}}; }},mounted() {
      /* istanbul ignore if */
      this.coreWidth = this.width || 40;
      if (this.activeColor || this.inactiveColor) {
        this.setBackgroundColor();
      }
      this.$refs.input.checked = this.checked; }};</script>
Copy the code

It is straightforward to see the best way to encapsulate design component UIs other than syntactically

  • Zero service code
  • goodUIandAPIdesign
  • Easy to learnandEasy to use

Let’s look at another way to encapsulate components

React For Menu

This is one way of encapsulating React components

  • createcontextManage the data flow of component groups
  • The presence of a parent component to determine the type of child component increases robustness
  • inindexMount the export components separately

Vue For Menu

<template>
    <div
    class="menu"// Event binding >
      // menuItem
      <slot></slot>
    </div>
</template>

<script>
export default {
    mixins: [...]. .name: 'Menu'.componentName: 'Menu'.inject: {
      menu: {
        default: ' '}},provide() {
      return {
        'menu': this}; }}</script>
Copy the code

In the design encapsulation of VUE-UI components, provide and Inject are often used to communicate components.

Vue can do this with JSX & Function Component in addition to slot, which is designed in much the same way as React

JSX is heavily used in Ant Design for Vue in Vue3 to encapsulate components

Here’s a quick summary

  • In the componentUIdataSeparate business as much as possible
  • The UI viewComponent should not containBusiness code
  • Consider components at the beginning of designUniversal, easy to use, extensible, robust and stableAs well asGood code structure, Api design and use

Think and discuss and ask questions

  • You have different or better designs that encapsulate componentsskillsandDemo
  • How do you determine whether a component is encapsulated? How are components designed?
  • Think back to the components you designedcode,Api,namedWhether to bring inconvenience to other students
  • And so on…

According to the above questions, thinking or you have different ideas might as well in the comments section we discuss, learn!