學習 Vue.js 應該像學習一門程式語言一樣,首先要熟練掌握常用的知識,而對於不常用的內容可以簡單瞭解一下。先對整個框架和語言有一個大致的輪廓,然後再逐步補充細節。千萬不要像學習演算法那樣,一開始就鑽牛角尖。
前序:
vueAPI的風格分為: 選項式和組合式,vue2中一般用選項式, 所以文章中講到的api一般都是組合式
vue3官方推薦用基於 Vite 的構建專案,首先安裝 node.js, 然後在終端輸入
npm create vue@latest
,根據提示構建專案。npm install
和 npm run dev
分別是下載依賴和執行專案
當然你也可以透過vue ui
來構建專案
1、初識vue:
1.1、專案結構:
當我們新建一個vue專案時, 整個專案剛開始會有很多.vue, .js, .html .css
檔案,但真正有用的只有index.html, App.vue和一些配置檔案
vue元件包含三個區域, 分別用來寫html,js(ts),css
其中style
的scoped
可以讓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
函式中定義了name和click方法給當前元件的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)
toRefs
和toRef
:當我們把一個物件或者變數解構的時候。假設person
物件中有name, age
const {name, age} = person
,那麼此時解構出來的的name
和age
不再具有響應式。為了使解構出來的仍然具有響應式,那麼應該用toRefs
或toRef
包括起來。 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-if
和v-else
就如他們的字面意思, 判斷用的
注意: 上面文章和下面文章中用到的函式需要自己引入,如computed
、ref
等等,文章中就不再過多贅述了。
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不把後面的內容解析成字串)
to="/home"
:to="{path : "/home"}"
:to="{name: "HomeView"}"
路由模式
- Hash 模式路由使用 URL 的 hash(#)部分來模擬一個完整的 URL,實際 URL 的路徑部分始終是 # 符號之前的內容
- History 模式利用 HTML5 History API 來管理路由,這使得 URL 看起來像普通的 URL,沒有 # 符號。
2.2、路由的傳參:
一般用第二種。(useRoute()
建立的物件中包含query
和params
)
-
透過
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)
-
透過
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
了。(defineProps
3.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.vue
和 App.vue
, 其中App.vue中使用<Person/>
建立Person元件例項, 即App
是Person
的父元件
在一般情況下我們父傳子
的時候用的是下面講的第一種1. 透過defineProps
、子傳父
的時候用的第二種2. 透過自定義事件
3.1、父元件向子元件傳遞資料
- 透過
defineProps
父元件中<Person :person="person" :str="str" />
, 前面我們向子元件中傳遞了person
和str
變數, 其中=
右邊的"person"
和"str"
是我們在父元件<script>的setup
中定義的兩個變數, =
左邊的是 子元件接受時用到的name
, 一般命名和父元件中定義時的變數名相同。(個人習慣)
子元件中const AppProps = defineProps(["person", "str"])
, 其中[]
中的兩個字串對應父元件中的name
, AppProps 是包含person
和str
的物件, 這樣子元件中就可以透過AppProps.person
訪問
3.2、子元件向父元件傳遞資料
-
透過
defineProps
在上面父向子
傳的時候,可以讓父元件給子元件傳遞一個get方法, 然後讓子元件中用defineProps
接收一下這個方法,在合適的時候呼叫, 把要傳的資料放到get方法的引數列表 -
透過自定義事件
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)
}
- 透過
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、資料的修改
- 直接修改
PersonStore.name = 'ld'
- 透過
$patch
修改
PersonStore.$patch({
name: 'ld'
})
- 透過
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中不常用的內容放到下篇文章了
下篇文章地址,待更