vue3较vue2的改动点总结
1.响应式系统升级
Vue3.0相比于2.x版本做了响应式系统的升级,在Vue2.x版本中是通过defineProperty去设置响应式,但在3.0中采取了Proxy的方式,这样做有三个优点
- 可以监听动态添加对象的属性
- 可以监听删除的属性
- 可以监听数组的索引以及数组的length属性
2.Composition API
随着Vue组件的增大,组件内代码变得越来越难以理解和维护。其中的一些可以复用的代码很难被抽离出来。同时 Vue2.0还缺少 TS支持。在Vue2中,逻辑概念(功能)被管理在组件中,但是功能和组件并不是一对一关系。一个功能可以被多个组件使用同时一个组件可以有多个功能。在Vue中,一个功能可能需要依赖多个Options(components、props、data、computed、methods及生命周期方法),在 Composition API中提供了 setup 方法。
核心API
- reactive
- ref
- computed
- readonly
- watchEffect
- watch
Fragments
Vue3中不在要求模版的跟节点必须是只能有一个节点。跟节点和和render函数返回的可以是纯文字、数组、单个节点,如果是数组,会自动转化为 Fragments。
Teleport
对标 React Portal。可以做一些关于响应式的设计,如果屏幕宽度比较宽的时候,加入某些元素,屏幕变窄后移除。
Suspense
等待嵌套的异步依赖。再把一个嵌套的组件树渲染到页面上之前,先在内存中进行渲染,并记录所有的存在异步依赖的组件。只有所有的异步依赖全部被resolve之后,才会把整个书渲染到dom中。当你的组件中有一个 async的 setup函数,这个组件可以被看作是一个Async Component,只有当这个组件被Resolve之后,再把整个树渲染出来
- async setup()
- Async Component
Typescript
Vue3源码使用 TS重写,但不意味着vue3的项目也要使用TS。但Vue3会对 TS有更好的支持
- 支持 TSX
- 支持 Class componen
生命周期的变化
- beforeCreate -> 使用 setup()
- created -> 使用 setup()
- beforeMount -> onBeforeMount
- mounted -> onMounted
- beforeUpdate -> onBeforeUpdate
- updated -> onUpdated
- beforeDestroy -> onBeforeUnmount
- destroyed -> onUnmounted
- errorCaptured -> onErrorCaptured
setup
setup 相当于是组件的入口了,可以调用所有组合函数。最后的return,可以作为出口,确认要暴露给模板哪些内容。
setup接收两个参数,props和context
setup(props, context) {
return {}
}
- props:跟 2.x 的 props 一样,接受父组件传过来的值。
- context:是一个上下文对象,包含了一些
2.xthis中的属性。如:
attrs: Object // => this.$attrs
emit: f() // => this.$emit
isServer: false // 是否服务端渲染
listeners: Object // => this.$listeners
parent: VueComponent // => this.$parent
refs: Object // => this.$refs
root: Vue // => main.js 中的全局唯一的 vue 实例
slots: {} // => this.$slots
ssrContext: {} // => 服务端渲染
reactive
对于响应式数据,我们可以通过reactive来创建。响应式转换是基于es6中的proxy实现的,返回的是一个代理后的对象,并不等于原始对象。
<template>
<div class="add">
<h1>{{ state.count }}</h1>
<button @click="addCount">add</button>
</div>
</template>
<script>
import { reactive } from vue'
export default {
setup() {
const state = reactive({
count: 0
})
const addCount = () => {
state.count++
}
return { state, addCount }
}
}
</script>
ref
接受一个内部值,返回一个响应式的、可更改的 ref 对象,此对象只有一个指向其内部值的属性 .value。
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
toRefs
上面的例子中,我们在模板中使用的是state.count这种方式,获取响应式的数据。如果要把{{ state.count }}写成{{ count }},就需要用toRefs了。
import { reactive, toRefs } from 'vue'
export default {
setup() {
const state = reactive({
count: 0
})
return {...toRefs(state)}
}
}
computed
接受一个 getter 函数,返回一个只读的响应式ref对象。该 ref 通过 .value 暴露 getter 函数的返回值。它也可以接受一个带有 get 和 set 函数的对象来创建一个可写的 ref 对象。
创建一个只读的计算属性 ref:
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
plusOne.value++ // 错误
创建一个可写的计算属性 ref:
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: (val) => {
count.value = val - 1
}
})
plusOne.value = 1
console.log(count.value) // 0
watch
watch用来监听一个或多个数据的变化,并在回调中执行副作用。
setup() {
const state = reactive({
count: 0,
msg: 'ha',
})
// 监听一个数据的变化
watch(
() => state.count,
(count, preCount) => {
console.log(count, preCount)
},
)
// 监听多个数据的变化
watch([() => state.count, () => state.msg], ([count, msg], [preCount, preMsg]) => {
console.log(count, msg, preCount, preMsg)
})
const addCount = () => {
state.count++
}
return { ...toRefs(state), addCount }
}
watchEffect
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。
watchEffect(() => {
console.log(state.count)
})
watch 与 watchEffect 的区别
- watchEffect 在组件初始化时,立即执行传入的一个副作用函数。并且在副作用函数中使用的属性有变化时,会重新执行。需要注意,当副作用函数中执行的函数,若该函数又改变了响应式的数据,可能会造成死循环问题。
- watch 是监听指定的属性,当指定属性变化时,才会执行回调。watch 可以接收指定的一个或多个属性。 watch中可以获取状态变化前后的值。
readonly
接受一个对象 (不论是响应式还是普通的) 或是一个 ref,返回一个原值的只读代理。
const original = reactive({ count: 0 })
const copy = readonly(original)
watchEffect(() => {
// 用来做响应性追踪
console.log(copy.count)
})
// 更改源属性会触发其依赖的侦听器
original.count++
// 更改该只读副本将会失败,并会得到一个警告
copy.count++ // warning!
<script setup>语法糖
<script setup>是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。当同时使用 SFC 与组合式 API 时该语法是默认推荐。相比于普通的 <script> 语法,它具有更多优势:
- 更少的样板内容,更简洁的代码。
- 能够使用纯 TypeScript 声明 props 和自定义事件。
- 更好的运行时性能 (其模板会被编译成同一作用域内的渲染函数,避免了渲染上下文代理对象)。
- 更好的 IDE 类型推导性能 (减少了语言服务器从代码中抽取类型的工作)。
顶层的绑定会被暴露给模板
当使用 <script setup> 的时候,任何在 <script setup> 声明的顶层的绑定 (包括变量,函数声明,以及 import 导入的内容) 都能在模板中直接使用:
<script setup>
// 变量
const msg = 'Hello!'
// 函数
function log() {
console.log(msg)
}
</script>
<template>
<button @click="log">{{ msg }}</button>
</template>
import 导入的内容也会以同样的方式暴露。这意味着我们可以在模板表达式中直接使用导入的 helper 函数,而不需要通过 methods 选项来暴露它:
<script setup>
import { capitalize } from './helpers'
</script>
<template>
<div>{{ capitalize('hello') }}</div>
</template>
响应式
响应式状态需要明确使用响应式 API来创建。和 setup() 函数的返回值一样,ref 在模板中使用的时候会自动解包:
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">{{ count }}</button>
</template>
使用组件
<script setup>范围里的值也能被直接作为自定义组件的标签名使用:
<script setup>
import MyComponent from './MyComponent.vue'
</script>
<template>
<MyComponent />
</template>
defineProps()和 defineEmits()
为了在声明 props 和 emits 选项时获得完整的类型推导支持,我们可以使用 defineProps 和 defineEmits API,它们将自动地在 <script setup> 中可用:
<script setup>
const props = defineProps({
foo: String
})
const emit = defineEmits(['change', 'delete'])
// setup 代码
</script>
defineExpose
使用 <script setup> 的组件是默认关闭的——即通过模板引用或者 $parent 链获取到的组件的公开实例,不会暴露任何在 <script setup> 中声明的绑定。
可以通过 defineExpose 编译器宏来显式指定在 <script setup> 组件中要暴露出去的属性:
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
defineExpose({
a,
b
})
</script>
顶层 await
在 <script setup>中可以使用顶层 await。结果代码会被编译成 async setup():
<script setup>
const post = await fetch(`/api/post/1`).then((r) => r.json())
</script>
3.Composition API中 Vue Router 的使用
新建目录src/router,在该目录新建文件index.js
//导入路由方法
import { createRouter, createWebHistory } from 'vue-router'
//导入组件
import Home from '@/views/home'
import About from '@/views/about'
//路由规则
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About }
]
//路由创建
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
组建中使用:
useRoute
返回当前路由地址。相当于在模板中使用 $route。必须在 setup() 中调用。
useRouter
返回 router实例。相当于在模板中使用 $router。必须在 setup() 中调用。
<script setup>
import { useRoute, useRouter } from 'vue-router'
const route = useRoute();
const router = useRouter();
</script>
4.Composition API 中使用vuex
创建index.js
import { createStore } from 'vuex'; //引入vuex
import app from './modules/app'; //引入modules的方法;
import getters from './getters' //引入getters
const store = createStore({
modules: {
app
},
getters
})
export default store; //导出
使用:
<script setup>
import { useStore } from "vuex";
const store = useStore();
</script>
官方推荐在composition API中使用pinia来实现vuex的功能pinia.vuejs.org/introductio…