Vue3 + TSX搭建组件库

484 阅读3分钟

基础知识

  1. 组件命令规则
    官方建议我们使用大驼峰命名法注册组件
  • 单词大写开头对于编辑器的自动补全比较友好
  • 能够与基础的HTML元素区分
  • 基础组件(也就是展示类的、无逻辑的或无状态的组件)应该以Base,App开头;eg: BaseButton, BaseTable
  1. 属性定义Prop
    使用更细节的方式定义属性
    <script setup>
        // 不推荐
        const props = defineProps(['title'])
        // 推荐
        const props = defineProps({
            title: {
                type: String,
                required: true,
                default: ''
            }
        })
    </script>
    
  2. 事件定义emit
    自定义事件的名称会被自动做转换,我们通常使用驼峰做事件名,但监听时需要转换为横线连接方式(kebab-base)
    <!-- MyComponent -->
    <script setup>
        const emit = defineEmits('someEvent')
        emit('someEvent')
    </script>
    <!-- 使用页面 -->
    <MyComponent @some-event="callback" />
    
  3. 透传特性
    那些没有在props和emits中声明的特性或者事件监听被称为透传特性, 比如class, style, id这些,当组件只有一个根节点时, 透传特性会默认加到根元素上;多个根节点则不存在
    // 如果不需要继承这些属性 需要使用inheritAttrs: false
    <script>
        // use normal <script> set this option
        export default {
            inheritAttrs: false
        }
    </script>
    <script setup>
        // 访问透传特性
        import { useAttrs } from 'vue'
        const attrs = useAttrs()
    </script>
    

TSX in Vue3

在TSX文件中定义组件

  • 函数式组件
    export default (props, ctx) => <div>test<div>
  • defineComponent + render函数式
    // render函数内部需要使用this
    export default defineComponent({
        render() {
            return <div>test</div>
        }
    })
  • defineComponent + setup
    export default defineComponent({
        setup(props, ctx) {
            // 利用Composition API,避免this
            return <div>test</div>
        }
    })

vue3中的JSX语法

  • JSX中使用修饰符
    // button.tsx
    import { withModifiers, defineComponet, ref } from 'vue'
    export default defineComponet({
        const count = ref(0)
        const inc = () => count.value++
        setup(props) {
            return () => {
                return 
                <>
                <a href="" onClick={withModifiers(inc, ['prevent'])}>click</a>
                <button onClick={withModifiers(inc, ['self'])}> click Me </button>
                </>
            }
        }
    })
  • JSX中使用指令v-if/v-for
    v-if使用条件语句或三元表达式代替
    v-for通过map循环来替换
    // test.jsx
    import { defineComponet, ref } from 'vue'
    export default defineComponet({
        const condition = ref(true)
        const list = ref([1,2,3,4])
        setup() {
            return () => {
                <div>{ condition ? <span>A</span> : <span></span> }</div>
                <ul>
                    list.map(item => {
                        return <li> {item} </li>
                    })
                </ul>
            }
        }
    })
  • JSX中使用插槽
    通过v-slots指令 指定具名插槽
    // Parent.txs
    import { defineComponent } from 'vue'
    export default defineComponet({
        setup() {
            return () => {
                return (
                    <Child v-slots={{
                        prefix: () => <i>prefix</i>,
                        suffix: (props: Record<"name", String>) => <i>{props.name}</i>
                    }}>
                        <!--默认插槽-->
                    </Child>
                )
            }
        }
    })
    // Child.tsx
    import { defineComponent } from 'vue'
    export default defineComponet({
        setup(props, { slots }) {
            return () => (
                <>
                默认插槽:{ slots.default && slots.default() }
                <br />
                具名插槽:{ slots.prefix && slots.prefix() }
                <br />
                作用域插槽:{ slots.suffix && slots.suffix({name: 'suffix'})}
                </>
            )
        }
    
    })
  • JSX中使用emit
    import { defineComponent } from 'vue'
    export default defineComponent({
        emits: ['click'],
        setup(props, {emit}) {
            return () => (
                <button onClick={() => emit('click', 'msg')}>子向父传递</button>
            )
        }
    })

基础环境搭建

项目创建(基于vite创建)

创建基础模板

  • 使用npm
    npm init vite@latest
    // 依次输入project-name
    // 选择vue
    // 选择vue-ts版本
  • 使用yarn
    yarn create vite
  • 使用pnpm
    pnpm create vite

引入JSX

通过@vitejs/plugin-vue-jsx插件

    yarn add @vitejs/plugin-vue-jsx -D

在vite.config.ts中配置jsx插件

    import vueJSX from '@vitejs/plugin-vue-jsx'
    export default {
        plugins: [
            //...
            vueJSX({
                // 配置项
            })
        ]
    }

代码规范(eslint + prettier)

eslint配置

使用eslint --init 初始化配置文件
    npx eslint --init

依次执行如下操作:

image.png 适配vue3语法

//.eslintrc.js
module.exports = {
    extends: [
        "plugin:vue/vue3-recommended"
    ]
}

添加prettier

  • 安装插件
    yarn add -D prettier eslint-plugin-prettier eslint-config-prettier
  • 修改.eslintrc.js
module.exports = {
    extends: [
        plugin:vue/vue3-recommended",
        "plugin:prettier/recommended"
    ]
}
  • 创建.prettierrc.js
module.exports = {
  // printWidth: 80,
  // tabWidth: 2,
  // useTabs: false,
  semi: false, // 未尾逗号, default:  true
  singleQuote: true, // 单引号 default: false
  // quoteProps: 'as-needed',
  // jsxSingleQuote: false,
  trailingComma: 'none', // 未尾分号 default: es5    all | none | es5
  // bracketSpacing: true,
  // bracketSameLine: false,
  // jsxBracketSameLine: false,
  arrowParens: 'avoid', // default: always
  // insertPragma: false,
  // requirePragma: false,
  proseWrap: 'never',
  // htmlWhitespaceSensitivity: 'css',
  // vueIndentScriptAndStyle: false,  // .vue 缩进
  endOfLine: 'auto' // default lf
}

文档系统(vitepress)

添加vitePress文档

  • 安装vitepress
yarn add -D vitepress

  • 创建第一个文档
    mkdir docs && echo "hello vitepress" > docs/index.md
  • 添加脚本
// package.json
{
    "scripts": {
        "docs:dev": "vitepress dev docs",
        "docs:build": "vitepress build docs",
        "docs:serve": "vitepress serve docs"
    }
}
  • 本地启动
yarn docs:dev

访问效果http://localhost:3000

image.png

配置vitepress

  • 配置左侧菜单
// 修改 docs/.vitepress/config.ts文件 没有则创建该文件
const sidebar = [
  {
    text: '组件',
    items: [
      { text: 'Button 按钮', link: '/components/button/' },
      { text: 'Tree 树形控件', link: '/components/tree/' },
      { text: 'Input 输入框', link: '/components/input/' },
      { text: 'Select 选择器', link: '/components/select/' },
      { text: 'Table 表格', link: '/components/table/' },
    ]
  }
]
export default {
   themeConfig: {
     sidebar
  }
}
  • 创建左侧菜单对应的md文件
    // 在docs/components依次创建button、tree、input等文件夹以及对应的index.md文件

样式体系(sass + tailwind.css)

  • 安装sass + tailwind
npm install -D sass tailwindcss postcss autoprefixer
  • 初始化tailwind
npx tailwindcss init -p
  • 配置tailwind
// tailwind.config.js
module.exports = {
  content: [
    "./index.html",
    "./src/**/*.{vue,js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
  • 添加Tailwind指令到主样式文件
/* index.scss */
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";