今天的內容有意思了,朋友們繼續對我們之前的案例完善,是這樣的我們之前是不是靠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裡面寫,這裡直接呼叫