Vue.js设计与实现读书笔记一

第一章 权衡的艺术

1.什么是声明式和命令式?更新局部数据innerHTML和虚拟DOM 以及原生DOM操作之间的性能对比?
=》声明式就是:给参数 然后不用管过程,直接得到结果 命令式:从一步一步从开始到结果, 声明式式命令式的封装
命令式:效率高 声明式:维护方便 简单
=》innerHTML 跟新数据,是必须跟新全部数据,计算量包括,DOM操作
innerHTML原理

  1. 创建一个fragment(document碎片)
  2. 将 newHTML 设置到 fragment 内部 (这里怎么设置的不必关心,反正不是用的innerHTML 呵呵)
  3. 清除el下的所有子节点 , 类似 el.removeChildren()
  4. 将fragment加入到 el内, 类似 el.appendChild(fragment)

(1)创建一个docuoment (2)解析字符串,提取标签 (3)将标签属性按层次生成
(4)删除所有子节点,(5)将创建的dom添加到目标dom上
=》虚拟DOM是,用对象 变量响应的节点和属性,()用原生的DOM方法创建DOM
当更改是,虚拟DOM只修改改变的DOM,不用全部重新创建,innerHTML要全部重来

2.什么是运行时和编译时?纯运行时的例子是?
=》运行时是指,代码直接可以运行,编译时,写一个无法直接执行的代码,编译后可以直接执行,书中的例子是:

1.运行时Render(obj,dom):将虚拟dom(对象)树,渲染到为HTML代码

2.编译:compiler(htmlstr) :将html(类html)代码编译成 树形数据对象(虚拟dom)

由HTML模板-compiter(html)->得到Vnode->Render( obj,root)->HTML
纯编译就是直接将HTML compiler() 为命令代码,document.xx…(性能高,无法预测优化)
纯运行时就是将只有render函数,直接写Vnode,操作对象,来改变HTML(不灵活)

第二章 框架设计的核心要素

1._DEV_是什么?
简单来说是webpack定义的常量,用于区分生产和发布环境,在vue3源码中也有,当时生产环境的时候,不会打印错误
框架提供的错误提示,在打包的发布版本会自动剔除

(静态分析)
2.tree-shaking是什么?
就是没有引入的代码,和不会执行的代码,不打包到最终结果,用/PURE/来标记,函数内部调用,只要外部函数没有调用过,就可以删除这部分代码,

3.直接是src引入的文件是?webpack编译的文件是?
直接导入有两种:
有两种,低级浏览器IIEF格式的.vue.global.js (压缩版,没有错误提示,tree-shaking不起作用)
高级浏览器支持ESM规范,vue.esm.brower.js (压缩版,没有错误提示,tree-shaking不起作用)

给webpack使用的的vue.esm.bundler.js(去除一切没有用到的代码,tree-shaking起作用)

vue3.如何实现错误处理的?

同一函数处理,fn是执行肯能出错的函数,args是一个参数数组
作者主要是想表达,vue框架实现了错误统一处理,屏蔽某些底层错误,让用户更好的找到错误代码的位置

Vue3源码如下:

function callWithErrorHandling(fn, instance, type, args) {
    let res;
try {
// 执行函数并且监控是否参数
        res = args ? fn(...args) : fn();
    }
    catch (err) {
        handleError(err, instance, type);
    }
    return res;
}

handleError


function handleError(err, instance, type, throwInDev = true) {
    const contextVNode = instance ? instance.vnode : null;
    if (instance) {
        let cur = instance.parent;
        // the exposed instance is the render proxy to keep it consistent with 2.x
        const exposedInstance = instance.proxy;
        // in production the hook receives only the error code
        const errorInfo = ErrorTypeStrings[type] ;
        while (cur) {
            const errorCapturedHooks = cur.ec;
            if (errorCapturedHooks) {
                for (let i = 0; i < errorCapturedHooks.length; i++) {
                    if (errorCapturedHooks[i](err, exposedInstance, errorInfo) === false) {
                        return;
                    }
                                    }
            }
            cur = cur.parent;
        }
        // app-level handling
        const appErrorHandler = instance.appContext.config.errorHandler;
        if (appErrorHandler) {
            callWithErrorHandling(appErrorHandler, null, 10 /* APP_ERROR_HANDLER */, [err, exposedInstance, errorInfo]);
            return;
        }
    }
    logError(err, type, contextVNode, throwInDev);
}

第三章 Vue.js3的设计思路

1.模板声明描述UI和JavaScript对象描述UI的不同之处?

描述ui主要是几个问题:
1.DOM元素:例如div标签
2.标签上的属性:a属性
3.标签上的事件例如:onclick事件
4.标签的层次关系,例如父子元素
Vue的声明描述是


JS对象描述是

const title={
      tag:”h1”,
    props:{ onClick:handler},
   children:[ { tag:”span”} ]
}

JavaScript对象描述更为灵活,而模板声明描述UI更为直观,VUE两者都保留了
虚拟DOM(Vndoe)就是用JS对象描述UI的方式

2.render的h函数是什么作用?
h函数是将声明描述的ui模板文件变为js对象描述的Vnode,
3.实现简单的渲染器(renderer)?

渲染器就是将Vnode对象变成可执行的DOM语句,创建DOM并添加到HTML上

 /*
         渲染器的作用是将Vnode 变成 HTML对象,并添加到container节点上
        
        */
      function renderer(Vnode, container) {
        // 创建顶级的节点
        let el = document.createElement(Vnode.tag);
        // 添加属性
        for (key in Vnode.props) {
          // 如果是on开头的是事件
          if (/^on/.test(key)) {
            // 事件将事件添加       onClick=>click
            el.addEventListener(key.slice(2).toLowerCase(), Vnode.props[key]);
          }
          el;
          {
            // 普通属性将属性添加到元素上
            el.setAttribute(key, Vnode.props[key]);
          }
        }
        if (Array.isArray(Vnode.childrens)) {
            // 如果是数组就使用递归遍历下一层
          Vnode.childrens.forEach((item) => {
            renderer(item, el);
          });
        } else if (typeof Vnode.childrens === "string") {
          el.appendChild(document.createTextNode(Vnode.childrens));
        } else {
          new Error("子节点格式错误");
        }
        // 将此节点挂载跟节点上
        container.appendChild(el);
      }

      // Vnode的例子
      const demo = {
        tag: "div",
        props: { onClick: () => alert("hello") },
        childrens: [
          {
            tag: "h1",
            props: { title: "h1" },
            childrens: "Renderer渲染器",
          },
          { tag: "p", props: { class: "red" }, childrens: "这是p标签" },
        ],
      };

      // 测试
      renderer(demo, document.body);

4.组件的本质是什么?如何用Vnode来描述组件? 如何用renderer来渲染组件?
上面已经实现了用Vnode来描述普通的DOM元素,但是如何描述组件?组件毕竟不是真实的DOM元素。

组件的本质就是一组DOM元素的封装
(从作者这句话中,我似乎知道了vue2中为啥组件只有一个容器元素,其实就是一个Vnode的根元素)

renderer函数的实现:

/*
            注意组件也是一系列的DOM组成的,可以将组件设为一个函数,或一个对象,最终返回一个组件根元素的虚拟DOM*/
      **// renderer 渲染组件**
      function renderer(Vnode, container) {
        if (typeof Vnode.tag === "string") {
          // 渲染普通元素
          mountElement(Vnode, container);
        } else if (typeof Vnode.tag === "object") {
          // 渲染组件
          mountComponent(Vnode, container);
        }
      }

      **// 渲染普通元素,之前写过**
      function mountElement(Vnode, container) {
        // 创建顶级的节点
        let el = document.createElement(Vnode.tag);
        // 添加属性
        if (Vnode.props) {
          // Vnode.props判断要存在
          for (key in Vnode.props) {
            // 如果是on开头的是事件
            if (/^on/.test(key)) {
              // 事件将事件添加       onClick=>click
              el.addEventListener(key.slice(2).toLowerCase(), Vnode.props[key]);
            }
            el;
            {
              // 普通属性将属性添加到元素上
              el.setAttribute(key, Vnode.props[key]);
            }
          }
        }
        if (Array.isArray(Vnode.childrens)) {
          // 如果是数组就使用递归遍历下一层
          Vnode.childrens.forEach((item) => {
            renderer(item, el);
          });
        } else if (typeof Vnode.childrens === "string") {
          el.appendChild(document.createTextNode(Vnode.childrens));
        } else {
          return new Error("子节点错误");
        }
        // 将此节点挂载跟节点上
        container.appendChild(el);
      }

      **// 渲染组件函数,其实就是获得容器节后,和渲染普通节点一样**
      function mountComponent(Vnode, container) {
        // 获得组件的容器元素的Vnode
        const subtree = Vnode.tag.render();
        mountElement(subtree, container);
      }

上面两个函数实现了,渲染普通DOM元素和组件
下面是测试例子:
模板描述UI的形式

 <div>
        <div @click="alter('hello')">
          <h1 title="h1">Renderer渲染器h1>
          <p class="red">这是p标签p>
        div>
        <myComponent>myComponent>
      div>

      
      <template>
        <div title="myComponent组件">myComponent的第一个容器元素div>
      template>

js对象描述UI的形式

**//组件的例子 组件是一个对象,当然组件也可以是一个函数**
      const myComponent = {
        render() {
          return {
            tag: "div", //组件的第一个容器元素
            props: { title: "myComponent组件" },
            childrens: "myComponent的第一个容器元素",
          };
        },
      };
    **// 用Vnode描述组件例子**
      const Vnode = {
        tag: "div",
        props: {},
        childrens: [
          //第一个div
          {
            tag: "div",
            props: { onClick: () => alert("hello") },
            childrens: [
              {
                tag: "h1",
                props: { title: "h1" },
                childrens: "Renderer渲染器",
              },
              { tag: "p", props: { class: "red" }, childrens: "这是p标签" },
            ],
          },
          // 组件
          {
            tag: myComponent,
            // renderer中没有处理组件的属性
            // props: {
            //   color: "red",
            // },
          },
        ],
      };

最后执行渲染函数,将Vndoe 变成真实的DOM渲染到页面中

 renderer(Vnode, document.body);

结果如下:
Vue.js设计与实现读书笔记一_第1张图片

你可能感兴趣的:(Vue3,vue.js,javascript,前端)