Worktile 的前端构建之路
2013年,那时候 Angular.js 才刚刚兴起,我们大胆了选择了当时看来比较新的技术,经过 3年的技术积累,Worktile 团队基本上把 Angular 1.x 可能踩得坑都踩了一遍,我们自己也写了一些关于 Angular.js 的插件,可以说团队目前采用 Angular.js 开发项目没有太大的技术难点。
Worktile 刚开始的时候是自己用 Node.js 写脚本进行合并和压缩 JS 代码,现在想想当时是多么的原始,后来切换到 Grunt 工具进行简单的合并压缩,编译 LESS,使用 Grunt 一段时间之后发现其打包的速度实在太慢,当做企业版的时候就切换到 Gulp 上了,在速度上的确有质的飞跃,使用 Gulp 的时候我们做了稍微复杂一点的任务构建,开发环境自动监控 LESS 代码变化等等,目前一直使用的是 Gulp,我们也在不停的跟随时代的变化,保持自己永不落后,所以在一些边缘项目上也会使用最近比较火的框架、类库或者工具。
Angular 1.x 使用模块化
大家都知道 Angular 1.x 有自己的一套模块机制,定义一个模块如下:
- angular.module("module.abc", ["module.def"]);
上面的代码定义了一个 "module.abc" 模块,并且 "module.abc" 依赖 "module.def" 模块,然而这个模块机制很容易写出有文件顺序依赖的代码,比如 a.js 文件定义了一个 "module.abc" 模块,b.js 没有定义自己的模块,而是直接使用了 "module.abc",这样 b.js 必须在 a.js 加载之后才能工作,如果有了文件的先后依赖,合并的时候就需要手动维护一个顺序列表。所以说 Angular.js 的模块机制没有解决JS文件依赖关系和JS文件异步加载的问题。
现在大家谈起前端的模块化,之前很多选择让人眼花缭乱,比如 CommonJS,AMD(Require.js),CMD(Sea.js),UMD,从未来标准的角度首选 ES6 Modules,不要为我为什么,关于如果在Angular 1.x 使用 ES6 的 Modules,网上有很多文章介绍怎么使用,推荐一篇前端大叔徐飞的博客,这里就不详细的一一展开了。
谈到这里大家或许会问:如果使用 Require.js ,在开发阶段是每个 JS 都动态加载的,生产环境根据 r.js 这样的工具合并成一个或者多个JS来提高前端性能,如果使用 ES6 的Modules,开发环境和生产环境是怎么样的呢?
在生产环境每个js文件都动态加载显然不是最合适,中小型项目基本不需要动态加载,我觉得合并成1个或者多个js是最好的解决方案。
那怎么合并 ES6 Modules 的JS,这类的工具很多很多,包括下面要介绍的 Webpack(目前1.x版本还不能原生支持,需要借助 Babel 工具),基本上开发环境也使用合并之后的 Bundle JS,调试使用 Source Map 工具,如果你开发环境就不想使用合并之后的,systemjs也是个不错的选择。
ES2015(ES6)
如果可以,尽快使用,因为他是 Javascript 未来的标准,作为一个前端技术人员,如果不能与时俱进那就只能等着淘汰,ES 2015 有着现代语言的语法,解决了 Javascript 语言层面上的很多问题,有 Class,Modules,Arrows,Template Strings 等等一系列的新特性,用了之后腰不酸腿不痛(请一定要相信我),最终的目的只有一个:提高生产力,让代码更简单,当然现在很多浏览器还不能原生支持 ES2015,没关系,有Babel在,生产环境转换好成 ES5 不是问题。
其实 ES 2015 的新特性数也能数出来,稍微花上 1 天就全学会了,推荐2个地址:
Webpack
Webpack 是最近1-2年来非常火的构建工具,如果说前端工程师没有听过或者尝试下 Webpack ,简直会被嫌弃,当然工具之所以火当然有他的道理,在我看来他能解决很多模块化和工程化的问题,提高生产力,如果你还不知道 Webpack 是什么,赶紧花几个小时去了解下,了解之后不要不了了之,结合自己的项目,切换到 Webpack 是否可行,如果可行,接下来怎么完美过渡,即使目前不用,下一个项目开始的时候试着尝试下。
Webpack 的优点:
- 支持CommonJS 和AMD 模块,ES 2015 Modules 在 2.0 中将会原生支持,这个问题不大,因为我们肯定要使用 Babel 的;
- 模块加载器,能够处理一切资源,包括 CSS,LESS,Image,JSON 等等,比如使用 babel-loader 加载器让我们能够用ES6的语法来编写代码;
- Code Spliting,可以通过配置打包成多个文件,有效的利用浏览器的缓存功能提升性能,并且能自动抽取多个入口公用的代码;
插件机制提供了更多扩展功能,弥补 Loaders 的不足,比如自带的 UglifyJsPlugin 压缩代码。
Angular 1.x + ES2015 + Webpack Seed
说了那么多,我都有点烦了,直接上代码 https://github.com/why520craz...
这是我写的一个怎么使用 Angular 1.x + ES2015 + Webpack 的一个示例项目,基本上是我目前水平觉得比较好的组织形式了,里面涵盖了:
- 使用 ES6 Modules 进行 Angular 1.x 的模块化开发;
- 使用全组件化开发一个简单的 Angular 1.x 单页项目;
- 使用 Webpack 合并 JS,CSS,Image 等一切静态资源;
- 包管理工具使用 NPM,任务的启动脚本使用 NPM Scripts;
- 使用 Webpack 的 CommonsChunkPlugin 将引用的第三方类库单独打包成一个独立的 bundle,并把多个入口公用的JS抽取出独立的 bundle;
- 使用 HtmlWebpackPlugin 插件生成入口的HTML文件,并把打包之后的JS和CSS引入到HTML中,不需要手动添加 scripts 标签和 style link;
- 使用 ExtractTextPlugin 插件把 CSS 抽取成独立的文件,当然你也可以不这么做,直接放在 JS 中;
- 使用 postcss 处理样式的兼容性问题,autoprefixer 自动追加前缀等。
Webpack 还有一些更高级的特性没有在示例中展现,比如通过dllplugin只将有变化的JS生成不同的 Hash,充分利用浏览器的缓存,频繁打包部署后,没有改动的类库将不会重新生成。
前端开发远不止于此,有人总结了前端开发的四个阶段:
- 库/框架选型;
- 简单构建优化;
- 模块化开发;
- 组件化开发与资源管理。
大部分团队还是停留在第二第三阶段,每个阶段的实现都有很多种选择。
如果只想达到第二阶段,那么选择一个压缩合并工具几分钟可能就能实现,具体任务是使用 Grunt,Gulp,还是 NPM Scripts 都可以;
第三阶段就需要代码级别的支持,选择 Webpack 原生支持 CommonJS,AMD 写法,不需要单独引入 Require.js 或者 Sea.js 这样的类库, 省去了很多麻烦,你也可以选择其他的支持模块化的构建工具:systemjs,Browserify, 或者rollup;
第四阶段的组件化开发和资源管理暂时就不讨论了,关于是全组件开发和半组件开发也有很多争议。
总结
时至今日,谈起 Angular.js ,或许有些人都觉得有些过时,人人都在谈 React,Vue。当然 Angular 2 经过一些波折之后也在逐渐走进我们的视线,其实最终哪个框架会一统天下谁都没法预料。但也有些前端大牛认为 Angular.js 在某些行业或者企业才刚刚兴起!
不得不承认 Angular 1 有很多问题,如果你的项目之前选择的是 Angular.js ,不是一拍脑袋就升级 Angular 2 的,即使不升级,我们也可以在 Angular 1.x 的基础上做一些工作来适应未来的变化,慢慢的哪一天你就会突然发现升级就是那么的简单。
希望这篇文章能够激起大家永远保持积极向前追求完美代码的心,不仅对自己的成长也会对公司带来无限的价值。对了!虽然Webpack 只是一个工具,但目前来看应该是个不错的工具,值得前端的朋友去学习,因为使用了他的确使前端的开发更简单了。
最后的内心OS篇
其实这篇文章来源于我在公司内部的一个分享前端构建工具 - Webpack
分享的时候主要是现场撸代码,通过博客的话就不好展现了,所以我基本上没有介绍 Webpack 入门的一些用法,并且网上已经有很多大牛的分享,推荐大家看一下Webpack 教程资源收集,这里面基本涵盖了各路大神,从入门到精通全看一遍即可, 如果你的英文足够好,请直接看 Webpack 官方文档~
作者:Worktile
来源:51CTO