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-…