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 都没脾气。

相关推荐
陈_杨9 小时前
鸿蒙APP开发-带你走近分构App的分子数据
前端·javascript
Goodbye9 小时前
深入理解 JavaScript 变量提升(Hoisting)—— 从现象到原理
javascript
橘子星9 小时前
从零上手!Node.js 快速搭建生成式 AI 后端项目|密钥安全 + 完整可运行代码
前端·后端
陈_杨9 小时前
鸿蒙APP开发-带你开发锻艺册APP的材料清单功能
前端·javascript
xixixin_9 小时前
Promise.all 和 Promise.allSettled 详解
前端·javascript·vue.js
暗冰ཏོ9 小时前
前端数据大屏开发完整指南:Vue3 + ECharts 自适应可视化实战
前端·javascript·echarts·数据大屏·大屏端
陈_杨9 小时前
鸿蒙APP开发-带你了解单块酷APP参数管理的功能
前端·javascript
moMo9 小时前
# 从重置样式到 BEM 命名:写一个微信的按钮
前端·css
2301_815645389 小时前
saas 一面
前端·面经
无风听海10 小时前
OAuth 2.0 Scope 的使用与设计规划
前端