前端工程简史
时间线
- 2005 ajax
- 2008 Google推出javascript引擎 V8,大大提高js运行性能,业内开始提倡REST api 和SPA客户端
- 2009 Node.js问世
前端工程师技能栈
- 硬技能:html/css/js,良好的抽象能力和架构能力
- 软技能: 用户体验 ,就是性能
- 扩展技能: Node.js 代表的web服务端知识。
有助于前端工程师编写更合理的客户端逻辑和即使定位问题。
前端工程体系不是vue/react这种业务开发框架,而是一种服务,对象是一线业务开发人员。
构建
Node.js 是前端工具得以发展的技术基础, 前端项目不断复杂化是催动前端工具发展的环境因素。
构建,或者叫编译,是将源代码转化为宿主浏览器可执行的代码。
构建需要解决的问题归纳为3类
- 面向语言(Less/sass/jade/es6)
- 面向优化
- 依赖打包,将同步依赖的文件打包在一起,减少http请求数量
- 资源嵌入,小于10k图片转base64, 减少http请求
- 文件压缩
- hash指纹, 文件名加入hash指纹,以应对浏览器缓存策略
- 面向部署(本地、生产环境)
ECMAScript
Javascript = ECMAScript + 宿主API
ES6给javascript跨时代的意义,不仅仅是语言本身加入了类、静态模块体系、块级作用域等高级编程语言的特性,更多的是以ES6为代表的前端界规范意识的加强。但是目前浏览器对ECMAScript规范的实现仍远远落后于规范的更新。即使最新版本的Chrome浏览器也没有完全支持ECMAScript2015,但可以使用Babel-下一代Js语法编译器。
CSS预编译与postCSS
- 使用CSS预编译弥补CSS源码的弱编程能力,比如变量、运算、继承、模块化等。
- 使用PostCSS处理针对浏览器的需求,比如autoprefix, 自动CSS Sprites
webpack配置
loader按照索引反向执行1
2
3
4
5
6
7
8{
test: /\.less$/,
use:[
'style-loader',
'css-loader',
'less-loader'
]
}
css-loader作用是解析css源文件并获取其引用的资源,比如@import或url(),然后根据webpack配置编译这些资源。
style-loader负责将css代码通过style标签出入html文档,所以独立导出css就不需要style-loader, 可以替换为extract-text-webpack-plugin,如下1
2
3
4
5
6
7
8
9
10
11{
test: /\.less/,
use: ExtractTextPlugin.extract({
use:[
{loader:'css-loader', options:{ importLoaders:2 }},
{loader:'postcss-loader', options:{}},
{loader:'less-loader', options:{}}
]
publicPath: '/'
})
}
模块化开发
前端模块化发展史
CommonJs
最初叫ServerJs, 是一种只适合于JavaScript的静态模块化规范,适合Node.js,并不适合浏览器,因为浏览器的前端资源 不仅仅是js, 还有css、图片等,而且CommonJs所有模块是同步阻塞加载,无法实现按需异步加载
AMD/CMD–着力于浏览器的模块化规范
在CommonJS基础上,AMD/CMD扩展了一下功能
- 可以处理js以外的资源
- 源码无需编译可在浏览器运行
- 按需异步加载、并行加载
- 插件系统
然而CommonJs、 AMD/CMD 三者共同缺点如下:
- | 缺点 |
---|---|
1 | 应用场景单一,无法跨环境 |
2 | 构建工具不统一,比如针对CommonJs的Broserify,AMD的r.js,CMD的SPM |
3 | 不同规范模块无法混合使用,模块复用性不高 |
4 | 未来不可期 |
ES6 Module
ES6 Module是一种静态模块体系,在最新版本的Node.js中完全可以取代CommonJS,目前处于stage 3阶段的import()函数可以满足按需加载需求。受限于浏览器的实现程度,目前针对浏览器的模块仍然需要构建工具进行编译。
webpack模块化构建
webpack支持CommonJs/ AMD 和ES6。 针对三者异步模块构建中存在的细微差别
模块化规范 | 特点 | 构建 |
---|---|---|
CommonJs | 不具备异步加载功能 | 借助webpack提供的api实现(require.ensure API是webpack v1版本,已废弃,v2推荐import函数) |
AMD | 具备异步加载功能 | 不能定义输出的文件名称,欠缺语义 |
ES6 | 不支持异步加载 | 目前处于stage3的import()函数已被webpack支持 |
综上所述,AMD虽然具备异步加载功能,但是webpack对其支持度并不理想。CommonJs和ES6 Module虽然自身不支持异步加载,但是webpack提供了require.ensuire API和支持import()函数。从可移植性角度考虑,虽然require.ensure是webpack特有的api,如果代码移植到其他构件系统,此api会引起未知的错误。import函数处于stage 3状态,非常有可能加入未来的正式规范中,从可移植性和未来可期角度考虑,优于require.ensure。唯一缺点就是需要借助 特注释定义异步文件的名称。
增量更新与缓存
客户端的缓存分以下两种
- 本地存储,如localStorage/ SessionStorage等
- 利用http缓存,又分为强缓存和协商缓存
增量更新是目前大部分团队采用的缓存更新方案,结合http强缓存,既能保证用户第一时间获取最新资源,又能减少网络消耗。前端工程体系在此中作用如下:
- 构建产出文件hash指纹,这是实现增量更新必要条件
- 构建更新html文件及其他静态资源的引用url
hash指纹?通过既定的数据摘要算法(MD5)计算出的文件的值,文件内容修改,hash值跟着修改
策略 | 实现 | 缺点 | 优点 |
---|---|---|---|
覆盖更新 | 在引用资源的url后加参数?v=1.0 | 手动操作耗时耗力 | - |
增量更新 | hash指纹作为资源文件名的一部分 | 利于版本回滚和资源同步 |
ip、路径、后缀、参数不同的url会被浏览器视为全新的url,发出实体请求,但是hash的改变不会触发浏览器发出请求,这也是单页面应用使用hash作为路由的原因
按需加载与多模块加载架构场景下的增量更新
同步模块修改影响主模块的hash指纹,对异步文件无影响。
异步模块的修改影响对应异步文件的hash指纹,主文件hash指纹也同步修改,这样才能保证用户得到最新的异步文件。(coz主文件可能引用异步模块)
webpack实现增量更新构建方案
hash与chuckhash
hash定义: The hash of the compilation.compilation对象不是针对单个文件,而是针对项目中所有参与构建的文件。因此如果此hash作为构建输出的hash指纹,任何一个文件改动都会影响所有资源的缓存。hash并与不适用于增量更新的构建场景
chuckhash定义: 散列模块经过合并后的块,chuckhash就是一个个块依据自身的代码内容计算得到的hash值
contentHash: 解耦js与css文件hash指纹的关键。
js在webpack中是一等公民,其他类型的资源必须借助js文件参与构建,比如css在js中引入,如main.js引入main.app.css, webpack默认将构建的css代码合并到引用它的js文件中,然而实际项目中往往吧css抽离出来独立维护,有利于浏览器渲染优化和利用客户端缓存。
contentHash并不是webpack自身另外一种hash值,而是有ExtractTextPlugin提供,代表被导出内容计算后的hash