一、问题背景和描述:
背景:在Android系统中进行Hybrid混合开发,脚手架是通过vue-cli2.0搭建。整体框架做了个性化调整,添加了eslint代码校验、prettier代码风格统一、引入postcss解决CSS前缀问题、Babel配置、browserslist配置等。框架搭建好之后,在各个主流浏览器和市面主流手机中测试都正常。然而。。。
问题:在某款2018年购买的手机和IE11中显示异常。 IE11报错如下:
同事打电话找到我,由于工位距离和忙于本职工作的原因,就通过电话沟通解决了。根据同事的描述,基本可以判断是代码向下兼容的问题导致的,另外,还提到工程中引入了vant,如果不使用Button按钮就正常。过程像极了中医里的“望闻问切”,当然还没到切的那一步。问题的原因知道了,就相当于解决了一半。解决方案立刻就确定到了Babel的转码上了。
二、问题解决方案一: 初步解决
在Babel中默认是不会对node_modules里的代码做转义的,解决方案:
1.babel的配置文件为babel.config.js。引用官方:
You want to compile
node_modules
?
babel.config.json
is for you!You have a configuration that only applies to a single part of your project?
.babelrc.json
is for you!
2.babel.config.js配置中include:
{
include: [path.resolve('src'), path.resolve('node_modules/xxx')],
}
或者 在vue.config.js里配置transpileDependencies:
transpileDependencies:true // 默认情况下 `babel-loader` 会忽略所有 `node_modules` 中的文件。你可以启用本选项,以避免构建后的代码中出现未转译的第三方依赖。
问题似乎很so easy!把解决方案给同事说了下,同事回复“transpileDependencies”的方案尝试过,然而木有用!!!有点尴尬了!但我坚信方向是对的~~~
三、问题解决方案二: “切”
将源代码下载到本地,直接进行代码问题分析。通过浏览器里的DevTools工具的“切换设备方针”进行查看,并没有报异常。页面有个vconsole按钮引起我的好奇(很多年未开发h5页面了),点击后可以弹出调试窗口。好奇有会“害死猫”,但有时候会帮你解决问题!找到vconsole使用相关代码:
代码问题1:
// 问题代码
const VConsole = require("vconsole/dist/vconsole.min")
if(process.env.NODE_ENV !== "production"){
new VConsole()
}
代码的目的是想在本地开发中使用vconsole这个插件。再仔细一想,为什么在production模式中也要加载呢?代码是否应该如下:
// 修改后的代码
if(process.env.NODE_ENV !== "production"){
const VConsole = require("vconsole/dist/vconsole.min")
new VConsole()
}
IE浏览器里跑一下,还是报错,但是报错内容变了!!!:
看来:“细节决定成败!” 对代码负责,代码也会对你好!窃喜~~
代码问题2:
在调试过程中发现,针对生产环境打的包和测试环境打的包“报错结果不同”。理论上应该是一样的,为什么会不同?好奇心驱使我一探究竟。 通过查看.env.testing配置文件发现:
NODE_ENV = testing
package.json里构建测试环境的命令是:
build_test: "vue-cli-service build --mode testing
然后翻看了vue-cli的官方文档:
当你运行
vue-cli-service build
命令时,无论你要部署到哪个环境,应该始终把NODE_ENV
设置为"production"
来获取可用于部署的应用程序。
修改了NODE_ENV的环境变量值,导致了代码问题1中测试环境打包仍然出现vconsole的问题。另外,NODE_ENV = production,vue-cli会默认开启一些生产构建功能,针对测试环境不应该修改变量值。还有,--mode 制定的值和NODE_ENV之间的关系也应该要梳理清楚,这一点在学习官方文档的时候也是有点模糊。这一次逻辑清晰了!好奇心,似乎又一次帮助了我!
四、解决方案三:还得死磕Babel
在主流浏览器中运行正常,部分浏览器执行异常。原因肯定是代码兼容性问题,而解决代码兼容性问题,首要考虑就必须是babel。代码向下兼容设计的解决方案有:babel的targets配置和.browserslist
1.Babel的targets配置解决方案:
Babel官方对targets的解释:
Describes the environments you support/target for your project.
Babel官方对于no targets的解释:
When no targets are specified: Babel will assume you are targeting the oldest browsers possible. For example,
@babel/preset-env
will transform all ES2015-ES2020 code to be ES5 compatible.
目前代码工程中属于no targets,因此,代码最终被转义为ES5。
2. .browserslist解决方案:
框架搭建过程中,容易被忽视的一个配置。查看代码工程中确实存在.browserslist的配置文件,但内容就是脚手架初始化的状态。 browserlist的官方解释是:
Shared browser compatibility config for popular JavaScript tools like Autoprefixer, Babel, ESLint, PostCSS, and Webpack
browserslist的配置是给Babel用的。我先解决下IE 11再说:
> 1%
last 2 versions
not dead
IE 11
重新构建运行,IE 11运行结果正常了,如图所示:
What!!! so easy!有点小确幸~~
注意:
代码向下兼容,需要babel-polyfill的支持,在代码工程发现main.js文件中已经引入了,在babel.config.js中也设置了
'useBuiltIns':'entry' // usage的用法更值得推荐。这里只能选entry,因为不确定是哪个语法导致的问题
。Babel-polyfill的作用需要梳理清楚~~
3. 剩下任务就是确定出问题手机的浏览器内核了,查看android的webview的浏览器内核方法如下图所示:
同事告诉了具体的版本号之后,问题又来了,.browserslist中具体哪个关键字是用来兼容android手机内核的呢? 以前,对.browserslist真的不熟悉,趁此机会搞清楚~~
a. npx .browserslist命令可以查看当前代码工程支持的默认兼容的浏览器版本,具体可以自行尝试
b. 貌似对android的浏览器起作用的有and_开头的关键字和android,进一步核实后是android
最后:
> 1%
last 2 versions
not dead
IE 11
android >= 68
放到测试环境运行后,问题解决了~~~
五、总结:
- 良好的编程习惯,会产生高杠杆受益。
- 好奇心+行动力,往往会得到惊喜
- 解决bug真的会带来乐趣~~