Vue 3 的setup還能這么用?
一、前言
昨天講了什么是組合式API,\# 馬上都2202年了你還不知道什么是Vue3的組合式API嗎?[2]今天就來聽我吹一吹vue3的setup都能怎么用
借用官網一句話
setup 選項是一個接收 props 和 context 的函數
也就是說它的基本寫法應該是這樣的
- export default{
- name: 'test',
- setup(props,context){
- return {} // 這里返回的任何內容都可以用于組件的其余部分
- }
- // 組件的“其余部分”
- }
- 復制代碼
接收一個props和context函數并且將setup內的內容通過return暴露給組件的其余部分。
二、setup注意點
- 由于在執行 setup函數的時候,還沒有執行 Created 生命周期方法,所以在 setup 函數中,無法使用 data 和 methods 的變量和方法
- 由于我們不能在 setup函數中使用 data 和 methods,所以 Vue 為了避免我們錯誤的使用,直接將 setup函數中的this 修改成了 undefined
三、定義響應式數據
ref reactive
vue3通過ref reactive來定義響應式數據
ref我們用來將基本數據類型定義為響應式數據,其本質是基于Object.defineProperty()重新定義屬性的方式來實現(ref更適合定義基本數據類型)
reactive用來將引用類型定義為響應式數據,其本質是基于Proxy實現對象代理
- 基本數據類型(單類型):除Object。String、Number、boolean、null、undefined。
- 引用類型:object。里面包含的 function、Array、Date。
定義
- <script>
- import {ref, reactive} from "vue";
- export default {
- name: "test",
- setup(){
- // 基本類型
- const nub = ref(0)
- const str = ref('inline')
- const boo = ref(false)
- // 引用類型
- const obj = reactive({
- name:'inline',
- age:'18'
- })
- const arr = reactive(['0','1','2'])
- return{
- nub,
- str,
- boo,
- obj,
- arr,
- }
- }
- }
- </script>
- 復制代碼
使用
- <template>
- <div>
- <h1>基本類型</h1>
- <p>nub:{{ nub }}</p>
- <p>str:{{ str }}</p>
- <p>boo:{{ boo }}</p>
- </div>
- <div>
- <h1>引用類型</h1>
- <p>obj:{{ obj.name }}</p>
- <p>arr:{{ arr[1] }}</p>
- </div>
- </template>
- 復制代碼
結果
四、toRefs
如果我們用reactive的形式來定義響應式變量
- setup(){
- const obj = reactive({
- name:'inline',
- gender:'男',
- age:'18'
- })
- return{
- obj
- }
- }
- 復制代碼
使用
- <div>
- <p>姓名:{{ obj.name }}</p>
- <p>性別:{{ obj.gender }}</p>
- <p>年齡:{{ obj.age }}</p>
- </div>
- 復制代碼
這樣我們是不是發現在模板內使用參數很麻煩,那我們想直接用{{ name }}的方式訪問行不行,答案是可行的
這里我們使用es6的擴展運算符
- setup(){
- const obj = reactive({
- name:'inline',
- gender:'男',
- age:'18',
- })
- return{
- ...obj,
- }
- }
- 復制代碼
使用
- <div>
- <p>姓名:{{ name }}</p>
- <p>性別:{{ gender }}</p>
- <p>年齡:{{ age }}</p>
- </div>
- <div>
- <button @click="name = 'juejin'">改變姓名</button>
- <button @click="gender = '女'">改變性別</button>
- <button @click="age = '20'">改變年齡</button>
- </div>
- 復制代碼
結果
動畫.gif
這里看到我們的參數都正常的顯示到了頁面上,但是我們去改變參數時發現視圖并沒有更新,這是為什么呢???
我們把擴展運算符寫成它的等價格式
- const obj = reactive({
- name:'inline',
- gender:'男',A
- age:'18',
- })
- // ...obj ==> name:obj.name
- 復制代碼
哎哎哎,等下 我的鼠標浮動上去怎么提示我name只是一個字符串?
image.png
那我們在看看我們用ref定義值時提示什么
image.png
奧奧,這個時候我們看到name是一個Ref<string>,是一個響應式字符串。
這樣我們就找到了為什么沒有更新視圖的原因,當我們用...擴展運算符時我們得到的只是一個普通類型的數值,并不是一個響應式數據
為了解決這個問題呢,vue3給我們提供了toRefs函數,來讓我們看看效果如何
- setup(){
- const obj = reactive({
- name:'inline',
- gender:'男',
- age:'18',
- })
- return{
- ...toRefs(obj),
- }
- }
- 復制代碼
- <div>
- <p>姓名:{{ name }}</p>
- <p>性別:{{ gender }}</p>
- <p>年齡:{{ age }}</p>
- </div>
- <div>
- <button @click="name = 'juejin'">改變姓名</button>
- <button @click="gender = '女'">改變性別</button>
- <button @click="age = '20'">改變年齡</button>
- </div>
- 復制代碼
參數都可以正常改變,成功改頭換面
動畫1.gif
toRefs總結
toRefs會將我們一個響應式的對象轉變為一個普通對象,然后將這個普通對象里的每一個屬性變為一個響應式的數據
五、setup中執行方法
方式一
以reactive定義響應式數據的方式來定義方法
- <script>
- import {ref, reactive,toRefs} from "vue";
- export default {
- name: "test",
- setup(){
- const str = ref('inline')
- const fun = reactive({
- fun1(data){
- console.log(str.value)
- this.fun2(data)
- },
- fun2(data){
- console.log(data)
- console.log('我是fun2')
- }
- })
- return{
- ...toRefs(fun),
- }
- }
- }
- </script>
- 復制代碼
通過點擊事件將值傳給fun1,fun1接收到后在傳給fun2
這里我們用this.fun2()的方式去調用fun2,為什么這里用this可以正常執行不會報undefind,因為這里的this非彼this,Vue2里的this是實例這里的this是對象
- <button @click="fun1('你好')">點我1</button>
- 復制代碼
結果,成功調用并輸出
image.png
方式二
注意這里調用fun2的方式與方式一不同,直接調用就可以,不用this調用
- export default {
- name: "test",
- setup(){
- const fun1 = (data) => {
- fun2(data)
- }
- const fun2 = (data) => {
- console.log(data)
- }
- return{
- fun1,
- }
- }
- }
- 復制代碼
調用
- <button @click="fun1('你好 inline')">點我1</button>
- 復制代碼
結果
image.png
方式三
這種方式避免了將功能邏輯都堆疊在setup的問題,我們可以將獨立的功能寫成單獨的函數
這里我在setup外寫了fun() login()兩個功能函數,并在setup內分別調用
- import {ref, reactive,toRefs} from "vue";
- export default {
- name: "test",
- setup(){
- const test1 = fun() // 如果函數返回參數過多,可以賦值給變量并用擴展運算符暴露給組件的其余部分
- const { test } = login() // 也可單個接收
- return{
- ...toRefs(test1),
- test,
- }
- }
- }
- // 功能1
- function fun(){
- let str = ref('我是功能1')
- function fun1(data){
- console.log(str.value)
- fun2(data)
- }
- function fun2(data){
- console.log(data)
- }
- return{
- fun1,
- fun2,
- }
- }
- // 功能2
- function login() {
- const obj = reactive({
- msg:'我是功能2,我愛掘金'
- })
- function test() {
- console.log(obj.msg)
- }
- return{
- test
- }
- }
- </script>
- 復制代碼
調用
- <button @click="fun1('你好 inline')">點我1</button>
- <button @click="test">點我2</button>
- 復制代碼
結果
動畫.gif
方式四
與方式三一樣,只是我們將兩個功能函數提取出來放在單獨的.js文件中
image.png
然后引入組件,并在setup內調用
- <template>
- <div style="text-align: center;margin-top: 50px">
- <button @click="fun1('你好 inline')">點我1</button>
- <button @click="test">點我2</button>
- </div>
- </template>
- <script>
- import {ref, reactive,toRefs} from "vue";
- import { fun,login } from './test.js'
- export default {
- name: "test",
- setup(){
- const test1 = fun()
- const { test } = login()
- return{
- ...toRefs(test1),
- test,
- }
- }
- }
- </script>
- 復制代碼
正常執行且輸入圖片
方式五
我們還可以這樣寫,這里我定義一個reactive響應式對象,賦值給login變量,這個響應式對象內有我們登錄需要的參數、驗證和方法,這里我們全部放在login這個響應式對象內然后用toRefs及擴展運算符暴露出去
- <script>
- import {ref, reactive,toRefs} from "vue";
- export default {
- name: "test",
- setup(){
- const login = reactive({
- param: {
- username: '123',
- password: '123456',
- },
- rules: {
- username: [{ required: true, message: '請輸入用戶名', trigger: 'blur' }],
- password: [{ required: true, message: '請輸入密碼', trigger: 'blur' }],
- },
- login(){
- this.param.username = 'inline'
- this.param.password = '123456'
- console.log('成功登錄!')
- }
- })
- return{
- ...toRefs(login),
- }
- }
- }
- </script>
- 復制代碼
我們使用一下
- <input type="text" v-model="param.username">
- <input type="password" v-model="param.password">
- <button @click="login">登錄</button>
- 復制代碼
image.png
正常執行,所以我們還可以將一個功能的所有方法和相關參數寫在一個reactive對象內
如有遺漏的執行方式歡迎評論區指出~~~
六、script setup
script setup已在vue3.2的版本上正式發布
用法
- <script setup>
- </script>
- 復制代碼
是不是異常簡單
變量方法無需return
使用<script setup>時,模板被編譯成一個內聯在 setup 函數作用域內的渲染函數。這意味著內部聲明的任何頂級綁定<script setup>都可以直接在模板中使用
- <script setup>
- const msg = 'Hello!'
- </script>
- <template>
- <div>{{ msg }}</div>
- </template>
- 復制代碼
script setup內定義的變量和方法無需返回,可直接使用
組件引入
導入的組件無需注冊,可直接使用
- <script setup>
- // 導入的組件也可以直接在模板中使用
- import Foo from './Foo.vue'
- import { ref } from 'vue'
- // 編寫合成API代碼,就像在正常設置中一樣
- // 不需要手動返回所有內容
- const count = ref(0)
- const inc = () => {
- count.value++
- }
- </script>
- <template>
- <Foo :count="count" @click="inc" />
- </template>
- 復制代碼
發布Props和Emits
- <script setup>
- const props = defineProps({
- foo: String
- })
- const emit = defineEmits(['update', 'delete'])
- </script>
- 復制代碼
普通script和script setup
- script setup可以和script同時存在
- <script>
- export const name = 1
- </script>
- <script setup>
- import { ref } from 'vue'
- const count = ref(0)
- </script>
- 復制代碼
script setup 附加選項
script setup給我們提供了大多數與 options api等效的能力
就是說options api能辦到的事 script setup大部分都能辦到
那還有哪些是script setup做不到的呢?如下:
- name
- inheritAttrs
- 插件或庫所需要的自定義選項
那我要是想用這些怎么辦呢?答案是分開寫
- <script>
- export default {
- name: 'CustomName',
- inheritAttrs: false,
- customOptions: {}
- }
- </script>
- <script setup>
- // script setup logic
- </script>
- 復制代碼
defineExpose
script setup定義的變量默認不會暴露出去,因為變量這時候包含在setup的閉包中。這時我們可以使用definExpose({ })來暴露組件內部屬性給父組件使用
- <script setup>
- const a = 1
- const b = ref(2)
- defineExpose({
- a,
- b
- })
- </script>
- 復制代碼
當父組件通過模板引用獲取此組件的實例時,檢索到的實例將會是這樣{ a: number, b: number }(引用會像在普通實例上一樣自動展開)
七、寫在最后
script setup可以說的東西還有很多,等我這幾天整明白了可以單獨出一期它的用法。
最后感謝您的閱讀~~~您的點贊和閱讀就是對我最大的鼓勵~~~