今天的內容有意思了,朋友們繼續對我們之前的案例完善,是這樣的我們之前是不是靠props來完成父給子,子給父之間傳數據,其實父給子最好的方法就是props但是自給父就不是了,並且今天學下來,不僅如此,組件間任何層級的關係我都可以傳數據了,兄弟之間,爺孫之間等等等等 七.瀏覽器本地存儲 1.localS ...
今天的內容有意思了,朋友們繼續對我們之前的案例完善,是這樣的我們之前是不是靠props來完成父給子,子給父之間傳數據,其實父給子最好的方法就是props但是自給父就不是了,並且今天學下來,不僅如此,組件間任何層級的關係我都可以傳數據了,兄弟之間,爺孫之間等等等等
七.瀏覽器本地存儲
1.localStorage
關閉瀏覽器數據不會丟失
通過它上面的一個api可以存儲數據 .setitem()兩個參數,前面是鍵,後面是值,要註意的是都要以加引號以字元串形式進行存儲,而且就算你不加引號,最終呈現效果也會強制給你調用toString這個方法
可以看到就算是字元串也變成了字元串類型,那麼問題就來了,那如果我傳入的是一個對象,也被強制調用了toString那我對象就沒了,什麼也看不到了
所以如果是要存儲對象數據的時候需要將對象用轉為json數據在進行存儲,我們都知道json本質上還是一個字元串
剩下還有三個api:讀取getitem()、刪除removeitem()、清空clear()這個不用傳數據
註意:讀取沒有的屬性,將會顯示null
2.sessionStorage
瀏覽器一關就沒有數據了
他的api跟local一樣
3.TodoList案例完善
我們剛纔做的案例數據在一刷新,就會回到初始案例,顯然這樣是不行的,每個人都應該有屬於自己的數據,就可以配合localstorage來,通過監視屬性來做,監視我們的data數據,當一發生變動就讓最新的值setitem到我們的本地存儲
我在這裡卡了半天啊,我一直在糾結,為什麼這個local裡面的屬性名會給我變成個數組,最終查到了,原來watch監視屬性裡面的參數本身就是個數組,放的對象的形式,做到這一步當我們敲擊回車,已經可以看到localstorage將我們的數據存儲下來了,但是呢我們的頁面還沒有,這個時候需要將我們原來的數據,註意datas本身是一個數組這裡就不用再用數組包裹了
這個時候我們我們刷新就能看到我們之前已經添加過的數據了
bug:當我們把local清空準備重新試一下,就會報錯
不能夠讀取null的長度差不多是這個意思,哪裡來的null?還記得前面說過當我們本地存儲如果讀取的是一個不存在的屬性那他讀取出來就是null,所以問題出現在這裡,這個時候localstorage裡面還沒有這個屬性的,主需要採用短路運算,讓原始數據,存在就用它否則就為一個空數組
bug:雖然完成了刷新添加的數據不會丟失,但是我們的選擇刷新了會丟失,為什麼,問題出現在watch這裡,我們對watch的監視只是看dataArr有沒有整體改動,用了unshift,vue都能檢測到,很明顯這是一個能夠改變自身的方法,但是我們去勾選前面的選擇,卻是深層次裡面的了,所以監視的deep屬性就來,只有監測到深層次的改動才又會重新setitem
八.自定義事件
- 方法一 : @或者v-on方法完成一個案例,當我們點擊一個子組件,會把子組件的名字傳到app裡面來並列印出來,用到props的子傳父方法,怎麼用自定義事件來完成這件事情,首先在app的組件標簽寫上我們的自定義事件名,用v-on來綁定,
那麼這個事件怎麼來觸發呢,這樣來理解,當我們通過組件標簽給她綁定了一個自定義事件,那麼此時這個組件實例對象vc身上肯定已經有這個事件了,至於怎麼來觸發就需要回到我們的組件裡面去定義
這裡先把我們的觸發後的事件處理函數定義好,然後關鍵邏輯 主要是在我們要觸發的這個組件裡面,想要這個自定義事件要怎麼觸發其實是聽你自己的,同樣還是需要在這邊生命一個內置的事件,然後在這個事件函數裡面用到一個api $emit,他的第一個參數就是你的自定義的事件的名字,第二個參數可以傳參給你自定義事件的處理函數
props和自定義事件異同點:
共同:都可以實現子給父傳數據,而且都是通過父組件裡面的回調來實現的
不同點:props需要接收,而這種方法,直接拿來用都不用調用這個函數,props還需要自己去調用這個函數把參數傳進去
-
方法二: 針對於上面的自定義事件的形式,我們還有第二種方法也叫做 ref法,通過在app給子組件添加一個ref,前面就說過我們用ref獲取來的組件是這個組建的實例對象,有了實例對象,直接通過mounted這個生命周期鉤子,為什麼要在這裡做,因為只有掛載完畢了我們才能拿到這個實例對象,vue才會new 組件構造函數,繼續通過$on這個方法給這個實例對象在他身上添加一個自定義事件
做到這一步,我們組件裡面的代碼不變,還是click同時還是通過$emit 這個api來觸發自定義事件,做到這一步意思就是,一切準備就緒,一旦在這個組件觸發了這個自定義事件就會執行自定義事件的回調函數,那麼回到函數呢?直接在後面以參數的形式添加
-
如果說我只想執行一次用v-on方法就是直接用事件修飾符,用ref方法,就調用$once這個api
註意如果是子組件多個參數傳進來,可以使用這種方式將其以一個數組方式接受
8.1解綁自定義事件
-
解綁單個事件,寫在子組件裡面,全新api $off
-
解綁多個,用字元串包裹
-
解綁所有,this.$off()不傳參數
回顧前面所說的一個生命周期的問題,說過當執行了destroy之後,身上的事件、監聽器等都會被銷毀,但是說的事件是自定義事件,和vue事件不包括原生js事件,比如你點擊一個按鈕n++,這個時候n肯定不加了,但是如果你寫的有console.log那麼這個log肯定還是你點一次輸出一次的,自定義事件就更不用說了,也是vue實現的,所以也會被銷毀實現不了了,還有一個點就是,vm被銷毀了他下麵的子組件的這些自定義事件等也會被銷毀
8.2註意
-
是一種組件間的通信方式,適用於 子組件給父組件傳
-
我們的組件標簽上也可以綁定內置事件,但是需要 .native這個修飾符,綁定後比如一個click那就是這個子組件的最大的div可以觸發也就是子組件的最外層
-
如果用下麵這鐘方式綁定自定義事件的時候,需要註意回調這裡要麼寫在mtehods裡面,要麼寫成一個箭頭函數,不然this指向會變
8.3TodoList自定義事件
將之前這個案例子傳父數據的地方都改成自定義事件,我就直接傳幾個,比如敲回車放入數組這裡直接將原來的動態綁定改為自定義事件,簡寫形式
然後我們header這邊的 props就可以刪除了,同時在我們的鍵盤事件添加上調用api去觸發自定義事件的命令
然後我們在用ref打標識的方法再做一次全選的方式,首先需要在app裡面找到footer的子組件,給她打一個標識,然後在我們app這個組建的mounted函數裡面來通過ref得到這個組件實例對象通過on這個api將自定義事件綁定上去,同時第二個參數為我們要調用這個事件的回調函數
然後後面基本是一樣的,在我們子組件裡面這個自定義事件該怎麼觸發,就寫內置事件,寫一個事件處理函數,然後在這個處理函數裡面,emit這個api來觸發自定義事件,同時將我們需要的參數傳過來
九.全局事件匯流排
9.1安裝全局事件匯流排
可實現任意組件間的通信
其實原理是這樣的,我們左邊這麼多組件,要實現任意互相通信,可以由一個中間者實現,比如我們在B裡面定義這個x給她通過$on綁定一個自定義事件,同時回調在我們B裡面,註意是在B裡面,所以當執行這個自定義事件的時候,是在B裡面執行,完成了這一步,比如說我們要把A的數據傳到B裡面來,那麼A就可以通過x這個對象用他身上的$emit這個api來觸發我們的自定義事件同時把A身上的數據,傳過來,這個時候我們B的回調裡面就能收到這個數據了
其實原理跟我們前面的自定義事件很像,所以這麼一看,這個x是不是要滿足兩個特性:一個是它能夠被所有組件訪問到,一個是他身上要有$on、$emit等api,第一個問題它能夠被所有組件訪問到,放在VueComponent上實現不了,因為我們說過每個VueComponent都是一個全新的構造函數,放在自己vc上更不可能,那就只有你能訪問了,那麼順著那條線,是不是答案已經出來了,沒錯,要讓所有組件都訪問得到,那就只有Vue的原型對象了
第二個問題,怎麼讓他身上有這些api,這些api其實都是在vue原型對象上的,所以我們vm、vc都可訪問得到,但是將它等於一個vc不太現實,因為我們是將它定義在入口文件的,那就只能為一個vm了,並且還要在vm的beforecreate這個生命周期函數來賦值,為什麼要在生命周期函數里來賦值,因為這裡講究一個時間效應,太慢了不行的,前面也已經看過了ref打標識這種方法調用on這個api就是在mounted生命周期函數裡面調用的,為什麼因為講mounted的時候就說過這裡面是萬物的開始,呱呱墜地的時候,什麼定義定時器、自定義事件等等都是在這裡,而且也只能在這裡,不然時間晚了,我觸發了你還沒綁定好那怎麼可以,所以說我們講究一個時間效應,如果等到vm都定義完了,來一個x=vm,這個時候已經整個都掛載到頁面上去了,我們組件裡面的mounted也早已經執行完了,你的x都還沒有這些api那就會報錯,然後還只能寫在beforecreate這個生命周期鉤子,為什麼,也不是說只能嘛,最好寫在這裡,created感覺也是可以的,因為這裡數據掛載這些都還沒做,但是也說過vue內置的一些事件、api之類的定義完了,我要這些就夠了,我不需要數據
所以最終定義成這樣就是最標準的了,而且我們一般x叫做$bus,因為這個東西的作用一是本身有點公交車的感覺,誰都能用,還有一個就是他的中文意思還有匯流排的意思
有一點必須要清楚,我想把A發送到B,那調用emit發射這個api的就是前者並且參數是數據,而後者就調用on並且參數為回調,你就拿到這個數據了,想幹嘛幹嘛了
然後就是,定義自定義事件也就是on肯定一掛載就要定義,所以寫在mounted裡面,而發送數據這邊決定觸發事件的方式
既然用到了出生mounted,最好也在臨別beforeDestroy解綁我們的事件,一定要寫事件名
9.2TODOList全局事件匯流排
又可以繼續完善之前的案例了,其實父給子傳數據,props是最好的方法,子給父傳,用我們原始方法也行(props),自定義事件也好,都差不多的,這裡最需要用到全局事件匯流排的,是孫給爺傳,之前一直靠著list這個中間量props,他自己也沒用只是給item帶過去函數
定義好api
我們是item給app傳,那就在item先emit,都是傳過去id進行更新我們的數據裡面的completed是否勾選和篩選進行刪除操作
然後再mounted裡面聲明事件
十.消息訂閱與發佈
一種組件間的通信方式,適用於任意組件間通信
如果A需要C的數據,那麼A就訂閱消息,C就發佈消息即可
原生JS無法輕鬆實現消息訂閱與發佈,需要第三方庫(這裡推薦 pubsub-js)
- 先安裝這個庫
- 哪裡需要就在那裡導入,我要把student的學生姓名傳給school那他們兩個都需要
,導入時註意 直接pubsub沒有js
-
學生這邊發佈信息,參數為要傳過去的數據,用到的api pubsub.publish,第一個參數為消息名,發佈與訂閱都需要的
-
學校這邊訂閱消息用到的是 pubsub.subscribe接受兩個參數,第一個是我們的消息名字,所以一般取名為msgName,第二個參數才是我們傳過來的數據,一般取名data,註意消息訂閱同自定義事件、全局事件接收數據這邊都是放在mounted函數裡面的
-
註意一,同樣需要在beforeDestory函數裡面取消訂閱,用到的api .unsubscribe,但是我們這裡的消息訂閱有點類似於定時器,需要一個變數去接收,這邊取消訂閱為這個變數名,註意不是消息名
像這樣是拿不到的,都是兩個作用域了,把它放到實例對象上去,作為一個屬性即可
-
註意二:直接在訂閱消息裡面寫這個函數同樣要寫為箭頭函數,不然this會亂,或者也可以在methods裡面寫,這裡直接調用