Vue兼容ie9的问题全面解决方案
前言
背景情况
- vue-2.5.11
- vue-cli使用模板webpack-simple
- http请求:axios
Vue官方对于ie浏览器版本兼容情况的描述是ie9+,即是ie9及更高的版本。经过测试,Vue的核心框架vuejs本身,以及生态的官方核心插件(VueRouter、Vuex等)均可以在ie9上正常使用。
Vue的作者尤雨溪对于Vue的学习建议中有提及为了将项目更好的生态化/工程化,要尽可能学习及使用新的ECMAScript规范。目前ES6/ES2015是可用度和稳定度较高的规范,文档齐全,国内还有阮一峰《ECMAScript6入门》做了大量的文档翻译,开发环境可谓完善。然而版本较旧的浏览器并不支持es6规范,尤其是ie浏览器,即使是最高的ie11版本,对于es6规范也支持得并不全。如此则需要对所有原生不支持ES6特性的浏览器做兼容性处理。
本文将针对使用Vue生态开发完成的网站,以ie9版本为基础兼容目标,实现全功能正常使用的全面兼容解决方案。
ES6兼容
在ie9的环境上,es6的部分新对象、表达式,并不支持,解决方案是使用babel-polyfill组件,它可以将es6的代码翻译成低版本浏览器可以识别的es5代码
npmibabel-polyfill--save-dev
安装完成后,在项目的主入口文件main.js的首行就可以直接引用
import'babel-polyfill';
在项目使用vue-cli生成的代码中,根目录有一个.babelrc文件,这是项目使用babel的配置文件。在默认生成的模板内容中,增加"useBuiltIns":"entry"的设置内容,这是一个指定哪些内容需要被polyfill(兼容)的设置
useBuiltIns有三个设置选项
- false-不做任何操作
- entry-根据浏览器版本的支持,将polyfill需求拆分引入,仅引入有浏览器不支持的polyfill
- usage-检测代码中ES6/7/8等的使用情况,仅仅加载代码中用到的polyfill
这里推荐设置为entry,完整的.babelrc内容如下:
{ "presets":[ [ "env", { "modules":false, "useBuiltIns":"entry" } ], "stage-3" ] }
加入这些代码后,工程里的大部分内容已可兼容到ie9版本
Number对象
即使在使用babel-polyfill做代码翻译后,发现还是有一些es6的新特性并没有解决,比如说Number对象的parseInt和parseFloat方法
es6将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。这样做的目的,是逐步减少全局性方法,使得语言逐步模块化。
解决这个问题不需要引入包来解决,同样在项目主入口文件main.js加入以下代码(代码尽可能靠前,最好是在引用babel-polyfill之后)
if(Number.parseInt===undefined)Number.parseInt=window.parseInt; if(Number.parseFloat===undefined)Number.parseFloat=window.parseFloat;
requestAnimationFrame方法
window.requestAnimationFrame是浏览器用于定时循环操作的一个接口,类似于setTimeout,主要用途是按帧对网页进行重绘。
requestAnimationFrame的优势,在于充分利用显示器的刷新机制,比较节省系统资源。显示器有固定的刷新频率(60Hz或75Hz),也就是说,每秒最多只能重绘60次或75次,requestAnimationFrame的基本思想就是与这个刷新频率保持同步,利用这个刷新频率进行页面重绘。此外,使用这个API,一旦页面不处于浏览器的当前标签,就会自动停止刷新。这就节省了CPU、GPU和电力。
不过有一点需要注意,requestAnimationFrame是在主线程上完成。这意味着,如果主线程非常繁忙,requestAnimationFrame的动画效果会大打折扣。
window.requestAnimationFrame()方法告诉浏览器您希望执行动画并请求浏览器在下一次重绘之前调用指定的函数来更新动画。该方法使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。
有部分第三方组件就使用了这个方法,例如部分文件上传、图片处理类的组件;那么在这类型的组件在ie9下使用时,会报出
SCRIPT5007:Expectedobject.
window.requestAnimationFrame()的最低兼容ie版本为10,那么在ie9上做兼容就需要制作requestAnimationFramepolyfill
(function(){ varlastTime=0; varvendors=['ms','moz','webkit','o']; for(varx=0;xGist:requestAnimationFramepolyfill
这部分代码同样是尽可能在网站入口处就执行
http网络请求(跨域)
在大多数的Web项目中(以JavaWeb为例),网站的页面和服务(至少是controller层)在同一个工程进行开发和部署,在大前端的新型模式下,我们建议尽可能对网站的前端和后端进行完全分离,前后端分离的好处和意义这里不再赘述。
既然是前后端分离,那么部署也必然是各自独立部署,不同的访问路径,就会产生跨域访问的问题(同一站点,不同端口号也是跨域)
在此设定背景情况:
- 服务端已完整开启CROS跨域支持
- http组件使用axios
- axios设置withCredentials为true开启跨域访问时携带cookie数据
高版本浏览器(ie10+或chrome,ff)仅需要完成背景情况中的功能,即可支持跨域数据请求功能
axios进行数据请求时,默认使用XMLHttpRequest对象,在检测到当前请求是跨域访问时,axios会测试浏览器是否支持XDomainRequest对象,若支持则优先使用。
ie8/ie9的XMLHttpRequest对象,不支持跨域访问,该对象在ie10后才原生支持跨域访问。微软的解决方案是在ie8/ie9中提供了XDomainRequest(XDR)对象来进行解决跨域问题,虽然使用该对象可以跨域访问成功,并返回数据,但它却依然是一个功能不完整的半成品,它的使用有诸多限制:
- XDR仅支持GET与POST两种请求方式
- XDR不支持自定义的请求头,若服务端使用header的自定义参数进行做身份验证,则不可用
- 请求头的Content-Type只允许设置为text/plain
- XDR不允许跨协议的请求,如果网页在HTTP协议下,就只能请求HTTP协议下的接口,不能访问HTTPS接口
- XDR只接受HTTP/HTTPS的请求
- 发起请求的时候,不会携带authentication或cookies
微软虽然提供了解决方案,但却是不折不扣的鸡肋,根本无法胜任系统中各种场景的数据请求需求,至此,axios对ie9的跨域数据请求已无能为力。
完美解决方案:代理(proxy)
虽然axios对ie9跨域已无能为力,但前端项目打包的解决方案webpack提供了一个优雅而彻底解决问题的方式:代理
devServer.proxy
webpack的devServer.proxy的功能是由http-proxy-middleware项目来实现的
实现原理是将目标位置的请求代理为前端服务本地的请求,既然是代理成为本地的请求,就不存在跨域的问题,axios就会用回XMLHttpRequest对象进行数据请求,一切都恢复正常了,header、cookies、content-type、authentication等内容都被正确传递到服务端。
项目中webpack.config.js的配置
devServer:{ historyApiFallback:true, noInfo:true, overlay:true, proxy:{ '/api':{ target:'http://localhost:8081/myserver', pathRewrite:{ '^/api':'' } } } }配置中指定了将http://localhost:8081/myserver服务的位置代理为本地前端服务的http://localhost:8080/api。例如需要读取用户信息的原请求是http://localhost:8081/myserver/user/zhangsan,代理后,就变为http://localhost:8080/api/user/zhangsan。
即是/api的前缀代表了服务端,所以在使用axios时,需要对每个服务端请求都增加上/api的前缀;通常在项目开发中,需要对数据请求组件axios进行二次封装,以达到统一设置默认参数,统一数据请求入口等目的,那么此时就只需要在二次封装的文件里统一调整请求前缀即可。
不过,webpack的devServer.proxy仅在开发模式下可用,生产模式下无法使用。开发模式下,调试服务可以读取webpack.config.js中的配置内容进行实时代理,而项目在部署到生产环境前,需要将工程进行编译转换成静态的js文件,没有调试服务的支撑自然是无法进行请求代理的。
nginx配置
虽然devServer.proxy的功能仅能工作于开发模式,那么在生产模式下,自然也是有解决方案的;通常Vue的项目在编译成最终的js文件后,仅需要静态服务器即可,这其中又以nginx为最优选择方案,轻量、高性能、高并发、反向代理服务等均为其优点,这里需要做的数据请求代理的功能就使用到了nginx的反向代理功能
conf/nginx.conf文件配置增加以下内容
location/api/{ proxy_passhttp://localhost:8081/myserver/; }该配置同样是将http://localhost:8081/myserver/的目标服务端位置代理为本地服务的/api路径,如此,生产环境下的数据请求问题也得以解决
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。