Vue.js 設計的初衷就包括能夠被漸進式地採用 ,因此Vue3支持2中的大部分特性 (遷移指南)。javascript
巧黑板,實際開發沒有多大用,面試極大機率問道。💪html
性能vue
虛擬DOM的重寫java
打包大小減小( tree shaking
)react
初次渲染快面試
內存使用減小(compositionAPI
)npm
新增語法(compositionAPI
)markdown
ref和reactiveapp
computed和watch框架
新的生命週期函數
自定義函數-Hooks函數(像React Hooks
)
其它新贈特性
任何的修改都已經移到全局的實例上去
全局配置
全局註冊類API
Vue.component 改成 app.component
Vue.directive 改成 app.directive
行爲擴展類API
Vue.mixin 改成 app.mixin
Vue.use 改成 app.use
更好的Typescript支持
Vue2的組件 API 設計所面對的核心問題之一就是如何組織邏輯,以及如何在多個組件之間抽取和複用邏輯。基於 Vue 2 目前的 API 咱們有一些常見的邏輯複用模式,但都或多或少存在一些問題。這些模式包括:
整體來講,以上這些模式存在如下問題:
模版中的數據來源不清晰
舉例來講,當一個組件中使用了多個 mixin 的時候,光看模版會很難分清一個屬性究竟是來自哪個 mixin。HOC 也有相似的問題。
命名空間衝突
由不一樣開發者開發的 mixin 沒法保證不會正好用到同樣的屬性或是方法名。HOC 在注入的 props 中也存在相似問題。
性能
HOC 和 Renderless Components 都須要額外的組件實例嵌套來封裝邏輯,致使無謂的性能開銷。
Function-based API 受 React Hooks 的啓發,提供了一個全新的邏輯複用方案,且不存在上述問題。使用基於函數的 API,咱們能夠將相關聯的代碼抽取到一個 "composition function"(組合函數)中 —— 該函數封裝了相關聯的邏輯,並將須要暴露給組件的狀態以響應式的數據源的方式返回出來。這裏是一個用組合函數來封裝鼠標位置偵聽邏輯的例子:
function useMouse() {
const x = ref(0)
const y = ref(0)
const update = e => {
x.value = e.pageX
y.value = e.pageY
}
onMounted(() => {
window.addEventListener('mousemove', update)
})
onUnmounted(() => {
window.removeEventListener('mousemove', update)
})
return { x, y }
}
// 在組件中使用該函數
const Component = {
setup() {
const { x, y } = useMouse()
// 與其它函數配合使用
const { z } = useOtherLogic()
return { x, y, z }
},
template: `<div>{{ x }} {{ y }} {{ z }}</div>`
}
複製代碼
從以上例子中能夠看到:
Vue3本來指望Class類解決類型推倒問題,最後依然存在類型問題.最後採用了自然對類型很友好的基於函數的API,由於基於函數API在使用TS或是原生JS時寫出來的代碼幾乎一摸同樣
基於函數的 API 每個函數均可以被 ES export 被單獨引入,主要這樣對tree-shaking很友好
沒有使用的API打包的時候會被移除,基於函數API有更好的壓縮效率,可是Class的屬性和方法名不行
<script src="https://unpkg.com/vue@next"></script>
複製代碼
// 最新穩定版
npm install vue@next
複製代碼
// 最新穩定版
yarn global add @vue/cli
// 而後在Vue項目中運行(更新相關插件)
vue upgrade --next
// 建立Vue3項目跟2同樣
vue create vue3_demo
複製代碼
// yarn
yarn create @vitejs/app <project-name>‘
cd <project-name>
yarn
yarn dev
複製代碼
hello,word!!!
<script src="https://unpkg.com/vue@next"></script>
<body>
<div id="app">
{{msg}} == {{count}}
</div>
</body>
<script> // 沒用compositionAPI跟傳統寫法同樣 const CountApp = { data() { return { msg: 'hello,word', count: 0 } }, mounted() { setInterval(() => { this.count++ }, 1000) } } // 聲明式渲染 Vue.createApp(CountApp).mount('#app') </script>
複製代碼
CompositionAPI:
const { ref, computed, watch, onMounted } = Vue
const App = {
template: ` <div> <span> 鐵蛋兒很帥!!!{{ count }}</span> <span> 你也是這麼認爲的!!!! {{ plusOne }}</span> <button @click="increment">點贊+++</button> </div> `,
setup() {
// 響應的數據
const count = ref(0)
// 計算屬性
const plusOne = computed(() => count.value + 1)
// method方法
const increment = () => { count.value++ }
// 觀察
watch(() => count.value * 2, val => {
console.log(`count * 2 is ${val}`)
})
// 生命週期
onMounted(() => {
console.log(`mounted`)
})
// 雙向綁定的數據必須返回
return {
count,
plusOne,
increment
}
}
}
// 聲明式渲染
Vue.createApp(App).mount('#app')
複製代碼
setup函數是組件邏輯的地方,它在組件實例被建立時,初始化 props 以後調用。
// setup() 會接收到初始的 props 做爲參數:
const MyComponent = {
props: {
name: String
},
setup(props) {
console.log(props.name)
}
}
複製代碼
注意:
傳進來的 props 對象雖然是響應式的能夠被看成數據源去觀測,可是後續props 發生變更時它也會被框架內部同步更新,因此不要直接修改。(會致使警告)
組件狀態 :
setup()
能夠返回一個對象相似data,這個對象上的屬性將會被暴露給模版的渲染上下文:
const MyComponent = {
props: {
name: String
},
setup(props) {
return {
msg: `hello ${props.name}!`
}
},
template: `<div>{{ msg }}</div>`
}
複製代碼
建立一個能夠在
setup()
內部被管理的值,可使用ref
函數。
ref
函數返回的是隻有一個value
屬性的包裝對象(主要是用來包裝原始類型的數據
)。
import { ref } from 'vue'
const MyComponent = {
setup(props) {
const msg = ref('hello')
const appendName = () => {
msg.value = `hello ${props.name}`
}
return {
msg,
appendName
}
},
// 當包裝對象被暴露給模版渲染上下文,或是被嵌套在另外一個響應式對象中的時候,它會被自動展開爲內部的值。
template: `<div @click="appendName">{{ msg }}</div>`
}
// 這樣包裝對象的值就能夠直接修改了
console.log(msg.value) // 'hello'
msg.value = 'tiedan' // 修改成tiedan
複製代碼
爲何須要包裝對象?
咱們知道在 JavaScript 中,原始值類型如 string 和 number 是隻有值,沒有引用的。若是在一個函數中返回一個字符串變量,接收到這個字符串的代碼只會得到一個值,是沒法追蹤原始變量後續的變化的。
所以,包裝對象的意義就在於提供一個讓咱們可以在函數之間以引用的方式傳遞任意類型值的容器。
返回一個沒有包裝的響應對象(
主要用於非原始類型的數據
)
import { reactive } from 'vue'
const object = reactive({
count: 0
})
object.count++
複製代碼
將響應式對象轉換爲普通對象,其中結果對象的每一個 property 都是指向原始對象相應 property 的ref
說人話就是: 響應式對象轉爲普通對象用它能夠再變成響應式的
const { reactive, toRefs } = Vue
const MyComponent = {
setup() {
const obj = reactive({
count: 0
})
const inc = () => {
obj.count++
}
return {
//
// 若是直接用ES6擴展運算符,會取消雙向數據綁定的特性,使用toRefs(),轉爲響應式數據
...toRefs(obj),
inc
}
},
template: `<h1 @click="inc">{{ count }}</h1>`
}
複製代碼
自動收集依賴源,依賴源更新時從新執行自身
當即執行,沒有惰性,頁面的首次加載就會執行。
自動檢測內部代碼,代碼中有依賴就會執行。
不須要傳遞要偵聽的內容, 會自動感知代碼依賴。
const { watchEffect, ref } = Vue
Vue.createApp({
setup() {
const userId = ref(0)
// 只要數據數據發生改變就觸發
watchEffect(() => console.log(userId.value))
setTimeout(() => {
userId.value = 1
}, 1000)
return {
userId
}
}
}).mount('#app')
複製代碼
顯式指定依賴源,依賴源更新時執行回調函數
// 偵聽一個 getter
const state = reactive({ count: 0 })
watch(
() => state.count,
(count, prevCount) => {
/* ... */
}
)
// 直接偵聽ref
const count = ref(0)
watch(count, (count, prevCount) => {
/* ... */
})
// 偵聽多個數據源
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
/* ... */
})
複製代碼
兩着的區別 :
能夠監聽多個
)相同點 :
watch 與 watchEffect共享中止偵聽(源碼都是調用doWatch方法只是傳的參數不一樣
)
相同的反作用刷新時機
都能經過返回的中止函數中止監聽
除去 beforeCreate和 created以外,在咱們的 setup 方法中,有9箇舊的生命週期鉤子,咱們能夠在setup方法中訪問
onBeforeMount——掛載開始前調用
onMount——掛載後調用
onBeforeUpdate——當響應數據改變,且從新渲染前調用
onUpdated——從新渲染後調用
onBeforeUnmount——Vue實例銷燬前調用
onUnmounted——實例銷燬後調用
onActivated——當keep-alive組件被激活時調用
onDeactivated——當keep-alive組件取消激活時調用
onErrorCaptured——從子組件中捕獲錯誤時調用
改變兩個銷燬生命週期更具備語義化
新增兩個方便調試生命週期
一個常見的場景是建立一個包含全屏模式的組件。在大多數狀況下,你但願模態框的邏輯存在於組件中,可是模態框的快速定位就很難經過 CSS 來解決,或者須要更改組件組合
當在初始的 HTML 結構中使用這個組件時,咱們能夠看到一個問題——模態框是在深度嵌套的 div
中渲染的,而模態框的 position:absolute
以父級相對定位的 div
做爲引用。
Teleport 提供了一種乾淨的方法,容許咱們控制在 DOM 中哪一個父節點下渲染了 HTML,而沒必要求助於全局狀態或將其拆分爲兩個組件。
Suspense組件用於在等待某個異步組件解析時顯示後備內容
應用實例:
定義異步:
app.component('component-a', {
setup() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
result: "異步組件"
});
}, 2000)
})
},
template: `<h1>{{result}}</h1>`,
})
複製代碼
Suspense組件應用:
<div id="app">
<Suspense>
<template #default>
<component-a></component-a>
</template>
<template #fallback>
<h1>Loadding...</h1>
</template>
</Suspense>
</div>
複製代碼
以上就是我對Vue3的簡單總結