相关文章链接:
《前端工程化》-- 1. 前端工程简史
《前端工程化》-- 2. 脚手架/3. 构建
4. 本地开发服务器
4.1 本地开发服务器解决的问题
动态构建和Mock服务是本地开发服务器的主要功能。
动态构建解决的问题是面向开发层面的,通过监听--修改--触发--构建的流程避免源码的每次修改都需要人为地执行一次构建,便于开发过程中的即时调试。
Mock服务解决的问题是面向前后端协作层面的,以提前约定好的规范为前提,通过本地服务容器提供的Mock数据接口辅助前端逻辑的编写。
此外,如果项目需要SSR(服务器端渲染),本地开发服务器还需要具备解析HTML模板的功能,同时Mock服务提供SSR所需要的初始数据。
4.2 动态构建
动态构建,或者叫作动态编译(Dynamic compilation)。本地开发服务器动态编译功能的目的是为了节省人力、方便前端开发和调试,本质原理是监听 触发。
4.2.1 webpack-dev-middleware
webpack-dev-server是官方提供的用于搭建本地开发环境的一个微型Node.js服务框架,并且提供动态编译、HMR(热更新)等功能。如果项目不需要Mock服务,webpack-dev-server完全可以满足需求。
webpack-dev-middleware是Express框架的一个中间件,结合一些必要的功能模块可以实现动态编译以及热更新等功能。
简单来说,中间件是在输入到输出过程中对内容进行加工从而输出预想的数据。
webpack-dev-middleware将webpack构建输出的文件存储在内存中。正常情况下,webpack构建产出的文件会存储在output配置项指定的硬盘目录中。 webpack-dev-middleware在此基础上建立了一个文件映射机制,每当匹配到一个webpack构建产出文件的请求后便会将内存中与其对应的数据返回给发起请求的客户端。
由于是内存的文件系统,没有耗时的硬盘读写过程,数据的更新非常快,这也是webpack相较其他同类工具的优势之一。
实际上,webpack-dev-server就是在Express和webpack-dev-middleware基础上进行的封装。
4.2.2 Livereload和HMR
Livereload的原理是在浏览器和服务器之间创建WebSocket连接,服务器端在执行完动态编译之后发送reload事件至浏览器,浏览器收到此事件之后刷新整个页面。
流程如图:
Livereload虽然能够保证动态构建的资源被浏览器即时获取,但无法保存页面状态。
HMR以局部更新取代整体页面刷新,有效地弥补了Livereload无法保存页面状态的缺陷。
在开启webpack-dev-server模式下,webpack向构建输出的文件中注入了一项额外的功能模块--HMR Runtime,同时在服务器端也注入了对应的服务模块--HMR Server。
HMR热更新的流程:
1)修改源文件并保存后,webpack监听到Filesystem Event事件并触发了重新构建行为;
2)构建完成之后,webpack将模块变动信息传递给HMR Server;
3)HMR Server通过WebSocket发送Push信息告知HMR Runtime需要更新客户端模块,HMR Runtime随后通过HTTP获取待更新模块的内容详情;
4)最终,HMR Runtime将更新的模块进行替换,在此过程中浏览器不会进行刷新。
webpack-hot-middleware是可实现HMR的中间件,用于Express服务器端集成,集成方式很简单,只需在webpack-dev-middleware之后接入HMR中间件即可。
4.3 Mock服务
Mock服务针对的是前后端协作层面的问题,通过模拟数据解耦了前端逻辑的编写对后端接口的依赖,Mock服务是实现前后端分离和并行开发的核心。
4.3.1 Mock的必要前提和发展进程
Mock的必要前提是在正式进入开发阶段之前,前后端开发人员需要协定接口的规范细节,包括请求方法、入参、返回值等。
1)假数据
假数据的普遍用法是在业务代码中直接声明一个变量,代替接口返回的数据。
在业务代码中编写假数据的方式虽然简单方便,但是无论是从代码逻辑的严谨度,还是产品的质量保障角度考虑,这种方案都存在难以忽视的弊端。
2)客户端Mock
在客户端拦截JavaScript代码发出的AJAX请求并返回由Mock.js创建的假数据。
客户端Mock的优点是解决了代码中直接编写假数据无法模拟请求流程和异常处理的问题,并且客户端Mock相当于创建了一个模拟接口,而不是针对某个接口的假数据。
可以将客户端Mock的代码集中写入一个单独的js文件,一方面便于统一维护,另一方面在接口完成之后直接把引用Mock的js文件删除即可。
即便客户端Mock提供了诸多便利,Mock相关的代码或文件仍然必须存在于业务代码中,而部署上线之前需要将其删除,这对于产品质量保障始终存在一定的隐患。
3)Mock Server
将Mock作为一种服务集成到前端工程体系中的工作流程如图:
在开发阶段使用Mock Server提供的与真实接口规范和逻辑一致的本地接口进行开发;
开发完成后,在构建阶段将Mock的地址修改为已完成的真实服务器端接口地址。
4.3.2 异步数据接口
Mock Server最普遍的使用场景是模拟异步数据接口,比如使用AJAX或者JSONP获取和提交数据,
模拟的方式通常有两种:
1)Local:本地模式
在开发阶段使用本地API代替真实API地址,使用本地的JSON数据作为异步接口的请求响应。
2)Proxy:代理模式
将异步接口代理到线上的其他接口地址,类似于转接者角色。主要的功能是为了解决某些接口不支持跨域请求的限制。
express-http-proxy是一个能够实现HTTP请求代理的Express中间件。
Mock Server本质上是一个简化版的Web Server,最基础的组件是负责分发的路由:
3)DefinePlugin和环境变量
开发环境使用Mock Server将所有的真实接口地址修改为本地域名地址,在部署测试和生产环境之前必须将接口的地址复原。
项目代码从开发环境迁移到测试和生产环境之前必须经过构建,我们可以将构建系统作为切入点,使用工具自动完成接口地址的修改。
接口地址的修改涉及两个方面:
a. 执行环境,开发、测试、生产环境下的接口地址均不同,所以必须能够根据部署目标环境将接口修改为对应的地址。
b. 字符串修改,对于JavaScript代码来说,接口地址就是一个字符串,我们要做的是将该字符串修改为指定的值。
DefinePlugin是webpack的一个插件,用于定义一系列在构建阶段被替换的全局变量(针对webpack而言的全局可访问变量)。
4.3.3 SSR
虽然目前市场大多数采用前后端分离开发的团队将HMTL的渲染工作交给了客户端,但是依赖于SEO的产品仍然难以避免使用服务器端渲染。
也就是说,HTML模板源文件需要由服务器端维护,前端开发人员使用与服务器端语言统一的Mock Server承担HTML模板的渲染工作以便于前端逻辑的开发。
除此之外,如果页面HTML文档中初始静态内容过多,前端工程师会偏向于使用HTML模板语法编写源代码,便于模块化开发和维护。
加入SSR支持的Mock Server架构如图:
Mock Server支持SSR的场景有两种:
1)页面初始输出的静态内容较多,使用HTML模板语言便于模块化开发和维护;
2)依靠服务器端动态数据渲染初始页面。
对于第一种场景,使用HTML模板语法编写的文件只存在于源代码中,经过构建被编译为规范的HTML语法。处理这类需求的方案,在webpack中配置对应的loader和plugin即可。
在这类场景下,HTML模板语法只是为了便于开发和维护,构建产出规范HTML文件由前端工程师负责部署,而不是与服务器端代码一同部署。
换句话说,渲染是在构建阶段“预执行”的,而不是在生产环境下即时执行的,这类场景可以称为“预服务器端渲染”。预SSR场景和无SSR场景解决资源定位的方案一致。
第二类场景是常规意义上的SSR,也就是即时服务器端渲染,针对的是非前后端分离项目。
Mock Server支持即时SSR的必要前提是必须使用与服务器端相同的编程语言搭建。
5. 部署
5.1 部署流程的设计原则
在此讨论的部署指的是开发人员将构建产出的代码包部署至测试机的过程,而并非是将测试完成的代码发布至生产环境的过程。
测试机通常是为了搭建测试环境或者仿真生产环境使用,也有些团队称之为跳板机,一般只供内网访问。
5.1.1 速度--化繁为简
用简单自动化的方式取代烦琐的工具使用,必须遵循两个原则:
1)可配置化:
部署的目标服务器、路径信息应该与项目一一对应,并且可供负责部署的人员进行配置。
2)操作简化:
部署行为的操作应该足够简单。
5.1.2 协作--代码审查和部署队列
协作的本质是个体或团体之间的交流互助的过程。
在此讨论的代码审查指的是轻量的、经常性的、贯穿整个迭代周期的审查行为。
常规的代码审查往往在一次迭代完成之后进行,审查内容以代码为主,以同步进度为目的,以沟通为主要途径。
代码审查只是一种作为辅助性、非强制性的手段。在此基础上仍然需要绝对可控的途径加强规范的约束,部署队列便是典型的解决案例。
简单来说,部署队列就是将所有部署请求按顺序形成一个队列,由专门的部署审查人员负责队列的控制,如果审查通过则允许部署,否则便拒绝此次部署请求,并且当前队列中的此次请求之后的所有请求均被拦截。
5.1.3 安全--严格审查和权限控制
保障部署安全最常用的方案是制定严格的审查制度和权限控制规范。
严格审查在代码审查基础上新增了代码层面的约束,比如校验部署测试环境的代码是否包含线上API的非法调用,这项工作也可以与单元测试结合,由测试工具自动完成。
权限控制规范下的开发人员和审查人员进行重新分工,开发人员不再具备部署权限,审查人员除严格审查代码以外,还需要执行具体的部署行为。
5.2 流程之外:前端静态资源的部署策略
5.2.1 协商缓存与强制缓存
html文件是Web站点的唯一入口,所有其他资源必须由html文件直接或者间接引用才可以被加载。
html的特殊性决定了它只能使用协商缓存,其他资源(如JS、CSS等)更适用于强制缓存。
这种缓存策略的分配能够保证用户每次访问网站均能够获取到最新的html资源,其他静态资源,不论是增量更新还是覆盖更新,其URL的更新均直接体现到html文档中。
只要保证html更新的及时性,便等于保证了全站资源的更新效率。
5.2.2 Apache设置缓存策略
协商缓存涉及HTTP协议Header中的Expires和Cache-control字段,具体的值为:
Expires设置为0;
Cache-control设置为no-cache和max-age=0。
6. 工作流
6.1 本地工作流
本地工作流是本地工具链阶段的前端工程体系所对应的工作模式,此阶段的各个功能模块均由开发人员在本机环境下执行。
执行人:前端开发人员;
执行环境:分散的本地开发环境。
基本流程如图:
6.2 云平台工作流
云平台工作流在本地工作流的基础上,将容易因个体差异产生问题的功能模块(比如构建、部署等)提升至云平台运行,通过严谨的流程控制增强开发的规范性。
下图是一个简易的云平台工作流:
6.3 持续集成与持续交付
持续集成强调将散列开发人员提交的代码进行快速集成,并且实现自动构建和测试。
持续交付在持续集成的基础上,将集成并自动构建、测试通过的代码自动部署至测试或仿真生产环境中,而生产环境的部署仍须人工操作。
持续部署在持续交付的基础上进一步自动化,将部署生产环境的工作自动化。
持续集成的目标群体是开发人员,持续交付的目标是测试环境和测试人员,持续部署的目标是生产环境和真实用户,三者可以理解为逐步强大的串联流程。