好久沒上傳了,閑來無事把囤積已久的筆記給上傳上傳 1. Vue3簡介 2020年9月18日,Vue.js發(fā)布版3.0版本,代號:One Piece(n 經(jīng)歷了:4800+次提交、40+個RFC、600+次PR、300+貢獻者 官方發(fā)版地址:Release v3.0.0 One Piece ·
好久沒上傳了,閑來無事把囤積已久的筆記給上傳上傳
2020年9月18日,
Vue.js
發(fā)布版
3.0
版本,代號:
One Piece
(n
經(jīng)歷了: 4800+次提交 、 40+個RFC 、 600+次PR 、 300+貢獻者
官方發(fā)版地址: Release v3.0.0 One Piece · vuejs/core
截止2023年10月,最新的公開版本為:
3.3.4
打包大小減少
41%
。
初次渲染快
55%
, 更新渲染快
133%
。
內存減少
54%
。
使用
Proxy
代替
defineProperty
實現(xiàn)響應式。
重寫虛擬
DOM
的實現(xiàn)和
Tree-Shaking
。
Vue3
可以更好的支持
TypeScript
。
Composition API
(組合
API
):
setup
ref
與
reactive
computed
與
watch
......
新的內置組件:
Fragment
Teleport
Suspense
......
其他改變:
新的生命周期鉤子
data
選項應始終被聲明為一個函數(shù)
移除
keyCode
支持作為
v-on
的修飾符
......
點擊查看 官方文檔
備注:目前
vue-cli
已處于維護模式,官方推薦基于Vite
創(chuàng)建項目。
## 查看@vue/cli版本,確保@vue/cli版本在4.5.0以上
vue --version
## 安裝或者升級你的@vue/cli
npm install -g @vue/cli
## 執(zhí)行創(chuàng)建命令
vue create vue_test
## 隨后選擇3.x
## Choose a version of Vue.js that you want to start the project with (Use arrow keys)
## > 3.x
## 2.x
## 啟動
cd vue_test
npm run serve
vite
是新一代前端構建工具,官網(wǎng)地址:
https://vitejs.cn
,
vite
的優(yōu)勢如下:
輕量快速的熱重載(
HMR
),能實現(xiàn)極速的服務啟動。
對
TypeScript
、
JSX
、
CSS
等支持開箱即用。
真正的按需編譯,不再等待整個應用編譯完成。
webpack
構建 與
vite
構建對比圖如下:
## 1.創(chuàng)建命令
npm create vue@latest
## 2.具體配置
## 配置項目名稱
√ Project name: vue3_test
## 是否添加TypeScript支持
√ Add TypeScript? Yes
## 是否添加JSX支持
√ Add JSX Support? No
## 是否添加路由環(huán)境
√ Add Vue Router for Single Page Application development? No
## 是否添加pinia環(huán)境
√ Add Pinia for state management? No
## 是否添加單元測試
√ Add Vitest for Unit Testing? No
## 是否添加端到端測試方案
√ Add an End-to-End Testing Solution? ? No
## 是否添加ESLint語法檢查
√ Add ESLint for code quality? Yes
## 是否添加Prettiert代碼格式化
√ Add Prettier for code formatting? No
自己動手編寫一個App組件
你好。
安裝官方推薦的
vscode
插件:
總結:
Vite
項目中,
index.html
是項目的入口文件,在項目最外層。
index.html
后,
Vite
解析
Vue2
的
API
設計是
Options
(配置)風格的。
Vue3
的
API
設計是
Composition
(組合)風格的。
Options
類型的
API
,數(shù)據(jù)、方法、計算屬性等,是分散在:
data
、
methods
、
computed
中的,若想新增或者修改一個需求,就需要分別修改:
data
、
methods
、
computed
,不便于維護和復用。
可以用函數(shù)的方式,更加優(yōu)雅的組織代碼,讓相關功能的代碼更加有序的組織在一起。
說明:以上四張動圖原創(chuàng)作者:大帥老猿
setup
是
Vue3
中一個新的配置項,值是一個函數(shù),它是
Composition API
“表演的舞臺
”
,組件中所用到的:數(shù)據(jù)、方法、計算屬性、監(jiān)視......等等,均配置在
setup
中。
特點如下:
setup
函數(shù)返回的對象中的內容,可直接在模板中使用。
setup
中訪問
this
是
undefined
。
setup
函數(shù)會在
beforeCreate
之前調用,它是“領先”所有鉤子執(zhí)行的。
姓名:{{name}}
年齡:{{age}}
setup(){
return ()=> '你好。'
}
Vue2
的配置(
data
、
methos
......)中
可以訪問到
setup
中的屬性、方法。
setup
中
不能訪問到
Vue2
的配置(
data
、
methos
......)。
Vue2
沖突,則
setup
優(yōu)先。
setup
函數(shù)有一個語法糖,這個語法糖,可以讓我們把
setup
獨立出去,代碼如下:
姓名:{{name}}
年齡:{{age}}
這樣寫了之后,之前的script一般就拿來寫個name就可以了,但是只是寫個name專門用個script標簽麻煩,所以可以借助下面的方法
直接在steup的script標簽寫上
擴展:上述代碼,還需要編寫一個不寫
setup
的
script
標簽,去指定組件名字,比較麻煩,我們可以借助
vite
中的插件簡化
npm i vite-plugin-vue-setup-extend -D
vite.config.ts
import { defineConfig } from 'vite'
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
export default defineConfig({
plugins: [ VueSetupExtend() ]
})
ref
,否則報錯)
let 響應式對象= reactive(源對象)
。
Proxy
的實例對象,簡稱:響應式對象。
這個是將一個對象編程一個proxy創(chuàng)建的對象接受響應式(數(shù)組同理也是這個方法)
reactive
定義的響應式數(shù)據(jù)是“深層次”的。
汽車信息:一臺{{ car.brand }}汽車,價值{{ car.price }}萬
游戲列表:
- {{ g.name }}
測試:{{obj.a.b.c.d}}
ref
接收的數(shù)據(jù)可以是:
基本類型
、
對象類型
。
ref可以對對象響應式數(shù)據(jù),但是注意改的時候要加value,特別是數(shù)組形式加在哪里
其原理還是把變量做了一個響應式變成了value形式,但是里面的對象還是做了一個proxy
ref
接收的是對象類型,內部其實也是調用了
reactive
函數(shù)。
汽車信息:一臺{{ car.brand }}汽車,價值{{ car.price }}萬
游戲列表:
- {{ g.name }}
測試:{{obj.a.b.c.d}}
宏觀角度看:
ref
用來定義: 基本類型數(shù)據(jù) 、 對象類型數(shù)據(jù) ;
reactive
用來定義: 對象類型數(shù)據(jù) 。
ref
創(chuàng)建的變量必須使用.value
(可以使用volar
插件自動添加.value
)。
![]()
![]()
reactive
重新分配一個新對象,會 失去 響應式(可以使用Object.assign
去整體替換)。
![]()
這樣寫也不行
![]()
可以這樣
![]()
如果是用的ref響應對象,就可以直接改
![]()
- 若需要一個基本類型的響應式數(shù)據(jù),必須使用
ref
。- 若需要一個響應式對象,層級不深,
ref
、reactive
都可以。- 若需要一個響應式對象,且層級較深,推薦使用
reactive
。
如果像這樣解構賦值,那么出來的name age都不是響應式數(shù)據(jù)
想讓響應式數(shù)據(jù)轉換出來也是響應式就押用到這個
ref
對象。
注意:torefs之后會創(chuàng)建一個新的reactive響應式對象,但是值都是指向同一個值你修改一個兩個都會變
toRefs
與
toRef
功能一致,但
toRefs
可以批量轉換。
姓名:{{person.name}}
年齡:{{person.age}}
性別:{{person.gender}}
作用:根據(jù)已有數(shù)據(jù)計算出新數(shù)據(jù)(和
Vue2
中的
computed
作用一致)。
計算屬性本身是監(jiān)視它里面的值的變化從而引起自身的變化,所以要改他的時候,也不是直接改他本身,而是改他里面的值
姓:
名:
全名:{{fullName}}
Vue2
中的
watch
作用一致)
Vue3
中的
watch
只能監(jiān)視以下
四種數(shù)據(jù)
:
ref
定義的數(shù)據(jù)。reactive
定義的數(shù)據(jù)。- 函數(shù)返回一個值(
getter
函數(shù))。- 一個包含上述內容的數(shù)組。
我們在
Vue3
中使用
watch
的時候,通常會遇到以下幾種情況:
監(jiān)視
ref
定義的【基本類型】數(shù)據(jù):直接寫數(shù)據(jù)名即可,監(jiān)視的是其
value
值的改變。
stopwatch監(jiān)視會返回一個值是一個箭頭函數(shù),調用這個函數(shù)可以取消掉監(jiān)視
情況一:監(jiān)視【ref】定義的【基本類型】數(shù)據(jù)
當前求和為:{{sum}}
監(jiān)視
ref
定義的【對象類型】數(shù)據(jù):直接寫數(shù)據(jù)名,監(jiān)視的是對象的【地址值】,若想監(jiān)視對象內部的數(shù)據(jù),要手動開啟深度監(jiān)視。
注意:
- 若修改的是
ref
定義的對象中的屬性,newValue
和oldValue
都是新值,因為它們是同一個對象。
![]()
- 若修改整個
ref
定義的對象,newValue
是新值,oldValue
是舊值,因為不是同一個對象了。不開啟深度,整個對象改變了,才會觸發(fā)監(jiān)視
![]()
情況二:監(jiān)視【ref】定義的【對象類型】數(shù)據(jù)
姓名:{{ person.name }}
年齡:{{ person.age }}
監(jiān)視
reactive
定義的【對象類型】數(shù)據(jù),且
默認開啟了深度監(jiān)視
。
并且修改屬性新老value還是一樣的
情況三:監(jiān)視【reactive】定義的【對象類型】數(shù)據(jù)
姓名:{{ person.name }}
年齡:{{ person.age }}
測試:{{obj.a.b.c}}
監(jiān)視
ref
或
reactive
定義的【對象類型】數(shù)據(jù)中的
某個屬性
,注意點如下:
若該屬性值 不是 【對象類型】,需要寫成函數(shù)形式。
若該屬性值是 依然 是【對象類型】,可直接寫,也可寫成函數(shù),建議寫成函數(shù)。
但是此時如果是這整個對象改變不會監(jiān)視到
這樣就是這個對象怎么變都監(jiān)視得到,寫成函數(shù)是為了整個對象變,deep是對象里面變化
結論:監(jiān)視的要是對象里的屬性,那么最好寫函數(shù)式,注意點:若是對象監(jiān)視的是地址值,需要關注對象內部,需要手動開啟深度監(jiān)視。
情況四:監(jiān)視【ref】或【reactive】定義的【對象類型】數(shù)據(jù)中的某個屬性
姓名:{{ person.name }}
年齡:{{ person.age }}
汽車:{{ person.car.c1 }}、{{ person.car.c2 }}
監(jiān)視上述的多個數(shù)據(jù)
寫成數(shù)組形式就可以,value是這個數(shù)組
情況五:監(jiān)視上述的多個數(shù)據(jù)
姓名:{{ person.name }}
年齡:{{ person.age }}
汽車:{{ person.car.c1 }}、{{ person.car.c2 }}
官網(wǎng):立即運行一個函數(shù),同時響應式地追蹤其依賴,并在依賴更改時重新執(zhí)行該函數(shù)。
watch
對比
watchEffect
都能監(jiān)聽響應式數(shù)據(jù)的變化,不同的是監(jiān)聽數(shù)據(jù)變化的方式不同
watch
:要明確指出監(jiān)視的數(shù)據(jù)
watchEffect
:不用明確指出監(jiān)視的數(shù)據(jù)(函數(shù)中用到哪些屬性,那就監(jiān)視哪些屬性)。
比如這個例子,都可以完成但是watcheffect上來就執(zhí)行一次,而且會根據(jù)里面所寫的自動監(jiān)測要監(jiān)視誰
示例代碼:
需求:水溫達到50℃,或水位達到20cm,則聯(lián)系服務器
水溫:{{temp}}
水位:{{height}}
作用:用于注冊模板引用。
用在普通
DOM
標簽上,獲取的是DOM
節(jié)點。用在組件標簽上,獲取的是組件實例對象。
用在普通
DOM
標簽上:
尚硅谷
前端
Vue
用在組件標簽上:
但是要獲取組件上的數(shù)據(jù)需要子組件做出defineexpose導出操作
注意vue3引用組件直接導入即可
如果在子組件定義了一組數(shù)據(jù),但是不小心名字打錯了
后期就容易調用不到
我們就可以去定義一個接口來約束類型,一般是在src下面創(chuàng)建一個types文件夾,然后導出
要用的話就在組件導入,并且前面要加type表明導入的是一個類型,然后在要用的變量后規(guī)則如下
如果是一個數(shù)組數(shù)據(jù)想這樣用就不行
除非是用泛型先規(guī)定它是一個數(shù)組,然后數(shù)組里面每一項需要符合這個規(guī)則
當然也可以去自定義類型,直接在ts里面聲明
// 定義一個接口,限制每個Person對象的格式 export interface PersonInter { id:string, name:string, age:number } // 定義一個自定義類型Persons export type Persons = Array
App.vue
中代碼:注意用reactive響應式數(shù)據(jù),此時接口應該用泛型來約束,而且數(shù)據(jù)是不能多不能少的
![]()
如果某個屬性想可有可無
![]()
Person.vue
中代碼:只接受會出問題,萬一傳過來是個數(shù)字 也會遍歷
![]()
注意接收+限制類型的寫法,prop括號里面不寫了,提出來規(guī)定一個泛型,里面為一個對象,誰要約束什么
![]()
![]()
限制必要性,一個?父組件就可傳可不傳了
![]()
如果不寫自己還有默認值,導入,再用
![]()
用的時候注意,把原來寫的全部包起來,第二個參數(shù)寫默認值,并且鍵值對,屬性值不能為一個數(shù)組形式,可以寫為一個函數(shù)返回
![]()
- {{item.name}}--{{item.age}}
規(guī)律:
生命周期整體分為四個階段,分別是: 創(chuàng)建、掛載、更新、銷毀 ,每個階段都有兩個鉤子,一前一后。
Vue2
的生命周期
創(chuàng)建階段:
beforeCreate
、created
掛載階段:
beforeMount
、mounted
更新階段:
beforeUpdate
、updated
銷毀階段:
beforeDestroy
、destroyed
v3的創(chuàng)建就是setup本身
Vue3
的生命周期
創(chuàng)建階段:
setup
掛載階段:
onBeforeMount
、onMounted
更新階段:
onBeforeUpdate
、onUpdated
卸載階段:
onBeforeUnmount
、onUnmounted
常用的鉤子:
onMounted
(掛載完畢)、
onUpdated
(更新完畢)、
onBeforeUnmount
(卸載之前)
示例代碼:
當前求和為:{{ sum }}
如果一個頁面中全是數(shù)據(jù)和方法,看起很臃腫復雜,跟vue2又很像
注意:如果要往空數(shù)組push等作出操作需要規(guī)定數(shù)組的泛型
而hooks就類似于mixin,把所有代碼模塊化,src創(chuàng)建文件夾hooks,創(chuàng)建模塊的ts文件,把該屬于這個部分的代碼都放進來
注意:這里面就跟剛才一樣什么都能寫包括生命鉤子,但是會對外暴露一個函數(shù),然后把所有數(shù)據(jù)方法返回
組件要用就直接導入,并且那邊是函數(shù),這邊直接調用,通過解構賦值,獲得return出來的數(shù)據(jù)和方法
示例代碼:
useSum.ts
中內容如下:
import {ref,onMounted} from 'vue'
export default function(){
let sum = ref(0)
const increment = ()=>{
sum.value += 1
}
const decrement = ()=>{
sum.value -= 1
}
onMounted(()=>{
increment()
})
//向外部暴露數(shù)據(jù)
return {sum,increment,decrement}
}
useDog.ts
中內容如下:
import {reactive,onMounted} from 'vue'
import axios,{AxiosError} from 'axios'
export default function(){
let dogList = reactive([])
// 方法
async function getDog(){
try {
// 發(fā)請求
let {data} = await axios.get('https://dog.ceo/api/breed/pembroke/images/random')
// 維護數(shù)據(jù)
dogList.push(data.message)
} catch (error) {
// 處理錯誤
const err = error
console.log(err.message)
}
}
// 掛載鉤子
onMounted(()=>{
getDog()
})
//向外部暴露數(shù)據(jù)
return {dogList,getDog}
}
組件中具體使用:
當前求和為:{{sum}}
加載中......
Vue3
中要使用
vue-router
的最新版本,目前是
4
版本。
基本規(guī)則
第一步,創(chuàng)建導航區(qū),展示區(qū)
入口文件導入注冊
路由配置文件代碼如下:
import {createRouter,createWebHistory} from 'vue-router'
import Home from '@/pages/Home.vue'
import News from '@/pages/News.vue'
import About from '@/pages/About.vue'
const router = createRouter({
history:createWebHistory(),
routes:[
{
path:'/home',
component:Home
},
{
path:'/about',
component:About
}
]
})
export default router
main.ts
代碼如下:
import router from './router/index'
app.use(router)
app.mount('#app')
App.vue
代碼如下
第三步
Vue路由測試
路由組件通常存放在
pages
或views
文件夾,一般組件通常存放在components
文件夾。通過點擊導航,視覺效果上“消失” 了的路由組件,默認是被 卸載 掉的,需要的時候再去 掛載 。
history
模式
優(yōu)點:
URL
更加美觀,不帶有#
,更接近傳統(tǒng)的網(wǎng)站URL
。缺點:后期項目上線,需要服務端配合處理路徑問題,否則刷新會有
404
錯誤。const router = createRouter({ history:createWebHistory(), //history模式 /******/ })
hash
模式
優(yōu)點:兼容性更好,因為不需要服務器端處理路徑。
缺點:
URL
帶有#
不太美觀,且在SEO
優(yōu)化方面相對較差。const router = createRouter({ history:createWebHashHistory(), //hash模式 /******/ })
主頁
Home
作用:可以簡化路由跳轉及傳參(后面就講)。
給路由規(guī)則命名:
routes:[
{
name:'zhuye',
path:'/home',
component:Home
},
{
name:'xinwen',
path:'/news',
component:News,
},
{
name:'guanyu',
path:'/about',
component:About
}
]
跳轉路由:
跳轉
跳轉
編寫
News
的子路由:
Detail.vue
配置路由規(guī)則,使用
children
配置項:
const router = createRouter({
history:createWebHistory(),
routes:[
{
name:'zhuye',
path:'/home',
component:Home
},
{
name:'xinwen',
path:'/news',
component:News,
children:[
{
name:'xiang',
path:'detail',
component:Detail
}
]
},
{
name:'guanyu',
path:'/about',
component:About
}
]
})
export default router
跳轉路由(記得要加完整路徑):
xxxx
xxxx
記得去
Home
組件中預留一個
傳遞參數(shù)
跳轉
{{news.title}}
接收參數(shù):
import {useRoute} from 'vue-router'
const route = useRoute()
// 打印query參數(shù)
console.log(route.query)
接收參數(shù)解構賦值版
但是這樣有個問題,之前說過在響應式對象上面直接解構,會讓他失去響應式,解決方法是一個torefs
要配置
傳遞參數(shù)
{{news.title}}
{{news.title}}
接收參數(shù):
import {useRoute} from 'vue-router'
const route = useRoute()
// 打印params參數(shù)
console.log(route.params)
備注1:傳遞
params
參數(shù)時,若使用to
的對象寫法,必須使用name
配置項,不能用path
。備注2:傳遞
params
參數(shù)時,需要提前在規(guī)則中占位。
就算剛才用這個方法,但是還是復雜在標簽上
作用:讓路由組件更方便的收到參數(shù)(可以將路由參數(shù)作為
props
傳給組件)
第一種,只需要在路由配置里面開啟
第二種
{
name:'xiang',
path:'detail/:id/:title/:content',
component:Detail,
// props的對象寫法,作用:把對象中的每一組key-value作為props傳給Detail組件
// props:{a:1,b:2,c:3},
// props的布爾值寫法,作用:把收到了每一組params參數(shù),作為props傳給Detail組件
// props:true
// props的函數(shù)寫法,作用:把返回的對象中每一組key-value作為props傳給Detail組件
props(route){
return route.query
}
}
作用:控制路由跳轉時操作瀏覽器歷史記錄的模式。
瀏覽器的歷史記錄有兩種寫入方式:分別為
push
和
replace
:
push
是追加歷史記錄(默認值)。
replace
是替換當前記錄。
開啟
replace
模式:
News
路由組件的兩個重要的屬性:
$route
和
$router
變成了兩個
hooks
import {useRoute,useRouter} from 'vue-router'
const route = useRoute()
const router = useRouter()
console.log(route.query)
console.log(route.parmas)
console.log(router.push)
console.log(router.replace)
作用:將特定的路徑,重新定向到已有路由。
具體編碼:
{
path:'/',
redirect:'/about'
}
點擊獲取,就往數(shù)組增加一條土味情話
注意先從promise返回解構data,如果還想繼續(xù)從data里面解構可以在后面來個:加對象,因為content是data對象里面的,甚至直接給content:給他來個別名
第一步:
npm install pinia
第二步:操作
src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
/* 引入createPinia,用于創(chuàng)建pinia */
import { createPinia } from 'pinia'
/* 創(chuàng)建pinia */
const pinia = createPinia()
const app = createApp(App)
/* 使用插件 */{}
app.use(pinia)
app.mount('#app')
此時開發(fā)者工具中已經(jīng)有了
pinia
選項
它有三個概念:
state
、
getter
、
action
,相當于組件中的:
data
、
computed
和
methods
。
具體編碼:
src/store/count.ts
取名最好要跟hooks一樣, 函數(shù)里面的名字最好跟文件名保持一致
// 引入defineStore用于創(chuàng)建store
import {defineStore} from 'pinia'
// 定義并暴露一個store
export const useCountStore = defineStore('count',{
// 動作
actions:{},
// 狀態(tài)
state(){
return {
sum:6
}
},
// 計算
getters:{}
})
具體編碼:
src/store/talk.ts
// 引入defineStore用于創(chuàng)建store
import {defineStore} from 'pinia'
// 定義并暴露一個store
export const useTalkStore = defineStore('talk',{
// 動作
actions:{},
// 狀態(tài)
state(){
return {
talkList:[
{id:'yuysada01',content:'你今天有點怪,哪里怪?怪好看的!'},
{id:'yuysada02',content:'草莓、藍莓、蔓越莓,你想我了沒?'},
{id:'yuysada03',content:'心里給你留了一塊地,我的死心塌地'}
]
}
},
// 計算
getters:{}
})
組件中使用
state
中的數(shù)據(jù)
注意:單獨用ref那么是.value,但是如果里面是ref定義,外面又用了一個reactive,那他就會給你拆包,也就是直接拿不用再value
得到對應store之后它是一個reactive對象,然后里面state定義的數(shù)據(jù)是放在ref里面的,那就拆包了
當前求和為:{{ sumStore.sum }}
-
{{ talk.content }}
第一種修改方式,直接修改
countStore.sum = 666
第二種修改方式:批量修改
要改的數(shù)據(jù)較多的時候
countStore.$patch({
sum:999,
school:'atguigu'
})
第三種修改方式:借助
action
修改(
action
中可以編寫一些業(yè)務邏輯)
import { defineStore } from 'pinia'
export const useCountStore = defineStore('count', {
/*************/
actions: {
//加
increment(value:number) {
if (this.sum < 10) {
//操作countStore中的sum
this.sum += value
}
},
//減
decrement(value:number){
if(this.sum > 1){
this.sum -= value
}
}
},
/*************/
})
組件中調用
action
即可
// 使用countStore
const countStore = useCountStore()
// 調用對應action
countStore.incrementOdd(n.value)
有個問題,現(xiàn)在模板上全部要加什么.sum都有個前綴,所以我想就是直接解構賦值拿到我要的數(shù)據(jù) ,但是從響應式解構賦值又是老問題了
這樣做可以得到響應式數(shù)據(jù),但有點浪費,因為他不僅是將數(shù)據(jù)變成ref,把整個store包括里面的方法也變了
所以就可以用pinia上有個專門的方法來轉 他就是只變數(shù)據(jù)
storeToRefs
將
store
中的數(shù)據(jù)轉為
ref
對象,方便在模板中使用。
pinia
提供的
storeToRefs
只會將數(shù)據(jù)做轉換,而
Vue
的
toRefs
會轉換
store
中數(shù)據(jù)。
當前求和為:{{sum}}
概念:當
state
中的數(shù)據(jù),需要經(jīng)過處理后再使用時,可以使用
getters
配置。
追加
getters
配置。
// 引入defineStore用于創(chuàng)建store
import {defineStore} from 'pinia'
// 定義并暴露一個store
export const useCountStore = defineStore('count',{
// 動作
actions:{
/************/
},
// 狀態(tài)
state(){
return {
sum:1,
school:'atguigu'
}
},
// 計算
getters:{
bigSum:(state):number => state.sum *10,
upperSchool():string{
return this. school.toUpperCase()
}
}
})
組件中讀取數(shù)據(jù):
const {increment,decrement} = countStore
let {sum,school,bigSum,upperSchool} = storeToRefs(countStore)
通過 store 的
$subscribe()
方法偵聽
state
及其變化
talkStore.$subscribe((mutate,state)=>{
console.log('LoveTalk',mutate,state)
localStorage.setItem('talk',JSON.stringify(talkList.value))
})
import {defineStore} from 'pinia'
import axios from 'axios'
import {nanoid} from 'nanoid'
import {reactive} from 'vue'
export const useTalkStore = defineStore('talk',()=>{
// talkList就是state
const talkList = reactive(
JSON.parse(localStorage.getItem('talkList') as string) || []
)
// getATalk函數(shù)相當于action
async function getATalk(){
// 發(fā)請求,下面這行的寫法是:連續(xù)解構賦值+重命名
let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
// 把請求回來的字符串,包裝成一個對象
let obj = {id:nanoid(),title}
// 放到數(shù)組中
talkList.unshift(obj)
}
return {talkList,getATalk}
})
Vue3
組件通信和
Vue2
的區(qū)別:
mitt
代替。
vuex
換成了
pinia
。
.sync
優(yōu)化到了
v-model
里面了。
$listeners
所有的東西,合并到
$attrs
中了。
$children
被砍掉了。
常見搭配形式:
概述:
props
是使用頻率最高的一種通信方式,常用與 :
父 ? 子
。
父組件:
父組件,
我的車:{{ car }}
兒子給的玩具:{{ toy }}
子組件
字傳父一種是可以直接寫在標簽里面類似下面
子組件
我的玩具:{{ toy }}
父給我的車:{{ car }}
還有一種就是一個變量接受
event類型可以為event
click
、
mosueenter
等等)
$event
: 是包含事件相關信息的對象(
pageX
、
pageY
、
target
、
keyCode
)
$event
: 是調用
emit
時所提供的數(shù)據(jù),可以是任意類型!!
示例:
//子組件中,觸發(fā)事件:
this.$emit('send-toy', 具體數(shù)據(jù))
也可以正常走邏輯,這樣來傳
概述:與消息訂閱與發(fā)布(
pubsub
)功能類似,可以實現(xiàn)任意組件間通信。
安裝
mitt
npm i mitt
新建文件:
src\utils\emitter.ts
都是寫在這個ts文件里面,一共四個api,on是綁定,off解綁,emit觸發(fā),all全部事件可以.clear全部清空
// 引入mitt
import mitt from "mitt";
// 創(chuàng)建emitter
const emitter = mitt()
/*
// 綁定事件
emitter.on('abc',(value)=>{
console.log('abc事件被觸發(fā)',value)
})
emitter.on('xyz',(value)=>{
console.log('xyz事件被觸發(fā)',value)
})
setInterval(() => {
// 觸發(fā)事件
emitter.emit('abc',666)
emitter.emit('xyz',777)
}, 1000);
setTimeout(() => {
// 清理事件
emitter.all.clear()
}, 3000);
*/
// 創(chuàng)建并暴露mitt
export default emitter
組件內使用
接收數(shù)據(jù)的組件中:綁定事件、 同時在銷毀前解綁事件 :
import emitter from "@/utils/emitter";
import { onUnmounted } from "vue";
// 綁定事件
emitter.on('send-toy',(value)=>{
console.log('send-toy事件被觸發(fā)',value)
})
onUnmounted(()=>{
// 解綁事件
emitter.off('send-toy')
})
【第三步】:提供數(shù)據(jù)的組件,在合適的時候觸發(fā)事件
import emitter from "@/utils/emitter";
function sendToy(){
// 觸發(fā)事件
emitter.emit('send-toy',toy.value)
}
注意這個重要的內置關系,總線依賴著這個內置關系
概述:實現(xiàn) 父?子 之間相互通信。
前序知識 ——
v-model
的本質
直接寫$event.target.value會報錯,e.target是一個對象,這個獨享就可能為空,所以這里用了個斷言告訴他是一個html輸入的元素
$event.target).value"
>
組件標簽上的
v-model
的本質:
:moldeValue
+
update:modelValue
事件。
下面是本質,上面是程序員簡寫方式,如果v-model不寫別名,默認名字傳過去就是modelValue
AtguiguInput
組件中:
也可以更換
value
,例如改成
abc
也可以改名字,注意:使用父組件這邊可以直接使用簡寫方式,但是接受子組件這邊,還是要跟著原理來,一個props綁定,一個觸發(fā)自定義事件
AtguiguInput
組件中:
如果
value
可以更換,那么就可以在組件標簽上多次使用
v-model
概述:( 祖→孫 )。
具體說明:
$attrs
是一個對象,包含所有父組件傳入的標簽屬性。
注意:
$attrs
會自動排除props
中聲明的屬性(可以認為聲明過的props
被子組件自己“消費”了)
![]()
父組件:
父組件
子組件:
子組件
孫組件:
v-bind后面寫對象,相當于一個一個綁定
孫組件
a:{{ a }}
b:{{ b }}
c:{{ c }}
d:{{ d }}
x:{{ x }}
y:{{ y }}
父傳一個自定義事件,同理也可以實現(xiàn)孫給祖?zhèn)?
$refs、
$parent】
概述:
$refs
用于 :
父→子。
父組件這邊先給子組件綁定ref,然后給這個ref的名字調用ref函數(shù),通過事件就可以拿到子組件傳過來的
子組件
一個宏函數(shù)把數(shù)據(jù)曝光出來
`$refs就是一個api獲取所有子組件
這里面放的就是一個個打了ref的子組件對象
$parent
用于:
子→父。
父組件
原理如下:
屬性 | 說明 |
---|---|
$refs
|
值為對象,包含所有被
ref
屬性標識的
DOM
元素或組件實例。
|
$parent
|
值為對象,當前組件的父組件實例對象。 |
概述:實現(xiàn) 祖孫組件 直接 通信(attrs需要中間人)
具體使用:
provide
配置向后代組件提供數(shù)據(jù)
inject
配置來聲明接收數(shù)據(jù)
具體編碼:
【第一步】父組件中,使用
provide
提供數(shù)據(jù)
父組件
資產(chǎn):{{ money }}
汽車:{{ car }}
注意:子組件中不用編寫任何東西,是不受到任何打擾的
提供數(shù)據(jù)時候,ref數(shù)據(jù)不用.value,前面名字,后面數(shù)據(jù)
【第二步】孫組件中使用
inject
配置項接受數(shù)據(jù)。
我是孫組件
資產(chǎn):{{ money }}
汽車:{{ car }}
注入數(shù)據(jù),就用一個變量來接受就可以了,第二個參數(shù)是不寫的話也可以由默認值
孫給爺傳
爺組件
孫組件
參考之前
pinia
部分的講解
父組件中:
- {{ g.name }}
子組件中:
{{ title }}
父組件中:
- {{ g.name }}
更多
子組件中:
{{ title }}
理解:
數(shù)據(jù)在組件的自身,但根據(jù)數(shù)據(jù)生成的結構需要組件的使用者來決定。
(新聞數(shù)據(jù)在
News
組件中,但使用數(shù)據(jù)所遍歷出來的結構由
App
組件決定)
具體編碼:
父組件中:
- {{ g.name }}
子組件中:
今日游戲榜單
作用于插槽也可以有名字
shallowRef
作用:創(chuàng)建一個響應式數(shù)據(jù),但只對頂層屬性進行響應式處理。
用法:
之前用ref綁定的數(shù)據(jù)隨便改
如果都改為shallowref
之前單數(shù)據(jù)和整個對象還可以改,但是單獨修改對象里面某個屬性就不行了
此淺層就是只把第一層編程響應式,.value之后就不管了
用處就是如果有個對象很大,我不關心它里面某個值變沒變化,就關心對象有沒有整體被改就可以用這個
特點:只跟蹤引用值的變化,不關心值內部的屬性變化。
shallowReactive
作用:創(chuàng)建一個淺層響應式對象,只會使對象的最頂層屬性變成響應式的,對象內部的嵌套屬性則不會變成響應式的
用法:
此時修改都可以生效
如果用淺層次,那就是第二層改不了了
作用同上
特點:對象的頂層屬性是響應式的,但嵌套對象的屬性不是。
通過使用
shallowRef()
和shallowReactive()
來繞開深度響應。淺層式API
創(chuàng)建的狀態(tài)只在其頂層是響應式的,對所有深層的對象不會做任何處理,避免了對每一個內部屬性做響應式所帶來的性能成本,這使得屬性的訪問變得更快,可提升性能。
readonly
用法:
只能讀不能改
也可以針對對象,里面的屬性都不能改
特點:
應用場景:
shallowReadonly
作用:與
readonly
類似,但只作用于對象的頂層屬性。
用法:
只對淺層次生效,也就是第二層可以改里面的屬性可以改
上面不可改,下面可改
特點:
只將對象的頂層屬性設置為只讀,對象內部的嵌套屬性仍然是可變的。
適用于只需保護對象頂層屬性的場景。
toRaw
作用:用于獲取一個響應式對象的原始對象,
toRaw
返回的對象不再是響應式的,不會觸發(fā)視圖更新。
官網(wǎng)描述:這是一個可以用于臨時讀取而不引起代理訪問/跟蹤開銷,或是寫入而不觸發(fā)更改的特殊方法。不建議保存對原始對象的持久引用,請謹慎使用。
何時使用? —— 在需要將響應式對象傳遞給非
Vue
的庫或外部系統(tǒng)時,使用toRaw
可以確保它們收到的是普通對象
使用場景,并不推薦,把一個響應對象變?yōu)椴豁憫蠼o到另一個對象,而是加入一個函數(shù)需要這個對象,要動到他,但是也不想引起改變
具體編碼:
將一個響應式數(shù)據(jù)編程不再響應
markRaw
作用:標記一個對象,使其 永遠不會 變成響應式的。
例如使用
mockjs
時,為了防止誤把mockjs
變?yōu)轫憫綄ο,可以使?markRaw
去標記mockjs
編碼:
/* markRaw */
let citys = markRaw([
{id:'asdda01',name:'北京'},
{id:'asdda02',name:'上海'},
{id:'asdda03',name:'天津'},
{id:'asdda04',name:'重慶'}
])
// 根據(jù)原始對象citys去創(chuàng)建響應式對象citys2 —— 創(chuàng)建失敗,因為citys被markRaw標記了
let citys2 = reactive(citys)
作用:創(chuàng)建一個自定義的
ref
,并對其依賴項跟蹤和更新觸發(fā)進行邏輯控制。
如果使用ref,那就是數(shù)據(jù)已更新,頁面馬上更新
如果我想實現(xiàn)數(shù)據(jù)更新,頁面等一下在更新這個做不到
customref基本使用
需要一個初試值,但是模板用的還是ref的
但如果是這樣寫的,頁面根本不會響應
真正寫法,需要在其回調兩個參數(shù),在讀取時調用前面那個函數(shù),修改后調用后面那個函數(shù)
這兩個函數(shù)很重要,面試容易問道
這樣就可以實現(xiàn)剛才的效果了
自定義ref感覺就類似于defineproperty那個函數(shù),就是對數(shù)據(jù)更新變化的時候可以去寫一些自己的邏輯在里面
但是此時快速輸入有bug,解決方式如下
實現(xiàn)防抖效果(
useSumRef.ts
):
import {customRef } from "vue";
export default function(initValue:string,delay:number){
let msg = customRef((track,trigger)=>{
let timer:number
return {
get(){
track() // 告訴Vue數(shù)據(jù)msg很重要,要對msg持續(xù)關注,一旦變化就更新
return initValue
},
set(value){
clearTimeout(timer)
timer = setTimeout(() => {
initValue = value
trigger() //通知Vue數(shù)據(jù)msg變化了
}, delay);
}
}
})
return {msg}
}
真實公司開發(fā)需求可能是要在這里來一個hooks
to里面可以為id class 標簽等選擇器
我是一個彈窗
我是彈窗中的一些內容
實驗性功能
比如現(xiàn)在子組件在發(fā)請求,如果半天出不來,那么先展示fallback插槽里面的內容,等異步回來之后在展示上面的內容
Suspense
包裹組件,并配置好
default
與
fallback
import { defineAsyncComponent,Suspense } from "vue";
const Child = defineAsyncComponent(()=>import('./Child.vue'))
我是App組件
加載中.......
app.component
注冊全局組件
app.config
全局配置對象
所有組件都訪問得到
但是會報錯 vue官網(wǎng)關于這個屬性global有解釋
app.directive
注冊全局指令
app.mount
app.unmount
app.use
過渡類名
v-enter
修改為
v-enter-from
、過渡類名
v-leave
修改為
v-leave-from
。
keyCode
作為
v-on
修飾符的支持。
v-model
指令在組件上的使用已經(jīng)被重新設計,替換掉了
v-bind.sync。
v-if
和
v-for
在同一個元素身上使用時的優(yōu)先級發(fā)生了變化。(可以用在同一組件上)
移除了
$on
、
$off
和
$once
實例方法。
移除了過濾器
filter
。
移除了
$children
實例
propert
。
......
vue官網(wǎng)
基于vue2 vue3的改變 面試暢聊
本站所有軟件,都由網(wǎng)友上傳,如有侵犯你的版權,請發(fā)郵件[email protected]
湘ICP備2022002427號-10 湘公網(wǎng)安備:43070202000427號© 2013~2025 haote.com 好特網(wǎng)