毕业论文
您现在的位置: 框架 >> 框架优势 >> 正文 >> 正文

谈谈我这些年对前端框架的理解

来源:框架 时间:2022/5/11
白癜风手术 https://m.39.net/disease/a_5390504.html

最早的时候页面是服务端渲染的,也就是PHP、JSP那些技术,服务端通过模版引擎填充数据,返回生成的html,交给浏览器渲染。那时候表单会同步提交,服务端返回结果页面的html。

后来浏览器有了ajax技术,可以异步的请求,服务端返回xml或者json。ajax最早是基于xml的,这也是它名字的由来。因为xml多了很多没必要的标签,内容比较多,所以后来json流行开来。

网页和服务端的数据交互变成了异步的,可以服务端返回json数据,浏览器里拼接html,之后渲染(浏览器里面生成dom就等同于渲染)。页面基本没啥刷新的必要了,于是后来就逐渐演变出了单页应用SPA(singlepageapplication)。

早期开发页面的时候就是基于浏览器的domapi操作dom来做渲染和交互,但是domapi比较啰嗦,而且当时浏览器的兼容性问题也比较麻烦,不同浏览器有不同的写法。为了简化dom操作和更方便的兼容各种浏览器,出现了jquery并且迅速流行开来,那个时代jquery是如日中天的。

我一直习惯把网页分为物理层和逻辑层,dom就算是物理层,jquery是操作dom的一系列工具函数,也是工作在物理层。

网页做的事情基本就是拿到数据渲染dom,并且数据改变之后更新dom,这个流程是通用的,后来逐渐出现了mvvm框架,来自动把数据的变更映射到dom,不再需要手动操作dom。也就是vue、react等现代的前端框架。我把这一层叫做逻辑层。

前端框架除了提供了数据驱动视图变化的功能以外,还支持了dom的逻辑划分,可以把一部分dom封装成组件,组件和组件之间相互组合,构成整个界面。物理层依然是dom,只是实现了数据到dom的自动映射之后,我们只需要在逻辑层写组件就可以了。

现在前端入门也不会再学物理层的操作dom的jquery了,而是直接从vue、react这种逻辑层的前端框架开始。

但是也不是说完全不需要jquery,前端框架主要解决的是数据到dom的绑定,可以变化以后自动更新dom。如果不需要更新,那么直接操作dom即可,比如各种活动页,没啥数据更新,用jquery操作dom还是很方便。

前端框架是UI=f(state)这种声明式的思想,只需要声明组件的视图、组件的状态数据、组件之间的依赖关系,那么状态改变就会自动的更新dom。而jquery那种直接操作dom的工具函数库则是命令式的。

对于视图的描述这件事react和vue用了不同的方案,react是给js扩展了jsx的语法,由babel实现,可以在描述视图的时候直接用js来写逻辑,没啥新语法。而vue是实现了一套template的DSL,引入了插值、指令、过滤器等模版语法,相对于jsx来说更简洁,template的编译器由vue实现。

vuetemplate是受限制的,只能访问data,prop、method,可以静态的分析和优化,而react的jsx因为直接是js的语法,动态逻辑比较多,没法静态的做分析和优化。

但是vuetemplate也不全是好处,因为和js上下文割裂开来,引入typescript做类型推导的时候就比较困难,需要单独把所有prop、method、data的类型声明一遍才行。而react的jsx本来就是和js同一个上下文,结合typescript就很自然。

所以vuetemplate和reactjsx各有优缺点。

前端框架都是数据驱动视图变化的,而这个数据分散在每个组件中,怎么在数据变化以后更新dom呢?

数据变化的检测基本只有三种方式:watch、脏检查、不检查。

vue就是基于数据的watch的,组件级别通过Object.defineProperty监听对象属性的变化,重写数组的api监听数组元素的变化,之后进行dom的更新。

angular则是基于脏检查,在每个可能改变数据的逻辑之后都对比下数据是否变了,变了的话就去更新dom。

react则是不检查,不检查难道每次都渲染全部的dom么?也不是,不检查是因为不直接渲染到dom,而是中间加了一层虚拟dom,每次都渲染成这个虚拟dom,然后diff下渲染出的虚拟dom是否变了,变了的话就去更新对应的dom。

这就是前端框架的数据驱动视图变化的三种思路。

vue是组件级别的数据watch,当组件内部监听数据变化的地方特别多的时候,一次更新可能计算量特别大,计算量大了就可能会导致丢帧,也就是渲染的卡顿。所以vue的优化方式就是把大组件拆成小组件,这样每个数据就不会有太多的watcher了。

react并不监听数据的变化,而是渲染出整个虚拟dom,然后diff。基于这种方案的优化方式就是对于不需要重新生成vdom的组件,通过shouldComponentUpdate来跳过渲染。

但是当应用的组件树特别大的时候,只是shouldComponentUpdate跳过部分组件渲染,依然可能计算量特别大。计算量大了同样可能导致渲染的卡顿,怎么办呢?

树的遍历有深度优先和广度优先两种方式,组件树的渲染就是深度优先的,一般是通过递归来做,但是如果能通过链表记录下路径,就可以变成循环。变成了循环,那么就可以按照时间片分段,让vdom的生成不再阻塞页面渲染,这就像操作系统对多个进程的分时调度一样。

这个通过把组件树改成链表,把vdom的生成从递归改循环的功能就是reactfiber。

fiber节点相对于之前的组件节点来说,没有了parent、children这种属性,多了child、sibling、return属性。

通过fiber链表树,优化了渲染的性能。

可以看到vue的性能优化和react的性能优化是不一样的:

vue是组件级别的数据监听的方案,问题可能出现在一个属性太多watcher的时候,所以优化思路就是大组件拆分成小组件,保证每个属性不要有太多watcher。

react不监听、不检查数据变化,每次都渲染生成vdom,然后进行vdom的对比,那么优化的思路就是shouldComponentUpdate来跳过部分组件的render,而且react内部也做了组件树的链表化(fiber)来把递归改成可打断的渲染,按照时间片来逐渐生成整个vdom。

组件之间难免要有逻辑的复用,react和vue有不同的方案:

vue的组件是option对象的方式,那么逻辑复用方式很自然可以想到通过对象属性的mixin,vue2的组件内逻辑复用方案就是mixin,但是mixin很难区分混入的属性、方法的来源,比较乱,代码维护性差。但也没有更好的方案。

react刚开始也是支持mixin的,但后来废弃了。

react的组件是class和function两种形式,那么类似高阶函数的高阶组件(highorder

转载请注明:http://www.0431gb208.com/sjszjzl/244.html