淺入淺出前端這些技術

近期要給外包培訓,需要準備一些內容,稍微梳理了一下,發現前端技術名詞的確多啊,不過很多新概念都是換湯不換藥的紙老虎,本文嘗試淺入淺出的梳理一下。

從 HTML 說起

老司機可跳過本段直接下一題。

大家學前端應該都是從 HTML 開始的吧,1991年的時候出現了世界上第一個網頁,你可以在這裡訪問它,感受下當年的源代碼:

<HEADER><TITLE>The World Wide Web project</TITLE><NEXTID N="55"></HEADER><BODY><H1>World Wide Web</H1>The WorldWideWeb (W3) is a wide-area<ANAME=0 HREF="WhatIs.html">hypermedia ...

一開始全都是標籤,而且沒有規範,於是一幫人組成了復仇者萬維網聯盟專門來搞網路標準化,也就是今天常說的 W3C。

當時的網頁只能做簡單信息展示還很 LOW,後來一家叫 SUN 的公司搞了個 Java 小程序(Applet),可以跑在網頁中製造很酷炫的效果。另外一家叫網景的公司覺得很牛逼,於是趕緊招人準備出復刻版,由於項目緊,程序員花了兩周時間搞出了JavaScript,結果項目上線後效果出乎意外的好!於是 JS 大火。

有了 JS 用戶就可以和網頁更好的交互了,但是頁面還是很 LOW,於是又過了一年,CSS 出現了,專門用來美化頁面。至此,前端三巨頭誕生了,網頁看起來終於有模有樣了,於是 WEB 發展進入繁榮時代,出現了一大波新技術比如 Spring/JSP/ASP/AJAX 等等,也出現了一大波瀏覽器比如 IE、Firefox、Safari、Chrome、Opera...

於是前端出現了一個非常棘手的問題:瀏覽器兼容問題。同一個DOM操作需要寫很多適配代碼來兼容不同瀏覽器,這是一個很枯燥低效的事情。於是jQuery 誕生了:一套代碼,多端運行。眾前端大喜,紛紛使用至今。

由於技術發展飛快,人們對網頁的要求越來越高,這時一家叫 Google 的公司認為瀏覽器需要更好的體驗和性能,JS 需要一款更快速的引擎來迎接現代化 Web 應用。而當時微軟的 IE6 佔據了大部分市場份額,微軟認為 IE6 已經很完美了,於是解散了IE6開發團隊。

後來的事大家都知道了,IE 成了前端開發的陰影,而谷歌的 Chrome 搭上 V8 引擎上了天,美滋滋。

V8 的出現讓一個叫 Ryan Dahl 的程序員大喜。Ryan一直在研究高性能Web服務,但平時用 C/C++寫太痛苦了,JS 的單線程事件驅動+非同步 IO+V8引擎剛好滿足他的需求,於是他搞出 Node.js。自此 JS 從前端邁向了後端。

然而 Node.js出現的意義遠不止向後端邁了一步,它意味著 JS 可以脫離瀏覽器了,這直接促進了前端工具開發和生態的繁榮,程序員都很懶,為了方便寫代碼發明了各種新技術,比如懶得寫 HTML 便發明了 Jade、Handlebars等模板語言,懶得操作 DOM 便發明了 Angular、React、Vue,懶得寫 CSS 便發明了 SASS、LESS 這種預處理語言、懶得寫 JS 便發明了 coffeeScript。但這還不夠,前端頁面變得越來越複雜,為了寫出更好的代碼,又發明了各種語法檢測工具,比如 jshint、eslint,為了讓JS代碼變得更易維護,還發明了各種各樣的模塊化方案比如 CMD,AMD,直到 W3C 站出來規範了 ES6 的模塊化方案。但是如何把這些模板、預處理語言、JS模塊合併、壓縮、打包成最終能夠線上跑的文件卻成了問題,於是程序員們又發明了各種各樣的打包工具比如 Grunt、Gulp、Bower、Browserify、Webpack、rollup、parcel...

終於前端邁向了工業化之路,下面開始進入正題。

從Webpack說起

在眾多打包工具中,Webpack 是目前最流行的方案,其理念非常簡單:

給定入口文件,webpack 會分析所有依賴到的靜態資源並載入相應代碼進行處理,最終打包成指定的bundle輸出到目標文件夾中。

當然其背後的實現是相當複雜,這裡只拋幾個關鍵問題:

1 如何根據入口文件打包所有依賴資源?

Webpack 本身只能理解 JavaScript,但是卻可以打包 sass、less、png 等非 JS 資源,主要是因為在載入這些資源前都會鏈式調用 loader 進行預處理,將其轉換成 Webpack可以理解的 JS 模塊,比如下面這個 loader 配置:

module.exports = { entry: { app: ./src/app.js }, module: { rules: [ { test: /.js$/, use: babel-loader }, { test: /.(less|css)$/, use: style-loader?css-loader!less-loader!autoprefixer-loader } ]};

當檢測到 app.js 入口文件時就會調用 babel-loader 進行預處理,如果在 js 文件裡面又檢測到 require或import 依賴了.less後綴的文件,就會調用 autoprefixer-loader 進行預處理,處理結果傳遞給 less-loader,css-loader,最後傳遞到 style-loader處理為 webpack 可識別的 JS 模塊。

有了 loader,webpack 理論上可以處理圖片、視頻、字體等各種文件,將其轉化為 JS 模塊,遞歸構造出依賴關係圖,並打包成一個或者多個bundle。

2 Webpack 是如何對 JS 進行模塊化打包的?

3 為什麼Webpack有時很慢?如何優化?

4 插件是如何對依賴資源進行處理的?

loader 只能對資源進行預處理,如果需要更強大的能力就需要引入插件了。Webpack 的插件是一個具有 apply 屬性的 JS 對象,apply 屬性會被 Webpack compiler 調用,而這個 compiler 在整個編譯的生命周期都是可以訪問的。比如最常用的HMR插件:

module.exports = { plugins: [ new webpack.HotModuleReplacementPlugin() ]};

有了 compiler 的訪問權,就可以監聽整個模塊系統的 runtime,檢測文件依賴變化並且實時更新,實現 HMR。

可以說 loader 和插件機制給 Webpack 提供了無限可能,幾乎可以滿足所有前端開發場景了。但是程序員不僅懶,還精益求精。比如 ES6規範出來後又殺出個 rollup,獲得了tree shaking 的新技能,可以幹掉 JS 中的 dead-code 以精簡代碼。眾前端大喜,紛紛試用。但本質上,tree shaking 是依賴了ES6中 static module structure 的設計,在編譯階段就可以確定依賴關係而非等到運行時。所以說......好像這個功能 webpack 用 loader 和插件也能做?的確, Webpack 後來機智地加上了。

然而程序員的作風就是你這個東西不好用,我要重新造一個。所以不僅殺出了 rollup,還中出了一個號稱極速0配置的打包工具 Parcel,眾前端大喜,紛紛試用。

而當我開始試用時發現這個0配置原來是就是個最簡單的 index.html 引入 index.js的 demo??哼,我就是要用 less,ts,vue,你給我0配置試試?所以說0配置是不存在滴,只有絕對可配置才能滿足亂七八糟的前端場景,懶的話搞個 yeoman generator 就好啦。不過這個極速倒是真的(驚喜),多核編譯+文件緩存,讓重啟構建後也能快速編譯,雖然這些 feature 用 Webpack 也能做,不過二者在理念上還是有所差異。Webpack基於JS,強調可配置,而 Parcel對文件無感知,不同資源會被並行處理,並且強調的是插件,你把東西都在插件裡面弄好,然後我直接用就好了,盡量別讓我做配置程序員。然前端場景太多,現有插件可能無法滿足你項目需求,這時你就得親自動手寫插件了,比如我寫的這個 markdown to html的插件 parcel-plugin-md。

新輪子的出現總是伴隨著解決新的問題,有著一定的使用場景, 比如在寫小 DEMO 或者某些輕量級場景/模塊庫下用 Parcel 還是很高效的。Webpack 的靈活讓其經久不衰,但使用場景終究還是打包構建,在工業化之路上還有發布、部署等一系列流程,再加上程序員只要稍微用的不爽就很可能抄傢伙的特點,日後必然還會有新的輪子出現的,眾前端大喜。

從 AngularJS 說起

AngularJS 的出現可以說是前端的一個里程碑,其雙向綁定和 MVC 模式(後面改成 MVW 了)影響深遠。尤其是將前端從 DOM 操作中解放出來,可以把重點放在數據和業務上(當時用的時候是在15年做畢設,還是1.x 版本,現在沒怎麼關注了)。Angular 是個龐然大物,相比之下 Vue.js 要輕巧的多,很適合當下無線端項目,自己工作後也一直在用。外包同學在這方便疑惑較多,重點講下。

Vue 數據和視圖綁定的原理?

這個問題已經成常用面試題了,而且根據面試者的回答深度可以大致了解其CS段位。 本文既然是淺入淺出,就先實現一個最簡單粗暴的Vue版本。

這個最簡單的場景就是:實現一個代表元素內部文案的 v-text 指令,當內部數據變化時 DOM 元素上的內容會自動變化:

<div id="app"> <h1 v-text="title"></h1> <p>Whats Time now: <span v-text="datetime"></span></p></div>

調用代碼跟完整版的 Vue 實例化方式類似:

window.app = new Vue({ el: app, // 為簡單處理這裡的 el 直接傳 id data: { title: 閹割版響應式數據方案, datetime: 2018-03-01: 00: 00: 00 }})

執行之後的效果是這樣的:

當你在控制台實時改變 app.data 的 title 和 datatime 屬性值的時候,頁面上的數據會自動更新。

下面就是實現方案(二十行代碼粗暴版):

function Vue({ data = {}, el = } = {}) { this.data = {} const compiler = ($node) => { if (!$node) return watcher($node, v-text) for (let $child of $node.children) { compiler($child) } } const watcher = ($node, directive) => { const key = $node.getAttribute(directive) key && ($node.innerText = data[key]) && Object.defineProperty(this.data, key, { configurable: true, enumerable: true, get: () => data[key], set: val => data[key] = $node.innerText = val }) } compiler(document.getElementById(el))}

遞歸讀取指令,通過 Object.defineProperty的 setter/getter 方法將內部數據和 DOM 更新關聯起來,實現響應式變化。但是這裡只處理了最簡單的字元串響應,而且只實現了一個指令,沒有考慮字元串插值,沒有構造 Virtual DOM,沒有依賴收集......真要實現一個完善的響應式系統,可以深挖的東西很多,還好這裡只是淺入淺出。

Vue 開發中注意的點?

只要理解了 Vue 的響應式系統和組件設計,就可以避免走很多彎路,比如:

  • 為什麼我改了數據頁面沒更新?
  • 為什麼輸入框沒反應?
  • 為什麼要給 style 加 scope?
  • ...

這些問題網上都有解答,Vue 的資料應該是非常好找了,平時多看下原理多總結,遇到問題按照 `分析錯誤提示 -> google -> github issue -> 源碼定位` 的流程去解決一般沒啥問題。

嗯,感覺還有好多東西,先去看個電影吧。

// TODO

推薦閱讀:

zzz 周刊 - 1039 期 - 公孫離幻舞玲
2017新出爐的前端資源匯總
前端日刊-2018.01.24
React源碼分析 - 事件機制
我理解的同步載入與非同步載入

TAG:前端開發 | 前端入門 | 前端開發框架和庫 |