---
# System prepended metadata

title: 0503 JavaScript 的模組化以及 NodeJS
tags: [JavaScript]

---

# 0503 JavaScript 的模組化以及 NodeJS
###### tags: `JavaScript`

- [西瓜投影片連結](https://hackmd.io/@pastleo/S1k-eozDu#/)
- [隨堂練習包](https://ppt.cc/fQJ6lx)

## JavaScript module / ES module
### 使用 module(模組化) 的原因
- 解決 JavaScript 檔案過大問題
  - 例：[過大的檔案](https://github.com/pastleo/besg_archive/blob/master/besg/data/js/contest.js)
  - 過大造成維護性低，要找特定功能修改會花很多時間
  - 拆解成很多檔案之後維護性較高
  - 如果有重複使用的功能可直接做引用
- 傳統 JavaScript 沒有內建取得別的 JavaScript 檔案的功能
  - 產生很多工具
  - [browserify](http://browserify.org/)
  - [rails assets pipeline](https://guides.rubyonrails.org/v3.2/asset_pipeline.html#asset-organization)
  - [gulp.js](https://gulpjs.com/)
  - [rollup.js](https://rollupjs.org/)
  - [webpack](https://webpack.js.org/)


## 模組化
### commonJS
- 模組化寫法 – commonJS （CJS）
  - NodeJS 的引入方式
    - 其實瀏覽器看不懂 require 這個指令
    - 在沒有特定設定下，NodeJS 只看得懂 require
- 還是有很多人在用
```javascript=
pkg = require('pkg_name')
// require 非瀏覽器內建
module.export = ...
```


### ES module
  - ECMA Script 歐洲電腦製造商協會
    - 專門制定規則
    - 讓瀏覽器廠商提供標準功能
    - 用規格書制定 JavaScript 所需的功能
  - 現代化的標準
    - 之前可能都亂寫的，現在理論上要朝公告的規則走
  - [V8 dev: JavaScript modules](https://v8.dev/features/modules)
  - [MDN 文件](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)
  - `import` / `export` 關鍵字



### 練習 10_type-module
 - 用 ES module 改寫

* 如果要用 ES module 的話要在 html 檔案引入時加上 attribute: `type="module"`
  * 用以避免使用舊規定建置的網站出現問題
  * 因為 module 對於舊的傳統 JS 有破壞性影響
```javascript=
// ↓需要開發者指名這是 ES mudule，所以補上type
<script type="module"></script>
export default moduleDefault  // 可以在最下面加上 或 直接在宣告 function 時寫上
```
- [MDN - export](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Statements/export#%E8%AA%9E%E6%B3%95)
- 預設輸出，==只能有一個==
- 有名輸出
```javascript=
import moduleDefault from './another.js'
```
* [MDN - import](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Statements/import#%E8%AA%9E%E6%B3%95)
* 路徑設定上一定要用`'./'`或`'../'` 及副檔名指定明確路徑
* 拿取 ==預設輸出== 不用使用指定的名字
* 拿取 ==有名輸出== 時要使用輸出時指定的名字
* `import * as 想呼叫的名字 from 路徑` 
  * 把路徑下所有有名輸出都引入
  * 使用時要用name.(路徑內的function)


- 在瀏覽器上不使用 ES module 的狀況
  - 直接 `<script />` 引入，定義到 `window` 上
  - [AMD](https://requirejs.org/docs/whyamd.html) / [UMD](https://github.com/umdjs/umd)


## NodeJS
- 在瀏覽器外面的 JavaScript
- Runtime 的一種
- 可以取用系統 API
  - 系統API 
    - terminal (tty) 
    - net http
    - 擁有較高的 file system 權限
- 目前最常被拿來處理前端 assets


### NodeJS interactive shell
- 就打指令 `node`
- 等同於瀏覽器的 `Console`
- 沒有 `window`，取而代之的是 `global`
    - 沒有 BOM / DOM API
    - `console`, `JSON`, `setInterval`…等工具跟瀏覽器是一樣的
- 使用 node 查看 JSON 檔案
  - 但實務上會使用網路上的 JSON Formatter & Validator 或丟入瀏覽器從開發者工具看

### 練習 11_node-pretty-json
- 有個 JSON 檔案：`posts.json`
  - 裡面有一堆東西比較難查找資料
- `$ node pretty-print-json.js posts.json`
  - 這一行指令就是一個 process
  - process 裡面會有叫 argv 的陣列 => 就是你丟進去的參數
  - argv => argument value
  - 以上方為例：
    - process.argv[0] = node
    - process.argv[1] = pretty-print-json.js
    - process.argv[2] = posts.json
- [NodeJS process API](https://nodejs.org/api/process.html)
- [NodeJS fs API](https://nodejs.org/docs/latest/api/fs.html)
  - [fsPromises.readFile\(path\[, options\]\)](https://nodejs.org/docs/latest/api/fs.html#fs_fspromises_readfile_path_options)
- [stackoverflow: 如何讓 JSON 比較好閱讀？](https://stackoverflow.com/questions/5670752/how-can-i-pretty-print-json-using-node-js)
    - [JSON.stringify](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)

```javascript=
const fs = require('fs').promises

const filename = process.argv[2];
if (!filename) {
  console.error("Please provide json filename");
  process.exit(127)
}

async function main(jsonFilename) {
  const jsonContent = await fs.readFile(jsonFilename, {encoding: 'utf8'})
  json = JSON.parse(jsonContent)
  process.stdout.write(JSON.stringify(json, null, 2))
}

main(filename);
```

## NPM
- NodeJS package manager
  - 有點像 ruby 的 gem 跟 bundle 加在一起
- 相關指令
  - npm init
    - 建立 nodeJS 專案
  - npm install ...
  - npm install -g ...
    - 全域安裝
    - 例: `$ npm install -g http-server`
    - [npm http-server](https://www.npmjs.com/package/http-server)
- package.json
  - 執行 npm init 後產生
  - 管理專案的版本... 等等資訊
- nvm → 管理 nodeJS 版本的工具

## yarn
- 提供的功能跟 npm 是一樣的
- 當初 facebook 嫌 npm 爛所以自己做了這個
  - 因為當初沒有設計 `package-lock.json` 所以會有版本問題
- `yarn.lock` ~= 約等於 `package-lock.json`
  - 功能相同，但一個是給 yarn 用的，一個是給 npm 用的
- 同一個專案下不要跟 `npm` 混用



### 練習 12_node-project-json-to-csv
- 把 JSON 轉換成 [CSV](https://en.wikipedia.org/wiki/Comma-separated_values)
- 建立 NodeJS 專案: 
  - `npm init`
- 安裝套件
  - `npm install --save fast-csv`
  - `--save` 讓套件被放進整個專案的 dependency (在 package.json 裡面)
- [fastcsv](https://c2fo.io/fast-csv/docs/introduction/example#formatting
)
#### 安裝後產生出來的檔案
- `package-lock.json`
  - 把使用的套件版本鎖定或做特別設定
- `node_modules`
  - 不會被 git commit 包含到，有需要的話可以用 package.json 裡面的敘述安裝
  - 屬於外部內容，所以不放入
- `package.json`
  - `"scripts": { "main": "node main.js posts.json posts.csv" }`
  - 設立捷徑，在 terminal 輸入時就可以少打一點字
    - 每次要呼叫的時候都要在前面加 `npm run`
    - 例：`$ npm run main`


## Webpack
- 前端 Assets 打包工具
- 目前主流的前端 bundle 工具
  - rails 的 [webpacker](https://github.com/rails/webpacker) 就是這個的橋接
- [官方連結](https://webpack.js.org/)
- 使用的原因：
  - 讓瀏覽器讀 `node_modules` 不是容易的事
  - 讀取效能最佳化，例如壓縮、合併成一個檔案
- [ES Module 的相容性](https://caniuse.com/#search=modules)
- [plugins](https://webpack.js.org/plugins/) / [loaders](https://webpack.js.org/concepts/loaders/) 非常多 (今天只會帶最基本的部分)

### 練習 13_hello-webpack
#### 用 webpack 包成一包
- [官方 Getting started 文件](https://webpack.js.org/guides/getting-started/)
    - [建立專案、安裝 webpack](https://webpack.js.org/guides/getting-started/#basic-setup)
      - 指令後面加 `-y` 代表最後會跳過 OK 嗎 的問題，直接回答 yes
    - [`webpack.config.js`](https://webpack.js.org/guides/getting-started/#using-a-configuration)
      - npx: Executes `<command>` either from a local `node_modules/.bin`, or from a central cache, installing any packages needed in order for `<command>` to run.
      - 把指令放進 script 裡面的時候不用放 npx (系統會自帶這個功能)
      - 執行 `npm run 簡寫` 就可以
    - [`npm run bundle`](https://webpack.js.org/guides/getting-started/#npm-scripts)
#### 加入 [typed.js](https://github.com/mattboldt/typed.js) 打字效果
- 安裝、引入 npm 套件，並透過 webpack 打包給瀏覽器
    - `import Typed from 'typed.js';`
- `new Typed(textSpan, { strings: [ ... ] })`
- 用打字效果顯示 `Loading...`
- 延遲完成後，用打字效果顯示 `Completed!` 以及日期
- [打完](https://github.com/mattboldt/typed.js#customization)日期時移除 cursor

## Babel
- [官網連結](https://babeljs.io/)
- frontend assets
- 轉換 JavaScript 語法
  - 提高相容性
  - 原始碼裡寫新的語法
- 與 webpack 的整合：[babel-loader](https://webpack.js.org/loaders/babel-loader/)

## ESLint
- [官方連結](https://eslint.org/)
- JavaScript 語法檢查工具
- 為什麼需要語法檢查？
  - 強迫團隊使用 best practice
    - 避免錯誤，例如 [`eqeqeq`](https://eslint.org/docs/rules/eqeqeq)
  - 維護程式碼品質，例如[程式碼縮排](https://eslint.org/docs/rules/indent)
  - 讓程式碼風格統一
- 通常會在 CI 流程裡面檢查
  - [circle CI](https://circleci.com/)
  - [GitHub Action](https://github.com/features/actions)

### 練習 14_eslint
- [官方 Getting started 文件](https://eslint.org/docs/user-guide/getting-started)
    - `npx eslint --init`
      - 問一大堆問題，幫你設定好、裝好
    - `enforce code style`, `standard`
- 設定檔: `.eslintrc.js`
    - 規定要有分號：[semi](https://eslint.org/docs/rules/semi)
- 自動修正: `--fix`
- `npm run lint`



