参考来源,李南江:https://www.bilibili.com/video/BV14k4y117LL
一、创建vue3的三种方式:
- vite
vite是vue作者开发的一个项目打包工具,利用ES6的import会发送请求去加载文件的特性,拦截这些请求,做一些预编译,省去webpack冗长的打包时间。
// 1、安装Vite
npm install -g create-vite-app
// 2、创建vue3.0项目
create-vite-app 项目名称
// 3、切换至项目目录
cd 项目名称
// 4、安装依赖
npm install
// 5、运行项目
npm run dev
- vue-cli
// 1、安装vue-cli
npm install -g @vue/cli
// 2、创建vue项目
vue create 项目名称
// 3、切换至项目目录
cd 项目名称
// 4、添加vue3.0
vue add vue-next
// 5、运行项目
npm ru serve
- webpack
// TODO
二、composition API
(1)、setup()函数:setup()函数是组合api的入口函数
-
通常情况下是将业务处理的数据和事件抽离出去,如下所示:
业务抽离
在第四步中,setup()return出去的方法和数据将会被vue注入data和methods中
1.1、composition API(vue3.0)可以和options API(vue2.x)混合使用。也就是data、computed、methods、setup可以混合使用
1.2、 composition API本质(组合API/注入API):
composition API return出去的数据将会注入到data中,return出去的方法将会注入到methods。
1.3 、setup()执行时机:
setup函数,是在vue生命周期beforecreate钩子之前完成的。
1.4、 setup注意点:
在setup()函数中无法使用data、computed和methods(data、computed和methods是在生命周期created才初始化的)
setup()函数只能是同步的,不能是异步的。???????不理解!!!!
(2)、ref函数和reactive函数
2.1、reactive函数
reactive是vue3中实现响应式数据的方法:
实现原理:vue2中响应式数据是通过defineProperty来实现的;vue3中响应式数据是通过ES6中的proxy来实现的,将传入数据包装成一个Proxy对象。reactive的注意点:
- reactive的参数必须是对象(json/数组)
- 如果reactive传递了其他对象,
+默认情况下修改对象,数据不会自动跟新(数据是单向的)
+如果想更新,可以通过重新赋值的方式
// 当传入对象为json或数组时
<template>
<div>
<div>{{obj}}</div>
<input type="submit" @click="changeObj">
</div>
</template>
<script>
import { reactive } from 'vue' //1.引入reactive函数
export default {
name: 'HelloWorld',
setup () {
// refactive本质上是把传入的数据包装成一个Proxy对象
let obj = reactive({
age: 12 // 2.通过reactive函数传入数据
})
function changeObj () {
obj.age = 666 //3.改变对象数据
}
return { obj, changeObj }
}
}
</script>
// 当传入其他对象时,需要重新赋值来实现响应式数据。
<template>
<div>
<div>{{obj.time}}</div>
<input type="submit" @click="changeObj">
</div>
</template>
<script>
import { reactive } from 'vue'
export default {
name: 'HelloWorld',
setup () {
let obj = reactive({
time: new Date()
})
function changeObj () {
// 直接修改obj.time数据不会自动更新
// obj.time.setDate(obj.time.getDate() + 1)
// 重新赋值
const newTime = new Date(obj.time.getTime())
newTime.setDate(obj.time.getDate() + 1)
obj.time = newTime
}
return { obj, changeObj }
}
}
</script>
2.2、ref函数
- ref 和reactive一样,也是用来实现响应式数据的方法
- ref本质:ref底层还是reactive,vue会自动将ref传入的值转换为普通变量。例如:ref(3)=> reactive({ value: 3 })
- 注意:template中使用ref,不用通过.value获取相应变量的值;但是在js中使用ref就需要通过.value来获取值。
2.3、ref函数和reactive函数的区别
1、如果在template'中使用ref类型的数据,vue会自动帮我们添加.value(obj.key)。
如果在template中使用的是reactive类型的数据,vue不会自动添加obj.key,
需要我们自己手动添加obj.key获取数据。
2、vue是如何判断当前数据是否为ref数据类型:
vue通过私有属性__v_ref来判断的,若值为true则数据为ref类型;反之。
3、vue提供isRef和isReactive方法给我们判断当前数据类型为ref或reactive。
<template>
<div>
<div>{{ age }}</div>
<input type="submit" @click="change">
</div>
</template>
<script>
import { ref, reactive, isRef, isReactive } from 'vue'
export default {
name: 'HelloWorld',
setup () {
let obj = reactive({age: 12})
let age = ref(12)
function change () {
console.log(isRef(age)) //true
console.log(isReactive(age)) //false
}
return { age, obj, change }
}
}
</script>
(3)、toRef()方法
- 3.1
<template>
<div>
<p>{{ students }}</p>
<button @click="change">按钮</button>
</div>
</template>
<script>
import { toRef } from 'vue'
export default {
setup() {
let obj = { name: 'nico' }
let students = toRef(obj, 'name')
function change() {
students.value = 'allen'
console.log(obj)
console.log(students)
}
return {students, change}
}
}
</script>
(4)、递归监听和非递归监听
1、递归监听:默认情况下,无论是通过ref还是reactive都是递归监听。
- // 验证reactive是递归监听
// 验证reactive是递归监听
<template>
<div>
<div>{{ obj.a }}</div>
<div>{{ obj.grandFather.b }}</div>
<div>{{ obj.grandFather.father.c }}</div>
<div>{{ obj.grandFather.father.son.d }}</div>
<input type="submit" @click="change">
</div>
</template>
<script>
import { ref, reactive } from 'vue'
export default {
name: 'HelloWorld',
setup () {
let obj = reactive({
a: 'a',
grandFather: {
b: 'b',
father: {
c: 'c',
son: {
d: 'd'
}
}
}
})
function change () {
obj.a = '1'
obj.grandFather.b = '2'
obj.grandFather.father.c = '3'
obj.grandFather.father.son.d = '4'
}
return { obj, change }
}
}
</script>
// 验证ref是递归监听
<template>
<div>
<div>{{ obj.a }}</div>
<div>{{ obj.grandFather.b }}</div>
<div>{{ obj.grandFather.father.c }}</div>
<div>{{ obj.grandFather.father.son.d }}</div>
<input type="submit" @click="change">
</div>
</template>
<script>
import { ref, reactive } from 'vue'
export default {
name: 'HelloWorld',
setup () {
let obj = ref({
a: 'a',
grandFather: {
b: 'b',
father: {
c: 'c',
son: {
d: 'd'
}
}
}
})
function change () {
// ref类型数据在js中需要通过.value获取变量的值
obj.value.a = '1'
obj.value.grandFather.b = '2'
obj.value.grandFather.father.c = '3'
obj.value.grandFather.father.son.d = '4'
}
return { obj, change }
}
}
</script>
2、递归监听存在的问题:如果数据量比较大,递归监听是非常消耗性能的。
原理: vue3是通过把数据包装成proxy来实现响应式数据的。所以,递归监听是vue3将每一层变量递归取出包装成proxy。
3、非递归监听:只能监听变量的第一层,不能监听其他层。
- 3.1、通过shallowReactive创建非递归监听。
如上图所示,只有第一层的数据被包装成proxy。vue只监听第一层数据的变化,若不修改第一层obj.a的数据,则视图不会发生更新。
- 3.2、通过shallowRef创建Ref类型的非递归监听。
注意:如果是通过shallowRef创建的数据,那么vue3监听的是.value的变化,而不是第一层的变化。
若只修改某一层的数据,可使用tirggerRef()方法。
vue3只提供了triggerRef()方法,
没有提供triggerReactive()方法来出发某一层变量变化触发视图更新。
<template>
<div>
<div>{{ obj.a }}</div>
<div>{{ obj.grandFather.b }}</div>
<div>{{ obj.grandFather.father.c }}</div>
<div>{{ obj.grandFather.father.son.d }}</div>
<input type="submit" @click="change">
</div>
</template>
<script>
// 1.引入triggerRef()方法
import { shallowRef, triggerRef } from 'vue'
export default {
name: 'HelloWorld',
setup () {
let obj = shallowRef({
a: 'a',
grandFather: {
b: 'b',
father: {
c: 'c',
son: {
d: 'd'
}
}
}
})
function change () {
obj.value.grandFather.father.son.d = '4'
// 2.triggerRef方法触发更新视图
triggerRef(obj)
// Ref非递归监听中,vue3监听的是.value的变化,而不是第一层的变化。
console.log(obj.value)
console.log(obj.value.grandFather)
console.log(obj.value.grandFather.father)
console.log(obj.value.grandFather.father.son)
}
return { obj, change }
}
}
</script>
- 3.3、shallowRef和shallowReactive的本质。
- shallowRef本质上是shallowReactive,调用shallowRef其实就是调用shallowReactive
所以shallowRef创建的变量,vue3监听的是 .value的变化,本质上.value才是第一层
- 3.4、应用场景:
一般情况下使用ref和和reactive即可;只有监听数据量比较大的时候才使用shallowRef和shallowReactive。
4.toRaw()方法及其作用
- toRaw用来获取ref和reactive的原始数据,也就是获取ref和reactive的引用。
// reactive数据类型
<template>
<div>
<p>{{ students }}</p>
<button @click="change">按钮</button>
</div>
</template>
<script>
import { reactive, toRaw } from 'vue'
export default {
setup() {
let obj = { name: 'nico', age: '21' }
let students = reactive(obj)
// console.log(obj === students) //false
// 1.students和obj的关系是引用关系;students本质上是一个Proxy对象,这个Proxy对象引用了obj对象
/*
3.ref和reactive数据每次修改都会被监听,都会跟新视图层。
如果我们一些逻辑操作是不需要被监听和更新视图层的,那么操作ref和reactive数据将会带来很大的性能消耗;
在这种情况下,我们可以通过toRaw()方法拿到原始数据进行操作,这样就能提高性能。
* */
let obj2 = toRaw(obj)
console.log(obj2 === obj) // true
function change() {
// 2.直接修改obj是无法触发视图层更新的
// 只有通过修改Proxy包装之后的对象,才会触发视图层更新
// obj.age = '18'
// console.log(obj) // {name: "nico", age: "18"}
// ---
// students.age = '30'
// console.log(students) // Proxy {name: "nico", age: "30"}
}
return {students, change}
}
}
</script>
// ref类型的数据
<template>
<div>
<p>{{ students }}</p>
</div>
</template>
<script>
import { ref, toRaw } from 'vue'
export default {
setup() {
let obj = { name: 'nico', age: '21' }
let students = ref(obj)
//ref类型数据要通过.value来获取
let obj2 = toRaw(students.value)
console.log(obj)
console.log(students)
console.log(obj2)
return {students}
}
}
</script>
5 .markRaw()方法可以将数据设置为非响应式数据
<template>
<div>
<p>{{ students }}</p>
<button @click="change">按钮</button>
</div>
</template>
<script>
import { reactive, markRaw } from 'vue'
export default {
setup() {
let obj = { name: 'nico', age: '21' }
obj = markRaw(obj) //obj将永远不被追踪
let students = reactive(obj)
function change() {
students.name = 'allen'
console.log(students)
}
return {students, change}
}
}
</script>