current position:Home>Tsx functional component practice of Vue in devui

Tsx functional component practice of Vue in devui

2021-08-26 19:59:48 JS old dog

Recently, I have the honor to participate devui Development of open source component library , Begin to contact vue. In the use of the vue3 + tsx After a while , It's very interesting , And comfortable .

In the development process , Many times I need to find information from the Internet , Learn from the code within the team , Learning and doing . The overall feeling is ,vue Medium jsx Application , Mainly replace template, It's just a change of writing . And I'm ( First out of a habit ), At the same time, I hope to react One way data flow in 、 Functional components, etc vue.

In this context , combination devui Details of the project , After a period of groping , Finally, a complete set of vue3 + tsx Component writing , Share with you , I hope it can inspire .

Here I would also like to emphasize : stay vue Introduce one-way data flow and functional components , For the purpose of exploring only one technical possibility , Hope to enrich vue Development model , Regardless of the strength of the framework . And , because vue3 Many other features are experimental , It is suggested to adopt a relatively conservative approach in the production process .

Common ways to write functional components

The code form of a component should look like the following

// hello.jsx
export default ({ value = 'Hello Vue' }) => <h1>{value}</h1>
 Copy code 

In order to dilute the differences in the framework , There is no import React.

One of the benefits of functional expressions , That is, we can reuse... Directly with higher-order functions .

// main.jsx
import Hello from './hello.jsx'
export default ({ name = 'JS Old dog ', ...rest }) => 
    <div><Hello { ...rest } /><h3>by {name}</h3></div>
 Copy code 

comparison Class inheritance Component reuse mode , High order reuse can avoid too long prototype chain , At the same time, attribute passing is also flat .

Vue3 defineComponent+JSX

Feel the vue3 Component writing of

import { defineComponent } from 'vue'
export default defineComponent({
    props: {
        value: { type: String, default: 'Hello Vue3' }
    },
    setup(props) {
        //  Don't write that here , If so, it becomes a closure variable , Out of responsiveness .
        // const { value = 'Hello Vue3' } = props || {}
        return () => {
            //  return render Internal function , deconstruction props yes ok Of 
            const { value = 'Hello Vue3' } = props || {}
            return <h1>{value}</h1>
        }
    }
})
 Copy code 

In the example vue Components are declared in object mode , In the simplest case, include props and setup label , among setup Back to render function , So there is no need to restate render—— This practice is limited to VUE3.

props

props Is to declare the external interface of this component , Among them

  • type Indicates the type , But note that here is used Constructor Indicates the type , That is to say JS Those types of constructors for native data .
  • default Represents the default value
  • required Indicates whether the attribute is required .

setup

there setup It's actually a higher-order function , yes render The closure of the . The whole process did not use this, Explain that this part can be context independent , It is possible to decouple .

use Vue compile jsx The functional component of

We use first webpack To achieve , Actually touch the compilation process .

React project , Usually by babel-loader To deal with it js file , At the same time loader Mount in react A dedicated presets and plugin, Realize to jsx Support for .

Webpack Support

in fact ,vue There is a corresponding loader To support jsx:@vue/babel-plugin-jsx

Loader Support jsx

We will jsx Of rule Change the configuration :

{
    test: /\.jsx?$/,
    use: {
        loader: 'babel-loader',
        options: {
            plugins: ['@vue/babel-plugin-jsx'],
        }
    },
    exclude: /node_modules/,
}
 Copy code 

Don't ask me why I didn't presets To configure , This configuration is made by hand , Close test effectively .

After this configuration ,jsx The functional component of , Can be correctly compiled into vue Components .

Loader Support tsx

Just one before ts-loader Can .

speculation vue There is no formal on tsx Be supported ,jsx It was just a substitute for template Of , therefore tsx This piece has no clue at all , I can't find the relevant information .

Later I received style-loader css-loader Inspired by the , Go straight jsx Pad to ts-loader You can just :

{
    test: /\.tsx?$/,
    use: [{
        loader: 'babel-loader',
        options: {
            plugins: ['@vue/babel-plugin-jsx'],
        }
    }, 'ts-loader'],
    exclude: /node_modules/,
}
 Copy code 

This code , It looks simple , Actually, I tossed 2 More than a hour .

Stateless component

It's very simple :

export default ({ value = 'Hello Vue' }) => (
    <div> <h1>{value}</h1> <button>click me</button> </div>
)
 Copy code 

Built in status component

stay react in , The built-in state of functional components is through useState To achieve , Life cycle weakening , Replace with state update driven useEffect.Vue There are also corresponding supporting schemes :reactive and onMouted onUpdated Wait, series life cycle hook.

React The built-in state implementation of

stay react in , State machines and return Components are in the same function field :

export default ({ value = 'Hello Vue' }) => {
    const [count, setCount] = useState(0)
    return (
        <div> <h1>{value}</h1> <button onClick={() => setCount(count + 1)}> click me {count} </button> </div>
    )
}
 Copy code 

Vue Built in state implementation

but vue I can't write this way yet , We can imitate setup, Encapsulate the state machine in a closure :

const factory = () => {

    const state = reactive({
        count: 0
    })
    
    return ({ value = 'Hello Vue' }) => (
        <div> <h1>{value}</h1> <button onClick={() => state.count += 1}> click me {state.count} </button> </div>
    )
}

export default factory()
 Copy code 

Very elegant , Close test effectively .

Strong type tsx alignment props

Functional components are right setup The decoupling , Now align props The ability of .

props And state Type declaration

props And state It's all about the core of the component , We learn from react How to do it , Declare separately :

type TProps = {
    value?: string
}

type TState = {
    count?: number
}
 Copy code 

Through these two types , We did props in type and required Declaration of two properties , The rest default, Just implement it in the program , For example, default values are given during deconstruction , Or short circuit, etc .

const { value = 'abc' } = props
 Copy code 

The code is slightly refactored

const factory = () => {
    const state = reactive<TState>({
        count: 0
    })

    return (props: TProps) => {
        const { value = 'Hello Vue' } = props
        const handleClick = () => {
            state.count = (state.count || 0) + 1
        }
        return (
            <div> <h1>{value}</h1> <button onClick={handleClick}>click me [{state.count}]</button> </div>
        )
    }
}
export default factory()
 Copy code 

Compile and pass , Running normally .

Key code

webpack.config.js

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
    mode: 'production',
    // mode: 'development',
    entry: './test1/index.jsx',
    output: {
        path: path.resolve(__dirname, './dist/test1'),
        filename: 'index.js',
    },
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            template: './test1/index.html',
            filename: './index.html',
            inject: 'body'
        }),
    ],
    resolve: {
        extensions: ['.tsx', '.ts', '.jsx', '.js'],
        alias: {}
    },
    module: {
        rules: [
            {
                test: /\.jsx?$/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        plugins: ['@vue/babel-plugin-jsx'],
                    }
                },
                exclude: /node_modules/,
            },
            {
                test: /\.tsx?$/,
                use: [{
                    loader: 'babel-loader',
                    options: {
                        plugins: ['@vue/babel-plugin-jsx'],
                    }
                }, 'ts-loader'],
                exclude: /node_modules/,
            },
        ]
    },
    devServer: {
        compress: true,
        port: 9001,
    }
}
 Copy code 

Project entry documents

// index.tsx
import { createApp } from 'vue'
import Page from './page'

createApp({
    render: () => <Page />
}).mount('#app')
 Copy code 

Postscript

It's mentioned above that ,jsx Maybe it's just template An alternative to , I use jsx Write function components , It may go against the original intention .

In the development process , Just className still class The problem of , Raised objections , But in the end, it was unified into class. Your advice to me is , Don't dwell on these details . But if vue Really out of a set jsx Personalized writing , Not friendly to developers , Not necessarily a good thing .

Life cycle use vue Provided on A series of hooks can , But some event binding names are different , such as onMouseenter, instead of onMouseEnter, No more extensive testing has been done .

above .

copyright notice
author[JS old dog],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2021/08/20210826195943020o.html

Random recommended