一、Vue3新特性:setup、ref、reactive、computed、watch、watchEffect函數、生命周期鉤子、自定義hooks函數、toRef和toRefs、shallowReactive 與 shallowRef、readonly 與 shallowReadonly、toRaw 與 markRaw、customRef、provide 與 inject、Fragment、Teleport、Suspense、data選項應始終被聲明為一個函數
2、setup是所有composition? API(組合式api)展示的舞臺, 返回一個對象,則對象中的屬性、方法, 在模板中均可以直接使用,setUp(props, contex)接受兩個參數,props:值為對象,包含:組件外部傳遞過來,且組件內部聲明接收了的屬性(其實就是vue2.0的props功能),context:上下文對象(其中可以獲取到1、attrs組件外部傳遞過來,但沒有在props配置中聲明的屬性。2、slots:插槽內容3、emit:分發自定義事件的函數,并且以后在setup中不能寫this.$emit,要寫context.emit)
3、ref一般用來定義一個基本類型的響應式數據, reactive定義一個響應式源對象,接收一個普通對象然后返回該普通對象的響應式代理器對象,響應式轉換是“深層的”:會影響對象內部所有嵌套的屬性,內部基于 ES6 的 Proxy 實現,通過代理對象操作源對象內部數據都是響應式的;watch默認是惰性的,watchEffect默認立即執行;watch可以獲取到當前值和之前值,watchEffect只能獲取當前值;watch可以傳遞多個函數作為參數,watchEffect只需要傳遞一個回調函數
4、
(1)、vue2響應式原理:
核心:
對象: 通過defineProperty對對象的已有屬性值的讀取和修改進行劫持(監視/攔截)
數組: 通過重寫數組更新數組一系列更新元素的方法來實現元素修改的劫持
(2)、Vue3的響應式原理:
核心:
通過Proxy(代理):攔截對data任意屬性的任意(13種)操作, 包括屬性值的讀寫, 屬性的添加, 屬性的刪除等…
通過 Reflect(反射): 動態對被代理對象的相應屬性進行特定的操作
5、生命周期:
為什么要在生命周期前加"on"?
因為setup是圍繞beforeCreate和created生命周期來運行的,所以不需要顯式地定義它們,這些生命周期函數接受一個回調函數,當鉤子被組件調用時將會被執行。
beforeCreate -> 使用 setup()
created -> 使用 setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
errorCaptured -> onErrorCaptured
1.vue3中已經沒有destroyed 和beforeDestroy 了
2.vue3也可以用vue2的生命周期,vue3生命周期比vue2快
6、自定義hook函數
什么是hook? ----本質是一個函數,把setup函數中使用的composition API進行了封裝
類似于vue2中的mixin
自定義hook的優勢:復用代碼,讓setup中的邏輯更清晰易懂
(1)、基礎封裝hooks
//定義的hooks
import {reactive,onMounted,onBeforeUnmount} from 'vue'
export default function(){
? ? let point=reactive(
? ? ? ? {
? ? ? ? ? ? x:0,
? ? ? ? ? ? y:0
? ? ? ? }
? ? )
? ? function sponk(event){
? ? ? ? console.log(point);
? ? ? ? point.x=event.pageX;
? ? ? ? point.y=event.pageY;
? ? ? ? console.log(event);
? ? }
? ? onMounted(()=>{
? ? ? ? console.log('onMounted');
? ? ? ? document.getElementById('HelloWorld').addEventListener('click',sponk)
? ? })
? ? onBeforeUnmount(()=>{
? ? ? ? console.log('onBeforeUnmount');
? ? ? ? document.getElementById('HelloWorld').removeEventListener('click',sponk)
? ? })
? ? return point;
}
//使用hooks的組件
<template>
? <div class="hello" id="HelloWorld">
? ? x軸位置:{{valz.x}}<br/>
? ? y軸位置:{{valz.y}}<br/>
? </div>
</template>
<script>
? //引入定義的hooks
? import point from './../hook/commonFirst'
? export default {
? ? name: 'HelloWorld',
? ? props: {
? ? ? msg: String
? ? },
? ? setup(){
? ? ? console.log('---setup---');
? ? ? //觸發執行此方法即可
? ? ? let valz=point()
? ? ? return{
? ? ? ? valz
? ? ? }
? ? },
}
</script>
(2)、 封裝發 axios 請求的 hook 函數
hooks/useRequest.ts
import { ref } from 'vue'
import axios from 'axios'
/*
使用axios發送異步ajax請求
*/
export default function useUrlLoader<T>(url: string) {
? const result = ref<T | null>(null)
? const loading = ref(true)
? const errorMsg = ref(null)
? axios
? ? .get(url)
? ? .then(response => {
? ? ? loading.value = false
? ? ? result.value = response.data
? ? })
? ? .catch(e => {
? ? ? loading.value = false
? ? ? errorMsg.value = e.message || '未知錯誤'
? ? })
? return {
? ? loading,
? ? result,
? ? errorMsg
? }
}
<template>
? <div class="about">
? ? <h2 v-if="loading">LOADING...</h2>
? ? <h2 v-else-if="errorMsg">{{ errorMsg }}</h2>
? ? <!-- <ul v-else>
? ? <li>id: {{result.id}}</li>
? ? <li>name: {{result.name}}</li>
? ? <li>distance: {{result.distance}}</li>
? </ul> -->
? ? <ul v-for="p in result" :key="p.id">
? ? ? <li>id: {{ p.id }}</li>
? ? ? <li>title: {{ p.title }}</li>
? ? ? <li>price: {{ p.price }}</li>
? ? </ul>
? ? <!-- <img v-if="result" :src="result[0].url" alt=""> -->
? </div>
</template>
<script lang="ts">
import { watch } from 'vue'
import useRequest from './hooks/useRequest'
// 地址數據接口
interface AddressResult {
? id: number
? name: string
? distance: string
}
// 產品數據接口
interface ProductResult {
? id: string
? title: string
? price: number
}
export default {
? setup() {
? ? // const {loading, result, errorMsg} = useRequest<AddressResult>('/data/address.json')
? ? const { loading, result, errorMsg } = useRequest<ProductResult[]>('/data/products.json')
? ? watch(result, () => {
? ? ? if (result.value) {
? ? ? ? console.log(result.value.length) // 有提示
? ? ? }
? ? })
? ? return {
? ? ? loading,
? ? ? result,
? ? ? errorMsg
? ? }
? }
}
</script>
其他可以查看從0到1學vue3_山竹回家了的博客-CSDN博客_vue3
下面寫一下vue3、react hooks原理及其區別
vue3 帶來的六大新特性
Performance:性能比vue2.x塊1.2~2倍
Tree shaking support:支持按需編譯,體積更小
Composition API:組合API,類似React Hooks
Custom Renderer API:暴露了自定義渲染API
Fragment,Teleport(Protal),Suspense:新增三個組件
Better TypeScript support:更好的支持TS
Performance
Vue3.0在性能方面比Vue2.x快了1.2~2倍。
重寫虛擬DOM的實現
運行時編譯
靜態提升與事件偵聽器緩存
SSR 速度提高
Three-shaking support
Vue3.x中的核心API都支持tree-shaking,這些API都是通過包引入的方式而不是直接在實例化時就注入,只會對使用到的功能或特性進行打包(按需打包),這意味著更多的功能和更小的體積。
Composition API
Vue2.x中,我們通常采用mixin來復用邏輯代碼,使用起來雖然方便,但也存在一些問題:代碼來源不清晰、方法屬性可能出現沖突。因此,Vue3.x引入了Composition API(組合API),使用純函數分割復用代碼。和React Hooks的概念相似。
更好的邏輯復用和代碼組織
更好的類型推導
Fragment、Teleport、Suspense
新增三個組件。
Fragment
在書寫Vue2.x時,由于組件必須是一個根結點,很多時候會添加一些沒有意義的節點用于包裹。Fragment組件就是用于解決這個問題的(這和React 中的Fragment組件是一樣的)。
Teleport
Teleport其實就是React中的Portal。Portal 提供了一種將子節點渲染到存在于父組件以外的 DOM 節點的優秀的方案。
一個 portal 的典型用例是當父組件有 overflow: hidden 或 z-index 樣式時,但你需要子組件能夠在視覺上“跳出”其容器。例如,對話框、懸浮卡以及提示框。
Suspense
同樣的,這和React中的Supense是一樣的。
Suspense 讓你的組件在渲染之前進行“等待”,并在等待時顯示 fallback 的內容。
Vue3.0 是如何變快的
diff 算法優化
Vue2 中的虛擬dom 是進行全量對比
Vue3 新增靜態標記,這和react的fiber類似,都是打tag
hoistStatic 靜態提升
Vue2 中無論元素是否參與更新,每次都會重新創建,然后在渲染
Vue3 中對于不參與更新的元素,會做靜態提升,只被創建一次,在渲染時直接復用即可
cacheHandlers 事件偵聽器緩存
默認情況下默認情況下onClick會被視為動態綁定,所以每次都會去追蹤它的變化,但是因為是同一個函數,所以沒有追蹤變化,直接緩存起來復用即可
ssr 渲染
當有大量靜態的內容的時候,這些內容會被當作純字符串推進一個buffer里面,即使存在動態的綁定,會通過模版插值嵌入進去,這樣會比通過虛擬dom來渲染的快上很多很多
當靜態內容大到一定量級的時候,會用_createStaticVNode方法在客戶端去生成一個static node。這些靜態node,會被直接innerHtml,就不需要創建對象,然后根據對象渲染。
vue3底層設計思想:
1.瀏覽器性能提升
首先,隨著ES6的發展已及廣泛使用,瀏覽器對這些新的特性逐漸增加,性能不斷優化,這就給vue3優化提供了一個機會,通過重寫來優化提升vue的性能。
2、底層實現方法
在框架設計上,vue2.0 是采用Object.defineProperty來實現雙向綁定原理,這個屬性本身就存在一些不足的地方,比如:
1.Object.defineProperty無法監控到數組下標的變化,導致直接通過數組的下標給數組設置值,不能實時響應。 為了解決這個問題,經過vue內部處理后可以使用以下幾種方法來監聽數組,push(),pop(),shift(),unshift(),splice(),sort(),reverse();由于只針對了以上八種方法進行了hack處理,所以其他數組的屬性也是檢測不到的,還是具有一定的局限性。
2.Object.defineProperty只能劫持對象的屬性,因此我們需要對每個對象的每個屬性進行遍歷。Vue 2.x里,是通過 遞歸 + 遍歷 data 對象來實現對數據的監控的,如果屬性值也是對象那么需要深度遍歷,顯然如果能劫持一個完整的對象是才是更好的選擇,新增的屬性還行通過set方法來添加監聽,有一定的局限性。
vue3主要采用的Proxy特性,有以下優點:
1.可以劫持整個對象,并返回一個新的對象
2.有13種劫持操作,但同時Proxy作為ES6的新特性,有一定的兼容問題,最主要的是這個屬性無法用polyfill來兼容,這個需要在vue3中需要解決的問題。
React原理分析:
實現和更新原理
React將每個節點轉化為fiber對象,最終形成一個fiber樹結構,來依次渲染。通過兩個fiber的對比來實現更新。這里要說到幾個diff算法,分別是tree diff,component diff和element diff。同時更新過程可能會被打斷,讓優先級更高的任務優先執行(例如瀏覽器渲染)
Tree diff
新舊兩個DOM樹,逐層對比的過程;當整個DOM樹逐層對比完畢,則所有需要被更新的元素必然能被找到;
Component diff
在進行Tree Diff的時候,每一層中,組件級別的對比。如果對比前后,組件的類型相同,則暫時認為此組件不需要被更新;如果對比前后,組件類型不同,則需要移除舊組件,創建新組件,并追加到頁面上;
Element diff
在進行組件對比的時候,如果兩個組件類型相同,則需要進行元素級別的對比,叫做element diff;
React 原理分析(一) —— React 設計思想 - 掘金 (juejin.cn)
React 源碼分析(二)—— Fiber 的 render 階段 - 掘金 (juejin.cn)
React 原理分析(三)—— Fiber 的commit 階段 - 掘金 (juejin.cn)
vue與react的區別:
1、Vue和React存在著很多的共同點:
數據驅動視圖
組件化
都使用Virtual DOM
2、核心思想不同
vue的主要特點:靈活易用的漸進式框架,進行數據攔截/代理,它對偵測數據的變化更敏感、更精確。
Reactt推崇函數式編程(純組件),數據不可變以及單向數據流,當然需要雙向的地方也可以手動實現, 比如借助onChange和setState來實現。
由于兩者核心思想的不同,所以導致Vue和React在后續設計產生了許多的差異。
3、組件寫法差異
Vue 推薦的做法是 template 的單文件組件格式(簡單易懂,從傳統前端轉過來易于理解),即 html,css,JS 寫在同一個文件(vue也支持JSX寫法)
React推薦的做法是JSX + inline style, 也就是把 HTML 和 CSS 全都寫進 JavaScript 中,即 all in js
這個差異一定程度上也是由于二者核心思想不同而導致的。
4.、響應式原理不同
Vue依賴收集,自動優化,數據可變。Vue遞歸監聽data的所有屬性,直接修改。當數據改變時,自動找到引用組件重新渲染。
React基于狀態機,手動優化,數據不可變,需要setState驅動新的state替換老的state。當數據改變時,以組件為根目錄,默認全部重新渲染, 所以 React 中會需要 shouldComponentUpdate 這個生命周期函數方法來進行控制
vue3.2中的defineProps、defineEmits、defineExpose,方便使用
vue3.2中的defineProps、defineEmits、defineExpose - 簡書 (jianshu.com)