本文基于vue3.2,主要写了 vue3新特性和与vue2相比写法用法有差距的地方,包括响应式Api、composition Api、setup语法糖等。和vue2用法相同的部分模板语法、列表渲染、条件判断等,可以查看官网v3.cn.vuejs.org/
vue3亮点
- 比vue快近2倍
- Tree Shaking 按需编译代码
- Composition API 更好的逻辑复用与代码组织
- 更好的类型推导(typeScript支持)
应用入口
createApp()函数创建应用实例,需要在vue中导入函数mount()函数挂载应用根组件
// 参考main.js
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
- 通过链式操作使用插件
import router from './router/index'
import ElementPlus from '@/plugins/element-plus.js';
// ...
createApp(App).use(router).use(ElementPlus).mount('#app')
响应性API
- reactive:对象响应式 接收⼀个普通对象然后返回该普通对象的响应式代理。等同于 vue 2.x 的 Vue.observable()
import { reactive } from 'vue'
const obj = reactive({ count: 0 })
- ref :单值响应式 接受⼀个参数值并返回⼀个响应式Ref 对象。Ref 对象拥有⼀个指向内部值的单⼀属性 value 。
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
在模板中使用可以直接 {{count}},在模板中会自动解包。只有访问嵌套的 ref 时需要在模板中添加 .value 。
- toRefs方法 把⼀个响应式对象转换成普通对象,该普通对象的每个属性都是⼀个Ref。
const state = reactive({
foo: 1,
bar: 2,
})
const stateAsRefs = toRefs(state)
使用ES6结构响应式对象时,解构出来的 property 的响应性都会丢失。这时可以使用toRefs方法防止响应式丢失。
const book = reactive({
author: 'Vue Team',
year: '2020',
title: 'Vue 3 Guide',
description: 'You are reading this book right now ;)',
price: 'free'
})
// let { author, title } = book // 这样解构响应式会丢失
let { author, title } = toRefs(book)
title.value = 'Vue 3 Detailed Guide' // 我们需要使用 .value 作为标题,现在是 ref
console.log(book.title) // 'Vue 3 Detailed Guide'
tips 上面一些API使用时都需要先import导入。
组合式API
vue2中使用选项式API(options Api),vue3使用组合式PAI(composition Api)。组合式API ⼀组低侵⼊式的、函数式的 API,使得我们能够更灵活地「组合」组件的逻辑。避免了选项式API中逻辑代码块反复横跳的问题。
1. setup
一个组件选项,在组件被创建之前,props 被解析之后执行。它是组合式 API 的入口。setup中不可以使用this,也没必要使用。
import { onMounted } from 'vue'
export default {
components: { Temp },
props: {
user: {
type: String,
required: true
}
},
setup(props) {
console.log(props) // { user: '' }
// 生命周期钩子
onMounted(() => {
console.log('Component is mounted!')
})
return {} // 这里返回的任何内容都可以用于组件的其余部分
}
// 组件的“其余部分”
}
vue3.2版本以前一些API、生命周期函数都要写在setup方法中。从vue3.2版本新增了一个setup的语法糖<script setup>,使用这个语法糖就不必写setup方法了 。
2. <script setup>语法糖
相比于普通的 <script> 语法,它具有更多优势:
- 更少的样板内容,更简洁的代码。
- 能够使用纯 Typescript 声明 props 和抛出事件。
- 更好的运行时性能 (其模板会被编译成与其同一作用域的渲染函数,没有任何的中间代理)。
- 更好的 IDE 类型推断性能 (减少语言服务器从代码中抽离类型的工作)。
下面是如何使用它:
- 要使用这个语法,需要将
setupattribute 添加到<script>代码块上:
<script setup>
const msg = 'hello world!';
</script>
里面的代码会被编译成组件 setup() 函数的内容。这意味着与普通的 <script> 只在组件被首次引入的时候执行一次不同,<script setup> 中的代码会在每次组件实例被创建的时候执行。
- 声明的顶层的绑定 (包括变量,函数声明,以及 import 引入的内容) 都能在模板中直接使用:
<template>
<p> {{ msg }} </p>
</template>
- 响应式
<script setup>
const count = ref(0)
function addcount() {
count.value++
}
</script>
- 使用组件
<template>
<Foo />
</template>
<script setup>
import Foo from './components/Foo.vue'
</script>
3. defineProps 和 defineEmits
在 <script setup> 中必须使用 defineProps 和 defineEmits API 来声明 props 和 emits ,它们具备完整的类型推断并且在 <script setup> 中是直接可用的:
子组件
<template>
<div class="box">
<p>我是Foo组件</p>
<button @click="add">{{ num }}</button>
</div>
</template>
<script setup>
const props = defineProps({
num: Number,
})
// console.log(props)
const emits = defineEmits(['add'])
// console.log(emits)
function add() {
emits('add')
}
</script>
父组件
<Foo :num="num" @add="add" />
<script setup>
const num = ref(100)
const add = () => {
num.value++
}
</script>
defineProps和defineEmits都是只在<script setup>中才能使用的编译器宏。$emit等方法可以直接在模板中使用,但是不能直接在<script setup>使用
4. 生命周期
可以通过直接导入 onX 函数来注册生命周期钩子:
<script setup>
import { onMounted, onUpdated } from 'vue'
onMounted(() => {
console.log('Component is mounted! -------------')
})
onUpdated(() => {
console.log('updated! ----------------num', num.value)
})
</script>
选项式 API 的生命周期选项和组合式 API 之间的映射
-> 使用beforeCreatesetup()-> 使用createdsetup()beforeMount->onBeforeMountmounted->onMountedbeforeUpdate->onBeforeUpdateupdated->onUpdatedbeforeUnmount->onBeforeUnmountunmounted->onUnmountederrorCaptured->onErrorCapturedrenderTracked->onRenderTrackedrenderTriggered->onRenderTriggeredactivated->onActivateddeactivated->onDeactivated
计算属性和侦听器
- 计算属性computed 返回一个不可手动更改的ref对象。
const plusOne = computed(() => count.value + 1);
plusOne.value++; // error
- 侦听器watch 帧听单个数据源
// 侦听一个 getter
const state = reactive({ count: 0 })
watch(
() => state.count,
(count, prevCount) => {
/* ... */
}
)
// 直接侦听ref
const count = ref(0)
watch(count, (count, prevCount) => {
/* ... */
})
watch也可以使用数组帧听多个数据源
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {})
- 副作⽤侦听器 watchEffect
⽴即执⾏传⼊的⼀个函数,并收集响应式的依赖,当依赖变更时重新运⾏该函数。
watchEffect(() => console.log(count.value)) // 初始执行打印出 0
本文demo地址 github.com/kongcodes/v…