Top Banner
JAVASCRIPT CODE QUALITY 講者 josephj 關於 JavaScript 品質,我想說的是...
77

JavaScript Code Quality

Jan 15, 2015

Download

Technology

Joseph Chiang

「關於 JavaScript 品質,我想說的是...」於 JSDC.tw 2013
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: JavaScript Code Quality

JAVASCRIPT CODE QUALITY

講者 josephj

關於 JavaScript 品質,我想說的是...

Page 2: JavaScript Code Quality

當談到 JavaScript 品質...

大家心裡的重要性就好像左邊那一本 XD

Page 3: JavaScript Code Quality

JavaScript 的興起...讓開發者覺得 JavaScript 就是要複製貼上

Page 4: JavaScript Code Quality

Bad JavaScript = 違章建築

寫程式應該是被維護的,而不是一看就想要拆掉的

6000 ⾏行 JavaScript!

Page 5: JavaScript Code Quality

Agenda

·在 Yahoo! 學到的教訓

·需要建立前端團隊的架構

·在 miiiCasa 學到的教訓

·需要先設計、多抽離

·未來可持續努力的方向

·自動化 Plus

成長 = 從過去的錯誤反省!

Page 6: JavaScript Code Quality

第 1 課 from Yahoo!前端⼯工程師必須成為真正的團隊、⽽而⾮非只是資源

Page 7: JavaScript Code Quality

中央游泳池當時「前端⼯工程師」不屬於任何產品線

專案 1

專案 2

F2E Pool不會有閒置資源

Page 8: JavaScript Code Quality

中央游泳池當時「前端⼯工程師」不屬於任何產品線

專案 1

專案 2

F2E Pool基本上都在專案

任務導向、缺少團隊機制

大家只管自己的眼前的案子

缺少水平的機制去管理品質

Page 9: JavaScript Code Quality

久了必會發生慘案

• 由新人負責、跟其他專案成員關在小房間

(沒有其他 F2E)

• View 全部用 JS 組、後端只給 JSON

• 不支援 NoJS、沒法上一頁

• 重複程式碼非常多,沒人敢大改

• 專案結束沒多久,新人離職

• 2012 年底,此服務關閉

Page 10: JavaScript Code Quality

永遠在我心裡的一句話

“As a team, F2E 的價值究竟在哪裡?”

好不容易培訓⼀一個新⼈人、卻⼜又因制度不健全害了他

Page 11: JavaScript Code Quality

開始去思考「團隊」這件事

• Code Convention 團隊規範

• Code Review 定期 Review 程式碼

• Checklist 上線前需達成的清單

⾮非常重視⼀一致性、維護性

Page 12: JavaScript Code Quality

規範一些最基礎的小事情

一致性、像同一個人寫的

https://github.com/josephj/f2e-styleguide

Page 13: JavaScript Code Quality

Code Review

http://programmers.stackexchange.com/questions/170211/php-data-access-layer

程式碼接受所有⼈人檢驗

Page 14: JavaScript Code Quality

比 JSLint 更傷感情 XDhttps://github.com/miiicasa/youtube-iframe/issues/1

Page 15: JavaScript Code Quality

需要比較正面的態度

每次 Code Review 都能學到很多東⻄西!

Page 16: JavaScript Code Quality

Pair Programming

http://en.wikipedia.org/wiki/File:Pair_programming_1.jpg

⼀一個⼈人寫程式、⼀一個⼈人當觀察員

Page 17: JavaScript Code Quality

導入正確的模式模組化 (Module) 與觀察者 (Observer) 模式

Page 18: JavaScript Code Quality

模組:寫法一致、避免全域變數

welcome/_notification.js

⼀一個模組即有⼀一個 JS 檔

Page 19: JavaScript Code Quality

模組:寫法一致、避免全域變數

YUI.add("welcome/_notification",  function  (Y)  {    var  trans  =  {},            module;

   module  =  new  Y.Module({            selector:  "#notification",            init:  function  ()  {                                },            on:  {                    viewload:  function  (node)  {

                   }            }    });

},  "0.0.1",  {"requires":  [            "module"    ]});

welcome/_notification.js

以 CLI ⾃自動從樣板建⽴立模組⼀一個模組 (Partial) 配⼀一個 JavaScript 檔

好⽤用⽅方法與屬性都圍繞在此 instance

⼀一旦當此模組存在於 DOM 之中

相依模組

此模組的 Selector 或物件參考

Page 20: JavaScript Code Quality

模組:寫法一致、避免全域變數

YUI.add("welcome/_notification",  function  (Y)  {    var  trans  =  {},            module;

   module  =  new  Y.Module({            selector:  "#notification",            init:  function  ()  {                                },            on:  {                    viewload:  function  (node)  {

                   }            }    });

},  "0.0.1",  {"requires":  [            "module"    ]});

                           module.broadcast("greeting",  trans.greeting);                            module.listen("need-­‐love",  function  (e)  {  /*  do  something  */  });                            new  Y.ScrollPagination({node:  node});

welcome/_notification.js

所有的功能都圍繞著模組化開發

多國語系

送廣播

聽廣播

加上其他相依模組

trans.greeting  =  module.getTrans("greeting",  "Hello  World");

,"module-­‐popup",  "module-­‐intl",  "gallery/scroll-­‐pagination"

Page 21: JavaScript Code Quality

模組:寫法一致、避免全域變數

YUI.add("welcome/_notification",  function  (Y)  {    var  trans  =  {},            module;

   module  =  new  Y.Module({            selector:  "#notification",            init:  function  ()  {                                },            on:  {                    viewload:  function  (node)  {

                   }            }    });

},  "0.0.1",  {"requires":  [            "module"    ]});

                           module.alert(trans.greeting);                            module.broadcast("greeting",  trans.greeting);                            module.listen("need-­‐love",  function  (e)  {  /*  do  something  */  });                            new  Y.ScrollPagination({node:  node});

welcome/_notification.js

所有的功能都圍繞著模組化開發

多國語系

共⽤用的 popup

送廣播

聽廣播

加上其他相依模組

trans.greeting  =  module.getTrans("greeting",  "Hello  World");

,"module-­‐popup",  "module-­‐intl",  "gallery/scroll-­‐pagination"

限制團隊成員在沙箱(Sandbox) 中開發

功能擴充也能有一致介面

只專注開發該模組功能、無法污染外界

Page 22: JavaScript Code Quality

不只是 JavaScript Ninja!認識軟體⼯工程與持續集成,事半功倍!

Page 23: JavaScript Code Quality

需從軟體工程的角度出發

• Lint 自動檢查程式碼

• Combine/Compress 自動合併及壓縮 JS/CSS

• Optimize image 圖檔最佳化

• Compass 用 High Level 的語言讓 CSS 簡化

• Skeleton 自動建立樣板檔案

導⼊入⾃自動化、減少「⼿手⼯工」、才能提昇品質

Page 24: JavaScript Code Quality

在 miiiCasa 實踐了這些理念很幸運可以從零開始、擔任前端⼯工程部⾨門主管

這些想法與理念都⼀一⼀一地實踐了

難道就不會有問題了嗎?

http://www.slideshare.net/josephj/ss-7705095

建立前端開發團隊

Page 25: JavaScript Code Quality

第 2 課 from miiiCasa 必須先設計、並導⼊入更好的 JavaScript Pattern

Page 26: JavaScript Code Quality

miiiCasa Space 家庭雲:用單一 URL 存取家中裝置上的資料

• 4 個不同的媒體:照片、音樂、影片、文件

• 2 個不同的類型:首頁列出 Storage、內頁只列目錄

• 都是檔案列表、但差異很大:• View 都各自有差異

• 照片要 Slideshow

• 音樂要播放器

• 影片用 VLC / Flowplayer

• 文件要用 Lightbox 顯示照片、同時支援

影片、音樂、Google Drive Preview...

Page 27: JavaScript Code Quality

有時程壓⼒力...該怎麼快速產出?

Page 28: JavaScript Code Quality

『coding』= 複製貼上的工作

http://but.tw/2008/10/programmers_rule/

團隊的每個⼈人都有責任

Page 29: JavaScript Code Quality

用「複製、貼上」達成需求與滿足時程

• 一模組約 1,500 行 JS

• 4 個不同的媒體

• 2 個不同的類型

• 差異很大 = 修修改改

1,500*4*2 = 12,000 行

後續有新功能都幾乎是「複製貼上 x 8」

Page 30: JavaScript Code Quality

Winchester Mystery House

http://www.flickr.com/photos/harshlight/3670192350/

外表富麗堂皇、也確實地達成應有功能與時程

Page 31: JavaScript Code Quality

http://www.flickr.com/photos/dalvenjah/33560263/lightbox/

內部的結構...

http://www.flickr.com/photos/skinnylawyer/5570163978/

疊床架屋是軟體開發最常見的問題

在煙囪上架屋頂? ⾛走向屋頂的樓梯?

Page 32: JavaScript Code Quality

壓縮開發時程之謎

Stoyan Stefanov

The few man-hours spent writing the code initially end up in man-weeks spent reading it.

數⼩小時寫出的程式碼、得花數週或更多的時間維護它

要在 8 個 JavaScript 上:

• 增加新功能

• 修改 Bug

• 改變邏輯

• 看不懂、乾脆重寫

⽼老闆跟專案經理永遠無法理解的事...

Page 33: JavaScript Code Quality

面對問題未來要怎麼預防同樣的問題、或避免讓問題繼續蔓延

Page 34: JavaScript Code Quality

EDD – 設計文件圖表

BranchURLs

檔案結構

Controller

模組詳細說明

預先規劃的假資料

JS 的架構日益複雜

需要跟後端程式一樣先規劃

負責人先仔細思考、撰寫出此文件

所有開發人員一起討論可行性、問題點

把思考不周延之處找出來、了解配合模式

時程預估較為準確、有機會先預防

Page 35: JavaScript Code Quality

Refactoring - 重構

Page 36: JavaScript Code Quality

重構 = 打掉重寫?

應避免 NIH 綜合症(非我所創、Not Invented Here Syndrome)

NO!

技術人員應該用更客觀的心態面對重構

重構 = 持續且小的改進。

Page 37: JavaScript Code Quality

開發 重構 開發 重構

把重構納入產品開發的 Iteration

......

無法⼀一開始寫出完美的程式碼藉由實作、看到問題與需求,再著⼿手修正

逐步達成理想的程式碼品質

Page 38: JavaScript Code Quality

重構案例:Pop-up 視窗

util.showDialog(module,  title,  message,                                  okText,  cancelText);

module.broadcast("dialog-­‐show-­‐request",  {        title:  title,        msg:  message,        buttons:  {                ok  :  {                        label:  okText,                        callback:  okHandler                },                cancel  :  {                        label:  cancelText                }        }});

new  Y.Panel({        headerContent:  title,        bodyContent:  message,        buttons:  []        render:  true,        visible:  true})

過去⾄至少有三種不同的作法

統⼀一作法、將預設⾏行為確認

module.alert(message);

module.confirm({        title      :  title        content  :  message},  okHandler);

module.inform(message);

對程式碼精簡、⾏行為的⼀一致性獲得⼤大幅改善

Page 39: JavaScript Code Quality

重構案例:Pop-up 視窗

util.showDialog(module,  title,  message,                                  okText,  cancelText);

module.broadcast("dialog-­‐show-­‐request",  {        title:  title,        msg:  message,        buttons:  {                ok  :  {                        label:  okText,                        callback:  okHandler                },                cancel  :  {                        label:  cancelText                }        }});

new  Y.Panel({        headerContent:  title,        bodyContent:  message,        buttons:  []        render:  true,        visible:  true})

過去⾄至少有三種不同的作法

統⼀一作法、將預設⾏行為確認

module.alert(message);

module.confirm({        title      :  title        content  :  message},  okHandler);

module.inform(message);

對程式碼精簡、⾏行為的⼀一致性獲得⼤大幅改善

重構的價值

減少程式碼中光怪陸離的現象

未來開發更容易

減少 UI 的不一致

Page 40: JavaScript Code Quality

Decoupling - 抽離

避免把所有程式碼都放在一個 JS 中

Page 41: JavaScript Code Quality

為什麼會產出「一大包」JS?

• 麻煩!JS 載入需考慮「前後順序」<script/> 有先後順序,例如載入 jQuery UI 的 Tabview.

• JS 本身缺乏「模組」的觀念直到最近 ECMAScript 才要內建

• 「頁面」眾多、分割不易不像其他的語言,大多只要 require 就可以使用

• 對 JavaScript 缺乏「軟體工程」的態度非本科:對程式抽離、正規化概念薄弱。本科:對 JS 不用想這麼多吧!

Page 42: JavaScript Code Quality

抽離範例:Space Photo

• 主要功能:列出 Router USB 裡的照片。

• 其他功能:

提供下載檔案、幻燈秀、縮圖產生機制、

檔案描述、LightBox、分享、重新命名等機制。

這才是此程式中最重要的

每一個都可以另開 Module 處理

Page 43: JavaScript Code Quality

代碼抽象三原則

• DRY - 不要重複自己出現重複時就抽象出一個解決方法

• YAGNI - 你不會需要他快 + 簡單!不要把精力放在抽象化上

• Rule of 3 - 三次原則當同樣的情況出現三次才進行抽象化

http://www.ruanyifeng.com/blog/2013/01/abstraction_principles.html

Page 44: JavaScript Code Quality

功能抽離範例

space-centralize.js (共 103 行)

/**  *  @module  space-­‐centralize  */YUI.add("space-­‐centralize",  function  (Y)  {

       //  Implementation  here...

       Y.Space.bindCentralize  =  bindCentralize;

},  "0.0.1",  {        "requires":  [                "space",  "node-­‐base",  "event-­‐resize"        ]});

需要讓圖⽚片隨螢幕寬度置中對⿑齊,瀏覽較舒適

原本負擔很重、1500+ ⾏行的 JS 檔

/**  *  @module  space/photos/_photos_main  */YUI.add("space/photos/_photos_main",  function  (Y)  {

       //  ...        Y.Space.bindCentralize();        //  ...

},},  "0.0.1",  {        "requires":  [                //  ...                "space-­‐centralize",                //  ...        ]});

只增加兩行

Page 45: JavaScript Code Quality

採用模組化 Library解決 HTML 中 JavaScript 缺乏模組機制的問題

define(“editor”, [‘a’,’b’,’c’], function () {    function Editor { /* Constructor */ }    return Editor;});

require(["editor"], function (Editor) {    new Editor();});

editor.js – 定義 editor 模組

demo.js – 使⽤用 editor 模組

任何⼀一個網站都應該要⽤用!

Page 46: JavaScript Code Quality

應加入的規範

• 一個 JS 檔案不應超過 500 行

• 一行不應超過 100 個字元

維護一個大雜匯的 JavaScript 有何意義?

會超過 500 行應代表你該抽離功能了

Page 47: JavaScript Code Quality

未來還能做些什麼

Page 48: JavaScript Code Quality

給自己未來的一些題目

大多是更多工具的整合、讓自動化來提升品質

⼯工具 ⼯工程師

•無聊•重複性的•浪費時間的 藉由⼯工具持續回饋

Page 49: JavaScript Code Quality

自動偵測重複的原始碼

需要把「避免重複原始碼」變成⼀一個習慣

Page 50: JavaScript Code Quality

是很重要的一件事!

大家所熟悉的 JSLint, JSHint 卻沒有 Orz...

都只有「單一檔案內」的語法檢查

Page 51: JavaScript Code Quality

PMD - 原始碼分析器

其中 CPD 工具可以分析 JS 的重複

/bin/run.sh  cpd  -­‐-­‐minimum-­‐tokens  50                                  -­‐-­‐files  ~/project/foo/js                                  -­‐-­‐language  ecmascript

Page 52: JavaScript Code Quality

跑出來的結果重複的行數 重複的檔案列表

非常需要納入 Commit 流程或 Auto Build 檢查

Page 53: JavaScript Code Quality

Error Log 管理JavaScript 錯誤應該被即時記錄並回報

瀏覽器錯誤

window.onerror

try-catch

錯誤發生

程式決定是否要往上丟

程式決定是否要往上丟 把錯誤丟到 Server 做記錄、回報

就不用辛苦地翻程式碼找問題

控管好這兩個時間點

Page 54: JavaScript Code Quality

       //  Log  to  server.        window.onerror  =  function  (message,  url,  line)  {                var  queryString,                        el;                queryString  =  location.search.slice(1);                el  =  document.createElement("img");                el.src  =  LOG_URL  +  "?"  +  [                        "hostname="  +  encodeURIComponent(location.host),                        "message="  +  encodeURIComponent(message),                        "line="  +  encodeURIComponent(line),                        "url="  +  encodeURIComponent(url),                        "query="  +  encodeURIComponent(queryString)                ].join("&");                return  true;  //  Avoid  browser  error.                                                                                                                                                                  };

把錯誤回報給 Server常見作法:用假圖片 Request 達成、可跨網域

但是!錯誤需要被 Grouping 才有價值

Page 55: JavaScript Code Quality

推薦工具 - Sentry接收各種程式語言錯誤、分組、寄通知信

Page 56: JavaScript Code Quality

Esprima

http://esprima.org/

最近只要聊到 JavaScript Code 品質,幾乎都會聽到這個工具

Page 57: JavaScript Code Quality

Esprima 運行原理

程式碼 抽象語法樹Source Code Abstract Syntax Tree

var  answer  =  6  *  7;

{    "type":  "Program",    "body":  [        {              "type":  "VariableDeclaration",              "declarations":  [                  {                      "type":  "VariableDeclarator",                      "id":  {                          "type":  "Identifier",                          "name":  "answer"                      },                      "init":  {                          "type":  "BinaryExpression",                          "operator":  "*",                          "left":  {                              "type":  "Literal",                              "value":  6,                              "raw":  "6"                          },                          "right":  {                              "type":  "Literal",                              "value":  7,                              "raw":  "7"                          }                      }                  }              ],              "kind":  "var"        }    ]}

將程式碼轉成結構⼀一致的 AST 格式,很容易做後續處理

經過 Esprima Parse

Page 58: JavaScript Code Quality

Esprima 範例 – CodePainter問題:每個⼈人寫的空格、引號習慣都不⼀一樣,能否統⼀一?

跑⼀一次⽤用 Esprima 實作的 CodePainter ⼯工具,問題全搞定

https://github.com/fawek/codepainter

Page 59: JavaScript Code Quality

Esprima 的可能應用

• JSHint: 將會改用 Esprima

• 語法高亮

• 塞 Log,協助偵錯:

• 在每個 Method 都放:console.log("someMethod()  is  executed.")

http://www.slideshare.net/ariyahidayat/javascript-parser-infrastructure-for-code-quality-analysis

Page 60: JavaScript Code Quality

Continuous Integration 持續集成「所有」⾃自動化檢查,都應該與 CI 整合

中央 Git

個⼈人 Git

git  push

post-­‐receive

trigger

git  clone

Build

JSHint

Code Duplication

Unit Test

Functional Test

Code Coverage

通知

成功或失敗

Performance

讓檢查像喝⽔水⼀一樣簡單、確保品質

CI 伺服器

Page 61: JavaScript Code Quality

推薦服務

CodeShip

懶得⾃自⼰己架⼀一台 CI 伺服器?市⾯面上有好幾家 SaaS 的服務可⽤用

若你的原始碼是放在 GitHubCodeShip 與 Travis 都只要勾選就可以使⽤用

Page 62: JavaScript Code Quality

把團隊更緊密的綁在一起把所有訊息 (Commit, Wiki, Issue) 都倒進聊天室

不用交談也知道彼此的做了什麼、點選可看詳細內容

Page 63: JavaScript Code Quality

文學編程Literate Programming

寫程式的理想型態

Page 64: JavaScript Code Quality

Donald Knuth現代電腦科學的鼻祖

A style of programming that maximize our ability to perceive the structure of a complex piece of software.

文學編程的理念

「是時候了,讓我們的程式碼更好!」

文學編程是一種寫程式的理想形態 :

讓我們對於軟體的每個複雜環節都能

充分地理解

Page 65: JavaScript Code Quality

更有組織維護自己的文件Leo Editor - 依自己的思路撰寫文件、組織代碼

可以是任何的內外部⽂文件、節點可以任意地被複製搬移

⾃自由地組織、編排節點順序

產出的程式碼可與其他⼈人 Merge、Leo 會幫你追蹤⽀支援 Textile、Web 投影⽚片的輸出

Page 66: JavaScript Code Quality

更有組織維護自己的文件Leo Editor - 依自己的思路撰寫文件、組織代碼

可以是任何的內外部⽂文件、節點可以任意地被複製搬移

⾃自由地組織、編排節點順序

產出的程式碼可與其他⼈人 Merge、Leo 會幫你追蹤⽀支援 Textile、Web 投影⽚片的輸出

Page 67: JavaScript Code Quality

Leo Editor 很強大、也不針對特定語言,但是...

每個人都有自己愛用的編輯器(Vim 或 Sublime)

Leo 在程式碼編輯功能上似乎不怎麼樣...

在一堆編輯器中切換也是件很愚蠢的事

以喜歡的編輯器、⽤用⽂文學編程寫 JavaScript

Leo 的缺點

Page 68: JavaScript Code Quality

https://github.com/jostylr/literate-programming

以 JavaScript 實作的文學編程

編譯 Markdown

產出 JavaScript

James Taylor

Page 69: JavaScript Code Quality

https://gist.github.com/josephj/5580918

Demo

>  File  count.js  savedliterate-­‐programming  ⽂文學編程.md                                                                                                                                                                                                                                                                    

寫程式就像寫部落格⼀一樣!

可重新組合章節讓別⼈人⾮非常容易理解

Page 70: JavaScript Code Quality

Literate CoffeeScriptCoffeeScript 的作者 = 文學編程的大力推廣者

Jeremy Ashkenas

Journo 部落格系統

Docco API 文件產生器

以⽂文學咖啡寫成:

缺點:得依照原始碼的順序寫

Page 71: JavaScript Code Quality

Journo 的 README.md就是部落格系統的完整原始碼 ( journo.litcoffee)

$  coffee  -­‐c  journo.litcoffee  #output  journo.js

Page 72: JavaScript Code Quality

文學編程過去不是主流、未來也不會

文學編程小結

但身為一個開發者,應秉持文學編程的精神:

「程式碼是給人閱讀的」

以上兩種作法,似乎也可以稱為「部落格編程」

是我們現今最容易理解技術的一種 Material

Page 73: JavaScript Code Quality

Code Review

設計⽂文件 產出 API ⽂文件

模組化

模組架構代碼規範Pair Programming

檢查清單⾃自動合併與最⼩小化

Lint

單元測試物件導向

重構

代碼重複檢查

JS 語法分析、重置

持續整合

品質的 TODO LIST

抽離原則⽂文學編程

團隊活動 觀念及架構 ⾃自動化

整合訊息

看團隊需求來做導入或整合

Prototyping

Page 74: JavaScript Code Quality

寫程式是個良心的事業

過去的評價會⼀一直跟著你,我們應該盡⼒力寫好

Page 75: JavaScript Code Quality

程式碼品質,大師們都會強調再強調

Page 76: JavaScript Code Quality

2b || !2bthat is a question.