这里先给出几个链接:
1.Vue-cli浏览器兼容性:https://cli.vuejs.org/guide/browser-compatibility.html#usebuiltins-usage
2.browserslist:https://www.npmjs.com/package/browserslist
3.babel.config.js:
https://babeljs.io/docs/en/configuration#babelconfigjs
https://cli.vuejs.org/zh/config/#babel
4.@vue/babel-preset-app:https://www.npmjs.com/package/@vue/babel-preset-app
5.@babel/preser-env:https://babeljs.io/docs/en/next/babel-preset-env.html#usebuiltins-usage
如果英文文档看不懂的话,中文版献上https://juejin.im/post/5cb9833b6fb9a068a84fe4d0#heading-18
用Vue-cli3.0搭建vue项目的时候,我们会发现有.browserslistrc和babel.config.js文件,那么这两个文件是干嘛的呢?我们先看一下文件里面的内容,举个栗子
.browserslistrc文件内容是这样的
> 1%
ios >= 9
android >= 5
not dead
not op_mini all
babel.config.js文件的内容是这样的
// babel.config.js
module.exports = {
presets: [
['@vue/app', {
polyfills: [
'es6.promise',
'es6.symbol'
]
}]
]
}
看到代码之后,大概心理就有个谱了,Babel 可以通过babel.config.js进行配置。配置babel其实有三种方式,除了创建babel.config.js文件,还可以创建.babelrc 或通过package.json文件中的babel字段进行设置,但是babel.config.js这个配置文件不会使用基于文件位置的方案,而是会一致地运用到项目根目录以下的所有文件,包括node_modules内部的依赖。所以推荐使用babel.config.js对babel进行配置。
上面的babel.config.js文件,我们看到了使用的是@vue/app-->@vue/babel-preset-app,所有的 Vue CLI 应用都使用@vue/babel-preset-app,它包含了 @babel/preset-env、JSX 支持以及为最小化包体积优化过的配置。
在很久以前,配置文件还用.babelrc,其基本格式如下
{
"presets": [],
"plugins": []
}
之前的转码规则有
1.最新的转码规则:babel-preset-latest;
2.react转码规则:babel-preset-react
3.不同阶段语法提案的转码规则(共有4个阶段),任选一个
babel-preset-stage-0
babel-preset-stage-1
babel-preset-stage-2
babel-preset-stage-3
然后要使用其中的一些规则的话,需要在.babelrc文件里面如下配置
{
"presets": [
"lastest",
"react",
"stage-2"
],
"plugins": []
}
但是上面的语法规则,都是有很多冗余,不管语法规则你有没有用到,需不需用,在打包的时候通通给你打包,导致项目会比较大。所以后来Vue-cli3.0推出了babel.config.js来配置babel,它可以配置用@vue/babel-preset-app自动根据.browserslistrc文件列出来的所需要适配的端机,通过@babel/preset-env自动选择提取语法规则,也可以按需要手动添加语法规则。为什么要用到这些语法规则呢?这样,在一些比较旧的端机,也可以运行比较新的语法规则 ,比如ES6的map,weakmap等。
关于@vue/babel-preset-app的定义,在其npm相关文档的第一句就提到
This is the default Babel preset used in all Vue CLI projects. Note: this preset is meant to be used exclusively in projects created via Vue CLI and does not consider external use cases.
用我三流的英文水平翻译,大概是说这个包是VueCli项目的默认Babel预设,注意:这个预设只能用在VueCli脚手架搭建的项目,其他的并不支持。在它的依赖里面,我们可以发现一些很常见的包,比如@babel/preset-env和 core.js等等。
其实说白了,默认的Vue CLI项目使用@vue/babel-preset-app,这个包它使用@babel/preset-env和.browserslistrc配置来确定项目所需的polyfills。@vue/babel-preset-app把@babel/preset-env重新包装了,原来的@babel/preset-env可能在配置的时候有好几个属性,经过包装之后,开发者只需要配置一些项即可
useBuiltIns默认usage,除了默认的还有entry以及false两个选项,而@babel/preset-env默认的useBuiltIns是false,上面有说到@vue/babel-preset-app是把@babel/preset-env重新封装了一遍,所以useBuiltIns也是被重新更改默认了。
@vue/babel-preset-app的polyfills默认以下四项['es6.array.iterator', 'es6.promise', 'es6.object.assign', 'es7.promise.finally']
当useBuiltIns为usage的时候,才会使用到默认的polyfills这个属性,这个时候,@vue/babel-preset-app会根据.browserslistrc以及根据项目源码有使用到的语法去导入所需要的polyfills,但也因为仅是检测项目源码,如果项目使用的 npm 包也需要 polyfill, 必须找到该 npm 包所需要的 polyfill 并修改babel.config.js,@vue/cli v3使用的依然是core-js@2,如果需要查阅每个 polyfill 的模块名字,请参考core-js@2的相关文档 https://github.com/zloirock/core-js/tree/v2。
根据上面所说的,打个比方,.browserslistrc文件里面规定这个项目要兼容chrome 36版本,而且有使用到某种语法比如A,恰巧语法A在chrome 36版本是不兼容的,那么就会引入这个语法的polyfill,但是如果chrome 36及以上版本兼容语法A,那么就没必要导入语法A的polyfill了。
如果在一个项目中,项目里面的.broswerlistrc的配置为ios >= 9 android >= 5 chrome >= 36,然后我们有使用到比较新的语法,比如
var a = {};
Object.entries(a);
那么在usage下,编译出来的,就会有entries这个包
再比如用到下面这种语法
var a = new WeakMap();
那么编译之后会有weak-map这个包
上面四个默认选项是一定会导入的,如果不想使用以上四个默认的选项,那可以自定义重新设置polyfills覆盖默认的。至于为什么会知道四个默认选项一定会引入,可以使用一个插件source-map-explorer,查看打包之后dist文件夹的js的构建状态,安装完之后输入
source-map-explorer .\dist\js\*.js
然后我们可以在构建表里面看到这四个分别在这里
@babel/preset-env文档里面有这句话
上面这段话的意思是说,在你整个app项目里面,只能使用一次import 'core-js';和import 'regenerator-runtime/runtime';,如果你使用了@babel/polyfill,它已经包含了core-js和regenerator-runtime/runtime,导入两次的话会报错,多次导入这些软件包可能会导致全局冲突或一些其他难以跟踪的问题。
如果使用导入@babel/polyfill的方式的话,现在会报错了:
`@babel/polyfill` is deprecated. Please, use required parts of `core-js`
and `regenerator-runtime/runtime` separately
从 Babel 7.4.0 开始,@babel/polyfill这个包已经被废弃了,以支持直接导入 core-js/stable(polyfill ECMAScript 特性)和regenerator-runtime/runtime(需要使用转换后的 generator 函数)。这里的core-js不一定要导入stable,具体视项目而定,点击这里可以查看更多的core-js模式。
如果使用
import "core-js/stable";
import "regenerator-runtime/runtime";
两行代码将会被转换成 browserslist 配置包含的浏览器所需要的全部 polyfill,core-js的全部大小是131.98kb。
如果@babel/polyfill可以使用的话,babel-polyfill 等同于regenerator-runtime+core-js,这个插件是对core-js和regenerator-runtime的再次封装。
不在代码中使用polyfills,当使用ES6+语法及API时,在不支持的环境下会报错。
import "core-js/stable";
import "regenerator-runtime/runtime";
必须在main.js最顶部导入,不然有些代码会漏编译。
答案就是,在npm run build的时候会报一个提示
When setting `useBuiltIns: 'usage'`, polyfills are automatically imported when needed.
Please remove the direct import of `core-js` or use `useBuiltIns: 'entry'` instead.
当然是叫你把core-js去掉或者是使用entry作为useBuiltIns。当然,编译的时候还是按照usage的规则来。
css兼容采用的是postcss-loader + autoprefixer + browserslist作为技术栈,打包的时候根据.browserslistrc对一些比较新的css属性添加前缀,而.browserslistrc是根据caniuse的数据来作为支撑的,但是现在caniuse的数据已经不够准确了,大量的项目实践表明,android >= 5 并不能涵盖所有 Android 5.0 以上的设备,增加 chrome >= 36 可以解决,因为Android 5.0 设备内置 webview 的最低版本为chrome36。所以.browserslistrc如果有android >= 5的话,后面最好再加上chrome >= 36。
可以尝试删除掉node_modules里面的.cache文件,然后构建重试。
项目在build的时候,babel默认不解析转化node_modules的内容,如果node_modules里有es6的的语法,那么在有些不支持ES6一些语法的手机就会报错。我们先来说说为什么脚手架默认不解析转化node_modules依赖的内容,因为一般发布到npm上面的依赖包,都是经过开发者babel解析过的,我们用的时候没有必要再解析一次。那么如果真真遇到上面的情况,解决方法是
1.在vue.config.js文件中对webpack参数重新配置,让babel重新编译node_modules(超级不建议)
ps:这里只是为了引出下面的一些知识点,不建议大家这么做!!!
vue-cli3是在vue.config.js的configureWebpack属性对webpack进行配置,这里直接copy脚手架官网的一段话
如果这个值是一个对象,则会通过 webpack-merge合并到最终的配置中。
如果这个值是一个函数,则会接收被解析的配置作为参数。该函数及可以修改配置并不返回任何东西,也可以返回一个被克隆或合并过的配置版本。
当然也可以采用chainWebpack属性,链式操作的方式修改配置,下面采用此方式。
我们先在终端输入
vue inspect > output.js
输出解析好的 webpack 配置文件。至于其他审查项目的 webpack 配置,请点击这里。
在文件中查找字段“babel-loader”,找到
或者可以直接在终端输入
vue inspect --rule js
直接输出js的编译打包规则。
接下来修改一下js打包编译规则:
至于为什么是这种写法,看此链接也许有所收获:https://github.com/neutrinojs/webpack-chain
2.假设是node_modules里面一个iview包有用到ES6语法,那么可以在main.js里面导入
import 'iview';
这样在build的时候就会被编译到了。