修改webpack的publicPath为动态设置以适配公司活动平台

背景:

我们需要将React开发的应用部署到一个活动搭建平台上,这意味我们只需要上传源码,没有搭建服务器的环节,没有配置Nginx的环节。具体步骤就是在该平台新建一个活动,然后将自己的源码传到这个活动下,然后打开这个活动提供的地址,然后就能够看到页面。

我们上传的js文件main.js会得到一个js/main.js的路径,然后将这个路径放到html的script标签的src属性上即可。main.css同理会得到一个css/main.css的路径,然后放到link标签的href属性上即可。

这些做完查看页面的时候我们会发现script标签的src属性被替换成了//xxx.com/xxx/xxx/main.js?t=xxx这种形式。同样css也被换成了//xxx.com/xxx/xxx/main.css?t=xxx。需要注意的是这个路径中并没有js/和css/这种东西。

注:另外上传的html文件存储的位置和JS/CSS并不在一起。

现在我们知道上传的资源除了HTML文件会被存在//xxx.com/xxx/xxx/目录下。

当我们用createReactApp创建的脚手架打包React应用的时候可以修改publicPath为//xxx.com/xxx/xxx/这样打出来的包中HTML文件的script标签和link标签的对应属性就绝对路径(绝对路径并不会被自动替换)。并且是正确的绝对路径。

到目前为止玩耍的很开心。

有一天的一个时间节点之后,发现传上去的应用访问不通,报错了,JS和CSS文件找不到。排查之后发现活动平台升级之后,将存储JS和CSS这种静态资源的域名换掉了。而我们打包的时候将域名写死了这就出错了。

解决的方法固然很简单将publicPath换下就行了。

但是这种解决方法并不稳妥,因为我们依赖服务端并不修改静态资源存储的域名。这显然是不靠谱的。

那么为什么我们不将打包出来的HTML中script标签的src属性变成相对的呢?例如平台要求的js/main.js,css/main.css。

这显然是可以的,我们试下。

结果是部分可以部分不可以–!

我们都知道webpack打包需要一个入口文件,webpack配置文件提供的entry字段提供的入口文件。

性能优化的时候,这个入口文件还会被分为至少三个文件,一个是vendor.js这是入口文件中被引入的第三方库,基本不怎么变动可以缓存。一个是runtime.js这是每次打包都会改变的,不利于缓存,最好直接插入到HTML文件中。一个是业务代码,基本每次发版都会变动的main.js。其中真正的入口文件是被插入到HTML中的runtime.js。

有了这些资源之后使用插件HtmlWebpackPlugin将这些资源注入到index.html中。

这些都是webpack打包的时候就确定下来的,在publicPath修改成’'并且将output.filename和output.chunkFilename改成’js/[name].[contenthash].js’之后,上面确定下来的部分生成的script标签的src属性为 ‘js/xxxxx.js’ 现在将这个生成的HTML和其他静态资源传到活动平台,发布之后我们发现页面正常。但是还有一部分有问题。

这部分是动态加载的,webpack优化的时候不出现在首屏的部分我们会现在稍后加载,或者说使用webpack切割代码的功能让它动态加载出来。具体方法就是 import('./a.js')这种形式。

那么这样动态加载出来的文件有什么问题呢?

首先动态加载的模块是通过webpack后期自动构建一个script标签然后插入的HTML中然后加载执行的。

既然是后期动态添加的,那么活动平台的统一替换标签的src就没有作用到这个动态添加的标签上,这个标签的src还是js/xxx.js这种形式,这就报错了。

小结:

为了解决线上静态资源地址会变的问题,将HTML文件中替换线上资源地址的工作重新交还给活动平台自身。具体表现为HTML中引入的资源都是相对地址’js/xxx.js’或者’css/xxx.css’这种形式,这样就解决了我们写死线上资源地址,但是活动平台替换后我们不知道导致的问题。

具体配置修改:

publicPath从’//xxxx.com/xx/xxx/‘变成了’’

filename和chunkFilename从’xxx.js’变成了’js/xxx.js’

上面的配置得到的src就是 ‘’ + ‘js/xxx.js’ -> ‘js/xxx.js’ 这正是我想要的。

但是通过这个可以看出来还有一种方法可以做到。

将publicPath设置为’js/’,filename还保持’xxx.js’这样也能得到’js/xxx.js’。

但这是不行的,因为publicPath的语义是所有静态资源的公共路径前缀。这么一搞不仅得到了’js/xxx.js’还得到了’js/xxx.css’等。

注:HTML中被注入的script的src属性和动态生成的script标签的src属性都是通过publicPath + filename得到的。

到这里主要矛盾是活动平台的地址替换并不能作用到动态添加的script标签,导致js/xxx.js这种形式并没有被替换。其次编译代码的时候我们并不知道publicPath的值是什么,只有等到代码执行,也就是活动平台替换script标签的src属性之后才能知道。但是publicPath是在编译的时候写死的。

怎么让publicPath变成动态的?让publicPath在代码执行的时候动态获取被替换之后的script标签的src属性,然后解析出其中的path设置上,这样后面动态生成的script标签的src就可以正常访问,得到正确的地址。

翻阅文档可以找到这段描述:

在编译时(compile time)无法知道输出文件的 publicPath 的情况下,可以留空,然后在入口文件(entry file)处使用自由变量(free variable) __webpack_public_path__,以便在运行时(runtime)进行动态设置。

 __webpack_public_path__ = myRuntimePublicPath// 应用程序入口的其他部分

这就给了我们动态修改publicPath的能力。

看到了可行的希望。

那么这个原理是啥呢?翻看编译过后未压缩的代码可以看到如下内容:

// __webpack_public_path__
__webpack_require__.p = "";

上面的属性p就是我们配置的publicPath,在编译后的代码内部是被存在一个对象的属性上的。如果webpack暴露给我们这个对象,我们自己是可以修改的。当然也可以看出,这个必须代码一执行就需要修改,否则后面会有问题。这就是为什么修改publicPath的代码要放在入口文件顶部的原因。

__webpack_public_path__ = myRuntimePublicPath

为什么这段代码就能完成__webpack_require__.p的修改呢?

例如:

__webpack_public_path__ = 'publicPath/'

会变成:

{"./src/config.js":/*!***********************!*\!*** ./src/config.js ***!\***********************//*! no static exports found *//***/ (function(module, exports, __webpack_require__) {eval("__webpack_require__.p = 'publicPath/'\n\n\n//# sourceURL=webpack:///./src/config.js?");/***/ })
}

可以看到两点,一个是模块被编译成被一个函数包裹的代码块,并且函数的最后一个入参是__webpack_require__。第二是代码__webpack_public_path__变成了__webpack_require__.p

这就证明了webpack自身提供了我们动态修改publicPath的能力。

最后我们还发现了如下代码:

// script path function
function jsonpScriptSrc(chunkId) {return __webpack_require__.p + "js/" + chunkId + ".index.js"
}

这段代码是用于获取动态加载的script的src属性的。这里也看到了__webpack_require__.p的身影。

注:为啥是chunkId + “.index.js”?当没有指定动态引入的模块的名字的时候就是0.index.js, 1.index.js, 2.index.js…

好,现在我们知道动态的publicPath是可以实现的。

还有一个问题,我们怎么在入口JS中获取这个script标签的src属性呢?

并没有手段直接获取JS当前标签的DOM对象,但是我们可以换个方法,JS可以通过属性id获取对应的DOM。

但是HtmlWebpackPlugin插入标签的时候并没有提供id属性。

虽然没有提供id,但是却提供了开发相关插件的能力,这样我们可以通过开发HtmlWebpackPlugin的插件来给对应的script标签加上id。这样我们就拿到了对应的src解析出了静态资源存储的path。

上面的方法我试了一下,是可以的。但是只可以了一半。我们能做的这一半,做完了。剩下的一半也是无能为力了。

通过上面的方法,我们得到了//xx.com/xx/xx/这个线上的publicPath。然后动态生成的js的filename是js/xxx.js,那么拼起来就是//xx.com/xx/xx/js/xx.js这种形式,这中间多了一个js/。并且这个我们去除不了。所以还是破产了。

但我觉得这并不是我们的问题。

本身我们打包出来的文件是在文件夹js目录下,并且传上去之后得到的目录也是js/xxx.js但是实际替换的时候,目录结构却变了,没有了js/这一层。这有点怪了。

所以破产了。

但是如果url有能力去除后面部分倒还是可以的,但是明显不行啊。例如 //xxx.com/a/b/…/c 表示的是不要b/这一层,但是不要b/这一层是由b/后面部分决定的,如果可以由b/这部分前面部分决定就好了。

还有如果我们可以实现上面提到的jsonpScriptSrc这个函数到也可以,直接将js/这部分去掉就好了。

总结:

上面对于src="js/xxx.js"这部分js/的替换分为了两部分,一部分是静态生成的,webpack打包出来的HTML模板就是这样的。这部分会由活动平台自动替换成线上的资源路径。

第二部分是动态生成的资源部分,这部分我们需要自己手动替换,方法是,动态修改publicPath,拼接完成,但是因为目录层级的改变,我们失败了。

对于这个问题的解决方案是上传到活动平台的代码不要使用动态引入JS。

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

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

相关文章

多个气泡向上冒出!

这里展示白色半透明气泡如下图:实际是动态 思路:HTML里只需要一个CANVAS元素,Javascript里操作canvas 1、给canvas里绘制背景图片 2、在绘制半径为0-10px的圆形,x坐标屏幕水平随机,y所标竖直大于屏幕高度。 圆形背景…

注入域对象而不是基础结构组件

依赖注入是Java(以及许多其他编程语言)中广泛使用的软件设计模式,用于实现控制反转 。 它提高了可重用性,可测试性,可维护性,并有助于构建松耦合的组件。 如今,依赖注入是将Java对象连接在一起的…

分享25个优秀的网站底部设计案例

相对于网站头部来说,关注网站底部设计的人很少。我们平常也能碰到有些网站的底部设计得很漂亮,给网站的呈现来一个完美的结尾。这篇文章收集了25个优秀的网站底部设计案例,一起欣赏。 me & oli La Bubbly Poogan’s Porch GiftRocket Lin…

wbepack中output.filename和output.chunkFilename

对于webpack配置中filename和chunkFilename在使用中有些不懂的地方,研究之后记录如下。 filename: string | function 此选项决定了每个输出 bundle 的名称。这些 bundle 将写入到 output.path 选项指定的目录下。 对于单个入口起点,filename 会是一个…

页面添加锚点后如何点击不改变URL?

直接奔主题&#xff0c;前端简单地锚点实现方法大家都会&#xff0c;无非就是在把 a 标签的 href 写成想要跳到的元素的id &#xff0c;比如点击 <a href"#box"></a> 页面就会自动滚动到 <div id"box"></div> 元素的位置。 那么这…

OP AMP - 反馈理论在运放中的应用

实际应用的系统绝大多数是闭环的&#xff0c;运放更是如此。 关于反馈理论&#xff0c;需要了解极点&#xff0c;零点&#xff0c;波特图&#xff0c;以及如何用相位裕度&#xff0c;幅值裕度 确定系统的稳定性&#xff0c;这里不作赘述 &#xff0c;不懂的部分可以到网上搜点…

具有Spring Boot和Java配置的Spring Batch教程

我一直在努力将Podcastpedia.org的一些批处理作业迁移到Spring Batch。 以前&#xff0c;这些工作是以我自己的方式开发的&#xff0c;我认为现在是时候使用一种更“标准化”的方法了。 因为我以前从未在Java配置中使用过Spring&#xff0c;所以我认为通过在Java中配置Spring B…

阅读react-redux源码 - 零

阅读react-redux源码 - 零阅读react-redux源码 - 一阅读react-redux源码(二) - createConnect、match函数的实现 react的技术栈一定会遇到redux&#xff0c;而在react中使用redux需要使用react-redux&#xff0c;那么react-redux是怎么包装redux已适用react的呢&#xff1f; …

kali linux 开启配置ssh服务

1. 一、配置SSH参数 修改sshd_config文件&#xff0c;命令为&#xff1a;vi /etc/ssh/sshd_config将#PasswordAuthentication no的注释去掉&#xff0c;并且将NO修改为YES //kali中默认是yes2. 将PermitRootLogin without-password修改为PermitRootLogin yes3. 然后&#xf…

CORS(跨域资源共享)

CORS&#xff08;跨域资源共享&#xff09;使用额外的HTTP头部来告诉浏览器&#xff0c;允许运行在origin(domain)上的Web应用访问来自不同源服务器上的指定资源。 浏览器访问一个web应用&#xff0c;这个web应用会发很多的跨域请求&#xff0c;例如加载不同源的JS/CSS脚本&am…

[转]jQuery设计思想

转自&#xff1a;http://kb.cnblogs.com/page/109875/ jQuery是目前使用最广泛的javascript函数库。 据统计&#xff0c;全世界排名前100万的网站&#xff0c;有46%使用jQuery&#xff0c;远远超过其他库。微软公司甚至把jQuery作为他们的官方库。 对于网页开发者来说&#xff…

Java转换难题者,不适合工作(或面试)

一个非常艰苦的面试问题可能是这样的&#xff1a; int i Integer.MAX_VALUE; i 0.0f; int j i; System.out.println(j Integer.MAX_VALUE); // true为什么打印出正确的文字&#xff1f; 乍一看&#xff0c;答案似乎很明显&#xff0c;直到您意识到如果长时间更改int&…

问题:jquery给标签添加事件,但标签还未加载会成功吗

之后研究解决转载于:https://www.cnblogs.com/sz-toosimple/p/11170912.html

Access-Ctrol-Allow-Headers:*兼容问题导致的跨域失败

现象&#xff1a; 通过抓包看到在部分客户端上跨域的非简单请求只发送一个预检的OPTIONS请求&#xff0c;之后的真实请求并没有发送。 出现问题的环境&#xff1a; 部分IOS低版本系统。 windows系统微信内必现&#xff08;2020-04-29&#xff09;。 分析 通过上面条件OPT…

在github上托管Maven存储库(包含源代码和javadoc)

如何通过maven将小型开源库提供给其他开发人员&#xff1f; 一种方法是将其部署在Maven Central Repository上 。 我想要做的是将其部署到github &#xff0c;因此我可以自由地对其进行修改。 这篇文章将告诉您如何做到这一点。 我将工件部署到github的典型方法是使用mvn depl…

关于移动端页面强制竖屏

最近工作中写了一个移动端的页面&#xff0c;本来是没什么的&#xff0c;但是有一个要求感觉很奇怪&#xff0c;从前也没有遇到过&#xff0c;就是我写的这个页面需要放在一个APP中&#xff0c;但是这个APP是横屏的&#xff0c;打开这个页面的webview也是横屏的&#xff08;最新…

敏捷开发绩效管理之四:为团队设立外部绩效目标(目标管理,外向型绩效)...

这是敏捷开发绩效管理的第四篇。&#xff08;之一&#xff0c;之二&#xff0c;之三&#xff0c;之四&#xff0c;之五&#xff0c;之六&#xff0c;之七&#xff09;最近在看德鲁克的书&#xff0c;发现其中很明确地写着“企业的绩效只存在于外部&#xff0c;而企业内部只有成…

面向对象程序设计-C++ Default constructor Copy constructor Destructor Operator Overloading【第九次上课笔记】...

先上笔记内容吧&#xff1a; 这次上课的内容有关 构造函数析构函数运算符重载return * this内容很细&#xff0c;大家好好回顾笔记再照应程序复习吧 :) #include <iostream>using namespace std;class Integer { public:int i;int geti () const {return this->i;}vo…

阅读react-redux源码 - 一

阅读react-redux源码 - 零阅读react-redux源码 - 一阅读react-redux源码(二) - createConnect、match函数的实现 阅读react-redux源码零中准备了一些react、redux和react-redux的基础知识。从使用的例子中可以看出来顶层的代码中需要用一个来自react-redux的Provider组件提供r…

【K8S in Action】服务:让客户端发现pod 并与之通信(2)

一 通过Ingress暴露服务 Ingress (名词&#xff09; 一一进入或进入的行为&#xff1b;进入的权利&#xff1b;进入的手段或地点&#xff1b;入口。一个重要的原因是每个 LoadBalancer 服务都需要自己的负载均衡器&#xff0c; 以及 独有的公有 IP 地址&#xff0c; 而 Ingres…