初學vue3, 全是黑盒子,vue3知識點彙總

小程xy發表於2024-07-03

學習 Vue.js 應該像學習一門程式語言一樣,首先要熟練掌握常用的知識,而對於不常用的內容可以簡單瞭解一下。先對整個框架和語言有一個大致的輪廓,然後再逐步補充細節。千萬不要像學習演算法那樣,一開始就鑽牛角尖。

前序:

vueAPI的風格分為: 選項式和組合式,vue2中一般用選項式, 所以文章中講到的api一般都是組合式

vue3官方推薦用基於 Vite 的構建專案,首先安裝 node.js, 然後在終端輸入
npm create vue@latest,根據提示構建專案。npm installnpm run dev 分別是下載依賴和執行專案

當然你也可以透過vue ui來構建專案

1、初識vue:

1.1、專案結構:

當我們新建一個vue專案時, 整個專案剛開始會有很多.vue, .js, .html .css檔案,但真正有用的只有index.html, App.vue和一些配置檔案

vue元件包含三個區域, 分別用來寫html,js(ts),css 其中stylescoped可以讓css樣式的作用域限定到當前元件

<template>
</template>


<script>
</script>


<style scoped>
</style>

1.2、vue常用函式

createApp():是Vue 3中用來建立一個新的Vue應用例項的方法。它接受一個根元件作為引數,並返回一個應用例項。根元件就是export出去的name字串

mount(): 是把createApp建立的應用例項掛載到一個容器上。 接受的引數容器可以是一個css的選擇器

createApp(App).mount('#app') // App.vue檔案中
<div id="app"></div>	//index.html中
我們剛建立的專案中會有上面的兩個檔案, 實際上就是建立了一個App實列, 透過id選擇器把它掛載到一個div上

setup(): 是一個函式, 函式里面可以定義響應式變數、計算屬性、方法, 然後把變數, 方法 return 出去, 就可以在該元件的template中使用。 (我們一個元件所用到的函式、變數都寫在這裡面)

我們每個元件的name、setup、components...都需要export出去。 其中name是這個元件的名字, 當其他元件要用到當前元件的時候就是要<name/>,

如下: 在setup函式中定義了nameclick方法給當前元件的html使用。 程式碼中的(@用來繫結事件, 包括html所有事件和自定義事件)

<template>
  <h1>{{ age }}</h1> 		<!-- {{ }} 雙大括號將 Vue 例項中的資料繫結到 HTML 元素中 -->
  <button @click="handleClick">click</button>
</template>

<script lang="ts">
export default {
  name : "App",
  setup : () => {
	    let age= 10;
	    const handleClick = () => {
	    age = 18;
    }
    return {name, handleClick}
  }
}
</script>

前面講到setup裡面定義的方法,變數等都需要return出去之後 html 中才能使用,下面這個語法糖可以讓我們不用一個個寫return

setup語法糖: 在<script setup> </script>中寫方法、狀態的話,不需要把方法或狀態return回去, vue會自動幫我們return, 而且也不需要用components註冊用到的元件。

ref(): 是一個函式,使用 ref 函式可以建立一個包裝過的響應式物件,使其能夠在 Vue 元件中進行響應式資料繫結。ref接受的引數是基本型別、陣列、物件型別的。(當引數是物件時, ref內部其實是呼叫的reactive)

reactive(): 使用 reactive函式可以建立一個包裝過的響應式物件,reactive接受的引數是物件型別

響應式資料指的是當我們的變數發生變化的時候, 跟這個變數相關的元素就會自動重新整理
上面的程式碼中我們點選button的時候會把age = 18, 但實際上我們頁面顯示的時候age還是等於10, 因為age不是響應式的變數
所以我們需要用ref建立age變數, let age = ref(10), 這樣age發生變化時, 跟age相關的元素也會自動重新整理。(需要注意: 在js中使用由ref建立的響應式物件是要加上".value", 比如 age.value;在html中則不需要, 可直接寫成age)

toRefstoRef:當我們把一個物件或者變數解構的時候。假設person物件中有name, age const {name, age} = person,那麼此時解構出來的的nameage不再具有響應式。為了使解構出來的仍然具有響應式,那麼應該用toRefstoRef包括起來。 const {name, age} = toRefs(person).

computed():是基於其他資料的值計算出來屬性,並且當這些資料變化時,計算屬性的值會自動更新

計算屬性是基於它們的依賴進行快取的,只在其依賴發生變化時才會重新計算,而方法每次呼叫時都會重新執行。

如下, 如果是基於函式getFullName()計算全名的話當name發生變化是控制檯會輸出3次1, 如果是用FullNameByComputed的話, 控制檯只會輸出一個1

<span>{{ getFullName() }}</span><br>
<span>{{ getFullName() }}</span><br>
<span>{{ getFullName() }}</span><br>

setup : () => {
    let firstname = ref('');
    let lastname = ref('');

    const getFullName = () => {
      return firstname.value + lastname.value;
    }
	const FullNameByComputed= computed(() => {
      console.log(1); 
      return firstname.value + lastname.value;
    });
    
	return {firstname, lastname, getFullName, FullNameByComputed}
}

透過上面的computed建立的FullNameByComputed是一個類似ref建立的物件, 在js(script標籤)中要訪問FullNameByComputed的話需要FullNameByComputed.value
如果需要直接修改value的話需要在建立物件的時候傳遞get和set方法,如下

const getFullNameComputed = computed({
      get: ()=> {
        return firstname.value + "-" + lastname.value;
      },
      set: (newName)=> {
        let name = newName.split('-');
        firstname.value = name[0];
        lastname.value = name[1];
      }
    });

上面程式碼中,當我們FullNameByComputed.value的話, vue會自動呼叫get方法, 當我們FullNameByComputed.value=newName的時候,會自動呼叫set方法, 並把等號右邊的值傳遞到set引數中

1.3、常用語法糖

v-model: 是 Vue.js 中用於在表單控制元件(如輸入框、核取方塊、單選按鈕等)和元件之間建立雙向資料繫結
如下: 我們讓username和輸入框雙向繫結, 當使用者在輸入框中輸入字元時, username 就會變成使用者輸入的值。其中username需要是響應式的變數

<input v-model="username" type="text">
<p>{{ username }}</p>
let username = ref('');

v-for: 透過 ... in ... 用來遍歷陣列
如下: 假設我們有一個persons陣列, 陣列長度是3,那麼我們可以透過v-for來生成3個li標籤。其中對於遍歷生成的標籤我們需要透過:key="“給他繫結一個唯一的key, 一般用id。(注意key前面的:, 不寫:的話,vue會把person.id當成字串解析)

<li v-for="person in persons" :key="person.id">{{ person.name }}</li>

v-ifv-else就如他們的字面意思, 判斷用的

注意: 上面文章和下面文章中用到的函式需要自己引入,如computedref等等,文章中就不再過多贅述了。

2、路由器router:

2.1、路由的定義與使用:

透過 Vue Router,我們可以輕鬆地定義路由規則,將不同的 URL 對映到不同的元件,並在這些元件之間切換,通俗的講就是我們可以讓頁面在不同的url上顯示我們想要顯示的元件。

router是路由器的意思, route是路由的意思,我們需要給vue的例項建立一個router, 透過createRouter()函式.

const router = createRouter({
    history: createWebHistory(),    // 路由器的工作模式, 下面會講到
    routes : [
        {
            path : "/home",	// url匹配path的時候路由器會渲染這個路由的元件
            name : "HomeView",	// 當前的路由route的名字
            component : Person,	// 路由的元件
        },	// 後面可以繼續寫多個route, router匹配時會從前往後匹配
        {},...
    ]
})

使用router:

引入:
我們透過createApp()可以建立一個vue例項, const app = createApp(App), 透過app.use(router)使例項使用router。use(router)的引數是我們上面程式碼用createRouter()建立的

上面我們建立並在例項中使用了router, 但是我們並沒有給router說,我們要在頁面的哪個部分渲染元件, 所以我們要在我們想要渲染的位置寫上<RouterView/>(一般是在App.vue的template的某個地方)。

最後實現的效果就是, 當url跟我們的某個route匹配時,vue的router就會自動的在我們指定的渲染位置上渲染url對應的元件

渲染模式

渲染模式分為前端渲染和後端渲染, html中可以透過a href標籤實現點選跳轉url,這是後端渲染。(點選a標籤的時候頁面會重新載入一下, 感覺不出來的話可以點選F12 選中network, 每次點選時network中會重新載入檔案,)

vue中我們可以透過<router-link to="">點選跳轉</router-link>,來代替a標籤,to相當於href,這就是實現了前端渲染。(當我們點選的時候會發現瀏覽器並沒有重新載入。)(這裡不詳細講前後端渲染了)

to的三種寫法
<router-link to="">點選跳轉</router-link>中的to有三種寫法.(to前面加上:, 才能讓vue不把後面的內容解析成字串)

  1. to="/home"
  2. :to="{path : "/home"}"
  3. :to="{name: "HomeView"}"

路由模式

  1. Hash 模式路由使用 URL 的 hash(#)部分來模擬一個完整的 URL,實際 URL 的路徑部分始終是 # 符號之前的內容
  2. History 模式利用 HTML5 History API 來管理路由,這使得 URL 看起來像普通的 URL,沒有 # 符號。

2.2、路由的傳參:

一般用第二種。(useRoute()建立的物件中包含queryparams)

  1. 透過props.query + path
    傳遞:
    上面我們透過to的第一種寫法後面加上?加上我們要傳的引數即可(透過&分隔要傳不同的引數)。<router-link :to="`/home?name=${name}`">router link</router-link><router-link :to="{ path : '/home', query: { name, } }" > router link </router-link>, 透過這種方式即可把當前元件中的name傳遞到要路由到的元件中
    接收:
    路由元件透過useRoute()建立物件, 物件的query物件中就存著我們傳遞的資料const route = useRoute(); console.log(route.query.name)

  2. 透過props.params + name
    前提我們需要修改router的routes的path路徑, 透過用:佔位。path : "/home/:name"。
    然後可以透過<router-link :to="{ name: 'HomeView', params: { name : '1', } }" > router link </router-link>,或 <router-link :to="`/home/${name}`">router link</router-link> 進行傳遞
    需要渲染的路由元件中透過route.params.name訪問

2.3、路由的props配置:

一般傳參用上面2.2的第二種寫法就行, 下面這個也是一種傳參的寫法, 只不過不需要用useRoute

{
            path : "/home/:name",
            name : "HomeView",
            component : Person,
            props(route) {	// props接收的是當前的路由資訊, 裡面有path,params,query等等, 你可以輸出檢視下
                return route.params;
            },
          //  props:true,	// 等價於 return route.params
          // props(route) {return route.query}
        },

如上在路由中新增程式碼中的三種props程式碼的話,就相當於是在<Person :name="name"/>, 那麼我們就可以透過defineProps來解析出路由中要傳的name了。(defineProps3.1中會講, 注意:每種props寫法要對應上面路由的傳參對應的傳遞寫法)

2.4、程式設計式路由:

上面我們都是透過router-link標籤的to屬性實現跳轉, router-link標籤相當於html的a標籤,那麼我們只能實現透過點選連結的形式實現跳轉, 如果要實現點選button實現跳轉,那就可以透過管理路由器實現。(透過路由器就可以實現多種形式的跳轉)

透過useRouter()建立一個router路由器,router.push()就可以實現跳轉,push中可傳遞的引數和2.1中 "to的三種寫法" 的引數一樣

2.4、路由重定向:

透過在route配置中新增下面程式碼("/"重定向到"/home")

{
      path : "/",
      redirect : "/home",
},
{
      path: '/:pathMatch(.*)',	// 匹配所有路徑
      redirect: '/404',	//重定向到/404
    },

3、元件傳遞資料

我們在專案中分別建立Person.vueApp.vue, 其中App.vue中使用<Person/>建立Person元件例項, 即AppPerson的父元件

在一般情況下我們父傳子的時候用的是下面講的第一種1. 透過defineProps子傳父的時候用的第二種2. 透過自定義事件

3.1、父元件向子元件傳遞資料

  1. 透過defineProps

父元件中<Person :person="person" :str="str" />, 前面我們向子元件中傳遞了personstr變數, 其中=右邊的"person""str" 是我們在父元件<script>的setup中定義的兩個變數, =左邊的是 子元件接受時用到的name, 一般命名和父元件中定義時的變數名相同。(個人習慣)

子元件中const AppProps = defineProps(["person", "str"]), 其中[]中的兩個字串對應父元件中的name, AppProps 是包含personstr的物件, 這樣子元件中就可以透過AppProps.person訪問

3.2、子元件向父元件傳遞資料

  1. 透過defineProps
    在上面父向子傳的時候,可以讓父元件給子元件傳遞一個get方法, 然後讓子元件中用defineProps接收一下這個方法,在合適的時候呼叫, 把要傳的資料放到get方法的引數列表

  2. 透過自定義事件defineEmits(不只侷限於子元件向父元件, 任意兩個元件之間都可以)
    父元件中定義了get-child-name自定義事件, 給事件繫結了一個函式, 子元件在合適的時機觸發get-child-name事件即可

//父元件中
<Person @get-child-name="getChildName" />
const getChildName = (ChildName) => {
  console.log(ChildName.value)
}

//子元件中
const name = ref('child')
const emit = defineEmits()
const sendName = () => {
  emit('get-child-name', name)
}
  1. 透過defineExpose(可跳過,不常用3!)
    父元件中<Person ref="child_Person" />在子元件的例項上新增上ref屬性, 然後在setup中定義const child_Person = ref();,child_Person就是包含子元件暴漏出來的資料的物件(記得ref響應式物件要.value取值)。
    (注意: 用child_Person訪問子元件的資料時要寫在onMounted函式中, onMounted函式可以確保子元件已經被掛載, 不然可能訪問不到)
    子元件中透過let name = 'hh'; let age = 18; defineExpose({name, age});, 將自己要暴漏出去的寫在defineExpose

3.3、子元件修改父元件的資料

<script setup></script>如果是在這個中透過defineProps中過去的父元件資料的話, 可以直接修改父元件的資料

3.4 mitt自定義事件管理庫

上面講到的3.2的第二種自定義事件一般用於子元件和父元件, 但對於層級較深的就需要一層一層傳遞,很麻煩, mitt就可以很好幫我們解決問題

mitt一個js的事件管理庫, 一般用於管理自定義事件。(安裝指令npm install mitt)

一般在一個js檔案中建立一個mitt例項, 在其他要用到自定義事件管理的檔案中引入mitter

建立一個新的事件匯流排例項,返回一個包含 on, off, emit, all 等方法的物件,用於管理事件的訂閱和釋出。
const mitter = mitt();

訂閱指定型別的事件,當該型別的事件被觸發時,執行指定的處理函式。
mitter.on('eventName', (data) => {
  console.log('Event received with data:', data);
});

取消訂閱指定型別的事件,停止執行特定的處理函式。
mitter.off('eventName', handlerFunction);

觸發指定型別的事件,並傳遞可選的事件資料給訂閱該事件的處理函式。
mitter.emit('eventName', { message: 'Hello, mitt!' });

訂閱所有型別的事件,當任何型別的事件被觸發時執行指定的處理函式。
mitter.all((type, data) => {
  console.log(`Event ${type} received with data:`, data);
});

// 清除所有事件監聽器
mitter.all.clear();

4、元件之間共享資料Pinia

對於子元件和父元件之間,我們可以透過上面講到的方法實現資料共享,但是對於有更深層級的元件,我們就需要集中管理狀態的方法。Pinia就可以解決這個問題, 安裝命令npm install pinia

4.1、資料的定義

在例項中引入pinia, 跟引入router一樣

import { createPinia } from 'pinia'
const app = createApp(App)
const pinia = createPinia();
app.use(pinia)

一般來說我們會把要共享的元件的資料的pinia放到一個src資料夾下的store中的js檔案中。然後在需要使用的檔案中引入js, 生成store

import { defineStore } from 'pinia'
// defineStore接收的第一個引數是這個store的id(自己定義)
export const usePersonStore = defineStore('Person', {
  state() {
    return {
      name: 'hello',	//return 的就是要共享的資料
    }
  }
})

如果要使用Person的資料的話就:

import { usePersonStore } from '@/store/Person'
const PersonStore = usePersonStore()	//PersonStore 是一個物件
//PersonStore 中包含的就有name, PersonStore.name或PersonStore.$state.name

4.2、資料的修改

  1. 直接修改
PersonStore.name = 'ld'
  1. 透過$patch修改
PersonStore.$patch({
    name: 'ld'
  })
  1. 透過action修改
export const usePersonStore = defineStore('Person', {
// 在action中可以寫方法, vue會自動維護一個this, 用來訪問當前store中的資料
  actions: {
    changeName() {
      this.name = 'ld'
    }
  },
  state() {
    return {
      name: 'yxc'
    }
  }
})

PersonStore.changeName()	// 

對於PersonStore解構出來的資料跟1.2中講的一樣也是不再具有響應式,我們可以用storeToRefs來解構。(雖然也可以用toRefs,但非常不建議)

4.3、getters

如果我們需要計算和返回基於已有狀態(state)的新資料,那麼可以在getters中寫方法

state() {
    return {
      name: 'yxc',
      age: 18
    }
  },
  getters: {	// vue也會自動維護一個this
    doubleName() {
      return this.name + this.name
    }
  }

4.4、subscribe

subscribe 是用於監聽 store 的狀態變化的方法。Pinia 提供了一種簡單而直觀的方式來訂閱狀態變化

訂閱方法:透過 store.$subscribe(callback) 方法來訂閱狀態變化,callback 接受一個引數 mutation,可以是狀態的變化資訊或其他相關內容。

取消訂閱:訂閱方法返回一個取消訂閱的函式 unsubscribe(),你可以在不需要繼續監聽狀態變化時呼叫它來取消訂閱。

響應式更新:Pinia 使用 Vue.js 的響應式系統來管理狀態,因此任何影響狀態的變化都會觸發訂閱者的回撥函式。

PersonStore.$subscribe((mutation, state) => {})

mutation: 一個物件,描述了引起狀態變化的 mutation。它包含以下屬性:

  • storeId: 發生變化的 store 的 ID。
  • type: 變化的型別,通常是 direct(直接變化)或 patch(透過 patch 變化)。
  • events: 包含變化詳情的陣列。

state: 變化後的 store 狀態。


學到這裡, 你就可以用vue寫專案了。
猶豫篇幅太長,其他vue中不常用的內容放到下篇文章了

下篇文章地址,待更

相關文章