前端部署的发展历程-程序员宅基地

技术标签: kubernetes  java  运维  devops  docker  

关注公众号 前端开发博客,领27本电子书

回复加群,自助秒进前端群

前端一说起刀耕火种,那肯定紧随着前端工程化这一话题。随着 react/vue/angulares6+webpackbabeltypescript 以及 node 的发展,前端已经在逐渐替代过去 scriptcdn 开发的方式了,掀起了工程化这一大浪潮。得益于工程化的发展与开源社区的良好生态,前端应用的可用性与效率得到了很大提高。

前端以前是刀耕火种,那前端应用部署在以前也是刀耕火种。那前端应用部署的发展得益于什么,随前端工程化带来的副产品?

这只是一部分,而更重要的原因是 devops 的崛起。

为了更清晰地理解前端部署的发展史,了解部署时运维和前端(或者更广泛地说,业务开发人员)的职责划分,当每次前端部署发生改变时,可以思考两个问题

  1. 缓存,前端应用中 http 的 response header 由谁来配?得益于工程化发展,可以对打包后得到带有 hash 值的文件可以做永久缓存

  2. 跨域,/api 的代理配置由谁来配?在开发环境前端可以开个小服务,启用 webpack-dev-server 配置跨域,那生产环境呢

这两个问题都是前端面试时的高频问题,但话语权是否掌握在前端手里

时间来到 React 刚刚发展起来的这一年,这时已经使用 React 开发应用,使用 webpack 来打包。但是前端部署,仍是刀耕火种

本篇文章要求你有一定的 Docker,DevOps 以及前端工程化的知识储备。如果没有的话,本系列文章以及 个人服务器运维指南[1] 中的 Docker 部分会对你有所帮助。

刀耕火种

一台跳板机

一台生产环境服务器

一份部署脚本

前端调着他的 webpack,开心地给运维发了部署邮件并附了一份部署脚本,想着第一次不用套后端的模板,第一次前端可以独立部署。想着自己基础盘进一步扩大,前端不禁开心地笑了

运维照着着前端发过来的部署邮件,一遍又一遍地拉着代码,改着配置,写着 try_files, 配着 proxy_pass

这时候,前端静态文件由 nginx 托管,nginx 配置文件大致长这个样子

server {
  listen 80;
  server_name shanyue.tech;

  location / {
    # 避免非root路径404
    try_files $uri $uri/ /index.html;
  }

  # 解决跨域
  location /api {
    proxy_pass http://api.shanyue.tech;
  }

  # 为带 hash 值的文件配置永久缓存
  location ~* \.(?:css|js)$ {
      try_files $uri =404;
      expires 1y;
      add_header Cache-Control "public";
  }

  location ~ ^.+\..+$ {
      try_files $uri =404;
  }
}

不过...经常有时候跑不起来

运维抱怨着前端的部署脚本没有标好 node 版本,前端嚷嚷着测试环境没问题

这个时候运维需要费很多心力放在部署上,甚至测试环境的部署上,前端也要费很多心力放在运维如何部署上。这个时候由于怕影响线上环境,上线往往选择在深夜,前端和运维身心俱疲

不过向来如此

鲁迅说,向来如此,那便对么。

这个时候,无论跨域的配置还是缓存的配置,都是运维来管理,运维不懂前端。但配置方式却是前端在提供,而前端并不熟悉 nginx

使用 docker 构建镜像

docker 的引进,很大程度地解决了部署脚本跑不了这个大 BUG。dockerfile 即部署脚本,部署脚本即 dockerfile。这也很大程度缓解了前端与运维的摩擦,毕竟前端越来越靠谱了,至少部署脚本没有问题了 (笑

这时候,前端不再提供静态资源,而是提供服务,一个 http 服务

前端写的 dockerfile 大致长这个样子

FROM node:alpine

# 代表生产环境
ENV PROJECT_ENV production
# 许多 package 会根据此环境变量,做出不同的行为
# 另外,在 webpack 中打包也会根据此环境变量做出优化,但是 create-react-app 在打包时会写死该环境变量
ENV NODE_ENV production
WORKDIR /code
ADD . /code
RUN npm install && npm run build && npm install -g http-server
EXPOSE 80

CMD http-server ./public -p 80

单单有 dockerfile 也跑不起来,另外前端也开始维护一个 docker-compose.yaml,交给运维执行命令 docker-compose up -d 启动前端应用。前端第一次写 dockerfiledocker-compose.yaml,在部署流程中扮演的角色越来越重要。想着自己基础盘进一步扩大,前端又不禁开心地笑了

version: "3"
services:
  shici:
    build: .
    expose:
      - 80

运维的 nginx 配置文件大致长这个样子

server {
  listen 80;
  server_name shanyue.tech;

  location / {
    proxy_pass http://static.shanyue.tech;
  }

  location /api {
    proxy_pass http://api.shanyue.tech;
  }
}

运维除了配置 nginx 之外,还要执行一个命令: docker-compose up -d

这时候再思考文章最前面两个问题

  1. 缓存,由于从静态文件转换为服务,缓存开始交由前端控制 (但是镜像中的 http-server 不太适合做这件事情)

  2. 跨域,跨域仍由运维在 nginx 中配置

前端可以做他应该做的事情中的一部分了,这是一件令人开心的事情

当然,前端对于 dockerfile 的改进也是一个慢慢演进的过程,那这个时候镜像有什么问题呢?

  1. 构建镜像体积过大

  2. 构建镜像时间过长

使用多阶段构建优化镜像

这中间其实经历了不少坎坷,其中过程如何,详见我的另一篇文章: 如何使用 docker 部署前端应用[2]

其中主要的优化也是在上述所提到的两个方面

  1. 构建镜像体积由 1G+ 变为 10M+

  2. 构建镜像时间由 5min+ 变为 1min (视项目复杂程度,大部分时间在构建时间与上传静态资源时间)

FROM node:alpine as builder

ENV PROJECT_ENV production
ENV NODE_ENV production

WORKDIR /code

ADD package.json /code
RUN npm install --production

ADD . /code

# npm run uploadCdn 是把静态资源上传至 oss 上的脚本文件,将来会使用 cdn 对 oss 加速
RUN npm run build && npm run uploadCdn

# 选择更小体积的基础镜像
FROM nginx:alpine
COPY --from=builder code/public/index.html code/public/favicon.ico /usr/share/nginx/html/
COPY --from=builder code/public/static /usr/share/nginx/html/static

那它怎么做的

  1. ADD package.json /code, 再 npm install --production 之后 Add 所有文件。充分利用镜像缓存,减少构建时间

  2. 多阶段构建,大大减小镜像体积

另外还可以有一些小优化,如

  • npm cache 的基础镜像或者 npm 私有仓库,减少 npm install 时间,减小构建时间

  • npm install --production 只装必要的包

前端看着自己优化的 dockerfile,想着前几天还被运维吵,说什么磁盘一半的空间都被前端的镜像给占了,想着自己节省了前端镜像几个数量级的体积,为公司好像省了不少服务器的开销,想着自己的基础盘进一步扩大,又不禁开心的笑了

这时候再思考文章最前面两个问题

  1. 缓存,缓存由前端控制,缓存在 oss 上设置,将会使用 cdn 对 oss 加速。此时缓存由前端写脚本控制

  2. 跨域,跨域仍由运维在 nginx 中配置

CI/CD 与 gitlab

此时前端成就感爆棚,运维呢?运维还在一遍一遍地上线,重复着一遍又一遍的三个动作用来部署

  1. 拉代码

  2. docker-compose up -d

  3. 重启 nginx

运维觉得再也不能这么下去了,于是他引进了 CI: 与现有代码仓库 gitlab 配套的 gitlab ci

  • CIContinuous Integration,持续集成

  • CDContinuous Delivery,持续交付

重要的不是 CI/CD 是什么,重要的是现在运维不用跟着业务上线走了,不需要一直盯着前端部署了。这些都是 CI/CD 的事情了,它被用来做自动化部署。上述提到的三件事交给了 CI/CD

.gitlab-ci.ymlgitlab 的 CI 配置文件,它大概长这个样子

deploy:
  stage: deploy
  only:
    - master
  script:
    - docker-compose up --build -d
  tags:
    - shell

CI/CD 不仅仅更解放了业务项目的部署,也在交付之前大大加强了业务代码的质量,它可以用来 linttestpackage 安全检查,甚至多特性多环境部署,我将会在我以后的文章写这部分事情

我的一个服务器渲染项目 shfshanyue/shici[3] 以前在我的服务器中就是以 docker/docker-compose/gitlab-ci 的方式部署,有兴趣的可以看看它的配置文件

  • shfshanyue/shici:Dockerfile[4]

  • shfshanyue/shici:docker-compose.yml[5]

  • shfshanyue/shici:gitlab-ci.yml[6]

如果你有个人服务器的话,也建议你做一个自己感兴趣的前端应用和配套的后端接口服务,并且配套 CI/CD 把它部署在自己的自己服务器上

而你如果希望结合 githubCI/CD,那可以试一试 github + github action

另外,也可以试试 drone.ci,如何部署可以参考我以前的文章: github 上持续集成方案 drone 的简介及部署[7]

使用 kubernetes 部署

随着业务越来越大,镜像越来越多,docker-compose 已经不太能应付,kubernetes 应时而出。这时服务器也从 1 台变成了多台,多台服务器就会有分布式问题

一门新技术的出现,在解决以前问题的同时也会引进复杂性。

k8s 部署的好处很明显: 健康检查,滚动升级,弹性扩容,快速回滚,资源限制,完善的监控等等

那现在遇到的新问题是什么?

构建镜像的服务器,提供容器服务的服务器,做持续集成的服务器是一台!

需要一个私有的镜像仓库,这是运维的事情,harbor 很快就被运维搭建好了,但是对于前端部署来说,复杂性又提高了

先来看看以前的流程:

  1. 前端配置 dockerfiledocker-compose

  2. 生产环境服务器的 CI runner 拉代码(可以看做以前的运维),docker-compose up -d 启动服务。然后再重启 nginx,做反向代理,对外提供服务

以前的流程有一个问题: 构建镜像的服务器,提供容器服务的服务器,做持续集成的服务器是一台!,所以需要一个私有的镜像仓库,一个能够访问 k8s 集群的持续集成服务器

流程改进之后结合 k8s 的流程如下

  1. 前端配置 dockerfile,构建镜像,推到镜像仓库

  2. 运维为前端应用配置 k8s 的资源配置文件,kubectl apply -f 时会重新拉取镜像,部署资源

运维问前端,需不需要再扩大下你的基础盘,写一写前端的 k8s 资源配置文件,并且列了几篇文章

  • 使用 k8s 部署你的第一个应用: Pod,Deployment 与 Service[8]

  • 使用 k8s 为你的应用配置域名: Ingress[9]

  • 使用 k8s 为你的域名加上 https[10]

前端看了看后端十几个 k8s 配置文件之后,摇摇头说算了算了

这个时候,gitlab-ci.yaml 差不多长这个样子,配置文件的权限由运维一人管理

deploy:
  stage: deploy
  only:
    - master
  script:
    - docker build -t harbor.shanyue.tech/fe/shanyue
    - docker push harbor.shanyue.tech/fe/shanyue
    - kubectl apply -f https://k8s-config.default.svc.cluster.local/shanyue.yaml
  tags:
    - shell

这时候再思考文章最前面两个问题

  1. 缓存,缓存由前端控制

  2. 跨域,跨域仍由运维控制,在后端 k8s 资源的配置文件中控制 Ingress

使用 helm 部署

这时前端与运维已不太往来,除了偶尔新起项目需要运维帮个忙以外

但好景不长,突然有一天,前端发现自己连个环境变量都没法传!于是经常找运维修改配置文件,运维也不胜其烦

于是有了 helm,如果用一句话解释它,那它就是一个带有模板功能的 k8s 资源配置文件。作为前端,你只需要填参数。更多详细的内容可以参考我以前的文章 使用 helm 部署 k8s 资源[11]

假如我们使用 bitnami/nginx[12] 作为 helm chart,前端可能写的配置文件长这个样子

image:
  registry: harbor.shanyue.tech
  repository: fe/shanyue
  tag: 8a9ac0

ingress:
  enabled: true
  hosts:
  - name: shanyue.tech
    path: /

  tls:
  - hosts:
      - shanyue.tech
    secretName: shanyue-tls

    # livenessProbe:
    #   httpGet:
    #     path: /
    #     port: http
    #   initialDelaySeconds: 30
    #   timeoutSeconds: 5
    #   failureThreshold: 6
    #
    # readinessProbe:
    #   httpGet:
    #     path: /
    #     port: http
    #   initialDelaySeconds: 5
    #   timeoutSeconds: 3
    #   periodSeconds: 5

这时候再思考文章最前面两个问题

  1. 缓存,缓存由前端控制

  2. 跨域,跨域由后端控制,配置在后端 Chart 的配置文件 values.yaml

到了这时前端和运维的职责所在呢?

前端需要做的事情有:

  1. 写前端构建的 dockerfile,这只是一次性的工作,而且有了参考

  2. 使用 helm 部署时指定参数

那运维要做的事情呢

  1. 提供一个供所有前端项目使用的 helm chart,甚至不用提供,如果运维比较懒那就就使用 bitnami/nginx[13] 吧。也是一次性工作

  2. 提供一个基于 helm 的工具,禁止业务过多的权限,甚至不用提供,如果运维比较懒那就直接使用 helm

这时前端可以关注于自己的业务,运维可以关注于自己的云原生,职责划分从未这般清楚

统一前端部署平台

后来运维觉得前端应用的本质是一堆静态文件,较为单一,容易统一化,来避免各个前端镜像质量的参差不齐。于是运维准备了一个统一的 node 基础镜像,做了一个前端统一部署平台,而这个平台可以做什么呢

  1. CI/CD: 当你 push 代码到仓库的特定分支会自动部署

  2. http headers: 你可以定制资源的 http header,从而可以做缓存优化

  3. http redirect/rewrite: 如果一个 nginx,这样可以配置 /api,解决跨域问题

  4. hostname: 你可以设置域名

  5. CDN: 把你的静态资源推到 CDN

  6. https: 为你准备证书

  7. Prerender: 结合 SPA,做预渲染

前端再也不需要构建镜像,上传 CDN 了,他只需要写一份配置文件就可以了,大致长这个样子

build:
  command: npm run build
  dist: /dist

hosts:
- name: shanyue.tech
  path: /

headers:
- location: /*
  values:
  - cache-control: max-age=7200
- location: assets/*
  values:
  - cache-control: max-age=31536000

redirects:
- from : /api
  to: https://api.shanyue.tech
  status: 200

此时,前端只需要写一份配置文件,就可以配置缓存,配置 proxy,做应该属于前端做的一切,而运维也再也不需要操心前端部署的事情了

前端看着自己刚刚写好的配置文件,怅然若失的样子...

不过一般只有大厂会有这么完善的前端部署平台,如果你对它有兴趣,你可以尝试下 netlify,可以参考我的文章: 使用 netlify 部署你的前端应用[14]

服务端渲染与后端部署

大部分前端应用本质上是静态资源,剩下的少部分就是服务端渲染了,服务端渲染的本质上是一个后端服务,它的部署可以视为后端部署

后端部署的情况更为复杂,比如

  1. 配置服务,后端需要访问敏感数据,但又不能把敏感数据放在代码仓库。你可以在 environment variablesconsul 或者 k8s configmap 中维护

  2. 上下链路服务,你需要依赖数据库,上游服务

  3. 访问控制,限制 IP,黑白名单

  4. RateLimit

  5. 等等

我将在以后的文章分享如何在 k8s 中部署一个后端

小结

随着 devops 的发展,前端部署越来越简单,可控性也越来越高,建议所有人都稍微学习一下 devops 的东西。

参考资料

[1]

个人服务器运维指南: https://github.com/shfshanyue/blog#%E4%B8%AA%E4%BA%BA%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%BF%90%E7%BB%B4%E6%8C%87%E5%8D%97

[2]

如何使用 docker 部署前端应用: https://juejin.im/post/5c83cbaa6fb9a04a0f65fdaa

[3]

shfshanyue/shici: https://github.com/shfshanyue/shici

[4]

shfshanyue/shici:Dockerfile: https://github.com/shfshanyue/shici/blob/master/Dockerfile

[5]

shfshanyue/shici:docker-compose.yml: https://github.com/shfshanyue/shici/blob/master/docker-compose.yml

[6]

shfshanyue/shici:gitlab-ci.yml: https://github.com/shfshanyue/shici/blob/master/.gitlab-ci.yml

[7]

github 上持续集成方案 drone 的简介及部署: https://juejin.im/post/5dc0b563f265da4cef190b8a

[8]

使用 k8s 部署你的第一个应用: Pod,Deployment 与 Service: https://juejin.im/post/5db8c2b46fb9a020256692dc

[9]

使用 k8s 为你的应用配置域名: Ingress: https://juejin.im/post/5db8da4b6fb9a0204520b310

[10]

使用 k8s 为你的域名加上 https: https://juejin.im/post/5db8d94be51d4529f73e2833

[11]

使用 helm 部署 k8s 资源: https://juejin.im/post/5dbf7909f265da4d4b5fe7b4

[12]

bitnami/nginx: https://hub.helm.sh/charts/bitnami/nginx

[13]

bitnami/nginx: https://hub.helm.sh/charts/bitnami/nginx

[14]

使用 netlify 部署你的前端应用: https://shanyue.tech/op/deploy-fe-with-netlify.html

往期推荐:

2021年前端部署的灵魂拷问

Vue项目打包部署总结

4W字从0到部署上线,用 TS 带你彻底掌握前端工程化

面试常见的7个高频正则表达式,没你想的那么难!

另外欢迎大家围观我的朋友圈,搞搞技术,吹吹牛逼。关注我,秒添加,回复加群,可以进入 500人前端群。

f116ffa194d91377b3bf9a48c8cd05ce.png

转发文章并关注公众号:前端开发博客,回复 1024,领取前端进阶资料

  1. 回复「电子书」领取27本精选电子书

  2. 回复「加群」加入前端大神交流群,一起学习进步

  3. 回复「面试」获取 面试 精选文章

430b4d22fcce12f2afca41272bdbca8d.png

分享和在看就是最大的支持️

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/lgno2/article/details/123244205

智能推荐

回忆当初写过的代码_bebibb啥意思-程序员宅基地

文章浏览阅读1.7k次。只是想当初,怀旧一下_bebibb啥意思

十个最为戳心测试/开程序员笑话,念茫茫人海,该如何寻觅?_软件测试笑话-程序员宅基地

文章浏览阅读759次。十个最为戳心测试/开程序员感言1、IT工程师=加班狂+程序员+测试工程师+实施工程师+网络工程师+电工+装卸工+搬运工+超人,有同感的吗。2、 用一句话总结了HTML,CSS,JS的关系。HTML是名词,JS是动词,CSS是形容词和副词。3、我是个程序猿,一天我坐在路边一边喝水一边苦苦检查bug。这时一个乞丐在我边上坐下了,开始要饭,我觉得可怜,就给了他1块钱,然后接着调试程序。他可能生意不好,就无聊的看看我在干什么,然后过了一会,他幽幽的说,这里少了个分号。。。分号。。。分号。。。4、女友对程序员说:“紫_软件测试笑话

良心推荐几款好玩的生存类手游:方舟生存进化、饥荒等等_饥荒和方舟生存进化哪个更好玩-程序员宅基地

文章浏览阅读2.4k次。生存类手游哪些比较好玩?有喜欢玩生存游戏的小伙伴吗,本人是生存手游的重度爱好者,本着资源共享的原则,给大家良心推荐几款好玩的生存类手游,可玩度都在4星以上。很多在国外也很受欢迎....1、方舟:生存进化方舟:生存进化多有名就不用我说了吧?目前手机版的只有国际版,据说蜗牛已经代理国服版的方舟手游了。整天来说手机版的难度系数比电脑版方舟低一些,虽然降低了难度但是对很多萌新来说还是有点难可能,建..._饥荒和方舟生存进化哪个更好玩

Autosar CAN开发11-1(CAN控制器相关参数,位时间、Tq、采样点等)_仲裁域波特率和数据域波特率-程序员宅基地

文章浏览阅读2.9k次,点赞21次,收藏48次。Autosar BSW层CAN控制器相关参数,物理层。位时间、Tq、采样点等_仲裁域波特率和数据域波特率

Springboot打包发布时SNAPSHOT.jar中没有主清单属性_file-1.0-snapshot.jar 中没有主清单属性-程序员宅基地

文章浏览阅读4.3k次。<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin>在pom文件中添加上面依赖就行FR:徐海涛(hunk xu)QQ..._file-1.0-snapshot.jar 中没有主清单属性

Unity GameFramework框架笔记-基础组件之ObjectPool_gameframework objectpoolbase-程序员宅基地

文章浏览阅读507次。自动释放对象逻辑:需要产生的对象数量大于容量,自动扩容自动释放:每个一段时间自动释放大于容量的个数过期判断:释放时,判断最后使用时间和过期时间(当前时间减一段时间,比如1分钟内未被使用),小于则为过期可释放的对象:未使用、未锁定、未自定义标记可释放每隔一段时间释放超过容量且可释放的对象,先释放过期的对象,再释放使用时间较早且优先级小的对象假设容量10,目..._gameframework objectpoolbase

随便推点

n条直线最多把平面分割成几部分? n个平面最多把空间分割成几部分?_k条直线可以将平面最多分割成(k^2 k 2)/2个部分 给出此部分证明-程序员宅基地

文章浏览阅读1.9w次,点赞10次,收藏17次。看了一道水题,发现这个两个问题值得记录一下。一,直线分割平面:首先考虑 n条直线最多把平面分成an部分于是a0=1 a1=2 a2=4对于已经有n条直线 将平面分成了最多的an块那么加一条直线 他最多与前n条直线有n个交点 于是被它穿过的区域都被一分为二 那么增加的区域数就是穿过的区域数 也就是这条直线自身被分成的段数 就是n+1_k条直线可以将平面最多分割成(k^2 k 2)/2个部分 给出此部分证明

eclipse python java_如何使用Eclipse使用Python导入Java类?-程序员宅基地

文章浏览阅读60次。我试图编写Jython,其中Python文件从Java导入类我在PyDev中使用Eclipse。在我的Python代码如下:from eclipsejavatest import eclipseJavaTestfrom eclipsejavatest import JavaClassclass eclipsePyPrint(eclipseJavaTest):def eclipsepyMain(se..._pydev中调用 java class

Go1.13:使用go mod 管理依赖, 提示cannot find module providing package或cannot find main module_the go.mod file for the module providing named pac-程序员宅基地

文章浏览阅读3.8w次,点赞6次,收藏8次。问题现象编译go程序的时候报错:GOROOT=D:\go\GoSDK #gosetupGOPATH=D:\hc\workspace\fly\go\src\github.com\learngo;D:\hc\workspace\fly\go #gosetupD:\go\GoSDK\bin\go.exe build -o C:\Users\FLY\AppData\Local\Temp\__..._the go.mod file for the module providing named packages contains one or more

Pytorch网络可视化——tensorboard、netron、torchviz_netron torchviz 哪个好-程序员宅基地

文章浏览阅读1.8k次。文章目录前言一、Tensorboard1.源码2.结果二、netron1.简介2.运行上述代码3.打开方式3.结果三、grahviz1.简介(比较强大)2.会出现报错前言pytorch网络模型的可视化主要有以下几种方法:1、使用Tensorboard2、使用netron3、使用torchviz下面分别介绍一下,怎样使用这些网络可视化,这里以可视化alexnet为例一、Tensorboard之前的文章已经讲过什么是Tensorboard以及怎么安装和可视化了,这里不再做详细的描述,直接上代._netron torchviz 哪个好

deepin微信无法启动_libldap-2.4-2:i386-程序员宅基地

文章浏览阅读3k次,点赞3次,收藏2次。删除 主目录文件夹下的.deepinwine文件夹里的Deepin-WeChat文件夹 重新点击微信图标即可启动_libldap-2.4-2:i386

2020年焊工(初级)报名考试及焊工(初级)模拟考试系统_电焊工模拟考试操作系统-程序员宅基地

文章浏览阅读238次。题库来源:安全生产模拟考试一点通公众号小程序2020年焊工(初级)报名考试及焊工(初级)模拟考试系统,包含焊工(初级)报名考试答案和解析及焊工(初级)模拟考试系统练习。由安全生产模拟考试一点通公众号结合国家焊工(初级)考试最新大纲及焊工(初级)考试真题汇总,有助于焊工(初级)怎么考考前练习。1、【判断题】()焊件内部由于受热不均匀、温度差异所引起的应力称为拘束应力。(×)2、【判断题】不同的焊接方法,它们的电弧静特性曲线是不同的,由于手工电弧焊使用电流受到限制,所以静特性为上升特性区..._电焊工模拟考试操作系统

推荐文章

热门文章

相关标签