June 5, 2017

Frontend Intermediate Course - 作業八

作業八的 GitHub 連結:當我們包在一起:Webpack

作業的說明裡面,胡立老師也清楚的說這次作業比較複雜,所以讓我很擔心無法完成作業,結果居然可以即時完成作業,實在太感動了!(當然也是問了胡立老師很多問題才有辦法完成 XD)

稍微研究了學習資源裡的文章,其實都看得霧煞煞,能確定理解的只有安裝而已 XD 所以想說還是先動手安裝,然後再慢慢解決其他問題,不然一直看文章也沒進展。

前幾次作業我有提到對 command line 有莫名的恐懼感,而 Webpack 許多安裝與操作都要用到 command line,所以只好認真惡補一下,除了重新看過之前胡立老師在 Hahow 上的 初心者的計概與 coding 火球術 之外,另外找到 thenewboston 的 youtube 頻道上的 Windows Command Line Tutorials,增進一些基礎。

這次作業套句胡立老師說的「以往是寫 code 難,但作業八難在不是寫 code,所以難」。其實原本 JavaScript 檔案裡的程式碼變動不多,難在 Webpack 安裝跟環境設定,然後利用 requiremodule.exports 把原本的 script 檔案都打包起來。

為什麼要用 Webpack 這種艱難的問題我還沒有能力回答,也是看了好幾篇文章才大略明白,然後中間夾雜更多沒聽過的東西(像是 Gulp、Browserify、CommonJS、AMD、UMD、ReactJS...等等),看得我超混亂。簡單來說就是要讓 JavaScript 模組化,因為還有很多東西都還沒搞懂,所以細節我也解釋不清楚,Preethi Kasireddy 寫的這兩篇講解的蠻詳細的(第二篇只看一半就放棄了 XD):
黃玄寫的 JavaScript 模块化七日谈 也蠻清楚的,只是我看到中間也放棄了 XD

大概了解為什麼要用 Webpack 之後,就開始這次的作業了。Webpack 需要 Node.js 支援,所以要先安裝 Node.js 跟 npm (Node Package Manager),忘記 Node.js 哪一個版本之後就會同時安裝 npm,否則要另外安裝 npm,建議安裝最新版本的 Node.js。

原本想說直接全域(global)安裝 Webpack 到本機之下比較簡單,後來看了 Webpack 官方教學 裡不建議全域安裝,因為會將你鎖定在某一個版本的 Webpack,如果專案採用別的版本就會產生問題。後來把 Webpack 安裝在作業的專案資料夾底下,想要把一開始的全域安裝 Webpack 解除安裝,找到這篇有教學解除全域安裝的項目:Uninstalling Global Packages

全域安裝:
npm install webpack -g

全域解除安裝:
//check folder
npm ls -g --depth=0
//uninstall
npm uninstall -g webpack


以上的前置作業以及一些廢話之後,開始來寫作業了。

1. 開啟 cmd.exe 之後,cd 切換到專案根目錄,然後安裝 Webpack。以下的安裝跟操作都是在專案根目錄下。
npm install --save-dev webpack

2. 創建 package.json,用來記錄專案資料,像是專案名稱、版本、作者、套件資訊等等。後面加 -y 可以不用填寫專案資料,之後再編輯也可以。
npm init -y

3. 執行 Webpack。如果使用 Mac 或是安裝在全域環境下可以參考官方教學:Webpack Getting Started
.\node_modules\.bin\webpack --help // for windows user and not installed globally
檢查專案資料夾內的 node_modules 資料夾下,如果有 Webpack 資料夾的話表示安裝成功。

4. 安裝 jQuery(參考 npm-jquery),後面加 --save 才會把 jQuery 版本資料新增到 package.json 裡面。
npm install jquery --save

5. 安裝 json-loader。
npm install json-loader --save

6. 建立設定檔 webpack.config.js。我先塞一個內容之後才在 Sublime 上編輯。
// creat webpack.config.js with content "test"
echo test > webpack.config.js

7. 在 Sublime 新增兩個 json 語言檔、constants.js、i18n.js 跟 utils.js。資料夾跟檔案結構參考胡立老師的這篇:我也想要模組化開發:Webpack。然後在 index.js 中 require 引入 utils.js 跟 constants.js。

1) 兩個 json 語言檔:
// lang-zh-tw.json
{
  "title": "用中文直播的頻道"
}

// lang-en.json
{
  "title": "The Streams in English"
}

2) constants.js 檔:
module.exports = {
    
  LOCAL_ID: {
    TITLE: 'title'
  }
}
原本不知道為什麼要加這個變數檔案,胡立老師的文章說明是說,json 語言檔案內的 key 是字串,這樣另外用大寫變數儲存 json 語言檔案內的 key,可以用 constants.Local_ID.TITLE 代表 key,同時避免在編譯時沒發現打錯字,到執行時才會發現。如果 constants.Local_ID.TITLE 打錯成 constants.Local_ID.TOTLE,有些幫忙檢查語法的程式就會告訴你找不到這個變數,就可以即使更改,不會到執行才發現出錯。

3) i18n.js 檔(把兩個語言檔引入):
module.exports = {
  "en": require('./i18n/lang-en.json'),
  "zh-tw": require('./i18n/lang-zh-tw.json')
}

4) utils.js 檔:
var i18n = require('./i18n.js');

function getLocalString(id, region) {
  if(!region || !i18n[region]) {
    region = 'zh-tw';
  }

  return i18n[region][id];
}

module.exports = {
  getLocalString
}
參考胡立老師的文章時做作業時,出現了 Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object> 錯誤。

卡住了一陣子,後來才知道是因為 Webpack 2.2.x 之後的版本不可以將 importmodule.exports 放在同一檔案內(參考文章:Webpack@^2.2.x: Uncaught TypeError: Cannot assign to read only property 'exports' of object '#<Object>)。
import i18n from './i18n'; 改成用 var i18n = require('./i18n.js');。後來回報給胡立老師,他說當時他是用 Webpack 1,所以是版本不同造成的 bug。

5) 在 index.js 中 require 引入 utils.js 跟 constants.js:
var utils = require('./utils.js') ;
var constants = require('./constants.js') ;
...

修改之前作業用到 window.i18n 的部分:
$('.title').text(window.I18N[lang].TITLE);

改成:
$('.title').text(utils.getLocalString(constants.LOCAL_ID.TITLE, lang));

8. 編輯 webpack.config.js 檔:
var webpack = require('webpack');

module.exports = {
  entry: './src/js/index.js',
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  },
  module: {
    loaders: [
      { 
        test: /\.json$/, 
        loader: 'json-loader' // npm install json-loader
      }
    ]
  },

  plugins: [
        new webpack.ProvidePlugin({
           $: "jquery",
           jQuery: "jquery" // npm install jquery
       })
    ]
}
專案資料夾的架構確定之後,才可以確定 entryoutput 的路徑。dist 資料夾是輸出最後的 bundle.js 的地方,等一下執行 Webpack 後自動產生的檔案。

這裡也因為參考胡立老師的文章而出現錯誤,目前版本的 Webpack 必須打 loader: 'json-loader' 才可以,文章內是舊版本所以用 loader: 'json'

還有一點想要特別提出的是,jQuery 我是利用 Webpack plugin 直接在 webpack.config.js 檔裡面定義 jQuery $,所以就沒有在 index.js 裡定義 $(參考 Stackoverflow 文章:Webpack Using Bootstrap jQuery Is Not Defined)。

9. 在 HTML 檔案裡,把之前的 script 檔全部取消改成:
<script type="text/javascript" src="./dist/bundle.js"></script>

10. 最後只要在 cmd.exe 專案根目錄下執行 Webpack,就會自動把所有的 script 檔都打包成 bundle.js 一個檔案。
.\node_modules\.bin\webpack // for windows user and not installed globally

成功的話,在瀏覽器上跑網頁就會如同上一個作業一模一樣,表面上看起來一樣,但後面做了很多手腳 XD

如果之後在任何的 script 檔案(或是 json 檔)裡面有修改,都要再次執行 Webpack(步驟 10),這樣才能更新 bundle.js 的資料。一開始我不知道,所以只在 Sublime 存檔但網頁都沒有變動 XD

要交出作業前,問了胡立老師有點笨的問題(反正我也蠻常問笨問題的 XD),我看其他同學上傳到 GitHub 的作業都沒附上 node_module 那個資料夾,我問為什麼不需要附上那個資料夾?原因是執行 Webpack 之後打包出來的 bundle.js 內已經有專案內需要的所有東西,加上 package.json 那個檔案裡面有每個專案的套件資料,而 node_module 那個資料夾通常很大,所以沒有必要附上 node_module 資料夾到 GitHub 上。

感謝胡立老師每次都耐心回答我的問題,這次也是學習很多,雖然從網頁上根本看不出變化 XD


因為這次作業無法放在 CodePen 上,所以放上我這次作業的 GitHub 跟 GitHub Page。

這只是我寫作業過程的筆記,沒有詳細解釋 Webpack 的其他功能,可能也有一些錯誤,如果有錯還請指正。


No comments:

Post a Comment