VueJs 源码解析 (四) initRender.Js
文章出处:SmallW---原文地址
在之前的文章中提到了 vuejs 源码中的 架构部分,以及 谈论到了 vue 源码三要素 vm、compiler、watcher 这三要素,那么今天我们就从这三要素逐步了解清楚。这部分主要是来解读 render.js。
一、initRender 初始化 render 函数
核心代码一:
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false); // normalization is always applied for the public version, used in // user-written render functions. vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true);
解析:
这里我们先做个一个大胆的猜测:createElement() 方法的最后一个参数 是来判断是 开发者是否在调用vue的时候 使用了 render 函数,从而给了不同的 参数,去render。 后面,我们再来一一验证,前面的这些猜测.
找到 createElement() 方法
import { createElement } from '../vdom/create-element';
核心代码:
export function createElement(context, tag, data, children, normalizationType, alwaysNormalize) { if (Array.isArray(data) || isPrimitive(data)) { normalizationType = children; children = data; data = undefined; } if (isTrue(alwaysNormalize)) { normalizationType = ALWAYS_NORMALIZE; } return _createElement(context, tag, data, children, normalizationType);}
其中 createElement(context, tag, data, children, normalizationType, alwaysNormalize) context // 上下文tag // 标签data // 数据对象 (hook, on , pendingInsert)children // 子节点 (位置)
核心代码二:
/* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => { !isUpdatingChildComponent && warn(`$attrs is readonly.`, vm); }, true); defineReactive(vm, '$listeners', options._parentListeners || emptyObject, () => { !isUpdatingChildComponent && warn(`$listeners is readonly.`, vm); }, true); } else { defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true); defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true); }
其中 常见的 一个 vue 的方法是 defineReactive() 在 observe 文件夹下 找到 index.js 的 defineReactive()
核心代码:
export function defineReactive(obj, key, val, customSetter, shallow) { const dep = new Dep(); const property = Object.getOwnPropertyDescriptor(obj, key); if (property && property.configurable === false) { return; } // cater for pre-defined getter/setters const getter = property && property.get; const setter = property && property.set; if ((!getter || setter) && arguments.length === 2) { val = obj[key]; } let childOb = !shallow && observe(val); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter() { const value = getter ? getter.call(obj) : val; if (Dep.target) { dep.depend(); if (childOb) { childOb.dep.depend(); if (Array.isArray(value)) { dependArray(value); } } } return value; }, set: function reactiveSetter(newVal) { const 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 (process.env.NODE_ENV !== 'production' && customSetter) { customSetter(); } if (setter) { setter.call(obj, newVal); } else { val = newVal; } childOb = !shallow && observe(newVal); dep.notify(); } });}
这个核心的代码,就是 Vue 的 实现数据监听以及 单向数据流的 主要方式。
二、initInjections 初始化
核心代码 :
export function initInjections(vm) { const result = resolveInject(vm.$options.inject, vm); if (result) { toggleObserving(false); Object.keys(result).forEach(key => { /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { defineReactive(vm, key, result[key], () => { warn(`Avoid mutating an injected value directly since the changes will be ` + `overwritten whenever the provided component re-renders. ` + `injection being mutated: "${key}"`, vm); }); } else { defineReactive(vm, key, result[key]); } }); toggleObserving(true); }}
三、initState 初始化
核心代码 :
export function initState(vm) { vm._watchers = []; const 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); }}
判断传入的 options 对象中是否有 props methods data computed watch 等属性如果有的话, 会分别去 进行实例化操作。
initProps(vm, opts.props)
defineReactive(props, key, value)
initMethods(vm, opts.methods)
for (const key in methods) { ... vm[key] = methods[key] == null ? noop : bind(methods[key], vm); }
initData(vm)
function initData(vm) { ... // observe data observe(data, true /* asRootData */);}
initComputed(vm, opts.computed)
const watchers = vm._computedWatchers = Object.create(null); // computed properties are just getters during SSR const isSSR = isServerRendering(); ... if (!isSSR) { // create internal watcher for the computed property. watchers[key] = new Watcher(vm, getter || noop, noop, computedWatcherOptions); }
initWatch(vm, opts.watch)
createWatcher(vm, key, handler)function createWatcher(vm, expOrFn, handler, options) { if (isPlainObject(handler)) { options = handler; handler = handler.handler; } if (typeof handler === 'string') { handler = vm[handler]; } return vm.$watch(expOrFn, handler, options);}
看到了 这里是不是 突然有种 豁然开朗的感觉, 各种属性方法的实例化,让我们比较清楚的知道了在props methods data computed watch 等属性或者方法中 的内部变化。