前端本地化部署

news2025/1/19 14:14:31

前言

现在成熟的前端团队里面都有自己的内部构建平台,我司云长便是我们 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.coloradmin.cn/o/1469981.html

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

相关文章

Servlet使用Cookie和Session

一、会话技术 当用户访问web应用时&#xff0c;在许多情况下&#xff0c;web服务器必须能够跟踪用户的状态。比如许多用户在购物网站上购物&#xff0c;Web服务器为每个用户配置了虚拟的购物车。当某个用户请求将一件商品放入购物车时&#xff0c;web服务器必须根据发出请求的…

大厂面试-美团高频考察算法之重排链表

本文学习目标或巩固的知识点 学习如何处理链表重排类题目 巩固反转链表巩固快慢指针巩固合并链表 提前说明&#xff1a;算法题目来自力扣、牛客等等途径 &#x1f7e2;表示简单 &#x1f7e1;表示中等 &#x1f534;表示困难 &#x1f92e;表示恶心 博主真实经历&#xff0c;…

docker-compose 搭建laravel环境

laravel环境包含nginx,mysql,php7.4,redis 一、安装好docker后pull镜像 1.nginx镜像 docker pull nginx:latest单独启动容器 docker run --name nginx -p 80:80 -d nginx 2.php镜像 docker pull php:7.4-fpm3.mysql镜像 docker pull mysql:5.74.redis镜像 docker pull r…

React18原理: React核心对象之Update、UpdateQueue、Hook、Task对象

Update 与 UpdateQueue 对象 1 ) 概述 在fiber对象中有一个属性 fiber.updateQueue是一个链式队列&#xff08;即使用链表实现的队列存储结构&#xff09;是和页面更新有关的 2 &#xff09;Update对象相关的数据结构 // https://github.com/facebook/react/blob/v18.2.0/pa…

Sqli-labs靶场第11关详解[Sqli-labs-less-11]

Sqli-labs-Less-11 前言&#xff1a; SQL注入的三个条件&#xff1a; ①参数可控&#xff1b;&#xff08;从参数输入就知道参数可控&#xff09; ②参数过滤不彻底导致恶意代码被执行&#xff1b;&#xff08;需要在测试过程中判断&#xff09; ③参数带入数据库执行。&…

[rust] 10 project, crate, mod, pub, use: 项目目录层级组织, 概念和实战

文章目录 一 项目目录层级组织概念1.1 cargo new 创建同名 的 Project 和 crate1.2 多 crate 的 package1.3 mod 模块1.3.1 创建嵌套 mod1.3.2 mod 树1.3.3 用路径引用 mod1.3.3.1 使用绝对还是相对? 1.3.4 代码可见性1.3.4.1 pub 关键字1.3.4.2 用 super 引用 mod1.3.4.3 用 …

【微服务】mybatis typehandler使用详解

目录 一、前言 二、TypeHandler简介 2.1 什么是TypeHandler 2.1.1 TypeHandler特点 2.2 TypeHandler原理 2.3 mybatis自带的TypeHandler 三、环境准备 3.1 准备一张数据表 3.2 搭建一个springboot工程 3.2.1 基础依赖如下 3.2.2 核心配置文件 3.2.3 测试接口 四、T…

JAVA工程师面试专题-《Mysql》篇

目录 一、基础 1、mysql可以使用多少列创建索引&#xff1f; 2、mysql常用的存储引擎有哪些 3、MySQL 存储引擎&#xff0c;两者区别 4、mysql默认的隔离级别 5、数据库三范式 6、drop、delete 与 truncate 区别&#xff1f; 7、IN与EXISTS的区别 二、索引 1、索引及索…

Linux字符设备驱动中同类型多设备节点的创建---一个驱动程序支持多个同类型设备

文章目录 前言1 代码解析1.1 驱动层1.2 应用层 2 运行结果总结 前言 本期分享的内容相对比较简单&#xff0c;那就是同时注册多个同类型的字符设备驱动&#xff0c;那么这样我们就可以同时支持多个同类型的设备了&#xff01;下面来带大家看一下&#xff1a; 1 代码解析 1.1 …

python 线程笔记二 (概念+示例代码)

1. 线程介绍 1. 在前面了解了进程的概念&#xff0c;简单来说进程就是在内存中申请了一块内存空间&#xff0c;其实还有一个线程的概念&#xff0c; 线程包含在进程之中&#xff0c;是代码真正的执行者。也就是说进程其实是一个资源单位&#xff0c;而线程是执行单位。 2. 线程…

瑞_Redis_初识Redis(含安装教程)

文章目录 1 初识Redis1.1 认识NoSQL1.1.1 结构化与非结构化1.1.2 关联和非关联1.1.3 查询方式1.1.4 事务1.1.5 总结 1.2 认识Redis1.2.1 介绍1.2.2 特征1.2.3 优势 1.3 安装Redis ★★★1.3.1 Linux安装Redis1.3.1.0 资源准备1.3.1.1 安装Redis依赖1.3.1.2 上传安装包并解压1.3…

vim恢复.swp [BJDCTF2020]Cookie is so stable1

打开题目 扫描目录得到 关于 .swp 文件 .swp 文件一般是 vim 编辑器在编辑文件时产生的&#xff0c;当用 vim 编辑器编辑文件时就会产生&#xff0c;正常退出时 .swp 文件被删除&#xff0c;但是如果直接叉掉&#xff08;非正常退出&#xff09;&#xff0c;那么 .swp 文件就会…

windows 中, bash: conda: command not found(已解决)

git bash 中运行conda命令&#xff0c;出现这种错误&#xff0c;原因是你没有在git bash中 配置conda&#xff0c;导致git bash无法找到conda 那就配置一下&#xff0c;找到你的conda的安装位置下的bash.sh文件&#xff0c;一般在安装位置&#xff08;我的安装在C盘的自定义路径…

在Linux操作系统的ECS实例上安装Hive

目录 1. 完成hadoop安装配置2. 安装配置MySql安装配置 3. 安装Hive4. 配置元数据到MySQL5. hiveserver2服务配置文件测试 1. 完成hadoop安装配置 在Linux操作系统的ECS实例上安装hadoop 以上已安装并配置完jdk、hadoop也搭建了伪分布集群 2. 安装配置MySql 安装 下下一步…

Unity中URP实现水体效果(泡沫)

文章目录 前言一、给水上色1、我们在属性面板定义两个颜色2、在常量缓冲区申明这两个颜色3、在片元着色器中&#xff0c;使用深度图对这两个颜色进行线性插值&#xff0c;实现渐变的效果 二、实现泡沫效果1、采样 泡沫使用的噪波纹理2、控制噪波效果强弱3、定义_FoamRange来控制…

算法-计算机基础知识

1&#xff0c;坐标系与数学不同&#xff0c;x轴向下&#xff0c;y轴向右 2.案例&#xff1a;螺旋矩阵 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 class Solution {public List<Integer> spiralOrder(int[][] matrix) { List<Integer&…

解锁苏宁电商数据新纪元:关键字搜索API接口引领业务升级

苏宁关键字搜索API接口&#xff1a;电商数据探索的新篇章 一、引言 在电商领域&#xff0c;数据的重要性不言而喻。为了帮助开发者更高效地获取和利用电商数据&#xff0c;苏宁开放平台提供了关键字搜索API接口。本文将带你深入了解这一接口的技术细节&#xff0c;让你在电商…

【电子通识】为什么单片机芯片上会有多组VDD电源?

在单片机芯片规格书中&#xff0c;我们经常能看到多个组VDD的设计&#xff0c;如下红框所示管脚都是VDD管脚。 为什么需要这样设计&#xff1f;只设置一个VDD管脚&#xff0c;把其他的VDD管脚让出来多做几个IO或是其他复用功能不好吗&#xff1f;接下来我们从单片机内部的电路结…

Jenkins自动化部署构建说明(8)

Jenkins构建说明 - 20211012 什么是Jenkins? Jenkins 是一款流行的开源持续集成&#xff08;Continuous Integration&#xff09;工具&#xff0c;广泛用于项目开发&#xff0c;具有自动化构建、测试和部署等功能。它是一个自动化的周期性的集成测试过程&#xff0c;从检出代…

安装 Ubuntu 22.04.3 和 docker

文章目录 一、安装 Ubuntu 22.04.31. 简介2. 下载地址3. 系统安装4. 系统配置 二、安装 Docker1. 安装 docker2. 安装 docker compose3. 配置 docker 一、安装 Ubuntu 22.04.3 1. 简介 Ubuntu 22.04.3 是Linux操作系统的一个版本。LTS 版本支持周期到2032年。 系统要求双核 C…