当前位置:网站首页>TDesign compositionapi refactoring path
TDesign compositionapi refactoring path
2022-07-19 14:49:00 【PY】
TDesign CompositionAPI The road to reconstruction
Tdesign-vue-next Of Composition Api Refactoring plan It took five months to finish 44 Refactoring of components , share 20 Developers participate in component refactoring .
Go online first and then optimize
Tdesign-vue-next The warehouse is from Tdesign-vuefork To come over , And start doing vue-next The adaptation of ( It was called vue-next, Become vue The official version of is called core 了 ), The first priority at that time was to options Writing compatible , Tool function difference smoothing 、 Component library construction is compatible , Let it run fastest , It can be used by users .
Comparison of popular libraries in the market
Here are a few library pairs vue3 Upgrade strategy comparison :
ui library | Warehouse strategy |
|
|---|---|---|
Same as warehouse , Different branches | except | |
Same as warehouse , Different branches | All components use | |
Same as warehouse , Different branches | Have in hand , be in | |
New warehouse | No burden of history , New warehouse , Use | |
New warehouse | At a relatively early stage , Use |
You can see , except iview All other libraries are going compositionAPI Do refactoring . The above libraries exist for a long time , The number of components is also relatively complete ,vue2 The version of has been polished for a long time , It will be more mature to a certain extent . Compared with these long-lived libraries ,TD Is in a special situation ,TD Of vue2 The version started relatively late , In the middle of long-term iteration , It's still in beta , We will also support it for a long time vue2 Iteration .
So in td-vue3 When the library was developed ,vue2 The library of is not perfect , In parallel forward , For a while , Often td-vue2 Changed a problem ,td-vue3 I have to change it again . And the internal collaborative developer resources are basically td-vue2 above , At that stage td-vue2 towards td-vue3 Blood transfusion . also vue2 library , There is no strong requirement for the implementation in the component , You can see different development paradigms and design patterns inside the component library , The code structure is chaotic , There is no basic standard . and td-vue3 Master td-vue2, Basically inherited all the problems .
Then we are 2021 Open source at the end of the year , Go out , Hear more community voices .
Community voices
The above is the voice from the community at the beginning of open source , This question has been thought of before open source, and someone will definitely ask , This issue When you bring it up , What I think is , Finally here .
contributor
The stock at that time 44 It is certainly unrealistic to rely on existing development resources to do components . The original plan of open source is tdesign-vue3 We need to rely on the power of the open source community , Attract more external developers . Because of internal use vue3 There are not so many teams , Many teams will only use vue3. And after more than a year of incubation inside , There is not much incremental space for collaborative developers .
After that, I was with amadeus711 After some communication , Launched the recruitment of reconstruction in the community Composition Api Refactoring plan .
in addition , With the PMCchaishi After some communication , Through her to promote the follow-up components, first from vue3 Start , Again to vue2 Blood transfusion , At the same time, promote the long-term contribution of internal collaborative developers to vue3 Do transformation , The following components are required to use compositionAPI Development .
The above is the record of the whole reconstruction process , every last PR Are very meaningful . In the process , Yes 7 Contributors have become our core contributors . Thank all contributors for their contributions .
The meaning of reconstruction
When refactoring starts for a while , Some code has also been submitted , There were some heated discussions in the middle , It also caused everyone to think , What is the meaning of refactoring , If you simply change the previous writing , time-consuming .
come from chaishi The question of
At this stage , The confused point is , Why do you do this , The benefits of this matter are there .
From the perspective of that time , At the code level , Before, there were various paradigms in our code , And the aggregation between components has not done much , Through the aggregation between components and the unification of development paradigm , It will bring great help to the later maintenance work .
Another point , Whole vue The community is embracing compositionAPI, Open source needs to keep pace with the community . Refactoring is very important , Our code will be seen by others , If people come up to have a look , It's all old things , There is no attraction . Open source code is just the beginning , We need to constantly absorb new things from the community .
vue3 The official version of
You Yuxi is also 1 month 20 Japan announce :Vue3 Will be in 2 month 7 Day becomes the new default version , Also more determined to everyone in vue3 The above investment .
Better frame base
The base of a frame will affect the components of the whole upper layer , As refactoring begins , Mainly from Basics hook, Component cohesion , Component development specification , Type specification Go get started
Basics hook
On the basis of hook aspect , Corresponding hook:
- Sub assembly slots :
useChildComponentSlots, - TNode Rendering :
useTNode,useTNodeDefault,useContent,useKeepAnimation - Global configuration :
useCommonClassName,useConfig,usePrefixClass - Controlled and uncontrolled :
useDefaultValue,useVModel - Drag and drop :
useDragSort - event :
useListener,useResize - Click animation :
useKeepAnimation,getRippleColor - Virtual scrolling :
useVirtualScroll
Component cohesion
We have new output TagInput,SelectInput, And cohesion of related components : cascader, select, tree-select , date-picker, time-picker. With the help of this migration compositionAPI The transformation of .
Component development specification
In our old code , The component code is chaotic , There are various development paradigms . In the process of component refactoring , We wrote the component development specification , stay compositionAPI On the basis of , Collaborative contributors can write component code under a general standard . Form a unified component development code .
TD Component development specification
Removal of higher-order functions
The previous code has a very headache , It's a higher-order function mapProps, This function is used to handle controlled and uncontrolled components , And some rewriting of components .
Source , The code is as follows :
import { ComponentOptions, defineComponent, ComponentPublicInstance, h } from 'vue';
import kebabCase from 'lodash/kebabCase';
function toCamel(str: string): string {
return str.replace(/-([a-z])/gi, (m, letter) => letter.toUpperCase());
}
type PropOption = {
name: string;
event?: string | string[];
alias?: string[];
};
type ParsedPropOption = {
defaultName: string;
dataName: string;
events: string[];
alias?: string[];
[propName: string]: any;
};
function getPropOptionMap(props: (string | PropOption)[]): { [name: string]: ParsedPropOption } {
const propOptionMap = {};
function parseProp(propOption: PropOption): ParsedPropOption {
const { name: propName, alias, ...others } = propOption;
const camelName = propName.replace(/^[a-z]/, (letter: string) => letter.toUpperCase());
const defaultName = `default${camelName}`;
const dataName = `data${camelName}`;
let events: string[] = [];
if (propOption.event) {
events = events.concat(propOption.event);
}
events.push(`update:${propName}`);
if (alias) {
events = events.concat(alias.map((item) => `update:${item}`));
}
return {
events,
defaultName,
dataName,
alias,
...others,
};
}
props.forEach((prop: string | PropOption) => {
const defaultOption = {
alias: [] as string[],
};
let propOption: PropOption;
if (typeof prop === 'string') {
propOption = { ...defaultOption, name: prop };
} else {
propOption = { ...defaultOption, ...prop };
}
propOptionMap[propOption.name] = parseProp(propOption);
});
return propOptionMap;
}
export default function (props: (string | PropOption)[]): any {
function mapProps(componentConstructor: ComponentPublicInstance): any {
const component: ComponentOptions<ComponentPublicInstance> = componentConstructor;
const propOptionMap = getPropOptionMap(props);
const defineProps: Record<string, any> = { ...component.props };
const defineWatches = {};
let defineEvents: string[] = [];
const defineMethods = {};
const camelPropsKeys = Object.keys(component.props).map((key) => toCamel(key));
Object.keys(propOptionMap).forEach((propName) => {
const { events, alias, defaultName, dataName } = propOptionMap[propName];
defineProps[propName] = component.props[propName];
defineProps[defaultName] = component.props[defaultName];
if (alias) {
alias.forEach((prop: string) => {
defineProps[prop] = defineProps[propName];
});
}
defineEvents = defineEvents.concat(events);
// does not destroy the original defaultValue logic
const defaultList: string[] = [];
// watch default prop
defineWatches[defaultName] = {
handler(v: any): void {
if (defaultList.indexOf(defaultName + this.$.uid) > -1) return;
const { props } = this.$.vnode;
const hasDefault = props && (defaultName in props || kebabCase(defaultName) in props);
if (hasDefault && !(propName in props)) {
this.$data[dataName] = v;
}
defaultList.push(defaultName + this.$.uid);
},
immediate: true,
};
// Listen for aliases
alias.forEach((aliasItem) => {
defineWatches[aliasItem] = {
handler(v: any): void {
const { props } = this.$.vnode;
if (props && aliasItem in props && !(propName in props)) {
this.$data[dataName] = v;
}
},
immediate: true,
};
});
// monitor props change , And then hang on to data Up
defineWatches[propName] = {
handler(v: any): void {
const { props } = this.$.vnode;
if (props && (propName in props || kebabCase(propName) in props)) {
this.$data[dataName] = v;
}
},
immediate: true,
};
});
if (component.methods) {
Object.keys(component.methods).forEach((key) => {
defineMethods[key] = function (this: any, ...args: any[]): any {
if (this.$refs.component) {
return this.$refs.component[key](...args);
}
};
});
}
const { name } = component;
// Return a wrapped proxy component
return defineComponent({
name: `${name}-mapprops`,
inheritAttrs: false,
props: {
...defineProps,
},
data() {
const data = {};
Object.keys(propOptionMap).forEach((propName: string): void => {
const { dataName } = propOptionMap[propName];
data[dataName] = undefined;
});
return { ...data };
},
computed: {
_listeners(): Record<string, any> {
const others = {};
Object.keys(this.$attrs).forEach((attr: string): void => {
const event = attr.startsWith('on') ? attr[2].toLowerCase() + attr.substr(2) : null;
if (event && defineEvents.indexOf(event) === -1) {
others[attr] = (...args: any[]): void => {
this.$emit(event, ...args);
};
}
});
return others;
},
},
watch: defineWatches,
methods: {
updateData(this: any, propName: string, v: any, ...args: any[]): any {
propOptionMap[propName].events.forEach((event) => {
this.$emit(event, v, ...args);
});
const { props } = this.$.vnode;
if (!props || !(propName in props)) {
this[propOptionMap[propName].dataName] = v;
return true;
}
},
...defineMethods,
},
render() {
const propMap = {};
const handlerMap = {};
Object.keys(propOptionMap).forEach((propName: string): void => {
const { dataName, events } = propOptionMap[propName];
const eventName = `on${events[0].charAt(0).toUpperCase()}${events[0].substr(1)}`;
const { props } = this.$.vnode;
if ((props && propName in props) || typeof this[dataName] !== 'undefined') {
propMap[propName] = this[dataName];
}
handlerMap[eventName] = (v: any, ...args: any[]): any => this.updateData(propName, v, ...args);
});
const attrs = {};
Object.keys(this.$attrs).forEach((attrName) => {
const camelAttrKey = toCamel(attrName);
if (camelPropsKeys.indexOf(camelAttrKey) === -1) {
attrs[attrName] = this.$attrs[attrName];
}
});
return h(
componentConstructor,
{
...this.$props,
...propMap,
...attrs,
...(this._listeners as Record<string, any>),
...handlerMap,
ref: 'component',
},
this.$slots,
);
},
});
}
return mapProps;
} stay vue The disadvantages of using higher-order functions in are as follows :
- The development paradigm of higher-order functions is
vueNot mainstream , Many developers cannot understand . - This code is hard to maintain , And smelly and long .
- When developers use components ,
devToolWhen debugging, there will be axxxMappropsPackaging components , The debugging experience for developers is very poor .
Better type support
We almost abandoned the prototype development method , The code of components is written into setup Inside ,setup Only return one render function . The source and use of each variable in the component are very clear . And make full use of TS Type derivation of , Reduce active assertions , Increase code readability . Avoid the following types of code .
export interface DatePickerComputed {
inputListeners: any;
startText: string;
endText: string;
formattedValue: string;
rangeText: string;
min: Date | null;
max: Date | null;
classes: any;
pickerStyles: any;
tDisabled: boolean;
popClass: (string | ClassName)[];
popupObject: PopupProps;
}New trends in the community
We have also followed the new trend of the community , It includes the following aspects :
- Unit testing , Access
vitest, Greatly improved efficiency . - Components are compatible
nuxt, Provided by community contributorstdesign-nuxt-starter. starterUsing the latest state managerpinia- The developer documentation component sample code is completely rewritten to
setup script
summary
All the way down, right vue Have a deeper understanding of the development paradigm . At the same time, I also have a deeper understanding of open source .TDesign Still very young , There is still a long way to go , The core team is backed by a group of people , Everyone is reliable , Expect us to be better and better . In the future, we will explore more about the quality of components , Looking forward to the release of the official version .
边栏推荐
- Dr. Tao's lunar line reverses by 6.0
- 定时任务,vim直接创建修改用户
- Problème de la valeur maximale de la fenêtre coulissante
- ShanDong Multi-University Training #3
- Encrypt Ogg users
- Redis 与 Mysql 的数据一致性
- CompositionAPI 组件开发范式
- Minuterie logicielle à puce unique v2.0
- 44. Use orienmask for instance segmentation target detection, MNN deployment and ncnn deployment
- 深入理解事务隔离级别
猜你喜欢

数据填报、报表展示哪家强?亿信ABI给你答案

Manuel incomplet, comment tracer manuellement l'information de surveillance de tongweb?

ospf-LSA

MySQL read / write separation

Redis source code and design analysis -- 1 Simple dynamic string

Comparison of two virtual machines

MongoDB分片集群搭建

滑动窗口最大值问题

JVM performance optimization

QChartView添加在QGridLayout中时覆盖了之前的控件
随机推荐
Classification of blocks
MySQL storage functions and triggers
Common built-in functions, iteratable objects, iterator objects, exception capture, purpose of exception capture, generator objects, modules, absolute and relative imports, package concepts, modules
Code Runner for VS Code,下载量突破 4000 万!支持超过50种语言
Oracle - 锁
MySQL view
The first step of agile: turn "iteration" into "sprint" and start!
定时任务,vim直接创建修改用户
Classes abstraites et dérivées
SQL相关的时间日期类型
CF 807 E. Mark and Professor Koro(权值线段树)
Deployment 原理
Huawei wireless device configuration intelligent roaming
见鬼,U盘空间怎么少了,原来是EFI分区搞的鬼,删除它
Redis source code and design analysis -- 1 Simple dynamic string
Notes on random nodes of force buckle 382 linked list
Principle and simple implementation of custom MVC
Use tongweb's hot deployment function with caution
The bill module of freeswitch
[flask introduction series] exception handling