30-mini-vue 更新 element 的 props

更新 element 的 props

  1. 目标

    • 假如我们有一个 div,上面有一个属性 foo: 'foo', 我们把这个属性改为 foo: 'new-foo' ------> 修改
    • 属性由 foo: 'foo' 变为 foo: null || undefined ------> 删除这个属性
    • 属性 bar: 'bar' 变为这个属性不存在
  2. 初始化文件

    js 复制代码
    import { h, ref } from "../../lib/guide-mini-vue.esm.js";
    
    export const App = {
      name: "App",
      render() {
        return h("div",
          {
              ...this.props
          },
          [
            h('p',{},'标签'),
            h('button', {
              onClick: this.onChangePropsDemo1
            },
              "btn: props修改了"
            ),
            h('button', {
              onClick: this.onChangePropsDemo2
            },
              "btn2: props改变为null或undefined"
            ),
            h('button', {
              onClick: this.onChangePropsDemo3
            },
              "btn3: props被删除了"
            ),
          ]
        )
      },
      setup() {
        const props = ref({
          foo: "foo",
          bar: "bar"
        });
        // 修改
        const onChangePropsDemo1 = () => {
          props.value.foo = 'new-foo'
        }
        // 置空
        const onChangePropsDemo2 = () => {
          props.value.foo = undefined
        }
        // 删除
        const onChangePropsDemo3 = () => {
          props.value = {
            foo: "foo"
          }
        }
        return {
          props,
          onChangePropsDemo1,
          onChangePropsDemo2,
          onChangePropsDemo3
        }
      },
    }
  3. 实现点击 btn: props 修改了 ,到这里实现了点击按钮修改属性

js 复制代码
// App.ts
const onChangePropsDemo1 = () => {
  props.value.foo = 'new-foo'
}
js 复制代码
// renderer.ts
function patchElement(n1, n2, container) {  // ✅ 
  // 1. 拿到新旧虚拟dom的 props 进行比对
  let oldProps = n1.props || {}
  let newProps = n2.props || {}
  let el = n2.el = n1.el
  if(oldProps !== newProps) {
    // 2. 如果不相等,就进行修改
    patchProps(el, oldProps, newProps)
  }
}
function patchProps(el, oldProps, newProps) { // ✅
  // 遍历新 props
  for (let key in newProps) {
    // 拿到 新props 与旧props 的值进行对比
    let newVal = newProps[key]
    let oldVal = oldProps[key]
    // 如果不相等,进一步进行修改
    if(newVal !== oldVal) {
      hostPatchPros(el, key, oldVal, newVal)
    }
  }
}
  function mountElement(n2, container, parentComponent) {
  const { type, props, children, shapeFlag } = n2
  const el = n2.el = hostCreateElement(type)
  if (shapeFlag & shapeFlags.TEXT_CHILDREN) {
    el.textContent = children
  } else if (shapeFlag & shapeFlags.ARRAY_CHILDREN) {
    mountChildren(children, el, parentComponent)
  }
  for (let key in props) {
    let value = props[key]
    hostPatchPros(el, key, null, value)  // ✅ 这里增加了一个 旧虚拟dom的 props 值的参数
  }
  hostInsert(el, container)
}
// runtime-dom/index.ts
export function patchProps(el, key, preVal, nextVal) { // ✅ 这里增加了一个 旧虚拟dom的 props 值的参数
  const isOn = (key) => /^on[A-Z]/.test(key)
  if (isOn(key)) {
    const event = key.slice(2).toLocaleLowerCase() 
    el.addEventListener(event, nextVal, false) // ✅
  } else {
    el.setAttribute(key, nextVal)
  }
}
  1. 下面实现点击 btn2: props改变为null或undefined
js 复制代码
// App.js
const onChangePropsDemo2 = () => {
  props.value.foo = undefined
}
js 复制代码
export function patchProps(el, key, preVal, nextVal) {
  const isOn = (key) => /^on[A-Z]/.test(key)
  if (isOn(key)) {
    const event = key.slice(2).toLocaleLowerCase()
    el.addEventListener(event, nextVal, false)
  } else {
    if(nextVal === undefined || nextVal === null) { // ✅ 当赋值为 null 或者 undefined 时,就进行直接删除属性
      el.removeAttribute(key)
    } else {
      el.setAttribute(key, nextVal)
    }
  }
}
  1. 下面实现点击 btn3: props被删除了 就进行属性删除
js 复制代码
// 删除
const onChangePropsDemo3 = () => {
  props.value = {
    foo: "foo"
  }
}
js 复制代码
function patchProps(el, oldProps, newProps) {
  for (let key in newProps) {
    let newVal = newProps[key]
    let oldVal = oldProps[key]
    if(newVal !== oldVal) {
      hostPatchPros(el, key, oldVal, newVal)
    }
  }
  for(let key in oldProps) { // ✅ 当就属性的 key 在新属性里面不存在,说明被删除了,直接走上面封装的删除的逻辑,进行删除
    if(!(key in newProps)) {
      hostPatchPros(el, key, oldProps[key], null)  
    }
  }
}
  1. 查看可优化的点
js 复制代码
// shared/index.ts
export const EMPTY_OBJECT = {}
// renderer.ts
function patchElement(n1, n2, container) {

  let oldProps = n1.props || EMPTY_OBJECT // ✅
  let newProps = n2.props || EMPTY_OBJECT // ✅

  let el = n2.el = n1.el
  patchProps(el, oldProps, newProps)
}
function patchProps(el, oldProps, newProps) {
  if (oldProps !== newProps) { // ✅ 判断不相等才往下走
    for (let key in newProps) {
      let newVal = newProps[key]
      let oldVal = oldProps[key]
      if (newVal !== oldVal) {
        hostPatchPros(el, key, oldVal, newVal)
      }
    }
    if(oldProps !== EMPTY_OBJECT) { // ✅ 只有不为空才能往下走,优化性能
      for (let key in oldProps) {
        if (!(key in newProps)) {
          hostPatchPros(el, key, oldProps[key], null)
        }
      }
    }
  }
}
相关推荐
创新技术阁10 分钟前
FastapiAdmin 系统日志体系与核心配置参数详解
前端·后端·fastapi
掘金酱10 分钟前
【社区公告】签到与矿石奖励解耦说明
前端
promiseThen12 分钟前
LWC Workflow:用 7 个 Cursor Skill 搭一条 AI 协作开发流水线
前端·ai编程
一个游离的指针35 分钟前
函数管道:消除深度嵌套调用
前端·javascript
浅诺44 分钟前
Nginx sub_filter 的“幽灵陷阱”:为什么页面能打开,懒加载的 JS 却全是 404?
前端
PBitW1 小时前
为什么vite中TS报错,可以继续运行?Webpack不行?
前端·webpack·typescript·vite
光影少年1 小时前
react navite手写 FlatList 优化配置
前端·react native·react.js
曹牧1 小时前
C#:文本文件读取
服务器·前端·c#
柚yuzumi1 小时前
前端优化,从少触发一次开始:防抖与节流
前端·javascript
默_笙2 小时前
🚤 CSS 布局的"圈地运动":BFC 就是浏览器的独立领地
前端·javascript