日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > vue >内容正文

vue

vector 不初始化时什么状态_Vue原理解析(三):初始化时created之前做了什么?...

發(fā)布時間:2025/3/11 vue 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 vector 不初始化时什么状态_Vue原理解析(三):初始化时created之前做了什么?... 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

讓我們繼續(xù)this._init()的初始化之旅,接下來又會執(zhí)行這樣的三個初始化方法:

initInjections(vm) initState(vm) initProvide(vm)

5. initInjections(vm): 主要作用是初始化inject,可以訪問到對應(yīng)的依賴。

inject和provide這里需要簡單的提一下,這是vue@2.2版本添加的一對需要一起使用的API,它允許父級組件向它之后的所有子孫組件提供依賴,讓子孫組件無論嵌套多深都可以訪問到,很cool有木有~

  • provide:提供一個對象或是返回一個對象的函數(shù)。
  • inject:是一個字符串?dāng)?shù)組或?qū)ο蟆?/li>

這一對API在vue官網(wǎng)有給出兩條食用提示:

provide 和 inject 主要為高階插件/組件庫提供用例。并不推薦直接用于應(yīng)用程序代碼中。
  • 大概是因?yàn)闀尳M件數(shù)據(jù)層級關(guān)系變的混亂的緣故,但在開發(fā)組件庫時會很好使。
provide 和 inject 綁定并不是可響應(yīng)的。這是刻意為之的。然而,如果你傳入了一個可監(jiān)聽的對象,那么其對象的屬性還是可響應(yīng)的。
  • 有個小技巧,這里可以將根組件data內(nèi)定義的屬性提供給子孫組件,這樣在不借助vuex的情況下就可以實(shí)現(xiàn)簡單的全局狀態(tài)管理,還是很cool的~
app.vue 根組件export default {provide() {return {app: this}},data() {return {info: 'hello world!'}} }child.vue 子孫組件export default {inject: ['app'],methods: {handleClick() {this.app.info = 'hello vue!'}} }

一但觸發(fā)handleClick事件之后,無論嵌套多深的子孫組件只要是使用了inject注入this.app.info變量的地方都會被響應(yīng),這就完成了簡易的vuex。更多的示例大家可以去vue的官網(wǎng)翻閱,這里就不碼字了,現(xiàn)在我們來分析下這么cool的功能它究竟是怎么實(shí)現(xiàn)的~

雖然inject和provide是成對使用的,但是二者在內(nèi)部是分開初始化的。從上面三個初始化方法就能看出,先初始化inject,然后初始化props/data狀態(tài)相關(guān),最后初始化provide。這樣做的目的是可以在props/data中使用inject內(nèi)所注入的內(nèi)容。

我們首先來看一下初始化inject時的方法定義:

export function initInjections(vm) {const result = resolveInject(vm.$options.inject, vm) // 找結(jié)果... }

vm.$options.inject為之前合并后得到的用戶自定義的inject,然后使用resolveInject方法找到我們想要的結(jié)果,我們看下resolveInject方法的定義:

export function resolveInject (inject, vm) {if (inject) {const result = Object.create(null)const keys = Object.keys(inject) //省略Symbol情況for (let i = 0; i < keys.length; i++) {const key = keys[i]const provideKey = inject[key].fromlet source = vmwhile (source) {if (source._provided && hasOwn(source._provided, provideKey)) { //hasOwn為是否有result[key] = source._provided[provideKey]break}source = source.$parent}... vue@2.5后新增設(shè)置inject默認(rèn)參數(shù)相關(guān)邏輯}return result} }

首先定義一個result返回找到的結(jié)果。接下來使用雙循環(huán)查找,外層的for循環(huán)會遍歷inject的每一項(xiàng),然后再內(nèi)層使用while循環(huán)自底向上的查找inject該項(xiàng)的父級是否有提供對應(yīng)的依賴。

Ps:這里可能有人會有疑問,之前inject的定義明明是數(shù)組,這里怎么可以通過Object.keys取值?這是因?yàn)樯弦徽略僮鰋ptions合并時,也會對參數(shù)進(jìn)行格式化,如props的格式,定義為數(shù)組也會被轉(zhuǎn)為對象格式,inject被定義時是這樣的:

定義時: {inject: ['app'] }格式化后: {inject: {app: {from: 'app'}} }

書接上文,source就是當(dāng)前的實(shí)例,而source._provided內(nèi)保存的就是當(dāng)前provide提供的值。首先從當(dāng)前實(shí)例查找,接著將它的父組件實(shí)例賦值給source,在它的父組件查找。找到后使用break跳出循環(huán),將搜索的結(jié)果賦值給result,接著查找下一個。

Ps:可能有人又會有疑問,這個時候是先初始化的inject再初始化的provide,怎么訪問父級的provide了?它根本就沒初始化阿,這個時候我們就要再思考下了,因?yàn)関ue是組件式的,首先就會初始化父組件,然后才是初始化子組件,所以這個時候是有source._provided屬性的。

找到了想到的結(jié)果之后,我們補(bǔ)全之前initInjections的定義:

export function initInjections(vm) {const result = resolveInject(vm.$options.inject, vm)if(result) { // 如果有結(jié)果toggleObserving(false) // 刻意為之不被響應(yīng)式Object.keys(result).forEach(key => {...defineReactive(vm, key, result[key])})toggleObserving(true)} }

如果有搜索結(jié)果,首先會調(diào)用toggleObserving(false),具體實(shí)現(xiàn)不用理會,只用知道這個方法的作用是設(shè)置一個標(biāo)志位,將決定defineReactive()方法是否將它的第三個參數(shù)設(shè)置為響應(yīng)式數(shù)據(jù),也就是決定result[key]這個值是否會被設(shè)置為響應(yīng)式數(shù)據(jù),這里的參數(shù)為false,只是在vm下掛載key對應(yīng)普通的值,不過這樣就可以在當(dāng)前實(shí)例使用this訪問到inject內(nèi)對應(yīng)的依賴項(xiàng)了,設(shè)置完畢之后再調(diào)用toggleObserving(true),改變標(biāo)志位,讓defineReactive()可以設(shè)置第三個參數(shù)為響應(yīng)式數(shù)據(jù)(defineReactive是響應(yīng)式原理很重要的方法,這里了解即可),也就是它該有的樣子。以上就是inject實(shí)現(xiàn)的相關(guān)原理,一句話來說就是,首先遍歷每一項(xiàng),然后挨個遍歷每一項(xiàng)父級是否有依賴。

6. initState(vm): 初始化會被使用到的狀態(tài),狀態(tài)包括props,methods,data,computed,watch五個選項(xiàng)。

首先看下initState(vm)方法的定義:

export function initState(vm) {...const opts = vm.$optionsif(opts.props) initProps(vm, opts.props)if(opts.methods) initMethods(vm, opts.methods)if(opts.data) initData(vm)...if(opts.computed) initComputed(vm, opts.computed)if(opts.watch && opts.watch !== nativeWatch) {initWatch(vm, opts.watch)} }

現(xiàn)在這里的話只會介紹前面三類狀態(tài)的初始化做了什么,也就是props,methods,data,因?yàn)閏omputed和watch會涉及到響應(yīng)式相關(guān)的watcher,這里先略過。接下來我們依次有請這三位的初始化方法登場:

6.1 initProps (vm, propsOptions):
  • 主要作用是檢測子組件接受的值是否符合規(guī)則,以及讓對應(yīng)的值可以用this直接訪問。
function initProps(vm, propsOptions) { // 第二個參數(shù)為驗(yàn)證規(guī)則const propsData = vm.$options.propsData || {} // props具體的值const props = vm._props = {} // 存放propsconst isRoot = !vm.$parent // 是否是根節(jié)點(diǎn)if (!isRoot) {toggleObserving(false)}for (const key in propsOptions) {const value = validateProp(key, propsOptions, propsData, vm)defineReactive(props, key, value)if (!(key in vm)) {proxy(vm, `_props`, key)}}toggleObserving(true) }

我們知道props是作為父組件向子組件通信的重要方式,而initProps內(nèi)的第二個參數(shù)propsOptions,就是當(dāng)前實(shí)例也就是通信角色里的子組件,它所定義的接受參數(shù)的規(guī)則。子組件的props規(guī)則是可以使用數(shù)組形式的定義的,不過再經(jīng)過合并options之后會被格式化為對象的形式:

定義時: {props: ['name', 'age'] }格式化后: {name: {type: null},age: {type: null} }

所以在定義props規(guī)則時,直接使用對象格式吧,這也是更好的書寫規(guī)范。

知道了規(guī)則之后,接下來需要知道父組件傳遞給子組件具體的值,它以對象的格式被放在vm.$options.propsData內(nèi),這也是合并options時得到的。接下來在實(shí)例下定義了一個空對象vm._props,它的作用是將符合規(guī)格的值掛載到它下面。isRoot的作用是判斷當(dāng)前組件是否是根組件,如果不是就不將props的轉(zhuǎn)為響應(yīng)式數(shù)據(jù)。

接下來遍歷格式化后的props驗(yàn)證規(guī)則,通過validateProp方法驗(yàn)證規(guī)則并得到相應(yīng)的值,將得到的值掛載到vm._props下。這個時候就可以通過this._props訪問到props內(nèi)定義的值了:

props: ['name'], methods: {handleClick() {console.log(this._props.name)} }

不過直接訪問內(nèi)部的私有變量這種方式并不友好,所以vue內(nèi)部做了一層代理,將對this.name的訪問轉(zhuǎn)而為對this._props.name的訪問。這里的proxy需要介紹下,因?yàn)橹蟮膁ata也會使用到,看下它的定義:

格式化了一下: export function proxy(target, sourceKey, key) {Object.defineProperty(target, key, {enumerable: true,configurable: true,get: function () {return this[sourceKey][key]},set: function () {this[sourceKey][key] = val}}) }

其實(shí)很簡單,只是定義一個對象值的get方法,讀取時讓其返回另外的一個值,這里就完成了props的初始化。

6.2 initMethods (vm, methods):
  • 主要作用是將methods內(nèi)的方法掛載到this下。
function initMethods(vm, methods) {const props = vm.$options.propsfor(const key in methods) {if(methods[key] == null) { // methods[key] === null || methods[key] === undefined 的簡寫warn(`只定義了key而沒有相應(yīng)的value`)}if(props && hasOwn(props, key)) {warn(`方法名和props的key重名了`)}if((key in vm) && isReserved(key)) {warn(`方法名已經(jīng)存在而且以_或$開頭`)}vm[key] = methods[key] == null? noop // 空函數(shù): bind(methods[key], vm) // 相當(dāng)于methods[key].bind(vm)} }

methods的初始化相較而言就簡單了很多。不過它也有很多邊界情況,如只定義了key而沒有方法具體的實(shí)現(xiàn)、key和props重名了、key已經(jīng)存在且命名不規(guī)范,以_或$開頭,至于為什么不行,我們第一章的時候有說明了。最后將methods內(nèi)的方法掛載到this下,就完成了methods的初始化。

6.3 initData (vm):
  • 主要作用是初始化data,還是老套路,掛載到this下。有個重要的點(diǎn),之所以data內(nèi)的數(shù)據(jù)是響應(yīng)式的,是在這里初始化的,這個大家得有個印象~。
function initData (vm: Component) {let data = vm.$options.datadata = vm._data = typeof data === 'function'? getData(data, vm) // 通過data.call(vm, vm)得到返回的對象: data || {}if (!isPlainObject(data)) { // 如果不是一個對象格式data = {}warn(`data得是一個對象`)}const keys = Object.keys(data)const props = vm.$options.props // 得到propsconst methods = vm.$options.methods // 得到methodslet i = keys.lengthwhile (i--) {const key = keys[i]if (methods && hasOwn(methods, key)) {warn(`和methods內(nèi)的方法重名了`)}if (props && hasOwn(props, key)) {warn(`和props內(nèi)的key重名了`)} else if (!isReserved(key)) { // key不能以_或$開頭proxy(vm, `_data`, key)}}observe(data, true) }

首先通過vm.$options.data得到用戶定義的data,如果是function格式就執(zhí)行它,并返回執(zhí)行之后的結(jié)果,否則返回data或{},將結(jié)果賦值給vm._data這個私有屬性。和props一樣的套路,最后用來做一層代理,如果得到的結(jié)果不是對象格式就是報錯了。

然后遍歷data內(nèi)的每一項(xiàng),不能和methods以及props內(nèi)的key重名,然后使用proxy做一層代理。注意最后會執(zhí)行一個方法observe(data, true),它的作用了是遞歸的讓data內(nèi)的每一項(xiàng)數(shù)據(jù)都變成響應(yīng)式的。

其實(shí)不難發(fā)現(xiàn)它們仨主要做的事情差不多,首先不要相互之間有重名,然后可以被this直接訪問到。

7. initProvide(vm): 主要作用是初始化provide為子組件提供依賴。

provide選項(xiàng)應(yīng)該是一個對象或是函數(shù),所以對它取值即可,就像取data內(nèi)的值類似,看下它的定義:

export function initProvide (vm) {const provide = vm.$options.provideif (provide) {vm._provided = typeof provide === 'function'? provide.call(vm): provide} }

首先通過vm.$options.provide取得用戶定義的provide選項(xiàng),如果是一個function類型就執(zhí)行一下,得到返回后的結(jié)果,將其賦值給了vm._provided私有屬性,所以子組件在初始化inject時就可以訪問到父組件提供的依賴了;如果不是function類型就直接返回定義的provide。

8. callHook(vm, 'created'): 執(zhí)行用戶定義的created鉤子函數(shù),有mixin混入的也一并執(zhí)行。

終于我們越過了created鉤子函數(shù),還是分別用一句話來介紹它們主要都干了什么事:

  • initInjections(vm):讓子組件inject的項(xiàng)可以訪問到正確的值
  • initState(vm):將組件定義的狀態(tài)掛載到this下。
  • initProvide(vm):初始化父組件提供的provide依賴。
  • created:執(zhí)行組件的created鉤子函數(shù)

初始化的階段算是告一段落了,接下來我們會進(jìn)入組件的掛載階段。按照慣例我們還是以一道vue容易被問道的面試題作為本章的結(jié)束吧~:

面試官微笑而又不失禮貌的問道:
  • 請問methods內(nèi)的方法可以使用箭頭函數(shù)么,會造成什么樣的結(jié)果?

懟回去:

  • 是不可以使用箭頭函數(shù)的,因?yàn)榧^函數(shù)的this是定義時就綁定的。在vue的內(nèi)部,methods內(nèi)每個方法的上下文是當(dāng)前的vm組件實(shí)例,methods[key].bind(vm),而如果使用使用箭頭函數(shù),函數(shù)的上下文就變成了父級的上下文,也就是undefined了,結(jié)果就是通過undefined訪問任何變量都會報錯。

順手點(diǎn)個贊或關(guān)注唄,找起來也方便~

胡成:你可能會用的上的一個vue功能組件庫,持續(xù)完善中...?zhuanlan.zhihu.com

總結(jié)

以上是生活随笔為你收集整理的vector 不初始化时什么状态_Vue原理解析(三):初始化时created之前做了什么?...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。