第 51 本《大前端三剑客——Vue+React+Flutter》
简介
随着移动互联时代到万物互联超级终端时代的变化,开发者也从移动互联开发转型到万物互联时代的超级终端开发,本书全面讲解大前端时代的核心技术栈和核心开发语言,并通过一本书融汇贯通,本书是普通前端开发者通向大前端架构师的桥梁,本书中涉及大量案例和企业一线实践操作经验,是前端开发者转型为大前端架构师的必备书籍
本书共 4 篇 15 个章。第一篇为基础篇(第 1~6 章),主要介绍大前端的发展趋势,大前端的基础开发语言(ES6、TypeScript、Dart),大前端构建工具和前端工程化体系,以及大前端的包管理和如何搭建一个企业级的脚手架工具。从第二篇至第四篇(第 7~15 章),分别介绍 Vue 3、React 和 Flutter 2 三大主流框架,帮助开发者学习和掌握常用的框架用法和生态体系。 学习本书内容,需要具备一定的 HTML、CSS、JS 基础知识,本书可以作为前端开发者提升技能的工具书也可以作为普通开发者从网页开发过渡到万物互联开发的书籍
阅读笔记
《大前端三剑客——Vue+React+Flutter》 徐礼文 195个笔记
◆ 点评
2024/5/26 认为好看 本书总体很不错,作为一个工作几年的前端开发工程师,读完之后,感觉对自身前端技术体系塑造和完善有了更好的清晰认识和学习规划。毕竟现在是大前端发展趋势时代,前端行业领域不在局限于曾经的“切图仔”工作了,而是可以开发各个领域及跨端、跨平台的应用产品了。惊叹技术发展的飞速,历史的车轮总是不停地向前驶进。虽有感慨学不动了的情绪,但也不得不向前看,因为自身的职业技能也是需要不断地提升,不断地完善自己的工作能力要求,只做增量的内卷,不然就会被社会无情的淘汰吧,共勉
作者讲的还是比较全面的,虽然不够深入,但对于构建技术广度和自身技术体系还是非常有帮助的,很多知识点或技术实际工作中可能应用不到,但不妨碍我们了解学习。从前端发展趋势讲起、到常用的基础知识点、相关构建工具的使用、主流编程语法知识、前端工程化相关知识、主流框架使用及源码原理、组件库开发实战等方面由浅入深,学习到很多框架整体及细节开发的知识点,结合很多项目代码片段的说明,可以保证上手实践的练习,对于了解当前大前端方面的知识还是收获比较大的
当然,也深刻了解到,干技术开发这一行,虽然不可能干一辈子,但起码是对于现在的或年轻的自己来说,是最低成本且高回报的投资,加油哈~💪🏻
◆ 第1篇 基础篇
前端的概念已经从传统的网页开发发展到了所有能够使用JavaScript语言开发的应用领域,这就是所谓大前端的由来。
大前端的概念可以从不同的角度来理解,可以分为广义的“大前端”和狭义的“大前端”。广义的“大前端”是从前端技术(JavaScript语言)能解决问题的领域范围来定义的,有些领域并非传统意义上的前端领域范围,如桌面开发,但是Electron框架使用JavaScript语言进行开发,所以桌面开发也被包含在广义的“大前端”范围内。也就是说只要是使用前端技术去解决的领域都可以定义为“大前端”,
狭义的“大前端”是从全栈(传统前端+传统后端)的角度定义的,指从传统前端延伸到传统后端领域的整个全栈范围,
[插图]
对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的、但尚未被读取的属性分配到指定的对象上面。所有的键和它们的值,都会复制到新对象上面
注意,解构赋值的复制是浅复制,即如果一个键的值是复合类型的值(数组、对象、函数),则解构赋值复制的是这个值的引用,而不是这个值的副本,
扩展运算符的解构赋值不能复制继承自原型对象的属性
ES6规定,在变量声明语句之中,如果使用解构赋值,则扩展运算符后面必须是一个变量名,而不能是一个解构赋值表达式
如果扩展运算符后面不是对象,则会自动将其转换为对象
ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript语言的第7种数据类型,前6种是undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)
Symbol函数前不能使用new命令,否则会报错。这是因为生成的Symbol是一个原始类型的值,而不是对象。也就是说,由于Symbol值不是对象,所以不能添加属性。基本上,它是一种类似于字符串的数据类型
如果Symbol的参数是一个对象,就会调用该对象的toString()方法,将其转换为字符串,然后才生成一个Symbol值
Symbol()函数的参数只是表示对当前Symbol值的描述,因此相同参数的Symbol()函数的返回值是不相等的。
Symbol值不能与其他类型的值进行运算,否则会报错
由于每个Symbol值都是不相等的,所以这意味着Symbol值可以作为标识符,用于对象的属性名,这样就能保证不会出现同名的属性。这对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖
Map对象用于保存键-值对。任何值(对象或者原始值)都可以作为一个键或一个值
虽然NaN和任何值,甚至和自己都不相等(NaN!==NaN返回值为true),但是NaN作为Map的键来讲是没有区别的
Set是ES6提供的一种新的数据结构,类似于数组,但是成员的值都是唯一的,没有重复的值。Set本身是一个构造函数,用来生成Set数据结构。
Set对象的特点如下: (1)Set对象允许存储任何类型的唯一值,无论是原始值还是对象引用。 (2)Set中的元素只会出现一次,即Set中的元素是唯一的。 (3)NaN和undefined都可以被存储在Set中,NaN之间被视为相同的值(尽管NaN!==NaN)。 (4)Set()函数可以接收一个数组(或者具有iterable接口的其他数据结构)作为参数,用来初始化
通过Set进行交集、并集、差集计算
Proxy是ES6中新增的一个特性。Proxy可以监听对象本身发生了什么事情,并在这些事情发生后执行一些相应的操作。利用Proxy可以对一个对象有很强的追踪能力,同时在数据绑定方面也非常有用
Proxy中提供了13种拦截监听的方法,这里重点介绍Proxy拦截方法中最重要的set()和get()方法的用法
Proxy相比较Object.defineProperty具备的优势如下: (1)Proxy可以直接监听整个对象而非属性。 (2)Proxy可以直接监听数组的变化。 (3)Proxy有13种拦截方法,如ownKeys、deleteProperty、has等。 (4)Proxy返回的是一个新对象,只操作新的对象达到目的,而Object.defineProperty只能遍历对象属性进行直接修改;有属性,也无法监听动态新增的属性,但Proxy可以。
Reflect是ES6为操作对象而提供的新API,Reflect设计的目的有以下几点: (1)主要是优化了语言内部的方法,把Object对象的一些内部方法放在Reflect上,例如Object.defineProperty()。 (2)修改Object方法的返回值,例如:Object.definePropery(obj,name,desc)无法定义属性时报错,而Reflect.definedProperty(obj,name,desc)的返回值为false。 (3)让Object变成函数的行为,如以前的name in obj和delete obj[name]使用新方法Reflect.has(name)和Reflect.deleteProperty(obj,name)替代。 (4)Reflect方法和Proxy方法一一对应。主要是为了实现本体和代理的接口一致性,方便用户通过代理操作本体。
通过Reflect和Proxy组合实现观察者模式
ES6为异步操作带来了3种新的解决方案,分别是Promise、Generator、async/await。规避了异步编程中回调地狱问题,解决了异步编程中异常难以处理、编程代码复杂等问题
Promise的实例对象有3种状态:pending、fulfilled、rejected。Promise对象根据状态来确定执行哪种方法。Promise在实例化的时候默认状态为pending。 (1)pending:等待状态,如正在进行网络请求时,或者定时器没有到时间,此时Promise的状态就是pending状态。 (2)fulfilled:完成状态,当主动回调了resolve时,就处于该状态,并且会回调.then()方法。 (3)rejected:拒绝状态,当主动回调了reject时,就处于该状态,并且会回调.catch()方法
Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject方法处理的失败状态的值
Promise.race是赛跑的意思,意思就是Promise.race([p1,p2,p3])里面哪个结果获得得快,就返回哪个结果,不管结果本身是成功状态还是失败状态
Promise有以下两个优点: (1)Promise将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。 (2)Promise对象提供统一的接口,使控制异步操作更加容易。
Promise的缺点如下: (1)无法取消Promise,一旦新建它就会立即执行,无法中途取消。 (2)如果不设置回调函数,则Promise内部抛出的错误不会反映到外部。 (3)当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
为了解决Promise的问题,ES6中提供了另外一种异步编程解决方案(Generator)。Generator()函数的优点是可以随心所欲地交出和恢复函数的执行权,yield用于交出执行权,next()用于恢复执行权。
yield语句后面的表达式,只有当调用next()方法、内部指针指向该语句时才会执行,因此等于为JavaScript提供了手动的“惰性求值”(Lazy Evaluation)的语法功能
yield句本身没有返回值(返回undefined)。next()方法可以带一个参数,该参数会被当作上一个yield语句的返回值,如代码示例2-148所示
async函数是ES7提出的一种新的异步解决方案,它与Generator()函数并无大的不同。语法上只是把Generator()函数里的∗换成了async,将yield换成了await,它是目前为止最佳的异步解决方案
与Generator()函数比较起来,async/await具有以下优点: (1)内置执行器。这表示它不需要不停地使用next来使程序继续向下进行。 (2)更好的语义。async代表异步,await代表等待。 (3)更广的适用性。await命令后面可以跟Promise对象,也可以是原始类型的值。 (4)返回的是Promise。
async()函数返回一个Promise对象,可以使用then()方法添加回调函数。当函数执行时,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句
JavaScript实现的面向对象编程是建立在函数原型上的面向对象编程
ES6引入了传统的面向对象语言中的类概念。类的写法让对象原型的写法更加清晰、更像面向对象编程的语法,也更加通俗易懂
ES6类和模块的内部默认采用严格模式,所以不需要使用use strict指定运行模式。只要将代码写在类或模块中,就只有严格模式可用。编写的所有代码其实都运行在模块中,所以ES6实际上把整个语言升级到了严格模式。
constructor()方法是类的构造函数,用于传递参数,返回实例对象。当通过new命令生成对象实例时,会自动调用该方法。如果没有显式定义,则类内部会自动创建一个constructor()
静态属性或者方法,又被叫作类属性或者类方法,使用static关键字修饰属性或者方法就是类属性或者类方法,静态的属性或方法需要通过类直接调用,不能通过实例进行调用
super这个关键字,既可以当作函数使用,也可以当作对象使用。第1种情况,super()作为函数调用时,代表父类的构造函数,只能用在子类的构造函数中。ES6要求,子类的构造函数必须执行一次super()函数。第2种情况,super作为对象时,指代父类的原型对象。
ECMAScript 6模块化的特点如下: (1)一个ECMAScript 6的模块就是一个JS文件。 (2)模块只会加载和执行一次,如果下次再加载同一个文件,则直接从内存中读取。 (3)一个模块就是一个单例对象。 (4)模块内声明的变量都是局部变量,不会污染全局作用域。 (5)模块内部的变量或者函数可以通过export导出。 (6)模块与模块直接可以相互依赖和相互引用。
模块化实现了把一个复杂的系统分成各个独立的功能单元,每个单元可以独立设计和自由组合,模块化的优点如下: (1)减少命名冲突。 (2)避免引入时的层层依赖。 (3)可以提升执行效率。 (4)架构清晰,可灵活开发。 (5)降低耦合,可维护性高。 (6)方便模块功能调试、升级及模块间的组合拆分。 模块化也存在一些缺点: (1)损耗性能。 (2)系统分层,调用链长。 (3)目前浏览器无法使用import导入模块,需要第三方打包工具的支持。
export default与export的主要区别有两个: (1)不需要知道导出的具体变量名。 (2)导入(import)时不需要{}。
export只支持在最外层静态导出,并且只支持导出变量、函数、类
当export有多个函数或变量时,如导出多个内容,可以使用∗as关键字来导出所有函数及变量,同时as后面跟着的名称作为该模块的命名空间
从模块文件中导入单个或多个函数,与∗as namepage方式不同,这个是按需导入。如代码示例2-178所示。 代码示例2-178 
前端构建工具能帮助前端开发人员把编写的Less、SASS等代码编译成原生CSS,也可以将多个JavaScript文件合并及压缩成一个JavaScript文件,对前端不同的资源文件进行打包,它的作用就是通过将代码编译、压缩、合并等操作,来减少代码体积,减少网络请求,方便在服务器上运行。
[插图]
[插图]
Webpack相比较其他构建工具,具有以下几个优点。 (1)智能解析:对CommonJS、AMD、CMD等支持得很好。 (2)代码拆分:创建项目依赖树,每个依赖都可拆分成一个模块,从而可以按需加载。 (3)Loader:Webpack核心模块之一,主要处理各类型文件编译转换及Babel语法转换。 (4)Plugin(插件系统):强大的插件系统,可实现对代码压缩、分包chunk、模块热替换等,也可实现自定义模块、对图片进行base64编码等,文档非常全面,自动化工作都有直接的解决方案。 (5)快速高效:开发配置可以选择不同环境的配置模式,可选择打包文件使用异步I/O和多级缓存提高运行效率。 (6)微前端支持:Module Federation也对项目中如何使用微型前端应用提供了一种解决方案。 (7)功能全面:最主流的前端模块打包工具,支持流行的框架打包,如React、Vue、Angular等,社区全面。
Webpack的工作方式是把项目当作一个整体,通过一个给定的主文件(如index.js),Webpack将从这个主文件开始找到项目的所有依赖文件,使用Loader处理它们,最后打包为一个(或多个)浏览器可识别的JavaScript文件
NPM scripts原理:当执行npm run xx命令时,实际上运行的是package.json文件中的xx命令
Webpack的模块打包工具通过分析模块之间的依赖,最终将所有模块打包成一份或者多份代码包,供HTML直接引用
Webpack最核心的概念如下。 (1)Entry:入口文件,Webpack会从该文件开始进行分析与编译。 (2)Output:出口路径,打包后创建包的文件路径及文件名。 (3)Module:模块,在Webpack中任何文件都可以作为一个模块,会根据配置使用不同的Loader进行加载和打包。 (4)Chunk:代码块,可以根据配置将所有模块代码合并成一个或多个代码块,以便按需加载,提高性能。 (5)Loader:模块加载器,进行各种文件类型的加载与转换。通过不同的Loader,Webpack有能力调用外部的脚本或工具,实现对不同格式文件的处理,例如分析及将scss转换为css,或者把ES6+文件(ES6和ES7)转换为现代浏览器兼容的JS文件,对React的开发而言,合适的Loader可以把React中用到的JSX文件转换为JS文件。 (6)Plugin:拓展插件,可以通过Webpack相应的事件钩子,介入打包过程中的任意环节,从而对代码按需修改。
Loader的作用: (1)Loader让Webpack能够去处理那些非JavaScript文件。 (2)Loader专注实现资源模块加载从而实现模块的打包
Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,例如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(例如Object.assign)都不会转码。 转译新的API,需要借助polyfill方案去解决,可使用@babel/polyfill或@babel/plugin-transform-runtime,二选一即可。
Module Federation使JavaScript应用得以在客户端或服务器上动态运行另一个包的代码。Module Federation主要用来解决多个应用之间代码共享的问题,可以更加优雅地实现跨应用的代码共享。
[插图]
Webpack-dev-server是Webpack官方推出的一个开发工具,它提供了一个开发服务器,并且集成了自动编译和自动刷新浏览器等一系列功能的安装指令
[插图]
IgnorePlugin是Webpack的内置插件,其作用是忽略第三方包指定目录。
在使用Webpack进行打包时,对于依赖的第三方库,例如React、Redux等不会修改的依赖,可以让它和自己编写的代码分开打包,这样做的好处是每次更改本地代码文件时,Webpack只需打包项目本身的文件代码,而不会再去编译第三方库,第三方库在第一次打包时只打包一次,以后只要不升级第三方包,Webpack就不会对这些库打包,这样可以快速地提高打包速度,因此为了解决这个问题,DllPlugin和DllReferencePlugin插件就产生了。
Webpack 4.0后通过开启mode:production即可开启摇树优化功能。 Tree Shaking摇掉代码中未引用部分(dead-code),production模式下会自动在使用Tree Shaking打包时除去未引用的代码,其作用是优化项目代码。
PurgeCSS是一个能够通过字符串对比来决定移除不需要的CSS的工具
插件件随Webpack构建的初始化到最后文件生成的整个生命周期,插件的目的是解决Loader无法实现的其他事情。另外,插件没有像Loader那样的独立运行环境,所以插件只能在Webpack里面运行
Webpack通过Plugin机制让其更加灵活,以适应各种应用场景。在Webpack运行的生命周期中会广播出许多事件,Plugin可以监听这些事件,在合适的时机通过Webpack提供的API改变输出结果。
Rollup是下一代ES6模块化工具,它最大的亮点是利用ES6模块设计,生成更简洁、更简单的代码。Rollup更适合构建JavaScript库
Rollup将所有资源放到同一个地方,一次性加载,利用Tree Shaking特性来剔除未使用的代码,减少冗余。它有以下优点: (1)配置简单,打包速度快。 (2)自动移除未引用的代码(内置Tree Shaking)。 但是它也有以下几个不可忽视的缺点: (1)开发服务器不能实现模块热更新,调试烦琐。 (2)浏览器环境的代码分割时依赖AMD。 (3)加载第三方模块比较复杂。
[插图]
ESBuild是一个用Go语言编写的JavaScrip、TypeScript打包工具
ESBuild是由Figma的CTO(Evan Wallace)基于Go语言开发的一款打包工具,相比传统的打包工具,主打性能优势在于构建速度上可以快10~100倍
ESBuild的主要特征有以下几点: (1)打包时极致的速度,不需要缓存。 (2)支持Source Maps。 (3)支持压缩、支持插件。 (4)支持ES6和CommonJS模块。 (5)支持ES6模块的Tree Shaking。 (6)提供JavaScript和Go的API。 (7)支持TypeScript和JSX的语法
Vite是Vue框架的作者尤雨溪为Vue 3开发的新的构建工具,目的是替代Webpack,其原理是利用现代浏览器已经支持ES6的动态import,当遇到import时会发送一个HTTP请求去加载文件,Vite会拦截这些请求,进行预编译,省去了Webpack冗长的打包时间,提升开发体验
Vite借鉴了Snowpack,在生产环境使用Rollup打包。相比Snowpack,它支持多页面、库模式、动态导入、自动polyfill等
Vite解决了Webpack开发阶段Dev Server冷启动时间过长,HMR(热更新)反应速度慢的问题。早期的浏览器基本上不支持ES Module,这个时候需要使用Webpack、Rollup、Parcel等打包构建工具来提取、处理、连接和打包源码,但是当项目变得越来越复杂,模块数量越来越多时,特别是在开发过程中,启动一个Dev Server所需要的时间也会变得越来越长,当编辑代码、保存、使有HRM功能时,可能也要花费几秒才能反映到页面中。这种开发体验是非常耗时的,同时体验也非常差,而Vite就是为解决这种开发体验上的问题的。总体来讲Vite有以下优点: (1)去掉打包步骤,快速地冷启动。 (2)及时进行模块热更新,不会随着模块变多而使热更新变慢。 (3)真正按需编译。
在介绍Vite原理之前,需要先了解打包模式(Bundle)和无打包模式(Bundleless)。Vite借鉴了Snowpack,采用无打包模式。无打包模式只会编译代码,不会打包,因此构建速度极快,与打包模式相比时间缩短了90%以上。
[插图]
开发模式:Vite提供了一个开发服务器,然后结合原生的ESM,当代码中出现import时,发送一个资源请求,Vite开发服务器拦截请求,根据不同的文件类型,在服务器端完成模块的改写(例如单文件的解析、编译等)和请求处理,实现真正的按需编译,然后返回浏览器。请求的资源在服务器端按需编译返回,完全跳过了打包这个概念,不需要生成一个大的包。服务器随启随用,所以开发环境下的初次启动是非常快的,而且热更新的速度不会随着模块增多而变慢,因为代码改动后,并不会有打包的过程。
生产模式:利用Rollup来构建源码,Vite将需要处理的代码分为以下两大类。 第三方依赖:这类代码大部分是纯JavaScript代码,而且不会经常变化,Vite会通过pre-bundle的方式来处理这部分代码。Vite 2使用ESBulid来构建这部分代码,ESBuild是基于Go语言实现的,处理速度会比用JavaScript写的打包器快10~100倍,这也是Vite为什么在开发阶段处理代码很快的一个原因。 业务代码:通常这部分代码不是纯的JavaScript(例如JSX、Vue等)代码,经常会被修改,而且也不需要一次性全部加载(可以根据路由进行代码分割后加载)
TypeScript从一开始就提出了自己的设计目标,主要目标如下: (1)遵循当前及未来出现的ECMAScript规范。 (2)为大型项目提供构建机制(通过Class、接口和模块等支撑)。 (3)兼容现存的JavaScript代码,即任何合法的JavaScript程序都是合法的TypeScript程序。 (4)对于发行版本的代码没有运行开销。使用过程可以简单划分为程序设计阶段和执行阶段。 (5)成为跨平台的开发工具,TypeScript使用Apache的开源协议作为开源协议,并且能够在所有主流的操作系统上安装和执行
常见的装饰器有属性装饰器、方法装饰器、参数装饰器、类装饰器。装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参),装饰器是ES7的标准特性之一。装饰器的执行顺序:属性>方法>方法参数>类
属性装饰器会被应用到属性描述上,可以用来监视、修改或者替换属性的值。属性装饰器会在运行时传入下列两个参数:(1)对于静态成员来讲是类的构造函数,对于实例成员来讲是类的原型对象。(2)成员的名字
方法装饰器会被应用到方法描述上,可以用来监视、修改或者替换方法定义。方法装饰器会在运行时传入下列3个参数:(1)对于静态成员来讲是类的构造函数,对于实例成员来讲是类的原型对象。(2)成员的名字。(3)成员的属性描述符
参数装饰器表达式会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元素数据,传入下列3个参数:(1)对于静态成员来讲是类的构造函数,对于实例成员来讲是类的原型对象。(2)方法的名字。(3)参数在函数参数列表中的索引
模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入,并以此向其他模块提供相应的功能
命名空间和模块的区别:命名空间是内部模块,主要用于组织代码,避免命名冲突;模块ts是外部模块的简称,侧重代码的复用,一个模块里可能会有多个命名空间。
Dart是面向对象、类定义的、单继承的语言。它的语法类似Java语言,可以转译为JavaScript,支持接口(interfaces)、混入(mixins)、抽象类(abstract classes)、具体化泛型(reified generics)、可选类型(optional typing)和sound type system
Dart的特性主要有以下几点:(1)执行速度快,Dart是采用AOT(Ahead Of Time)编译的,可以编译成快速的、可预测的本地代码,也可以采用JIT(Just In Time)编译。(2)易于移植,Dart可编译成ARM和x86代码,这样Dart可以在Android、iOS和其他系统运行。(3)容易上手,Dart充分吸收了高级语言的特性,如果开发者已经熟悉C++、C、Java等其中的一种开发语言,基本上就可以快速上手Dart开发。(4)易于阅读,Dart使Flutter不需要单独的声明式布局语言(XML或JSX),或者单独的可视化界面构建器,这是因为Dart的声明式编程布局易于阅读。(5)避免抢占式调度,Dart可以在没有锁的情况下进行对象分配和垃圾回收,和JavaScript一样,Dart避免了抢占式调度和共享内存,因此不需要锁
MonoRepo(Monolithic Repository,单体式仓库)并不是一个新的概念。从软件开发早期,就已经广泛使用这种模式了。这种模式的一个核心就是用一个Git仓库来管理所有的源码。除了这种模式以外,另一个比较受推崇的模式就是MultiRepo(Multiple Repository),也就是用多个Git仓库来管理自己的源码
MonoRepo模式的多包管理工具比较多,目前比较流行的有以下两个。(1)Lerna:单一代码库管理器,Lerna是由Babel团队推出的一个多包管理工具。(2)Yarn Workspace:用一个命令在多个地方安装和更新Node.js的依赖项
Yarn Workspace(工作区)是Yarn提供的MonoRepo的依赖管理机制,从Yarn 1.0开始默认支持,用于在代码仓库的根目录下管理多个package的依赖。Workspace能更好地统一管理多个项目的仓库,既可在每个项目下使用独立的package.json文件管理依赖,又可便利地享受一条yarn命令安装或者升级所有依赖等。更重要的是可以使多个项目共享同一个node_modules目录,提升开发效率和降低磁盘空间的占用
1 使用Yarn Workspace的好处(1)当开发多个互相依赖的package时,Workspace会自动对package的引用来设置软链接(symlink),比yarn link命令更加方便,并且链接仅局限在当前Workspace中,不会对整个系统造成影响。(2)所有package的依赖会安装在最根目录的node_modules下,节省磁盘空间,并且给了Yarn更大的依赖优化空间。(3)所有package使用同一个yarn.lock文件,更少造成冲突且易于审查
Lerna的依赖管理基于Yarn/NPM,但是安装依赖的方式和Yarn Workspace有些差异:Yarn Workspace只会在根目录安装一个node_modules,这有利于提升依赖的安装效率和不同package间的版本复用,而Lerna默认会进入每个package中运行Yarn/NPM install,并在每个package中创建一个node_modules。目前社区中最主流的方案,也是Yarn官方推荐的方案,则集成Yarn Workspace和Lerna。使用Yarn Workspace来管理依赖,使用Lerna来管理NPM包的版本发布
前端脚手架,主要解决以下几个主要问题:(1)统一团队开发风格,降低新人上手成本。(2)规范项目开发流程,减少重复性工作。(3)提供一键实现项目的创建、配置、开发、插件等,让开发者将更多时间专注于业务
◆ 第2篇 Vue 3框架篇
Vue借鉴Angular和React框架核心思想,取其精华去其糟粕。Angular框架核心思想是MVVM模式,React框架核心思想是基于虚拟DOM的组件化,而Vue的核心思想为数据驱动和组件化
(1)Object.defineProperty无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值时不能实时响应。为了解决这个问题,Vue 2使用几种方法来监听数组,如push()、pop()、shift()、unshift()、splice()、sort()、reverse();由于只针对以上方法进行了hack处理,所以其他数组的属性无法检测到,还是具有一定的局限性。(2)Object.defineProperty只能劫持对象的属性,因此需要对每个对象的每个属性进行遍历。在Vue 2.x里,通过递归+遍历data对象实现对数据的监控,如果属性值也是对象,则需要深度遍历,显然如果能劫持一个完整的对象才是更好的选择,新增的属性还是通过set()方法来添加监听,有一定的局限性
Vue 3采用ES6的Proxy代替ES5的Object.defineProperty,Proxy有以下优点:(1)可以劫持整个对象,并返回一个新的对象。(2)Proxy支持13种劫持操作,对象劫持操作更加方便
1)运行时+编译器vs仅运行时的区别如果需要在客户端上编译模板(将字符串传递给template选项,或者使用元素的DOM内HTML作为模板挂载到元素),将需要编译器,因此需要完整的构建版本
Rollup是一个JavaScript模块打包器,可以将小块代码编译成大块复杂的代码,在打包模块过程中,通过Tree Shaking的方式,利用ES6模块能够静态分析语法树的特性,剔除各模块中最终未被引用的方法,通过仅保留被调用的代码块来减小bundle文件的大小。一般情况下,开发应用时使用Webpack,开发库时使用Rollup
组件的data选项是一个函数。Vue在创建新组件实例的过程中调用此函数。它应该返回一个对象,Vue会通过响应式系统将其重新包装,并以$data的形式存储在组件实例中
注意:一般不要直接在模板表达式中调用方法,在模板表达式中可以使用计算属性代替方法调用
注意:当变更(不是替换)对象或数组并使用deep选项时,旧值将与新值相同,因为它们的引用指向同一个对象/数组。Vue不会保留变更之前值的副本
Vue中组件的视图定义有3种模式:第一,在挂载点指定的位置内创建视图;第二,通过template选项创建视图;第三,通过render()函数,使用h()方法创建虚拟DOM树
注意:template和render()函数不要同时定义,如果同时定义,则template选项会被忽略,优先渲染render中定义的VNode视图
插值表达式是Vue框架提供的一种在HTML模板中绑定数据的方式,使用方式绑定Vue实例中data中的数据变量会将绑定的数据实时地显示出来
插值表达式支持的写法有以下4种:变量、JS表达式、三目运算符、方法调用
注意:括起来的区域就是一个JS语法区域,在里面可以写受限的JS语法。不能写var a=10;分支语句或循环语句
如果不想改变标签的内容,则可以通过v-once指令一次性地插值,当数据改变时,插值处的内容不会更新
指令(Directive)是Vue对HTML标签新增加的、拓展的属性(也称为特性),这些属性不属于标准的HTML属性,只有Vue认为是有效的,能够处理它。指令的职责是当表达式的值改变时,将其产生的连带影响响应式(Reactive)地作用于DOM,也就是双向数据绑定
v-if是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。v-if是惰性的:如果在初始渲染时条件为假,则什么也不做,直到条件第一次变为真时,才会开始渲染条件块。相比之下,v-show就简单得多了,不管初始条件是什么,元素总会被渲染,并且只是简单地基于CSS进行切换。一般来讲,v-if有更高的切换开销,而v-show有更高的初始渲染开销,因此,如果需要非常频繁地切换,则使用v-show较好;如果在运行时条件很少改变,则使用v-if较好
注意:不要使用对象或数组之类的非基本类型值作为v-for的key。应用字符串或数值类型的值
2 v-memo记住一个模板的子树,在元素和组件上都可以使用。该指令接收一个固定长度的数组作为依赖值进行记忆比对。如果数组中的每个值都和上次渲染时相同,则整个该子树的更新会被跳过
注意:在v-for中使用v-memo时,确保它们被用在同一个元素上。v-memo在v-for内部是无效的
1 多事件处理器事件处理程序中可以有多种方法,这些方法由逗号运算符分隔
注意:使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生,因此,用@click.prevent.self会阻止元素本身及其子元素的单击的默认行为,而@click.self.prevent只会阻止对元素自身的单击的默认行为
注意:不要把.passive和.prevent一起使用,因为.prevent将会被忽略,同时浏览器可能会展示一个警告。需要记住,.passive会告诉浏览器开发者不想阻止事件的默认行为
组件的命名包括标签名命名和对象名命名
除index.vue之外,其他.vue文件统一用PascalBase(首字母大写命名)风格
注意:不同于prop,事件名不存在任何自动化的大小写转换,而是触发的事件名需要完全匹配监听这个事件所用的名称
2)验证抛出的事件与prop类型验证类似,如果使用对象语法而不是数组语法定义发出的事件,则可以验证它。要添加验证,为事件分配一个函数,该函数接收传递给$emit调用的参数,并返回一个布尔值以指示事件是否有效
1)beforeCreate在实例初始化之后,数据观测(Data Observer)和event/watcher事件配置之前被调用。2)created实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测、属性和方法的运算、watch/event事件回调,然而,挂载阶段还没开始,$el属性目前不可见。3)beforeMount在挂载开始之前被调用:相关的render()函数首次被调用。
4)mountedel被新创建的vm.$el替换,并挂载到实例上之后调用该钩子。5)beforeUpdate数据更新时调用,发生在虚拟DOM重新渲染和打补丁之前。可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。6)updated由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件DOM已经更新,所以现在可以执行依赖于DOM的操作,然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
7)beforeUnmount实例销毁之前调用。在这一步,实例仍然完全可用。8)unmountedVue实例销毁后调用。调用后,Vue实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
9)renderTracked将在跟踪虚拟DOM重新渲染时调用,此事件告诉开发者哪个操作跟踪了组件及该操作的目标对象和键。10)renderTriggered与renderTraced功能类似,它将告诉开发者是什么操作触发了重新渲染,以及该操作的目标对象和键
作用域插槽允许在自定义组件的组件模板内定义的Slot插槽中给Slot添加属性,用来把组件内的值传递到组件声明的标签内。这是一种非常好的用法,可以在组件外部定义一些DOM元素,以便很好地扩展组件的模板视图
在大型应用中,可能需要将应用分割成小一些的代码块,并且只在需要时才从服务器加载一个模块。Vue 3.x提供了一个函数defineAsyncComponent,以此来简化使用异步组件。组件内使用异步组件
Composition API的优点如下:(1)提供了更完善的TS支持。(2)组件拥有了更加良好的代码组织结构。(3)相同的代码逻辑在不同的组件中进行了完整复用。Composition API提供了以下几个函数。(1)setup:组合API的方法都写在这里面。(2)ref:定义响应式数据字符串bool。(3)reactive:定义响应式数据对象。(4)watchEffect:监听数据变化。(5)watch:监听数据变化。(6)computed:计算属性。(7)toRefs:解构响应式对象数据。(8)新的生命周期的Hook。
setup()函数会在beforeCreate()之后且在created()之前执行
注意:setup中应避免使用this,因为无法获取组件实例;同理,setup的调用发生在data、property、computed property、methods被解析之前,同样在setup中无法获取
注意:reactive()中传递的参数必须是JSON对象或者数组,如果传递了其他对象,如new Date(),则可以使用ref()函数处理基本数据,变成响应式数据
当把ref()创建出来的响应式数据对象挂载到reactive()上时,会自动把响应式数据对象展开为原始的值,不需通过.value就可以直接被访问
toRef是将某个对象中的某个值转化为响应式数据,其接收两个参数,第1个参数为obj对象,第2个参数为对象中的属性名
ref()是对原数据的一个复制,不会影响原始值,同时响应式数据对象的值改变后会同步更新视图。toRef是对原数据的一个引用,会影响原始值,但是响应式数据对象的值改变后会不会更新视图
2 watchEffect与watch的区别watchEffect与watch的区别主要有以下三点:(1)不需要手动传入依赖。(2)每次初始化时会执行一次回调函数来自动获取依赖。(3)无法获取原值,只可以得到变化后的值
setup()函数是处于生命周期函数beforeCreate()和Created()两个钩子函数之间的函数,也就是说,在setup()函数中无法使用data()和methods()中的数据和方法
在script setup模式下,不必再配合async就可以直接使用await了,在这种情况下,组件的setup自动变成async setup
Vue提供了Transition的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡:(1)条件渲染(使用v-if)。(2)条件展示(使用v-show)。(3)动态组件(使用:is)。(4)组件根节点
在插入、更新或从DOM中移除项时,Vue提供了多种应用转换效果的方法,包括以下几种:(1)自动为CSS过渡和动画应用class。(2)集成第三方CSS动画库,例如animate.css。(3)在过渡钩子期间使用JavaScript直接操作DOM。(4)集成第三方JavaScript动画库
一个视图使用一个组件渲染,因此对于同一个路由,多个视图就需要多个组件。确保证确使用components配置(带上s)
Vue 3的响应式原理相比较Vue 2来讲,并没有本质上的变化,在语法上更新了部分函数和调用方式,在性能上有很大的提升。(1)Vue 3用ES6的Proxy重构了响应式,如new Proxy(target,handler)。(2)在Proxy的get handle里执行track()用来跟踪收集依赖(收集activeEffect)。(3)在Proxy的set handle里执行trigger()用来触发响应(执行收集的effect)。(4)effect副作用函数代替了watcher
◆ 第3篇 React框架篇
React框架的特点包括自动化的UI状态管理、高效的虚拟DOM、细颗粒的组件化开发、JSX语法支持、轻量级库等特点
很多React的核心API围绕着更容易创建更小的界面组件进行扩展,这些界面组件随后可以与其他界面组件组合,创建更大更复杂的界面组件
React突破了传统的Web标准中的UI的结构、表现形式和行为部分分别分离的原则,采用了直接在JS中定义UI,JSX很好地支持了这种要求,同时又不会让UI开发者感到任何困难,因为它看起来和在HTML文档中编写HTML代码一样,但是JSX只有被编译成JS后才可以执行
JSX允许在模板中插入数组,数组会自动展开所有成员
React自定义了一套事件处理系统,包含事件监听、事件分发、事件回调等过程。浏览器本身有事件系统接口(原生事件,Native Event),React把它重新按自己的标准包装了一下(合成事件,Synthetic Event),即大部分合成事件与原生事件的接口是一一对应的,但接口为了兼容原生的一些不同事件进行了合成,目的就是为了使用React工作在不同的浏览器上,即同时消除了IE与W3C标准实现之间的兼容问题
使用index直接当key会带来哪些风险?因为React会使用key来识别列表元素,当元素的key发生改变时,可能会导致React的Diff执行在错误的元素上,甚至导致状态错乱。这种情况在使用index时尤其常见。虽然不建议,但是在以下场景中可以使用index作为key值:(1)列表和项目是静态的,不会进行计算也不会被改变。(2)列表中的项目没有id。(3)列表永远不会被重新排序或过滤
React元素(React Element)是React中最小的基本单位,一旦创建,其子元素、属性等都无法更改,元素名使用小驼峰法命名。React元素是简单的JS对象,描述的是React虚拟DOM(结构及渲染效果)。React元素是React应用的最基础组成单位。通常情况下不会直接使用React元素。React组件的复用,本质上是为了复用这个组件返回的React元素
组件的三大内置属性包括props(组件的输入接口)、state(组件的状态)、refs(组件的引用)
每个组件都可以获取props.children,它包含组件的开始标签和结束标签之间的内容
setState()将对组件state的更改排入队列,并通知React需要使用更新后的state重新渲染此组件及其子组件。这是用于更新用户界面以响应事件处理器和处理服务器数据的主要方式。将setState()视为请求而不是立即更新组件的命令。为了更好地感知性能,React会延迟调用它,然后一次传递更新多个组件。React并不会保证state的变更会立即生效
使用componentDidUpdate或者setState()的回调函数,如setState(updater,callback),这两种方式都可以保证在应用更新后立即触发
(1)节流:基于时间的频率进行抽样更改(例如_.throttle)。(2)防抖:一段时间的不活动之后发布更改(例如_.debounce)
组件从创建到销毁的过程被称为组件的生命周期。在生命周期的各个阶段都有相对应的钩子函数,会在特定的时机被调用,被称为组件的生命周期钩子
[插图]图10-22 React 17生命周期方法
高阶组件(High Order Component,HOC)是React中对组件逻辑复用部分进行抽离的高级技术,但高阶组件并不是一个React API,它只是一种设计模式,类似于装饰器模式。具体而言,高阶组件就是一个函数,并且该函数接收一个组件作为参数,并返回一个新组件
使用高阶组件的意义主要有以下两点:(1)重用代码。有时很多React组件需要公用同一个逻辑,例如Redux中容器组件的部分,没有必要让每个组件都实现一遍shouldComponentUpdate()这些生命周期函数,把这部分逻辑提取出来,利用高阶组件的方式再次应用,就可以减少很多组件的重复代码。(2)修改现有React组件的行为。有些现成的React组件并不是开发者自己开发的,而是来自于第三方,或者即便是自己开发的,但是不想去触碰这些组件的内部逻辑,这时可以用高阶组件。通过一个独立于原有组件的函数,可以产生新的组件,对原有组件没有任何侵入性
高阶组件的实现方式可以分为两大类:代理方式的高阶组件和继承方式的高阶组件
React.PureComponent与React.Component很相似。两者的区别在于React.Component并未实现shouldComponentUpdate()函数,而React.PureComponent中以浅层对比props和state的方式实现了该函数
React.memo为高阶组件。如果组件在相同props的情况下渲染相同的结果,则可以通过将其包装在React.memo中调用,以此通过记忆组件渲染结果的方式来提高组件的性能表现。这意味着在这种情况下,React将跳过渲染组件的操作并直接复用最近一次渲染的结果
React.memo()使用场景就是用纯函数式组件频繁渲染props
React.lazy()函数能让开发者像渲染常规组件一样处理动态引入的组件。React.lazy()接收一个函数,这个函数需要动态地调用import()。它必须返回一个Promise,该Promise需要处理一个default export的React组件
Portal提供了一种将子节点渲染到存在于父组件以外的DOM节点的优秀的方案
一个Portal的典型用例是当父组件有overflow:hidden或z-index样式时,但需要子组件能够在视觉上“跳出”其容器。例如,对话框、悬浮卡及提示框
React Hook是React 16.8版本中的新增特性。Hook允许在函数式组件中使用state及其他的React特性。这是React一次重大的尝试,之前在函数式组件中无法使用状态,只能在class定义的组件中使用state。类的组件写法随着项目工程的复杂化也带来了一些问题,同时React一直倡导函数式编程,Hook实现了真正意义上的函数式组件编程
使用React Hook的好处(1)针对优化类组件的三大问题:状态逻辑难复用、趋向复杂难以维护、this指向问题。(2)在无须修改组件结构的情况下复用状态逻辑(自定义Hook)。(3)将组件中相互关联的部分拆分成更小的函数(例如设置订阅或请求数据)。(4)副作用的关注点分离:副作用指那些没有发生在数据向视图转换过程中的逻辑,如Ajax请求、访问原生DOM元素、本地持久化缓存、绑定/解绑事件、添加订阅、设置定时器、记录日志等。这些副作用都写在类组件生命周期函数中
如果想执行只运行一次的effect(仅在组件装载和卸载时执行),则可以传递一个空数组([])作为第2个参数。这就告诉React此effect不依赖于props或state中的任何值,所以它永远都不需要重复执行
使用Hook其中的一个目的就是要解决class中生命周期函数经常包含不相关的逻辑,但又把相关逻辑分离到了几个不同方法中的问题。Hook允许按照代码的用途分离它们,而不是像生命周期函数那样。React将按照effect声明的顺序依次调用组件中的每个effect
useLayoutEffect()函数签名与useEffect()类似,但它会在所有的DOM变更之后同步调用effect。可以使用它来读取DOM布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect()内部的更新计划将被同步刷新
useLayoutEffect():会在浏览器layout之后,painting之前执行。如果需要改变DOM或者DOM需要获取测量数值,除非要修改DOM并且不让用户看到修改DOM的过程,才考虑用它来读取DOM布局并同步触发重渲染,否则应当使用useEffect()。在浏览器执行绘制之前,useLayoutEffect()内部的更新计划将被同步刷新。尽可能使用标准的useEffect()以避免阻塞视图更新
useEffect():useEffect()在全部渲染完毕后才会执行。如果根本不需要与DOM交互或者DOM更改是不可观察的,那就用useEffect()
useRef()有以下特点:(1)useRef()返回一个可变的ref对象,并且只有current属性,初始值为传入的参数(initialValue)。(2)返回的ref对象在组件的整个生命周期内保持不变。(3)当更新current值时并不会渲染,而useState()新值时会触发页面渲染。(4)更新useRef()是Side Effect(副作用),所以一般写在useEffect()或Event Handler里。(5)useRef()类似于类组件的this
获取子组件的属性或方法在下面的例子中,综合使用useRef()、forwardRef()、useImperativeHandle()获取子组件数据
useMemo()有两个参数:(1)第1个参数是个函数,返回的对象指向同一个引用,不会创建新对象。(2)第2个参数是个数组,只有数组中的变量改变时,第1个参数的函数才会返回一个新的对象
自定义Hook的主要目的是重用组件中使用的逻辑。构建自己的Hook可以让开发者将组件逻辑提取到可重用的函数中
Redux的核心基于发布和订阅模式。View订阅了Store的变化,一旦Store状态发生改变就会通知所有的订阅者,View接收到通知之后会进行重新渲染
Redux遵循Flux思想,Redux将状态以一个可JSON序列化的对象的形式存储在单个Store中,也就是说Redux将状态集中存储。Redux采用单向数据流的形式,如果要修改Store中的状态,则必须通过Store的dispatch()方法。调用store.dispatch()之后,Store中的rootReducer()会被调用,进而调用所有的Reducer()函数生成一个新的state
在React Europe 2020 Conference上,Facebook内部开源了一种状态管理库Recoil。Recoil是Facebook推出的一个全新的、实验性的JavaScript状态管理库,它解决了使用现有Context API在构建较大应用时所面临的很多问题
Recoil为了解决React全局数据流管理的问题,采用分散管理原子状态的设计模式。Recoil提出了一个新的状态管理单位Atom,它是可更新和可订阅的,当一个Atom被更新时,每个被订阅的组件都会用新的值来重新渲染。如果从多个组件中使用同一个Atom,则所有这些组件都会共享它们的状态
React框架的设计初衷就是要使用JavaScript语言来构建“快速响应”的大型Web应用程序,但是“快速响应”主要受下面两方面的原因影响。(1)CPU的瓶颈:当项目变得庞大、组件数量繁多、遇到大计算量的操作或者设备性能不足时会使页面掉帧,导致卡顿。(2)IO的瓶颈:发送网络请求后,由于需要等待数据返回才能进一步操作而导致不能快速响应
浏览器有多个线程:JS引擎线程、GUI渲染线程、HTTP请求线程、事件处理线程、定时器触发线程,其中JS引擎线程和GUI渲染线程是互斥的,所以JS脚本执行和浏览器布局、绘制不能同时执行。超过16.6ms就会让用户感知到卡顿
对于浏览器来讲,页面的内容都是一帧一帧绘制出来的,浏览器刷新率代表浏览器1秒绘制多少帧。原则上说1s内绘制的帧数越多,画面表现就越细腻。当每秒绘制的帧数(FPS)达到60时,页面是流畅的,当小于这个值时,用户会感觉到卡顿。目前浏览器大多是60Hz(60帧/秒),每一帧耗时大约为16.6ms
浏览器一帧中需要完成的6件事情,具体如下:(1)处理输入事件,能够让用户得到最早的反馈。(2)处理定时器,需要检查定时器是否到时间,并执行对应的回调。(3)处理Begin Frame(开始帧),即每一帧的事件,包括window.resize、scroll、media query change等。(4)执行请求动画帧requestAnimationFrame(rAF),即在每次绘制之前会执行rAF回调。(5)进行Layout操作,包括计算布局和更新布局,即这个元素的样式是怎样的,它应该在页面如何展示。(6)进行Paint操作,得到树中每个节点的尺寸与位置等信息,浏览器针对每个元素进行内容填充
虚拟DOM是一种基于内存的JS对象,该对象简化了真实DOM的复杂性,通过Diff算法,达到局部更新DOM提升了性能的目的,但是同样Diff算法会带来性能的消耗
在React 15版本中虚拟DOM比对的过程采用了分层递归,递归调用的过程不能被终止,如果虚拟DOM的层级比较深,递归比对的过程就会长期占用主线程,而JS的执行和UI的渲染又是互斥的,此时用户要么看到的是空白界面,要么是有界面但是不能响应用户操作,处于卡顿状态,用户体验差
在页面DOM发生更新时,就需要更新虚拟DOM,此时React协调器就会执行如下操作:(1)调用函数式组件、类组件的render()方法,将返回的JSX转化为虚拟DOM。(2)将虚拟DOM和上次更新时的虚拟DOM对比。(3)通过对比找出本次更新中变化的虚拟DOM。(4)通知Renderer将变化的虚拟DOM渲染到页面上
React 16版本中提出了两种解决方案:Concurrent(并行渲染)与Scheduler(调度)。(1)Concurrent:将同步的渲染变成可拆解为多步的异步渲染,这样可以将超过16ms的渲染代码分几次执行。(2)Scheduler:调度系统,支持不同渲染优先级,对Concurrent进行调度。当然,调度系统对低优先级任务会不断提高优先级,所以不会出现低优先级任务总得不到执行的情况。为了保证不产生阻塞的感觉,调度系统会将所有待执行的回调函数存在一份清单中,在每次浏览器渲染时间分片间尽可能地执行,并将没有执行完的内容保留到下个分片处理
组件库作为基础设施,可以根据原子化理论构建企业的组件化体系,把组件分为基础组件库、业务组件库和模块组件库等。组件库就像一个设计好的积木块,可以像堆积木一样快速拼装成不同的产品,从而提升团队的交付速度和交付质量
◆ 第4篇 Flutter 2框架篇
2021年3月,谷歌发布了Flutter 2版本,这是Flutter的重要里程碑版本,该版本是针对Web端、移动端和桌面端构建的下一代开发框架,使开发人员能够为任何平台创建美观、快速且可移植的应用程序
Flutter 2可以使用同一套代码库将本机应用程序发布到5个操作系统:iOS、Android、Windows、macOS和Linux,以及针对Chrome、Firefox、Safari或Edge等浏览器的Web体验。Flutter甚至可以嵌入汽车、电视和智能家电中,为环境计算世界提供普遍且可延展的体验
Flutter的目标是从根本上改变开发人员构建应用程序的思路,让开发者从用户体验,而不是适配的平台开始
Flutter的开发语言是由Chrome v8引擎团队的领导者Lars Bak主持开发的Dart。Dart语言语法类似于C。Dart语言为了更好地适应Flutter UI框架,在内存分配和垃圾回收方面做了很多优化