2017年5月21日 星期日

Typescript + Webpack (or SystemJs) 練習

今天要來練習使用Typescript編寫Javascript,順便紀錄下如果JS Module的使用方式。

Typescript可以讓我們編寫的 ts 檔編譯成 js 檔,而如果有在 ts 檔裡撰寫Module的語法,
例如export, import等語句,Typescript可以依我們指定的module spec 來將 ts 檔編譯成不同
寫法的 js 檔,而可提供的有例如 amd, system, commonjs 等。

這裡會練習以下項目;

模組化 JS 執行工具:
SystemJs是一個可以執行JS Module的工具,
支援許多module spec,目前有
esm (ECMAScript Module),
cjs (CommonJS),
amd (Asynchronous Module Definition),
global (Global shim module format),
system (System.register or System.registerDynamic compatibility module format),
可以使用在node.js及Web應用中,在Web應用中,
可以在頁面上真接用 <script src="XXX/system.js"></script> 引入,
然後直接寫Javascript code 並指定程式進入點即可。

Webpack是一個功能強大的模組化打包工具,可以將多個資源(JS, CSS, images...)打包成一個檔案,可以解析例如commonjs的module spec,主要以命令列的方式使用,也可搭配其他外掛使用。

在這邊我們會先練習利用Typescript編譯 ts 檔,並且練習兩種不同的 module spec ,
system 和 commonjs, Typescript 對不同的 module spec 編譯出不同的 js 檔,
然後
system 我們會利用 SystemJs 來執行模組化的 js。
commonjs 我們會利用 Webpack 來打包編譯好的 js 檔成一個可執行的模組化 js。

Typescript IDE :
在這邊,可以準備一個有 Typescript 功能的 IDE,例如在這邊我使用 Visual Studio Code ,它有不錯的 Typescript 提供檢查功能及許多外掛,並且本身乾淨可以當一般的編輯器使用,當然也可以根據開發環境使用想應的工具外掛,例如 Netbeans 及 Eclipse (可以參考"Angular2簡易安裝使用 - Eclipse + Typescript plugin + System.js") 也有人提供Typescript外掛,享受使用 Typescript 的好處。
** Note **
Visual Studio Code 如果看不到JS、Typescript的提示文字,可以用"系統管理員"的方式開啟,應該就會出來了。

Typescript 的編譯:
在這邊為了了解最源頭的運作,我選擇使用最單純的方式,用 npm 安裝了 Typescript 後,直接用 tsc 指令配合 tsconfig.json 來執行編譯,許多開發環境的外掛,例如 Visual Studio Code 自己就有編譯Typescript的能力,不過其實最底層也都基本是呼叫 tsc 指令來完成工作。

簡易Server:
這邊使用了 lite-server 來在專案當下目錄建立簡易Server,因為Module JS的寫法會用到Ajax的呼叫,所以需要建立Server,當然要用其他例如webpack-dev-server、Netbeans, Eclipse 配合 Tomcat 之類的也是OK。

=========================================

首先,請先安裝 node.js (自動有 npm),
在全局安裝 Typescript、Webpack
npm install typescript -g
npm install webpack -g




在專案下安裝SystemJS (當然你也可以直接去官網下載JS檔之類)
npm install systemjs --save
使用npm安裝專案下的檔案時不要忘了建立package.json,沒有請用npm init創建一個

專案一開始的結構為如下:

下面依依解釋:

  1. build 資料夾
    用來練習Webpack,之後會用Webpack手包檔案並放到這。
  2. js 資料夾
    用來練習 Typescript,之後會用 Typescript 編譯 ts 檔成 js 檔並放到這。
  3. node_modules資料夾
    npm 安裝的套件工具等放的地方,例如systemjs用npm局域安裝好了以後會放到這。
  4. ts 資料夾
    練習寫 ts 檔的地方,等下要撰寫 Greeter.ts 和 test.ts 。
  5. index.html
    這裡主要也是唯一個網頁進入點。
  6. package.json
    npm 的設定檔。
  7. tsconfig.json
    Typescript 的設定檔。
  8. webpack.config.js
    Webpack 的設定檔。

練習 Webpack:
  1. 首先先撰寫 Greeter.ts :
    export class Greeter{
        name : string;
        constructor(name :string){
            this.name = name;
        }
        greet(){
            console.log("Hello, " + this.name);        
        }
    }
    其中以Typescrpt建立了一個Class,有一個string型別的name屬性,一個必須送入一個string的constructor,和一個會在console印出歡迎字串的方法,greet()。
    並且用 export 的module語句讓其他的 js 可以 import 這個Class來用。
  2. 再來撰寫 test.ts :
    import {Greeter} from "./Greeter.js";
    
    let name : string = "Hugo";
    let greeter : Greeter = new Greeter(name);
    greeter.greet();
    其中使用 import 來引入 greeter.js export 出去的 Greeter 這個 Class ,建立一個Greeter的實例,然後呼叫greet(),預其應該要console裡印出 "Hello, Hugo"
  3. 撰寫tsconfig.json
    {
        "compilerOptions": {
            "outDir": "./js",
            "module": "commonjs",
            "noImplicitAny": true,
            "removeComments": true,
            "preserveConstEnums": true,
            "sourceMap": true,
            "watch" : true
        },
        "include": [
            "./**/*.ts"
        ],
        "exclude": [
            "./node_modules/**.ts"
        ]
    }
    tsconfig.json 是 Typescript的設定檔,如果沒有這個檔的話,也是可以在命令列指令(Command-line)中執接加上參數執行tsc,但是如果有tsconfig.json的話,我們就可以把參數設定寫在裡面,只要在tsconfig.json所在的資料夾位置執行命令列指令
    tsc
    Typescript就會去讀取設定執行了。
    其中, outDir 設定了 ts 檔編譯成 js 檔後 js 要放的位置。
    module 先選擇 commonjs,因為要用 webpack練習。 使用 system.js 時可以換成 system。
    watch 設定成 true 時,執行 tsc 後會監看 ts 檔的變動,如果檔案有改就會自動進行編譯。
    include 設定了從哪裡可以找到要被編譯的 ts 檔,使用了include設定時請設定exclude來表示不想要編譯哪些路徑下的檔。
  4. 以上都完成了以後,就可以在命令列視窗中,到專案所在的位置,執行指令(Visual Studio Code 有內建命令列視窗(終端機)不錯用,直接用電腦的也行)
    tsc
    應該就可以在 js 資料夾看到被編譯好的 js 檔了,其中因為我們在 tsconfig.json 設定了sourceMap為true,所以還有map檔也被產生了出來,如下圖:
    這時可以用命令列視窗到 js 資料夾中的路徑中,執行
    node test.js
    應該就會看到以下輸出了
    Hello, Hugo
  5. 接下來要使用 Webpack 將 test.js 及它有用到的module (例如此例是 Greeter.js) 打包成 一個 index.js 放在 build 資料夾裡,webpack跟typescript一樣可以利用設定檔來簡化指令的執行,先撰寫設定檔 webpack.config.js
    module.exports = {
        entry: "./js/test.js",
        output: {
            path: __dirname + "/build",
            filename: "index.js"
        },
        watch: true
    }
    其中 entry 為要打包的程式(可以不止只一個,此例只有一個),output 為打包好的檔案放置位置, path 為路徑 (__dirname為node.js的變數,為當下所在位置),filename為檔名,watch表示監看檔案(此例是test.js)有變動就會再次打包。
    在命列視窗打上以下指令即可在 build資料夾中輸出index.js
    webpack --progress --color
    只有webpack指令即可,之後的都是非必要參數,--progress為顯示進度條、 --color 為標亮訊息。
  6. 這時應該會在build資料夾中出現被打包好的index.js,如下圖
  7. 現在就可以在index.html中引入build/index.js了:
    <!DOCTYPE html>
    <html>
    
    <head>
        <title>TODO supply a title</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
        <!-- For Webpack -->
        <script src="build/index.js"></script>
    
        <!-- For SystemJS -->
        <!--
        <script src="node_modules/systemjs/dist/system.js"></script>
        <script>
            System.config({
                packages: {
                    "js": {
                        "main": "./test.js",
                        "defaultExtension": "js"
                    }
                },
            });
            SystemJS.import('js').catch(function (err) {
                console.error(err);
            });;
        </script>
        -->
    </head>
    
    <body>
        Please press F12 to open developer console.
    </body>
    
    </html>
    打開網頁,按下F12打開Developer Console,應該就可以看到印出 Hello, Hugo 的字串了
練習 SystemJs:

  1. 事實上 CommonJS 的 Module 標淮 SystemJS 也支持,所以我們只要把index.html改成如下就一樣可以成功了,不過要注意一點的是,因為不像Webpack會把所有的 JS 打包成一個檔,SystemJS 在要使用Module時會使用 Ajax 去索取要用到的 JS ,所以必須要建立一個伺服器,不然會有 XMLHttpRequest cannot load 等的錯誤。
    <!DOCTYPE html>
    <html>
    
    <head>
        <title>TODO supply a title</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
        <!-- For Webpack -->
        <!-- <script src="build/index.js"></script> -->
    
        <!-- For SystemJS -->
        <script src="node_modules/systemjs/dist/system.js"></script>
        <script>
            System.config({
                packages: {
                    "js": {
                        "main": "./test.js",
                        "defaultExtension": "js"
                    }
                },
            });
            SystemJS.import('js').catch(function (err) {
                console.error(err);
            });;
        </script>
        
    </head>
    
    <body>
        Please press F12 to open developer console.
    </body>
    
    </html>
    伺服器可以使用webpack-dev-server、 Netbeans, Eclipse 等設定 Tomcat 之類,只要能開啟伺服器就好,這邊介紹使用一個簡單的伺服器工具 lite-server,可以在當下路徑臨時開啟一個簡易伺服器,可以執行以下指令全局安裝:
    npm install lite-server -g
    安裝好以後,在命令列視窗至專案所在路徑執行
    lite-server
    就可以開啟一個預設port為3000的Server了
  2. 不過SystemJS也可以支持 system 的 Module標準,我們試著把 tsconfig.json 的 module 改成system,
    再用Typescript編譯一次,可以發現在網頁中的Developer console還是可以看到歡迎詞的正常輸出,不過如果使用Webpack的方式的話,就會發現有錯誤訊息了。
原始碼下載:
typescript_webpack_systemjs_test.7z

參考資料:

  1. Angular2 - 與 Webpack 整合環境設定 - 1
  2. webpack
  3. systemjs
  4. Watch 和 WatchOptions



1 則留言 :

  1. 讀了您的文章對於SystemJs有了初步的理解,
    謝謝分享 :)

    回覆刪除