导图社区 Vue.js设计与实现
设计与实现 1 学习引导 本章的目标是介绍运用UML进行面向对象软 件设计,并了解系统实现时的问题。
编辑于2022-09-30 19:51:15 浙江省第一篇 框架设计概览
第一章 权衡的艺术
视图层框架分类
命令式:jQuery、Javascript——关注过程
声明式:VUE——关注结果
两种框架的性能与可维护性的权衡
声明式代码的性能不优于命令式代码的性能
命令式代码开发需要维护实现目标的整个过程。声明式代码则展示结果,因此声明式代码的可维护性更强
框架设计者:在保持可维护性的同时让性能损失最小化
虚拟DOM的性能
innnerHTML和虚拟DOM创建页面的性能比较
innerHTML创建页面性能=HTML字符串拼接的计算量+innerHTML的DOM计算量
虚拟DOM创建页面性能=创建JavaScript对象的计算量+创建真实DOM的计算量
无论纯JavaScript层面计算还是DOM层面计算,两者差距不大
innnerHTML和虚拟DOM更新页面的性能比较
innerHTML:渲染HTML字符串(JS)+销毁所有旧DOM(DOM)+新建所有新DOM(DOM)
虚拟DOM:创建新的Javascript对象+Diff(JS)+必要的DOM更新(DOM)
innerHTML需要全量更新,虚拟DOM只会更新必要的内容
影响innnerHTML和虚拟DOM性能因素
innerHTML:与模板大小相关,页面越大消耗越大
虚拟DOM:与数据变化量相关,无论页面多大都只会更新变化的内容
innerHTML、虚拟DOM、原生Javascript在更新页面时的性能
innnerHTML:心智负担中等
虚拟DOM:心智负担小、可维护性强、性能不错
原生Javascript:心智负担大、可维护性差、性能高
运行时和编译时
设计框架三种选择
纯运行时
用户提供一个树形结构的数据对象+Render函数根据对象递归地将数据渲染成DOM元素
运行时+编译时
调用Compiler把HTML字符串编译成树形结构的数据对象+调用Render函数进行渲染
纯编译时
调用Compiler将HTML字符串直接编译为命令式代码
三种选择优缺点
纯运行时:没有编译的过程,无法分析用户提供的内容
运行时+编译时:可以分析用户内容,,在编译时提取它们将其传递给Render函数进行进一步的优化
纯编译:也可以分析用户提供的内容且性能更好,但是用户提供的内容必须编译后才能用,缺乏灵活性。
第二章 框架设计的核心要素
提升用户开发体验
提供必要的警告信息——调用console.warn函数
直观的输出内容
控制框架代码的体积(Vue.js通过设置预定义常量_DEV_)
Vue.js构建开发环境资源时:把_DEV_设置为true,if(true&&res){warn('警告信息')},必定能输出警告信息
Vue.js构建生产环境资源时:把_DEV_设置为false,if(false&&res){warn('警告信息')},该代码永不执行,因此会被移除
优点:在开发环境中为用户提供友好警告信息的同时,不会增加生产环境代码的体积
框架要做到良好的Tree-Shaking
Vue.js通过Tree-Shaking消除永远不会执行的代码——排除deadcode
实现Tree-Shaking条件和关键点
条件:模块必须是ESM(ESModule)因为Tree-Shaking依赖ESM的静态结构
关键点:如果一个函数调用会产生副作用,那么就不能将其移除
判断是否会产生副作用的方法
注释代码/*_PURE_*/:告诉rollup.js,某函数调用不会产生副作用
顶级调用可能产生副作用,函数内调用不会,在Vue.js3中基本都是在顶级调用的函数上使用/*_PURE_*/注释
框架应该输出怎样的构建产物
输出IIFE格式的资源,实现直接在HTML页面中使用<script>
实现方法:在rollup.js中可以通过配置format:'life'来输出这种形式的资源
使用<script>标签直接引入ESM格式的资源
实现方法:在rollup.js的输出格式需要配置为format:'esm'
带有-bundler字样的ESM资源给rollup.js或者weback等打包工具使用的,带有-browser字样的ESM资源是直接给<script type='module'>使用的
当构建用于<script>标签的ESM资源时,如果是提供给打包工具,不能直接把_DEV_设置为true或false,而是要使用(process.env.NODE_ENV!='production')替换该常量
希望用户在Node.js中通过require语句引用资源
原因:当进行'服务器渲染'时,Vue.js的代码是在Node.js环境中运行的,而非浏览器环境
实现方法:为了输出cjs模块的资源,修改rollup.config.js的配置format:'cjs'
特性开关
提供特性开关,设置开关为true或false来代表开启或关闭对应的特性
使用rollup.js的预定义常量插件weback.DefinePlugin来实现,设置api预定义常量的值来控制是否要包含这段代码
api开关作用:使用api开关关闭不使用的特性或功能,这样在打包时Vue.js的这部分代码就不会包含在最终的资源中,从而缩小资源体积
错误处理
将错误处理封装为一个函数callWithErrorHandling,为用户提供统一的错误处理借口
用户再通过registerErrorHandler函数注册错误处理程序,然后在callWithErrorHandling函数内部捕获错误后,把错误传递给用户注册的错误处理程序
优点:错误处理能力完全由用户控制,用户即可以选择忽略错误,也可以调用上报程序讲错误上报给监控系统
良好的TypeScript类型支持
TypeScript(TS)是由微软开源的编程语言,是JavaScript的超集能够为其提供类型支持
使用TS的好处:代码即文档、编辑器自动提示、一定程度上能够避免低级bug、代码的可维护性更强等
使用TS编写代码与对TS类型支持友好是两件事。想要做到完善的类型支持除了要做类型推导从而做到更好的类型支持外,还要考虑对TSX的支持
第三章 VUE.js 3的设计思路
声明式地描述UI
Vue.js3是声明式的UI框架——用户在使用Vue.js3开发页面时,是声明式地描述UI的
编写前端页面涉及的内容
DOM元素:如div标签、a标签
属性:如href属性、id、class属性
事件:如click、keydown等
元素的层级结构:DOM树的层级结构,既有子节点又有父节点
解决方案1:使用模板来声明式的描述UI
使用与HTML标签一致的方式来描述DOM元素:如<div></div>
使用与HTML标签一致的方式来描述属性,如<div id="app">
使用:或v-bind来描述动态绑定的属性
使用@或v-on来描述事件
使用与HTML标签一致的方式来描述层级结构:如<div><span></span></div>
解决方案2:使用Javascript对象描述UI——虚拟DOM
优点:比使用模板更加灵活
初始渲染器
渲染器:把虚拟DOM渲染为真实的DOM
虚拟DOM的组成
标签名:tag
对象:props
描述标签子节点的children
渲染器renderer的实现思路
创建元素
为元素添加属性和事件
处理children
组件
本质
组件就是一组DOM元素的封装,这组DOM元素就是组件要渲染的内容
表达方式
定义一个函数来代表组件,而函数的返回值就代表组件要渲染的内容
使用一个JavaScript对象来表达组件,该对象有一个函数render,其返回值代表组件要渲染的内容也就是虚拟DOM
模板的工作原理
编译器的作用
编译器会把模板内容编译成渲染函数并添加到<script>标签块的组件对象上
无论是使用模板还是直接手写渲染函数,对于一个组件来说,它要渲染的内容最终都是通过渲染函数产生的,然后渲染器再把渲染函数返回的虚拟DOM渲染为真实的DOM
Vue.js是各个模块组成的有机整体
组件的实现依赖于渲染器,模板的编译依赖于编译器
编译后生成的代码是根据渲染器和虚拟DOM的设计决定的
编辑器和渲染器之间存在信息交流,它们之间交流的媒介就是虚拟DOM对象
Vue.js的各个模块之间是相互关联、相互制约的,共同构成一个有机整体
组件渲染函数:一个组件要渲染的内容要通过渲染函数render来描述,Vhe.js会根据组件的render函数的返回值拿到虚拟DOM,然后就把可以把组件的内容渲染出来了
IIFE:立即调用的函数表达式
副作用:当调用函数时会对外部产生影响,如修改了全局变量
Diff算法的作用:计算出 Virtual DOM 中被改变的部分,然后针对该部分进行原生DOM操作,而不用重新渲染整个页面