Vue自定义组件库(一)

119 阅读4分钟

Vue自定义组件库(一)

编写过程中十分感谢 @Laster 的帮助,解答了实际操作过程中的诸多疑点。

1、项目搭建

1.1、创建项目

npm create vite@latest

1.2、基础依赖

# node的类型声明
npm install @types/node
# sass
npm install -D sass
# 支持TSX文件处理
npm install -D @vitejs/plugin-vue-jsx
# eslint 语法规范
npm install -D eslint vite-plugin-eslint
# prettier 语法规范
npm install -D prettier eslint-config-prettier eslint-plugin-prettier
# 自动生成组件声明文件
npm install -D vite-plugin-dts

1.3、项目配置

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "path";
import vueJsxPlugin from "@vitejs/plugin-vue-jsx";
​
export default defineConfig({
  // 配置 TSX 插件
  plugins: [vue(), vueJsxPlugin()],
  resolve: {
    // 配置路径别名
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
  server: {
    host: "localhost",
    port: 5274,
  },
});

1.4、语法规范

# 初始化eslint配置文件
PS D:\Github\chenxing> npx eslint --init
You can also run this command directly using 'npm init @eslint/config'.
? How would you like to use ESLint? ...
  To check syntax only
> To check syntax and find problems
√ How would you like to use ESLint? · problems
√ What type of modules does your project use? · esm
√ Which framework does your project use? · vue
√ Does your project use TypeScript? · No / Yes
√ Where does your code run? · browser, node
√ What format do you want your config file to be in? · JavaScript
The config that you've selected requires the following dependencies:
​
@typescript-eslint/eslint-plugin@latest eslint-plugin-vue@latest @typescript-eslint/parser@latest
√ Would you like to install them now? · No / Yes
√ Which package manager do you want to use? · npm
Installing @typescript-eslint/eslint-plugin@latest, eslint-plugin-vue@latest, @typescript-eslint/parser@latest

在 webstorm 中配置下 eslint 规则来源 image-20231111221737929.png

module.exports = {
  "env": {
    "browser": true,
    "es2021": true,
    "node": true
  },
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:vue/vue3-essential",
    // 兼容 prettier
    'plugin:prettier/recommended',
    'eslint-config-prettier'
  ],
  "overrides": [
    {
      "env": {
        "node": true
      },
      "files": [
        ".eslintrc.{js,cjs}"
      ],
      "parserOptions": {
        "sourceType": "script"
      }
    }
  ],
  "parserOptions": {
    "ecmaVersion": "latest",
    "parser": "@typescript-eslint/parser",
    "sourceType": "module"
  },
  "plugins": [
    "@typescript-eslint",
    "vue"
  ],
  "rules": {}
}

在 webstorm 中配置下 prettier 规则来源 image-20231111221932472.png

{
  "name": "chenxing",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vue-tsc && vite build",
    "preview": "vite preview",
    // 配置代码格式化命令
    "eslint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx",
    "prettier": "prettier --write ./**/*.{vue,ts,tsx,js,jsx,css,less,scss,json,md}"
  },
  "dependencies": {
    "vue": "^3.3.4"
  },
  "devDependencies": {
    "@types/node": "^20.4.10",
    "@typescript-eslint/eslint-plugin": "^6.3.0",
    "@typescript-eslint/parser": "^6.3.0",
    "@vitejs/plugin-vue": "^4.2.3",
    "@vitejs/plugin-vue-jsx": "^3.0.1",
    "eslint": "^8.47.0",
    "eslint-config-prettier": "^9.0.0",
    "eslint-plugin-prettier": "^5.0.0",
    "eslint-plugin-vue": "^9.17.0",
    "prettier": "^3.0.1",
    "sass": "^1.65.1",
    "typescript": "^5.0.2",
    "vite": "^4.4.5",
    "vite-plugin-eslint": "^1.8.1",
    "vue-tsc": "^1.8.5"
  }
}

2、Button组件

src 同级建目录 package ,package 下建 components/button 目录

目录结构如下

image.png

2.1、button.tsx

import { defineComponent, toRefs } from "vue";
import { ButtonProps, buttonProps } from "./type/button";
import "./style/button.scss";
​
export default defineComponent({
  name: "XButton",
  props: buttonProps,
  setup(props: ButtonProps, { slots }) {
    const { theme } = toRefs(props);
    console.log(theme);
    return () => {
      return <div class={`x-button x-button-${theme.value}`}>{slots.default ? slots.default() : "Button"}</div>;
    };
  },
});

2.2、style/button.scss

.x-button {
  position: relative;
  text-align: center;
  border-radius: 5px;
  transition: all 0.15s;
  font-size: 12px;
  padding: 0.5rem 0.75rem;
  margin: 0.125rem 0.25rem;
  background-color: #f5f5f5;
}

2.3、type/button.ts

import { ExtractPropTypes, PropType } from "vue";
​
type theme = "light" | "success" | "info" | "warning" | "error" | "dark";
​
export const buttonProps = {
  theme: {
    type: String as PropType<theme>,
    default: "light",
  },
};
​
export type ButtonProps = ExtractPropTypes<typeof buttonProps>;

2.4、index.ts

import XButton from "./button";
import { App } from "vue";
​
XButton.install = (Vue: App) => {
  Vue.component("XButton", XButton);
};
​
export default XButton;

3、组件注册/使用

3.1、package/components/index.ts

import XButton from "./button";
​
export { XButton };

3.2、index.ts

import { App } from "vue";
import { XButton } from "./packages/components";
​
export * from "./packages/components";
​
const components = [XButton];
​
export default {
  install(Vue: App) {
    components.forEach((c) => {
      Vue.component(c.name, c);
    });
  },
};

3.3、使用

import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
import chenxing from "../index";
​
createApp(App).use(chenxing).mount("#app");

3.4、App.vue

<script setup lang="ts">
import HelloWorld from "./components/HelloWorld.vue";
</script>

<template>
  <div>
    <a href="https://vitejs.dev" target="_blank">
      <img src="/vite.svg" class="logo" alt="Vite logo" />
    </a>
    <a href="https://vuejs.org/" target="_blank">
      <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
    </a>
  </div>
  <HelloWorld msg="Vite + Vue" />
  <x-button theme="info"></x-button>
</template>

<style scoped>
.logo {
  height: 6em;
  padding: 1.5em;
  will-change: filter;
  transition: filter 300ms;
}

.logo:hover {
  filter: drop-shadow(0 0 2em #646cffaa);
}

.logo.vue:hover {
  filter: drop-shadow(0 0 2em #42b883aa);
}
</style>

image.png

4、打包配置

4.1、tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "module": "ESNext",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "skipLibCheck": true,
    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "preserve",
    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": [
    "packages/**/*.ts",
    "packages/**/*.d.ts",
    "packages/**/*.tsx",
    "packages/**/*.vue",
    "index.ts"
  ],
  "references": [
    {
      "path": "./tsconfig.node.json"
    }
  ]
}

4.2、vite.config.ts

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueJsxPlugin from "@vitejs/plugin-vue-jsx";
import dtsPlugin from "vite-plugin-dts";
import path from "path";
​
export default defineConfig({
  plugins: [vue(), vueJsxPlugin(), dtsPlugin()],
  resolve: {
    // 配置路径别名
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
  build: {
    rollupOptions: {
      external: ["vue"],
      output: [
        {
          entryFileNames: "[name].mjs",
          preserveModules: true,
        },
      ],
    },
    minify: false,
    lib: {
      entry: "./index.ts",
      name: "chenxing",
      fileName: "chenxing",
    },
  },
  server: {
    host: "localhost",
    port: 5274,
  },
});

5、发布

5.1、登录到npm

如无账号需在在www.npmjs.com/注册账号,同时使用npm get registry检查当前npm源,源应为registry.npmjs.org,如果不是需使用npm config set registry registry.npmjs.org将npm源指定到npm官方(或者使用nrm切换源)

PS D:\Github\chenxing> npm login
npm WARN adduser `adduser` will be split into `login` and `register` in a future version. `adduser` will become an alias of `register`. `login` (currently an alias) will become its own command.
npm notice Log in on https://registry.npmjs.org/
Username: xumeng03
Password:
Email: (this IS public) xumeng03@qq.com
npm notice Please check your email for a one-time password (OTP)
Enter one-time password: 47246353
Logged in as xumeng03 on https://registry.npmjs.org/.
# 验证当前用户
PS D:\Github\chenxing> npm who am i
xumeng03

5.2、package.json

{
  "name": "chenxing",
  "private": false,
  "version": "0.1.0",
  "author": {
    "name": "xumeng03",
    "email": "xumeng03@qq.com"
  },
  "description": "一个适用于vue3的组件库",
  "license": "MIT",
  "type": "module",
  "main": "dist/index.mjs",
  "module": "dist/index.mjs",
  "types": "dist/index.d.ts",
  "files": [
    "dist",
    "README.md",
    "package.json"
  ],
  "scripts": {
    "dev": "vite",
    "build": "vue-tsc && vite build",
    "preview": "vite preview",
    "eslint": "eslint . --ext .vue,.js,.ts,.jsx,.tsx",
    "prettier": "prettier --write ./**/*.{vue,ts,tsx,js,jsx,cjs,css,less,scss,html,json,md}"
  },
  "dependencies": {
    "@types/node": "^20.9.0",
    "vue": "^3.3.4"
  },
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^6.10.0",
    "@typescript-eslint/parser": "^6.10.0",
    "@vitejs/plugin-vue": "^4.2.3",
    "@vitejs/plugin-vue-jsx": "^3.0.2",
    "eslint": "^8.53.0",
    "eslint-config-prettier": "^9.0.0",
    "eslint-plugin-prettier": "^5.0.1",
    "eslint-plugin-vue": "^9.18.1",
    "prettier": "^3.0.3",
    "sass": "^1.69.5",
    "typescript": "^5.0.2",
    "vite": "^4.4.5",
    "vite-plugin-dts": "^3.6.3",
    "vite-plugin-eslint": "^1.8.1",
    "vue-tsc": "^1.8.5"
  }
}

5.3、推送

# 推送包
npm publish

5.4、其他命令

# 删除包
npm unpublish <package-name> -f
​
# 增加版本号:小变动,比如修复bug等,版本号变动 v1.0.0->v1.0.1
npm version patch
# 增加版本号:增加新功能,不影响现有功能,版本号变动 v1.0.0->v1.1.0
npm version minor
# 增加版本号:破坏模块对向后的兼容性,版本号变动 v1.0.0->v2.0.0
npm version major

最后贴一下项目地址 github.com/xumeng03/ch…