vue3学习心得

vue3学习心得_第1张图片
关注公众号可获得更多干货

一、vue3是如何变快的

1、 diff 算法
+ Vue2中的虚拟DOM是进行全量对比
+ Vue3新增了静态标记【PatchFlag】

说明:
    
与上次虚拟节点对比,只对比带有静态标记的节点
并且通过flag的信息能得知当前要对比的内容
<div>
	<p>这是一个例子p>
	<p>{{msg}}p>
div>

vue3学习心得_第2张图片


2、 hoist static 静态提升
+ vue2中无论元素是否参与更新,每次都会重新创建,然后渲染
+ vue3中对于不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用即可
3、 事件侦听器缓存
+ 默认情况下onClick事件会被视为动态绑定,所以每次都会追踪他的变化,但是因为是同一个函数,所以没有追踪变化,直接缓存起来复用
4、 ssr渲染
服务端渲染

二、Vite

vue作者试图取代webpack的一个工具
1、使用方式:
1. 安装
    npm install -g create-vite-app
2. 创建项目
    create-vite-app projectName
3. 目录下创建好了项目
    cd projectName
    npm install
    npm run dev

三、 组合api

1、setup函数
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 可以监听引用数据类型

1.1、 执行时间
beforeCreate():表示组建刚刚被创建出来。组建的data和methods还么有被创建好
setup()Created():表示组建刚刚被创建出来。组建的data和methods已经被初始化好了

setup()

  • 由于在执行setup函数的时候,还没有执行到Created方法 ,所以还不能使用 data 和 methods 的方法
  • this 为 undefined【因为vue避免我们错误使用,所以将this置为undefined】
  • setup函数必须是同步的不能是异步的
1.2、 什么是reactive?
 reactive是vue3提供的一个响应式数据方法
 在vue2中通过defindProperty来实现的
 在vue3中响应式数据是通过ES6的Proxy实现的

reactive注意点:

  • 值必须是一个对象(json/arr)
  • 如果没有给reactive传递对象,默认情况下给对象修改值,是无法作出页面响应,如果想反映到页面上,则需要给对象重新复值。
1.3、 什么是ref?
  • ref和reactive一样, 也是用来显示响应式数据的方法
  • 犹豫reactive必须传递一个对象,所以导致在企业开发中如果我们只想让某一个变量实现响应式的时候就会非常麻烦,所以vue3就给我们提供了ref方法,实现对简单值的监听
1.4、 ref本质:
  • 底层的本质其实还是reactive,系统会自动根据我们传入的ref值将他转换成
    ref(10) ===> reactive({value:10})

ref注意点:

  • 在vue中的template中使用ref的值不用通过value获取
  • 在js中使用ref的值必须通过value获取
1.5、ref和reactive的区别
  • 如果在template里使用的是ref类型的数据,vue会自动帮我们添加.value

  • 如果在template里使用的是reactvie类型的数据,vue不会自动帮我们添加.value

     vue是如何决定是否需要自动添加.value的?
      
     vue在解析数据之前,会自动判断这个数据是否是ref类型的,如果是ref类型的数据就自动添加.value,如果不是就不自动添加.vlaue
    
1.6、vue是如何判断当前数据是否为ref类型的

通过当前数据的 __v_ref来判断的
如果有__v_ref这个私有属性的hauler,并且值为true,那么代表就是一个ref类型的数据
vue3学习心得_第3张图片

1.7、vue中给我们提供了isRef(),isReactive()函数

__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

四、 递归监听 与 非递归监听

4.1、存在的问题:如果数据量比较大,非常消耗性能
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对象,只要改变某一个值,都会更新到页面上

4.2、非递归监听
	import { shallowRef, shallowReactive } from 'vue'

非递归监听就是,视图只能监听到数据第一层变化,而监听不到更深层的变化。这两个函数创建出来变量是非递归监听的,只为某个对象的私有(第一层)属性创建浅层的响应式代理,不会对“属性的属性”做深层次、递归地响应式代理,而只是保留原样。

注意:
 如果通过shallowRef创建数据,那么vue监听的是.value的变化,并不是第一层的变化
import { triggerRef } from 'vue'

使用triggerRef(obj1)方法则可对shallowRef方法类型的数据做到页面更新

注意:
vue3只提供了triggerRef的方法,没有提供triggerReactive方法
所以如果是reactive类型的数据,那么是无法主动触发界面更新

vue3学习心得_第4张图片

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}
    }
4.3、 应用场景

一般情况下我们使用 ref 和 reactive即可,只有我们在需要监听的数据量比较大的时候,我们才使用shallowRef/shallowReactive提供监听

4.4、 shallowRef底层原理
ref -> reactive
ref(10) -> reactive({value:10})
shallowRef -> shallowReactive
shallowRef(10) -> shallowReactive({value:10})

所以如果是通过shallowRef创建的数据,他监听的是.value的变化
因为底层本质上vlaue才是第一层

五、 toRaw()

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}
}

六、 markRaw()

对数据永远不追踪,数据如何修改都不会响应到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 }
}

七、 toRef(),toRefs()

toRef()
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 }
}
toRefs()

需求:若想讲一个对象中若干属性都改为响应式属性

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 }
}
customRef

自定义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}
	}
}

八、ref补充

如何获取页面元素?

若在组合函数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

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 }
    }
}
9.1和const 区别

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 }
	
}

十、Vue3 响应式数据本质

通过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, shallowRef

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"
            }
        }
    }
}

手写reactive, ref

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
}

vue3学习心得_第5张图片
关注公众号可获得更多干货

你可能感兴趣的:(web前端,vue,js)