vue.js 源代碼學習筆記 ----- 工具方法 env


/* @flow */
/* globals MutationObserver */

import { noop } from 'shared/util'

// can we use __proto__?  有些瀏覽器不能讓你明目張膽的使用 __proto__
export const hasProto = '__proto__' in {}

// Browser environment sniffing  這里作者不太嚴謹, 直接用 navigator.userAget 判斷瀏覽器

//利用 window 來檢測瀏覽器環境 export const inBrowser = typeof window !== 'undefined'

export const UA = inBrowser && window.navigator.userAgent.toLowerCase()
//IE的內核是trident export const isIE
= UA && /msie|trident/.test(UA) export const isIE9 = UA && UA.indexOf('msie 9.0') > 0 export const isEdge = UA && UA.indexOf('edge/') > 0


//還可以這樣來判斷 android ios export const isAndroid = UA && UA.indexOf('android') > 0 export const isIOS = UA && /iphone|ipad|ipod|ios/.test(UA)

//判斷chrome export const isChrome
= UA && /chrome\/\d+/.test(UA) && !isEdge // this needs to be lazy-evaled because vue may be required before // vue-server-renderer can set VUE_ENV
// 這個需求需要延遲加載, 因為在 vue服務器渲染設置VUE_ENV環境之前, 需要先加載vue let _isServer export const isServerRendering = () => { if (_isServer === undefined) { /* istanbul ignore if */ if (!inBrowser && typeof global !== 'undefined') { // detect presence of vue-server-renderer and avoid // Webpack shimming the process
  
    //檢測 vue的服務器渲染是否存在, 而且避免webpack去填充process _isServer = global['process'].env.VUE_ENV === 'server' } else { _isServer = false } } return _isServer } // detect devtools 輸出vue的工具方法的全局鈎子 export const devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__ /* istanbul ignore next */
//這里判斷 函數是否是系統函數, 比如 Function Object ExpReg window document 等等, 這些函數應該使用c/c++實現的
//這樣可以區分 Symbol是系統函數, 還是用戶自定義了一個Symbol, 下面這個函數可以看出來 export function isNative (Ctor: Function): boolean { return /native code/.test(Ctor.toString()) }
//這里使用了ES6的Reflect方法, 使用這個對象的目的是, 為了保證訪問的是系統的原型方法,
// ownKeys 保證key的輸出順序, 先數組 后字符串 export const hasSymbol
= typeof Symbol !== 'undefined' && isNative(Symbol) && typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys) /** * Defer a task to execute it asynchronously.
  延遲一個任務, 異步執行; 在node.js中, next會在setTimeout之前執行, 也就是在當前執行棧之后, 事件隊列之前執行
  比如在同一個事件循環中, 反復設置一個vm的值, 最后只會執行 一次對應UI的更新.

    JS 的 event loop 執行時會區分 task 和 microtask,引擎在每個 task 執行完畢,

    從隊列中取下一個 task 來執行之前,會先執行完所有 microtask 隊列中的事件。

    setTimeout 回調會被分配到一個新的 task 中執行,而 Promise 的 resolver、MutationObserver 的回調都會被安排到一個新的 microtask 中執行,

    會比 setTimeout 產生的 task 先執行    

    用 microtask?根據 HTML Standard,在每個 task 運行完以后,UI 都會重渲染,
    那么在 microtask 中就完成數據更新,當前 task 結束就可以得到最新的 UI 了。
    反之如果新建一個 task 來做數據更新,那么渲染就會進行兩次。所以優先不使用task
*/
export const nextTick = (function () {
  const callbacks = []
  let pending = false
  let timerFunc
  
//在適當的時機調用 nextTickHnadleer
function nextTickHandler () { pending = false const copies = callbacks.slice(0) callbacks.length = 0
    for (let i = 0; i < copies.length; i++) { copies[i]() } }
  
 
 /* var a = [1,2,3]; 
   var b = a.slice(0)
b[0] = 22 ; a[0] => 1 
   這里完成了數組的淺復制, 注意這種slice不能完成數組的深度復制
 */
 
 //舉例來說,如果在文檔中連續插入1000個段落(p元素),會連續觸發1000個插入事件,執行每個事件的回調函數,這很可能造成瀏覽器的卡頓;
// 而Mutation Observer完全不同,只在1000個段落都插入結束后才會觸發,而且只觸發一次。
 
// the nextTick behavior leverages the microtask queue, which can be accessed // via either native Promise.then or MutationObserver. // MutationObserver has wider support, however it is seriously bugged in // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It // completely stops working after triggering a few times... so, if native // Promise is available, we will use it: /* istanbul ignore if */

//這里的nextTick是利用了事件隊列,
// MutationsObserver 在 IOS的底層方法UIWebView 會有幾個bug, 比如touch事件, 或者在一個事件觸發幾次以后, 它就懶的工作了
//所以我們優先使用 Promise
if (typeof Promise !== 'undefined' && isNative(Promise)) {
  //這種寫法是一個語法糖
var p = Promise.resolve()
var logError = err => { console.error(err) }
timerFunc
= () => {
   
 //不知道then底層是怎么實現de, 如果模擬then也可用 setTimeout(fn,0)方法 p.then(nextTickHandler).catch(logError)
// in problematic UIWebViews, Promise.then doesn't completely break, but // it can get stuck in a weird state where callbacks are pushed into the // microtask queue but the queue isn't being flushed, until the browser // needs to do some other work, e.g. handle a timer. Therefore we can // "force" the microtask queue to be flushed by adding an empty timer.
    
    //在有問題的IOS中, promise的then方法不能完全斷開? 不能異步?
// 當回調函數進入到隊列后, 它會卡在一個奇怪的狀態, 不會刷新, 知道瀏覽器需要處理其他任務, 比如timeer
// 需要利用timeq 強制刷新任務隊列, 並執行 if (isIOS) setTimeout(noop)
}
}
else if (typeof MutationObserver !== 'undefined' && ( isNative(MutationObserver) || // PhantomJS and iOS 7.x MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
// use MutationObserver where native Promise is not available, // e.g. PhantomJS IE11, iOS7, Android 4.4
var counter = 1 var observer = new MutationObserver(nextTickHandler) var textNode = document.createTextNode(String(counter)) observer.observe(textNode, { characterData: true })
  //重新設置 textNode的data屬性, 讓Mutaiont檢查到變化后, 執行異步調用 timerFunc
= () => { counter = (counter + 1) % 2 textNode.data = String(counter) } } else { // fallback to setTimeout /* istanbul ignore next */ timerFunc = () => { setTimeout(nextTickHandler, 0) } }  
//最終返回的這個函數, 其實會執行 nextTickHandler方法, 從而執行各類的回調函數
return function queueNextTick (cb?: Function, ctx?: Object) { let _resolve
callbacks.push(()
=> {
if (cb) cb.call(ctx) if (_resolve) _resolve(ctx)
})
if (!pending) {
pending
= true timerFunc() }
if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve }) } } })()

let _Set
/* istanbul ignore if */ if (typeof Set !== 'undefined' && isNative(Set)) { // use native Set when available. _Set = Set } else { // a non-standard Set polyfill that only works with primitive keys. 設置一個簡單的Set, 只支持 _Set = class Set { set: Object; constructor () { this.set = Object.create(null) } has (key: string | number) { return this.set[key] === true } add (key: string | number) { this.set[key] = true } clear () { this.set = Object.create(null) } } }
export { _Set }

 


免責聲明!

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



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