# learn-snabbdom **Repository Path**: xiaoxfa/learn-snabbdom ## Basic Information - **Project Name**: learn-snabbdom - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-02-14 - **Last Updated**: 2021-02-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 学习 snabbdom ## 安装依赖启动项目 npm i parcel-bundler 补充 npm scripts ,包括 dev 和 build ## 基本使用 观察 1 2 3 三个 js 能了解基本用法 观察 dist 的 build 打包情况。 ## 源码解析 ### h 函数 首先看 h 函数,对 h 函数打断点进去。可以看到 h 函数式重载 如果我们传递两个或者三个参数,对应不同的逻辑。 还是相对比较简单的 ### vnode 整理参数为对象,处理 ### patch 核心。 ```js patch(oldVnode, newVnode); ``` - 对比新旧节点的类型是否相同,key 和 sel - 如果相同。继续判断 - 如果不同,删除之前内容,重新渲染 - 判断新节点是否有 text - 如果有,继续判断 text 是否相同,直接更新内容 - 如果有 children,继续判断子节点,判断过程就是 diff 算法 - diff 算法只在同层进行比较 ### init 高阶函数,内部返回了 patch 函数 接收两个参数。 看下实现: - 定义变量和钩子 - 定义 cbs 存储所有钩子,循环钩子列表塞入各自的分类 - 最终返回 patch 函数 ```ts // 定义hook 各种类型 const hooks: Array = [ "create", "update", "remove", "destroy", "pre", "post", ]; // 先定义csb为各生命周期空数组 const cbs: ModuleHooks = { create: [], update: [], remove: [], destroy: [], pre: [], post: [], }; // ... // hooks 循环 for (i = 0; i < hooks.length; ++i) { // hooks[i] 为每一项hook cbs[hooks[i]] = []; // modules为用户init传递的插件模块 for (j = 0; j < modules.length; ++j) { // 每个模块也有独立的hook modules[j] // 每个模块每个周期 modules[j][hooks[i]] const hook = modules[j][hooks[i]]; if (hook !== undefined) { // 有hook就塞入cbs的统一收口中 (cbs[hooks[i]] as any[]).push(hook); } } } ``` 最终 `cbs = {created:[fn1,fn2]}` 接下来看 patch 函数 ### patch 核心。把 vnode 渲染成真实 dom init 大致代码: ```ts return function patch(oldVnode: VNode | Element, vnode: VNode): VNode { let i: number, elm: Node, parent: Node; // 保存新插入节点的队列,为了触发钩子函数 const insertedVnodeQueue: VNodeQueue = []; // 执行模块的pre钩子函数 for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i](); // 看旧节点是节点还是标签容器 if (!isVnode(oldVnode)) { // 如果是容器,虚拟化成vnode节点 oldVnode = emptyNodeAt(oldVnode); } // 看是不是相同节点 key sel if (sameVnode(oldVnode, vnode)) { // 找差异并更新dom patchVnode(oldVnode, vnode, insertedVnodeQueue); } else { // 渲染并插入 // 当前dom元素 elm = oldVnode.elm!; // 找到父节点 parent = api.parentNode(elm) as Node; // vnode--real dom createElm(vnode, insertedVnodeQueue); if (parent !== null) { // 插入兄弟,移除 api.insertBefore(parent, vnode.elm!, api.nextSibling(elm)); removeVnodes(parent, [oldVnode], 0, 0); } } // 新插入的节点 insert hook for (i = 0; i < insertedVnodeQueue.length; ++i) { insertedVnodeQueue[i].data!.hook!.insert!(insertedVnodeQueue[i]); } // post hook for (i = 0; i < cbs.post.length; ++i) cbs.post[i](); return vnode; }; ``` ### createEle