卷不动也得继续学!紧跟vue3的步伐,再来get一波进阶新特性!
vue3進階新特性
- 一、📗watch和watchEffect
- 1、watch和watchEffect的區別
- 2、舉個例子
- (1)wtach監聽
- (2)watchEffect監聽
- 二、📘setup如何獲取組件實例
- (1)為什么需要獲取組件實例
- (2)舉個例子
- 三、📒 Vue3為何比Vue2快
- 1、Proxy響應式
- 2、PatchFlag
- (1)什么是PatchFlag
- (2)舉個例子🌰
- 3、hoistStatic
- (1)什么是hoistStatic
- (2)舉個例子🌰
- 4、cacheHandler
- (1)什么是cacheHandler
- (2)舉個例子🌰
- 5、SSR優化
- (1)什么是SSR優化
- (2)舉個例子🌰
- 6、tree-shaking
- (1)什么是tree-shaking
- (2)舉個例子🌰
- 四、📚Vite為什么啟動非???/li>
- 1、Vite是什么
- 2、Vite為何啟動快?
- 3、ES Module演示
- (1)基本使用
- (2)ES Module在瀏覽器中的應用
- 1)基本演示
- 2)引入外鏈
- 3)遠程引用
- 4)動態引入
- 五、📙全局API修改
- 1、Vue2全局API
- 2、Vue3全局API
- (1)Vue3新寫法
- (2)常見配置更新
- 六、📮結束語
之前寫了一篇文章談論 vue3 的新特性,然鵝……周一最近又 get 到了幾個比較進階的新特性, 比如: vue2 用 watch , vue3 為什么用 watchEffect 。還有 vue3 為什么比 vue2 快, vite 為什么啟動會非???#xff0c;以及 vue3 對全局注冊 API 做出的重大改變。
一起來了解一波 vue3 新特性📷
一、📗watch和watchEffect
1、watch和watchEffect的區別
我們在 vue2 時,經常用 watch 來監聽數據。但現在的 vue3 已經改用 watchEffect 來進行數據監聽了。這兩者具體有以下區別:
- 兩者都可以監聽 data 屬性變化;
- watch 需要明確監聽哪個屬性;
- 而 watchEffect 會根據其中的屬性,自動監聽其變化。
2、舉個例子
(1)wtach監聽
我們用 watch 來監聽數據。具體代碼如下:
<template><!-- <p>watch vs watchEffect</p> --><p>{{numberRef}}</p><p>{{name}} {{age}}</p> </template><script> import { reactive, ref, toRefs, watch, watchEffect } from 'vue'export default {name: 'Watch',setup() {const numberRef = ref(100)const state = reactive({name: 'monday',age: 18})watch(numberRef, (newNumber, oldNumber) => {console.log('ref watch', newNumber, oldNumber)}, {immediate: true // 初始化之前就監聽,可選})setTimeout(() => {numberRef.value = 200}, 1500)watch(// 第一個參數,確定要監聽哪個屬性() => state.age,// 第二個參數,回調函數(newAge, oldAge) => {console.log('state watch', newAge, oldAge)},// 第三個參數,配置項{immediate: true, // 初始化之前就監聽,可選// deep: true // 深度監聽})setTimeout(() => {state.age = 25}, 1500)setTimeout(() => {state.name = 'mondayLab'}, 3000)return {numberRef,...toRefs(state)}} } </script>此時瀏覽器的顯示效果如下:
綜上,我們可以知道,當使用 watch 進行屬性監聽時,需要明確要監聽哪一個屬性,并且如果想要在初始化時就被監聽,需要加上第三個可選參數 immediate:true 。這樣看來,如果我們要監聽多個屬性時,那就要寫很多個 watch ,屬實有點麻煩。所以, vue3 就引進了 watchEffect 來解決這幾個問題。
(2)watchEffect監聽
我們用 watchEffect 來監聽數據。具體代碼如下:
<template><!-- <p>watch vs watchEffect</p> --><p>{{numberRef}}</p><p>{{name}} {{age}}</p> </template><script> import { reactive, ref, toRefs, watch, watchEffect } from 'vue'export default {name: 'Watch',setup() {const numberRef = ref(100)const state = reactive({name: 'monday',age: 18})watchEffect(() => {// 初始化時,一定會先執行一次(收集要監聽的數據)console.log('numberRef', numberRef.value)console.log('state.age', state.age)console.log('state.name', state.name)})setTimeout(() => {numberRef.value = 2000}, 1000)setTimeout(() => {state.age = 25}, 1500)setTimeout(() => {state.name = 'mondayLab'}, 3000)return {numberRef,...toRefs(state)}} } </script>此時瀏覽器的顯示效果如下:
從上圖中可以看到, watchEffect 只要做一次監聽,就可以同時監聽到三個屬性。同時,值得注意的是, watchEffect需要初始化,且初始化時一定會先執行一次,這個初始化的目的在于收集要監聽的數據。所以,控制臺打印的第一組數據就是初始化時的數據。
第一次收集到它要監聽這三個屬性后,在此之后呢,這三個屬性也相應地擁有了響應式的功能。相對應的三個計時器再打印出三組數據出來,所以一共是四組數據。
二、📘setup如何獲取組件實例
(1)為什么需要獲取組件實例
剛聽到這個概念時,我其實時有點懵的。為什么要用setup來獲取組件的實例?其實說的就是一個this的指向問題。
在 vue2 中, Options API 可以使用 this 來獲取組件的實例,但是到現在的 vue3 ,已經被摒棄掉了。在 setup 和其他 Composition API 中沒有 this ,但是它提供了一個 getCurrentInstance 來獲取當前的實例。
(2)舉個例子
我們先用 Options API 來獲取實例。具體代碼如下:
<template><p>get instance</p> </template><script> import { onMounted, getCurrentInstance } from 'vue'export default {name: 'GetInstance',data() {return {x: 1,y: 2}},mounted() {console.log('this2', this)console.log('x', this.x, 'y', this.y)} } </script>此時瀏覽器的顯示效果如下:
正如我們所想的,用 options API ,具體的實例都可以如期的被調用出來。
下面我們用 Composition API 來看看,是否可以調用出來。具體代碼如下:
<template><p>get instance</p> </template><script> import { onMounted, getCurrentInstance } from 'vue'export default {name: 'GetInstance',data() {return {x: 1,y: 2}},setup() {//無法獲取this實例console.log('this1', this)const instance = getCurrentInstance()console.log('instance', instance)onMounted(() => {//無法獲取this實例console.log('this in onMounted', this)//通過getCurrentInstance獲取this實例console.log('x', instance.data.x)})} } </script>此時瀏覽器的顯示效果如下:
通過上圖我們可以知道,如果用 Composition API 來獲取組件實例,是沒有辦法獲取的。需要通過 getCurrentInstance 方法來獲取當前的組件實例。
三、📒 Vue3為何比Vue2快
有一次看面經的時候發現有一道題:Vue3為何比Vue2快。當時我也挺納悶的,那個時候我的心里🤯: vue3 的出現不就是因為更好才出現嘛?不是更好難道還能更差?
事實證明……是我孤陋寡聞了。 Vue3 比 Vue2 快的原因主要體現在以下6個方面:
- Proxy響應式
- PatchFlag
- hoistStatic
- cacheHandler
- SSR優化
- tree-shaking
接下來就讓我們一起來了解一下吧🙋
1、Proxy響應式
vue3 中實現響應式的 Proxy 會比 vue2 中的 Object.defineProperty 快。具體原因可翻看我的另外一篇文章,這里不再講述。
2、PatchFlag
(1)什么是PatchFlag
- 在編譯模板時,使用動態節點做標記;
- 標記,分為不同的類型,如TEXT 、 PROPS ;有的是直接獲取 text ,有的則是修改 props ;
- diff 算法比較時,可以區分靜態節點,以及不同類型的動態節點。此處要注意的是, patchflag 并不是專門對 diff 算法做優化,而是在輸入上做一些變更和做一些標記,從而達到對 diff 算法的優化。
(2)舉個例子🌰
我們用一個在線網站來演示 patchflag ,在線網站網址為https://vue-next-template-explorer.netlify.app/,大家可以根據需要自行演示~
具體使用方式如下圖所示:
接下來我們來演示 patchflag ,此時右邊的 options 不做選擇。我們在左邊的框輸入下面代碼:
<div id="app"><span>hello vue3</span><span>{{msg}}</span><span :class="name">monday</span><span :id="name">monday</span><span :id="name">{{mag}}</span><span :id="name" :msg="msg">monday</span> </div>此時右邊的框顯示如下:
import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"export function render(_ctx, _cache, $props, $setup, $data, $options) {return (_openBlock(), _createBlock("div", { id: "app" }, [_createVNode("span", null, "hello vue3"),_createVNode("span", null, _toDisplayString(_ctx.msg), 1 /* TEXT */),_createVNode("span", { class: _ctx.name }, "monday", 2 /* CLASS */),_createVNode("span", { id: _ctx.name }, "monday", 8 /* PROPS */, ["id"]),_createVNode("span", { id: _ctx.name }, _toDisplayString(_ctx.mag), 9 /* TEXT, PROPS */, ["id"]),_createVNode("span", {id: _ctx.name,msg: _ctx.msg}, "monday", 8 /* PROPS */, ["id", "msg"])])) }// Check the console for the AST大家可以看到,除了第一個是靜態節點以外,其他都是動態節點。此時模板編譯后的結果,在最后邊有對應的數字出現,這個數字就是標記。 vue3 通過給每個動態節點做數字標記,達到優化 diff 算法的效果。
3、hoistStatic
(1)什么是hoistStatic
- 將靜態節點的定義,提升到父作用域上,并緩存起來;
- 多個相鄰的靜態節點,會被合并起來;
- 典型的拿空間換時間的優化策略。
(2)舉個例子🌰
我們同樣用在線網站來做一個演示。此時我們在左邊框輸入以下代碼:
<div id="app"><span>monday</span><span>monday</span><span>monday</span><span>{{msg}}</span> </div>此時右邊的框顯示如下:
import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"const _hoisted_1 = { id: "app" } const _hoisted_2 = /*#__PURE__*/_createVNode("span", null, "monday", -1 /* HOISTED */) const _hoisted_3 = /*#__PURE__*/_createVNode("span", null, "monday", -1 /* HOISTED */) const _hoisted_4 = /*#__PURE__*/_createVNode("span", null, "monday", -1 /* HOISTED */)export function render(_ctx, _cache, $props, $setup, $data, $options) {return (_openBlock(), _createBlock("div", _hoisted_1, [_hoisted_2,_hoisted_3,_hoisted_4,_createVNode("span", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)])) }// Check the console for the AST通過以上代碼可以發現, vue3 在每一個靜態節點的外部都定義了父節點。這樣看好像更冗余了一點,原因在于現在節點還比較少。
下面我們來演示更多的節點,此時右邊的 options 選擇 hoistStatic 。我們在左邊框輸入以下代碼:
<div id="app"><span>monday</span><span>monday</span><span>monday</span><span>monday</span><span>monday</span><span>monday</span><span>monday</span><span>monday</span><span>monday</span><span>monday</span><span>{{msg}}</span> </div>此時右邊的框顯示如下:
import { createVNode as _createVNode, toDisplayString as _toDisplayString, createStaticVNode as _createStaticVNode, openBlock as _openBlock, createBlock as _createBlock } from "vue"const _hoisted_1 = { id: "app" } const _hoisted_2 = /*#__PURE__*/_createStaticVNode("<span>monday</span><span>monday</span><span>monday</span><span>monday</span><span>monday</span><span>monday</span><span>monday</span><span>monday</span><span>monday</span><span>monday</span>", 10)export function render(_ctx, _cache, $props, $setup, $data, $options) {return (_openBlock(), _createBlock("div", _hoisted_1, [_hoisted_2,_createVNode("span", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)])) }// Check the console for the AST此時可以看到, vue3 把所有的靜態節點都包圍成一個父節點了,就好像 vue3 跟它的甲方爸爸說,要不這樣吧,我幫你做一個靜態節點的集合,幫你把所有內容都定義到一起。
4、cacheHandler
(1)什么是cacheHandler
- cacheHandler ,指緩存事件的意思。
(2)舉個例子🌰
我們同樣用在線網站來做一個演示,此時右邊的 options 選擇 cacheHandler 。我們在左邊框輸入以下代碼:
<div id="app"><span @click="clickHandler">monday</span> </div>此時右邊的框顯示如下:
import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from "vue"export function render(_ctx, _cache, $props, $setup, $data, $options) {return (_openBlock(), _createBlock("div", { id: "app" }, [_createVNode("span", {onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.clickHandler && _ctx.clickHandler(...args)))}, " monday ")])) }// Check the console for the AST觀察代碼 onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.clickHandler && _ctx.clickHandler(...args))) 可以發現, vue3 在處理點擊事件時,會進行緩存。這行代碼的意思就是,當有 _cache[1] 的值時就取 _cache[1] ,如果沒有 _cache[1] 就再給 _cache[1] 定義一個函數。
5、SSR優化
(1)什么是SSR優化
- 靜態節點會直接進行輸出,繞過了 vdom ;
- 如果是動態節點,還是需要進行動態渲染。
(2)舉個例子🌰
我們同樣用在線網站來做一個演示,此時右邊的 options 選擇 SSR 。我們在左邊框輸入以下代碼:
<div id="app"><span @click="clickHandler">monday</span> </div>此時右邊的框顯示如下:
import { mergeProps as _mergeProps } from "vue" import { ssrRenderAttrs as _ssrRenderAttrs, ssrInterpolate as _ssrInterpolate } from "@vue/server-renderer"export function ssrRender(_ctx, _push, _parent, _attrs, $props, $setup, $data, $options) {const _cssVars = { style: { color: _ctx.color }}_push(`<div${_ssrRenderAttrs(_mergeProps({ id: "app" }, _attrs, _cssVars))}><span>monday</span><span>monday</span><span>monday</span><span>${_ssrInterpolate(_ctx.msg)}</span></div>`) }// Check the console for the AST通過以上代碼可以發現,用 SSR 來進行模板編譯時,靜態節點會直接進行輸出,直接繞過 vdom 。而如果是動態節點時,依然需要進行動態渲染。
6、tree-shaking
(1)什么是tree-shaking
- 編譯時,根據不同的情況,引入不同的 API 。
(2)舉個例子🌰
同樣用在線網站來做一個演示,此時右邊的 options 不做選擇。左邊框輸入以下代碼:
<div id="app"><span>monday</span><span>monday</span><span>monday</span><span>{{msg}}</span> </div>此時右邊的框的第一行顯示如下:
import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"再來演示另外一種情況。同樣我們在左邊框輸入以下代碼:
<div id="app"><span :id="msg"></span><input v-model="msg"> </div>此時右邊的框的第一行顯示如下:
import { createVNode as _createVNode, vModelText as _vModelText, withDirectives as _withDirectives, openBlock as _openBlock, createBlock as _createBlock } from "vue"大家可以發現, vue3 在編譯時,它不會一次性引入很多 API ,而是根據我們所需要的,我們要什么它就引入什么,我們不要的,它一概不會幫我們引入。這在某種程度上就優化了很多性能。
四、📚Vite為什么啟動非???/h1>
第一次看見 vite 是在 vue3 的官方文檔里面,官方文檔介紹: vite 是一個 web 開發構建工具,由于其使用 原生ES模塊 導入方式,可以實現閃電般的冷服務器啟動。通過在終端中運行相應的命令,可以使用Vite快速構建 Vue 項目。
1、Vite是什么
- vite 是一個前端的打包工具,是 vue 作者發起的一個項目;
- vite 借助 vue 的影響力,發展較快,和 webpack 有著一定的競爭關系;
- 優勢: vite 使得程序在開發環境下無需打包,且啟動非??焖?。
2、Vite為何啟動快?
在開發環境下使用 ES6 Module ,無需打包,速度非???#xff1b;
在生產環境下使用 rollup 打包,并不會快很多。
3、ES Module演示
(1)基本使用
在 vue2 時,我們加載一個工程文件需要先轉為 ES5 ,然后經過一些列的打包才能正式加載項目頁面。而在 vue3 ,生產環境目前還沒有做到,但是在開發環境下,通過 ES6 Module 的方式,無需打包,速度非???。
下面我們講演示集中 ES Module 在瀏覽器中的應用。
(2)ES Module在瀏覽器中的應用
1)基本演示
<!DOCTYPE html> <html> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>ES Module demo</title> </head> <body><p>基本演示</p><script type="module">import plus from './src/plus.js'const res = add(1, 2)console.log('add res', res)</script><script type="module">import { plus, multi } from './src/math.js'</script> </body> </html>我們在 <script> 標簽下設置 type="module" ,之后就可以按照我們平常寫 vue 程序一樣,用 import 引入相應的文件。
2)引入外鏈
<!DOCTYPE html> <html> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>ES Module demo</title> </head> <body><p>外鏈</p><script type="module" src="./src/index.js"></script> </body> </html>同時,也可以通過 src 的方式直接引入外部js文件。
3)遠程引用
<!DOCTYPE html> <html> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>ES Module demo</title> </head> <body><p>遠程引用</p><script type="module">import { createStore } from 'https://unpkg.com/redux@latest/es/redux.mjs'console.log('createStore', createStore)</script> </body> </html>也可以直接引入 cdn 上的網址,即遠程引用。
4)動態引入
<!DOCTYPE html> <html> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>ES Module demo</title> </head> <body><p>動態引入</p><button id="btn1">load1</button><button id="btn2">load2</button><script type="module">document.getElementById('btn1').addEventListener('click', async () => {const add = await import('./src/add.js')const res = add.default(1, 2)console.log('add res', res)})document.getElementById('btn2').addEventListener('click', async () => {const { add, multi } = await import('./src/math.js')console.log('add res', add(10, 20))console.log('multi res', multi(10, 20))})</script> </body> </html>比如說我們要給兩個 button 綁定兩個事件,并且要讓他們引入兩個不同的 js 文件,那我們可以在執行點擊后,在箭頭函數里面進行 import 操作,按需引入,即想要讓它引入哪個再引入哪個。
五、📙全局API修改
講完 vite ,我們再來將一個 vue3 的重大改變,那就是全局 API 的修改。具體看下方。
1、Vue2全局API
在 Vue2 中,全局API經常會遇到以下問題:
- 在單元測試中,全局配置非常容易污染全局環境;
- 在不同的 apps 中,共享一份有不同配置的 Vue 對象,也變得非常困難。
Vue2入口文件寫法:
import Vue from 'vue' import App from './App.vue'Vue.config.ignoredElements = [/^app-/] Vue.use(/*...*/) Vue.mixin(/*...*/) Vue.component(/*...*/) Vue.directive(/*...*/)Vue.prototype.customProperty = () = {}new Vue({render: h => h(App) }).$mount('#app')2、Vue3全局API
(1)Vue3新寫法
因此, Vue3 為了解決 Vue2 的問題,推出了新的寫法。具體代碼如下:
Vue3的新寫法:
import { createApp } from 'vue' import App from './App.vue'const app = createApp(App)app.config.isCustomElement = tag => tag.startsWith('app-') app.use(/*...*/) app.mixin(/*...*/) app.component(/*...*/) app.directive(/*...*/)app.config.globalProperties.customProperty = () = {}app.mount('#app')(2)常見配置更新
1)全局配置:Vue.config->app.config
- config.productionTip被刪除
- config.ignoredElements改名為config.isCustomElement
2)全局注冊類API
- Vue.component -> app.component
- Vue.directive -> app.directive
3)行為擴展類API
- Vue.mixin -> app.mixin
- Vue.use -> app.use
六、📮結束語
到這里,碎碎念一波!在學習過程中,要明白 watch 和 watchEffect 的不同之處,還要知道 setup 如何獲取組件實例,這其中談論的就是關于 this 的問題。
最后的最后,就是 Vue3 為什么比 Vue2 快,涉及到6個性能優化的方法,學有余力之余,盡量用在線網站演示一波。隨之,緊跟著 vue3 的步伐, vite 也成為了很多開發人員在開發環境中使用的工具。還有就是,解決全局污染等各種問題, Vue3 對全局注冊 API 做出的改變。
一波碎碎念結束, vue3 的進階新特性講解就結束啦!如有疑問或文章有誤歡迎評論區留言或私信我交流~
- 關注公眾號 星期一研究室 ,第一時間關注學習干貨,更多有趣的專欄待你解鎖~
- 如果這篇文章對你有用,記得 一鍵三連 再走哦~
- 我們下期見!🥂🥂🥂
總結
以上是生活随笔為你收集整理的卷不动也得继续学!紧跟vue3的步伐,再来get一波进阶新特性!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 敲黑板!vue3重点!一文了解Compo
- 下一篇: 与高通、虹软深度合作 真我GT5 Pro