人在上海,因为疫情,没有办法拿到这本书,本书没有电子版,那就看代码仓库吧。

书的介绍
s34120804.jpg

本书基于Vue.js 3,从规范出发,以源码为基础,并结合大量直观的配图,循序渐进地讲解Vue.js中各个功能模块的实现,细致剖析框架设计原理。全书共18章,分为六篇,主要内容包括:框架设计概览、响应系统、渲染器、组件化、编译器和服务端渲染等。通过阅读本书,对Vue.js 2/3具有上手经验的开发人员能够进一步理解Vue.js框架的实现细节,没有Vue.js使用经验但对框架设计感兴趣的前端开发人员,能够快速掌握Vue.js的设计原理。

下面开始阅读代码

course1-权衡的艺术

我对源码进行了注释和修改
这部分代码主要是写了一个render函数,递归的将一个固定对象结构渲染到DOM中
固定对象结构形如

  1. interface node {
  2. tag: string,
  3. children: string | node[]
  4. }
  1. <body></body>
  2. <script type="module">
  3. import { Render, obj } from "./render.js";
  4. // 渲染到 body 下
  5. Render(obj, document.body);
  6. </script>
  1. export async function Render(obj, root) {
  2. // 创建DOM标签
  3. const el = document.createElement(obj.tag)
  4. if (typeof obj.children === 'string' /* 如果children类型是string */ ) {
  5. const text = document.createTextNode(obj.children)
  6. el.appendChild(text)
  7. } else if (Array.isArray(obj.children)) /* 如果children的类型是Array */ {
  8. // array,递归调用 Render,使用 el 作为 root 参数
  9. for (let child of obj.children) {
  10. Render(child, el)
  11. }
  12. }
  13. // 将元素添加到 root
  14. root.appendChild(el)
  15. }
  16. export const obj = {
  17. tag: "div",
  18. children: [{
  19. tag: "span",
  20. children: "hello world",
  21. },
  22. {
  23. tag: "div",
  24. children: [{
  25. tag: "span",
  26. children: "this ",
  27. },
  28. {
  29. tag: "span",
  30. children: "is ",
  31. },
  32. {
  33. tag: "span",
  34. children: "render",
  35. },
  36. ],
  37. },
  38. ],
  39. };

没了

course2-框架设计的核心要素

这一部分应该是在介绍框架使用的打包工具配置和演示吧。
没什么可说的。
没了

course3-Vue3 的设计思路

这一部分主要演示vnode绑定事件
此时固定对象结构有了名字,那就是vnode

  1. interface vnode {
  2. tag: string,
  3. props: {
  4. onClick: () => void
  5. },
  6. children: string | vnode[]
  7. }

code1.htmlcode3.html,从开始的vnode对象,到后来的组件对象

  1. <body></body>
  2. <script type="module">
  3. import { renderer } from "./code3.js";
  4. const MyComponent = {
  5. // render函数
  6. render() {
  7. return {
  8. tag: "div",
  9. props: {},
  10. children: [
  11. {
  12. tag: "span",
  13. props: {
  14. onClick: () => alert("hello"),
  15. },
  16. children: "click me",
  17. },
  18. {
  19. tag: "div",
  20. props: {},
  21. children: "this is span",
  22. },
  23. ],
  24. };
  25. },
  26. };
  27. // vnode
  28. const vnode = {
  29. tag: MyComponent,
  30. };
  31. renderer(vnode /* vnode */, document.body /* 挂载容器 */);
  32. </script>
  1. export function renderer(vnode /* vnode */ , container /* 挂载容器 */ ) {
  2. if (typeof vnode.tag === 'string') {
  3. // 说明 vnode 描述的是标签元素
  4. mountElement(vnode, container)
  5. } else if (typeof vnode.tag === 'object') {
  6. // 说明 vnode 描述的是组件
  7. mountComponent(vnode, container)
  8. }
  9. }
  10. function mountElement(vnode, container) {
  11. // 使用 vnode.tag 作为标签名称创建 DOM 元素
  12. const el = document.createElement(vnode.tag)
  13. // 遍历 vnode.props 将属性、事件添加到 DOM 元素
  14. for (const key in vnode.props) {
  15. if (/^on/.test(key)) {
  16. // 如果 key 以 on 开头,那么说明它是事件
  17. el.addEventListener(
  18. key.substr(2).toLowerCase(), // 事件名称 onClick ---> click
  19. vnode.props[key] // 事件处理函数
  20. )
  21. }
  22. }
  23. // 处理 children
  24. if (typeof vnode.children === 'string') {
  25. // 如果 children 是字符串,说明是元素的文本子节点
  26. el.appendChild(document.createTextNode(vnode.children))
  27. } else if (Array.isArray(vnode.children)) {
  28. // 递归地调用 renderer 函数渲染子节点,使用当前元素 el 作为挂载点
  29. vnode.children.forEach(child => renderer(child, el))
  30. }
  31. // 将元素添加到挂载点下
  32. container.appendChild(el)
  33. }
  34. function mountComponent(vnode, container) {
  35. // 调用组件函数,获取组件要渲染的内容(虚拟 DOM)
  36. const subtree = vnode.tag.render()
  37. // 递归调用 renderer 渲染 subtree
  38. renderer(subtree, container)
  39. }

没了