UNI-APP相关笔记 - `Uncaught TypeError: t.$el.prepend is not a function`
- 200318 -
Uncaught TypeError: t.$el.prepend is not a function
- 问题场景还原
- 解决方案
- 实验方案6 - 使用最新版本的
@Babel
来转换试试 - 实验方案5 - 修改全局的的
uniapp-cli
的browserslist
兼容性配置 - 实验方案4 - 研究
@financial-times/js-features-analyser
源码寻找原因 - 实验方案3 - 研究
Polyfill.io - create-polyfill-service-url
源码寻找原因 - 实验方案2 - 结合
有效方案1
+实验方案1
- 实验方案1 - 使用
Polyfill.io - create-polyfill-service-url
分析看看 - 有效方案1 - 使用
Polyfill.io - url-builder
直接选择prepend
关键字
- 实验方案6 - 使用最新版本的
- 参考资料
200318 - Uncaught TypeError: t.$el.prepend is not a function
问题场景还原
-
index.vue
里使用<picker>
来选取日期 -
在Chrome 44 里测试
-
显示picker之后关闭它
-
此时console里就出现了这个错误.
-
发生错误的文件为:
webpack:///./node_modules/@dcloudio/uni-h5/dist/index.umd.min.js?1c31
-
发生错误的代码行:
_close: function() { var t = this; this.visible = !1, setTimeout((function() { var e = t.$refs.picker; e.remove(), t.$el.prepend(e), e.style.display = "none" } ), 260) }
解决方案
- 从使用体验上来说,没有受到肉眼可见的影响,所以理论上可以暂不处理.
实验方案6 - 使用最新版本的@Babel
来转换试试
-
从源码
HBuilderX\Bin\plugins\uniapp-cli\node_modules\@dcloudio\vue-cli-plugin-uni\lib\h5\index.js
得知useBuiltIns === 'entry' ? `import '@babel/polyfill'
-
从@babel/polyfill · Babel 中文网 得知
polyfill = import "core-js/stable";+import "regenerator-runtime/runtime";
-
从项目运行时调试可知引用的类型为
@babel/core-js
查询`"HBuilderX\Bin\plugins\uniapp-cli\package.json"`可知当前版本为 "core-js": { "version": "2.6.1",
-
在线搜索 zloirock/core-js: Standard Library 关键字
prepend
没有任何匹配结果
-
结论:
@babel/core-js
无论新3旧0版本都不包含prepend
的转换可能是因为它是实验性特性功能?
实验方案5 - 修改全局的的uniapp-cli
的browserslist
兼容性配置
-
- Android 4.4.4 不支持
- Chrome 53 不支持
- 实验性功能
-
"HBuilderX\Bin\plugins\uniapp-cli\package.json"
参考: [解决方案5:[√有效的 最终采用的方案]](#解决方案5:[√有效的 最终采用的方案])
"browserslist": [ "Chrome >= 53" ] "browserslist": [ "Android >= 4" ]
-
无论怎么修改都无法解决问题.
-
大胆假设:难道是
@babel
版本太老的问题? 结论:不是的.
实验方案4 - 研究@financial-times/js-features-analyser
源码寻找原因
-
在[实验方案3](#实验方案3 - 研究
Polyfill.io - create-polyfill-service-url
源码寻找原因) 基础上一步步追溯源头发现是入口文件里在调用generatePolyfillURL
关键函数async function generatePolyfillURL(features = [], supportedBrowsers = [])
"node_modules\create-polyfill-service-url\index.js"
-
然后关键
features
参数内容由@financial-times/js-features-analyser
组件提供-
它是
@babel/core
的插件
Create a bundle which targets the syntax level you support (ES3/5/2017/2019) and then pass that bundle through this tool
-
-
在其源码目录可见其支持的各种类型
node_modules\create-polyfill-serv ice-url\node_modules\@financial-times\js-features-analyser\src
- built-in-definitions.js
- features.js
- globals.js
- instanceMethods.js
- staticMethods.js
-
结论就是这个组件根本不支持检测
prepend
关键字 -
灵光一闪:是否可以通过
polyfill-library
获取所有支持的特性,来代替js-features-analyser
里的特性node_modules\create-polyfill-service-url\node_modules\polyfill-library\polyfills\__dist\Element.prototype.prepend
{ "aliases": ["default-3.4", "default-3.5", "default-3.6", "default"], "dependencies": ["document", "Element", "_mutation"], "spec": "http://dom.spec.whatwg.org/#dom-parentnode-prepend", "browsers": { "android": "<5", "bb": "*", "chrome": "<54", "edge": "<17", "edge_mob": "<17", "firefox": "<49", "ios_chr": "*", "ios_saf": "<10", "ie": "6 - *", "ie_mob": "10 - *", "opera": "<39", "op_mini": "*", "safari": "<10", "firefox_mob": "<49", "samsung_mob": "<6" }, "detectSource": "\"Element\"in self&&\"prepend\"in Element.prototype\n", "baseDir": "Element/prototype/prepend", "hasTests": true, "isTestable": true, "isPublic": true, "size": 123 }
思路:
-
获取所有
polyfill-library
支持的特性 -
想办法检测"项目源码"里是否有用到上面的特性
- 办法1: 参考
js-features-analyser
的方式
优点: 项目用到啥就检测啥
缺点: 怕检测不准确- 办法2: 读取所有特性配置文件里的
browsers
,然后根据项目配置的浏览器兼容性
来筛选需要用到的所有特性集合
优点: 一次性将最低版本不兼容性的特性全弄出来.
缺点: 返回的数据可能有些大
-
caniuse-api
returns browsers which support some specific feature. -
Fyrd/caniuse: Raw browser/feature support data from caniuse.com
-
然后拼接成URL
-
然后自己部署polyfill-service - npm 在线服务
防止官方CDN网址失效
- 在@Babel编译后的基础上再检测,会更加的精简.
-
实验方案3 - 研究Polyfill.io - create-polyfill-service-url
源码寻找原因
-
进入其所在目录找到入口文件
node_modules\create-polyfill-service-url\index.js
-
发现代码很短,且用到了
browserslist
const browsersListConfig = browserslist.loadConfig({ path: process.cwd() })
-
在
实验方案1
的开始前,在all.js
脚本所在目录增加package.json
{ "browserslist": [ "Android >= 4", "ios >= 8" ] }
-
然后再按照
实验方案1
开始执行结果报错了android 4.0.0 supports document
ios_saf 7.0.0 supports document
android 80.0.0 supports Promise
android 4.4.3 does not support Promise
android 80.0.0 does not support WeakSet
android 80.0.0 supports Set
android 4.4.3 does not support Set
Failed to parse the JavaScript from xxx because an error occured:
TypeError: Cannot read property 'browsers' of undefined -
根据爆出错误的堆栈信息,将源码修复以下
"\node_modules\create-polyfill-service-url\src\index.js"
generatePolyfillURL
函数里
async function generatePolyfillURL(features = [], supportedBrowsers = [])
- 增加以下代码
if (supportedBrowsers.length > 0) { for (const feature of featuresInPolyfillLibrary) { const featureConfig = await polyfillLibrary.describePolyfill(feature); +if(!featureConfig) { + console.error( "not support the feature: ", feature ); + continue; +} const browsersWithoutFeature = featureConfig.browsers;
-
重新运行就不再报错了.
-
结果生成的URL特性里还是没有 没有 检测到
prepend
关键字!!!
实验方案2 - 结合有效方案1
+ 实验方案1
-
使用
有效方案1
的思路 -
将URL替换为
实验方案1
里得到的URL -
实验结果:失败:
polyfill.min.js
文件加载8秒后失败 -
对比分析后发现失败原因是"无法解析cdn.polyfill.io的对应IP地址"
-
然后将其修改为
polyfill.io
再次实验 -
最后实验结果:失败
- 成功的下载到了
polyfill.min.js
文件 - 结果关闭Picker时还是爆出同样的异常
- 成功的下载到了
-
结论就是:
Polyfill.io - create-polyfill-service-url
功能没有检测到.
实验方案1 - 使用Polyfill.io - create-polyfill-service-url
分析看看
-
将发布后的几个.js文件使用
uglify-js
合并为一个all.js文件此时在该文件里搜索
prepend
关键字,能正常匹配到. -
使用
Polyfill.io - create-polyfill-service-url
功能检测.JS文件"D:\Program Files\Node.JS\node-v12.16.1-win-x64\create-polyfill-service-url.cmd" analyse --file all.js >r.md
-
最终得到了哪些函数特性需要转换
https://cdn.polyfill.io/v3/polyfill.min.js?features=Array.from,Array.of,Array.prototype.copyWithin,Array.prototype.entries,Array.prototype.fill,Array.prototype.find,Array.prototype.findIndex,Array.prototype.includes,Array.prototype.keys,Array.prototype.values,ArrayBuffer,console,CustomEvent,DataView,document,es5,Event,getComputedStyle,JSON,localStorage,Map,Math.acosh,Math.asinh,Math.atanh,Math.expm1,Math.fround,Math.imul,Math.log1p,Math.sign,Math.sinh,modernizr:es6string,MutationObserver,Number.parseFloat,Number.parseInt,Object.assign,Object.getOwnPropertySymbols,Object.is,Object.isExtensible,Object.isFrozen,Object.preventExtensions,Object.setPrototypeOf,Object.values,Promise,Promise.prototype.finally,Reflect,Reflect.defineProperty,Reflect.ownKeys,RegExp.prototype.flags,requestAnimationFrame,Set,setImmediate,String.fromCodePoint,Symbol,Symbol.iterator,Symbol.toStringTag,Uint16Array,Uint8ClampedArray,WeakMap,WeakSet,XMLHttpRequest
-
结果居然 没有 检测到
prepend
关键字!!!
有效方案1 - 使用Polyfill.io - url-builder
直接选择prepend
关键字
-
打开Polyfill.io - url-builder 网站 通过筛选
prepend
关键字将匹配的都勾选上得到以下URLhttps://polyfill.io/v3/polyfill.min.js?features=document%2CElement.prototype.prepend%2CDocumentFragment.prototype.prepend
-
然后在发布后的
index.html
文件里chunk-vendors.??.js
脚本之前增加以上脚本引用<div id=app></div> <script src=https://polyfill.io/v3/polyfill.min.js?features=document%2CElement.prototype.prepend%2CDocumentFragment.prototype.prepend></script> <script src=/H5/js/chunk-vendors.6a015935.js></script> <script src=/H5/js/index.07e4de0f.js></script>
-
然后在Chrome 44 版本上打开网页 - 选择时间Picker - 再关闭
-
√成功解决问题.
参考资料
-
- browserslist能够把上面近似于人类语言的配置,转换成一组浏览器集合。
- Browserslist的浏览器数据来源就是这个caniuse-lite,而它是caniuse-db库的精简版本
- 每当增加一个新特性时,都要对以上浏览器列表以及对应版本列表进行实测,特性的测试可使用以下两个官方推荐的网站https://www.browserstack.com 和 http://saucelabs.com。
-
- Android 4.4.4 不支持
- Chrome 53 不支持
-
javascript - Error with method ParentNode.append() transpiling ES6 with Babel & Webpack - Stack Overflow javascript - Error with method ParentNode.append() transpiling ES6 with Babel & Webpack - Stack Overflow
Babel is a JavaScript compiler. It polyfills the JS language features. But
append
is a DOM feature so it can't be polyfilled by babel. You could use ember-cli-polyfill-io to polyfill the DOM.Reference:
(1) ParentNode.append polyfill is missing
(2) What babel-polyfill doesn't include
List of missing polyfills:
JSON
is missing only in IE7String#normalize
is missing due to its rare usage and large size. Alternative polyfill: unormProxy
can't be polyfilledwindow.fetch
is not part of ECMAScript, but a web standard. While core-js does include some web standards,fetch
is currently not one of them. Alternative polyfill: github fetchIntl
is missing because of its large size. Alternative polyfill: Intl.js- DOM polyfills. For example
element.closest
. What is included however are iterable DOM collections
at this moment DOM polyfills are not in the
core-js
scope.2017- ``. Instead, use bundlers like
webpack
orrollup
-
ParentNode.prepend() - Web APIs | MDN
- You can polyfill the
prepend()
method if it's not available - Chrome 54 以上版本
- You can polyfill the
-
-
Element.prototype.prepend
-
DocumentFragment.prototype.prepend
-
Supported Browsers
- Internet Explorer 8
- Chrome 29
- Android 4.3
-