vanjs源码笔记

vanjs官网

前言

最开始看到 vanjs 的时候,也是很震惊,短短100多行代码就实现了一个框架。阅读源码时候,越来越震惊,这么点代码连 state、 derive(类 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>

然后再来看看内部的参数,第一个参数代表 dom 的 props,由于涉及到了 state 放到下一步细说,第二个参数以及后面的参数,就是表示 div 的 children ,创建 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

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

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

最后的最后

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

相关推荐
子兮曰3 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰3 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万3 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝3 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋3 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁3 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄3 天前
JavaScript 隐式全局变量解析
javascript
汉堡大王95273 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大3 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师3 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端