vue3 是 vue 的最新版本,它在 vue2 的基础上进行了更新升级,并且引入了 composition API 等新特性。它采用函数式编程方式(hooks),兼容 vue2 的所有写法。
相关更新
- 重写virtual DOM : vue3 采用全新的 virtual DOM 实现,优化了对应的 diff 算法和静态节点的处理,提高渲染性能。
- 响应式系统重构:vue3 使用对应的 proxy 来替代了 vue2 的 object.defineProperty,提高了对应的可维护性和对应的功能性。
- composition Api:vue3引入了composition api(组合式api),替代了vue2的option api(选项式api),避免对应的逻辑代码混乱及代码臃肿,并提供了对应的类型推断。
- 更好的支持 TypeScript: vue3全面支持 typescript,包括自动生成对应的类型和对应的相关类型泛型支持。(vue2 的底层代码是 js,vue3 的底层是 ts)
- 相关组件新增: 传送门 Teleport(它可以将一个组件内部的一部分模板“传送”到该组件的DOM结构外层的位置去,实现父子组件之间的内容传递)。Suspense 用来在组件树中协调对异步依赖的处理。它让我们可以在组件树上层等待下层的多个嵌套异步依赖项解析完成,并可以在等待时渲染一个加载状态。
- 其他的新增内容: 新增了对应的指令,以及指令修饰符等特性
性能提升
- 构建的大小减少了41%
- 初始渲染提高了55%
- 视图更新速度提高了133%
- 内存使用率降低了54%
【面试题: vue2和vue3的区别】!!!
Vue3项目构建
通过vue-cli构建 (cli版本必须大于4)
vue create 项目名
通过vite构建
npm create vite@latest 项目名 -- --template vue
构建的项目目录
main.js
在vue3里面存储的内容全部是属性,如果需要使用解构导入即可
import { createApp } from 'vue'
import App from './App.vue'
//在vue3里面存储的内容全部是属性 如果需要使用就需要解构导入
//工厂方法 createApp根据传入的组件生产对应的app对象
// app对象的方法 mount 挂载
createApp(App).mount('#app')
app对象的组成
Vue3兼容路由(vue-router4)
安装
npm i vue-router -S
书写对应的router文件夹,返回对应的router
import { createRouter, createWebHashHistory } from "vue-router"
let routes = [{
name: 'hello-world',
path: '/',
component: () =>
import ('../components/HelloWorld.vue')
}]
export default createRouter({
routes,
history: createWebHashHistory(),
})
main.js中使用
import { createApp } from 'vue'
// 需要引入router
import router from "./router/index"
//全局css引入
import './style.css'
import App from './App.vue'
// 在vue3中,里面存储的内容全部都是属性,如果需要使用就需要解构导入
//工厂方法:createApp根据传入的组件生产对应的app对象
createApp(App).use(router).mount('#app')
//use可以使用多次,mount必须在use完后调用
composition API(组合式API,按需引入)
- setup:舞台,所有的组合式内容都需要写到setup中
- 响应式数据:ref、reactive
- 监听属性:watch
- 计算属性:computed
- 生命周期钩子函数
- ...
新增内置组件:
- Teleport: 是一个内置组件,它可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去。
- ...
内部使用的方法都需要导入,具体导入操作如下:
import {
ref, reactive, computed, watch, watchEffect, watchPostEffect,
watchSyncEffect,onBeforeUpdate } from 'vue'
setup
- setup 没有 this,为undefined
- 所有的composition api 都只能在 setup 中使用
- setup 返回的数据可以被组件内容调用,但必须要 return 数据或方法
- 返回可以是对象、数组及方法等
- setup 优先,它会覆盖的所有原本内容
setup有两个参数,一个参数为props(接收的数据, 一个参数为context(上下文对象、相当于vue实例)
setup的书写方法一:
- 函数式写法(已过时)
<script>
export default {
name:'HelloWorld',
//传统形式下的setup函数
setup(){
// console.log(arguments) 俩个参数
//第一个为props(传递的数据) 第二个为context(对象的上下文对象)
// console.log(this)
//setup中返回的数据是可以在组件内容使用的
let message = 'hello'
//可以返回函数
const sayHello = ()=>{
console.log('hello')
}
return {
message,
sayHello
}
}
}
</script>
- 语法糖(推荐写法)
<script setup>
const message = 20
const sayHello = ()=>{
console.log('hello')
}
</script>
响应式数据
- ref 一般用于修饰值的响应式(单个值) ,返回 refImpl 对象
- reactive 修饰对象的响应式(多个属性) ,返回 proxy 对象
<script setup>
import { ref, reactive } from 'vue'
// var message = 20 //默认数据不是响应式
var message = ref(20) //更改为响应式数据
const sayHello = () => {
//更改ref对象的数据
console.log(message)
//ref中的值设置
message.value = 10
console.log('hello')
}
//ref可以修饰对象 但是一般用于修饰值 返回的是一个RefImpl对象
//reactive一般用于修饰对象 返回的是一个Proxy对象
var user = reactive({
username:'jack'
})
var handlerUser = ()=>{
//reactive中的值设置
user.username ='tom'
}
</script>
readOnly
- readOnly 是一个函数,它修饰的内容会转换成一个只读响应式对象,主要原理是使用对象冻结。
- 只读代理是深层的,对任何嵌套属性的访问都是只读的。它的ref 解包行为与 reactive() 相同,但解包得到的值是只读的。
//在reactive的基础上进行冻结的操作
let read = readonly({
name:'jack'
})
// console.log(read)
let student = reactive({
name:'张三',
likes:[{
name:'苹果'
}],
city:{
name:'你好'
}
})
let reactiveOnly = readonly(student)
computed
计算属性,返回的是ComputedRefImpl对象,如果需要修改不能直接修改,得修改其中的value值
<template>
{{ user.name }}
<button @click = "change">改变username</button>
{{ count }}
<button @click = "_count = 12">count点击</button>
</template>
<script setup>
let user = reactive({
name: '',
firstName: '张',
lastName: '小明',
})
user.name = computed({
get() {
return user.firstName+user.lastName
},
set(v){
user.firstName = v[0],
user.lastName = v[1]
}
})
let change = () => {
user.name='刘三'
}
let _count = ref(5)
let count = computed({
get() {
return _count
},
set(v){
_count.value = v
}
})
</script>
watch
- 如果监听 reactive 里面的是第一级数据,那么不需要 deep,它会自动deep
- 如果监听 reactive中 里面的内容非第一次,resource的值要以函数返回的形式传递
- 监听数据必须为为响应式数据,也就是 ref 修饰或者 reactive 修饰的数据
- watch的参数有三个,分别为对应的监听的资源resource、回调函数cb、选项对象options ( immediate,deep )
- 可以同时监听多个资源
// watch的参数有三个,分别为对应的监听的资源、回调函数、选项对象(immediate deep)
//监听ref
watch( _count ,(newValue,oldValue) => {
console.log(newValue,oldValue)
})
//监听reactive
watch( user,(newValue,oldValue) => {
console.log(newValue,oldValue)
},{ deep:true })
//监听对象中的属性,得使用箭头函数
//监听对应得reactive中的属性值
watch( ()=>user.username,(newValue,oldValue) => {
console.log(newValue,oldValue)
})
//同时监听多个
watch( [_count , ()=>user.username],(newValue,oldValue) => {
console.log('同时监听多个')
//获取到的value和oldvalue是一个数组 数组中内容的顺序就是对应的开始监听的顺序
console.log(newValue,oldValue)
})
watchEffect (watchSyncEffect,watchPostEffect)
- 默认会立即执行(immediate:true)
- 它监听里面使用到的数据
- flush监听的回调的调用时机:pre(默认)数据更改完成之前调用 、sync同步执行调用、 post数据更改完成之后调用
- watchPostEffect 对应的options里面flush为post (等待组件渲染完再调用回调)
- watchSyncEffect 对应的options里面flush为sync (数据更改立即调用回调)
//数据更新前调用
watchEffect(() => {
console.log('watchEffect调用了' , _count);
},{
// flush?: 'pre' | 'post' | 'sync';
//监听的回调的调用时机
//pre 数据更改完成之前 默认
flush:'pre'
//post 数据更改完成之后
//sync 同步执行 (数据更改完成通知马上调用)效率低)
//数据更新完成后调用
watchPostEffect(() => {
console.log('watchPostEffect调用了' , _count);
})
//数据更新同步调用
watchSyncEffect(()=>{
console.log('watchSyncEffect调用了' , _count);
})
生命周期钩子函数
示例
//生命周期钩子
onBeforeMount(function(){
console.log('挂载之前')
})
onMounted(()=>{
console.log('挂载完成')
})
onBeforeUpdate(()=>{
console.log('修改之前')
})
onUpdated(()=>{
console.log('修改完成')
})
onBeforeUnmount(() => {
console.log('卸载之前')
}),
onUnmounted(()=>{
console.log('卸载完成')
})
keepalive新增了两个钩子函数
- onActivated :组件激活
- onDeactivated :组件离开
自定义hook
- hook 相当于 mixin 的使用,它是为了抽取对应的公共逻辑业务代码作为 hook 形成复用,一般都是对应功能代码
- 造轮子:封装对应的组件及抽取对应的hooks(公共逻辑业务代码),行话就叫造轮子
- 先创建一个hooks文件夹,里面创建一个index.js,在index.js内封装的hook代码
import { reactive } from "vue"
// 自定义hook函数
export const handlerUpdate = () => {
console.log('修改了hook封装的username');
}
class User {
constructor(username) {
this.username = username
}
}
export const defineUser = (username) => {
let user = new User(username)
return reactive(user)
}
// 自己封装一个readOnly
export const myReadOnly = (target) => {
if (typeof target == 'object' && target) {
Object.freeze(target)
} else {
throw new Error('传参错误 ')
}
return reactive(target)
}
- 在组建中调用
<template>
<!-- hook封装测试 -->
{{newUser.username}}
<button @click = "newUser.username = '巴拉巴拉'" >defineUser点击</button>
<!-- 自己封装的readOnly测试 -->
{{myRead.age}}
<button @click = "myRead.age = 20" >readOnly点击</button>
</template>
<script setup>
// 导入自定义的hook
import { defineUser,handlerUpdate,myReadOnly } from '../hooks/index'
onBeforeUpdate(handlerUpdate)
const newUser = defineUser('啦啦啦啦')
//readOnly自己封装的调用
const myRead = {
age:15
}
myReadOnly(myRead)
</script>