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