前言
经过一周艰苦的工作后终于有时间进行第三篇的记录了,还是一样的先上文章整体介绍
- 整个项目的介绍和使用流程以及实现目标和未实现目标
- 整体分析前端使用的vue3.0以及我们后面使用会有哪些坑
- nodejs后端怎么去做类似java springMVC的强类型限定
- 小服务器上怎么实现整个项目自动化集群部署
项目架构
这个东西我在之前的文章中介绍过了,在这里就不过多的进行介绍,主要简单描述一下,有兴趣的大佬可以移步这篇文章看看juejin.cn/post/684490…
- 首先进行查看这个整体流程,是不是感觉很像spring的感觉,各种层服务实体,通过一步步分离基础业务和逻辑实现达到项目的高度复用性
- 然后再来看装饰器,主要修饰了一些服务层和控制了路由注册的地方
- 接着利用了mapper层达到数据库映射的功能,能够避免进行不必要的sql语句的书写
- 最后就是静态文件的app注册等
为什么会选用这种方式来进行架构项目呢? 作为一个前端开发者来说理解这些东西需要一定的适应过程(ts大佬另当别论),这种分层的优势又体现在哪了? 在这我就不进行描述spring对于java有什么优势了,单纯进行为什么这样构架以及构架的一些好处。
使用装饰器
我们先来比对两段代码带给我们的直观感受
// 假设一个函数,如果参数一样就不处理原始函数,采用保存数据返回(redis)
function cachable(){
...
}
// 现在我们有这么一个函数需要进行封装
function add(a, b) {
return a + b;
}
// 方法1
function add(a, b) {
add = cachable((a, b) => {
return a + b;
})
}
// 方法2
@cachable
function add(a, b) {
return a + b;
}
虽然已经对于方法1进行了简写了,但是第一眼看过去我们任然不知道这个函数的主体内容和额外功能,而方法2却很清晰。
利用实体构建对象
在我们进行开发的时候创建实体类可以作为我们的数据操作对象,好比我们在写ts的时候添加了类型校验的interface的接口的作用,并且可以自定义实体类的复杂逻辑方法,去简化我们操作数据对象的复杂度和强化规范性。在书写的时候同时也可以得到一定的规范性提示和方法的提示,这样可以简化我们操作具体对象的难度。
打个不缺当的比方:如果我在不利用框架的情况下怎么实现一个对象的数据绑定呢?
- 在正常情况下进行数据的响应值的get、set重写,通过proxy或者原生方法
- 在封装的实体类中调用数据绑定方法
这样做可以简化我们代码的逻辑复杂度。
并且实体对象和数据库一一对应这样也可以更好的了解数据库结构。那我们来简单看一下一个只有构造函数的简单实体类吧:
export default class CodeTypes {
private _id: number;
private _typename: string;
constructor(id: number, typename: string) {
this._id = id;
this._typename = typename;
}
get id(): number {
return this._id;
}
set id(value: number) {
this._id = value;
}
get typename(): string {
return this._typename;
}
set typename(value: string) {
this._typename = value;
}
}
看到这了如果对于项目具体的一些配置和注册功能还是挺感兴趣的大佬可以继续看这篇文章啦,剩下的路由注册,redis控制等就不再过多描述了。juejin.cn/post/684490…
功能实现
对于基础的实现功能,比如数据库的增删改查这里就不再过多描述,对于这方面有问题可以查看之前的模板项目了解具体流程。
在线编辑
之前的文章也提过在线编辑的逻辑,就是前端传递代码至后端、然后访问接口、最后访问网页就得到编辑的结果了。那么具体后端完成了什么内容呢?就分两个地方来说明吧。
- 得到前端代码进行处理:
/**
* 返回标准html页面方法
* @route POST /code/codeOnline/setHtml
* @group 代码在线编辑
* @param {string} findId.formData.required 返回
* @param {string} sendHtml.formData.required 测试
* @returns {VoidFunction} 200 - 返回true
* @returns {VoidFunction} 500 - 返回错误
*/
@routerDec.RequestMapping('/setHtml', MyType.post)
async setHtml(
@routerDec.RequestParams('String', 'findId') findId: string,
@routerDec.RequestParams('String', 'sendHtml') sendHtml: string,
@routerDec.Response() res: express.Response
): Promise<void> {
const response = new BaseResponse<boolean>();
try {
MyRedis.set(findId, sendHtml);
MyRedis.exp(findId, 10);
response._datas = true;
response.changeType(BackType.success);
} catch (e) {
response._datas = false;
response._msg = BaseErrorMsg.redisError;
}
res.json(response);
}
对于看不懂整体结构的可以查看刚才提到的那篇文章,这里我们就看函数内具体执行了一个redis存储的功能,然后存储的时间为10秒。存储成功后就进行了结果状态的返回。(其实就是返回了true)
所以可以看出具体的实现其实是在访问网页接口的时候处理的,那么我们看一下处理了哪些东西呢?
export default class CodeOnlineServices implements CodeOnlineServicesImp{
private static ThreePacksServices: ThreePacksServices = new ThreePacksServices()
/**
* 处理vue代码在线显示
* @param {String} a 代码片段
* @returns {string} 返回处理完成的html片段或显示错误提示
*/
async dealVueOnlineCode(a: string): Promise<string> {
```
}
/**
* 返回生成需要的js
* @param {Object} script 传入export default内容
* @param {Array} listObj 传入组件数组
* @return {string} 返回生成js片段
*/
private static createJs(script: object, listObj: any[]): string {
```
}
/**
* 处理对象为一条条语句
* @param {Object} obj 传入需要处理对象
* @param {String} name 处理后生成对象名称
* @returns {string} 返回处理后代码片段
*/
private static serialize(obj: object, name: string): string{
```
}
/**
* 返回截取页面字符串截取
* @param {String} source 需要截取的资源
* @param {String} type 截取标签类型
* @returns {any} 返回截取结果
*/
private static getSource(source: string, type: string): string {
```
}
}
可以看出整个处理类CodeOnlineServices只有一个暴露的方法:dealVueOnlineCode()处理通过双方的key值的redis缓存的代码,然后通过处理返回网页的html给iframe渲染就实现我们的在线编辑了。
整体处理流程还是为下图所示:
npm发布
在具体将实现内容前需要各位了解一下npm包发布的一些要素,以及一些要求在这里不过多进行描述
由于相较而已实现比较特殊所以直接上流程图:
稍微有那么一点点复杂,其实简单理解就是对获取的数据进行了一些处理后生成了文件后执行发布的操作罢了。
在这里由于代码量比较多,我就只方入口代码出来了,有兴趣的大佬可以文章最后下源码下来对着这个图来看
/**
* 添加一个新的redis npm包管理缓存
* @param {String} path 路径
* @param {Object} componentsObj 构成npm包对象
* @returns {Promise<boolean>}
*/
addNewPackage(name: string, componentsObj: { [p: string]: Codes[] }, version: string, components: Components[]): Promise<boolean> {
return new Promise(async (resolve) => {
try {
// 获取基础路径
const path = `project/${name}`;
// 添加redis缓存
await MyRedis.rpush('npmPublishControl', JSON.stringify({
componentsObj,
path,
version,
name,
threePacks: Array.from(components.reduce((all, item) => {
if (item.threePacks) {
item.threePacks.split(',').forEach((item) => {
all.add(item);
});
}
return all;
}, new Set())).join(',')
}));
PublishPackageServices.npmRunFn();
resolve(true);
} catch (e) {
console.log(e);
resolve(false);
}
});
}
最后我们来看一下整个发布流程所需要的对象长啥样的
interface NpmRedisObj {
componentsObj: { [p: string]: Codes[] };
path: string;
version: string;
name: string;
threePacks: string;
}
得到了这些信息之后我们就可以进行砸门的发布流程了。
下章预告
下一章:Vue3.0一个不算小的项目实践(四)(文章完成后会将该标题换为链接)
主要内容:作为一个小服务器怎么利用docker搭建属于自己的服务集群,实现自动化构建部署
虽然这个和写代码没啥关系了,但是还是有shell、配置文件之类的东西,在这还是来一段:
#!/bin/bash
echo $PATH
IMAGE_NAME='registry.cn-hangzhou.aliyuncs.com/bymycode/vue-assemble-ui'
IMAGE_VERSION=$(date "+%Y%m%d%H%M%S")
echo IMAGE_NAME=$IMAGE_NAME
echo '================开始创建镜像================'
docker build -t $IMAGE_NAME:$IMAGE_VERSION .
docker tag $IMAGE_NAME:$IMAGE_VERSION $IMAGE_NAME:latest
echo '================开始推送镜像================'
docker login --username= --password= registry.cn-hangzhou.aliyuncs.com
docker push $IMAGE_NAME:$IMAGE_VERSION
docker push $IMAGE_NAME:latest
echo '====================OK!================'
源码
由于本人网撇,访问github难受,所以选择的是gitee码云,所以后面的操作包括WebHooks操作都是在码云上进行完成的。欢迎大家下载源码给一个小星星哦
源码地址: gitee.com/beon/vue-as…
运行命令: npm run start
结尾
这就是本文全部内容了,如果有任何问题或者想让我帮忙进行开发欢迎进行评论的私聊我!