• 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 }
  • 相关阅读:
    web版ssh的使用
    Django高级篇三。restful的解析器,认证组件,权限组件
    Django中的缓存(内存,文件,redis)
    Python使用redis
    跨域请求
    python发送邮箱
    登录服务器失败 IP 统计和处理方法
    centos中单进程监控
    编译搭建lnmp+zabbix
    Django+Uwsgi+Nginx项目部署文档
  • 原文地址:https://www.cnblogs.com/dhsz/p/7064913.html
Copyright © 2020-2023  润新知