本章內容未完成。Rails 前端打包工具,近年來變化較大,詳見 Blog文章(2022版) 說明。
現代前端工程開發
Atwood’s Law: any application that can be written in JavaScript, will eventually be written in JavaScript. - https://blog.codinghorror.com/the-principle-of-least-power/
現代前端工程開發包括了一系列以 Node.js 工具鍊的前端開發方式,包括 npm、Webpack,透過 Webpack 就可以編譯 Babel、React、Vue.js 和 Angular 等等。
而 Rails 在 5.1 終於正式支援 Yarn 和 Webpack 了,詳情請見 webpacker gem,這在 Rails 4.2+ 也可以安裝。
自 2022 年起,Rails 官方已棄用了 webpacker gem,請改用 JavaScript Bundling for Rails
以下介紹近年來的前端工程演進。
JavaScript 歷史
- JavaScript 是 1995年由網景公司的 Brendan Eich 為 Netscape Navigator 2.0瀏覽器開發的程式語言,本來叫做 LiveScript,後來跟風 Java 所以臨時改名為 JavaScript,日後這成為大眾對這門語言有諸多誤解的原因之一,兩者的關係跟張三跟張三豐差不多。
- 第一次瀏覽器大戰時,網景將 JavaScript 提交給 ECMA 成為網路標準,學名叫做 ECMAScript
- ES3: IE6,7,8
- ES4: Flash, ActionScript 採用,後來被放棄
- ES5: 2009/12 目前最廣為支援的版本
- ES6: 2015/6 目前的熱門話題,現代瀏覽器努力增加支援中,支援度詳見 compatibility table
為了最大相容於瀏覽器,ES6 可以透過 Babel 編譯技術,將 ES6 程式碼編譯回 ES5。
以下介紹幾點 JavaScript 常考的特別之處:
global 變數
JavaScript 的變數,如果沒有宣告 var
的話,會變成 global variable。
- https://jsbin.com/monoliqoyo/edit?js,console
建議都加上 'use strict';
用 嚴格模式 提早發現問題。
Week Typing 弱型別
盡量用 ===
而不要用 ==
123 == "123"; // true
1 == true; // true
123 === "123"; // false
1 === true; // false
this 之謎
JavaScript 的 this 設計,跟 Ruby, Java, Swift 等等物件導向語言不一樣。JavaScript 的 this 比較殘有坑…
- https://jsbin.com/wivilo/edit?html,console,output
- 解法: 用 closure 把 this 先存下來
- https://jsbin.com/wujuzod/edit?html,console,output
- 解法一: 多包一個 function 用 closure
- 解法二: 用 bind 方法
跟
bind
很像可以修改this
的方法還有call
跟apply
,差別在於這兩個會直接呼叫,而call
跟apply
的差別只在參數接受不同而已,apply
第二個參數吃陣列。
Hoisting 變數抬升
JavaScript 會將變數和函數的宣告,搬到所屬 function 的最上面(也就是被抬升到該作用域的最高處)。但是卻不會先 assign 變數值,而暫時是 undefined。這個叫做 Hoisting 特性。
- https://jsbin.com/qiwijakuko/edit?js,console
Immediately-Invoked Function Expression (IIFE)
這個小技巧還蠻常見的,用這種形式 (function(){ /* code */ }());
執行程式碼來限制裡面的 Variable Scope 不會影響到外面的變數。
Prototype
ES5 的物件導向並沒有大家熟悉的 Class 類別語法,而是用比較原始的 Prototype 設計。
- JavaScript Object Creation: Patterns and Best Practices 這篇文章介紹了如何在 JavaScript 建構物件。
- https://jsbin.com/hediziyate/edit?js,console
若想完全了解,推薦課程 Udacity: Object-Oriented JavaScript
MV* 設計模式和 JavaScript Template: EJS
這節我們使用 MV* 設計模式和 JavaScript Template: EJS 來實作一個 SPA (Single-page Application) 應用,透過這個練習,你可以更了解 JavaScript 物件用法。
MV* 設計模式
後端框架大多使用 MVC 架構,因此一開始 SPA 也希望套用這樣的設計模式,將程式碼拆分成 Model 和 Controller 進行組織,並搭配 JavaScript Template 實作 View 的部分。
練習一:票選貓咪 App
可能需要複習一下 jQuery
- Cat Clicker 請 clone 這個 repo 開始練習
練習二:Todo App
打造例如 TodoMVC 這樣的應用
- ejs https://ejs.co/
- ejs gem https://github.com/sstephenson/ruby-ejs/
Example Code:
- https://github.com/ihower/rails-exercise-ac8/blob/spa/app/views/todos/v1.html.erb
- https://github.com/ihower/rails-exercise-ac10/blob/master/app/views/welcome/v1.html.erb (無架構單純 jQuery 版本)
- https://github.com/ihower/rails-exercise-ac10/blob/master/app/views/welcome/v2.html.erb (自幹 MVC 版本)
其他推薦練習課程
https://www.udacity.com/course/javascript-design-patterns–ud989
其他 JavaScript Template
ejs 因為跟 erb 很像,所以對 Rails 工程師來說相對親切。不過不熟 Rails 的 F2E 沒感覺,而會選擇以下方案:
- http://handlebarsjs.com/
- http://mustache.github.io/
- http://jade-lang.com/
知名的 MVC javascript framework
- http://backbonejs.org/
- http://emberjs.com/
這些 framework 還包括處理 Routing 路由。
歷史演進
JavaScript 的 MV* 設計模式+ JavaScript Template 的架構,基本上仍是模仿後端 MVC framework 的設計,從 2010 年 Backbone.js 開始這個風潮,而 Ember.js 集大成延續這個設計。這堂課並沒有直接學習現成的框架,而是透過模仿學習 MV* 設計模式來練習 JavaScript 技能。
另一方面,這套 MVC 架構在 2012 年已被 Angular.js 的 MVVM 架構超越,在 2014 年 Angular.js 又被 React.js 的 Component 元件化架構超越,在 2016 年 React.js 又被 Vue.js…… (現在進行式,還說不準)
這些新的框架,都是為了解決直接去修改 DOM 造成的 Single Source of Truth 問題:希望只需要修改 Model 的值,所有畫面上相關的元件都會自動變化好。而不是用 jQuery 直接去處理 DOM 元素和事件綁定,因為實在太瑣碎了。
初探 Vue.js
Vue.js 剛開始學跟 Angular.js 很類似是一套 MVVM 架構,但是較了解之後卻像 React.js 一樣是元件化。算是一個融合雙方優點,而設計上又更為輕巧容易入門的前端框架。
簡易安裝方式
相比 React.js 或 Angular 需要編譯,Vue.js 如果沒有用到單組件功能,可以不需要 Webpack 編譯,可用以下方式安裝進 Asset Pipeline。
下載 Vue.js 程式,放在 /vendor/javascripts/ 下,然後在 app/assets/javascripts/application.js 載入即可。
Vue.js 有提供 Developmemt 和 Production 版本,前者有額外的開發錯誤提示,建議可以兩個版本都下載放在 ` /vendor/assets/javascripts/ 下,然後將
application.js 改成
application.js.erb,透過判斷
Rails.env` 來決定載入哪一個版本,請參考 設定範例。
另外還可以安裝 Chrome 外掛 Vue.js devtools。
練習
將上一節的 MVC 題目,改成用 Vue.js 實作。
- Cat Clicker Example
- https://github.com/ihower/rails-exercise-ac12/blob/master/app/views/cats/v2.html.erb
- Todo App Example Code
- https://github.com/ihower/rails-exercise-ac8/blob/spa/app/views/todos/v2.html.erb (Vue 1.0)
- https://github.com/ihower/rails-exercise-ac10/blob/master/app/views/welcome/v3.html.erb (Vue 2.0)
初探 Node.js
Node.js 使用了 Chrome 的高效 JavaScript V8 引擎,讓 JavaScript 可以單獨跑在 server 上,無須瀏覽器環境。這讓 JavaScript 如同 Ruby/PHP/Python 一樣可以成為後端的程式語言。
- 開發機用
brew install node
就可以安裝使用 - npm 套件管理工具 https://www.npmjs.com/
npm init
初始當前目錄成為 Node.js 專案(會產生一個 package.json 檔案)npm install XXXX --save
可以安裝套件到這個專案npm install
根據package.json
安裝套件- 使用 git 的話,將
node_modules
這個目錄加入.gitignore
- CommonJS 載入模組 http://webpack.github.io/docs/commonjs.html
-
不過因為 npm 跑太慢,Facebook 受不了在前一陣子新出了 YARN 來取代 npm,詳見 Yarn 官網 和指令對照 NPM vs Yarn Cheat Sheet。開發中的 Rails 5.1 也會使用 Yarn 這個工具來安裝 JavaScript 套件。
brew install yarn
即可安裝
Node.js 作為前端開發的打包工具
等同於 Rails 的 Asset Pipeline,在 Node.js 也有一樣的工具可用於打包和預處理 JavaScript、CSS 等靜態檔案。
- 知名的有:
- Webpack https://webpack.github.io/
- Browserify http://browserify.org/
- 瀏覽器不支援 Node.js 的 CommonJS 語法,而上述的打包工具會做編譯處理好讓瀏覽器可以載入
- 上述的打包工具當然也會做編譯預處理,例如
- ES6 透過 Babel
- React.js 的 JSX 語法也需要編譯預處理才能給瀏覽器使用
- 前後端分離的架構,前端可以單獨佈署至 CDN。
- 順道一提,Rails 的 Asset Pipeline 並沒有 CommonJS 和 ES6 的編譯功能。(據說之後 Sprockets 4 會支援 ES6,但是目前的 Rails 4 和 Rails 5 用的 Sprockets 3 並沒有支援)。也因為如此,有激進 F2E 的 Rails 團隊,會改用 webpack 來作打包工具,弱化甚至拆掉 Asset Pipeline)
以下示範用 webpack 建立一個簡單的專案:
Example project: https://github.com/ihower/basic-front-end-ac8
新增專案
mkdir your_app
cd your_app
yarn init
yarn add webpack --dev
yarn add jquery
yarn global add webpack-dev-server
用 Git 的話,請把 node_modules 這個目錄加到 .gitignore 裡面
新增 webpack.config.js
、index.html
、entry.js
檔案
啟動開發伺服器
webpack-dev-server --progress --colors
打開瀏覽器前往 http://localhost:8080
React 和 Vue.js
上述 webpack 拿來開發 React 或 Vue.js 還需要安裝很多套件,React 和 Vue.js 官方都有包好的套件可以直接拿來使用,請參考:
這兩個都採用 ES6 語法
Node.js 用於後端開發
Node.js 採用 event-driven 架構和 non-blocking asynchronous I/O,對於 I/O intensive 的應用有非常好的性能表現,特別適合即時通訊和網頁遊戲的應用。但是也由於隨處都是非同步 callback 的撰寫方式,造成程式碼 Callback Hell 的缺點。
- Node 入門:用 Node.js 實作一個 Hello World 的 HTTP server
- Node.js 的後端框架們
- http://expressjs.com/ (Sinatra-like MVC)
- http://hapijs.com/ (Sinatra-like MVC)
- http://koajs.com/ (Sinatra-like MVC)
- http://socket.io/ (WebSocket only)
- http://sailsjs.org/ (Rails-like MVC, integrated with socket.io )
- https://www.meteor.com/ (Full-stack, including websocket)
- https://strongloop.com/node-js/loopback-framework/ (REST API Only)
- Isomorphic/Universal JavaScript 前後端通吃的野心
- Electron 用 Web 技術做桌面 App
初探 React.js
https://facebook.github.io/react/
React 是一套強調組件化的 View library,並且採用了 JSX 語法將 HTML template 融合進 JavaScript 語法裡面。不過這也導致了它一定需要如 webpack 的打包工具進行編譯,才能佈署上瀏覽器。
比起當時的 Angular.js,輕量可組合、效能好很多、學習曲線更平滑,而且不像 Google 這是 Facebook prodcution 就在用的東西,品質可靠。
Example Code:
要在 Rails 裡面用 React 的話,安裝 react-rails 是最簡便的方式,但缺點是沒有 CommonJS 支援。
https://github.com/ihower/rails-exercise-ac8/blob/spa/app/views/todos/v3.html.erb
典範轉移
React.js 帶來的組件化 Component 概念影響了所有 JavaScript Library/Framework 後進者,包括 Vue.js、Angular 2 和 Polymer,以及 Web components 標準。
而其 Virtual DOM 的內部實作,讓 React 不只可以運作在瀏覽器的 DOM 環境,也可以在後端 Node.js 執行,也就是 server-side rending,也可以做跨平台的 React Native
另外,也因為 React 需要編譯的關係,因此順便用 ES6 一起編譯也是順理成章了。
大型應用
無論是 React.js 或 Vue.js,都只有包含 View 組件功能。而在大型應用中,多個組件的狀態管理和資料流會變得複雜,因為狀態分散在許多組件內,而這些狀態又需要透過 Ajax 需伺服器溝通。另外,也會考量實作路由 Route 修改網址,可作為瀏覽時的進入點。
Facebook 因此延伸出 Flux 架構,以單向資料流的概念實作出一個框架。近來最有名的 Flux 實作是 Redux。
Vue.js 的部分請參考 構建大型應用 。
參考資料
學習資源
- A curated list of free resources to master React Development
- 從零開始學 ReactJS
- React 技術棧系列教程
- Thinking in React
- 6周学习计划,攻克JavaScript难关(React/Redux/ES6 etc.)
- Step-by-step tutorial to build a modern JavaScript stack from scratch
- JavaScript 全棧工程師培訓教程