前端工程的昨天、今天和明天

Hex 为您分享 / Github: @hex-ci

前端的工程化

  • 早期的年代,前端不工程
    • 页面交互复杂度不高
    • 设计师如果会一些基础的 HTML、CSS 的话,与程序员配合就几乎已经足以开发前端
    • 架构简单,没有可维护性或可复用性等问题
  • 近些年的前端工程化需求
    • Web Application 的普及,伴随而来用户界面的复杂性提高,需要有组织的架构与工作流程
    • 原有的前端技术与标准难以应付日渐增长的工程化需要

时间线

2007 ~ 2008:百废待举

  • Firefox 刚从 IE 大一统的天下瓜分掉大半领土,逼迫微软开始继续关注浏览器开发与 Web 领域
  • Google Chrome 带着 V8 引擎横空出世,打响新一波的浏览器大战
  • Web 标准开始继续在各家浏览器厂商的角力与争执中推动
  • jQuery 开始普及,逐渐成为最热门的 JavaScript 库
  • HTML5 第一份正式草案发布

2009 ~ 2010:寻求方向

  • ECMAScript 5 发布
  • 微软发布 世纪遗毒 IE 8
  • Node.js 问世,原本只能运行于浏览器的 Javascript 开始能作为一门服务端语言使用
  • HTML5 成为大多数人认可的 Web 未来主流技术,众多特性开始被主流浏览器所支持
  • 移动设备访问 Web 流量开始显著增长

2011 ~ 2012:移动设备的崛起

  • HTML5 技术获得重大发展以及广泛地被应用,称霸十余载的 Flash 开始渐渐凋零
  • Web 标准开始稳定的由各家浏览器厂商角力与协商来慢慢的持续推进,最后经过共识而产生的标准,各家浏览器也绝大部分会遵守标准来实现
  • 移动设备上的 Web 体验开始被重视,Responsive Web Design 概念广泛被采用,相关的前端工具如雨后春笋般冒出
  • 前端的工程化开始被讨论与推动
    • 社区自发推进各种模块机制
    • CoffeeScript 与 TypeScript 等 Transpiler 问世

2013 ~ 2014:工程化逐渐发展

  • HTML5 正式定稿成为推荐标准
  • 各种 CSS 预处理器出现并被使用
  • PhoneGap 这种 Hybrid Mobile App 模式被广泛应用
  • ECMAScript 6 渐渐受到瞩目与发展,但还未普遍用于开发
  • 第一代的 SPA 开发技术成熟,如 Angular / Backbone / Ember
  • 新时代的前端框架 ReactJS 刚问世,前端应用的组件化开发趋势开始萌芽
  • Node.js 开始大量被用于企业级应用

2015 ~ 2017:新时代的来临

  • ES6 定稿,随着 Babel 的普及而开始大规模的被使用到实际开发中
  • npm 与 Webpack 脱颖而出,成为前端构建工具的主流
  • React 相关生态系大放异彩,全面的影响并主导前端技术与社区的趋势走向
    • 受到 React 影响并借鉴之,Angular 2、Vue 等新一代前端解决方案到来,与 React 生态系互相竞争与推动进步
    • React Native 这种 JS Bridge Native App 解决方案问世,Weex、NativeScript 等接踵而至
    • Redux 问世,将函数式编程在前端的趋势大幅拉升

昨天

HTML

HTML

  • HTML API 分成两大部分
    • DOM(Document Object Model)
    • BOM(Browser Object Model)
  • W3C 曾经决定放弃 HTML,转而推动 XHTML
  • 浏览器厂商们不认同,搞小团体(WHATWG)以民间力量自行推动新一代 HTML 标准
  • 因为 WHATWG 的成员就是浏览器厂商,所以如果他们不愿意支持的话,W3C 强行推动什么标准也都是枉然
  • 最后 W3C 屈服了,接纳 WHATWG 所推动的成果,重开 HTML 工作小组,最后与浏览器厂商合力推出现在的 HTML5 标准

HTML5

  • HTML5 提供了大量的新特性,其中最重要的一类是提供浏览器原生的 RIA(Rich Internet Application)能力,而不再需要 Flash 等外部插件
    • 视频播放

    • 音频播放

    • 绘图

    • 游戏

    • 音视频处理

jQuery

jQuery

  • 早期各家浏览器对于标准的制定流程没有共识,导致各浏览器的 DOM 操作或是 AJAX 等 API 都非常不一致
  • jQuery 带来了在当时相当先进的思想与功能
    • 抽象化统一各家浏览器的 API 操作差异
    • 使用 CSS Selector 来选择 DOM
    • 链式操作的习惯
    • ...etc
  • 首个取得巨大成功与广泛应用的 JS 库
  • 现今已无法用于大型且复杂的前端应用开发,但仍然拥有承前启后的重要历史地位,在简单的场景中也依然可以使用

Flash

Flash

  • Flash 在那个 HTML 原生不支持 RIA 功能的年代,提供了重要的替代方案,使得 Web 能够更快速的流行于世界
  • 跟 jQuery 一样,在 HTML5 发达的这个年代已经逐渐被淘汰,但仍然在历史中有重要地位,完成了承前启后的历史使命

今天

ECMAScript

  • ECMAScript 是 JavaScript 的语言标准规范,后者是前者的实现
  • Node.js 是 ECMAScript 在服务器端的语言实现
  • ECMAScript 6 是 2015 年时正式发表的新一代版本标准,加入了许多重要的新语法与功能,令 JavaScript 向成为企业级语言迈进一大步
  • 目前 ES7(ES 2016)、ES8(ES 2017)也已正式发表,惯例将之统称为 ES6+,代表新时代的 JavaScript 版本

前端开发所面临的问题

  • 构建工具
  • 标准兼容性
  • 模块化与包管理
  • 异步处理
  • 大型且复杂的前端应用开发

构建工具

(Building Tool)

Webpack

  • 能够对前端代码或图片、字体等内容进行一些编译、打包、压缩和一些额外的加工处理
  • Webpack 取代 Gulp 与 Grunt 成为主流的最大原因
    • Gulp 或 Grunt 都是纯粹的工作自动化工具,并不是专为 Web 前端所设计
    • Webpack 是只用于 Web 前端的构建处理,为其定制了一套平台与接口,以实现系统性的配置与构建
  • Webpack 的发展为前端 SPA 提供了更强大的开发工具

标准兼容性

ECMAScript 6+ 的问题

  • 各家浏览器的支持还不够完美与普遍,目前仍有部分功能未被实现完成
  • 某些使用者可能永远也不会更新到支持 ES6+ 的浏览器
  • 因此我们需要依赖工具将我们的 ES6+ 代码编译成 ES5 环境能运行的版本,让我们能放心的使用 ES6+ 语法或功能来开发

Babel

  • Babel 是目前最主流的 JavaScript 编译工具
  • 可以单独运行,也可以配合 Webpack 这种前端打包工具一起使用
  • 早期原名 6 to 5,专门处理 ES6 to ES5 的代码转换
  • 后来 Babel 决定平台化,除了可以使用官方维护的 ES6+ 与 React JSX 转换功能之外,也提供 API 让大家自行开发其他的转换工具
  • Babel 现今已经成为 ECMAScript 标准的重要推动力量

模块化与包管理

为什么需要模块机制?

  • 命名冲突
    • 全局命名空间下容易造成变量命名冲突,尤其是跟第三方包冲突时更难解决
  • 代码间的顺序与依赖性维护困难
    • 开发人员必须自行理解并解决不同 JS 文件之间,以及与第三方包的相互依赖关系
  • 在大型项目中各种资源难以管理,长期积累的问题导致代码库混乱不堪

但是...

  • JavaScript 这门语言本身长时间以来都没有设计这样的模块机制
  • 因此社区只好自行设计并推广一些民间标准
    • AMD(Asynchronous Module Definition)
    • CommonJS:Node.js 采用的模块规范
  • 直到 ES6 里才制订了原生的模块机制规范与语法
    • 融合了各家社区标准的优点,并更加先进易用

现状

  • ES6 Module 标准本身已经定案,Google Chrome 从 61 开始支持 ES6 Module
  • 可以使用 Webpack 来做打包处理,以实现前端环境中模拟模块机制的效果

npm(Node Package Manager)

  • npm 本身是 Node.js(后端)的包管理系统
  • 因为 Node.js 的逐渐崛起以及 npm 的普及,前端的 JS 或 CSS 代码也被放上了 npm,渐渐也成为了前端的包管理系统主流,通吃前后端
  • Bower 等纯前端的包管理系统逐渐式微
  • 配合 Webpack 的模块机制模拟能力,可以让一些本来只能在 Node 上使用的模块也能在前端中使用

异步处理

回调地狱

  • 在 JavaScript 中做异步流程处理,传统的方法是使用回调函数
  • 当连续性的异步回调函数一多的时候,就像是...

Promise

  • Promise 是一种异步程序设计的解决方案,比传统使用回调函数的方式要更合理与强大
  • Promise 是一个容器,里面保存了某个未来才会结束的事件
  • 通过原生的 Promise API 来操作这些异步事件的流程与数据传输

const timeout = (delay) => new Promise((resolve, reject) => {
  if (typeof delay === 'number') {
    setTimeout(() => {
      resolve(`${delay}ms passed away.`);
    }, delay);
  } else {
    reject('input is a non-number value.');
  }
});
							

timeout(3000)
.then(value => {
  console.log(value);
}).catch(error => {
  console.error(error);
});
// "3000ms passed away."
							

timeout('abc')
.then(value => {
  console.log(value);
}).catch(error => {
  console.error(error);
});
// "input is a non-number value."
							

Promise

  • 以下是结合 jQuery Ajax 的范例

const getArticleList = () => new Promise((resolve, reject) => {
  $.ajax({
    url: '/api/article/list',
    dataType: 'json',
    success: (result) => resolve(result)
  });
});

const getArticle = (id) => new Promise((resolve, reject) => {
  $.ajax({
    url: `/api/article/detail/${id}`,
    dataType: 'json',
    success: (result) => resolve(result)
  });
});

const getAuthor = (id) => new Promise((resolve, reject) => {
  $.ajax({
    url: `/api/author/${id}`,
    dataType: 'json',
    success: (result) => resolve(result)
  });
});

getArticleList()
.then(articles => getArticle(articles[0].id))
.then(article => getAuthor(article.authorId))
.then(author => console.log(author))
.catch(error => console.error(error));
							

async / await

  • async 函数是基于 Promise 的扩充语法
  • 能够让 Promise 异步操作更接近同步的语意
    • async 关键字
      • 表示这是一个 async 函数,该函数会自动变成一个会返回 promise 实例的函数
    • await 关键字
      • 表示在此处等待这个 promise 的 resolve 或 reject 结果

const getAuthorByArticle = async() => {
  try {
    const articles = await getArticleList();
    const article = await getArticle(articles[0].id);
    const author = await getAuthor(article.authorId);
    console.log(author);
  } catch (error) {
    console.error(error);
  }
}
							

Fetch API

  • Fetch 是目标取代 XHR 的新 AJAX 技术标准,已被一些主流浏览器支持
  • 是一个 HTML BOM 的 API,并非来自 ECMAScript 标准
  • Fetch 原生基于 Promise 实现,因此也可以直接结合 async / await 使用

const getArticleList = async() => {
  const response = await fetch('/api/article/list');
  return await response.json();
}

const getArticle = async(id) => {
  const response = await fetch(`/api/article/detail/${id}`);
  return await response.json();
}

const getAuthor = async(id) => {
  const response = await fetch(`/api/author/${id}`);
  return await response.json();
}

try {
  const articles = await getArticleList();
  const article = await getArticle(articles[0].id);
  const author = await getAuthor(article.authorId);
  console.log(author);
} catch (error) {
  console.error(error);
}
							

前端应用开发

主要趋势

  • 组件化开发
  • Virtual DOM
  • One-way Dataflow vs Two-way Binding

组件化

  • 将 HTML 代码拆分为一个一个的组件模板
  • 方便进行抽象化以及复用
  • React、Vue、Angular 2 等技术都有实现自己的组件机制,而 HTML 标准本身也在发展原生的组件系统

class ProductItem extends React.Component {
  handleButtonClick = () => {
    alert(this.props.price);
  }

  render() {
    return (
      <div className="item">
        <div className="title">{this.props.title}</div>
        <div className="price">{this.props.price}</div>
        <button onClick={this.handleButtonClick}>购买</button>
      </div>
    );
  }
}
							

Virtual DOM

  • 在框架与浏览器 DOM 之间实现一套模拟的 DOM 中间层
  • 当页面需要改变时
    • 根据最新的数据重绘出新的 VDOM 树
    • 与更改前的旧 VDOM 树进行全面的比较与计算
    • 其中新旧差异的地方,才真的会在实际的 DOM 上发生操作改变,以此来减少不必要的实际 DOM 操作以提高性能

One-way Dataflow

& Two-way Binding

  • One-way Dataflow
    • 数据是 UI 发生改变的唯一途径,只有数据改变时才会导致 UI 改变,并且 UI 不能直接改变数据
  • Two-way Binding
    • UI 拥有直接改变特定数据的能力,自动与指定的数据做双向绑定

主流方案

React Vue Angular 2
类型 专注 UI 专注 UI 全能型框架
主导者 Facebook 个人项目 Google
组件化
Virtual DOM
数据流 单向 单向 / 双向 单向 / 双向
View 结构定义 JSX template / JSX template
学习门槛

明天

PWA(Progressive Web App)

  • Google 推广的一种 Web Mobile App 技术,目的是希望提高移动端网页应用的体验
    • 安装性:可以瞬间添加到主页面并且全屏幕运行,不用包成真的 App 到 Store 里下载安装
    • 离线使用:安装时会将应用的基本架构存储在本地,以达到在断网或网络信号弱的时候依然能秒开的体验
    • 通知:能够主动发起通知信息

JS Bridge Native App

  • 使用 JavaScript 来开发原生的移动设备 App(Android、iOS)
  • 原理是框架包含了 JS 与原生程序的桥梁,以及 JS 代码解释引擎,以达到写 JS 代码产生原生组件的效果,并且两大平台的应用能共用大部分代码
  • 与 Web 三大前端应用方案有密切联系
    • React:React Native
    • Vue:Weex
    • Angular 2:NativeScript

WebAssembly

  • 各大浏览器厂商合作发展的一项技术,目的是能够将其他编程语言经过编译之后在浏览器环境中执行
  • 目前主要的用途是游戏(C++)
  • 还在发展阶段,到正式广泛应用可能还要 3 ~ 5 年的时间
  • 发展顺利并成熟的话,我们可能可以看到用多个语言皆可编写前端应用的一天

展望 2019

2019 前端鬼畜大预测

  • Babel 继续担任 ECMAScript 的重要推动力量
  • Vue 会迎来高峰,力压 Angular 2 的地位,紧逼 React
  • HTML 原生的 Web Component 依然受到冷落,但伺机而动
  • 更多前端技术与开发厂商放弃支持 IE 9,IE 10 准备上刑场
  • WebAssembly 进一步完善和发展
  • Typescript & Flow 越来越多的用于实际开发中

大前端時代

大前端時代

  • 前端工程由于使用者体验的需求提高、UI 复杂度大幅增加而重要性提高
  • 各种移动设备、平台更让前端的需求广泛
  • HTML5 的发展、Hybrid App、PWA、JS Bridge Native App 让 Web 前端工程师能有效的跨足于移动设备平台的开发
  • 前端技术发展成熟,工程化技术不断在推翻与进步
  • 对前端工程师来说,学习的能力非常重要

学习渠道

  • 实体书籍
  • 网络资源:文章、视频教程、线上社区、线上读书会
  • 社区活动:Conf、小型分享会、沙龙
  • 参与开源社区
  • 认识并跟其他人交流、沟通、合作

THE END

谢谢!

整理自 @Zet 的分享