+ Vue2中的虚拟DOM是进行全量对比
+ Vue3新增了静态标记【PatchFlag】
说明:
与上次虚拟节点对比,只对比带有静态标记的节点
并且通过flag的信息能得知当前要对比的内容
<div>
<p>这是一个例子p>
<p>{{msg}}p>
div>
+ vue2中无论元素是否参与更新,每次都会重新创建,然后渲染
+ vue3中对于不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用即可
+ 默认情况下onClick事件会被视为动态绑定,所以每次都会追踪他的变化,但是因为是同一个函数,所以没有追踪变化,直接缓存起来复用
服务端渲染
vue作者试图取代webpack的一个工具
1. 安装
npm install -g create-vite-app
2. 创建项目
create-vite-app projectName
3. 目录下创建好了项目
cd projectName
npm install
npm run dev
import { ref, reactive } from 'vue'
setup(){
let varA = ref(0)
function funA() {
console.log('我是函数')
}
return { varA, funA }
}
setup() 函数是一个组合api,在执行组合函数时,会将varA自动注入到data中,会将funA自动注入到methods中。
注意:ref 可以监听基础数据类型数据,reactive 可以监听引用数据类型
beforeCreate():表示组建刚刚被创建出来。组建的data和methods还么有被创建好
setup():
Created():表示组建刚刚被创建出来。组建的data和methods已经被初始化好了
setup()
reactive是vue3提供的一个响应式数据方法
在vue2中通过defindProperty来实现的
在vue3中响应式数据是通过ES6的Proxy实现的
reactive注意点:
ref注意点:
如果在template里使用的是ref类型的数据,vue会自动帮我们添加.value
如果在template里使用的是reactvie类型的数据,vue不会自动帮我们添加.value
vue是如何决定是否需要自动添加.value的?
vue在解析数据之前,会自动判断这个数据是否是ref类型的,如果是ref类型的数据就自动添加.value,如果不是就不自动添加.vlaue
通过当前数据的 __v_ref来判断的
如果有__v_ref这个私有属性的hauler,并且值为true,那么代表就是一个ref类型的数据
__v_ref为私有属性,所以vue3提供了isRef(),isReactive()函数来判断当前变量是什么类型的数据。
import { ref, reactive, isRef, isReactive } from 'vue'
let age = ref(12)
let state = reactive({
name: 'bob'
})
isRef(age); //true
isRef(state);//false
isReactive(age);//false
isReactive(state);//true
import { ref, reactive, isRef, isReactive } from 'vue'
let obj1 = ref({
a:"a",
grandfather:{
b:"b",
father:{
c:"c",
son:{
d:"d"
}
}
}
})
let obj2 = reactive({
a:"a",
grandfather:{
b:"b",
father:{
c:"c",
son:{
d:"d"
}
}
}
})
上面情况默认使用的是递归监听,打印其本身,ref第一层是一个ref对下你给,reactive的每一层都是一个Proxy对象,只要改变某一个值,都会更新到页面上
import { shallowRef, shallowReactive } from 'vue'
非递归监听就是,视图只能监听到数据第一层变化,而监听不到更深层的变化。这两个函数创建出来变量是非递归监听的,只为某个对象的私有(第一层)属性创建浅层的响应式代理,不会对“属性的属性”做深层次、递归地响应式代理,而只是保留原样。
注意:
如果通过shallowRef创建数据,那么vue监听的是.value的变化,并不是第一层的变化
import { triggerRef } from 'vue'
使用triggerRef(obj1)方法则可对shallowRef方法类型的数据做到页面更新
注意:
vue3只提供了triggerRef的方法,没有提供triggerReactive方法
所以如果是reactive类型的数据,那么是无法主动触发界面更新
import { shallowRef, shallowReactive } from 'vue'
import { triggerRef } from 'vue'
setup() {
let person = shallowRef({
a: 'a',
gf: {
b: 'b',
f: {
c: 'c',
s: {
d: 'd'
}
}
}
})
function change() {
person.value.a = 1 //因为是shallowRef所以需要修改.vlaue属性
person.value.gf.b = 2
person.value.gf.f.c = 3
person.value.gf.f.s.d = 4
triggerRef(person) // 使用该方法,页面才可以监听变化
}
return { change,person }
}
上面代码若不调用triggerRef函数,也可以整体给person.value的值更新,也可更新视图
person.value = {
a: '1',
gf: {
b: '2',
f: {
c: '3',
s: {
d: '4'
}
}
}
}
setup() {
let obj = shallowReactive({
a: 'a',
gf: {
b: 'b',
f: {
c: 'c',
s: {
d: 'd'
}
}
}
})
function change() {
console.log(obj)
obj.a = 1 //此时如果注释掉第一层变量修改值,则页面无法监听数据变化,若一层数据也会修改,则整体数据就会重新渲染
// obj.gf.b = 2
// obj.gf.f.c = 3
obj.gf.f.s.d = 4
}
return { obj, change}
}
一般情况下我们使用 ref 和 reactive即可,只有我们在需要监听的数据量比较大的时候,我们才使用shallowRef/shallowReactive提供监听
ref -> reactive
ref(10) -> reactive({value:10})
shallowRef -> shallowReactive
shallowRef(10) -> shallowReactive({value:10})
所以如果是通过shallowRef创建的数据,他监听的是.value的变化
因为底层本质上vlaue才是第一层
import { reactive } from 'vue'
setup() {
let obj = {
name:'张三',
age: 18
}
let state = reactive(obj)
console.log(obj === state) //false
/*
state 和 obj 的关系
引用关系,state的本质是一个Proxy对象,在这个Proxy对象中引 用了obj
*/
let changeData = function() {
obj.name = 'lisi'
// 修改了obj对象中一个属性,obj和state引用中都已修改,但是视图数据不变
// 若想视图变化,需要修改包装后的对象。才能触发视图的更新
state.obj.name = 'wangwu'
}
return {state,changeData}
}
toRaw()函数是 获取ref和reactive类型数据的原始数据
ref/reactive类型数据的特点:
每次修改都会被追踪,都会更新UI界面,但是这样其实是非常消耗性能的,所以如果我们有一些操作不需要追踪,不需要更新UI界面,那么这时候,就可以通过toRaw方法拿到他的原始数据,对原始数据进行修改,这样就不会被追踪,也不会更新UI界面,从而来优化性能
import { reactive, toRaw } from 'vue'
setup() {
let obj = {
name:'张三',
age: 18
}
let state = reactive(obj)
let obj2 = toRaw(state)
console.log(obj === obj2) //true
let changeData = function() {
// 此时调用修改数据方法,则页面ui不会更新,但数据已经修改
obj2.name = 'lisi'
}
return {state,changeData}
}
ref 的本质: reactive
ref(obj) -> reactive({value:obj})
注意:
如果想通过toRaw拿到ref类型的原始数据(创建时传入的按那个数据),那么就必须明确的告诉toRaw方法,要获取的是.value的值,因为经过Vue处理后,.value中保存的才是当初创建时传入的那个原始数据
import { ref, toRaw } from 'vue'
setup() {
let obj = {
name:'张三',
age: 18
}
let state = ref(obj)
let obj2 = toRaw(state)
console.log(obj)
console.log(state)
console.log(obj2) //此时obj2 并不是{name:'张三',age:18}
let obj3 = toRaw(state.value)
console.log(obj3) // {name:'张三',age:18}
return {state}
}
对数据永远不追踪,数据如何修改都不会响应到UI上
import {reactive,markRow} from 'vue'
setup() {
let obj = {
name: 'zhangsan',
age:18
}
obj = markRow(obj)
let state = reactive(obj)
function changeFn() {
// 此时执行函数无法修改数据
state.name = 'lisi'
}
return { state, changeFn }
}
import { ref } from 'vue'
setup() {
let obj = { name:'zhangsan' }
/*
ref(obj.name) => ref('zhangsan') => reactive({value:'zhangsan'})
ref => 复制
*/
let state = ref(obj.name)
function changeData() {
state.value = 'zs'
/*
使用ref将某一个对象中的属性变成响应式数据,我们修改响应式的数据是不会影响到原始数据的。
*/
}
return { state,changeData }
}
import { ref, toRef } from 'vue'
setup() {
let obj = { name:'zhangsan' }
/*
toRef => 引用
ref与toRef的区别:
ref => 复制,修改响应式数据不会影响原始数据;数据改变,界面就会自动更新
toRef => 引用,修改响应式数据会影响原始数据的改变;数据改变,界面不会更新
toRef应用场景:
如果想让响应式数据和以前的数据关联起来,并且更新响应式数据之后还不想更新UI,就可食用toRef
*/
let state = toRef(obj,'name')
console.log(state)
function changeData() {
state.value = 'zs' // 会修改到 obj 的 name 属性
/*
使用toRef将某一个对象中的属性变成响应式数据,我们修改响应式的数据是会影响到原始数据的。
但是如果响应式的数据是通过toRef创建的,那么修改数据并不会触发界面UI的更新
*/
}
return { state,changeData }
}
需求:若想讲一个对象中若干属性都改为响应式属性
import { toRef } from 'vue'
setup() {
let obj = { name:'zhangsan', age:18 }
let name = toRef(obj,'name')
let age = toRef(obj,'age')
function changeData() {
name.value = 'zs'
age.value = 20
}
return { name, age, changeData }
}
import { toRefs } from 'vue'
setup() {
let obj = { name:'zhangsan', age:18 }
let state = toRefs(obj)
function changeData() {
state.name.value = 'zs'
state.age.value = 20
}
return { state, changeData }
}
自定义Ref函数:返回一个ref对象,可以显示地控制以来追踪和触发响应式,定义customRef时,我们要在获取值时告诉vue数据要追踪变化,使用track()方法,在设置值时告诉vue要触发页面更新,使用trigger()
customRef((track,trigger) => {
return {
// 获取值
get() {
track(); // 告诉vue这个数据是需要追踪变化的
cosnole.log('get',value)
return value
},
// 设置值
set(newValue) {
cosnole.log('newValue',newValue)
value = newValue
trigger(); // 告诉vue要触发页面更新
}
}
})
import { ref,customRef } from 'vue'
function myRef(value) {
return customRef((track,trigger) => {
return {
// 获取值
get() {
track(); // 告诉vue这个数据是需要追踪变化的
cosnole.log('get',value)
return value
},
// 设置值
set(newValue) {
cosnole.log('newValue',newValue)
value = newValue
trigger(); // 告诉vue要触发页面更新
}
}
})
}
export default{
name: 'App',
setup() {
// let age = ref(18)
let age = myRef(18)
function myFn() {
age.value ++
}
return {age,myFn}
}
}
若在组合函数setup中获取,该函数的执行顺序在created之前,元素还没有创建,所以无法获取到元素;但setup中也可以监听钩子函数:
<template>
<div ref="box">我是divdiv>
template>
/*
beforeCreate
setup
created
*/
import { ref, onMounted } from 'vue'
setup() {
let box = ref(null); // reactive({value:null})
// 会执行生命周期函数
onMounted(() => {
console.log('onMounted',box.value) // 我是div
})
consooe.log(box.value); //null
return { box }
}
readonly 创建只读数据,并且是递归只读,所有属性都不可修改
shallowReadonly 创建只读数据,不是递归只读,只对首层属性只读
isReadonly 判断是否为只读数据
import { readonly, isReadonly, shallowReadonly } from 'vue'
export default {
name: 'test',
setup() {
let person = readonly({ name: 'zhangsan', attr: { age: 13, height: 188 } })
let women = shallowReadonly({ name: 'Alice', attr: { age: 20, height: 165 } })
function btnFn() {
console.log('person1', person) // 原样输出
person.name = 'lisi' // 报出警告,说该属性是只读属性无法修改
person.attr.age = 20 // 报出警告,说该属性是只读属性无法修改
console.log('person2', person) // 原样输出
console.log('women1', women) // 原样输出
women.name = 'lisi' // 报出警告,说该属性是只读属性无法修改
women.attr.age = 30 // 修改生效,但UI界面没有更新
console.log('women2', women) // 原样输出
console.log("person", isReadonly(person)) // true
console.log("women", isReadonly(women)) // true
}
return { person, women, btnFn }
}
}
const:赋值保护,不能给变量重新赋值
readonly: 属性保护,不能给属性重新赋值
let { readonly } from 'vue'
setup() {
const man = { name: 'Bob', age: 13 };
let woman = readonly({ name: 'Alice', age: 12 })
function changeVal() {
man.age = 14 // man的age属性修改
woman.age = 15 //报出警告,属性为只读,不可修改
man = "123" // 报错,无法修改
woman = "123" // woman被修改为123
}
return { man, woman, changeVal }
}
通过Proxy方法封装了数据,来设置监听,在给变量赋值时会走到Proxy方法重的set方法,在set方法中修改完值去更新UI界面
let obj = { name: 'Bob', age: 18 }
let state = new Proxy(obj,handler: {
get(obj,key) {
console.log(obj,key) // { name: 'Bob', age: 18 } name
return obj[key]
}
set(obj, key, value) {
console.log(obj,key,value) // { name: 'Bob', age: 18 } name 鲍勃
obj[key] = value
console.log('更新UI界面')
}
})
console.log(state.name)
state.name = "鲍勃"
set方法必须通过返回值告诉Proxy此次操作是否成功
let arr = [1,3,5]
let state = new Proxy(arr,handler: {
get(obj,key) {
console.log(obj,key) // [1,3,5] 1
return obj[key]
}
set(obj, key, value) {
console.log(obj,key,value)
// [1,3,5] 3 7 先将数组最后插入一个7
// [1,3,5,7] length 4 再将数组长度改为4
obj[key] = value
console.log('更新UI界面')
return true // 这个是一个返回值,告诉本次操作成功,才能执行再一次操作,因为修改数组时,第一步修改数组数据,第二步修改数组长度
}
})
console.log(state[1]) // 3
state.push(7) // [1,3,5,7]
shallowReactive
function shllowReactive(obj) {
return new Proxy(obj, {
get(obj, key) {
return obj[key]
},
set(obj, key, value) {
obj[key] = value
console.log('更新UI界面')
return true
}
})
}
let obj = {
a: "a",
gf: {
b: "b",
f: {
c: "c",
s: {
d: "d"
}
}
}
}
let test = shllowReactive(obj)
test.a = "1" // 会更新ui界面,若不修改第一层,则不会输出“更新UI界面”
shallowRef
function shallowRef(val) {
shllowReactive({ value: val })
}
function shllowReactive(obj) {
return new Proxy(obj, {
get(obj, key) {
return obj[key]
},
set(obj, key, value) {
obj[key] = value
console.log('更新UI界面')
return true
}
})
}
let obj = {
a: "a",
gf: {
b: "b",
f: {
c: "c",
s: {
d: "d"
}
}
}
}
let test = shallowRef(obj)
test.value = {
a: "1",
gf: {
b: "2",
f: {
c: "3",
s: {
d: "4"
}
}
}
}
function ref(val) {
reactive({ value: val })
}
function reactive(obj) {
// 递归判断属性是否为对象,如果为对象则将对象继续创建为Proxy
if (typeof obj === 'object') {
if (obj instanceof Array) {
obj.forEach((item, index) => {
if (typeof item === 'object')
obj[index] = reactive(obj[index])
})
} else {
for (let key in obj) {
if (typeof obj[key] === 'object')
obj[key] = reactive(obj[key])
}
}
}
return new Proxy(obj, {
get(obj, key) {
return obj[key]
},
set(obj, key, value) {
obj[key] = value
console.log('更新UI界面')
return true
}
})
}
let obj = {
a: "a",
gf: {
b: "b",
f: {
c: "c",
s: {
d: "d"
}
}
}
}
let test = reactive(obj)
function btnFn() {
test.a = "1"
test.gf.b = "2"
test.gf.f.c = "3"
test.gf.f.s.d = "4"
console.log('change test', test)
}
let arr = [{ name: 'bob', attr: { sex: 'boy', age: 12 } }, { name: 'alice', attr: { age: 16 } }]
let arrReactive = reactive(arr)
function changeArr() {
arr[0].name = "特朗普"
arr[0].sex = "1"
arr[1].age = "89"
}
let state = ref(123)
function changeRef() {
console.log(state)
state.value = 345
}