初学vue3时应该注意的几个问题
声明响应式
响应式数据的声明在vue2
的时候很简单,在data
中声明就行了。但现在可以使用多个方式。
reactive
用于声明Object, Array, Map, Set
;
ref
用于声明String, Number, Boolean
使用reactive
来声明基础数据类型(String, Number, Boolean
)将导致警告,并且该值不会变为响应式。
<script setup>
import { reactive } from "vue";
const count = reactive(0);
</script>
但是反过来,使用ref
来声明复杂数据类型(Object, Array, Map, Set
等)是生效的,因为ref
内部对于复杂数据类型会调用reactive
来声明。
解构reactive值
假设我们有一个带有计数器和增加计数器的按钮的响应式对象。
<template>
Counter: {{ state.count }}
<button @click="add">Increase</button>
</template>
<script>
import { reactive } from "vue";
export default {
setup() {
const state = reactive({ count: 0 });
function add() {
state.count++;
}
return {
state,
add,
};
},
};
</script>
当使用es6
进行解构时就会出问题。
<template>
<div>Counter: {{ count }}</div>
<button @click="add">Increase</button>
</template>
<script>
import { reactive } from "vue";
export default {
setup() {
const state = reactive({ count: 0 });
function add() {
state.count++;
}
return {
...state,
add,
};
},
};
</script>
代码看起来是一样的,根据我们之前的经验,应该是没问题的,但事实上,Vue
的反应性跟踪是通过属性访问来工作的。这意味着我们无法分配或解构响应式对象,因为与第一个引用的反应性连接丢失了。这是使用响应式的限制之一。
.value
ref
接受一个值并返回一个响应式对象。可以通过.value
来访问对象。
const count = ref(0)
console.log(count) // { value: 0 }
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
refs
在模板中使用时会被解开,不需要写.value
.
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
count.value++
}
</script>
<template>
<button @click="increment">
{{ count }}
<!--不需要.value -->
</button>
</template>
不过要小心!只有顶级属性才能通过.
访问。下面的代码片段将产生[object Object]
<script setup>
import { ref } from 'vue'
const object = { foo: ref(1) }
</script>
<template>
{{ object.foo + 1 }} // [object Object]
</template>
emit事件
在vue
中,子组件可以通过$emit
来与父组件进行通信,只需要添加一个自定义侦听器来侦听事件。
父组件:
<my-component @my-event="doSomething" />
子组件
this.$emit('my-event')
现在emit
事件需要使用defineEmits
来声明。
<script setup>
const emit = defineEmits(['my-event'])
emit('my-event')
</script>
另一件要记住的事情是,defineEmits
或者(defineProps
用于声明 props
)在script setup.
都不需要导入。使用时它们会自动可用。
<script setup>
const props = defineProps({
foo: String
})
const emit = defineEmits(['change', 'delete'])
// setup code
</script>
.native
修饰符已被移除
声明附加选项
选项式 API 方法中的一些属性在script setup
并不被支持:
- name
- inheritAttrs
- 插件或库所需的自定义选项
解决方案是在script setup RFC中定义的同一组件中使用 2 个不同的script
标签。
<script>
export default {
name: 'CustomName',
inheritAttrs: false,
customOptions: {}
}
</script>
<script setup>
// script setup logic
</script>
定义异步组件
异步组件以前是通过将它们包含在函数中来声明的
const asyncModal = () => import('./Modal.vue')
从 Vue 3
开始,异步组件需要使用defineAsyncComponent
方法显式定义。
import { defineAsyncComponent } from 'vue'
const asyncModal = defineAsyncComponent(() => import('./Modal.vue'))
在模板中使用不必要的包装元素
Vue 2
中需要组件模板的单个根元素,这有时会引入不必要的包装元素。
<template>
<div>
<header>...</header>
<main>...</main>
<footer>...</footer>
</div>
</template>
而vue3
支持多个根元素
<template>
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
</template>
使用错误的生命周期事件
所有组件生命周期事件都通过添加on
前缀或完全更改名称来重命名。