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

修改webpack的publicPath为动态设置以适配公司活动平台背景:我们需要将React开发的应用部署到一个活动搭建平台上,这意味我们只需要上传源码,没有搭建服务器的环节,没有配置Nginx的环节。具体步骤就是在该平台新建一个活动,然后将自己的源码传到这个活动下,然后打开这个活动提供的地址,然后就能够看到页面。我们上传的js文件main.js会得到一个js/main.js的路径,然后将这个路径放到html的script标签的src属性上即可。main.c…

背景:

我们需要将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。

今天的文章修改webpack的publicPath为动态设置以适配公司活动平台分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/11485.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注