【前端小白向】前端常見名詞大盤點

最近寫了太多技術文章,今天想寫一點簡單的東西。當一個新人問:如何開始學前端?很多知乎人都會發這樣的腦圖:

新人表示很淦,並點上右上角的“關閉”。

個人也有很討厭找學習資源的時候,老手總是給一些“大而全”但對新人極度不友好的答案,我知道發圖的人可能真的想爲了新人好,但是這種圖除了增加焦慮,沒有大多作用。

但是前端發明了那麼多的“名詞”,不去了解又會一頭霧水。這篇文章就給大家盤一盤前端開發那些“名詞”的由來。

萬維網

讓我們把時間倒回 1989 年。一個英國佬 Tim Berners-Lee

發現他們實驗室 CERN(European Organization for Nuclear Research) 的資料越來越難管理了,資料很容易“丟失”,比如文件名忘了、或者維護這個文件的人走了,那麼這些文件可能就永遠“消失”在茫茫資料中了。

這個 CERN 實驗室其實是研究物理的,並不是搞計算機的!但是當時各個地方的實驗室都和這個實驗室有合作,就難免要相互分享資料。當資料變得越發龐大的時候就很難管理了。另一個點是 CERN 的全名並不是英文名,而是法文:Conseil européen pour la recherche nucléaire

爲此,Lee 提出 “Linked information systems” 的構想,並稱爲 World Wide Web,也即我們熟知的“萬維網”。

兩年後,在 1990 年,Lee 又發了第二個提案。最後提出需要2個人在6個月內造出的萬維網的想法。最後造出了第一個網站:http://info.cern.ch/

從上面可以看到,一個簡單的 Client-Server 架構已經出現了。把文件放到服務器上,在客戶端上訪問它。

構建頁面骨架

對於學術論文,一般都是有專業格式的,如果只是純文字展示,換誰也受不了呀。

所以,Lee 受 SGML(Standard Generalized Markup Language) 的啓發創建了 HTML(Hyper Text Markup Language)。一般常用的標籤有:h1~h6, div, header, body, main, footer, table, ul, li, button 等。

<header>頭</header>
<main>身</main>
<footer>腳</footer>

如果你簡單嘗試寫一個 .html 出來,會發現 <article><div> 的效果是一樣的。但是,爲了代碼更語義化,也即可以讓後面的人能看得懂,一般在頭部會使用 <header>,如果是文章則用 <article> 作代碼塊的區分。如果非要扛:我就喜歡用 div 行不行,那不用扛了,當然行!

美化頁面

雖然標籤可以做一些簡單的樣式,但是依然滿足不了設計師的樣式要求。

爲了解決網頁的樣式問題,Lee 的同事 Håkon Wium Lie 在 1994 年,起草並提出了 CSS(Cascading Styling Sheet)。一段簡單的 CSS 就可以讓頁面豐富起來了:

body {
  color: red;
}

很多人可能都知道 CSS 這個玩意,用得理所當然,但是你有沒有想過,其實 XML 也可以用來表示樣式的,比如在 Android 上就是這麼做的。在那個時候, DSSSLFOSI 也曾是瀏覽器樣式的候選人,但是用這兩玩意來寫樣式太麻煩了,所以最後才選擇 CSS 作爲瀏覽器的樣式書寫標準。

現在我們使用 CSS 已經是非常好用了,但在以前 CSS 的標準化之路是充滿着坎坷的:

  • 當 CSS1.0 發佈後,幾乎沒多少瀏覽器可以支持它。等支持 CSS1.0 的時候 CSS2.0 都已經被放出來了
  • CSS2.0 增加了更多的樣式選擇。CSS2.0 是在1997年提出來的,但是在升級爲 3.0 的時候經歷了大幅的打回、重改,重新提名。直到 2011年 CSS2.1 才作爲標準發佈出來
  • 到了 CSS3.0,它不像 1.0 和 2.0 那樣整個版本升級,而是將樣式的升級模塊化。現在,雖然我們用的還是 “CSS3.0”,但是其實某些模塊已經可以算是 4.0

CSS UI 庫

寫了很多次 CSS 後,前端工程師們發現,一些好看的 CSS 樣式可以拿出來共享呀,比如我寫好了一個按鈕的樣式:

.btn {
  ...
}

然後,其它人只要複製這個 CSS 到他的項目裏,然後在 HTML 用上 CSS 類名,就可以直接用上我寫的效果啦。

<button class="btn">我的按鈕</button>

在這段時間裏,各種 UI 小組件的 CSS 樣式滿天飛,比如今天出個按鈕的,明天就出了個輸入框的。市面上還曾出現過很多類似《XX個好看的UI組件》和《XXXX年最好看的Y個UI組件》的文章。

不久後,開發者就發現另一 個問題:組件樣式之間的衝突,比如,按鈕的樣式影響了自定義按鈕的樣式。另一個大問題是,單看一個組件的樣式挺好看的,但是如果一個網頁用了4 5 個別人寫好的 CSS 樣式,就會顯得非常不協調,沒有統一感。兼容性也很差。

Twitter 的 Mark Otto 和 Jacob Thornton 也想過這個問題,所以他們開發了 Bootstrap UI 庫:

<div class="input-group mb-3">
  <span class="input-group-text" id="basic-addon1">@</span>
  <input type="text" class="form-control" placeholder="Username" aria-label="Username" aria-describedby="basic-addon1">
</div>

把常用的按鈕、字體、輸入框的一些樣式都寫好了,還提供示例代碼。這個 UI 庫發佈了之後,幾乎所有開發者用過了,畢竟 CSS 終於可以不用自己寫了。

當時我的也用 Bootstrap 寫過課堂作業,那真是爽啊,一行 CSS 都沒寫過。

響應式佈局

2011年,蘋果發佈了那款經典的 iPhone4S,標誌着智能手機真正成爲人們不可或缺的一部分,而手機上訪問的需求也同時增加了不少。

當年在手機上看網頁,用戶都要先進入頁面,手動放大,點擊對應鏈接再進入下一個網頁,操作非常複雜,而且界面很醜陋。

另一個方面,隨着手機進入人們的生活,手機上的 App 也像雨後春筍一樣瘋狂冒出。這時,程序員就想:就把網頁做成 App 的樣子不就好看了嘛。

那怎麼判斷用戶用的是手機還是電腦呢?很簡單:通過屏幕寬度來判斷嘛,而 CSS 的 media query(媒體查詢) 正好可以用來解決這個問題:

/*屏幕寬度在 600px 以內時,背景色顯示紅色*/
@media only screen and (max-width: 600px) {
  body {
    background-color: red;
  }
}

/*大於 600它時,背景色顯示白色*/
@media only screen and (min-width: 600px) {
  body {
    background-color: white;
  }
}

問題又來了:難道每個兼容樣式都要寫兩遍?能不能只寫一種樣式就能兼容手機和電腦端呢?程序員們開始通過百分比,em,rem,優化佈局等方式,使得屏幕變小後樣式還是不會亂。

這種寫 CSS 的思路就叫做 響應式 佈局,兼容了手機和網頁。

不過 響應式 也不是萬能的。比如今天淘寶頁面裏的一些花裏胡哨的樣式就沒法兼容手機端,所以現在的做法是做兩套網頁,電腦端做一套,手機端做一套。區別是:手機端的樣式做得像 App 一些,而且功能不會太多,樣式也不會很複雜,而且提供“從XX App”打開的按鈕,向自家的 App 引流。電腦端更酷炫,功能更強大。畢竟現在應該沒人在手機網頁上購物吧?

如果你仔細看手機端的網頁地址,都會以 m.xxxxx 開頭,就表示網頁只在手機上看的。

JavaScript 將內容動起來

注意:上面的 HTML、CSS 都不屬於編程語言,HTML 是標記語言,而 CSS 是樣式表。

現在我們有 HTML 和 CSS 已經可以讓頁面變好看了,但是頁面內容都是定死的。爲了能讓頁面“動”起來,瀏覽器必須要引入一種編程語言。那就是 JavaScript

當年,本來有人想把 Java 用作瀏覽器的編程語言的,但是搞 Java 的 Sun 公司說:沒空理你。所以,網景公司 NetSpace 只好自研編程語言。本來新語言想叫作 LiveScript,但是覺得這個名字又平平無其,不能一炮而紅。當時 Java 正如日中天,所以,爲了做個標題黨、蹭個熱度,在 12 月發佈的時候改名爲 JavaScript,並引入了一些 Java 的特性,然而,令人沒想到的是,這些特性將會成爲前端工程師的噩夢。

JavaScript 除了做一些簡單的業務邏輯,比如判斷是男是女:

if (you === 'male') {
  console.log('男男')
}

還會操作 HTML,但是 HTML 不是一段文本麼?怎麼操作呢?實際上當瀏覽器拿到 .html 文件後,會自動解析 HTML 文本,將其轉爲爲 DOM(Document Object Modal),將普通的文本轉換爲一棵樹的結構。

JS 只需操作 DOM 就可以修改 HTML 的佈局和結構了。

document.getDocumentById('hello').textContent = '我是帥哥' // 內容變成“我是帥哥”

JavaScript 是一個設計極其糟糕的語言,比如 getMonth() 時,如果現在是1 月,返回的則是 0,而 getHours() 又會準確返回當前的小時數。對於數組的操作又少得可憐,比如沒有 unique, findBy 這些 API。

爲了給 JavaScript 的設計擦屁股,一些工具庫營運而生:

  • jQuery: 提供很多操作 DOM 的 API
  • moment, dayjs:提供很多操作 Date 對象的 API
  • lodash:更多像是個工具庫

服務端渲染

雖然 JS 能動態修改內容了,但是,在以前發異步請求是一件很麻煩的事情。

Java 程序員想到了一個辦法:JSP。反正要訪問服務端,不如在你訪問的時候,我直接從數據庫裏把數據讀出來,生成一個 HTML 給你不就好了嘛。這種技術就叫做 JSP。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鳥教程(runoob.com)</title>
</head>
<body>
<h2>HTTP 頭部請求實例</h2>
<table width="100%" border="1" align="center">
<tr bgcolor="#949494">
<th>Header Name</th><th>Header Value(s)</th>
</tr>
<%
   Enumeration headerNames = request.getHeaderNames();
   while(headerNames.hasMoreElements()) {
      String paramName = (String)headerNames.nextElement();
      out.print("<tr><td>" + paramName + "</td>\n");
      String paramValue = request.getHeader(paramName);
      out.println("<td> " + paramValue + "</td></tr>\n");
   }
%>
</table>
</body>
</html>

被程序員一直喊爲 “世界上最好的編程語言” PHP也沿用了這個思路。這類通過服務端動態生成 HTML 的方法就叫 SSR(Server Side Rendering) 服務端渲染

但是這上面有兩個問題:

  1. 如果 Java 裏有報錯的時候,頁面會顯示整個錯誤 Stack 給用戶,體驗非常不友好,而且一崩全崩
  1. 高度耦合的代碼非常不利於維護,比如,第一眼看上面的代碼能看出個啥子哦
  2. 一個工程師除了要處理服務端的邏輯、也要考慮樣式要怎麼寫、頁面邏輯,職責不明確

雖然上面的 JSP 和 PHP 流行過一段時間,但是程序員們爲了分工更明確,都選擇了前端程序員管頁面開發、而後端程序員管服務端的開發。這種開發模式也被稱作 前後端分離

而前後端的重要溝通橋樑就是異步請求,也即大家現在經常說的 Ajax請求,但是在很久以前,異步請求還是一個實現上特別困難的事情。在那個時候發完請求,頁面就不得不重新刷新一遍,用戶體驗非常差。

JSONP

上面說到的問題在於:瀏覽器很難在不刷新頁面的情況下,向服務器發異步請求來獲取內容。

聰明的程序員就開始想:什麼東西能發異步請求呢?然後他們發現如果直接創建一個 img 標籤並寫上 src 就會向服務器發一個 xxx.jpg 的異步請求了:

<img src="xxx.jpg">

那麼如果要發個異步的 Get 請求,可以這樣搞呀:

  1. 偷偷摸摸地創建一個看不見的 img 標籤
  2. 把要訪問的 url 放到 src 裏
function getData(url) {
  var imgEl = document.createElement('img') // 創建 img 標籤

  imgEl.visibility = 'none' // 把 img 標籤變成不可見

  imgEl.src = url

  document.appendChild(img) // 加在網頁上,自動發送 Get 請求
}

但是上面這麼又引出下面的問題:

  1. 不知道什麼時候要清理新生成的 img
  2. 請求發了就發了,響應後不知道怎麼獲取數據
  3. 每次都要寫 imgEl.visibility = 'none' // 把 img 標籤變成不可見 這句話

爲此,程序員再次想了很多辦法。

首先,不再使用 img 標籤,而使用 script 標籤,就可以把第 3 步省略了。

第二步,在全局定義一個函數用於獲取 users 信息:

function getUsers(users) {
  console.log(users)
}

在寫 url 的時候加一個參數上去:https://www.baidu.com/users?callback=getUsers。服務端從參數裏讀取到 getUsers,向瀏覽器返回 JS 腳本:

getUsers(['Jack'. 'Mary'])

由於剛剛添加的標籤是 script 標籤,所以等服務器返回後,getUsers(['Jack'. 'Mary']) 就會被馬上執行,最終就會將 users 打印出來。這種技術就叫做 JSONP,全稱爲 JSON with padding。

JSONP 另一個好處是可以實現跨域請求,因爲 script 標籤可以請求非同源策略的資源並獲取返回的數據。但是這非常不安全,服務器很容易被一些惡意的 JS 代碼給攻擊了。

Ajax

從上面看出來 JSONP 能用但是很不規範,程序員們非常想要一套完善的異步請求機制。

2004 年,Google 在開發 Gmail 和 Map 兩個應用的時候,完善了異步請求的機制,並制定了一些標準。

2006年時, W3C 起草了第一份 XMLHttpRequest 的草案,然後不斷完善,一直到最後一份草案則在 2016年 被提出,XMLHttpRequest 才成爲正式的標準。我們經常所說的 Ajax 請求其實就是使用這個對象來發請求的。下面就是用這個對象發送請求的代碼:

// 生成請求對象
xmlhttp = new XMLHttpRequest();
// 監聽請求的狀態和返回
xmlhttp.onreadystatechange = function () {
  if (xmlhttp.readyState === 4) {
    if (xmlhttp.status === 200) { // 200 = OK
      console.log('成功');
    } else {
      console.log('失敗');
    }
  }
};
// 打開通道
xmlhttp.open('GET', url, true)
// 發送請求
xmlhttp.send(null);

Ajax 全名是 Asynchronous JavaScript and XML,它不是單單一門技術,而是多門技術的全集,所表求的是:在客戶端創建異步應用的一系列技術。只不過核心其中一步就是發異步請求,而 XMLHttpRequest 正好可以幫助我們完成這項工作。

Node.js

2009 年,前端另一大颶風席捲了全球。Ryan Dahl 編寫了第一個最初版本的 Node.js,使得 JavaScript 除了可以在瀏覽器裏運行,也可以在擁有 Node.js 平臺的地方運行,比如自己電腦的終端裏。

JavaScript 終於不再是客戶端語言,也可以做服務端的開發了。爲了更方便做服務端的開發,TJ Holowaychuk 一個國外超級大佬,借鑑了 Ruby 社區的 Rack,開發了 Express.js,一個簡易的 JS 服務器框架。

由於 Express.js 提供的功能太簡單了,所以,很多開發者不斷給這個框架開發各種各樣的中間件,使用者可以用這些中間件增強自己服務器的功能,比如 body-parser, cookie-parser, passport 等。

後來,TJ 覺得 Express.js 寫得還是不夠精簡,本來想重構的,但是重構成本太大了,乾脆再造一個輪子吧,這個輪子也就是我們熟悉的 Koa.js。

爲了讓 JS 更好地完成服務端開發的工作,前端開發人員把後端開發的一些工具都造了一遍:

  • 連接數據庫:mysql, mysql2, mongodb
  • 緩存:session, redis
  • ORM: TypeORM, sequelize
  • 定時任務:node-schedule, cron
  • ...

Serverless

當很多人都開始用 Node.js 的時候,大家又發現一些問題:

  1. 寫完代碼,本地跑起來也挺好的,那怎麼部署到服務器上呢?
  2. 服務器要怎麼買?HTTPS證書從哪裏獲取?Nginx是個啥?啊,好煩啊,我只想 npm run start

作爲前端程序員,平時搬磚就夠累了,還要我配置服務器,一劍殺了我算了。

聰明的程序員發現,不管你寫 Express.js 還是 Koa.js 不就是寫響應函數麼?

app.get('/users', (req, res) => {
  res.send('我是帥哥')
})

那我把服務器、證書、域名這些東西都統統給你弄好,你就負責寫相應函數和給錢不就很爽了麼?這就是 Serverless 的由來。它的好處是不再操心服務器的配置、擴展等瑣碎的事情,只需要寫好響應函數就好了。而這種“響應函數”也被稱爲 雲函數,Amazon 稱此爲 Lambda

這時候有人發現,我自己寫好的一些服務,比如收發郵件、數據庫的存取也可以作爲一種服務對外提供,前端工程師只需要給錢,然後請求我提供的 API 接口就可以享受我的服務啦。這也是很多雲廠商另一種收入來源:賣服務。

比如,常見的:語言翻譯服務、手機短信發送服務、鑑黃師、圖形識別等。

模塊化

當工具變得越來越多,Web 應用體積也變得越來越大了。一個大項目裏可能有成百上千個 JavaScript 文件,它們之間相互依賴,可怕的是當前沒有工具可以告訴你到底哪個文件是最先被執行的。

文件的管理就成了一個大問題,所以以應用需要劃分模塊。

在 ES6 之前,社區制定了一些模塊加載方案,最主要的有 CommonJSAMD 兩種。前者用於服務器,後者用於瀏覽器。

const xxx = require('xxx')

ES6 在語言標準的層面上,實現了模塊功能,而且實現得相當簡單,完全可以取代 CommonJS 和 AMD 規範,成爲瀏覽器和服務器通用的模塊解決方案。ES6 模塊的設計思想是儘量的靜態化,使得編譯時就能確定模塊的依賴關係,以及輸入和輸出的變量。

import xxx from 'xxx'

export default xxx

模塊化思想的提出大大提高了 JS 程序員的幸福感,所有的 JS 文件都不再是同一層級,而可以分塊管理了。對前端的工程化有着不可或缺的作用。

包管理工具

有的工程師發現,自己抽象出來的模塊其實也可以放在社區讓別人使用,比如發異步請求的 axios,工具庫 lodash 等。這就需要一箇中心倉庫來存放這些庫了,同時也需要一個包管理工具來管理包的發佈、安裝、升級等。

目前 npm 就是使用最多的包管理工具,當電腦裏裝了 Node.js 後,npm 也會一併裝上。不過使用 npm 在國內下載時會很慢,一般推薦使用 yarn 這個包管理工具,速度更快。

工程化

模塊拆分使得寫代碼時候爽了,但是如果把這些 JS 文件都引入到一個 HTML 上是不是太恐怖了?一個 HTML 裏有 1000 個 script 標籤,比內容還多也有點反人類了吧。CSS 文件同理。

爲了解決這個問題,前端工程師提出了 bundle 這個概念——不管你的模塊多亂,多分散,最終通過一個工具,直接轉換爲1 個 .js 文件。這樣的工具就叫做打包工具。

在 2016 年,Grunt,一個 JavaScript Task Runner 被製作出來了,開發者可以編寫自己的任務,然後流水線地執行。這已經有了工程化的雛形了。

但是 Grunt 的打包速度太慢了。工程師們受不了了,又造了一個 Glup 的打包工具,功能差不多,但是一個字快!


與此同時,另一隻巨獸也在悄然進化——Webpack

Webpack 以其強大的功能、高靈活配置度的特性直接搶奪了 Glup 的市場,很多人都紛紛用起了 Webpack。

隨着 Webpack 的功能不斷增強,開發者的要求也不斷提高,市面上充斥着大量的 Loader 和插件:

  • 熱加載
  • 代碼混淆
  • 代碼壓縮
  • 精簡代碼、TreeShaking
  • loader: file-loader, css-loader, vue-loader
  • ...

Webpack 的另一個問題是,不同環境需要不同的 Webpack 打包配置,導致 Webpack 的配置越來越繁瑣,前端工程師除了平常寫代碼之外,還要負責維護 webpack.config.js 的配置項目。

Parcel 的出現正好打破了這一局面,使用 Parcel 就像使用 iPhone 一樣,不用太多配置可以馬上跑出小網頁。但是,治本不治根,在大型項目面前,還是沒辦法解決繁瑣配置的問題。所以,Webpack 依舊是佔領市場的巨頭。

使用 Webpack 的另一個問題是本地開發打包很慢,Webpack 一般先打包構建再啓動開發服務器。而 Evan You 則想到另一個方法:先啓動開發服務器,當代碼執行到模塊加載時再請求對應模塊的文件。加快本地開發時的打包編譯速度,然後造出了 Vite。目前 Vite 還是個新生兒,可以關注一波,看看以它以後會迸發出什麼好玩的東西。

CSS 預處理器

CSS 讓人詬病的一點是不夠簡潔,很多東西不能複用。比如:

.container { 
  background: red; /*背景爲紅色*/
}

.container .title { 
  color: red; /*標題字體爲紅色*/
}

爲什麼不寫成這樣呢:

.container {
  backtround: red; /*背景爲紅色*/
  .title {
    color: red; /*標題字體爲紅色*/
  }
}

遺憾的是,瀏覽器只認識 CSS,不認識上面這樣的寫法。程序員又開始思考了:其實我不用瀏覽器認識第二種寫法,我只要把第二種寫法在打包的時候轉換成 CSS 不就行了嘛。有了打包工具的加成,這件事我覺得能成!所以,第二種高級寫法被稱爲 CSS 預處理器,即這些寫法要先被處理成 CSS,然後再以普通 CSS 使用。

於是,在 2007 年,Sass 誕生了,借鑑了 Ruby 社區的 Sass 語法,但是前端程序員比較傲嬌:憑啥要跟你叫一個名,就叫爲 Scss,不過一般叫法還是叫 Sass,因爲 Scss 不會讀,哈哈。

後來在 2009 年,Less 被創造出來了,語法 Scss 差不多,沒多大差別。

在 2010 年,又一個預處理器 Stylus 誕生了。

目前 Scss 和 Less 用的比較多,Stylus 名氣比較小。新手不用擔心,這三玩意語法都差不多,大同小異,會一個相當於3個都會了。

進擊的 JavaScript

雖然 JavaScript 本來是一個設計非常糟糕的編程語言,但是 JS 它不是鹹魚,它也要努力成爲世界上最好的編程語言!

JavaScript 總不能一成不變吧?但是也不能一下子就變天了吧?所以,需要有一個起草方案,審覈方案,同意方案,變成規範的過程,這需要大量的人力和專家,那不如做一個 “JS愛好者協會”?European Computer Manufacturers Association (ECMA) 就是類似這樣的組織,不過這個組織格局比“JS愛好者協會”的格局大多了,主要任務是是將 Computer System 標準化。

ECMA International 在 ECMA-262 裏規範了 JavaScript,可以認爲 ECMAScript 就是標準的 JavaScript,所有瀏覽器都要支持標準的 JavaScript。這個 262 的標準規範從 1997 年開始提出了第 1 版。

從上面可以看到,往後幾年就是第 N 代的 ES 標準對應標準的 JavaScript 就是 ECMAScript 201(N-1),比如我們最熟悉的 ES6 其實就是 ECMAScript 2015。而 ES6 對 JavaScript 一個大變革,後面的 ES7,ES8 新增的東西就很少了,所以現在 ES6 其實是 ES6+ 的一個泛指。

但是你有沒有想過一個問題:雖然我用最新的語法做開發,但是用戶用的可能還是老版本的瀏覽器呀,這要怎麼辦呢?

再次得益於自動化打包工具的興起,我們可以在開發的時候用最新的語法,在打包生產代碼時將新語法都轉成舊語法就好了嘛。這種語法的轉換聽起來就很麻煩,不過,聰明的前端工程師已經幫各位大哥大嫂做好,那就是 Babel

Babel 發展到了現在,除了做新舊語法的轉換,還支持 JSX 語法的轉換。

TypeScript

雖然 ES6 新增的語法和 API 已經大幅提升前端程序員的幸福感了,但是 JavaScript 依然是個弱類型的語言:

類型不規範,同事兩行淚。當不正確使用類型時:

那能不能強行給 JavaScript 加上類型呢?微軟說:可以!由微軟牽頭,開發了 TypeScript 編程語言和 TypeScript 的編譯器,前者其實是 JavaScript 的超集,只是多加了很多料;後者則是負責將 TypeScript 編譯成 JavaScript。

注意:這裏的 TypeScript -> JavaScript 是不能用 Babel 實現的,因爲這一步是編譯,而不是新舊語法的替換。TypeScript 不是新語法,是一門正經的編程語言,只不過可以被編譯成 JavaScript。

有了 TypeScript,開發者終於享受到了強類型約束的福利了:

const x: string = '123'

再再再一次得益於自動化打包工具人 Webpack,可以在輸出生產代碼時將 TypeScript 編譯成 JavaScript,如果再加上 Babel,則能進一步轉爲某個時代的 ECMAScript。

單頁應用

在打包工具不斷廝殺的同時,單頁應用框架也一併發展。

在以前,大部分都是一直在用 jQuery 直接操作 DOM 來更新頁面。

每次操作 DOM 時就不得不寫一些麪條代碼。但是這樣很麻煩啊,操作 DOM 這一步能不能封裝一下,數據更新時自動操作 DOM 去更新頁面?

2010 年,Google 研發的 Angular.js 率先實現了 MVVM 想法,即開發者不再需要操作 DOM,可以直接拿數據渲染頁面 Modal-View,而頁面的變化,比如輸入值改變,可以反過來改變數據內容。後來又研發了 Angular2,但是無論是 Angular.js 還是 Angular 本身都太複雜了,借鑑了非常多的後端設計,前端工程師上手難度非常巨大,最終並沒有形成大潮流。

注意 Angular.js 和 Angular 是兩個不同的東西!

2013 年,一個新的前端框架誕生了——Facebook 的 React.js。React 可以說是一個非常純淨的 JS 框架,沒有 Angular 繁瑣的內容,開發者只需要關注單向數據流就可以上手擼頁面了。最後 React.js 在前端社區流行了起來。

但是 React.js 也有自己的問題:由於 React.js 內容太過於純淨了,本身沒有太多的功能,導致開發 React 應用時出現非常多的解決方案,但沒有一個方案是最優的,各有各的優缺點。這也導致了 React 社區總是罵戰不斷、幫派林立、出現各式各樣的鄙視鏈的局面。

比如,在寫樣式時你可以直接 import 樣式文件:

import 'xxx.css'

const xxx = <button className="xxx">xxx</button>

也可以用 CSS module

import styles from 'xxx.css'

const xxx = <button className={styles.xxx}>xxx</button>

也可以用 styled-component

const Button = styled.a`
  color: white;
`

const xxx = <Button>xxx</Button>

但是偏偏有人說 styled-components 是高級人,別的都是垃圾,使用 CSS Module 的人就受不了了,說你天天引那麼多庫來幹嘛?

另一個麻煩點是,單向數據流並不是所有人都喜歡的,人們開始懷念 Angular 的數據雙向綁定了。

到了 2014 年,那個男人來了,他帶着 Vue.js 來了!

Evan You 以前在 Google 和 Meteor 工作過。Vue.js 取了 Angular 和 React 的中間位置,以一種優雅、輕便的姿態登陸前端社區。保留了 Angular 的數據雙向綁定,但是摒棄了 Angular 很多複雜的設計和 API,同時不像 React.js 那麼純淨,開放很多方便的 API 給使用者爽爽。而且還基於 Webpack 開發了 vue-loader 用來解析 .vue 模板文件。這種 .vue 模板文件和 .html 非常相象,新人上手十分簡單。

同時,得益於 Vue 簡潔好看的中文官方文檔,Vue.js 在中國迅速搶佔了小公司的市場。

但是由於 Vue.js 太容易上手了,所以經常被 React.js 社區的一些人覺得寫 Vue.js 的人都是新手。而 Vue.js 的人又覺得寫 React.js 的人天天折騰這麼多“最佳實踐”,簡直是在浪費生命,而且 JSX 的語法太醜了,不如我的 template 語法簡潔好懂。直到現在,Vue 和 React 社區時不時就會爆發小規模的罵戰。

另一個問題出現了:原來的 UI 庫僅僅提供了簡單的 CSS 和原生的 JS,這些 JS 放到單頁應用裏顯得有點冗餘了,有的還會報錯,因爲大多數都是 DOM 的操作。所以 UI 庫必須要配合對應的 SPA 框架進行升級。在別的 UI 庫升級的同時,餓了麼針對 Vue.js 開發 Element UI,而螞蟻金服則針對 React 開發了 Antd。隨後,更多的 UI 庫再次湧現,比如 iView、Ant Design for Vue, Ant Design for Angular 等。

總得來說,Angular, React.js, Vue.js 都開發了自己的一套單頁應用框架,這套框架最後要做的就是 SPA(Single Page Application) 單頁應用。即所有的邏輯都打包在 JavaScript 文件裏了,對外,只會看到一個 .html,一個 .css 和一個 .js 文件。

等一下?一個 .html 文件?那不同頁面怎麼做跳轉呢?這就是前端路由的由來了。

前端路由

不妨想想以前是怎麼做路由的:用戶頁是 user.html,首頁是 index.html,一個 url 對應着一個文件,也就說我們每次鍵入 url 時,實際上是訪問某個 .html。

而瀏覽器裏有一個監聽瀏覽器地址改變的功能,單頁應用的開發者就想了:我只要監聽地址 url 的變化,再用 JS 渲染對應的頁面組件,不就可以實現前端控制路由了麼?這就是前端路由的基本思想。

上面的三大單頁應用框架都有自己的前端路由框架:@angular/router, react-router, vue-router

數據管理

單頁應用框架另一個問題就是數據的管理,子組件訪問的數據都只能靠父組件傳過來,如果一個在很深的子組件想要最外層組件的數據時,就不得不把數據從頭一路傳到尾。

爲了應對這種數據很難共享的問題,程序員就想:我把數據都存到一個公共的地方不就行了嘛?要的時候隨便拿。

那公共地方是哪裏呢?存全局變量?不行啊,會被別人覆蓋啊,而且數據改了之後視圖不能隨之改變呀。所以,工程師又開發一些全局數據管理庫:mobx, vuex, redux。

同構渲染

用多了單頁框架之後,程序員們又發現問題了,單頁最後生成的 HTML 是這樣的:

<body>
  <div id="root"></div>
  <script src="bundle.js"></script>
</body>

要知道搜索引擎每天都會用網絡爬蟲抓取成千上萬個網頁,分析 HTML 裏的內容,以此來提高搜索準確性,這種做法做叫 SEO(Search engine optimization) 搜索引擎優化。但是你看上面這樣的結構,搜索引擎根本不知道你這個網頁是用來幹嘛的。另一個問題是,如果用戶網絡環境很差,那隻要 JS 還沒被加載出來,網頁永遠是一片白色,這非常影響用戶體驗。這要怎麼解決呢?

大家開始懷念當時 JSP、PHP 服務端渲染 HTML 的時候了,因爲服務端渲染 HTML 可以馬上返回 HTML 結構,頁面會先展示一些內容,不至於白屏,而且有了大概的 HTML 結構,搜索引擎更容易做 SEO。服務端的渲染也被叫做 SSR(Server Side Rendering)

前端工程師想到了:對於一些靜態內容,比如商品種類、導航欄內容,其實可以在生成 HTML 的時候就加上,不需要再通過 API 獲取了。這樣的技術就叫做 SSG(Static Site Generation)。那動態的內容,比如朋友圈列表怎麼做呢?初始展示的數據可以先通過服務端先渲染,等用戶與頁面發生交互,比如點擊按鈕後再發請求獲取數據。這就是 同構渲染

與傳統的服務端渲染不同,同構渲染的服務端也使用 JavaScript 來編寫,這樣一來前後端都使用上了 JavaScript 了。

同構渲染簡單來說就是一份代碼,服務端先通過服務端渲染(server-side rendering),生成html以及初始化數據,客戶端拿到代碼和初始化數據後,通過對 html 的 dom 進行 patch 和事件綁定對 Dom 進行客戶端激活(client-side hydration)。

這個整體的過程叫同構渲染。既可以吸取服務端的優點,比如加速首屏渲染,又保留了 SPA 應用的特點,比如前端控制路由跳轉,使得跳轉時不需要再渲染新 HTML。

Single-SPA

當越來越多項目用上了 SPA 框架後,當公司要把多個項目合併成一個項目的時候,這就很麻煩了,不同項目可能用的框架都不一樣,比如用戶詳情用的是 Vue.js,而首頁用的是 React.js,怎麼把這兩者結合呢?

single-spa 和國內阿里開發的乾坤就是解決這種問題的。本質上相當於造一個更大的“單頁的應用”,這個“單頁應用”裏又會有多個單頁的應用。

這種將多個 SPA 整合成一個大 Project 的技術就是 微前端

自動化測試

隨着前端越來越工程化,自動化測試是比不可少的一件事。當造好一些輪子後,就需要接入自動化測試,不然每次修改都要做點點點的人工測試。

最簡單的莫過於單元測試,目前單測常用的庫有 *ava, jest, moch, sinon, chai 等。而前端又是一個非常依賴環境的工種,經常要用到不同的環境,比如 JSX 環境、瀏覽器環境,Vue 環境。又催生出很多提供 Mock 環境的庫,比如 enzyme 就是用來提供 React 環境的。

同時,爲了模擬一些特定的場景,前端還要 Mock 一些東西,比如 localStorage, indexedDB, cookie 等。這些都有不同的庫和工具來實現,比如 mock-axios, mock-redux, mock-cookie 等。

上面的只是屬於白盒測試的做法,前端能不能直接模擬人工做點點點的操作呢?可以的,這就叫端對端測試、或者叫 e2e 測試,或者叫集成測試。現在比較火的工具是 cypressnightwatch

不過對於業務經常頻繁改動的項目,自動化測試並不是一件好事,改動頻繁的業務帶來的是變化無常的測試用例。寫太多的測試代碼,有時候變得事倍功半。

手機H5

隨着智能手機發展得越來越快,微信等一些應用裏都不得不內嵌一些前端的 H5 頁面,比如微信公衆號文章,其實就是一個小型網頁。

另一個應用場景是,工程師們發現 App 裏也可以嵌入 H5 來做簡單的展示和交互,這樣一來移動端就可以少開發一些內容了。這種內嵌H5頁面的應用開發也稱爲混合開發,開發出來的 App 就是 Hybird App.

現在我們手機上的支付寶、微信、QQ 等都是採用了混合開發的模式,既能使用原生的代碼保證流暢度,同時又可以內嵌H5提升開發速度(注意,速度不等於效率)。

低代碼

手機上的 H5 寫多了之後,前端工程師們又發現很多 H5 都千篇一律,很多都是模板一套,再改個顏色就OK了。

因此,聰明的程序員就想到了能不能用拖拽就生成網頁呢?其實拖拽生成網頁並不是什麼新鮮事,也不難實現,早在 wordpress 的時代已經出現拖拽生成個人博客的工具了。只不過現在用到手機上了。

但是拖拽的問題在於不靈活,有時候非常死板。有些運營人員還是懂一點代碼的。所以拖拽工具再度進化成了:可以給別人留個入口做一些簡單的自定義事情。這種模板+簡單代碼做開發的就稱爲“低代碼”開發,通過簡單的配置就能自動生成頁面。

需求生成代碼

除了拖拽,有沒有更高級的生成代碼工具呢?有!比如,用文字描述場景來生成前端代碼,這也是阿里正在做的事情,它們正在研究 P2C (PRD to Code)。個人覺得這是一件好事,對於簡單需求來說可以直接生成比什麼都快。

有人會擔心:這會不會取代前端工程師呢?答案是不可能,也不科學。再厲害的人工智能最終也無法實現設計複雜、靈活多變、千奇百怪、瘋狂迭代的產品需求。最終取代的是隻會寫簡單 html、css 的低級工程師。

最後

不知不覺就寫好了多東西,可見前端東西真的很多很雜。其實再下寫去還能再細寫下去,但是文章實在太長了,到此結束了。

這篇文章除了帶大家瞭解前端常見的“名詞”之外,還希望大家學前端時不要上來就我要精通XXX。你看前端的發展多麼坎坷、都是通過解決一個一個問題纔有今天的前端,學習也是同樣的道理,先實現一個最 Low 的版本,再慢慢升級自己。

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