前端工程化详解——理解与实践前端工程化

Posted 程序员啊楠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端工程化详解——理解与实践前端工程化相关的知识,希望对你有一定的参考价值。

前言:
前端工程化一直是一个老生常谈的问题,不管是面试还是我们在公司做基建都会经常提到前端工程化,那么为什么经常会说到前端工程化,并没有听过后端工程化、Java工程化或者Python工程化呢?我们理解的前端工程化是不是一直都是Webpack的性能调优,或者是一个cli工具呢?今天我们带着问题来一起寻找一下答案吧!

文章目录

一、前端工程化简介

我们整天再说前端工程化,那我我们思考一下,为什么会有前端工程化这个
概念?我们来一起回顾前端的发展历史,通过这个发展历史我们就能知道为什么会有【前端工程化】这个概念。

1. 前端发展历史

看下面流程图我们可以看出来
第一阶段:前端最早期的只是html、CSS、JS(此时只是前端发展的一个雏形,【JS】还没有模块化的概念),随着页面发展我们发现一个页面引入太多JS脚本,将大大增加维护成本;
第二阶段:已经出现了模块的概念,会按照模块的概念进行拆分,我们如何拆分模块,如何放置这些模块?此时已经有了一个工程化的概念;
第三个阶段:模块已经划分出来,但是我们部署的时候还是想合并在一起,就涉及到了早起的打包处理;
第四个阶段:前端进一步复杂之后,前端需要承载的功能更多了,逐步开始进行前后端分离,前端也可以独立的开发了。此时前端也出来了一些新的概念,比如说:SPA、Angular、Vue等。此时就要开始考虑路由如何规划?开发时如何调试?开发完如何构建?构建完如何发布?这一切的东西才构成了【前端工程化的概念】;

HTML
CSS
JS
AMD:异步模块加载
CMD:同步模块加载
Commonjs:09年Node.js
ES-Model:15年ES6的出现
Grunt
gulp
jade
前后端分离

综上所述我们可以看出来,前端工程化绝不只是webpack或babel构建这一块,前端工程化是一个整体,从前端开始写代码 --> 如何构建 --> 如何发布测试 --> 如何上线 --> 上线后的应用状态如何监控等这一套的流程我们把他叫做【前端工程化】

二、工程化整体流程

**下图所示,就是我们从零开始整个前端工程化的考虑范围**
1. 从创建项目与开发阶段--> 我们要使用脚手架,对应的Eslint规范以及我们要使用什么UI组件库
2. CI --> 持续集成: 在一个集中的环境去构建我们的项目(避免不同协作人员环境不同带来的Bug)
3. CD --> 持续部署
下面面试题 【前端项目应该如何部署上线】 会对 CI/CD做详细的介绍
创建项目 开发 构建 测试 上线 脚手架 规范 + Eslint 组件库 CI:持续构建 CD:持续部署 监控

为什么开篇说到后端没有工程化的概念?
我们比如Java语言,他天生就是一种企业级的开发语言,他已经把上述流程包括在里面,不需要自己在去做这些基础建设;而JS是一门脚本语言,在不断开发不断迭代的进展中演变出了前端工程化的这个概念。

三、相关面试题

1. 一个新项目由你来做技术选型,你会从那几个方面来考虑?

第二个大话题,【工程化整体流程 】都是我们需要考虑的

2. 前端项目应该如何部署上线

老规矩,先看图,下图是正常的前端发布流程

下面的流程大家觉得有什么问题吗?
很多公司都是这样的一个流程,我们要知道服务器是做什么的?服务器应该是运行一些动态的程序,比如我们的Java代码,NodeJs代码,他是动态去处理数据,处理我们客户端的请求的。但是我们打包构建好的JS是一个文件,我们把一个静态文件放置一台服务器是不是就会很浪费成本,所以下图的流程一般是后端的部署流程

开发代码 git push--触发webHook 集成
Jenkins:Docker
构建代码
发布
服务器
nginx

前端部署一般会加上CDN(内容分发网络)
为什么要加CDN,第一优化加载速度(网络时延导致的速度过慢),第二把不需要动态处理的文件(JS/CSS/Image/Video)放在CDN节省服务器资源。

最后两个步骤,主要是快速回滚,假如我们发布到线上的代码出现了问题,再重头集成,大概需要十分钟,而这十分钟客户一直看到的是有问题的页面!
每次 HTML 加载的时候我们会先去读取版本,然后拿到对应版本的JS/CSS,这样的话所有的CSS和JS都是有对应版本的,一旦发现问题直接通过HTML 加载上一次的版本即可。

开发代码 git push--触发webHook 集成
Jenkins:Docker
构建代码
HTML JS + CSS =>CDN Version
a. 集成

这里详细解释一下为什么要在集成的环境(也叫云构建)去进行 npm run build ?
为什么不在本地环境进行构建,要在集成的环境构建,这里核心的问题就是,没有办法保证每个人的环境(比如:npm版本、node版本)是一样的,假设不环境不一样的话,构建出来的产物就会有差异,发布上线以后出现问题很难排查。

b. 发布

前端的代码应该是运行在哪里? 运行在一台物理服务器或者云服务器上

四、大厂工程化实践及开源方案

  1. 蚂蚁金服开源的 UmiJS;
    它提前预定好很多功能,我们可以做到开箱即用,其实 UmiJS 已经是前端工程化一个很好的范例与实践(包含基础配置比如路由、mock、构建(Webpack)、部署)。

  2. 阿里开源的 飞冰
    它和 UmiJS 一样也是基于React去设计的,飞冰比 UmiJS 内置的功能要更多一点,比如:数据请求、状态管理、日志打印、菜单配置等等。

  3. 字节跳动开源的 MODERN.js
    MODERN 会比飞冰与UmiJS包含的内容更多一些,它是按照业务场景把功能做了一个更细致的分类,比如:正常网站、中后台、桌面应用、微前端等等,主要的是支持Vue

五、迷你工程化脚手架实践

时间有限稍后给大家补上,抱歉抱歉!!!

前端工程化思考与实践

4.         前端工程化开发实践

由于Nodejs npm的环境搭建往上很多,这里就不过多介绍它们了

这里我们将更多介绍FIS3RequireJS r.js

 

4.1    模块化开发:

4.1.1            开发目录结构

   技术分享图片

左图为开发目录结构,

技术分享图片 右图中新增js 目录、fis-conf.js文件,js 目录用来存放require.js的页面级入口文件

4.1.2            HTML文件的模块化处理

任何页面都可以拆分为若干组件,在开发环境下,可以迅速定位组件位置,修改组件

FIS3为我们提供了方便的资源嵌入功能http://fis.baidu.com/fis3/docs/user-dev/inline.html

简单来说,我们可以如同模板开发一样去写HTML页面,例如:

技术分享图片

 

这个首页页面本身的样式和代码极少,页面内容都是由” ?__inline”功能从其他页面引入了,

如:banner.html

技术分享图片

 

当文件编译时,FIS3会自动帮我们把这些地方替换为文件中的HTML代码:

编译后的index.html

技术分享图片

 

可以看到,FIS3帮我们将banner.html中的代码完成写入了index.html

这里我们完成了简单的页面代码模块化分离,但是这远远还不够,因为我们的页面还需要包括CSS文件,JS文件,以及一系列的图片文件等等。那么这些东西该这么完成模块化分离,然后再打包呢。对于CSS我们就需要用到LESS的,js则需要Requirejs的帮助。

 

 

4.1.3            JS文件的模块化处理了

这里我们需要使用到Requirejs,前面提到过,它是AMD模式的一个实现。由于js语言的历史原因,它在很长一段时间都算不上一个严谨的言语,不过经过多年来的不断努力,人们提出了许多增强它,规范它的方案,这些规范的目的都是为了 JavaScript 的模块化开发,特别是在浏览器端的。目前这些规范的实现都能达成浏览器端模块化开发的目的。

Requirejs 首先需要一个requirejs.config的配置,该配置的目的在于确定文件间的依赖关系,设定各个文件的别名,设定文件的加载顺序等等。

技术分享图片

 

上面是一个简单的配置,baseUrl定义了一个相对文件所在目录的相对路径,paths设置了相关文件的别名,shim规定文件间的依赖关系,比如baseUI模块依赖jquery模块,因此,baseUI需要在jquery加载完后加载。http://www.requirejs.cn/ 详细配置选项查看这里。

对于requirejs.config 定义的位置,我尝试过三种方式:

每个入口文件中都定义一次

统一到config.js中去定义,在加载时优先require这个文件,然后在回调函数里面在写页面的依赖

这样做到时解决了冗余的问题,但是require的嵌套显然不太符合作者的初衷.

统一到config.js中去定义,然后加require.jsconfig.js打包到一起,在引用入口文件前就同步调用了。

这样做相当于全局定义了require.config并且在页面中同步调用,一定是会在入口文件前,解决了之前的问题

技术分享图片 注意require.configshim只用做两个功能,一个是表明依赖顺序关系,一个是加载不符合AMD规范的js文件。不要用作模块加载。

接下来就是入口文件和模块文件的编写,入口文件我认为应该是每个一个,这样能保证不会加载多余的文件进来。当然,在特殊情况下,可以不同页面引用同一个,灵活应用。

对于模块文件,我推荐的书写方式如下,在对象中定义方法,并将对象作为接口暴露,提供给其他模块使用


技术分享图片 特别提示一点,无论是入口文件,还是模块文件,只要是需要使用的依赖就要写到依赖数组中,不要想着a依赖过b,我现在只要写依赖a那边b也自动依赖了,这样是违反requirejs的初衷的。

 

4.1.4            css文件的模块化处理

目前来说,有两种方式来对css文件做模块化处理

利用LESS@import引入模块

利用FIS3的“声明依赖”功能引入模块

 

两者区别在于@import会在LESS文件编译阶段将引用的LESS文件加入该文件中,而@require是在文件打包阶段将各个css文件合并到一起。

 

1.1.5            图片的处理

FIS3fis-spriter-csssprites插件即可帮助我们生成页面级别的雪碧图

 

4.2   自动构建和优化

4.2.1            FIS3 介绍

FIS3 是基于文件对象进行构建的。

FIS3有自己的内置语法,实现了“内容嵌入”,“定位资源”,“声明依赖”三个功能。

FIS3 编译的整个流程都是通过配置文件fis-conf.js来控制的。

FIS3 定义了一种类似 CSS 配置方式。固化了构建流程,以期让工程构建变得简单。

FIS3 提供了文件指纹的功能,通过分析文件大小,在文件名中添加MD5码,来解决文件更新时的浏览器缓存问题。

以上是我对FIS3一点理解,详细的教程请移步FIS3网站查看。

FIS3教程http://fis.baidu.com/fis3/docs/beginning/intro.html

技术分享图片 强烈建议跟着教程走一遍,对FIS3有一点了解后再继续往下看

目前使用到的最重要的几个功能:

FIS3编译功能,通过配置文件fis-conf.js我们可以轻松的告诉FIS3该如何去处理我们的前端文件,具体语法和使用方法请查看教程

在这个HTML文件中我们引用了一个CSS文件,一个LESS文件,同步引用了html5.jsrequire.js以及config.js,此外我们还通过Requirejs异步引入了一个模块名为index_mainjs模块。

那么根据上面fis-conf.js中的配置,我们的文件将会构建成什么样子呢。

首先,我们的配置命中了当前目录及其子目录下的所有.less文件,并且调用了插件fis-parser-less插件将.less文件转换为.css文件后输出。

然后我们在文件打包阶段调用了fis-postpackager-loader插件,用过allInOne这个配置,将页面中同步引用的CSS文件、JS文件分别打包。这里我们同步引用的js文件有html5.jsrequire.js以及config.js

但是仔细看,在我们HTML代码中有个<!--ignore-->的注释,这里就是为了告诉FIS3,打包时请忽略这个文件,因此FIS3打包会跳过html5.js,然后打包require.jsconfig.js。并将其输出为 libs/require_conf.js文件。(由于html5.js需要dom加载前调用,所以不打包它)

对于CSS文件,我们在html文件中引入了一个reset.css,同时我们通过前面一步编译出了一个index.css,所以这里FIS3会把这两个文件打包,然后输出到index.html_aio.css

文件中,配置中的${filepath}就代表当前文件路径

 

FIS3给我们提供了一个本地的简易Nodejs服务器,使用命令行 fis3 server start就可以启动。并且该服务一直存在后台,不关机/重启 或者 主动停止服务,该服务不会关闭,另外使用命令行fis3 server open可以打开服务器所在文件夹,给我们前端开发提供了一个非常方便的服务器环境。一般来说我们可以通过127.0.0.1:3000来访问这个本地服务器。

 

FIS3的有强大的监视自动刷新功能,当你的文件夹下任何文件发生变动时,FIS3会自动刷新页面并且,重新编译文件。这个功能需要fis3 release –wL来激活。

 

 

4.2.2            使用r.js

在使用FIS3的过程中,发现异步的JS文件的打包,FIS3的库目前支持还不足,所以我决定暂时使用r.js去替代。

当我在用FIS3打包整个项目之前,我会先用r.jsrequirejs的依赖进行打包。如果FIS3一样,我们需要编写一个配置文件来告诉他改如何打包,配置文件命名为build.js

 

以上是一个简单的build.js配置,更多配置可以参考http://www.chenliqiang.cn/node/22

技术分享图片 前面提到过require.config中最好不要通过shim依赖模块,这是因为这会造成打包后多次拉取文件,因此全部放在require的依赖数组中处理。

这里配置完后,我们通过 node r.js –o build.js 进行打包,生成的新文件夹temp-build

 

 

4.2.3            使用FIS3

使用完r.js,我们开始使用FIS3构建整个项目

 技术分享图片

 

在这个配置文件中,我们设定了两套构建规则,如同CSS语言中的媒体查询一样。我们这里使用media来配置第二套构建规则。

在命令行中输入fis3 release可以开始执行构建

如果我要执行第二套方案只需要写为fis3 release temp 即可

技术分享图片

 

上图中的-d命令代表修改输出目录到../temptestFIS3构建时还有其他参数可以选择,具体可以参考FIS3的文档。

完成构建后我们可以看到,我们页面的请求数减少了,并且文件都带上了MD5指纹

技术分享图片

           技术分享图片

 

4.2.4            使用过程中遇到的问题

由于我们使用了文件指纹,所以每个文件的文件名中间会插入一个表示文件大小的MD5码。

比如index.js会变成index_d82af77.js,根据FIS3“定位资源”的规则,构建时FIS3会自动替换掉html中的scriptlinkstylevideoaudioembed等标签的srchref属性中的值,但是使用Requirejs时在data-main中的异步引入的文件和在config.js中配置的路径就没有那么好运了。

解决方法:

u前面有提到全局配置require.config,事先就将data-main中文件路径用模块名替代。这样能解决data-main中路径无法替换的问题。

u这里可以使用FIS3提供的资源定位能力,在config.js中使用__uri()函数解决该问题

 

5.    结语

这篇文章写于去年4月,由于没有注册博客园的账号,因此现在才发布过来。一年多的时间前端工程领域的变化很大,我们团队也经历了FIS、grunt到现在webpack的变化,我也希望通过这篇基础文章 ,抛砖引玉,让大牛们更多来分享下你们的前端工程化经验。

 

 

 

 


以上是关于前端工程化详解——理解与实践前端工程化的主要内容,如果未能解决你的问题,请参考以下文章

腾讯IVWEB前端工程化工具feflow思考与实践

前端代码工程化-演进与实践

2.携程架构实践 --- 移动大前端

前端学习之路一

前端工程化实战:React 模块化开发性能优化和组件化实践

前端工程化- ReactNative整合Taro工程可行性实践方案