Vue2.0 傳值方式:
在Vue的框架開發(fā)的項目過程中,經(jīng)常會用到組件來管理不同的功能侮腹,有一些公共的組件會被提取出來问顷。這時必然會產(chǎn)生一些疑問和需求?比如一個組件調(diào)用另一個組件作為自己的子組件冗荸,那么我們?nèi)绾芜M行給子組件進行傳值呢承璃?如果是電商網(wǎng)站系統(tǒng)的開發(fā),還會涉及到購物車的選項蚌本,這時候就會涉及到非父子組件傳值的情況盔粹。當(dāng)然你也可以用Vuex狀態(tài)管理工具來實現(xiàn),這部分我們后續(xù)會單獨介紹程癌。
引用官網(wǎng)的一句話:父子組件的關(guān)系可以總結(jié)為 prop 向下傳遞舷嗡,事件向上傳遞。父組件通過 prop 給子組件下發(fā)數(shù)據(jù)嵌莉,子組件通過事件給父組件發(fā)送消息进萄,如下圖所示:
接下來,我們通過實例來看可能會更明白一些:
父組件:
? ? 父組件:
<!-- 引入子組件 -->
importchildfrom'./child'
exportdefault{
components: {
? ? ? child
? ? },
? ? data () {
return{
name:''
? ? ? }
? ? }
? }
子組件:
? ? 子組件:
{{inputName}}
exportdefault{
// 接受父組件的值
props: {
inputName:String,
required:true
? ? }
? }
2. 子組件向父組件傳值$emit,父組件通過子組件的ref屬性獲取值
子組件:
? ? 子組件:
{{childValue}}
<!-- 定義一個子組件傳值的方法 -->
exportdefault{
? ? data () {
return{
childValue:'我是子組件的數(shù)據(jù)'
? ? ? }
? ? },
methods: {
? ? ? childClick () {
// childByValue是在父組件on監(jiān)聽的方法
// 第二個參數(shù)this.childValue是需要傳的值
this.$emit('childByValue',this.childValue)
? ? ? }
? ? }
? }
父組件:
? ? 父組件:
{{name}}
<!-- 引入子組件 定義一個on的方法監(jiān)聽子組件的狀態(tài)-->
importchildfrom'./child'
exportdefault{
components: {
? ? ? child
? ? },
? ? data () {
return{
name:''
? ? ? }
? ? },
methods: {
childByValue:function(childValue){
// childValue就是子組件傳過來的值
this.name = childValue
? ? ? }
? ? }
? }
盡管有 prop 和事件锐峭,但是有時仍然需要在 JavaScript 中直接訪問子組件中鼠。
為此可以使用 ref 為子組件指定一個引用 ID。例如:
</div>
varparent=newVue({ el:'#parent'})
// 訪問子組件實例
varchild =parent.$refs.profile
當(dāng) ref 和 v-for 一起使用時沿癞,獲取到的引用會是一個數(shù)組援雇,包含和循環(huán)數(shù)據(jù)源對
應(yīng)的子組件。
$refs 只在組件渲染完成后才填充抛寝,并且它是非響應(yīng)式的熊杨。它僅僅是一個直接
操作子組件的
應(yīng)急方案——應(yīng)當(dāng)避免在模板或計算屬性中使用 $refs。
具體使用方法:
父組件
{{a}}
通過ref得到子組件的值
<!-- 使用組件 -->
importthreefrom'./components/three'
exportdefault{
components:{
? ? three,
? },
? data(){
return{
a:'我是父組件',
? ? }
? },
methods:{
? ? getchild(){
// 通過this.$refs調(diào)用
console.log(this.$refs.mesage.mes)
? ? }
? }
}
子組件:?
{{mes}}
exportdefault{
? data() {
return{
mes:'666'
? ? };
? }
};
非父子組件之間傳值盗舰,需要定義個公共的公共實例文件bus.js晶府,作為中間倉庫來傳值,不然路由組件之間達不到傳值的效果钻趋。這種方法通過一個空的Vue實例作為中央事件總線(事件中心)川陆,用它來觸發(fā)事件和監(jiān)聽事件,巧妙而輕量地實現(xiàn)了任何組件間的通信,包括父子蛮位、兄弟较沪、跨級鳞绕。當(dāng)我們的項目比較大時,可以選擇更好的狀態(tài)管理解決方案vuex尸曼。
公共bus.js
//bus.js
importVuefrom'vue'
exportdefaultnewVue()
組件A:
? ? A組件:
{{elementValue}}
// 引入公共的bug们何,來做為中間傳達的工具
importBusfrom'./bus.js'
exportdefault{
? ? data () {
return{
elementValue:4
? ? ? }
? ? },
methods: {
elementByValue:function(){
Bus.$emit('val',this.elementValue)
? ? ? }
? ? }
? }
組件B:
? ? B組件:
{{name}}
importBusfrom'./bus.js'
exportdefault{
? ? data () {
return{
name:0
? ? ? }
? ? },
mounted:function(){
varvm =this
// 用$on事件來接收參數(shù)
Bus.$on('val',(data) =>{
console.log(data)
? ? ? ? vm.name = data
? ? ? })
? ? },
methods: {
getData:function(){
this.name++
? ? ? }
? ? }
? }
(1)簡要介紹Vuex原理
Vuex實現(xiàn)了一個單向數(shù)據(jù)流,在全局擁有一個State存放數(shù)據(jù)控轿,當(dāng)組件要更改State中的數(shù)據(jù)時冤竹,必須通過Mutation進行,Mutation同時提供了訂閱者模式供外部插件調(diào)用獲取State數(shù)據(jù)的更新茬射。而當(dāng)所有異步操作(常見于調(diào)用后端接口異步獲取更新數(shù)據(jù))或批量的同步操作需要走Action鹦蠕,但Action也是無法直接修改State的,還是需要通過Mutation來修改State的數(shù)據(jù)在抛。最后钟病,根據(jù)State的變化,渲染到視圖上刚梭。
(2)簡要介紹各模塊在流程中的功能:
Vue Components:Vue組件肠阱。HTML頁面上,負責(zé)接收用戶操作等交互行為朴读,執(zhí)行dispatch方法觸發(fā)對應(yīng)action進行回應(yīng)辖所。
dispatch:操作行為觸發(fā)方法,是唯一能執(zhí)行action的方法磨德。
actions:操作行為處理模塊,由組件中的?$store.dispatch('action 名稱',data1)來觸發(fā)。然后由commit()來觸發(fā)mutation的調(diào)用 , 間接更新 state吆视。負責(zé)處理Vue Components接收到的所有交互行為典挑。包含同步/異步操作,支持多個同名方法啦吧,按照注冊的順序依次觸發(fā)您觉。向后臺API請求的操作就在這個模塊中進行,包括觸發(fā)其他action以及提交mutation的操作授滓。該模塊提供了Promise的封裝琳水,以支持action的鏈式觸發(fā)。
commit:狀態(tài)改變提交操作方法般堆。對mutation進行提交在孝,是唯一能執(zhí)行mutation的方法。
mutations:狀態(tài)改變操作方法淮摔,由actions中的?commit('mutation 名稱')來觸發(fā)私沮。是Vuex修改state的唯一推薦方法。該方法只能進行同步操作和橙,且方法名只能全局唯一仔燕。操作之中會有一些hook暴露出來造垛,以進行state的監(jiān)控等。
state:頁面狀態(tài)管理容器對象晰搀。集中存儲Vue components中data對象的零散數(shù)據(jù)五辽,全局唯一,以進行統(tǒng)一的狀態(tài)管理外恕。頁面顯示所需的數(shù)據(jù)從該對象中進行讀取杆逗,利用Vue的細粒度數(shù)據(jù)響應(yīng)機制來進行高效的狀態(tài)更新。
getters:state對象讀取方法吁讨。圖中沒有單獨列出該模塊髓迎,應(yīng)該被包含在了render中,Vue Components通過該方法讀取全局state對象建丧。
vuex 是 vue 的狀態(tài)管理器排龄,存儲的數(shù)據(jù)是響應(yīng)式的。但是并不會保存起來翎朱,刷新之后就回到了初始狀態(tài)橄维,具體做法應(yīng)該在vuex里數(shù)據(jù)改變的時候把數(shù)據(jù)拷貝一份保存到localStorage里面,刷新之后拴曲,如果localStorage里有保存的數(shù)據(jù)争舞,取出來再替換store里的state。
letdefaultCity ="北京"
try{
// 用戶關(guān)閉了本地存儲功能澈灼,此時在外層加個try...catch
if(!defaultCity){
defaultCity =JSON.parse(window.localStorage.getItem('defaultCity'))
? }
}catch(e){}
exportdefault
newVuex.Store({
state: {
city: defaultCity
? },
mutations: {
? ? changeCity(state, city) {
? ? ? state.city = city
try{
window.localStorage.setItem('defaultCity',JSON.stringify(state.city));
// 數(shù)據(jù)改變的時候把數(shù)據(jù)拷貝一份保存到localStorage里面
}catch(e) {}
? }
}
})
這里需要注意的是:由于vuex里竞川,我們保存的狀態(tài),都是數(shù)組叁熔,而localStorage只支持字符串委乌,所以需要用JSON轉(zhuǎn)換:
JSON.stringify(state.subscribeList);
// array -> string
JSON.parse(window.localStorage.getItem("subscribeList"));
// string -> array
1.簡介
多級組件嵌套需要傳遞數(shù)據(jù)時,通常使用的方法是通過vuex荣回。但如果僅僅是傳遞數(shù)據(jù)遭贸,而不做中間處理,使用 vuex 處理心软,未免有點大材小用壕吹。為此Vue2.4 版本提供了另一種方法----?$attrs/?$listeners
$attrs:包含了父作用域中不被 prop 所識別 (且獲取) 的特性綁定 (class 和 style 除外)。當(dāng)一個組件沒有聲明任何 prop 時删铃,這里會包含所有父作用域的綁定 (class 和 style 除外)耳贬,并且可以通過 v-bind="$attrs" 傳入內(nèi)部組件。通常配合 interitAttrs 選項一起使用泳姐。
$listeners:包含了父作用域中的 (不含 .native 修飾器的) v-on 事件監(jiān)聽器效拭。它可以通過 v-on="$listeners" 傳入內(nèi)部組件接下來我們看個跨級通信的例子:
//index.vue
浪里行舟
importchildCom1from"./childCom1.vue"
exportdefault{
components: {childCom1},
? ? data (){
return{
foo:"javascript",
boo:"Html",
coo:"Css",
doo:"Vue"
? ? ? ? }
? ? }
}
//childCom1.vue
foo:{{foo}}
childCom1的$attrs:{{$attrs}}
importchildCom2from"./childCom2.vue";
exportdefault{
components:{ childCom2 },
inheritAttrs:false,//可以關(guān)閉自動掛載到組件跟元素上的沒有在props聲明的屬性
props: {foo:String},// foo作為props屬性綁定
created() {console.log(this.$attrs);// { "boo": "Html", "coo": "CSS", "doo": "Vue", "title": "前端工匠" }
? }
}
//childCom2.vue
boo:{{boo}}
childCom2的$attrs:{{$attrs}}
importchildCom3from"./childCom3.vue";
exportdefault{
components:{ childCom3 },
inheritAttrs:false,//可以關(guān)閉自動掛載到組件跟元素上的沒有在props聲明的屬性
props: {foo:String},// foo作為props屬性綁定
created() {console.log(this.$attrs);// { "boo": "Html", "coo": "CSS", "doo": "Vue", "title": "前端工匠" }
? }
}
// childCom3.vue
childCom3:{{$attrs}}
exportdefault{
props: {
coo:String,
title:String
? }
};
?$attrs表示沒有繼承數(shù)據(jù)的對象,格式為{屬性名:屬性值}。Vue2.4提供了?$attrs?,$listeners?來傳遞數(shù)據(jù)與事件缎患,跨級組件之間的通訊變得更簡單慕的。
簡單來說:?$attrs與?$listeners?是兩個對象,?$attrs?里存放的是父組件中綁定的非 Props 屬性挤渔,?$listeners里存放的是父組件中綁定的非原生事件肮街。
1.簡介
Vue2.2.0新增API,這對選項需要一起使用,以允許一個祖先組件向其所有子孫后代注入一個依賴判导,不論組件層次有多深嫉父,并在起上下游關(guān)系成立的時間里始終生效。一言而蔽之:祖先組件中通過provider來提供變量眼刃,然后在子孫組件中通過inject來注入變量绕辖。provide / inject API 主要解決了跨級組件間的通信問題,不過它的使用場景擂红,主要是子組件獲取上級組件的狀態(tài)仪际,跨級組件間建立了一種主動提供與依賴注入的關(guān)系。
2.舉個例子
假設(shè)有兩個組件: A.vue 和 B.vue昵骤,B 是 A 的子組件
// A.vue
exportdefault{
provide: {name:'浪里行舟'}
}
// B.vue
exportdefault{
inject: ['name'],
? mounted () {
console.log(this.name);// 浪里行舟
? }
}
可以看到树碱,在 A.vue 里,我們設(shè)置了一個provide: name变秦,值為 浪里行舟成榜,它的作用就是將name這個變量提供給它的所有子組件。而在 B.vue 中蹦玫,通過?inject注入了從 A 組件中提供的name變量赎婚,那么在組件 B 中,就可以直接通過this.name訪問這個變量了樱溉,它的值也是 浪里行舟惑淳。這就是 provide / inject API 最核心的用法。
需要注意的是:provide 和 inject 綁定并不是可響應(yīng)的饺窿。這是刻意為之的。然而移斩,如果你傳入了一個可監(jiān)聽的對象肚医,那么其對象的屬性還是可響應(yīng)的----vue官方文檔 所以,上面 A.vue 的 name 如果改變了向瓷,B.vue 的?this.name?是不會改變的肠套,仍然是 浪里行舟。
3.provide與inject 怎么實現(xiàn)數(shù)據(jù)響應(yīng)式
一般來說猖任,有兩種辦法:
provide祖先組件的實例你稚,然后在子孫組件中注入依賴,這樣就可以在子孫組件中直接修改祖先組件的實例的屬性,不過這種方法有個缺點就是這個實例上掛載很多沒有必要的東西比如props刁赖,methods
使用2.6最新API Vue.observable 優(yōu)化響應(yīng)式 provide(推薦)
我們來看個例子:孫組件D搁痛、E和F獲取A組件傳遞過來的color值,并能實現(xiàn)數(shù)據(jù)響應(yīng)式變化宇弛,即A組件的color變化后鸡典,組件D、E枪芒、F不會跟著變(核心代碼如下:)
// A 組件
<div>
? ? ? <h1>A 組件</h1>
改變color
? ? ? <ChildrenB/><ChildrenC/>
</div>
......
data() {
return
? ? {
color:"blue"
? ? };
? },
// provide() {
//? return {
//? ? theme: {
//? ? ? color: this.color //這種方式綁定的數(shù)據(jù)并不是可響應(yīng)的
//? ? } // 即A組件的color變化后彻况,組件D、E舅踪、F不會跟著變
//? };
// },
? provide() {
return{
theme:this//方法一:提供祖先組件的實例
? ? };
? },
? methods: {
? ? changeColor(color) {?
if(color) {
this.color = color;
}else{
this.color =this.color ==="blue"?"red":"blue";
? ? }
? }
? }
// 方法二:使用vue2.6最新API Vue.observable 優(yōu)化響應(yīng)式 provide
// provide() {
//? this.theme = Vue.observable({
//? ? color: "blue"
//? });
//? return {
//? ? theme: this.theme
//? };
// },
// methods: {
//? changeColor(color) {
//? ? if (color) {
//? ? ? this.theme.color = color;
//? ? } else {
//? ? ? this.theme.color = this.theme.color === "blue" ? "red" : "blue";
//? ? }
//? }?
// }
// F 組件
F 組件
exportdefault
{
inject: {
theme: {
//函數(shù)式組件取值不一樣? ?
default:() =>({})
? }
? }
};
雖說provide 和 inject 主要為高階插件/組件庫提供用例纽甘,但如果你能在業(yè)務(wù)中熟練運用,可以達到事半功倍的效果抽碌!
ref:如果在普通的 DOM 元素上使用悍赢,引用指向的就是 DOM 元素;如果用在子組件上咬展,引用就指向組件實例
$parent/$children:訪問父 / 子實例
需要注意的是:這兩種都是直接得到組件實例泽裳,使用后可以直接調(diào)用組件的方法或訪問數(shù)據(jù)。我們先來看個用?ref來訪問組件的例子:
// component-a 子組件
exportdefault
{
? data () {?
return
? ? {
title:'Vue.js'
? ? }
? },
methods: {
? ? sayHello () {
window.alert('Hello');
? ? }
? }
}
// 父組件
<template>
</template>
exportdefault
{
? ? mounted () {? ?
constcomA =this.$refs.comA;
console.log(comA.title);// Vue.js
comA.sayHello();// 彈窗
? ? }
? }
不過破婆,這兩種方法的弊端是涮总,無法在跨級或兄弟間通信。
// parent.vue
我們想在 component-a 中祷舀,訪問到引用它的頁面中(這里就是 parent.vue)的兩個 component-b 組件瀑梗,那這種情況下,就得配置額外的插件或工具了裳扯,比如 Vuex 和 Bus 的解決方案抛丽。
常見使用場景可以分為三類:
父子通信: 父向子傳遞數(shù)據(jù)是通過 props,子向父是通過 events($emit)饰豺;通過父鏈 / 子鏈也可以通信($parent/$children)亿鲜;ref 也可以訪問組件實例;provide / inject API冤吨;$attrs/$listeners
兄弟通信: Bus蒿柳;Vuex
跨級通信: Bus;Vuex漩蟆;provide / inject API垒探、$attrs/$listeners