【前端】 尤雨溪知乎live——不吹不黑聊聊前端框架

很早之前就买了尤大大的这一场live,一直也没有做什么整理。闲来无事,今天就把它略作整理。

本来是想再做整理放上去的,但是突然搜了一下发现 传送门 这个链接居然有写…那我就按照他写的文章样式大概再稍微做下整理吧。

框架、工具和语言的选择基于场景实现的不同

我们先定个基调。作为vue的作者,经常有其他人问vue跟其他的框架哪个好。被问多了呢我就写了一些比较在文档里面,但是呢,有人看了一些比较以后说,不客观,不满意。其实我是觉得就是说上来就问那些框架好逗问错了方向,首先框架工具语言这些东西,不谈场景都是耍流氓。前端时一个产品形态差异很大的平台,所以不同的产品需求会有很大的区别,其次开发者群体时很多样化的,出于学习背景、习惯、思维模式的差异,不同的开发者的偏好肯定不一样的。这些区别是没有办法抹平的,所以与其去浪费时间争论哪个框架更好,不如我们努力让偏好不同的场景下的群体更有效。这样我们最为开发者作为一个整体去为这个世界创造的价值也就更多,从这种方向来看多种方向的并存是必然的甚至有益的。

如何正确选择自己所需的工具?

如何针对不同的场景需求去选择工具,这就涉及到经验和对工具本身的理解。如果你对框架背后所想要解决的问题缺乏理解的话,那你谈怎么选框架是根本没法谈的,所以这次live的希望就是说透过框架的表层我们不说哪个框架好哪个框架不好,我们所要说的是各个框架背后他们共同需要解决哪些问题

主流框架基本上都是以组件作为一个抽象单元

组件这个东西,现在主流框架基本上都已经以组件做为一个抽象的基本单元,那么回想这个组件是如何一步一步成为一个主流的。我们想最早的前端开发一般是以页面为单位,我们的思维方式是一个页面,所有的script js css 、所有的这些html全在一个页面里面,那么慢慢的后来这个页面转移到了应用,然后那么一旦成为应用的话这个就会有各种模块封装、切分的这些需求,那么很快就发现这个应用是可以抽象为组件库的,这是一个比较直观的理解。但是组件库这样的理解呢,是偏展示层的一种理解方式,那么React 在这个领域最大的贡献就是他揭示了一个事实,就是组件可以是函数。

组件可以是一个函数

假设我们的一整个应用是一个大的函数,而我们在函数里面可以调用原函数。每一个组件是一个函数,一个组件可以调用其它的函数,这样的一个树状的结构其实在函数的调用中也是共通的。

组件可以分为四大类
  • 纯展示型的组件。数据进, DOM 出,很直观很直接的表现出输入输出。
  • 接入型组件。也就是在react 的这个场景下我们有个概念叫container component,接入型的组件他会跟数据层的service 打交道,它会去包含一些跟服务器端、数据源打交道的这些逻辑,那么这些 container 会把这些数据往下传,传给一些比较简单的展示型的组件。
  • 交互型组件,这种典型的例子就是对于表单组件的一种封装和加强,大部分这些现成的组件库,像element ui 或者是react各种组件库,其实都是这样的交互型组件为主,这一类的组件它会有比较复杂的交互逻辑,但是它是一种非常通用的逻辑,所以它强调复用。
  • 功能型组件,这种功能型组件是一种比较抽象的东西,举例来说在vue的场景下,比如说我们路由的router-view还有transition组件,这些是本身它不渲染任何内容,它是一个逻辑性的东西,它作为一个拓展或者是一种抽象机制存在。
模板和JSX之间的对比

JSX本质上就是JavaScript,只是个语法糖。JavaScript相对于模版而言,它最大的优点就是它的灵活性。JSX获得了JavaScript这个语言的完全的灵活度。实际上JSX最大的价值和功能型组件的相似,它是原窗模板,但是模板在纯展示型这个和其他的用例上也是不差的,而且在纯展示型的模板上写起来会更爽一点,另一个就是说模板会让你更强执性的让你把尽可能更少的逻辑放在视图结构里。展示型的这些组件虽然视图逻辑很简单,但是它在样式上可能还是会有相当的复杂度的。那么逻辑少的模板在你写样式的时候会让你更容易用一种视图化的方式去思考,也是有一定的好处的。

组件的另一个概念(colocation拆分)

关于组件还有一个概念,就是colocation ,英文的意思就是说把放一起的东西放一起,比如说在vue的单文件组件里面我们把模板样式和JavaScript的逻辑都是放在一起的,然后像这种react 比如说你用上 react 用的这些 js css 虽然形式不一样,但是也是colocation,angular里面你也可以把模板跟 style 都放在组件的装饰器里面,其实主流框架都是这样做,这个概念扩展开去我们还可以在组件里面colocation 其他东西。当然有些人喜欢把它切成不同文件,但是它会把三个文件譬如说(Html、CSS、js)放在一个文件,这本质上也是一种colocation,其实组件化之后就跟传统的separation of concern就有区别了,传统的separation 是以语言为单位作为切分,组件是以组件本身作为一个切分的抽象,组件化之后在这点还是有一定的进化。

变化侦测和渲染机制
  • 渲染机制

现代的前端框架里面的渲染这块最重要的是声明式,英文是Declarative,相比较之下的概念是命令式,Imperative,Imperative最直接的例子是我们用jQuery“就是干”,拿到一个选择器 拿到 dom 然后就去命令式的操作它,这种很直接很爽。但是很快就会遇到维护性的问题,所谓英文有个词叫jQuery Spaghetti ,Spaghetti 的意思就是意大利面,就是你写的代码写到最后就像一坨意大利面,根本就没法维护了。声明式的好处就是说我们直接描述说数据跟dom的结构之间的映射关系是怎么样的, 那你就不需要去手动去做这些操作。

熟悉react的朋友都可能知道这个等式,view = render(state),这里render就是我们在react里面写的render函数,那vue的模板其实也是编译成渲染函数,所以模板本身跟JSX本质之间是相似的,他们的输入时state,输入就是DOM,当然这是极简化的,在理想情况下描述了这样的关系之后那输入变了输出就变了,就不需要顾虑输入到输出之间发生了什么事情,这个具体实现对底层可以时Virtual Domain,但是并不一定得是Virtual DOM,可以是细腻度的绑定。比如说 或者说是 都是比较细腻度的绑定。其实 算是覆盖式的更新,等于它是编译成一些直接的操作。

  • 变化侦测

用过vue的朋友知道vue的数据是响应式的,你把数据交给vue之后,vue会把它进行转换,转换完之后,化过后当你改变一些属性的值的时候,vue就会进行相应的更新。
ppt地址

简单的总结,变化侦测主要分为两种:

  • pull
    所谓pull,系统不知道数据什么时候变了,它需要一个信号去告诉它说数据有可能变了,在这个系统才会去进行一次比较暴力的比对,在React里面的表现是Virtual Dom Diff,在Angular里面就是整个脏检查的流程,能够这么做的前提是现在Javascript足够快,虽然有浪费但是性能上也可以接受

push
相比之下,vue的响应式数据或者RXJS的数据机制,在数据变动之后立刻就可以知道数据变动了,而且一定程序上我们会知道哪些数据变了,这样就可以进行相对更细粒度的更新,pull的这种更新是最粗粒度的,所以在大型应用里面我们要帮助系统来减少一些无用功,但是push的形式也有它的缺陷,粒度越细,你的每一个绑定都会需要一个observabel/watcher,这样会带来相应的内存以及依赖追踪的开销,所以在vue2里面选择的是一个比较中等粒度的方案,在组件级别是push,每一个组件是一个响应式的watcher,当数据变动时候我们可以对组件进行更新,在每个组件内部则是用Virtual Dom进行比对,push和pull之间的本质区别是在于用侦测成本换取一定程度的自动优化

状态管理

状态管理这个概念其实也是在FB提出了Flux之后才搬到台面上来讲,Flux在经历了初期的混乱竞争之后慢慢的合流到了Redux上,vux在一定程度受到了Redux的影响,状态管理的本质是从源事件(source event)映射到状态的迁移和改变,然后在映射到UI的变化,声明式的渲染已经帮我们解决了从状态到UI的映射,这一块,所以状态管理这些库他们做的实际上是如何管理将事件源映射到状态变化的过程,如何将这个映射的过程从视图组件中剥离出来,如何组织这一部分代码来提高可维护性,是状态管理要解决的本质问题

把 Vue 当 Redux 用
把 Vue 当 MobX 用

现在的状态管理方案还面临一些其他的共同的尴尬,一个是组件的局部状态和全局状态如何区分,现在是局部状态和全局状态并没有很明显的区分,另一个是全局状态和服务端数据之间,现有的方案是把服务端抓过来的数据塞到store里面去。

路由

路由是只有在大型的单页应用才会遇到的一个问题,传统的路由思想是比较有侵入式的,每个路由有自己的数据模型,有自己的模板等,但是当Reac和vue出现之后人们发现把路由和组件解耦是可行的并且还更加灵活,比如Reac直接用不带路由是完全没问题的,另一个启示是,如果从组件出发去思考路由,本质上就变成了把一个url映射到组件树结构的一个过程,url到组件的映射会有一些小的分歧,我们到底是应该从url出发,还是从这个状态出发,其实本质是一样的,因为url就是一个序列化的状态。

当实际在SPA中去做一个你会发现路由会涉及到许多其他问题,比如说hash模式和history模式如何兼容,重定向,别名,懒加载,然后最复杂的是跳转,路由之间的跳转需要提供各种”钩子”,然后这些”钩子”里面又可能做异步操作,”钩子”里面也有可能取消这次跳转,使得这次跳转无效等等。

整体来说现在主要的路由方案都有点相似,比较有意思的是最新的reat-router4,他推崇的是一种用组件本身来做路由的一种思路,这里很大程度上利用了上述第四大组件”功能型组件”,在父组件里面声明式的渲染其他组件,跟传统的路由组件方案的区别是”去中心化”,他不是把整个路由表写在一个地方,是分散的写在各个组件里头,这样做的好处是灵活性非常好,但是也有一些问题,首先,集中式的路由表对于理解整个应用的结构是有帮助的,另一方面,去中心化的路由对于跳转的管理会弱一些,他对于跳转的管理是直接用组件的生命周期去做的。

web路由和app路由的区别:

目前web路由整体思路上是一样的,将url映射到组件树,从一个url跳转到另一个url,我们把新的url push到历史的stack里面去了,但是stack前一个位置所对应的位置是被我们丢弃掉的,我们从一个状态迁移到另一个状态我们整个应用界面迁移到另一个状态了,原生应用上的跳转就像一叠卡片一样,新的界面会盖在现有的界面上,当你退回去的时候只是把当前的卡片拿掉,之前的卡片就会出现,web用的路由方案做app会比较别扭。

CSS方案

主流的 CSS 方案

  • 跟 JS 完全解耦,靠预处理器和比如 BEM 这样的规范来保持可维护性,偏传统
  • CSS Modules,依然是 CSS,但是通过编译来避免 CSS 类名的全局冲突
  • 各类 CSS-in-JS 方案,React 社区为代表,比较激进
  • Vue 的单文件组件 CSS,或是 Angular 的组件 CSS(写在装饰器里面),一种比较折中的方案

比较 CSS 方案时首先要明确场景的问题,如果应用逻辑已经组件化了,是一个比较复杂应用的开发,传统的 CSS 方式可维护性就有问题了。

react-css-in-js
反对css-in-js的文章

传统 css 的一些问题:

  1. 作用域
  2. Critical CSS
  3. Atomic CSS
  4. 分发复用
  5. 跨平台复用

css-in-js有很多不同的方案,这些方案各自解决了上述的一些问题,但是并不完美:
6. CSS Modules,Inline-Styles,vue的单文件组件里面直接加一个scoped都可以解决这个问题
7. 所谓的Critical ,比如说我们直出一个页面,可能我们整个应用有几十个页面,但是我们直出的永远是第一个页面,如果没一个页面都有一个对应的CSS的话,理论上渲染首屏我们只需要首屏的CSS就够了,这就是所谓的Critical CSS,在服务端渲染尤为重要,解决的办法是在服务端渲染的时候侦测到渲染要用到哪些CSS,css-in-js和vue2.3+有一个运行时的功能,在编译过程里面可以把CSS的插入跟组件的生命周期挂钩,同样可以起到收集Critical CSS的效果

Live提问:
问:在vue里 使用CSS Modules 会不会比 使用 scoped 好?
答:我个人觉得没有什么本质的区别,scoped的成本会更低一点,CSS Modules会有一定的运行时的代价,因为需要用动态的class绑定

  1. Atomic CSS的概念:比如说我们有两条CSS规则,一条是color:red,一条是color:green,我们写两个button的样式,一个按钮是红的,一个按钮是绿的,原子类的话就会把color:red单独拆成一个类:A,把color:green单独拆成一个类:B,然后所有button共享的再拆成一个类:C,然后红色的button可以说是AC,绿色的button是BC,总而言之就是把尽可能多的共享的一些单独的规则都拆成一个很小很小的类,这样出来的最后的结果是你的CSS可压缩性更好了,可以变得更小,对应于css-in-js里面的Style Chunk
  2. 分发复用的论点是说css-in-js都是Javascript,所以可以跟普通的Javascript模块一样直接发包到npm上去复用,确实Javascript比纯CSS更容易去组合复用,但是css你也可以发到npm上然后webpack直接引用,这一点上并不算完全的优势.
  3. 跨平台复用:VueX里面就是把静态的css在parse之后编译成Javascript,就可以跨平台复用了.
构建工具

构建工具解决的其实是几方面的问题:

  • 任务的自动化
  • 开发体验和效率(新的语言功能,语法糖,hot reload 等等)
  • 部署相关的需求
  • 编译时优化

大公司里怎样开发和部署前端代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值