vue源码解读
2019-12-22 22:59:08 0 举报仅支持查看
AI智能生成
vue源码笔记
vue
模版推荐
作者其他创作
大纲/内容
入口
vue\package.json
scripts: { dev: ... scripts/config.js ... :web-full-dev }
vue\scripts\config.js
web-full-dev: {<br> entry: resolve('web/entry-runtime-with-compiler.js')<br> }
/* 扩展$mount */<br>vue\src\platforms\web\entry-runtime-with-compiler.js
const mount = Vue.prototype.$mount // 缓存$mount
// 对$mount进行覆盖及扩充,把传入的模板生成渲染函数<br>Vue.prototype.$mount = function ( ... ) {<br> el = el && query(el); // 获取传入的元素<br> const options = this.$options; // 获取实例的选项<br> if (!options.render) { // 如果选项中没有render<br> let template = options.template // 获取选项的template<br> if (template) {<br> // 如果有template的相关操作<br> }else if (el) {<br> template = getOuterHTML(el) // 如果有el就获取outerHtml<br> }<br> if (template) {<br> // 通过编译器得到render函数<br> const { render, staticRenderFns } = compileToFunctions(template, { ... }, this)<br> }<br> }<br> return mount.call(this, el, hydrating) // 把mount挂载到当前实例上<br>}
/* 实现$mount */<br>vue\src\platforms\web\runtime\index.js
// 指令和组件的全局注册<br>extend(Vue.options.directives, platformDirectives)<br>extend(Vue.options.components, platformComponents)
// 打补丁<br>Vue.prototype.__patch__ = inBrowser ? patch : noop
// 把得到的组件挂载到当前实例<br>Vue.prototype.$mount = function ( ... ): Component {<br> return mountComponent(this, el, hydrating)<br>}
/* initGlobalAPI */<br>vue\src\core\index.js
// 初始化全局API<br>initGlobalAPI(Vue)<br>
路径:vue\src\core\global-api\index.js
// 全局工具函数的初始化<br>Vue.util = {<br> warn,<br> extend,<br> mergeOptions,<br> defineReactive<br> }
// 全局方法的初始化<br>Vue.set = set<br> Vue.delete = del<br> Vue.nextTick = nextTick
Vue.observable = <T>(obj: T): T => {<br> observe(obj)<br> return obj<br> }
initUse(Vue)<br> initMixin(Vue)<br> initExtend(Vue)<br> initAssetRegisters(Vue)
vue\src\core\instance\index.js
// Vue的全局构造函数<br>function Vue (options) {<br> this._init(options)<br>}
// 实现_init函数<br>initMixin(Vue)
路径:vue\src\core\instance\init.js
Vue.prototype._init = function ( ... ) {<br> if (options && options._isComponent) {<br> initInternalComponent(vm, options)<br> } else {<br> vm.$options = mergeOptions(<br> resolveConstructorOptions(vm.constructor),<br> options || {},<br> vm) // 整合自己的option和默认的option<br> }<br>}
// 初始化<br>initLifecycle(vm)<br> initEvents(vm)<br> initRender(vm)<br> callHook(vm, 'beforeCreate')<br> initInjections(vm) // resolve injections before data/props<br> initState(vm)<br> initProvide(vm) // resolve provide after data/props<br> callHook(vm, 'created')
路径:vue\src\core\instance\lifecycle.js
// initLifecycle初始化实例组件的常用属性<br>vm.$parent = parent<br> vm.$root = parent ? parent.$root : vm<br> vm.$children = []<br> vm.$refs = {}<br> vm._watcher = null<br>
路径:vue\src\core\instance\events.js
// initEvents事件的初始化<br>const listeners = vm.$options._parentListeners<br> if (listeners) {<br> // 如果父级有监听器,就更新组件的监听器<br> updateComponentListeners(vm, listeners) <br> }<br>
路径:vue\src\core\instance\render.js
// $slots和$scopedSlots初始化。createElement的声明。$attrs和$listeners的响应化<br>const options = vm.$options<br> const parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent tree<br> const renderContext = parentVnode && parentVnode.context<br><br> vm.$slots = resolveSlots(options._renderChildren, renderContext)<br> vm.$scopedSlots = emptyObject<br><br> // 把createElement函数挂载到当前组件上,编译器编译的时候需要用。柯里化<br> vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)<br><br> // 用户编写渲染函数使用这个<br> vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)<br><br> const parentData = parentVnode && parentVnode.data<br><br>// 两个响应式<br>defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)<br> defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true)<br>
路径:vue\src\core\instance\state.js
// 执行各种数据状态初始化,包括数据响应化<br>vm._watchers = []<br>const opts = vm.$options<br>// 初始化所有属性<br><br>if (opts.props) initProps(vm, opts.props)<br>// 初始化所有回调函数<br> if (opts.methods) initMethods(vm, opts.methods)<br>// 数据响应化<br> if (opts.data) {<br> initData(vm)<br> } else {<br> observe(vm._data = {}, true /* asRootData */)<br> }<br>// 初始化computed<br> if (opts.computed) initComputed(vm, opts.computed)<br>// 初始化watch监听<br> if (opts.watch && opts.watch !== nativeWatch) {<br> initWatch(vm, opts.watch)<br> }<br>
function initData (vm: Component) {<br> let data = vm.$options.data<br> // 如果选项中的data是函数就调用getData得到正确的格式,否则就直接取用或定义为空对象<br> data = vm._data = typeof data === 'function'<br> ? getData(data, vm)<br> : data || {}<br><br> // proxy data on instance 把data代理到当前实例<br> const keys = Object.keys(data)<br> const props = vm.$options.props<br> const methods = vm.$options.methods<br> // observe data 劫持监听data做数据响应式<br> observe(data, true /* asRootData */)<br>}
路径:vue-dev\src\core\observer\index.js
// observe方法返回一个Observer实例<br><br>let ob: Observer | void<br> if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {<br> ob = value.__ob__ // 如果data中有observe就用本来有的<br> } else if (<br> shouldObserve &&<br> !isServerRendering() &&<br> (Array.isArray(value) || isPlainObject(value)) &&<br> Object.isExtensible(value) &&<br> !value._isVue<br> ) {<br> ob = new Observer(value) // 如果没有就new一个Observer观察者实例<br> }<br> return ob // 返回观察者
// Observer类根据数据类型做数据响应化<br>export class Observer {<br> value: any;<br> dep: Dep;<br> vmCount: number; // number of vms that have this object as root $data<br> constructor (value: any) {<br> this.value = value<br> this.dep = new Dep()<br> this.vmCount = 0<br> def(value, '__ob__', this)<br> // 覆盖数组原型<br> if (Array.isArray(value)) {<br> if (hasProto) {<br> // 替换数组原型 value.__proto__ = arraryMethods<br> protoAugment(value, arrayMethods)<br> } else {<br> copyAugment(value, arrayMethods, arrayKeys)<br> }<br> this.observeArray(value)<br> } else {<br> this.walk(value)<br> }<br> }<br> // 如果data是对象的处理方法<br> walk (obj: Object) {<br> const keys = Object.keys(obj)<br> for (let i = 0; i < keys.length; i++) {<br> defineReactive(obj, keys[i])<br> }<br> }<br> // 如果data是数组的处理方法<br> observeArray (items: Array<any>) {<br> for (let i = 0, l = items.length; i < l; i++) {<br> observe(items[i])<br> }<br> }<br>} <br>
路径:vue-dev\src\core\observer\array.js
// 数组响应化。数组数据变化的侦测跟对象不同,通常操作数组使用push、splice、pop等方法,此时没法知道数据的变化。<br>// Vue采取的办法是拦截这些方法并通知dep<br><br>// 数组原型<br>const arrayProto = Array.prototype<br>// 修改后的原型<br>export const arrayMethods = Object.create(arrayProto)<br>// 七个待修改的数组方法<br>const methodsToPatch = [<br> 'push',<br> 'pop',<br> 'shift',<br> 'unshift',<br> 'splice',<br> 'sort',<br> 'reverse'<br>]<br>/**<br> * 拦截这些方法并发出通知<br> */<br>methodsToPatch.forEach(function (method) {<br> // 原始数组方法<br> const original = arrayProto[method]<br> // 修改这些方法的descriptor。def相当是defineProperty<br> def(arrayMethods, method, function mutator (...args) {<br> // 原始操作<br> const result = original.apply(this, args)<br> // 获取ob实例用于发送通知<br> const ob = this.__ob__<br> // 三个能新增元素的方法特殊处理<br> let inserted<br> switch (method) {<br> case 'push':<br> case 'unshift':<br> inserted = args<br> break<br> case 'splice':<br> inserted = args.slice(2)<br> break<br> }<br> // 若有新增则做响应处理<br> if (inserted) ob.observeArray(inserted)<br> // 通知更新<br> ob.dep.notify()<br> return result<br> })<br>})<br>
// defineReactive方法定义对象属性的getter/setter方法,getter添加依赖,setter通知更新<br><br>const dep = new Dep()<br> const property = Object.getOwnPropertyDescriptor(obj, key)<br> if (property && property.configurable === false) {<br> return<br> }<br> const getter = property && property.get<br> const setter = property && property.set<br> if ((!getter || setter) && arguments.length === 2) {<br> val = obj[key] // 一个key对应一个Dep实例<br> }<br>// 递归执行子对象响应化<br> let childOb = !shallow && observe(val)<br>// 定义对象属性的getter/setter<br>Object.defineProperty(obj, key, {<br> enumerable: true,<br> configurable: true,<br> get: function reactiveGetter () {<br> const value = getter ? getter.call(obj) : val<br> if (Dep.target) {<br> dep.depend() // getter被调用的时如果存在依赖就追加<br> if (childOb) {<br> childOb.dep.depend() // 若也存在子Observer,则依赖也追加到子ob<br> if (Array.isArray(value)) {<br> dependArray(value) // 数组的处理方式<br> }<br> }<br> }<br> return value<br> },<br> set: function reactiveSetter (newVal) {<br> const value = getter ? getter.call(obj) : val<br> if (newVal === value || (newVal !== newVal && value !== value)) {<br> return<br> }<br> if (process.env.NODE_ENV !== 'production' && customSetter) {<br> customSetter()<br> }<br> if (getter && !setter) return<br> // 如果值变了就更新值<br> if (setter) {<br> setter.call(obj, newVal)<br> } else {<br> val = newVal<br> }<br> childOb = !shallow && observe(newVal) // 递归更新子对象<br> dep.notify() // 通知更新<br> }<br> })<br>// 数组的所有项添加依赖,数据变化的时候就可以通过__ob__.dep发送通知<br>function dependArray (value: Array<any>) {<br> for (let e, i = 0, l = value.length; i < l; i++) {<br> e = value[i]<br> e && e.__ob__ && e.__ob__.dep.depend()<br> if (Array.isArray(e)) {<br> dependArray(e)<br> }<br> }<br>}<br>
路径:vue-dev\src\core\observer\dep.js<br>
// Dep类负责管理一组Watcher,包括watcher的增删以及通知更新<br><br>static target: ?Watcher; // 依赖收集时的Watcher引用<br> id: number;<br> subs: Array<Watcher>; // Watcher数组<br> constructor () {<br> this.id = uid++<br> this.subs = []<br> }<br>// 添加Watcher实例<br> addSub (sub: Watcher) {<br> this.subs.push(sub)<br> }<br>// 移除Watcher实例<br> removeSub (sub: Watcher) {<br> remove(this.subs, sub)<br> }<br>// 添加Watcher和Dep的相互引用<br> depend () {<br> if (Dep.target) {<br> Dep.target.addDep(this)<br> }<br> }<br>// 批量通知更新<br> notify () {<br> const subs = this.subs.slice()<br> if (process.env.NODE_ENV !== 'production' && !config.async) {<br> subs.sort((a, b) => a.id - b.id)<br> }<br> for (let i = 0, l = subs.length; i < l; i++) {<br> subs[i].update()<br> }<br> }<br>
路径:vue-dev\src\core\observer\watcher.js
// Watcher类 解析一个表达式并收集依赖,当数值发生变化触发回到函数,常用于watcher API和指令中<br>// 每个组件都有一个对应的Watcher,当数值发生改变调用其update函数并重新渲染<br><br>constructor (<br> vm: Component,<br> expOrFn: string | Function,<br> cb: Function,<br> options?: ?Object,<br> isRenderWatcher?: boolean<br> ) {<br> this.vm = vm<br> // 组件保存render watcher<br> if (isRenderWatcher) {<br> vm._watcher = this<br> }<br> // 组件保存非render watcher<br> vm._watchers.push(this)<br><br> // options<br><br> // 将表达式解析为getter函数<br> if (typeof expOrFn === 'function') {<br> // 那些和组件实例对应的watcher一起创建时会传递组件更新函数updateComponent<br> this.getter = expOrFn // 如果是函数直接指定为getter<br> } else {<br> // 这种是$watch传递进来的表达式,它们需要被解析成函数<br> this.getter = parsePath(expOrFn)<br> if (!this.getter) {<br> this.getter = noop<br> }<br> }<br> this.value = this.lazy ? undefined : this.get() // 如果是非延迟watcher则立即执行getter<br>}<br><br>// 模拟getter,重新收集依赖<br>get () {<br> pushTarget(this)<br> let value<br> const vm = this.vm<br> try {<br> // 从组件中获取value并触发依赖收集<br> value = this.getter.call(vm, vm)<br> } catch (e) {<br> } finally {<br> // 递归触发深层属性<br> if (this.deep) {<br> traverse(value)<br> }<br> popTarget()<br> this.cleanupDeps()<br> }<br> return value<br> }<br><br>addDep (dep: Dep) {<br> const id = dep.id<br> if (!this.newDepIds.has(id)) {<br> // watcher添加dep引用<br> this.newDepIds.add(id)<br> this.newDeps.push(dep)<br> if (!this.depIds.has(id)) {<br> // dep添加watcher引用<br> dep.addSub(this)<br> }<br> }<br> }<br>update () {<br> // 更新逻辑<br> if (this.lazy) {<br> this.dirty = true<br> } else if (this.sync) {<br> this.run()<br> } else {<br> // 默认lazy和sync都是false<br> queueWatcher(this)<br> }<br> } <br>
路径:vue-dev\src\core\observer\scheduler.js
// 异步更新队列<br>// 执行watcher入队操作,如果id重复就跳过<br><br>export function queueWatcher (watcher: Watcher) {<br> const id = watcher.id<br> if (has[id] == null) { // id不存在才入队<br> has[id] = true<br> if (!flushing) { // 如果没有在执行更新,则插入到队尾<br> queue.push(watcher)<br> } else {<br> // 若已更新,根据id插入队列<br> // 若已经更新过了,就在下次刷新时立即执行<br> let i = queue.length - 1<br> while (i > index && queue[i].id > watcher.id) {<br> i--<br> }<br> queue.splice(i + 1, 0, watcher)<br> }<br> // 刷新队列<br> if (!waiting) {<br> waiting = true<br> if (process.env.NODE_ENV !== 'production' && !config.async) {<br> flushSchedulerQueue()<br> return<br> }<br> nextTick(flushSchedulerQueue)<br> }<br> }<br>} <br>
路径:vue-dev\src\core\util\next-tick.js
// nextTick按照特定的异步策略执行队列刷新操作<br>export function nextTick (cb?: Function, ctx?: Object) {<br> let _resolve<br> // cb不是立即执行,是加入到数组中,等待调用<br> callbacks.push(() => {<br> if (cb) {<br> try {<br> cb.call(ctx) // 真正执行cb<br> } catch (e) {<br> handleError(e, ctx, 'nextTick')<br> }<br> } else if (_resolve) {<br> _resolve(ctx)<br> }<br> })<br> // 没有处在挂起状态就执行异步队列<br> if (!pending) {<br> pending = true<br> timerFunc() // 时间函数<br> }<br> // $flow-disable-line<br> if (!cb && typeof Promise !== 'undefined') {<br> return new Promise(resolve => {<br> _resolve = resolve<br> })<br> }<br>}<br><br>// 定义时间函数<br>let timerFunc<br>// nextTick异步行为通过微任务队列<br>if (typeof Promise !== 'undefined' && isNative(Promise)) {<br> const p = Promise.resolve()<br> timerFunc = () => {<br> p.then(flushCallbacks)<br> if (isIOS) setTimeout(noop)<br> }<br> isUsingMicroTask = true<br>} else if (!isIE && typeof MutationObserver !== 'undefined' && (<br> isNative(MutationObserver) ||<br>MutationObserver.toString() === '[object MutationObserverConstructor]'<br>)) {<br> // 不能用Promise时<br> let counter = 1<br> const observer = new MutationObserver(flushCallbacks)<br> const textNode = document.createTextNode(String(counter))<br> observer.observe(textNode, {<br> characterData: true<br> })<br> timerFunc = () => {<br> counter = (counter + 1) % 2<br> textNode.data = String(counter)<br> }<br> isUsingMicroTask = true<br>} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {<br> // 退化到setImmediate,利用的是宏任务队列<br> timerFunc = () => {<br> setImmediate(flushCallbacks)<br> }<br>} else {<br> // 最后退化到setTimeout,也是宏任务队列<br> timerFunc = () => {<br> setTimeout(flushCallbacks, 0)<br> }<br>}<br>
defineReactive中的getter和setter对应着订阅和发布行为<br>Dep相当于主题Subject,维护订阅者、通知观察者更新<br>Watcher相当于观察者Observer,执行更新<br>Vue中的Observer不是上面说的观察者,它和data中的对象一一对应,有内嵌的对象就会有child Obsever与之对应
stateMixin(Vue)<br>
路径:vue\src\core\instance\state.js
// 定义$data、$props两个实例属性和$set、$delete、$watch三个实例方法<br><br>const dataDef = {}<br>dataDef.get = function () { return this._data }<br> const propsDef = {}<br> propsDef.get = function () { return this._props }<br>Object.defineProperty(Vue.prototype, '$data', dataDef)<br>Object.defineProperty(Vue.prototype, '$props', propsDef)<br><br>Vue.prototype.$set = set<br>Vue.prototype.$delete = del<br><br>// $watch是与数据响应机制息息相关的API,它指定一个监控表达式,当数据变化时执行回调函数<br>Vue.prototype.$watch = function ( <br> expOrFn: string | Function,<br> cb: any,<br> options?: Object<br>): Function {<br> const vm: Component = this<br> // 对象形式回调解析<br> if (isPlainObject(cb)) {<br> return createWatcher(vm, expOrFn, cb, options)<br> }<br> options = options || {}<br> options.user = true<br> // 创建watcher监听数值变化<br> const watcher = new Watcher(vm, expOrFn, cb, options)<br> if (options.immediate) { // 如果实例的选项中有immediate就立即执行一次cb }<br> return function unwatchFn () {<br> watcher.teardown() // 返回一个监听的销毁<br> }<br>}
eventsMixin(Vue)
路径:vue-dev\src\core\instance\events.js
// 定义$on、$once、$off、$emit四个事件相关的实例方法<br><br>Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component { ... }<br>Vue.prototype.$once = function (event: string, fn: Function): Component { ... }<br>Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component { ... }<br>Vue.prototype.$emit = function (event: string): Component { ... }<br>
lifecycleMixin(Vue)
路径:vue-dev\src\core\instance\lifecycle.js
// 定义_update、$forceUpdate、$destroy三个生命周期钩子函数<br>// _update方法是组件周期中关键方法,组件触发更新就会调用该函数,执行组件的diff和patch等方法<br><br>Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) { <br> const vm: Component = this<br> const prevEl = vm.$el<br> const prevVnode = vm._vnode<br> const restoreActiveInstance = setActiveInstance(vm)<br> vm._vnode = vnode<br> if (!prevVnode) {<br> // initial render 第一次初始化时调用<br> vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)<br> } else {<br> // updates 更新时调用<br> vm.$el = vm.__patch__(prevVnode, vnode)<br> }<br>}<br>Vue.prototype.$forceUpdate = function () { ... }<br>Vue.prototype.$destroy = function () { ... }<br>
renderMixin(Vue)
路径:vue-dev\src\core\instance\render.js
// 定义$nextTick和_render函数<br>Vue.prototype.$nextTick = function (fn: Function) {<br> return nextTick(fn, this)<br> }<br>Vue.prototype._render = function (): VNode {<br> const vm: Component = this<br> const { render, _parentVnode } = vm.$options<br> if (_parentVnode) {<br> vm.$scopedSlots = normalizeScopedSlots(<br> _parentVnode.data.scopedSlots,<br> vm.$slots,<br> vm.$scopedSlots<br> )<br> } <br> vm.$vnode = _parentVnode<br> // render self<br> let vnode<br> try {<br> currentRenderingInstance = vm<br> vnode = render.call(vm._renderProxy, vm.$createElement) <br> } ......<br> vnode.parent = _parentVnode<br> return vnode<br>}
Collect
Get Started
Collect
Get Started
Collect
Get Started
Collect
Get Started
评论
0 条评论
下一页