[TOC]
# 前端工程化
## 模塊化與組件化
模塊化中的 **模塊** 一般指 JavaScript 模塊,比如一個用來格式化時間的模塊。
從 UI 拆分下來的每個包含模板(HTML)+ 樣式(CSS)+ 邏輯(JS)的功能完備的結構單元,我們稱之為 **組件**,比如 vue 組件包含了 template、style、script,它的 script 可以由許多 js 模塊組成。

| 名稱 | 說明 | 舉例 |
| --- | --- | --- |
| 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)

* 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://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)
[http://www.hmoore.net/kancloud/midway/48192](http://www.hmoore.net/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)
- 序言 & 更新日志
- H5
- Canvas
- 序言
- Part1-直線、矩形、多邊形
- Part2-曲線圖形
- Part3-線條操作
- Part4-文本操作
- Part5-圖像操作
- Part6-變形操作
- Part7-像素操作
- Part8-漸變與陰影
- Part9-路徑與狀態
- Part10-物理動畫
- Part11-邊界檢測
- Part12-碰撞檢測
- Part13-用戶交互
- Part14-高級動畫
- CSS
- SCSS
- codePen
- 速查表
- 面試題
- 《CSS Secrets》
- SVG
- 移動端適配
- 濾鏡(filter)的使用
- JS
- 基礎概念
- 作用域、作用域鏈、閉包
- this
- 原型與繼承
- 數組、字符串、Map、Set方法整理
- 垃圾回收機制
- DOM
- BOM
- 事件循環
- 嚴格模式
- 正則表達式
- ES6部分
- 設計模式
- AJAX
- 模塊化
- 讀冴羽博客筆記
- 第一部分總結-深入JS系列
- 第二部分總結-專題系列
- 第三部分總結-ES6系列
- 網絡請求中的數據類型
- 事件
- 表單
- 函數式編程
- Tips
- JS-Coding
- Framework
- Vue
- 書寫規范
- 基礎
- vue-router & vuex
- 深入淺出 Vue
- 響應式原理及其他
- new Vue 發生了什么
- 組件化
- 編譯流程
- Vue Router
- Vuex
- 前端路由的簡單實現
- React
- 基礎
- 書寫規范
- Redux & react-router
- immutable.js
- CSS 管理
- React 16新特性-Fiber 與 Hook
- 《深入淺出React和Redux》筆記
- 前半部分
- 后半部分
- react-transition-group
- Vue 與 React 的對比
- 工程化與架構
- Hybird
- React Native
- 新手上路
- 內置組件
- 常用插件
- 問題記錄
- Echarts
- 基礎
- Electron
- 序言
- 配置 Electron 開發環境 & 基礎概念
- React + TypeScript 仿 Antd
- TypeScript 基礎
- React + ts
- 樣式設計
- 組件測試
- 圖標解決方案
- Storybook 的使用
- Input 組件
- 在線 mock server
- 打包與發布
- Algorithm
- 排序算法及常見問題
- 劍指 offer
- 動態規劃
- DataStruct
- 概述
- 樹
- 鏈表
- Network
- Performance
- Webpack
- PWA
- Browser
- Safety
- 微信小程序
- mpvue 課程實戰記錄
- 服務器
- 操作系統基礎知識
- Linux
- Nginx
- redis
- node.js
- 基礎及原生模塊
- express框架
- node.js操作數據庫
- 《深入淺出 node.js》筆記
- 前半部分
- 后半部分
- 數據庫
- SQL
- 面試題收集
- 智力題
- 面試題精選1
- 面試題精選2
- 問答篇
- 2025面試題收集
- Other
- markdown 書寫
- Git
- LaTex 常用命令
- Bugs