前端本地化部署

前言

现在成熟的前端团队里面都有自己的内部构建平台,我司云长便是我们 CI/CD 的提效利器。我先来简单介绍下我司的云长,此云长非彼云长,云长主要做的是:获取部署的项目,分支,环境基本信息后开始拉取代码,安装依赖,打包,并且将项目的一些资源静态文件上传 CDN,再将生成的代码再打包成镜像文件,然后将这份镜像上传到镜像仓库后,最后调用 K8S 的镜像部署服务,进行镜像按环境的部署,这就是我们云长做的事情。如果想从零开始搭建一个自己团队的部署平台可以看下我们往期文章 如何搭建适合自己团队的构建部署平台@Jeson/机械键盘收藏家,本期我们只是针对云长中静态资源本地化的功能做细致阐述。

场景分析

为了网络安全,客户会要求我们的应用是要完全部署在内网的,那我们需要做什么呢?第一我们需要考虑前端代码中是不是有些直接访问外网资源?第二是不是后端返回了静态资源地址在某种情况下就访问了?第三 CDN 资源具体有那些类型呢?前端直接访问的 CDN 的资源太普遍了,如下既有 at.alicdn.com,又有我们自己内部的静态资源luban.zcycdn.com, sitecdn.zcycdn.com 等。如下这些就在我们代码中使用的静态资源地址。

ini

<link rel='stylesheet' href='//at.alicdn.com/t/fontnm.css' /> <img src="https://sitecdn.zcycdn.com/f2e/8.png"alt="收货人"/> <img src="https://luban.zcycdn.com/f2e/8.png"alt="收货人"/> //css中字体文件 src:url(https://sitecdn.zcycdn.com/t/font_148178j4i.eot); src:url(https://sitecdn.zcycdn.com/t/font1_4i.woff);

为了保证我们内网中可以访问我们讨论出以下两个方案

方案一

DNS 解析做转发

我们通过 DNS 服务这一层去处理,具体 DNS 如何进行的二域名,三级域名进行解析,如何DNS 缓存,以及什么是 13 台根服务器,我们这次不做深入探讨,我们只需要 DNS 的可以进行域名解析,解析到指定的 IP 服务上即可。

那我们是不是可以想一下,是不是把代码中访问的静态资源的域名拦截一下,DNS 解析成本地服务的地址是不是就可以了呢?为了更清楚的理解,我做一个例子如下:

我们代码中需要访问某个图片,CDN 地址:cdn.zcycdn.com/b/a.js

上传提前把 a.js 这个文件提前放到本地服务器上访问地址:demo.com/b/a.js

当代码运行的时候,代码中访问了 cdn.zcycdn.com 的时候,DNS 直接地址解析成 demo.com 的 IP 地址,达到访问静态资源的目的

看起来这个蛮简单的,不需要各个业务负责人排查修改自己代码中的静态资源,胜利在望了,兴致冲冲的跑去找运维童鞋提议是不是可以这样做,然而运维把我说的服服帖帖。运维童鞋说:静态资源放在对象存储或者服务器上,通过IP或者域名的方式都可以请求的到,不过 IP 只支持 HTTP 的方式,域名+SSL 证书的方式支持 HTTPS,可以做一些加密,让你的资源或者请求内容进行加密,不容易被破解,域名证书之前有 3 到 5 年的,3 年前已经改掉了,目前申请的证书都是一年的,那就预示着不仅仅要用户配置我们提供的 DNS 规则,还要配合我们一年一更新证,想要客户这样配合那是不容易。如下图所示:

image (10).png

DNS 只是帮我们把域名解析成了 IP, HTTPS 还需要证书验证服务器身份,仅仅 DNS 拦截解析还不够。模拟实现了一波大致思路:自己启动一个静态资源服务,以及 DNS 本地解析服务,当访问 juejin.cn 域名的时候 IP 解析成本地的 IP 并且成功访问到静态资源,具体如下。

image (21).png

自己写一个DNS服务

step1: 本地起一个服务

image (12).png

暂时存放静态资源,模拟服务器上的资源

image (13).png

启动服务访问静态资源

我们的目的:如果访问 juejin.cn:3000/zcy.png 的时候访问到我们本地服务的静态资源:http://10.201.45.121:3000/zcy.png

step2: 启动一个本地 DNS 服务,拦截所有请求转发到自己启动的 IP 点击查看源码

step3:配置本地 DNS 解析

image (16).png

step4: 测试访问HTTP 和 HTTPS

访问:juejin.cn:3000/zcy.png

如果是juejin.cn:3000/zcy.png

image (18).png

如果访问的是 HTTP 请求那就可以访问,HTTPS 就不能访问,侧面证明了 HTTPS 的证书问题。HTTPS 对称加密的秘钥我们采用非对称加密传输,数据传输还是使用对称加密,这保证了数据加密传输,为了保证防止冒充,CA(Certificate Authority), 颁发的证书就称为数字证书 (Digital Certificate),在非对称加密阶段,服务器会把证书会带着非对称加密的公钥,一起返回,向浏览器证明服务器的身份 HTTPS 相比 HTTP 多了一层 SSL/TLS(安全层)如下图。

image (19).png

方案二

项目在构建的时候扫描出项目中的静态资源地址,从我们公网的 CDN 服务放到客户自己的服务器上,修改源文件中的静态资源地址为客户本地服务的访问地址。

优缺点一目了然,方案一无需修改代码,但是需要充分得到客户的大力信任与支持需要配置 DNS 转发,方案二无需劳烦客户,即使后面有新增域名也不需要和客户沟通,完全自己解决,但是对代码有侵入性,会替换静态资源的地址

我们通过以下4个阶段拆解

统一封装runCommand 执行命令

javascript

function runCommand(cmd, args, options, before, end) {  return new Promise((resolve, reject) => {    log(before, blue)    const spawn = childProcess.spawn(      cmd,      args,      Object.assign(       {          cwd: global.WORKSPACE,          stdio: 'inherit',          shell: true,       },        options     )   )    spawn.on('error', (error) => {      log(error, chalk.red)      reject(error)   });    spawn.on('close', (code) => {      if (code !== 0) {        return reject(`sh: ${cmd} ${args.join(' ')}`)     }      end && log(end, green)      resolve()   }); }) }

1、pre 前置环境校验

切换公司nrm

less

runCommand('nrm', ['use', 'zcy-server'], {}, 'switch nrm registry to zcy', 'switch nrm registry to zcy success')

下载依赖

less

runCommand('npm', ['i', '--unsafe-perm'], {}, 'npm install', 'npm install success')

2、compile 编译

不同环境需要上传不同的地址因此需要动态修改webpack 的publicPath

ini

const cdnConfigStr = `assetsPublicPath: 'http://dev.com',` replaceFileContent(configPath, /assetsPublicPath:.+,/g, cdnConfigStr) exports.replaceFileContent = function(filePath, source, target) {  const fileContent = fs.readFileSync(filePath, 'utf-8')  let targetFileContent = fileContent  if (Array.isArray(source)) {    source.forEach(([s, target]) => {      if (target) {        targetFileContent = targetFileContent.replace(s, target)     }   }) } else {    targetFileContent = fileContent.replace(source, target) }  fs.writeFileSync(filePath, targetFileContent, 'utf-8') }

编译项目

less

runCommand('npm', ['run', 'build'], {}, `webpack build`, `webpack build success`)

3、静态资源替换

替换url源码地址
ini

const replaceWebpackDistContent = async function(options = {},collectionAssets,folder) { const fileContent = fs.readFileSync(filePath, 'utf-8'); let targetFileContent=fileContent; [ [/(https:)?//g.alicdn.com/[-a-zA-Z0-9@:%_+.~#?&//=]+.[-a-zA-Z0-9@:%_+.~#?&//=]+/g, cdn], [/(https?:)?//sitecdn.zcycdn.com/[-a-zA-Z0-9@:%_+.~#?&//=]+.[-a-zA-Z0-9@:%_+.~#?&//=]+/g, cdn], [/(https:)?//cdn.zcycdn.com/[-a-zA-Z0-9@:%_+.~#?&//=]+.[-a-zA-Z0-9@:%_+.~#?&//=]+/g, cdn], ].forEach(([reg,uri])=>{ targetFileContent=targetFileContent.replace(reg,function(match){ let basename = ''; let uriMath = match; basename = path.basename(uriMath); if(uriMath.slice(0,4)!='http'){ uriMath='https:'+uriMath; } const parseUrl = url.parse(uriMath); collectionAssets({src:uriMath,fileName:path.basename(parseUrl.pathname)}); console.log('🚀替换前',match); const myURL= new URL(projectName, uri); const replacedUrl = uri+'/'+projectName+parseUrl.path+(parseUrl.hash||''); console.log('🚀替换后', replacedUrl); return replacedUrl; }) }) fs.writeFileSync(filePath, targetFileContent, 'utf-8') }

获取写死在前端代码中的静态资源

javascript

const downloadAssetsFiles= async function(img,forder){ const staticAssets='staticAssets'; let assetsUrl=getPwdPath(`${forder||''}${path.sep}${staticAssets}`); if(!fs.existsSync(assetsUrl)){ fs.mkdirSync(assetsUrl); } return Promise.all(img.objUnique('src').map(({src,fileName})=>{ if(fileName){ return new Promise(function(resolve,reject){ const originFileDir = path.join(assetsUrl,path.dirname(url.parse(src).pathname)); fs.mkdirSync(originFileDir,{recursive:true}); const uri = path.join(originFileDir,fileName); download(uri,src,resolve,reject); }).catch(err=>{ console.log(err) throw new Error(err); }) } })) } function download(loadedUrl,src){ const writeStream = fs.createWriteStream(loadedUrl); const readStream = request(src); readStream.pipe(writeStream); readStream.on('end', function() { console.log(fileName,'文件下载成功'); }); writeStream.on("finish", function() { console.log(fileName,"文件写入成功"); writeStream.end(); }); } downloadAssetsFiles(assetsArr,'dist'); // 发现替换资源里还有cdn,因此替换下载后的cdn里面的cdn const assetsArr=[]; await replaceWebpackDistContent(options,collectionAssets,'staticAssets'); await downloadAssetsFiles(assetsArr,'dist');

4、OSS推送静态资源到客户资源服务

ini

const ossEndpoint = process.env.OSS_ENDPOINT; const commonOptions = { accessKeyId: process.env.OSS_ACCESSKEYID , accessKeySecret: process.env.OSS_ACCESSKEYSECRET, bucket: process.env.OSS_BUCKET, timeout: '120s', } const extraOptions = ossEndpoint ? { endpoint: ossEndpoint, // 从全局数据获取,没有会依赖 region cname: true, } : { region: process.env.OSS_REGION, } const ossOptions = Object.assign({}, commonOptions, extraOptions); const client = new OSS(ossOptions); //onlinePath 访问的文件地址 //curPath 上传的文件地址 result = await client.put(onlinePath, curPath);

如有抄袭 请告知,仅供参考!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/701710.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java ExecutorService 线程池(IO密集型、CPU密集型建议)

注&#xff1a;这是我很久之前在博客里面看到的&#xff0c;忘记是哪一篇了&#xff0c;分享一下 测试响应耗时 private String test1() {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}return "test1";}private String test2() …

java基于redis实现分布式锁

文章目录 前言一、redis二、Redisson1.引入库2. 分布式锁3. 锁自动续期 总结 前言 上篇文章介绍了Java中锁的应用,在SpringBoot单体应用中完全够用,但是SpringCloud微服务集群中就力所不及了。 我的使用场景是某些微服务应用中使用spring注解的形式来完成定时任务的功能,服务集…

pip换源

windows环境下&#xff1a; 比如windows账号是 admin 那么建立 admin主目录下的 pip子目录&#xff0c;在此pip子目录下建立pip的配置文件&#xff1a;pip.ini c:\users\admin\pip\pip.ini # coding: GBK [global] index-url https://pypi.tuna.tsinghua.edu.cn/simple [ins…

C++-带你走进多态(1)

1. 多态的概念 1.1 概念 多态的概念&#xff1a;通俗来说&#xff0c;就是多种形态&#xff0c;具体点就是去完成某个行为&#xff0c;当不同的对象去完成时会产生出不同的状态。 举个栗子&#xff1a;比如买票这个行为&#xff0c;当普通人买票时&#xff0c;是全价买票&am…

四、ChatGPT的回答从哪里来?—我耀学IT

ChatGPT回答问题时通常比问题本身更长&#xff0c;这是因为它需要通过补充额外的信息来提供完整的答案。它的回答来源于对现有信息的抽取和整合&#xff0c;那么具体是怎么进行抽取和整合的呢&#xff0c;下面我们带着这个疑问来详细讨论一下它的工作原理。首先&#xff0c;英语…

Jenkins解决Host key verification failed (2)

Jenkins解决Host key verification failed 分析原因情况 一、用OpenSSH的人都知ssh会把你每个你访问过计算机的公钥(public key)都记录在~/.ssh/known_hosts。当下次访问相同计算机时&#xff0c;OpenSSH会核对公钥。如果公钥不同&#xff0c;OpenSSH会发出警告&#xff0c;避免…

Spring中关于事务的一些方方面面

事务隔离级别&#xff1a; 先了解一些事务隔离级别有哪些&#xff1a; 未提交读(Read Uncommitted)&#xff1a; 允许脏读&#xff0c;也就是可能读取到其他会话中未提交事务修改的数据 提交读(Read Committed)&#xff1a; 只能读取到已经提交的数据。Oracle等多数数据库默…

车载电子电器架构 —— OEM基础技术概念开发流程

车载电子电器架构 —— 基础技术概念开发 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明…

vue间的组件通讯

文章目录 父子组件通讯&#xff08;通过props和$emit&#xff09;兄弟组件通讯&#xff08;使用事件总线&#xff09;&#xff1a;跨级组件通讯&#xff08;使用provide/inject&#xff09;&#xff1a;使用Vuex状态管理&#xff1a;使用$refs引用组件&#xff1a; 父子组件通讯…

广度优先-BFS

介绍 以广度为准&#xff0c;先访问从岔道能直接到达的所有结点&#xff0c;然后再按这些结点被访问顺序访问它们能直接到达的结点&#xff0c;直至全部遍历完。 求矩阵中元素上下左右均为1所组成的块的数目 #include <iostream> #include <queue> using namesp…

写点技术人员离职的心得

离职的心态 人们在辞退或者被辞退都会对原公司抱有意见&#xff0c;因为疫情&#xff0c;公司业务告急&#xff0c;工资发不出来&#xff0c;我也失去了工作。虽然情绪上难免会有波动&#xff0c;但是转念一想&#xff0c;我应该用开心的心态来看待这次辞职&#xff0c;并希望…

Linux之JAVA环境配置jdkTomcatMySQL

目录 一. 安装jdk 1.1 查询是否有jdk 1.2 解压 1.3 配置环境变量 二. 安装Tomcat&#xff08;开机自启动&#xff09; 2.1 解压 2.2 启动tomcat 2.3 防火墙设置 2.4 创建启动脚本&#xff08;设置自启动&#xff0c;服务器开启即启动&#xff09; 三. MySQL安装&#xff08;…

vscode使用restClient实现各种http请求

vscode使用restClient实现各种http请求 一&#xff0c;安装插件 首先&#xff0c;我们要在vscode的扩展中&#xff0c;搜索rest Client&#xff0c;然后安装它&#xff0c;这里我已经安装过了。 安装后&#xff0c;我们就可以使用rest client插件进行http各种操作了。 二&…

Unity接入SQLite (二):SQL常用命令

在上一篇上《Unity接入SQLite (一):SQLite介绍-CSDN博客》中已经介绍了如何在Unity中接入SQLite插件&#xff0c;并且创建了一个db文件。如何进行数据库的读取&#xff0c;其中SQL命令非常重要 1.SQL支持的数据类型 SQL数据类型是用来定义数据库中存储的数据的类型&#xff0…

二分图匹配详解

二分图的原始模型及相关概念 二分图又称作二部图&#xff0c;是图论中的一种特殊模型。 设G(V,E)G(V,E)是一个无向图。 如顶点集V可分割为两个互不相交的子集&#xff08;A, B&#xff09;&#xff0c;并且图中每条边(i&#xff0c;j)所关联的两个顶点 i 和 j 就都分属两个不…

定时任务处理-Spring Task

目录 1 前言 2 cron表达式 2.1 相关概念的介绍 2.2 举个例子(白雪警告) 2.3 使用网站自动生成 3 Spring Task的使用 3.1 导入依赖坐标 3.2 开启任务调度 3.3 自定义定时任务类 1 前言 当我们需要处理一些定时任务的时候就需要用到我们的Spring Task&#xff0c;接下来…

(done) 两个矩阵 “相似” 是什么意思?

参考视频&#xff1a;https://www.bilibili.com/video/BV1zu411673J/?spm_id_from333.337.search-card.all.click&vd_source7a1a0bc74158c6993c7355c5490fc600 参考资料&#xff1a;https://baike.baidu.com/item/%E7%9B%B8%E4%BC%BC%E7%9F%A9%E9%98%B5/10369874?frge_a…

算能RISC-V通用云编译飞桨paddlepaddle@openKylin留档

尝试一在riscv里编译飞桨。 先总结&#xff1a; 下载飞桨代码&#xff0c;参照pr修改代码 然后编译 cmake ../ -DWITH_GPUOFF -DWITH_RISCVON make -j 16 TARGETRISCV64_GENERIC 编译好后安装&#xff1a; pip install paddlepaddle-0.0.0-cp38-cp38-linux_riscv64.whl -…

Opencv(C++)学习 ARM上引用opencv报相关头文件找不到

简单问题记录&#xff0c;C 与C互相引用时应该多注意类似问题。 问题描述&#xff1a;在项目中&#xff0c;建立了一个interface.h提供了一个C语言兼容的接口void work()&#xff0c;并在对应的interface.cpp中使用OpenCV完成相关处理实现。在PC端测试时&#xff0c;main.cpp成…

【HTML/CSS/JavaScript-编程指南】

HTML/CSS/JavaScript-编程指南 ■ HTML/CSS/JavaScript简介■ HTML/CSS/JavaScript学习网站■ VScode■ VSCode编写HTML■ VSCode编写CSS■ VSCode编写JavaScript ■ 语法■ HTML语法■ CSS语法■ JavaScript 语法 ■ HTML/CSS/JavaScript简介 HTML&#xff08;全称 Hypertext…