前端技術演進(七):前端跨棧技術

這個來自之前做的培訓,刪減了一些業務相關的,參考了很多資料(參考資料列表),謝謝前輩們,麼麼噠 😘

隨着互聯網架構的不斷演進,前端技術框架從後臺輸出頁面到後臺MVC,再到前端MVC、MVP、MVVM,以及到Virtual DOM和MNV*的實現,已經發生了巨大的變化。整體上來看,前端也正在朝着模塊化、組件化和高性能Web開發模式化的方向快速發展。除了傳統桌面瀏覽器端Web上的應用,前端技術棧在服務端或移動端上的嘗試和發展也從來沒有停止過,而且形成了一系列成熟的解決方案。前端的技術棧能解決的不只是頁面上的問題,前端工程師的追求也絕不只是頁面上的技術。

跨後端技術

這幾年全棧工程師已成爲一個很熱門的關鍵詞,從最早的MEAN技術棧到後端直出,再到現在的前後端同構,前端通過與Node結合的開發模式越來越被開發者認同並在越來越多的項目中得到實踐。前端開發者都熱衷於在Node上開發有以下幾個原因:

  • Node是一個基於事件驅動和無阻塞的服務器,非常適合處理併發請求,因此構建在Node上的應用服務相比其他技術實現的服務性能表現要好。
  • Node端運行的是JavaScript,對於前端開發者來說學習成本較低,要關注的問題相對來說比前端更純粹些。
  • 作爲一名前端工程師確實需要掌握一門後臺語言來輔助自己的技術學習。
  • Node端處理數據渲染的方式能夠解決前端無法解決的問題,這在大型Web應用場景下的優勢就體現出來了,這也是目前Node後端直出或同構的實現方式被開發者廣泛使用的一個重要原因。

Node後端開發

Node.js 是一個基於 Chrome V8 引擎 的 JavaScript 運行時。

有個叫Ryan Dahl的歪果仁,他的工作是用C/C寫高性能Web服務。對於高性能,異步IO、事件驅動是基本原則,但是用C/C寫就太痛苦了。於是這位仁兄開始設想用高級語言開發Web服務。他評估了很多種高級語言,發現很多語言雖然同時提供了同步IO和異步IO,但是開發人員一旦用了同步IO,他們就再也懶得寫異步IO了,所以,最終,Ryan瞄向了JavaScript。在2009年,Ryan正式推出了基於JavaScript語言和V8引擎的開源Web服務器項目,命名爲Node.js。

Node第一次把JavaScript帶入到後端服務器開發,加上世界上已經有無數的JavaScript開發人員,所以Node一下子就火了起來。Node最大的優勢是藉助JavaScript天生的事件驅動機制加V8高性能引擎,使編寫高性能Web服務輕而易舉。

阻塞和非阻塞

阻塞 是說 Node.js 中其它的 JavaScript 命令必須等到一個非 JavaScript 操作完成之後纔可以執行。這是因爲當 阻塞 發生時,事件機制無法繼續運行JavaScript。

在 Node.js 中,JavaScript由於 CPU 密集操作而表現不佳。而不是等待非 JavaScript操作 (例如I/O)。這被稱爲  阻塞

阻塞 方法執行起來是 同步地 ,但是  非阻塞 方法執行起來是 異步地 。 使用文件系統模塊讀取一個文件,同步方法看上去如下:

const fs = require('fs');
const data = fs.readFileSync('/file.md'); // 這裏會阻塞

與之功能等同的  異步 版本:

const fs = require('fs');
fs.readFile('/file.md', (err, data) => {
  if (err) throw err;
});

在第二個例子中,  fs.readFile() 因爲是 非阻塞 的,所以 JavaScript 會繼續執行,不會發生阻塞, 這對於高效吞吐來說是絕佳的設計。

在 Node.js 中 JavaScript 的執行是單線程的,所以並行與事件輪詢能力(即在完成其它任務之後處理 JavaScript 回調函數的能力)有關。任何一個企圖以並行的方式運行的代碼必須讓事件輪詢機制以非 JavaScript 操作來運行,像 I/O 操作。

比如 每個對服務器的請求消耗 50 毫秒完成,其中的 45 毫秒又是可以通過異步操作而完成的數據庫操作。選擇  非阻塞 操作可以釋放那 45 毫秒用以處理其它的請求操作。這是在選擇 阻塞 和 非阻塞 方法上的重大區別。

Node.js 中的事件輪詢機制和其它語言相比而言有區別,其它語言一般需要創建線程來處理並行任務。

MEAN

Node出現的早期還不像現在一樣擁有很複雜的概念,相關技術和語言的標準還不成熟,Node開發一般用的比較多的方案就是使用Express作爲Web框架進行小型的Web站點建設,與之結合的主流技術則以M(Mysql)、E(Express)、 A(Angular)、 N(Node)最爲典型,甚至到了今天MEAN技術組合的方式仍在沿用。

image.png | center | 712x530

前端一般使用Angular來管理實現頁面應用,服務端Web框架以Express爲主,同時使用免費開源的MongoDB數據庫,這樣就可以很快地構建一個Web應用了。

今天可能不一定再去選擇使用它,因爲可以代替實現的成熟方案已經很多了,各類其他前後端框架都可以用來靈活組合作爲MEAN的替代選型方案,比如 Vue、React可以替代 Angular,Koa 可以替代 Express,數據庫的選擇也有很多。

Node後端數據渲染

對於前端開發者來說,在大型Web應用開發中,很多時候並不需要完全重新設計整個應用後臺的架構,更多的情況下需要結合Node的能力幫助我們解決前後端分離開發模式下無法解決的問題。我們先來看下通常前後端分離的開發模式下有哪些問題,利用Node 端的服務又是如何幫助我們解決這些問題的:

SPA場景下SEO的問題

通常情況下,SPA應用或前後端分離的開發模式下頁面加載的基本流程是:

  1. 瀏覽器端先加載一個空頁面和JavaScript 腳本。
  2. 然後異步請求接口獲取數據。
  3. 渲染頁面數據內容後展示給用戶。

那麼問題來了,搜索引擎抓取頁面解析該頁面HTML中關鍵字、描述或其他內容時,JavaScript尚未調用執行,搜索引擎獲取到的僅僅是一個空頁面,所以無法獲取頁面上<body>中的具體內容,這就比較影響搜索引擎收錄頁面的內容排行了。儘管我們會在空頁面的<meta>裏面添加keyword和description的內容,但這肯定是不夠的,因爲頁面關鍵性的正文內容描述並沒有被搜索引擎獲取到。

如果使用Node後端數據渲染(有人稱之爲直出或服務端渲染 SSR),在頁面請求時將內容渲染到頁面上輸出,那麼搜索引擎獲取到的HTML就已經包含頁面完整的內容,頁面也就更容易被檢索到了。

前端頁面渲染展示緩慢的問題

除了SEO問題,在前後端分離的開發模式下頁面在JavaScript執行渲染之前是空白的(或提示用戶加載中)。用戶在看到數據時已經花費的網絡等待時間包括:

DOM下載時間 + DOM解析時間 + JavaScript 文件請求時間 + JavaScript部分執行時間 + 接口請求時間 + DOM渲染時間。

這時用戶看到頁面數據時已經是三次串行網絡資源請求之後的事情了。如果使用後端直出來進行數據渲染,首先SEO的問題不復存在,用戶瀏覽器加載完DOM的內容解析後即可立即展示,網絡加載的問題也得到解決。其他的邏輯操作(如事件綁定和滾動加載的內容)則可按需、按異步加載,從而大幅度減少展示頁面內容花費的時間。

一般後臺頁面數據直出的通用架構設計如下:

image.png | center | 600x446

直出層接受前端的路由請求,並在Node端的Controller層異步請求服務接入層接口,獲得Model數據並進行組裝拼接,然後提取相對應的Node端View模板渲染出HTML輸出給用戶瀏覽器,而不用通過前端JavaScript請求動態數據後渲染。

不僅如此,直出層根據不同的瀏覽器userAgent,也可以提取不同的模板渲染頁面返回給不同的用戶瀏覽器,所以這種實現方式不僅非常適合大型應用服務的實現場景,而且可以方便地實現網站的響應式內容直出。

前後端同構

在前後端分離的開發模式上加入直出層,解決了SEO和數據加載顯示緩慢的問題。可是有兩個新的問題:

  • 前端的開發實現向直出層偏移,不得不在原來的開發模式上做出修改來 適應直出層內容的開發,例如修改後端模板來適應現有的開發模式,結果我們不得不維護兩套不同的前後臺模板或技術實現——前端渲染實現邏輯和後端直出實現邏輯,儘管可能都是用JavaScript寫的。
  • 如果是在移動端Hybrid應用上,離線包機制實現可能就會出現問題。因爲每次都是從後端直出HTML結構給前端,這樣就難做到將HTML文件進行離線緩存,而只能進行其他靜態文件的緩存。在Hybrid App的應用場景下,其實我們更希望做到的是移動端首次打開頁面時使用後端直出內容來解決加載慢和SEO問題,而在有離線緩存的情況下則使用客戶端本地緩存的靜態文件拉取數據返回渲染的方式來實現,或者未來在高版本的瀏覽器支持HTTP2的條件下使用前端渲染,低端瀏覽器不支持HTTP2的情況下則使用直出的方式實現。

所以需要一套完善的開發方式,和原有開發方式保持一致,且能夠同時用於前後端分離的開發模式和後端數據渲染模板開發方式中。這種開發模式就是我們所說的前後端同構。

實現同構的核心

前後端同構的宗旨是,只開發一套項目代碼,既可以用來實現前端的JavaScript 加載渲染,也可以用於後臺的直出渲染。

爲什麼可以這樣做呢?和前端渲染數據內容的方式相同,頁面直出層內容也是通過數據加上模板編譯的方式生成的,前端渲染和後臺直出的模式生成DOM結構的區別只在於 數據和模板的渲染髮生在什麼時候。如果使用一套能在前端和後端都編譯數據的模板系統,就可以做到使用同一套開發代碼在前後端分別進行數據渲染解析。因此前後端同構的核心問題是實現前後臺數據渲染的統一性。

同構的優勢

除了解決前後端開發方式的問題,前後端同構的網站具有一些明顯的優勢:

  • 可以根據用戶的需求方便地選擇使用前端渲染數據還是後臺直出頁面數據;
  • 開發者只需維護一套前端代碼,而且可以沿用前端原有的項目組件化管理、打包構建方式,根據不同的構建指令生成類似的前後端數據模板或組件在前後端執行解析,所以這對於DOM結構層上的開發方式應該是一致的。

前後端同構的實現原理

基於數據模板的前後端同構方案

早在前端MVC開發的時代,前端模板的使用就非常廣泛,例如Mustache、Handlebar 等,基本原理是將模板描述語法與數據進行拼接生成HTML代碼字符串插入到頁面特定的元素中來完成數據的渲染。同理,後端直出層也可以通過該方法來實現數據的渲染產生HTML字符串輸出到頁面上。

如果前後端使用同一個模板解析引擎,那麼我們只需要編寫同一段模板描述語法結構就可以在前端和後端分開進行渲染了。比如同樣的模板:

<div class="entry">
  <h1>{{title}}</h1>
  <div class="body">
    {{body}}
  </div>
</div>

前端,後端拿到數據後解析保持一致:

{
  "title": "Hello",
  "body": "World"
}
<div class="entry">
  <h1>Hello</h1>
  <div class="body">
    World
  </div>
</div>

對於前端開發的同一段模板語法結構,我們既可以選擇在瀏覽器端渲染生成HTML字符串輸出,也可以選擇在後端渲染生成HTML字符串輸出。如果選擇在前端渲染,則可以將模板進行打包編譯,在數據請求成功後進行DOM渲染;如果選擇後端渲染,就可以將模板數據直接發送到直出層的View視圖進行渲染,實現同一個模板語法結構在前後端渲染出相同的內容。這裏的前提是要保證前後端使用的模板渲染引擎或者模板解析的語法是一致的。

基於MVVM的前後端同構

MVVM框架頁面上的JavaScript邏輯主要是通過Directive(不只是Directive,還有filter、 表達式等,以Directive爲主)來實現的,一般前端頁面加載完成後會開始掃描DOM結構中的Directive指令並進行DOM操作渲染或事件綁定,所以數據的顯示仍然需要頁面執行Directive後才能完成。那麼如果將Directive的操作在直出層實現,瀏覽器直接輸出的頁面就是渲染後的內容數據了。

<div class="entry">
  <h1 x-html="title"></h1>
  <div class="body" x-html="body"></div>
</div>

前端編寫的同一段MVVM的語法結構,通過前端MVVM框架解析或後端Directive 運行解析最終都可以生成相同的HTML結構,不同的是前端執行解析後生成的是ViewModel對象並通過瀏覽器體現,後端渲染則生成HTML標籤的文本字符串輸出給瀏覽器。這裏同樣需要做一件事,即在後臺實現一個與前端解析Directive相同的模塊,甚至還包括filter、語法表達式等的實現。這樣就可以在前後端完成同一段語法結構的解析了。

基於VirtualDOM的前後端同構

之前說過,VirtualDOM作爲一種新的編程概念被廣泛應用在實際項目開發中,其核心是使用JavaScript 對象來描述DOM結構。那麼既然Virtual DOM是一個JavaScript對象,就表示其可以同時存在於前後端,通過不同的處理方式來實現同構。

在前端開發的組件中聲明某段VirtualDOM描述語法,然後通過VirtualDOM框架解析生成VirtualDOM,這裏的VirtualDOM既可以用於在瀏覽器端生成前端的DOM結構,也可以在直出層直接轉換成HTML標記的文本字符串輸出,後面這種情況就可以在服務端上實現Virtual DOM到HTML文本字符串的轉換。這樣,通過對Virtual DOM的不同操作處理,就可以統一前後端渲染機制,實現組件的前後端對同一段描述語法進行渲染。

這裏VirtualDOM上的邏輯實現仍然需要在瀏覽器端進行事件綁定來完成,最好能讓同構框架幫助我們自動完成,根據HTML的結構進行特定的事件綁定處理,保證最後展示給用戶的頁面是完整且帶有交互邏輯的

無論哪一種方式,核心都體現在HTML的結構形式變化上,頁面內容的描述方式有很多,而且可以通過特定的處理過程實現轉化,這樣就提供了更多的可能性。

image.png | center | 390x370

Egg.js

image.png | center | 89x28

Node雖然生態比較火熱,但是至今還沒有一款公認的成熟的企業級框架,主要是因爲使用Node來開發大型後端應用的企業還很少。現在主要有兩款:Sails 和 Egg.js。

設計原則

一個插件只做一件事:Egg 沒有內置很多額外的功能,而是通過插件的方式來實現,Egg 通過框架聚合這些插件,並根據自己的業務場景定製配置,這樣應用的開發成本就變得很低。

約定優於配置:按照一套統一的約定進行應用開發,團隊內部採用這種方式可以減少開發人員的學習成本,這也是很多框架的思路。

特點

  • 提供基於 Egg 定製上層框架的能力:可以基於 Egg 去封裝適合團隊的上層框架。
  • 高度可擴展的插件機制:可以促進業務邏輯的複用,生態圈的形成。
  • 內置多進程管理。
  • 基於 Koa 開發,性能優異:支持所有的Koa中間件。
  • 框架穩定,測試覆蓋率高。
  • 漸進式開發:可以流暢的實現編碼 --> 編碼抽象成功能 --> 功能抽象成插件 --> 插件封裝到框架 的漸進過程。

https://eggjs.org/zh-cn

現在我們部門的Node項目基本上是 Koa,Egg流。其他部門也有 Express 流。

跨終端技術

移動端

移動互聯網興起後,智能移動設備出現,大量應用市場的Native應用也開始涌現。隨着第一波移動端互聯網開發浪潮漸漸平靜,各類Native應用開始進入有序更新迭代的階段。人們對移動互聯網需求急劇增長,Native 應用快速迭代開發的需求也越來越多,但是現有Native應用的開發迭代速度依然無法滿足市場快速變化的需要。隨之而來的是HTML5的出現,它允許開發者在移動設備上快速開發網頁端應用,並讓移動互聯網應用開發很快進入了Native應用、Web應用、Hybrid 應用並存的時代。

image.png | center | 600x305

在發展過程中,最大限度的利用原生能力成爲了一大趨勢。出現了 React Native、Weex 等框架,可以直接使用 Javascript 來編寫原生應用。比如React Native,產出的並不是“網頁應用”, 或者說“HTML5應用”,又或者“混合應用”。 最終產品是一個真正的移動應用,從使用感受上和用Objective-C或Java編寫的應用相比幾乎是無法區分的。

import React, { Component } from 'react';
import { Text, View } from 'react-native';

class WhyReactNativeIsSoGreat extends Component {
  render() {
    return (
      <View>
        <Text>
          如果你喜歡在Web上使用React,那你也肯定會喜歡React Native.
        </Text>
        <Text>
          基本上就是用原生組件比如'View'和'Text'
          來代替web組件'div'和'span'。
        </Text>
      </View>
    );
  }
}

image.png | center | 618x544

也就是說即使不懂原生應用的開發,也可以用 Javascript 來編寫原生應用了。

桌面端

現在,也可以使用 JavaScript, HTML 和 CSS 構建跨平臺的桌面應用。通過 Electron 之類的應用,可以直接把 Web 項目打包成桌面應用,運行在各個操作系統中。

著名的 Atom IDE 就是通過 Electron 構建的,其他的包括 VS Code、Skype、Github Desktop 之類的 App 也都是通過 Electron 構建的。

image.png | center | 718x902

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章