前言
前面篇章我们已经完成了开源组件库编码,接下来需要做的是打包发布到 NPM 上。我们来大致梳理下实现思路:
编译打包
组件库需要做 2 种打包方式:全量打包、单组件打包,以提供给第三方调用者灵活选择使用。
打包方式:
- 全量打包:打包全部组件
- 按需打包:单个组件打包,⽤户单独引⼊
全量组件打包
我们先来做全量组件打包,整体思路如下:
- 创建打包文件 build.js;
- 读取组件库的全局入口,设置为 entry;设置 outputDir 输出目录为 build 目录;
- 使用vite build配置编译参数;
- 使用node fs生成package.json。
具体代码实现如下:
scripts/build.js
const path = require("path")const fs = require("fs-extra")// 引入 vite 的 build 方法,进行编译构建const { defineConfig, build } = require("vite")const vue = require("@vitejs/plugin-vue")const vueJSX = require("@vitejs/plugin-vue-jsx")const version = require("../package.json").version// 基础配置const baseConfig = defineConfig({publicDir: false,plugins: [vue(), vueJSX()]})const rollupOptions = defineConfig({// that shouldn't be bundledexternal: ["vue"],globals: {vue: "Vue"}})// 组件库全局入口const compontsDir = path.resolve(__dirname, "../packages/components")// 输出目录const outputDir = path.resolve(__dirname, "../build")// 生成 package.jsonconst createPackageJson = name => {const fileStr = `{"name": "${name ? name : "pc-vue3-ui"}","version": "${version}","description": "Vue3组件库","main": "${name ? "index.umd.js" : "pc-vue3-ui.umd.js"}","module":"${name ? "index.mjs" : "pc-vue3-ui.mjs"}","repository": {"type": "git","url": "git+https://github.com/GGXXMM/vue3-ui.git"},"keywords": ["vue3", "组件库", "UI"],"author": "guoxinming","license": "ISC"}`// 单个组件 or 全量const filePath = path.resolve(outputDir,name ? `${name}/package.json` : `package.json`)fs.outputFile(filePath, fileStr, "utf-8")}/** 全量构建 */const buildAll = async () => {await build(defineConfig({...baseConfig,build: {lib: {entry: compontsDir,name: "pc-vue3-ui",fileName: "pc-vue3-ui",formats: ["es", "umd"]},rollupOptions,outDir: outputDir}}))createPackageJson()}const path = require("path") const fs = require("fs-extra") // 引入 vite 的 build 方法,进行编译构建 const { defineConfig, build } = require("vite") const vue = require("@vitejs/plugin-vue") const vueJSX = require("@vitejs/plugin-vue-jsx") const version = require("../package.json").version // 基础配置 const baseConfig = defineConfig({ publicDir: false, plugins: [vue(), vueJSX()] }) const rollupOptions = defineConfig({ // that shouldn't be bundled external: ["vue"], globals: { vue: "Vue" } }) // 组件库全局入口 const compontsDir = path.resolve(__dirname, "../packages/components") // 输出目录 const outputDir = path.resolve(__dirname, "../build") // 生成 package.json const createPackageJson = name => { const fileStr = `{ "name": "${name ? name : "pc-vue3-ui"}", "version": "${version}", "description": "Vue3组件库", "main": "${name ? "index.umd.js" : "pc-vue3-ui.umd.js"}", "module":"${name ? "index.mjs" : "pc-vue3-ui.mjs"}", "repository": { "type": "git", "url": "git+https://github.com/GGXXMM/vue3-ui.git" }, "keywords": ["vue3", "组件库", "UI"], "author": "guoxinming", "license": "ISC" } ` // 单个组件 or 全量 const filePath = path.resolve( outputDir, name ? `${name}/package.json` : `package.json` ) fs.outputFile(filePath, fileStr, "utf-8") } /** 全量构建 */ const buildAll = async () => { await build( defineConfig({ ...baseConfig, build: { lib: { entry: compontsDir, name: "pc-vue3-ui", fileName: "pc-vue3-ui", formats: ["es", "umd"] }, rollupOptions, outDir: outputDir } }) ) createPackageJson() }const path = require("path") const fs = require("fs-extra") // 引入 vite 的 build 方法,进行编译构建 const { defineConfig, build } = require("vite") const vue = require("@vitejs/plugin-vue") const vueJSX = require("@vitejs/plugin-vue-jsx") const version = require("../package.json").version // 基础配置 const baseConfig = defineConfig({ publicDir: false, plugins: [vue(), vueJSX()] }) const rollupOptions = defineConfig({ // that shouldn't be bundled external: ["vue"], globals: { vue: "Vue" } }) // 组件库全局入口 const compontsDir = path.resolve(__dirname, "../packages/components") // 输出目录 const outputDir = path.resolve(__dirname, "../build") // 生成 package.json const createPackageJson = name => { const fileStr = `{ "name": "${name ? name : "pc-vue3-ui"}", "version": "${version}", "description": "Vue3组件库", "main": "${name ? "index.umd.js" : "pc-vue3-ui.umd.js"}", "module":"${name ? "index.mjs" : "pc-vue3-ui.mjs"}", "repository": { "type": "git", "url": "git+https://github.com/GGXXMM/vue3-ui.git" }, "keywords": ["vue3", "组件库", "UI"], "author": "guoxinming", "license": "ISC" } ` // 单个组件 or 全量 const filePath = path.resolve( outputDir, name ? `${name}/package.json` : `package.json` ) fs.outputFile(filePath, fileStr, "utf-8") } /** 全量构建 */ const buildAll = async () => { await build( defineConfig({ ...baseConfig, build: { lib: { entry: compontsDir, name: "pc-vue3-ui", fileName: "pc-vue3-ui", formats: ["es", "umd"] }, rollupOptions, outDir: outputDir } }) ) createPackageJson() }
单组件打包
单组件打包,与全量打包的主要差异是获取打包入口和出口。入口entry是单组件的index.ts,输出outputDir则设置为 build 目录下的具体组件。代码实现如下所示:
const path = require("path")const fs = require("fs-extra")// 引入 vite 的 build 方法,进行编译构建const { defineConfig, build } = require("vite")const vue = require("@vitejs/plugin-vue")const vueJSX = require("@vitejs/plugin-vue-jsx")const version = require("../package.json").version// 基础配置const baseConfig = defineConfig({publicDir: false,plugins: [vue(), vueJSX()]})const rollupOptions = defineConfig({// that shouldn't be bundledexternal: ["vue"],globals: {vue: "Vue"}})// 组件库全局入口const compontsDir = path.resolve(__dirname, "../packages/components")// 输出目录const outputDir = path.resolve(__dirname, "../build")// 生成 package.jsonconst createPackageJson = name => {const fileStr = `{"name": "${name ? name : "pc-vue3-ui"}","version": "${version}","description": "Vue3组件库","main": "${name ? "index.umd.js" : "pc-vue3-ui.umd.js"}","module":"${name ? "index.mjs" : "pc-vue3-ui.mjs"}","repository": {"type": "git","url": "git+https://github.com/GGXXMM/vue3-ui.git"},"keywords": ["vue3", "组件库", "UI"],"author": "guoxinming","license": "ISC"}`// 单个组件 or 全量const filePath = path.resolve(outputDir,name ? `${name}/package.json` : `package.json`)fs.outputFile(filePath, fileStr, "utf-8")}/** 单组件按需构建 */const buildSingle = async name => {await build(defineConfig({...baseConfig,build: {lib: {entry: path.resolve(compontsDir, name),name: "index",fileName: "index",formats: ["es", "umd"]},rollupOptions,outDir: path.resolve(outputDir, name)}}))createPackageJson(name)}const path = require("path") const fs = require("fs-extra") // 引入 vite 的 build 方法,进行编译构建 const { defineConfig, build } = require("vite") const vue = require("@vitejs/plugin-vue") const vueJSX = require("@vitejs/plugin-vue-jsx") const version = require("../package.json").version // 基础配置 const baseConfig = defineConfig({ publicDir: false, plugins: [vue(), vueJSX()] }) const rollupOptions = defineConfig({ // that shouldn't be bundled external: ["vue"], globals: { vue: "Vue" } }) // 组件库全局入口 const compontsDir = path.resolve(__dirname, "../packages/components") // 输出目录 const outputDir = path.resolve(__dirname, "../build") // 生成 package.json const createPackageJson = name => { const fileStr = `{ "name": "${name ? name : "pc-vue3-ui"}", "version": "${version}", "description": "Vue3组件库", "main": "${name ? "index.umd.js" : "pc-vue3-ui.umd.js"}", "module":"${name ? "index.mjs" : "pc-vue3-ui.mjs"}", "repository": { "type": "git", "url": "git+https://github.com/GGXXMM/vue3-ui.git" }, "keywords": ["vue3", "组件库", "UI"], "author": "guoxinming", "license": "ISC" } ` // 单个组件 or 全量 const filePath = path.resolve( outputDir, name ? `${name}/package.json` : `package.json` ) fs.outputFile(filePath, fileStr, "utf-8") } /** 单组件按需构建 */ const buildSingle = async name => { await build( defineConfig({ ...baseConfig, build: { lib: { entry: path.resolve(compontsDir, name), name: "index", fileName: "index", formats: ["es", "umd"] }, rollupOptions, outDir: path.resolve(outputDir, name) } }) ) createPackageJson(name) }const path = require("path") const fs = require("fs-extra") // 引入 vite 的 build 方法,进行编译构建 const { defineConfig, build } = require("vite") const vue = require("@vitejs/plugin-vue") const vueJSX = require("@vitejs/plugin-vue-jsx") const version = require("../package.json").version // 基础配置 const baseConfig = defineConfig({ publicDir: false, plugins: [vue(), vueJSX()] }) const rollupOptions = defineConfig({ // that shouldn't be bundled external: ["vue"], globals: { vue: "Vue" } }) // 组件库全局入口 const compontsDir = path.resolve(__dirname, "../packages/components") // 输出目录 const outputDir = path.resolve(__dirname, "../build") // 生成 package.json const createPackageJson = name => { const fileStr = `{ "name": "${name ? name : "pc-vue3-ui"}", "version": "${version}", "description": "Vue3组件库", "main": "${name ? "index.umd.js" : "pc-vue3-ui.umd.js"}", "module":"${name ? "index.mjs" : "pc-vue3-ui.mjs"}", "repository": { "type": "git", "url": "git+https://github.com/GGXXMM/vue3-ui.git" }, "keywords": ["vue3", "组件库", "UI"], "author": "guoxinming", "license": "ISC" } ` // 单个组件 or 全量 const filePath = path.resolve( outputDir, name ? `${name}/package.json` : `package.json` ) fs.outputFile(filePath, fileStr, "utf-8") } /** 单组件按需构建 */ const buildSingle = async name => { await build( defineConfig({ ...baseConfig, build: { lib: { entry: path.resolve(compontsDir, name), name: "index", fileName: "index", formats: ["es", "umd"] }, rollupOptions, outDir: path.resolve(outputDir, name) } }) ) createPackageJson(name) }
完整代码
const path = require("path")const fs = require("fs-extra")// 引入 vite 的 build 方法,进行编译构建const { defineConfig, build } = require("vite")const vue = require("@vitejs/plugin-vue")const vueJSX = require("@vitejs/plugin-vue-jsx")const version = require("../package.json").version// 基础配置const baseConfig = defineConfig({publicDir: false,plugins: [vue(), vueJSX()]})const rollupOptions = defineConfig({// that shouldn't be bundledexternal: ["vue"],globals: {vue: "Vue"}})// 组件库全局入口const compontsDir = path.resolve(__dirname, "../packages/components")// 输出目录const outputDir = path.resolve(__dirname, "../build")// 生成 package.jsonconst createPackageJson = name => {const fileStr = `{"name": "${name ? name : "pc-vue3-ui"}","version": "${version}","description": "Vue3组件库","main": "${name ? "index.umd.js" : "pc-vue3-ui.umd.js"}","module":"${name ? "index.mjs" : "pc-vue3-ui.mjs"}","repository": {"type": "git","url": "git+https://github.com/GGXXMM/vue3-ui.git"},"keywords": ["vue3", "组件库", "UI"],"author": "guoxinming","license": "ISC"}`// 单个组件 or 全量const filePath = path.resolve(outputDir,name ? `${name}/package.json` : `package.json`)fs.outputFile(filePath, fileStr, "utf-8")}/** 单组件按需构建 */const buildSingle = async name => {await build(defineConfig({...baseConfig,build: {lib: {entry: path.resolve(compontsDir, name),name: "index",fileName: "index",formats: ["es", "umd"]},rollupOptions,outDir: path.resolve(outputDir, name)}}))createPackageJson(name)}/** 全量构建 */const buildAll = async () => {await build(defineConfig({...baseConfig,build: {lib: {entry: compontsDir,name: "pc-vue3-ui",fileName: "pc-vue3-ui",formats: ["es", "umd"]},rollupOptions,outDir: outputDir}}))createPackageJson()}const buildLib = async () => {await buildAll()// 按需打包fs.readdirSync(compontsDir).filter(name => {// 获取组件的目录const componentDir = path.resolve(compontsDir, name)const isDir = fs.lstatSync(componentDir).isDirectory()return isDir && fs.readdirSync(componentDir).includes("index.ts")}).forEach(async name => {await buildSingle(name)})}buildLib()const path = require("path") const fs = require("fs-extra") // 引入 vite 的 build 方法,进行编译构建 const { defineConfig, build } = require("vite") const vue = require("@vitejs/plugin-vue") const vueJSX = require("@vitejs/plugin-vue-jsx") const version = require("../package.json").version // 基础配置 const baseConfig = defineConfig({ publicDir: false, plugins: [vue(), vueJSX()] }) const rollupOptions = defineConfig({ // that shouldn't be bundled external: ["vue"], globals: { vue: "Vue" } }) // 组件库全局入口 const compontsDir = path.resolve(__dirname, "../packages/components") // 输出目录 const outputDir = path.resolve(__dirname, "../build") // 生成 package.json const createPackageJson = name => { const fileStr = `{ "name": "${name ? name : "pc-vue3-ui"}", "version": "${version}", "description": "Vue3组件库", "main": "${name ? "index.umd.js" : "pc-vue3-ui.umd.js"}", "module":"${name ? "index.mjs" : "pc-vue3-ui.mjs"}", "repository": { "type": "git", "url": "git+https://github.com/GGXXMM/vue3-ui.git" }, "keywords": ["vue3", "组件库", "UI"], "author": "guoxinming", "license": "ISC" } ` // 单个组件 or 全量 const filePath = path.resolve( outputDir, name ? `${name}/package.json` : `package.json` ) fs.outputFile(filePath, fileStr, "utf-8") } /** 单组件按需构建 */ const buildSingle = async name => { await build( defineConfig({ ...baseConfig, build: { lib: { entry: path.resolve(compontsDir, name), name: "index", fileName: "index", formats: ["es", "umd"] }, rollupOptions, outDir: path.resolve(outputDir, name) } }) ) createPackageJson(name) } /** 全量构建 */ const buildAll = async () => { await build( defineConfig({ ...baseConfig, build: { lib: { entry: compontsDir, name: "pc-vue3-ui", fileName: "pc-vue3-ui", formats: ["es", "umd"] }, rollupOptions, outDir: outputDir } }) ) createPackageJson() } const buildLib = async () => { await buildAll() // 按需打包 fs.readdirSync(compontsDir) .filter(name => { // 获取组件的目录 const componentDir = path.resolve(compontsDir, name) const isDir = fs.lstatSync(componentDir).isDirectory() return isDir && fs.readdirSync(componentDir).includes("index.ts") }) .forEach(async name => { await buildSingle(name) }) } buildLib()const path = require("path") const fs = require("fs-extra") // 引入 vite 的 build 方法,进行编译构建 const { defineConfig, build } = require("vite") const vue = require("@vitejs/plugin-vue") const vueJSX = require("@vitejs/plugin-vue-jsx") const version = require("../package.json").version // 基础配置 const baseConfig = defineConfig({ publicDir: false, plugins: [vue(), vueJSX()] }) const rollupOptions = defineConfig({ // that shouldn't be bundled external: ["vue"], globals: { vue: "Vue" } }) // 组件库全局入口 const compontsDir = path.resolve(__dirname, "../packages/components") // 输出目录 const outputDir = path.resolve(__dirname, "../build") // 生成 package.json const createPackageJson = name => { const fileStr = `{ "name": "${name ? name : "pc-vue3-ui"}", "version": "${version}", "description": "Vue3组件库", "main": "${name ? "index.umd.js" : "pc-vue3-ui.umd.js"}", "module":"${name ? "index.mjs" : "pc-vue3-ui.mjs"}", "repository": { "type": "git", "url": "git+https://github.com/GGXXMM/vue3-ui.git" }, "keywords": ["vue3", "组件库", "UI"], "author": "guoxinming", "license": "ISC" } ` // 单个组件 or 全量 const filePath = path.resolve( outputDir, name ? `${name}/package.json` : `package.json` ) fs.outputFile(filePath, fileStr, "utf-8") } /** 单组件按需构建 */ const buildSingle = async name => { await build( defineConfig({ ...baseConfig, build: { lib: { entry: path.resolve(compontsDir, name), name: "index", fileName: "index", formats: ["es", "umd"] }, rollupOptions, outDir: path.resolve(outputDir, name) } }) ) createPackageJson(name) } /** 全量构建 */ const buildAll = async () => { await build( defineConfig({ ...baseConfig, build: { lib: { entry: compontsDir, name: "pc-vue3-ui", fileName: "pc-vue3-ui", formats: ["es", "umd"] }, rollupOptions, outDir: outputDir } }) ) createPackageJson() } const buildLib = async () => { await buildAll() // 按需打包 fs.readdirSync(compontsDir) .filter(name => { // 获取组件的目录 const componentDir = path.resolve(compontsDir, name) const isDir = fs.lstatSync(componentDir).isDirectory() return isDir && fs.readdirSync(componentDir).includes("index.ts") }) .forEach(async name => { await buildSingle(name) }) } buildLib()
执行完打包脚本 scripts/build.js,build文件夹下自动编译生成如下文件:
发布 NPM
组件库源码编译打包完成后,我们准备将打包好代码发到 NPM 上,激动人心的时刻即将到来!!我们先来梳理下发布步骤:
-
注册npm账号,前往 www.npmjs.com/ 注册(若已有账号跳过此步骤)
-
设置npm registry源地址(若当前源已是npm可跳过此步骤)
npm config set registry https://registry.npmjs.orgnpm config set registry https://registry.npmjs.orgnpm config set registry https://registry.npmjs.org
- 账号登录及核对(若已登录并确认账号可跳过此步骤)
# 登录账号npm login# 核对账号who am I# 登录账号 npm login # 核对账号 who am I# 登录账号 npm login # 核对账号 who am I
- npm publish 发布
# 此处只想发布打包编译后的文件npm publish ./build# 此处只想发布打包编译后的文件 npm publish ./build# 此处只想发布打包编译后的文件 npm publish ./build
看到上图信息表示发布成功啦??,到 NPM 官网也可搜索验证:
如果发布操作频繁,我们可以封装一个发布脚本,自动化执行发布,节省时间。脚本代码如下:
publish.sh:
npm config set registry https://registry.npmjs.orgnpm login # 登录,如果有 OTP, 邮箱会接收到验证码,输入即可# 登录成功后,短时间内会保存状态,可以直接npm publish ./build # 可能会报错提示已存在,升级个版本号再发# 还原镜像地址npm config set registry https://registry.npmmirror.comnpm config set registry https://registry.npmjs.org npm login # 登录,如果有 OTP, 邮箱会接收到验证码,输入即可 # 登录成功后,短时间内会保存状态,可以直接 npm publish ./build # 可能会报错提示已存在,升级个版本号再发 # 还原镜像地址 npm config set registry https://registry.npmmirror.comnpm config set registry https://registry.npmjs.org npm login # 登录,如果有 OTP, 邮箱会接收到验证码,输入即可 # 登录成功后,短时间内会保存状态,可以直接 npm publish ./build # 可能会报错提示已存在,升级个版本号再发 # 还原镜像地址 npm config set registry https://registry.npmmirror.com
总结
行文至此,我们已实现了从0-1构建Vue3组件,从概要设计到组件开发,再到单元测试和打包发布的全流程。(Vue3组件库专题完美收官啦!!)剩下的工作就是继续完善组件库,有兴趣参与的小伙伴欢迎到 github 协同开发~~
本专栏文章:
© 版权声明
文章版权归作者所有,未经允许请勿转载,侵权请联系 admin@trc20.tw 删除。
THE END