likes
comments
collection
share

【前端工程化-组件库】从0-1构建Vue3组件库(打包发布)

作者站长头像
站长
· 阅读数 44

前言

前面篇章我们已经完成了开源组件库编码,接下来需要做的是打包发布到 NPM 上。我们来大致梳理下实现思路: 【前端工程化-组件库】从0-1构建Vue3组件库(打包发布)

编译打包

组件库需要做 2 种打包方式:全量打包、单组件打包,以提供给第三方调用者灵活选择使用。

打包方式

  • 全量打包:打包全部组件
  • 按需打包:单个组件打包,⽤户单独引⼊

全量组件打包

我们先来做全量组件打包,整体思路如下:

  1. 创建打包文件 build.js;
  2. 读取组件库的全局入口,设置为 entry;设置 outputDir 输出目录为 build 目录;
  3. 使用vite build配置编译参数;
  4. 使用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 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 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 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文件夹下自动编译生成如下文件: 【前端工程化-组件库】从0-1构建Vue3组件库(打包发布)

发布 NPM

组件库源码编译打包完成后,我们准备将打包好代码发到 NPM 上,激动人心的时刻即将到来!!我们先来梳理下发布步骤:

  1. 注册npm账号,前往 www.npmjs.com/ 注册(若已有账号跳过此步骤) 【前端工程化-组件库】从0-1构建Vue3组件库(打包发布)

  2. 设置npm registry源地址(若当前源已是npm可跳过此步骤)

npm config set registry https://registry.npmjs.org
  1. 账号登录及核对(若已登录并确认账号可跳过此步骤)
# 登录账号
npm login

# 核对账号
who am I
  1. npm publish 发布
# 此处只想发布打包编译后的文件
npm publish ./build

【前端工程化-组件库】从0-1构建Vue3组件库(打包发布) 看到上图信息表示发布成功啦👏👏,到 NPM 官网也可搜索验证:

【前端工程化-组件库】从0-1构建Vue3组件库(打包发布)

如果发布操作频繁,我们可以封装一个发布脚本,自动化执行发布,节省时间。脚本代码如下:

publish.sh:

npm 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 协同开发~~

本专栏文章:

  1. 【前端工程化-组件库】从0-1构建Vue3组件库(概要设计)
  2. 【前端工程化-组件库】从0-1构建Vue3组件库(组件开发)
  3. 【前端工程化-组件库】从0-1构建Vue3组件库(单元测试)
  4. 【前端工程化-组件库】从0-1构建Vue3组件库(打包发布)
转载自:https://juejin.cn/post/7255514764754894907
评论
请登录