Vue.js作者⼪⾬溪:Vue2.0——渐进式前端解决⽅案
框架是什么?为什么要有框架?在众多的框架之中,Vue独具魅⼒之处在哪⾥呢?其背后的核⼼思想是什么?Vue究竟⽕到什么程度?最近发布的Vue2.0⼜做了哪些改进呢?Vue和Weex⼜是怎样的⼀种合作?
本⽂根据尤⾬溪在2016 QCon 全球软件开发⼤会(上海)上的讲演整理⽽成。
Tips:今天⽂章头图来⾃于两位迷妹的坚持,有颜有才,这样的程序员请再来⼀打!
作者简介
尤⾬溪,Vue Technology LLC 创始⼈, Vue.js作者,设计师,开发者,开源爱好者,前端框架 Vue.js 的作者。
曾就职于 Google Creative Lab,参与多个实验项⽬的界⾯原型研发,后加⼊ Meteor,参与 Meteor 框架本⾝的维护和Meteor Galaxy 平台的交互设计与前端开发。现全职投⼊ Vue.js 的开发与维护,⽴志将 Vue.js 打造成与 Angular/React 平起平坐的世界顶级框架。
为什么要有框架
1、框架的存在是为了帮助我们应对复杂度
前端框架特别多,那么为什么要有框架呢?我个⼈的看法是,框架的存在是为了帮助我们应对复杂度。当我们需要解决⼀些前端上⼯程问题的时候,这些问题会有不同的复杂度。
如果你⽤太简陋的⼯具应对⾮常复杂的需求,就会极⼤地影响你的⽣产⼒。所以,框架本⾝是帮我们把⼀些重复的并且已经受过验证的模式,抽象到⼀个已经帮你设计好的API封装当中,帮助我们去应对这些复杂的问题。
2、框架⾃⾝也有复杂度
但是,框架本⾝也会带来复杂度。相信⼤家在调研各种框架或学习各种框架时,会遇到学习曲线问题——有些框架会让⼈⼀时不知如何上⼿。这⾥就抽象出⼀个问题,就是要做的应⽤的复杂度与所使⽤的框架的复杂度的对⽐。进⼀步说,是所要解决的问题的内在复杂度,与所使⽤的⼯具的复杂度进⾏对⽐。
3、⼯具复杂度是为了处理内在复杂度所做的投资
⼯具的复杂度是可以理解为是我们为了处理问题内在复杂度所做的投资。为什么叫投资?那是因为如果投的太少,就起不到规模的效应,不会有合理的回报。这就像创业公司拿风投,投多少是很重要的问题。如果要解决的问题本⾝是⾮常复杂的,那么你⽤⼀个过于简陋的⼯具应付它,就会遇到⼯具太弱⽽使得⽣产⼒受影响的问题。
反之,是如果所要解决的问题并不复杂,但你却⽤了很复杂的框架,那么就相当于杀鸡⽤⽜⼑,会遇到⼯具复杂度所带来的副作⽤,不仅会失去⼯具本⾝所带来优势,还会增加各种问题,例如培训成本、上⼿成本,以及实际开发效率等。
4、Pick the right tool for the job
“Pick the right tool for the job”——在国外,跟开发者讨论⼀些框架选型问题时,⼤家都会说这句话——⼀切都要看场景。因为,前端开发原⽣开发或者桌⾯开发模式相⽐,有⾃⼰的独特之处,它跟其实并不那么固定。在Web上⾯,应⽤可以有⾮常多的形态,不同形态的Web应⽤可能有完全不同程度的复杂度。这也是为什么我要谈⼯具复杂度和所要做的应⽤复杂度的问题。
5、怎么看前端框架的复杂度
⽬前的前端开发已经越来越⼯程化,⽽我们需要解决的实际问题也是不同的。如下图所⽰,我们可能
在任何情况下都需要声明式的渲染功能,并希望尽可能避免⼿动操作,或者说是可变的命令式操作,希望尽可能地让DOM的更新操作是⾃动的,状态变化的时候它就应该⾃动更新到正确的状态;
我们需要组件系统,将⼀个⼤型的界⾯切分成⼀个⼀个更⼩的可控单元;客户端路由——这是针对单页应⽤⽽⾔,不做就不需要,如果需要做单页应⽤,那么就需要有⼀个URL对应到⼀个应⽤的状态,就需要有路由解决⽅案;
⼤规模的状态管理——当应⽤简单的时候,可能⼀个很基础的状态和界⾯映射可以解决问题,但是当应⽤变得很⼤,涉及多⼈协作的时候,就会涉及多个组件之间的共享、多个组件需要去改动同⼀份状态,以及如何使得这样⼤规模应⽤依然能够⾼效运⾏,这就涉及⼤规模状态管理的问题,当然也涉及到可维护性,还有构建⼯具。
现在,如果放眼前端的未来,当HTTP2普及后,可能会带来构建⼯具的⼀次⾰命。但就⽬前⽽⾔,尤其是在中国的⽹络环境下,打包和⼯程构建依然是⾮常重要且不可避免的⼀个环节。
主流框架分析
我们看⼀下现有的⼀些主流框架从少到多所解决的问题。这个多少并不是来评价框架的好坏,⽽是从设计的⾓度出发看它涵盖多少内容。
纯模板引擎:最少的就是纯模板引擎,只管状态到界⾯的映射。
React和Vue:其实这两者都是⾮常专注的只做状态到界⾯映射,以及组件。
Backbone:它会给你多⼀些架构上指导,⽐如它会让你分层。
Angular:它做的事情就更多,它有⾃⼰的路由,这些都会包含在⾥⾯。
Ember:相⽐Angular,Ember做得就更加彻底,Ember信奉的是约定优于配置,它会将⼀切都帮你设计好打包好,你就开箱⽤就可以了。
Meteor:Meteor只是⼀个极端,它是从前到后全都包含,从前端到数据层到数据库,全都帮你打包好。
通过简单的分析,我们可以感受到,做得少的框架不⼀定就不如做得多的框架,这体现出⼀种取舍。也就是说,做得少的框架可以给你更多的灵活性,但你需要做更多的选择;做得多的框架有更强的侵⼊性,学习成本更⾼,灵活性更低。⼀旦选择了⼀个侵⼊性强的框架,那么⼀些⼩的部分你就没有机会去切换成其他你更想⽤的⽅案。
所以,React和Vue有⼀个共同特点,它们都有各⾃的配套⼯具,核⼼虽然只解决⼀个很⼩的问题,但它们有⽣态圈及配套的可选⼯具,当你把他们⼀个⼀个加进来的时候,就可以组合成⾮常强⼤的栈,就可以涵盖其他的这些更完整的框架所涵盖的问题。
这样的⼀个配置⽅案,使得在你构建技术栈的时候有可弹性伸缩的⼯具复杂度:当所要解决的问题内在复杂度很低的时候,可以只⽤核⼼的这些很简单的功能;当需要做⼀个更复杂的应⽤时,再增添相应的⼯具。
例如做⼀个单页应⽤的时候才需要⽤路由;做⼀个相当庞⼤的应⽤,涉及到多组件状态共享以及多个开发者共同协作时,才可能需要⼤规模状态管理⽅案。
时,才可能需要⼤规模状态管理⽅案。
⼀个纯粹的复杂的单页应⽤,和只是在后端渲染的静态页⾯上嵌⼊交互内容所需要选择的⼯程栈其实是有相当⼤区别的。这就是为什么我觉得,核⼼+⽣态的栈会是⼀个在整体选型更为灵活的栈。
React和Vue都选择这个模式。Facebook团队只是专注做React本⾝,但React社区⾮常活跃,贡献了⼤量的第三⽅解决⽅案。不可否认,React社区是当前最活跃的社区,很多优秀的想法和思路,包括状态管理⽅案最早都是从React社区萌发出来。但是社区的这种活跃也带来⼀定程度的副作⽤,那就是时代变化太快,三天出⼀个新版本,同⼀个问题曾经存在⼏⼗种不同的解决⽅案。
这就使得我们在去搭建⾃⼰的栈时,需要花很多的时间去鉴别所选择的部件。同时,由于整个⽣态圈的这些库并不是由⼀个统⼀的团队去规划和设计的,所以很难考虑到不同的库之间的协作,这就会导致磨合问题。
同时,这也使得很多开发者抱怨有⼀个“Java Fatigue”,说JS⽣态圈东西太多了,为了跟上潮流,需要不停学习最新的东西,觉得很累。当然,有很多⼈觉得这是⽣态圈繁荣的表现,但它确实使得⼤家⾯临了选择困难症的问题。
Dan Abramov 是之前React社区⾮常活跃的开发者,已经加⼊了React团队。有⼀天他在推特上说,极度的模块化使得他⾮常难去构建⼀个统⼀的体验。这句话指的就是,React⽣态圈整个栈的每⼀个部分来⾃不同开发者,想全部整合到⼀起的时就有很多零碎的问题。
这是他刚开始做React现在的官⽅的CLI的时候发出的感慨,他在试图整合社区的各种东西放到⼀个架⼦⾥⾯,于是遇到了很多这样的问题。我这⾥完全没有否认React⽣态圈繁荣的意思,我只是觉得可以有另外⼀种选择,那就是我们可以做⼀个渐进式框架,这就是Vue选择的⽅向。
渐进式框架Vue.js
好用的前端框架1、Vue.js现状
以下数据可以体现出Vue.js的现状。
前⼀段时间突破了三万星,总下载量过百万。
官⽹上每个⽉的⽤户量为26万,这个应该是不包含中国区数据。官⽅开发者插件的周活跃⽤户数在5万5左右。这个数据是我觉得最有说服⼒的数据。安装并且使⽤开发者插件的Vue⽤户,应该会在实际⽣产中真正频繁使⽤Vue。
Google搜索趋势的相关数据如下图所⽰。图中,绿⾊的是Backbone的数据,黄⾊是Ember,红⾊是React,蓝⾊是Vue。可以看出React和Vue近两年发展势头都⽐较迅猛。
可以看出,Vue的曲线开始的是很早,2013年已经开始,但是有很长⼀段时间的增长是⽐较低的。因为在那⼀段时间我还在⾕歌⼯作,Vue基本上是作为个⼈项⽬在运营。在过去⼀两年中,Vue获得了⾮常⼤的突破性发展。这个图⾥没有Angular,因为Angular的量还是⾮常⼤的,如果放进去就破表了。
这些数据并不能绝对地代表框架当前的热度,但有⼀定的参考价值。可以看到React的势头很⾜。⽽由Vue的曲线还可以看出它的增长速度还在不停上扬。
2、Vue的定位
我在做Vue的过程中也在不停地思考它的定位,现在,我觉得它与其他框架的区别就是渐进式的想法,也就
我在做Vue的过程中也在不停地思考它的定位,现在,我觉得它与其他框架的区别就是渐进式的想法,也就
是“Progressive”——这个词在英⽂中定义是渐进,⼀步⼀步,不是说你必须⼀竿⼦把所有的东西都⽤上。
3、Vue的设计
接下来我们回到之前看的图:
Vue从设计⾓度来讲,虽然能够涵盖这张图上所有的东西,但是你并不需要⼀上⼿就把所有东西全⽤上,因为没有必要。⽆论从学习⾓度,还是实际情况,这都是可选的。声明式渲染和组建系统是Vue的核⼼库所包含内容,⽽客户端路由、状态管理、构建⼯具都有专门解决⽅案。这些解决⽅案相互独⽴,你可以在核⼼的基础上任意选⽤其他的部件,不⼀定要全部整合在⼀起。
4、Vue的实现
接下来深⼊讲⼀讲这些具体的概念以及Vue在这些概念上具体是做怎样的实现。
(1)声明式渲染
现在基本所有的框架都已经认同这个看法——DOM应尽可能是⼀个函数式到状态的映射。状态即是唯⼀的真相,⽽DOM状态只是数据状态的⼀个映射。所有的逻辑尽可能在状态的层⾯去进⾏,当状态改变的时候,View应该是在框架帮助下⾃动更新到合理的状态,⽽不是说当你观测到数据变化之后⼿动选择⼀个元素,再命令式地去改动它的属性。
下图是Vue的⼀个模板⽰例,如果没有⽤过Vue的话,可以⼤概感觉到这是⼀个怎样的概念。
其实,在模板语法上,Vue跟Angular是⽐较相似。在Vue1.0⾥⾯,模板实现跟Angular类似,如下图所⽰,把模板直接做成在浏览器⾥⾯parse成DOM树,然后去遍历这个树,提取其中的各种绑定。
在Vue2.0中,渲染层的实现做了根本性改动,那就是引⼊了虚拟DOM。
从架构来讲,Vue2.0 依然是写⼀样的模板(Vue2.0于前段时间发布,具体报道参见
在这个过程中,Vue有⾃⾝的响应式系统来侦测在渲染过程中所依赖到的数据来源。在渲染过程中,侦测到的数据来源之后,之后就可以精确感知数据源的变动。到时候就可以根据需要重新进⾏渲染。当重新进⾏渲染之后,会⽣成⼀个新的树,将新树与旧树进⾏对⽐,就可以最终得出应施加到真实DOM上的改动。最后再通过patch函数施加改动。
这样做的主要原因是,在浏览器当中,Java的运算在现代的引擎中⾮常快,但DOM本⾝是⾮常缓慢的东西。当你调⽤原⽣DOM API的时候,浏览器需要在Java引擎的语境下去接触原⽣的DOM的实现,这个过程有相当的性能损耗。所以,本质的考量是,要把耗费时间的操作尽量放在纯粹的计算中去做,保证最后计算出来的需要实际接触真实DOM的操作是最少的。
下⾯看渲染函数。⽤过React的开发者可能知道,React是没有模板的,直接就是⼀个渲染函数,它中间返回的就是⼀个虚拟DOM树。JSX实际就是⼀套⽤于让我们更简单地去描述树状结构的语法糖。
如下图所⽰,在Vue2.0当中,可以看到就是说当⽐如左侧的模板,经过Vue的编译之后就会变成右侧的东西。
这个函数类似于创建⼀个虚拟元素的函数,我们可以给它⼀个名字,给它描述应该有的属性特性和可能其他的数据。然后后⾯这个最后这个参数是个数组,包含了该虚拟元素的⼦元素。总的来说2.0的编译器做的就是这个活。
同时,在Vue2.0⾥,⽤户可以选择直接跳过模板这⼀层去⼿写渲染函数,同时也有可选JSX⽀持。从开发者的偏好以及开发者的效益的⾓度来考量,模板和JSX是各有利弊的东西。模板更贴近我们的HTML,可以让我们更直观地思考语义结构,更好地结合CSS的书写。
JSX和直接渲染函数,因为是真正的Java,拥有这个语⾔本⾝的所有的能⼒,可以进⾏复杂的逻辑判断,进⾏选择性的返回最终要返回的DOM结构,能够实现⼀些在模板的语法限制下,很难做到的⼀些事情。
所以在Vue2.0⾥,两个都是可以选择的。在绝⼤部分情况下使⽤模板,但是在需要复杂逻辑的情况下,使⽤渲染函数。在Vue2.0的路由和内部的⼀些实践上,都⼤量地应⽤渲染函数做复杂的抽象组件,⽐如过渡动画组件以及路由⾥⾯的link组件,都是⽤渲染函数实现的,同时还保留了它本⾝的依赖追踪系统。
如下图所⽰,Vue的依赖追踪通过ES5的 Object.defineProperty ⽅法实现。⽐如,我们给它⼀个原⽣对象,Vue会遍历这个数据对象的属性,然后进⾏属性转换。每⼀个属性会被转换为⼀个 getter 和⼀个 setter。同时每个组件会有⼀个对应的 watcher 对象,这个对象的职责就是在当前组件被渲染的时候,记录数据上⾯的哪些属性被⽤到了。
例如,在渲染函数⾥⾯⽤到A.B的时候,这个就会触发对应的 getter。整个渲染流程具体要点如下:
当某个数据属性被⽤到时,触发 getter,这个属性就会被作为依赖被 watcher 记录下来。
整个函数被渲染完的时候,每⼀个被⽤到的数据属性都会被记录。
相应的数据变动时,例如给它⼀个新的值,就会触发 setter,通知数据对象对应数据有变化。
此时会通知对应的组件,其数据依赖有所改动,需要重新渲染。
对应的组件再次调动渲染函数,⽣成 Virtual DOM,实现 DOM 更新。
这样⼀个流程跟主流的⼀些框架,例如React是有较⼤区别的。在React中,当组件复杂的时候需要⽤shouldComponentUpdate 做优化。但是,它也有⾃⼰的各种坑,⽐如要确保该组件下⾯的组件不依赖外部的状态。虽说这在⼤部分情况下是够⽤的,但遇到极⼤复杂度的应⽤,遇到性能瓶颈的时候,这个流程优化起来也是相当复杂的⼀个话题。
如下图所⽰,在Vue⾥⾯由于依赖追踪系统的存在,当任意数据变动的时,Vue的每⼀个组件都精确地知道⾃⼰是否需要重绘,所以并不需要⼿动优化。⽤Vue渲染这些组件的时候,数据变了,对应的组件基本上去除了⼿动优化的必要性。
(2)组件系统
相信基本上所有的现代框架都已经⾛向了组件化道路,Web Components 从规范层⾯做这个实践。主流框架都有各有不同的封装,但核⼼思想都是⼀样,把UI结构映射到恰当的组件树。
在Vue中,⽗⼦组件之间的通信是通过 props 传递。从⽗向⼦单向传递;⽽如果⼦组件想要在⽗组件作⽤⾥⾯产⽣副作⽤,就需要去派发事件。这样就形成⼀个基本的⽗⼦通信模式,在涉及⼤规模状态管理的时候会有额外的⽅案,这个后⾯会提到。
Vue的组件引⼊构建⼯具之后有⼀个单⽂件组件概念,如下图所⽰,就是这个Vue⽂件。在同⼀个Vue⽂件⾥,可以同时写 template、和 style,三个东西放在⼀个⾥⾯。
同时,Vue的单⽂件组件和 Web Components 有⼀个本质不同,它是基于构建⼯具实现。这样的好处是有了⼀个构建的机会,可以对这些单⽂件组件做更多的分析处,在每⼀个语⾔块⾥可以单独使⽤不同的处理器,这点后⾯还会讲到。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。