Skip to content

对大前端的思考

架构师

  • 客户的想法(需求)和执行人(开发)直接有一段距离,这个距离就需要架构师的工作
  • 架构师的工作就是把客户的想法和执行人的想法连接起来,把客户的想法和执行人的想法连接起来
  • 架构师做的就是结构的设计与实现,需要具备解决复杂问题的能力

分析vue的架构

  • 目标: 分析vue的架构,了解vue的架构设计,了解vue的架构设计的目的和意义
  • 实现

目标

  • vue框架的最大的特点是什么
    • 可以实现视图和数据的关联
  • 什么是视图
    • 视图可以算是虚拟dom,也算是一种数据
    • 就是数据和数据的关联
    • 但数据和数据关联不存在,例如excel,求和之后,那个单元格背后其实是一个求和公式
    • 即是数据与计算的关联
    • 我们需要将视图的意思尽量往计算的方向去靠拢
    • 得出结论
      • 创建视图的过程和数据发生了关联
    • 而过程,一般表现为函数或者方法,即创建视图的函数与数据产生了关联
  • 创建视图的方法,我们假设叫他render
    • 希望函数用到的数据发生变化的时候,render函数会重新执行,返回修改后的数据,这样就实现了视图和数据的关联
    • 函数运行过程中用到了哪些数据,就关联哪些数据
    • 与函数运行过程中用到的标记数据产生关联
    • 需要建立对应关系(函数-数据)
  • 总结
    • vue框架的最大的特点可以被描述为: 函数运行过程中用到的标记数据,与函数产生关联,需要建立对应关系(函数-数据)
      • 监听数据的读取和修改
      • 如何知晓数据对应的函数
        • 建立对应关系就是依赖收集

监听数据的读取和修改

有2种方式 这两个东西都要求数据是对象

  • defineProperty
    • 监听的范围很窄,只能监听对象的属性
    • 只能通过属性描述符监听已有属性的描述和修改
    • 优点是兼容性更好
  • proxy
    • 监听的范围很广
    • 语法层面的书写,会转变为内部方法的调用
      • data.a = 1 => Reflect.set(data, 'a', 1)
      • a in data => Reflect.has(data, 'a')
    • 这些内部方法都可以被proxy进行拦截
    • es6的语法,可以使用proxy进行监听,不兼容低版本浏览器
    • 这个被标记的数据在vue里面被称之为响应式数据
    • 响应式数据就是,当这个数据发生改变的时候,重新运行一次这个函数
  • 用简单的例子描述被用到的数据(即需要被响应式的数据)
js
let data1 = true, data2, data3

const fn = ()=> {
  if(data1) {
    data2  // data2是被用到的数据,需要被响应式
  } else {
    data3  // 不用被关联,不需要响应式
  }
}

// 在vue里面,表示一个被标记的响应式数据
let proxyData = tag(data)

const tag = (data) => {
  return new Proxy(data, {
    get(target, key) {
      // 返回对象的属性值
      return target[key]
      // 依赖收集
      // 当数据被读取的时候,需要收集依赖了该数据的函数
      // 即当数据被读取的时候,需要收集使用了这个数据的函数
      // 这个函数就是当前的函数
      track(target, key)
    },
    set(target, key, value) {
      // 设置对象的属性值
      // target[key] = value
      // 建议使用反射赋值,因为它本身会返回一个布尔值
      // 赋值成功返回true,失败返回false
      return Reflect.set(target, key, value)
      // 对应函数重新执行的过程,我们称之为派发更新
      // 需要对哪个对象的哪个属性进行派发更新,就需要知道这个对象的哪个属性
      tigger(target, key)
    }
  })
}

// 在vue3里面,这个tag方法名字叫做reactive方法
// 即
let proxyData = reactive(data)
let data1 = true, data2, data3

const fn = ()=> {
  if(data1) {
    data2  // data2是被用到的数据,需要被响应式
  } else {
    data3  // 不用被关联,不需要响应式
  }
}

// 在vue里面,表示一个被标记的响应式数据
let proxyData = tag(data)

const tag = (data) => {
  return new Proxy(data, {
    get(target, key) {
      // 返回对象的属性值
      return target[key]
      // 依赖收集
      // 当数据被读取的时候,需要收集依赖了该数据的函数
      // 即当数据被读取的时候,需要收集使用了这个数据的函数
      // 这个函数就是当前的函数
      track(target, key)
    },
    set(target, key, value) {
      // 设置对象的属性值
      // target[key] = value
      // 建议使用反射赋值,因为它本身会返回一个布尔值
      // 赋值成功返回true,失败返回false
      return Reflect.set(target, key, value)
      // 对应函数重新执行的过程,我们称之为派发更新
      // 需要对哪个对象的哪个属性进行派发更新,就需要知道这个对象的哪个属性
      tigger(target, key)
    }
  })
}

// 在vue3里面,这个tag方法名字叫做reactive方法
// 即
let proxyData = reactive(data)
  • effect.js
js
// 依赖收集
export const track = (target, key) => {
  
}

// 派发更新
export const tigger = (target, key) => {
  
}
// 依赖收集
export const track = (target, key) => {
  
}

// 派发更新
export const tigger = (target, key) => {
  
}
  • 测试使用
js
import { reactive } from './reactive.js'
const state = reactive({
  a: 1,
  b: 2
})

function fn() {
  state.a
  state.b
}

fn()

state.a ++
import { reactive } from './reactive.js'
const state = reactive({
  a: 1,
  b: 2
})

function fn() {
  state.a
  state.b
}

fn()

state.a ++
  • 上面reactive.js和effect.js两个内容加起来,就是响应式系统的实现
  • 响应式系统的目标就是要实现一个函数以及这一块的数据,他们之间建立对应关系
  • 由于响应式系统的函数,它并不一定是渲染函数,他可以是任何普通函数
  • 所以响应式系统在整个vue的源吗中是相对独立的