还不会 Vue3 ?一篇笔记带你快速入门
点击下方星标本公众号,实用前端技术文章及时了解
前言
这篇文章是我之前学习 Vue3 做的笔记,做笔记一方面让自己更加理解和掌握新的特性这些知识点,另一方面也希望能让大家入门Vue3,自己学到了才是赚到了,结语注明参考资料。
Vue3简介
面临的问题:随着功能的增长,复杂组件的代码变得难以维护,Vue3
就随之而来,TypeScript
使用的越来越多,Vue3
就是 TS
写的所以能够更好的支持 TypeScript
在这里介绍就这么简单
vue2
的绝大多数的特性 在 Vue3
都能使用,毕竟 Vue
是渐进式的
响应式原理进行使用 Proxy
实现,v-model
可以传参了等等新特性
基础工作
使用Vue3
的话,那么必须通过使用构建工具创建一个 Vue3
项目
安装 vue-cli
# npm
npm install -g @vue/cli
# yarn
yarn global add @vue/cli
创建一个项目
使用 create
命令行创建 或者 用 ui
可视化创建
大家用 Vue
都用了这么久,我就不一一说怎么去创建了
# create
vue create 项目名
# 可视化
vue ui
当然也可以选择 vite
,vite
创建的速度比 上面的方法快了一些
npm init vite-app 项目名
cd 项目名
npm install
npm run dev
Vue3入门
Composition API
Vue3
提出了Composition API
在 Vue2.X 我们使用的是 OptionAPI 里面有我们熟悉的 data
、computed
、methods
、watch
...
在 Vue3
中,我们依旧可以使用 OptionAPI当然不建议 和 Vue3
混用
在 Vue2
中,我们实现一个功能得分到不同的地方,把数据放在 data
,computed
方法放在 methods
里面,分开的太散乱了,几个功能还好,几十个上百个,那就有点...
所以 Vue3
提出了 Composition API ,它可以把 一个逻辑的代码都收集在一起
单独写个hook
,然后再引入,这样就不到处分布,显得很乱了
Fragment
在
template
中不再需要一个根元素包裹
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App" />
template>
实际上内部会将多个标签包含在一个Fragment
虚拟元素中
好处: 减少标签层级, 减小内存占用
script
差异
来看看
script
和Vue2
的区别
可以再 script
使用ts
只需 设置lang
即可defineComponent
方法创建一个组件export default
直接导出一个组件
setup
setup
是Composition API
的入口
setup
执行顺序
它在beforeCreate
之前执行一次,beforeCreate
这个钩子 的任务就是初始化,在它之前执行,那么 this
就没有被初始化 this = undefined
这样就不能通过 this
来调用方法 和 获取属性

setup
返回值
setup
返回的是一个对象,这个对象的属性会与组件中 data
函数返回的对象进行合并,返回的方法和 methods
合并,合并之后直接可以在模板中使用,如果有重名的情况,会使用 setup
返回的属性和方法,methods
和 data
能够拿到 setup
中的方法应该进行了合并,反之 setup
不能拿到它们的属性和方法,因为这个时候 this
= undefined

Suspense
组件
setup
使用async
/await
我们需要 setup
返回数据那么它肯定就不能使用 async
修饰,这样返回 promise
是我们不想看见情况,如果我们硬要用 async
修饰,我们就得用的在它的父组件外层需要嵌套一个suspense
(不确定)内置组件,里面放置一些不确定的操作,比如我们就可以把异步组件放入进去
1.子组件
<template>
{{ res }}
template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'Son',
async setup() {
const res = await axios.get('地址')
return {
res,
}
},
})
script>
2.父组件
<template>
<Suspense>
<Son>Son>
Suspense>
template>
setup
参数
setup
(props
,context
)
setup
函数中的第一个参数是 props
。它接收父组件传递的值,是的就是父子组件信息传递的 props
第二个参数是 context
里面包含3个属性 { attrs, slots, emit }
,这三个属性大家看名字就应该知道是什么吧 分别对应 this.$attrs
,this.$slots
,this.$emit
attrs
: 除了props
中的其他属性slots
: 父组件传入插槽内容的对象emit
: 和用于父子组件通信
ref
定义/转为 响应式
在上面 setup
写的数据都不是响应式的,修改了数据,视图并不会更新
在 Vue3
中提供了两种方式定义响应式数据,先来介绍下 ref
导入 ref
方法
import { defineComponent, ref } from 'vue'
你可以先声明一个基本类型变量后再当做 ref
的形参穿进去或者直接在 ref
中传入
setup() {
// 方式一
let number1 = ref(10)
let num = 0
// 方式二
let number2 = ref(num)
return {}
},
来查看一下 number1
是什么吧

可以看见的是 number1
是一个 Ref
对象,我们设置的 10
这个值在这个对象的 value
属性上
也就是说我们修改的时候必须要修改的是 number1.value
通过给value
属性添加 getter
/setter
来实现对数据的劫持
但是在模板上使用的时候 不用写 number1.value
直接写 number1
即可
在模板编译的时候回自动加上 value
<template>
{{ number1 }}
<button @click="updateNum">+button>
template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
export default defineComponent({
name: 'Son',
setup() {
let number1 = ref(10)
// 修改 number1
function updateNum() {
number1.value++
}
return {
number1,
updateNum,
}
},
})
script>

使用起来完全没有问题
刚才强调了说 ref
接收 基本类型的数据,那么它可以接收 复杂类型吗,object
类型等,当然可以
给 ref
传入复杂类型,其实它是调用 reactive
来实现的
reactive
下面会提到
ref
获取元素
同样的 ref
还可以用了获取元素
大家在 Vue2.X
中是怎么获取的呢,先在 标签上定义 :ref='XXX'
然后 this.$refs.XXX
来获取
在 Vue3
上获取元素就有些许不同了
1.首先在 模板元素上
ref='XXX'
这里不用v-bind
<template>
<div id="haha" ref="haha">div>
template>
2.在
setup
中
得给 ref
指定类型 HTMLElement
setup() {
let haha = refnull>(null)
console.log(haha)
return {
haha,
}
},
如果在组件中需要使用到 haha
,就必须把 haha
return
出去合并 data
我们来看看打印的是什么

可以看见的是 haha
是个 Ref
对象,value
值就是我们想要获取到的元素
然后我们可以对 haha
这个 DOM
元素进行操作,比如这个
haha.style.fontSize = '20px'
reactive
reactive
接收一个普通对象然后返回该普通对象的响应式代理对象
没错 它的底层就是使用 Proxy
进行代理
简单写个Vue3响应式例子来说下
Proxy
new Proxy(target, handler)
target
:要使用Proxy
包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)handler
:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理p
// 模拟 Vue data
let data = {
msg: '',
age: '',
}
// 模拟 Vue 的一个实例
// Proxy 第一个
let vm = new Proxy(data, {
// get() 获取值
// target 表示需要代理的对象这里指的就是 data
// key 就是对象的 键
get(target, key) {
return target[key]
},
// 设置值
// newValue 是设置的值
set(target, key, newValue) {
// 也先判断下是否和之前的值一样 节省性能
if (target[key] === newValue) return
// 进行设置值
target[key] = newValue
document.querySelector('#app').textContent = target[key]
},
})
reactive
基础用法
导入,当然写的时候,vscode
会自动帮你引入
import { defineComponent, reactive } from 'vue'
简单使用
setup() {
let obj = reactive({
name: '小浪',
age: 21,
})
return {
obj,
}
}
来看看返回的 Proxy
对象吧

数据都在 target
中,
在模板使用直接 {{obj.name}}
即可
修改直接修改 obj[name]
=
‘xxx’
操作代理对象,obj中的数据也会随之变化,同时如果想要在操作数据的时候,界面也要跟着重新更新渲染,那么也是操作代理对象
响应式的数据是深层次的(递归深度响应式)
对于多层嵌套的数据也是响应式的
setup() {
let obj = reactive({
name: '小浪',
age: 21,
phone: {
p_name: '小米',
p_apps: {
app_name: '小米运动',
},
},
})
function upadateName() {
obj.phone.p_apps.app_name = '掘金'
}
console.log(obj)
return {
obj,
upadateName,
}
},

shallowReactive
它是一个简单的 reactive ,只把第一层的对象改为响应式,这里就不多说了
使用
ref
传入对象
setup() {
let obj = ref({
name: '小浪',
age: 21,
})
console.log(obj)
return {
obj,
}
}

实际上是 ref
使用 reactive
来进行操作的
toRefs
这个方法可以把 reactive
响应式对象,转化为 普通对象,普通对象的每个属性都是 Ref
对象,这样的话保证了 reactive
的每个属性还是响应式的,我们还可以把每个属性进行分解使用,这样在组件就不用 obj[属性],代码量减轻了,yyds
setup() {
const user = reactive({
name: '小浪',
age: 21,
})
let userObj = toRefs(user)
console.log(userObj)
return {}
}

可以看见 name
和 age
已经变成了 Ref
对象
我们可以解构 出 name
和 age
单独使用
setup() {
const user = reactive({
name: '小浪',
age: 21,
})
let userObj = toRefs(user)
return {
...userObj,
}
}
toRef
还有一个 toRef
方法,它的作用和 toRefs
差不多,但是它只能把响应式对象/普通对象的某一个属性变为 Ref
对象
可以用来为源响应式对象上的
property
性创建一个ref
。然后可以将ref
传递出去,从而保持对其源property
的响应式连接。
export default {
setup(props) {
useSomeFeature(toRef(props, 'foo'))
}
}
function useSomeFeature(foo: Ref) {
// ...
}
拷贝了一份新的数据值单独操作, 更新时相互不影响
当您要将 prop
的 ref
传递给复合函数时,toRef
很有用
可以从官方文档看出,用于在于组件之前的传递数据 从 props
拿出 'foo'
属性给复合函数,复合函数 useSomeFeature
,接收的参数 foo
为 Ref
类型,刚好可以使用toRef
来进行转化
判断响应式
几个判断是否哪种响应式创建的方法
1.isRef
: 检查一个值是否为一个 ref 对象
let ref1 = ref(1)
console.log(isRef(ref1)) // true
2.isReactive
: 检查一个对象是否是由 reactive
创建的响应式代理
let ref2 = reactive({name: '小浪'})
console.log(isReactive(ref2)) // true
3.isReadonly
: 检查一个对象是否是由 readonly
创建的只读代理
let ref3 = readonly({name: '小浪'})
console.log(isReadonly(ref3)) // true
4.isProxy
: 检查一个对象是否是由 reactive
或者 readonly
方法创建的代理
let ref2 = reactive({name: '小浪'})
console.log(isProxy(ref2)) // true
let ref3 = readonly({name: '小浪'})
console.log(isProxy(ref3)) // true
customRef
上面提到了这么多的 Ref
都是 Vue 帮我们内置的,
我们可以通过 customRef
实现我们自己的 Ref
创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。它需要一个工厂函数,该函数接收
track
和trigger
函数作为参数,并应返回一个带有get
和set
的对象。
官方文档给了一个防抖的例子,我们也写个来看
<h2>Apph2>
<input v-model="keyword"/>
<p>{{keyword}}p>
</template>