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