0


超详细:30分钟入门docker前端容器化

1.引言

前端容器化是一种将前端应用程序打包成容器的技术,使其可以在不同的环境中快速、高效地部署和运行。

2.背景

前后端分离的趋势已形成现状,前端工程复杂度叠加增长,新、老项目部署依赖的环境和Node.js版本会存在差异,生产环境下构建混淆后的脚本、静态资源文件依赖环境部署服务进行访问,前端工程未能形成"单体工件"部署,容器的出现大大简化了部署流程。

前端容器化可以方便的管理前端环境变量注入、运行环境(不同项目依赖不同的node环境,node的版本兼容是个很大的问题)、节约服务器成本、更快捷方便的版本回滚、多架构部署、CI/CD自动化集成部署、DevOps等等,好处只有多到你想不到(此处手动偷笑)。

本文基于React项目结合Docker,分享在前端引入容器技术带来的变革。

3.容器化在github的运用

github推出了github-action来做容器化的ci/cd,我下面展示用github-action做一个npm自动化发包的示例:

  • 在项目根目录下新建.github/workflows/ci.yml文件
  • 去npm官网申请一个token(具体怎么去申请,请自己去搜索解决)
  • 将这段代码贴入ci.yml文件
  • push代码到master分支,就会自动走ci/cd进行部署啦!
  1. name: CI
  2. on:
  3. push:
  4. branches:
  5. - master
  6. jobs:
  7. build:
  8. # 指定操作系统
  9. runs-on: ubuntu-latest
  10. steps:
  11. # 将代码拉到虚拟机
  12. - name: Checkout repository
  13. uses: actions/checkout@v2
  14. # 指定node版本
  15. - name: Use Node.js
  16. uses: actions/setup-node@v3
  17. with:
  18. node-version: '16.x'
  19. registry-url: 'https://registry.npmjs.org'# 依赖缓存策略
  20. - name: Cache
  21. id: cache-dependencies
  22. uses: actions/cache@v3
  23. with:
  24. path: |
  25. **/node_modules
  26. key: ${{runner.OS}}-${{hashFiles('**/pnpm-lock.yaml')}}
  27. - name: Install pnpm
  28. run: npminstall-g pnpm@7.5.0
  29. # 依赖下载
  30. - name: Installing Dependencies
  31. if: steps.cache-dependencies.outputs.cache-hit !='true'
  32. run: pnpminstall# 打包
  33. - name: Running Build
  34. run: pnpm run build
  35. # 测试
  36. - name: Running Test
  37. run: pnpm run test-unit
  38. # 发布
  39. - name: Running Publish
  40. run: npm publish
  41. env:
  42. # NPM_TOKEN is access token
  43. NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

4.基于docker构建前端镜像

在学习前端项目ci/cd构建之前,让我们先学习下前端镜像怎么构建

4.1 安装docker

点此处坐飞机去安装docker
安装完成后执行以下命令查看docker版本,尽量带buildx的版本

  1. docker-v
  2. Docker version 24.0.2, build cb74dfc

4.2 编写Dockerfile

这里先需要普及一个前端工程知识,我们都知道一个基于npm的项目,需要一个package.json文件,然后执行npm run install下载包,npm run build打包,打包出来的文件其实是不能直接运行的,需要启动一个node服务运行,所以我们就写一个最基本的基于node和nginx的镜像,示例如下

在项目根目录下添加nginx配置文件,取名为nginx.conf,内容如下

  1. worker_processes 1;
  2. events {
  3. worker_connections 1024;}
  4. http {
  5. sendfile on;
  6. tcp_nodelay on;
  7. keepalive_timeout 30;
  8. include /etc/nginx/mime.types;
  9. default_type application/octet-stream;
  10. server {
  11. listen 80;
  12. server_name localhost;
  13. root /usr/share/nginx/front/dist;
  14. autoindex on;
  15. autoindex_exact_size off;
  16. autoindex_localtime on;
  17. location / {
  18. try_files $uri$uri/ =404;
  19. index index.html index.htm;
  20. gzip_static on;
  21. expires max;
  22. add_header Cache-Control public;if($request_filename ~* ^.*?\.(eot)|(ttf)|(woff)|(svg)|(otf)$){
  23. add_header Access-Control-Allow-Origin *;}}}}

在项目根目录下添加docker配置文件,取名为Dockerfile,内容如下

  1. FROM node:17-buster as builder
  2. WORKDIR /src
  3. COPY ./ /src
  4. RUN npminstall-gpnpm\&&pnpminstall\&&pnpm build
  5. FROM nginx:alpine-slim
  6. RUN mkdir /usr/share/nginx/front \&&mkdir /usr/share/nginx/front/dist \&&rm-rf /etc/nginx/nginx.conf
  7. COPY --from=builder /src/nginx.conf /etc/nginx/nginx.conf
  8. COPY --from=builder /src/dist /usr/share/nginx/front/dist
  9. EXPOSE 80

接下来使用docker build打包镜像(如果有桌面工具,打包成功后docker桌面工具的images栏目能看到), docker run执行镜像(如果有桌面工具,运行成功后docker桌面工具的containers栏目能看到), docker run运行成功后可以打开浏览器输入:http://localhost 进行查看

  1. docker buildx build -t webapp-demo:v1 .docker run -d-p80:80 webapp-demo:v1

4.3 如何基于Dockerfile做pnpm缓存

这里我引用一段话:
使用多阶段构建,构建的镜像中只包含了目标文件夹 dist,但仍然存在一些问题,当 package.json 文件变动时,RUN npm i && rm -rf ~/.npm 这一层会重新执行,变更多次后,生成了大量的中间层镜像。

为解决这个问题,进一步的我们可以设想一个类似 数据卷 的功能,在镜像构建时把 node_modules 文件夹挂载上去,在构建完成后,这个 node_modules 文件夹会自动卸载,实际的镜像中并不包含 node_modules 这个文件夹,这样我们就省去了每次获取依赖的时间,大大增加了镜像构建效率,同时也避免了生成了大量的中间层镜像。

此处课代表总结一下:就是尽量减少中间层镜像的可能,最小化docker映像大小和构建时间

由于我使用的是pnpm进行npm包管理,所以我去翻阅了pnpm官方文档关于此优化如下:

  1. FROM node:20-slim AS base
  2. ENV PNPM_HOME="/pnpm"
  3. ENV PATH="$PNPM_HOME:$PATH"
  4. RUN corepack enable
  5. COPY . /app
  6. WORKDIR /app
  7. FROM base AS prod-deps
  8. RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpminstall--prod --frozen-lockfile
  9. FROM base AS build
  10. RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpminstall --frozen-lockfile
  11. RUN pnpm run build
  12. FROM base
  13. COPY --from=prod-deps /app/node_modules /app/node_modules
  14. COPY --from=build /app/dist /app/dist
  15. EXPOSE 8000
  16. CMD ["pnpm", "start"]

于是本着依葫芦画瓢的精神,还有使用生产化的nginx配置,我找了同事写的nginx包装镜像,同样你可以执行docker build、docker run进行验证,然后我改造后的代码如下:

  1. FROM node:17-buster AS builder
  2. ENV PNPM_HOME="/pnpm"
  3. ENV PATH="$PNPM_HOME:$PATH"
  4. RUN corepack enable
  5. WORKDIR /src
  6. COPY ./ /src
  7. RUN --mount=type=cache,target=/src/node_modules,id=myapp_pnpm_module,sharing=locked \--mount=type=cache,target=/pnpm/store,id=pnpm_cache \pnpminstall
  8. RUN --mount=type=cache,target=/src/node_modules,id=myapp_pnpm_module,sharing=locked \pnpm run build
  9. FROM ghcr.io/zboyco/webrunner:0.0.7
  10. COPY --from=builder /src/dist /app

4.4 如何利用buildx制作多架构镜像

docker buildx的工具,说白了就是给你提供一个能力,当你的宿主机是x86 64的架构时,你想构建镜像为ARM64的架构,就需要这个工具,给人的感觉有点类似交叉编译,诸如:go build的交叉编译,在win10下编译可执行程序,可用于特定linux平台

buildx本质上调用了 buildkit 的 api,构建是在 buildkit 的环境中进行的。 是否支持多架构,取决于 buildkit 的环境,如果需要 buildkit支持多架构,需要在宿主机执行(当然这个不是必须的,按构建的需求进行控制,Docker桌面版无需进行此项设置):

  1. docker run --privileged--rm tonistiigi/binfmt --install all

我们这里改造上面Dockerfile代码,使它支持多架构,由于platform是实验性质,所以需要先执行docker pull docker/dockerfile 拉取镜像

  1. # syntax = docker/dockerfile:experimental# --platform, 会让 builder 只会有一份,且 arch 与宿主机一致
  2. FROM --platform=${BUILDPLATFORM:-linux/amd64} node:17-buster AS builder
  3. ENV PNPM_HOME="/pnpm"
  4. ENV PATH="$PNPM_HOME:$PATH"
  5. RUN corepack enable
  6. WORKDIR /src
  7. COPY ./ /src
  8. RUN --mount=type=cache,target=/src/node_modules,id=myapp_pnpm_module,sharing=locked \--mount=type=cache,target=/pnpm/store,id=pnpm_cache \pnpminstall
  9. RUN --mount=type=cache,target=/src/node_modules,id=myapp_pnpm_module,sharing=locked \pnpm run build
  10. FROM ghcr.io/zboyco/webrunner:0.0.7
  11. COPY --from=builder /src/dist /app

在执行打包镜像命令之前,我们先查看下我们机器默认的 builder 实例

  1. docker buildx ls
  2. NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
  3. default docker
  4. default default running v0.11.7-0.20230525183624-798ad6b0ce9f linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/mips64le, linux/mips64, linux/arm/v7, linux/arm/v6
  5. desktop-linux * docker
  6. desktop-linux desktop-linux running v0.11.7-0.20230525183624-798ad6b0ce9f linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/mips64le, linux/mips64, linux/arm/v7, linux/arm/v6

使用buildx执行上面的脚本打包镜像会报错如下

  1. docker buildx build --platform linux/arm,linux/arm64,linux/amd64 -t webapp-official-website:v1 .
  2. ERROR: Multiple platforms feature is currently not supported fordocker driver. Please switch to a different driver (eg. "docker buildx create --use")

由于 Docker 默认的 builder 实例不支持同时指定多个 --platform,我们必须首先创建一个新的 builder 实例。同时由于国内拉取镜像较缓慢,我们可以使用配置了镜像加速地址的 dockerpracticesig/buildkit:master 镜像替换官方镜像。

如果你有私有的镜像加速器,可以基于 https://github.com/docker-practice/buildx 构建自己的 buildkit 镜像并使用它。

  1. # 适用于国内环境
  2. $ docker buildx create --use--name=mybuilder-cn --driver docker-container --driver-opt image=dockerpracticesig/buildkit:master
  3. # 适用于腾讯云环境(腾讯云主机、coding.net 持续集成)
  4. $ docker buildx create --use--name=mybuilder-cn --driver docker-container --driver-opt image=dockerpracticesig/buildkit:master-tencent
  5. # $ docker buildx create --name mybuilder --driver docker-container
  6. $ docker buildx use mybuilder

我们选择适用于国内环境的方案的命令进行创建, 可以看到多了name为 mybuilder-cn 的实例

  1. docker buildx create --use--name=mybuilder-cn --driver docker-container --driver-opt image=dockerpracticesig/buildkit:master
  2. docker buildx ls
  3. NAME/NODE DRIVER/ENDPOINT STATUS BUILDKIT PLATFORMS
  4. mybuilder-cn * docker-container
  5. mybuilder-cn0 desktop-linux inactive
  6. default docker
  7. default default running v0.11.7-0.20230525183624-798ad6b0ce9f linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/mips64le, linux/mips64, linux/arm/v7, linux/arm/v6
  8. desktop-linux docker
  9. desktop-linux desktop-linux running v0.11.7-0.20230525183624-798ad6b0ce9f linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/mips64le, linux/mips64, linux/arm/v7, linux/arm/v6
  10. $ docker buildx build --platform linux/arm,linux/arm64,linux/amd64 -t myusername/hello .--push# 查看镜像信息
  11. $ docker buildx imagetools inspect myusername/hello

在不同架构运行该镜像,可以得到该架构的信息。

  1. $ docker run -it--rm myusername/hello

5.如何利用容器化做前端环境变量注入

  • 前端虽然对环境变量的需求场景不多,但是基本的api baseURL, appName, env这些是需要的
  • 如果是微前端的场景,那么需要的其他网站url都是环境变量,还挺多的
  • 依稀记得刚入行时,前端区分测试环境,线上环境就是直接通过域名进行判断,例如: inlcudes(url, “.com”)?, 然后得到isProd去获取项目中配置的不同环境的变量,这样显得很low
  • 后面出了vue, react这种框架,可以在npm run dev时候指明–prod,然后通过process拿到isProd,去获取对应配置
  • 现在可以直接通过容器化注入环境变量,然后使用nginx将容器中的环境变量注入前端项目html的meta标签content中, 然后从meta标签获取变量
  • 如果是monorepo项目,npm run build的时候,Dockerfile里面也需要通过容器中的环境变量获取打包哪个项目
  • 测试环境通过ts文件配置环境变量,然后项目启动时组合这些环境变量信息,生成config default.yml, ci/cd时k8s自动将default.yml中配置的环境变量写入容器中
  • 线上环境直接提供UI页面配置环境变量,然后调用api,后端api通过k8s将变量写入容器中
  • 如何通过k8s读取配置的环境变量并写入容器中且听下回分解

下面贴一段生产场景下的Dockerfile示例代码(公司应该不会砍我吧!),这里省略k8s如何往容器中注入环境变量,只考虑如何从容器中读取环境变量(假设环境变量已经注入容器中)

  1. FROM --platform=${BUILDPLATFORM} hub-dev.rockontrol.com/rk-infrav2/docker.io/library/node:17-bullseye as builder
  2. WORKDIR /src
  3. COPY ./ ./
  4. ARG APP
  5. ARG ENV
  6. ARG PROJECT_GROUP
  7. ARG PROJECT_NAME
  8. ARG PROJECT_VERSION
  9. ARG YARN_NPM_REGISTRY_SERVER
  10. RUN npminstall-g--registry=${YARN_NPM_REGISTRY_SERVER}pnpm
  11. RUN pnpm--registry=${YARN_NPM_REGISTRY_SERVER}install
  12. RUN PROJECT_GROUP=${PROJECT_GROUP}PROJECT_VERSION=${PROJECT_VERSION}\
  13. npx devkit build --prod${APP}${ENV}
  14. FROM hub-dev.rockontrol.com/rk-infrav2/ghcr.io/zboyco/webrunner:0.0.7
  15. ARG PROJECT_NAME
  16. COPY --from=builder /src/public/${PROJECT_NAME} /app

下面贴一段代码展示nginx如何组合环境变量,并利用nginx配置写入html的mate标签中

  1. #!/bin/sh# This script is used to start the application# 初始化一个字符串,用于存储拼接后的值app_config="${APP_CONFIG}"ext_config=""# 遍历所有环境变量forvarin$(env|cut-d=-f1);do# 检查变量是否以 "APP_CONFIG__" 开头if["$(echo"$var"|grep'^APP_CONFIG__')"];then# 去除变量名前缀 "APP_CONFIG__"trimmed_var=$(echo"$var"|sed's/^APP_CONFIG__//')# 使用 eval 来获取变量值并拼接到字符串中value=$(evalecho"\$$var")app_config="${app_config},${trimmed_var}=${value}"fidone# 去掉起始的逗号exportapp_config=$(echo"$app_config"|sed's/^,//')# 解析app_config变量# 以,分割 app_configIFS=","set -- $app_config# 遍历数组forconfigin"$@";do# 以等号分剥数组IFS="="set -- $config# 将单个环境变量单独注入ext_config="${ext_config} sub_filter '__$1__' '$2';\n"echo"$1: $2"done# Install envsubstecho"Installing envsubst"# 将扩展变量替换到 conf.template 中sed"s@__EXTENT_CONFIG__@${ext_config}@g" /etc/nginx/conf.d/conf-base.template > /etc/nginx/conf.d/conf.template
  2. envsubst '${PROJECT_VERSION} ${ENV} ${app_config}'< /etc/nginx/conf.d/conf.template > /etc/nginx/conf.d/default.conf
  3. # Start nginxecho"Starting nginx"
  4. nginx -g'daemon off;'

nginx配置代码,mate标签的占位符将会被替换成真实的环境变量,见代码中的sub_filter

  1. server {
  2. listen 80;
  3. listen [::]:80;
  4. server_name localhost;
  5. root /app;#开启gzipgzip on;#低于1kb的资源不压缩
  6. gzip_min_length 1k;#压缩级别1-9,越大压缩率越高,同时消耗cpu资源也越多,建议设置在5左右。
  7. gzip_comp_level 6;#需要压缩哪些响应类型的资源,多个空格隔开。不建议压缩图片.
  8. gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css;
  9. location ~ ^/(static|__built__)/ {
  10. root /app;
  11. expires max;
  12. proxy_cache static_memory_cache;# 使用内存缓存
  13. proxy_cache_valid 200 1d;
  14. proxy_cache_lock on;}
  15. location / {
  16. expires -1;
  17. try_files $uri /index.html;
  18. add_header X-Frame-Options sameorigin;
  19. add_header X-Content-Type-Options nosniff;
  20. add_header X-XSS-Protection "1;mode=block" always;
  21. sub_filter '__PROJECT_VERSION__''$PROJECT_VERSION';
  22. sub_filter '__ENV__''$ENV';
  23. sub_filter '__APP_CONFIG__''$app_config';# 需要将以下字符串替换为注入的扩展环境变量
  24. __EXTENT_CONFIG__
  25. sub_filter_once on;}}

下面贴一段前端如何从html meta标签中读取环境变量

  1. import appConfig from "../../config";
  2. interface IConfig {
  3. appName: string;
  4. baseURL: string;
  5. version?: string;
  6. env?: string;}exportfunction getConfig(): IConfig {
  7. const defaultAppConfig ={
  8. appName: "",
  9. version: "",
  10. env: "",
  11. baseURL: "",
  12. };
  13. console.log("metaEnv", import.meta.env);if(import.meta.env.DEV){return appConfig;}else{
  14. const appConfigStr = getMeta("app_config");if(!appConfigStr)return defaultAppConfig;return parseEnvVar(appConfigStr);}}function getMeta(metaName: string){
  15. const metas = document.getElementsByTagName("meta");for(let i =0; i < metas.length; i++){if(metas[i].getAttribute("name")=== metaName){return metas[i].getAttribute("content");}}return"";}function parseEnvVar(envVarURL: string){
  16. const arrs = envVarURL.split(",");return arrs.reduce((pre, item)=>{
  17. const keyValues = item.split("=");return{...pre,
  18. [keyValues[0]]: keyValues[1],
  19. };}, {} as IConfig);}
  20. const BASE_URL = getConfig().baseURL;
  21. const instance = axios.create({
  22. baseURL: BASE_URL,
  23. headers: {"Content-Type":"application/json",
  24. },
  25. timeout: 60000, // 超时时间60秒
  26. });

最后的最后,都贴了Dockerfile了,那就一不做二不休,贴个真实场景下的ci文件源码吧!!!

  1. stages:
  2. - ship
  3. - deploy
  4. ship:
  5. stage: ship
  6. image: hub-dev.rockontrol.com/rk-infrav2/gitlab-runner-buildx:0.0.0-b0450fe
  7. # variables:# MULTI_ARCH_BUILDER: 1
  8. before_script:
  9. - echo"${DOCKER_PASSWORD}"|docker login "${DOCKER_REGISTRY}"-u="${DOCKER_USERNAME}" --password-stdin
  10. - BUILDKIT_NAME=node-buildkit hx buildx ci-setup
  11. script:
  12. - exportPLATFORM=linux/amd64,linux/arm64
  13. - |if[[-f ./.platform ]];thensource ./.platform
  14. elseecho"WARNING, there is no .platform in project, USE default PLATFORM=${PLATFORM} "fi
  15. - hx buildx --with-builder --push--platform=${PLATFORM}
  16. tags:
  17. - webapp
  18. deploy:
  19. stage: deploy
  20. script:
  21. - hx config
  22. - hx deploy
  23. dependencies:
  24. - ship
  25. tags:
  26. - webapp

6.前端应用的Kubernetes部署

Kubernetes是一个开源的容器编排平台,可以实现自动化部署、扩展和管理容器化应用程序。以下是将前端应用部署到Kubernetes集群的步骤:

6.1 创建Deployment

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: frontend-app
  5. spec:
  6. replicas: 3
  7. selector:
  8. matchLabels:
  9. app: frontend-app
  10. template:
  11. metadata:
  12. labels:
  13. app: frontend-app
  14. spec:
  15. containers:
  16. - name: frontend-app
  17. image: my-frontend-app:latest
  18. ports:
  19. - containerPort: 3000

6.2 创建Service

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: frontend-service
  5. spec:
  6. selector:
  7. app: frontend-app
  8. ports:
  9. - protocol: TCP
  10. port: 80
  11. targetPort: 3000
  12. type: LoadBalancer

6.3 部署到Kubernetes集群

使用kubectl命令部署应用到Kubernetes集群:

  1. kubectl apply -f deployment.yaml
  2. kubectl apply -f service.yaml

现在,您的前端应用已经在Kubernetes集群中运行,并可以通过LoadBalancer类型的Service外部访问。

7.关于前端React项目架构

7.1 核心技术

  • 打包编译 - vite
  • 包管理 - pnpm
  • 编程语言 - typescript
  • 前端框架 - react
  • 路由 - react-router
  • UI组件库 - antd
  • cssinjs(不考虑性能开销) - emotion
  • 全局数据共享 - zustand
  • 自动生成api - openapi
  • 网络请求 - axios
  • 数据请求利器 - react-query
  • 通用hook(可不用) - ahooks
  • 错误边界 - react-error-boundary
  • 前端日志(暂未集成) - sentry-javascript
  • hack - babel
  • 代码检查 - eslint
  • ts代码检查插件 - typescript-eslint
  • 代码美化 - prettier
  • git钩子 - husky
  • commit格式化 -commitlint

7.2 基于openapi自动获取api请求函数

  1. // src/core/openapi/index.ts
  2. // 示例代码
  3. generateService({
  4. // openapi地址
  5. schemaPath: `${appConfig.baseURL}/${urlPath}`,
  6. // 文件生成目录
  7. serversPath: "./src",
  8. // 自定义网络请求函数路径
  9. requestImportStatement: `/// <reference types="./typings.d.ts" />\nimport request from "@request"`,
  10. // 代码组织命名空间, 例如:Api
  11. namespace: "Api",
  12. });

7.3 调用接口(react-query), 支持自动loading和接口请求联动

  1. // HelloGet是一个基于axios的promise请求
  2. export async function HelloGet(
  3. // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
  4. params: Api.HelloGetParams,
  5. options?: {[key: string]: any },
  6. ){return request<Api.HelloResp>('/gin-demo-server/api/v1/hello', {
  7. method: 'GET',
  8. params: {...params,
  9. },
  10. ...(options ||{}),
  11. });}
  12. // 自动调用接口获取数据
  13. const { data, isLoading }= useQuery({
  14. queryKey: ["hello", name],
  15. queryFn: ()=>{return HelloGet({ name: name });},
  16. });export async function HelloPost(body: Api.HelloPostParam, options?: {[key: string]: any }){return request<Api.HelloResp>('/gin-demo-server/api/v1/hello', {
  17. method: 'POST',
  18. headers: {'Content-Type':'application/json',
  19. },
  20. data: body,
  21. ...(options ||{}),
  22. });}
  23. // 提交编辑数据
  24. const { mutate, isLoading }= useMutation({
  25. mutationFn: HelloPost,
  26. onSuccess(data){
  27. setName(data?.data ||"");},
  28. onError(){
  29. // 清除queryKey为hello的接口数据缓存,自动重新获取接口数据
  30. queryClient.invalidateQueries({ queryKey: ['hello']});}})
  31. mutate({ name: "lisi"});

8.前端React代码CLI

9.结语

  • 介绍了gitlab-action的在前端npm领域的基本配置
  • 介绍了前端Dockerfile文件的编写,以及pnpm在docker的优化方案,如何利用buildx生成前端多架构镜像
  • 介绍了生产场景下前端环境变量的使用
  • 介绍了前端React项目技术架构方案
标签: 前端 react docker

本文转载自: https://blog.csdn.net/luo1055120207/article/details/132742642
版权归原作者 lane_developer 所有, 如有侵权,请联系我们删除。

“超详细:30分钟入门docker前端容器化”的评论:

还没有评论