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

相关推荐
理想不理想v1 分钟前
vue经典前端面试题
前端·javascript·vue.js
不收藏找不到我2 分钟前
浏览器交互事件汇总
前端·交互
小阮的学习笔记15 分钟前
Vue3中使用LogicFlow实现简单流程图
javascript·vue.js·流程图
YBN娜16 分钟前
Vue实现登录功能
前端·javascript·vue.js
阳光开朗大男孩 = ̄ω ̄=16 分钟前
CSS——选择器、PxCook软件、盒子模型
前端·javascript·css
minDuck21 分钟前
ruoyi-vue集成tianai-captcha验证码
java·前端·vue.js
小政爱学习!41 分钟前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript
魏大帅。1 小时前
Axios 的 responseType 属性详解及 Blob 与 ArrayBuffer 解析
前端·javascript·ajax
花花鱼1 小时前
vue3 基于element-plus进行的一个可拖动改变导航与内容区域大小的简单方法
前端·javascript·elementui
k09331 小时前
sourceTree回滚版本到某次提交
开发语言·前端·javascript