- 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關鍵字
- 參考資料
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
-