# [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());
```

但如果我不小心要自己創一個 `moment` 的全域變數,那套件的 `moment` 就 GG 了。
(JavaScript 就會覺得該變數為與值的記憶體連結,所以提早讀取就會報錯,即便我只是想要再創一個相同名稱的變數,但想在宣告以前還是用同樣的名字使用套件的方法)
(當然有其他方式可以解,比方來說就用 module pattern 來讓變數私有化)
```javascript=
console.log("Hello from JavaScript!");
console.log(moment().startOf('day').fromNow());
const moment = 'hi';
console.log(momnet);
```

當然也可以透過 npm 取得套件的 JavaScript 檔案
1. 先用 `npm init -y` 來創造 `package.json`
2. 可以發現在 `node_modules/moment/min` 裡面有 `moment.min.js`

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());
```

但是這樣子也會遇到一些問題:
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 就會把這些區塊打包成一個檔案。

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