johnnychq / blog

blog
4 stars 0 forks source link

HTML及其静态资源时间戳部署 #6

Open johnnychq opened 6 years ago

johnnychq commented 6 years ago

本文针对的阅读对象是想系统了解时间戳机制,对应部署机制,尤其是与HTML结合部分的同学,需要对前端运维有一定了解。

关于时间戳

关于前端资源时间戳问题,知乎上有个经典的回答 大公司里怎样开发和部署前端代码,详细讲述了,时间戳问题的起因以及对应2种解决方案:

以及在静态资源部署在cdn情况下覆盖式发布非覆盖式发布

不过,答案中未对时间戳如何更新到HTML中作详细的介绍,本文主要讲解这个HTML及其时间戳部署问题及解决方案

接下来的讨论,假定用的是信息摘要时间戳以及非覆盖式发布机制

关于HTML及其时间戳

时间戳部署问题,指的是将生成的时间戳更新到对应HTML的资源引用链接当中,当资源内容发生变化后,通过工程化的手段,自动更新对应的时间戳。

<link rel="stylesheet" href="http://static.xxx.com/a_{timestamp}.css"/>
<script src="http://static.xxx.com/a_{timestapm}.js"></script>

页面刷新时,用户永远能够访问到最新的文件内容,即通过一种机制让timestamp永远保持最新。

影响部署方案的三个方面

不同条件下的部署方案是不同的,主要受 HTML存储在哪里HTML部署的哪里解决过程采用编译时还是运行时三方面的影响。

HTML存储在哪里

这里的HTML存储的哪里指的是在代码托管上,HTML(也可能是服务端模板)是否与静态资源(js/css/img)在同一个代码仓库中。不关心HTML是否是服务端(java/php/nodejs)模板生成,只要是在同一个代码仓库,文件层面上在一起即可。

是否在同一代码仓库中非常重要,因为如果是,那么在文件目录结构上HTML与资源具有物理上的关联,那么在生成文件时间戳的同时可以根据关联关系立即更新/部署对应HTML的时间戳;否的话,必须提供一种异步机制,让二者产生关联。

HTML与静态资源如果不在同一个仓库时,必须建立一个中间存储机制,保存静态资源文件对应的时间戳,然后在HTML编译或运行时环境中,获取并匹配中间存储对应的时间戳进行更新/部署。

幸运的是,现在主流的研发模式里,越来越推崇全栈纯粹的前后端分离了,两种方式都极大的简化了时间戳的部署方式。尤其是后者,更加简单,Webpack提供了专门的html插件,自动创建html的同时帮你完成时间戳部署的事情。

HTML部署的哪里

在传统开发中,HTML均以服务端模板的方式存在于应用服务器中,通过域名www.xxx.com即可访问。其中,静态资源如果同机部署的话,也可以用www.xxx.com进行访问;如果静态资源分开部署到了oss/cdn中的话,则用要用static.xxx.com访问,但不影响用www.xxx.com访问HTML。

但在纯粹的前后端分离方案中,如果把HTML当做普通静态资源部署到oss/cdn中的话,就只能通过static.xxx.com/index.html访问到HTML文件,这对用户极其不友好,我们传达给用户的域名应该是www.xxx.com;另外,也无法做不同路径url映射同一个html的功能(browserHistory)。所以需要对这种情况下的HTML部署做特殊处理。

纯粹的前后端分离在部署时,将HTML作为特殊静态资源,单独打包部署到nginx中;其他资源单独部署到oss/cdn中。这个主要影响的是打包和部署策略,跟运维同学沟通清楚即可。

上述问题只会存在于将普通静态资源单独部署到oss/cdn情况,且采用纯粹的前后端分离方案下,其他情况不存在此问题。但因为纯粹的前后端分离是当下主流的研发模式,且涉及运维层面(前端对运维可能不是特别清楚),因此单独提出来。

解决过程采用编译时还是运行时

能够预编译解决的问题,尽可能的不要选择运行时,让生产环境保持简单意味着生产环境更加稳定和高效。

代码的上线过程:开发 -> 发布 -> 运行,时间戳的部署可以选择在发布过程中的预编译环节,也可以在运行时解决,优先选择编译时。

HTML存储在哪里小节中其实已经讲过了,如果HTML与静态文件在同一仓库,那么预编译时,在生成静态文件时间戳的同时,根据仓库的文件路径关系,找到对应的HTML页面,更新/部署对应的时间戳即可。所以全栈开发, 纯粹前后端分离开发的同学尽情享用这种方式把。

如果HTML与静态文件隶属不同仓库,就麻烦了,那先编译生成静态文件时间戳并存储到中间存储;再编译HTML,与此同时获取刚才中间存储中的最新时间戳;最后更新/部署HTML中的时间戳。不过这里有个缺点编译时序依赖,要求你在发布前,要严格保证发布的顺序,先发布前端仓库,后发布后端。人肉保证比较困难,系统保证感觉有点重,自己权衡把。

运行时动态获取最新时间戳可以解决编译时序依赖问题,但必须通过模板宏及运行时获取中间存储时间戳的机制,增加了生产环境的复杂度。

常见开发模式下的HTML及其时间戳部署

前文讲解偏理论,本文看下常见前后端开发模式下的具体案例,假定前端资源基于webpack。

纯粹前后端分离

此模式下,HTML与静态资源统一存放,部署时假定要求cdn,无视服务端代码是否同一仓库

  1. html-webpack-plugin自动生成html
  2. 发布编译环节,运行webpack自动生成dist编译后资源(包括html)
  3. 发布打包环节,将dist中的html单独打包html.zip
  4. 发布打包环节,将dist中的静态资源单独打包static.zip
  5. 发布打包环节,将服务端代码打包成server.zip
  6. 发布部署环节,将html.zip解压缩到nginx静态资源目录,自行配置nginx转发规则
  7. 发布部署环节,将static.zip发布到oss,cdn会自动回源
  8. 发布部署环节,将server.zip进行部署

step3, step5需要特别关注。

传统前后端分离(同一仓库)

此模式下,HTML在服务端,服务端与前端代码同一仓库,是否部署cdn无所谓

  1. 提前准备好html,并手动维护到服务端view目录下, html中无时间戳
  2. 发布编译环节,运行webpack自动生成dist编译后资源(包括html)
  3. 发布编译环节,根据静态资源与view模板目录关系,自动将dist中的文件时间戳更新到view中的html
  4. 发布打包环节,将dist中的静态资源打包为static.zip
  5. 发布打包环节,将服务端代码(含html)打包为server.zip
  6. 发布部署环节,将static.zip进行部署
  7. 发布部署环节,将server.zip进行部署

step3需要特别关注。另外传统开发模式(非前后端分离)下也是此过程。

传统前后端分离(分开仓库/编译时)

此模式下,HTML在服务端,服务端与前端代码不同仓库,是否部署cdn无所谓

  1. 提前准备好html,并手动维护到服务端view目录下, html中无时间戳
  2. 发布编译环节,运行webpack自动生成dist编译后资源(包括html)
  3. 发布编译环节,自动将dist中的文件时间戳存放到timestamp.server
  4. 发布编译环节,编译后端代码,其中编译html时,从timestamp.server取回最新时间戳,进行编译。
  5. 发布打包环节,将dist中的静态资源打包为static.zip
  6. 发布打包环节,将服务端代码(含html)打包为server.zip
  7. 发布部署环节,将static.zip进行部署
  8. 发布部署环节,将server.zip进行部署

step2,3,4需要特被关注,尤其是需要保证编译的顺序

传统前后端分离(分开仓库/运行时)

此模式下,HTML在服务端,服务端与前端代码不同仓库,是否部署cdn无所谓,运行时方案

  1. 提前准备好html,并手动维护到服务端view目录下, html中无时间戳
  2. 发布编译环节,运行webpack自动生成dist编译后资源(包括html)
  3. 发布编译环节,自动将dist中的文件时间戳存放到timestamp.server
  4. 发布打包环节,将dist中的静态资源打包为static.zip
  5. 发布打包环节,将服务端代码(含html)打包为server.zip
  6. 发布部署环节,将static.zip进行部署
  7. 发布部署环节,将server.zip进行部署
  8. 运行时,HTML模板宏通过一定机制,实时获取timestamp.server中的最新时间戳,实时编译生成最终html

step8是关键。