Node.js筆記

埃羅芒阿老師2.jpg
  • 個人入門學習用筆記、不過多作為參考依據。如有錯誤歡迎斧正

  • 目錄

簡書好像不支持錨點、復制搜索(反正也是寫給我自己看的)
  • 瀏覽器
  • javaScript
  • 編程語言的能力
  • Node
  • 安裝(Mac)
  • 使用
  • Node---REPL環境
  • 終端命令
  • 異步操作
  • I/O
  • Node多線程
  • NPM包管理工具
  • nrm
  • querystring字符串查詢模塊
  • URL模塊
  • 制作一個簡單的表單提交
  • 文件操作
  • 模板字符串
  • HTTP
  • http傳輸方式
  • NET.socket
  • 手動實現一個簡單的靜態資源管理服務
  • exports&&require
  • node_modules文件夾
  • package.json
  • Post請求處理(原生)
  • 一些工具框架(庫)
  • 瀏覽器

1、請求http地址(報文---傳輸的數據文檔)
瀏覽器的最大作用就是將一個url地址封裝成一個請求報文、并解析相應報文。
2、服務器返回的相應報文。
報文內容:
html/css/image = >渲染
js=>解釋(執行)js

  • javaScript

腳本語言
瀏覽器中運行
客戶端頁面交互實現

運行在瀏覽器內核中的js引擎(engine)下
作用:
 1、操作DOM(對DOM的修改、增刪改、注冊事件)
 2、AJAX/跨域
 3、BOM對象(頁面跳轉、歷史記錄、console.log()、alert()、window對象下的大部分子對象)
 4、ECMAScript

不能進行的操作(由于js運行環境處于客戶端。影響安全性):
1、文件操作(文件和文件夾的CRUD增刪改查)
2、操作系統信息(版本號等)
  • 編程語言的能力

所有編程語言本身只提供定義變量、函數、對象、流程控制、循環等操作

能力高低取決于運行該語言的平臺(環境)----瀏覽器

對于js來講。DOM/BOM等能力均由瀏覽器引擎提供。
java
既是語言、又是平臺。
java運行與java虛擬機(vm)平臺上(虛擬機本身可以跨操作系統運行)。

 C#
   平臺:.net framework(Windows)
   也可以運行于MONO這樣的平臺(Linux)

 PHP
   既是語言、又是平臺
javaScript并不只能運行于瀏覽器
能運行在哪取決于環境有沒有特定的平臺
  • Node

  • Node.js并不是一種語言。(使用v8引擎+js語法。V8:高效解析js、大部分為異步。node就是將V8中的一些功能移植到了服務器上)
  • 一個js在服務端的運行平臺(runtime)
  • 實際上就是JavaScript的虛擬機
  • 是Node選擇了支持JavaScript、而不是JavaScript選擇了Node。
  • 與PHP、JSP、Python等直接運行在服務器程序(Apache、Naginx、Tomcat、IIS)上的不同,Node.js跳過了HTTP服務器、不需要建立在任何服務器軟件之上。
  • 不同于許多經典架構(LAMP=Linux(操作系統)+Apache(服務器)+MySQL(數據庫)+PHP(語言))、有著很大的不同、演變成LMP形式。并不存在web容器(根目錄)。?Node部署成功之后、并沒有運行服務器、但是Node線程卻擔當了服務器的作用。
  • 特點

所謂特點、就是node.js如何解決服務器高性能瓶頸問題。
服務器語言(PHP、Java等)會為每一個客戶端建立一個新的線程(大約2MB)、理論上一個8GB內存的服務器可以連接4000個用戶。
Node只有一個線程、當用戶連接時、觸發一個內部事件。通過非阻塞I/O、事件驅動機制、讓node在宏觀上實現并行。使用node.js、一個8GB的服務器可以處理4萬用戶的鏈接。

單線程:充分利用CPU.

提高鏈接上限、但是主線程崩潰會出現大面積崩潰。

非阻塞I/O(non-blocking I/O ):

在執行了I/O操作時、會將I/O操作移交磁盤進行物理并發、并立即執行其后面的代碼、I/O結束后以事件的形式返回并執行回調。

事件驅動(event-driven):

新用戶請求進入、或老用戶I/O異步回調返回時、會以事件形式加入事件環、等待調度(回調擁有更高的優先級)。

善于I/O、長連接、不善于計算:

擅長于業務調度(用戶表單收集--百度貼吧投訴、考試系統)、如果業務過渡占用CPU進行計算、實際上過多計算會阻塞線程。長連接也是業務調度。

  • 作用:

    1、web網站服務器(直接服務器)---比如淘寶
    2、web網站請求包裝、服務器返回數據分發、渲染HTML頁面。(間接服務器)---Node的并發數、抗壓性高于傳統服務器平臺。比如天貓
    https://github.com/NetEase/pomelo 網易開源游戲服務端
  • 安裝(Mac)

  • 條件:

1、xcode(因為內含GCC、python等)
   xcode-select -p(可以檢查一下)
2、python(有了xcode基本python也具備了)
   python -V(區分大小寫)
  • 安裝Homebrew

ruby -v(安裝依賴ruby)
   www.brew.sh(Homebrew官網)
  • 安裝Node.js

brew install node
  • 版本控制

n 7.7.2(下載7.7.2版本的node.js)
  • 安裝完成之后
  n(上下鍵選擇本地版本。回車確定安裝)
  • 使用

nodejs.org(官方教程)

你需要一個js文件控制服務器行為(例:)

 //加載“http”模塊。由JavaScript編寫、負責創建web服務器、以及處理http相關任務
 const http = require('http');
 //加載其他js文件。無返回值、直接加載即可。
 require('./vue.js')
 //獲取調用者對象
 module.parent
 
 //ip、字符串
 const hostname = '127.0.0.1';
 //端口、數字
 const port = 3005;
 
 //創建服務器
 //http.createServer(function(回調函數));添加一個回調函數
 //req含有請求相關的信息(來源url、請求類型)
 //res含有請求響應的內容
 //1.返回文字等信息
 const server = http.createServer(function(req, res){
   res.statusCode = 200;
   //文本內容類型
   res.setHeader('Content-Type', 'text/plain');
   res.end('Hello World\n');
 });
 
 //2.返回一個html頁面
 /*不同于Apache。node.js沒有web容器的概念、也就是沒有文件夾的概念*/
 const server = http.createServer(function(req, res){
     //node作為跨平臺文件、不支持同文件夾下直接index_01.html這種windows寫法、必須使用./index_01.html的linux寫法
     fs.readFile('./index_01.html',(err,data) =>{
         res.statusCode = 200;
         res.setHeader('Content-Type', 'text/html');
         if (!err) {
             res.end(data);
         }else {
             res.end('html not found');
         }
     });
 });
 
 //3.返回路由頁面
 const server = http.createServer(function(req, res){
 //http://127.0.0.1:3007/index_01.html    req.url =/index_01.html
 //http://127.0.0.1:3007/index_02.html    req.url =/index_02.html
   var url = req.url;
   //如果你愿意、也可以手寫url以隱藏文件路徑
   //比如
   if (req.url == '/lalala') url = '/index_01.html';
     fs.readFile('.'+url,(err,data) =>{
         res.statusCode = 200;
         res.setHeader('Content-Type', 'text/html');
         if (!err) {
             res.end(data);
         }else {
             res.end('html not found');
         }
     });
 });
 
 //讓服務器在端口監聽請求然后調用上面的函數
 server.listen(port, hostname, function()  {
     //成功啟動服務器監聽時、在控制臺打印
     console.log(`Server running at http://${hostname}:${port}/`);
 });

res.end(data);是回調內必須實現的一個函數。

//ES6中匿名函數
function(a){} === (a) => {}

關于路由文件、有個重點

node.js在每一次讀取文件時、都會調取一遍createServer的回調
也就是說、內部的圖片也需要手動讀取。所以:

 var url = req.url;
 fs.readFile('.'+url,(err,data)

這樣的構成就非常重要。
否則就連圖片都需要手動指定文件路徑了。
當然。對于node.js。根目錄就是js文件所在目錄。

啟動服務器
 1.cd到js文件夾目錄下
 2.node servers.js(啟動服務器文件 )
 3.control+c(關閉服務器)
帶參數運行腳本
 node servers.js 123 asd
 123可以通過process.argv[2]獲取
 asd可以通過process.argv[3]獲取
 argv[2]||'lalalalal' === argv[2]?argv[2]:'lalalala'
  • Node---REPL環境

//進入REPL環境(類似web控制臺、用來測試代碼模塊)
(控制臺)node
node --use_strict//嚴格模式--直接a = 10;之類會報錯
//退出REPS環境
.exit
上次取值替代符號
_ //代表上次所取的值
stdin&&stdout
stdin:采集用戶輸入
stdout:控制臺輸出(不帶\n的console.log())

一個簡單的控制臺登錄demo

var q = '請輸入用戶名';
 
 var users = {
     'user1':'111',
     'user2':'222',
     'user3':'333'
 };
 
 process.stdout.write(q+'\n');
 
 var type = 0;
 var user;
 
 process.stdin.on('data',(input) => {
     //獲取keys的數組、并且查找是否含有input內容
     input = input.toString().trim();
 
     if (type === 0 ){
         if(Object.keys(users).indexOf(input) === -1) {
             //用戶名不存在
             process.stdout.write('用戶名不存在、');
             process.stdout.write(q+':\n');
             type = 0;
         }else {
             user = input;
             type = 1;
             process.stdout.write('請輸入密碼:\n');
         }
     }else {
         var pwd = users[user];
         if (input === pwd){
             process.stdout.write('用戶'+user+'登錄成功\n');
             type = 0;
         }else  {
             process.stdout.write('密碼錯誤、請重新輸入密碼:\n')
         }
     }
 });
  • 終端命令

輸出控制臺輸出+創建文件
echo 'aaa' >>1.txt
查看文件內容
cat 1.txt
創建一個目錄/文件
mkdir dirname
刪除文件/目錄
rm -rf dirname

  • 異步操作

1、網絡請求
2、文件操作

  • I/O

input/output

  • Node多線程

1、Node平臺將一個任務聯通該任務的回調函數放到一個時間循環系統中。
2、事件循環高效的管理系統線程池、同事高效執行每一個任務。
3、當任務執行完成過后自動執行回調函數。

Node將所有的阻塞操作、交付給內部線程池。
Node本身主線程主要就是不斷的“往返調度”(文件操作、網絡請求全部交由其他線程執行)。

Node可以充分利用單核CPU的優勢。

  • NPM包管理工具

npm(Node Pack Manager)
npm會隨著node的安裝、自動安裝
Nodejs本身是輕內核、不包含太多的功能。
在開發過程中可能需要引用大量的依賴包(再小的功能都有成熟的模塊)
個人開發者制作的功能擴展
https://www.npmjs.com/
require時、有限查找系統模塊。
然后才會查找項目中node_modules目錄;
查看所有依賴包

 npm ls

查看深度為0的根包

 npm --depth 0

使用

 npm install 包名稱
  • nrm

可以自動切換npm服務器指向
//安裝

 npm install -g nrm
 //查看所有可用指向
 nrm ls
 //修改npm指向
 nrm use taobao
 //檢測節點速度
 nrm test
  • querystring字符串查詢模塊

  • querystring.parse字符串 =>字典
const querystring = require('querystring');
querystring.parse('foo=bar&abc=xyz&abc=123');
//return
{ foo: 'bar',  abc: ['xyz', '123'] };

默認情況下,查詢字符串中的百分號編碼的字符會被認為使用了 UTF-8 編碼。 如果使用的是另一種字符編碼,則 decodeURIComponent 選項需要被指定,如以下例子:

 querystring.parse('w=%D6%D0%CE%C4&foo=bar', null, null,{ decodeURIComponent: gbkDecodeURIComponent });
  • URL模塊

const url = require('url');
url.parse()可以將一個完整的url地址分為很多部分、例如:
 var url = req.url;
 res.write('<h1>'+'url  --->   '+url+'</h1>');
 //url ---> /p/index_01.html?id=123
 res.write('<h1>'+'host  --->   '+urls.parse(url).host+'</h1>');
 res.write('<h1>'+'port  --->   '+urls.parse(url).port+'</h1>');
 res.write('<h1>'+'path  --->   '+urls.parse(url).path+'</h1>');
 //path ---> /p/index_01.html?id=123
 res.write('<h1>'+'pathname  --->   '+urls.parse(url).pathname+'</h1>');
 //pathname ---> /p/index_01.html
 res.write('<h1>'+'query  --->   '+urls.parse(url).query+'</h1>');
 query ---> id=123
 //url.parse(urlstr,true).query 可以將參數轉化為json對象
 var query = urls.parse(url,true).query;
 res.write('<h1>'+'id  --->   '+ query.id  +'</h1>');
 //id ---> 123
  • 制作一個簡單的表單提交

  • HTML

     <form action="http://127.0.0.1:3010/" method="get">
     <input type="text" name="name" />姓名<br>
     <input type="text" name="age" />年齡<br>
     <input type="radio" name="sex" value="男" />男
     <input type="radio" name="sex" value="女" />女
     <br>
     <input type="submit">
     </form>
    
  • Node.js

     const http = require('http');
     var url = require('url');
    
     var hostName = '127.0.0.1';
     var port = 3010;
         
     http.createServer((req,res) => {
       
         var queryJson = url.parse(req.url,true).query;
         var name = queryJson.name;
         var age = queryJson.age;
         var sex = queryJson.sex;
        //支持中文顯示
         res.writeHead(200, {
             "Content-Type": "text/html;charset=utf-8"
         });
         
         
         res.write(`<h1>服務器收到了表單請求</h1>`);
         res.write(`姓名:${name}
     年齡:${age}
     性別:${sex}`);
       
       
         res.end();
     }).listen(port,hostName,()=>{
         console.log(`成功監聽 http://${hostName}:${port}/`);
     });
    
  • 文件操作

文件操作下、必須使用"絕對路徑(物理路徑)"

  • fs:

基礎的文件操作

  //異步(options為編碼格式)
  fs.readFile(path[, options], (err, data) => {
       if (err) throw err;
       console.log(data);
    });
   //同步(options為編碼格式)
   try {
       var data = fs.readFileSync(path[, options])
    } catch(error){       }

如不聲明編碼格式。解析結果會一buffer(二進制緩沖區)格式輸出

流的形式讀取文件
 const fs = require('fs');
   const rr = fs.createReadStream('foo.txt');
   var dara = ' ';
   rr.on('data', (chunk) => {
         //chunk為每次讀取出來的文件塊  (buffer---字節數組)
         //chunk.lenth
         data+=chunk.toString();
   });
   rr.on('end', () => {
     console.log('end');
   });
  • buffer:

數據容器。將大文件整個放入緩沖區再分批寫入磁盤

   //讀取
   buffer.toString('utf8')
   //寫入(字符串)
   buffer.write(string[, offset[, length]][, encoding])
  • path:

提供和路徑相關的操作

   //獲取文件名
   path.basename(temp)
   //獲取路徑中目錄名稱
   path.dirname(temp)
   //獲取不同系統中路徑分隔符、Windows是“;”Linux/MacOS是“:”
   path.delimiter
   //獲取環境變量
   process.env.PATH.split(path.delimiter)
   //獲取路徑中后綴(擴展)名/包含"."
   path.extname(temp)
   //獲取文件信息對象.(文件目錄、文件名、擴展名等)
   var obj = path.parse(temp);
   //將對象路徑轉化成字符串
   path.format(obj)
   //判斷是否為絕對路徑
   path.isAbsolute(temp)
   //拼接路徑
   path.join([path1],[path2],..) 
   //根據當前系統常規化一個路徑
   var a = path.normalize('C://dev\\abc//cba////1.txt');
   console.log(a);
   //獲取to路徑相對于from的相對路徑
   path.relative(from,to);   
   //通過文件操作獲取目錄(允許從絕對路徑開始)
   path.resolve(__dirname,'..','./','./code');
   //獲取當前操作系統中默認用的路徑成員分隔符.Windows“\” Linux、MacOS“/”
   path.sep
   //判斷操作系統win32:windows&&LinuxMacOS:posix
   //可以通過path.win32//posix改變node操作方式
   console.log(path=== path.win32);
  • readline:

讀取大文本/歌詞等文件、一行一行讀

 const readline = require('readline');
 const fs = require('fs');
 const rl = readline.createInterface({
 input: fs.createReadStream('sample.txt')
   });
     
   rl.on('line', (line) => {
     //確保每次返回一整行數據
     //與createReadStream用流的形式返回buffer不用。
     //readLIne直接返回字符串對象
     console.log(`Line from file: ${line}`);
   });
 const readline = require('readline');
   const rl = readline.createInterface({
       input: process.stdin,
       output: process.stdout,
       prompt: 'OHAI> '
   });
   rl.prompt();  
   //用戶輸入
   rl.on('line', (line) => {
       switch (line.trim()) {
         case 'hello':
           console.log('world!');
           break;
         default:
           console.log(`Say what? I might have heard${line.trim()}'`);
           break;
       }
       rl.prompt();
   //用戶結束了輸入。
   }).on('close', () => {
       console.log('Have a great day!');
        process.exit(0);
   });
  • 文件寫入
//只要是文件操作。fs以及path就是必須模塊
   const fs = require('fs');
   const path = require('path');
   const data = {id:10};
   //文件寫入時傳入對象data:結果為[object Object]
   //JSON.stringify(data)序列化:結果為{id:10}
   //JSON.parse(data)反序列化
   //寫入、覆蓋
   fs.writeFile(file, data[, options], callback);
   //追加
   fs.appendFile(file, data[, options], callback);
   //文件流
   var streamWriter = fs.createWriteStream(path.join(__dirname,'temp.txt'));
   streamWriter.write('hello1',() => {
       console.log('+1');
   });
   //同步書寫文件
   fs.writeSync(fd, string[, position[, encoding]])
  • pipe()方式copy文件
readStream.pipe(writeStream);
   writeStream.on('pipe',(src) => {
       src===readStream;
   }

pipe()支持鏈式編程

  • 判斷文件是否存在
fs.stat(path, callback(err, stats))
  • 重命名文件或目錄
fs.rename(oldPath, newPath, callback);
   fs.renameSync(oldPath, newPath, callback);
  • 刪除文件
fs.unlink(path, callback);
   fs.unlinkSync(path, callback)
  • 創建文件夾(要求路徑必須存在)
fs.mkdir(path, callback);
  • 監視文件變化
fs.watchFile(path.join(__dirname,'temp.txt'),(curr, prev) => {
       console.log(`previous:${prev.size}`);
       console.log(`current:${curr.size}`);
   });
  • fs-extra(第三方):

https://www.npmjs.com/package/fs-extra
擴展了文件拷貝、刪除等操作

  • 模板字符串

通過不同的引號來包裹
' 普通字符串------` 模板字符串

  console.log(`  鵝,鵝,鵝,
     曲項向天歌。
    白毛浮綠水,
   紅掌波清波。`);
 
  console.log(`
    this is ${nameHe}, 
   this is ${nameShe}`
   , nameHe);
  • HTTP

客戶端輸入url
封裝成特定格式(http/https等)的請求報文
通過socket(連接方式)傳遞給服務端
服務端根據不同協議類型對報文進行解析
。。。。。
客戶端接收到服務端返回的報文并加以解析
客戶端渲染內容到頁面當中

  • http傳輸方式

PUT:傳輸文件(<b>增</b>)
DELETE:刪除文件(<b>刪</b>)
POST:傳輸實體主題(<b>改</b>)
Get:獲取資源(<b>查</b>)
HEAD:獲得報文首部
OPTIONS:詢問支持的方法
TRACE:追蹤路徑
CONNECT:要求用隧道協議鏈接代理
LINK:建立和資源之間聯系(1.1已廢棄)
UNLINK:斷開連接關系(1.1已廢棄)

  • NET.socket

一個簡單的socketdemo

服務端

 const  net = require('net');
  //創建socket服務器
  var server = net.createServer(socketConnect);

 // 當有客戶端與服務器連接時、觸發
 function socketConnect (socket) {
     //自己的ip
     var myIp = socket.address().address;
     //其他人的ip
     var userip = socket.remoteAddress;
   
     var clientIp = socket.address().address;
     console.log(`被鏈接from:${userip}`);
   
     //監聽socket數據流
     socket.on('data',(chunk) => {
         process.stdout.write(`客戶端輸入:${chunk.toString()}`);
         // console.log(`客戶端輸入:${chunk.toString()}`);
     });
   
    socket.on('end',() => {
   console.log('客戶端斷開連接');
     });
   
     //通過socket對象向另一端通信
     socket.write('hello kirito!')
 }
   
 // 讓socket服務器監聽端口
 //post傳入0則會由系統分配空閑端口
 var post = 2080;
 server.listen(post,(err) =>{
     //監聽成功回調
     if (err){
         //通常是端口被其他對象占用ing
         throw err;
     }
   
     console.log(`服務端開啟,監聽:${post}端口`);
 });

客戶端

const net = require('net');
     
 //與服務端socket作為參數可返回多個不同。客戶端只返回一個socket對象.
 const socket = net.createConnection({ port: 2080 }, () => {
     //'connect' listener
     console.log('連接成功!');
   
 });
 //數據接收監聽
 socket.on('data', (data) => {
     console.log(`服務端返回${data.toString()}`);
     process.stdout.write('client:');
     // socket.end();
 });
 //鏈接斷開監聽
 socket.on('end', () => {
     console.log('disconnected from server');
 });
   
   
 //監聽控制臺輸入
 process.stdin.on('data',input =>{
     process.stdout.write('client:');
     if (input.toString().trim() === 'exit') {
         socket.end();
         return;
  }
     socket.write(input.toString());
 });
  • 手動實現一個簡單的靜態資源管理服務

 const http = require('http');
 const url = require('url');
 const fs = require('fs');
 const path = require('path');
 
 
 const post = 3000;
 const host = '127.0.0.1';
 var mimeJSON;
 
 
 
 http.createServer((req,res) => {
     //拿到路徑
     var pathName = url.parse(req.url).pathname;
     console.log(pathName);
     if (pathName.length == 1){
         pathName = '/index.html';
     }
     var extname = path.extname(pathName);
     //讀取文件
     fs.readFile('./static/'+pathName,(err,files) => {
         if (err){
             res.writeHead(404, {
                 "Content-Type": "text/html;charset=utf-8"
             });
             fs.readFile('./static/'+'404.html',(err,files) => {
                 res.end(files);
             });
         }else  {
 
 
             getMine(extname,(mime) => {
                 //MIME類型
                 //網頁:text/html
                 //jpg:image/jpg
                 res.writeHead(200, {
                     "Content-Type":mime
                 });
                 res.end(files);
             });
         }
     });
 }).listen(post,host,() => {
     console.log(`成功監聽${host}:${post}`);
 });
 
 
 function getMine(extname,callback) {
     var mime;
     if (!mimeJSON) {
         fs.readFile('./static/mime.json',(err,data) => {
             if (err) {
                 mimeJSON = {'aaa': 'dddd'};
             }else {
                 mimeJSON = JSON.parse(data);
             }
             mime = mimeJSON[extname] || 'text/plain';
             console.log('11'+mime);
             callback(mime)
         })
     }else {
         mime = mimeJSON[extname] || 'text/plain';
         console.log('22'+mime);
         callback(mime);
     }
 }

exports&&require

(在前端中、js與js文件通過html文件相互聯系。
而在node.js中、二者則通過exports與require相互聯系)

  • 暴露函數、變量

當需要從A.js引用B.js內部的變量、函數時。
必須在B中使用exports對象進行暴露。
A中使用require命令引用B.js文件

B

 var msg = '你好';
 exports.msg = msg;

A

 //將foo.exports解固成頂層變量
 const foo = require('./foo.js');
 console.log(foo.msg);
  • 暴露類、構造函數

B

 function People(name,sex,age) {
     this.name = name;
     this.sex = sex;
     this.age = age;
 }
 
 People.prototype.sayHello = function () {
     console.log(this.name+this.sex+this.age);
 }
 
 //此時People就被視為構造函數、可以用new來實例化
 module.exports = People; 

A

 var People = require('./people.js');
 
 var xiaoming = new People('小明','男','12歲');
 
 xiaoming.sayHello();

exports&&module.exports

  • module.exports

  • 將模塊的require變成一個函數、或者任意類型的對象。
   module.exports = function(name, age) {
       this.name = name;
       this.age = age;
       this.about = function() {
           console.log(this.name +' is '+ this.age +' years old');
       };
   };

 var Rocker = require('./rocker.js');
 var r = new Rocker('Ozzy', 62);
 r.about(); // Ozzy is 62 years old
  • exports

  • exports.xxx= xxx本質上等于為module.exports添加新的方法。或者屬性
  • 如果你已經實現了module.exports、那么之后的 exports.xxx將會失效、不會動態添加.
  • 如果你想你的模塊是一個特定的類型就用Module.exports。如果你想的模塊是一個典型的“實例化對象”就用exports。
  • node_modules文件夾

假如 /home/ry/projects/foo.js 調用了

require('bar.js') 

那么node查找的位置依次為:

/home/ry/projects/node_modules/bar.js
/home/ry/node_modules/bar.js
/home/node_modules/bar.js

假如 /home/ry/projects/foo.js 調用了

require('bar') 

1、那么node將會先查找文件:

/home/ry/projects/node_modules/bar/package.json

關于package.json、每一個模塊文件夾的根文件中推薦都寫一個package.json文件(文件名固定)。node將自動讀取內部配置。

 {
   "name": "kiritoVar",
   "version": "0.0.1",
   "main":"ffff.js"
 }

2、然后node將會查找文件:

/home/ry/projects/node_modules/bar.js

3、若沒有找到在繼續查找文件

/home/ry/projects/node_modules/bar/index.js

package.json

  • 在你的項目完成之后、可以用npm為自己的項目創建一個package.json
 控制臺$  npm init
 會出現如下引導(均可不填)

 package name: (beginning) beginning  //項目名稱
 version: (1.0.0) //版本
 description: my node demo//項目說明
 entry point: (demo_01.js) url_demo.js//入口
 test command: //測試
 git repository: //git倉庫
 keywords: suibian//標簽、關鍵詞
 author: kritio_song//作者
 license: (ISC) ISC//版權

然后將會生成一個package.json文件
其中最重要的一點、他將會含有一個dependencies字段

 "dependencies": {
     "date": "^1.0.2"
   其中'^',表示固定這個數字
    "^1.^0.2"就表示固定為1.0.x這樣
 },

dependencies下、記錄著此項目所有引用的第三方模塊、以及node_modules文件夾下你自己創建的模塊

  • 作用

在項目目錄下 $ npm install
將會檢測所有dependencies下的依賴、并且自動安裝

Post請求處理

  • 原生
 if (req.url == '/dopost' && req.method.toLowerCase() == 'post'){
        var allData = '';
        //為了防止參數過多影響線程利用、node讀取參數的方式是分塊讀取。參數過多時、可能會多次回調
       req.addListener('data',(chunk) =>{
          allData += chunk;
         });
         //讀取完畢
         req.addListener('end',(chunk) =>{
             console.log(allData.toString());
             //輸出foo=bar&abc=xyz&abc=123
             //可以使用querystring模塊轉義
             querystring.parse(str)字符串 =>字典
             res.end(allData);
         });
     }

原生處理post請求、需要兩個監聽。并且文件上傳需要特殊處理

  • formidable
     const http = require('http');
     const path = require('path');
     const fs = require('fs');
     const formidable = require('formidable');
     const post = 8080;
     const host = '127.0.0.1';
     
     
     http.createServer((req,res) => {
         if (req.url == '/dopost' && req.method.toLowerCase() == 'post'){
     
             var form = new formidable.IncomingForm();
             //設置文件上傳存放地址
             form.uploadDir = __dirname+"/fileDir";
     
             //執行里面的回調函數時、表單已經接收完畢
             form.parse(req, function(err, fields, files) {
                 //所有的文本域、單選框,都在fields存放
                 //所有的文件域、都在files存放
                 console.log(err);
                 console.log(fields);
                 console.log(files);
     
                 if (files){
                     var extname = path.extname(files.file.name);
                     var random = parseInt(Math.random() * 999999 + 100000);
                     var dir = form.uploadDir;
                     var timestamp=new Date().getTime();
                     var oldName = files.file.path;
                     var newName = dir+'/'+timestamp+random+extname;
                     //修改文件名以及后綴
                     fs.rename(oldName,newName,()=>{
                         console.log(`renameed to ${newName}`);
                     })
                 }
                 res.end('succes');
             });
         }
     }).listen(post,host,() =>{
         console.log(`成功監聽${host}:${post}`)
     });
    
  • formidable只能接收表單形式提交上來的post參數。
  • 模板引擎

EJS(通過字符串進行邏輯替換)

  • Embedded JavaScript templates
  • 后臺的一個模板引擎
  • 現在很少用、基本都是前端采用模板拼接(比如vue.js)
 const ejs = require('ejs');
 
 var string = '我買了一個iphone<%= a%>s';
 var data = {
     a : 6
 };
 
 var html = ejs.render(string,data);
 
 console.log(html);

也可以直接替換html文件中的模板標記

node文件中:
 const fs = require('fs');
 const ejs = require('ejs');
 const http = require('http');
 
 http.createServer((req,res) => {
 
     fs.readFile('./view/index.ejs',(err ,data ) => {
         var template = data.toString();
         var dictionary = {
                a:6,
                news:['第一個','第二個','第三個']
         };
 
         var html = ejs.render(template,dictionary);
 
         res.writeHead(200, {
             "Content-Type":'text/html'
         });
 
         res.end(html);
     });
 
 }).listen(3000,'127.0.0.1',() =>{
     console.log('監聽成功');
 });
html文件中:
 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <title>Title</title>
     <link rel="stylesheet" type="text/css" href="css/css_01.css">
 </head>
 <body>
     <h1>好高興啊、我今天買了一個iphone<%= a%>s</h1>
     <ul>
     <% 
         for (var i = 0 ; i< news.length ; i ++){
             if(news){
     %>
         <li><%= news[i]%></li>
     <% 
             }
         }
     %>
     </ul>
 </body>
 </html>

<% %>之中支持所有js語法(for if等)

  • include引用

     <% include nav.ejs %>
    
  • 圖片處理

http://www.graphicsmagick.org/

  • webSocket

  • 使用SocketIO模塊
  • app.js
 /**
  * Created by kiritoSong on 17/9/29.
  */
 var http = require('http');
 var fs = require('fs');
 var server = http.createServer(function (req, res) {
     if (req.url == '/'){
         fs.readFile('./index.html',function (err,data) {
             res.end(data);
         })
 
     }
 });
 
 
 //將server變成socket
 //http://127.0.0.1:3000/socket.io/socket.io.js 會是一個文件地址
 var io = require('socket.io')(server);
 
 //監聽鏈接事件
 io.on('connection',function (socket) {
     socket.on('提問',function (msg) {
 
         console.log('客戶端提問:'+msg);
         socket.emit('回答',['吃了','還是沒吃']);
     })
 
 
     socket.on('廣播',function (msg) {
         //broadcast 廣播
         socket.broadcast.emit('有人問我'+msg);
         console.log('客戶端提問:'+msg);
         //io.emit可以向所有連接廣播
         io.emit('回答',['吃了','還是沒吃']);
     })
 });
 
 
 
 server.listen(3000);
  • index.html
 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <title>Socket</title>
 </head>
 <body>
 
     <ht>index頁面</ht>
 
     <script src="/socket.io/socket.io.js" type="text/javascript"></script>
 
     <script type="text/javascript">
         //客戶端socket、與服務端相互對應
         var socket = io();
 
 
         socket.emit('提問','你吃了么');
 
         socket.on('回答',function (msg) {
             for (var i = 0 ; i < msg.length ; i++ ){
                 console.log('服務器回答'+i+'::'+msg[i]);
             }
         })
     </script>
 </body>
 </html>
WechatIMG8.jpeg
WechatIMG10.jpeg
  • socket.emit('k',msg)負責發送
  • socket.on('k',msg)負責監聽
  • 服務器&&客戶端均是
  • express搭配socket.io

  • 一些工具框架(庫)

  • nodemon

一個可以監聽node文件改變、并且重新開啟node文件的工具

webSocket(瀏覽器不支持socket則自動模擬長輪詢)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,367評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,001評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,213評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,535評論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,317評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,868評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,963評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,090評論 0 285
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,599評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,549評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,712評論 1 367
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,233評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,961評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,353評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,607評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,321評論 3 389
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,686評論 2 370

推薦閱讀更多精彩內容

  • node.js介紹 簡單的說node.js就是運行在服務器端的JavaScript。 node.js是一個基于Ch...
    清心挽風閱讀 512評論 0 2
  • Node.js 簡介 Nodejs 是什么? Node.js 不是一種獨立的語言,也不是一個 JavaScript...
    鐘濤閱讀 444評論 0 2
  • cmd命令: ./ 當前目錄 ../ 上一級 dir 查看當前目錄 ls 查看當前目錄下文件 win...
    3hours閱讀 551評論 0 1
  • 誠摯約稿: 一張圖,一段話,還你一個故事 來了美國,我發現大家的廚藝都見長了,都誤以為我們進錯了新東方。 來了美國...
    A步閱讀 457評論 0 1
  • 上了大學整個人都放松下來了 脾氣也大了許多 偶爾回想起高中時代的輝煌經歷 居然還會感覺不可思議自己就這么干凈利落的...
    不回頭丶閱讀 518評論 1 0