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 recentlyscript setup + typescript, this really sweet, used small partners say good!
  • But that’s not the point of this articlejsx + 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 Vue3jsx + typescriptSome 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 asv-model, modifystate.countIs an object{}, you’ll findv-modelThere are no errors. In fact, the current version of defineComponent([email protected]) is not derivedv-modelShould be of typemodelValueThe type of;
  • If you want to listen inupdate:modelValueEvent, which can be listened for in template:@update:modelValue; But in TSX, it doesn’tonAdd-numSo that’s the way to write the listening event,onUpdate:modelValueErrors 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 isdefineComponentTurned 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 asonAddNumEvent (event.emit. OnAddNum (val)) is automatically dispatched with the name event.emitadd-numEvent, so that both in the template@add-numAgain, in TSXonAddNum, can listen to the event correctly, and get the correct type of prompt;
  • At the same time, the designComponent willv-modelThe type of is derived asmodelValueType, so at this point ifstate.countThe 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 dispatchedonUpdateModelValue, 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 TSXonUpdateModelValueEvents;
  • The reason for dispatching the second event is for adaptationv-modelSyntax 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 eventupdate:modelValueIs a synchronous process, so before the event is dispatched, onUpdateModelValue gets the binding valuestate.countIs the old value, the binding value that onChange gets after this event is executedstate.countIs 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-apiDesignComponent 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 calleduseRefsTo 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 useuseRefsDeclare the child components you want to reference, and getrefsAs well asonRefTwo objects;
  • You need toonRefIs assigned to the corresponding child componentrefProperties, and then you can putrefsUsed as a total component reference object.
  • In addition to the function of type hints; Such asrefs.numberThe 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.btnforHTMLButtonElementDom object, you can get the corresponding type hint;
  • iHTMLButtonElementIs 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 availableHTMLButtonElementThis object right hereplain-ui-ompositionExport this simple object to aid type hints;
    • If you can ensure that the code is running in a browser environment, theniHTMLButtonElementSwitch toHTMLButtonElementThat’s ok;

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 usingprovide/injectWhen 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 runningprovide('@@design-number',refer)
  • Then the child component just needs to callDesignNumber.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, passinheritPropsTypeOption 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.
  • inheritPropsTypeIn combination withinheritAttrsThe 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 itHTMLDivElementThis 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-compositionThe emergence of small series in the development of component librariesplain-uiI felt it out step by step;
  • At presentplain-uiAll 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-uiIs a Vue3.0 component library, based onplain-ui-compositionAll components are currently usedjsx + typescript + composition apiDeveloped, 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-compositionI have a twin brother —plain-design-compositionWith a few lines of configuration, you can use the VueCompositionApi and two-way binding functionality in existing React applicationsplain-ui-compositionMore 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-designIs based onplain-design-compositionReact 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-uiVery similar. This is the same as the other daySemi DesignThe proposedfundation/adapterThe architecture coincides; The difference is that plain currently only implements Vue3.0 and React; Fundation in part byplain-ui-composition.plain-design-compositionThe 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 inVue + jsx + typescriptThis 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 apiIf you’ve taken some inspiration from this article, congratulations, you’ve learned another trick. If you prefer to usetemplate + typescriptDon’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.