Vue3比Vue2有什么优势/区别

优势

  • 性能更好
  • 体积更小
  • 更好的ts支持
  • 更好的代码组织
  • 更好的逻辑抽离
  • 更多新功能

Vue3生命周期

  • Options API 生命周期
  • Composition API 生命周期

Options API 生命周期

  • beforeDestroy 改为 beforeUnmount
  • destroyed 改为 unmount
  • 其他沿用Vue2的生命周期

Composition API 生命周期

  • setup //相当于beforeCreate 和 created
  • onBeforeMount // beforeMount
  • onMount // mount
  • onBeforeUpdate // beforeUpdate
  • onUpdate // update
  • onBeforeUnmount // beforeUnmount
  • onUnmount // unmount

Composition API 带来了什么

  • 更好的代码组织
  • 更好的逻辑复用
  • 更好的类型推到

如何理解ref toRef 和 toRefs

Ref
生成值类型的响应式数据
可用于模板和reactive
通过.value修改值
获取dom元素
import { ref } from 'vue'
export default {
	setup(){
		// 如果是用ref声明,建议在后面加上Ref后缀, 后面修改的时候不会显得很怪异
		const ageRef = ref(20) 
		ageRef.value = 21
	}
}

toRef 和 toRefs
针对一个响应式对象(reactive封装)的prop
创建一个ref,具有响应式
两者保持引用关系
import { ref, reactive, toRefs } from 'vue'
export default {
	setup(){
		const state = reactive({
			age: 20,
			name: 'yoy'
		})
		// const { age,name } = state	直接解构会丢失响应式
		const ageRef = toRef(state,'age')
		const stateAsRefs = toRefs(state)	
		const { age: ageRef, name: nameRef } = stateAsRefs // 每一个属性,都是ref
		return stateAsRefs 
	}
}

ref toRef和toRefs 的最佳使用方式

	// 合成函数返回响应式对象
	import { reactive, toRefs } from 'vue'
	function useFeatureX() {
		const state = reactive({
			x:1,
			y:2	
		})
		return toRefs(state)
	}
	
	export default {
		setup(){
			// 可以在不失去响应式的情况下进行解构
			const {x,y} = useFeatureX()
			return { x, y }
		}
	}
  • 用reactive做对象的响应式,用ref做值类型的响应式
  • setup中返回toRefs(state), 或者toRef(state,‘xx’)
  • 合成函数返回响应式对象时,使用toRefs

为什么需要用ref

  • 返回值类型,会丢失响应式
  • 如在setup, computed, 合成函数, 都有可能返回值类型
  • Vue 不定义ref, 用户将自造ref, 造成混乱

为什么需要.value

  • ref是一个对象(不丢失响应式), value存储值
  • 通过.value属性的get 和 set 实现响应式
  • 用于模板,reactive时,不需要.value, 其他情况都需要
// 错误
function computed(getter) {
	let value = 0 
	setTimeout(()=>{
		value = getter()
	})
	return value
}
// 正确
function computed1(getter) {
	const ref = {
		value: null
	}
	setTimeout(()=>{
		ref.value = getter()
	})
	return ref
}
// 测试

let test = computed(()=> 100) // 0
let test1 = computed(()=> 100) // {value: 100}

为何需要 toRef 和 toRefs

  • 初衷:不丢失响应式的情况下,把对象数据 分解/扩散
  • 前提: 针对的是响应式对象(reactive封装的)非普通对象
  • 注意:不创建 响应式,而是 延续 响应式

Composition API 实现逻辑复用

  • 抽离逻辑代码到一个函数
  • 函数命令约定为useXxxx格式 (React Hooks 也是)

// demo 获取鼠标移动位置
import { ref, onMounted, onUnMounted } from 'vue'

function useMousePosition(){
	const x = ref(0)
	const y = ref(0)
	
	function update (e) {
		x.value = e.pageX
		y.value = e.pageY
	}
	onMounted(()=>{
		window.addEventListener('mousemove', update )
	})
	onMounted(()=>{
		window.removeEventListener('mousemove', update )
	})
	return {
		x,
		y
	}
}
// test
export default {
	setup(){
		const {x, y} = useMousePosition()
		return {
			x,y
		}
	}
}

vue3 和 vue2 响应式对比

vue2使用Object.defineProperty, vue3 使用Proxy

Object.defineProperty的缺点
深度监听需要一次性递归
无法监听新增属性/删除属性(Vue.set Vue.delete)
无法监听原生数组,需要重写覆盖数组方法
Proxy的缺点
Proxy无法兼容所有浏览器,无法polyfill

// Object.defineProperty


const updateView = () => {
	console.log('视图更新了')
}

// 重新定义数组原型
const oldArrayProperty = Array.prototype 
// 创建新对象,原型指向oldArrayProperty,在拓展新方法不会影响原型
const arrProto = Object.create(oldArrayProperty);
['push','pop','shift','unshift','splice'].forEach(methodName => {
	arrProto[methodName] = function () {
		updateView()
		oldArrayProperty[methodName].call(this, ...arguments)	
	}
})


function observer(target) {
	if (typeof target !== 'object' || target == null ) {
		// 不是对象或者数组,则直接返回
		return target
	}	
	// 如果是数组则需要修改监听的对象数组的原型,避免全局污染
	if (Array.isArray(target)) {
		target.__proto__ = arrProto
	}
	for (let key in target) {
		defineReactive(target, key, target[key])
	}
}

function defineReactive(target, key, value){
	// 深度监听
	observer(value)
	Object.defineProperty(target, key, {
		get(){
			return value
		},
		set(newValue){
			if (newValue !== value) {
				// 深度监听
				observer(newValue)
				value = newValue 
				// 更新视图
				updateView()
			}
		}
	})
}

// test

const data = { 
	name: 'zhangsan', 
	age: 20, 
	info:{ adddres: 'guangzhou'},
	nums: [10,20,30]
 }

observer(data)

data.name = 'list' 
data.x  = '100' // 新增属性, 监听不到 -- 所以有Vue.set
delete data.name // 删除属性,监听不到 -- 所以有Vue.delete
data.nums.push(40)

// Proxy

function reactive(target = {}){
	if (typeof target !== 'object' || target == null ) {
		// 不是对象或者数组,则直接返回
		return target
	}
	// 代理配置
	const proxyConfig = {
		get(target, key, receiver){
			// 只处理本身 (非原型的)属性
			const ownKeys = Reflect.ownKeys(target)
			const result = Reflect.get(target, key, receiver)
			// 深度监听
			// 性能如何提升的? -> 这里是在get的时候在进行(递归)深度代理,
			// 而Object.definePropert则需要一次性递归监听
			return reactive(result)
		},
		set(target,key, val, receiver){
			// 重复的数据,不处理
			if (val === target[key]) {
				return true
			}
			const result = Reflect.set(target, key, val, receiver)
			return result
		},
		deleteProperty(target,key){
			const result = Reflect.deleteProperty(target, key)
			return result
		}
	}
	const observe = new Proxy(target, proxyConfig)
	return observe
}

vue3 为何要比 vue2快

  • Proxy响应式
  • PatchFlag
  • hoistStatic
  • cacheHandler
  • SSR优化
  • tree-shaking
PatchFlag
编译模板时,动态节点做标记
标记,分为不同类型,如 TEXT,PROPS
diff算法时,可以区分静态节点,以及不同类型的动态节点
在这里插入图片描述
在这里插入图片描述
如果是纯静态的则不需要比较,是不会变化的,在输入的时候会新增PatchFlag参数,对比的时候在根据PatchFlag参数进行对比
hoistStatic
将静态节点的定义,提升到父作用域,缓存起来
多个相邻的静态节点, 会被合并起来
典型的拿空间换时间的优化策略

在这里插入图片描述
在输入的时候加了-1的标识,标记该节点已经被缓存起来,多个相邻的静态节点, 会被合并起来

cacheHandler
缓存事件
在这里插入图片描述
先去缓存中找cache[0],没有则定义一个函数
SSR
静态节点直接输出,绕过了vdom
动态节点,还是需要动态渲染
在这里插入图片描述
tree shaking
编译时, 根据不同的情况,引入不同的API
在这里插入图片描述
在这里插入图片描述
加入插值表达式,import的参数增多了
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值