preface
- Vue3 has been around for quite some time now, and I’m sure you’ve read a lot of articles about Vue3 + typescript, especially since it was released recently
script setup + typescript
, this really sweet, used small partners say good! - But that’s not the point of this article
jsx + typescript
; I don’t know how many people like Xiaobi, both template development and JSX development; Repeatedly cross jump, switch back and forth; Of course, this is not arbitrary, but is usually used when writing small componentsjsx + typescript
“Is usually used when writing a pagetemplate + typescript
; - As a front-end developer with more than four years of experience in developing hundreds of components, today I’m going to share with you how to use it in Vue3
jsx + typescript
Some of the inner workings of developing components;
Related information:
- Official document rendering function;
- Official documentation: JSX-Next;
background
For example, now you want to develop a stepper component that binds a number variable bidirectionally. Click the plus sign and add one binding value, click the minus sign and subtract one binding value; It looks something like this:
Start with a simple implementation of this component using defineComponent:
const DefineNumber = defineComponent({
props: {
modelValue: {type: Number}},emits: {
'update:modelValue': (val? :number) = > true.'add-num': (val: number) = > true.'sub-num': (val: number) = > true,},setup(props, ctx) {
const handler = {
onClickAdd: () = > {
const val = props.modelValue == null ? 1 : props.modelValue + 1
ctx.emit('add-num', val)
ctx.emit('update:modelValue', val)
},
onClickSub: () = > {
const val = props.modelValue == null ? 1 : props.modelValue - 1
ctx.emit('sub-num', val)
ctx.emit('update:modelValue', val)
},
}
return () = > (
<div>
<button onClick={handler.onClickSub}>-</button>
<button>{props.modelValue == null ? 'N' : props.modelValue}</button>
<button onClick={handler.onClickAdd}>+</button>
</div>)}})Copy the code
Use the stepper component in the parent component:
export const DemoPage = defineComponent(() = > {
const state = reactive({
count: 123
})
return () = > <>
<h1>Hello world:{state.count}</h1>
<DefineNumber
v-model={state.count}
onAdd-num={val= > console.log('add', val)}
/>
</>
})
Copy the code
This is a controlled component and will not be used if there is no V-model binding value or if state.count is not a reactive variable;
The event
As you can see, the event type is defined like this:
emits: { 'update:modelValue': (val? : number) => true, 'add-num': (val: number) => true, 'sub-num': (val: number) => true, },Copy the code
When listening for events, listen like this:
onAdd-num={val => console.log('add', val)}
Copy the code
Normal events of type Add-num are now correct and can be prompted for the correct type. But bidirectionally bound events don’t work;
- Such as
v-model
, modifystate.count
Is an object{}
, you’ll findv-model
There are no errors. In fact, the current version of defineComponent([email protected]) is not derivedv-model
Should be of typemodelValue
The type of; - If you want to listen in
update:modelValue
Event, which can be listened for in template:@update:modelValue
; But in TSX, it doesn’tonAdd-num
So that’s the way to write the listening event,onUpdate:modelValue
Errors are reported in TSX because the colon is not a special compilable symbol. The only way to listen for this event in TSX is to write:
<DefineNumber
v-model={state.count}
onAdd-num={val= > console.log('add', val)} {... {'onUpdate:modelValue': (val) = > console.log('get change', val)
}}
/>
Copy the code
Next look at the writing method after the improvement of small series;
import {designComponent} from 'plain-ui-composition'
const DesignNumber = designComponent({
props: {
modelValue: {type: Number}},emits: {
onUpdateModelValue: (val? :number) = > true.onAddNum: (val: number) = > true.onSubNum: (val: number) = > true,},setup({props, event}) {
const {emit} = event
const handler = {
onClickAdd: () = > {
const val = props.modelValue == null ? 1 : props.modelValue + 1
emit.onAddNum(val)
emit.onUpdateModelValue(val)
},
onClickSub: () = > {
const val = props.modelValue == null ? 1 : props.modelValue - 1
emit.onSubNum(val)
emit.onUpdateModelValue(val)
},
}
return () = > (
<div>
<button onClick={handler.onClickSub}>-</button>
<button>{props.modelValue == null ? 'N' : props.modelValue}</button>
<button onClick={handler.onClickAdd}>+</button>
</div>)}})// Used in the parent component
export const DemoPage = defineComponent(() = > {
const state = reactive({
count: 123
})
return () = > <>
<h1>Hello world:{state.count}</h1>
<DefineNumber
v-model={state.count}
onAdd-num={val= >console.log('add', val)} {... { 'update:modelValue': (val) => console.log('get change', val) }} /><DesignNumber
v-model={state.count}
onAddNum={val= >console.log('add', val)} onUpdateModelValue={val => console.log('get update value', val, val? .toFixed(0))} onChange={val => console.log('get change value', val, val? .toFixed(0))} /></>
})
Copy the code
- The first is
defineComponent
Turned out to bedesignComponent
; - And then there are a lot of changes in the way the event definition is written. When the emits option defines the event type, the event name is the name of the event that is listened on TSX, but when the event is sent at runtime, it is automatically converted to a horizontal name. Such as
onAddNum
Event (event.emit. OnAddNum (val)) is automatically dispatched with the name event.emitadd-num
Event, so that both in the template@add-num
Again, in TSXonAddNum
, can listen to the event correctly, and get the correct type of prompt; - At the same time, the designComponent will
v-model
The type of is derived asmodelValue
Type, so at this point ifstate.count
The type is notnumber|undefined
, the V-Model attribute of DesignNumber will have a TS type detection error; - There is also an implicit rule within designComponent that events are dispatched
onUpdateModelValue
, three events will be sent at once, in the following order:- update-model-value
- update:modelValue
- change
- The reason for sending the first event is to accommodate listening in THE TSX
onUpdateModelValue
Events; - The reason for dispatching the second event is for adaptation
v-model
Syntax sugar bidirectional binding value; - The third event is distributed so that developers can easily monitor component value changes while binding events. For example, if the developer wants to get the new and old values of the binding value in a change, he can write:
<DesignNumber
v-model={state.count}
onUpdateModelValue={val= > console.log([
['Parameter is new value', val],
['Now state.count is the old value', state.count]
])}
onChange={val= > console.log([
['Parameter is new value', val],
['Now state.count is a new value', state.count]
])}
/>
Copy the code
- Because of the distribution event
update:modelValue
Is a synchronous process, so before the event is dispatched, onUpdateModelValue gets the binding valuestate.count
Is the old value, the binding value that onChange gets after this event is executedstate.count
Is the new value; - In addition, Vue3 has removed event listening from components, and designComponent has been added here. Those who have a lot of experience in component development should know how important event listening is for component development. In conjunction with the emits option event definition, deisgnComponent internally designs a set of component internal event apis that take type hints first, as shown in the following example:
const DesignNumber = designComponent({
props: {
modelValue: {type: Number}},emits: {
onUpdateModelValue: (val? :number) = > true.onAddNum: (val: number) = > true.onSubNum: (val: number) = > true,},setup({props, event}) {
/* Send events */
event.emit.onAddNum(100)
/* Listen on events */
const eject = event.on.onAddNum(val= > {
// Where val is automatically derived to type number
console.log('on_1', val)
})
/* Logout event */
eject()
/* Listen for an event */
event.once.onAddNum(val= > {
// Where val is automatically derived to type number
console.log('once', val)
})
/* Listen for events and remove events */ when the component is destroyed
/* In general, when a component is destroyed, its own events are automatically logged out. However, if the current component is listening for the event of another component that has not been destroyed, you need to unregister this event listener */ during the destruction
onBeforeUnmount(event.on.onAddNum(val= > {
console.log('on_2', val)
}))
/* Manually logout events */
const handler: Parameters<typeof event.on.onAddNum>[0] = val= > {
console.log('on_2', val)
}
event.on.onAddNum(handler)
setTimeout(() = > {
event.off.onAddNum(handler)
})
return () = > null}})Copy the code
slot
- There is no specific definition of slot in Vue3, and when I saw the official release of Vue3, I was a little disappointed with this part of the content. Because of the maintenance of the slot, the previous Vue2 version had caused a lot of trouble to the small editor;
- In previous Vue2, components did not need to declare events or slots when they were defined. The events that a component dispatches, and the slots it uses, are all over the file, and sometimes it’s not even clear that the dispatches are being called within another component. When you need to adjust a component written by someone else, you often need to search the component for keywords such as $emits and slot to determine which events the component will send and which slots it will have. Which slots are normal slots, which slots are scoped slots, and what are the parameter types of scoped slots? This is largely up to the developer to add this information as comments to the component. Sometimes it is difficult to enforce the annotation specification because there is no way to make such canonical adjustments to older components or to have complete control over the quality of the component developer’s code.
- In the early Vue2
@vue/composition-api
DesignComponent has a set of options for defining event types, as well as slots and scope slots, as shown below.
For example, the DesignNumber component now needs to be able to customize the contents of the add/subtract button (slot), as well as the contents of the display value (scoped slot, parameter to current value); The sample code looks like this:
const DesignNumber = designComponent({
props: {
modelValue: {type: Number}},emits: {
onUpdateModelValue: (val? :number) = > true.onAddNum: (val: number) = > true.onSubNum: (val: number) = > true,},slots: [
'add'.'sub',].scopeSlots: {
default: (scope? :number) = >{},},setup({props, event: {emit}, slots, scopeSlots}) {
const handler = {
onClickAdd: () = > {
const val = props.modelValue == null ? 1 : props.modelValue + 1
emit.onAddNum(val)
emit.onUpdateModelValue(val)
},
onClickSub: () = > {
const val = props.modelValue == null ? 1 : props.modelValue - 1
emit.onSubNum(val)
emit.onUpdateModelValue(val)
},
}
return () = > (
<div>
<button onClick={handler.onClickSub}>{slots.sub(<span>-</span>)}</button>
{
scopeSlots.default(
props.modelValue,
<button>{props.modelValue == null ? 'N' : props.modelValue}</button>)},<button onClick={handler.onClickAdd}>{slots.add(<span>+</span>)}</button>
</div>)}})// Use the component
export const DemoPage = defineComponent(() = > {
const state = reactive({
count: 123
})
return () = > <>
<h1>Hello world:{state.count}</h1>{/ *<DefineNumber
v-model={state.count}
onAdd-num={val= >console.log('add', val)} {... { 'update:modelValue': (val) => console.log('get change', val) }} />*/}<DesignNumber v-model={state.count}/>
<DesignNumber v-model={state.count} v-slots={{
add:() = > <span>add</span>,
sub: () => <span>sub</span>,
default: val => <input type="text" value={val}/>}} / ></>
})
Copy the code
- slot
- The slots option is an array of strings;
- The setup function gets a slots object. Each value of the slots object is a function, and the function argument is the default slot content; The custom slot content is used when the component receives it; the default slot content is used otherwise.
- Scope slot
- The scopeSlots option is an object whose key is the slot name and whose value is the function type of the scoped slot. This function takes only one argument, which defines the type of the scoped object obtained when the component is used;
- The setup function gets a scopeSlots object, and each value is a function that renders the contents of the scope slot. The function takes two arguments. The first argument is the scope object and the second argument is the default content. When the parent component does not customize the scope slot, the default content is rendered;
- v-slots
- There are two ways to pass a slot to a component in JSX, and this one is official. One is to pass an object through V-slots, where the key of the object is the name of the slot, and the value must be a function. The other is passed through the location of the component’s children; For example, the above example can be written as:
<DesignNumber v-model={state.count}>
{{
add: () = > <span>add</span>,
sub: () = > <span>sub</span>.default: val= > <input type="text" value={val}/>
}}
</DesignNumber>
Copy the code
Note that plain-UI-composition currently only supports v-slots strip type derivation. The children method above still does not support type derivation (that is, in the code above, the val argument in the default slot function is derived to an implicit any type). But plain-design-composition is delivered with children support and type derivation; The type of the children component cannot be defined.
reference
- The most common way of communication between parent and child components is that the parent component passes properties to the child component, and the parent component listens for events sent by the child component. However this kind of means has relatively big limitation, flexibility is not high. At this time, in order to give full play to its own capabilities, the child component can expose some methods and state variables. After the parent component gets the reference of the child component, it uses these exposed methods and variables to achieve more complex functions. A common scenario is that when writing a form, the verification function of the form component should be called before submitting the form. After the verification is passed, the form data can be submitted to the background.
- There are two ways to get a reference:
- Get a reference to a DOM node;
- Get a reference to a custom component;
- In designComponent, to get a full type hint when retrieving a reference, a class called
useRefs
To manage references to child nodes, as shown in the following example;
const DesignNumber = designComponent({
props: {
modelValue: {type: Number}},emits: {
onUpdateModelValue: (val? :number) = > true.onAddNum: (val: number) = > true.onSubNum: (val: number) = > true,},setup({props, event: {emit}}) {
const handler = {
onClickAdd: () = > {
const val = props.modelValue == null ? 1 : props.modelValue + 1
emit.onAddNum(val)
emit.onUpdateModelValue(val)
},
onClickSub: () = > {
const val = props.modelValue == null ? 1 : props.modelValue - 1
emit.onSubNum(val)
emit.onUpdateModelValue(val)
},
}
const methods = {
reset: (val? :number) = > {
emit.onUpdateModelValue(val == null ? 0 : val)
},
}
return {
refer: {
methods,
},
render: () = > (
<div>
<button onClick={handler.onClickSub}>-</button>
<button>{props.modelValue == null ? 'N' : props.modelValue}</button>
<button onClick={handler.onClickAdd}>+</button>
</div>),}}})// Use the component's code
export const DemoPage = defineComponent(() = > {
const {refs, onRef} = useRefs({
number: DesignNumber, // Get a reference to the DesignNumber component
btn: iHTMLButtonElement, // Get a reference to the button node
})
const state = reactive({
count: 123
})
const handler = {
onReset: () = > {
console.log(refs) refs.number? .methods.reset() }, }return () = > <>
<h1>Hello world:{state.count}</h1>
<DesignNumber v-model={state.count} ref={onRef.number}/>
<button ref={onRef.btn} onClick={handler.onReset}>reset value</button>
</>
})
Copy the code
- The first is to use
useRefs
Declare the child components you want to reference, and getrefs
As well asonRef
Two objects; - You need to
onRef
Is assigned to the corresponding child componentref
Properties, and then you can putrefs
Used as a total component reference object. - In addition to the function of type hints; Such as
refs.number
The type is the refer object returned by the DesignNumber component at the end of the DesignNumber component. If the val parameter type of the reset function in the example code is removed from the question mark and becomes a required parameter, it is in DemoPagerefs.number? .methods.reset()
You will get a type error, missing the required parameter val; In the same way asrefs.btn
forHTMLButtonElement
Dom object, you can get the corresponding type hint; iHTMLButtonElement
Is an anonymous object{}
, but of typeHTMLButtonElement
- Source:
export const iHTMLButtonElement = {} as typeof HTMLButtonElement
- The reason is that some non-browser environments, such as applets, such as SSR, are not available
HTMLButtonElement
This object right hereplain-ui-omposition
Export this simple object to aid type hints; - If you can ensure that the code is running in a browser environment, then
iHTMLButtonElement
Switch toHTMLButtonElement
That’s ok;
- Source:
injection
- The above mention of how a parent component gets a type hint when referencing a child component only applies to parent and child components. When using
provide/inject
When communicating with descendant components, this method is not applicable. - The following example shows how to get the type of the injected object at injection time;
// A parent component that provides state to descendant components
const DesignNumber = designComponent({
provideRefer: true.name: 'design-number'.setup() {
const methods = {
reset: (val? :number) = > {
console.log('reset', val)
},
}
return {
refer: {
methods,
},
render: () = > null,}}})// Descendant components inject objects
const DesignNumberChild = designComponent({
setup() {
Inject inject does not have a default value. It means that the parent component DesignNumber must be injected, otherwise the runtime error will occur
const certainParent = DesignNumber.use.inject()
console.log(certainParent.methods.reset())
Inject inject has a default value of null, which is the default value when the parent component DesignNumber is not injected
const uncertainParent = DesignNumber.use.inject(null)
console.log(uncertainParent? .methods.reset())// uncertainParent must be followed by the optional operator? Object is possibly null, otherwise there will be ts error
return () = > null}})Copy the code
- First, the parent component DesignNumber, which provides data to descendant components, needs to provide two options:
- name:’design-number’
- provideRefer:true
- This is done automatically while the component is running
provide('@@design-number',refer)
- Then the child component just needs to call
DesignNumber.use.inject()
You can inject state variables provided by the parent component. This inject function is the same as Vue3 standard Inject function, but this inject function will provide type hint function;
inheritance
- In Vue3, a property is passed to a child component. If it is not declared in props and emits, the property is stored in attrs and is passed to the root node of the child component by default. If the child is multiple root nodes, a runtime warning is raised.
- In TSX, passing properties to a component that are not defined in props or emits causes TS compilation errors;
- The following example shows how to declare inherited property types in designComponent.
const DesignNumber = designComponent({
props: {
modelValue: {type: Number},
max: {type: Number},
min: {type: Number,}},emits: {
onUpdateModelValue: (val? :number) = > true.onAddNum: (val: number) = > true.onSubNum: (val: number) = > true,},setup() {
return () = > null}})const InheritHTMLButton = designComponent({
inheritPropsType: iHTMLButtonElement,
props: {
// Define the type attribute to override button's type attribute
// Because it is defined in props, type is not automatically passed to the root node button
type: {type: Number}},setup() {
return () = > <button/>}})const InheritDesignNumber = designComponent({
inheritPropsType: DesignNumber,
props: {
// Override DesignNumber's Max attribute type is string
max: {type: String},
// Customize the required attributes
precision: {type: Number.required: true}},emits: {
// The onAddNum event overrides DesignNumber with an argument of type string
onAddNum: (val: string) = > true.onAddNum2: (val: number) = > true,},setup() {
return () = > <DesignNumber/>}})export const DemoPage = defineComponent(() = > {
const state = reactive({
count: 123
})
return () = > <>{/* tabIndex, inherit button attribute */} {/*type, overwrite attribute */}<InheritHTMLButton tabindex={1} type={100} />{/*precision, for custom mandatory attribute */} {/* Max, override type string*/} {/*min, inherit attribute */} {/*onAddNum, override type function, function parameter string*/} {/*onAddNum2, Custom event */} {/*onSubNum, inherited event type */}<InheritDesignNumber
precision={100}
max={"100"}
min={100}
onAddNum={val= > console.log(val.charAt(0))}
onAddNum2={val => console.log(val.toPrecision(0))}
onSubNum={val => console.log(val.toPrecision(0))}
/>
</>
})
Copy the code
- The example has two components that inherit properties:
- InheritHTMLButton inherits the properties of the native Button component;
- InheritDesignNumber inherits the attributes of the custom component DesignNumber;
- When defining a component, pass
inheritPropsType
Option to specify the inherited property type; The only function of this option is to provide hints for inherited property types, and it has no effect at runtime; - If a property or event defined by the component itself conflicts with the name of the inherited property event, then the last property event defined by the component itself takes precedence because the property is not automatically passed to the root node.
inheritPropsType
In combination withinheritAttrs
The option can be used in another way;- For example, you want to wrap a PlInput component based on the input native component, but the root of the PlInput component is not an input, but a div, because this div can enrich the PlInput component’s functions, such as displaying postfix ICONS, pre-content slots, post-content slots, and so on.
- In this case, it seems that PlInput is set to the inherited property type when defining it
HTMLDivElement
This is reasonable, but in real development scenarios, you tend to attribute the input node more often than the root div node. - Set the inheritPropsType inheritance attribute type to HTMLInputElement. [inheritAttrs:false] [inheritAttrs:false] [inheritAttrs:false] [inheritAttrs:false] [inheritAttrs:false]
const PlInput = designComponent({
inheritPropsType: HTMLInputElement,
inheritAttrs: false.props: {
modelValue: {type: String},
wrapperAttrs: {type: Object}},setup({props, attrs}) {
return () = > (
/* If the developer needs to set the root node attribute, the wrapperAttrs attribute object can be set */
<div {. props.wrapperAttrs} >{/* Manually pass attrs to the input node */}<input type="text" {. attrs} / >
</div>)}})export const App = () = > <>{/*div does not have a type attribute<div type="submit"/>} {/*PlInput inherits the HTMLInputElement attribute type, so it can receive the type attribute; Because we are using inheritAttrs:false, type is not defined in props, but is not passed to the root div. Instead, we are manually passing type to the input via attrs.<PlInput wrapperAttrs={{class: 'class-on-div'}} class="class-on-input" type="submit"/>
</>
Copy the code
The binding
plain-ui-composition
The emergence of small series in the development of component librariesplain-ui
I felt it out step by step;- At present
plain-ui
All components that support binding are uncontrolled components; The differences between controlled and uncontrolled components, as well as their advantages and disadvantages, are covered in a number of articles on the web that will not be covered here. Here’s a little introductionplain-ui-composition
, a combinatorial function for quickly implementing the binding values of uncontrolled componentsuseModel
;
Single-valued binding: Implements a counter component that increases (subtracts) the bound value count by one by clicking the plus (minus) button
const PlNumber = designComponent({
props: {
modelValue: {type: Number}},emits: {
onUpdateModelValue: (val? :number) = > true,},setup({props, event: {emit}}) {
const model = useModel(() = > props.modelValue, emit.onUpdateModelValue)
return () = > (
<div>
<button onClick={()= > model.value = (model.value == null ? 0 : model.value - 1)}>-</button>
<button>{model.value == null ? 'N' : model.value}</button>
<button onClick={()= > model.value = (model.value == null ? 0 : model.value + 1)}>+</button>
</div>)}})export const App = designComponent({
setup() {
const state = reactive({
count: undefined
})
return () = > <>
<PlNumber v-model={state.count}/>
<PlNumber v-model={state.count}/>
{state.count}
</>}})Copy the code
Multi-value binding:
- Implement a component to edit numbers: PlNumber;
- Define a range property. If range is not set to true, edit a single value and bind to a single value.
- When range is true, edit multiple values and bind multiple values;
const PlNumber = designComponent({
props: {
modelValue: {type: [Number.String]},
range: {type: Boolean},
start: {type: [Number.String]},
end: {type: [Number.String]}},emits: {
onUpdateModelValue: (val? :number | string) = > true.onUpdateStart: (val? :number | string) = > true.onUpdateEnd: (val? :number | string) = > true,},setup({props, event: {emit}}) {
const model = useModel(() = > props.modelValue, emit.onUpdateModelValue)
const startModel = useModel(() = > props.start, emit.onUpdateStart)
const endModel = useModel(() = > props.end, emit.onUpdateEnd)
return () = > (
<div>
{
!props.range ? <>
<button onClick={()= > model.value = (model.value == null ? 0 : Number(model.value) - 1)}>-</button>
<button>{model.value == null ? 'N' : model.value}</button>
<button onClick={()= > model.value = (model.value == null ? 0 : Number(model.value) + 1)}>+</button>
</> : (<>
<input type="text" v-model={startModel.value}/>
至
<input type="text" v-model={endModel.value}/>
</>)
}
</div>
)
},
})
export const App = designComponent({
setup() {
const state = reactive({
formData: {
value: undefined.startValue: undefined.endValue: undefined,}})return () = > <>{/* Alone */}<PlNumber v-model={state.formData.value}/>{/* Input numeric range */}<PlNumber range v-models={[
[state.formData.startValue, 'start'],
[state.formData.endValue, 'end'],
]}/>
{JSON.stringify(state.formData)}
</>}})Copy the code
To bind multiple values in template, write:
<template>
<pl-number v-model:start="state.formData.startValue" v-model:end="state.formData.endValue"/>
</template>
Copy the code
conclusion
- Attach a small make up a video recording, some examples of some of the instructions on this article, the video address: www.bilibili.com/video/BV1Q4… ;
plain-ui
Is a Vue3.0 component library, based onplain-ui-composition
All components are currently usedjsx + typescript + composition api
Developed, there is a need for students can refer to the source code of some components; The default theme color of the component library is green.Plain – pot. Gitee. IO/plain – UI – do…; Sometime, I will write an article about the interesting things I developed in this component library.plain-ui-composition
I have a twin brother —plain-design-composition
With a few lines of configuration, you can use the VueCompositionApi and two-way binding functionality in existing React applicationsplain-ui-composition
More powerful and accurate; And the API is exactly the same. Online Document address:Plain – pot. Gitee. IO/plain – desig…; There will also be a special article on this library.plain-design
Is based onplain-design-composition
React component library, the default theme color is dark blue.Plain – pot. Gitee. IO/plain – desig…; This component library can be used directly in existing React applications and coexist with other existing React component libraries. Interestingly, the source code of the components in theplain-ui
Very similar. This is the same as the other daySemi Design
The proposedfundation/adapter
The architecture coincides; The difference is that plain currently only implements Vue3.0 and React; Fundation in part byplain-ui-composition
.plain-design-composition
The adapter is implemented by the developer. The adapter code reuse rate is up to 99%. In most cases, it is difficult to tell which is Vue and which is React components after removing the types of components into ES6 source code.- Small make up in
Vue + jsx + typescript
This is a summary of some of the explorations that took more than two years. Of course, different people will have different opinions. Some people might think that packaging a designComponent based on defineComponent is “you’re painting a snake by tampering with my lyrics”, while others might take inspiration from some of the examples in this article. Anyway, first of all, this is not malicious and is not intended to change anyone’s programming habits if you usejsx + typescript + composition api
If you’ve taken some inspiration from this article, congratulations, you’ve learned another trick. If you prefer to usetemplate + typescript
Don’t even hate JSX, just laugh it off. Finally, none of the open source libraries mentioned above are KPI-driven (at least not yet rewarded or promoted at work), they are not implemented for the sake of implementation, they are implemented as a result of the idea in your daily life.