前端工程化体系

whiteswan 发布于2年前
0 条问题

何谓前端工程化?即根据业务特点,将前端开发流程规范化、标准化。它主要包含不同业务场景的技术选型、代码规范、构建发布方案等。主要目地是为了提升前端开发工程师的开发效率与代码质量,降低前后端联调的沟通成本,使得前后端工程师更加专注于自身擅长领域。

根据自身从业这些年的一些产品和项目经验,对前端工程体系的设计有一些自己的见解:

  • 前端开发应该是“自成体系”的,包括运维布署、日志监控等
  • 针对不同的场景有不同的解决方案,并不是一套大而全的框架体系。比如针对以产品宣传展示为主的网页(Site),采用多页模式和响应式设计开发;以用户交互为主的且无强烈 SEO 要求的应用(Application),采用单页模式开发
  • 产品组件化,为提高复用性尽量将组件的颗粒度分细一些,且低耦合高内聚
  • 避免重复造轮子,引入一些优秀的开源资源,取长补短

根据以上思考,大致将自己理解的前端工程体系分为三大块:

  • Node 服务层:主要做数据的代理和 Mock,url 的路由分发,还有模板渲染
  • Web 应用开发层:主要专注 Web 交互体验
  • 前端运维层:构建布署、日志监控等

前端工程化体系

Node 服务层

数据代理

一般在 web 应用中,数据的来源分为两类:

  • 用户交互产生的 ajax 请求(客户端发起)
  • 服务端模板渲染所需初始数据

前端工程化体系

前者来说,传统的做法是后端直接提供 api 以供客户端调用,但面临微服务化逐渐成为主流的今天,后端系统也趋于拆分为众多后端服务,提供不同的 api,直接调用面临请求认证和跨域等众多问题,Node 做为中转站利用 http-proxy 将 http 请求和响应传输于前后两端,起到桥梁作用。

有人说直接请求后端 api 岂不是性能更佳?跨域问题直接用 CORS(Cross-origin resource sharing)不是也能解决?

我们先来看跨域问题:首页 CORS 当前还是有兼容性问题(不支持 IE10 及以下),其次它有一些限制或者说是自身局限:

需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。

针对性能问题我则拿了公司项目的 api 随机的几个接口来做了些测试,不是非常精确,但也能看个大概。分别在 No throtting 和 Regular 4G 的模式下,用 proxy(http-proxy 代理请求),direct(直接请求后端地址)和 node(利用 request 模块发起请求)模式,做了 GET 和 POST 请求的延迟比对。在比对之前心里也大概有数,肯定是直接请求后端地址的延迟最低,毕竟加了 Node 一层,性能会由损耗。以下是测试结果:

No throtting GET: 前端工程化体系

No throtting POST: 前端工程化体系

Regular 4G GET: 前端工程化体系

Regular 4G POST: 前端工程化体系

可以看出,GET 请求 direct 是最快的,proxy 次之,node request 垫底,POST 则相差不明显。但延迟差距在 30ms 左右,是否可以接受,就要看自己的系统架构对性能的要求有多高了。对于我司目前的规模体量用户数来说,完全是可以忽略的。

但为什么一定要用 Node 来做 http 请求的代理转发?我的理由:

  1. 前期前端开发过程中 mock 可与后期后端接口做平滑过渡,客户端做到无感知。甚至还能在 mock 数据和真实数据之间来回切换,十分灵活
  2. 当后端服务增多时,特别是内部系统,跨域调用很成问题,Node 代理转发从根本上解决跨域问题,不用担心 Cookie 丢失等问题,而且在 Node 层还能做进一步的 http 拦截和根据业务需求定制这些二次加工,适用于更加广泛的场景

我在之前做公司的模拟炒股项目时,大概就是这么地用的:

账户系统 行情系统 交易系统
/api/account /api/stock /api/trade
var url = require('url');

// 账户系统 API
app.use('/api/account', proxy(config.api.accout, {
  forwardPath: function (req, res) {
    return url.parse(req.url).path;
  }
}));

// 股票行情 API
app.use('/api/stock', proxy(config.api.stock, {
  forwardPath: function (req, res) {
    return url.parse(req.url).path;
  }
}));

// 股票交易 API 
app.use('/api/trade', proxy(config.api.trade, {
  forwardPath: function (req, res) {
    return url.parse(req.url).path;
  }
}));

至于服务端渲染,之前一直是用 JSP or PHP 等后端模板来做。现在又多了一种选择,Node 本身就是个服务端,而且模板本身是属于视觉层和数据层的结合品。在前后端分离的比较合理的条件下,让前端工程师来写服务端模板更加适合,因为此时 Node 服务层应该不包含复杂的数据运算(CPU 密集型非 Node 擅长场景),这里的数据来源都是比较直接和清晰的,顶多要做一些数据的拆分或整合处理而已,但对前端人员来说,了解业务逻辑是必备的,也是必需的,我们倡导的。

还有一点是因为下文中将要介绍的 Web 应用开发中,开发构建的工具本身就是基于 Node 的,build 后的静态资源大部分适合需在模板中引入,如何引入还是前端工程师最为清楚。所以虽模板属于服务端范畴,但与前端是结合的更加紧密的。

数据 Mock

一般来说,项目中后端接口实现的任务未完成,前端写好了页面只能等待。此时,如果后端有空可以造一些假数据,让前端可以继续开发调试。但我们鼓励前端工程师自己 mock 数据,为什么呢?

  • 更好的了解业务
  • 做为数据的第一级消费者,更清楚怎样的数据结构,适合前端页面展示。比如,用数组好,还是用字符串拼接好等等
  • mock 效率更高,实现简单:json-server,mockjs

但目前这两种简单的 mock 工具都不是太适合当前公司的项目场景,因为我们公司大部分接口都不是 restful 的,所以用下来遇到两个问题:

  • json-server 只支持 restful,而且只能生成 json 文件不够灵活
  • mockjs 是比较灵活高效,但做不到数据的持久化

比较完美的方案是两者结合再加上支持非 restful 的 mock,这个可能需要以后自己定制了。PS. 最近 github 上看到了一个利用 service workers 搞出来的一个 mock 服务: service-mocker ,不管是否适用,但要为这样的思路点赞。

url 路由

前端来制定路由,也促使前端工程师更全面深入地了解业务。这属于设计范畴,需要经验加持。

因为具体路由下,基本就是业务逻辑 controller 了:

router.route('/:catalogId')
  .get(catalogController.findOne)
  .put(validate(paramSchema.updateCatalog), catalogController.update)
  .delete(catalogController.re