一. Vue3快速上手1. 認識Vue31 瞭解相關資訊Vue.js 3.0 "One Piece" 正式版在今年9月份釋出2年多開發, 100+位貢獻者, 2600+次提交, 600+次PRVue3支援vue2的大多數特性更好的支援Typescript2 效能提升:打包大小減少41%初次渲染快55%, 更新渲染快133%記憶體減少54%使用Proxy代替defineProperty實現資料響應式重寫虛擬DOM的實現和Tree-Shaking3 新增特性Composition (組合) APIsetupref 和 reactivecomputed 和 watch新的生命週期函式provide與inject...新元件Fragment - 文件碎片Teleport - 瞬移元件的位置Suspense - 非同步載入元件的loading介面其它API更新全域性API的修改將原來的全域性API轉移到應用物件模板語法變化2. 建立vue3專案1 使用 vue-cli 建立
文件: https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create
## 安裝或者升級npm install -g @vue/cli## 保證 vue cli 版本在 4.5.0 以上vue --version## 建立專案vue create my-project
然後的步驟
Please pick a preset - 選擇 *Manually select features*Check the features needed for your project - 選擇上 *TypeScript* ,特別注意點空格是選擇,點回車是下一步Choose a version of Vue.js that you want to start the project with - 選擇 *3.x (Preview)*Use class-style component syntax - 直接回車Use Babel alongside TypeScript - 直接回車Pick a linter / formatter config - 直接回車Use history mode for router? - 直接回車Pick a linter / formatter config - 直接回車Pick additional lint features - 直接回車Where do you prefer placing config for Babel, ESLint, etc.? - 直接回車Save this as a preset for future projects? - 直接回車2 使用 vite 建立文件: https://v3.cn.vuejs.org/guide/installation.htmlvite 是一個由原生 ESM 驅動的 Web 開發構建工具。在開發環境下基於瀏覽器原生 ES imports 開發,它做到了本地快速開發啟動, 在生產環境下基於 Rollup 打包。快速的冷啟動,不需要等待打包操作;即時的熱模組更新,替換效能和模組數量的解耦讓更新飛起;真正的按需編譯,不再等待整個應用編譯完成,這是一個巨大的改變。npm init vite-app <project-name>cd <project-name>npm installnpm run dev
二. Composition API1. Composition API(常用部分)
文件:
https://composition-api.vuejs.org/zh/api.html
1 setup新的option, 所有的組合API函式都在此使用, 只在初始化時執行一次函式如果返回物件, 物件中的屬性或方法, 模板中可以直接使用2 ref作用: 定義一個數據的響應式語法: const xxx = ref(initValue):建立一個包含響應式資料的引用(reference)物件js中操作資料: xxx.value模板中操作資料: 不需要.value一般用來定義一個基本型別的響應式資料<template> <h2>{{count}}</h2> <hr> <button @click="update">更新</button></template><script>import { ref} from 'vue'export default { /* 在Vue3中依然可以使用data和methods配置, 但建議使用其新語法實現 */ // data () { // return { // count: 0 // } // }, // methods: { // update () { // this.count++ // } // } /* 使用vue3的composition API */ setup () { // 定義響應式資料 ref物件 const count = ref(1) console.log(count) // 更新響應式資料的函式 function update () { // alert('update') count.value = count.value + 1 } return { count, update } }}</script>
3 reactive作用: 定義多個數據的響應式const proxy = reactive(obj): 接收一個普通物件然後返回該普通物件的響應式代理器物件響應式轉換是“深層的”:會影響物件內部所有巢狀的屬性內部基於 ES6 的 Proxy 實現,透過代理物件操作源物件內部資料都是響應式的<template> <h2>name: {{state.name}}</h2> <h2>age: {{state.age}}</h2> <h2>wife: {{state.wife}}</h2> <hr> <button @click="update">更新</button></template><script>/* reactive: 作用: 定義多個數據的響應式 const proxy = reactive(obj): 接收一個普通物件然後返回該普通物件的響應式代理器物件 響應式轉換是“深層的”:會影響物件內部所有巢狀的屬性 內部基於 ES6 的 Proxy 實現,透過代理物件操作源物件內部資料都是響應式的*/import { reactive,} from 'vue'export default { setup () { /* 定義響應式資料物件 */ const state = reactive({ name: 'tom', age: 25, wife: { name: 'marry', age: 22 }, }) console.log(state, state.wife) const update = () => { state.name += '--' state.age += 1 state.wife.name += '++' state.wife.age += 2 } return { state, update, } }}</script>
4 比較Vue2與Vue3的響應式(重要)vue2的響應式核心:物件: 透過defineProperty對物件的已有屬性值的讀取和修改進行劫持(監視/攔截)陣列: 透過重寫陣列更新陣列一系列更新元素的方法來實現元素修改的劫持Object.defineProperty(data, 'count', { get () {}, set () {}})
問題物件直接新新增的屬性或刪除已有屬性, 介面不會自動更新直接透過下標替換元素或更新length, 介面不會自動更新 arr[1] = {}Vue3的響應式核心:透過Proxy(代理): 攔截對data任意屬性的任意(13種)操作, 包括屬性值的讀寫, 屬性的新增, 屬性的刪除等...透過 Reflect(反射): 動態對被代理物件的相應屬性進行特定的操作文件:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/GlobalObjects/Proxyhttps://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/GlobalObjects/Reflect
const proxy = new Proxy(data, { // 攔截讀取屬性值 get (target, prop) { return Reflect.get(target, prop) }, // 攔截設定屬性值或新增新屬性 set (target, prop, value) { return Reflect.set(target, prop, value) }, // 攔截刪除屬性 deleteProperty (target, prop) { return Reflect.deleteProperty(target, prop) }})proxy.name = 'tom'
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Proxy 與 Reflect</title></head><body> <script> const user = { name: "John", age: 12 }; /* proxyUser是代理物件, user是被代理物件 後面所有的操作都是透過代理物件來操作被代理物件內部屬性 */ const proxyUser = new Proxy(user, { get(target, prop) { console.log('劫持get()', prop) return Reflect.get(target, prop) }, set(target, prop, val) { console.log('劫持set()', prop, val) return Reflect.set(target, prop, val); // (2) }, deleteProperty (target, prop) { console.log('劫持delete屬性', prop) return Reflect.deleteProperty(target, prop) } }); // 讀取屬性值 console.log(proxyUser===user) console.log(proxyUser.name, proxyUser.age) // 設定屬性值 proxyUser.name = 'bob' proxyUser.age = 13 console.log(user) // 新增屬性 proxyUser.sex = '男' console.log(user) // 刪除屬性 delete proxyUser.sex console.log(user) </script></body></html>
5 setup細節setup執行的時機在beforeCreate之前執行(一次), 此時元件物件還沒有建立this是undefined, 不能透過this來訪問data/computed/methods / props其實所有的composition API相關回調函式中也都不可以setup的返回值一般都返回一個物件: 為模板提供資料, 也就是模板中可以直接使用此物件中的所有屬性/方法返回物件中的屬性會與data函式返回物件的屬性合併成為元件物件的屬性返回物件中的方法會與methods中的方法合併成功元件物件的方法如果有重名, setup優先注意:一般不要混合使用: methods中可以訪問setup提供的屬性和方法, 但在setup方法中不能訪問data和methodssetup不能是一個async函式: 因為返回值不再是return的物件, 而是promise, 模板看不到return物件中的屬性資料setup的引數setup(props, context) / setup(props, {attrs, slots, emit})props: 父元件傳給子元件的屬性(在子元件中透過props宣告過的屬性)attrs: 沒有在子元件中透過props宣告過的屬性, 相當於 this.$attrsslots: 包含所有傳入的插槽內容的物件, 相當於 this.$slotsemit: 用來分發自定義事件的函式, 相當於 this.$emit<template> <h2>App</h2> <p>msg: {{msg}}</p> <button @click="fn('--')">更新</button> <child :msg="msg" msg2="cba" @fn="fn"/></template><script lang="ts">import { reactive, ref,} from 'vue'import child from './child.vue'export default { components: { child }, setup () { const msg = ref('abc') function fn (content: string) { msg.value += content } return { msg, fn } }}</script>
<template> <div> <h3>{{n}}</h3> <h3>{{m}}</h3> <h3>msg: {{msg}}</h3> <h3>msg2: {{$attrs.msg2}}</h3> <slot name="xxx"></slot> <button @click="update">更新</button> </div></template><script lang="ts">import { ref, defineComponent} from 'vue'export default defineComponent({ name: 'child', props: ['msg'], emits: ['fn'], // 可選的, 聲明瞭更利於程式設計師閱讀, 且可以對分發的事件資料進行校驗 data () { console.log('data', this) return { // n: 1 } }, beforeCreate () { console.log('beforeCreate', this) }, methods: { // update () { // this.n++ // this.m++ // } }, // setup (props, context) { setup (props, {attrs, emit, slots}) { console.log('setup', this) console.log(props.msg, attrs.msg2, slots, emit) const m = ref(2) const n = ref(3) function update () { // console.log('--', this) // this.n += 2 // this.m += 2 m.value += 2 n.value += 2 // 分發自定義事件 emit('fn', '++') } return { m, n, update, } },})</script>
6 reactive與ref-細節是Vue3的 composition API中2個最重要的響應式APIref用來處理基本型別資料, reactive用來處理物件(遞迴深度響應式)如果用ref物件/陣列, 內部會自動將物件/陣列轉換為reactive的代理物件ref內部: 透過給value屬性新增getter/setter來實現對資料的劫持reactive內部: 透過使用Proxy來實現對物件內部所有資料的劫持, 並透過Reflect操作物件內部資料ref的資料操作: 在js中要.value, 在模板中不需要(內部解析模板時會自動新增.value)<template> <h2>App</h2> <p>m1: {{m1}}</p> <p>m2: {{m2}}</p> <p>m3: {{m3}}</p> <button @click="update">更新</button></template><script lang="ts">import { reactive, ref} from 'vue'export default { setup () { const m1 = ref('abc') const m2 = reactive({x: 1, y: {z: 'abc'}}) // 使用ref處理物件 ==> 物件會被自動reactive為proxy物件 const m3 = ref({a1: 2, a2: {a3: 'abc'}}) console.log(m1, m2, m3) console.log(m3.value.a2) // 也是一個proxy物件 function update() { m1.value += '--' m2.x += 1 m2.y.z += '++' m3.value = {a1: 3, a2: {a3: 'abc---'}} m3.value.a2.a3 += '==' // reactive對物件進行了深度資料劫持 console.log(m3.value.a2) } return { m1, m2, m3, update } }}</script>
7 計算屬性與監視computed函式:與computed配置功能一致只有getter有getter和setterwatch函式與watch配置功能一致監視指定的一個或多個響應式資料, 一旦資料變化, 就自動執行監視回撥預設初始時不執行回撥, 但可以透過配置immediate為true, 來指定初始時立即執行第一次透過配置deep為true, 來指定深度監視watchEffect函式不用直接指定要監視的資料, 回撥函式中使用的哪些響應式資料就監視哪些響應式資料預設初始時就會執行第一次, 從而可以收集需要監視的資料監視資料發生變化時回撥<template> <h2>App</h2> fistName: <input v-model="user.firstName"/><br> lastName: <input v-model="user.lastName"/><br> fullName1: <input v-model="fullName1"/><br> fullName2: <input v-model="fullName2"><br> fullName3: <input v-model="fullName3"><br></template><script lang="ts">/*計算屬性與監視1. computed函式: 與computed配置功能一致 只有getter 有getter和setter2. watch函式 與watch配置功能一致 監視指定的一個或多個響應式資料, 一旦資料變化, 就自動執行監視回撥 預設初始時不執行回撥, 但可以透過配置immediate為true, 來指定初始時立即執行第一次 透過配置deep為true, 來指定深度監視3. watchEffect函式 不用直接指定要監視的資料, 回撥函式中使用的哪些響應式資料就監視哪些響應式資料 預設初始時就會執行第一次, 從而可以收集需要監視的資料 監視資料發生變化時回撥*/import { reactive, ref, computed, watch, watchEffect} from 'vue'export default { setup () { const user = reactive({ firstName: 'A', lastName: 'B' }) // 只有getter的計算屬性 const fullName1 = computed(() => { console.log('fullName1') return user.firstName + '-' + user.lastName }) // 有getter與setter的計算屬性 const fullName2 = computed({ get () { console.log('fullName2 get') return user.firstName + '-' + user.lastName }, set (value: string) { console.log('fullName2 set') const names = value.split('-') user.firstName = names[0] user.lastName = names[1] } }) const fullName3 = ref('') /* watchEffect: 監視所有回撥中使用的資料 */ /* watchEffect(() => { console.log('watchEffect') fullName3.value = user.firstName + '-' + user.lastName }) */ /* 使用watch的2個特性: 深度監視 初始化立即執行 */ watch(user, () => { fullName3.value = user.firstName + '-' + user.lastName }, { immediate: true, // 是否初始化立即執行一次, 預設是false deep: true, // 是否是深度監視, 預設是false }) /* watch一個數據 預設在資料發生改變時執行回撥 */ watch(fullName3, (value) => { console.log('watch') const names = value.split('-') user.firstName = names[0] user.lastName = names[1] }) /* watch多個數據: 使用陣列來指定 如果是ref物件, 直接指定 如果是reactive物件中的屬性, 必須透過函式來指定 */ watch([() => user.firstName, () => user.lastName, fullName3], (values) => { console.log('監視多個數據', values) }) return { user, fullName1, fullName2, fullName3 } }}</script>
8 生命週期與 2.x 版本生命週期相對應的組合式 API
beforeCreate -> 使用 setup()created -> 使用 setup()beforeMount -> onBeforeMountmounted -> onMountedbeforeUpdate -> onBeforeUpdateupdated -> onUpdatedbeforeDestroy -> onBeforeUnmountdestroyed -> onUnmountederrorCaptured -> onErrorCaptured新增的鉤子函式
組合式 API 還提供了以下除錯鉤子函式:
應用: 當從合成函式返回響應式物件時,toRefs 非常有用,這樣消費元件就可以在不丟失響應式的情況下對返回的物件進行分解使用
問題: reactive 物件取出的所有屬性值都是非響應式的
解決: 利用 toRefs 可以將一個響應式 reactive 物件的所有原始屬性轉換為響應式的 ref 屬性
<template> <h2>App</h2> <h3>foo: {{foo}}</h3> <h3>bar: {{bar}}</h3> <h3>foo2: {{foo2}}</h3> <h3>bar2: {{bar2}}</h3></template><script lang="ts">import { reactive, toRefs } from 'vue'/*toRefs: 將響應式物件中所有屬性包裝為ref物件, 並返回包含這些ref物件的普通物件 應用: 當從合成函式返回響應式物件時,toRefs 非常有用, 這樣消費元件就可以在不丟失響應式的情況下對返回的物件進行分解使用*/export default { setup () { const state = reactive({ foo: 'a', bar: 'b', }) const stateAsRefs = toRefs(state) setTimeout(() => { state.foo += '++' state.bar += '++' }, 2000); const {foo2, bar2} = useReatureX() return { // ...state, ...stateAsRefs, foo2, bar2 } },}function useReatureX() { const state = reactive({ foo2: 'a', bar2: 'b', }) setTimeout(() => { state.foo2 += '++' state.bar2 += '++' }, 2000); return toRefs(state)}</script>
11 ref獲取元素利用ref函式獲取元件中的標籤元素
功能需求: 讓輸入框自動獲取焦點