前端工程本质上是软件工程的一种。软件工程化关注的是性能、稳定性、可用性、可维护性等方面,注重基本的开发效率、运行效率的同时,思考维护效率。一切以这些为目标的工做都是"前端工程化"。工程化是一种思想而不是某种技术。举例说明:php
要盖一栋大楼,假如咱们不进行工程化的考量那就是一上来掂起瓦刀、砖块就开干,直到把大楼垒起来,这样作每每意味着中间会出现错误,要推倒重来或是盖好之后结构有问题但又不知道出如今哪谁的责任甚至会在某一天轰然倒塌,那咱们若是用工程化的思想去作,就会先画图纸、肯定结构、肯定用料和预算以及工期,另外须要用到什么工种多少人等等,咱们会先打地基再建框架再填充墙体这样最后创建起来的高楼才是稳固的合规的,什么地方出了问题咱们也能找到源头和负责人。css
咱们知道前端技术的主要应用场景并不是只是高大上的基础库/框架,拽炫酷的宣传页面,或者屌炸天的小游戏等这些一两个文件的小项目,更具商业价值的则是复杂的Web应用,它们功能完善,界面繁多,为用户提供了完整的产品体验。(多是新闻聚合网站,多是在线购物平台,多是社交网络,多是金融信贷应用,多是音乐互动社区,也多是视频上传与分享平台……)前端
从本质上讲,全部Web应用,都是一种运行在网页浏览器中的软件,这些软件的图形用户界面(Graphical User Interface,简称GUI)即为前端。jquery
如此复杂的Web应用,动辄几十上百人共同开发维护,其前端界面一般也颇具规模,工程量不亚于通常的传统GUI软件:webpack
简单来讲,模块化就是将一个大文件拆分红相互依赖的小文件,再进行统一的拼装和加载。(方便了多人协做)。git
分而治之是软件工程中的重要思想,是复杂系统开发和维护的基石,这点放在前端开发中一样适用。模块化是目前前端最流行的分治手段。es6
模块化开发的最大价值应该是分治!
无论你未来是否要复用某段代码,你都有充分的理由将其分治为一个模块。github
AMD/CommonJS/UMD/ES6 Module等等。web
CommonJS的核心思想是把一个文件当作一个模块,要在哪里使用这个模块,就在哪里require这个模块,而后require方法开始加载这个模块而且执行其中的代码,最后会返回你指定的export对象。bootstrap
module.export = function() { hello: function() { alert("你好"); } } var a = require('./xxx/a.js'); a.hello(); // ==> 弹窗“你好”
CommonJS 加载模块是同步的,因此只有加载完成才能执行后面的操做,不能非阻塞的并行加载多个模块。
AMD(异步模块定义,Asynchronous Module Definition),特色是能够实现异步加载模块,等全部模块都加载而且解释执行完成后,才会执行接下来的代码。
// 经过AMD载入模块 // define( // module_id /*可选*/, // [dependencies] 可选, // definition function /*回调 用来初始化模块或对象的函数*/ // ); define(['myModule', 'myOtherModule'], function(myModule, myOtherModule) { console.log(myModule.hello()); //会先并行加载全部的模块a b 并执行其中模块的代码后,在执行逐步执行下面的 console require("a"); console.log("a required"); require("b"); console.log("b required"); console.log("all modules have been required"); });
在一些同时须要AMD和CommonJS功能的项目中,你须要使用另外一种规范:Universal Module Definition(通用模块定义规范)。UMD创造了一种同时使用两种规范的方法,而且也支持全局变量定义。因此UMD的模块能够同时在客户端和服务端使用。
幸运的是在JS的最新规范ECMAScript 6 (ES6)中,引入了模块功能。
ES6 的模块功能汲取了CommonJS 和 AMD 的优势,拥有简洁的语法并支持异步加载,而且还有其余诸多更好的支持(例如导入是实时只读的。(CommonJS 只是至关于把导出的代码复制过来))。
// CommonJS代码 // lib/counter.js var counter = 1; function increment() { counter++; } function decrement() { counter--; } module.exports = { counter: counter, increment: increment, decrement: decrement }; // src/main.js var counter = require('../../lib/counter'); counter.increment(); console.log(counter.counter); // 1
// 使用 es6 modules 经过 import 语句导入 // lib/counter.js export let counter = 1; export function increment() { counter++; } export function decrement() { counter--; } // src/main.js import * as counter from '../../counter'; console.log(counter.counter); // 1 counter.increment(); console.log(counter.counter); // 2
在less、sass、stylus等预处理器的import/mixin特性支持下实现、css modules。
虽然SASS、LESS、Stylus等预处理器实现了CSS的文件拆分,但没有解决CSS模块化的一个重要问题:选择器的全局污染问题;
CSS in JS是完全抛弃CSS,使用JS或JSON来写样式。这种方法很激进,不能利用现有的CSS技术,并且处理伪类等问题比较困难;
CSS Modules 原理:使用JS 来管理样式模块,它可以最大化地结合CSS生态和JS模块化能力,经过在每一个 class 名后带一个独一无二 hash 值,这样就不有存在全局命名冲突的问题了。
webpack 自带的 css-loader 组件,自带了 CSS Modules,经过简单的配置便可使用。
{ test: /\.css$/, loader: "css?modules&localIdentName=[name]__[local]--[hash:base64:5]" }
前端做为一种GUI软件,光有JS/CSS的模块化还不够,对于UI组件的分治也有着一样迫切的需求。分治的确是很是重要的工程优化手段。
页面上的每一个 独立的 可视/可交互区域视为一个组件;
==每一个组件对应一个工程目录==,组件所需的各类资源都在这个目录下就近维护;
因为组件具备独立性,所以组件与组件之间能够 自由组合;
页面只不过是组件的容器,负责组合组件造成功能完整的界面;
当不须要某个组件,或者想要替换组件时,能够整个目录删除/替换。
因为系统功能被分治到独立的模块或组件中,粒度比较精细,组织形式松散,开发者之间不会产生开发时序的依赖,大幅提高并行的开发效率,理论上容许随时加入新成员认领组件开发或维护工做,也更容易支持多个团队共同维护一个大型站点的开发。
模块化/组件化开发以后,咱们最终要解决的,就是模块/组件加载的技术问题。然而前端与客户端GUI软件有一个很大的不一样:前端是一种远程部署,运行时增量下载的GUI软件。
若是用户第一次访问页面就强制其加载全站静态资源再展现,相信会有不少用户由于失去耐心而流失。根据“增量”的原则,咱们应该精心规划每一个页面的资源加载策略,使得用户不管访问哪一个页面都能按需加载页面所需资源,没访问过的无需加载,访问过的能够缓存复用,最终带来流畅的应用体验。
这正是Web应用“免安装”的魅力所在。
由“增量”原则引伸出的前端优化技巧几乎成为了性能优化的核心。
有加载相关的按需加载、延迟加载、预加载、请求合并等策略;
有缓存相关的浏览器缓存利用,缓存更新、缓存共享、非覆盖式发布等方案;
还有复杂的BigRender、BigPipe、Quickling、PageCache等技术。
这些优化方案无不围绕着如何将增量原则作到极致而展开。
静态资源管理系统 = 资源表 + 资源加载框架
资源表是一份数据文件(好比JSON),是项目中全部静态资源(主要是JS和CSS)的构建信息记录,经过构建工具扫描项目源码生成,是一种k-v结构的数据,以每一个资源的id为key,记录了资源的类别、部署路径、依赖关系、打包合并等内容。
{ "res" : { "widget/a/a.css" : "/widget/a/a_1688c82.css", "widget/a/a.js" : "/widget/a/a_ac3123s.js", "widget/b/b.css" : "/widget/b/b_52923ed.css", "widget/b/b.js" : "/widget/b/b_a5cd123.js", "widget/c/c.css" : "/widget/c/c_03cab13.css", "widget/c/c.js" : "/widget/c/c_bf0ae3f.js", "jquery.js" : "/jquery_9151577.js", "bootstrap.css" : "/bootstrap_f5ba12d.css", "bootstrap.js" : "/bootstrap_a0b3ef9.js" }, "pkg" : { "p0" : { "url" : "/pkg/lib_cef213d.js", "has" : [ "jquery.js", "bootstrap.js" ] }, "p1" : { "url" : "/pkg/lib_afec33f.css", "has" : [ "bootstrap.css" ] }, "p2" : { "url" : "/pkg/widgets_22feac1.js", "has" : [ "widget/a/a.js", "widget/b/b.js", "widget/c/c.js" ] }, "p3" : { "url" : "/pkg/widgets_af23ce5.css", "has" : [ "widget/a/a.css", "widget/b/b.css", "widget/c/c.css" ] } } }
在查表的时候,若是一个静态资源有pkg字段(用来记录web应用中一个页面加载过的静态资源,当下个页面用到这个资源就无需加载了,有效利用缓存),那么就去加载pkg字段所指向的打包文件,不然加载资源自己。
规范化实际上是工程化中很重要的一个部分,项目初期规范制定的好坏会直接影响到后期的开发质量。
...
任何简单机械的重复劳动都应该让机器去完成。
使用 AMD、CommonJS 及 ES Harmony 编写模块化的 JavaScript
前端工程与性能优化
前端工程——基础篇