高頻前端面試題匯總之Vue篇

Vue共識(shí):
在 Vue 中我們習(xí)慣把虛擬DOM稱(chēng)為 VNode,它既可以代表一個(gè) VNode 節(jié)點(diǎn),也可以代表一顆 VNode 樹(shù)。
組件的核心是它能夠產(chǎn)出一堆VNode。
對(duì)于 Vue 來(lái)說(shuō)一個(gè)組件的核心就是它的渲染函數(shù),組件的掛載本質(zhì)就是執(zhí)行渲染函數(shù)并得到要渲染的VNode,至于data/props/computed 這都是為渲染函數(shù)產(chǎn)出 VNode 過(guò)程中提供數(shù)據(jù)來(lái)源服務(wù)的,最關(guān)鍵的就是組件最終產(chǎn)出的VNode,因?yàn)檫@個(gè)才是要渲染的內(nèi)容。


vue.png

一、Vue基礎(chǔ)

1. Vue的基本原理

當(dāng)一個(gè)Vue實(shí)例創(chuàng)建時(shí),vue會(huì)遍歷data選項(xiàng)的屬性,用 Object.defineProperty(vue3.0使用proxy )將它們轉(zhuǎn)為 getter/setter 并且在內(nèi)部追蹤相關(guān)依賴(lài),在屬性被訪(fǎng)問(wèn)和修改時(shí)通知變化。 每個(gè)組件實(shí)例都有相應(yīng)的 watcher程序?qū)嵗?,它?huì)在組件渲染的過(guò)程中把屬性記錄為依賴(lài),之后當(dāng)依賴(lài)項(xiàng)的setter被調(diào)用時(shí),會(huì)通知watcher重新計(jì)算,從而致使它關(guān)聯(lián)的組件得以更新。


12cd66d0cdf5233131cc966ff9e70a37.png

2. 雙向數(shù)據(jù)綁定的原理

vue.js 是采用數(shù)據(jù)劫持結(jié)合發(fā)布者-訂閱者模式的方式,通過(guò)Object.defineProperty()來(lái)劫持各個(gè)屬性的setter,getter,在數(shù)據(jù)變動(dòng)時(shí)發(fā)布消息給訂閱者,觸發(fā)相應(yīng)的監(jiān)聽(tīng)回調(diào)。主要分為以下幾個(gè)步驟:

1、需要observe的數(shù)據(jù)對(duì)象進(jìn)行遞歸遍歷,包括子屬性對(duì)象的屬性,都加上setter和getter這樣的話(huà),給這個(gè)對(duì)象的某個(gè)值賦值,就會(huì)觸發(fā)setter,那么就能監(jiān)聽(tīng)到了數(shù)據(jù)變化

2、compile解析模板指令,將模板中的變量替換成數(shù)據(jù),然后初始化渲染頁(yè)面視圖,并將每個(gè)指令對(duì)應(yīng)的節(jié)點(diǎn)綁定更新函數(shù),添加監(jiān)聽(tīng)數(shù)據(jù)的訂閱者,一旦數(shù)據(jù)有變動(dòng),收到通知,更新視圖

3、Watcher訂閱者是Observer和Compile之間通信的橋梁,主要做的事情是:
①在自身實(shí)例化時(shí)往屬性訂閱器(dep)里面添加自己
②自身必須有一個(gè)update()方法
③待屬性變動(dòng)dep.notice()通知時(shí),能調(diào)用自身的update()方法,并觸發(fā)Compile中綁定的回調(diào),則功成身退。

4、MVVM作為數(shù)據(jù)綁定的入口,整合Observer、Compile和Watcher三者,通過(guò)Observer來(lái)監(jiān)聽(tīng)自己的model數(shù)據(jù)變化,通過(guò)Compile來(lái)解析編譯模板指令,最終利用Watcher搭起Observer和Compile之間的通信橋梁,達(dá)到數(shù)據(jù)變化 -> 視圖更新;視圖交互變化(input) -> 數(shù)據(jù)model變更的雙向綁定效果。

3. 使用 Object.defineProperty() 來(lái)進(jìn)行數(shù)據(jù)劫持有什么缺點(diǎn)?

有一些對(duì)屬性的操作,使用這種方法無(wú)法攔截,比如說(shuō)通過(guò)下標(biāo)方式修改數(shù)組數(shù)據(jù)或者給對(duì)象新增屬性,vue 內(nèi)部通過(guò)重寫(xiě)函數(shù)解決了這個(gè)問(wèn)題。在 Vue3.0 中已經(jīng)不使用這種方式了,而是通過(guò)使用 Proxy 對(duì)對(duì)象進(jìn)行代理,從而實(shí)現(xiàn)數(shù)據(jù)劫持。使用Proxy 的好處是它可以完美的監(jiān)聽(tīng)到任何方式的數(shù)據(jù)改變,唯一的缺點(diǎn)是兼容性的問(wèn)題,因?yàn)檫@是 ES6 的語(yǔ)法。

4. MVVM和MVC的區(qū)別

MVC、MVP 和 MVVM 是三種常見(jiàn)的軟件架構(gòu)設(shè)計(jì)模式,主要通過(guò)分離關(guān)注點(diǎn)的方式來(lái)組織代碼結(jié)構(gòu),優(yōu)化我們的開(kāi)發(fā)效率。
在開(kāi)發(fā)單頁(yè)面應(yīng)用時(shí),往往一個(gè)路由頁(yè)面對(duì)應(yīng)了一個(gè)腳本文件,所有的頁(yè)面邏輯都在一個(gè)腳本文件里。頁(yè)面的渲染、數(shù)據(jù)的獲取,對(duì)用戶(hù)事件的響應(yīng)所有的應(yīng)用邏輯都混合在一起,這樣在開(kāi)發(fā)簡(jiǎn)單項(xiàng)目時(shí),可能看不出什么問(wèn)題,當(dāng)時(shí)一旦項(xiàng)目變得復(fù)雜,那么整個(gè)文件就會(huì)變得冗長(zhǎng),混亂,這樣對(duì)我們的項(xiàng)目開(kāi)發(fā)和后期的項(xiàng)目維護(hù)是非常不利的。

(1)MVC

MVC 通過(guò)分離 Model、View 和 Controller 的方式來(lái)組織代碼結(jié)構(gòu)。其中 View 負(fù)責(zé)頁(yè)面的顯示邏輯,Model 負(fù)責(zé)存儲(chǔ)頁(yè)面的業(yè)務(wù)數(shù)據(jù),以及對(duì)相應(yīng)數(shù)據(jù)的操作。并且 View 和 Model 應(yīng)用了觀(guān)察者模式,當(dāng) Model 層發(fā)生改變的時(shí)候它會(huì)通知有關(guān) View 層更新頁(yè)面。Controller 層是 View 層和 Model 層的紐帶,它主要負(fù)責(zé)用戶(hù)與應(yīng)用的響應(yīng)操作,當(dāng)用戶(hù)與頁(yè)面產(chǎn)生交互的時(shí)候,Controller 中的事件觸發(fā)器就開(kāi)始工作了,通過(guò)調(diào)用 Model 層,來(lái)完成對(duì) Model 的修改,然后 Model 層再去通知 View 層更新。


dbcd93678d7afbc145082ca7c41d7841.png

(2)MVVM

MVVM 分為 Model、View、ViewModel 三者。

Model代表數(shù)據(jù)模型,數(shù)據(jù)和業(yè)務(wù)邏輯都在Model層中定義;
View代表UI視圖,負(fù)責(zé)數(shù)據(jù)的展示;
ViewModel負(fù)責(zé)監(jiān)聽(tīng)Model中數(shù)據(jù)的改變并且控制視圖的更新,處理用 戶(hù)交互操作;

Model和View并無(wú)直接關(guān)聯(lián),而是通過(guò)ViewModel來(lái)進(jìn)行聯(lián)系的,Model和ViewModel之間有著雙向數(shù)據(jù)綁定的聯(lián)系。因此當(dāng)Model中 的數(shù)據(jù)改變時(shí)會(huì)觸發(fā)View層的刷新,View中由于用戶(hù)交互操作而改變的 數(shù)據(jù)也會(huì)在Model中同步。
這種模式實(shí)現(xiàn)了 Model和View的數(shù)據(jù)自動(dòng)同步,因此開(kāi)發(fā)者只需要專(zhuān)注 對(duì)數(shù)據(jù)的維護(hù)操作即可,而不需要自己操作DOM。


2003aeb436b48322038d4b21891f1758.png

(2)MVP

MVP 模式與 MVC 唯一不同的在于 Presenter 和 Controller。在 MVC 模式中我們使用觀(guān)察者模式,來(lái)實(shí)現(xiàn)當(dāng) Model 層數(shù)據(jù)發(fā)生變化的時(shí)候,通知 View 層的更新。這樣 View 層和 Model 層耦合在一起,當(dāng)項(xiàng)目邏輯變得復(fù)雜的時(shí)候,可能會(huì)造成代碼的混亂,并且可能會(huì)對(duì)代碼的復(fù)用性造成一些問(wèn)題。MVP 的模式通過(guò)使用 Presenter 來(lái)實(shí)現(xiàn)對(duì) View 層和 Model 層的解耦。MVC 中的Controller 只知道 Model 的接口,因此它沒(méi)有辦法控制 View 層的更新,MVP 模式中,View 層的接口暴露給了 Presenter 因此我們可以在 Presenter 中將 Model 的變化和 View 的變化綁定在一起,以此來(lái)實(shí)現(xiàn) View 和 Model 的同步更新。這樣就實(shí)現(xiàn)了對(duì) View 和 Model 的解耦,Presenter 還包含了其他的響應(yīng)邏輯。

5. Computed和Watch的區(qū)別

對(duì)于Computed:

它支持緩存,只有依賴(lài)的數(shù)據(jù)發(fā)生了變化,才會(huì)重新計(jì)算
不支持異步,當(dāng)Computed中有異步操作時(shí),無(wú)法監(jiān)聽(tīng)數(shù)據(jù)的變化
computed的值會(huì)默認(rèn)走緩存,計(jì)算屬性是基于它們的響應(yīng)式依賴(lài)進(jìn)行緩存的,也就是基于data聲明過(guò),或者父組件傳遞過(guò)來(lái)的props中的數(shù)據(jù)進(jìn)行計(jì)算的。
如果一個(gè)屬性是由其他屬性計(jì)算而來(lái)的,這個(gè)屬性依賴(lài)其他的屬性,一般會(huì)使用computed
如果computed屬性的屬性值是函數(shù),那么默認(rèn)使用get方法,函數(shù)的返回值就是屬性的屬性值;在computed中,屬性有一個(gè)get方法和一個(gè)set方法,當(dāng)數(shù)據(jù)發(fā)生變化時(shí),會(huì)調(diào)用set方法。

對(duì)于Watch:

它不支持緩存,數(shù)據(jù)變化時(shí),它就會(huì)觸發(fā)相應(yīng)的操作
支持異步監(jiān)聽(tīng)
監(jiān)聽(tīng)的函數(shù)接收兩個(gè)參數(shù),第一個(gè)參數(shù)是最新的值,第二個(gè)是變化之前的值
當(dāng)一個(gè)屬性發(fā)生變化時(shí),就需要執(zhí)行相應(yīng)的操作
監(jiān)聽(tīng)數(shù)據(jù)必須是data中聲明的或者父組件傳遞過(guò)來(lái)的props中的數(shù)據(jù),當(dāng)發(fā)生變化時(shí),會(huì)出大其他操作,函數(shù)有兩個(gè)的參數(shù):
    immediate:組件加載立即觸發(fā)回調(diào)函數(shù)
    deep:深度監(jiān)聽(tīng),發(fā)現(xiàn)數(shù)據(jù)內(nèi)部的變化,在復(fù)雜數(shù)據(jù)類(lèi)型中使用,例如數(shù)組中的對(duì)象發(fā)生變化。需要注意的是,deep無(wú)法監(jiān)聽(tīng)到數(shù)組和對(duì)象內(nèi)部的變化。

當(dāng)想要執(zhí)行異步或者昂貴的操作以響應(yīng)不斷的變化時(shí),就需要使用watch。

總結(jié):

computed 計(jì)算屬性 : 依賴(lài)其它屬性值,并且 computed 的值有緩存,只有它依賴(lài)的 屬性值發(fā)生改變,下一次獲取 computed 的值時(shí)才會(huì)重新計(jì)算 computed 的值。
watch 偵聽(tīng)器 : 更多的是觀(guān)察的作用,無(wú)緩存性,類(lèi)似于某些數(shù)據(jù)的監(jiān)聽(tīng)回調(diào),每 當(dāng)監(jiān)聽(tīng)的數(shù)據(jù)變化時(shí)都會(huì)執(zhí)行回調(diào)進(jìn)行后續(xù)操作。

運(yùn)用場(chǎng)景:

當(dāng)我們需要進(jìn)行數(shù)值計(jì)算,并且依賴(lài)于其它數(shù)據(jù)時(shí),應(yīng)該使用 computed,因?yàn)榭梢岳?用 computed 的緩存特性,避免每次獲取值時(shí),都要重新計(jì)算。
當(dāng)我們需要在數(shù)據(jù)變化時(shí)執(zhí)行異步或開(kāi)銷(xiāo)較大的操作時(shí),應(yīng)該使用 watch,使用 watch 選項(xiàng)允許我們執(zhí)行異步操作 ( 訪(fǎng)問(wèn)一個(gè) API ),限制我們執(zhí)行該操作的頻率, 并在我們得到最終結(jié)果前,設(shè)置中間狀態(tài)。這些都是計(jì)算屬性無(wú)法做到的。

6. Computed 和 Methods 的區(qū)別

https://segmentfault.com/a/1190000014478664
methods與computed之間的差別:
(1)methods和computed里的方法在初始化執(zhí)行過(guò)后,只要任何值有更新,那么所有在computed計(jì)算屬性里和其相關(guān)的值都會(huì)更新。

methods只有在調(diào)用的時(shí)候才會(huì)執(zhí)行對(duì)應(yīng)的方法,不會(huì)自動(dòng)同步數(shù)據(jù)。
(2) computed是屬性訪(fǎng)問(wèn),而methods是函數(shù)調(diào)用
computed帶有緩存功能,而methods不是
computed其實(shí)是就是屬性,之所以與data區(qū)分開(kāi),只不過(guò)為了防止文本插值中邏輯過(guò)重,會(huì)導(dǎo)致不易維護(hù)

(3) computed定義的方法我們是以屬性的形式訪(fǎng)問(wèn)的,和data里的屬性訪(fǎng)問(wèn)形式一樣,{{function}}
但是methods定義的方法,我們必須要加上()來(lái)調(diào)用,如{{function()}},否則,視圖會(huì)出現(xiàn)function (){[native code]}的情況

7. slot是什么?有什么作用?原理是什么?

slot又名插槽,是Vue的內(nèi)容分發(fā)機(jī)制,組件內(nèi)部的模板引擎使用slot元素作為承載分發(fā)內(nèi)容的出口。插槽slot是子組件的一個(gè)模板標(biāo)簽元素,而這一個(gè)標(biāo)簽元素是否顯示,以及怎么顯示是由父組件決定的。slot又分三類(lèi),默認(rèn)插槽,具名插槽和作用域插槽。

(1) 默認(rèn)插槽:又名匿名插槽,當(dāng)slot沒(méi)有指定name屬性值的時(shí)候一個(gè)默認(rèn)顯示插槽,一個(gè)組件內(nèi)只有有一個(gè)匿名插槽。
(2) 具名插槽:帶有具體名字的插槽,也就是帶有name屬性的slot,一個(gè)組件可以出現(xiàn)多個(gè)具名插槽。
(3)作用域插槽:默認(rèn)插槽、具名插槽的一個(gè)變體,可以是匿名插槽,也可以是具名插槽,該插槽的不同點(diǎn)是在子組件渲染作用域插槽時(shí),可以將子組件內(nèi)部的數(shù)據(jù)傳遞給父組件,讓父組件根據(jù)子組件的傳遞過(guò)來(lái)的數(shù)據(jù)決定如何渲染該插槽。

實(shí)現(xiàn)原理:當(dāng)子組件vm實(shí)例化時(shí),獲取到父組件傳入的slot標(biāo)簽的內(nèi)容,存放在vm.slot中,默認(rèn)插槽為vm.slot.default,具名插槽為vm.slot.xxx,xxx 為插槽名,當(dāng)組件執(zhí)行渲染函數(shù)時(shí)候,遇到slot標(biāo)簽,使用slot中的內(nèi)容進(jìn)行替換,此時(shí)可以為插槽傳遞數(shù)據(jù),若存在數(shù)據(jù),則可稱(chēng)該插槽為作用域插槽。

slot的意思是插槽,想想你的電腦主板上的各種插槽,有插CPU的,有插顯卡的,有插內(nèi)存的,有插硬盤(pán)的,所以假設(shè)有個(gè)組件是computer,組件computer:

<template>
<div>
  <slot name="CPU">這兒插你的CPU</slot>
  <slot name="GPU">這兒插你的顯卡</slot>
<slot></slot>
  <slot name="Memory">這兒插你的內(nèi)存</slot>
  <slot name="Hard-drive">這兒插你的硬盤(pán)</slot>
</div>
</template>


那么組裝一個(gè)電腦,就可以在調(diào)用組件的頁(yè)面這么寫(xiě):

<template>
<computer>
  <div slot="CPU">Intel Core i7</div>
  <div slot="GPU">GTX980Ti</div>
<div>想加內(nèi)容就加內(nèi)容</div>
  <div slot="Memory">Kingston 32G</div>
  <div slot="Hard-drive">Samsung SSD 1T</divt>
</computer>
</template>
<script>
import computerfrom "./computer";
export default {
  name: "page",
  components: {
    computer
  },
  data() {
    return {
      
    };
  },
  computed: {},
  methods: {
    
  }
};
</script>

頁(yè)面顯示:Intel Core i7 GTX980Ti 想加內(nèi)容就加內(nèi)容 Kingston 32G Samsung SSD 1T


二、生命周期

vue生命周期.png
vue生命周期desc.png

使用建議:
1. beforeCreate:加載loading事件
2. created:結(jié)束loading、初始化、請(qǐng)求數(shù)據(jù)、實(shí)現(xiàn)函數(shù)自執(zhí)行
3. mounted:拿回?cái)?shù)據(jù),配合路由鉤子做一些事
4. beforeDestory:destoryed:當(dāng)前組件已被刪除,清空相關(guān)內(nèi)容

1. created和mounted的區(qū)別

(1)created:在模板渲染成html前調(diào)用,即通常初始化某些屬性值,然后再渲染成視圖。
(2)mounted:在模板渲染成html后調(diào)用,通常是初始化頁(yè)面完成后,再對(duì)html的dom節(jié)點(diǎn)進(jìn)行一些需要的操作。

2. 接口請(qǐng)求一般放在哪個(gè)生命周期中?

可以在鉤子函數(shù) created、beforeMount、mounted 中進(jìn)行調(diào)用,因?yàn)樵谶@三個(gè)鉤子函數(shù)中,data 已經(jīng)創(chuàng)建,可以將服務(wù)端端返回的數(shù)據(jù)進(jìn)行賦值。

推薦在 created 鉤子函數(shù)中調(diào)用異步請(qǐng)求,因?yàn)樵?created 鉤子函數(shù)中調(diào)用異步請(qǐng)求有以下優(yōu)點(diǎn):
    能更快獲取到服務(wù)端數(shù)據(jù),減少頁(yè)面loading 時(shí)間;
    ssr不支持 beforeMount 、mounted 鉤子函數(shù),所以放在 created 中有助于一致性;

三、組件通信

組件通信1.png

如圖所示:
A和B、B和C、B和D都是父子關(guān)系,C和D是兄弟關(guān)系,A和C是隔代關(guān)系(可能隔多代)。
eg:
props、emit/on、vuex、parent/children、attrs/listeners和provide/inject

方法一、props/$emit

父組件A通過(guò)props的方向子組件B傳遞,B到A通過(guò)在B組件中$emit,A組件中v-on的方式實(shí)現(xiàn)。
eg:
子組件

<template>
  <div class="navBar">
    <div class="navBarItem" v-for="item in dataList" :key="item.id" :index="item.id">
      <span v-if="item.isNow" class="nowTitle" @click="navClick(item)">{{item.title}}</span>
      <span v-else class="title" @click="navClick(item)">{{item.title}}</span>
      <span class="separator">></span>
    </div>
  </div>
</template>
<script>
export default {
  name: 'navBar',
  data () {
    return {}
  },
  props: {
    dataList: Array
  },
  methods: {
    navClick (item) {
      this.$emit('navClick', item)
    }
  }
}
</script>

父組件:

<template>
  <NavBar :dataList="navBarData" @navClick="navClick"></NavBar>
</template>
<script>
import NavBar from '@/components/NavBar'
export default {
  name: 'detail',
  data () {
    return {
 navBarData: [
        { title: '鯨選資源', url: '/whaleselect', id: 1, isNow: false },
        { title: '文件詳情', url: '/jx', id: 2, isNow: true }
      ]}
  },
  props: {
    dataList: Array
  },
  methods: {
    navClick ({ url, id }) {
      this.$utils.link(`${url}/` + id)
    }
  },
 components: {
    NavBar,
   
  }
}
</script>

1.父組件向子組件傳值

父組件通過(guò)props向下傳遞數(shù)據(jù)給子組件

2.子組件向父組件傳值(通過(guò)事件形式)

子組件通過(guò)this.$emit('方法名',傳遞的值),父組件定義同名方法即可

EventBus

EventBus事件總線(xiàn)適用于父子組件、非父子組件等之間的通信;
全局或者公共組件注冊(cè)一個(gè)vue實(shí)例,利用里面的注冊(cè)喝監(jiān)聽(tīng)事件。

(1)創(chuàng)建事件中心管理組件之間的通信

// event-bus.js
import Vue from 'vue'
export const EventBus = new Vue()

(2)發(fā)送事件

假設(shè)我們有兩個(gè)兄弟組件C和D:

<template>
  <div>
    <C></C>
    <D></D>
  </div>
</template>

<script>
import Cfrom './C.vue'
import D from './D.vue'
export default {
  components: { C, D}
}
</script>

在C組件中發(fā)送事件:

<template>
  <div>
    <button @click="add">加法</button>    
  </div>
</template>

<script>
import {EventBus} from './event-bus.js' // 引入事件中心

export default {
  data(){
    return{
      num:0
    }
  },
  methods:{
    add(){
      EventBus.$emit('addition', {
        num:this.num++
      })
    }
  }
}
</script>

(3)接收事件

在D組件中發(fā)送事件:

<template>
  <div>求和: {{count}}</div>
</template>

<script>
import { EventBus } from './event-bus.js'
export default {
  data() {
    return {
      count: 0
    }
  },
  mounted() {
    EventBus.$on('addition', param => {
      this.count = this.count + param.num;
    })
  }
}
</script>

在上述代碼中,這就相當(dāng)于將num值存貯在了事件總線(xiàn)中,在其他組件中可以直接訪(fǎng)問(wèn)。事件總線(xiàn)就相當(dāng)于一個(gè)橋梁,不用組件通過(guò)它來(lái)通信。

雖然看起來(lái)比較簡(jiǎn)單,但是這種方法也有不變之處,如果項(xiàng)目過(guò)大,使用這種方式進(jìn)行通信,后期維護(hù)起來(lái)會(huì)很困難。

方法三 Vuex

vuex.png

1.原理

Vuex實(shí)現(xiàn)了一個(gè)單向數(shù)據(jù)流,在全局擁有一個(gè)State存放數(shù)據(jù),當(dāng)組件要更改State中的數(shù)據(jù)時(shí),必須通過(guò)Mutation進(jìn)行,Mutation同時(shí)提供了訂閱者模式供外部插件調(diào)用獲取State數(shù)據(jù)的更新。當(dāng)所有異步操作(常見(jiàn)于調(diào)用后端接口異步獲取更新數(shù)據(jù))或批量的同步操作需要走Action,但是Action也是無(wú)法直接修改State的,還是需要通過(guò)Mutation來(lái)修改State的數(shù)據(jù)。最后,根據(jù)State的變化,渲染到視圖上。

(1)vuex下的store.js文件

import Vuex from 'vuex'
export default new Vuex.Store({
  // 定義狀態(tài) 值 方法
  state: {
 // 獲取文件夾
    getfolderscallback: function () {},
    // 驗(yàn)證用戶(hù)的Id的token
    token: '',
    // 用戶(hù)id
    uid: '',
    // 是否登錄
    isLogin: false,
    // 用戶(hù)名
    username: '',
    // 用戶(hù)頭像
    logo: ''
  },
  mutations: {
   // 獲取文件夾
    getfolders (state) {
      state.getfolderscallback()
    },
    // 設(shè)置上面的全局變量
    setAttr (state, data) {
      state[data.name] = data.val
    }
  }
})

(2)頁(yè)面改變值

  mounted () {
    // 設(shè)置全局變量-方法-獲取文件夾
    this.$store.commit('setAttr', {
      name: 'getfolderscallback',
      val: this.getfolders
    })
  },}
  methods: {
    getuser () {
      // 本地判斷Cookie,判斷用戶(hù)是否登錄
      if (this.$utils.getCookie(this.$glb.fmCookieName) !== null) {
        this.$api.post('/center/getuser', {}, res => {
          if (!res.status) {
            return
          }
          this.$store.commit('setAttr', {name: 'isLogin', val: true})
          this.$store.commit('setAttr', {name: 'logo', val: res.data.logo})
          this.$store.commit('setAttr', {name: 'uid', val: res.data.userid})
          this.$store.commit('setAttr', {name: 'token', val: res.data.token})
          this.$store.commit('setAttr', {name: 'username', val: res.data.username})
        })
      }
    },
  // 上傳文件框選擇文件夾
    getfolders () {
      this.$api.post('/file/foldertreelist', {}, (res) => {
        this.folderList = res.data
      })
    },
}

(3)頁(yè)面調(diào)用vuex State中的值

<template>
  <!-- 登錄或未登錄 --S-->
  <div class="isLoginBox">
    <!-- 登錄 -->
    <div class="loginBox" v-if="this.$store.state.isLogin">
退出
    </div>
    <!-- 未登錄 -->
    <div class="unLoginBox" v-else>
      <div class="loginOrRegister">
        <span class="login" >登錄</span>
        <span class="line"></span>
        <span class="register">注冊(cè)</span>
      </div>
    </div>
  </div>
  <!-- 登錄或未登錄 --E-->
</template>
<script>
export default {
  name: 'SignUpBar',
  data () {
    return {
    }
  },
  methods: {
    
}
</script>

2.各模塊在核心流程中的主要功能:

1.Vue Components∶ Vue組件。HTML頁(yè)面上,負(fù)責(zé)接收用戶(hù)操作等交互行為,執(zhí)行dispatch方法觸發(fā)對(duì)應(yīng)action進(jìn)行回應(yīng)。

  1. dispatch∶操作行為觸發(fā)方法,是唯一能執(zhí)行action的方法。
    3.actions∶ 操作行為處理模塊,由組件中的$store.dispatch('action 名稱(chēng)',data1)來(lái)觸發(fā)。然后由commit()來(lái)觸發(fā)mutation的調(diào)用,間接更新state。負(fù)責(zé)處理Vue Components接收到的所有交互行為。包含同步/異步操作,支持多個(gè)同名方法,按照注冊(cè)的順序依次觸發(fā)。向后臺(tái)API請(qǐng)求的操作就在這個(gè)模塊中進(jìn)行,包括觸發(fā)其他action以及提交mutation的操作。該模塊提供了Promise的封裝,以支持action的鏈?zhǔn)接|發(fā)。
  2. commit∶狀態(tài)改變提交操作方法。對(duì)mutation進(jìn)行提交,是唯一能執(zhí)行mutation的方法。
  3. mutations∶狀態(tài)改變操作方法,由actions中的commit('mutation 名稱(chēng)')來(lái)觸發(fā)。是Vuex修改state的唯一推薦方法,其他修改方式在嚴(yán)格模式下將會(huì)報(bào)錯(cuò)。該方法只能進(jìn)行同步操作,且方法名只能全局唯一。操作之中會(huì)有一些hook暴露出來(lái),以進(jìn)行state的監(jiān)控等。
  4. state∶ 頁(yè)面狀態(tài)管理容器對(duì)象。集中存儲(chǔ)Vuecomponents中data對(duì)象的零散數(shù)據(jù),全局唯一,以進(jìn)行統(tǒng)一的狀態(tài)管理。頁(yè)面顯示所需的數(shù)據(jù)從該對(duì)象中進(jìn)行讀取,利用Vue的細(xì)粒度數(shù)據(jù)響應(yīng)機(jī)制來(lái)進(jìn)行高效的狀態(tài)更新。
    7。 getters∶ state對(duì)象讀取方法。圖中沒(méi)有單獨(dú)列出該模塊,應(yīng)該被包含在了render中,Vue Components通過(guò)該方法讀取全局state對(duì)象。

3.Vuex(狀態(tài):數(shù)組)與localStorage(字符串)

vuex是vue的狀態(tài)管理器,存儲(chǔ)的數(shù)據(jù)是響應(yīng)式的。但是并不會(huì)保存起來(lái),刷新之后就回到初始狀態(tài),具體做飯應(yīng)該在vuex里數(shù)據(jù)改變的時(shí)候拷貝一份保存到localStorge里面,刷新之后,如果localStorge里有保存的數(shù)據(jù),取出來(lái)再替換store里面的state

方法四、 attrs/listeners

方法五、依賴(lài)注入 provide/inject

祖先組件中通過(guò)provider來(lái)提供變量,然后在子孫組件中通過(guò)inject來(lái)注入變量。provide/inject API主要解決了跨級(jí)組件間的通信問(wèn)題,不過(guò)它的使用場(chǎng)景,主要是子組件獲取上級(jí)組件的狀態(tài),跨級(jí)組件間建立了一種主動(dòng)提供與依賴(lài)注入的關(guān)系。

project / inject是Vue提供的兩個(gè)鉤子,和data、methods是同級(jí)的。并且project的書(shū)寫(xiě)形式和data一樣。

project 鉤子用來(lái)發(fā)送數(shù)據(jù)或方法
inject鉤子用來(lái)接收數(shù)據(jù)或方法

eg:兩個(gè)組件:A.vue和B.vue,B是A的子組件

// A.vue
export default {
  provide: {
    name: '測(cè)試張三'
  }
}
// B.vue
export default {
  inject: ['name'],
  mounted () {
    console.log(this.name);  // 測(cè)試張三
  }
}

核心用法:在A.vue里,設(shè)置了一個(gè)provide:name,值是測(cè)試張三,它的作用就是將name這個(gè)變量提供給它的所有子組件。
在B.vue中,通過(guò)inject注入了從A組件中提供的name變量,在B組件中,就可以直接通過(guò)this.name 訪(fǎng)問(wèn)這個(gè)變量,值就是測(cè)試張三。
注意:**provide和inject綁定并不是可響應(yīng)的。這是可以為之的。然而,如果你傳入了一個(gè)可監(jiān)聽(tīng)的對(duì)象,那么其對(duì)象的屬性還是可響應(yīng)的。A.vue的name如果改變了,B.vue的this.name是不改變的,仍然是測(cè)試張三

provide與inject實(shí)現(xiàn)數(shù)據(jù)響應(yīng)式

兩個(gè)辦法:
(1)provide祖先組件的實(shí)例,然后在子孫組件中注入依賴(lài),這樣就可以在子孫組件中直接修改祖先組件的實(shí)例的屬性,不過(guò)這種方法有個(gè)缺點(diǎn)就是這個(gè)實(shí)例上掛載很多沒(méi)有必要的東西,eg:props,methods
(2)使用2.6最新API Vue.observable 優(yōu)化響應(yīng)式 provide

provide和inject.png
// A 組件 
<div>
      <h1>A 組件</h1>
      <button @click="() => changeColor()">改變color</button>
      <ChildrenB />
      <ChildrenC />
</div>
......
  data() {
    return {
      color: "blue"
    };
  },
  // provide() {
  //   return {
  //     theme: {
  //       color: this.color //這種方式綁定的數(shù)據(jù)并不是可響應(yīng)的
  //     } // 即A組件的color變化后,組件D、E、F不會(huì)跟著變
  //   };
  // },
  provide() {
    return {
      theme: this//方法一:提供祖先組件的實(shí)例
    };
  },
  methods: {
    changeColor(color) {
      if (color) {
        this.color = color;
      } else {
        this.color = this.color === "blue" ? "red" : "blue";
      }
    }
  }
  // 方法二:使用2.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 組件 
<template functional>
  <div class="border2">
    <h3 :style="{ color: injections.theme.color }">F 組件</h3>
  </div>
</template>
<script>
export default {
  inject: {
    theme: {
      //函數(shù)式組件取值不一樣
      default: () => ({})
    }
  }
};
</script>

方法五、parent/children 與ref

ref:如果在普通的DOM元素上使用,引用指向的就是DOM元素;如果用在子組件上,引用就指向組件實(shí)例;
parent/children:訪(fǎng)問(wèn)父/子實(shí)例
注意:這兩種都是直接得到組件實(shí)例,使用后可以直接調(diào)用組件的方法或訪(fǎng)問(wèn)數(shù)據(jù)。
(1)用ref來(lái)訪(fǎng)問(wèn)組件:

// component-a 子組件
export default {
  data () {
    return {
      title: 'Vue.js'
    }
  },
  methods: {
    sayHello () {
      window.alert('Hello');
    }
  }
}

// 父組件(頁(yè)面)
<template>
  <component-a ref="comA"></component-a>
</template>
<script>
  export default {
    mounted () {
      const comA = this.$refs.comA;
      console.log(comA.title);  // Vue.js
      comA.sayHello();  // 彈窗
    }
  }
</script>

不過(guò),這兩種方法的弊端是:無(wú)法在跨級(jí)或者兄弟間通信

// parent.vue
<component-a></component-a>
<component-b></component-b>
<component-b></component-b>

總結(jié)

常用使用場(chǎng)景可以分為三類(lèi):

(1)父子通信:
父to子傳遞數(shù)據(jù)是通過(guò)props,子to父是通過(guò)emit; 通過(guò)父鏈/子鏈也可以通信(parent/children); ref也可以訪(fǎng)問(wèn)組件實(shí)例; provide/inject API;attrs/$listeners;

(2)兄弟通信:
EventBus;
Vuex;

(3跨級(jí)通信:
Event Bus;
Vuex;
provide/inject API;
attrs/listeners;

四、

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容