上海古都建筑设计集团,上海办公室装修设计公司,上海装修公司高质量的内容分享社区,上海装修公司我们不是内容生产者,我们只是上海办公室装修设计公司内容的搬运工平台

【Vue.js设计与实现】第一篇:框架设计概览-阅读笔记(完结)

guduadmin221月前

从高层设计的角度去探讨框架需要关注的问题。

参考:速读《Vue.js 设计与实现》 - 掘金 (juejin.cn)

系列目录:

标题博客
第一篇:框架设计概览【Vue.js设计与实现】第一篇:框架设计概览-阅读笔记
第二篇:响应系统【Vue.js设计与实现】第二篇:响应系统-阅读笔记
第三篇:渲染器【Vue.js设计与实现】第三篇:渲染器-阅读笔记
第四篇:组件化【Vue.js设计与实现】第四篇:组件化-阅读笔记
第五篇:编译器【Vue.js设计与实现】第五篇:编译器-阅读笔记
第六篇:服务端渲染【Vue.js设计与实现】第六篇:服务端渲染-阅读笔记

第一篇:框架设计概览

  • 第 1 章 权衡的艺术
  • 第 2 章 框架设计的核心要素
  • 第 3 章 Vue.js 3 的设计思路

    文章目录

      • 第一章 权衡的艺术
        • 1.1 命令式和声明式
        • 1.2 性能与可维护性的权衡
        • 1.3 运行时和编译时
        • 第二章:框架设计的核心要素
          • 2.1 `__DEV__`:在开发环境中为用户提供友好的警告信息的同时,不会增加生产环境代码的体积
          • 2.2 ` /*#__PURE__*/`与Tree Shaking
          • 2.3 框架应该输出怎样的构建产物
          • 2.4 错误处理
          • 2.5 良好的TypeScript类型支持
          • 总结
          • 第三章:Vue.js 3 的设计思路
            • 3.1 声明式地描述UI
            • 3.2 初识渲染器
            • 3.3 组件的本质
            • 3.4 模板的工作原理
            • 3.5 Vue.js 是各个模块组成的有机整体
            • 总结

              第一章 权衡的艺术

              框架的设计,本身就是一种权衡的艺术。

              1.1 命令式和声明式

              命令式关注过程,声明式关注结果

              命令式:

              const div=document.querySelector('#app') //获取div
              div.innerText='hello world' //设置文本内容
              div.addElementListener('click',()=>{alert('OK')}) //绑定事件
              

              声明式:

              Hello world

              vue的内部是命令式的,而我们使用vue的时候是声明式的。

              即,vue封装了命令式的过程,对外暴露出了声明式的结果

              1.2 性能与可维护性的权衡

              从性能的角度上看,命令式的性能>声明式的性能。

              原因:

              命令式的代码通过原生的JS实现,声明式的代码要实现同样功能时,还要再调用相同的命令式代码。

              声明式代码的更新性能消耗 = 找出差异的性能消耗+直接修改的性能消耗.

              所谓的虚拟 DOM,就是为了最小化找出差异这一步的性能消耗而出现的。

              显然命令式的性能更高。此时vue还要对外暴露出声明式的接口,原因是声明式的可维护性,远大于命令式的可维护性

              而且,vue在性能优化之下,它并不会比纯命令式的性能差太多。

              在前端领域,用JS修改HTML的方式主要有3种:原生JS,innerHTML,虚拟DOM。

              心智负担:虚拟DOM < innerHTML< 原生JS

              性能:innerHTML < 虚拟DOM < 原生JS

              可维护性:原生JS < innerHTML < 虚拟DOM

              虚拟DOM的性能并不是最高的,但是vue依然选择虚拟DOM来进行渲染层的构架。

              这也是性能与可维护性的权衡。

              1.3 运行时和编译时

              都是框架设计的一种方式,可单独出现,也可组合使用。

              • 运行时:runtime

                利用render函数,把虚拟DOM转化为真实DOM的方式。

                • 编译时:compiler

                  把template模板中的内容,转化为真实DOM。

                  注意,存在编译过程,可以分析用户提供的内容。同时,没有运行时理论上性能会更好。

                  • 运行时+编译时

                    过程分两步:

                    1. 先把template模板转化成render函数,即编译时
                    2. 再利用render函数,把虚拟DOM转化为真实DOM,即运行时

                    两者的结合,可以:

                    • 在编译时:分析用户提供的内容
                    • 在运行时,提供足够的灵活性

                      这也是vue的主要实现方式。

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

                    2.1 __DEV__:在开发环境中为用户提供友好的警告信息的同时,不会增加生产环境代码的体积

                    有一个常量__DEV__,存在于所有的console.warn中:

                    if (__DEV__ && !res) {
                    	warn(
                    		`Failed to mount app: mount target selector "${container}"
                        returned null.`
                    	);
                    }
                    

                    在开发环境中__DEV__永远为true,在生产环境中__DEV__永远为false。永远不会执行的代码成为dead code,不会出现在最终产物中。

                    2.2 /*#__PURE__*/与Tree Shaking

                    Tree Shaking:消除那些永远不会被执行的代码。

                    想要实现Tree Shaking,模块必须是ESM。

                    Tree Shaking的关键点:副作用。如果一个函数调用会产生副作用,那么就不能将其移除。副作用就是,当调用函数的时候会对外部产生影响,例如修改了全局变量。

                    举个例子:

                    如果 obj 对象是一个通过 Proxy 创建的代理对象,那么当我们读取对象属性时,就会触发代理对象的 get 夹子(trap),在 get 夹子中是可能产生副作用的,例如我们在 get 夹子中修改了某个全局变量。而到底会不会产生副作用,只有代码真正运行的时候才能知道,JavaScript 本身是动态语言,因此想要静态地分析哪些代码是 dead code 很有难度

                    静态地分析 JavaScript 代码很困难,所以像 rollup.js 这类工具都会提供一个机制,让我们能明确地告诉 rollup.js:这段代码没有副作用,可以移除。

                    import {foo} from './utils'
                    /*#__PURE__*/ foo()
                    

                    注释代码/*#__PURE__*/,就是告诉rollup.js,对于foo的调用不会产生副作用,可以Tree-Shaking。

                    实际上,通常产生副作用的代码都是模块内函数的顶级调用。

                    foo() // 顶级调用
                    function bar(){
                    	foo() // 函数内调用
                    }
                    

                    可以看到,对于顶级调用来说,是可能产生副作用的;对于函数内调用来说,只要函数 bar 没有调用,那么 foo 函数的调用自然不会产生副作用。

                    因此,在 Vue.js 3 的源码中,基本都是在一些顶级调用的函数上使用 /*#__PURE__*/ 注释。此注释也可以应用在语句上。

                    2.3 框架应该输出怎样的构建产物

                    用户能够使用

                    ESM格式的资源中,文件会有一个-browser字样。其实对于ESM格式的资源来说,Vue.js还会输出一个vue.esm-bundler.js 文件。这样做的原因是:在寻找资源时,如果package.json中存在module字段,那么会优先使用module字段指向的资源来代替main字段指向的资源。

                    如Vue.js源码中的packages/vue/package.json:

                    {
                    	"main":"index.js",
                    	"module":"dist/vue.runtime.esm-bundler.js"
                    }
                    

                    带有 -bundler 字样的 ESM 资源是给 rollup.js 或 webpack 等打包工具使用的,而带有 -browser 字样的 ESM 资源是直接给

                    其中