摘要:說明源碼萬多行,完全解析透太耗時間里面細(xì)節(jié)處理很多,通讀代碼,語法都不難個人認(rèn)為重點在于理解它的思想,掌握面向數(shù)據(jù)編程的原理。一案例代碼及運行流程用說明里面提供的命令行,生成的項目,稍微改動。
說明
vue源碼1萬多行,完全解析透太耗時間;里面細(xì)節(jié)處理很多,通讀代碼,語法都不難;個人認(rèn)為重點在于理解它的思想,掌握面向數(shù)據(jù)編程的原理。
通過一個合適的例子,斷點調(diào)試來查看代碼運行流程,可以快速了解編碼的思路。
vue@2.5.13
用api說明里面提供的命令行,生成的vue項目,稍微改動。
目錄結(jié)構(gòu):
components/HelloWorld.vue
router/index.js
import Vue from "vue" import Router from "vue-router" import index from "../index.vue" Vue.use(Router) export default new Router({ routes: [ { path: "/", name: "index", component: index } ] })
App.vue
index.vue
main.js
import Vue from "vue" import App from "./App" import router from "./router" import helloworld from "./components/HelloWorld.vue" Vue.config.productionTip = false /* eslint-disable no-new */ new Vue({ el: "#app", router, components: { App, helloworld}, template: "斷點運行流程" })
建議用工具打開,里面的縮進(jìn)代表函數(shù)的層級關(guān)系
function Vue$3 (options) { //創(chuàng)建新的vue實例 this._init(options); //初始化 vm.$options = mergeOptions( //混合options resolveConstructorOptions(vm.constructor), //獲取構(gòu)造函數(shù)Vue$3的options對象;key有beforeCreate、components、destroyed、directives、filters、_base options || {}, vm ); checkComponents(child);//驗證options里面components的命名 validateComponentName(key);//校驗components的命名合法性 normalizeProps(child, vm);//規(guī)范props;無值返回;數(shù)組[string]返{string:{ type: null }},對象{key:val}返回 val為string為{key:{type:val}},val為obj為{key:val} normalizeInject(child, vm);//規(guī)范inject;無值返回;數(shù)組[string]返{string:{ from: string }},對象{key:val}返回 val為string為{key:{from:val}},val為obj為{key:extend({from:key},val)} normalizeDirectives(child);//規(guī)范directives;無值不處理;默認(rèn)為對象{key:def},僅處理def類型為function,返回{key:{bind:def,update:def}} mergeField(key); //parent //components、directives、filters調(diào)用mergeAssets; 淺合并child到parent //_base調(diào)用defaultStrat; child無值取parent,有值取child //beforeCreate、destroyed調(diào)用mergeHook; child無值取parent,child有值 parent有值取parent.concat(child);parent無值 child是數(shù)組取child,不是數(shù)組取[child] //child父級無此屬性執(zhí)行 //el調(diào)用strats.el; return defaultStrat(parent, child) //router、template調(diào)用defaultStrat //components父級有,忽略 initProxy(vm); vm._renderProxy = new Proxy(vm, handlers);//handlers = hasHandler initLifecycle(vm); /** 此時vm={ _uid: 0, _isVue: true, $options: { beforeCreate: [1], destroyed: [1], directives: {}, filters: {}, _base: Vue$3(options), el: "#app", router: VueRouter, template: "二、數(shù)據(jù)雙向綁定原理", components:{ App: {}, helloworld: {} } }, _renderProxy: new Proxy(vm, hasHandler), $parent: undefined, $root: vm, $children: [], $refs: {}, _watcher: null, _inactive: null, _directInactive: false, _isMounted: false, _isDestroyed: false, _isBeingDestroyed: false } **/ initEvents(vm); /** vm添加 { _events: {}, _hasHookEvent: false } **/ initRender(vm); /** vm添加 { _vnode: null, _staticTrees: null, $vnode: undefined, $slots: {}, $scopedSlots: {}, _c: function (a, b, c, d) { return createElement(vm, a, b, c, d, false); }, $createElement: function (a, b, c, d) { return createElement(vm, a, b, c, d, true); } } **/ defineReactive(); var dep = new Dep(); /** 此時vm添加 { $attrs: , $listeners: } 監(jiān)聽屬性變動 **/ callHook(vm, "beforeCreate"); beforeCreate: function beforeCreate () {}//調(diào)用vue-router.js方法 this._router.init(this);//調(diào)用vue-router.js this.apps.push(app);//app為此vue實例 var setupHashListener = function () { history.setupListeners(); }; history.transitionTo( history.getCurrentLocation(),//調(diào)return getHash();返回href; setupHashListener, setupHashListener ); Vue.util.defineReactive(this, "_route", this._router.history.current); var dep = new Dep(); registerInstance(this, this); Vue.extend = function (extendOptions) { validateComponentName(name); var Sub = function VueComponent (options) { this._init(options); }; Sub.options = mergeOptions( Super.options, extendOptions ); return Sub; initInjections(vm); // resolve injections before data/props var result = resolveInject(vm.$options.inject, vm); initState(vm); /** vm添加 { _watchers: [], _data: {} } **/ observe(vm._data = {}, true /* asRootData */); ob = new Observer(value); var dep = new Dep(); def(value, "__ob__", this); this.walk(value); initProvide(vm); callHook(vm, "created"); vm.$mount(vm.$options.el); el = el && query(el);//獲取el元素 var ref = compileToFunctions(template, {}, this) new Function("return 1"); var compiled = compile(template, options); var finalOptions = Object.create(baseOptions); /* baseOptions = { expectHTML: true, modules: [{ staticKeys: ["staticClass"], transformNode: transformNode, genData: genData }, { staticKeys: ["staticStyle"], transformNode: transformNode$1, genData: genData$1 }, { preTransformNode: preTransformNode }], directives: { model: model, text: text, html: html }, isPreTag: isPreTag, isUnaryTag: isUnaryTag, mustUseProp: mustUseProp, canBeLeftOpenTag: canBeLeftOpenTag, isReservedTag: isReservedTag, getTagNamespace: getTagNamespace, staticKeys: genStaticKeys(modules$1) }; */ var compiled = baseCompile(template, finalOptions); var ast = parse(template.trim(), options); transforms = pluckModuleFunction(options.modules, "transformNode"); //例:pluckModuleFunction(modules,key);遍歷modules,返回鍵為key的值的數(shù)組 //transforms=[transformNode,transformNode$1] preTransforms = pluckModuleFunction(options.modules, "preTransformNode"); postTransforms = pluckModuleFunction(options.modules, "postTransformNode"); parseHTML(template, {}) var startTagMatch = parseStartTag(); /*例 startTagMatch = { attrs: [], end: 6, start: 0, tagName: "App", unarySlash: "/" } */ handleStartTag(startTagMatch); options.start(tagName, attrs, unary, match.start, match.end); var element = createASTElement(tag, attrs, currentParent); /* element = { type: 1, tag: tag, attrsList: attrs, attrsMap: makeAttrsMap(attrs), parent: parent, children: [] } */ element = preTransforms[i](element, options) || element;//preTransformNode(el, options);僅處理tag為input processPre(element);//v-pre processFor(element);//v-for //getAndRemoveAttr(el, name, removeFromMap)刪除el.attrsList數(shù)組里的name,removeFromMap為真刪除el.attrsMap[name],返回el.attrsMap[name] processIf(element);//v-if v-else-if v-else processOnce(element);//v-once processElement(element, options); processKey(element);//key //getBindingAttr(el, name, getStatic);返回綁定的屬性值;有:name或v-bind:name返回parseFilters(getAndRemoveAttr(el,":"+name||"v-bind:"+name)),沒有動態(tài)屬性值查找靜態(tài);getStatic不為false,有name,返回JSON.stringify(getAndRemoveAttr(el,name)) processRef(element);//ref processSlot(element);//slot||template||slot-scope processComponent(element);//is||inline-template element = transforms[i](element, options) || element;//transformNode(element, options);transformNode$1(element, options);處理class和style processAttrs(element);//處理attrsList里的屬性值 checkRootConstraints(root);//組件根約束,slot、template、v-for closeElement(element); parseEndTag(); return root; /* root={ attrsList: [], attrsMap: {}, children: [], parent: undefined, plain: true, tag: "App", type: 1 } */ optimize(ast, options); isStaticKey = genStaticKeysCached(options.staticKeys || ""); /* isStaticKey=function (val) { return map[val]; } map={ type: true, tag: true, attrsList: true, attrsMap: true, plain: true, parent: true, children: true, attrs: true, staticClass: true, staticStyle: true } */ markStatic$1(root); node.static = isStatic(node);//false markStaticRoots(root, false); node.staticRoot = false; var code = generate(ast, options); var state = new CodegenState(options); var code = ast ? genElement(ast, state) : "_c("div")"; //code="_c("App")" /* code={ render: "with(this){return _c("App")}", staticRenderFns: [] } */ /* compiled={ ast: { attrsList: [], attrsMap: {}, children: [], parent: undefined, plain: true, static: false, staticRoot: false, tag: "App", type: 1 }, render: "with(this){return _c("App")}", staticRenderFns: [] } */ errors.push.apply(errors, detectErrors(compiled.ast)); checkNode(ast, errors); /* compiled加{ errors: [], tips: [] } */ res.render = createFunction(compiled.render, fnGenErrors); return new Function(code) /* res={ render: function anonymous(){with(this){return _c("App")}} staticRenderFns: [] } */ return mount.call(this, el, hydrating) /*注: var mount = Vue$3.prototype.$mount; //1 Vue$3.prototype.$mount = function(){} //2 初次調(diào)用,1被2重寫,調(diào)用2; 此時調(diào)用,指定mount,調(diào)用1; */ el = el && inBrowser ? query(el) : undefined;//獲取el元素 return el; return mountComponent(this, el, hydrating) callHook(vm, "beforeMount"); updateComponent = function () { vm._update(vm._render(), hydrating); }; new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */); /* 此時Watcher Watcher={ active: true, cb: ? noop(a, b, c), deep: false, depIds: Set(0) {}, deps: [], dirty: false, expression: "function () {? vm._update(vm._render(), hydrating);? }", getter: ? (), id: 1, lazy: false, newDepIds: Set(0) {}, newDeps: [], sync: false, user: false, vm : vue實例 } */ this.get(); pushTarget(this);//將Watcher實例賦值給Dep._target; value = this.getter.call(vm, vm); vm._update(vm._render(), hydrating); Vue.prototype._render vnode = render.call(vm._renderProxy, vm.$createElement); vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); }; return _createElement(context, tag, data, children, normalizationType)//(vue實例, "App", undefined, undefined, undefined) vnode = createComponent(Ctor, data, context, children, tag);//(組件App, undefined, vue實例, undefined, "App" ) Ctor = baseCtor.extend(Ctor); validateComponentName(name); var Sub = function VueComponent (options) { Sub.options = mergeOptions(Super.options,extendOptions); checkComponents(child); normalizeProps(child, vm); name = camelize(key); normalizeInject(child, vm); normalizeDirectives(child); mergeField(key); //parent //components、directives、filters調(diào)用mergeAssets; 淺合并child到parent,child無值取{} //_base調(diào)用defaultStrat; child無值取parent,有值取child //beforeCreate、destroyed調(diào)用mergeHook; child無值取parent,child有值 parent有值取parent.concat(child);parent無值 child是數(shù)組取child,不是數(shù)組取[child] //child父級無此屬性執(zhí)行 //beforeDestroy調(diào)用mergeHook; //name、render、staticRenderFns、_compiled、_file、_Ctor調(diào)用defaultStrat //beforeCreate父級有,忽略 ASSET_TYPES.forEach(function (type) {}) return Sub; //Ctor=Sub; resolveConstructorOptions (Ctor) var superOptions = resolveConstructorOptions(Ctor.super); //遞歸獲取最初的options //返回混合后的options var propsData = extractPropsFromVNodeData(data, Ctor, tag); mergeHooks(data);//data={on:undefined}; var vnode = new VNode()//("vue-component-4-App",data1, undefined, undefined, undefined, vue實例,組件options, undefined,) /* data1={ hook: { destroy: ? destroy(vnode), init: ? init( vnode, hydrating, parentElm, refElm ), insert: ? insert(vnode), prepatch: ? prepatch(oldVnode, vnode) }, on: undefined } 組件options={ Ctor: ? VueComponent(options) children: undefined listeners: undefined propsData: undefined tag: "App" } */ return vnode; /* vnode={ asyncFactory: undefined asyncMeta: undefined children: undefined componentInstance: undefined componentOptions: {Ctor: ?, propsData: undefined, listeners: undefined, tag: "App", children: undefined} context: Vue$3 {_uid: 0, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: Vue$3, …} data: {on: undefined, hook: {…}} elm: undefined fnContext: undefined fnOptions: undefined fnScopeId: undefined isAsyncPlaceholder: false isCloned: false isComment: false isOnce: false isRootInsert: true isStatic: false key: undefined ns: undefined parent: undefined raw: false tag: "vue-component-4-App" text: undefined } */ return vnode //vnode //vnode return vnode; //參數(shù)vm._render()為vnode = new VNode(); vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false, vm.$options._parentElm, vm.$options._refElm) oldVnode = emptyNodeAt(oldVnode); return new VNode(nodeOps.tagName(elm).toLowerCase(), {}, [], undefined, elm) var oldElm = oldVnode.elm; var parentElm$1 = nodeOps.parentNode(oldElm);//parentElm$1=body; createElm(vnode, insertedVnodeQueue, oldElm._leaveCb ? null : parentElm$1, nodeOps.nextSibling(oldElm) ); if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {} i(vnode, false /* hydrating */, parentElm, refElm);//i=componentVNodeHooks.init var child = vnode.componentInstance = createComponentInstanceForVnode()//(vnode,vue實例,body,下一節(jié)點) return new vnode.componentOptions.Ctor(options) /* 調(diào)function VueComponent (options); options={ parent: Vue$3 {_uid: 0, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: Vue$3, …} _isComponent: true _parentElm: body _parentVnode: VNode {tag: "vue-component-4-App", data: {…}, children: undefined, text: undefined, elm: undefined, …} _refElm: text } */ this._init(options); /* 重新調(diào)用_init方法; var Sub = function VueComponent (options) { this._init(options); }; */ initInternalComponent(vm, options); initProxy(vm); vm._renderProxy = new Proxy(vm, handlers);//handlers為getHandler initLifecycle(vm); initEvents(vm); initRender(vm); defineReactive(vm, "$attrs", parentData && parentData.attrs || emptyObject, function () { !isUpdatingChildComponent && warn("$attrs is readonly.", vm); }, true); defineReactive(vm, "$listeners", options._parentListeners || emptyObject, function () { !isUpdatingChildComponent && warn("$listeners is readonly.", vm); }, true); callHook(vm, "beforeCreate"); initInjections(vm); // resolve injections before data/props initState(vm); initProvide(vm); // resolve provide after data/props callHook(vm, "created"); /* child={ $attrs: (...), $children: [], $createElement: ? (a, b, c, d), $listeners: (...), $options: {parent: Vue$3, _parentVnode: VNode, _parentElm: body, _refElm: text, propsData: undefined, …}, $parent: Vue$3 {_uid: 0, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: Vue$3, …}, $refs: {}, $root: Vue$3 {_uid: 0, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: Vue$3, …}, $scopedSlots: {}, $slots: {}, $vnode: VNode {tag: "vue-component-4-App", data: {…}, children: undefined, text: undefined, elm: undefined, …}, _c: ? (a, b, c, d), _data: {__ob__: Observer}, _directInactive: false, _events: {}, _hasHookEvent: false, _inactive: null, _isBeingDestroyed: false, _isDestroyed: false, _isMounted: false, _isVue: true, _renderProxy: Proxy {_uid: 1, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}, _routerRoot: Vue$3 {_uid: 0, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: Vue$3, …}, _self: VueComponent {_uid: 1, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}, _staticTrees: null, _uid: 1, _vnode: null, _watcher: null, _watchers: [] } */ child.$mount(hydrating ? vnode.elm : undefined, hydrating); return mount.call(this, el, hydrating) return mountComponent(this, el, hydrating) callHook(vm, "beforeMount"); updateComponent = function () { vm._update(vm._render(), hydrating); }; new Watcher(vm, updateComponent, noop, null, true /* isRenderWatcher */); this.get(); pushTarget(this); value = this.getter.call(vm, vm); vm._update(vm._render(), hydrating); vnode = render.call(vm._renderProxy, vm.$createElement); /* App.vue; var render = function() { var _vm = this var _h = _vm.$createElement var _c = _vm._self._c || _h return _c( "div", { attrs: { id: "app" } }, [ _c("img", { attrs: { src: require("./assets/logo.png") } }), _vm._v(" "), _c("router-view") ], 1 ) } */ initComponent(vnode, insertedVnodeQueue); insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert); if (isPatchable(vnode)) {} invokeCreateHooks(vnode, insertedVnodeQueue); cbs.create[i$1](emptyNode, vnode);//updateAttrs、updateClass、updateDOMListeners、updateDOMProps、updateStyle、_enter_、create、updateDirectives //_enter_ enter(vnode); //create registerRef(vnode); setScope(vnode); nodeOps.createElement(tag, vnode); setScope(vnode); createChildren(vnode, children, insertedVnodeQueue); checkDuplicateKeys(children); //3個createElm createElm(children[i], insertedVnodeQueue, vnode.elm, null, true); if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {} nodeOps.createElement(tag, vnode); setScope(vnode); createChildren(vnode, children, insertedVnodeQueue); invokeCreateHooks(vnode, insertedVnodeQueue); cbs.create[i$1](emptyNode, vnode); insert(parentElm, vnode.elm, refElm); nodeOps.appendChild(parent, elm); createElm(children[i], insertedVnodeQueue, vnode.elm, null, true); if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {} vnode.elm = nodeOps.createTextNode(vnode.text); insert(parentElm, vnode.elm, refElm); nodeOps.appendChild(parent, elm); createElm(children[i], insertedVnodeQueue, vnode.elm, null, true); if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {} i(vnode, false /* hydrating */, parentElm, refElm); initComponent(vnode, insertedVnodeQueue); insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert); if (isPatchable(vnode)) {} invokeCreateHooks(vnode, insertedVnodeQueue); cbs.create[i$1](emptyNode, vnode);//updateAttrs、updateClass、updateDOMListeners、updateDOMProps、updateStyle、_enter_、create、updateDirectives //_enter_ enter(vnode); //create registerRef(vnode); setScope(vnode); invokeCreateHooks(vnode, insertedVnodeQueue); insert(parentElm, vnode.elm, refElm); nodeOps.insertBefore(parent, elm, ref$$1);//頁面展示出來,created data; invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch); return vnode.elm popTarget(); Dep.target = targetStack.pop(); this.cleanupDeps(); dep.removeSub(this$1); remove(this.subs, sub); this.newDepIds.clear(); return value;//value=undefined; return vm;//$el為div#app這個VueComponent;
這邊有一篇文章 剖析Vue原理&實現(xiàn)雙向綁定MVVM 講的很細(xì),就不再重復(fù)寫
以上面文章的內(nèi)容為基礎(chǔ)補充一張 vue數(shù)據(jù)雙向綁定原理圖
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/93298.html
摘要:而鉆研最好的方式,就是閱讀的源代碼。整個的源代碼,核心內(nèi)容包括兩部分。逃而動手腳的代碼,就存在于源代碼的中。整個源代碼讀下來一遍,雖然有些部分不太理解,但是對和一些代碼的使用的理解又加深了一步。 筆記中的Vue與Vuex版本為1.0.21和0.6.2,需要閱讀者有使用Vue,Vuex,ES6的經(jīng)驗。 起因 俗話說得好,沒有無緣無故的愛,也沒有無緣無故的恨,更不會無緣無故的去閱讀別人的源...
摘要:扎實基礎(chǔ)幸好自己之前花了大力氣去給自己打基礎(chǔ),讓自己現(xiàn)在的基礎(chǔ)還算不錯。 寫文章不容易,點個贊唄兄弟專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 如果你覺得排版難看,請點擊 下面鏈接 或者 拉到 下面關(guān)注公眾號也可以吧 【Vue原理】Vue源碼閱讀總結(jié)大會 - 序 閱讀源碼是需...
摘要:沒有具體對應(yīng)源碼分析,只是閱讀源碼的筆記,等之后學(xué)好點再寫成文章,構(gòu)造生成的。帶指令的所有元素,通過獲取,涉及,返回屬性選擇器對里的進(jìn)行綁定處理節(jié)點提取所有,返回數(shù)組,元素是對象,包含。 沒有具體對應(yīng)源碼分析,只是閱讀源碼的筆記,等之后學(xué)好點再寫成文章 Vue self,構(gòu)造生成的this。 root根元素。 els帶指令的所有元素,通過querySelectorAll獲取,涉及ge...
摘要:實際上,我在看代碼的過程中順手提交了這個,作者眼明手快,當(dāng)天就進(jìn)行了修復(fù),現(xiàn)在最新的代碼里已經(jīng)不是這個樣子了而且狀態(tài)機標(biāo)識由字符串換成了數(shù)字常量,解析更準(zhǔn)確的同時執(zhí)行效率也會更高。 最近饒有興致的又把最新版?Vue.js?的源碼學(xué)習(xí)了一下,覺得真心不錯,個人覺得 Vue.js 的代碼非常之優(yōu)雅而且精辟,作者本身可能無 (bu) 意 (xie) 提及這些。那么,就讓我來吧:) 程序結(jié)構(gòu)梳...
摘要:實際上,我在看代碼的過程中順手提交了這個,作者眼明手快,當(dāng)天就進(jìn)行了修復(fù),現(xiàn)在最新的代碼里已經(jīng)不是這個樣子了而且狀態(tài)機標(biāo)識由字符串換成了數(shù)字常量,解析更準(zhǔn)確的同時執(zhí)行效率也會更高。 最近饒有興致的又把最新版?Vue.js?的源碼學(xué)習(xí)了一下,覺得真心不錯,個人覺得 Vue.js 的代碼非常之優(yōu)雅而且精辟,作者本身可能無 (bu) 意 (xie) 提及這些。那么,就讓我來吧:) 程序結(jié)構(gòu)梳...
閱讀 993·2021-11-15 18:06
閱讀 2362·2021-10-08 10:04
閱讀 2647·2019-08-28 18:03
閱讀 892·2019-08-26 13:42
閱讀 1913·2019-08-26 11:31
閱讀 2417·2019-08-23 17:13
閱讀 916·2019-08-23 16:45
閱讀 2049·2019-08-23 14:11