🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] # 前端工程化 ## 模块化与组件化 模块化中的 **模块** 一般指 JavaScript 模块,比如一个用来格式化时间的模块。 从 UI 拆分下来的每个包含模板(HTML)+ 样式(CSS)+ 逻辑(JS)的功能完备的结构单元,我们称之为 **组件**,比如 vue 组件包含了 template、style、script,它的 script 可以由许多 js 模块组成。 ![](https://img.kancloud.cn/da/0e/da0e351ff39fbbe13e1e45383a423c65_593x367.png =450x) | 名称 | 说明 | 举例 | | --- | --- | --- | | JS 模块 | 独立的算法和数据单元 | 浏览器环境监测(detect)、网络请求(ajax)、应用配置(config)、工具函数(utils) | | CSS 模块 | 独立的功能性样式单元 | 字体图标(icon-fonts)、动画样式(animate)、reset.css | | UI 组件 | 独立的可视 / 可交互功能单元 | 页头(header)、页尾(footer)、导航栏(nav)、搜索框(search) | | 页面 | UI 组件的容器、GUI 界面状态 | 首页(index)、列表页(list)、登录注册(login) | | 应用 | 整个项目或整个站点被称之为应用,由多个页面组成 | | ## 流程 前端工程化具体又分为哪些步骤呢? 1、代码规范:保证团队所有成员以同样的规范开发代码 2、分支管理:不同的开发人员开发不同的功能或组件,按照统一的流程合并到主干 3、模块管理:一方面,团队引用的模块应该是规范的;另一方面,必须保证这些模块可以正确地加入到最终编译好的包文件中(模块化和组件化开发) 4、自动化测试:为了保证代码的质量,必须有测试,而且测试应该是自动化、可以回归的 5、构建:主干更新后,自动将代码编译为最终的目标格式,并且准备好各种静态资源 6、部署:将构建好的代码部署到线上环境 ## 异常监控 如果用户使用网页,发现白屏,现在联系上了你们,我们怎么排查问题?先想一下为什么会白屏? 我们以用户访问页面的过程为顺序,大致排查一下 1. 用户没打开网络 2. DNS 域名劫持 3. http 劫持 4. cdn 或是其他资源文件访问出错 5. 服务器错误 6. 前端代码错误 7. 前端兼容性问题 8. 用户操作出错 通过以上可能发生错误的环节,我们需要获取以下的用户信息 1. 当前的网络状态 2. 运营商 3. 地理位置 4. 访问时间 5. 客户端的版本(如果是通过客户端访问) 6. 系统版本 7. 浏览器信息 8. 设备分辨率 9. 页面的来源 10. 用户的账号信息 11. 通过 performance API 收集用户各个页面访问流程所消耗的时间 12. 收集用户 js 代码报错的信息 前端所要做的异常监控就是收集 js 代码报错信息并上报服务器(做日志记录)。 <span style="font-size:20px;">直接使用工具</span> [FunDebug](https://www.fundebug.com/) 是一个不错的异常监控工具,我们只需要照着它的 [说明文档](https://docs.fundebug.com/notifier/javascript/) 操作即可。 <span style="font-size:20px;">错误异常的捕获方式</span> 当然,就算是使用工具也得大致了解下它帮我们做了哪些事情,这里整理下 JavaScript 中错误异常的一些捕获方式。 ### try-catch-finally 将能引发异常的代码块放到 try 中,并对应一个响应,然后有异常会被捕获 ```js try { // 模拟一段可能有错误的代码 throw new Error("会有错误的代码块") } catch (e) { // 捕获到 try 中代码块的错误得到一个错误对象 e,进行处理分析 report(e) } finally { console.log("finally") } ``` ### window.onerror 当 JavaScript 运行时错误(包括语法错误)发生时,window 会触发一个 ErrorEvent 接口的事件,并执行 window.onerror() 但这里有个信息要注意,语法错误会导致出现语法错误的那个脚本块执行失败,所以语法错误会导致当前代码块运行终止,从而导致整个程序运行中断,如果语法错误这个发生在我们的错误监控语句块中,那么我们就什么也监控不到了。 ```js /** * @description 运行时错误处理器 * @param {string} message 错误信息 * @param {string} source 发生错误的脚本 URL * @param {number} lineno 发生错误的行号 * @param {number} colno 发生错误的列号 * @param {object} error Error对象 */ function err(message,source,lineno,colno,error) {...} window.onerror = err ``` ### element.onerror 当一项资源(如`<img>`或`<script>`)加载失败,加载资源的元素会触发一个 Event 接口的 error 事件,并执行该元素上的 onerror() 处理函数。 ```js element.onerror = function(event) { ... } // 注意和 window.onerror 的参数不同 ``` 注意:这些 error 事件不会向上冒泡到 window,不过能被单一的 window.addEventListener 捕获。 ### window.addEventListener 详细参数配置见 [MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener) - error 事件捕获资源加载错误 资源加载失败,不会冒泡,但是会被 addEventListener 捕获,所以我们可以指定在加载失败事件的捕获阶段捕获该错误。 >注意: 接口同时也能捕获运行时错误。 ```js window.addEventListener("error", function(e) { var eventType = [].toString.call(e, e); if (eventType === "[object Event]") { // 过滤掉运行时错误 // 上报加载错误 report(e) } }, true ); ``` - unhandledrejection 事件捕获 Promise 异常 最新的规范中定义了 unhandledrejection 事件用于全局捕获 promise 对象没有 rejection 处理器时异常情况。 ```js window.addEventListener("unhandledrejection", function (event) { // ...your code here to handle the unhandled rejection... // Prevent the default handling (error in console) event.preventDefault(); }); ``` - Promise.then().catch(cb).finally() Promise 中的错误会被 Promise.prototype.catch 捕获,所以我们通过这种方式捕获错误,这包括一些不支持 unhandledrejection 事件的环境中 promisede polyfill 实现。 ```js new Promise(function(resolve, reject) { throw 'Uncaught Exception!'; }).catch(function(e) { console.log(e); // Uncaught Exception! }); ``` ### 封装 XMLHttpRequest & fetch | 覆写请求接口对象 ```js // 覆写XMLHttpRequest API if(!window.XMLHttpRequest) return; var xmlhttp = window.XMLHttpRequest; var _oldSend = xmlhttp.prototype.send; var _handleEvent = function (event) { if (event && event.currentTarget && event.currentTarget.status !== 200) { report(event) } } xmlhttp.prototype.send = function () { if (this['addEventListener']) { this['addEventListener']('error', _handleEvent); this['addEventListener']('load', _handleEvent); this['addEventListener']('abort', _handleEvent); this['addEventListener']('close', _handleEvent); } else { var _oldStateChange = this['onreadystatechange']; this['onreadystatechange'] = function (event) { if (this.readyState === 4) { _handleEvent(event); } _oldStateChange && _oldStateChange.apply(this, arguments); }; } return _oldSend.apply(this, arguments); } // 覆写fetch API if (!window.fetch) return; var _oldFetch = window.fetch; window.fetch = function() { return _oldFetch .apply(this, arguments) .then(function(res){ if (!res.ok) { // True if status is HTTP 2xx report(res) } return res; }) .catch(function(error){ report(res) }); } ``` <span style="font-size:20px;">日志上报</span> - 异步请求上报, 后端提供接口,或者直接发到日志服务器 - img 请求上报, url 参数带上错误信息 `eg:(new Image()).src = 'http://baidu.com/tesjk?r=tksjk'` ## 前端埋点 ## B/S 架构与 C/S 架构 B/S就是“Browser/Server”的缩写,即“浏览器/服务器”模式。这种“B/S”结构有很多好处,维护和升级方式更简单,客户端是浏览器,基本不需要维护,只需要维护升级服务器端就可以。 C/S就是“Client/Server”的缩写,即“客户端/服务器”模式。这种结构是将需要处理的业务合理地分配到客户端和服务器端,这样可以大大降低通信成本,但是升级维护相对困难。比如我们手机中安装的微信、qq、王者荣耀等应用程序就是C/S结构。 | | C/S| B/S| | --- | --- | --- | |硬件环境 |专用网络 |广域网| |安全要求 |面向相对固定的用户群信息安全的控制能力强| 面向不可知的用户群对安全的控制能力相对较弱| |程序架构 |更加注重流程系统运行速度可较少考虑| 对安全以及访问速度都要多重的考虑,是发展趋势| |软件重用 |差| 好| |系统维护 |升级难 |开销小,方便升级| |处理问题 |集中 |分散| |用户接口 |与操作系统关系密切 |跨平台,与浏览器相关| |信息流 |交互性低 交互密集| 表格来源:百度百科 ## toB 与 toC [https://www.jianshu.com/p/32df491a33a8](https://www.jianshu.com/p/32df491a33a8) 如果是面对企业客户,就是 toB 的,如果是面对个人用户,就是 toC 的。举个例子,阿里巴巴是 toB 的业务,淘宝是 toC 的业务。 ## 敏捷开发 敏捷开发以用户的需求进化为核心,采用迭代、循序渐进的方法进行软件开发。在敏捷开发中,软件项目在构建初期被切分成多个子项目,各个子项目的成果都经过测试,具备可视、可集成和可运行使用的特征。换言之,就是把一个大项目分为多个相互联系,但也可独立运行的小项目,并分别完成,在此过程中软件一直处于可使用状态。 # MVC、MVVM 架构来源于业务,并没有好坏之分。好的架构是在业务、成本、时间之间取得一个完美的平衡。 ## MVC(Model、View、Controller) ![](https://img.kancloud.cn/37/b3/37b319572f3845bf0f0c19cd21f444ad_1000x542.png) * Controller * 用户动作映射成模型更新 * 选择响应的视图 * View * 获取模型数据 * 模型展示、更新 * 收集用户行为,发送给控制器 * Model * 封装应用程序状态 * 响应状态查询 * 通知视图更新 MVC 只是一种设计规范,有多种实现形式(而且有的框架实现的也并不是标准的 MVC)。Vue 在其 [官方文档](https://cn.vuejs.org/v2/guide/instance.html#%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA-Vue-%E5%AE%9E%E4%BE%8B) 中也称其没有完全遵守 MVVM 模型: 在 JSP 中,控制器是 servlet,视图是 jsp 页面,模型是 bean 对象和 db。 ***** 在 Android 中,Activity 属于控制器,它接收了所有的用户输入请求;layout.xml 等各种界面布局属于视图;各种 bean、repository 等属于模型。不过在 Android 中,也可以把 Activity 也看作视图,它响应用户的输入,从模型层获取数据,进而控制视图的显示与隐藏,主要原因是 xml 没有自处理的能力,只能靠 Activity 来控制,这样就只能把 Activity 和 xml 等都归属于视图,类似在 iOS 中 ViewController 的作用。 ***** 在 node.js 的 MVC 中,.pug、.ejs 等模板引擎文件可以视为视图、控制器控制路由,根据用户的请求返回不同的页面,将一些操作数据库、访存文件的操作视为模型。 ***** 再看看 BackBone 这一轻量级 MVC 框架,其包含 Model,View,Collection,Router 等模块 ```js var M = Backbone.Model.extend({   defaults:{name:"hello"} ,   initialize : function(){ // new M 时,会执行这个初始化函数。     this.on("change",function(){ // 监听 change事件       alert(1);     })   } }) var model = new M(); model.set("name","hi"); // 改变模型的 name 值时,就会触发 change 事件,弹出 1.其实这里只要改变模型,就会触发。 ``` 这里 Model 的对象不只包含数据,也有对属性(name)的监听事件。所以 BackBone 里的 Model 也不是纯 Model,它有一部分 Controller 的功能。 我们再看看 BackBone 的 View: ```js var M = Backbone.Model.extend({   defaults:{name:"hello"}   }); var V = Backbone.View.extend({   initialize:function(){ //new V 时,会跟这个视图的 model 绑定 change 事件,回调方法是视图的 show 方法 // listenTo 方法跟 on 一样是绑定事件的,但是 listenTo 可以设置 this 的指向,它多一个参数。它的意思就是:给 this.model 绑定 change 事件     this.listenTo(this.model, "change", this.show); 。   },   show:funtion(model){     $("#tt").append(this.model.name);   } }); var m= new M(); var v = new V({model:m}); m.set("name","hi"); // 改变模型的 name 值时,就会触发 change 事件,在视图中弹出模型设置的 name 值。 ``` 这里 View 既包含了视图显示也包含了事件监听,属于传统的 View+Model,暂且不讨论 BackBone 中到底 MVC 对应的啥(我没用过),可以看到视图更新的流程是这样的: 用户操作 -> 触发绑定的事件 -> 改变模型中的状态 -> 更新视图(需要自己定义如何更新) ***** 看到这里,用过 MVVM 框架的就知道其与 MVC 的最明显的区别了(前端角度来看):它实现了 View 和 Model 的自动同步,也就是当 Model 的属性改变时,我们不用再自己手动操作 Dom 元素,来改变 View 的显示,而是改变属性后该属性对应 View 层显示会自动改变。 ***** React 和 Vue ,引用《深入浅出 React 与 Redux》中的话来说可以归结为一个公式:*UI=render(data)* 用户看到的界面(UI),应该是一个函数(在这里叫 render)的执行结果,只接受数据(data)作为参数。对于开发者来说,重要的是区分开哪些属于 data,哪些属于 render,想要更新用户界面,要做的就是更新 data,用户界面自然会作出响应。所以 React 实践的也是“响应式编程”(Reactive Programming)的思想,React 的名字由此而来。 ![](https://img.kancloud.cn/6b/44/6b44bc595c76d4141ea85ce22aac4219_771x232.png) 图片来源:[维基百科](https://zh.wikipedia.org/wiki/MVVM) # 参考资料 [https://www.jianshu.com/p/171996f5b12c](https://www.jianshu.com/p/171996f5b12c) [https://www.cnblogs.com/ihardcoder/p/5378290.html](https://www.cnblogs.com/ihardcoder/p/5378290.html) [https://www.jianshu.com/p/85d9a2778d80](https://www.jianshu.com/p/85d9a2778d80) [https://segmentfault.com/a/1190000016959011#articleHeader2](https://segmentfault.com/a/1190000016959011#articleHeader2) [https://www.kancloud.cn/kancloud/midway/48192](https://www.kancloud.cn/kancloud/midway/48192) [https://github.com/myz7656/android-mvp](https://github.com/myz7656/android-mvp) [https://blog.csdn.net/qq\_36228442/article/details/79470408](https://blog.csdn.net/qq_36228442/article/details/79470408) [前端埋点1](https://juejin.im/post/5c178aaaf265da6147702108) [前端埋点2](https://juejin.im/post/5b62d68df265da0f9d1a1cd6#heading-1) [前端埋点3](https://juejin.im/post/5d182a3bf265da1b667bf0be#heading-17)