翁阳(沪江开发工程师)
本文为原创翻译,如需转载请标明出处,不当之处敬请指出javascript
前端,是一个常常会被小觑的技术领域,在大多不明因此的人眼里,前端不过是排排版、布布局,甚至是一些前端的新手也会这样认为(这里的前端并不特指 Web 前端,移动端也可归结为前端)。那么前端真的就如此无趣且一成不变么?css
之因此本系列取名为 Thinking in FE,是由于 Thinking 让人沉静、不浮躁,就该用这种心态来面对前端。做为本系列的第一篇,我以为是颇有必要把 Web 前端拿出来讲说,这几年 Web 前端变革得太快,若是你仍是觉得吃透了 float 就吃透了整个布局,搞定了 css + div 就能纵横 Web FE 的话,本篇就是为你而准备的。html
前几年,当我还奋战在 Web 前端开发的第一线时,那时候的项目开发模式是简单但容易出问题的。当时后端主要使用的技术是微软的 WebAPI,前端的 IDE 天然就被 Visual Studio 包揽了(固然那时 WebStorm、PhpStorm 也都在不一样项目中承担着 IDE 的角色)。IDE 倒不是什么问题,当时主要的问题在于前端第三方库依赖的管理,基本上都是手动引用,长时间后会连一些库的具体版本都忘记了。这在多人协做开发时很容易出问题,也不利于项目的持续和快速发展,长此以往一个项目会变得陈旧、死气沉沉。前端
如今的开发模式,悄然变得更轻却又更重了。更轻的是 IDE,咱们开始倾向于使用像 Sublime、Atom、VSCode 这种轻量级的文本编辑器,再配合一些平常须要的小插件;而更重的是项目的依赖管理和构建方式,依赖管理已经被 NodeJS 的包管理工具npm包揽了,基本上咱们须要什么样的库,只要简单的npm install一下就能够了,而构建工具不少,好比 Webpack、Gulp、Grunt、Yeoman 等,但也慢慢的有被 Webpack 一统江湖的趋势。java
有了依赖管理、构建工具,而且能够经过npm配合其余工具来执行单元测试,咱们即可以很容易的将项目进行持续集成。这才是更加现代化的开发模式,而整个 Web 前端的生态也趋向完整了。react
做为一个站在时代前沿的 Web 前端开发者,可能会是全部开发工种中接触开发语言最多的一个,至少你须要掌握三门语言:html、css、javascript,这是最终的宿主,也就是浏览器所原生支持的三种语言,分别用于:结构、样式、交互。但若是你真的只会这三种语言,那你确定算不上一个合格的 Web 前端开发者,随着广大先驱者的智慧凝结,这三种基础的语言衍化出了不少独立的语言,而这些衍化的产物已经愈来愈被现代化的 Web 前端所普遍使用。webpack
从 html 所衍化出来的是各类模板语言,好比 backbone、angular 所提供的。模板的做用是将结构高度抽象,从而避免不少没必要要的重复工做,而且使得前端页面更加动态化。html 自己是静态的描述语言,有了模板的支持,咱们能够像下面这样来让其动态化:git
从 css 所衍化出来的,即是和样式相关的语言了,与 html 语言同样,css 也是一种静态的描述语言,自己不支持变量和条件分支。做为对 css 的扩充,市面上出现了像 less、sass 这样一些语言,它们使得样式的描述更加结构化,而且能够经过变量很方面的来修改和维护,这对须要提供样式定制化的第三方组件而言仍是很是有用的。下面是 sass 的变量和嵌套示例:es6
最后从 javascript 中衍化出来的,即是不少对 javascript 特性进行补充的语言了。javascript 自己是基于原型的语言,自身也有一些设计上的缺陷,最多见的即是变量的做用域问题,也就是所谓的变量提高问题。不过在 ES6 出来后,javascript 获得了质的提高,而在这以前,出现了 javascript 的替代语言,以 typescript 和 coffeescript 最为经常使用,而且如今还被普遍使用着。不管是 typescript 仍是 coffeescript,它们都是对 javascript 的补充,而 coffeescript 更像是一门新的语言。它们使得 javascript 更加的面向对象,并引入了更多函数式语言的特性,让书写更加优雅、温馨,下面一段 coffeescript(摘自Coffee-Script中文网),你们感觉下:github
自 ES6 出来并受到不少工具的支持后,已经更加推荐直接使用 ES6 来编写项目了,ES6 弥补了 javascript 以前一直缺少的原生模块化支持(这里说的是原生,排除 CommonJS、AMD、CMD 规范的第三方实现),对面向对象也有了更好的支持,而且明确了变量、做用域,也引入了不少函数式编程的概念。最重要的是在2013年 ES6 标准就已经肯定了,对于新的提案 TC39 只会往 ES7 归入,因此在项目中使用不会面临像使用 Swift 同样不断变动的窘境。
上面说过了,浏览器原生只支持最基本的那三种语言,那么若是想使用这些衍化出来的语言或者是如今还不被很好支持的ES六、ES7,咱们须要相应的转换工具。而这些转换操做均可以很是简单的使用 webpack 对应的 loader 来完成。不得不说 webpack 已经成为了 Web 前端构建的一站式工具,经过组合不一样的 loader,咱们能够完成转换->合并->压缩->打包等一系列中间过程。
若是要论这几年来,对 Web 前端思想产生颠覆性的框架,那应该是非 React 莫属了。Facebook 在2013年开源了这个框架,由此引起了一系列的变革。React 的核心思想是组件化,化整为零,分而治之。而 React 出现的缘由,也正是由于 Facebook 对当时市面上全部的前端框架都不满意,既然不满意,他们就立马本身作了一个。
在 React 出来以前,市面上使用较多的都是一些MV*系列的框架,比较有表明性的应该算是谷歌的 Angular 了。但这类框架的学习曲线仍是比较高的,最重要的是,对于通常人而言它们所表述的意图不够直观。从视图到模型,虽然力求低耦合,但仍是不得不进行约定、依赖,由于最终视图和模型须要绑定,那不管如何解耦都不可能作到干净利落,约定只会徒增维护的复杂度。
对此,React 提出了组件的概念(固然这个概念在其它领域早就有过),一个组件就是一个高内聚的封装。对外部而言组件的输入是属性(props),输出是最终的视图,属性是恒定的,也就是说外部输入以后,就不会被改变了。而让组件改变的是状态(state),对于 React 而言,状态是由组件内部进行维护的,这种思想让组件变得更加内聚、可控。下面是一个很是简单的 React 组件:
上面这个组件拥有一个hidden的状态,而render方法中的内容也是让人一目了然(JSX语法让组件更加内聚)。经过界面交互或其它一些手段咱们能够改变hidden的值,而这会实时体现到render方法中。React 本身维护了一套虚拟 DOM,通常状况下咱们没必要刻意考虑渲染性能问题,但若是你想本身控制是否重绘的话,React 的组件也给你提供了这样的控制能力。
React 的组件除了内聚以外,还能够进行组合,一个组件能够嵌入其它多个组件。这使得咱们在进行实际开发以前,须要对即将完成的内容进行组件划分,在通用和简单两方面来做权衡。也就是说,React 的思想已经颠覆了咱们思考问题的方式,而它给咱们带来的收获是组件的不断积累,以及开发速度和可维护性的提升。
在绝大数MV*系列架构的框架中,视图 DOM 和视图模型之间是进行双向绑定的,这种强绑定的情形在不少复杂的场景下会带来让人没法维护的问题。当这样的状况愈来愈广泛时,Facebook 提出了单向数据流的概念,并把这种思想称之为Flux,且推出了官方实现flux。不得不说 Facebook 是个了不得的公司,也不得不说 Web 前端一直是这些新思惟的探路者。不过flux很快就被另外一个开源项目慢慢取代了,也就是社区中很是火爆的redux,但思想仍是一致的。
这里有必要解释一下单向的概念,整个 Flux 的数据流以下:
1.用户触发 View 的某个操做,View 向 Dispatcher 发出一个 Action 2.Dispatcher 收到 Action 后,对 Store 进行更新 3.Store 更新后,发出事件通知 View 4.View 收到事件后,进行页面更新
这里整个数据流都是单向流动的(概念抽象中没有双向箭头),全部状态都维护在 Store 之中,这让咱们对状态变动进行追踪变得很是简单。在redux的实现中,从 Dispatcher 到 Store 之间,咱们还能够安装不少自定义的中间件,来进行一些切面处理,好比日志、受权、统计等。
Facebook 的 React 仅仅是提供了组件化的构建方案,而对于组件所构成的模块并无提供更多架构上的支持,这点基于 Flux 思想的redux恰好能够对其进行补充。在解释如何让它们衔接以前,咱们有必要先看点其它内容。
全部的项目开发中,为了追求更好的用户体验,咱们不可避免的要面对异步问题。同步操做下,咱们对流程管理和安排很是简单清晰,相比之下,异步就没有那么容易去维护了。
而在 Web 前端,很长一段时间里,ajax 几乎就成了异步的代名词,由于在实际开发中,80%以上的异步都来自于异步网络请求。时至今日,我以为须要从新定义下异步在 Web 前端中的定位了,特别是在使用了redux以后。从最初的action派发,到最终的状态变动,以及状态变动后引起的视图渲染,这一系列的步骤,咱们都应该将其视为异步(参考上文 Flux 的图片)。
众所周知,javascript 处于一个单线程的运行环境中,但异步的引入使得咱们也须要面临一些多线程下才存在的问题。而且咱们从新定义了并发的概念,在 javascript 中,并发指的是一个异步任务还没有完成,同时又产生了其它异步任务。好比,咱们同时发出了两个 ajax 请求,那么咱们就必需要面对这两个请求返回时间、顺序不肯定性的结果。
在 ES6 的语言标准中,引入了Promise概念,能够方便咱们对异步任务进行链式编排,而且能够统一进行错误处理,下面是一个简单的例子:
虽然这种链式调用从某种程度上让代码更加清晰,但在对异步返回数据须要进行条件分支判断,或者一些更加复杂的逻辑操做时,Promise也就显得有些力不从心了。在 ES6 中还引入了另一个概念,叫Generator,与之对应的关键字是yield,Generator的特性是函数内部维护了上一次执行到的位置,而在外部调用next()控制它进一步执行(关于这方面更多的知识,请参考相关ES6手册)。其实这点无疑是走了微软 C# 的老路,而且在 ES7 中引入的async和await也是与 C# 同出一辙,在 C# 推出yield关键字后,社区也有达人以此实现了一套异步任务编织的框架,那么 Web 前端天然也不例外了。
这里不得不说一下redux的一个中间件redux-saga,它是彻底基于Generator特性实现的一套异步任务编织框架,而且很是强大。一个saga对应一个Generator函数,而且saga分为两种:
1.watcher saga: 负责监控 redux 的 action,而且对任务进行具体编排 2.worker saga: 处理由watcher saga编排的具体任务
若是想要了解更多关于redux-saga的内容,仍是建议去翻阅下官方文档,这里给出一个简单的示例,一睹它的威力(摘自saga文档):
上面的示例中有两个saga,其中loginFlow为 watcher 而authorize为 worker。在loginFlow中,当咱们收到LOGIN_REQUEST的action时,取出其中的user和password状态,非阻塞的去调用authorize,而且开始监控LOGOUT和LOGIN_ERROR两个action,当收到的action为LOGOUT,此时前一个LOGIN_REQUEST可能并未执行完成,因此咱们须要取消它,在这一切完成后,咱们调用clearItem来清空本地存储的token,再次回归到监控LOGIN_REQUEST。而authorize这个saga中的流程也相对简单明了,这里就不做更多的阐述了。
经过saga的实现,能够与Promise进行对比,不难发现它更加的同步化,全部的代码彻底看不出异步的影子,因此在进行复杂的异步任务编排和分支控制时,会很是的简洁明了。
上面说到了项目构建工具、各类新兴的开发语言、React、Redux 以及 Redux-saga,那么在一个实际的项目中,咱们如何将它们融合起来,进行更加现代化的 Web 开发呢?其实很简单,咱们能够经过npm来进行项目包依赖管理,而且经过webpack来将它们所有串联起来。
对于webpack,咱们须要安装一系列的loader而且在webpack.config.js中进行配置,大概会用到下面这些loader:
babel-loader:用于转换 ES六、ES七、JSX 语法
file-loader:用于简单的文件拷贝
css-loader:用户 CSS 的压缩,打包
less-loader:用户 LESS 的转换
url-loader:用户图片资源的转换、打包
html-loader:用户 HTML 文件的连接替换、打包
html-minify-loader:用于 HTML 文件的压缩
具体的配置须要根据项目自己而定,详细的细节能够参考webpack的官方文档。当咱们把webpack这些loader配置完毕后,能够按照一些咱们须要的框架和中间件了,大致应该以下:
react:react 组件化的核心库
react-dom:react 提供的一些 DOM 操做辅助方法库,用于在 DOM 上渲染 react 组件
react-router:使用 react 开发单页应用时,这个库是必须的,提供了基于 react 组件化思路的路由解决方案
redux:redux 库,上文中已经有所解释,这里就很少说了
redux-actions:方便在 redux 中 action 和对应的state进行管理
redux-saga:redux 的中间件,用于异步任务编织
react-router-redux:redux 的中间件,用于同步 redux 中的路由状态,能够经过 - redux 的 ation 来控制路由
reselect:在较大型项目中使用,用于react和redux链接时connect中map参数的管理,能够有效减小状态变动,减小组件渲染的次数
有了上面的这些组件,咱们基本上能够愉快的进行项目开发了,那么在整个项目的流程中,它们是如何进行协做的呢?能够参考下图:
能够看到,贯穿其中最多的即是action和state,而redux显然是整个项目链接起来的枢纽,saga则是用于封装了全部的异步网络请求(封装成更加业务化的任务)。这里举一条比较常见的流程:用户从界面上点击了某个按钮,而后发送了网络请求,以及请求响应后对界面的更新:
1.首先从 react 组件的 View 上派发了一个 action 到 Redux 2.Redux 的中间件 Saga 会监控这个 action 并做出网络请求 3.响应回来后,Saga 会经过 put 将响应的 action 派发到 Redux 4.Redux 接收到这个 action 对相应状态做出变动 5.react 链接到(connect)这个状态的相应组件会收到状态变动,从新渲染 View
彷佛看起来比较复杂,但在了解透彻相应组件的职责后,其实并没那么复杂。为了让开发过程当中调试更加快捷,咱们还能够安装一些开发中须要用到的工具模块。这里推荐使用 dora,配合dora-plugin-proxy咱们能够在开发过程当中模拟后端响应数据,再配合dora-plugin-webpack-hmr能够实现开发过程当中,模块的热加载,让咱们无需不断刷新浏览器就能看到最新的界面效果,固然选择还有不少,dora 只是我以为比较好用的一款。
本篇带着你们蜻蜓点水的将现代化的 Web 前端看了个大概,这其中更多的是在阐述思想和理念,随着时代的发展,Web 前端并非大多数人们眼中的“作作网页而已”,它的理念走在了时代的前言,它的生态也比不少其它方向丰富、健全。
不小看、自觉得是,要永远抱着敬畏的态度,这是成长的基础,也是咱们做为技术人员该有的素质。
Webpack: https://webpack.github.io
dora-js: https://github.com/dora-js
Redux 中文文档: http://cn.redux.js.org/
React-Router: https://github.com/reactjs/re...
reselect: https://github.com/reactjs/re...
Redux-Sage: http://yelouafi.github.io/red...
ECMAScript 6 中文文档: http://es6.ruanyifeng.com
iKcamp原创新书《移动Web前端高效开发实战》已在亚马逊、京东、当当开售。