解读Vue之第一天

恩,简单来解读下一步步阅读vue的源码,首先,从github上download vue的源码下来,直接npm install 安装相关依赖,其所有基本文件都放在src/core里面,执行命令npm run dev 将会生成一个dist文件夹,dist文件下会存在vue.js 及其他相关文件,vue.js 文件就是我接下来观摩的源文件了

*!
 * Vue.js v2.6.10
 * (c) 2014-2019 Evan You
 * Released under the MIT License.
 */
(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  typeof define === 'function' && define.amd ? define(factory) :
  (global = global || self, global.Vue = factory());
}(this, function () { 'use strict';
*******
}));

首先一进来我们就能看到的是版本号及版权说明等,接下来还是惯有的框架写法,如大多数的框架都会是一自执行函数,将其所有变量方法等都放置在这个封闭的函数内部,避免了对外部的污染,接下来传递的是this关键字及factory工厂,并且这里还使用了工厂模式,将第二个参数执行结果暴露到外面,同时还将引入了AMD及CommonJS规范

// 通过freeze来定义一个无法继承的空对象
var emptyObject = Object.freeze({});
// 然后接下来定义了一系列的判断类型方法及一些工具方法
function isUndef (v) {
  return v === undefined || v === null
}
***
恩,说下工具方法
//  接收一个函数作为参数,下次调用直接使用镜像函数,不再创建函数句柄
  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() : ''; })
  });


//一系列的工具方法
// 简单的合并对象,将第二个参数对象的值赋值到第一个参数中,若存在相同的键,将会被覆盖
  function extend (to, _from) {
// 通过for..in 遍历第二个参数,将第二个参数的的每一个属性值赋值到第一个对象
    for (var key in _from) {
      to[key] = _from[key];
    }
    return to
  }
// 定义的空方法,在某些情况下参数需要传递一个函数时,可使用这个作为默认值
 function noop (a, b, c) {}
// 改函数,传递什么返回什么,在大部分框架中都会有这样的一个函数
  var identity = function (_) { return _; };
// 严格的判断a,b是否相等
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);
// object 对象,对比key值,并且对比对象中的每一个值,同样尾调 递归判断是否全等
          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
    }
}
// 其类型一般出现Object的情况有 {},[],new Date,Null 这三种,上述也判断的常见这三种object类型
// 只能调用一次,通过这种方式,将函数调用帧保存下来,再次调用便不会再执行该方法,比如在使用请求,只需要执行一次的情况下
  function once (fn) {
    var called = false;
    return function () {
      if (!called) {
        called = true;
        fn.apply(this, arguments);
      }
    }
  }
// 接下来,声明钩子hooks
  var LIFECYCLE_HOOKS = [
    'beforeCreate',
    'created',
    'beforeMount',
    'mounted',
    'beforeUpdate',
    'updated',
    'beforeDestroy',
    'destroyed',
    'activated',
    'deactivated',
    'errorCaptured',
    'serverPrefetch' // 一般用于服务端ssr
  ];
接下来config配置相对应的配置属性(使用()避免变量污染)
  var config = ({
***
})
// 定义对象属性值通用操作,常规通过Object.defineProperty来定义属性
  function def (obj, key, val, enumerable) {
    Object.defineProperty(obj, key, {
      value: val,
      enumerable: !!enumerable,
      writable: true,
      configurable: true
    });
  }
// 这里处理路径,主要是在whater中使用,监听对象中的变量时用到,获取对象的最后一级的key值

const obj = {
    test:{
        name:232
    }
}
console.log(parsePath('test.name')(obj)); // 232
  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__ (其实除了null 其他一切对象都应该会有一个__proto__并且指向prototype)
var hasProto = '__proto__' in {}
// 我们来插播下原型 拿Function 来说
Function: 函数对象,
我们定义一个函数 function foo(){} foo.prototype.__proto__ 指向的是 Object.prototype 实际上指向的是Function.prototype.__proto__ 这里我们引用
https://blog.csdn.net/taoerchun/article/details/81170840 这篇文章来简述Function 与 Object之间的联系
每个__proto__都会有个constructor 它一般会指向它本身,
foo.__proto__ === Function.prototype
foo.__proto__.constructor === foo 
foo.prototype.__proto__ === Function.__proto__.__proto__ === Object.prototype
foo.prototype.__proto__.constructor === Object
foo.prototype.__proto__.__proto__ === null 就达到原型链顶端了
//**************************************************************
// 接下来是相关的判断平台
 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+)/);
// 判断是否是在浏览器平台
  if (inBrowser) {
    try {
      var opts = {};
      Object.defineProperty(opts, 'passive', ({
        get: function get () {
          /* istanbul ignore next */
          supportsPassive = true;
        }
      })); // https://github.com/facebook/flow/issues/285
// 监听test-passive 事件
      window.addEventListener('test-passive', null, opts);
    } catch (e) {}
  }
// 从这里可以判断是否是服务端,一般服务端node才会有global关键字,
  var isServerRendering = function () {
    if (_isServer === undefined) {
      /* istanbul ignore if */
// 判断不是浏览器,微信,且global有值,一般该环境为node
      if (!inBrowser && !inWeex && typeof global !== 'undefined') {
        // detect presence of vue-server-renderer and avoid
        // Webpack shimming the process
        _isServer = global['process'] && global['process'].env.VUE_ENV === 'server';
      } else {
        _isServer = false;
      }
    }
    return _isServer
  };
// 判断是否是原始函数方法
 function isNative (Ctor) {
    return typeof Ctor === 'function' && /native code/.test(Ctor.toString())
  }
// 兼容Set, 判断若支持Set,则直接使用原始Set,若没有,则通过自执行函数创建一个Set对象,不过Vue这里只实现了add,has 及 clear 三个实例方法
  var _Set;
  /* istanbul ignore if */ // $flow-disable-line
  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 = /*@__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;
    }());
  }
// 重复n次str字符,这里使用while且使用左移运算来达到循环次数减少为n的2次幂
    var repeat = function (str, n) {
      var res = '';
      while (n) {
        if (n % 2 === 1) { res += str; }
        if (n > 1) { str += str; }
        n >>= 1;
      }
      return res
    };

百度未收录
分享