详解Vue Cli浏览器兼容性实践


Posted in Javascript onJune 08, 2020

浏览器市场占有率

在处理浏览器兼容性问题之前,我们先来看一下现在的浏览器市场份额是怎样的,?下面是来自statCounter的数据,基本上覆盖了全世界浏览器市场份额的统计,而且前端er经常使用的caniuse所拉取的浏览器数据就是来自statCounter。

世界范围

详解Vue Cli浏览器兼容性实践

天朝范围

详解Vue Cli浏览器兼容性实践

Plus移动端

详解Vue Cli浏览器兼容性实践

分析

从统计数据可以看出,对于国内的PC端浏览器,QQ浏览器以及Sogou的占比还是挺高的,所以在做兼容性处理的时候也需要多考虑这两款浏览器。不过QQ浏览器和Sogou都是封装了Chrome的内核,而它们的更新通常也就是随着Chrome的版本更新而更新,但是这两款浏览器通常不会和Chrome的最新版本同步,而是会有一定的滞后性,所以通常我们兼容的时候要去考虑它们所使用的Chrome内核版本来处理。

Vue CLI3

简述

随着尤大最新力作-Vue CLI3的发布,团队也将项目的脚手架升级为最新的CLI3了,CLI3带来了很多新的特性,比如支持webpack4、支持可视化化地配置项目、封装了很多官方的插件降低了上手的成本等。其中还有对浏览器兼容性的支持-结合了社区最新的工具以及现代模式。而CLI 3.0的浏览器兼容性处理,主要分为三个部分。browserlist、polyfill以及modern mode(现在模式)。

Broserslist

指定项目的目标浏览器范围,这个值会被@babel/preset-env和Autoprefixer等工具用来确定需要转译的JS特性以及需要添加前缀的CSS特性。

{
 "browserslist": [
  "last 1 version", //表示最新一个版本
 ]
}

Browserslist本质是对一系列浏览器的queries,查询浏览器的版本, 它会从caniuse中拉取数据来查询!它的好处是给予了开发者一个标准的地方存放项目支持的浏览器版本,他们可以在配置中找到这个支持版本。

下面是用法解释

详解Vue Cli浏览器兼容性实践

Transpile

下面要要介绍的是Vue CLI3所用到的Babel预设- @Babel/preset-env,它是在CLI3中指导Babel进行转译的一个工具,说起Babel就不得不提一下转译,和其他语言中的编译不同,转译是类似如下图所示的一个过程:

详解Vue Cli浏览器兼容性实践

总的来说,静态语言的编译比如Java是将源代码编译为字节码,这两种通常是处于不同抽象层次的语言,而Transpile的两端本质上是相同层次上的抽象,它一般是从一种支持更高级特性的比如ES2017的实现,转为一种语言特性较少的比如ES5的实现。在解决浏览器兼容性时,很大程度上是需要依赖Babel转译来解决低版本浏览器不支持某些新特性的情况。

CLI3如何使用browserslist以及babel进行转译

babel是一个编译JS文件的和工具。用于编译新JS新特性到支持的目标浏览器。babel/preset-env从browserlistrc文件中加载目标浏览器:

"babel": {
 "presets": [
  [
   "@babel/preset-env"
  ]
 ]
}

然后babel会把新标准中的JS语法特性编译到当前目标浏览器支持的代码:

const array = [1, 2, 3];
const [first, second] = array;

output:

const array = [1, 2, 3];
const first = array[0],
  second = array[1];

此处的const特性,因为该目标浏览器支持了,所以不需要降级处理。

** Babel的转译可以简单地用以下三个阶段概括: **

  • parser: 通过babel-parser,基于babel-AST-format将JS代码转译成AST。
  • transform: 所有的插件/presets进一步做语法等自定义转译,仍然为AST
  • generator: 最后通过generator生成转译后的字符串。

不同的特性,结合Browserslist以及@Babel/preset-env会根据当前的目标浏览器支持的特性的情况来转译,不支持的特性再去降级处理。Babel本身是个复杂的话题,后续有余力结合编译原理再去深挖一下。

Polyfills

Polyfill通常是在特性检测后,来决定是否需要引入的一段JS代码,它是用基于目标浏览器支持的代码编写的。

if(browserSupportAllFeatures()) {
 main();
} else {
 loadScript('polyfills.js', main);
}

原理默认情况下,它会把useBuiltIns: 'usage' 传递给 babel/preset-env,这样它会根据源代码中出现的语言特性自动检测需要的polyfill。 确保了最终包里的polyfill数量最小化。然而,这也意味着如果其中一个依赖需要特殊的polyfill,默认情况下babel无法检测出来。

// 源代码
var a = new Promise();
// 输出
import 'core-js/modules/es6.promise';
var a = new Promise();

依赖需要Polyfills如果有依赖需要polyfill,你有几种选择:(来自babel-preset-app)

如果该依赖基于一个目标环境不支持的ES版本撰写,将其添加到vue.config.js中的 traspileDependcies选项。这会为该依赖同时开启语法转换和根据使用情况检测polyfill。默认情况下,babel-loader会忽略所有node_modules中的文件。如果想要通过Babel显式转译一个依赖,可以在这选项中列出:

trasnpileDependencies: [lodash,...]

如果该依赖交付了ES5代码并显式列出了需要的polyfill: 你可以使用@vue/babel-preset-app 的polyfills选项 预包含所需要的polyfill。( 注册es6.promise将被默认包含,因为现在的库依赖Promise非常普遍。)

// babel.config.js
module.exports = {
 presets: [
  ['@vue/app', {
   polyfills: [
    'es6.promise',
    'es6.symbol'
   ],
   useBuiltIns: 'entry'
  }]
 ]
}

推荐这种方式添加polyfills而不是在源代码中直接导入它们,因为如果这里列出的polyfill在browserflist的目标不需要,则它会被自动排除。 3. 如果该依赖交付了ES5 代码,但使用了ES6+ 特性且没有显式列出需要的polyfill,请使用useBuiltIns: 'entry' 然后在入口文件添加 import '@babel/polyfill'。 这会根据browserlist 目标导入所有polyfill,这就不需要担心依赖的polyfill问题了, 缺点:但是包含了一些没有用到的polyfill所以最终的包可能会增加。(这也没办法,因为依赖没有显式列出需要的polyfill)

// 源代码
import '@babel/polyfill'
// 输出
import 'core-js/modules/es7.string.pad-start';
import 'core-js/modules/es7.string.pad-end';

Modern mode(现代模式)

什么是现代模式?现代模式是Vue CLI3提出的,为了解决支持更老的浏览器而为它们交付笨重的代码的问题,使用官方所给的脚手架搭建好项目后,可以像下面这样运行:

vue-cli-service build --modern

CLI3内置的Service服务会调用webpack构建编译生成两种类型的包,一种是现代版的包,它不会引入额外的polyfill。 一种是旧版的包,面向不支持的旧版浏览器,它会根据browserslist以及babel-preset-env的配置引入额外的polyfill。

  • 现代的包会通过 <script type="module"> 在被支持的浏览器中加载;它们还会使用 <link rel ="modulepreload"> 进行预加载。
  • 旧版的包通过 <script nomodule> 加载,会被支持ESM的浏览器忽略。

输出对比未使用现代模式:

详解Vue Cli浏览器兼容性实践

Vue CLI 3 现代模式:

详解Vue Cli浏览器兼容性实践

使用了modern的方式,会在目录下生成了两种包:

详解Vue Cli浏览器兼容性实践

解释

<script src="/xx/sxx.js" nomodule></script>

script标签中声明了nomodule特性的,会告诉支持ES2015 Module的浏览器,不要去执行这个脚本。这种标签通常就是在老旧浏览器中使用的,引用的是带有legacy标识的脚本。

<script type="module" src="/xxx/sss/a.js"></script>

当script标签中声明了type特性的值为module时,浏览器就会把里面的代码视作一整个JS模块,脚本内容的处理不受charset和defer属性的影响,代码的行为可能会与不指定为module时表现得不同。 (以前type的值一般是JS MIME值,在符合HTML5的浏览器,表示脚本为JS。但现在H5规范要求开发者省略这个属性而不提供冗余的MIME类型。)

小结

Vue CLI 3提供了一整套的解决方案,里面提供了社区以及Vue官方的工具,包含Browserslist、Babel、@Babel/preset-env以及现代模式来共同处理浏览器兼容性这个大坑。CLI3构建出的工程会利用Browserslist去进行浏览器查询(包括类型和版本),然后结合预设对Babel进行配置共同来针对目标环境对源代码进行转译或引入Polyfills来解决兼容性问题。Moreover,现代模式同样会根据这些设定以及目标环境去构建出适合运行在支持项目所有新特性的浏览器以及不支持特性的浏览器的两种包。最后,预告一下,下一篇文章会结合Modernizr和CLI3来进行实践 ---- 《浏览器兼容性实践-Mondernizr篇》

Reference

Stat Counter

浏览器对H5、CSS3特性支持

目标浏览器配置表

browserlist

Vue CLI3

Babel

Babel AST format

到此这篇关于详解Vue Cli浏览器兼容性实践的文章就介绍到这了,更多相关Vue Cli浏览器兼容性内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
基于jQuery UI CSS Framework开发Widget的经验
Aug 21 Javascript
jquery在Chrome下获取图片的长宽问题解决
Mar 20 Javascript
js实现日期级联效果
Jan 23 Javascript
javascript 判断整数方法分享
Dec 16 Javascript
JS实现文件动态顺序载入的方法
Mar 07 Javascript
JavaScript将当前时间转换成UTC标准时间的方法
Apr 06 Javascript
jQuery控制元素显示、隐藏、切换、滑动的方法总结
Apr 16 Javascript
JQuery 的跨域方法推荐_可跨任何网站
May 18 Javascript
jQuery 点击获取验证码按钮及倒计时功能
Sep 20 jQuery
JavaScript常用数组操作方法,包含ES6方法
May 10 Javascript
JavaScript定时器设置、使用与倒计时案例详解
Jul 08 Javascript
Js实现复选框的全选、全不选反选功能代码实例
Feb 28 Javascript
解决微信授权成功后点击按返回键出现空白页和报错的问题
Jun 08 #Javascript
微信h5静默和非静默授权获取用户openId的方法和步骤
Jun 08 #Javascript
基于javascript处理二进制图片流过程详解
Jun 08 #Javascript
vue-router的hooks用法详解
Jun 08 #Javascript
Vue自定义render统一项目组弹框功能
Jun 07 #Javascript
用云开发Cloudbase实现小程序多图片内容安全监测的代码详解
Jun 07 #Javascript
Electron整合React使用搭建开发环境的步骤详解
Jun 07 #Javascript
You might like
上传多个文件的PHP脚本
2006/11/26 PHP
phpmyadmin中禁止外网使用的方法
2014/11/04 PHP
php查询ip所在地的方法
2014/12/05 PHP
php验证手机号码
2015/11/11 PHP
基于jquery tab切换(防止页面刷新)
2012/05/23 Javascript
jquery 多行文本框(textarea)高度变化
2013/07/03 Javascript
Jquery原生态实现表格header头随滚动条滚动而滚动
2014/03/18 Javascript
node.js操作mysql(增删改查)
2015/07/24 Javascript
基于JavaScript实现通用tab选项卡(通用性强)
2016/01/07 Javascript
Javascript中级语法快速入手
2016/07/30 Javascript
JavaScript获取中英文混合字符串长度的方法示例
2017/02/04 Javascript
解决JS内存泄露之js对象和dom对象互相引用问题
2017/06/25 Javascript
js实现京东轮播图效果
2017/06/30 Javascript
超级简易的JS计算器实例讲解(实现加减乘除)
2017/08/08 Javascript
axios post提交formdata的实例
2018/03/16 Javascript
Node.js中你不可不精的Stream(流)
2018/06/08 Javascript
Vue中实现权限控制的方法示例
2019/06/07 Javascript
[03:37]2014DOTA2国际邀请赛 主赛事第一日胜者组TOPPLAY
2014/07/19 DOTA
Python实现的HTTP并发测试完整示例
2020/04/23 Python
解决Python 遍历字典时删除元素报异常的问题
2016/09/11 Python
机器学习python实战之手写数字识别
2017/11/01 Python
python+opencv实现动态物体识别
2018/01/09 Python
对pandas将dataframe中某列按照条件赋值的实例讲解
2018/11/29 Python
Python 实现文件打包、上传与校验的方法
2019/02/13 Python
Python中那些 Pythonic的写法详解
2019/07/02 Python
python使用socket 先读取长度,在读取报文内容示例
2019/09/26 Python
Python环境使用OpenCV检测人脸实现教程
2020/10/19 Python
Expedia印度:您的一站式在线旅游网站
2017/08/24 全球购物
英国在线发型和美容产品商店:Beauty Cutie
2019/04/27 全球购物
会计专业毕业生推荐信
2013/11/05 职场文书
爱与责任演讲稿
2014/05/20 职场文书
毕业生就业推荐表导师评语
2014/12/31 职场文书
小学生学习保证书
2015/02/26 职场文书
2015年法律事务部工作总结
2015/07/27 职场文书
2016年班主任培训心得体会
2016/01/07 职场文书
2016年质量月活动总结报告
2016/04/05 职场文书