一. 生命周期鈎子
1. 說明
setup 可以用來替代 data 、 methods 、 computed 、watch 等等這些選項,也可以替代 生命周期鈎子。
注:因為 setup
是圍繞 beforeCreate
和 created
生命周期鈎子運行的,所以不需要顯式地定義它們。換句話說,在這些鈎子中編寫的任何代碼都應該直接在 setup
函數中編寫。
2. 實戰
<template> <div> <h3>我是child1組件</h3> <h4>{{counter}}</h4> <h4><button @click="AddNum()">加1</button></h4> </div> </template> <script> import { ref, onMounted, onUpdated, onUnmounted } from 'vue'; export default { setup(props, context) { // 編寫相關業務 let counter = ref(100); const AddNum = () => counter.value++; // 測試生命周期 onMounted(()=>{ console.log('App onMounted'); }); onUpdated(()=>{ console.log('App onUpdated'); }); onUnmounted(()=>{ console.log('App onUnmounted'); }); // 返回值 return { counter, AddNum } } } </script>
二. provide/Inject
1. 說明
之前學習的Option Api中有Provide和Inject,Composition API也可以替代之前的 Provide 和 Inject 的選項。
(1). Provide:用於向子組件中傳遞數據。provide可以傳入兩個參數:① name:提供的屬性名稱; ② value:提供的屬性值。
注:為了保證數據的響應性,一般傳遞ref對象;而且傳遞的數據要符合單向數據流原則,即傳遞的數據只允許子組件調用,不允許子組件修改,所有通常再用readonly包裹一下。
(2). Inject:用於接收父組件傳遞過來的數據。
inject
函數有兩個參數:
A. 要 inject 的 property 的 name
B. 默認值 (可選)
2. 實戰
這里准備三個組件,從上往下依次是: App.vue → father1.vue → child1.vue, 下面代碼實現:App組件 向 child1組件傳值。
App.vue代碼
<template> <div> <h3>我是最上級App組件</h3> <h4>{{counter}}</h4> <h4><button @click="AddNum()">App.vue中加1</button></h4> <father1></father1> </div> </template> <script> import father1 from './father1.vue'; import { ref, readonly, provide } from 'vue'; export default { components: { father1 }, setup() { let counter = ref(100); const AddNum = () => counter.value++; provide('myCounter', readonly(counter)); return { counter, AddNum } } } </script>
father1.vue代碼

<template> <div> <h3>我是father1組件</h3> <child1></child1> </div> </template> <script> import child1 from './child1.vue'; export default { components:{ child1 }, setup(props, context) { return {} } } </script>
child1.vue代碼
<template> <div> <h3>我是child1組件</h3> <h4>{{counter}}</h4> <h4>{{msg}}</h4> <h4><button @click="AddNum()">child.vue加1</button></h4> </div> </template> <script> import { inject } from 'vue'; export default { setup(props, context) { let counter = inject('myCounter'); let msg = inject('myMsg','hello ypf'); //如果沒有傳遞,則為默認值 // 傳遞過來的是readonly對象,不能被修改,符合單向數據流原則,如果修改,需要通過emit向父組件發送通知,讓父組件修改 const AddNum = () => counter.value++; // 返回值 return { counter, msg, AddNum } } } </script>
三. 綜合練習-hook封裝
1. 相關規則
(1). import導入的匹配規則
(2). ESModule導入導出規則
詳見:https://www.cnblogs.com/yaopengfei/p/14496363.html
2. 代碼實操
(1). hooks文件夾下5個js文件
useCounter.js

/* 自增、自減、雙倍 */ import { ref, computed } from 'vue'; // 對應外面的接收方式 import useCounter from './hooks/useCounter.js' export default function() { let counter = ref(100); let doubleCounter = computed(() => counter.value * 2); const AddNum = () => counter.value++; const Reduce = () => counter.value--; return { counter, doubleCounter, AddNum, Reduce } }
useTitle.js

/* 修改標題 */ import { ref, watch } from 'vue' export default function(title = '我是默認title哦') { let titleRef = ref(title); watch(titleRef, (newValue, oldValue) => { document.title = newValue; }, { immediate: true }); return titleRef; }
useScrollPosition.js

import { ref } from 'vue'; export default function() { const scrollX = ref(0); const scrollY = ref(0); document.addEventListener("scroll", () => { scrollX.value = window.scrollX; scrollY.value = window.scrollY; }); return { scrollX, scrollY } }
useMousePosition.js

import { ref } from 'vue'; export default function() { const mouseX = ref(0); const mouseY = ref(0); window.addEventListener("mousemove", (event) => { mouseX.value = event.pageX; mouseY.value = event.pageY; }); return { mouseX, mouseY } }
useLocalStorage.js

import { ref, watch } from 'vue' export default function(key, value) { var data = ref(value); if (value) { window.localStorage.setItem(key, JSON.stringify(value)); } else { data.value = JSON.parse(window.localStorage.getItem(key)); } watch(data, (newValue, oldValue) => { window.localStorage.setItem(key, JSON.stringify(newValue)); }); return data; }
(2). hooks文件夾中新建index.js統一入口
import useCounter from './useCounter.js'; import useTitle from './useTitle.js'; import useScrollPosition from './useScrollPosition.js' import useMousePosition from './useMousePosition.js' import useLocalStorage from './useLocalStorage.js' export { useCounter, useTitle, useScrollPosition, useMousePosition, useLocalStorage }
(3). App.vue調用代碼
<template> <div> <h4>{{counter}}</h4> <h4>{{doubleCounter}}</h4> <h4><button @click="AddNum()">加1</button></h4> <h4><button @click="Reduce()">減1</button></h4> <h4><button @click="Edit()">修改緩存</button></h4> <p class="content"></p> <div class="scroll"> <div class="scroll-x">scrollX: {{scrollX}}</div> <div class="scroll-y">scrollY: {{scrollY}}</div> </div> <div class="mouse"> <div class="mouse-x">mouseX: {{mouseX}}</div> <div class="mouse-y">mouseY: {{mouseY}}</div> </div> </div> </template> <script> // 方式一:逐個導入 // import useCounter from './hooks/useCounter.js'; // import useTitle from './hooks/useTitle.js'; // import useScrollPosition from './hooks/useScrollPosition.js' // import useMousePosition from './hooks/useMousePosition.js' // import useLocalStorage from './hooks/useLocalStorage.js' //方式二: 統一封裝導入 (可以省略,直接寫成 ./hooks) import { useCounter, useTitle, useScrollPosition, useMousePosition, useLocalStorage } from './hooks/index.js' export default { setup() { // 1.自增、自減、雙倍 const { counter, doubleCounter, AddNum, Reduce } = useCounter(); // 2.修改title let titleRef = useTitle('ypf'); setTimeout(() => { titleRef.value = 'lmr'; }, 3000); // 3. 頁面滾動位置 const { scrollX, scrollY } = useScrollPosition(); // 4. 鼠標移動位置 const { mouseX, mouseY } = useMousePosition(); // 5. 緩存的使用 var myData = useLocalStorage('myName', 'ypf'); console.log(myData); const Edit = () => myData.value = 'lmr'; return { counter, doubleCounter, AddNum, Reduce, Edit, scrollX, scrollY, mouseX, mouseY } } }
四. setup頂層開發模式
(詳見官網:https://v3.cn.vuejs.org/api/sfc-script-setup.html)
1. 說明
<script setup>
是在單文件組件 (SFC) 中使用組合式 API 的編譯時語法糖。相比於普通的 <script>
語法,它具有更多優勢:
(1). 更少的樣板內容,更簡潔的代碼。
(2). 能夠使用純 Typescript 聲明 props 和拋出事件。
(3). 更好的運行時性能 (其模板會被編譯成與其同一作用域的渲染函數,沒有任何的中間代理)。
(4). 更好的 IDE 類型推斷性能 (減少語言服務器從代碼中抽離類型的工作)。
2. 用法說明
(1). 將setup寫到<script>標簽中。
(2). 變量,函數聲明,以及 import 引入的內容都可以直接在template中使用
(3). 直接導入組件使用即可
(4). 父子組件之間相互傳值,需要使用:defineProps
和 defineEmits。
3. 實戰
(1). 父組件
<template> <div> <h4>我是主組件</h4> <h4>{{counter}}</h4> <h4><button @click="AddNum">加1</button></h4> <child1 msg="hahahhaha" @MyTest='MyTest'></child1> </div> </template> <script setup> import { ref } from 'vue'; // 1. 變量,函數聲明,以及 import 引入的內容都可以直接在template中使用 var counter = ref(0); var AddNum = () => counter.value++; //2. 直接導入組件使用即可 import child1 from './child1.vue'; //3. 測試子傳父 const MyTest = (obj) => { console.log(`我是子組件傳遞過來的值:${obj}`); } </script>
(2). 子組件
<template> <div> <h4>我是child1組件</h4> <h4>我是父組件傳遞過來的值:{{msg}}</h4> <h4><button @click="myClick">對外發送</button></h4> </div> </template> <script setup> import { defineProps, defineEmits } from 'vue'; // 接收父組件傳遞過來的值 const props = defineProps({ msg: { type: String, default: 'hello ypf' } }) // 聲明對外傳遞事件,並執行傳遞 const emit = defineEmits(['MyTest', '']); const myClick = () => { emit('MyTest', '10010'); } </script>
!
- 作 者 : Yaopengfei(姚鵬飛)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 聲 明1 : 如有錯誤,歡迎討論,請勿謾罵^_^。
- 聲 明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。