orangeyyy
10/28/2018 - 9:49 AM

脚手架

背景

从事中后台开发已经将近两年了,在日常的工作中,经常会听到类似的抱怨:"XXX写的代码真垃圾,完全看不懂", "业内明明提倡用tab缩进,你为啥要用空格?", "你这个项目要怎么再本地调试啊?为啥这么麻烦?连文档都没有?","我去,这哥们到低有没有用ESLint,这么多报错?"等等,我们会发现近几年我们已经将中后台的开发收拢到React+Webpack的技术栈,但是100个人心中有100份webpack配置,在开发中依然会存在编码习惯差异,在合作中依然会存在大量沟通成本,在项目交接时依然会存在各种吐槽。

面对上面的问题,我们小组(由于组织变动,当年的小组已经不在了)首先想到的是通过脚手架的方案来形成组内的统一开发规范,从而提高开发效率,降低一些不必要的沟通成本。what?天猫不是有tap了吗,你们又要重新造轮子?现在市面上那么多开发规范,你们又要搞一套?在这里我们只想做以下澄清:

  • tap在面向消费者领域已经支持的很好了,但是在面向中后台开发有很多不足之处,我们希望能够提供一个更贴合中后台的脚手架方案(其实在本地开发中我们使用了不少tap现有的功能);
  • 我们不生产规范,我们只是规范的搬运工,我们不去讨论语句后面跟分号还是不跟分号好,我们尊崇“约定优于配置原则”,只要组内成员达成一致就好;
  • 我们更多的是希望把日常开发过程的一些经验以及最佳实践能够沉淀下来,降低大家的试错及调优成本;

因此大家不用纠结这是不是一个新的轮子,如果你一定要纠结,那我就只有承认它是了,但是我写这篇文章的主要目的并不是为了炫耀我们又造了一个新轮子,而是希望把我们制作这个脚手架的过程记录下来,给予那些刚刚开始考虑前端工程化的同学一点借鉴,同时将我们的一些最佳实践以及思考沉淀下来,方便随时自省。

目录结构

目录结构是我们在开始构建一个项目时首先要考虑的东西,但是我们发现,在进行项目目录结构设计时,大家往往有不同的想法,例如工具库是应该放在common文件夹还是应该放在utils文件夹里面?组件文件夹是应该叫widgets还是应该叫components?构建产出文件是应该放在build还是dist文件夹下?这些问题其实都不重要,但是,如果我们不形成一个统一的目录结构,就会给合作方啊带来一定的理解成本或是文件查找成本,因此我们对目录结构做了最基本的约定:

  • 应用维度
build/                    webpack构建完成输出目录
src/                      源文件目录
  constants/              常量定义
  data/                   数据
  widgets/                纯样式组件库
  components/             全局业务组件库
    xxx/
      index.jsx
      index.less
  pages/                  页面目录
    xxx/                  子页面
      components/        
        index.jsx         
        index.less
      index.jsx
      index.less      
  entries/                入口文件
  utils/                  通用功能组件库 
lib/                      babel编译输出目录
docs/                     文档
test/                     测试用例
demo/                     演示页面文件
.babelrc                  babel配置文件
.eslintrc.js              eslint配置文件
.prettierrc               prettier格式化配置
.yo-rc.json               yeoman配置文件
.gitignore                gitignore文件
.storybook/               storybook相关配置
README.md                 项目说明
tap.conf.js               资源代理配置
webpack.config.dev.js     开发环境webpack配置
webpack.config.dll.js     打包第三方依赖包webpack配置
webpack.config.js         生产环境微博pack配置
package.json              package文件
postcss.config.js         postcss配置
  • 组件维度
build/                    webpack构建完成输出目录
src/                      源文件目录
lib/                      babel编译输出目录
docs/                     文档
test/                     测试用例
demo/                     演示页面文件
.babelrc                  babel配置文件
.eslintrc.js              eslint配置文件
.prettierrc               prettier格式化配置
.yo-rc.json               yeoman配置文件
.gitignore                gitignore文件
.storybook/               storybook相关配置
README.md                 项目说明
webpack.config.js         生产环境微博pack配置
package.json              package文件
postcss.config.js         postcss配置

PS: 通过上面的目录结构我们可以看到,对于一些工具的配置,例如 .babalrc、.postcss.config.js等等,我们尽量放到对应的配置中,而不是放到js script或者webpack配置中,这样方面我们快速查看对应工具配置。

初始化

我们这里说到的初始化主要包含两类:一类为项目初始化,这类初始化是在项目创建之初用于快速完成项目框架创建,这种初始化在整个项目周期中往往只会执行一次;另一类是模块初始化,这类初始化将伴随项目的整个生命周期,它可以帮我们快速初始化诸如页面、组件、demo等项目需要使用的模块,尤其是像React这种模板代码特别多的框架,可以帮我们节省很大一部分开发成本。

初始化的思路和实现方案其实再简单不过了,主要包含以下三步:

  • 提前制作好模板;
  • 根据用户在初始化时的输入结合模板产出真实代码;
  • 将真实代码拷贝到指定目录;

在我们的脚手架中使用到Yeoman来实现初始化功能,它很好的封装了模板制作、代码生成、用户交互、代码复制等功能,可以帮助我们快速实现脚手架的初始化功能,但是工具的使用不在本篇文章的讨论范围之内,有兴趣的同学可以自行了解。

代码规范

JS其实只是提供了最基本的语法要求,它并没有规定开发者要如果去书写,所以才有了要不要分号的争论,所谓的代码规范无非是大家在日常开发中的最佳实践以及一些共识的沉淀,为了更好的编写代码以及日后维护。业内比较有名的前端代码规范主要有Google JS代码规范Airbnb JS代码规范,其实集团前端委员会也定义了一套代码规范,作为集团的一部分,因此我们首选集团的代码规范。

有了代码规范,我们最先想到的是如果在组内统一这个代码规范,最基本的就是要求大家把集团的代码规范都通读一遍(虽然读了一遍也不一定记得住,记住了也不一定会用),另外就是在初始化时根据代码规范来初始化项目及模块代码,除了上面两种方式,我们还有下面两件法宝:

ESLint

ESLint是当下非常流行的一款JS代码检测工具,通过合理的配置,在配合IDE的ESlint插件能够方便快捷的检测出代码上不符合规范的地方,并给出响应级别的提示,开发同学要做到就是将IDE中的ESLint插件打开就可以了,ESLint的检测主要包含以下两种类型:

  • 语法检测 - 对代码的语法进行检测,可以避免用户在编码过程中出现低级的语法错误,从而导致代码运行失败;
  • 风格检测 - 对代码的风格进行检测,可以帮助用户约束代码风格,保证项目中代码风格统一;

具体ESLint的配置&使用方法不在本文的介绍范围之内,大家可以去官网进行查看,B3提供了一份比较通用的ESLint配置文件

prettier

上面介绍ESLint的时候提到了代码风格检测,检测虽易,但是执行起来就没有那么容易了,比如在我们的代码规范中约定代码缩进使用空格而不是tab,但是或多同学已经习惯使用tab来进行缩进,如果强行要求他使用空格真的是一件非常艰难的事情,写起代码来也会非常别扭。面对这种场景,prettier这款神器就能发挥它的作用了,妈妈再也不用担心我的项目代码风格五花八门了。prettier可以通过一份配置来规定代码的统一风格(其实prettier的默认配置已经基本满足我们的需求,几乎不用做太大变更就可以直接使用),在开发过程中,用户并不需要遵守这份风格规范,甚至你可以任性地把所有代码都写在同一行(开玩笑,应该没人会这么做),我们仅仅需要在代码构建的时候执行一下prettier将所有的源码都格式化一遍,再凌乱的代码也能边得井井有条。

具体prettier的配置&使用方法不在本文的介绍范围之内,大家可以去官网进行查看,B3提供了一份通过约定的prettier配置文件

PS:虽然上面两款法宝可以大大降低我们进行代码规范约束的成本,但是类似函数命名、变量声明位置等更加细节的规范约束还是需要大家的共同努力,才会出现读别人的代码如看自己的代码一般顺滑。

本地开发

本地开发是整个项目开发周期中非常重要的一环,我们不可能开发完代码就直接发布上线,肯定要进行本地验证调试,如何让本地调试更加接近线上真实环境,将决定整个项目的开发效率及联调成本,下面将通过项目调试及组件调试两个方面介绍B3在本地开发中使用的方案:

tap

从前,我们看到过这样的本地调试方式,就是在本地起一个server,然后将异步接口数据拉下来放到mock文件夹下,然后在本地访问页面请求这些接口时,从mock文件夹下拿响应的数据来返回,这种方案带来了以下几个问题:

  • 需要手动将所有的接口都拉到本地,开发效率较低;
  • 调用的是mock接口并不是线上真实接口,并不能体现线上真实情况,尤其是在写操作的时候;
  • 页面模板(即VM)也是使用本地mock,无法真实拿到线上真实模板数据;

tap工具为我们本地项目调试提供了更加便捷的方案,它可以让我们在尽可能模拟线上真实环境的情况下,调试本地代码:

上图的主要思路是:

  • 访问线上或者预发真实页面,保证页面模板和异步接口更接近真实环境;
  • 将页面中的cdn资源全部代理到本地;
  • 在本地通过webpack dev server 启动9898端口服务,用于访问本地调试代码;
  • 在本地通过tap assets 启动8000端口服务,用于通过cdn链接去请求cdn资源;
  • 在本地通过tap moniter 启动80/433端口服务,通过正则方式进行cdn资源匹配,将符合本地调试代码的cdn资源代理到9898端口,用于读取调试代码,将不符合本地调试代码的cdn资源代理到8000获取线上cdn资源;

具体tap的配置&使用方法不在本文的介绍范围之内,大家可以去官网进行查看,B3提供了一份通过约定的tap配置文件

storybook

上面tap的方案解决了本地应用代码调试的问题,可是,我们有时候会面临另外一种调试问题,就是组件的调试,按照通常的情况,我们需要建立一些测试页面来进行组件的调试,这样成本较高,而且不便于维护,还有就是在业务上线时,一定要记得将这些测试页面删掉,否则用户如果通过特殊渠道拿到了测试链接进行访问,会认为是平台的bug。

针对上面的问题,我们就要请出组件开发利器——storybook。storybook是当下非常流行的一款组件开发工具,在组件开发过程中,它可以方便的提供组件demo实例管理,组件本地开发工具,在项目开发过程中我们可以将组件开发和页面开发进行分离,在组件开发过程中创建组件demo,使用storybook来进行本地调试及验证,当组件完成验证后在页面开发过程中再使用已经开发完成的组件就可以高枕无忧了。B3 提供应用维度的组件demo初始化以及组件维度的组件demo初始化方案来快速建立组件demo进行本地调试及功能验证。

具体storybook的配置&使用方法不在本文的介绍范围之内,大家可以去官网进行查看,B3提供了一套完整的组件开发流程,大家可以前往试用。

项目构建

当整个项目完成开发以后,我们就需要考虑项目构建了,通常对于不同类型的代码文件我们可能需试用不同的方式来进行构建,但最终我们需要使用webpack来构建整个项目代码。

Babel

Babel为我们使用新的ES语法提供了可能,我们可以尽情享受新的ES语法为我们开发带来的便利,而不用担忧兼容性问题。

具体babel的配置&使用方法不在本文的介绍范围之内,大家可以去官网进行查看,B3提供了一份通过约定的babel配置文件

postCss

postCss提供丰富的插件来帮助我们解决浏览器的兼容性问题,或者为我们的开发提供遍历。

具体postCss的配置&使用方法不在本文的介绍范围之内,大家可以去官网进行查看,B3提供了一份通过约定的postcss配置文件

PS:通过上面的配置可以看到,我们为PC和无线端提供了不同的postCss配置,另外在这里要安利一下postCss的pxToViewport插件在解决无线端的适配问题上非常强大,远胜百分比及em/rem方案;

webpack

webpack大家肯定并不陌生,进行中后台开发的同学基本都会用到,但是webpack的配置确实让人头痛,正如我前面说到的,100个人心中有100个webpack配置,之前我也写过一篇关于webpack配置调优的文章,因此在这里就不加累述了。

具体webpack的配置&使用方法不在本文的介绍范围之内,大家可以去官网进行查看,B3提供了3个webpack配置供大家参考webpack.config.dll.jswebpack.config.dev.jswebpack.config.js

项目发布

项目发布作为项目周期的最后一环,关系到项目能否正常上线,说来惭愧,B3在这一环做的并不好,现在我们还是在使用本地构建并通过gitlab assets推送cdn的方案。下面只能简单列举一下现在集团内其他团队在项目发布环节的主要方案。

云构建

云构建主要用于解决以下两个问题:

  • 各个项目成员本地环境不一致的问题,本地环境的不一致往往会导致最终打包结果的不一致,最终会导致整个项目的不稳定,因此纯净且一致的打包环境对于项目构建和发布非常重要;
  • 如果将build后的代码推送到gitlab会导致线上代码diff变得非常艰难,在云构建场景下只需要提交源码;
  • 如果将每次打包后的结果都推送到gitlab会导致项目的体积呈指数级上升,到最后可能因为项目体积过大而不得不切换新的项目空间(这并不能从根本上解决问题),在云构建场景下只需要提交源码;

集团内的云构建方案有: DEF

持续集成

其他

总结