【Vue源码解读】第1~612行

1~10行:挂载Vue

(function (global, factory) {
  // 检查CommonJS规范
  if (typeof exports === 'object' && typeof module !== 'undefined') {
    module.exports = factory()
  } else {
    // 检查AMD规范
    if (typeof define === 'function' && define.amd) {
      define(factory)
    } else {
      // Vue挂载到global(node环境)或window(浏览器环境)
      global = global || self, global.Vue = factory()
    }
  }
}(this, function () {
	'use strict';
	/*省略*/
})

11~111行:全局判断函数

// 冻结对象
  var emptyObject = Object.freeze({});

  // 是否未定义
  function isUndef (v) {
    return v === undefined || v === null
  }
  // 是否已定义
  function isDef (v) {
    return v !== undefined && v !== null
  }
  // 是否为true
  function isTrue (v) {
    return v === true
  }
  // 是否为false
  function isFalse (v) {
    return v === false
  }

  /**
   * 是否为基础类型
   */
  function isPrimitive (value) {
    return (
      typeof value === 'string' ||
      typeof value === 'number' ||
      // $flow-disable-line
      typeof value === 'symbol' ||
      typeof value === 'boolean'
    )
  }

  /**
   * 是否为对象
   */
  function isObject (obj) {
    return obj !== null && typeof obj === 'object'
  }

  /**
   * toString原型函数
   */
  var _toString = Object.prototype.toString;
  // 原型类型
  function toRawType (value) {
    return _toString.call(value).slice(8, -1)
  }

  /**
   * 是否为纯粹对象
   */
  function isPlainObject (obj) {
    return _toString.call(obj) === '[object Object]'
  }
  // 是否正则
  function isRegExp (v) {
    return _toString.call(v) === '[object RegExp]'
  }

  /**
   * 是否为有效数组下标
   */
  function isValidArrayIndex (val) {
    var n = parseFloat(String(val));
    return n >= 0 && Math.floor(n) === n && isFinite(val)
  }
  // 是否Promise
  function isPromise (val) {
    return (
      isDef(val) &&
      typeof val.then === 'function' &&
      typeof val.catch === 'function'
    )
  }

  /**
   * 将纯粹对象、数组在渲染时转换成实际的字符
   */
  function toString (val) {
    return val == null
      ? ''
      : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)
        ? JSON.stringify(val, null, 2)
        : String(val)
  }

  /**
   * 转换为数字
   */
  function toNumber (val) {
    var n = parseFloat(val);
    return isNaN(n) ? val : n
  }

第112~354行:高阶函数、闭包、全局函数

/**
   * 创建一个map,用于检查key是否在map中。使用闭包
   */
  function makeMap (
    str,
    expectsLowerCase
  ) {
    var map = Object.create(null);
    var list = str.split(',');
    for (var i = 0; i < list.length; i++) {
      map[list[i]] = true;
    }
    return expectsLowerCase
      ? function (val) { return map[val.toLowerCase()]; }
      : function (val) { return map[val]; }
  }

  /**
   * 是否构建标签
   */
  var isBuiltInTag = makeMap('slot,component', true);

  /**
   * 是否保留属性
   */
  var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is');

  /**
   * 从数组移除一个元素
   */
  function remove (arr, item) {
    if (arr.length) {
      var index = arr.indexOf(item);
      if (index > -1) {
        return arr.splice(index, 1)
      }
    }
  }

  /**
   * 对象是否包含某个属性。不包括symbol属性
   */
  var hasOwnProperty = Object.prototype.hasOwnProperty;
  function hasOwn (obj, key) {
    return hasOwnProperty.call(obj, key)
  }

  /**
   * 高阶函数cached,用到闭包。保存键值对
   */
  function cached (fn) {
    var cache = Object.create(null);
    return (function cachedFn (str) {
      var hit = cache[str];
      return hit || (cache[str] = fn(str))
    })
  }

  /**
   * 驼峰化字符串
   */
  var camelizeRE = /-(\w)/g;
  var camelize = cached(function (str) {
    return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; })
  });

  /**
   * 首字母大写
   */
  var capitalize = cached(function (str) {
    return str.charAt(0).toUpperCase() + str.slice(1)
  });

  /**
   * 驼峰转连字符
   */
  var hyphenateRE = /\B([A-Z])/g;
  var hyphenate = cached(function (str) {
    return str.replace(hyphenateRE, '-$1').toLowerCase()
  });

  // bind函数兼容
  function polyfillBind (fn, ctx) {
    function boundFn (a) {
      var l = arguments.length;
      return l
        ? l > 1
          ? fn.apply(ctx, arguments)
          : fn.call(ctx, a)
        : fn.call(ctx)
    }

    boundFn._length = fn.length;
    return boundFn
  }
  // 原生bind函数
  function nativeBind (fn, ctx) {
    return fn.bind(ctx)
  }
  // bind兼容写法
  var bind = Function.prototype.bind
    ? nativeBind
    : polyfillBind;

  /**
   * 类数组转数组
   */
  function toArray (list, start) {
    start = start || 0;
    var i = list.length - start;
    var ret = new Array(i);
    while (i--) {
      ret[i] = list[i + start];
    }
    return ret
  }

  /**
   * 合并目标对象属性
   */
  function extend (to, _from) {
    for (var key in _from) {
      to[key] = _from[key];
    }
    return to
  }

  /**
   * 对象数组合并为一个对象
   */
  function toObject (arr) {
    var res = {};
    for (var i = 0; i < arr.length; i++) {
      if (arr[i]) {
        extend(res, arr[i]);
      }
    }
    return res
  }

  // 占位函数
  function noop (a, b, c) {}

  /**
   * 返回false的函数
   */
  var no = function (a, b, c) { return false; };

  /**
   * 返回相同内容的函数
   */
  var identity = function (_) { return _; };

  /**
   * 从编译模块中将静态key合并为一个字符串
   */
  function genStaticKeys (modules) {
    return modules.reduce(function (keys, m) {
      return keys.concat(m.staticKeys || [])
    }, []).join(',')
  }

  /**
   * 浅相等。
   */
  function looseEqual (a, b) {
    if (a === b) { return true }
    var isObjectA = isObject(a);
    var isObjectB = isObject(b);
    if (isObjectA && isObjectB) {
      try {
        var isArrayA = Array.isArray(a);
        var isArrayB = Array.isArray(b);
        if (isArrayA && isArrayB) {
          return a.length === b.length && a.every(function (e, i) {
            return looseEqual(e, b[i])
          })
        } else if (a instanceof Date && b instanceof Date) {
          return a.getTime() === b.getTime()
        } else if (!isArrayA && !isArrayB) {
          var keysA = Object.keys(a);
          var keysB = Object.keys(b);
          return keysA.length === keysB.length && keysA.every(function (key) {
            return looseEqual(a[key], b[key])
          })
        } else {
          /* istanbul ignore next */
          return false
        }
      } catch (e) {
        /* istanbul ignore next */
        return false
      }
    } else if (!isObjectA && !isObjectB) {
      return String(a) === String(b)
    } else {
      return false
    }
  }

  /**
   * 返回浅相等的索引
   */
  function looseIndexOf (arr, val) {
    for (var i = 0; i < arr.length; i++) {
      if (looseEqual(arr[i], val)) { return i }
    }
    return -1
  }

  /**
   * 只执行一次的函数
   */
  function once (fn) {
    var called = false;
    return function () {
      if (!called) {
        called = true;
        fn.apply(this, arguments);
      }
    }
  }

重点解析

  • makeMap 缓存标签的函数
    通过返回一个函数,在返回的函数中引用另外一个函数的 map 变量形成闭包。执行 makeMap 函数时,会缓存结果,然后执行返回函数,从结果中找到对应的值。
  • cached 高阶函数,缓存结果
    参数为有返回值的函数,每次执行会缓存值,再次执行,先从缓存中取值,未取到,再执行输入的函数。
  • polyfillBind bind兼容函数
    通过判断不同参数值,调用 callapply,实现bind函数
  • noop 占位函数
    Vue 源码中有部分方法在执行时需要传入参数,noop 函数可以在参数中占位使用,或者作为创建一个函数的默认值
  • looseEqual 浅相等函数
    递归比较2个对象属性值或者2个数组属性值或者其他类型值相等

355~612行

// 服务端渲染
  var SSR_ATTR = 'data-server-rendered';
  // 全局类型
  var ASSET_TYPES = [
    'component',
    'directive',
    'filter'
  ];
  // 生命周期钩子
  var LIFECYCLE_HOOKS = [
    'beforeCreate',
    'created',
    'beforeMount',
    'mounted',
    'beforeUpdate',
    'updated',
    'beforeDestroy',
    'destroyed',
    'activated',
    'deactivated',
    'errorCaptured',
    'serverPrefetch'
  ];

  // 全局配置Vue.config
  var config = ({
    // 自定义合并策略的选项。
    optionMergeStrategies: Object.create(null),

    /**
     * 取消 Vue 所有的日志与警告。
     */
    silent: false,

    /**
     * 设置为 false 以阻止 vue 在启动时生成生产提示。
     */
    productionTip: "development" !== 'production',

    /**
     * 配置是否允许 vue-devtools 检查代码
     */
    devtools: "development" !== 'production',

    /**
     * 性能面板展示组件渲染过程 
     */
    performance: false,

    /**
     * 错误处理函数
     */
    errorHandler: null,

    /**
     * 警告函数
     */
    warnHandler: null,

    /**
     * 忽略自定义元素
     */
    ignoredElements: [],

    /**
     * 自定义用户key别名
     */
    keyCodes: Object.create(null),

    /**
     * 是否保留标签
     */
    isReservedTag: no,

    /**
     * 是否保留属性
     */
    isReservedAttr: no,

    /**
     * 是否未知元素
     */
    isUnknownElement: no,

    /**
     * 获取标签名称空间
     */
    getTagNamespace: noop,

    /**
     * 解析特定平台的标签名
     */
    parsePlatformTagName: identity,

    /**
     * 必须使用prop
     */
    mustUseProp: no,

    /**
     * 是否异步
     */
    async: true,

    /**
     * 暴露生命周期钩子
     */
    _lifecycleHooks: LIFECYCLE_HOOKS
  });

  var unicodeRegExp = /a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD/;

  /**
   * 是否已_或$开头
   */
  function isReserved (str) {
    var c = (str + '').charCodeAt(0);
    return c === 0x24 || c === 0x5F
  }

  /**
   * 定义一个属性
   */
  function def (obj, key, val, enumerable) {
    Object.defineProperty(obj, key, {
      value: val,
      enumerable: !!enumerable,
      writable: true,
      configurable: true
    });
  }

  /**
   * 解析简单路径
   */
  var bailRE = new RegExp(("[^" + (unicodeRegExp.source) + ".$_\\d]"));
  function parsePath (path) {
    if (bailRE.test(path)) {
      return
    }
    var segments = path.split('.');
    return function (obj) {
      for (var i = 0; i < segments.length; i++) {
        if (!obj) { return }
        obj = obj[segments[i]];
      }
      return obj
    }
  }

  /*  */

  // 是否有__proto__属性
  var hasProto = '__proto__' in {};

  var inBrowser = typeof window !== 'undefined';
  var inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform;
  var weexPlatform = inWeex && WXEnvironment.platform.toLowerCase();
  var UA = inBrowser && window.navigator.userAgent.toLowerCase();
  var isIE = UA && /msie|trident/.test(UA);
  var isIE9 = UA && UA.indexOf('msie 9.0') > 0;
  var isEdge = UA && UA.indexOf('edge/') > 0;
  var isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android');
  var isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios');
  var isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge;
  var isPhantomJS = UA && /phantomjs/.test(UA);
  var isFF = UA && UA.match(/firefox\/(\d+)/);

  // 火狐浏览器对象原型有watch属性
  var nativeWatch = ({}).watch;

  var supportsPassive = false;
  if (inBrowser) {
    try {
      var opts = {};
      Object.defineProperty(opts, 'passive', ({
        get: function get () {
          supportsPassive = true;
        }
      })); 
      window.addEventListener('test-passive', null, opts);
    } catch (e) {}
  }

  // VUE_ENV 是否服务端渲染
  var _isServer;
  var isServerRendering = function () {
    if (_isServer === undefined) {
      if (!inBrowser && !inWeex && typeof global !== 'undefined') {
        _isServer = global['process'] && global['process'].env.VUE_ENV === 'server';
      } else {
        _isServer = false;
      }
    }
    return _isServer
  };

  // 检查devtools
  var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__;

  // 是否原生函数
  function isNative (Ctor) {
    return typeof Ctor === 'function' && /native code/.test(Ctor.toString())
  }
  // 是否有symbol
  var hasSymbol =
    typeof Symbol !== 'undefined' && isNative(Symbol) &&
    typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys);
  // Set兼容处理
  var _Set;
  if (typeof Set !== 'undefined' && isNative(Set)) {
    _Set = Set;
  } else {
    _Set = /*@__PURE__*/(function () {
      function Set () {
        this.set = Object.create(null);
      }
      Set.prototype.has = function has (key) {
        return this.set[key] === true
      };
      Set.prototype.add = function add (key) {
        this.set[key] = true;
      };
      Set.prototype.clear = function clear () {
        this.set = Object.create(null);
      };

      return Set;
    }());
  }

总结

  • 生成空对象使用 Object.create(null)
  • 缓存原型函数。例如:var _toString = Object.prototype.toString;
  • 缓存函数,使用闭包更合适
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值