vue3前端组件库的搭建与发布(一)

server/2024/12/25 1:33:45/

前言:

最近在做公司项目中,有这么一件事情,很是头疼,就是同一套代码,不同项目,要改相同bug,改好多遍,改的都想吐,于是就想做一个组件库,这样更新一下就全都可以了,当然也是第一次主导组件库的搭建,有哪些不对的,还请各位大佬指出来哈。

准备:

1、node(18+)

2、Verdaccio :是一个Node.js创建的轻量的私有npm proxy registry,可以直接在你本地起一个私有库

npm i verdaccio -g启动:verdaccio

就会出现下面的页面

可以直接创建一个用户:

npm adduser --registry http://localhost:4873/

会让你输入用户名和密码,这个要记好哈,后面上传的时候要用到

开始

看到网上大佬们用的是Monorepo方式,那咱们也用这种方式(虽然不太懂为啥要这样,总之随主流指定出错少,哈哈)

  1. 创建文件夹:

    mkdir?Monorepo?
    # 初始化文件
    pnpm init
    
  2. 在此目录下面创建.npmrc

    # 和npm一样,将别的包的依赖都放在node_modules下,不加的话会放在.pnpm下
    shamefully-hoist = true
    
  3. 新建pnpm-workspace.yaml文件

    packages:
    # 将所有的项目都放到这里
    - ‘packages/*’
    # 示例
    - ‘examples’

4.创建文件目录 packages、examples

packages – 将所有组件放到这里

examples – 测试组件

packages文件目录,里面的所有文件夹都要进行初始化 pnpm init

5、进入到components里面,一定要安装相应的依赖呀

?npm i vue typescript sass element-plus?decimal.js?@element-plus/icons-vue ?-D -w

-D 就不用介绍了

-w 是安装在根目录下

6、配置tsconfig.json文件

{"compilerOptions": {"allowJs": true, //允许编译器编译JS,JSX文件"target": "ES2015", //指定ECMAScript目标版本"useDefineForClassFields": true,"module": "ESNext", //设置程序的模块系统"moduleResolution": "Node", //模块解析策略。默认使用node的模块解析策略"strict": true, //启用所有严格类型检查选项"jsx": "preserve", //preserve模式,在preserve模式下生成代码中会保留JSX以供后续的转换操作使用"sourceMap": true, //生成目标文件的sourceMap文件"resolveJsonModule": true, //允许导入扩展名为“.json”的模块"esModuleInterop": false, //允许module.exports=xxx 导出,由import from 导入.因为很多老的js库使用了commonjs的导出方式,并且没有导出default属性"lib": [ //TS需要引用的库"ESNext","DOM"],"forceConsistentCasingInFileNames": true, //禁止对同一个文件的不一致的引用"allowSyntheticDefaultImports": true, //允许从没有设置默认导出的模块中默认导入"skipLibCheck": true, //忽略所有的声明文件( *.d.ts)的类型检查"baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录"paths": { //模块名到基于 baseUrl的路径映射的列表"/@/*": ["src/*"],},"types": [ //要包含的类型声明文件名列表"vite/client","element-plus/global",]},"include": [ //包含的文件"src/**/*.ts","src/**/*.d.ts","src/**/*.tsx","src/**/*.js","src/**/*.jsx","src/**/*.vue",]
}

7、初始化 examples 文件夹

1、初始化
pnpm init2、安装 vite 和 @vitejs/plugin-vue
pnpm vite @vitejs/plugin-vue -D -w3.新建vite.config.ts 并配置 import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({plugins:[vue()]
})4、新建index.html
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><div id="app"></div><script src="main.ts" type="module"></script></body>
</html>注意: vite 是基于 esmodule 的 所以 type="module"
@vitejs/plugin-vue 会默认加载 examples 下的 index.html5、新建app.vue<template><div>app</div>
</template><script setup lang="ts">
</script><style scoped>
</style>6、新建main.tsimport {createApp} from 'vue'
import App from './app.vue'
const app = createApp(App)
app.mount('#app')7、因为直接引入.vue 文件 TS 会找不到对应的类型声明;所以需要新建 typings(命名没有明确规定,TS 会自动寻找.d.ts 文件)文件夹来专门放这些声明文件。declare module '*.vue' {import type { DefineComponent } from "vue";const component:DefineComponent<{},{},any>
}8、在package.json 文件中配置 scripts 脚本"scripts": {"dev": "vite"},
9.pnpm run dev 启动项目

8、初始化packages/components 文件夹

components 文件夹1.目录结构
-- components-- src-- index.ts-- input-number-- inputNumber.vue-- index.ts
-- index.ts
-- package.json2.inputNumber.vue 如下代码3.input-number/index.tsimport InputNumber from './inputNumber.vue'
InputNumber.install = (app) => {app.component(InputNumber.name, InputNumber)
}
export default InputNumber4.components/index.ts
import InputNumber from "./src/input-number/inputNumber.vue";
// 将所有的组件都放到这里进行导出
const components = [InputNumber
]
// 定义install方法const install = (app) => {// 之策所有组件components.forEach(item => {app.component(item.name, item)})
}const DHSUI = {install
}// 支持按需引入
export {InputNumber
}// 导出install方法
export default DHSUI

以input框为例:

src/input-number/inputNumber.vue

<template><el-input v-model="inputValue" class="customInput" v-bind="$attrs" :maxlength="props.maxlength" @input="handleInput"><template #suffix><span class="iconBtn add" @click="add"><el-icon><ArrowUp /></el-icon></span><span class="iconBtn decrease" @click="decrease"><el-icon><ArrowDown /></el-icon></span></template><template v-if="props.isAppend" #append>{{ props.appendText }}</template></el-input>
</template>
<script lang="ts">export default {name: 'InputNumber'
}
</script>
<script setup lang="ts">import { ElInput, ElIcon } from 'element-plus'import 'element-plus/dist/index.css'import {ArrowUp, ArrowDown } from '@element-plus/icons-vue'import { Decimal } from "decimal.js";import { onlyNumOnePoint, canBeMinus } from "@dhs-ui/utils";
import { ref, watch } from 'vue';// 根据最长字符,生成最大值
const generateMaxString = (maxLength: any) => {const maxValue = "9".repeat(maxLength as unknown as number);return maxValue;
};
interface Props {modelValue: string;isAppend?: boolean;appendText?: string;min?: number;max?: number;step?: number;maxlength?: number | string;precision?: number;
}
const props = withDefaults(defineProps<Props>(), {modelValue: "",precision: 4,isAppend: false
});
const emits = defineEmits(["input", "update:modelValue"]);
const inputValue = ref(props.modelValue);
const add = () => {const step = props.step || 1;let val = inputValue.value;if (!val) {val = "0";}let decimalVal = new Decimal(val);if (maxNum() && new Decimal(maxNum()) <= decimalVal) {inputValue.value = decimalVal.toFixed();} else {inputValue.value = decimalVal.plus(step).toFixed();}
};
const decrease = () => {const step = props.step || 1;let val = inputValue.value;if (!val && parseFloat(val) !== 0) val = "0";if (props.min || props.min === 0) {if (parseFloat(val) <= props.min) {val = props.min.toFixed();inputValue.value = val;return;}}let decimalVal = new Decimal(val);inputValue.value = decimalVal.sub(step).toFixed();
};
// number 小数点位数
const vilidateNumberInput = (value: any, number: number) => {let result: any;if (props.min || props.min === 0) {result = onlyNumOnePoint(value, number, !!number);} else {result = canBeMinus(value, number);}return result;
};const maxNum = () => {if (props.max) {return props.max.toFixed();} else {return props.maxlength ? generateMaxString(props.maxlength) : null;}
};
watch(() => props.modelValue,(	newValue: any) => {inputValue.value = newValue;},{ deep: true }
);
watch(inputValue, (nv: any) => {emits("update:modelValue", nv);
});
const handleInput = (val: any) => {inputValue.value = vilidateNumberInput(val, props.precision);emits("input", val);
};
</script><style scoped lang="scss">
.customInput {.iconBtn {position: absolute;right: 1px;display: block;width: 32px;background-color: #f5f7fa;border-left: 1px solid var(--default-border-color);height: 15px;line-height: 15px;cursor: pointer;}.add {top: 1px;border-radius: 0 4px 0 0;}.decrease {border-top: 1px solid var(--default-border-color);bottom: 1px;border-radius: 0 0 4px 0;}&.is-disabled {.add,.decrease {pointer-events: none;}}
}
</style>

9、在examples/app.vue测试组件

<template><div><InputNumber :modelValue="inputValue" /></div>
</template><script setup lang="ts">
import { ref } from 'vue';
import {InputNumber} from '../packages/components/src/input-number/inputNumber.vue';const inputValue = ref(0)
</script><style scoped></style>

出现了你所要的组件就说明可以进行打包了。

10、在components文件夹中打包组件

components 文件夹 新建vite.config.ts
这里我们选择打包cjs(CommonJS)和esm(ESModule)两种形式,cjs模式主要用于服务端引用(ssr),而esm就是我们现在经常使用的方式,它本身自带treeShaking而不需要额外配置按需引入(前提是你将模块分别导出),非常好用~

为了也能在ts项目中使用,还需要自动生成类型声明文件

pnpm add vite-plugin-dts@1.4.1 -D -wimport { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import dts from "vite-plugin-dts";
export default defineConfig({build: {//打包文件目录outDir: "es",//压缩//minify: false,rollupOptions: {//忽略打包的文件external: ["vue", "element-plus"],input: ["index.ts"],output: [{//打包格式format: "es",//打包后文件名entryFileNames: "[name].mjs",//让打包目录和我们目录对应preserveModules: false,exports: "named",//配置打包根目录dir: "../DHS-UI/es",},{//打包格式format: "cjs",//打包后文件名entryFileNames: "[name].js",//让打包目录和我们目录对应preserveModules: false,exports: "named",//配置打包根目录dir: "../DHS-UI/lib",},],},lib: {entry: "./index.ts",},},plugins: [vue(),dts({entryRoot: "./src",outputDir: ["../DHS-UI/es/src", "../DHS-UI/lib/src"],//指定使用的tsconfig.json为我们整个项目根目录下,如果不配置,你也可以在components下新建tsconfig.jsontsConfigFilePath: "../../tsconfig.json",}),],
});

配置同目录下的package.json文件

"scripts": {"build": "vite build"},

11、运行 build 进行打包,会在目录中生成打包好的包

11、打包好的文件,进行初始化

pnpm init

修改package.json 文件

{"name": "dhs-uii","version": "1.0.2","description": "","main": "lib/index.js","module": "es/index.mjs","files": ["es","lib"],"scripts": {"test": "echo "Error: no test specified" && exit 1"},"sideEffects": ["**/*.css"],"keywords": ["dhs-ui","vue3组件库","frontend","element-plus"],"author": "dengdeng","license": "ISC","typings": "lib/index.d.ts"
}

下一章进行发布,及遇到的问题。

感谢大佬文章:搭建一个组件库(vue3)_vue3组件库搭建-CSDN博客


http://www.ppmy.cn/server/152925.html

相关文章

敲击键盘的悸动:Linux命令从‘零‘开始的浪漫冒险

man&#xff08;查询指令&#xff09; 我们进行这个man的安装 yum install -y man-pages这里就是说明我们已经下载好了 Linux的命令有很多参数&#xff0c;我们不可能全记住&#xff0c;我们可以通过查看联机手册获取帮助。访问Linux手册页的命令是man 语法: man [选项] 命令 …

计算机网络——练习题

一. 单选题&#xff08;共27题&#xff0c;67.5分&#xff09; 1. (单选题)计算机网络的最突出的优点是____。 A. 运算速度快 B. 运算精度高 C. 存储容量大 D. 资源共享 我的答案: D:资源共享;正确答案: D:资源共享; 2.5分 答案解析&#xff1a; 2. (单选题)TCP/IP协…

LLMs之PDF:MinerU(将PDF文件转换成Markdown和JSON格式)的简介、安装和使用方法、案例应用之详细攻略

LLMs之PDF&#xff1a;MinerU(将PDF文件转换成Markdown和JSON格式)的简介、安装和使用方法、案例应用之详细攻略 目录 MinerU的简介 0、日志 1、MinerU 的主要特点 2、已知问题 MinerU 安装和使用方法 1、MinerU的三种体验方式 T1、在线演示 T2、快速CPU演示 T3、GPU …

D101【python 接口自动化学习】- pytest进阶之fixture用法

day101 pytest的fixture执行顺序 学习日期&#xff1a;20241218 学习目标&#xff1a;pytest基础用法 -- pytest的fixture执行顺序 学习笔记&#xff1a; fixtrue的作用范围 实战结果 import pytestpytest.fixture(scopesession) def test_session():print(我是 session f…

118.KubeSphere应用商店安装Harbor提示镜像拉取失败的解决办法

目录 1.原因 2.修改Docker镜像地址 1.原因 docker镜像拉取有问题&#xff0c;很多都拉取不了了。 2.修改Docker镜像地址 vi /etc/docker/daemon.json { "registry-mirrors": [ "https://dockerhub.xianfish.site", "https://yxzrazem.mirror.aliy…

jsp中的四个域对象(Spring MVC)

在Spring MVC中&#xff0c;Model中的数据会被自动放入到请求域&#xff08;Request Scope&#xff09;中。也就是说&#xff0c;当我们在控制器中使用model.addAttribute()时&#xff0c;这些属性会被放入到HttpServletRequest对象的属性中。 让我们通过代码来详细解释&#…

springboot/ssm七彩云南文化旅游网站Java代码编写web在线旅游景点管理

springboot/ssm七彩云南文化旅游网站Java代码编写web在线旅游景点管理 基于springboot(可改ssm)htmlvue项目 开发语言&#xff1a;Java 框架&#xff1a;springboot/可改ssm vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服务器&#xff1a;tomcat 数据库&…

【算法day20】回溯:子集与全排列问题

题目引用 非递减子序列全排列全排列II 1. 非递减子序列 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 数组中可能含有重复元素&#xff0c;如出现两个整数相等&#x…