# [The Odin Project] ES6 Modules ###### tags: `前端筆記` `TOP` ## 這篇是什麼? 因為 The F2E 的活動需要串接 API,但是串接範例的代碼有用到 npm 的套件,所以就要知道怎麼使用打包工具 Webpack。 剛好在 The Odin Project 中有一篇是介紹相關的概念,依照 The Odin Project 的慣例,每篇文章都有 Knowledge Check ,所以這篇是記錄 [ES6 MODULES](https://www.theodinproject.com/paths/full-stack-javascript/courses/javascript/lessons/es6-modules#npm) 的 Knowledge Check。 ## 1. Describe what npm init does and what package.json is. `npm init` 的指令是建立 `package.json`,而 `package.json` 是一個很重要的 JSON 檔案,因為裡面記錄著 **1)使用了多少套件、2)每個套件的版本是什麼(有時候相同套件會因為版本不同而產生不同的結果)以及 3)讓套件可以再次被其他使用者優化 / 使用,他們可以很快低取得所有套件需要的東西(也就是其他套件)(簡單使用 command line `npm install`)。** ## 2. Know how to install packages using npm. 基本上就是 ```javascript= npm install 套件名稱 --save(如果要拿來打包的套件) npm install 套件名稱 --save-dev (如果該套件只用來寫專案用的 development purposes) ``` ## 3. Describe what a JavaScript module bundler like webpack is. 如果沒有使用 webpack 之類打包工具,又想使用其他套件怎麼辦? 就只能找該套件有沒有釋出 JavaScript 檔案,之後在下載該檔案到本地(還要確保順序)。因為在瀏覽器中每個 JavaScript 檔是共享一個全域物件的(也就是 global),所以每個檔案的變數及方法是可以共用的。 但這樣子會有很多缺點: 1. 每次都要全部下載檔案很麻煩 2. 順序放錯也很麻煩 3. 命名空間被壓縮了(有些變數 / 函式名稱在其他套件已經用了) 4. 每次瀏覽器都得下載很多套件的檔案 假設我今天想要用 [moment.js ](https://momentjs.com/) 這個套件,好不容易找到這個套件的 [JavaScript](https://momentjs.com/downloads/moment.min.js) 然後還要按順序正確性來放 `script` 標籤到 `index.html` ```htmlembedded= <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>JavaScript Example</title> <!-- 套件要先 --> <script src="./moment.min.js"></script> <!-- 自己在後面 --> <script src="./main.js"></script> </head> <body> <h1>Hello from HTML!</h1> </body> </html> ``` `main.js` 裡面才可以用套件的東西。 ```javascript= console.log("Hello from JavaScript!"); console.log(moment().startOf('day').fromNow()); ``` ![](https://i.imgur.com/j36yUMp.png) 但如果我不小心要自己創一個 `moment` 的全域變數,那套件的 `moment` 就 GG 了。 (JavaScript 就會覺得該變數為與值的記憶體連結,所以提早讀取就會報錯,即便我只是想要再創一個相同名稱的變數,但想在宣告以前還是用同樣的名字使用套件的方法) (當然有其他方式可以解,比方來說就用 module pattern 來讓變數私有化) ```javascript= console.log("Hello from JavaScript!"); console.log(moment().startOf('day').fromNow()); const moment = 'hi'; console.log(momnet); ``` ![](https://i.imgur.com/5ezaI6P.png) 當然也可以透過 npm 取得套件的 JavaScript 檔案 1. 先用 `npm init -y` 來創造 `package.json` 2. 可以發現在 `node_modules/moment/min` 裡面有 `moment.min.js` ![](https://i.imgur.com/PrRmmv7.png) 3. 之後再 `index.html` 直接新增 `script` 標籤用對應的路徑取得檔案,這樣子瀏覽器也可以用 ```htmlembedded= <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>JavaScript Example</title> <script src="./node_modules/moment/min/moment.min.js"></script> <script src="./main.js"></script> </head> <body> <h1>Hello from HTML!</h1> </body> </html> ``` ```javascript= console.log("Hello from JavaScript!"); console.log(moment().startOf('day').fromNow()); ``` ![](https://i.imgur.com/HTJ1MsK.png) 但是這樣子也會遇到一些問題: 1. 變數命名的問題,與上面直接下載檔案使用相同 2. 每次都要寫很長的路徑,很麻煩 > 以上兩個方法都是把套件裡面的變數及函式新增到全域物件裡面(在瀏覽器中是 `window`) ## 4. Explain what the concepts “entry” and “output” mean as relates to webpack. 以 webpack 文件中的 `webpack.config.js` 為例: ```javascript= const path = require('path'); module.exports = { // entry point // webpack 會一次打包這個檔案(如果該檔案有 import 其他檔案或者使用 npm 套件的話也會一次打包) entry: './path/to/my/entry/file.js', // output 內有兩個屬性 // ==> path 決定打包完成的檔案位置 // ==> filename 決定打包完成的檔案名稱 // 之後在 index.html 只需要放打包完成的 javascript 檔就可以使用其他套件或檔案了,不需要再手動地在 index.html 插入 script 標籤 output: { path: path.resolve(__dirname, 'dist'), filename: 'my-first-webpack.bundle.js', }, }; ``` ## 5. Briefly explain what a development dependency is. 這個部分我不太瞭解,但我查一下覺得大概是 webpack 開始打包的時候會建立所有要打包檔案的區塊,這些區塊視覺化就會看起來像下圖,之後 webpack 就會把這些區塊打包成一個檔案。 ![](https://i.imgur.com/dS5FmZM.png) ## 6. Explain what “transpiling code” means and how it relates to frontend development. >Transpiling code means converting the code in one language to code in another similar language. This is an important part of frontend development — since browsers are slow to add new features, new languages were created with experimental features that transpile to browser compatible languages. [ref.: Modern JavaScript Explained For Dinosaurs](https://peterxjang.com/blog/modern-javascript-explained-for-dinosaurs.html) >簡單來說 Transpiling code 就是把代碼轉譯成另一個類似的語言。 為什麼需要這個呢? 根據引用的敘述,因為瀏覽器的更新速度太慢了,所以開發者在使用新的語言時(比方來說 TypeScript 或者 SCSS 等等)在開發的時候享受了這些新語言的特性及功能,加速開發速度,但是這些語言瀏覽器並不支援,所以就需要一些轉譯的工具將這些語言轉化成瀏覽器理解的語言(也就是 JavaScript,但有時後也會因為瀏覽器不同及版本問題,有時候一些 ES6 的語法在舊的瀏覽器並不支援)。 ## 7.Briefly describe what a task runner is and how it’s used in frontend development & 8. Describe how to write an npm automation script. 我覺得這兩個應該可以一起談,因為一個其實設定還蠻簡單的。 簡單來說 task runner 就是可以自動處理我們下給它的任務(比方來說每次 JavaScript 如果有更新的話就會重新打包檔案)。 >當然還有其他的 task runner,比如說每次都會把 JavaScript 透過更精簡的寫法壓縮檔案大小等等。 參考的[文章](https://peterxjang.com/blog/modern-javascript-explained-for-dinosaurs.html)有範例代碼: ```javascript= { "name": "modern-javascript-example", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", // 新增的部分開始 "build": "webpack --progress --mode=production", "watch": "webpack --progress --watch" // 新增的部分結束 }, "author": "", "license": "ISC", "dependencies": { "moment": "^2.22.2" }, "devDependencies": { "@babel/core": "^7.0.0", "@babel/preset-env": "^7.0.0", "babel-loader": "^8.0.2", "webpack": "^4.17.1", "webpack-cli": "^3.1.0" } } ``` 上面為 `package.json`,在 `scripts` 中新增兩個屬性 `build` 及 `watch`,就可以省略一些步驟,幫助開發速度。 1. `"build": "webpack --progress --mode=production"` 只要打 command line `npm run build` 就會開始打包,就不用輸入預設的 `npx webpack` 2. `"watch": "webpack --progress --watch"` 只要 JavaScript 有更動的話就會自行執行打包 **為什麼可以直接這樣子做呢?** 因為 Node.js 知道 node_modules 的路徑,所以不用再次寫出全部的路徑了。(Node.js 好強!) ## 9. Explain one of the main benefits of writing code in modules. >要注意 ES6 Modules 跟前篇 Factory Function 及 Module Pattern 是不同的東西。 使用 Module 的話就可以把每個功能區分開來寫,之後再把各個區塊 `import` 到主要的 JavaScript 檔案中,這樣子在大型專案中就可以使得代碼更容易讀取了。 ```javascript= // 一個專門負責計算的 function function add (x, y) { console.log(x + y); } export { add }; ``` ```javascript= //主要的檔案 // 注意要寫絕對路徑 import { add } from './add.js' // 還是可以被叫用! add(1, 3); // 4 ``` 但要記得不能直接用本地執行檔案,原因如下[.ref](https://www.youtube.com/watch?v=pbVlFc0bTt8&t=727s) >如果在主機直接測試JavaScript Module的話會出現CORS 錯誤,這就像利用JS去讓瀏覽器讀取主機的文件時會出現的問題一樣,這是因為瀏覽器是不允許讀取主機的文件的,所以import 讀取文件的方式或機制應該跟script src的不一樣。由於browser不能讀取主機文件,所以本地沒法測試,只能從伺服器入手了,因此,Javascript Module 應該是在放server裹的前端,類似PHP 或 Node等等。 liver server 應該是模擬把文件放到server的環境上。 ## 10. Explain “named exports” and “default exports”. 1. default exprots - 一個檔案內只能出現一次 default export ```javascript= function myName () { console.log('lun'); } export default myName; ``` 2. named exports - 把變數、函式等的名稱放進 `export { }` 之中 ```javascript= const obj1 = {x: 2}; const obj2 = {v: 10}; export { obj1, obj2 }; ``` 3. export inline - 在宣告的時候就在前面新增 `export` ```javascript= export function myName () { console.log('lun'); } ``` ## 參考資料 1. [THE ODIN PROJECT - JavaScript ES6 MODULES](https://www.theodinproject.com/paths/full-stack-javascript/courses/javascript/lessons/es6-modules) 2. [Webpack - Getting Started](https://webpack.js.org/guides/getting-started/) 3. [NPM - Creating a package.json file](https://docs.npmjs.com/creating-a-package-json-file) 4. [完全解析 JavaScript import、export](https://wcc723.github.io/development/2020/03/25/import-export/) 5. [Modern JavaScript Explained For Dinosaurs](https://peterxjang.com/blog/modern-javascript-explained-for-dinosaurs.html) 6. [你懂 JavaScript 嗎?#28 ES? 現在 vs 未來](https://ithelp.ithome.com.tw/articles/10207876) 7. [[ES6] Javascript Import & Export 教學 | Modules | 模組化](https://www.youtube.com/watch?v=pbVlFc0bTt8&t=727s)