沪江网校前端架构漫谈

做者: 将来
本文转自互联网技术联盟(ITA1024)技术分享实录css

正文以下

没有统一架构的时候是怎样的一种状况?

起初前端是没有架构的,你们只是在完成一个一个的页面。咱们来看看会发生什么。前端

  • A同事是一个很是有意思的人,他喜欢把跟这个页面相关的全部的JS都写在同一个文件里面。嗯,传说中2000行代码的JS文件就是这么出来的。react

  • B同事是一个对技术比较有追求的人。他以为模块化不错,因此他在本身作的页面里选用了requireJS。看上去不错哦,巧的是C同事也是一个对技术有追求的人,可是他不喜欢AMD的规范,因此他支持国货seaJS,哦,他还在里面使用了他喜欢的模版引擎Jade。webpack

嗯,同一个网站,每一个页面的技术选择彻底不同的。别忘了,网站是须要维护的,修Bug阿,改需求啊。有一天B同事跑去负责C同事作的那个页面的需求改动,当他看到那些他不熟悉的技术时,心里是极度崩溃的。程序员

  • D同事玩的就比较高级了,他喜欢写es6,也用sass。这就要求不管在开发的时候仍是发布的时候代码都要先编译,并且显然你是不可能每次都是手动去作这个事情的。这就是他对工程化有需求了。es6

这个时候他极可能选择放弃,或者妥协(只使用少部分构建工具很好支持的功能),毕竟要实现一套完整的工程化是很花时间的事情(即便只是支持他本身的页面),而程序员的时间每每会被业务需求所淹没。毕竟以单我的和单个页面的单位来看,工程化这个东西是得不偿失的,若是以团队和整个系统去看那就不同了。web

其实到这里就能够看出来,我的所使用的技术的天花板每每会被整个团队的现状所制约。npm

架构是否是必须的?

上面描述的是没有架构的时候发生的状况,这些状况看上去固然都不太好,可是若是跳出前端这个角色,咱们怎么去描述说这些问题会形成的影响,反过来的意思就是,若是有一个统一架构可以带来的好处。redux

回答出这个问题,能够解决两方面的困扰。浏览器

第一是说服大团队里的其它角色前端架构这件事情的重要性(在如今的大环境下,这个其实很重要);

第二是本身要时刻记住架构的目的,可以不忘初心,不沉迷于形式和概念。

如今回过头来看看,

架构的目的是什么?

答案是提高质量和效率。

没有架构的状况下,新技术没法获得引入,技术没法统一,使得团队的总体技术能力没法获得提高,也没法提供技术上的通用解决方案,从团队的角度来考量的话,效率是很是低下的。

同时,由于技术过于陈旧,再加上代码没有统一规范,致使碰到页面业务逻辑比较复杂,或者对老页面进行维护的时候,产生Bug的几率很是高,产品质量堪忧。

架构应该怎么玩?

上面讲到,架构的目的是提高质量和效率。那咱们看看架构应该作到哪些方面才能实现这个目的。

  • 架构是一个抽象的过程,它是架构师根据本身的经验对大量具体的业务项目进行分析,发现其中的规律,抽象出具体的规范,最终又应用于具体的业务项目中去。好比常说的MVVM就是一种规范。

  • 要把跟业务无关的问题都在架构层面处理掉。好比代码压缩,打包这种工程化的问题都要在架构层面统一解决的。要作到业务的归业务,架构的归架构。

  • 架构要考虑到能够方便团队成员提供和使用通用技术解决方案。好比分页组件这种。

  • 架构设计的时候要综合考虑当前的主流技术跟本身业务系统的实际状况。由于前端正处在高速发展,各类新技术,工具,插件,框架层出不穷,这个时候要特别谨慎,有时候一个坑跳下去,就呵呵了。

沪江网校如今的架构是怎么样的?

基于以上原则,在搭建架构的时候,通过讨论和尝试,咱们最终肯定出4个方向,模块化,组件化,工程化,规范化。(你也看出来了,大方向是跟主流走的,太阳底下没有新鲜事啊。)

说了这么多虚的^_^,下面来点干货。

第一点-工程化:

构建工具用的是webpack,发布系统用的是jekins。

构建这里是分开发环境和生产环境。开发环境须要提供jsmap, css map,livereload等开发时候须要的功能,而生产环境须要压缩,打包,静态资源文件名添加hash等功能的。这里插一句,若是要启动开发环境,只须要 npm start。

第二点-模块化:

如今都是commonJS当道了,因此选择es6+ babel。这里顺便提下咱们使用的框架,PC端knockout(为了支持IE7), 触屏端和hybrid端redux+react。

第三点-组件化:

这一块咱们是作的挺完全的,也思考了不少。

  • 咱们的页面是由一颗组件树组成的。看下图,invitationActivity表明了一个页面,components下面的每个文件夹都表明一个组件。每一个组件包含本身须要的js,css,image等资源。

clipboard.png

  • 保证组件的封闭性。由于JS方面是模块化的,在css方面咱们也引入了cssmodule来作到这点。

  • 组件的功能界限问题。也就是什么是应该在组件内部实现,什么是应该由组件的调用者来实现的。看下图,下面这个界面会封装成一个业务组件,由于不少页面上都会有这个组件,因此在咱们的系统里面,它是被看成一个公用组件的。顺便提下,这个组件自己是由多个子组件组成的。

clipboard.png

如今有两个问题须要考虑下:

  1. 为了显示热门词汇有哪些热词,须要调接口从后台获取。那在哪里去调用接口呢,是组件自己去调用,仍是由使用者传进来。

  2. 如今点击搜索按钮,须要跳转到搜索结果页(还有可能要打点)。那完成这些操做的代码写在哪里呢?是直接写在组件里面仍是由调用者传入,由组件在相应的时机调用传入的函数。

这就是组件的功能界限问题。咱们的作法是组件只负责跟UI显示相关的部分,全部业务逻辑都不属于组件自己的功能。

根据这个原则,咱们来回答上面的问题。

  1. 组件只负责显示热词,至于具体有哪些热词,由它的调用者传入。

  2. 组件只知道搜索按钮被点击了,至于按钮被点击具体要作些什么,它是不知道的,它能作的就是调用传给它的回调函数。

备注:关于组件的功能界限问题咱们也思考了很长时间,而且作过不一样的尝试。好比把 点击搜索按钮要作的事情放在组件里面本身作怎么样呢,后来发现不一样的页面上点击搜索按钮须要打点的关键字是不同的,这时候你若是把组件写死了就无法重用 这个组件了。其中关于接口调用是否是要写在组件内部的问题更是一度相持不下,彷佛两边都有点道理。后来碰到一个真实的事情就是由于接口调用都写在组件里 面,致使同一个接口在某个页面上被调用了两次。当这件事情发生以后,天平彷佛往另一边倾斜了点。

  1. 通常组件化开发以后,咱们会碰到两个方面的问题。

第一个问题是咱们去看别人的代码的时候,没办法方便的知道这个页面的组件树是怎么组成的,以及每一个组件须要哪些数据。

第二个问题是当组件树的层次很深的时候,父子组件间参数的传递会很是繁琐。并且一旦须要增长或删减掉某个参数的时候,整个父组件到根组件路径上全部组件参数的传递都要修改。

关于第二个问题,也就是父子组件之间参数传递的问题,我举个例子来详细说明。假设如今组件树的结构是这样的。

A组件全部子组件加起来须要的参数是9个,那么调用A组件的写法是

<Aa1= “1” a2=“2” … a9=“9 />

而后在A组件内部,A组件自己只须要参数a1,其它的八个参数是被它的子组件B1,B2消费的。那么A组件内部的写法大概是这样的。

<B1a2=“2” a3=“3” a4=“4” a5=“5” />,<b2 a5=“5” a6=“6” a7=“7” a8=“8” a9=“9” />

B1组件自己不须要参数,四个参数都是被子组件C1,C2消费。那么B1组件内部的写法大概是这样的。

<C1a2=“2” a3=“3” />,<C2a4=“4” a5=“5” />

这个时候已经能够看出来,若是C1须要增长或者删减一个参数,从组件自己C1到根组件A之间的全部组件都须要改动。想象一下当组件树的横向和纵向的层次都变的很是深的时候,这个时候每一个组件的参数传递都会变的很是庞大并且混乱。

若是要把一个组件的位置换一下的话,要改变的地方之多,也是让人很是头疼的。咱们想了一个方案来同时解决这两个问题,看下图,

clipboard.png

咱们每一个页面都有一个param.js文件,能够看到从param.js里面能够清晰的看到当前这颗组件树的结构,以及每一个组件本身须要的参数。而在组件内部,写法也至关简单,以A组件内部为例,组件内部的写法是这样的,

<B1{…B1_param} />,<B2 {…B2_param} />

能够看到,这样的话,不管是增减参数仍是移动某个组件,都会变的很是简单。

因为咱们对组件功能界限的定义是只负责UI相关的功能,全部的业务逻辑都是从调用者传递过的。也便是写在param.js。因此param.js文件是很是重要的一个文件,里面基本包涵了这个页面全部业务处理逻辑。

很显然,随着页面业务逻辑变的复杂,param.js将会变得愈来愈大。不要紧,把不一样的组件参数分拆到不一样的js文件里面去实现,而后建个params文件夹把它们组织起来。

第四点-规范化:

  • JS语法检查选用了eslint。

  • 项目目录结构很是清晰。当进行开发的时候,哪些代码应该放到哪里都进行了明确的规定,而且每一个文件的功能都尽可能清晰而且单一。

顶层目录结构以下图:

clipboard.png

  1. src文件夹存放的是全部的的源代码和其余静态资源(好比图片,iconfont)。

  2. dist文件夹存放的是全部编译后的代码。

  3. build文件夹存放的是全部工程化所须要的代码。

  4. document文件夹固然存放的文档。

下面重点看下src目录结构,以下图:

clipboard.png

  1. app文件夹里的每个子文件夹表明了一个页面,每一个页面所用到的全部静态资源都存放在这个子文件下面(除了引用的公共资源之外),构建的时候,每一个子文件夹会生成本身的静态资源供页面引用。

  2. common文件夹里面的全部代码在构建的时候会单独生成js文件和css文件供页面引用。因此一个页面会引用两个js和两个css.里面存放的是每一个页面都会用到的一些共用资源。好比触屏端使用了react,那么跟react相关的那些包就会放在common里面。

  3. components文件夹里面存放的是共用组件,每个子文件夹表明了一个组件。有多是通用的功能组件,好比分页组件,Loading组件,ModalDialog组件。也有多是一个通用的业务组件,好比站点通用头部,通用footer,通用分享组件。注意,在其余地方引用这些组件时,是不须要写相对路径的,直接写组件名字就能够了,好比import pager from ‘pager’。这样对使用者更方便。

  4. lib文件夹存放的是通用的js类库。好比检测浏览器用的browserDetect.js,处理日期用的dateUtil.js。一样的,在其余地方须要引入这些JS时,也不须要写相对路径,直接写JS的名字就能够了。好比import{isIE} from ‘browserDetect’。

  5. style文件夹里面存放的一些公用的sass资源。好比function,mixing, variable。其余的sass文件须要引入这些资源的时候,使用方式跟使用通用js同样,直接@import “base.scss"便可。

写在最后

沪江网校的架构才刚刚有个雏形,后面还有更多的功能会加进来,好比脚手架(等到架构更成熟的时候在出一套完整的),NodeJS中间层(在大方向上已经达成统一),前端监控系统等。

架构是个不断完善的过程,而把架构尤为是跟规范相关的部分落实到具体业务系统里面更是个团队不断磨合的过程。它最终考验的,同时也是最终磨合出来的是团队的成熟度。

谢谢你们。

clipboard.png

clipboard.png

iKcamp原创新书《移动Web前端高效开发实战》已在亚马逊、京东、当当开售。

相关文章
相关标签/搜索