26 | {{ count }} {{ object.foo }}
27 |
28 |
29 |
45 | ```
46 |
47 | 注意 `setup` 返回的 ref 在模板中会自动解开,不需要写 `.value`。
48 |
49 | - **渲染函数 / JSX 中使用**
50 |
51 | `setup` 也可以返回一个函数,函数中也能使用当前 `setup` 函数作用域中的响应式数据:
52 |
53 | ```js
54 | import { h, ref, reactive } from 'vue'
55 |
56 | export default {
57 | setup() {
58 | const count = ref(0)
59 | const object = reactive({ foo: 'bar' })
60 |
61 | return () => h('div', [count.value, object.foo])
62 | },
63 | }
64 | ```
65 |
66 | - **参数**
67 |
68 | 该函数接收 `props` 作为其第一个参数:
69 |
70 | ```js
71 | export default {
72 | props: {
73 | name: String,
74 | },
75 | setup(props) {
76 | console.log(props.name)
77 | },
78 | }
79 | ```
80 |
81 | 注意 `props` 对象是响应式的,`watchEffect` 或 `watch` 会观察和响应 `props` 的更新:
82 |
83 | ```js
84 | export default {
85 | props: {
86 | name: String,
87 | },
88 | setup(props) {
89 | watchEffect(() => {
90 | console.log(`name is: ` + props.name)
91 | })
92 | },
93 | }
94 | ```
95 |
96 | 然而**不要**解构 `props` 对象,那样会使其失去响应性:
97 |
98 | ```js
99 | export default {
100 | props: {
101 | name: String,
102 | },
103 | setup({ name }) {
104 | watchEffect(() => {
105 | console.log(`name is: ` + name) // Will not be reactive!
106 | })
107 | },
108 | }
109 | ```
110 |
111 | 在开发过程中,`props` 对象对用户空间代码是不可变的(用户代码尝试修改 `props` 时会触发警告)。
112 |
113 | 第二个参数提供了一个上下文对象,从原来 2.x 中 `this` 选择性地暴露了一些 property。
114 |
115 | ```js
116 | const MyComponent = {
117 | setup(props, context) {
118 | context.attrs
119 | context.slots
120 | context.emit
121 | },
122 | }
123 | ```
124 |
125 | `attrs` 和 `slots` 都是内部组件实例上对应项的代理,可以确保在更新后仍然是最新值。所以可以解构,无需担心后面访问到过期的值:
126 |
127 | ```js
128 | const MyComponent = {
129 | setup(props, { attrs }) {
130 | // 一个可能之后回调用的签名
131 | function onClick() {
132 | console.log(attrs.foo) // 一定是最新的引用,没有丢失响应性
133 | }
134 | },
135 | }
136 | ```
137 |
138 | 出于一些原因将 `props` 作为第一个参数,而不是包含在上下文中:
139 |
140 | - 组件使用 `props` 的场景更多,有时候甚至只使用 `props`
141 |
142 | - 将 `props` 独立出来作为第一个参数,可以让 TypeScript 对 `props` 单独做类型推导,不会和上下文中的其他属性相混淆。这也使得 `setup` 、 `render` 和其他使用了 TSX 的函数式组件的签名保持一致。
143 |
144 | - **`this`的用法**
145 |
146 | **`this` 在 `setup()` 中不可用**。由于 `setup()` 在解析 2.x 选项前被调用,`setup()` 中的 `this` 将与 2.x 选项中的 `this` 完全不同。同时在 `setup()` 和 2.x 选项中使用 `this` 时将造成混乱。在 `setup()` 中避免这种情况的另一个原因是:这对于初学者来说,混淆这两种情况的 `this` 是非常常见的错误:
147 |
148 | ```js
149 | setup() {
150 | function onClick() {
151 | this // 这里 `this` 与你期望的不一样!
152 | }
153 | }
154 | ```
155 |
156 | - **类型定义**
157 |
158 | ```ts
159 | interface Data {
160 | [key: string]: unknown
161 | }
162 |
163 | interface SetupContext {
164 | attrs: Data
165 | slots: Slots
166 | emit: (event: string, ...args: unknown[]) => void
167 | }
168 |
169 | function setup(props: Data, context: SetupContext): Data
170 | ```
171 |
172 | ::: tip
173 | 为了获得传递给 `setup()` 参数的类型推断,需要使用 [`defineComponent`](#defineComponent)。
174 | :::
175 |
176 | ## 响应式系统 API
177 |
178 | ### `reactive`
179 |
180 | 接收一个普通对象然后返回该普通对象的响应式代理。等同于 2.x 的 `Vue.observable()`
181 |
182 | ```js
183 | const obj = reactive({ count: 0 })
184 | ```
185 |
186 | 响应式转换是“深层的”:会影响对象内部所有嵌套的属性。基于 ES2015 的 Proxy 实现,返回的代理对象**不等于**原始对象。建议仅使用代理对象而避免依赖原始对象。
187 |
188 | - **类型定义**
189 |
190 | ```ts
191 | function reactive