vanjs源码笔记

vanjs官网

前言

最开始看到 vanjs 的时候,也是很震惊,短短100多行代码就实现了一个框架。阅读源码时候,越来越震惊,这么点代码连 statederive(类 computed) 也实现了,而且实现得十分巧妙。

除了代码风格读起来实在是难受,bind(() => (setter(v.val), dom)) 就这一句就卡了我好久,一直以为是逗号分隔参数,实际却是逗号语句,只是一个参数。

源码理解

由于总共就 100多行代码,就不贴源码了,简单介绍一下思路,有兴趣的大哥可以自己去看一看,逻辑并不是很复杂。

demo

首先,上一个比较简单的使用demo

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script type="module" src="./src/van.js"></script>
  <style>
    #div {
      background: red;
      color: green;
    }
  </style>
</head>
<body>
<script type="module">
  import van from "./src/van.js";
  const { button, div } = van.tags;

  const counter = van.state(0)

  const Counter = () => {
    return div(
        {
          id: 'div',
          style: () => `font-size: 16px;margin-top: ${counter.val}px`
        },
        [
          "❤️ ", counter, " ",
          button({onclick: () => ++counter.val}, "👍"),
          button({onclick: () => --counter.val}, "👎"),
          () => {
            if (counter.val) {
              return 1111
            }
            return 2222
          }
        ]
      )
  }

  van.add(document.body, Counter())
</script>
</body>
</html>

页面的展现如下图所示

API解析

dom

首先,可以看到demo中使用了 div() button() 这样的函数来创建 dom

其实是使用 proxy 代理了 document.createElement,

div() => document.createElement('div')

html 复制代码
<script>
  const tags = new Proxy((name) => document.createElement(name), {
    get(target, name) {
      return target.bind(undefined, name);
    }
  })

  const { div } = tags;
  console.log('div', div());
</script>

然后再来看看内部的参数,第一个参数代表 domprops,由于涉及到了 state 放到下一步细说,第二个参数以及后面的参数,就是表示 divchildren ,创建 dom 的方法和现在一致,创建成功后,通过 add 函数,append 到父元素下。

children 也可以使用一个函数返回 dom ,类似于上面代码中涉及条件判断时候会使用到, van 会运行后进行依赖收集(如果有)后,返回 dom

props

第二个参数就是绑定在 dom 上的,props ,然后根据 val 的类型做了区分。

  1. 函数的话,运行后(函数中可能会带 state ,因此也要收集依赖)获取返回值。
  2. 直接是 state 的话,也封成一个函数用来依赖收集。
  3. 正常的值,直接返回。

最后返回的值也是通过 dom.setAttribute 修改 dom 属性。

state

一般来说,state 会用在 3 个方面。

  1. 直接用在 props{ key: state }
  2. 也是用在 props 中,但是通过函数返回 { key: () => state.val }
  3. 返回 dom 节点相关操作 , () => (state ? dom1 : dom2 )

实现原理也很简单,类似于 vue 的依赖收集,在 getter 中收集依赖,然后将操作 state 的相关函数绑定,然后,修改时,触发绑定的函数,然后更新 dom

垃圾收集

因为要涉及新老 dom 的更新替换,所以原有的 dom 会被保存起来,但是我们知道就算 dom 卸载了,如果还存在引用,那这个 dom 节点也会继续存在,导致内存泄露, van 中也根据这个做了一个处理,通过

dom.isConnected 来判断节点是否还存在 document 中,进行释放。

let keepConnected = l => l.filter(b => b._dom?.isConnected)

结语

derive部分的代码粗略看了看,基本和 state 是差不多的逻辑,多了一个 listeners 的绑定,原来应该也是一致的,通过依赖收集重新运行绑定的 fn ,然后 updateDom

就我个人而言,感觉最优秀的设计,就是通过 proxybinddocument.createElement 实现成了div() 这样的语法,然后代码中也运用了大量的 bind 来绑定 this 和 固定参数,用并不是很复杂的代码实现了这个 麻雀虽小,五脏俱全 的框架。

上周起立了个每周写一篇笔记的 flag ,结果周末喝酒喝的醉生梦死,还是拖到了周一。=-,希望这礼拜能按时完成(flag + 1)。

最后的最后

帮大哥捞捞人,杭州。 大哥人很好 0.0,天天被我 diss 都没脾气。

相关推荐
UrbanJazzerati1 分钟前
Vue 3 全局错误处理详解与示例
前端
木斯佳3 分钟前
前端八股文面经大全:小红书前端日常实习(2026-1-5)·面经深度解析
前端
HelloReader4 分钟前
Trunk + Tauri 前端配置Rust/WASM 项目如何稳定接入桌面与移动端(Trunk 0.17.5)
前端
小碗细面7 分钟前
告别996!Claude Code 6个实用工作流程
前端·人工智能·ai编程
HelloReader8 分钟前
Vite + Tauri 2 一套配置同时搞定桌面开发、调试体验、iOS 真机联调(Vite 5.4.8)
前端
顾青8 分钟前
仅仅一行 CSS,竟让 2000 个节点的页面在弹框时卡成 PPT?
前端·vue.js·性能优化
用户4099322502129 分钟前
如何在Vue3中优化生命周期钩子性能并规避常见陷阱?
前端·vue.js·trae
微风起皱10 分钟前
企业级WEB应用服务器TOMCAT
java·前端·tomcat
__不想说话__10 分钟前
前端开发者的 AI 时代生存指南:大模型如何重塑岗位要求与技能
前端·人工智能·面试
青青家的小灰灰10 分钟前
深入React源码:解析setState的批量更新与异步机制
前端·javascript·react.js