背景:

我们需要将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为动态设置以适配公司活动平台相关推荐

  1. vue修改meta值_vue中动态设置meta标签和title标签的方法

    vue中动态设置meta标签和title标签的方法 因为和原生的交互是需要h5这边来提供meta标签的来是来判断要不要显示分享按钮,所有就需要手动设置meta标签,标题和内容 //router内的设置 ...

  2. vue动态设置style属性修改背景图片

    原始class属性设置背景样式 .bannerP {position: absolute;top: 0;left: 2.96rem;right: 0;bottom: 0;background: url ...

  3. Silverlight动态设置WCF服务Endpoint

    去年12月收到一位朋友的邮件,咨询Silverlight使用WCF服务,应用部署后一直无法访问的问题,通过几次交流,才发现在他的项目中,全部使用静态URL作为WCF服务的Endpoint地址,后来修改 ...

  4. js如何改变HTML属性,javascript – 如何动态设置HTML lang属性?

    如何在Web应用程序中动态设置HTML'lang'属性? 我尝试使用jQuery如下插入'lang'属性: $(document).ready(function() { $("html&qu ...

  5. 解决layui前端动态设置radio、checkbox默认选项的解决方案

    前几天接触一个二次开发,后台UI用的是layui框架,程序原来的TAG标签设置中自由输入的,想修改为单选.这样子的功能用于,一个产品属于两种分类的作法.第一种就是系统自带的产品分类,第二种使用标签来实 ...

  6. Quartz在Spring中动态设置cronExpression

    什么是动态定时任务:是由客户制定生成的,服务端只知道该去执行什么任务,但任务的定时是不确定的(是由客户制定). 这样总不能修改配置文件每定制个定时任务就增加一个trigger吧,即便允许客户修改配置文 ...

  7. 你有没有觉得邮件发送人固定配置在yml文件中是不妥当的呢?SpringBoot 动态设置邮件发送人

    明月当天,不知道你有没有思念的人 前言 之前其实已经写过SpringBoot异步发送邮件,但是今天在一个小项目中要用到发送邮件时,我突然觉得邮件发送人只有一个,并且固定写在yml文件中,就是非常的不妥 ...

  8. Spring AOP根据JdbcTemplate方法名动态设置数据源

    2019独角兽企业重金招聘Python工程师标准>>> 说明:现在的场景是,采用数据库(Mysql)复制(binlog)的方式在两台不同服务器部署并配置主从(Master-Slave ...

  9. 微信小程序 setData动态设置数组中的数据

    现在有一组死数据 但是想在使用时动态修改其中的值 在setData中不能直接设置,真想......,算了是这个和谐的社会救了那个工程师. 接下来,我们去征服她! 死数据: //地图上方控件contro ...

最新文章

  1. iOS显示gif图片的几种方法
  2. 一篇文章搞懂人脸识别的十个概念
  3. 近期活动盘点:工业大数据讲座、大数据自杀风险感知讲座、数据法学研讨会、海外学者短期讲学(12.3-12.13)
  4. 用frontpage制作网页,字体大小用PT做单位好还是用PX做单位好?
  5. zabbix监控多台站点服务器
  6. reactjs组件通信方式总结
  7. 检索数据_21_处理空值的排序
  8. 网银系统服务器架构设计,网上银行建设架构精选.pdf
  9. C++: C++函数声明的时候后面加const
  10. 一篇文章带你领悟 Frida 的精髓(基于安卓8.1)
  11. Android studio gradle task list 不显示问题
  12. 【微信支付开发流程】
  13. 64k超高清3d程序 Warez出品的精品动画 近25万倍的压缩的精品
  14. R语言_决策树rpart中的cp值
  15. log4j2自定义级别日志
  16. 关于excel表格直接引用和间接引用
  17. 怦然心动(Flipped)-3
  18. JAVA简单大数运算
  19. Ultimate Member插件注册登录流程分析
  20. 软件项目开发与管理(赢得值分析参考例题)

热门文章

  1. python c4.5完整代码_python实现c4.5/Id3自我练习
  2. html5 canvas获取坐标,HTML5 canvas坐标
  3. Java Web Token - JWT
  4. 乐观锁的两种实现方式
  5. SQL Search
  6. Apache-SimpleEmail 简单应用
  7. linux查找设备所在分片,Linux设备驱动统一模型解析
  8. C++:vector中的resize()函数 VS reserve()函数
  9. 以实例让你真正明白mapreduce---填空式、分布(分割)编程
  10. spring中的设计模式_面试:设计模式在spring中的应用