葵花宝典

vuePress-theme-reco 前端小菜-贺俊兰    2026
葵花宝典 葵花宝典

Choose mode

  • 关灯
  • 自动
  • 开灯
主页
分类
  • LeetCode
  • JavaScript
  • Node
  • 其他
  • VUE
标签
时间轴
author-avatar

前端小菜-贺俊兰

33

文章

7

标签

主页
分类
  • LeetCode
  • JavaScript
  • Node
  • 其他
  • VUE
标签
时间轴
  • vue2

    • vue框架简介
    • Slot简介
    • vue响应式讲解-object
    • vue响应式讲解-array
    • 虚拟dom&diff算法
    • 服务端渲染SSR
    • Vue-router功能详解
    • vue-cli3项目性能优化步骤
    • keep-alive 总结
    • vue组件库搭建
  • vue3

vue组件库搭建

vuePress-theme-reco 前端小菜-贺俊兰    2026

vue组件库搭建

前端小菜-贺俊兰 2021-10-18 VUE

# 初始化项目

项目文件夹内执行 npm init -y,执行成功目录结构如下:

project ----------- // 项目文件夹名称
└─package.json ---- // npm 配置文件
1
2

# 初始化目录结构

创建三个文件夹,分别是 build、packages、styles,和一个 index.js 文件。

project ----------- // 项目文件夹名称
├─build ----------- // webpack 配置文件夹
├─index.js -------- // 入口文件
├─package.json ---- // npm 配置文件
├─packages -------- // 组件文件夹
└─styles ---------- // 组件样式文件夹
1
2
3
4
5
6

# 配置公共 webpack 配置

在 build 文件夹内新建一个 webpack.base.js 文件。

project ---------------- // 项目文件夹名称
├─build ---------------- // webpack 配置文件夹
│ └─webpack.base.js ---- // webpack 公共配置
├─index.js ------------- // 入口文件
├─package.json --------- // npm 配置文件
├─packages ------------- // 组件文件夹
└─styles --------------- // 组件样式文件夹
1
2
3
4
5
6
7
const webpack = require("webpack");
const VueLoaderPlugin = require("vue-loader/lib/plugin");

module.exports = {
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: "vue-loader",
      },
      {
        test: /\.js$/,
        loader: "babel-loader",
        exclude: /node_modules/,
      },
      {
        test: /\.css$/,
        loaders: [
          {
            loader: "style-loader",
          },
          {
            loader: "css-loader",
          },
        ],
      },
      {
        test: /\.(gif|jpg|png|woff|svg|eot|ttf)\??.*$/,
        loader: "url-loader?limit=8192",
      },
    ],
  },
  plugins: [
    new webpack.optimize.ModuleConcatenationPlugin(),
    new VueLoaderPlugin(),
  ],
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
npm i -D webpack webpack-cli vue-loader vue-template-compiler babel-loader @babel/core @babel/preset-env style-loader css-loader url-loader

1
2

因为装有 babel-loader ,得创建一个 .babelrc 的配置文件。

project ---------------- // 项目文件夹名称
├─.babelrc ------------- // babel 配置文件
├─build ---------------- // webpack 配置文件夹
│ └─webpack.base.js ---- // webpack 公共配置
├─index.js ------------- // 入口文件
├─package-lock.json ---- // package.json 安装的包的快照
├─package.json --------- // npm 配置文件
├─packages ------------- // 组件文件夹
└─styles --------------- // 组件样式文件夹
1
2
3
4
5
6
7
8
9

并写入如下配置:

{
  "presets": [
    "@babel/preset-env"
  ]
}
1
2
3
4
5

# 编写两个简陋的组件

创建一个 button 组件和一个 link 组件。 目录结构如下:

project ---------------- // 项目文件夹名称
├─.babelrc ------------- // babel 配置文件
├─build ---------------- // webpack 配置文件夹
│ └─webpack.base.js ---- // webpack 公共配置
├─index.js ------------- // 入口文件
├─package-lock.json ---- // package.json 安装的包的快照
├─package.json --------- // npm 配置文件
├─packages ------------- // 组件文件夹
│ ├─button ------------- // button 组件
│ │ ├─index.js
│ │ └─src
│ │   └─main.vue
│ └─link --------------- // link 组件
│   ├─index.js
│   └─src
│     └─main.vue
└─styles --------------- // 组件样式文件夹
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

组件的的目录结构借鉴了 element-ui ,方便 webpack 打包成按需加载的结构。

// packages/button/src/main.vue
<template>
  <button class="my-button">Click</button>
</template>

<script>
export default {
  name: "MyButton",
};
</script>
1
2
3
4
5
6
7
8
9
10
// packages/button/index.js
import Button from "./src/main.vue";

Button.install = function(Vue) {
  Vue.component(Button.name, Button);
};

export default Button;
1
2
3
4
5
6
7
8
// packages/link/src/main.vue
<template>
  <a class="my-link">Link</a>
</template>

<script>
export default {
  name: "MyLink",
};
</script>
1
2
3
4
5
6
7
8
9
10
// package/link/index.js
import Link from "./src/main.vue";

Link.install = function(Vue) {
  Vue.component(Link.name, Link);
};

export default Link;
1
2
3
4
5
6
7
8

每个组件都要有 name 属性。样式并没有写在 vue 文件内部,全部都单独写到了 styles 文件夹内。

button 样式:

// styles/button.less
.my-button {
  background: yellowgreen;
}
1
2
3
4

link 样式:

// styles/link.less
.my-link {
  color: blueviolet;
}
1
2
3
4

入口文件 index.js 编写:

// index.js
import Button from "./packages/button/index.js";
import Link from "./packages/link/index.js";

const components = [Button, Link];

const install = function(Vue, opt = {}) {
  components.forEach((component) => {
    Vue.component(component.name, component);
  });
};

if (typeof window !== "undefined" && window.Vue) {
  install(window.Vue);
}

export default {
  install,
  Button,
  Link,
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

引入写好的组件,每个组件添加相应的 install 方法,这样注册组件的时候就可以使用 Vue.use() 进行注册。

# 配置完全引入 webpack 打包配置

目录结构:

project ---------------- // 项目文件夹名称
├─.babelrc ------------- // babel 配置文件
├─build ---------------- // webpack 配置文件夹
│ ├─webpack.base.js ---- // webpack 公共配置
│ └─webpack.prod.js ---- // 全量打包 webpack 配置
├─index.js ------------- // 入口文件
├─lib
│ └─index.js
├─package-lock.json ---- // package.json 安装的包的快照
├─package.json --------- // npm 配置文件
├─packages ------------- // 组件文件夹
│ ├─button ------------- // button 组件
│ │ ├─index.js
│ │ └─src
│ │   └─main.vue
│ └─link --------------- // link 组件
│   ├─index.js
│   └─src
│     └─main.vue
└─styles --------------- // 组件样式文件夹
  ├─button.less
  └─link.less
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// build/webpack.prod.js
const path = require("path");
const merge = require("webpack-merge");
const webpackBaseConfig = require("./webpack.base.js");

module.exports = merge(webpackBaseConfig, {
  mode: "production",
  entry: {
    main: path.resolve(__dirname, "../index.js"),
  },
  output: {
    path: path.resolve(__dirname, "../lib"),
    publicPath: "/lib/",
    filename: "index.js",
    library: "my-library",
    libraryTarget: "umd",
    umdNamedDefine: true,
  },
  externals: {
    vue: {
      root: "Vue",
      commonjs: "vue",
      commonjs2: "vue",
      amd: "vue",
    },
  },
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

这里用到了 webpack-merge 这个工具,需要 npm 安装,这个工具主要以追加的形式合并 webpack 配置。

npm i -D webpack-merge
1

以上配置大概就是合并了 webpack.base.js 里的 loader 配置,设置了入口文件,输出文件路径,并以 umd 的模式进行打包,把 vue 设置成外部依赖,不需要打包进输出文件内。再配置一下 package.json 的 script ,添加一条打包命令。

{
  "name": "my-library",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build:prod": "webpack --config build/webpack.prod.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.10.2",
    "@babel/preset-env": "^7.10.2",
    "babel-loader": "^8.1.0",
    "css-loader": "^3.5.3",
    "style-loader": "^1.2.1",
    "url-loader": "^4.1.0",
    "vue-loader": "^15.9.2",
    "vue-template-compiler": "^2.6.11",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.11",
    "webpack-merge": "^4.2.2"
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

控制台执行 npm run build:prod 即可打包完成,打包完成后多出了一个 lib 文件夹,文件夹下有个 index.js 文件,这个文件就是全量组件的 js 文件。

# 组件的样式文件打包

组件的的逻辑代码已经完成,但是样式却没有,样式需要借助 gulp 进行打包处理,gulp 打包 css 比 webpack 的方便一些,所以选择了 gulp 。 在 build 文件夹下新建一个 gen-style.js 文件。

project ---------------- // 项目文件夹名称
├─.babelrc ------------- // babel 配置文件
├─build ---------------- // webpack 配置文件夹
│ ├─gen-style.js ------- // gulp 打包样式配置
│ ├─webpack.base.js ---- // webpack 公共配置
│ └─webpack.prod.js ---- // 全量打包 webpack 配置
├─index.js ------------- // 入口文件
├─lib
│ └─index.js
├─package-lock.json ---- // package.json 安装的包的快照
├─package.json --------- // npm 配置文件
├─packages ------------- // 组件文件夹
│ ├─button ------------- // button 组件
│ │ ├─index.js
│ │ └─src
│ │   └─main.vue
│ └─link --------------- // link 组件
│   ├─index.js
│   └─src
│     └─main.vue
└─styles --------------- // 组件样式文件夹
  ├─button.less
  └─link.less
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// build/gen-style.js
const gulp = require("gulp");
const cleanCSS = require("gulp-clean-css");
const less = require("gulp-less");
const rename = require("gulp-rename");
const autoprefixer = require("gulp-autoprefixer");

function buildCss(cb) {
  gulp
    .src("../styles/index.less")
    .pipe(less())
    .pipe(autoprefixer())
    .pipe(cleanCSS())
    .pipe(rename("index.css"))
    .pipe(gulp.dest("../lib/styles"));
  cb();
}

exports.default = gulp.series(buildCss);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

需要安装一下几个包,不喜欢用 less 的话,可以把 gulp-less 替换成自己想用的预处理器。

npm i -D gulp gulp-clean-css gulp-less gulp-rename gulp-autoprefixer
1

gen-style.js 文件中,有一个 buildCss 方法,这个方法就是把 styles/index.less 文件,打包成一个预处理好之后的 index.css 文件。gule 配置好,但是我们 styles 并没有 index.less 文件,先创建他并写入一些代码。

// styles/index.less
@import "./button.less";
@import "./link.less";
1
2
3

全量加载组件的时候,就会用到这个 index.less 的样式。

project ---------------- // 项目文件夹名称
├─.babelrc ------------- // babel 配置文件
├─build ---------------- // webpack 配置文件夹
│ ├─gen-style.js ------- // gulp 打包样式配置
│ ├─webpack.base.js ---- // webpack 公共配置
│ └─webpack.prod.js
├─index.js ------------- // 入口文件
├─package-lock.json ---- // package.json 安装的包的快照
├─package.json --------- // npm 配置文件
├─packages ------------- // 组件文件夹
│ ├─button ------------- // button 组件
│ │ ├─index.js
│ │ └─src
│ │   └─main.vue
│ └─link --------------- // link 组件
│   ├─index.js
│   └─src
│     └─main.vue
└─styles --------------- // 组件样式文件夹
  ├─button.less
  ├─index.less --------- // 全量加载使用时的样式
  └─link.less
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

package.json 文件添加打包样式的命令。

{
  "name": "my-library",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build:style": "gulp --gulpfile build/gen-style.js",
    "build:prod": "webpack --config build/webpack.prod.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.10.2",
    "@babel/preset-env": "^7.10.2",
    "babel-loader": "^8.1.0",
    "css-loader": "^3.5.3",
    "gulp": "^4.0.2",
    "gulp-autoprefixer": "^7.0.1",
    "gulp-clean-css": "^4.3.0",
    "gulp-less": "^4.0.1",
    "gulp-rename": "^2.0.0",
    "style-loader": "^1.2.1",
    "url-loader": "^4.1.0",
    "vue-loader": "^15.9.2",
    "vue-template-compiler": "^2.6.11",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.11",
    "webpack-merge": "^4.2.2"
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

使用 npm run build:style 命令试一下样式打包,正常打包。

# 按需加载打包(组件逻辑/样式打包)

全量加载完成了,接下来就是按需加载的打包配置了,先配置组件逻辑部分的打包,也是用到的 webpack 进行打包。 在 build 文件夹下新建一个 webpack.component.js 文件和 components.json 文件,components.json 文件是用来记录组件的路径。

project --------------------- // 项目文件夹名称
├─.babelrc ------------------ // babel 配置文件
├─build --------------------- // webpack 配置文件夹
│ ├─components.json --------- // 组件路径文件
│ ├─gen-style.js ------------ // gulp 打包样式配置
│ ├─webpack.base.js --------- // webpack 公共配置
│ ├─webpack.component.js ---- // 按需加载打包配置
│ └─webpack.prod.js
├─index.js ------------------ // 入口文件
├─package-lock.json --------- // package.json 安装的包的快照
├─package.json -------------- // npm 配置文件
├─packages ------------------ // 组件文件夹
│ ├─button ------------------ // button 组件
│ │ ├─index.js
│ │ └─src
│ │   └─main.vue
│ └─link -------------------- // link 组件
│   ├─index.js
│   └─src
│     └─main.vue
└─styles -------------------- // 组件样式文件夹
  ├─button.less
  ├─index.less -------------- // 全量加载使用时的样式
  └─link.less
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// build/components.json
{
  "button": "packages/button/index.js",
  "link": "packages/link/index.js"
}
1
2
3
4
5
// build/webpack.component.js
const path = require("path");
const merge = require("webpack-merge");
const webpackBaseConfig = require("./webpack.base.js");
const components = require("./components.json");

const basePath = path.resolve(__dirname, "../");

let entries = {};

Object.keys(components).forEach((key) => {
  entries[key] = path.join(basePath, components[key]);
});

module.exports = merge(webpackBaseConfig, {
  mode: "production",
  entry: entries,
  output: {
    path: path.resolve(__dirname, "../lib"),
    publicPath: "/lib/",
    filename: "[name].js",
    chunkFilename: "[id].js",
    libraryTarget: "umd",
    umdNamedDefine: true,
  },
  externals: {
    vue: {
      root: "Vue",
      commonjs: "vue",
      commonjs2: "vue",
      amd: "vue",
    },
  },
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

webpack.component.js 配置,合并了基础的配置,然后遍历 components.json 的组件路径,创建一个多入口文件的 webpack 配置,然后使用 umd 的打包方式把每个组件打包成一个 js 文件,跟 webpack.prod.js 一样,打包的时候不加入 vue 这个库。

package.json 添加打包按需加载的命令。

{
  "name": "my-library",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build:style": "gulp --gulpfile build/gen-style.js",
    "build:component": "webpack --config build/webpack.component.js",
    "build:prod": "webpack --config build/webpack.prod.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.10.2",
    "@babel/preset-env": "^7.10.2",
    "babel-loader": "^8.1.0",
    "css-loader": "^3.5.3",
    "gulp": "^4.0.2",
    "gulp-autoprefixer": "^7.0.1",
    "gulp-clean-css": "^4.3.0",
    "gulp-less": "^4.0.1",
    "gulp-rename": "^2.0.0",
    "style-loader": "^1.2.1",
    "url-loader": "^4.1.0",
    "vue-loader": "^15.9.2",
    "vue-template-compiler": "^2.6.11",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.11",
    "webpack-merge": "^4.2.2"
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

执行 npm run build:component 测试打包,打包正常。

打包按需加载样式,只需要在 gen-style.js 文件内引入 components.json 文件和加一个新的处理方法即可。

const gulp = require("gulp");
const cleanCSS = require("gulp-clean-css");
const less = require("gulp-less");
const rename = require("gulp-rename");
const autoprefixer = require("gulp-autoprefixer");
const components = require("./components.json");

function buildCss(cb) {
  gulp
    .src("../styles/index.less")
    .pipe(less())
    .pipe(autoprefixer())
    .pipe(cleanCSS())
    .pipe(rename("index.css"))
    .pipe(gulp.dest("../lib/styles"));
  cb();
}

function buildSeperateCss(cb) {
  Object.keys(components).forEach((compName) => {
    gulp
      .src(`../styles/${compName}.less`)
      .pipe(less())
      .pipe(autoprefixer())
      .pipe(cleanCSS())
      .pipe(rename(`${compName}.css`))
      .pipe(gulp.dest("../lib/styles"));
  });

  cb();
}

exports.default = gulp.series(buildCss, buildSeperateCss);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

执行一下 npm run build:style 测试打包,打包成功。

到这,全部打包配置已完成,可以开始自己的组件库编写之旅了。最后,添加一条一次完成所有命令的命令,只要执行一条命令,就可以把全部打包完成。

{
  "name": "my-library",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build:style": "gulp --gulpfile build/gen-style.js",
    "build:component": "webpack --config build/webpack.component.js",
    "build:prod": "webpack --config build/webpack.prod.js",
    "lib": "npm run build:prod && npm run build:component && npm run build:style"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.10.2",
    "@babel/preset-env": "^7.10.2",
    "babel-loader": "^8.1.0",
    "css-loader": "^3.5.3",
    "gulp": "^4.0.2",
    "gulp-autoprefixer": "^7.0.1",
    "gulp-clean-css": "^4.3.0",
    "gulp-less": "^4.0.1",
    "gulp-rename": "^2.0.0",
    "style-loader": "^1.2.1",
    "url-loader": "^4.1.0",
    "vue-loader": "^15.9.2",
    "vue-template-compiler": "^2.6.11",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.11",
    "webpack-merge": "^4.2.2"
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

执行 npm run lib 即可全部打包完成。

# npm 包的发布

  1. name 是包的名字,如果这个名字已经跟 npm 上的重复了,就自己改一下,不然是发布不了的。
  2. version 每次发布都需要修改一下版本号才能发布。
  3. main 入口设置成 lib/index.js 路径,也可以根据你打包的名字自己修改。
  4. files 设置上传到 npm 的文件或文件夹,一定要把打包好的 lib 文件上传,其他随意。

# 使用组件库

npm i [name]

全量引入

import MyUI from "MyUI";
import "MyUI/lib/styles/index.css";

Vue.use(MyUI);
1
2
3
4

MyUI 是发布 npm 包的名字。

按需引入,先安装 babel-plugin-component:

npm install -D babel-plugin-component

1
2

配置 .babelrc 文件

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules": false
      }
    ]
  ],
  "plugins": [
    [
      "component",
      {
        "libraryName": "MyUI",
        "libDir": "lib",
        "styleLibrary": {
          "name": "styles",
          "base": false,
          "path": "[module].css"
        }
      }
    ]
  ]
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

如果你打包的目录结构跟我这个是一样的话,秩序修改 libraryName 即可。

使用组件:

import { Button, Link } from "MyUI";

Vue.use(Button);
Vue.use(Link);
1
2
3
4
欢迎来到 葵花宝典
看板娘