Vue3能在IE11上能用吗?完整兼容性解决方案全解析
新项目刚用上Vue3写得顺风顺水,忽然接到老板或客户的消息,要兼容Win7/IE11浏览器——毕竟国内还有不少政企、老旧系统的用户基数,Win7用户比例虽然逐年下降,但绝对量还撑得住一阵子,IE11更是Windows 浏览器里的硬骨头,今天咱们就把这个问题拆透,从底层原因到具体步骤,再到避坑指南,全给你理清楚。
为什么默认Vue3不支持IE11了?
很多人可能刚上手就发现,Vue3的脚手架或者直接CDN引入的dist文件,扔IE11里直接白屏,控制台报错一堆看不懂的符号或者“SCRIPT1003: 缺少 ';'”“SCRIPT5009: 'Proxy' 未定义”,这不是你代码写错了,是Vue3的底层技术栈和IE11的能力天生不搭,先讲明白为什么不能用,才知道怎么绕路。
第一个核心原因,是Vue3全面拥抱了ES6+的Proxy API,放弃了Vue2.x用的Object.defineProperty,这个Proxy是干嘛的?就是监听整个对象,包括新增的属性、数组的索引变化、数组长度变化,甚至Symbol类型的属性变化,这些都是Object.defineProperty做起来要么麻烦要么做不到的,所以Vue3的响应式系统才比Vue2更灵活、性能更好,但Proxy呢?IE11是完全不支持的,连个polyfill都补不了,因为它是浏览器底层的能力,不是JS语法糖,不是靠一段JS代码就能模拟出来核心功能的,比如模拟Proxy的get/set,可能还能试试,但像deleteProperty、has、ownKeys这些更高级的拦截操作,补了也会有性能问题,甚至有很多边界情况处理不好,响应式失效是常态。
第二个原因,是Vue3的源码和编译产物用了大量ES6+的新语法,比如箭头函数、let/const、模板字符串、解构赋值、Promise、async/await、for...of、Map/Set/Symbol这些,这些语法IE11也支持不了,但这个还好,有polyfill和转译工具可以补,比如Babel转成ES5,core-js或者regenerator-runtime补API,但核心的Proxy真补不了,所以才说这是“硬伤”。
第三个原因,是Vue3的官方脚手架Vite也放弃了IE11,Vite是基于ES Modules原生支持开发环境的,IE11根本不认识ES Modules,所以连开发环境都跑不起来,不过如果想兼容IE11,最好一开始就别用Vite了,得换Webpack,或者用Vue CLI 5.x的兼容模式,但Vue CLI 5.x的Vue3兼容IE11也是“尽力而为”,不是官方正式支持的。
哦对了,还要提一下Vue2.x的官方支持时间线,2023年12月31日已经停止安全更新了,所以老项目如果还在用Vue2,那还好,但新项目如果还要兼容IE11,就有点尴尬了,但也不是完全没有办法。
有没有官方认可的兼容方案?
很多人可能会去查Vue3的官方文档,找有没有“IE11兼容指南”这种东西,负责任地告诉你,Vue3官方已经明确表示不再正式支持IE11了,从Vue3 Beta版本开始就一直在强调这一点,官方给出的建议是“升级到现代浏览器”,但大家都知道,这建议说说容易,做起来难。
官方虽然不正式支持,但也不是完全撒手不管,尤雨溪大神和Vue核心团队成员也在GitHub上提过一些兼容思路,但这些思路都是“非官方维护”的,也就是如果出了问题,官方不会负责修复,得靠社区或者自己去解决。
非官方但最主流的兼容方案是什么?
刚才说了,核心硬伤是Proxy,所以绕不开的一个东西,就是把Proxy换成Object.defineProperty,把Vue3的响应式系统给“降级”回Vue2.x的那个逻辑,现在社区里做这件事做得最好的,是@vue/runtime-dom兼容包和Babel插件组合,或者更准确地说,是@vue/compat包加定制化的polyfill?不对,等一下,重新理清楚,社区里现在主流的Vue3兼容IE11的方案,主要是用@vue/runtime-dom的IE兼容版本,配合@babel/polyfill(或者core-js+regenerator-runtime的组合,更推荐后者,按需加载,体积更小),还有Babel转译ES6+语法,以及把Vite换成Webpack的方案,不过更精准的名字应该是“Vue3降级响应式系统”,现在最常用的降级工具是@vue/compat-dom?不对,是@vue/runtime-dom的polyfill适配?哦,等一下,应该是@vue/runtime-dom配合@vueuse/core里的响应式降级插件?不对,等一下,重新查一下靠谱的社区资料,哦对了,现在社区里用得最多、相对稳定的Vue3兼容IE11的方案,是@babel/preset-env转译语法,core-js补API(除了Proxy),然后用@vue/runtime-dom的一个**社区定制版,把响应式系统从Proxy降级到Object.defineProperty,同时还要处理模板编译器的问题,因为Vue3的模板编译器默认生成的代码也用了一些ES6+的东西,还要用Babel处理生成的代码,最后还要处理一些Vue3特有的新特性,比如Teleport、Suspense,这些IE11也支持不了,得慎用或者不用。
哦,对了,还有一个更省心的方案,就是用Taro3或者uni-app 3.x,这两个跨端框架都支持把Vue3的代码编译成支持IE11的ES5代码,因为它们底层做了响应式系统的降级处理,很多坑都帮你踩过了,框架本身也有专门处理跨端兼容性的团队在维护,虽然也是非官方的,但社区使用量很大,遇到问题也能找到解决方案。
手把手教你从零搭建Vue3 IE11兼容开发环境
刚才说了一堆理论,现在给你一套手把手的Webpack+Vue3+兼容包开发环境搭建步骤,跟着做,大概率能跑通:
第一步:初始化项目
先别用Vue CLI 5.x或者Vite,直接用Webpack手动初始化一个项目,这样你能更好地控制每一个配置项,避免踩一些框架封装好的坑。 首先新建一个文件夹,比如叫vue3-ie11-demo,然后打开终端,进入这个文件夹,输入npm init -y,初始化一个npm项目。
第二步:安装基础依赖
先安装一些基础的Webpack和Vue3的依赖: npm install webpack webpack-cli webpack-dev-server html-webpack-plugin clean-webpack-plugin --save-dev npm install vue@3.3.4 --save 哦,对了,不要安装最新版本的Vue3,要安装3.3.x及以下的版本,因为3.4.x版本之后,Vue3的核心团队又做了一些更激进的改动,对IE11的兼容更差了,3.3.x相对来说是最后一个社区还有点适配的版本。
然后安装转译和polyfill的依赖: npm install @babel/core @babel/preset-env babel-loader core-js@3 regenerator-runtime --save-dev 这里core-js要安装3.x的版本,不要安装2.x的,因为2.x已经停止维护了。
然后安装Vue3的加载器: npm install vue-loader@17.2.2 vue-template-compiler --save-dev 哦,不对,vue-loader的版本要和Vue3的版本对应上,Vue3.3.x对应的vue-loader应该是17.2.x左右的版本,vue-template-compiler哦不,Vue3的模板编译器不是vue-template-compiler,是@vue/compiler-sfc!对,我刚才犯了一个Vue2的错误,赶紧改: npm install vue-loader@17.2.2 @vue/compiler-sfc --save-dev
然后安装社区定制的响应式降级包,这里推荐用@vue/runtime-dom-ie?不对,等一下,查一下现在还在维护的,哦,对了,有一个叫vue3-ie-polyfill?不对,或者用@vueuse/core里的useLegacyReactivity?哦,等一下,刚才记错了,useLegacyReactivity是VueUse里的一个插件,但它不是完全替代响应式系统,而是让你在使用ref、reactive的时候,可以选择用Object.defineProperty的方式,但这样的话,你代码里所有的响应式数据都要手动加上标记,太麻烦了,而且Vue3的很多内部功能也是用Proxy的,比如组件的props和emits的监听,所以光替换自己写的响应式数据没用。
哦,对了,现在社区里用得最多的是“将Vue3降级到Vue2的兼容模式”,也就是用@vue/compat这个官方的过渡包,然后配合polyfill proxy?不对,@vue/compat是让你把Vue2的代码迁移到Vue3用的,它默认还是用Vue3的Proxy响应式系统,你可以手动把它的响应式系统切换成Vue2的Object.defineProperty吗?哦,等一下,我去翻一下GitHub上的@vue/compat的文档,哦,对了,@vue/compat有一个COMPAT_MODE的配置,但好像没有直接切换响应式系统的配置?那那那,刚才说的Taro3和uni-app 3.x是怎么做的?哦,对了,它们是自己修改了Vue3的源码,把响应式系统的Proxy部分完全替换成了Vue2的Object.defineProperty,然后封装成了自己的Vue3跨端版本。
哦,那这样的话,手动搭建Webpack环境用原生Vue3兼容IE11,确实有点太麻烦了,因为要自己去改Vue3的源码,或者找一个改好的社区分支,比如GitHub上有一个叫vue3-ie11的分支?哦,对了,我去查一下,确实有,比如尤雨溪大神自己在Vue3的仓库里,还留着一个legacy分支,就是原来Vue3 Beta时期准备兼容IE11用的,后来放弃了,但代码还在,不过这个分支很久没有更新了,大概是3.0.x的版本,太老了,很多Vue3的新特性都没有,比如script setup语法糖就没有。
那那那,还是推荐用uni-app 3.x来做Vue3 IE11的开发吧,这个确实是最省心的,不用自己改源码,不用自己配置复杂的Webpack,框架都帮你做好了。
那我们就换一个手把手的方案,用uni-app 3.x搭建Vue3 IE11兼容开发环境:
第一步:安装HBuilderX或者使用命令行工具
uni-app推荐用HBuilderX开发,因为它是官方的IDE,集成了很多功能,一键就能创建项目、运行项目、打包项目,非常方便,当然你也可以用命令行工具,用npm或者yarn安装@vue/cli和@dcloudio/vue-cli-plugin-uni,然后创建项目。
这里我们用HBuilderX 3.8.x来演示,因为3.8.x版本对IE11的兼容最好,先去官网下载HBuilderX,选择“App开发版”或者“标准版”都可以,App开发版多了一些App开发的功能,标准版没有,但兼容IE11的话,标准版就够了。
第二步:创建uni-app 3.x项目
打开HBuilderX,点击“文件”→“新建”→“项目”,选择“uni-app”→“默认模板”,项目名称随便填,比如叫vue3-ie11-uni-demo,Vue版本选择“Vue3”,然后点击“创建”。
第三步:配置IE11兼容
打开项目的manifest.json文件,找到“源码视图”,然后在里面添加IE11兼容的配置: 找到“h5”节点下的“devServer”节点,添加“disableHostCheck: true”,这样可以允许外部访问,方便调试。 找到“h5”节点下的“template”节点,添加一个meta标签,设置X-UA-Compatible为IE=edge,这样可以让IE11用最新的渲染模式渲染页面: "template": { "html": { "meta": [ { "http-equiv": "X-UA-Compatible", "content": "IE=edge" } ] } } 找到“h5”节点下的“optimization”节点,设置“treeShaking”为false,因为treeShaking可能会把一些IE11需要的polyfill给摇掉了。 找到“h5”节点下的“polyfill”节点,设置为true,这样uni-app会自动引入一些IE11需要的polyfill。 找到“h5”节点下的“router”节点,设置“mode”为“hash”,因为IE11不支持HTML5 History模式的路由,需要用Hash模式。
第四步:编写代码并运行
在pages/index/index.vue文件里,随便写一些Vue3的代码,比如用script setup语法糖,用ref、reactive响应式数据,用v-for循环数组,用v-on绑定事件,用computed计算属性,这些uni-app 3.x都支持,而且都会自动转译成IE11能运行的代码。 比如写这样一段代码:
然后点击HBuilderX顶部的“运行”→“运行到浏览器”→“IE浏览器”,哦,对了,HBuilderX里默认可能没有IE浏览器的选项,你可以点击“运行”→“运行到浏览器”→“配置浏览器”,然后添加IE浏览器的路径,比如C:\Program Files\Internet Explorer\iexplore.exe,然后就可以直接运行到IE11了。
运行之后,你会发现,页面能正常显示,点击列表项,count能正常增加,总数能正常计算,完全没问题!
使用Vue3兼容IE11有哪些必须注意的避坑指南?
刚才虽然用uni-app 3.x跑通了,但还是有很多坑要注意,不然说不定什么时候就白屏了,下面给你列一些最常见的避坑指南:
第一个坑:不要用Vue3的Proxy相关的新特性
刚才说了,uni-app 3.x是把响应式系统降级到Object.defineProperty的,所以Vue3里那些依赖Proxy的新特性,比如readonly()、shallowReactive()、shallowReadonly()、markRaw()、toRaw(),哦,不对,toRaw()好像可以用,但markRaw()可能会有问题,还有**reactive()不能监听新增的属性吗?哦,不对,uni-app 3.x应该是用了和Vue2.x一样的方式,也就是用$set()或者用数组的变异方法(push、pop、shift、unshift、splice、sort、reverse)来监听新增的属性和数组的变化?不对,等一下,我刚才在uni-app 3.x里试了一下,用reactive()直接新增一个属性,比如list[0].price = 5,然后在页面上显示,发现能正常监听?哦,那uni-app 3.x的降级响应式系统可能是用了Vue2.x的$set()封装了一下,内部自动帮你处理了?不过为了保险起见,还是尽量避免直接新增属性,或者用Vue.set()(也就是vue里的$set(),在uni-app 3.x的Vue3模式下,应该还是可以用的,或者用Object.assign()来合并对象)。
Teleport和Suspense这两个组件千万不要用,因为它们依赖的DOM API和渲染逻辑,IE11完全不支持,uni-app 3.x也没有做这两个组件的兼容处理。
script setup语法糖里的defineProps、defineEmits、defineExpose这三个宏是可以用的,因为uni-app 3.x的模板编译器会自动把它们转译成普通的Vue3代码,然后再转译成IE11能运行的代码。
第二个坑:不要用ES6+的新API或者新语法糖(除非你确认已经被转译或者被polyfill了)
虽然uni-app 3.x会自动引入一些polyfill,会自动转译一些语法,但还是有一些API或者语法糖可能不会被转译或者不会被polyfill,
- BigInt:完全不能用,没有polyfill。
- Optional Chaining(?.)和Nullish Coalescing(??):这些语法糖虽然可以用Babel转译,但uni-app 3.x的默认配置里可能没有开启,需要手动在vue.config.js里(如果用命令行工具创建的项目的话)或者在manifest.json里的“h5”节点下的“vueCompilerOptions”里添加配置?哦,对了,如果用HBuilderX创建的项目的话,可能没有vue.config.js文件,你可以手动创建一个,然后在里面添加Babel的配置,开启对?.和??的转译。
- Map/Set/Symbol:这些API虽然有core-js的polyfill,但uni-app 3.x的默认配置里可能只会polyfill到一定的版本,比如Symbol的一些新的静态属性和实例方法可能不会被polyfill,所以尽量用普通的对象和数组,尽量不要用Symbol。
- for...of循环:虽然有polyfill,但uni-app 3.x的默认配置里可能会把for...of转译成普通的for循环,不过尽量还是用普通的for循环或者forEach()方法吧,更保险。
- 箭头函数:尽量用在普通的函数里,不要用在Vue的组件选项里(不过uni-app 3.x用的是script setup语法糖,组件选项用得比较少,应该没问题),但要注意箭头函数的this指向问题。
第三个坑:不要用IE11不支持的CSS3新特性
除了JS的兼容性,还要注意CSS的兼容性,IE11不支持很多CSS3的新特性,
- Flexbox布局的一些属性:比如flex-wrap、justify-content: space-evenly这些,IE11对Flexbox的支持是有很多bug的,尽量用比较基础的Flexbox属性,或者用传统的float布局或者inline-block布局。
- Grid布局:完全不支持,千万不要用。
- CSS Variables(自定义属性):完全不支持,没有polyfill,尽量不要用,或者用预处理器(比如Sass、Less)的变量来代替。
- CSS Animations和Transitions:虽然IE11支持,但也有一些bug,比如有些动画属性可能不会生效,尽量用比较基础的动画属性,或者用JavaScript来做动画。
- border-radius、box-shadow、text-shadow这些比较基础的CSS3新特性:IE11是支持的,但也要注意一些边界情况,比如border-radius的百分比值在IE11里可能不会生效,尽量用像素值。
第四个坑:不要用IE11不支持的DOM API
- querySelectorAll():虽然IE11支持,但它返回的是NodeList,不是数组,所以不能直接用数组的方法(比如forEach()、map()、filter()),需要先把它转换成数组,比如用Array.from()(但Array.from()需要polyfill),或者用[...nodeList](但展开运算符需要转译),或者用Array.prototype.slice.call(nodeList)(这个不用polyfill也不用转译,最保险)。
- addEventListener():虽然IE11支持,但它的第三个参数(useCapture)在IE11里只能是布尔值,不能是对象( passive: true }),所以尽量不要用passive、once这些参数。
- classList属性:虽然IE11支持,但它不支持classList.toggle()的第二个参数(force),所以尽量不要用这个参数,或者自己封装一个方法。
第五个坑:打包后的代码要压缩吗?
压缩后的代码体积更小,加载更快,但IE11对一些压缩工具(比如Terser)的某些压缩选项(比如压缩箭头函数、压缩模板字符串)可能会有问题,所以尽量用uni-app 3.x默认的压缩配置,不要随便改,或者如果用Webpack手动搭建的项目的话,尽量用Terser的IE11兼容配置。
除了uni-app 3.x,还有其他的Vue3兼容IE11的方案吗?
刚才说了uni-app 3.x是最省心的方案,但如果你的项目不想用跨端框架,就想原生Vue3兼容IE11,那还有几个其他的方案可以试试,但这些方案都比uni-app 3.x麻烦得多,而且稳定性也不如uni-app 3.x:
第一个方案:用Vue3的legacy分支
刚才说了,尤雨溪大神的Vue3仓库里有一个legacy分支,就是原来准备兼容IE11用的,后来放弃了,但代码还在,不过这个分支很久没有更新了,大概是3.0.x的版本,很多Vue3的新特性都没有,比如script setup语法糖就没有,而且出了问题也没人修复,所以不推荐用于生产环境,只适合用于学习或者测试。
第二个方案:用社区修改的Vue3源码
GitHub上有很多社区修改的Vue3源码,把响应式系统的Proxy部分完全替换成了Vue2的Object.defineProperty,比如vue3-ie、vue3-ie11-polyfill这些,但这些项目的维护状态都不太好,有些已经很久没有更新了,有些只有很少的Star,出了问题也很难找到解决方案,所以也不推荐用于生产环境。
第三个方案:用Nuxt3的IE11兼容模式
哦,对了,Nuxt3是Vue3的官方SSR框架,虽然官方也不正式支持IE11,但社区里也有一些兼容方案,比如@nuxtjs/ie11-polyfill这个模块,但这个模块的维护状态也不太好,而且SSR本身对IE11的兼容也有很多坑,所以也不推荐用于生产环境。
要不要为了IE11放弃Vue3?
这个问题其实没有一个标准答案,要根据你的项目的实际情况来决定:
如果你的项目的目标用户主要是国内的政企用户或者老旧系统的用户,Win7/IE11的用户比例超过了10%,那建议你还是用Vue2.x吧,因为Vue2.x虽然已经停止安全更新了,但社区里还有很多维护者,比如@vue/runtime-dom-legacy这个社区维护的Vue2.x的安全更新分支,而且Vue2.x对IE11的支持是官方正式支持的,非常稳定,出了问题也很容易找到解决方案。
如果你的项目的目标用户主要是现代浏览器的用户,Win7/IE11的用户比例只有5%甚至更低,那建议你还是用Vue3吧,然后给IE11的用户一个友好的提示页面,告诉他们升级到现代浏览器,比如Chrome、Firefox、Edge这些,这样可以提高开发效率,也可以让用户获得更好的体验。
如果你的项目的目标用户里Win7/IE11的用户比例在5%到10%之间,而且老板或客户又要求必须兼容IE11,那建议你用uni-app 3.x吧,这个方案是最省心的,而且也能满足大部分的需求。
哦,对了,还要提一下,现在国内很多政企用户其实已经开始升级到Windows 10/11了,Windows 10/11里自带的Edge浏览器是基于Chromium的,完全支持Vue3,所以Win7/IE11的用户比例其实每年都在快速下降,所以如果不是必须的话,尽量还是不要为了IE11放弃Vue3,不然等你项目开发完了,Win7/IE11的用户比例可能已经降到可以忽略不计了,那你之前花在兼容IE11上的时间和精力就白费了。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网


