【Vue3】setup(setup函數與script setup)示例,用法以及原理(持續更新)



Vue3 中的setup 一種是setup函數,一種是script setup

setup函數

setup函數原理說明

由於setup 是在beforeCreate 和 create 生命周期階段,組件還沒有創建,即還沒有進入 data 方法 階段。
setup 返回的結果集 作為 (傳統寫法)data 和 method 的值,確切點說是綁定到 組件對象的屬性。

setup函數特性

1、setup函數是處於 生命周期函數 beforeCreate 和 Created 兩個鈎子函數之間的函數 也就說在 setup函數中是無法 使用 data 和 methods 中的數據和方法的
2、setup函數是 Composition API(組合API)的入口
3、在setup函數中定義的變量和方法最后都是需要 return 出去的 不然無法再模板中使用

setup 函數將接收兩個參數,props&context
Props :props接收父組件傳入的值,為Proxy對象,且為響應式,所以不能使用 ES6 解構,它會消除 prop 的響應性
context:官方解釋=>context 是一個普通的 JavaScript 對象,它暴露組件的三個 property:

export default {
  setup(props, context) {
    // Attribute (非響應式對象)
    console.log(context.attrs)
 
    // 插槽 (非響應式對象)
    console.log(context.slots)
 
    // 觸發事件 (方法)
    console.log(context.emit)
  }
}

setup函數注意點

1、由於在執行 setup函數的時候,還沒有執行 Created 生命周期方法,所以在 setup 函數中,無法使用 data 和 methods 的變量和方法
2、由於我們不能在 setup函數中使用 data 和 methods,所以 Vue 為了避免我們錯誤的使用,直接將 setup函數中的this修改成了 undefined
3、setup函數只能是同步的不能是異步的

setup函數中的watch與computed

看下面的setup拆分實例 grandson.vue的例子

setup拆分實例

main.js

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

App.vue

<script setup>
import Parent from './components/parent.vue'
</script>

<template>
  <Parent></Parent>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

parent.vue

<template>
    <div @click="handleClick">我是父組件 {{info}}
        <son></son>
    </div>
</template>

<script>
import { defineComponent } from 'vue'
import son from './son.vue'
import useInfo from './info.js'

export default defineComponent({
    setup() {
        let {info, handleClick} = useInfo()
        return {
            info,
            handleClick
        }        
    },
    components: {
        son
    }
})
</script>

info.js


import {provide, reactive, ref } from 'vue'

let useInfo = () => {
    let info = ref(123);
        let datas = reactive({
            width: 100,
            height: 50,
            bg: 'pink'
        });
        const updateInfo = (val) => {
            info.value = val
        }

        const handleClick = () => {
            console.log('handleClick了');
            info.value++;
            console.log('info.value:' + info.value);
        }

        provide('info', info);
        provide('datas', datas)
        provide("updateInfo", updateInfo);

        return {
            info,
            handleClick
        }
}

export default useInfo


son.vue

<template>
    <div>我是子組件 {{info}}
        <grand-son ref="compa"></grand-son>
    </div>
</template>

<script>
import { defineComponent, inject } from 'vue'
import grandSon from './grandSon.vue';


export default defineComponent({
    setup() {
        const info = inject('info');
        
        return {
            info
        }
    },
    components: {
        grandSon
    }
})
</script>

grandson.vue

<template>
    <div>我是孫子組件 <button @click="updateInfo('456')">點擊更新info</button>{{info}} computed: {{computedInfo}}<br />寬度{{datas.width}},  高度:{{datas.height}}, 背景: {{datas.bg}}</div>
</template>

<script>
import { defineComponent , inject, watch, computed} from 'vue'


export default defineComponent({
    setup() {
        const info = inject('info');
        const updateInfo = inject('updateInfo');
        const datas = inject('datas');
        
        
        watch(info, (val) => {
            console.log('變了');
            console.log(val);
        });

        const computedInfo = computed(() => { 
            console.log('computed 改變了info')
            return info.value + 10
        })


        return {
            info,
            datas,
            updateInfo,
            computedInfo
        }
    },
})
</script>


script setup

script setup 是在單文件組件 (SFC) 中使用組合式 API 的編譯時語法糖。

<script setup>
// 變量
const msg = 'Hello!'

// 函數
function log() {
  console.log(msg)
}

// click 那里有問題
</script>

<template>
<div>
  <div @click="log">我是: {{ msg }}</div>
</div>
</template>

setup 包含的生命周期

script setup使用方法

<script setup></script>

script setup的作用

自動注冊子組件

Vue2

<template>
  <div>
    <h2>父組件!</h2>
    <Child />
  </div>
</template>
<script>
import Child from './Child.vue'
export default {
    component: {
        Child // 注冊子組件后方可在template中使用
    }
}
</script>

Vue3:

<template>
  <div>
    <h2>父組件!</h2>
    <Child />
  </div>
</template>
<script>
import { defineComponent, ref } from 'vue';
import Child from './Child.vue'
export default defineComponent({
 components: { 
     Child // 注冊子組件后方可在template中使用
 }, 
 setup() { 
     return { 
     } 
 }
});
</script>

setup script語法

<template>
  <div>
    <h2>父組件!-setup script</h2>
    
    <Child />
  </div>
</template>
<script setup>
import Child from './Child.vue'
// 省略了子組件注冊的過程,import后可直接在template中使用
</script>

屬性和方法無需返回

vue3.x語法:
如需在template中使用屬性和方法,必須手動返回對應的屬性和方法。這種composition API的編寫規則,相對繁瑣。如下:

<template>
  <div>
    <h2 @click="addCount">購買數量 {{ count}} 件</h2>
  </div>
</template>

<script>
import { defineComponent, ref } from 'vue';
export default defineComponent({
    setup() {
        const count = ref(1)
        const addCount = () => {
          count.value++
        }
        return {
            count,
            addCount
        }
    }
});
</script>

setup script語法

<template>
  <div>
    <h2 @click="addCount">購買數量 {{ count}} 件</h2>
  </div>
</template>

<script setup>
import { ref } from 'vue';
const count = ref(1)
const addCount = () => {
  count.value++
}
</script>

支持props、emit和context

vue3.x語法
前面setup函數中提及,setup函數包含props和context2個參數,通過這兩個參數,達到數據通信及事件觸發的目的。
setup scrip語法
沒有setup()那怎么獲取到props和context呢?
setup script語法糖提供了三個新的API來供我們使用:defineProps、defineEmit和useContext。
defineProps用來接收父組件傳來的值props;
defineEmit用來聲明觸發的事件表;
useContext用來獲取組件上下文context

defineProps, defineEmits

在 script setup 中必須使用 defineProps 和 defineEmits API 來聲明 props 和 emits ,它們具備完整的類型推斷並且在 script setup 中是直接可用的。

<script setup>
const props = defineProps({
  foo: String
});

const emit = defineEmits(['change', 'delete']);
// setup code
</script>

defineExpose

使用 script setup 的組件是默認關閉的,也即通過模板 ref 或者 $parent 鏈獲取到的組件的公開實例,不會暴露任何在 script setup 中聲明的綁定。當父組件通過模板 ref 的方式獲取到當前組件的實例。

<script setup>
import { ref } from 'vue'

const a = 1
const b = ref(2)

defineExpose({
  a,
  b
})
</script>

useSlots 和 useAttrs

在 script setup 使用 slots 和 attrs 的情況應該是很罕見的,因為可以在模板中通過 $slots 和 $attrs 來訪問它們。在你的確需要使用它們的罕見場景中,可以分別用 useSlots 和 useAttrs 兩個輔助函數:

<script setup>
import { useSlots, useAttrs } from 'vue'

const slots = useSlots()
const attrs = useAttrs()
</script>

useSlots 和 useAttrs 是真實的運行時函數,它會返回與 setupContext.slots 和 setupContext.attrs 等價的值,同樣也能在普通的組合式 API 中使用。

實例

父組件:

<template>
  <div>
    <h2>我是父組件!-setup script</h2>
    <Child ref="child" msg="hello" @child-click="childCtx" />
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import Child from './child.vue'

const childCtx = (e) => {
  console.log('我是emit出來的值:' + e) 
}

let child = ref(null);
onMounted(() => {
	console.log('我是expose出來的值:' + child.value.name); // 123
})


</script>

子組件:

<template>
  <div>
    <span @click="handleClick">我是子組件! -- msg: {{ props.msg }}</span>
  </div>
</template>

<script setup>
import { ref, defineProps, defineEmits, defineExpose, useAttrs, useSlots } from 'vue'

const name = ref('expose name value')
const emit = defineEmits(['child-click'])
const props = defineProps({
  msg: String
})

const handleClick = () => {
  emit('child-click', 'emit click value')
}

const slots = useSlots();
const attrs = useAttrs();
console.log(slots)
console.log(attrs)

defineExpose({
  name
})
</script>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM