# vue-comp-business
**Repository Path**: markerj/vue-comp-business
## Basic Information
- **Project Name**: vue-comp-business
- **Description**: Vue2 业务组件库开发示例
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2023-06-06
- **Last Updated**: 2024-12-08
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
### 使用vue-cli开发业务组件库
#### **一、前言**
随着业务的持续完善,前端项目的体积也在不断变大。如何有效的对代码进行管理、公共组件和业务组件的复用是需要考虑的问题。为了去较好的解决这个问题,看了很多的方案,各有各的优点,各有各的适用场景。
突然想到,既然是组件库,为何不去看那些优秀的组件库源码呢?于是,花了些时间,把[Element UI](https://github.com/ElemeFE/element)的源码仔细的看了下,获益匪浅,这里也推荐大家先去学习学习。最后,以它的结构为主,做了一定调整,完成了本示例项目。
#### **二、效果演示**
##### 1、项目使用
```javascript
/** 全量引入 **/
import vueCompBusiness from 'vue-comp-business';
import 'vue-comp-business/lib/css/index.css';
Vue.use(vueCompBusiness);
/** 按需引入 **/
// 1、安装babel
npm install babel-plugin-component -D
// 2、main.js
import { CbButton } from 'vue-comp-business';
Vue.use(CbButton);
// 3、babel.config.js
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
// vue-comp-business组件按需加载
plugins: [
[
'import',
{
libraryName: 'vue-comp-business',
// 定义寻找组件css文件路径逻辑,不调整的话,会找cb-button.css文件
customStyleName: (name) => {
console.log('customStyleName', name)
return `vue-comp-business/lib/css/${name.slice(3)}.css`
},
// 定义寻找组件路径逻辑,不调整的话,会找cb-button.js文件
customName: (name) => {
console.log('customName', name)
return `vue-comp-business/lib/${name.slice(3)}`
}
}
]
]
}
```
##### 2、效果截图



#### **三、实现步骤**
涉及到的知识点有:Vue Cli、Vue、Vuepress、scss、bem,不熟悉的可以先去官网熟悉熟悉。
##### 1、代码结构
```
vue-comp-business
├─ build --------------------------------------------------------- 打包配置文件夹
│ ├─ config.dev.js -------------------------------------------- 测试环境打包配置文件
│ └─ config.prod.js ---------------------------------------- 测试环境打包配置文件
├─ deploy ------------------------------------------------------ 文档打包配置文件夹
│ └─ docs.sh ---------------------------------------------------- 文档打包配置文件
│
├─ docs ---------------------------------------------------------- Vuepress相关文件夹
│ ├─ .vuepress ------------------------------------------------------- Vuepress默认
│ │ ├─ components ------------------------------------------ Vuepress拓展组件文件夹
│ │ │ ├─ press-row.vue ------------------------------------------------- 行组件
│ │ │ └─ source-block.vue ---------------------------------------- 代码查看组件
│ │ ├─ config.js ------------------------------------------ Vuepress菜单导航等配置
│ │ ├─ enhanceApp.js --------------------------------------- 异步导入第三方组件实现
│ │ └─ public --------------------------------------------- Vuepress 公共资源文件夹
│ │ └─ element.png ------------------------------------------------------ logo
│ ├─ guide ---------------------------------------------------------- 导航资源文件夹
│ │ ├─ CbButton.md ----------------------------------------------- 自定义按钮md
│ │ ├─ CbPanel.md --------------------------------------------------- 自定义面板md
│ │ └─ README.md -------------------------------------------------- guide 主页md
│ └─ README.md ---------------------------------------------------- Vuepress主页md
│
├─ examples ------------------------------------------------------ 开发环境示例文件夹
│ ├─ App.vue ------------------------------------------------------- 开发环境主页
│ └─ main.js ------------------------------------------------------- 开发环境入口
│
├─ lib ---------------------------------------- 打包后资源存放文件夹(普通模式\UMD模式)
│ ├─ button.js
│ ├─ css
│ │ ├─ button.css
│ │ ├─ index.css
│ │ └─ panel.css
│ ├─ index.js
│ └─ panel.js
│
├─ packages --------------------------------------------------- 业务组件库统一文件夹
│ ├─ components ------------------------------------------------ 业务组件文件夹
│ │ ├─ button ---------------------------------------------------- 按钮组件文件夹
│ │ │ ├─ index.js ------------------------------------------------- 组件入口
│ │ │ └─ src
│ │ │ └─ button.vue ----------------------------------------- 按钮组件实现
│ │ └─ panel
│ │ ├─ index.js
│ │ └─ src
│ │ └─ panel.vue
│ │ ├─ index.js ------------------------------------------------- 全部组件入口
│ └─ css ---------------------------------------------------- 组件css统一文件夹
│ ├─ common -------------------------------------------------------- 变量scss
│ │ └─ var.scss ----------------------------------------- 测试环境打包配置
│ ├─ fonts ------------------------------------------------------------ 字体
│ │ ├─ element-icons.ttf
│ │ └─ element-icons.woff
│ ├─ mixins -------------------------------------------------------- scss混入
│ │ ├─ config.scss --------------------------------------------- bem前缀配置
│ │ ├─ function.scss ---------------------------------------- 工具方法scss
│ │ ├─ mixins.scss -------------------------------------------- 工具混入scss
│ │ └─ utils.scss ---------------------------------------- 常用样式混入scss
│ ├─ index.scss --------------------------------------------- 全部组件样式入口
│ ├─ button.scss ----------------------------------------------- 按钮样式
│ └─ panel.scss --------------------------------------------------- 面板样式
│
├─ public ------------------------------------------------------ 静态资源文件夹
│ ├─ favicon.ico
│ └─ index.html -------------------------------------------------- html模板
│
├─ vue.config.js --------------------------------------------------- 打包配置
├─ jsconfig.json -------------------------------------------- 文件路径识别配置
├─ components.json ------------------------------------------------ 组件路径配置
├─ .gitignore
├─ .npmignore
├─ babel.config.js -------------------------------------------------- babel配置
├─ package-lock.json
├─ package.json
├─ README.md
```
##### 2、package.json
这里使用的脚手架版本是4.5,使用5版本的脚手架时,要么sass有问题,有么vuepress文档运行时出错,直接放弃了,哈哈...
```json
{
"name": "vue-comp-business",
"version": "0.1.0",
"private": true,
"/**main**/": "注释:指定包的入口,npm下载后看到的入口",
"main": "./lib/index.js",
"style": "./lib/theme/index.css",
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
"build:umd": "vue-cli-service build --target lib --dest lib --name vue-comp-business packages/components/index.js",
"docs:dev": "vuepress dev docs",
"docs:build": "vuepress build docs",
"docs:deploy": "bash ./deploy/docs.sh"
},
"dependencies": {
"core-js": "^3.6.5",
"vue": "^2.6.11"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vuepress/plugin-back-to-top": "^1.9.7",
"node-sass": "^4.14.1",
"sass": "^1.49.0",
"sass-loader": "^7.3.1",
"vue-template-compiler": "^2.6.11",
"vuepress": "^1.9.7"
},
"author": "admin",
"license": "MIT"
}
```
##### 3、打包配置
- dev打包配置
```javascript
const path = require('path');
module.exports = {
configureWebpack: {
entry: path.join(__dirname, '../examples/main.js')
}
};
```
- prod打包配置
```javascript
// components.json
{
"button": "./packages/components/button/index.js",
"panel": "./packages/components/panel/index.js",
"index": "./packages/components/index.js"
}
```
```javascript
const path = require('path');
const components = require('../components.json');
const resolve = dir => path.join(__dirname, '../', dir);
// 获取每个组件的绝对路径
const getComponentEntries = () => {
const entryKeys = Object.keys(components);
entryKeys.forEach(key => {
components[key] = resolve(components[key]);
});
return components;
};
// { button: '/Users/xxx/packages/button/index.js' }
const componentEntries = getComponentEntries();
module.exports = {
outputDir: resolve('lib'),
configureWebpack: {
entry: componentEntries,
output: {
// 文件名称
filename: '[name].js',
// 构建依赖类型
libraryTarget: 'umd',
// 库中被导出的项
libraryExport: 'default',
// 引用时的依赖名
library: 'vueTestComp'
}
},
// 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建
productionSourceMap: false,
css: {
extract: {
filename: 'css/[name].css' //在lib文件夹中建立 theme 文件夹中,生成对应的css文件。
}
},
/**
* 删除splitChunks,因为每个组件是独立打包,不需要抽离每个组件的公共js出来。
* 删除copy,不要复制public文件夹内容到lib文件夹中。
* 删除html,只打包组件,不生成html页面。
* 删除preload以及prefetch,因为不生成html页面,所以这两个也没用。
* 删除hmr,删除热更新。
* 删除自动加上的入口App。
*/
chainWebpack: config => {
config.optimization.delete('splitChunks');
config.plugins.delete('copy');
config.plugins.delete('html');
config.plugins.delete('preload');
config.plugins.delete('prefetch');
config.plugins.delete('hmr');
config.entryPoints.delete('app');
}
};
```
##### 4、按钮组件的实现
```vue
```
```scss
// button.scss文件
@charset "UTF-8";
@import "common/var";
@import "mixins/mixins";
@include b(button) {
display: inline-block;
line-height: 1;
white-space: nowrap;
cursor: pointer;
background: $--button-default-background-color;
border: $--border-base;
border-color: $--button-default-border-color;
color: $--button-default-font-color;
-webkit-appearance: none;
text-align: center;
box-sizing: border-box;
outline: none;
margin: 0;
padding: $--button-small-padding-vertical;
// 两个按钮间距10像素
& + & {
margin-left: 10px;
}
&:hover,
&:focus {
color: $--color-primary;
border-color: $--color-primary-light-7;
background-color: $--color-primary-light-9;
}
@include m(primary) {
color: $--button-primary-font-color;
background-color: $--button-primary-background-color;
border-color: $--button-primary-border-color;
&:hover,
&:focus {
background: mix($--color-white, $--button-primary-background-color, $--button-hover-tint-percent);
border-color: mix($--color-white, $--button-primary-border-color, $--button-hover-tint-percent);
color: $--button-primary-font-color;
}
}
@include m(success) {
color: $--button-success-font-color;
background-color: $--button-success-background-color;
border-color: $--button-success-border-color;
&:hover,
&:focus {
background: mix($--color-white, $--button-success-background-color, $--button-hover-tint-percent);
border-color: mix($--color-white, $--button-success-border-color, $--button-hover-tint-percent);
color: $--button-primary-font-color;
}
}
}
```
```javascript
// 按钮组件入口
import CommonButton from './src/button.vue'
CommonButton.install = (Vue) => {
Vue.component(CommonButton.name, CommonButton)
}
export default CommonButton
```
##### 5、全部组件导出
```javascript
import CbButton from './button/index.js'
import CbPanel from './panel/index.js'
const components = [
CbButton,
CbPanel
]
// 全局引入: 引入的组件是个对象时,必须要有install函数
const install = (Vue) => {
components.forEach(component => {
Vue.component(component.name, component);
});
}
/* istanbul ignore if */
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue);
}
export default {
install,
CbButton,
CbPanel
}
```
##### 6、开发环境测试
- App.vue
```vue
```
- main.js
```javascript
import Vue from 'vue';
import App from './App.vue';
// 批量注册组件
import CbComponent from '../packages/components';
import '../packages/css/index.scss';
Vue.use(CbComponent);
console.log(CbComponent, Vue)
Vue.config.productionTip = false;
new Vue({
render: h => h(App)
}).$mount('#app');
```
##### 7、文档编写
- 菜单等配置
```javascript
module.exports = {
title: 'vue-comp-bussiness',
description: 'vue-comp-bussiness 组件库文档',
markdown: {
lineNumbers: true
},
base: '/vue-comp-bussiness/',
locales: {
'/': {
lang: 'zh-CN',
title: '个人业务组件库',
description: '这是国际化相关配置'
},
'/en/': {
lang: 'en-US',
title: 'vue-comp-business',
description: 'this is locales config'
}
},
themeConfig: {
locales: {
'/': {
selectText: '选择语言',
label: '简体中文',
nav: [
{
text: '指南',
link: '/guide/'
},
{
text: 'github',
link: 'https://github.com'
}
],
sidebar: {
'/guide/': [
['', '介绍'],
{
title: '组件',
children: [
['../guide/CbButton.md', 'CbButton'],
['../guide/CbPanel.md', 'CbPanel']
]
}
]
}
}
// '/en/': {
// selectText: 'Languages',
// label: 'English',
// nav: [
// {
// text: 'guide',
// link: '/en/guide/'
// },
// {
// text: 'github',
// link: 'https://github.com'
// }
// ],
// sidebar: {
// '/en/guide/': [
// ['', 'introduction'],
// {
// title: 'Components',
// children: [
// ['../guide/Button.md', 'Button'],
// ['../guide/Card.md', 'Card']
// ]
// }
// ]
// }
// }
}
},
plugins: ['@vuepress/back-to-top']
};
```
- 第三方组件导入
```javascript
// 引入打包后的样式和文件。
import '../../lib/css/index.css';
export default async ({ Vue, options, router, siteData, isServer }) => {
if (!isServer) {
// 直接导入 window is not defined 原因就是编译的时候需要 node.js 服务端渲染。所以要改成异步导入
await import('../../lib').then(vueComp => {
Vue.use(vueComp);
});
}
};
```
#### 四、npm推送
##### 1、关键备注
`package.json` :配置信息。
- `private`: 选项改为 false;当 `private` 为 true 时,是不允许发布到 npm 的。
- `name`: 当前应用的名称或库名称。
- `version`: 原则上每次发布版本,版本号都应该是递增的。
- `main`:项目入口文件。例如:`./lib/test-comp-base.umd.min.js`
- `author`: 作者名称。
- `license`:使用哪种开源协议。
`.npmignore` :过滤不需要上传的文件
##### 2、推送
1. 之前没有提交到npm的先做准备工作:https://segmentfault.com/a/1190000015939001
2. cmd窗口添加全局用户信息配置:npm adduser
```shell
-- 426错误:镜像地址切换一下,还不行,检查一下网络问题
npm config set registry https://registry.npmjs.org/
```
3. 在项目根路径运行,npm publish