Vue 源码分析(简略版)

文章详细阐述了Vue.js框架在引入后如何在全局对象下创建Vue$3构造函数,以及Vue实例的初始化过程,包括_init方法、数据的代理和观察者模式,特别是数据响应式处理的细节,如defineReactive和Watcher类的角色。此外,还介绍了$mount方法在组件挂载时的作用,以及模板编译和渲染的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引入vue.js 后,是一个立即执行函数,将在全局对象下挂载一个名为 Vue$3 的构造函数

(function (global, factory) {
	global.Vue = factory();
}(this, (function () {
	function Vue$3 (options) {	  
	  	this._init(options);
	}
    
    ...
    // 扩展Vue$3的属性和方法
    initMixin(Vue$3);
    stateMixin(Vue$3);
    eventsMixin(Vue$3);
    lifecycleMixin(Vue$3);
    renderMixin(Vue$3);
    ...
    initGlobalAPI(Vue$3);

	return Vue$3;
})));

等价于

window.Vue = Vue$3(){
    this._init(options);
}

initData 执行顺序

  1. window.Vue = function Vue$3(options){ };
  2. initMixin(); // 在 Vue.prototype 上扩展一个 _init 方法
    function initMixin (Vue) {
      Vue.prototype._init = function (options) {
        var vm = this;
    
        vm._uid = uid$1++;
        vm._isVue = true;
    
        // merge options
        if (options && options._isComponent) {
          initInternalComponent(vm, options);
        } else {
          vm.$options = mergeOptions(
            resolveConstructorOptions(vm.constructor),
            options || {},
            vm
          );
        }
    
        {
          initProxy(vm);
        }
    
        vm._self = vm;
        initLifecycle(vm);
        initEvents(vm);
        initRender(vm);
        callHook(vm, 'beforeCreate');
        initInjections(vm); // resolve injections before data/props
        initState(vm);
        initProvide(vm); // resolve provide after data/props
        callHook(vm, 'created');
    
        if (vm.$options.el) {
          vm.$mount(vm.$options.el);
        }
      };
    }
  3. new Vue(); // 在构造完 Vue 实例后执行 实例的 _init 方法
  4. this._init(options);
  5. initState(); // 初始化相关状态:props,methods,data,computed,watch
    function initState (vm) {
      vm._watchers = [];
      var opts = vm.$options;
      if (opts.props) { initProps(vm, opts.props); }
      if (opts.methods) { initMethods(vm, opts.methods); }
      if (opts.data) {
        initData(vm);
      } else {
        observe(vm._data = {}, true /* asRootData */);
      }
      if (opts.computed) { initComputed(vm, opts.computed); }
      if (opts.watch && opts.watch !== nativeWatch) {
        initWatch(vm, opts.watch);
      }
    }
  6. initData();
    function initData (vm) {
      var data = vm.$options.data;
      data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {};
    
      // proxy data on instance
      var keys = Object.keys(data);
      var i = keys.length;
      while (i--) {
        var key = keys[i];
        proxy(vm, "_data", key);
      }
    
      // observe data
      observe(data, true /* asRootData */);
    }
  7. proxy(vm, "_data", key); // 代理,简化路径
  8. observe(data, true /* asRootData */);
  9. var ob = new Observer(value);
    var Observer = function Observer (value) {
      this.value = value;
      this.dep = new Dep();
      this.vmCount = 0;
      def(value, '__ob__', this);
      if (Array.isArray(value)) {
        var augment = hasProto
          ? protoAugment
          : copyAugment;
        augment(value, arrayMethods, arrayKeys);
        this.observeArray(value);
      } else {
        this.walk(value);
      }
    };
  10. this.walk(value); // 构造实例后自动调用 ob.walk()
    Observer.prototype.walk = function walk (obj) {
      var keys = Object.keys(obj);
      for (var i = 0; i < keys.length; i++) {
        defineReactive(obj, keys[i], obj[keys[i]]);
      }
    };
  11. defineReactive(obj, keys[i], obj[keys[i]]); // 遍历 data 对象的键值,对每个键值进行响应式处理
  12. Object.defineProperty(obj, key, { getter, setter }
    // 注:源码取自 defineReactive() 中
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter () {
          var value = getter ? getter.call(obj) : val;
          if (Dep.target) { // Watch类的实例
            dep.depend();
            if (childOb) {
              childOb.dep.depend();
              if (Array.isArray(value)) {
                dependArray(value);
              }
            }
          }
          return value
        },
        set: function reactiveSetter (newVal) {
          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 ("development" !== 'production' && customSetter) {
            customSetter();
          }
          if (setter) {
            setter.call(obj, newVal);
          } else {
            val = newVal;
          }
          childOb = !shallow && observe(newVal);
          dep.notify();
        }
    });

initWatch 执行顺序

  1. initState(vm);
  2. initWatch(vm, opts.watch); // 遍历 watch 中的每个键值执行一次 createWatcher 方法;
    function initWatch (vm, watch) {
      for (var key in watch) {
        var handler = watch[key];
        if (Array.isArray(handler)) {
          for (var i = 0; i < handler.length; i++) {
            createWatcher(vm, key, handler[i]);
          }
        } else {
          createWatcher(vm, key, handler);
        }
      }
    }
  3. createWatcher(vm, key, handler); // 在该函数中执行 Vue.prototype.$watch 方法;
  4. vm.$watch(keyOrFn, handler, options); 该函数将 new Watcher 实例,返回值为 unwatchFn 函数,用来卸载当前侦探器;
    // 源码:
    Vue.prototype.$watch = function (
        expOrFn,
        cb,
        options
      ) {
        var vm = this;
        if (isPlainObject(cb)) {
          return createWatcher(vm, expOrFn, cb, options)
        }
        options = options || {};
        options.user = true;
        var watcher = new Watcher(vm, expOrFn, cb, options);
        if (options.immediate) {
          cb.call(vm, watcher.value);
        }
        return function unwatchFn () {
          watcher.teardown();
        }
      };
    
    
    // 示例:
    var unwatchFn = this.$watch('name', function(){
        console.log('doSomeThing');
        ...
        if(unwatchFn ){
            unwatchFn();
        }
    }, {
        immediate: true,
    });
  5. var watcher = new Watcher(vm, expOrFn, cb, options);
    var Watcher = function Watcher (
      vm,
      expOrFn,
      cb,
      options,
      isRenderWatcher
    ) {
      this.vm = vm;
      ...
    
      if (typeof expOrFn === 'function') {
        this.getter = expOrFn;
      } else {
        this.getter = parsePath(expOrFn);    
      }
      this.value = this.get();
    };
  6. this.getter = parsePath(expOrFn); // parsePath 返回一个匿名函数,即 opts.watch 中键值的 handler 方法;
    var bailRE = /[^\w.$]/; // 匹配除了 字母\数字\下划线\$ 以外的任意一个字符
    function parsePath (path) {
      if (bailRE.test(path)) {
        return
      }
      var segments = path.split('.'); // 返回 a.b.c 的值
      return function (obj) {
        for (var i = 0; i < segments.length; i++) {
          if (!obj) { return }
          obj = obj[segments[i]];
        }
        return obj
      }
    }
  7. this.value = watcher.get(); // 构造函数执行完后立即执行watch实例的get方法
    Watcher.prototype.get = function get () {
      pushTarget(this);
      var value;
      var vm = this.vm;
      try {
        value = this.getter.call(vm, vm);
      } catch (e) {
        
      } finally {    
        popTarget();
        this.cleanupDeps();
      }
      return value
    };
  8. watcher.get() 方法中执行 pushTarget(this),设置了 Dep.target = _target;
  9. value = this.getter.call(vm, vm); // getter 就是 parsePath 返回的匿名函数,在此处被调用执行;

  10. 同时匿名函数中执行 obj = obj[segments[i]];触发 proxy 中的 getter 代理 vm['name'] = vm["_data"]["name"];

    在 initData() 时 data 的每个键值执行过 proxy(vm, "_data", key);
    在 initProps() 时 props 的每个键值执行过 proxy(vm, "_props", key);
    
    var sharedPropertyDefinition = {
      enumerable: true,
      configurable: true,
      get: noop,
      set: noop
    };
    function proxy (target, sourceKey, key) {
      sharedPropertyDefinition.get = function proxyGetter () {
        return this[sourceKey][key]
      };
      sharedPropertyDefinition.set = function proxySetter (val) {
        this[sourceKey][key] = val;
      };
      Object.defineProperty(target, key, sharedPropertyDefinition);
    }
  11. vm["_data"]["name"] 取值时触发 Object.defineProperty 的 get 访问器描述符 reactiveGetter 

    /**
     * Define a reactive property on an Object.
     */
    function defineReactive (
      obj,
      key,
      val,
      customSetter,
      shallow
    ) {
      var dep = new Dep(); // 针对Data中的每个属性建立以一个Dep类实例对象
    
      var property = Object.getOwnPropertyDescriptor(obj, key);
      if (property && property.configurable === false) {
        return
      }
    
      var getter = property && property.get;
      var setter = property && property.set;
    
      var childOb = !shallow && observe(val);
      Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter () {
          var value = getter ? getter.call(obj) : val;
          if (Dep.target) { // Watch类的实例
            dep.depend();
            if (childOb) {
              childOb.dep.depend();
              if (Array.isArray(value)) {
                dependArray(value);
              }
            }
          }
          return value
        },
        set: function reactiveSetter (newVal) {
          var value = getter ? getter.call(obj) : val;
    
          if (newVal === value || (newVal !== newVal && value !== value)) {
            return
          }
    
          if (setter) {
            setter.call(obj, newVal);
          } else {
            val = newVal;
          }
          childOb = !shallow && observe(newVal);
          dep.notify();
        }
      });
    }
  12.  在 getter 中因为存在 if(Dep.target),步骤8中赋值实现,Dep.target = Watcher 类实例 watcher,执行 dep.depend();

  13. watcher.addDep(this); // 将这个 watcher 实例加入到此 data属性的依赖收集 Dep 中

  14. popTarget(); 中将 Dep.target = null;

  15. watcher.cleanupDeps(); // 清除依赖收集

  16. 当 data 属性值发生变化时,触发 setter 函数,执行该 data属性上的 dep.notify()。

  17.  dep.notify(); // 遍历 dep.subs 里的所有 watcher, 执行 watcher.update();

    Dep.prototype.notify = function notify () {
      var subs = this.subs.slice();
      for (var i = 0, l = subs.length; i < l; i++) {
        subs[i].update();
      }
    };
  18. watcher.update();

    Watcher.prototype.update = function update () {
      if (this.lazy) {
        this.dirty = true;
      } else if (this.sync) {
        this.run();
      } else {
        queueWatcher(this);
      }
    };
  19. watcher.run();

    Watcher.prototype.run = function run () {
        var value = this.get();
        var oldValue = this.value;
        this.value = value;
        this.cb.call(this.vm, value, oldValue);
    };

$mount 执行顺序

  1. 在 this._init() 方法中执行 vm.$mount(vm.$options.el);
  2. vm.$mount(vm.$options.el);
  3. var mount = Vue$3.prototype.$mount;最早的公共方法 Vue$3.prototype.$mount 被保存在 mount 中,然后重写了 Vue$3.prototype.$mount 方法;
    // public mount method
    Vue$3.prototype.$mount = function (
      el,
      hydrating
    ) {
      el = el && inBrowser ? query(el) : undefined;
      return mountComponent(this, el, hydrating)
    };
    
    ...
    
    var mount = Vue$3.prototype.$mount;
    Vue$3.prototype.$mount = function (
      el,
      hydrating
    ) {
      el = el && query(el);
      var options = this.$options;
      if (!options.render) {
        var template = options.template;
        if (template) {
          var ref = compileToFunctions(template, {
            shouldDecodeNewlines: shouldDecodeNewlines,
            shouldDecodeNewlinesForHref: shouldDecodeNewlinesForHref,
            delimiters: options.delimiters,
            comments: options.comments
          }, this);
          var render = ref.render;
          var staticRenderFns = ref.staticRenderFns;
          options.render = render;
          options.staticRenderFns = staticRenderFns;
        }
      }
      return mount.call(this, el, hydrating)
    };
  4. 执行 mountComponent();
    function mountComponent (
      vm,
      el,
      hydrating
    ) {
      vm.$el = el;
      
      callHook(vm, 'beforeMount');
      
      var updateComponent = function () {
        vm._update(vm._render(), hydrating);
      };
      
      new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */);
      hydrating = false;
    
      if (vm.$vnode == null) {
        vm._isMounted = true;
        callHook(vm, 'mounted');
      }
      return vm
    }
  5. vm._update(vm._render(), hydrating);
  6. vm._render();
    Vue.prototype._render = function () {
        var vm = this;
        var ref = vm.$options;
        var render = ref.render;
        var _parentVnode = ref._parentVnode;
        if (vm._isMounted) {
          for (var key in vm.$slots) {
            var slot = vm.$slots[key];
            if (slot._rendered || (slot[0] && slot[0].elm)) {
              vm.$slots[key] = cloneVNodes(slot, true /* deep */);
            }
          }
        }
        vm.$scopedSlots = (_parentVnode && _parentVnode.data.scopedSlots) 
                           || emptyObject;
        vnode = render.call(vm._renderProxy, vm.$createElement);   
        vnode.parent = _parentVnode;
        return vnode
    };
  7. vm._update();
    Vue.prototype._update = function (vnode, hydrating) {
        var vm = this;
        if (vm._isMounted) {
          callHook(vm, 'beforeUpdate');
        }
    
        vm.$el = vm.__patch__(
            vm.$el, vnode, hydrating, false,
            vm.$options._parentElm,
            vm.$options._refElm
        );
    };
  8.  vm.__patch__()
    function createPatchFunction (backend) {
        ...
        retrun function patch (oldVnode, vnode, hydrating,
                            removeOnly,
                            parentElm,
                            refElm) {
            ...
            createElm(
                vnode,
                insertedVnodeQueue,
                oldElm._leaveCb ? null : parentElm$1,
                nodeOps.nextSibling(oldElm)
            );
            invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch);
            return vnode.elm
        }
    }
    
    var patch = createPatchFunction({ nodeOps: nodeOps, modules: modules });
    
    Vue$3.prototype.__patch__ = inBrowser ? patch : noop;
  9. createElm();
    function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested) {
        vnode.elm = vnode.ns
            ? nodeOps.createElementNS(vnode.ns, tag)
            : nodeOps.createElement(tag, vnode);
        setScope(vnode);
        createChildren(vnode, children, insertedVnodeQueue);
        ...
        insert(parentElm, vnode.elm, refElm);
    }
  10. insert(parentElm, vnode.elm, refElm);
    function insert (parent, elm, ref$$1) {
        if (isDef(parent)) {
          if (isDef(ref$$1)) {
            if (ref$$1.parentNode === parent) {
              nodeOps.insertBefore(parent, elm, ref$$1);
            }
          } else {
            nodeOps.appendChild(parent, elm);
          }
        }
    }
    
    var nodeOps = Object.freeze({
    	createElement: createElement$1,
    	createElementNS: createElementNS,
    	createTextNode: createTextNode,
    	createComment: createComment,
    	insertBefore: insertBefore,
    	removeChild: removeChild,
    	appendChild: appendChild,
    	parentNode: parentNode,
    	nextSibling: nextSibling,
    	tagName: tagName,
    	setTextContent: setTextContent,
    	setAttribute: setAttribute
    });
  11. nodeOps.insertBefore(parent, elm, ref$$1),执行该方法后最终将 template 内容完整插入到  el:"#app"  元素中;

### 信噪比 (SNR) 的计算公式 对于一段含有噪声的信号 \( x(k) = s(k) + n(k), k=1,2,\ldots,N \),其中 \( s(k) \) 是有用信号而 \( n(k) \) 表示噪声成分,信噪比可以被定义为信号功率与噪声功率的比例。 #### 功率形式的 SNR 定义 当考虑的是平均功率时,信噪比可以用下述方式表达: \[ \text{SNR} = \frac{\mathbb{E}[|s(t)|^2]}{\mathbb{E}[|n(t)|^2]} \] 这里 \( \mathbb{E}[\cdot] \) 表示期望值运算符[^1]。 #### 均方根电压形式的 SNR 定义 如果具体到电信号,则通常会基于均方根(rms)电平来衡量: \[ \text{SNR}_{\text{rms}}=\left(\frac{s_{\text{rms}}}{n_{\text{rms}}}\right)^2 \] 此处 \( s_{\text{rms}} \) 和 \( n_{\text{rms}} \) 分别代表信号和噪声的有效值(即均方根值)。然而,在实际操作中更常见的是直接取两者的比例作为 SNR 而不是平方后的结果。 #### 实际应用场景中的估算方法 在一个具体的例子中,比如处理图像数据时,可以通过选取感兴趣区域内像素强度统计特性来进行简单估算。例如选择图像中心的一个小窗口(如100×100),并在此基础上求解该区域内的亮度均值以及标准差,进而得到近似的信噪比评估指标: \[ \text{SNR}_\text{(image)} = \frac{\mu_\text{signal}}{\sigma_\text{noise}} \] 这表明了在特定上下文中如何利用样本统计数据快速获得一个合理的 SNR 近似值[^2]。 ```python import numpy as np def calculate_snr(signal_data): mean_signal = np.mean(signal_data) std_noise = np.std(signal_data) snr = mean_signal / std_noise return snr ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值