🎨
Notes
  • 持续更新中...
  • articles
    • browser
      • 深入理解现代浏览器 - 导航
      • 深入理解现代浏览器 - 架构
      • 深入理解现代浏览器 - 交互
      • 深入理解现代浏览器 - 渲染器进程
    • dsa
      • DSA - 并查集
      • DSA - 哈希表
      • DSA - AVL 树
      • DSA - 二叉树
      • 快速选择
      • Big O 算法复杂度
      • DSA - 栈和队列
      • DSA - 前缀树 Trie
      • DSA - 图
      • DSA - 链表
      • DSA - 递归
    • typescript
      • TypeScript 学习笔记 - 任意属性 (Indexable Types)
      • 力扣的 TypeScript 面试题
      • TypeScript 学习笔记 - as const
      • TypeScript 学习笔记 - infer
    • network
      • Internet Protocol (IP)
      • 计算机网络基础
      • 如何分辨同源和同站
      • DNS 如何查询 IP 地址?
    • vue
      • Nuxt.js 入门
      • 从零实现一个 Mini Vue
      • 从零实现一个简单的 VDOM 引擎
      • 从零实现一个响应式状态管理
    • sorting
      • 排序 - 归并排序
      • 排序 - 冒泡排序
      • 排序 - 选择排序
      • 排序 - 计数排序
      • 排序 - 插入排序
    • compile
      • Compiler and Interpreter
      • Just-In-Time (JIT) Compilers
      • 编译流程
    • others
      • 什么是上下文无关语法
      • 如何在终端打印出有颜色的字
    • dev-ops
      • github-actions
        • GitHub Action 简介
        • GitHub Actions for CI
    • workflow
      • 用 Node 写一个 cli
      • 如何规范 git commit 信息
      • 如何监听 git hooks
      • 如何规范代码风格 - prettier
      • 如何发布一个 npm package
      • 如何规范代码质量 - eslint
    • design-pattern
      • 代理模式
      • 单例模式
      • 策略模式
    • security
      • 点击劫持
      • CSP 内容安全策略
    • javascript
      • 尾调用优化
      • 4种常见的内存泄漏及解决方法
    • unit-test
      • Test Vuejs Application - Chapter 2
      • Test Vuejs Application - Chapter 1
      • Vue Unit Test Intro
    • performance
      • HTTP 缓存
      • 如何优化图片资源
Powered by GitBook
On this page
  • 前置
  • 使用场景
  • 响应式状态
  • 页面更新
  • 完整代码

Was this helpful?

  1. articles
  2. vue

从零实现一个 Mini Vue

PreviousNuxt.js 入门Next从零实现一个简单的 VDOM 引擎

Last updated 4 years ago

Was this helpful?

前置

前两篇已经实现的功能:

  1. h 创建 VNode

  2. mount 挂载 VNode

  3. unmount 移除 VNode

  4. patch 替换 VNode

  5. observe 将一个对象变成响应式

接下来就是将这些组合在一起。

使用场景

先来看看需求好了,我们的目标是实现一个计数器。

HTML 是这样子的:

<div id="counter"></div>
<button id="inc">inc</button>
  • #counter 显示当前数字。

  • #inc 按钮被点击时当前数字要加一。

响应式状态

首先我们需要一个状态来存储当前数字。

const counter = {
    count: 1,
};
observe(counter);

#inc 按钮被点击时 count++,这没什么好说的。

const incBtn = $('#inc');
incBtn.addEventListener('click', () => {
    counter.count++;
});

页面更新

重要的是,counter.count 更新的时候,页面上的数字也要更新。

首先,就像在 Vue 中的一样,我们需要一个组件来显示计数器的数字。

const counterComponent = {
    render(state) {
        return h('h1', {}, String(state.count));
    },
};

跟 Vue 中的 render 方法不同的是,我们这里的 render 方法接收一个响应式对象作为参数。

在 Vue 中,如果你使用的是模板写法,模板最终也是被编译成 render 方法的。

有了组件,接下来就是需要定义状态改变时要做的事情了。

autorun(function () {
    // counter.count 改变了
});

这里有两种情况:

  1. 初始状态时

  2. 状态发生改变时

1. 初始状态时

在初始状态的时候,直接调用组件的 render 方法生成 VNode,然后挂载到 DOM 上就行:

const node = counterComponent.render(counter);
mount(node, counterContainer);

2. 状态发生改变时

状态发生改变时,需要再一次调用组件的 render 方法生成新的 VNode,然后与旧的 VNode 对比,看哪些元素需要更新。

const newNode = counterComponent.render(counter);
patch(oldNode, newNode);

组合起来就是这样:

let oldNode = null;
autorun(function () {
    if (oldNode) {
        const newNode = counterComponent.render(counter);
        patch(oldNode, newNode);
        oldNode = newNode;
    } else {
        oldNode = counterComponent.render(counter);
        mount(oldNode, counterContainer);
    }
});

完整代码

从零实现一个简单的 VDOM 引擎
从零实现一个响应式状态管理
完整代码