node工具化对前端开发提效的实践
自我介绍
- Name 刘放
- Web前端开发@有道云课堂
- Github brizer
- Wechat brizer1992
分享大纲
Node背景介绍
脚手架
多工程管理
Mock
活动页面生成系统
Node背景介绍
Node到底是什么?
Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.
Node的组成
Application
你写的node应用或者说模块
Modules
Node.js 标准库,对外提供的 JavaScript 接口,例如模块 http、buffer、fs、stream 等
C++ Binding
JavaScript 与 C++ 连接的桥梁,对下层模块进行封装,向上层提供基础的 API 接口如zlib、OpenSSL、c-ares、http-parser 等
Addons
JavaScript 与 C++ 连接的桥梁,第三方胶水层代码
Node的组成
V8
Google 开源的高性能 JavaScript 引擎,以 C++ 实现。这也是集成在 Chrome 中的 JS 引擎
libuv
提供异步功能的 C 库。它在运行时负责一个事件循环(Event Loop)、一个线程池、文件系统 I/O、DNS 相关和网络 I/O,以及一些其他重要功能。 每个操作系统对于事件多路复用器有其自身的接口,Linux是epoll,Mac OSX是kqueue,Windows的IOCP API
Node的应用场景
- 跨平台: 传统的PC Web端,以及PC客户端 nw.js/electron 、移动端 cordova、HTML5、react-native、weex,硬件 ruff.io...
- 后端应用开发: 网站、Api、RPC服务,比如Express/Koa/Egg/Nest/Hapi/Restify/Socket.io...
- 前端基建: 三大框架 React/Vue/Angular 辅助开发,以及工程化演进如grunt/gulp/webpack/rollup/parcel/snowpack/rollup...
- 命令行工具: npm上各种模块如commander/yargs/shelljs/lerna/http-server/node-http-proxy...
![](http://edu-image.nosdn.127.net/9753ec47b7464eca95d4da4612866255.jpg?imageView&quality=100)
分享大纲
Node背景介绍
脚手架
多工程管理
Mock
活动页面生成系统
脚手架
一个标准的脚手架思路
- 命令注册: commander/yargs...
- git拉取: download-git-repo/@gitbeaker...
- 交互: inquirer/ora...
- 模板: Handlebars/EJS/Jade...
从头自建脚手架
- 重复造轮子!
- 劳民伤财!
- 稳定性不可保障,维护成本高
- 耦合设计,拓展性不高
合理利用生态优势,拥抱开源
库 | 使用场景 | 定制方式 |
---|---|---|
yeoman | 初始化工程 | 定制化generator,插件使用 |
plop | 工程内新增或修改文件 | 定制交互prompts及操作actions |
脚手架1.0
基于plop,工程内定制
工程内置模板及配置
自定义交互及行为
module.exports = {
prompts: [{//...各种不同类型的交互
type: "list",
message: "请选择新增的页面位于哪个一级路由下:",
name: "scope",
choices: pageList
},{
type: "input",
name: "chineseName",
message: "请输入页面中文名称(例如开屏配置):"
}],
actions: data => {//获取交互中的得到的参数
const { scope, name, chineseName } = data;
return [{//...各种不同类型的行为
type: "add",
path: `src/javascript/admin/module/${scope}/${name}.js`,
templateFile: "plop-templates/page/module.hbs",
data: {umiName}
},{
type: "modify",
path: `src/javascript/admin/config/admin.js`,
templateFile: "plop-templates/page/config.hbs",
pattern: /var exports = \{/,
data: {name}
}];
}
};
效果展示
遗留问题
模板在工程内维护,那么跨工程的通用模板不好复用
每次新增都需要改到配置文件,可维护性不怎么好
脚手架2.0
基于node-plop,分离模板库和脚手架
整体思路
物料库风格
├── blocks - 各个物料本身的模板
| ├── bower
| | ├── bower.hbs
| | └── bowerrc.hbs
| ├── example
| | └── demo.hbs
| ├── mocker
| | ├── dev.hbs
| | ├── httpmockrc.hbs
| | └── package.hbs
| ├── roll
| | ├── dev.hbs
| | └── package.hbs
| └── singleVue
| └── index.hbs
└── plop-templates - 各个物料自己的命令行交互及具体文件流操作
├── bowerGenerator.js
├── exampleGenerator.js
├── mockerGenerator.js
├── rollGenerator.js
└── singleVueGenerator.js
遗留问题(todo)
物料不能做到本地开发,调试不便(打算通过cli强化物料开发流程)
工程内需要到指定文件路径执行命令(打算通过vscode扩展或直接配上ui解决)
分享大纲
Node背景介绍
脚手架
多工程管理
Mock
活动页面生成系统
多工程管理
工程数量
10+
组件数量
60+
WTF
每次预发上线都头大?
合理利用生态优势,拥抱开源
库 | 特点 | 特点 | 场景 |
---|---|---|---|
lerna | mono-repo | 同一git仓库下,管理多个包,包含开发及发布完整流程 | util库及组件库 |
meta | multi-repo | 同一文件结构下,管理多个不同仓库,支持插件扩展子命令,管理git等 | 工程群 |
multi-repo-git | multi-repo | 通过全局配置文件,跨文件目录管理不同仓库,定制化git及包依赖,不支持插件 | 工程群 |
效果展示
核心原理
//透传参数,子进程指定路径执行
child_process.exec(command,{
cwd:'配置文件读取的工程路径'
})
分享大纲
Node背景介绍
脚手架
多工程管理
Mock
活动页面生成系统
Mock
哪些场景需要Mock
- 前端本地开发时,对依赖的后端接口的Mock
- 服务A开发时,对依赖的服务B的Mock
- 单元测试时,对某些模块或方法的Mock
接口Mock选型
库 | 特点描述 | 使用场景 |
---|---|---|
yapi | 重型平台,功能齐全 | 统一管理所有工程接口 |
rap2-delos | 重型平台,阿里妈妈出品 | 统一管理所有工程接口 |
nei | 重型平台,网易自研 | 统一管理所有工程接口 |
http-mocker | 笔者自研,轻量级 | 组件内或util等example对应mock |
轻量级场景
你是直接代码侵入式修改mock?
const actions = {
save: isLocal? '/mock/success.json':`/p/train/config/submit.do?courseId=${g.courseId}`,
detail: (isLocal?'/mock':'') + '/j/train/config/init.json',
}
export default {
save(data):Promise<boolean>{
console.warn(data)
return baseSvs[isLocal?'get':'post'](actions.save, data)
},
detail(courseId):Promise<any>{
return baseSvs.get(actions.detail+'?courseId='+courseId)
},
}
还是启动一个mock服务,来响应所有接口?
import Mock from 'mockjs'
export default [
// mock get all routes form server
{
url: '/article/pv',
type: 'get',
response: _ => {
return {
code: 20000,
data: {
pvData: [
{ key: 'PC', pv: 1024 },
{ key: 'mobile', pv: 1024 },
{ key: 'ios', pv: 1024 },
{ key: 'android', pv: 1024 }
]
}
}
}
},
]
侵入代码影响性能和可读性!
额外启动一个服务很繁琐!
无法动态切换某一个接口本地和远程模式!
每个规则都需要手写js,麻烦!
解决方案 Http-mocker
如何不启动服务、不侵入也可以进行mock?
直接将express或webpack-dev-server的app对象传入
const { mocker } = require('http-mockjs')
const webpackConfig = {
devServer: {
before:app=>{
mocker(app)
}
}
}
如何识别路由,并动态切换本地或远程?
利用app.all('/*')
,以path-to-regexp
风格拦截路由
app.all("/*",async (req, res, next) => {
const proxyMatch = getMatechedRoute(proxyLists, proxyURL);
// 匹配到对应路由
if (proxyMatch && proxyMatch.ignore !== true) {
// 延时返回功能
if(proxyMatch.delay && typeof proxyMatch.delay === 'number'){
await sleep(proxyMatch.delay)
}
// 校验入参格式
if(proxyMatch.validate && !isEmptyObject(proxyMatch.validate)){
//...
}
let responseBody;
// 同时支持js模式,沙盒运行
if(/js$/ig.test(curPath) ){
responseBody = vm.run(jsContent)(req);
}else{
// json模式直接读取返回值
responseBody = fs.readFileSync(curPath, "utf-8");
}
// 支持mockjs来灵活化数据
const result = mock.mock(responseBody);
// 自定义响应头
res.set(responseHeaders);
res.send(result);
res.end();
}
});
如何优化配置的便利性?
抽出独立的ui包,可视化编辑
分享大纲
Node背景介绍
脚手架
多工程管理
Mock
活动页面生成系统
活动页面生成系统
![](http://edu-image.nosdn.127.net/85613f39d38844ff889a45bb97811adb.jpg?imageView&quality=100)
完成页面数量
9700+
独立模块数量
120+
整体架构
模块元数据
{
"chineseName": "移动端课程卡片带切换(抢课,拼团,提醒,收藏)",
"content": "<ux-cms-course-list-with-tab tabList={list}></ux-cms-course-list-with-tab>",
"dataTemplate": "c2_courselist_tab",
"dependence": "pool/component-cms/src/course-list-with-tab/wap/ui",
"description": "移动端课程卡片带切换(抢课,拼团,提醒,收藏)",
"name": "m_course_list_with_tab_all_m",
"previewImg": "http://edu-image.nosdn.127.net/367ae591-32f3-4337-a195-cafd5543082e.png",
"style": "",
"type": 2,
"viewType": 2
}
数据元数据
{
"name" : "c2_voteList",
"description" : "投票列表模板",
"items" : [
{
"description" : "投票列表类型,10为机构,20为讲师",
"type" : "Number",
"propertyName" : "id"
},{
"description" : "一页数量",
"type" : "Number",
"propertyName" : "nums"
},{
"description" : "投票结束时间",
"type" : "Time",
"propertyName" : "endTime"
}
]
}
降低重复性工作
好好度个假