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 processing
Business dependent logic, registering business dependent hooks, passing in the appropriate familiarity and slots, and so on
- The view component is responsible
Presentation 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 from
UI
- 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
- good
UI
andAPI
design Easy to learn
andEasy to use
Let’s look at another way to encapsulate components
React For Menu
This is one way of encapsulating React components
- create
context
Manage the data flow of component groups - The presence of a parent component to determine the type of child component increases robustness
- in
index
Mount 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 component
UI
和data
Separate business as much as possible The UI view
Component should not containBusiness code
- Consider components at the beginning of design
Universal, easy to use, extensible, robust and stable
As well asGood code structure, Api design and use
Think and discuss and ask questions
- You have different or better designs that encapsulate components
skills
andDemo
吗 - How do you determine whether a component is encapsulated? How are components designed?
- Think back to the components you designed
code
,Api
,named
Whether 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!