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)
        }
      }
    }
  }
}
相关推荐
子兮曰2 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰2 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万3 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝3 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
A黄俊辉A3 天前
uniapp webview中实现 app和内嵌的H5双向通信
vue.js·json
三十而立洋3 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁3 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄3 天前
JavaScript 隐式全局变量解析
javascript
honkun63 天前
vue 表格组件 vxe-table 配置 ajax 请求自动加载数据与表单查询
vue.js·vxe-table
kybs19913 天前
全球灾害数据分析可视化 毕业设计-附源码66794
vue.js·spring boot·mysql·安全·django·c#·asp.net