UNI-APP相關筆記 - `Uncaught TypeError: t.$el.prepend is not a function`


200318 - Uncaught TypeError: t.$el.prepend is not a function

問題場景還原

  1. index.vue 里使用 <picker>來選取日期

  2. 在Chrome 44 里測試

  3. 顯示picker之后關閉它

  4. 此時console里就出現了這個錯誤.

  5. 發生錯誤的文件為:webpack:///./node_modules/@dcloudio/uni-h5/dist/index.umd.min.js?1c31

  6. 發生錯誤的代碼行:

    _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)
    }
    

解決方案

  1. 從使用體驗上來說,沒有受到肉眼可見的影響,所以理論上可以暫不處理.

實驗方案6 - 使用最新版本的@Babel來轉換試試

  1. 從源碼HBuilderX\Bin\plugins\uniapp-cli\node_modules\@dcloudio\vue-cli-plugin-uni\lib\h5\index.js得知

    useBuiltIns === 'entry' ? `import '@babel/polyfill'

  2. @babel/polyfill · Babel 中文網 得知

    polyfill = import "core-js/stable";+import "regenerator-runtime/runtime";

    最新版本查詢地址: babel/packages/babel-polyfill/package.json

  3. 從項目運行時調試可知引用的類型為@babel/core-js

    查詢`"HBuilderX\Bin\plugins\uniapp-cli\package.json"`可知當前版本為
    "core-js": {
      "version": "2.6.1",
    
  4. 在線搜索 zloirock/core-js: Standard Library 關鍵字prepend

    沒有任何匹配結果

  5. 結論: @babel/core-js無論新3舊0版本都不包含prepend的轉換

    可能是因為它是實驗性特性功能?

實驗方案5 - 修改全局的的uniapp-clibrowserslist兼容性配置

  1. Can I use - prepend

    • Android 4.4.4 不支持
    • Chrome 53 不支持
    • 實驗性功能
  2. "HBuilderX\Bin\plugins\uniapp-cli\package.json"

    參考: [解決方案5:[√有效的 最終采用的方案]](#解決方案5:[√有效的 最終采用的方案])

    "browserslist": [
    "Chrome >= 53"
    ]
    "browserslist": [
    "Android >= 4"
    ]
    
  3. 無論怎么修改都無法解決問題.

  4. 大膽假設:難道是@babel版本太老的問題? 結論:不是的.

實驗方案4 - 研究@financial-times/js-features-analyser源碼尋找原因

  1. 在[實驗方案3](#實驗方案3 - 研究Polyfill.io - create-polyfill-service-url源碼尋找原因) 基礎上一步步追溯源頭發現是入口文件里在調用generatePolyfillURL 關鍵函數

    async function generatePolyfillURL(features = [], supportedBrowsers = [])

    "node_modules\create-polyfill-service-url\index.js"

  2. 然后關鍵features 參數內容由@financial-times/js-features-analyser組件提供

    Create a bundle which targets the syntax level you support (ES3/5/2017/2019) and then pass that bundle through this tool

  3. 在其源碼目錄可見其支持的各種類型

    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
  4. 結論就是這個組件根本不支持檢測prepend關鍵字

  5. 靈光一閃:是否可以通過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
    }
    

    思路:

    1. 獲取所有polyfill-library支持的特性

    2. 想辦法檢測"項目源碼"里是否有用到上面的特性

    • 辦法1: 參考js-features-analyser的方式

    優點: 項目用到啥就檢測啥
    缺點: 怕檢測不准確

    • 辦法2: 讀取所有特性配置文件里的browsers,然后根據項目配置的瀏覽器兼容性來篩選需要用到的所有特性集合

    優點: 一次性將最低版本不兼容性的特性全弄出來.

    缺點: 返回的數據可能有些大

    1. 然后拼接成URL

    2. 然后自己部署polyfill-service - npm 在線服務

    防止官方CDN網址失效

    1. 在@Babel編譯后的基礎上再檢測,會更加的精簡.

實驗方案3 - 研究Polyfill.io - create-polyfill-service-url源碼尋找原因

  1. 進入其所在目錄找到入口文件

    node_modules\create-polyfill-service-url\index.js

  2. 發現代碼很短,且用到了browserslist

    const browsersListConfig = browserslist.loadConfig({
    path: process.cwd()
    })
    
  3. 實驗方案1的開始前,在all.js腳本所在目錄增加package.json

    {
    "browserslist": [
    "Android >= 4",
    "ios >= 8"
    ]
    }
    
  4. 然后再按照實驗方案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

  5. 根據爆出錯誤的堆棧信息,將源碼修復以下

    "\node_modules\create-polyfill-service-url\src\index.js"

    1. generatePolyfillURL 函數里

    async function generatePolyfillURL(features = [], supportedBrowsers = [])

    1. 增加以下代碼
    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;
    
  6. 重新運行就不再報錯了.

  7. 結果生成的URL特性里還是沒有 沒有 檢測到prepend關鍵字!!!

實驗方案2 - 結合有效方案1 + 實驗方案1

  1. 使用有效方案1的思路

  2. 將URL替換為實驗方案1里得到的URL

  3. 實驗結果:失敗: polyfill.min.js文件加載8秒后失敗

  4. 對比分析后發現失敗原因是"無法解析cdn.polyfill.io的對應IP地址"

  5. 然后將其修改為polyfill.io再次實驗

  6. 最后實驗結果:失敗

    • 成功的下載到了polyfill.min.js文件
    • 結果關閉Picker時還是爆出同樣的異常
  7. 結論就是:Polyfill.io - create-polyfill-service-url 功能沒有檢測到.

實驗方案1 - 使用Polyfill.io - create-polyfill-service-url分析看看

  1. 將發布后的幾個.js文件使用uglify-js 合並為一個all.js文件

    此時在該文件里搜索prepend關鍵字,能正常匹配到.

  2. 使用 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

  3. 最終得到了哪些函數特性需要轉換

    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
    
  4. 結果居然 沒有 檢測到prepend關鍵字!!!

有效方案1 - 使用Polyfill.io - url-builder直接選擇prepend關鍵字

  1. 打開Polyfill.io - url-builder 網站 通過篩選prepend關鍵字將匹配的都勾選上得到以下URL

    https://polyfill.io/v3/polyfill.min.js?features=document%2CElement.prototype.prepend%2CDocumentFragment.prototype.prepend
    
  2. 然后在發布后的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>
    
  3. 然后在Chrome 44 版本上打開網頁 - 選擇時間Picker - 再關閉

  4. √成功解決問題.

參考資料

  1. 重新認識caniuse - 知乎

    • browserslist能夠把上面近似於人類語言的配置,轉換成一組瀏覽器集合。
    • Browserslist的瀏覽器數據來源就是這個caniuse-lite,而它是caniuse-db庫的精簡版本
    • 每當增加一個新特性時,都要對以上瀏覽器列表以及對應版本列表進行實測,特性的測試可使用以下兩個官方推薦的網站https://www.browserstack.comhttp://saucelabs.com
  2. Can I use - prepend

    • Android 4.4.4 不支持
    • Chrome 53 不支持
  3. 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 IE7
    • String#normalize is missing due to its rare usage and large size. Alternative polyfill: unorm
    • Proxy can't be polyfilled
    • window.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 fetch
    • Intl 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 or rollup
  4. zloirock/core-js: Standard Library

  5. ParentNode.prepend() - Web APIs | MDN

    • You can polyfill the prepend() method if it's not available
    • Chrome 54 以上版本
  6. Polyfill.io

    • Element.prototype.prepend

    • DocumentFragment.prototype.prepend

    • Supported Browsers

    1. Internet Explorer 8
    2. Chrome 29
    3. Android 4.3


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM