vue组件库搭建
# 初始化项目
项目文件夹内执行 npm init -y,执行成功目录结构如下:
project ----------- // 项目文件夹名称
└─package.json ---- // npm 配置文件
2
# 初始化目录结构
创建三个文件夹,分别是 build、packages、styles,和一个 index.js 文件。
project ----------- // 项目文件夹名称
├─build ----------- // webpack 配置文件夹
├─index.js -------- // 入口文件
├─package.json ---- // npm 配置文件
├─packages -------- // 组件文件夹
└─styles ---------- // 组件样式文件夹
2
3
4
5
6
# 配置公共 webpack 配置
在 build 文件夹内新建一个 webpack.base.js 文件。
project ---------------- // 项目文件夹名称
├─build ---------------- // webpack 配置文件夹
│ └─webpack.base.js ---- // webpack 公共配置
├─index.js ------------- // 入口文件
├─package.json --------- // npm 配置文件
├─packages ------------- // 组件文件夹
└─styles --------------- // 组件样式文件夹
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(),
],
};
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
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 --------------- // 组件样式文件夹
2
3
4
5
6
7
8
9
并写入如下配置:
{
"presets": [
"@babel/preset-env"
]
}
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 --------------- // 组件样式文件夹
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>
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;
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>
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;
2
3
4
5
6
7
8
每个组件都要有 name 属性。样式并没有写在 vue 文件内部,全部都单独写到了 styles 文件夹内。
button 样式:
// styles/button.less
.my-button {
background: yellowgreen;
}
2
3
4
link 样式:
// styles/link.less
.my-link {
color: blueviolet;
}
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,
};
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
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",
},
},
});
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
以上配置大概就是合并了 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"
}
}
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
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);
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
gen-style.js 文件中,有一个 buildCss 方法,这个方法就是把 styles/index.less 文件,打包成一个预处理好之后的 index.css 文件。gule 配置好,但是我们 styles 并没有 index.less 文件,先创建他并写入一些代码。
// styles/index.less
@import "./button.less";
@import "./link.less";
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
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"
}
}
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
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"
}
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",
},
},
});
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"
}
}
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);
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"
}
}
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 包的发布
- name 是包的名字,如果这个名字已经跟 npm 上的重复了,就自己改一下,不然是发布不了的。
- version 每次发布都需要修改一下版本号才能发布。
- main 入口设置成 lib/index.js 路径,也可以根据你打包的名字自己修改。
- files 设置上传到 npm 的文件或文件夹,一定要把打包好的 lib 文件上传,其他随意。
# 使用组件库
npm i [name]
全量引入
import MyUI from "MyUI";
import "MyUI/lib/styles/index.css";
Vue.use(MyUI);
2
3
4
MyUI 是发布 npm 包的名字。
按需引入,先安装 babel-plugin-component:
npm install -D babel-plugin-component
2
配置 .babelrc 文件
{
"presets": [
[
"@babel/preset-env",
{
"modules": false
}
]
],
"plugins": [
[
"component",
{
"libraryName": "MyUI",
"libDir": "lib",
"styleLibrary": {
"name": "styles",
"base": false,
"path": "[module].css"
}
}
]
]
}
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);
2
3
4