This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!

Although vue3 has been out for a long time, I think vue.js source code is still very worth learning. Vue. js inside the package of a lot of tools in our daily work projects will often be used. So I’ll be reading the vue.js source code and sharing snippets worth learning in the near future, and this article will continue to be updated.

One, what is worth our learning:

1. The code is rigorous, do a lot of value and type judgment tool class. For example, determine defined, undefined, determine primitive type, determine object, and so on.

2. Source code in many places to use call, bind, apply, so learning call, apply, bind is very important, please read my another article to learn :(juejin.cn/post/698755…)

  1. Object. Freeze Freezes objects to improve performance.

4. Higher order functions :(many interviewers may ask)

① The cached function creates a cached version of the pure function. High-order cached function. Input parameters are functions and return values are functions. Taking advantage of the fact that the closure variable is not recycled, you can cache the variable, and the next time you call it, you can read it from the cache, use the cache if it exists, and recalculate it if it doesn’t

(2) looseEqual

ES6 has a method to determine whether two objects are equal object.is (). This method determines whether a and b are objects of the same pointer. If a contains b and B contains a, A = B = B = c = c = c = c = c = c = c

(3) polyfillBind

The advanced function, which simply binds to polyfill, is used for environments that do not support it, such as PhantomJS 1.x; Polyfill is a JS library that smooths out the differences between the JS implementations of different browsers

(4) passive

Passived is mainly used to optimize the performance of browser page scrolling. Chrome has proposed a new browser feature: The Web developer uses the new passive property to tell the browser whether the preventDefault function should be called within the event listeners registered on the current page to prevent the default event behavior, so that the browser can better use this information to optimize the performance of the page. If the value of passive is true, the preventDefault function will not be called within the listener to preventDefault sliding. Chrome calls this type of listener passive. Chrome is currently using this feature to optimize the sliding performance of pages, Therefore, Passive Event Listeners are currently only supported for mousewheel/touch related events. 2.Passive Event Listeners are designed to improve the smoothness of page gliding. Directly affect the user’s most intuitive experience of the page. 3. Simple implementation of passive function handler(event) {console.log(event.type); } document.addEventListener(“mousewheel”, handler, {passive:true});

Ii. Vue Core

① Data listening is one of the most important Dep

Dep is an observable, which can have multiple commands subscribed to it. It is the data dependency of the subscriber Watcher. Dep is equivalent to collecting the observed signal and notifying the corresponding Watcher via dep.notify() to update the view. In VUE we’re going to implement Dep and watcher and the main purpose of Dep is to collect every response attribute that’s dependent on vUE, we’re going to create a Dep object that’s going to take care of all the dependencies that the phone depends on that attribute, the subscribers, and we’re going to publish notifications when the data is updated, Calling the update method on the watcher object to update the view simply means adding a dependency on the get in the data hijacking listener to publish notifications in the set

② View update the most important VNode

VNode is designed based on object-oriented. The template template is described as VNode, and then a series of operations are performed to form a real DOM through VNode for mounting and updating. When comparing the old VNode with the new VNode, only the changed part is updated to improve the speed of view update

④ Bidirectional binding Observer: Traverses all the properties of an object to bind it bidirectionally

The Observer binds data in both directions with responsive attributes. In the case of an object, a depth walk is performed, binding methods to each child object, or to each member of an array. If you want to modify a member of an array, which is an object, you just need to recursively bind the members of the array bidirectionally. Listen to arrays: Create an object.create (arrayProto) Object from the array prototype. Modify the prototype to ensure that the native array methods are not contaminated. If the current browser supports __proto__, you can override this property directly, giving the array object an overridden array method. If you don’t have a browser with this property, you must override all array methods by iterating through the def

⑤ Dispatch update: defineReactive function (core function of response)

DefineReactive initializes an instance of the Dep object, then takes obj’s property descriptor, and recursively calls observe on the child object. This ensures that all of obj’s child attributes become responsive objects, no matter how complex its structure. In this way we can access or modify one of the more deeply nested properties in OBj and also trigger the getter and setter. Finally, use object.defineProperty to add getter and setter for obj’s attribute key. The core is to add getters and setters to the data using object.defineProperty. The purpose is to automatically perform some logic when we access and write data: the getter does the dependent collection, and the setter does the update distribution

3. Interpretation of 1~1200 lines of code:

//AMD specification and commonJS specification, both for modularity
// The AMD specification loads modules asynchronously and allows callback functions to be specified.
// The CommonJS specification loads modules synchronously, meaning that the following operations can be performed only after the loading is complete.
// Is an anonymous function, which has no self-executing design parameter window, and passes in the window object. It does not contaminate global variables, nor will it be contaminated by other code
(function (global, factory) {
// Check the CommonJS module scope. Each file is a module with its own scope, variables, methods, etc., and is not visible to other modules.
// All code runs in the module scope and does not contaminate the global scope.
// The module can be loaded multiple times, but it will only run once on the first load, and then the result will be cached, and later loads will read the cached result directly.
// In order for the module to run again, the cache must be cleared. Modules are loaded in the order in which they appear in the code
  typeof exports= = ='object' && typeof module! = ='undefined' ? module.exports = factory() :
  //AMD asynchronous module definition checks for the existence of the JavaScript dependency management library require.js
  typeof define === 'function' && define.amd ? define(factory) : 
  (global = global || self, global.Vue = factory()); } (this.function () {
   'use strict';

  / * * /
  // Freeze an object. A frozen object can no longer be modified. You can use Object.Freeze to improve performance
  var emptyObject = Object.freeze({});

// The judgment is undefined
  function isUndef (v) {
    return v === undefined || v === null
  }
// The judgment is defined
  function isDef (v) {
    returnv ! = =undefined&& v ! = =null
  }

  function isTrue (v) {
    return v === true
  }

  function isFalse (v) {
    return v === false
  }

  /** * Judge the original type */
  function isPrimitive (value) {
    return (
      typeof value === 'string' ||
      typeof value === 'number' ||
      // $flow-disable-line
      typeof value === 'symbol' ||
      typeof value === 'boolean')}// Judge as object
  function isObject (obj) {
    returnobj ! = =null && typeof obj === 'object'
  }

  /** * Gets a string of the original type of the value, for example, [object] */
  var _toString = Object.prototype.toString;
// Cut the reference type to get the following basic type. For example, [object RegExp] gives RegExp
  function toRawType (value) {
    return _toString.call(value).slice(8, -1)}// Judge pure objects: "pure objects" are objects created by {}, new Object(), object.create (null)
  function isPlainObject (obj) {
    return _toString.call(obj) === '[object Object]'
  }
  // Determine the native reference type
  function isRegExp (v) {
    return _toString.call(v) === '[object RegExp]'
  }

  /** * Check whether val is a valid index of the array, and whether it is a non-infinite positive integer. * /
  function isValidArrayIndex (val) {
    var n = parseFloat(String(val));
    return n >= 0 && Math.floor(n) === n && isFinite(val)
  }
  // Check whether it is a Promise
  function isPromise (val) {
    return (
      isDef(val) &&
      typeof val.then === 'function' &&
      typeof val.catch === 'function')}/** * Converts the value to a string. * /
  function toString (val) {
    return val == null
      ? ' '
      : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)
        ? JSON.stringify(val, null.2)
        : String(val) 
  }

  /** Convert the input value to a number for persistence. If the conversion fails, the original string is returned. * /
  function toNumber (val) {
    var n = parseFloat(val);
    return isNaN(n) ? val : n
  }

  /** * The makeMap method cuts a string and puts it into a map. This method is used to verify that a string in the map exists (case sensitive) */
  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) { returnmap[val]; }}/** * Check whether the tag is a built-in tag. * /
  var isBuiltInTag = makeMap('slot,component'.true);

  /** * Check whether the attribute is a reserved attribute. * /
  var isReservedAttribute = makeMap('key,ref,slot,slot-scope,is');

  /** * Remove items from the array */
  function remove (arr, item) {
    if (arr.length) {
      var index = arr.indexOf(item);
      if (index > -1) {
        return arr.splice(index, 1)}}}/** * Check whether the object has this property. The hasOwnProperty() method returns a Boolean value indicating whether the object itself has the specified property (that is, whether it has the specified key). * /
  var hasOwnProperty = Object.prototype.hasOwnProperty;
  function hasOwn (obj, key) {
    return hasOwnProperty.call(obj, key)
  }

  /** * Create a cached version of the pure function. * High-order cached function, which takes a function as input and returns a function. Taking advantage of the fact that closure variables are not recycled, * can cache the variables, and the next call can read them from the cache, use the cache if there is one, and recalculate */ if not
  function cached (fn) {
    var cache = Object.create(null);
    return (function cachedFn (str) {
      var hit = cache[str];
      return hit || (cache[str] = fn(str))
    })
  }

  /** * Humps a hyphenated string */
  var camelizeRE = /-(\w)/g;
  var camelize = cached(function (str) {
    return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ' '; })});/** * Capitalize the first letter of the string. * /
  var capitalize = cached(function (str) {
    return str.charAt(0).toUpperCase() + str.slice(1)});/** * a hump string */
  var hyphenateRE = /\B([A-Z])/g;
  var hyphenate = cached(function (str) {
    return str.replace(hyphenateRE, '- $1').toLowerCase()
  });

  /** * advanced function, simple binding polyfill, for environments that do not support it, such as PhantomJS 1.x; Polyfill is a JS library that smooths out the differences in js implementations between different browsers

  /* istanbul ignore next */
  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
  }

  function nativeBind (fn, ctx) {
    return fn.bind(ctx)
  }
  Function.prototype.bind()
// The bind() method creates a new function. When bind() is called, this is specified as the first argument to bind().
// The rest of the arguments will be used as arguments to the new function.
  var bind = Function.prototype.bind
    ? nativeBind
    : polyfillBind;

  /** * Convert array-like objects to real arrays */
  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
  }

  /** * Insert multiple properties into the target object */
  function extend (to, _from) {
    for (var key in _from) {
      to[key] = _from[key];
    }
    return to
  }

  /** * Merges the array of objects into a single object. * /
  function toObject (arr) {
    var res = {};
    for (var i = 0; i < arr.length; i++) {
      if(arr[i]) { extend(res, arr[i]); }}return res
  }

  /* eslint-disable no-unused-vars */

  /** * Perform no operation. * Stubbing args to make Flow happy without leaving useless transpiled code * with ... rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/). */
  function noop (a, b, c) {}

  /** * always returns false. * /
  var no = function (a, b, c) { return false; };

  /* eslint-enable no-unused-vars */

  /** * Returns the same value */
  var identity = function (_) { return _; };

  /** * generates a string containing a static key from the compiler module. * /
  function genStaticKeys (modules) {
    return modules.reduce(function (keys, m) {
      return keys.concat(m.staticKeys || [])
    }, []).join(', ')}/** * ES6 has a method to determine whether two objects are equal. Object.is() This method determines whether a and b are the same pointer. A = B * Determines that the two objects are equal (determines that the two objects have the same key name and key value, but not the same address) */
  function looseEqual (a, b) {
    // Check for constant equality
    if (a === b) { return true }
    // Check whether it is an object
    var isObjectA = isObject(a);
    var isObjectB = isObject(b);
    if (isObjectA && isObjectB) {
      try {
        // When both a and B are arrays
        var isArrayA = Array.isArray(a);
        var isArrayB = Array.isArray(b);
        if (isArrayA && isArrayB) {
          // Determine each item in both arrays recursively
          return a.length === b.length && a.every(function (e, i) {
            return looseEqual(e, b[i])
          })
          // If a and b are of type Date,
        } else if (a instanceof Date && b instanceof Date) {
          // Make a and B equal
          return a.getTime() === b.getTime()
          // When a and b are objects, first determine whether the length is the same, then determine whether the corresponding belong value of each attribute is the same
        } 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 {
          return false}}catch (e) {
        return false}}else if(! isObjectA && ! isObjectB) {return String(a) === String(b)
    } else {
      return false}}/** * returns the index, or -1 if not found, or looseEqual() */
  function looseIndexOf (arr, val) {
    for (var i = 0; i < arr.length; i++) {
      if (looseEqual(arr[i], val)) { return i }
    }
    return -1
  }

  /** * Make sure the function is called only once. * /
  function once (fn) {
    var called = false;
    return function () {
      if(! called) { called =true;
        fn.apply(this.arguments); }}}// cached
// polyfillBind
// looseEqual
// Closure, type determination, function calls to each other



// Define variables
  var SSR_ATTR = 'data-server-rendered';// Server rendering
  // Global functions component, directive, filter
  var ASSET_TYPES = [
    'component'.'directive'.'filter'
  ];
  // Life cycle
  var LIFECYCLE_HOOKS = [
    'beforeCreate'.'created'.'beforeMount'.'mounted'.'beforeUpdate'.'updated'.'beforeDestroy'.'destroyed'.'activated'.'deactivated'.'errorCaptured'.'serverPrefetch'
  ];

  / * * /


  // Global configuration
  var config = ({
    /** * Option merge policy (for core/util/options) */
    // $flow-disable-line
    optionMergeStrategies: Object.create(null),

    /** whether to suppress warnings */
    silent: false./** * Is production mode prompt displayed on startup? * /
    productionTip: "development"! = ='production'./** whether to enable devTools */
    devtools: "development"! = ='production'./** whether to record performance */
    performance: false.Error handlers that observe program errors */
    errorHandler: null.Warning handler for observer warnings */
    warnHandler: null./** * Ignore some custom elements */
    ignoredElements: []./** * Custom user key alias of v-ON */
    // $flow-disable-line
    keyCodes: Object.create(null),

    /** * Check whether the tag is preserved so that it cannot be registered as a component. Depending on the platform, it may be covered. * /
    isReservedTag: no,

    /** Check whether the property is preserved so that it cannot be used as a component property. Depending on the platform, it may be covered. * /
    isReservedAttr: no,

    /** * Check whether the tag is an unknown element. Platform dependent. * /
    isUnknownElement: no,

    /** * Get the element's namespace */
    getTagNamespace: noop,

    /** * Parse the real tag name for a particular platform. * /
    parsePlatformTagName: identity,

    /** * Check whether the property must use property binding, such as valuePlatform dependent. * /
    mustUseProp: no,

    /** * Execute the update asynchronously. Intended to be used by Vue Test Utils, this will significantly degrade performance if set to false. * /
    async: true./** * Exposed for legacy reasons */
    _lifecycleHooks: LIFECYCLE_HOOKS
  });

  / * * /

  /** Parse unicode letters for HTML tags, component names, and attribute paths. Use https://www.w3.org/TR/html53/semantics-scripting.html#potentialcustomelementname * /
  var unicodeRegExp = /a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3 001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD/;

  /** * Checks whether the string begins with $or _ */
  function isReserved (str) {
    var c = (str + ' ').charCodeAt(0);
    return c === 0x24 || c === 0x5F
  }

  /** * Define attributes. * a constructor that defines a property on an object where!! Enumerable casts Boolean */
  function def (obj, key, val, enumerable) {
    Object.defineProperty(obj, key, {
      value: val,
      enumerable:!!!!! enumerable,writable: true.configurable: true
    });
  }

  /** * Parse the simple path. * /
  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
    }
  }

  / * * /

  var hasProto = '__proto__' in {};

  // Determine the browser environment
  var inBrowser = typeof window! = ='undefined';
  // The operating environment is wechat
  var inWeex = typeofWXEnvironment ! = ='undefined'&&!!!!! WXEnvironment.platform;var weexPlatform = inWeex && WXEnvironment.platform.toLowerCase();
  // The browser UA determines
  var UA = inBrowser && window.navigator.userAgent.toLowerCase();
  // The kernel of IE is Trident
  var isIE = UA && /msie|trident/.test(UA);
  var isIE9 = UA && UA.indexOf('msie 9.0') > 0;
  var isEdge = UA && UA.indexOf('edge/') > 0;
  // Determine android ios
  var isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android');
  var isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios');
  / / chrome
  var isChrome = UA && /chrome\/\d+/.test(UA) && ! isEdge;var isPhantomJS = UA && /phantomjs/.test(UA);
  var isFF = UA && UA.match(/firefox\/(\d+)/);

  // Firefox has a "watch" function on Object.prototype...
  var nativeWatch = ({}).watch;

https://blog.csdn.net/dj0379/article/details/52883315 / * 1. What is the passive? Passived is mainly used to optimize the performance of browser page scrolling. Chrome has proposed a new browser feature: The Web developer uses the new passive property to tell the browser whether the preventDefault function should be called within the event listeners registered on the current page to prevent the default event behavior, so that the browser can better use this information to optimize the performance of the page. If the value of passive is true, the preventDefault function will not be called within the listener to preventDefault sliding. Chrome calls this type of listener passive. Chrome is currently using this feature to optimize the sliding performance of pages, Therefore, Passive Event Listeners are currently only supported for mousewheel/touch related events. 2.Passive Event Listeners are designed to improve the smoothness of page gliding. Directly affect the user's most intuitive experience of the page. 3. Simple implementation of passive function handler(event) {console.log(event.type); } document.addEventListener("mousewheel", handler, {passive:true}); * /


  var supportsPassive = false;
  if (inBrowser) {
    try {
      var opts = {};
      Object.defineProperty(opts, 'passive', ({
        get: function get () {
          /* istanbul ignore next */
          supportsPassive = true; }}));// https://github.com/facebook/flow/issues/285
      window.addEventListener('test-passive'.null, opts);
    } catch (e) {}
  }

  // The view server renderer can set the view environment
  var _isServer;
  var isServerRendering = function () {
    if (_isServer === undefined) {
      if(! inBrowser && ! inWeex &&typeof global! = ='undefined') {
        // Detect the presence of a Vue server renderer and avoid the web package populating process
        _isServer = global['process'] && global['process'].env.VUE_ENV === 'server';
      } else {
        _isServer = false; }}return _isServer
  };

  var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__;

  // A method that determines whether a function is a built-in JavaScript method
  function isNative (Ctor) {
    return typeof Ctor === 'function' && /native code/.test(Ctor.toString())
  }
  // Determine whether there is a Symbol type
  var hasSymbol =
    typeof Symbol! = ='undefined' && isNative(Symbol) &&
    typeof Reflect! = ='undefined' && isNative(Reflect.ownKeys);

  var _Set;
  // Check whether the Set is a built-in object
  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);
      }
      // The set.prototype.has () method takes an argument and returns a Boolean indicating whether the argument exists in the Set
      Set.prototype.has = function has (key) {
        return this.set[key] === true
      };
      // The set.prototype.add () method is used to add a specified value to the end of the Set and return the Set itself. If the value already exists, the addition will not succeed
      Set.prototype.add = function add (key) {
        this.set[key] = true;
      };
      // Used to clear all elements of a Set. No value is returned
      Set.prototype.clear = function clear () {
        this.set = Object.create(null);
      };

      return Set; } ()); }/* Log module */

  var warn = noop;
  var tip = noop;
  //generateComponentTrace this function, which is initialized with noop, does nothing to solve the traffic checking problem
  var generateComponentTrace = (noop);
  var formatComponentName = (noop);
  {
    var hasConsole = typeof console! = ='undefined';
    / / the regular writing is to convert the connector to the hump, and the first character capitalized ^ | [-] - which means the beginning of the string, or - _ behind one character at a time
    var classifyRE = / (? :^|[-_])(\w)/g;
    var classify = function (str) { return str
      .replace(classifyRE, function (c) { return c.toUpperCase(); })
      .replace(/[-_]/g.' '); };
    // The console prints an error message
    warn = function (msg, vm) {
      var trace = vm ? generateComponentTrace(vm) : ' ';
      // If console.silent is configured, no error log will be printed
      if (config.warnHandler) {
        config.warnHandler.call(null, msg, vm, trace);
      } else if(hasConsole && (! config.silent)) {console.error(("[Vue warn]: "+ msg + trace)); }};// The console displays a warning
    tip = function (msg, vm) {
      if(hasConsole && (! config.silent)) {console.warn("[Vue tip]: " + msg + (
          vm ? generateComponentTrace(vm) : ' ')); }};// Format the component name
    formatComponentName = function (vm, includeFile) {
      // If it is the root component, it will have a property.$root refers to itself
      if (vm.$root === vm) {
        return '<Root>'
      }
      Is there a custom name / / judgment first option, if not use the vm. The name, the name should be a vue configuration of a random number
      var options = typeof vm === 'function'&& vm.cid ! =null
        ? vm.options
        : vm._isVue
          ? vm.$options || vm.constructor.options
          : vm;
      var name = options.name || options._componentTag;
      var file = options.__file;
      if(! name && file) {// If there is no name but the file exists, the file name is taken
        var match = file.match(/([^/\\]+)\.vue$/);
        name = match && match[1];
      }

      return (
        // Return the hump component name
        (name ? ("<" + (classify(name)) + ">") : "<Anonymous>") +
        // An error message is displayed(file && includeFile ! = =false ? (" at " + file) : ' '))};// Return a string that loops n times. For example, STR ="a",n=5, returns "aaaaa"
    var repeat = function (str, n) {
      var res = ' ';
      while (n) {
        if (n % 2= = =1) { res += str; }
        if (n > 1) { str += str; }
        n >>= 1;
      }
      return res
    };
      // Generate component trace path (number of components rule)
    generateComponentTrace = function (vm) {
      if (vm._isVue && vm.$parent) {
        var tree = [];
        var currentRecursiveSequence = 0;
        while (vm) {
          if (tree.length > 0) {
            var last = tree[tree.length - 1];
            if (last.constructor === vm.constructor) {
              currentRecursiveSequence++;
              vm = vm.$parent;
              continue
            } else if (currentRecursiveSequence > 0) {
              tree[tree.length - 1] = [last, currentRecursiveSequence];
              currentRecursiveSequence = 0;
            }
          }
          tree.push(vm);
          vm = vm.$parent;
        }
        return '\n\nfound in\n\n' + tree
          .map(function (vm, i) { return ("" + (i === 0 ? '- >' : repeat(' '.5 + i * 2)) + (Array.isArray(vm)
              ? ((formatComponentName(vm[0+))"... (" + (vm[1]) + " recursive calls)")
              : formatComponentName(vm))); })
          .join('\n')}else {
        return ("\n\n(found in " + (formatComponentName(vm)) + ")")}}; }var uid = 0;
  // Vue core: data listening is one of the most important Dep
  // Dep is an observable that can have multiple directives subscribed to it
  // Dep is the data dependency for the subscriber Watcher
  // Dep is equivalent to collecting the observed signal
  // Then update the view by notifting the corresponding Watcher via dep.notify().
  // To implement the data response mechanism that the data changes view changes
// In vue's response mechanism we use the observation mode to listen for changes in the data
The main function of the Dep and watcher Dep in vue is to collect each response attribute that is dependent on vue
// Creates a DEP object that takes care of all the dependencies that the phone depends on for this property, namely subscribers, and publishes notifications when the data is updated
// Call the update method on the watcher object to update the view by adding a dependency on the set to publish notifications
  var Dep = function Dep () {
    this.id = uid++;
    this.subs = [];
  };
  // Add monitor objects to the subs array in the Dep instance
  Dep.prototype.addSub = function addSub (sub) {
    this.subs.push(sub);
  };
// Delete the monitor object specified in the subs array of the Dep instance
  Dep.prototype.removeSub = function removeSub (sub) {
    remove(this.subs, sub);
  };
// Set a dependency for a Watcher
// We added the dep. target to determine if it is a call to the Watcher constructor
Watcher's this.get call, not a normal call
// Continue listening if the target exists
  Dep.prototype.depend = function depend () {
    if (Dep.target) {
      Dep.target.addDep(this); }};// Notify the listener
  // monitor in the subs array of variable Dep and call its update method
  Dep.prototype.notify = function notify () {
    var subs = this.subs.slice();
    if(! config.async) {// If asynchron is not run, the sub is not sorted in the scheduler
      subs.sort(function (a, b) { return a.id - b.id; });
    }
     // Notify all bindings to Watcher. Call watcher's update()
    for (var i = 0, l = subs.length; i < l; i++) { subs[i].update(); }};// The current target observation procedure being evaluated.
  Dep.target = null;
  var targetStack = [];
  // Add this monitor to the end of the targetStack array if the target value of the Dep instance is true. Set the target of the current Dep instance to the incoming monitor.
  function pushTarget (target) {
    targetStack.push(target);
    Dep.target = target;
  }
  // Fetch a monitor object from the header of the targetStack array and assign it to the target property of the Dep instance.
  function popTarget () {
    targetStack.pop();
    Dep.target = targetStack[targetStack.length - 1];
  }

  // VNode is designed for object orientation
  // View updates the most important VNode
  // The template is described as a VNode, and then a series of operations are performed on the VNode to form a real DOM for mounting
  // Compare the old VNode to the new VNode, update only the part of the changes, improve the speed of view update
  var VNode = function VNode (tag, data, children, text, elm, context, componentOptions, asyncFactory) {
    this.tag = tag; // Tag attributes
    this.data = data;// After rendering to the real DOM, the node goes to the class attr style event
    this.children = children;// Child node, also on vnode
    this.text = text; / / text
    this.elm = elm;// Corresponds to the real DOM node
    this.ns = undefined;// Namespace of the current node
    this.context = context;// The scope of compilation
    this.fnContext = undefined;// Functionalize the component context
    this.fnOptions = undefined;// Functionalized component configuration items
    this.fnScopeId = undefined;// Functionalized component ScopeId
    this.key = data && data.key;// Only exists for bound data, which can improve performance during diff
    this.componentOptions = componentOptions;// This value is null if the vnode object is generated by the vue component. If the Vnode is generated by the dom, this value is null
    this.componentInstance = undefined;// The current component instance
    this.parent = undefined;// Vnode, component placeholder node
    this.raw = false; // Whether it's native HTML or just plain text
    this.isStatic = false;  / / static node id | | keep alive
    this.isRootInsert = true; // Whether to insert as the root node
    this.isComment = false;// Whether it is a comment node
    this.isCloned = false; // Whether the node is a clone
    this.isOnce = false; // Whether the node is v-once
    this.asyncFactory = asyncFactory;// Asynchronous factory method
    this.asyncMeta = undefined; Asynchronous Meta / /
    this.isAsyncPlaceholder = false;// Whether it is an asynchronous placeholder
  };

  var prototypeAccessors = { child: { configurable: true}}; prototypeAccessors.child.get =function () {
    return this.componentInstance
  };
  // Bind the Object prototypeAccessors to the VNode stereotype via object.defineProperties,
  // prototypeAccessors sets the child to a modifiable state.
  Object.defineProperties( VNode.prototype, prototypeAccessors );
  /* Create an empty VNode */
  var createEmptyVNode = function (text) {
    if ( text === void 0 ) text = ' ';

    var node = new VNode();
    node.text = text;
    node.isComment = true;
    return node
  };
  /* Create a text node */
  function createTextVNode (val) {
    return new VNode(undefined.undefined.undefined.String(val))
  }

  // cloneVNode Clones a VNode
  function cloneVNode (vnode) {
    var cloned = new VNode(
      vnode.tag,
      vnode.data,
      // Clone the subarray to avoid mutating the subarray of the original array during cloning
      vnode.children && vnode.children.slice(),
      vnode.text,
      vnode.elm,
      vnode.context,
      vnode.componentOptions,
      vnode.asyncFactory
    );
    cloned.ns = vnode.ns;
    cloned.isStatic = vnode.isStatic;
    cloned.key = vnode.key;
    cloned.isComment = vnode.isComment;
    cloned.fnContext = vnode.fnContext;
    cloned.fnOptions = vnode.fnOptions;
    cloned.fnScopeId = vnode.fnScopeId;
    cloned.asyncMeta = vnode.asyncMeta;
    cloned.isCloned = true;
    return cloned
  }

/* Get a prototype of the native array */
  var arrayProto = Array.prototype;
  /* Create a new array object and modify the seven methods of the array on that object to prevent contamination of the native array methods */
  var arrayMethods = Object.create(arrayProto);

  /* Here we override the methods of the array. We override the methods of the array without contaminate the original array prototype, intercept the changes to the array members, perform the native array operation while dep notifying all associated observers of the reactive processing */
  var methodsToPatch = [
    'push'.'pop'.'shift'.'unshift'.'splice'.'sort'.'reverse'
  ];

  // Arrays are special. Their operation methods do not trigger setters. Modify the array 7 change methods so that it can send update notifications
  methodsToPatch.forEach(function (method) {
    /* Cache the array native methods */
    var original = arrayProto[method];
    def(arrayMethods, method, function mutator () {
      var args = [], len = arguments.length;
      while ( len-- ) args[ len ] = arguments[ len ];
       /* Call the native array method */
      var result = original.apply(this, args);
      /* Insert into the array, and you need to observe again. */
      var ob = this.__ob__;
      var inserted;
      switch (method) {
        case 'push':
        case 'unshift':
          inserted = args;
          break
        case 'splice':
          inserted = args.slice(2);
          break
      }
      The // method is used to asynchronously monitor changes to an array, similar to object.observe () for an Object.
      if (inserted) { ob.observeArray(inserted); }
      Dep notifying all registered observers to respond */
      ob.dep.notify();
      return result
    });
  });

  // Get all the properties in a JSON object.
  // getOwnPropertyNames gets the enumerable and non-enumerable properties of the object itself, excluding the properties whose property name is a value of Symbol
  var arrayKeys = Object.getOwnPropertyNames(arrayMethods);

  /** * In some cases we may want to disable observation inside a component's * update computation. */
  var shouldObserve = true;
  // This method is an optimization of vue logic. If the current component is the root component, then the root component should not have props.
  // toggleObserving forbids dependency collection for root props
  function toggleObserving (value) {
    shouldObserve = value;
  }

  // The observer class attached to each observed object. Once attached, the observer converts the target object's property key into a getter/setter for collecting dependencies and dispatching updates.
  // The Observer binds data in both directions with responsive attributes. In the case of an object, a depth walk is performed, binding methods to each child object, or to each member of an array.
// If you want to modify a member of an array, which is an object, you only need to recursively bind the members of the array bidirectionally.
  export class Observer {
  var Observer = function Observer (value) {
    this.value = value;// This is the object passed in to be listened on
    this.dep = new Dep();// Save the new Dep instance
    this.vmCount = 0;// Set vmCount to 0
    // An __ob__ attribute is defined for the listener. The value of this attribute is the current Observer instance object
    // The def function is a simple encapsulation of the object.defineProperty function
    // The def function is used to define the __ob__ attribute.
    // This prevents the __ob__ attribute from being traversed later
    def(value, '__ob__'.this);
      /* If it is an array, replace the original method in the array prototype with the modified array method that can intercept the response, so that the array data is listened for. Here, if the __proto__ attribute is supported by the current browser, the native array method on the current array object stereotype is directly overridden, and if the attribute is not supported, the array object stereotype is directly overridden. * /
    if (Array.isArray(value)) {
      if (hasProto) {
        /* Override the prototype method to modify the target object */
        protoAugment(value, arrayMethods);
      } else {
        /* Defines (overrides) a method of the target object or array */
        copyAugment(value, arrayMethods, arrayKeys);
      }
      /* If it is an array, iterate over each member of the array and perform observe*/
      this.observeArray(value);
    } else {
      // If it is an object, the traversal performs a responsive operation
      this.walk(value); }};// Iterates over each property and converts them to getters/setters. This method should only be called when the value type is Object.
  Observer.prototype.walk = function walk (obj) {
    var keys = Object.keys(obj);
    for (var i = 0; i < keys.length; i++) { defineReactive$$1(obj, keys[i]); }};// Observe the list of array items. If it is an array, iterate over each item and perform an observe for each item
  Observer.prototype.observeArray = function observeArray (items) {
    for (var i = 0, l = items.length; i < l; i++) { observe(items[i]); }};// helpers

// Specify the prototype directly
  function protoAugment (target, src) {
    /* eslint-disable no-proto */
    target.__proto__ = src;
    /* eslint-enable no-proto */
  }

  // Iterate over keys, defining (overriding) a method on the target object or array
  function copyAugment (target, src, keys) {
    for (var i = 0, l = keys.length; i < l; i++) {
      varkey = keys[i]; def(target, key, src[key]); }}// Try to create an Observer instance (__ob__), and return a new Observer instance if successful,
// If an Observer instance exists, return the existing Observer instance.
// Vue's responsive data will be marked with an __ob__ attribute, which will store an Observer for that attribute, i.e. an instance of the Observer, to prevent repeated binding.
  function observe (value, asRootData) {
    /* Determine whether it is an object or a virtual node */
    if(! isObject(value) || valueinstanceof VNode) {
      return
    }
    var ob;
    If there is no Observer instance, a new Observer instance is created and assigned to the __ob__ attribute. If there is an Observer instance, the Observer instance is returned
    if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
      ob = value.__ob__;
    } else if (
       /* This is to ensure that value is a pure object, not a function or Regexp. * /shouldObserve && ! isServerRendering() && (Array.isArray(value) || isPlainObject(value)) &&
      Object.isExtensible(value) && ! value._isVue ) { ob =new Observer(value);
    }
    /* asRootData for observe in the following Observer is not true*/
    if (asRootData && ob) {
      ob.vmCount++;
    }
    return ob
  }
// defineReactive starts by initializing an instance of the Dep object, then gets obj's attribute descriptor,
// Then call observe recursively on the child object. This ensures that no matter how complicated the structure of obj is,
// All of its sub-properties can also become reactive objects, so we can access or modify one of the more deeply nested properties in obj,
// Can also trigger getters and setters. Finally, use object.defineProperty to add getter and setter for obj's attribute key.
// The core is to add getters and setters to the data using object.defineProperty
// In order to automatically perform some logic when we access and write data: getters do the dependent collection, setters do the update dispatch
  function defineReactive$$1 (obj, key, val, customSetter, shallow) {
    // The dep instance object referenced by the dep constant
 // Each data field refers to its own DEP constant through its closure
 // Each time defineReactive is called to define an accessor property, the setter/getter for that property is referenced by the closure to a basket of its own
    var dep = new Dep();
     // Unconfigurable direct return
  // Get the attribute description object that this field may have
    var property = Object.getOwnPropertyDescriptor(obj, key);
     // Check whether the field is configurable
  // A non-configurable property cannot be used and it is not necessary to use object.defineProperty to change its property definition.
    if (property && property.configurable === false) {
      return
    }
// Holds the get and set from the property object
  // Avoid overwriting the original set and get methods
    var getter = property && property.get;
    var setter = property && property.set;
    if((! getter || setter) &&arguments.length === 2) {
      val = obj[key];
    }
  // Recursive responsive processing adds an Obeserver instance to each layer attribute
  // If shallow does not exist, there is no __ob__ attribute
  // If it is an object, then call observe(val) to observe the object
  // The walk function defineReactive is not passed shallow, so the argument is und
  // The default is depth observation
    varchildOb = ! shallow && observe(val);// Data intercept
  // Intercept data for obj's key via object.defineProperty
    Object.defineProperty(obj, key, {
      enumerable: true.configurable: true.// Do dependency collection
      get: function reactiveGetter () {
        var value = getter ? getter.call(obj) : val;
        // Check if dep. target is present. If Dep is present, Dep has added Watcher instantiated objects
        if (Dep.target) {
        // Join deP to manage watcher
          dep.depend();
          if (childOb) {
            childOb.dep.depend();
            if (Array.isArray(value)) {
            // Loop to add watcherdependArray(value); }}}return value
      },
      set: function reactiveSetter (newVal) {
      // Getting the value triggers dependency collection
        var value = getter ? getter.call(obj) : val;
        /* eslint-disable no-self-compare */
        if(newVal === value || (newVal ! == newVal && value ! == value)) {return
        }
        /* eslint-enable no-self-compare */
        if (customSetter) {
          customSetter();
        }
        // For accessor properties that have no setter
        if(getter && ! setter) {return }
        if (setter) {
        // Set the new value
          setter.call(obj, newVal);
        } else {
        // If there is no setter, give the new value
          val = newVal;
        }
        observe-observe-observe-observe-observe-observe-observe-observe-observe-observe-observe-observe-observe-observe-obchildOb = ! shallow && observe(newVal);// Trigger notification when setdep.notify(); }}); }// Set a property to the object, add a new property and add a trigger change notification (dep.notify) if the property does not already exist
  function set (target, key, val) {
    // Check whether the data is undefined or null
    // Check whether the data type is string, number, symbol, Boolean
    if (isUndef(target) || isPrimitive(target)
    ) {
      // Target must be an object or array, otherwise a warning is issued
      warn(("Cannot set reactive property on undefined, null, or primitive value: " + ((target))));
    }
    // If it is an array and check whether the key is a valid array index
    if (Array.isArray(target) && isValidArrayIndex(key)) {
      // Set the array length
      target.length = Math.max(target.length, key);
      // Add a new data to the end of the array
      target.splice(key, 1, val);
      return val
    }
    // If the key is on target and not found through the prototype chain, assign it directly
    if (key intarget && ! (keyin Object.prototype)) {
      target[key] = val;
      return val
    }
    // Declare an object ob with the value of all methods and properties on the stereotype in the target object, indicating that the data was added to the observer
    var ob = (target).__ob__;
    // If it is vue or check the number of times vue has been instantiated vmCount
    if (target._isVue || (ob && ob.vmCount)) {
      warn(
        'Avoid adding reactive properties to a Vue instance or its root $data ' +
        'at runtime - declare it upfront in the data option.'
      );
      return val
    }
      // If ob does not exist, prove that no observer was added
    if(! ob) { target[key] = val;return val
    }
    // The observer to which ob.value was added via defineReactive
    defineReactive$$1(ob.value, key, val);
    // Trigger notification update, notifying subscriber obj.value to update data
    ob.dep.notify();
    return val
  }
Copy the code