blog
blog copied to clipboard
前端应用部署需知
项目形态:SSR、CSR、SSG
SSR
SSR(Server Side Render) 是指服务端渲染,在我们 web 较早的时候,开发者喜欢使用 jsp、php 或者其他模板渲染引擎来构造一个应用,当客户端向服务器发出请求,然后运行时查询数据,模板填充,生成 html 内容并返回给客户端。
优点:
- 有利于 SEO,由于页面在服务器生成,搜索引擎直接抓取到最终页面结果。
- 有利于首屏渲染,html 所需要的数据都在服务器处理好,直接生成 html,首屏渲染时间变短。
缺点:
- 占用服务器资源,渲染工作都在服务端渲染。
- 用户体验不好,每次跳转到新页面都需要在重新服务端渲染整个页面,不能只渲染可变区域。
CSR
CSR(Client Side Rendering) 是指客户端渲染。顾名思义,就是在渲染工作在客户端(浏览器)进行,而不是在服务器端进行。举个例子,我们平时用 vue,react 等框架开发的项目,都是先下载 html 文档(不是最终的完全的 html),然后下载 js 来执行渲染出页面结果。 以 react 为例,客户端渲染初始化的 html 一般如下
<!DOCTYPE html>
<html lang="en">
<head>
<title data-react-helmet="true">react app</title>
<noscript>
</noscript>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<script type="text/javascript" src="/static/js/bundle.js" defer=""></script>
<script type="text/javascript" src="/static/js/main.chunk.js" defer=""></script>
</body>
</html>
可以看出当前页面除了 <div id="root"></div>
元素,没有其他的元素,然后通过加载 bundle.js
, main.chunk.js
来执行渲染。整个渲染过程包括,生成 DOM 节点,注入样式,交互事件绑定,数据获取等等。
优点:
- 前后端分离。前端专注于界面开发,后端专注于 api 开发,且前端有更多的选择性,可以使用 vue,react 框架开发,而不需要遵循后端特定的模板。
- 服务器压力变轻了,渲染工作在客户端进行,服务器直接返回不加工的 html
- 用户在后续访问操作体验好,(首屏渲染慢)可以将网站做成 SPA,可以增量渲染
缺点:
- 不利于 SEO,因为搜索引擎不执行 JS 相关操作,无法获取渲染后的最终 html。
- 首屏渲染时间比较长,白屏或展示 loading 动画,因为需要页面执行 ajax 获取数据来渲染页面,如果接口等待时间长,不利于用户体验
SSG
SSG(Static Site Generation) 是静态站点生成,解析是在构建时执行的,当发出请求时,html 将静态存储,直接发送回客户端,相当于一个静态资源。SSG 这种渲染模式采取了 CSR 和 SSR 的共同优点,它不需要开发者介入服务器操作,开发者只需要准备 cdn 或者其他静态网页托管服务器,prerender 出静态资源这一步将在构建时就已经做了,呈现在用户眼前的虽然不是实时变更的,但是也保留了 CSR 和 SSR 的精髓,一定程度上有了平衡。但是因为 prerender 的缘故,它和 SSR 的大致工作方式会相似一点。
优点:
- 减轻服务器压力,可以把生成的静态资源(html)放到 CDN 上,合理利用缓存
- 有利于 SEO,由于 html 已经提前生成好,不需要服务端和客户端去渲染
缺点:
- 在构建阶段所取数据的任何更改都需要在服务端上重建构建生成页面。
- 随着业务的复杂,需要生成的页面可能不单单只有 1,2 个,所以这对于构建的要求很高
静态资源组织:http 缓存、CDN
- 最大限度的利用缓存,将页面入口(HTML)设置为协商缓存,将 Javascript、CSS 等静态资源设置为永久缓存。
协商缓存一般可在服务端通过设置 Last-Modified、ETag 等 ResponseHeader 实现。(304 状态码,表示资源未发生变更,可使用浏览器缓存。)
强缓存一般可在服务端通过设置 Cache-Control:max-age、Expires 等 ResponseHeader 实现。
设置强缓存后,Network 大致变成了这样:
From DiskCache:从硬盘中读取。
From MemoryCache:从内存中读取,速度最快。
- 为了解决强缓存更新问题,将文件摘要(hash)作为资源路径(URL)构成的一部分。为了解决覆盖式发布引发的问题,采用 name-hash 而非 query-hash 的组织方式,具体需要配置 Wbpack 的 output.filename 为 contenthash 。
nuxt.config.js by fileNames 选项中更改此设置。
默认情况下为:
{
app: ({ isDev }) => isDev ? '[name].js' : '[contenthash].js',
chunk: ({ isDev }) => isDev ? '[name].js' : '[contenthash].js',
css: ({ isDev }) => isDev ? '[name].css' : '[contenthash].css',
img: ({ isDev }) => isDev ? '[path][name].[ext]' : 'img/[contenthash:7].[ext]',
font: ({ isDev }) => isDev ? '[path][name].[ext]' : 'fonts/[contenthash:7].[ext]',
video: ({ isDev }) => isDev ? '[path][name].[ext]' : 'videos/[contenthash:7].[ext]'
}
content-hash,与 hash、chunkhash 的区别:
- 为了加快访问,结合 CDN 提升访问速度,采用了 Nginx 反向代理+ 将静态资源上传到 CDN。为了上传 CDN,我们需要按环境动态构造 publicPath + 按环境构造 CDN 上传目录并上传。
publicPath: webpack 提供一个非常有用的配置,该配置能帮助你为项目中的所有资源指定一个基础路径。它被称为公共路径(publicPath)。
import webpack from "webpack";
// 资源路径前缀,生产环境使用cdn域名
const ASSET_PATH =
process.env.PATH_TYPE !== "production" ? "/_nuxt/" : `//expample.cdn.cn`;
export default {
output: {
publicPath: ASSET_PATH,
},
plugins: [
// 该插件帮助我们安心地使用环境变量
new webpack.DefinePlugin({
"process.env.ASSET_PATH": JSON.stringify(ASSET_PATH),
}),
],
};
自定义 Jenkins 构建流程:shell 脚本
- https://github.com/yanyue404/blog/issues/228#issue-1214535495
- https://github.com/yanyue404/blog/issues/221#issue-1034213005