【Vue3】setup

setup

  • setup 是一个方法,组件中所用到的 [数据]、[方法] 均要配置到 setup 中

    返回 [对象]:对象中的 [属性]、[方法] 均可在模板中直接使用

    返回 [渲染函数]:可自定义渲染内容(了解)

<template>
    <h2>姓名:{{ name }}</h2>
    <h2>年龄:{{ showAge() }}</h2>
    <h2>特长:<button @click="selfIntroduction">说话</button></h2>
</template>

<script>
export default {
    name: 'App',
    setup() {
        let name = "superman"
        function showAge() {
            return 21
        }
        function selfIntroduction() {
            console.log(`我是${name},我今年${showAge()}岁了`);
        }
        return { name, showAge, selfIntroduction }
    }
}
</script>
<template></template>

<script>
import { h } from "vue"; // 引入渲染函数 h
export default {
    name: "App",
    setup() {
        // 渲染函数接收 2 个参数:[渲染的标签元素]、[标签元素的内容]
        return () => h("h1", "渲染内容");
    },
};
</script>

注意事项

  • Vue3 兼容 Vue2 的语法,但不推荐混着使用

    • 在 Vue2 配置 (data、methods、computed…) 中,可以获取 Vue3 (setup) 中的数据 (属性、方法)

      但在 Vue3 配置 (setup) 中,无法获取 Vue2 (data、methods、computed…) 中的数据 (属性、方法)

    • 如有重名,setup 优先!

<template>
    <h1>vue2</h1>
    <h2>{{ vue2_name }}</h2>
    <h2>{{ age }}</h2>
    <button @click="vue2_get_data">{{ show_vue2() }}</button>
    <hr>
    <h1>vue3</h1>
    <h2>{{ vue3_name }}</h2>
    <h2>{{ age }}</h2>
    <button @click="vue3_get_data">{{ show_vue3() }}</button>
</template>

<script>
export default {
    name: 'App',
    // Vue2 写法
    data() {
        return {
            vue2_name: "superman",
            age: 18
        }
    },
    methods: {
        show_vue2() {
            return "Vue2";
        },
        vue2_get_data() {
            console.log("vue2_property", this.vue2_name);
            console.log("vue3_property", this.vue3_name);
            console.log("vue2_function", this.show_vue2());
            console.log("vue3_function", this.show_vue3());
        }
    },
    // Vue3 写法
    setup() {
        let vue3_name = "superman"
        let age = 21
        function show_vue3() {
            return "Vue3";
        }
        function vue3_get_data() {
            console.log("vue3_property", vue3_name);
            console.log("vue2_property", this.vue2_name);
            console.log("vue3_function", show_vue3());
            console.log("vue2_function", this.show_vue2());
        }
        return { vue3_name, age, show_vue3, vue3_get_data }
    }
}
</script>
  • setup 不能是一个 async 函数(后期可以配合 Suspense 标签元素使用,此时 setup 可以是 async 函数)
    因为 async 函数的返回值不再是 return 的对象,而是 Promise 实例,模版会找不到 return 对象中的属性
  • setup 中的数据不是 [响应式] 的:即使数据被修改了,页面也不会更新
<template>
    <p>{{ name }}</p>
    <p>{{ showName() }}</p>
    <button @click="changeData">点击修改</button>
</template>

<script>
export default {
    name: "App",
    setup() {
        let name = "superman";
        function showName() {
            return name;
        }
        function changeData() {
            console.log("oldName", name);
            name = "superwoman";
            console.log("newName", name);
        }
        return { name, showName, changeData };
    },
};
</script>

执行顺序

  • setup > beforeCreate,此时的 this === undefined
  • 如果想在 setup 中获取 this,可以通过 [事件] 异步获取
<template>
    <button @click="showThis">异步获取 this</button>
</template>

<script>
export default {
    name: "App",
    beforeCreate() {
        console.log("beforeCreate", this); // beforeCreate Proxy {…} —— (2)
    },
    setup() {
        console.log("setup", this); // setup undefined —— (1)
        function showThis() {
            console.log("showThis", this); // showThis Proxy {showThis: ƒ} —— (点击)
        }
        return { showThis };
    },
};
</script>

setup 的参数

props

props - 接收父组件传递过来的数据
<template>
    <p>Father:{{ faMsg }}</p>
    <button @click="faMsg += '!'">change</button>
    <hr />
    <!-- 设置自定义属性,以传递数据 -->
    <Son :sonMsg="faMsg" />
</template>

<script>
import { ref } from "@vue/reactivity";
import Son from "./components/Son.vue";
export default {
    name: "App",
    components: { Son },
    setup() {
        let faMsg = ref("superman");
        return { faMsg };
    },
};
</script>
<template>
    <p>Son:{{ sonMsg }}</p>
    <button @click="sonMsg += '!'">change</button>
</template>

<script>
export default {
    name: "Son",
    props: ["sonMsg"], // 设置 props 属性,以接收数据
    setup(props) {
        // 参数 props:父组件传递过来的数据
        console.log("props", props); // Proxy {msg: 'superman'}
        console.log("props.sonMsg", props.sonMsg); // superman
    },
};
</script>
  • props 对象是响应式的 Proxy 对象
    就是说,传入新的 props 数据时,会更新子组件接收的数据,模版也会更新
  • 官方称,莫要解构 props 对象,会使其失去响应性(但我试了还是响应式的)
setup({ sonMsg }) {
    console.log("sonMsg", sonMsg); // superman
},

context

context - 上下文对象;有 attrsemitslotsexpose 共 4 个属性
  • attrsemitslots 分别等同于 Vue 实例的 $attrs$emit$slots 属性
  1. attrs 属性:用于 [兜住] 父组件传递给子组件的数据

如果组件没有设置 props 属性接收父组件传递的数据,则 context.attrs 会接收,此时可通过 context.attrs 获取数据
如果组件设置了 props 属性接收父组件传递的数据,则 context.attrs 不会接收,此时可通过 props 获取数据

<template>
    <p>父组件传递过来的数据:{{ msg }}</p>
</template>

<script>
export default {
    name: "Son",
    // 这里没有设置 props 属性接收父组件传递的数据
    setup(props, context) {
        // 参数 props
        console.log("props - ", props); // props -  Proxy {}
        // 参数 context
        console.log("context - ", context); // context -  {expose: ƒ}
        // context 的 attrs 属性
        console.log("context.attrs - ", context.attrs); // context.attrs -  Proxy {sonMsg: 'superman', …}
        return { msg: context.attrs.sonMsg }; // 通过 context.attrs 获取数据
    },
};
</script>
  1. emit:用于 [触发] 自定义事件,实现子组件到父组件的数据传递
<template>
    <p>父组件的数据:{{ msg }}</p>
    <hr />
    <!-- 设置自定义事件,以接收子组件的数据 -->
    <Son @showName="showFa" />
</template>

<script>
import { ref } from "@vue/reactivity";
import Son from "./components/Son.vue";
export default {
    name: "App",
    components: { Son },
    setup() {
        let msg = ref("App");
        function showFa(val) {
            console.log("子组件传递过来的数据", val); // 子组件传递过来的数据 Son
            msg.value += val;
        }
        return { showFa, msg };
    },
};
</script>
<template>
    <button @click="show">点击设置自定义事件传递数据给父组件</button>
</template>

<script>
export default {
    name: "Son",
    setup(_, context) {
        function show() {
            // 触发自定义事件,以传递数据给父组件
            // 格式:context.emit("自定义事件名", 传递的数据)
            context.emit("showName", "Son");
        }
        return { show };
    },
};
</script>
  1. slots:用于获取插槽信息
<template>
    <Son>
        <!-- 设置默认插槽 -->
        <p>默认插槽内容</p>
        <!-- 设置具名插槽 -->
        <template v-slot:mySlot>
            <p>具名插槽内容</p>
        </template>
    </Son>
</template>

<script>
import Son from "./components/Son.vue";
export default {
    name: "App",
    components: { Son },
};
</script>
<template>
    <!-- 使用默认插槽 -->
    <slot>默认插槽的默认内容</slot>
    <!-- 使用具名插槽 -->
    <slot name="mySlot" />
</template>

<script>
export default {
    name: "Son",
    setup(_, context) {
        console.log("context.slots - ", context.slots); // context.slots -  Proxy {…, mySlot: ƒ, default: ƒ}
    },
};
</script>
  1. expose 方法:用于暴露子组件的数据给父组件
<template>
    <p>sonMsg:{{ sonMsg }}</p>
    <button @click="sonFun">sonFun</button> |
    <button @click="sonMsg += '(son)'">changeMsg</button>
</template>

<script>
import { ref } from "@vue/reactivity";
export default {
    name: "About",
    setup(props, context) {
        let sonMsg = ref("子组件数据");
        function sonFun() {
            console.log("子组件方法");
        }
        // 暴露子组件的数据、方法给父组件
        context.expose({
            sonMsg,
            sonFun,
        });
        return { sonMsg, sonFun };
    },
};
</script>
<template>
    <p>faMsg:{{ faMsg }}</p>
    <p>faMsg.sonMsg:{{ faMsg.sonMsg }}</p>
    <button @click="faFun">点击修改子组件数据</button>
    <hr />
    <!-- 给子组件设置 ref 属性,以接收子组件暴露的数据 -->
    <About ref="faMsg" />
</template>

<script>
import { ref } from "@vue/reactivity";
import About from "./views/About.vue";
export default {
    name: "App",
    components: { About },
    setup() {
        // 定义 ref 变量,变量名为子组件设置的 ref 属性值
        let faMsg = ref("父组件数据");
        // 通过事件异步操作子组件传递过来的数据、方法
        function faFun() {
            // 在父组件中修改子组件的数据
            faMsg.value.sonMsg += "(father)";
            // 在父组件调用子组件的方法
            faMsg.value.sonFun();
        }
        return { faMsg, faFun };
    },
};
</script>
  • 不能直接在父组件的 setup 方法中获取 faMsg.value.sonMsg,因为 setup 方法在 beforeCreate 之前执行,此时在 setup 方法内的 this 还是 undefined,元素也尚未挂载,所以尚未能获取 faMsg.value.sonMsg 的值
<template>
    <p>faMsg:{{ faMsg }}</p>
    <p>faMsg.sonMsg:{{ faMsg.sonMsg }}</p>
    <hr />
    <!-- 给子组件设置 ref 属性,以接收子组件传递过来的数据 -->
    <About ref="faMsg" />
</template>

<script>
import { ref } from "@vue/reactivity";
import { onMounted } from "@vue/runtime-core";
import About from "./views/About.vue";
export default {
    name: "App",
    components: { About },
    setup() {
        // 定义 ref 变量,变量名为子组件设置的 ref 属性值
        let faMsg = ref("父组件数据");
        // 无法直接获取
        console.log("直接获取", faMsg.value.sonMsg); // undefined
        // 元素挂载完成之后,才能获取到 ref 对象的数据
        onMounted(() => {
            console.log("onMounted", faMsg.value.sonMsg);
            faMsg.value.sonFun(); // 调用子组件的方法
        });
        return { faMsg };
    },
};
</script>
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JS.Huang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值