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
- 上下文对象;有attrs
、emit
、slots
、expose
共 4 个属性
attrs
、emit
、slots
分别等同于 Vue 实例的$attrs
、$emit
和$slots
属性
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>
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>
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>
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>