使用 vue3 + ts 实现一个组件库

概述(实现功能)

本文章实现了vue3 + ts 的组件库,实现了如下功能呢:

1、可以进行对组件进行全局注册或者按需引入;

2、组件在使用时有较有好的参数以及类型提示;

3、对代码进行压缩减小代码包的大小;

开始

1、准备

开始前需要先安装vue-cli,因为项目使用vue-cli进行模板初始化;

npm install -g @vue/cli

#或者

yarn global add @vue/cli

2、项目模板初始化

安装完vue/cli 以后需要对模板进行初始化

vue create demo

选择自定义 并选择typescript 以及 vue3 的搭配

3、文件准备

项目初始化以后需要对初始化的模板进行改造;

1、将原来的 scr 文件夹 改为 examples(此文件夹用于展示组件demo);
2、新建 packages 文件夹(此文件夹存放组件库源码);
3、新建 typings 文件夹 (存放ts类型声明文件);
4、新建 lib 文件夹 (存放压缩打包后的组件代码,此文件夹会上传到npm);
5、新建 vue.config.js 文件 (对组件库运行以及打包进行自定义处理);

4、配置文件

1、vue.config.js
const { defineConfig } = require("@vue/cli-service");
const path = require("path");

function resolve(dir) {
  return path.join(__dirname, dir);
}

module.exports = defineConfig({
  configureWebpack: {
    resolve: {
      alias: {
        "~": resolve("packages"),
        typings: resolve("typings"),
      },
    },
  },
  lintOnSave: false,
  pages: {
    index: {
      entry: "examples/main.ts",
      template: "public/index.html",
      filename: "index.html",
    },
  },
  // 扩展 webpack 配置,使 packages 加入编译
  chainWebpack: (config) => {
    config.module
      .rule("js")
      .include.add("/packages")
      .end()
      .use("babel")
      .loader("babel-loader");
  },
});

2、tsconfig.json

ts配置文件增加如下两段代码

"include": [
    "examples/**/*.ts",
    "examples/**/*.tsx",
    "examples/**/*.vue",
    "packages/**/*.ts",
    "packages/**/*.tsx",
    "packages/**/*.vue",
    "typings/**/*.ts",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],

"paths": {
      "~/*": [
        "packages/*"
      ],
      "typings/*": [
        "typings/*"
      ]
    },

5、packages文件夹的结构

packages是组件源码存放的地方还是比较重要的看一下这个文件夹的结构;

1、packages/components存放组件的地方;
2、packages/index.ts 组件库的入口文件;
3、packages/components/button 单个组件
4、packages/components/button/index.ts 单个文件的入口

6、开始编写第一个组件

开始写第一个简单的button组件demo;按照上一章的目录结构创建好文件;

1、创建button组件 (packages/components/button/src/button.vue)

此处的类型文件生命先不用在意接下来会讲 import type { ButtonProps, ButtonType } from 'typings/button';

<template>
    <button class="nd-btn" :style="{ background: btnColor[props.type] }">
        <span v-if="$slots.default">

            <slot></slot>
        </span>
    </button>
</template>
  
<script lang="ts" setup>

import type { ButtonProps, ButtonType } from 'typings/button';

const props = defineProps<ButtonProps>()

const btnColor: {
    [key in ButtonType]: string
} = {
    success: 'green',
    warning: "orange",
    info: "blue",
    error: "red"
}

defineOptions({
    name: "w-button"
})

</script>
  
<style scoped>
.nd-btn {
    border-width: 0px;
    border-radius: 4px;
    padding: 2px 8px;
}
</style>
2、单个组件入口文件(packages/components/button/index.ts);

单个组件入口文件会有单个组件会于组件的全局注册有关所有提取出来;

import WButton from './src/Button.vue';
import { App } from 'vue';

// 实现vue.use() install方法 使用Vue.component将组件全局注册
WButton.install = (Vue: App) => {
    Vue.component(WButton.name, WButton)
}

export default WButton;
3、组件库入口文件(packages/index.ts)

此处与组件库的全局注册有关

import { App } from 'vue'
import WButton from '~/components/button'

// 所有组件列表
const components = [WButton]

// 定义 install 方法, App 作为参数
const install = (app: App): void => {
    // 遍历注册所有组件
    components.map((component) => app.component(component.name, component))
}

export {
    WButton
}

export default {
    install
}

7、组件的类型声明

组件的类型声明,以及参数提示实现的不太优雅但是好在能用

1、首先声明*.vue文件的类型声明 (typings/global.d.ts)
declare module '*.vue' {
    import { DefineComponent } from 'vue';
    // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types.ts
    const component: DefineComponent<{}, {}, any>;
    export default component;
}
2、单个文件的类型声明 (typings/button.d.ts)
import { PropType, DefineComponent, ExtractPropTypes } from "vue"
/** 按钮类型 */
export type ButtonType = 'success' | 'error' | 'info' | 'warning'

/** 组件入参 */
export type Button = {
    /** 按钮类型 */
    type: {
        type: PropType<ButtonType>;
        required: true;
    },
    text: {
        type: PropType<string>
        required: false;
    }
}

/** 组件参数类型 */
export type ButtonProps = ExtractPropTypes<Button>

3、组件库类型声明(typings/index.d.ts)
import { Button } from './button'
import { DefineComponent } from 'vue'
declare module 'demo' {
    /** 按钮  */
    export const WButton: DefineComponent<Button>

}

8、组件库打包

对组件库源码进行压缩打包 packages.json

  "private": false, // 此处一定要是false 不然npm上传不通过
  "main": "lib/demo.umd.min.js",  // 组件库打包后的入口文件
  "typings": "./typings", // 组件库的类型声明文件夹
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "lib": "vue-cli-service build  --target lib --name windaka-ui --dest lib packages/index.ts"  // 此处对代码进行压缩打包
  },

"lib": "vue-cli-service build --target lib --name demo --dest lib packages/index.ts"

这一行代码进行着重解释:

--target lib lib代表lib文件夹 打包后输出的位置

--name demo demo 是你打包后文件的前缀 与 main lib/demo.****.js 相呼应 要改的话一起改

--dest lib packages/index.ts 打包的入口

总结

组件库的开发流程大体就是这些,其中有些简单的小细节可能没有提到,有不对的或者有问题欢迎大家批评指正。

npm 包的发布流程单独开一个文章。

posted @   spongeCoder  阅读(1325)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示