First, the establishment of the basic framework

1. Create a basic Vue3. X + TS project using vuE-CLI scaffolding

2. Organize the project directory structure

2.1. Change the SRC directory to examples

Examples directory as the file entry directory for all sample pages, while cleaning the folder of some useless sample files added by vue-CLI default,

Since the SRC directory has been modified, vue.config.js needs to be modified to enable scaffolding to start the project correctly:

module.exports = {
  pages: {
    examples: {
      // Page entry
      entry: 'examples/main.ts'.// Template source
      template: 'public/index.html'.// Output in dist/index.html
      filename: 'index.html'.// When using the title option,
      / / title of the template tag needs to be < title > < % = htmlWebpackPlugin. Options. The title % > < / title >
      title: 'Examples'}}}Copy the code

2.2. Create packages directories

The Packages directory contains all components, entries, and so on in the component library

2.3 create the Typings directory

Create the typing directory under the project root and move the shims-vue.d.ts file under the examples directory to the current directory.

2.4. Modify tsconfig.json file

Modify the include field in the tsconfig.json file to include the modified examples and Packages directories. The Typings directory also needs to be added to this configuration.

{
    "include": [
        "examples/**/*.ts"."examples/**/*.tsx"."examples/**/*.vue"."packages/**/*.ts"."packages/**/*.tsx"."packages/**/*.vue"."typings/**/*.ts"]}Copy the code

2.5. Add some style specification and style specification configuration files

This area is set up according to the team coding style or individual coding style, so I won’t go into too much detail.

Summary:

After modification, the directory structure of the whole project is roughly as follows:

. ├ ─ ─ examples │ ├ ─ ─ App. Vue │ └ ─ ─ main. Ts ├ ─ ─ packages │ ├ ─ ─ but ts ├ ─ ─ public │ ├ ─ ─ the favicon. Ico │ └ ─ ─ index. The HTML ├ ─ ─ Typings │ └ ─ ─ shims - vue. Which s ├ ─ ─ the README. Md ├ ─ ─ Babel. Config. Js ├ ─ ─ tsconfig. Json ├ ─ ─ package. The json └ ─ ─ vue. Config. JsCopy the code

Component creation

1. Component A

Create the directory comp-a under the Packages directory and create the SRC /comp-a.vue and index.ts files under this folder

Comp-a. vue file code is as follows

<template>
  <div style="width: 200px; background-color: red">comp-a</div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
  name: 'CompA'
})
</script>

Copy the code

Index. ts serves as the entry point for the component to export the component and add the install method

import { App } from 'vue'

import CompA from './src/comp-a.vue'

// Add a registration method for the component
CompA.install = (app: App): void= > {
  app.component(CompA.name, CompA)
}

export default CompA

Copy the code

B

The creation of component B is similar to the creation of component A. Without going into too much detail, just show the code

comp-b.vue

<template>
  <div class="comp-b">comp-b</div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
  name: 'CompB'
})
</script>

<style lang="scss">
.comp-b {
  width: 200px;
  background-color: green;
}
</style>

Copy the code

index.ts

import { App } from 'vue'

import CompB from './src/comp-b.vue'

// Add a registration method for the component
CompB.install = (app: App): void= > {
  app.component(CompB.name, CompB)
}

export default CompB

Copy the code

3. Component entry

Write code in packages/index.ts as a unified export directory for exports throughout the component library

import { App } from 'vue'

import CompA from './comp-a'
import CompB from './comp-b'

const components = [CompA, CompB]

// Register all components
const install = function (app: App) :void {
  components.forEach((component) = > {
    app.component(component.name, component)
  })
}

// Export the registration method
export default {
  install
}

// Export a single component
export { CompA, CompB }

Copy the code

Three, the use of components

1. Global use

1.1. Import components in examples/main.ts and register all components with app.use

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

import Components from '.. /packages'

const examples = createApp(App)
examples.use(Components)
examples.mount('#app')

export default examples

Copy the code

1.2. Use:

<template>
  <div id="#app">
    <comp-a />
    <comp-b />
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
  name: 'App'
})
</script>

Copy the code

1.3 Effect:

2. Reference in a local component

2.1. Delete global references from main.ts

2.2. Import and register components in the page

<template> <div id="#app"> <comp-a /> <comp-b /> </div> </template> <script lang="ts"> import { defineComponent } from 'vue' import { CompA, CompB } from '.. /packages' export default defineComponent({ name: 'App', components: { CompA, CompB } }) </script>Copy the code

2.3 Effect:

Four, component library packaging

1. Vue-cli package

Vue – cli tool provides a packaged vue for lib library commands details: cli.vuejs.org/zh/guide/bu…

Add a command under the script field of package.json:

{
  "scripts": {
    "build:lib": "vue-cli-service build --target lib ./packages/index.ts"}}Copy the code

Using the command line

yarn build:lib
Copy the code

or

npm run build:lib
Copy the code

Can be packaged as a library file

The demo. HTML file describes how to use this library in an HTML interface.

2. Rollup

Vue-cli packaging only generates CJS and UMD libraries, but for a component library you might need esM libraries, and perhaps.d.ts type declaration files if used in a typescript project. Vue-cli packaging does not provide this support for us, so we need to use rollup to make our library better supported

Install the rollup dependency

  • Rollup Package tool
  • Rollup-plugin – Terser code compression tool
  • Plugin-node-resolve Plugin for locating third-party tool modules
  • Rollup-plugin-typescript processes typescript files
  • Rollup-plugin-vue VUE processing plug-in
  • Rollup-plugin-css-only Processing style (not used in this article)
npm install -D rollup rollup-plugin-terser @rollup/plugin-node-resolve rollup-plugin-typescript2 rollup-plugin-vue rollup-plugin-css-only
Copy the code

or

yarn add -D rollup rollup-plugin-terser @rollup/plugin-node-resolve rollup-plugin-typescript2 rollup-plugin-vue rollup-plugin-css-only
Copy the code

2.2, the ESM – bundle

  • Create a build folder in the project and directory to store the script files needed for packaging
  • Create rollup.config.esm-bundle.js in the build folder and use rollup to package the ESM module with all the components and the type declaration files for each component

Rollup.config.esm-bundle. js configuration file: rollup.config.esm-bundle.js

/* eslint-disable */
import path from 'path'
import pkg from '.. /package.json'
import { terser } from 'rollup-plugin-terser'
import { nodeResolve } from '@rollup/plugin-node-resolve'
import vue from 'rollup-plugin-vue'
import typescript from 'rollup-plugin-typescript2'

const deps = Object.keys(pkg.dependencies || {})

export default[{input: path.resolve(__dirname, '.. /packages/index.ts'), / / the entry
    output: {
      format: 'es'.file: 'lib/index.esm.js'
    },
    plugins: [
      terser(),
      nodeResolve(),
      vue({
        target: 'browser'.// Server render using 'node'
        css: false.exposeFilename: false
      }),
      typescript({
        tsconfigOverride: {
          compilerOptions: {
            declaration: true
          },
          include: ['packages/**/*'.'typings/vue-shim.d.ts'].exclude: ['node_modules']},abortOnError: false})]./ / the plugin
    external(id) {
      return /^vue/.test(id) || deps.some((k) = > new RegExp(A '^' + k).test(id))
    }
  }
]

Copy the code
  • Add a script to the package.json file to package the project
{
  "scripts": {
    "build:esm-bundle": "rollup --config ./build/rollup.config.esm-bundle.js"}}Copy the code

2.3, the ESM

A full module of ESM-bundle has been created above, but the component library still needs to be loaded on demand most of the time, so it needs to be packaged for individual widgets. Since we specify the entry file of the component as index.ts, So it’s much easier to package rollup.config.esm.js

/* eslint-disable */
import path from 'path'
import pkg from '.. /package.json'
import { terser } from 'rollup-plugin-terser'
import { nodeResolve } from '@rollup/plugin-node-resolve'
import vue from 'rollup-plugin-vue'
import typescript from 'rollup-plugin-typescript2'

const deps = Object.keys(pkg.dependencies || {})

import fs from 'fs'

const readdirectory = (dir) = > { // Recursive directory structure
  const list = fs.readdirSync(dir)
  const ret = []
  list.forEach((filename) = > {
    const dist = path.resolve(dir, filename)
    const stat = fs.statSync(dist)
    if (stat.isFile()) {
      if (filename === 'index.ts') {
        ret.push(dist)
      }
    } else{ ret.push(... readdirectory(dist)) } })return ret
}

const indexlist = readdirectory(path.resolve(__dirname, '.. /packages')) // Read all entry files

const configs = indexlist
  .filter((dist) = > dist.indexOf('packages\\index.ts') = = = -1) // Ignores packages/index.ts and only compiles submodules
  .map((dist) = > ({
    input: dist, / / the entry
    output: {
      format: 'es'.file: dist.replace('packages'.'lib').replace('.ts'.'.js') // Set the output directory
    },
    plugins: [
      terser(),
      nodeResolve(),
      vue({
        target: 'browser'.// Server render using 'node'
        css: false.exposeFilename: false
      }),
      typescript({
        tsconfigOverride: {
          compilerOptions: {
            declaration: false // Compile the module without output type declarations
          },
          include: ['packages/**/*'.'typings/vue-shim.d.ts'].exclude: ['node_modules']},abortOnError: false})]./ / the plugin
    external(id) {
      return /^vue/.test(id) || deps.some((k) = > new RegExp(A '^' + k).test(id))
    }
  }))

export default configs

Copy the code
  • Add the script in package.json
{
  "scripts": {
    "build:esm": "rollup --config ./build/rollup.config.esm.js"}}Copy the code

2.4, UMD

  • The UMD script is basically the same as the ESM script, except that the output.name attribute is specified
/* eslint-disable */
import path from 'path'
import pkg from '.. /package.json'
import { terser } from 'rollup-plugin-terser'
import { nodeResolve } from '@rollup/plugin-node-resolve'
import vue from 'rollup-plugin-vue'
import typescript from 'rollup-plugin-typescript2'

const deps = Object.keys(pkg.dependencies || {})

export default[{input: path.resolve(__dirname, '.. /packages/index.ts'), / / the entry
    output: {
      format: 'umd'./ / umd
      file: 'lib/index.umd.js'.// Output file
      name: pkg.name / / specified name
    },
    plugins: [
      terser(),
      nodeResolve(),
      vue({
        target: 'browser'.// Server render using 'node'
        css: false.exposeFilename: false
      }),
      typescript({
        tsconfigOverride: {
          compilerOptions: {
            declaration: true
          },
          include: ['packages/**/*'.'typings/vue-shim.d.ts'].exclude: ['node_modules']},abortOnError: false})]./ / the plugin
    external(id) {
      return /^vue/.test(id) || deps.some((k) = > new RegExp(A '^' + k).test(id))
    }
  }
]

Copy the code

2.5. Combination and packaging

  • Add scripts to Packages to package all library files at once, rather than executing individual packaging commands each time
{
    "scripts": {
        "build": "yarn build:esm && yarn build:esm-bundle && yarn build:umd"."build:esm-bundle": "rollup --config ./build/rollup.config.esm-bundle.js"."build:esm": "rollup --config ./build/rollup.config.esm.js"."build:umd": "rollup --config ./build/rollup.config.umd.js",}}Copy the code

Five, publish,

1. NPM account

1.1. Register an account at www.npmjs.com/signup (existing account) please ignore this step

1.2. Log in to the cli

npm login
Copy the code

Check the login success:

npm whoami
Copy the code

2. Modify package.json

2.1. Set the private field to false. If it is true, NPM refuses to publish

{
    "private": false
}
Copy the code

2.2. Set entry and type declaration

{
    "main": "lib/index.umd.js"."module": "lib/index.esm.js"."typings": "lib/index.d.ts"
}
Copy the code

3, publish,

3.1. Set the version number

Modify the version field in package.json to ensure that the field is not the same as the published field

3.2, publish,

Execute command :(make sure the current branch is a clean branch before publishing, i.e. no uncommitted files)

npm publish
Copy the code

conclusion

At this point, the basic framework of building a component library from 0 to 1 is complete, and there are many details to deal with, such as:

  • CSS, SCSS and other style file processing
  • Separation of tools and components
  • Using the Document Library

Ponder: Vue-CLI scaffolding actually uses WebPack and can package umD library files, so why use rollup for packaging?

As mentioned in the rollup documentation, Rollup has good support for ES, and its powerful tree-shaking feature compresses code to a great deal. The uE-cli package umd.min.js file size is 10,772 bytes compared to the rollup package umd.js file size is 963 bytes. The advantages need not be explained too much

This article is intended for beginners who want to maintain a component library of their own, but don’t know how to do it, and there is more to explore. If you are at work, you will have to maintain your internal business component library in the future. Read this article and you will not be confused when you really need to use it. More details please comment.

Finally, the code involved in the whole process is attached:

Making: gitee.com/AloneH/vue-…

Gitee: gitee.com/AloneH/vue-…