Skip to content

从0到1搭建Vue组件库10:如何实现组件的按需打包📦

image

0 上期直播回顾

上一次带着大家将【Vue DevUI开源指南】系列直播1-6期的所有内容串起来了,并诞生了mini-vue-devui项目,mini-vue-devui是一个迷你版的组件库产品,目前包含:

  1. vite+vue3+ts+jsx+sass的基础工程
  2. 基于vitepress+vitepress-theme-demoblock的文档系统
  3. jest+@vue/test-utils的单元测试环境
  4. 一个用jsx编写的初版Tree组件
  5. 一个初版devui-cli脚手架

直播的最后,我们快速过了一下单元测试环境搭建的流程,但并没有手把手教,而是留了一个作业,这个作业最后由QbjGKNick同学率先提交。

最终的 效果如下:

image.png

上一次直播的文章:

  1. 手把手带你从0到1搭建一个vue3组件库:mini-vue-devui
  2. 【我要做开源】给 vue devui 组件库项目增加单元测试

但这就是一个组件库的全部了吗?

这只是冰山一角而已。

这一期我们将继续之前的直播,完善mini-vue-devui项目,打通组件库按需构建的流程,并增加Monorepo支持。

1 组件库入口文件

devui/vue-devui.ts

import type { App } from 'vue'
import TreeInstall, { Tree } from './tree'

const installs = [
  TreeInstall,
]

export {
  Tree,
}

export default {
  version: '0.0.1',
  install(app: App): void {
    installs.forEach((p) => app.use(p as any))
  }
}

2 增加全量构建脚本

新增devui-cli/commands/build.js文件

const path = require('path')
const fs = require('fs')
const { defineConfig, build } = require('vite')
const vue = require('@vitejs/plugin-vue')
const vueJsx = require('@vitejs/plugin-vue-jsx')

const entryDir = path.resolve(__dirname, 'devui-vue/devui')
const outputDir = path.resolve(__dirname, 'devui-vue/build')

const baseConfig = defineConfig({
  configFile: false,
  publicDir: false,
  plugins: [ vue(), vueJsx() ]
})

const rollupOptions = {
  external: ['vue', 'vue-router'],
  output: {
    globals: {
      vue: 'Vue'
    }
  }
}

//全量构建
const buildAll = async () => {
  await build(defineConfig({
    ...baseConfig,
    build: {
      rollupOptions,
      lib: {
        entry: path.resolve(entryDir, 'vue-devui.ts'),
        name: 'vue-devui',
        fileName: 'vue-devui',
        formats: ['es', 'umd']
      },
      outDir: outputDir
    }
  }))
}

const buildLib = async () => {
  await buildAll()
}

buildLib()

3 修改package.json

"main": "vue-devui.umd.js",
"module": "vue-devui.es.js",

"build:components": "node ./devui-cli/commands/build.js",
"build:lib": "yarn predev && yarn build:components && cp package.json build && cp README.md build",

测试全量组件库构建

yarn build:lib

使用组件库

src/main.ts

import { createApp } from 'vue'
import App from './App.vue'

// import Tree from '../devui/tree'
import MiniVueDevUI from '../build'

createApp(App)
// .use(Tree)
 .use(MiniVueDevUI)
.mount('#app')

启动

yarn dev

4 增加按需构建脚本

修改devui-cli/commands/build.js文件

const fsExtra = require('fs-extra')

// 单组件按需构建
const buildSingle = async (name) => {
  await build(defineConfig({
    ...baseConfig,
    build: {
      rollupOptions,
      lib: {
        entry: path.resolve(entryDir, name),
        name: 'index',
        fileName: 'index',
        formats: ['es', 'umd']
      },
      outDir: path.resolve(outputDir, name)
    }
  }))
}

// 生成组件的 package.json 文件
const createPackageJson = (name) => {
  const fileStr = `{
  "name": "${name}",
  "version": "0.0.0",
  "main": "index.umd.js",
  "module": "index.es.js",
  "style": "style.css"
}`

  fsExtra.outputFile(
    path.resolve(outputDir, `${name}/package.json`),
    fileStr,
    'utf-8'
  )
}

const buildLib = async () => {
  await buildAll()

  // 获取组件名称组成的数组
  const components = fs.readdirSync(entryDir).filter(name => {
    const componentDir = path.resolve(entryDir, name)
    const isDir = fs.lstatSync(componentDir).isDirectory()
    return isDir && fs.readdirSync(componentDir).includes('index.ts')
  })

  // 循环一个一个组件构建
  for(const name of components) {
    // 构建单组件
    await buildSingle(name)
    
    // 生成组件的 package.json 文件
    createPackageJson(name)
  }
}

buildLib()

测试按需构建

yarn build:lib

使用组件库

src/main.ts

import { createApp } from 'vue'
import App from './App.vue'

// import Tree from '../devui/tree'
// import MiniVueDevUI from '../build'
import Tree from '../build/tree'

createApp(App)
// .use(Tree)
// .use(MiniVueDevUI)
.use(Tree)
.mount('#app')

启动

yarn dev

正常!

参考:

发布时间:

Made with ❤ by