1. 前端需要架构吗?
很多人这样认为,前端本来就是按一个个网页,天然解耦的,给每一个前端工程师分几个页面干不就行了吗,再说现在不是有很多现成的框架嘛。vue、react都是比较流行的框架,不喜欢原生开发的还可以使用脚手架.。但是实际情况是,即使使用了相同的框架,最后的软件质量也可能是天差地别的。正如架构是什么提到的,约束开发过程才能保证最终的软件质量。对于实际项目,尤其是中大型项目而言,只考虑只要实现功能,而不去保证最终的软件质量。这样的做法会导致验证的后果,所以前端肯定是需要考虑架构的。如果项目偏小,只有几个页面不需要长期维护的话,不考虑前端架构也不会出现太大的问题。从多个项目总结的经验看,我们认为前端架构一般一般需要解决四个问题。
- 规整化
- 适配性和兼容性
- 模块化
- 单页应用
2. 这些问题出现的原因
2.1. 规整化
由于前端网页相对容易入门,所以相当一部分前端工程师并没有系统的学习过前端开发。导致编写出来的代码一人一个样。很多代码细节都过于粗糙。再加上网上有很多质量难以分辨好坏的例子或者模版。导致最终看上去是完成了,但一旦发生BUG修改、UI改版、交互方式修改等状况的时候,都会出现工期和风险不可控的局面。
举个例子,如果不规范化前端工程,开发人员就会随意引用js或css文件。当图中的某个js文件的某个函数需要被修改时,可能会触发很多的bug。而且在这种情况下,想要定位哪里出现了问题,也是十分困难的,规整化是提升软件质量最有效的但又最容易被忽略的手段。
2.2. 适配性和兼容性
适配性指的是对不同设备的适配性。
如何一套代码适配多个显示端是前端架构需要考虑的问题。
兼容性是指对不同浏览器的兼容性,浏览器远没到大一统的地步。特别是手机浏览器。
2.3. 模块化
在前端开发时,很多时候都是在做重复的事情(这个页面有一个列表,那个页面也有一个列表。这个页面有一个播放器,那个页面也有一个播放器)。
一个中大型前端工程,相似的部分有很多。如何只用一份代码嵌入到各个页面。是模块化需要考虑的问题。
2.4. 单页应用
传统网页是不断的跳转页面的,点击搜索会跳转,点击翻页也会跳转。这种体验是糟糕的,为了改变这种糟糕的体验,单页应用的概念被提出。简单来说单页应用就是把多个网页做成一个网页,在接收到用户操作时, 把局部元素修改为正确元素.(以简单的翻页为例,点击翻页后,页面不会刷新,在页面获取到新的数据后,会直接修改页面数据,网上有很多单页应用的做法.但是把整个网站做成一个单页应用是不显示的,因为每个人都不想每次花几分钟打开一共网页.前端架构需要权衡好哪些网页需要做成单页应用,哪些网页必须要分离.)
知道问题才能有效解决问题.
3. 解决问题
3.1. 解决规整化问题
经常发生这样的问题,同是使用vue、react或者angular项目,为什么有些项目效果很好,却有些项目效果很差.其实并没有任何框架能保证软件的质量,而且vue、react其实考虑的是软件结构.而真实项目,项目过程才是决定软件质量的关键.规整化是提升软件质量最有效而最容易忽略的手段.
如果能规整出一套完整的规则,那忙无论是用Vue还是react或者其他框架,软件质量都是差不多的,当然规整化的目的并不是让代码像工艺品一样,而是通过限制开发过程,让代码混乱度限制在一定范围内.代码混乱才是影响软件质量的关键.
编码规范需要考虑以下几步:
3.1.1. 规范化目录结构
目录结构需要非常清晰,html文件、css文件、js文件、图片资源存放位置需要一目了然。
3.1.2. 规范化前端资源文件使用
限制每个网页只有专属的一个Html文件、一个CSS文件、一个JavaScript文件。让网页间代码解耦,互相不受影响。
3.1.3. 抽离通用部分
将通用的css、javascript抽离成通用文件。如抽离出主题css文件,通用的javascript文件。
并且通过命名规范,区分通用代码和网页专属代码。
3.1.4. 规范化第三方插件的使用
虽然过度依赖第三方插件不是一个好的行为,但是合理使用一些较为稳定的第三方插件却能大大降低开发工作量。
3.2. 解决适配性问题
响应式布局解决的是适配性问题。响应式布局本身是一个概念,它希望只用一份代码,适配所有大小的屏幕。也就是无需根据手机、PC、网页写两份代码。当然响应式布局不仅仅适用于跨终端的设备上。毕竟单纯的PC网页或者手机网页也会存在不同的浏览器大小或者屏幕大小的问题。
响应式布局的具体实现有很多,如Flex弹性布局、Grid网格布局、Bootstrap的栅格布局、Element-ui布局系统等。但是在实际项目中,这些布局工具不一定能很好的解决问题。(往往看起来页面做完了,但一旦缩放浏览器窗体大小,换成较大或较小的显示器,或者用手机打开时,网页元素会严重错误。这些问题很多时候我们都不以为然,认为项目后期集中改就好了。但是往往到项目后期就发现,这些网页布局根本就写错了,或者瞎写的还不如重写一遍。这样会导致前端成本远超预期。这样会导致项目一拖再拖。因为前端网页的工作量大部分都集中在网页布局上,而非普遍认为的功能上)
造成这些问题的原因,不是我们使用的响应式布局工具不够好,而是使用者对网页布局的认知或者做法是错误的。接下来我们讨论怎样才能做好响应式布局。
首先我们需要有正确的布局认知。错误的布局认知是一下子就看到了网页的所有元素。这样子由于要顾忌的细节太多,反而会导致无法很好的实现响应式布局。就好像把大象塞进冰箱一共需要几步的问题一样。如果一开始就着眼于全部细节则不会得出只需要三步的答案。
正确的布局认知应该是由层次的。
我们推荐把布局分为两层,整体层和模块层。
整体层是忽略细节,把一个网页分成几个合理的区域。而模块层是各个区域的具体细节。
3.2.1. 整体层的布局是考虑页面各区块的布局。虽然设备屏幕的分辨率是各式各样的,很多布局工具如BootStrap的栅格布局会按照浏览器横向分辨率将网页进行划分。
对于整体布局而言,更推荐使用BootStrap栅格系统等布局工具。但是这种布局工具也有局限,就是它们一般只会横向把屏幕分成12或者24份。让开发者选择每一个区块横向占几份。(这样就不能100%还原ui设计了(UI设计是在固定尺寸下设计的),但是既然选择了多分辨率适配,本身就不可能100%还原UI设计,所以对于整体布局而言只需要选择与ui设计相近的比例即可。)
以上都只关心区块的宽度,那区块的高度呢。在响应式布局中,一般认为高度是做为宽度变化的补偿。某些网页在较大的屏幕可能一行就显示完了,但是在较小的屏幕可能要显示多行。在较小的屏幕下,高度要增加,所以一般情况下,高度不是特别关心。
但是诸如导航栏需要与内容等高,列表区域铺满页面等场景的情况下。则需要考虑区域的高度。
-
- 如果是导航栏需要与内容等高场景则推荐使用aside标签。
- 如果是列表区域需要铺满页面等场景,则推荐使用calc设置区域高度。
3.2.2. 模块层布局
模块层布局是考虑区块元素细节的布局。
模块布局是比较精细的布局,但是相对于整体布局而言,模块布局一般不受屏幕大小的影响。所以我们不太推荐使用Bootstrap栅格布局等工具。我们推荐直接固定一部分元素的尺寸,其他元素直接使用calc,相对于这个固定尺寸的部分做填充。(一些列表形式的模块还是推荐使用BootStrap栅格布局等布局工具做初步布局,但是细节元素还是推荐上述的方式做布局)
一些特殊情况下,一些模块细节需要在pc网页,手机网页需要做区分。(例如,侧边导航栏需要在pc中需要固定显示,手机中则是下拉菜单。这种情况下则最好把有区别的部分各写一份代码,通过css的@media或者BootStrap等布局工具控制显示隐藏)。
如果按照整体层和模块层布局,则可以更好的使用模块化框架,在模块化的框架下整体层只管区域布局,而模块层可以封装成一个个模块,增加模块的复用度。当然我们仍然不推荐模块互相嵌套的方式。因为存在BootStrap、Element-UI等组件工具箱。所以模块并不需要互相嵌套以降低代码量。
3.3. 单页应用
传统网站会不断跳转页面。例如,单机搜索后会跳转页面,单机翻页后会跳转页面。而一旦出现跳转页面,用户就需要等待重新加载页面后,才能继续操作。为了逐渐改进这些糟糕的体验,单页应用的概念逐渐流行。单页应用指的是在浏览器中运行的应用,使用期间不会重新加载页面。简单是说就是把多个页面合成一个页面,通过Javascript脚本控制元素的展示。
下面讨论四个核心问题:
3.3.1. 单页应用的工作原理
单页应用本身就是一个概念,其实就是把多个网页合成一个。【很多人会误以为只有使用一些框架或者工具(如vue-cli、react-cli、Angular2及后续版本)才能实现单页应用,只因为他们提供了完备的模块化、路由方案,所有使用这些工具开发单页应用是相对容易的】。但其实单页应用就是一个页面,只不过是复杂的页面,其基本技术还是Html、Css、JavaScript。
单页应用的原理就是JavaScript监听事件发生,当事件发生后,javaScript会控制页面元素的显示。其具体的实现方式有两种:1.通过交互事件控制 2.通过路由控制
通过交互事件控制页面元素的变化是普遍做法。对于一些相似的页面,如列表页面、左导航右文章等页面。这些页面如果是点击翻页或者切换导航,页面其实只有数据变化,而页面布局基本是不变的。对于这些页面其实只需要监听点击或者其他交互事件然后向服务器请求新数据,最后替换新数据就可以了。
但是对于元素布局及内容完全不同的页面之间,如列表页和视频播放页。如果将这些页面通过监听、交互式事件的方式做成单页应用的话,则会变得很麻烦。对与这样场景就可以通过路由的方式进行控制,控制路由的方式简单的来说就是监听url的变化,来控制页面元素的显示。路由的方式有hash和history两种。
hash的工作方式是监听浏览器#部分的变化,已显示页面元素。由于浏览器地址#部分的变化是不会引起页面跳转的,所以在列表跳转到播放页面的时候,仍然可以使用a标签。
history的工作方式是使用浏览器的api去修改浏览器的地址。用这些api修改浏览器地址的话,是不会引起页面跳转的,javascript监听到浏览器地址变化后,会控制页面元素的显示。
3.3.2. 整个项目做成一个单页应用真的好吗?
不好,尤其是中大型项目。比如把300个页面做成一个单页应用,本地打开需要几分钟,性能差点的机器直接崩溃。后面也做了一些优化, 如懒加载,分离子项目等,性能有所提升,但是性能还是不好。其实网页本身就不适合做成应用的模式。网页的有点恰恰在于网页分散,用户无需像app一样下载安装,只需要在需要的时候再下载相关页面就可以了。
单页应用的初衷是缓解不断跳转的糟糕体验。如果极端的把整个项目做成一个单页应用,它就跟桌面应用app没啥区别了。需要长时间的下载以换取流畅的体验。再说了,浏览器的内存管理并没有操作系统的好。一个内容超多的网页的性能也不会流畅。那么把整个项目做成一个单页应用那还不如直接做成一个桌面应用或者app。
毕竟现在也有很多成熟的跨平台解决方案了。
3.3.3. 推荐的单页应用的应用方式
- 不推荐把整个项目做成一个单页应用
- 只推荐把相似的网页做成一个单页应用
例如相似的列表页可以做成单页应用,点击翻页或者导航切换,只需要更换数据或一小部分页面元素。
至于完全不同的页面,如视频列表页和视频播放页则完全没有必要合成单页应用,因为浏览器本身是由缓存机制的。即使跳转也不会花太长时间。另外把不同的页面分离还有一些好处,如页面性能可以单独调整,而不是面对一大推代码无从下手。
3.3.4. 使用Iframe实现伪单页应用
推荐不同的页面需要分离开来,如何更加便捷的去组织这些页面。
推荐使用iframe的方式组织这些页面。也就是伪单页应用的方式。这样的话,用户看到的页面其实是两层的,上层是固定不变的。(如顶层导航、用户信息、提示框、提示信息、底部网页地图等)
这样做的好处是既可以减少固定元素和固定信息的重复加载,又可以很好的组织切换不同的页面。这种方式是很多网站常用的做法,也是试验下来比较好用的做法。如果用这种方法的话需要解决几个问题。
1.iframe框需要根据内容自动变化高度。
子页面可以使用windows.MutationObserver等方式监听并修改父页面的Iframe框高度。
2.拦截a标签的跳转,并通知上层导航找到真实的地址,并替换iframe中的页面
这样就可以使用页面识别码跳转子页面了。
3.4. 模块化
模块化的目的是为了降低项目的工作量。也就是说项目中的某些部分是可以复用的。
3.4.1. 模块化的原则
模块化虽然能降低代码量,但如果像这样无线集嵌套模块的话,看上去能极大限度的降低代码量。但其实这样的代码维护起来是十分困难的。
我们推荐的模块化原则是模块只需要限制在单层,且模块之间不能相互嵌套关联。
页面分为整体层和模块层,整体层做为沙盒,引入模块,且对模块进行布局。模块层就是一个个模块,做精细页面布局,这样的话既可以保证模块的复用度,维护起来也是比较容易的。这个原则其实就和之前的前端响应式布局相呼应。
3.4.2. 现有的模块化方案
- iframe
iframe是html原生支持的方式,它允许将一个页面嵌入到另外一个页面。iframe是一个理论上比较好的模块化方式,因为被嵌入的模块是完整的页面,可以单独运行和调试。但是iframe的方式只适合嵌入一些较为复杂的模块。(播放器模块,即时聊天模块等)
因为一个页面嵌入多个iframe的话可能会出现性能问题,而且多个iframe模块的协调也是很麻烦的事情。虽然iframe不能作为页面模块化常用的方法。但是iframe可以很好的组织整个网站的页面。
- 插件的方式
这种也是html原生支持的,插件的方式也是比较常见的。如播放器video.js、文本编辑器Ckeditor、组件工具箱、BootStrap等。(如果使用vue.js的template的话其实也是插件的方式)。
插件的方式说白了就是把css、javascript打包成插件而html内容则变成字符串赛到JavaScript脚本中。页面使用插件时,直接引用Css、javascript文件即可。这种方式适合单独发布的插件video.js、Ckeditor,但不适合作为页面模块化的常用方法。包括使用vue.js的template方式。因为这种方式的html部分需要赛到Javascript脚本当中,这样html部分就会变得十分不直观,代码维护也比较麻烦。
- 使用vue-cli、react-cli等脚手架工具
最后是vue-cli、react-cli等脚手架工具,这类脚手架工具很大程度也是为了更好地实现前端模块化而被创建出来。脚手架工具为模块提供了独立的Html、Css、JavaScript(TypeScript)空间。
可以更加直观编写和使用模块。这类脚手架工具也是被很多人推崇,但是由于加了很多额外的辅助工具和编写规则,让学习难度变大,变相提升了团队的人力成本。而且实际项目的成果也未必那么好。一道项目调优,项目维护升级则可能出现各种各样的问题。
前端模块化必然是前端架构的发展方向。但是对以上提及的现有模块化而言,脚手架工具是最好的一种方式,但是我们认为这些脚手架工具并不一定是最好的模块化方案。因为这些框架工具要学习成本很大,很多工程师也仅仅是学会初步使用。一定性能优化就会出现各种各样的问题,我们认为更好的模块化方案应该是这样的