¡Continúe creando, acelere el crecimiento! Este es el primer día de mi participación en el "Nuggets Daily New Plan · June Update Challenge", haz clic para ver los detalles del evento
Hola a todos, soy Shanyue, esta es mi columna recién abierta: Serie de implementación de front-end . Incluyendo Docker, CICD, etc., el esquema es el siguiente:
Además, Bilibili está actualizando videos relacionados, vea la versión de video de la serie de implementación frontal
La serie de implementación front-end se está actualizando : 5/15
En el último artículo, cubrimos la optimización del almacenamiento en caché con el almacenamiento en caché de compilaciones y las compilaciones de varias etapas en Docker.
Pero al implementar aplicaciones de una sola página, todavía hay un problema, y es el enrutamiento del lado del cliente.
En este artículo, Docker react-router-dom
implementará .
PD: este proyecto utiliza el almacén cra-deploy como práctica, y el archivo de configuración se encuentra en router.Dockerfile
enrutamiento
Úselo para agregar una ruta react-dom
para una aplicación de una sola página. Dado que el enrutamiento no es el contenido principal de esta columna, se omite el uso de enrutamiento. El código final es el siguiente.
El código fuente se encuentra en cra-deploy/src/App.js
import logo from './logo.svg';
import './App.css';
import { Routes, Route, Link } from 'react-router-dom';
function Home() {
return (
<div>
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1>当前在 Home 页面</h1>
<Link to="/about" className="App-link">About</Link>
</header>
</div>
)
}
function About() {
return (
<div>
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1>当前在 About 页面</h1>
<Link to="/" className="App-link">Home</Link>
</header>
</div>
)
}
function App() {
return (
<div>
<Routes>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
</Routes>
</div>
);
}
export default App;
复制代码
En este punto tienes dos rutas:
/
,página delantera/about
, acerca de la página
Vuelva a implementar, hay un problema con el enrutamiento
Vuelva a implementar la página según el archivo de docker-compose
configuración .
$ docker-compose up --build simple
复制代码
En este punto https://localhost:4000/about
, se mostrará un 404.
其实道理很简单:在静态资源中并没有 about
或者 about.html
该资源,因此返回 404 Not Found。而在单页应用中,/about
是由前端通过 history API
进行控制。
解决方法也很简单:在服务端将所有页面路由均指向 index.html
,而单页应用再通过 history API
控制当前路由显示哪个页面。 这也是静态资源服务器的重写(Rewrite
)功能。
我们在使用 nginx 镜像部署前端应用时,可通过挂载 nginx 配置解决该问题。
nginx 的 try_files 指令
在 nginx 中,可通过 try_files 指令将所有页面导向 index.html
。
location / {
# 如果资源不存在,则回退到 index.html
try_files $uri $uri/ /index.html;
}
复制代码
此时,可解决服务器端路由问题。
除此之外,我们还可以通过 nginx 配置解决更多问题。
长期缓存 (Long Term Cache)
在 CRA 应用中,./build/static
目录均由 webpack 构建产生,资源路径将会带有 hash 值。
$ tree ./build/static
./build/static
├── css
│ ├── main.073c9b0a.css
│ └── main.073c9b0a.css.map
├── js
│ ├── 787.cf6a8955.chunk.js
│ ├── 787.cf6a8955.chunk.js.map
│ ├── main.a3facdf8.js
│ ├── main.a3facdf8.js.LICENSE.txt
│ └── main.a3facdf8.js.map
└── media
└── logo.6ce24c58023cc2f8fd88fe9d219db6c6.svg
3 directories, 8 files
复制代码
此时可通过 expires
对它们配置一年的长期缓存,它实际上是配置了 Cache-Control: max-age=31536000
的响应头。
那为什么带有 hash 的资源可设置长期缓存呢: **资源的内容发生变更,他将会生成全新的 hash 值,即全新的资源路径。**而旧有资源将不会进行访问。
location /static {
expires 1y;
}
复制代码
nginx 配置文件
总结缓存策略如下:
- 带有 hash 的资源一年长期缓存
- 非带 hash 的资源,需要配置 Cache-Control: no-cache,避免浏览器默认为强缓存
nginx.conf
文件需要维护在项目当中,经过路由问题的解决与缓存配置外,最终配置如下:
该 nginx 配置位于 cra-deploy/nginx.conf
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html index.htm;
location / {
# 解决单页应用服务端路由的问题
try_files $uri $uri/ /index.html;
# 非带 hash 的资源,需要配置 Cache-Control: no-cache,避免浏览器默认为强缓存
expires -1;
}
location /static {
# 带 hash 的资源,需要配置长期缓存
expires 1y;
}
}
复制代码
Dockerfile 配置文件
此时,在 Docker 部署过程中,需要将 nginx.conf
置于镜像中。
修改 router.Dockerfile
配置文件如下:
PS: 该 Dockerfile 配置位于 cra-deploy/router.Dockerfile
FROM node:14-alpine as builder
WORKDIR /code
# 单独分离 package.json,是为了 yarn 可最大限度利用缓存
ADD package.json yarn.lock /code/
RUN yarn
# 单独分离 public/src,是为了避免 ADD . /code 时,因为 Readme/nginx.conf 的更改避免缓存生效
# 也是为了 npm run build 可最大限度利用缓存
ADD public /code/public
ADD src /code/src
RUN npm run build
# 选择更小体积的基础镜像
FROM nginx:alpine
ADD nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=builder code/build /usr/share/nginx/html
复制代码
PS: 该 docker compose 配置位于 cra-deploy/router.Dockerfile
version: "3"
services:
route:
build:
context: .
dockerfile: router.Dockerfile
ports:
- 3000:80
复制代码
使用 docker-compose up --build route
启动容器。
- 访问
http://localhost:3000
页面成功。 - 访问
http://localhost:3000/about
页面成功。
检验长期缓存配置
访问 https://localhost:3000
页面,打开浏览器控制台网络面板。
此时对于带有 hash 资源, Cache-Control: max-age=31536000
响应头已配置。
此时对于非带 hash 资源, Cache-Control: no-cache
响应头已配置。
百尺竿头更进一步
在前端部署流程中,一些小小的配置能大幅度提升性能,列举一二,感兴趣的同学可进一步探索。
构建资源的优化:
- 使用 terser 压缩 Javascript 资源
- 使用 cssnano 压缩 CSS 资源
- 使用 sharp/CDN 压缩 Image 资源或转化为 Webp
- 使用 webpack 将小图片转化为 DataURI
- 使用 webpack 进行更精细的分包,避免一行代码的改动使大量文件的缓存失效
小结
其实,从这里开始,前端部署与传统前端部署已逐渐显现了天壤之别。
La implementación front-end tradicional está dominada por la operación y el mantenimiento. Cada vez que se conecta, notificará la operación y el mantenimiento de los pasos en línea del front-end del proyecto, que se completa con la operación y el mantenimiento, y el front-end tiene menos libertad para la implementación.
Por ejemplo, la apertura de la gzip/brotli
compresión , Cache-Control
el control de los encabezados de respuesta y la estrategia de almacenamiento en caché de diferentes rutas deben ser notificados de que la operación y el mantenimiento se han completado, y es difícil tener una gestión de versiones .
La extensión de la libertad de implementación de front-end se refleja en los siguientes dos aspectos:
- Contenga el front-end a través de Docker, ya no es necesario notificar los pasos de operación y mantenimiento en línea por correo electrónico
- Configure nginx en el front-end a través de los archivos de configuración de Docker y nginx, algunas configuraciones pequeñas y triviales que están fuertemente relacionadas con el proyecto no requieren intervención de operación y mantenimiento
En este punto, el capítulo sobre cómo implementar el front-end en Docker ha terminado y, en la práctica, los recursos estáticos a menudo se colocan en CDN.
Entonces, ¿cómo lidiar con eso?