前端工程化思考与实践
栏目: JavaScript · 发布时间: 6年前
内容简介:随着业务不断发展,产品规模不断壮大。越来越多应用建立起立,业务逻辑各有不同且日趋复杂,团队人员越发庞大,代码维护成本越来越高。 不断实践并不断思考,前端开发其实就是要推动前端工程化。本文阐述前端工程化的一些思考和实践。工程化是指
随着业务不断发展,产品规模不断壮大。越来越多应用建立起立,业务逻辑各有不同且日趋复杂,团队人员越发庞大,代码维护成本越来越高。 不断实践并不断思考,前端开发其实就是要推动前端工程化。
-
日常开发中,我们为什么需要前端工程化?
-
webpack、npm、gulp等它们有什么关系?
-
有多种构建工具,比如npm、yarn、browserify、gulp、grunt等,你知道他们之间的区别吗?
-
实际开发中却经常遇到第三方包报错,有时候我们可以通过
rm -rf node_modules && npm i暴力地将问题解决,有时候却不能。你知道是什么原因吗?
本文阐述前端工程化的一些思考和实践。
什么是前端工程化
工程化是指
将系统化的、严格约束的、可量化的方法应用于软件的开发、运行和维护,即将工程化应用于软件。———— 清华大学出版社《软件工程》
软件工程与其说是计算机中的一门科学,不如说是偏向于提高系统生产效率的一整套方法论。
前端工程化能够有效 提高 需求开始,需求开发,发布部署,测试阶段 的生产效率,提高代码质量,降低开发成本。
前端工程化是为开发者服务的。简而言之,就是将日常开发的重复步骤整化为固定流程,把前端开发工作带入到更加系统和规范体系的一系列过程
为什么要进行前端工程化
用户的需求从最开始简单的页面在向复杂的应用发展。前端需要做的事情更多,同时也要追求更友好的用户体验。
第一阶段:石器时代
适合小项目,不分前后端,页面由jsp、 php 、tornado template等在服务端生成,浏览器负责展现。
第二阶段:后端MVC
为了降低复杂度,有了Web Server层的框架升级,比如Structs、Spring MVC等。
以上两个阶段存在的问题
- 前端开发依赖开发环境
- 前后端没分离,可维护性差
第三阶段:SPA
2005年推出Ajax后,前端进入SPA阶段。 Single Page Application 单页面应用。
Ajax需要面临的问题
- 前后端接口约定,开发沟通效率低
- 大量的js来满足复杂的交互,维护成本高
前端工程化的主要目的是让开发更加规范化、流程化、自动化,实现敏捷开发。
以上三个阶段,开发者想要添加一些逻辑,最简单的做法是在HTML中插入一个script标签,然后直接在里面书写业务逻辑代码。这样做比较简单直接了当,但是也存在其他问题:
- 不同script标签之间命名冲突,容易造成全局作用域污染
- 代码重用性差
针对这些问题,可以将HTML中内联的JavaScript提取出来成单独的JS文件,和使用立即执行函数表达式IIFE包起来,只把接口暴露到全局。
(function() {
// 通过立即执行函数表达式将作用域隔离
// 逻辑代码...
})();
复制代码
但是会随着页面逻辑的复杂度增加了其他问题:
-
页面JS文件引用顺序。
HTML页面引用和处理js文件只能是顺序的(不考虑async等),因此js之间的依赖关系也是顺序的。简单的顺序依赖关系无法满足需求。一个大型工程内部的模块依赖关系通常是树状的。
-
页面引用js文件的长度与数量。
文件越来越长,越来越难维护,一个页面的单个js文件可能有几千行代码,如果按功能切割成多个小文件,就会导致页面请求过多,每个HTTP请求都需要单独建立连接,导致页面渲染速度下降
第四阶段:前端MVC/MVP/MVVC
为了降低前端开发复杂度,引入了模块化概念。AngularJS、React、Vue三分天下,还有Ember、Knockout、Polymer、Riot等大量前端框架涌现。
前端工程化的主要目的是让开发更加规范化、流程化、自动化,实现敏捷开发。
第五阶段:Node全栈
Nodejs兴起,前端语言可用于后端开发,带来一种新的开发模式。
后两个阶段的好处是
- 前后端分离,开发和维护职责分明
- 有利于重复代码模块化,减少迭代成本
- 部署相对独立,项目合理分层,更好维护
前端工程化的基本概念
工具和语言虽然差异大,但是解决的都是相似的问题,归纳为:
- 扩展 javascript 、html、css 本身的语言能力
- 解决重复工作
- 模板化、模块化
- 解决功能复用和变更问题
- 解决开发和产品环境差异问题
- 解决发布流程问题
开发同学不用担心不同模块间的框架和依附系统差异,只要知道以上命令就能够完成所有开发流程。优化倾向于底层,脱离业务,实现更高层级的复用
预编译语言、模块热加载等技术可以提升开发效率,而利用自动化测试、lint 工具等可以保证代码的功能和质量;本地环境下还要生成 source-map、配置模块热加载等等便于调试代码;而到了生产环境下则要对资源进行压缩,生成版本号等。
工程化基本思想:模块化
模块化的系统,当需求改动时,开发者可以更快速地将问题定位到相应模块中,模块逻辑清晰不耦合。因此模块化具备更强的可维护性。
早期设计的js并不具备这一特性,CommonJS 以及 AMD 的出现,为前端定义了模块的标准。也有了实现这些模块化的库,比如 RequireJS 以及 Browserify。
模块化设计的特点:
- 作用域封装
- 重用性
- 解除耦合
模块化带来的正面作用:
- 可以让开发者将自己工程中的代码按模块进行划分,模块之间也不再仅仅是简单的顺序依赖关系。
- 对于客户端来说,接受打包后的单一文件,解决文件过多导致的HTTP请求耗时长问题
- 并根据模块之间的依赖和实际需求,按需加载。
发布下载包工具:包管理器
包管理器是一个可以让开发者便捷地获取代码和发布代码的工具。JavaScript 应用中,最主流的包管理器是 npm 和 Yarn。( 发布自己的npm包教程 )
js没有强大的标准库,但是有很多小型的开源框架库满足常用的功能,比如日期处理、url处理、异步流程控制等,不需要人手工编写。一些具有特定功能的代码(框架、库等)按照特性形式被封装成包,开发者可以通过包管理器安装这些包,避免重复造轮子,也可以把自己的代码通过包的方式分享给别人使用。
npm 特性
npm 是node包管理器,通过 npm 获取项目需要的依赖,并且通过打包 工具 和业务代码打包在一起。其仓库是一个遵循 npm 特定包规范的站点,提供 API 来让用户上传和下载包、获取包信息、以及管理用户账号。
npm init 初始化npm项目,并根据终端输入的基本信息,生成配置文件package.json npm install 远程或者从目标路径获取npm包。( --save 和--save-dev参数区别 )
yarn的冲击
npm 存在 版本号锁定问题和性能问题,Yarn 这个竞争对手的出现可以说给 npm 带来了改进的动力。
Yarn 是 Facebook 公司在 2016 年 10 月 11 日开源的模块管理器,它宣称比 npm 更快、更安全、更可靠。Yarn 并不重头建立一个新的 Javascript 模块仓库,而只是替代 npm 客户端来管理原有的 node_modules 中的模块,并弥补 npm 的缺陷。 相对于npm的优点是:
-
会帮助开发者自动生成和维护版本号描述文件。 初次执行 Yarn 时会在项目中自动生成一个名为 yarn.lock 的文件,它与 npm shrinkwrap 的内容形式很相近,并且会随着模块的更新自动同步。
-
性能比当时 npm 更优。 Yarn 为了解决当时 npm 安装模块速度慢问题,在拉取包时采用并行操作,优化了请求队列,更高效地利用当前的网络资源。同时默认的 yarn.lock 也无形中减少了解析 semver 与获取模块最新版本的时间。
预编译语言是什么?
使用预编译语言的主要目的是为了实现 HTML、CSS、JavaScript本身语言所不具备的特性。比如最常见的 SASS,它是CSS的预编译语言,通过它开发者可以使用模块、定义变量、编写嵌套规则等等来提高开发效率。另外还有 Babel 预编译 JavaScript 来实现新的 ES 特性,以及使用 TypeScript 去做类型检查。
Gulp 和 Grunt 有什么作用?
通过Gulp 和 Grunt 等构建流程管理工具,使得构建变得更加简单化,通过项目中的一些配置,开发者可以使用简单的一行命令启动本地开发环境或者构建和发布整个工程。
构建流程优化是什么?可以做什么?
开发者修改代码并保存,构建工具重新打包刷新浏览器,完整构建一遍需要好几分钟,甚至更长,工程越庞大,需要的耗时越长。另外客户端资源体积过大也是问题,需要针对项目特点进行按需加载、异步加载、长效缓存等。
为什么使用webpack
- 拆分依赖树成块并按需加载
- webpack有着丰富的插件接口,满足不同的业务需求
- webpack支持
AMD和CommonJs模块样式 - 它巧妙的在你代码的
AST中进行静态分析 - 能处理简单的表达式,允许支持更多的类库
- 支持
SourceUrls和SourceMaps进行简单的调试.通过development middleware来监控文件和development server来自动刷新
如何实现前端工程化
AMD
Asynchronous Module Definition(异步模块定义)的缩写。下面的代码使用 AMD 规范定义了一个模块:
// 定义一个求和的模块
define('getSum', ['math'], function(math) {
//第一个参数是当前模块的 ID,相当于给这个模块起一个名字
//第二个参数是当前模块的依赖,比如上面我们定义的 getSum 模块需要 math 模块的依赖
//第三个参数可以是函数或者对象。
return function(a, b) {
console.log('sum: ' + math.sum(a, b));
}
});
复制代码
通过这种形式定义模块的好处在于,它** 显式 **地表达出了每个模块所依赖的其它模块。并且模块定义也不再绑定到全局对象上,不必担心其在别的地方被篡改。
CommonJS 与 Node.js 模块系统
近两年来对于开发者来说遵循 CommonJS 标准来编写和使用模块已经成为了一个基本通识。 CommonJS 是于 2009 年提出的 JavaScript 规范,它最开始是为了定义服务端标准,而非用于浏览器环境。
在 CommonJS 中每个文件是一个模块,并且拥有属于自己的作用域和上下文。模块的依赖通过 require 函数来引入。
const math = require('./math');
复制代码
如果想把模块的接口暴露给外部,则要通过 exports 将其导出,如:
exports.getSum = function(a, b) {
return a + b;
}
复制代码
缺点:
- 阻塞调用在网络中调用并不是很好,网络请求是异步的
- 多个模块无平行加载
AMD 和 CommonJS 具有同样的特性——模块的依赖必须显式引入,这样就解决了之前维护复杂模块引入时的顺序问题。但是AMD编码开销大,阅读和编写都更加困难
ES6 Module
之所以在过去我们有各种不同的模块化标准是因为 JavaScript 这门语言本身不具备模块化的特性,而现在 ES6 中已经具备了。ES6 Module 的模块语法和 CommonJS 很像,它通过 import 和 export 来进行模块的导入和导出。
import math from './math';
export function sum(a, b) {
return a + b;
}
复制代码
在 ES6 Module 中也是每个文件作为一个模块。和 CommonJS 不同的是,ES6 Module 的 模块的依赖是静态的,或者说是在编译时确定的,而不是运行时确定的。
举个例子,我们可以在 CommonJS 中的 if 语句中 require 模块,根据代码运行时 if 的判断条件决定是否要引入该模块。
// 根据运行时条件确定是否引入
if(Date.now() > new Date('2019-01-01')) {
require('./my_module');
}
复制代码
而在 ES6 Module 中则不允许这样做,import 必须在代码的顶层作用域,这意味着你不能把它放在 if 等代码块中。 ES6 Module 这样规定的原因在于可以使编译器在编译阶段就可以获取到整个依赖树,从而进行代码静态分析层面的优化,比如检测出哪些模块是从来没有被使用过的,然后从打包结果中优化掉等等 。但是现在ES6还不算完全普及,不少浏览器不兼容。
模块打包原理简述
Webpack 以及其它的一些打包工具最基本的功能就是按照我们定义好的依赖树将模块合并成单一的文件,让浏览器能够按照预想的依赖顺序去执行。这个过程我们通常将它叫做 模块打包 。
Webpack 进行打包:
# Webpack 版本需要大于等于 2,这里使用的版本是 3.5.5 webpack app.js dist/bundle.js 复制代码
app.js 是我们的打包入口文件,dist/bundle.js 是最终的打包合并结果文件。Webpack 会在打包的过程中从入口 app.js 开始查找所有依赖的模块,并最终包装和合并这些模块放在 bundle.js 中
总结
以上只是鄙人对于前端工程化的一些简单见解,想要真正熟悉工程化及其相关工具,还是需要多实践多踩坑。 编程是一种修行,应用修行的产物,也是我们与世界交流的方式。未来在哪里并不重要,重要的是以空杯心态持续学习和实践,用心写下每行代码。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Probability and Computing
Michael Mitzenmacher、Eli Upfal / Cambridge University Press / 2005-01-31 / USD 66.00
Assuming only an elementary background in discrete mathematics, this textbook is an excellent introduction to the probabilistic techniques and paradigms used in the development of probabilistic algori......一起来看看 《Probability and Computing》 这本书的介绍吧!