问题记录:
- 项目在产线中出现问题,定位问题后发现,是promise.finally() 报错is not a function导致。
- 问题只有部分人员出现
定位过程:
- 保证代码逻辑没有错误,定位出问题的浏览器,发现都是chrome版本较低的浏览器会报错,大概在60~61版本。
- 去MDN查看promise.finally的定义和支持情况,发现是ES7中引入,对chrome浏览器的支持度是83以上。
- 到此问题已经定位,但是为什么我们的babel没有polyfill这段代码呢? 因为对babel一直不太熟悉,正好借助这个问题,熟悉一下babel的相关知识点。
一直对babel的概念都停留在它可以自动polyfill新语法的js用来支持老旧浏览器,但是具体的配置情况到底如何,如何识别新语法的js等原理都不甚了解。由于是编译工具,在此只做了解,不做深入学习。
基本用法:
在项目的根目录下创建一个命名为 babel.config.json 的配置文件(需要 v7.8.0 或更高版本),并将以下内容复制到此文件中:
const presets = [
[
"@babel/env",
{
targets: {
edge: "17",
firefox: "60",
chrome: "67",
safari: "11.1",
},
useBuiltIns: "usage",
corejs: "3.6.4",
},
],
];
module.exports = { presets };
babel,主要作用是为了兼容浏览器,让你在爽快的使用新特性的同时,不用担心浏览器的兼容问题。 由于项目中使用的是vue-cli生成的脚手架,在使用过程中主要参考vue-cli对babel的配置。
browserslist
@babel/preset-env 和 Autoprefixer 会根据browserlist中的配置来匹配对应的浏览器。
"browserlist":[
"> 1%",
"last 2 versions",
"not ie <= 8"
]
一个默认的 Vue CLI 项目会使用 @vue/babel-preset-app,它通过 @babel/preset-env
和 browserslist
配置来决定项目需要的 polyfill。
默认情况下,它会把 useBuiltIns: 'usage'
传递给 @babel/preset-env
,这样它会根据源代码中出现的语言特性自动检测需要的 polyfill。这确保了最终包里 polyfill 数量的最小化。然而,这也意味着如果其中一个依赖需要特殊的 polyfill,默认情况下 Babel 无法将其检测出来
如果有依赖需要 polyfill,你有几种选择:
如果该依赖基于一个目标环境不支持的 ES 版本撰写: 将其添加到
vue.config.js
中的transpileDependencies
选项。这会为该依赖同时开启语法转换和根据使用情况检测 polyfill。如果该依赖交付了 ES5 代码并显式地列出了需要的 polyfill: 你可以使用
@vue/babel-preset-app
的 polyfills 选项预包含所需要的 polyfill。注意es.promise
将被默认包含,因为现在的库依赖 Promise 是非常普遍的。如果该依赖交付 ES5 代码,但使用了 ES6+ 特性且没有显式地列出需要的 polyfill (例如 Vuetify):请使用 useBuiltIns: 'entry' 然后在入口文件添加 import 'core-js/stable'; import 'regenerator-runtime/runtime';。这会根据 browserslist 目标导入所有 polyfill,这样你就不用再担心依赖的 polyfill 问题了,但是因为包含了一些没有用到的 polyfill 所以最终的包大小可能会增加。
babel 实际上使用各种插件来进行polyfill的转换,例如@babel/plugin-transform-arror-functions就是用来转义箭头函数。在整个项目中可能存在很多需要转换的特性,这时候可以直接使用babel的一个preset来进行设置。@babel/preset-env。 在使用vue-cli生成的项目中,默认使用了vue的preset,‘@vue/app’.
preset 中的useBuiltIns
该属性有3个参数:
- usage:也就是只包含你所需要的 polyfill,Babel 将检查你的所有代码,以便查找目标环境中缺失的功能,然后只把必须的 polyfill 包含进来。示例代码如下:
Promise.resolve().finally();
会被转换为
require("core-js/modules/es.promise.finally");
Promise.resolve().finally();
- entry
根据配置的浏览器兼容,引入浏览器不兼容的 polyfill。需要在入口文件手动添加 import '@babel/polyfill',会自动根据 browserslist 替换成浏览器不兼容的所有 polyfill。 - false
此时不对 polyfill 做操作。如果引入 @babel/polyfill,则无视配置的浏览器兼容,引入所有的 polyfill。