北京网站建设 地址海淀,音乐网站建立,竞价网站单页面,简洁网站设计欣赏什么是前端工程化#xff1f;根据具体的业务特点#xff0c;将前端的开发流程、技术、工具、经验等规范化、标准化就是前端工程化。它的目的是让前端开发能够“自成体系”#xff0c;最大程度地提高前端工程师的开发效率#xff0c;降低技术选型、前后端联调等带来的协调沟… 什么是前端工程化根据具体的业务特点将前端的开发流程、技术、工具、经验等规范化、标准化就是前端工程化。它的目的是让前端开发能够“自成体系”最大程度地提高前端工程师的开发效率降低技术选型、前后端联调等带来的协调沟通成本。 美团点评厦门智能住宿前端研发团队通过多个前端项目开发的探索和实践基于“约定优于配置”Convention Over Configuration的原则制定了一套前端工程化开发方案app-proto。本文将简要介绍其中的一些设计细节和约定。 面临的业务特点 智能住宿前端团队承担的前端业务主要面向B端项目用户主要是商家、销售、运营、产品经理以及研发人员。 诸如工单管理、信息管理、门锁运营、PMSProperty management system、CRMCustomer relationship management及AMSAsset management system等项目都是单页面工具类应用特点是功能交互繁多、复杂表单非展示类、无SEOSearch engine optimization需求。 如果这些项目脱离浏览器这个“外壳”与传统的原生桌面GUI软件无异。换言之这些项目就是一种运行于浏览器的工具软件。 实际上部分项目我们也确实利用CEFChromium Embedded Framework等技术给其套个“外壳”当作传统的桌面GUI应用提供给用户使用。 同时部分服务需要从智能门锁、控制盒Wifi等硬件设备收录状态数据限于硬件环境测试的不稳定性后端的开发测试周期远比前端开发周期长。大部分场景下前后端需并行开发后端工程师并不能在第一时间兼顾到前端所需的API接口等服务给前端开发造成没有必要的“等待期”影响开发进度。 此外项目多、敏捷需求多、开发周期短以及面向多后端服务多个后端团队等也是我们前端研发团队面临的挑战。 一些前端经验总结 针对多个项目的开发实践和探索我们在对前端工程化设计中得到如下一些经验总结 前端开发应该“自成体系”包括构建、部署及前端运维不应该和后端项目耦合在一起。避免“大而全”的重量级框架一个框架真的满足不了所有的业务场景。项目多了我们又不想为每个新项目重新造一遍技术“轮子”。新的前端技术React、Vue、Angular2等和工具Grunt/gulp、webpack、Babel等不断涌现、迭代新技术选型应避免“改头换面”式重构。工程化设计要合理分层且相互独立随时应对新需求和技术的变化任何一层能够低成本被替换、淘汰。设计概览 目前app-proto将前端工程化项目拆分成三大模块Node服务负责数据代理、url路由和服务端渲染、Web应用开发专注Web交互体验以及前端运维构建、测试、部署及监控等。整体的结构设计如图1所示。  app-proto 结构设计图 Node服务用于实现前后端分离核心功能是实现数据代理中转附带url路由分发和服务端渲染功能。Web应用开发纯粹的前端模块给予前端工程师极大的自由度进行技术选型专注于Web交互体验的开发。前端运维主要指前端项目构建和部署、工程质量源码质量检查和测试等及监控服务日志、性能等等工作。前后端分离 正如前文所强调的前端模块开发应该“自成体系”而不是后端项目的一部分Controller或View层。比如说前端工程师要在本地跑通完整的项目就必须配置好后端所需开发环境和各种服务如果后端涉及的服务多、变化频繁配置开发联调环境工作往往是耗时耗力的。为了实现彻底的前后端分离我们在前端开发体系中引入了Node服务层。 在最初的开发中为了降低Node端的开发和运营成本我们极力避免在Node服务中“掺合”过多的业务逻辑。经过几个项目的实践最后“约定”在Node服务中我们仅仅做三件事数据代理、路由分发和服务端渲染。 数据代理 首先前端数据从何而来通过Ajax的形式直接从后端服务中获取数据是传统的方式但是在应对多后端服务时还是面临着诸如请求认证、CORS(Cross-origin resource sharing)等困扰。常见的解决方案是通过http-proxy即在Node端通过HTTP请求得到数据后Web端再通过Ajax的方式从Node端间接获取后端数据Node服务起到“桥梁”的作用。 方案http-proxy对已经成熟的后端服务是具备实用价值的但是在后端服务并没有完成开发或前后端并行开发的场景下时开发阶段前端的数据来源依旧是个问题。同时前端还面临诸多请求合并、缓存等需求解决这些困扰前端工程师需要和后端技术人员做大量的沟通、约定。 在这里我们基于原有的http-proxy基础上在Node服务中添加datasources模块尝试在数据的处理上给予前端工程师很大的自由度并实现“按照约定写代码”。 举例说明开发某一前端业务时涉及到pms和upm两个后端服务且提供的API内容如下 # pms API
pms/api/v2.01/login
pms/api/v2.01/inn/create
pms/api/v2.01/inn/get# upm API
upm/api/v3.15/menu面对这些接口理想情况下前端直接通过ajax.post(pms/api/v2.01/login, params)方式获取即可。但是pms接口服务尚处在开发阶段面临跨域或不可用问题。upm接口服务虽稳定但是该服务由第三方团队维护请求需要权限认证。传统的Ajax方式在这类场景下并不适用。而datasources模块是通过怎样的设计来优化这些问题的呢首先我们将前端需要的API映射到前端源码仓库映射的目录结构如下 # server/datasources/{后端系统}/{接口目录}
── datasources├── pms│ ├── login.js│ ├── login.json│ └── inn│ ├── create.js│ └── get.js└── upm├── menu.js└── menu.json其中每个**.js后缀的文件的内容是将原本Web端Ajax操作转移到Node端的HTTP请求以pms/login.js为例 /* async 函数 */
export default async function (params) {const http this.httpconst pms this.config.api.pmstry {const apiUri ${pms.prefix}/login// http 请求http.post() 方法封装了权限认证const result await http.post(apiUri, params)// 简单的数据格式校验if (Number(result.status) 0 (data in result) (bid in result.data)) {// 将bid值记录至sessionthis.session.bid result.data.bid}return result} catch (e) {// 后端API出现异常 (实时通知 or 记录日志)}return null
} 当然对于那些已经成熟稳定的API服务直接通过http-proxy方式实现数据中转即可。但由于需求变更频繁后端API服务始终处在不断迭代中前端在进行数据处理过程中总会面临如下的几种情况 接口校验或数据二次加工面临多后端服务API的格式可能不一致或者对数据列表排序加工等。合并请求可以发多个http请求避免Web端同时发送多个Ajax请求。前端运维的数据比如城市字典、阴阳历转换表等固定数据。缓存数据如请求的用户信息短期内不会有大变动可以采用Half-life cache等算法实现简单缓存。需权限认证的接口HTTP Authentication。这些场景下都建议使用datasources模块进行数据中转将原本需由前后端沟通协调才能实现的功能全部交给前端自行处理给予前端工程师处理数据提供自由度的同时也降低了后端API的开发维度。 那该如何快捷地调用datasources目录下的async函数呢这里我们做了简单封装将该目录下的所有**.js文件解析到Koa的上下文环境中以this.ds对象进行存储并按照目录结构进行驼峰式Camel-Case命名转换过程见图2。  datasources 目录解析转换过程 在Koa中间件中通过this.ds对象调用比如src/datasources/pms/login.js函数映射至this.ds.PmsLogin() // Koa Middlewares
app.use(async (ctx, next) {// ...// 最后一个参数为是否使用mockconst loginData await this.ds.PmsLogin(params, false)// ...
})在Web端可以统一封装ds()方法无需关注Ajax请求Headers、是否跨域等问题 // Web (Browser)
ds(PmsLogin, { username, password }, true).then(success).catch(error)Mock支持 正如前文所提到的后端研发进度一般滞后于前端在后端API服务可用之前前端仅有一份API文档供参考。在规范中**.json后缀的文件就起到Mock作用同样以pms/login.json举例 {status: 0,message: 成功,data: { bid: string(32), innCount: 1 }
}具体的json格式写法请参考mockjs、Syntax Specification。 简言之当API服务可用时则执行**.js后缀文件中的async函数来获取数据不可用时则解析**.json后缀Mock文件并不需要单独开启一个Mock服务。 路由分发 对url路由的处理和数据代理的做法类似按照目录结构来管理。url路由配置在server/pages目录下目录下的文件会自动映射成为路由。 比如url为http://example.com/pms页面映射到server/pages/pms.js文件的写法如下 export default {urls: [/pms, /pms/error], // 多种正则如[/pms, [/pms/v1], [/pms/v**]]methods: [GET], // 多种method[GET, POST]js: [http://code.jquery.com/jquery-1.12.0.min.js],css: [http://yui.yahooapis.com/pure/0.6.0/pure-min.css],template: default, // 服务端渲染模板middlewares: [], // 针对本页面的中间件controller: async function(next) { // Koa中间件最后一环// 可以从this.ds对象中拿数据const loginData await this.ds.PmsLogin(params)return {foo: 来自服务端数据, loginData}}
}由于urls支持多种正则原则上每个根url映射server/pages/目录下一个**.js文件映射关系如图3所示。  pages目录文件与url映射关系 如果对js、css、template没有特殊设置采用默认设置的情况下可精简如下 export default {urls: [/pms, /pms/error],controller: async function (next) {const loginData await this.ds.PmsLogin(params)return {foo: 来自服务端数据, loginData}}
}需要注意的是controller项是Koa中间件的最后一环要求其返回值是可序列化的对象用于模板渲染的服务端参数在此处也可以进行权限校验、从this.ds对象中拿数据等操作。 服务端渲染 Node服务端最后一个核心功能是渲染输出 HTML Shell和 JSON。输出JSON字符串的用途是为了浏览器端能以Ajax形式动态获取数据而输出的HTML内容则是我们Web应用的所需的HTML“壳子”。 正如前文提到我们的业务特点是“一种运行于浏览器的工具软件”重操作交互、无SEO需求。因此同构Isomorphic JavaScript不是强需求不是每次都要依赖服务器来重复处理逻辑和数据。服务端只需要渲染简单完善的HTML结构即可具体的页面内容则由客户端JavaScript实现。简言之不鼓励将前端JavaScript脚本再在Node服务端重复执行一遍。 如果了解过Google推崇的 Progressive Web App你可以参考《The App Shell Model》一文来理解HTML“壳子”更多的用途。 渲染最简单的HTML“壳子”如下: !DOCTYPE html
html langenheadmeta charsetutf-8/titleapp-proto/titlescriptwindow.serveData{foo: 来自服务端数据}/script/headbodydiv idapp/divscript src//cdn/file-5917b08e4c7569d461b1.js/script/body
/html提供简单的服务端数据window.serveData供客户端使用更多渲染则由//cdn/file-5917b08e4c7569d461b1.js进行增量控制。 静态资源与Node端衔接 那Web端构建的静态资源是如何Node服务端做衔接的呢前端静态资源构建工作与Node服务相互分离Node服务在开启的过程中会读取前端构建生成的静态资源映射表。前端的构建过程如图4所示在构建工作完成之后会生成assets.json静态资源映射表。  静态资源映射文件assets.json构建 前端构建工具基本都提供静态资源映射表生成插件比如构建工具Webpack就存在插件assets-webpack-plugin来实现该功能。 生成的assets.json映射表内容参考如下 {index: // 对应的页面url: example.com/index{ js://s0.example.net/pms/index-2abb99.js }, // 涉及到的静态资源列表带版本号login:{ js://s0.example.net/pms/login-5917b0.js }
}比如在渲染页面example.com/index时Node服务会以index作为键值读取assets.json中带版本号的静态资源CDN地址列表用于在“壳子”中与前端资源的衔接工作。 Web端的一些“约定” Web端的技术选项是没有强制性限制的无论你采用何种构建工具、前端库只要生成符合约定供Node端使用的assets.json文件即可。 前端工程师可以根据具体的业务特点、团队技术喜好来选取合理的开发方案无论是React、Vue还是Angular2并不做强限制。尽管给予Web前端开发很大的自由度但是鼓励遵循下面几条“约定” Ajax请求从Node端代理而非具体后端服务。鼓励将JavaScript、CSS、HTML视为前端领域的“汇编”。重视前端页面状态管理推荐的方案有Redux、vuex及MobX等。强调组件化面向组件集开发。这里重点强调下面向组件集的前端开发。在项目初期我们一般不会马上投入到业务开发而是针对设计师和产品经理提供的设计稿、产品原型图实现一套组件集或选择合适的开源组件集积累好基础组件集后再投入到具体业务开发。 在进行前端技术调研时该技术是否有配套的开源组件集往往是我们考虑的重点。比如基于React实现的开源组件集ant.design、Material-UI等我们部分前端项目都直接或间接的使用到了极大地减少了研发成本。 当然美团点评内部也提供一个组件中心平台可参考美团点评前端组件中心介绍Slide鼓励大家将各自项目中的有价值组件分享出来实现组件跨项目复用。 工程化支持 项目脚手架 项目脚手架的作用是在启动一个新项目时通过几个简单命令就能快速搭建好项目的开发环境。我们基于Yeoman构建了一个完整的项目脚手架。 # 安装脚手架
$ npm install -g yo
$ npm install -g ia/generator-app-protolatest
# 初始化新项目进行简单选择
$ yo ia/app-proto工程质量保障 我们重视项目的每次commit同个项目要求遵循同一套编码规范并采用ESLint等工具进行约束对于一些复用性高的核心组件也强制要求写测试。 为保障项目质量每个项目都要求接入美团点评基于[Stash](https://en.wikipedia.org/wiki/Stash_(software)实现的Castle CI系统每次的源码提交都会自动执行一遍ESLint、测试和构建并生成构建日志通过公司内部沟通工具大象进行实时消息推送。 标准化测试环境管理 美团点评内部提供了基于Docker实现的测试环境管理服务Cargo用于提升测试和联调测试效率促进DevOps开发模式。将项目接入到Cargo服务后只需在仓库中提供简单的配置文件cargo.yml配置参考如下就会自动生成一套测试环境。 # 依赖的镜像
image: registry.cargo.example.com/node:v4.2.1
# 容器占用的端口
ports:- 8998
# 环境变量
env:- COMMON_VARIABLE true- NODE_ENV cargo- DEBUG app-proto,datasource.*
# 收集的日志文件
logs:- error /var/path/logs/app-proto/error.log- out /var/path/logs/app-proto/out.log
# 构建脚本
build_script: bin/pre-deploy-staging
# 运行脚本
run_script: bin/cargo-start总结 前端工程化体系的引入让前端开发能和原生App应用项目开发一样“自成体系”脱离了对后端项目的依赖。基于“约定优于配置”、“按照约定写代码”的原则对Node层功能的设定能够降低沟通协调成本构建、部署等工作的规范化使前端技术人员的开发重点回归到Web应用的交互体验本身回归到“纯粹”的前端研发。