# 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`