標籤:

AC 2017實錄(一)

第二次來Alloy Team的Conference了,依舊是乾貨滿滿。感謝老師們的精彩分享,下次再來就不是學生票了。從大二就開始追隨著大牛們的腳步,依然還是那句話:Be a warrior not a worrior。

0x001 開場

一如既往的小姐姐熱舞,主辦方致辭。大佬還小心機的打了個廣告。

AC大會:團隊成員的年度總結

TFC大會:業界邀請與分享

0x002 面向億萬級用戶的Web同構直出

其實我覺得,把分享主題改成 」如何把React項目改造成SSR 「比較容易懂(逃

需求

  • 首屏測速不理想
  • SEO優化不足

直出

非直出:Node伺服器返回Html頁面給瀏覽器,瀏覽器通過Ajax向伺服器請求數據,瀏覽器將數據渲染到頁面

直出:Node伺服器請求後台數據, 把數據渲染到頁面後直接返回給用戶

註:直出不一定比不直出快,但一般體驗要比不直出好(不會出現頁面載入出空白數據的問題)

同構

客戶端和服務端共享部分代碼,不需要完全重寫視圖邏輯

原理

ReactDOMServer.renderToString ==> virtual DOM ==> HTML

ReactDOM.render ==> virtual DOM ==> DOM

React在內存中維護了一個虛擬DOM樹。在我們平時使用時(瀏覽器端渲染),React將虛擬DOM翻譯成DOM節點插入Document。而直出則是在伺服器端,將虛擬DOM翻譯成HTML字元串,返回給用戶。因此在編譯時根據執行環境調用renderToString/render介面就行了。

難點

DOM一致性(React)

服務端 - 渲染組件

ReactDOMServer.renderToString(<Main />)

渲染後生成的div

// data-react-checksum 是用來檢驗一致性的標識符<div data-reactroot data-reactid="1" data-react-checksum="xxx">...</div>

將生成後的div 插入HTML中

<html> <head> </head> <body> <div id="root"> <div data-reactroot data-reactid="1" data-react-checksum="xxx"> ... </div> </div> </body></html>

瀏覽器接收到HTML,進行渲染,與此同時前端的React渲染執行

ReactDOM.render(<Main />, document.getElementById("root"))

將HTML生成的DOM和React生成的虛擬DOM對比,校驗checksum是否相同

  • 如果相同則綁定事件,進行接下來的操作
  • 如果不相同,則進行diff並更新DOM,然後綁定事件進行接下來的操作

Data一致性(Redux)

要解決這個問題,主要是保證Node渲染的HTML中Store里的數據和瀏覽器內執行Redux的數據一樣

在服務端可以將這樣的標籤嵌入HTML

<script> window.__INITIAL_STATE=JSON.stringfy(store.getState())</script>

在瀏覽器中獲取服務端嵌入的Store中的數據

const sotre = createStore(reducers, window.__INITIAL_STATE)ReactDOM.render( <Provider store={store}> <Main /> </Provider>)

Route一致性

使用正則匹配路由,解析到不同的組件,轉為String填充在HTML中

const vm = require(vm)// http://buluo.qq.com/a.html ==> a組件let page = //([^.,^/]+).html/.exec(url)[1]

還有一些小技巧

有些代碼不能在Node執行

  • 打標記,通過loader區別對待

_BROWSER_require(xxx.less)import xxx.gif_END_

  • 替換方法名稱

ajax ==> window.ajax// 瀏覽器使用ajax,node使用http.request

首屏的action往往不需要那麼多,可以提取幾個需要的單獨寫在一個文件里

// firstAction.jsimport {todo1, todo2} from ./actionsexport default () => async(dispatch) => { await Promise.all([ dispatch(todo1()), dispatch(todo2()) ])}

直出的邏輯

打包的邏輯

打包需要生成直出和非直出的兩份代碼

開發調試

本地調試工具

大佬本來提供了一個叫做fireDragon的工具,但是我在GIthub上搜索了一下,發現沒有什麼內容。感覺應該是涉及到一些公司內部的東西,暫時下架整改了吧。還是把使用方法貼出來。

tnpm link // 鵝廠的NPM包管理firedragon init // 初始化配置firedragon // 運行

調試流程

自動化測試

這裡大佬採用的是github中push的鉤子,在push之前會先執行一個動作。這個動作主要使用mocha、chai做自動化檢測。如果通過的話就可以成功push上去了。

可用性保障

柔性處理

在伺服器收到請求後,Nginx會首先檢測Node伺服器的負載和內存狀態,如果無法處理請求,則會轉到靜態HTML流程。

降級處理

在編譯報錯,或者服務不可達的狀態下(5xx),Nginx會自動將不可達的服務代理到靜態的HTML流程上去。

server { listen 80; server_name xxx; location ^~ /mobile/ { proxy_pass http://static_env; } location ^~ /mobile/v2/ { proxy_pass http://nodejs_env; proxy_intercept_errors on; error_page 403 404 408 500 501 502 503 504 =200 @static_page; } location @static_page { rewrite_log on; error_log logs/rewrite.log notice; rewrite /mobile/v2/(.*)$ /mobile/$1 last; }}

壓測處理

這裡大佬給自家WeTest壓測平台打了個廣告。我們主要關注以下幾個指標就可以了:

  • 響應時間
  • TPS(找到服務規模的最優值)
  • 伺服器性能(可以找到瓶頸:如內存泄露等)

灰度測試

為了防止Node伺服器爆炸,大佬選擇了控制一部分用戶走SSR,通過伺服器監控保障伺服器可用性。如果伺服器壓力較大,就放棄一部分SSR,轉向直接返回靜態HTML。

同構的弊端

  • 減輕了瀏覽器的消耗,但是加大了伺服器的消耗,伺服器比原來多4倍
  • 加大Node的IO和計算壓力

0x002 大型Web項目可用性提升

這個標題也很高大上呢,按照我的理解應該是,如何發現線上代碼的BUG(逃

糾錯流程

  • 代碼監控
  • 數據上報
  • 數據處理

代碼監控

  • try-catch

一般來說,意料之內的報錯都會用try-catch捕獲,但是它有一個致命的問題,沒法捕獲非同步錯誤

try { setTimeout(function(){ test // 非同步錯誤 }, 0)}catch(err){ console.log(error)}

  • window.onerror

/** * @param {String} errorMessage 錯誤信息 * @param {String} scriptURI 出錯的文件 * @param {Long} lineNumber 出錯代碼的行號 * @param {Long} columnNumber 出錯代碼的列號 * @param {Object} errorObj 錯誤的詳細信息,Anything */window.onerror = function(errorMessage, scriptURI, lineNumber,columnNumber,errorObj) { // TODO}

可以捕獲到錯誤的message,url等等,一般用來捕獲意料之外的錯誤

錯誤上報

  • ajax:缺點是不一定能夠上傳上去
  • img的src:缺點是長度有限制,需要手動限制長度

監控服務

  • 編寫介面,接收用戶請求
  • 將報錯信息存儲進資料庫
  • 統計分析

錯誤信息

同源策略

有些錯誤信息上傳上來只顯示script error 卻沒有具體的錯誤內容。這是瀏覽器的非同源策略導致的。解決這個問題我們可以有兩種方法

同域化:

  • 外聯同域
  • 灰度同域

CORS機制:

我們可以在script標籤中添加cross origin 屬性

<script crossorgin></script>

或者是在請求頭中添加兩行設置

Access-Control-Allow-Origin: http:a.comVary: Origin // 緩存

壓縮代碼查錯困難

這裡大佬採用了sourcemap,sourcemap是在代碼壓縮時生成的映射文件,我們可以通過sourcemap查找壓縮後代碼對應原來代碼的哪個句子。

比如,你有一個壓縮後的代碼文件jquery.min.js,你只需要在最後一行添加註釋信息,指定它soucemap的地址,然後在瀏覽器運行就可以了。

//@ sourceMappingURL=jquery.min.map

開源方案

Sentry是一個錯誤監控平台。可以幫助我們從報錯中提取發掘異常。

它分為客戶端和服務端兩部分,客戶端就嵌入在網頁中,程序出現異常就向服務端發送消息。服務端將消息記錄到資料庫中並提供一個頁面進行分析和統計。

應對流量劫持

大佬發現他的頁面被劫持了,因此採取了以下幾種方案。

上報白名單之外的資源

通過onload事件,對載入的src進行檢驗,只上報白名單之外的資源。

document.addEventListener(load, function(e) { if (...) { report(e.target) }}, true)

上報白名單之外的資源

通過onload事件,對載入的src進行檢驗,只上報白名單之外的資源。

document.addEventListener(load, function(e) { if (...) { report(e.target) }}, true)

CSP內容安全策略

使用HTML Meta標籤

<meta http-equiv="Content-Security-Policy" content="script-src self"></meta>

使用HTTP Header

Content-Security-Policy: script-src self *.qq.com *.url.cn// 直接組織Content-Security-Policy-Report-Only: script-src self *.qq.com *.url.cn// 只上報不阻止

使用HTTPs

開發時檢錯

這裡大佬編寫了一個開發報錯的插件 ,提供了及時、可視化的報錯。提示的報錯信息有:

  • 報錯位置
  • 基礎報錯(UA、客戶端類型等)

報錯位置使用sourcemap查詢,在star大佬的github的時候,看到了大佬寫的sourcemap示例。我們可以這樣根據原代碼、壓縮之後的代碼生成sourcemap:

import { generator } from js-sourcemap;// use cdn, window.js_sourcemap.generatorvar src = `function test(){console.log("js-sourcemap")};`;var dist = `function test(){ console.log(js-sourcemap)}`var sourcemap = generator(src, dist);console.log(sourcemap);

然後這樣根據sourcemap查找源代碼的行數:

import { consumer } from js-sourcemap;// use cdn, window.js_sourcemap.consumervar smConsumer = consumer(sourcemap);// get generated loc by original positionvar loc = smConsumer.getGenerated({ line: lineNum, column: columnNum})// get original loc by generated positionvar loc2 = smConsumer.getOriginal({ line: lineNum2, column: columnNum2})

結束

最後,大佬將來展示了將來的開發規劃:

  • 在uglify的plugin中自動捕獲錯誤並上報
  • 使用babel AST解析上報錯誤

0x003 如何構建後現代化的工程化開發體系

開發體系的構成

一個開發體系應該包括開發環境、生產環境以及他們之間的銜接。

Steamerjs

Steamerjs是Alloy Team提供的工程化工具。由模板引擎和命令行構成,模板引擎用來生成react、vue、angular等項目的腳手架,命令行則用來在不同場景下執行構建、編譯等命令。

我們可以通過即裝即用的插件形式安裝

npm i -g steamerjsnpm i -g steamer-plugin-kitnpm i -g steamer-react

然後我們可以通過命令生成項目

steamer kit

基於模板生成頁面

steamer kit react-mobx

更新腳手架

steamer kit -u

開發環境 - 腳手架與命令行

要考慮的五大因素

  • 快速部署與更新
  • 團隊約定
  • 快速配置
  • 頁面性能
  • 構建性能

開發環境 - 組件化

在項目內組建開發的流程是:

  • 無腳手架進行開發
  • 複製使用
  • 手寫demo和api文檔

跨項目的組件開發則需要更加規範、自動和專業:

  • 組件開發(目錄、命令、測試、打包方式)
  • 組件展示(example和demo)
  • 通過npm發布

開發環境 - 介面聯調

  • postman 定義數據結構
  • easys-mock.com 假數據測試
  • steamer-plugin-mock 記錄介面文檔

生產開發銜接 - 數據上報

  • 百度統計 / 谷歌統計 (產品數據)
  • wetest (性能數據)
  • sentry.io (報錯)

生產開發銜接 - 持續集成

開源項目: Github + Travis-CI

私有項目:Bitbucket + Pipeline / Gitlab + Pipeline

生產開發銜接 - 代碼規範

通過Pre-Commit添加Hook進行檢查

JavaScript(ESLint) + CSS(StyleLint)

發布上線

靜態資源:cos + cdn

  • webhook
  • git
  • cos script

動態資源:cvm

上半場結束,午餐茶歇。下半場請移步AC 2017實錄(二)

推薦閱讀:

前端日刊-2018.02.10
css垂直居中整理
前端日刊-2018.02.24
React填坑記(四):render !== hydrate

TAG:前端開發 |