更新说明:对文章目录排版做了调整。
更新时间:2022-05-04

第一章 周介绍

1-1 整体内容介绍

  • 前端组件演讲过程和大厂前端物料体系介绍
  • 组件复用体系架构设计
  • 脚手架组件创建+发布全流程实现

第二章 大厂物料体系介绍和前端组件平台架构设计

2-1 大厂前端物料体系介绍
2-2 组件平台架构设计
点击查看【processon】

第三章 脚手架组件创建和预览项目开发

3-1 新的组件库模板开发

  1. 发布npm包
  • 下载源至某处:https://git.imooc.com/class-110/lego-bricks
  • 新建文件 cloudscope-cli-components
  • cd cloudscope-cli-components
  • npm init -y
  • 将源码移动到 cloudscope-cli-components下,并将lego-bricks改名为template
  • cloudscope-cli-components/package.json下,添加publishConfig
1
2
3
4
5
6
7
8
9
10
11
{
"name": "cloudscope-cli-components",
"version": "1.0.2",
"description": "",
"keywords": [],
"author": "",
"license": "ISC",
"publishConfig": {
"access": "public"
}
}
  • cloudscope-cli-components/template/package.json下,添加files
1
2
3
4
5
6
7
8
9
10
11
12
13
14
"files": [
"build",
"src",
"tests",
"dist",
".browserslistrc",
".eslintrc.js",
".gitignore",
".travis.yml",
"babel.config.js",
"jest.config.js",
"README.md",
"tsconfig.json"
],
  • npm publish
  1. 维护mongodb,添加一条数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"_id" : ObjectId("612473d8e1694b2c0191177f"),
"name" : "通用的Vue3组件库模版",
"npmName" : "cloudscope-cli-components",
"version" : "1.0.1",
"type" : "normal",
"installCommand" : "npm install --registry=https://registry.npm.taobao.org",
"startCommand" : "npm run serve",
"tag" : [
"component"
],
"ignore" : [
"**/public/**",
"**.png"
]
}
  1. 新建components-test目录

cloudscope-cli init -tp /Users/liumingzhou/Documents/imoocCourse/Web前端架构师/cloudscope-cli/commands/init

3-2 组件库预览项目开发

这节听的有点懵逼,上一节,通过cloudscope-cli init了一个命令后,

  • npm run build
  • 根目录下继续新建examples目录,cd examples
  • vue create examples
  • cd examples中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//main.js
import LegoComponents from "../../../dist/component-test.esm";
createApp(App).use(LegoComponents).mount('#app')

//App.vue

<template>
<div id="app">
<l-image src="https://img.mukewang.com/5d5e51f900011f0c06550501-100-100.jpg"></l-image>
</div>
</template>

//vue.config.js
module.exports = {
publicPath:'./'
}

最后继续 npm run build,打开dist目录下的index.html,查看页面效果

3-3 组件多预览模式开发

有点小懵逼,组件库就开发完毕了

1
2
3
4
5
6
7
8
9
10
11
12
13
module.exports = {
publicPath:'./',
pages:{
index:{
entry:'./src/main.js',
templates:'./public/index.html'
},
index2:{
entry:'./src/main2.js',
templates:'./public/index.html'
},
}
}

3-4 将预览功能集成到组件库模板
3-5 组件初始化时自动生成配置文件
3-6 组件库命名优化

第四章 脚手架组件发布流程开发

4-1 组件仓库初始化流程优化
cloudscope-cli 本章相关代码提交至 cloudscope-cli/lesson32

本节主要为对仓库名称带有·@·进行一个合法的名字修改

1
2
3
4
5
6
7
8
9
10
11
12
// cloudscope-cli/models/git/lib/index.js
class Git {
constructor({name, version, dir},{
^^^^^^^
if(name.startsWith('@') && name.indexOf('/')>0){
const nameArray = name.split('/')
this.name = nameArray.join('_').replace('@','')
}else{
this.name = name //发布项目名称
}
}
}

4-2 组件上传前预检查流程开发
cloudscope-cli 本章相关代码提交至 cloudscope-cli/lesson32

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
// cloudscope-cli/models/git/lib/index.js
async prepare(){
await this.checkComponent() // 组件合法性检查
}
async checkComponent(){
let componentFile = this.isComponent()
if(componentFile){
log.info('开始检查build结果')
if(!this.buildCmd){
this.buildCmd = 'npm run build'
}
require('child_process').execSync(this.buildCmd,{
cwd:this.dir
})
const buildPath = path.resolve(this.dir,componentFile.buildPath)
if(!fs.existsSync(buildPath)){
throw new Error(`构建结果:${buildPath}不存在!`)
}
const pkg = this.getPackageJson()
if(!pkg.files || !pkg.files.includes(componentFile.buildPath)){
throw new Error(`packages.json中files属性未添加构建结果目录:[${componentFile.buildPath}],请在packag.json中收到添加`)
}
log.success('build结果检查通过!')
}

}

isComponent(){
const componentFilePath = path.resolve(this.dir,COMPONENT_FILE);
return fs.existsSync(componentFilePath) && fse.readJsonSync(componentFilePath)
}

4-3 组件发布前准备工作开发
cloudscope-cli 本章相关代码提交至 cloudscope-cli/lesson32

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
async publish(){
let ret = false;
if(this.isComponent()){
log.info('开始发布组件')
await this.saveComponentToDB()
}else{
………………
}
if (this.prod && ret) {
await this.uploadComponentToNpm()
this.runCreateTagTask();
}

async saveComponentToDB(){
// 将组件信息上传至数据库-RDS

// 将组件多预览页面上传至OSS
}
async uploadComponentToNpm(){
// 完成组件上传至npm
}

4-4 创建RDS组件表+后端MySQL插件集成
cloudscope-cli-server 本章相关代码提交至 cloudscope-cli-server/lesson32

本章主要是在服务端链接mysql数据库,并做相关测试
编写相关代码前:cnpm i -S egg-mysql

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
// app/config/db.js
const MYSQL_HOST = 'liugezhou.com'
const MYSQL_PORT = 3306
const MYSQL_USER = 'root'
const MYSQL_PWD = fs.readFileSync(path.resolve(userHome,'.cloudscope-cli','mysql_pwd')).toString().trim()
const MYSQL_DB ='imooc_web_architect_cli'

//app/config/plugin.js
exports.mysql = {
enable:true,
package: 'egg-mysql'
}

//app/config/config.default.js
config.mysql = {
client: {
host: MYSQL_HOST,
port: MYSQL_PORT,
user:MYSQL_USER,
password:MYSQL_PWD,
database:MYSQL_DB
},
app:true,
agent:false
}


//相关测试代码
// app/app/router.js
router.get('/mysql',controller.project.mysqlTest)

// app/controller/project.js
async mysqlTest(){
const {ctx, app } = this
const list = await app.mysql.select('component')
ctx.body = JSON.stringify(list)
}

4-5 组件上传数据库准备工作开发
cloudscope-cli 本章相关代码提交至 cloudscope-cli/lesson32

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// models/git/lib/index.js
const ComponentRequest = require('../lib/ComponentRequest');
async saveComponentToDB(){
// 将组件信息上传至数据库-RDS
log.info('上传组件信息至OSS+写入数据库')
const componentFile = this.isComponent()
let componentExamplePath = path.resolve(this.dir,componentFile.examplePath);
let dirs = fs.readdirSync(componentExamplePath)
if(dirs.includes('dist')){
componentExamplePath = path.resolve(componentExamplePath,'dist')
dirs = fs.readdirSync(componentExamplePath)
componentFile.examplePath = `${componentFile.examplePath}/dist`
}
dirs = dirs.filter(dir => dir.match(/^index(\d)*.html$/))
componentFile.exampleList = dirs
componentFile.exampleRealPath = componentExamplePath
const data = await ComponentRequest.createComponent({
component:componentFile,
git: {
type: this.gitServer.type,
remote: this.remote,
version: this.version,
branch: this.branch,
login: this.login,
owner: this.owner,
},
})
if (!data) {
throw new Error('上传组件失败');
}
// 2.将组件多预览页面上传至OSS
return true;
}

// models/git/lib/ComponentRequest.js
const axios = require('axios');
const log = require('@cloudscope-cli/log');

module.exports = {
createComponent: async function(component) {
try {
const response = await axios.post('http://liugezhou.com:7001/api/v1/components', component);
log.verbose('response', response);
const { data } = response;
if (data.code === 0) {
return data.data;
}
return null;
} catch (e) {
throw e;
}
},
};

4-6 组件上传restful api开发
cloudscope-cli-server 本章相关代码提交至 cloudscope-cli-server/lesson32

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// app/constant.js
module.exports = {
STATUS: {
ON: 1,
OFF: 0,
},
}

// app/router.js
router.resources('components', '/api/v1/components', controller.v1.components);

// app/Controller/v1/Components.js
'use strict'

const Controller = require("egg").Controller;
const constant = require('../../constant');
const ComponentService = require('../../service/ComponentService');

class ComponentsController extends Controller {

// api/v1/components
async index() {
const { ctx } = this;
ctx.body = 'get component'
}

// api/v1/components/:id
async show() {
const { ctx } = this;
ctx.body = 'get single component'
}

// post data
async create() {
const { ctx, app } = this;
const { component, git } = ctx.request.body;
const timestamp = new Date().getTime()
// 1. 添加组件信息
const componentData = {
name: component.name,
classname: component.className,
description: component.description,
npm_name: component.npmName,
npm_version: component.npmVersion,
git_type: git.type,
git_remote: git.remote,
git_owner: git.owner,
git_login: git.login,
status: constant.STATUS.ON,
create_dt: timestamp,
create_by: git.login,
update_dt: timestamp,
update_by: git.login,
};
const componentService = new ComponentService(app);
ctx.body = 'create component'
}
}

module.exports = ComponentsController

4-7 组件上传数据库逻辑开发

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// app/service/ComponentService.js
'use strict'

class ComponentService {
constructor(app){
this.app = app,
this.name = 'component'
}

async queryOne(query){
const data = await this.app.mysql.select(this.name, {
where: query,
});
if (data && data.length > 0) {
return data[0];
}
return null;
}

async insert(data) {
const res = await this.app.mysql.insert(this.name, data);
return res.insertId;
}
}

module.exports = ComponentService

//app/service/versionService.js
'use strict'

class VersionService {
constructor(app) {
this.app = app;
this.name = 'version'
}
async queryOne(query) {
const data = await this.app.mysql.select(this.name, {
where: query,
});
if (data && data.length > 0) {
return data[0];
}
return null;
}

async insert(data) {
const res = await this.app.mysql.insert(this.name, data);
if (res.affectedRows > 0) {
return true;
}
return false
}

async update(data, query) {
const res = await this.app.mysql.update(this.name, data, {
where: query
})
if (res.affectedRows > 0) {
return true;
}
return false
}
}

module.exports = VersionService

//app/controller/v1/Components.js
async create() {
const { ctx, app } = this;
const { component, git } = ctx.request.body;
const timestamp = new Date().getTime()
// 1. 添加组件信息
const componentData = {
name: component.name,
classname: component.className,
description: component.description,
npm_name: component.npmName,
npm_version: component.npmVersion,
git_type: git.type,
git_remote: git.remote,
git_owner: git.owner,
git_login: git.login,
status: constant.STATUS.ON,
create_dt: timestamp,
create_by: git.login,
update_dt: timestamp,
update_by: git.login,
};
const componentService = new ComponentService(app);
const haveComponentInDB = await componentService.queryOne({
className: component.className
})
let componentId;
if (!haveComponentInDB) {
componentId = await componentService.insert(componentData);
} else {
componentId = haveComponentInDB.id
}
if (!componentId) {
ctx.body = failed('添加组件失败')
}
// 2.添加组件版本信息
const versionData = {
component_id: componentId,
version: git.version,
build_path: component.buildPath,
example_path: component.examplePath,
example_list: JSON.stringify(component.exampleList),
status: constant.STATUS.ON,
create_dt: timestamp,
create_by: git.login,
update_dt: timestamp,
update_by: git.login,
}
const versionService = new VersionService(app);
const haveVersionInDB = await versionService.queryOne({
component_id: componentId,
version: git.version
});
if (!haveVersionInDB) {
const versionRes = await versionService.insert(versionData)
if (!versionRes) {
ctx.body = failed('添加组件失败')
}
} else {
const updateData = {
build_path: component.buildPath,
example_path: component.examplePath,
example_list: JSON.stringify(component.exampleList),
update_dt: timestamp,
update_by: git.login,
};
const versionRes = await versionService.update(updateData, {
component_id: componentId,
version: versionData.version,
});
if (!versionRes) {
ctx.body = failed('更新组件失败');
return;
}
}

}

4-8 组件NPM发布逻辑开发

本节主要是调试过多,重要几行代码为在脚手架 cloudscope-cli中添加NPM发布逻辑

1
2
3
4
5
6
7
8
9
10
async uploadComponentToNpm() {
// 完成组件上传至npm
if (this.isComponent()) {
log.info('开始发布组件NPM')
require('child_process').execSync('npm publish', {
cwd: this.dir
})
log.success('组件NPM发布成功')
}
}

4-9 组件自动生成远程仓库Tag问题解决

End