1、初始化 npm
项目文件夹内执行 npm init -y,执行成功目录结构如下:
|- project/ -------------- 项目文件
|- package.json -------------- npm 配置文件
2、初始化目录结构
项目文件夹内执行 npm init -y,执行成功目录结构如下:
|- project/ -------------- 项目文件
|- build -------------- webpack 配置文件夹
|- index.js -------------- 入口文件
|- package.json -------------- npm 配置文件
|- packages -------------- 组件文件夹
|- style -------------- 组件样式文件夹
3、配置公共 webpack 配置
在 build 文件夹内新建一个 webpack.base.js 文件:
|- project/ -------------- 项目文件
|- build -------------- webpack 配置文件夹
|- webpack.base.js -------------- webpack 公共配置
|- index.js -------------- 入口文件
|- package.json -------------- npm 配置文件
|- packages -------------- 组件文件夹
|- style -------------- 组件样式文件夹
写入如下配置
const webpack = require('webpack');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const path = require('path');
const resolve = function(dir) {
return path.join(__dirname, '..', dir);
};
module.exports = {
module: {
rules: [
{
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('packages')], // 指定检查的目录
options: {
formatter: require('eslint-friendly-formatter')
}
},
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
loaders: [
{
loader: 'style-loader'
},
{
loader: 'css-loader'
},
{
loader: 'sass-loader'
}
]
},
{
test: /\.(gif|jpg|png|woff|svg|eot|ttf)\??.*$/,
loader: 'url-loader?limit=8192'
}
]
},
plugins: [
new webpack.optimize.ModuleConcatenationPlugin(),
new VueLoaderPlugin()
],
resolve: {
extensions: ['.js', '.vue'],
alias: {
style: resolve('style'),
packages: resolve('packages')
}
}
};
以上配置就都只是一些常用的 loader ,和使用了一个 webpack 的优化打包的插件 new webpack.optimize.ModuleConcatenationPlugin() ,一下是官方解释的这个插件用途。
以上配置需要安装以下 npm 包:
npm i -D webpack webpack-cli vue-loader vue-template-compiler babel-loader @babel/core @babel/preset-env style-loader css-loader url-loader sass-loader
因为装有 babel-loader ,得创建一个 .babelrc 的配置文件。
|- project/ -------------- 项目文件
|- .babelrc ------------- babel 配置文件
|- build -------------- webpack 配置文件夹
|- webpack.base.js -------------- webpack 公共配置
|- index.js -------------- 入口文件
|- package.json -------------- npm 配置文件
|- packages -------------- 组件文件夹
|- style -------------- 组件样式文件夹
并写入如下配置:
{
"presets": [
"@babel/preset-env"
]
}
4、开始编写组件
创建一个 YsCustomPanel组件
|- project/ -------------- 项目文件
|- .babelrc ------------- babel 配置文件
|- build -------------- webpack 配置文件夹
|- webpack.base.js -------------- webpack 公共配置
|- index.js -------------- 入口文件
|- package.json -------------- npm 配置文件
|- packages -------------- 组件文件夹
|- custom-panel -------------- 自定义面板组件文件夹
|- src -------------- YsCustomPanel组件源代码文件夹
|- main.vue -------------- 组件源代码
|- index.js -------------- 引用组件并暴露install方法
|- style -------------- 组件样式文件夹
组件的的目录结构借鉴了 element-ui ,方便 webpack 打包成按需加载的结构。
custom-panel/src/main.vue 代码如下:
<div class="ys-custom-panel">
...
</div>
custom-panel/src/index.js 代码如下:
import YsCustomPanel from './src/main';
/* istanbul ignore next */
YsCustomPanel.install = function(Vue) {
Vue.component(YsCustomPanel.name, YsCustomPanel);
};
export default YsCustomPanel;
每个组件都要有 name 属性,还是以custom-panel为例。
export default {
name: 'YsCustomPanel'
}
样式并没有写在 vue 文件内部,全部都单独写到了 style 文件夹内。
YsCustomPanel组件样式:
@import "./var.scss";
.ys-custom-panel {
&__header {
position: fixed;
top: 0;
height: 60px;
z-index: 99;
display: flex;
align-items: center;
h3 {
font-size: 18px;
margin-right: 13.5px;
opacity: 0.85;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
color: #000000;
font-weight: bold;
}
}
.back-button.iconfont {
font-size: 24px !important;
padding-right: 8px !important;
float: left;
font-weight: bold;
color: $--color-primary;
cursor: pointer;
&:hover{
color: $--color-primary;
}
}
}
入口文件 index.js 编写:
import YsCustomPanel from './packages/custom-panel/index.js';
const components = [
YsCustomPanel
];
const install = function(Vue) {
components.forEach(component => {
Vue.component(component.name, component);
});
};
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue);
}
export default {
install,
YsCustomPanel
};
引入写好的组件,每个组件添加相应的 install 方法,这样注册组件的时候就可以使用 Vue.use() 进行注册。
4、配置完全引入 webpack 打包配置
|- project/ -------------- 项目文件
|- .babelrc ------------- babel 配置文件
|- build -------------- webpack 配置文件夹
|- webpack.base.js -------------- webpack 公共配置
|- webpack.prod.js -------------- 全量打包 webpack 配置
|- index.js -------------- 入口文件
|- package.json -------------- npm 配置文件
|- packages -------------- 组件文件夹
|- custom-panel -------------- 自定义面板组件文件夹
|- src -------------- YsCustomPanel组件源代码文件夹
|- main.vue -------------- 组件源代码
|- index.js -------------- 引用组件并暴露install方法
|- style -------------- 组件样式文件夹
// 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, '../dist'),
publicPath: '/dist/',
filename: 'index.js',
library: 'my-library',
libraryTarget: 'umd',
umdNamedDefine: true
},
externals: {
vue: {
root: 'Vue',
commonjs: 'vue',
commonjs2: 'vue',
amd: 'vue'
}
}
});
这里用到了 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"
}
}
控制台执行 npm run build:prod 即可打包完成,打包完成后多出了一个 dist 文件夹,文件夹下有个 index.js 文件,这个文件就是全量组件的 js 文件。
6、组件的样式文件打包
组件的的逻辑代码已经完成,但是样式却没有,样式需要借助 gulp 进行打包处理,gulp 打包 css 比 webpack 的方便一些,所以选择了 gulp 。
在 style 文件夹下新建一个 gulpfile.js 文件。
|- project/ -------------- 项目文件
|- .babelrc ------------- babel 配置文件
|- build -------------- webpack 配置文件夹
|- webpack.base.js -------------- webpack 公共配置
|- webpack.prod.js -------------- 全量打包 webpack 配置
|- index.js -------------- 入口文件
|- package.json -------------- npm 配置文件
|- packages -------------- 组件文件夹
|- custom-panel -------------- 自定义面板组件文件夹
|- src -------------- YsCustomPanel组件源代码文件夹
|- main.vue -------------- 组件源代码
|- index.js -------------- 引用组件并暴露install方法
|- style -------------- 组件样式文件夹
|- src -------------- 样式源代码文件夹
|- index.scss -------------- 总样式文件
|- ys-custom-panel.scss -------------- custom-panel样式文件
写下如下代码:
var gulp = require('gulp');
var sass = require('gulp-sass');
var autoprefixer = require('gulp-autoprefixer');
var cssmin = require('gulp-cssmin');
function compile(cb) {
gulp
.src('./src/*.scss')
.pipe(sass.sync())
.pipe(
autoprefixer({
browsers: ['ie > 9', 'last 2 versions'],
cascade: false
})
)
.pipe(cssmin())
.pipe(gulp.dest('../dist/style/'));
cb();
}
function copyfont(cb) {
gulp
.src('./src/fonts/**')
.pipe(cssmin())
.pipe(gulp.dest('../dist/style/fonts/'));
cb();
}
exports.build = gulp.series(compile, copyfont);
需要安装一下几个包。
npm i -D gulp gulp-sass gulp-autoprefixer gulp-cssmin
全量加载组件的时候,就会用到这个 index.scss 的样式。
package.json 文件添加打包样式的命令。
{
"name": "my-library",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build:theme": "gulp build --gulpfile style/gulpfile.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"
}
}
使用 npm run build:theme 命令试一下样式打包,正常打包。
7、按需加载打包(组件逻辑/样式打包)
全量加载完成了,接下来就是按需加载的打包配置了,先配置组件逻辑部分的打包,也是用到的 webpack 进行打包。
在 build 文件夹下新建一个 webpack.component.js 文件和 components.json 文件,components.json 文件是用来记录组件的路径。
|- project/ -------------- 项目文件
|- .babelrc ------------- babel 配置文件
|- build -------------- webpack 配置文件夹
|- webpack.base.js -------------- webpack 公共配置
|- webpack.prod.js -------------- 全量打包 webpack 配置
|- webpack.component.js -------------- 按需打包 webpack 配置
|- index.js -------------- 入口文件
|- package.json -------------- npm 配置文件
|- packages -------------- 组件文件夹
|- custom-panel -------------- 自定义面板组件文件夹
|- src -------------- YsCustomPanel组件源代码文件夹
|- main.vue -------------- 组件源代码
|- index.js -------------- 引用组件并暴露install方法
|- style -------------- 组件样式文件夹
|- src -------------- 样式源代码文件夹
|- index.scss -------------- 总样式文件
|- ys-custom-panel.scss -------------- custom-panel样式文件
// components.json
{
"ys-custom-panel": "./packages/custom-panel/index.js"
}
// build/webpack.component
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, '../dist'),
publicPath: '/dist/',
filename: '[name].js',
chunkFilename: '[id].js',
libraryTarget: 'umd',
umdNamedDefine: true
},
externals: {
vue: {
root: 'Vue',
commonjs: 'vue',
commonjs2: 'vue',
amd: 'vue'
}
}
});
webpack.component.js 配置,合并了基础的配置,然后遍历 components.json 的组件路径,创建一个多入口文件的 webpack 配置,然后使用 umd 的打包方式把每个组件打包成一个 js 文件,跟 webpack.prod.js 一样,打包的时候不加入 vue 这个库。
package.json 添加打包按需加载的命令。
{
"name": "my-library",
"version": "1.0.0",
"description": "描述",
"main": "dist/index.js",
"files": [
"dist"
],
"scripts": {
"build:theme": "gulp build --gulpfile style/gulpfile.js",
"build:prod": "webpack --config build/webpack.prod.js",
"build:component": "webpack --config build/webpack.component.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.11.6",
"@babel/preset-env": "^7.11.5",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
"core-js": "^2.0.0",
"cp-cli": "^1.0.2",
"css-loader": "^4.2.2",
"eslint": "^7.8.1",
"eslint-friendly-formatter": "^4.0.1",
"eslint-loader": "^4.0.2",
"eslint-plugin-vue": "^6.2.2",
"gulp": "^4.0.2",
"gulp-autoprefixer": "^4.0.0",
"gulp-cssmin": "^0.1.7",
"gulp-postcss": "^6.1.1",
"gulp-sass": "^3.1.0",
"gulp-style-aliases": "^1.1.11",
"highlight.js": "^10.2.0",
"rimraf": "^2.5.4",
"sass-loader": "^10.0.2",
"style-loader": "^1.2.1",
"url-loader": "^4.1.0",
"vue": "^2.6.12",
"vue-highlight.js": "^3.1.0",
"vue-loader": "^15.9.3",
"vue-template-compiler": "^2.6.12",
"vuepress": "^1.5.4",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12",
"webpack-merge": "^4.2.2"
},
"dependencies": {
"gleaf": "^1.1.35",
"lodash": "^4.17.20",
"moment": "^2.28.0"
}
}
执行 npm run build:component 测试打包,打包正常。
打包按需加载样式,只需要在 gulpfile.js遍历处理每个scss文件即可。
到这,全部打包配置已完成,可以开始自己的组件库编写之旅了。最后,添加一条一次完成所有命令的命令,只要执行一条命令,就可以把全部打包完成。
{
"name": "my-library",
"version": "1.0.0",
"description": "描述",
"main": "dist/index.js",
"files": [
"dist"
],
"scripts": {
"build:theme": "gulp build --gulpfile style/gulpfile.js",
"build:prod": "webpack --config build/webpack.prod.js",
"build:component": "webpack --config build/webpack.component.js",
"dist": "npm run clean && npm run lint && npm run build:prod && npm run build:component && npm run build:theme",
"clean": "rimraf dist",
"lint": "eslint --config .eslintrc.js --ext .js,.vue packages --fix",
"serve": "npm run lint && npm run docs:dev",
"docs:dev": "vuepress dev docs",
"docs:build": "vuepress build docs"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.11.6",
"@babel/preset-env": "^7.11.5",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
"core-js": "^2.0.0",
"cp-cli": "^1.0.2",
"css-loader": "^4.2.2",
"eslint": "^7.8.1",
"eslint-friendly-formatter": "^4.0.1",
"eslint-loader": "^4.0.2",
"eslint-plugin-vue": "^6.2.2",
"gulp": "^4.0.2",
"gulp-autoprefixer": "^4.0.0",
"gulp-cssmin": "^0.1.7",
"gulp-postcss": "^6.1.1",
"gulp-sass": "^3.1.0",
"gulp-style-aliases": "^1.1.11",
"highlight.js": "^10.2.0",
"rimraf": "^2.5.4",
"sass-loader": "^10.0.2",
"style-loader": "^1.2.1",
"url-loader": "^4.1.0",
"vue": "^2.6.12",
"vue-highlight.js": "^3.1.0",
"vue-loader": "^15.9.3",
"vue-template-compiler": "^2.6.12",
"vuepress": "^1.5.4",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12",
"webpack-merge": "^4.2.2"
},
"dependencies": {
"gleaf": "^1.1.35",
"lodash": "^4.17.20",
"moment": "^2.28.0"
}
}
8、npm 包的发布
package.json需要改好以上配置
name 是包的名字,如果这个名字已经跟 npm 上的重复了,就自己改一下,不然是发布不了的。
version 每次发布都需要修改一下版本号才能发布。
main 入口设置成 dist/index.js 路径,也可以根据你打包的名字自己修改。
files 设置上传到 npm 的文件或文件夹,一定要把打包好的 dist 文件上传,其他随意。
npm publish(非第一次登录,如果是第一次登录,要先npm addUser,输入账号、密码登录进来)
9、使用组件库
1.安装自己的组件库
npm i [name]
2.全量引入
import myLibrary from 'my-library';
import "myLibrary/lib/styles/index.css";
Vue.use(myLibrary);
myLibrary 是发布 npm 包的名字。
3.按需引入
先安装 babel-plugin-component:
npm install -D babel-plugin-component
配置 .babelrc 文件
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
plugins: [
[
"component",
{
"libraryName": "my-library",
"styleLibrary": {
"name": "style",
"base": false,
"path": "[module].css"
}
}
]
]
};
如果你打包的目录结构跟我这个是一样的话,秩序修改 libraryName 即可。
使用组件:
import Vue from 'vue';
import {
YsBasicPanel,
} from 'my-library';
Vue.use(YsBasicPanel);