---
# System prepended metadata

title: Node NPM Webpack
tags: [study]

---

# Node NPM Webpack
## Nodejs起手式
- 安裝node
- 檢查版本 `node -v`
- 開新專案 `mkdir project-name`
- 到新專案 `cd project-name`
- **初始化專案** 才能用npm, `npm init`, 加`-f`表示快速建立

### Nodejs是什麼
- Node.js 是一個讓 JavaScript 運行在服務端的開發平台。既是語言也是平台。等同於PHP, Python
- 不運行在瀏覽器中，所以不需要使用瀏覽器中的許多特性, i.e. DOM, BOM
- 讓JavaScript 運行在瀏覽器之外的平台。它實現了諸如文件系統、模塊、Package、操作系統 API、網絡通信等
- 使用Javascript V8引擎, 也是Chrome使用的版本
- 提供`exports` 和 `require` 兩個對象，`exports` 是module公開的接口，`require` 是import外部module
- 使用`require`調用module
```=javascript
var http = require('http')
```

### 核心模塊
在瀏覽器 JavaScript 中，通常 window 是全局對象， 而 Node.js 中的全局對像是 global，所有全局變量（除了 global 本身以外）都是 global 對象的屬性. 
要調用node中參數的話, 只能在自己模組中使用

<img src="https://i.imgur.com/2LbTa6p.png" alt="drawing" width="400"/>

<img src="https://i.imgur.com/rho8Ry5.png" alt="drawing" width="400"/>

### 預設 import 路徑
如果require參數不以“ / ”、“ ./ ”或“ ../ ”開頭，而該模塊又不是核心模塊，那麼會通過查找 node_modules 加載模塊
```=js
var exs = require('express');

// 等於
var exs = require('./node_modules/express');
```

當require 內容不是核心模組, 又不是以路徑形式表示, nodejs會試著根據當下目錄的node_module找, 如果沒有就在往上找, 重複動作直到找到為止
假設要從 `/home/byvoid/develop/foo.js` 中使用 `require('bar.js')`,會從下列目錄依序查找
- /home/byvoid/develop/node_modules/bar.js
- /home/byvoid/node_modules/bar.js
- /home/node_modules/bar.js
- /node_modules/bar.js 

### 加載緩存
Nodejs是根據實際文件名稱緩存, 不是require提供的參數, 所以加載過**不會被重複加載**

- 加載順序 require('xxxModule');
1. 如果some_module 是一個==核心module==，直接加載，結束
2. 如果some_module以“ / ”、“ ./ ”或“ ../ ”開頭，==按路徑加載 some_module==，結束
3. 假設當前目錄為 current_dir，按路徑加載 current_dir/node_modules/some_module
    - 如果加載成功，結束
    - 如果加載失敗，令current_dir為其父目錄
    - 重複這一過程，直到遇到根目錄，拋出異常，結束
### async 模組
控制流程的module,提供async相關的許多功能,大約20組, 可在瀏覽器中使用
異步流程控制模式包括，串行(series)，倂行(parallel)，瀑布(waterfall)等。
```
npm install async
```
### http 模組
Node.js 雖然提供了 http 模塊，卻不是讓你直接用這個模塊進行 Web 開發的。 http 模塊僅僅是一個 HTTP 服務器內核的封裝，你可以用它做任何 HTTP 服務器能做的事情，不僅僅是做一個網站，甚至實現一個 HTTP 代理服務器都行。

為了提升開發效率，誕生了`Express框架`, 是Nodejs官方推薦使用的輕量級`Web框架`
Express除了為 http 模塊提供了更高層的接口外，還實現了許多功能，其中包括：
- 路由控制
- 模板解析支持
- 動態視圖
- 用戶會話
- CSRF 保護
- 靜態文件服務
- 錯誤控制器
- 訪問日誌
- 緩存
- 插件支持

## Express.js
- install
```
npm install -g express
```
預設support模板引擎`Jade`, `ejs`
- 建立網站基本架構
```
express -t ejs microblog

// 自動安裝dependencies ejs & express
cd microblog && npm install
```
- 啟服務
```
node app.js
http://localhost:3000
```
- import express到專案
```=js
var express = require('express');

var app = express.createServer();
app.use(express.bodyParser());
app.all('/', function(req, res) {
 res.send(req.body.title + req.body.text);
});
app.listen(3000);
```

- 實做訪問Log和Error Log
app.js
```=js
var fs = require('fs');
var accessLogfile = fs.createWriteStream('access.log', {flags: 'a'});
var errorLogfile = fs.createWriteStream('error.log', {flags: 'a'}); 
```
app.configure
```=js
app.use(express.logger({stream: accessLogfile})); 

// error log 儲存記錄單獨實做
app.configure('production', function(){
    app.error(function (err, req, res, next) {
        var meta = '[' + new Date() + '] ' + req.url + '\n';
        errorLogfile.write(meta + err.stack + '\n');
        next();
    });
});
```
### Router
- Express 在處理路由規則時，會優先匹配先定義的路由規則，因此後面相同的規則被屏蔽
> 後面會被前面取代

因為上面的規則, express提供第三個參數做調用 `next()`, 會將路由控制權轉移給後面的規則
```=js
app.all('/user/:username', function(req, res, next) {
 console.log('all methods captured');
 next();
});
app.get('/user/:username', function(req, res) {
 res.send('user: ' + req.params.username);
}); 
```
同時, 因為有`next()`,可以把錯誤檢查分段化,降低程式耦合

### Template Engine 模板引擎
- 是一個從頁面模板根據一定的規則生成 HTML 的工具
- 按照這種模式，整個網站就由一個個的頁面模板組成，所有的邏輯都嵌入在模板中
- 可以RUN在server and client端, 大多是在server端直接parse to html, 解析完成後在傳回client端
- 模板引擎 javascript語言常用`ejs`撰寫

### EJS Embedded JavaScript
只有三種標籤語法
```
1. <% code %>：JavaScript 代碼代碼
2. <%= code %>：顯示替换过 HTML 特殊字符的内容
3. <%- code %>：顯示原始 HTML 内容
```

## NoSQL
![](https://i.imgur.com/IyNuxt5.png)

特點
1. NoSQL的數據之間無關係，容易擴展
2. NoSQL具有非常高的讀寫能力
3. NoSQL無需事先為要存儲的數據建立字段，可以隨時自定義數據格式
4. NoSQL在不太影響性能的情況就可以方便的實現高可用的結構。比如Cassandra，HBase模型，通過複製模型也能實現高可用。

[Ref](https://www.ithome.com.tw/news/92507)

## npm 常用套件指令
```
// 安裝library
npm install 套件名稱

// 全域是指整個作業系統,非限定專案內
-g

// 安裝在全域的特定版本
npm install -g [package]@[version]

// 套件加入 dependencies
npm install 套件名稱 --save

// 只有dev環境用到的dependencies
npm install 套件名稱 --save-dev

// 移除lib
npm uninstall 套件名稱

// 更新全域套件
npm update -g

// 查詢lib
npm serach lib

// 查詢lib detail
npm view lib
```

## npm 執行常用指定
```
"scripts": {
  "start": "node app.js",
  "build": "node build.js"
}
```
```
// 執行js檔案
node xxx.js

// 跑package.json script 的**start**執行檔案
npm start

// 跑package.json script 的**build** deploy檔案
npm run build
```

## Webpack
前端壓縮的打包工具, 大多React, Vue會用到或是SPA架構, Angular有自己的cli
[官網](https://webpack.js.org/)

### init
`-y` 可以快速產生
`--save-dev` 安裝在開發環境
```
npm init -y

npm install webpack webpack-cli --save-dev
```

### 進入點(entry),輸出點(output)
- 進入點是從`src`，輸出點是從`dist`
- 預設build的指令是`webpack`, 所以下這個指令時,等同於`build: "webpack"`
```
 "scripts": {
    "build": "webpack"
  },
```
```
npm run build
```
- 那預設的webpack會把`src`的`index.js`, build成`main.js`或`boundle.js`輸出到`dist`資料夾
- 輸入輸出檔案名稱可以修改客製

### webpack.config.js 客製檔案輸入輸出位置和檔案
- 新增config.js
- 同層目錄就`./`

修改進入輸出位置和檔名
- entry: `./src/index.js`
- output: `bundle.js`
- `path.resolve(__dirname, 'dist')` 
表示輸出到根目錄的`dist`資料夾, 也解決資料夾跨系統的問題
```=javascript
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },
};
```

## npm script
在package.json的script中可以客製指定, 一次執行多個檔案或是單一檔案都可以, 以此為例則是新增`hello`的指令
```=javacript
// 在package.json的script位置
"script": {
    "hello": "node hello.js"
}
```
所以下`npm run hello`, 等同於下`node hello.js`的指令

### dev, prod mode切換
```=javascript
{
  "name": "webpack-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && nexit 1",
    "dev": "webpack --mode development",
    "deploy": "webpack --mode production"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^5.49.0",
    "webpack-cli": "^4.7.2"
  }
}
```
執行`npm run dev` 等於跑`"dev": "webpack --mode development"`
dev環境打包的`dist/boundle.js`不會有壓縮換行等等,以便於debug

執行`npm run deploy`等於跑`"deploy": "webpack --mode production"`
上線才用deploy看, 打包會壓縮程式碼一行一行的,以節省容量大小提升效能

也可以把mode寫在`webpack.config.js`裡面,但通常不會這樣寫,因為會一直改config檔,用指令檔分模式會比較有效率
```=javascript
const path = require('path');
module.exports = {
  // 加在這邊
  mode: 'production'
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
  },
};
```

## export, import語法
javascript模組化又稱ES Module

![](https://i.imgur.com/brSaTKF.png)

### example
![](https://i.imgur.com/We3e1aJ.png)
- import 寫法
```=javascript
import data from './export1.js';
```
- export 寫法 
```=javascript
// export default ==只會匯出一個, 一個js檔案只會有一個==, 可以是物件或是值
let a = 3;
export default a;

// 或是
export default 3;

// 或是
export default {
  v: 6,
  content: 'txt'
}
```
### example2
![](https://i.imgur.com/U59XWdK.png)
- import 寫法
```=javascript
// 可以一個或多各
import { a, add } from './export2.js';

// * 表示載入全部, 通常不太建議用, 不好除錯
import * as data2 from './export2.js';
```
- export 寫法
```=javascript
// 具名匯出,每個檔案可以匯出多個
export const a = 'sky';
export function add(x,y) {
 return x+y;
}
```

### example3
![](https://i.imgur.com/fCEnX78.png)

- import 寫法
```=javascript
// 舊寫法 直接把js檔案拉進來執行
import './export3.js';
```
- export 寫法
```=javascript
function test() {
  console.log('hello');
}
test();
```

## 加入－index.html顯示boundle.js
在webpack上所有東西都要透過js做打包才能去做壓縮, 會把全部打包在一隻boundle.js
所以在`dist`新增一個`index.html`並載入`boundle.js`

透過打包好的js檔案, 在`index.html`中可以不用寫html,把專案要用的把包在boundle檔就可以,這邊的`<h1>`是練習用的

![](https://i.imgur.com/nRttHwj.png)

## 載入css-loader
[css-loader](https://github.com/webpack-contrib/css-loader)
1. install
```
npm install --save-dev css-loader style-loader
```

2. setting`webpack.config.js`

==`style-loader`一定要在`css-loader`前面==

```
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
};
```
3. 在`index.js`載入css, data

![](https://i.imgur.com/rfYmW9g.png)

![](https://i.imgur.com/SFjXGyE.png)

![](https://i.imgur.com/aTETE22.png)

## 載入scss-loader
scss is a pre-loader
[scss-loader](https://github.com/webpack-contrib/sass-loader)

1. install 
node-scss或scss都可以
```
npm install sass-loader node-sass --save-dev
```
2. webpack.config.js
```=javascript
module.exports = {
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [
          // Creates `style` nodes from JS strings
          "style-loader",
          // Translates CSS into CommonJS
          "css-loader",
          // Compiles Sass to CSS
          "sass-loader",
        ],
      },
    ],
  },
};
```

3. 在`index.js`載入scss, data

![](https://i.imgur.com/bU2wMqO.png)

![](https://i.imgur.com/Yowj3Wx.png)

![](https://i.imgur.com/BTUJfH8.png)

## 載入 webpack 測試伺服器
自動監聽自動開啟sever

[webpack sever](https://github.com/webpack/webpack-dev-server)
1. install
```
npm install webpack-dev-server --save-dev
```
2. 改webpack.config.js
加入
```=javascript
 devServer: {
    static: {
      directory: path.join(__dirname, 'dist'),
    },
    compress: true,
    port: 9000,
  },
```

自動打開瀏覽器,`open:true`
![](https://i.imgur.com/9rG5j3N.png)


3. 改packpage.json

改成`webpack serve`

```=javascript
"dev": "webpack serve--mode development",
```

![](https://i.imgur.com/S3mZLe1.png)

### 載入 axios
1. install

```
npm install axios --save
```

2. import axios
```=javascript
import axios from "axios";

axios.get('https://hexschool.github.io/ajaxHomework/data.json')
     .then( (resp) =>{
         console.log(resp);
     })
```
![](https://i.imgur.com/zcykq9P.png)

![](https://i.imgur.com/L64OlS4.png)

{%hackmd BkMW6iLTK %}

###### tags: `study`