vector 不初始化時什么狀態_Vue原理解析(三):初始化時created之前做了什么?...

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

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

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

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

  • provide:提供一個對象或是返回一個對象的函數。
  • inject:是一個字符串數組或對象。

這一對APIvue官網有給出兩條食用提示:

provideinject 主要為高階插件/組件庫提供用例。并不推薦直接用于應用程序代碼中。
  • 大概是因為會讓組件數據層級關系變的混亂的緣故,但在開發組件庫時會很好使。
provideinject 綁定并不是可響應的。這是刻意為之的。然而,如果你傳入了一個可監聽的對象,那么其對象的屬性還是可響應的。
  • 有個小技巧,這里可以將根組件data內定義的屬性提供給子孫組件,這樣在不借助vuex的情況下就可以實現簡單的全局狀態管理,還是很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!'}}
}

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

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

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

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

vm.$options.inject為之前合并后得到的用戶自定義的inject,然后使用resolveInject方法找到我們想要的結果,我們看下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后新增設置inject默認參數相關邏輯}return result}
}

首先定義一個result返回找到的結果。接下來使用雙循環查找,外層的for循環會遍歷inject的每一項,然后再內層使用while循環自底向上的查找inject該項的父級是否有提供對應的依賴。

Ps:這里可能有人會有疑問,之前inject的定義明明是數組,這里怎么可以通過Object.keys取值?這是因為上一章再做options合并時,也會對參數進行格式化,如props的格式,定義為數組也會被轉為對象格式,inject被定義時是這樣的:

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

書接上文,source就是當前的實例,而source._provided內保存的就是當前provide提供的值。首先從當前實例查找,接著將它的父組件實例賦值給source,在它的父組件查找。找到后使用break跳出循環,將搜索的結果賦值給result,接著查找下一個。

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

找到了想到的結果之后,我們補全之前initInjections的定義:

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

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

6. initState(vm): 初始化會被使用到的狀態,狀態包括propsmethodsdatacomputedwatch五個選項。

首先看下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)}
}

現在這里的話只會介紹前面三類狀態的初始化做了什么,也就是propsmethodsdata,因為computedwatch會涉及到響應式相關的watcher,這里先略過。接下來我們依次有請這三位的初始化方法登場:

6.1 initProps (vm, propsOptions):
  • 主要作用是檢測子組件接受的值是否符合規則,以及讓對應的值可以用this直接訪問。
function initProps(vm, propsOptions) {  // 第二個參數為驗證規則const propsData = vm.$options.propsData || {}  // props具體的值const props = vm._props = {}  // 存放propsconst isRoot = !vm.$parent // 是否是根節點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內的第二個參數propsOptions,就是當前實例也就是通信角色里的子組件,它所定義的接受參數的規則。子組件的props規則是可以使用數組形式的定義的,不過再經過合并options之后會被格式化為對象的形式:

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

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

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

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

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

不過直接訪問內部的私有變量這種方式并不友好,所以vue內部做了一層代理,將對this.name的訪問轉而為對this._props.name的訪問。這里的proxy需要介紹下,因為之后的data也會使用到,看下它的定義:

格式化了一下:
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}})
}

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

6.2 initMethods (vm, methods):
  • 主要作用是將methods內的方法掛載到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而沒有相應的value`)}if(props && hasOwn(props, key)) {warn(`方法名和props的key重名了`)}if((key in vm) && isReserved(key)) {warn(`方法名已經存在而且以_或$開頭`)}vm[key] = methods[key] == null? noop  // 空函數: bind(methods[key], vm)  //  相當于methods[key].bind(vm)}
}

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

6.3 initData (vm):
  • 主要作用是初始化data,還是老套路,掛載到this下。有個重要的點,之所以data內的數據是響應式的,是在這里初始化的,這個大家得有個印象~。
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內的方法重名了`)}if (props && hasOwn(props, key)) {warn(`和props內的key重名了`)} else if (!isReserved(key)) { // key不能以_或$開頭proxy(vm, `_data`, key)}}observe(data, true)
}

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

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

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

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

provide選項應該是一個對象或是函數,所以對它取值即可,就像取data內的值類似,看下它的定義:

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

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

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

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

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

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

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

懟回去:

  • 是不可以使用箭頭函數的,因為箭頭函數的this是定義時就綁定的。在vue的內部,methods內每個方法的上下文是當前的vm組件實例,methods[key].bind(vm),而如果使用使用箭頭函數,函數的上下文就變成了父級的上下文,也就是undefined了,結果就是通過undefined訪問任何變量都會報錯。

順手點個贊或關注唄,找起來也方便~

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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/542653.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/542653.shtml
英文地址,請注明出處:http://en.pswp.cn/news/542653.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

switch 字符串 java_JDK7新特性switch支持字符串

在JDK7中,switch語句的判斷條件增加了對字符串類型的支持。由于字符串的操作在編程中使用頻繁,這個新特性的出現為Java編程帶來了便利。接下來通過一個案例演示一下在switch語句中使用字符串進行匹配。public class Example {public static void main(String[] args) {String w…

cisco packet tracer路由器配置_【干貨】思科交換機路由器怎么配置密碼?

今天帶大家看看如何在思科的交換機路由器當中配置安全特性&#xff0c;也就是密碼的配置方式。在學習配置之前&#xff0c;我們先回顧一下密碼相關知識。密碼學是研究信息系統安全保密的科學。人類有記載的通信密碼始于公元前400年&#xff0c;古希臘人是置換密碼學的發明者。密…

perl 哈希數組的哈希_使用哈希檢查兩個數組是否相似

perl 哈希數組的哈希Prerequisite: Hashing data structure 先決條件&#xff1a; 哈希數據結構 Problem statement: 問題陳述&#xff1a; Check whether two arrays are similar or not using the hash table. The arrays are of the same size. 使用哈希表檢查兩個數組是否…

codevs3872 郵遞員送信(SPFA)

郵遞員送信 時間限制: 1 Sec 內存限制: 64 MB提交: 10 解決: 5[提交][狀態][討論版] 題目描述 有一個郵遞員要送東西&#xff0c;郵局在節點1.他總共要送N-1樣東西&#xff0c;其目的地分別是2~N。由于這個城市的交通比較繁忙&#xff0c;因此所有的道路都是單行的&#xff0…

java上傳csv文件上傳_java處理csv文件上傳示例詳解

前言&#xff1a;示例只是做了一個最最基礎的上傳csv的示例&#xff0c;如果要引用到代碼中去&#xff0c;還需要根據自己的業務自行添加一些邏輯處理。readcsvutil工具類package com.hanfengyeqiao.gjb.utils;import java.io.*;import java.util.*;/*** csv工具類*/public cla…

360更新補丁一直提示正在安裝_遠程利用POC公布|CVE20200796:微軟發布SMBv3協議“蠕蟲級”漏洞補丁通告...

更多全球網絡安全資訊盡在邑安全www.eansec.com0x00 事件描述2020年3月11日&#xff0c;360CERT監測到有海外廠家發布安全規則通告&#xff0c;通告中描述了一處微軟SMBv3協議的內存破壞漏洞&#xff0c;編號CVE-2020-0796&#xff0c;并表示該漏洞無需授權驗證即可被遠程利用&…

字符串的回文子序列個數_計算給定字符串中回文子序列的數量

字符串的回文子序列個數Problem statement: 問題陳述&#xff1a; Given a string you have to count the total number of palindromic subsequences in the giving string and print the value. 給定一個字符串&#xff0c;您必須計算給定字符串中回文子序列的總數并打印該值…

Linux-破解rhel7-root密碼

破解7的密碼1.linux16 rd.break2.mount -o remount,rw /sysroot3.chroot /sysroot4.passwd5.touch /.autorelabelexitexit7版本grub菜單加密1.grub2-mkpasswd-pbkdf22.vi /etc/grub.d/40_customset superusers"root"password_pbkdf2 root grub.pbkdf2.sha512.10000.…

適配接口 java_【Java 設計模式】接口型模式--Adapter(適配器)模式

簡介&#xff1a;【Java設計模式】接口型模式–Adapter(適配器)模式Adapter模式的宗旨就是&#xff1a;向客戶提供接口&#xff0c;并使用現有的類所提供的服務&#xff0c;以滿足客戶的需求。 或者說&#xff0c;現在有classA的方法滿足客戶的部分要求&#xff0c;將另一部分需…

deepinu盤制作工具_u盤啟動盤制作工具怎么制作 u盤啟動盤制作工具制作方法【詳細步驟】...

在電腦城很多技術人員都會使用u盤裝系統的方法給用戶電腦安裝系統&#xff0c;他們是怎么操作的呢?其實很簡單&#xff0c;就是通過u盤啟動盤來安裝系統的。而u盤啟動盤是需要用 u盤啟動盤制作工具 來制作的。那么問題又來了&#xff0c;u盤啟動盤制作工具怎么制作呢?下面就給…

openstack私有云_OpenStack-下一代私有云的未來

openstack私有云The OpenStack project is an open source cloud computing platform for all types of clouds, which aims to be simple to implement, massively scalable, and feature rich. Developers and cloud computing technologists from around the world create t…

outlook2010客戶端無法預覽及保存word,excel問題

outlook2010客戶端遇到的EXCEL預覽及保存問題今天遇到了一個這樣的問題&#xff0c;outlook2010打開以后其他的excel都可以打開預覽及保存&#xff0c;這個excel無法預覽既保存&#xff0c;經查是outlook2010預覽及打開的緩存有限制&#xff0c;超過后就無法預覽了&#xff0c;…

python自動化框架pytest pdf_Python 自動化測試框架 unittest 和 pytest 對比

一、用例編寫規則1.unittest提供了test cases、test suites、test fixtures、test runner相關的類,讓測試更加明確、方便、可控。使用unittest編寫用例,必須遵守以下規則:(1)測試文件必須先import unittest(2)測試類必須繼承unittest.TestCase(3)測試方法必須以“test_”開頭(4…

freemarker的測試結果框架_java必背綜合知識點總結(框架篇)

框架篇一、Struts1的運行原理在啟動時通過前端總控制器ActionServlet加載struts-config.xml并進行解析&#xff0c;當用戶在jsp頁面發送請求被struts1的核心控制器ActionServlet接收&#xff0c;ActionServlet在用戶請求時將請求參數放到對應的ActionForm對象中的成員變量中&am…

Java SecurityManager checkPackageDefinition()方法與示例

SecurityManager類的checkPackageDefinition()方法 (SecurityManager Class checkPackageDefinition() method) checkPackageDefinition() method is available in java.lang package. checkPackageDefinition()方法在java.lang包中可用。 We call getProperty("package.d…

java容器詳解_詳解Java 容器(第①篇)——概覽

![](http://img.blog.itpub.net/blog/2020/04/02/9d89d3008962c127.png?x-oss-processstyle/bb)容器主要包括 Collection 和 Map 兩種&#xff0c;Collection 存儲著對象的集合&#xff0c;而 Map 存儲著鍵值對(兩個對象)的映射表。# 一、Collection![](https://upload-images…

python圖形界面庫哪個好_8個必備的Python GUI庫

Python GUI 庫有很多&#xff0c;下面給大家羅列常用的幾種 GUI庫。下面介紹的這些GUI框架&#xff0c;能滿足大部分開發人員的需要&#xff0c;你可以根據自己的需求&#xff0c;選擇合適的GUI庫。1. wxPython wxPython 是一個跨平臺的 GUI 工具集&#xff0c;是 Python 語言的…

為什么在Python中使用string.join(list)而不是list.join(string)?

join() is a string method and while using it the separator string iterates over an arbitrary sequence, forming string representations of each of the elements, inserting itself between the elements. join()是一個字符串方法&#xff0c;使用它時&#xff0c;分隔…

js的client、scroll、offset詳解與兼容性

clientWidth&#xff1a;可視區寬說明&#xff1a;樣式寬padding參考&#xff1a;js的client詳解 scrollTop : 滾動條滾動距離說明&#xff1a;chrome下他會以為滾動條是文檔元素的&#xff0c;所以需要做兼容&#xff1a;var scrollTop document.documentElement.scrollTop |…

88是python語言的整數類型_Python基礎數據類型題

Python基礎數據類型 題 考試時間&#xff1a;三個小時 滿分100分&#xff08;80分以上包含80分及格&#xff09; 1&#xff0c;簡述變量命名規范&#xff08;3分&#xff09;1.必須是字母&#xff0c;數字&#xff0c;下劃線的任意組合。 2.不能是數字開頭 3.不能是python中的關…