前端工程化的概念在近些年来逐渐成为主流构建大型web应用不可或缺的一部分,在此我经过如下这三方面总结一下本身的理解。javascript
为何须要前端工程化。css
前端工程化的演化。前端
怎么实现前端工程化。vue
随着近些年来前端技术的不断发展,愈来愈多复杂的业务放在了前端,前端再也不是之前几个HTML + CSS + javascript就能解决的了。业务复杂了,须要维护的代码量就天然多了,如此一来,前端代码的可靠性,可维护性,可拓展性,以及前端web应用的性能,开发效率等等各方面就成了不起不考虑的问题。java
因而咱们就产生了前端工程化这个概念,来解决这些问题。现阶段的前端工程化,须要考虑到各个方面,包括但不限于如下这几点:node
webpack-dev-server 热加载
之前,咱们的平常前端开发的流程是这样的: 修改代码 -> 切换IDE到浏览器 -> 刷新浏览器查看效果(有时候还须要清除缓存) -> 修改代码 ....。
这套流程,尤为是刷新浏览器这个过程,无疑是至关低效繁琐枯燥的。 而webpack-dev-server 替咱们解决了这个问题,它有两种模式,两种模式,一种是 watch 模式,功能是你修改代码,自动帮你刷新页面,无需手动刷新;另外一种更增强大,基于 websocket 全双工通讯技术,直接无刷新帮你把修改的代码替换掉。 从而极大程度上提升了开发效率。
react
数据mock
在后端接口还没提供的时候,先后端制定好共同的接口协议,开发时前端可使用mock模拟数据,与后端完全分离,并行开发。面向接口编程,尽量减小先后端沟通成本。jquery
代码合并压缩,混淆加密
android
减小小图片请求
webpack中url-loader:loader: 'url-loader?limit=8192'
,使得小于8kb的图片使用data:image base64 编码内联,减小图片请求量
webpack
部署静态文件缓存管理
使用webpack的内置的chunkhash功能,能够给生成的js文件添加hash后缀,标识文件版本。
模块化
主要指 js 代码的模块化。之前的前端开发并无模块化这个概念,这给维护大型项目带来了极大的困难。发展到如今的前端有不少模块化的方法可供选择,如seajs ,requirejs, webpack 等。 模块化能很大程度上提升了代码的可维护性。
CSS 预处理
经过sass,less 等css 预处理器,能够实现 css 文件的拆分,颗粒化,实现css可复用。并且经过autoprefixer或postcss 还可让 css 样式对老旧浏览器向下兼容。
此外,经过使用 css-modules 可以避免css全局污染的问题,极大提升css代码的可控性,不须要设定一堆命名空间与命名规范来限制。
ES6 + babel 编译
javascript自己设计存在必定程度上的缺陷,例如“没有模块化”,“没有块级做用域”,“全局变量污染”,“回调地狱”等等之类的问题,为了改善这些缺陷,计算机协会在2015年推出了ECMAScript 6 标准(今年已经ES8 已经发布了),使用ES6的语法除了能有效减小代码量以外,还引入了块级做用域,模块化,类的语法糖,promise以及一些新的API,很大程度上填了之前javascript的遗留下的坑,以及提升了代码质量。
不过即使过了两年,ES6也并无被市面的主流浏览器彻底支持,因此咱们还需用 babel 将ES6 编译成ES5,再将一些不支持的API polyfill 处理。
eslint 代码检查
一直一来,代码风格都是一场无休止的争论,每一个人都有本身的代码风格习惯,而这些习惯无非就是tab仍是空格,换不换行,加不加空格等等之类的杂事,与其经过制定规范去强行限制开发者的编写习惯,不如从工具层面完全解决代码风格的问题。eslint能够自动处理一些代码风格的问题,直接将代码经过指定的规则格式化,使代码总体风格统一。
更进一步,eslint 还能够禁止代码的一些可能形成不良影响的行为(例如eval,未定义变量),使其抛出错误。下降代码产生bug的可能性。
单元测试
集成单元测试,提升代码可靠性。前端较为流行的单元测试 mocha,qunit 等
UI 自动化测试
UI 自动化测试是 软件经过模拟浏览器,对页面进行UI操做,判断是否产生预想的UI效果。目前较为流行的UI自动化测试套件主要是 基于phantomjs的 nightmare
web组件化
web组件化是经过自定义标签,从UI层面对代码的拆分,提升前端代码的可复用性。尽管w3c已经初步对web组件化制定了规范, 但目前浏览器对web 组件化的支持惨不忍睹,没法经过原生的方法来实现web组件,但目前流行的前端框架,如vue,angular,react都有提供本身的web组件化,从而提升代码可复用性。
<script>
直接引入加载在没有引入模块化的概念以前,前端每每须要手动处理js文件的依赖关系,例如;bootstartp 依赖 jquery,就须要在引入bootstrap以前引入jquery
<script src="src/jquery.min.js" ></script> <script src="src/bootstrap.min.js" ></script>
若是引入js文件顺序错了则会报错。 乍一看彷佛没什么难度呀,是人都能分清是吧。那么请看下面这种状况:
有 a.js, b.js, c.js, d.js, e.js 五个文件,其中
a 依赖 b和e,
b 依赖 d和e,
c 依赖 a和d,
d 依赖 e,
e 无依赖。
那么根据以上关系,请按正确顺序引入js文件(黑人问号???)。固然,事实上也并不难区分其优先级,逐级递推就很快能够推断出引入顺序为 e,d,b,a,c
。
毫无疑问,对于稍微复杂点的web工程,存在复杂依赖状况是极有可能发生的,而且把时间耗费在管理依赖关系上也不值当。
因此就诞生了前端模块化
经历了混乱加载的黑历史,咱们终于迎来了js的模块化,忽如一晚上春风来,一晚上之间冒出一堆模块化标准。
其中具备表明性的模块加载器分别是是遵循AMD(Asynchronous Module Definition)规范的RequireJS ,还有淘宝玉伯开源的 遵循CMD(Common Module Definition)规范的 SeaJS。 二者除了遵循规范不同以外,封装模块有差异以外,都各有所长,并且对旧版本浏览器的支持都至关完美。
固然除了这两个,还有各种其余开发者开发的模块加载器,当真是一番群魔乱舞百家争鸣的盛世呀。在此就不一一细述了。
下面有请咱们的主角出厂: ES6 Module。
ES6 Module 是新一代javascript标准 ECMAScript 6 的新增特性,其语法和Python类似,比较简洁易用。另外,相比于其余模块加载器,ES6 Module 是语法级别的实现,其静态代码分析相比于其余框架会更快更高效,方便作代码检测。
// import 基本语法 import React from 'react'; //等价于 var React = require("react"); import { stat, exists, readFile } from 'fs'; // 等价于 // var fs = require('fs'); // var stat = fs.stat, exists = fs.exists, readFile = fs.readFile;
并且,且不论其API优劣,其语法与前面说的模块化有什么区别的,ES6 Module最大优势是显而易见的: 它是官方标准,而不是其余妖艳贱货第三方开发的框架/库。跟着有名分的原配混,毫无疑问是有前途更稳定的吧。
固然,缺点也是很明显的,不一样于RequireJS,SeaJS 向下兼容到极致(ie6+),ES6 Module 的兼容性还未覆盖绝大部分浏览器,支持ES6 Module的浏览器寥寥无几,虽然能够经过babel进行语法转译,不过兼容性毕竟是硬伤,惟有时间能治愈。
从描述可知,前端工程化须要作的事情,单凭人力一个一个去处理基本没有可能完成,那么,咱们就须要学会使用工具,毕竟程序猿和猿之间最大的区别就是会不会使用工具。
grunt 和 gulp 就是自动化构建工具。咱们经过安装对应的node_module,根据gulp/grunt 的API编写相对应的任务(如:css预处理,代码合并压缩,代码校验检查等任务,js代码转译),那么就能够生成咱们想要的结果,完成前端工做流管理,极大程度地提升效率。其做用其实就至关于makefile 的make 操做,将手工操做自动化,其任务编写格式以下。
// gulp scss预处理任务 gulp.task('styles', function() { return gulp.src('src/styles/main.scss') .pipe(sass({ style: 'expanded' })) .pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4')) .pipe(gulp.dest('dist/assets/css')) .pipe(rename({suffix: '.min'})) .pipe(minifycss()) .pipe(gulp.dest('dist/assets/css')) .pipe(notify({ message: 'Styles task complete' })); });
前面说了那么多SeaJS,RequireJS的模块化 ,又有gulp ,grunt的自动化处理,想必都有点以为这前端工程化的技术栈也太繁琐了吧。
那么如今,你能够通通不用管啦,让咱们推出终极解决方案:Webpack。
相比于seajs / requirejs 须要在浏览器引入 sea.js 、require.js 的模块解析器文件,浏览器才能识别其定义的模块。 webpack不须要在浏览器中加载解释器,而是直接在本地将模块化文件(不管是AMD,CMD规范仍是ES6 Module)编译成浏览器可识别的js文件。
另外,相对于gulp/grunt 的批处理工做流功能,webpack 也能够经过 loader、plugin的形式对全部文件进行处理,来实现相似的功能。
其主要工做方式是: 整个项目存在一个或多个入口js文件,经过这个入口找到项目的全部依赖文件,经过loader,plugin进行处理后,打包生成对应的文件,输出到指定的output目录中。能够说是集模块化与工做流于一身的工具。
固然,webpack也并不是银弹。工具没有好坏,只有适合与否。即使是webpack也并不是适用于全部场合。
webpack 的最大特色是一切皆为模块,一切全包,最适和应用在SPA一站式应用场景。只有简单几个页面的状况下使用 webpack 反而可能会增长没必要要的配置成本,反而直接用gulp或者其余工具处理代码压缩,css 预处理之类的工做会更加快捷易用。
另外,除了最主流的 webpack 以外,同性质的模块化打包器还有 browserIfy,以及百度的 fis ,因为对这二者了解很少,就不一一比较了。
废话少说,talk is easy , show me the code,咱们来看看webpack是怎么工做的。如下是一个配置了webpack-dev-server
的本地开发webpack配置文件。 具体可访问 github 地址 查看完整信息
// webpack.dev.config.js let path = require('path'), webpack = require('webpack'); let resolve = path.resolve; let webRootDir = resolve(__dirname, '../'); module.exports = { entry: { // 入口文件,打包经过入口,找到全部依赖的模块,打包输出 main: resolve(webRootDir, './src/main.js'), }, output: { path: resolve(webRootDir, './build'), // 输出路径 publicPath: '/build/', // 公共资源路径 filename: '[name].js' // 输出文件名字,此处输出main.js, babel-polyfill.js , 视状况能够配置[name].[chunkhash].js添加文件hash, 管理缓存 }, module: { rules: [ //模块化的loader,有对应的loader,该文件才能做为模块被webpack识别 { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/