vue3源码解析

vue3源码解析与前端网络安全

VUE 3 拓展

vue3 对比 vue2

响应式数据

vue2 的响应式数据是通过 Object.defineProperty 进行数据劫持,其存在一些缺点:

  • 必须要预知劫持的 key 是什么,并不能很好的监听到对象属性的添加、删除;
  • 初始化递归遍历整个 data ,导致深层嵌套数据造成性能负担;

vue3 的响应式数据是通过 Proxy 进行数据劫持的,可以很好的规避 Object.defineProperty 带来的缺陷

组合式 Api
  • 代码更利于维护和封装
  • Vue2 中,我们会在一个 vue 文件的 data,methods,computed,watch 中定义属性和方法,共同处理页面逻辑 ,一个功能的实现,代码过于分散
  • vue3 中,代码是根据逻辑功能来组织的,一个功能的所有 api 会放在一起(高内聚,低耦合),提高可读性和可维护性,基于函数组合的 API 更好的重用逻辑代码
Diff 算法优化

首先了解 vue 的 虚拟dom
虚拟DOM就是通过 JS 去生成一个 AST 节点树
vue3源码解析_第1张图片

一个dom节点上的属性是非常多的,所以直接操作DOM是非常浪费性能的,解决方案就是可以通过JS的计算性能去换取操作DOM所消耗的性能,既然逃不开操作DOM,那我们可以尽可能少的去操作DOM;

diff 算法

vue3源码解析_第2张图片
规则一:前序对比
vue3源码解析_第3张图片
规则二:尾序对比
vue3源码解析_第4张图片
规则三:新增对比
vue3源码解析_第5张图片
vue3源码解析_第6张图片
规则四:卸载移除对比
vue3源码解析_第7张图片
规则五:乱序对比
vue3.x 中标记和提升所有的静态节点,diff 的时候只需要对比动态节点内容,尽可能复用标记的静态节点。

更好的 TSX 语法支持
// App.tsx

import {ref} from 'vue'

let v = ref<string>('')

const renderDom = ()=>{
    return (
    <div>
        <input v-model = {v.value} type="text" />
        <div>{v.value}</div>
    </div>
    )
}

export default renderDom
<template>
    <renderDom></renderDom>
</template>

<script setup lang="ts">
import renderDom from './App'
</script>

ref 全家桶

  1. ref 将数据包装成响应式数据;
    vue3源码解析_第8张图片
  2. shallowRef (浅层的ref,一般配合triggerRef强制更新dom);
  3. triggerRef (手动执行与shallowRef关联的副作用,强制更新视图,ref的视图更新也是因为触发了triggerRef)
  4. customRef (自定义ref,场景:可以做一些防抖操作)
  5. vue3源码解析_第9张图片

reative 全家桶

  1. reactive 接受复杂数据类型,返回响应式数据;
    1. 数组的赋值两种方式:push解构的值;
    2. 将数组包装成对象,通过属性赋值;
      vue3源码解析_第10张图片
      泛型约束了类型,不能绑定基本数据类型
      vue3源码解析_第11张图片
      shallowReactive

to 系列全家桶

  1. toRef, 如果想让响应式数据和以前的数据关联起来,并且更新响应式数据之后还不想更新视图,那么就可以使用 toRef ;
    vue3源码解析_第12张图片
    vue3源码解析_第13张图片
  2. toRefs,解构会破坏数据的响应式,通过 toRefs 可以解构出响应式的数据;
    源码解析:其实就是把reactive 对象的每一个属性都变成了ref 对象循环 调用了toRef
    vue3源码解析_第14张图片
  3. toRaw ,将响应式数据中得到原始数据;
    应用场景:
    A-发起请求前对数据的处理,不希望更新视图,可以先把双向绑定的响应式数据转成原始数据,再进行处理发请求;
    B-渲染具有不可变数据源的大列表时,可以跳出响应式的追踪,提高性能;
    vue3源码解析_第15张图片

组件通信 Provide inject

通常,当我们需要从父组件向子组件传递数据时,我们使用 props。想象一下这样的结构:有一些深度嵌套的组件,而深层的子组件只需要父组件的部分内容。在这种情况下,如果仍然将 prop 沿着组件链逐级传递下去,会很麻烦。

场景A:通过路由进行局部组件的刷新,对数据进行操作之后需要手动刷新页面;
场景B:父组件有很多数据需要分发给其子代组件的时候

源码解析:
vue3源码解析_第16张图片

Inject 接收的值是unknown的问题:
vue3源码解析_第17张图片
组件实例化的时候注入 provides 的值
vue3源码解析_第18张图片

自定义Hooks( 区别于vue2的mixins )

钩子主要用来处理复用代码逻辑的封装,在vue2的时候就已经有 Mixins 去做这样的事情了, Mixins 就是将这些多个相同的逻辑抽离出来,各个组件只需要引入 mixins 就能实现一次写代码,多组件受益的效果
缺点是会涉及覆盖问题,同名data、methods、filters覆盖,以及变量来源不明确,不利于阅读,使代码难以维护;

为了解决mixins存在的问题,Vue3.x版本使用了自定义Hooks去做这样的事情。

开源的hooks库(VueUse)

// 定义转换base64的hooks   
import { onMounted } from 'vue'
 
type Options = {
    el:string
}
export default function(options:Options)Promise<{baseUrl:string}>{
    return new Promise((reslove)=>{
        onMounted(()=>{
            let img:HTMLImageElement = document.querySelector(options.el) as HTMLImageElement
            img.onload = () => {
            resolve({
                baseUrl: base64(img)
            })
        }
    })
    
        const base64 = (el:HTMLImageElement) => {
            const canvas = document.createElement('canvas')
            const ctx = canvas.getContext('2d')
            canvas.width = el.width
            canvas.height = el.height
            ctx?.drawImage(el,0,0,canvas.width,canvas.height)
            return canvas.toDataURL('image/png')
        }
    })
  
}


// 使用 hooks
<template>
    <div>
        <img id='img' src='./assets/test.png'>
    </div>
</template>

<script lang="ts" setup>
import useBase64 from './hooks'

useBase64({el: '#img'}).then(res=>{
    window.console.log(res.baseUrl)
})
</script>

你可能感兴趣的:(vue.js,typescript,前端框架)