Vue中 computed 和 watch 的區(qū)別

computedwatchVue 項目中可以說是非常常見慧瘤,這兩個方法看似都能實現(xiàn)對數(shù)據(jù)的監(jiān)聽咙好,那么兩者之間有什么區(qū)別呢仗哨?

computed 計算屬性


計算屬性基于 data 中聲明過或者父組件傳遞的 props 中的數(shù)據(jù)通過計算得到的一個新值盗迟,這個新值只會根據(jù)已知值的變化而變化娩缰。通俗來講就是:這個屬性依賴其他屬性脂新,由其他屬性計算而來挪捕。 得出使用 computed 的常用場景為:

如果一個屬性是由其他屬性計算而來的,這個屬性依賴其他屬性争便,是一個多對一或者一對一级零,一般用 computed

先來看一看 computed 最常用的場景栗子:

<p>姓名:{{ fullName }}</p>
<script>
export default {
  data() {
    return {
      firstName: '張',
      lastName: '三'
    }
  },
  computed: {
    fullName() {
      return this.firstName + this.lastName
    }
  }
};
</script>

computed 屬性對象中定義計算屬性的方法滞乙,和取 data 對象里的數(shù)據(jù)屬性一樣以屬性訪問的形式調(diào)用奏纪,即在頁面中使用 {{ 方法名 }} 來顯示計算的結(jié)果。

如上栗子中我們在 computed 中使用了 fullName 方法斩启,如果此時我們在 data 中也聲明一個屬性 fullName序调,會報錯 Duplicated key 'fullName'。因為 data 中的 fullName 屬性和 computed 中的 fullName 方法存在重復(fù)兔簇。如下栗子:

// 錯誤用法
data() {
  return {
    firstName: '張',
    lastName: '三',
    fullName: '張三'
  }
},
computed: {
  fullName() {
    return this.firstName + this.lastName
  }
}

Vue 的官方文檔中发绢,還特意強調(diào)了 computed 的一個重要特點硬耍,就是它具有緩存功能,我們先來看個栗子:

<p>姓名:{{ fullName }}</p>
<p>姓名:{{ fullName }}</p>
<p>姓名:{{ fullName }}</p>
<p>姓名:{{ fullName }}</p>
<p>姓名:{{ fullName }}</p>
<script>
export default {
  data() {
    return {
      firstName: '張',
      lastName: '三'
    }
  },
  computed: {
    fullName() {
      console.log('computed') // 控制臺只打印了一次
      return this.firstName + this.lastName
    }
  }
};
</script>

我們在頁面上多次顯示 fullName 朴摊,實際上這個方法只執(zhí)行了一次默垄,所以此處又可得出結(jié)論:computed 中的內(nèi)部方法在重復(fù)的調(diào)用中,只要依賴數(shù)據(jù)不變甚纲,直接取緩存中的計算結(jié)果口锭。只有依賴型數(shù)據(jù)發(fā)生改變,computed 才會重新計算介杆。

computed 中鹃操,所有的屬性都有一個 get() 和一個 set(),默認一般走 get() 春哨,但是如果我們直接修改 fullName 時荆隘,則會調(diào)用 set() 方法。我們將上述代碼使用 get()set() 進行拆分赴背,得到如下栗子:

<p>姓名:{{ fullName }}</p>
<button @click="handleClick">更改姓名</button>
<script>
export default {
  data() {
    return {
      firstName: '張',
      lastName: '三'
    }
  },
  computed: {
    fullName: {
      get() {
        return this.firstName + this.lastName
      },
      set(val) {
        console.log(val) // 李四
      }
    }
  }
  methods: {
    handleClick() {
      this.fullName = '李四'
    }
  }
};
</script>

同時 computed 中不支持異步椰拒,當(dāng)computed內(nèi)有異步操作時會報錯,這里用 setTimeout 演示栗子如下:

computed: {
  fullName() {
    setTimeout(() => { // 報錯
      return this.firstName + this.lastName
    }, 30)
  }
}

所以綜上總結(jié) computed 的一些特點如下:

  • 支持緩存凰荚,只有依賴數(shù)據(jù)發(fā)生改變燃观,才會重新進行計算
  • 不支持異步,當(dāng) computed 內(nèi)有異步操作時無效
  • 如果一個屬性是由其他屬性計算而來的便瑟,這個屬性依賴其他屬性缆毁,是一個多對一或者一對一,一般用 computed
  • 如果 computed 屬性屬性值是函數(shù)到涂,那么默認會走 get() 脊框;函數(shù)的返回值就是屬性的屬性值;在 computed 中的践啄,屬性都有一個 get() 和一個 set()浇雹,當(dāng)數(shù)據(jù)變化時,調(diào)用 set()屿讽。

watch 監(jiān)聽屬性


通過 vm 對象的 $watch()watch 配置來監(jiān)聽 Vue 實例上的屬性變化箫爷,或某些特定數(shù)據(jù)的變化,然后執(zhí)行某些具體的業(yè)務(wù)邏輯和操作聂儒。當(dāng)屬性變化時虎锚,回調(diào)函數(shù)自動調(diào)用,在函數(shù)內(nèi)部進行計算衩婚。其可以監(jiān)聽的數(shù)據(jù)來源:data窜护,propscomputed 內(nèi)的數(shù)據(jù)非春。

監(jiān)聽函數(shù)有兩個參數(shù)柱徙,第一個參數(shù)是最新的值缓屠,第二個參數(shù)是輸入之前的值,順序一定是先新值再舊值护侮,如果只寫一個參數(shù)敌完,那就是最新屬性值。

在使用時選擇 watch 還是 computed羊初,還有一個參考點就是官網(wǎng)說的:當(dāng)需要在數(shù)據(jù)變化時執(zhí)行異步或開銷較大的操作時滨溉,watch方式是最有用的。所以 watch 一定是支持異步的长赞。

watch 默認的是淺監(jiān)聽晦攒,(淺監(jiān)聽指只能監(jiān)聽到基礎(chǔ)類型的值變化情況),無法進行深度監(jiān)聽(深度監(jiān)聽指比如引用類型的值變化就無法正確監(jiān)聽)得哆。直接看個栗子吧:

<div>
  <input type="text" v-model="name" />
  <input type="text" v-model="info.city" />
  <button @click="handleClickName">更改姓名</button>
  <button @click="handleClickInfo">更改信息</button>
</div>
<script>
export default {
  data() {
    return {
      name: '哪吒',
      info: {
        city: '北京'
      }
    }
  },
  watch: {
    name(newVal, oldVal) {
      // 值類型脯颜,可正常拿到
      console.log('watch name', newVal, oldVal) // 姜子牙,哪吒
    },
    info(newVal, oldVal) {
       // 引用類型贩据,拿不到
      console.log('watch info', newVal, oldVal) // 控制臺無輸出結(jié)果
    },
    info: {
      handler(newVal, oldVal) {
        console.log('watch info', newVal, oldVal)
      },
      deep: true // 深度監(jiān)聽
    }
  },
  methods: {
    handleClickName() {
      this.name = '姜子牙'
    },
    handleClickInfo() {
      this.info.city = '上海'
    }
  }
}
</script>

官方告訴我們使用 watch 監(jiān)聽復(fù)雜數(shù)據(jù)類型就需要用到深度監(jiān)聽 deep栋操。

  • deep:為了發(fā)現(xiàn)對象內(nèi)部值的變化,可以在選項參數(shù)中指定 deep: true饱亮。注意監(jiān)聽數(shù)組的變更不需要這么做讼庇。

我們針對上述代碼使用 deep 進行重寫:

watch: {
  info: {
    handler(newVal, oldVal) {
      console.log('watch info', newVal, oldVal) // {__ob__: Observer}
    },
    deep: true // 深度監(jiān)聽
  }
}

得到打印結(jié)果如下:

watch 深度監(jiān)聽.png

好吧!并沒有得到我們的預(yù)期近尚,打印出來的 newValoldVal 值是一樣的,所以深度監(jiān)聽雖然可以監(jiān)聽到對象的變化场勤,但是無法監(jiān)聽到對象里面哪個具體屬性的變化戈锻。這是因為它們的引用指向同一個對象/數(shù)組。Vue 不會保留變更之前值的副本和媳。 vm.$watch 深度監(jiān)聽

若果要監(jiān)聽對象的單個屬性的變化格遭,有兩種方法:

  • 直接監(jiān)聽對象的屬性
watch: {
  'info.city'(newVal, oldVal) {
    console.log('watch info', newVal, oldVal) // watch info 上海 北京
  }
}
  • computed 屬性配合使用,computed 返回想要監(jiān)聽的屬性值留瞳,watch 用來監(jiān)聽
computed: {
  changeCity() {
    return this.info.city
  }
},
watch: {
  changeCity(newVal, oldVal) {
    console.log('watch info', newVal, oldVal) // watch info 上海 北京
  }
}

上面的代碼都是我們定義了一個按鈕拒迅,通過按鈕手動修改值來觸發(fā)值的變化進而使用 watch 進行監(jiān)聽,那么我們能不能在頁面初始化的時候就直接使用 watch 呢她倘?認識 handler 方法和 immediate 屬性璧微。

<p>姓名:{{ fullName }}</p> // 張三
<script>
  data() {
    return {
      firstName: '張',
      lastName: '三',
      fullName: ''
    }
  },
  watch: {
    fullName: {
      handler() {
        this.fullName = this.firstName + this.lastName
      },
      immediate: true // 設(shè)置為 true,理解執(zhí)行 handle 方法
    }
  }
</script>

computed 中我們知道每一個屬性其實都有 get()set()硬梁,在 watch 中每一個屬性其實都有 handle()imediate 屬性(默認為 false)前硫。watch 的特點是最初綁定的時候是不會執(zhí)行的,而 immediate:true 代表如果在 wacth 里聲明了荧止,那么就會立即先去執(zhí)行里面的 handler 方法屹电。并且如果我們在 computed 中如果定義了 fullName 那么在 data 里就不能重復(fù)定義 fullName阶剑,而 watch 則不受影響。

日常開發(fā)中危号,我們還可以使用 watch 來監(jiān)聽路由的變化牧愁,如下栗子:

watch: {
  '$route'(to,from){
    console.log(to);   //to表示去往的界面
    console.log(from); //from表示來自于哪個界面
    if(to.path=="/shop/detail"){
      console.log("商品詳情");
    }
  }
},

總結(jié):

watchcomputed 都是以 Vue 的依賴追蹤機制為基礎(chǔ)的,當(dāng)某一個依賴型數(shù)據(jù)發(fā)生變化的時候外莲,所有依賴這個數(shù)據(jù)的相關(guān)數(shù)據(jù)會自動發(fā)生變化猪半,即自動調(diào)用相關(guān)的函數(shù),來實現(xiàn)數(shù)據(jù)的變動苍狰。當(dāng)依賴的值變化時办龄,在 watch 中,是可以做一些復(fù)雜的操作的淋昭,而 computed 中的依賴俐填,僅僅是一個值依賴于另一個值,是值上的依賴翔忽。

兩者常用應(yīng)用場景區(qū)分:

computed:用于處理復(fù)雜的邏輯運算英融;一個數(shù)據(jù)受一個或多個數(shù)據(jù)影響;用來處理 watchmethods 無法處理的歇式,或處理起來不方便的情況驶悟。例如處理模板中的復(fù)雜表達式、購物車?yán)锩娴纳唐窋?shù)量和總金額之間的變化關(guān)系等材失。

watch:用來處理當(dāng)一個屬性發(fā)生變化時痕鳍,需要執(zhí)行某些具體的業(yè)務(wù)邏輯操作,或要在數(shù)據(jù)變化時執(zhí)行異步或開銷較大的操作龙巨;一個數(shù)據(jù)改變影響多個數(shù)據(jù)笼呆。例如用來監(jiān)控路由、inpurt 輸入框值的特殊處理等旨别。

兩者主要的區(qū)別:


computed
  • 初始化顯示或者相關(guān)的 data诗赌、props 等屬性數(shù)據(jù)發(fā)生變化的時候調(diào)用
  • computed 不支持異步;
  • 計算屬性不在 data 中秸弛,它是基于 dataprops 中的數(shù)據(jù)通過計算得到的一個新值铭若,這個新值根據(jù)已知值的變化而變化;
  • computed 屬性對象中定義計算屬性的方法递览,和取 data 對象里的數(shù)據(jù)屬性一樣叼屠,以屬性訪問的形式調(diào)用,但是 computed 中定義的屬性名稱不能和 data 中的屬性名稱重合绞铃;
  • 如果 computed 屬性值是函數(shù)环鲤,那么默認會走 get 方法,必須要有一個返回值憎兽,函數(shù)的返回值就是屬性的屬性值冷离;
  • computed 屬性值默認會緩存計算結(jié)果吵冒,在重復(fù)的調(diào)用中,只要依賴數(shù)據(jù)不變西剥,直接取緩存中的計算結(jié)果痹栖,只有依賴型數(shù)據(jù)發(fā)生改變伙单,computed 才會重新計算然眼;
  • computed 中的,屬性都有一個 get 和一個 set 方法仔夺,當(dāng)數(shù)據(jù)變化時咆畏,調(diào)用 set 方法南捂。
watch
  • 主要用來監(jiān)聽某些特定數(shù)據(jù)的變化,從而進行某些具體的業(yè)務(wù)邏輯操作旧找,可以看作是 computedmethods 的結(jié)合體溺健;
  • 可以監(jiān)聽的數(shù)據(jù)來源:dataprops钮蛛,computed 內(nèi)的數(shù)據(jù)鞭缭;
  • watch 支持異步;
  • 不支持緩存魏颓,監(jiān)聽的數(shù)據(jù)改變岭辣,直接會觸發(fā)相應(yīng)的操作;
  • 監(jiān)聽函數(shù)有兩個參數(shù)甸饱,第一個參數(shù)是最新的值沦童,第二個參數(shù)是輸入之前的值,順序一定是新值叹话,舊值偷遗。

如果文中有不對的地方或者理解有誤的地方歡迎大家提出并指正。每一天都要相對前一天進步一點渣刷,加油!4V颉辅柴!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市瞭吃,隨后出現(xiàn)的幾起案子碌嘀,更是在濱河造成了極大的恐慌,老刑警劉巖歪架,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件股冗,死亡現(xiàn)場離奇詭異,居然都是意外死亡和蚪,警方通過查閱死者的電腦和手機止状,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門烹棉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人怯疤,你說我怎么就攤上這事浆洗。” “怎么了集峦?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵伏社,是天一觀的道長。 經(jīng)常有香客問我塔淤,道長摘昌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任高蜂,我火速辦了婚禮聪黎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘妨马。我一直安慰自己挺举,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布烘跺。 她就那樣靜靜地躺著湘纵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪滤淳。 梳的紋絲不亂的頭發(fā)上梧喷,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天,我揣著相機與錄音脖咐,去河邊找鬼铺敌。 笑死,一個胖子當(dāng)著我的面吹牛屁擅,可吹牛的內(nèi)容都是我干的偿凭。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼派歌,長吁一口氣:“原來是場噩夢啊……” “哼弯囊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起胶果,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤匾嘱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后早抠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體霎烙,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了悬垃。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片游昼。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖盗忱,靈堂內(nèi)的尸體忽然破棺而出酱床,到底是詐尸還是另有隱情,我是刑警寧澤趟佃,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布扇谣,位于F島的核電站,受9級特大地震影響闲昭,放射性物質(zhì)發(fā)生泄漏罐寨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一序矩、第九天 我趴在偏房一處隱蔽的房頂上張望鸯绿。 院中可真熱鬧,春花似錦簸淀、人聲如沸瓶蝴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽舷手。三九已至,卻和暖如春劲绪,著一層夾襖步出監(jiān)牢的瞬間男窟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工贾富, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留歉眷,地道東北人。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓颤枪,卻偏偏與公主長得像汗捡,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子畏纲,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,627評論 2 350