nodejs相關總結

一、Node快速體驗

1、 Node介紹

(1) Node.js是什么

  • Node 是一個基于Chrome V8 引擎的JavaScript 運行環境。
    • Node 不是一種獨立的語言、
    • Node不是 JavaScript 框架,
    • Node是一個除了瀏覽器之外的、可以讓JavaScript 運行的環境

(2) Node 能做什么

知乎 - Node.js能做什么,該做什么?

  • Web 服務器(重點)
  • 命令行工具
  • 網絡爬蟲:是一種按照一定的規則,自動地抓取網站信息的程序
  • 桌面應用程序開發

(3) 一些資源

① 文檔

Node.js 官方文檔
Node.js 中文文檔(非官方)

② 書籍

深入淺出 Node.js
Node.js 權威指南
Node.js 實戰
Node.js實戰(第2季)

③ github資源

Node.js 包教不包會
ECMAScript 6 入門
七天學會 NodeJS

④ 社區

Node.js 中文社區

2. Node起步

(1)安裝Node

對于已經裝過的,重新安裝就會升級

確認 Node 環境是否安裝成功

打開命令行,輸入 node --version

或者 node -v

(2)REPL環境(Read Eval Print Loop:交互式解釋器:了解)

在命令行工具界面輸入node 按回車 就進入到了REPL 環境

  • read
  • eval
  • print
  • loop

類似于瀏覽器中的 Console控制臺 ,可以做一些代碼測試。

按ctrl + 兩次c 退出REPL環境

但是, 我們寫代碼肯定不是在控制臺中寫,而是寫在一個單獨的.js文件中.

(3)HelloWorld

  1. 新建一個 hello.js 并寫入以下示例代碼
    var message = 'Hello World!'
    console.log(message)
  1. 打開命令行并定位到 hello.js 文件所屬目錄
  2. 在命令行中輸入 node hello.js 回車執行
    node hello.js

(4)Node中的js

node中的js和瀏覽器中的js不同:

  • 瀏覽器中的js
    • ECMAScript
    • DOM
    • BOM
  • node中的js
    • ECMAScript
    • 自己的API
    • 沒有DOM和BOM

二、Node中的模塊和包管理器npm

1、Node中的模塊體驗

(1)文件讀寫

文件讀取:

    // 1、引用fs核心模塊
    var fs = require('fs');
    // 2、讀取文件
    // fs.readFile中有兩個參數:
    //     path: 要讀取文件的路徑
    //     callback:讀取文件后面的回調函數
    //          err: 讀取文件異常的異常信息
    //              如果文件讀取出來err為null
    //              如果文件讀取失敗err為失敗信息
    //          data: 文件中的內容
    //              如果文件讀取成功,那么將來data會是一個十六進制的Buffer數組
    fs.readFile('./00.txt', function(err, data){
        // 輸出文件中的內容
        console.log(err);
        console.log(data.toString());
    });

文件寫入:

注意:

  • 在寫入時會覆蓋已有的內容

  • 如果寫入的文件不存在,會自動創建

    // 1、引用fs核心模塊
    var fs = require('fs');
    // 2、向文件中寫入內容
    //  這個方法是用來覆蓋原文件中的內容,
    // fs.wirteFile(file, data, callback)
    //      file:要寫入內容的文件
    //      data:要寫入的數據(可以為utf-8)
    //      callback:寫入后的回調函數
    //          err: 寫入失敗后面的異常信息     
    fs.writeFile('./0000.txt', '天若有情天易老,人間正道是滄桑', function(err){
        if (err) {
            console.log(err.message);
        } else {
            console.log('寫入成功');
        }
    });

文件追加寫:

    // 核心模塊:fs
    //      fs.readFile
    //      fs.writeFile
    //  思想:先讀,然后追加,然后再寫

    var fs =require("fs");
    // 1、讀取
    fs.readFile("./00.txt", function(err, data){
        if(err) {
            console.log(err.message);
        } else {
            // 2、追加
            var str = "小豬配奇身上紋,掌聲送給社會人";
            str = data + str;
            fs.writeFile("./00.txt", str, function(err) {
                if(err) {
                    console.log(err.message);
                } else {
                    console.log("寫入成功");
                }
            });
        }
    });

(2)HTTP 服務

    // node把處理web服務器相關的功能 封裝到了 http模塊中
    // 1、導入http模塊
    var http = require('http');
    // 2、使用http這個模塊中的createServer()創建一個服務器實例對象
    var server = http.createServer();
    // 3、給服務器對象注冊一個request事件,當瀏覽器有請求發送過來時,服務器會接收,并且執行后面的回調函數
    // 請求處理函數function(形參1,形參2){}
    // 形參1:request請求對象 獲取到當前請求的路徑,方法等信息
    // 形參2:response響應對象 發送響應數據
    server.on('request', function(request, response) {
        console.log('有瀏覽器連接上來了!');
        // 向客戶端頁面返回字符串
        response.write("hello node");
        // 結束響應
        response.end();
    });
    // 4、綁定端口號,啟動web服務器
    server.listen(12345, function() {
        console.log(' 請訪問http://localhost:12345');
    });

注意:

  1. 注冊request處理函數 有兩個形參 request和response 不要搞混
  2. 在寫完服務器代碼后 保存代碼 ->在cmd中 node 文件名 然后回車執行
  3. 每次修改代碼后 要重新在cmd中 node 文件名 執行這個文件
  4. !! 端口號盡量寫的大一些 大于3000 否則容易和已經運行的軟件所占用的接口相沖突!

2、小案例

(1)通過http模塊寫一個自己的服務器

需求:將來有請求鏈接上來以后,服務器需求響應一個hello world的內容回瀏覽器

    var http = require("http");
    var server = http.createServer();
    // 如果需要響應在requrest事件后面的回調函數中有兩個參數:
    //  request
    //  response
    // 參數:
    //  request:瀏覽器發送到服務器的請求
    //  response:服務器響應回瀏覽器的數據
    //      方法:
    //          write:向響應報文中寫入一段要發送瀏覽器的數據,不會將數據立即響應回瀏覽器
    //          end:將數據響應回瀏覽器
    //          setHeader:設置響應頭
    server.on('request', function(request, response){
        // 響應數據回瀏覽器要借助response
        // response.write('hello');
        // response.write(' world');
        // response.end('hello world');
        // 設置響應報文的響應頭
        response.setHeader("content-type", "text/html;charset=utf-8");
        response.end('<doctype><html><head></head><body></body></html>');
    });
    //下一行listen方法中第二個參數可以指定自己的IP,也可以不寫,不寫時在瀏覽器訪問localhost:3000即可
    server.listen(3000, "192.168.81.1", function(){
        console.log('服務器開啟成功');
    });

(2)通過服務器響應一個完整的html頁面回瀏覽器

js文件:

    var http = require("http");
    // 引用fs模塊
    var fs = require('fs');
    var server = http.createServer();
    server.on('request', function(request, response){
        // response.setHeader("content-type","text/html;charset=utf-8");
        // response.end('');
        // 響應一個完整頁面回瀏覽器的思路
        //  1、創建一個完整的html頁面
        //  2、將這個頁面中內容讀取出來
        fs.readFile('./views/index.html', function(err, data){
            if(err) {
               return console.log(err.message);
            }
            //  3、將內容通過response.end響應回瀏覽器
            response.end(data);
        });
    });
    server.listen(3000, "192.168.81.1", function(){
        console.log('服務器開啟成功');
    });

html文件:./views/index.html

    <body>
        <h1>你好,世界!</h1>
    </body>

(3)在上面基礎上顯示一張圖片

js文件:

    var http = require("http");
    var fs = require('fs');
    var server = http.createServer();
    // request:所有瀏覽器發送到服務器的請求數據
    //    屬性:
    //      url  
    server.on('request', function (request, response) {
        // 如果將來圖片的請求過來服務器,服務器是沒有作任何處理
        // 處理一個圖片和頁面的請求:
        //  約定:
        //      將來請求根目錄時,是請求頁面
        //      將來請求views/0.jpg時,是請求圖片
        //  問題:如何得到請求的路徑
        //      request.url
        // 判斷:如果請求的 / ,響應頁面回去
        //      如果請求的 /views/0.jpg響應圖片回去
        if (request.url === '/') {
            fs.readFile('./views/index.html', function (err, data) {
                if (err) {
                    return console.log(err.message);
                }
                response.end(data);
            });
        } else if (request.url === '/views/0.jpg') {
            fs.readFile("./views/0.jpg", function(err, data){
                if (err) {
                    return console.log(err.message);
                }
                response.end(data);
            });
        }
    });
    server.listen(3000, "192.168.81.1", function () {
        console.log('服務器開啟成功');
    });

html文件:./views/index.html

    <body>
        <h1>你好,世界!</h1>
        <!-- 因為這個路徑壓根就沒有在服務器中被解析過,是瀏覽器來解析,因此使用絕對路徑 -->
        <img src="/views/0.jpg" id="img">
    </body>

(4)在此基礎上響應一個js文件

    var http = require("http");
    var fs = require('fs');
    var server = http.createServer();
    server.on('request', function (request, response) {
        // 處理根目錄的請求
        if (request.url === '/') {
            fs.readFile('./views/index.html', function (err, data) {
                if (err) {
                    return console.log(err.message);
                }
                response.end(data);
            });
            // 處理了圖片的請求
        } else if (request.url === '/views/0.jpg') {
            fs.readFile("./views/0.jpg", function(err, data){
                if (err) {
                    return console.log(err.message);
                }
                response.end(data);
            });
            // 處理jquery
        } else if (request.url === '/views/jquery.min.js') {
            fs.readFile("./views/jquery.min.js", function(err, data){
                if (err) {
                    return console.log(err.message);
                }
                response.end(data);
            });
        }
    });
    server.listen(3000, "192.168.81.1", function () {
        console.log('服務器開啟成功');
    });

html:

    <body>
        <h1>你好,世界!</h1>
        <img src="/views/0.jpg" id="img">
        <input type="button" value="點我隱藏" id="btn">
    </body>
    <script src="/views/jquery.min.js"></script>
    <script>
         $("#btn").click(function(){
             $("#img").hide(1000);
         });
    </script>

(5)在此基礎上統一處理靜態文件

js文件:

    var http = require("http");
    var fs = require('fs');
    var server = http.createServer();
    server.on('request', function (request, response) {
        if (request.url === '/') {
            fs.readFile('./views/index.html', function (err, data) {
                if (err) {
                    return console.log(err.message);
                }
                response.end(data);
            });
        }   
        // 統一處理靜態資源:
        // 判斷請求路徑中是否有攜帶views
        else if (request.url.indexOf("/views") != -1) {
            // 直接將對應的文件讀取出來
            // 先將絕對路徑轉為相對路徑
            var url = '.' + request.url;
            fs.readFile(url, function(err, data){
                if(err) {
                    return console.log(err.message);
                } 
                response.end(data);
            });
        }
    });
    server.listen(3000, "192.168.81.1", function () {
        console.log('服務器開啟成功');
    });

html:

    <head>
        <link rel="stylesheet" href="/views/index.css">
    </head>
    <body>
        <h1>你好,世界!</h1>
        <img src="/views/0.jpg" id="img">
        <input type="button" value="點我隱藏" id="btn">
    </body>
    <script src="/views/jquery.min.js"></script>
    <script>
         $("#btn").click(function(){
             $("#img").hide(1000);
         });
    </script>

(6) 完成仿apache的目錄瀏覽功能

步驟:

  • 服務器
    • 1、創建一個服務器
    • 2、如果將來瀏覽器請求靜態頁面,我們就將一個靜態文件返回到瀏覽器
    • 4、服務器接收到瀏覽器的請求,將當前目錄下的所有的路徑讀取出來,并且返回給瀏覽器的異步對象
  • 瀏覽器
    • 3、通過瀏覽器發送一個異步請求到服務器,去請求當前服務器的目錄數據(getPath)
    • 5、瀏覽器接收數據,并且將數據渲染到頁面上

js文件:

    // 創建一個服務器
    // 1、引用http模塊
    var http = require('http');
    var fs = require('fs');

    // 2、創建一個服務器對象
    var server = http.createServer();

    // 3、設置request事件
    server.on('request', function (req, res) {
        // 3.1、請求請求參數
        var url = req.url;
        // 3.2、判斷是否請求的是首頁  /
        if (url === '/') {
            // 將首頁的靜態文件響應回瀏覽器
            // 先通過fs模塊將內容讀取出來
            fs.readFile('./views/index.html', function(err, data){
                if(err) {
                    return console.log(err.message);
                } 
                res.end(data);
            });
        }
        // 3.3、判斷是否請求的是靜態文件
        else if (url.indexOf('/views') != -1) {
            fs.readFile('.' + url, function(err, data){
                if(err) {
                    return console.log(err.message);
                } 
                res.end(data);
            });
        }
        // 3.4、判斷請求的是否是getPath
        else if (url === '/getPath') {
            //將當前目錄下的所有的路徑讀取出來,并且返回給瀏覽器的異步對象
            fs.readdir('./', function(err, files){
                if(err) {
                    return console.log(err.message);
                }
                // 將files數組轉為json格式的字符串
                var str = JSON.stringify(files);
                // console.log(str);
                // 響應回瀏覽器
                res.end(str);
            });
        }
    });
    // 4、開啟監聽
    server.listen(3000, '192.168.81.1', function () {
        console.log('開啟成功');
    });

但是此方法在打開網頁后會出現小bug,即文件和目錄分不清,圖標都會顯示文件夾的樣子;文件大小和修改時間顯示不正確,若要修復bug這需要后面的知識。

html:

      <style>
        h1 {
          border-bottom: 1px solid #c0c0c0;
          margin-bottom: 10px;
          padding-bottom: 10px;
          white-space: nowrap;
        }

        table {
          border-collapse: collapse;
        }

        th {
          cursor: pointer;
        }

        td.detailsColumn {
          -webkit-padding-start: 2em;
          text-align: end;
          white-space: nowrap;
        }

        a.icon {
          -webkit-padding-start: 1.5em;
          text-decoration: none;
        }

        a.icon:hover {
          text-decoration: underline;
        }

        a.file {
          background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMAAAAAAABupgeRAAABHUlEQVR42o2RMW7DIBiF3498iHRJD5JKHurL+CRVBp+i2T16tTynF2gO0KSb5ZrBBl4HHDBuK/WXACH4eO9/CAAAbdvijzLGNE1TVZXfZuHg6XCAQESAZXbOKaXO57eiKG6ft9PrKQIkCQqFoIiQFBGlFIB5nvM8t9aOX2Nd18oDzjnPgCDpn/BH4zh2XZdlWVmWiUK4IgCBoFMUz9eP6zRN75cLgEQhcmTQIbl72O0f9865qLAAsURAAgKBJKEtgLXWvyjLuFsThCSstb8rBCaAQhDYWgIZ7myM+TUBjDHrHlZcbMYYk34cN0YSLcgS+wL0fe9TXDMbY33fR2AYBvyQ8L0Gk8MwREBrTfKe4TpTzwhArXWi8HI84h/1DfwI5mhxJamFAAAAAElFTkSuQmCC ") left top no-repeat;
        }

        a.dir {
          background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAd5JREFUeNqMU79rFUEQ/vbuodFEEkzAImBpkUabFP4ldpaJhZXYm/RiZWsv/hkWFglBUyTIgyAIIfgIRjHv3r39MePM7N3LcbxAFvZ2b2bn22/mm3XMjF+HL3YW7q28YSIw8mBKoBihhhgCsoORot9d3/ywg3YowMXwNde/PzGnk2vn6PitrT+/PGeNaecg4+qNY3D43vy16A5wDDd4Aqg/ngmrjl/GoN0U5V1QquHQG3q+TPDVhVwyBffcmQGJmSVfyZk7R3SngI4JKfwDJ2+05zIg8gbiereTZRHhJ5KCMOwDFLjhoBTn2g0ghagfKeIYJDPFyibJVBtTREwq60SpYvh5++PpwatHsxSm9QRLSQpEVSd7/TYJUb49TX7gztpjjEffnoVw66+Ytovs14Yp7HaKmUXeX9rKUoMoLNW3srqI5fWn8JejrVkK0QcrkFLOgS39yoKUQe292WJ1guUHG8K2o8K00oO1BTvXoW4yasclUTgZYJY9aFNfAThX5CZRmczAV52oAPoupHhWRIUUAOoyUIlYVaAa/VbLbyiZUiyFbjQFNwiZQSGl4IDy9sO5Wrty0QLKhdZPxmgGcDo8ejn+c/6eiK9poz15Kw7Dr/vN/z6W7q++091/AQYA5mZ8GYJ9K0AAAAAASUVORK5CYII= ") left top no-repeat;
        }

        a.up {
          background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmlJREFUeNpsU0toU0EUPfPysx/tTxuDH9SCWhUDooIbd7oRUUTMouqi2iIoCO6lceHWhegy4EJFinWjrlQUpVm0IIoFpVDEIthm0dpikpf3ZuZ6Z94nrXhhMjM3c8895977BBHB2PznK8WPtDgyWH5q77cPH8PpdXuhpQT4ifR9u5sfJb1bmw6VivahATDrxcRZ2njfoaMv+2j7mLDn93MPiNRMvGbL18L9IpF8h9/TN+EYkMffSiOXJ5+hkD+PdqcLpICWHOHc2CC+LEyA/K+cKQMnlQHJX8wqYG3MAJy88Wa4OLDvEqAEOpJd0LxHIMdHBziowSwVlF8D6QaicK01krw/JynwcKoEwZczewroTvZirlKJs5CqQ5CG8pb57FnJUA0LYCXMX5fibd+p8LWDDemcPZbzQyjvH+Ki1TlIciElA7ghwLKV4kRZstt2sANWRjYTAGzuP2hXZFpJ/GsxgGJ0ox1aoFWsDXyyxqCs26+ydmagFN/rRjymJ1898bzGzmQE0HCZpmk5A0RFIv8Pn0WYPsiu6t/Rsj6PauVTwffTSzGAGZhUG2F06hEc9ibS7OPMNp6ErYFlKavo7MkhmTqCxZ/jwzGA9Hx82H2BZSw1NTN9Gx8ycHkajU/7M+jInsDC7DiaEmo1bNl1AMr9ASFgqVu9MCTIzoGUimXVAnnaN0PdBBDCCYbEtMk6wkpQwIG0sn0PQIUF4GsTwLSIFKNqF6DVrQq+IWVrQDxAYQC/1SsYOI4pOxKZrfifiUSbDUisif7XlpGIPufXd/uvdvZm760M0no1FZcnrzUdjw7au3vu/BVgAFLXeuTxhTXVAAAAAElFTkSuQmCC ") left top no-repeat;
        }

        html[dir=rtl] a {
          background-position-x: right;
        }

        #listingParsingErrorBox {
          border: 1px solid black;
          background: #fae691;
          padding: 10px;
          display: none;
        }
      </style>
    <body>
      <div id="listingParsingErrorBox" i18n-values=".innerHTML:listingParsingErrorBoxText">糟糕!Google Chrome無法解讀服務器所發送的數據。請<a >報告錯誤</a>,并附上<a href="LOCATION">原始列表</a>。</div>
      <span id="parentDirText" style="display:none" i18n-content="parentDirText">[上級目錄]</span>
      <h1 id="header" i18n-content="header">C:\dev\ 的索引</h1>
      <table>
        <thead>
          <tr class="header" id="theader">
            <th i18n-content="headerName" onclick="javascript:sortTable(0);">名稱</th>
            <th class="detailsColumn" i18n-content="headerSize" onclick="javascript:sortTable(1);">大小</th>
            <th class="detailsColumn" i18n-content="headerDateModified" onclick="javascript:sortTable(2);">修改日期</th>
          </tr>
        </thead>
        <tbody id="tbody">
          <tr>
            <td data-value=".."><a class="icon up" href="#">[上級目錄]</a></td>
            <td class="detailsColumn" data-value="0"></td>
            <td class="detailsColumn" data-value="0"></td>
          </tr>
        </tbody>
      </table>
    </body>
    <script src="/views/jquery.min.js"></script>
    <!-- 3、通過瀏覽器發送一個異步請求到服務器,去請求當前服務器的目錄數據 -->
    <script>
      $.ajax({
        url: '/getPath',
        type: 'GET',
        dataType: 'json',
        success: function(data){
          // console.log(data);
          // 通過js動態生成html代碼
          var str = '';
          for(var i = 0; i < data.length; i ++) {
            str += "<tr>";
            str += '<td data-value="播放器/"><a class="icon dir" href="#">'+ data[i] +'</a></td>';
            str += '<td class="detailsColumn" data-value="0"></td>';
            str += '<td class="detailsColumn" data-value="1489037313">2018/8/11 下午1:28:33</td>';
            str += '</tr>';
          }
          // 將data動態渲染到頁面上
          $('#tbody').append(str);
        }
      })
    </script>

(7)使用fs核心模塊來判斷路徑是否為文件&文件夾

    var fs = require('fs');
    var str = './01.js';
    // 可以使用fs核心模塊中的方法來進行判斷
    // fs.stat用來判斷路徑的狀態
    //  path:要判斷的路徑
    //  callback:判斷之后的回調函數
    //      err:異常信息
    //      stats:狀態對象(obj)
    //          size:大小
    //              如果是文件有大小
    //              如果是目錄,沒有大小
    //          mtime:內容被修改的時間
    fs.stat(str, function(err, stats){
        if(err) {
            return console.log(err.message);
        }
        // console.log(stats);
        if(stats.isFile()) {
            console.log(str + '是文件');
            console.log(stats.size);
        } else {
            console.log(str + '是目錄');
        }
    });

(8)仿apach的目錄瀏覽功能-服務器渲染

    var http = require('http');
    var fs = require('fs');
    //下載使用npm下載art-template 第三方包,用來渲染數據到html結構中
    var template = require('art-template');
    // 創建一個服務器
    var server = http.createServer();
    // 設置請求事件
    server.on('request', function (req, res) {
        // 如果讀取根目錄,并數據 + html結構響應回去
        var url = req.url;
        // 判斷
        if (url === '/') {
            // 得到html結構
            //  readFile讀取出來 的內容是十六進制的buffer數組
            fs.readFile('./views/index.html', function (err, data) {
                if (err) {
                    return console.log(err.message);
                }
                // res.end(data);
                // 得到數據
                fs.readdir('./', function (err1, files) {
                    if (err1) {
                        return console.log(err1.message);
                    }
                    // 區分文件和文件夾
                    var fileArr = [];
                    var dirArr = [];
                    var count = 0;
                    for (var i = 0; i < files.length; i++) {
                        (function (i) {
                            fs.stat(files[i], function (err2, stat) {
                                count ++;
                                if (err2) {
                                    return console.log(err2.message);
                                }
                                if (stat.isFile()) {
                                    fileArr.push({
                                        name: files[i],
                                        type: 'file',
                                        size: stat.size,
                                        time: stat.mtime
                                    });
                                } else {
                                    dirArr.push({
                                        name: files[i],
                                        type: 'dir',
                                        size: stat.size,
                                        time: stat.mtime
                                    });
                                }
                                if (count === files.length) {
                                    var newArr = dirArr.concat(fileArr);
                                    // newArr 就是我們要的數據對象
                                    // 將數據與結構結合
                                    // 數據: newArr [{name: ,type:'',size:'',time:''}]
                                    // 結構:data
                                    // 問題:如何裝飾數據與結構結合:
                                    //    找第三方包:art-template可以用來幫助我們完成這個操作
                                    var htmlStr = template.render(data.toString(), {
                                        newArr: newArr
                                    });
                                    res.end(htmlStr);
                                }
                            });
                        })(i);
                    }
                })
            });
        }
    })
    // 開啟服務器
    server.listen(3000, function () {
        console.log('running');
    });

3、 Node中的模塊系統

(1)什么是模塊?

  • 隨著項目的推進、邏輯的復雜,不可能把所有js代碼都寫在一個文件中
  • 一個js文件就是一個具有獨立功能的模塊
  • Node中按照模塊去劃分功能,有一套模塊加載機制:
    • module.exports導出模塊成員
    • require()導入模塊成員

(2)導入模塊(require方法)

  • 作用:加載并執行模塊中的代碼!!!!

  • 使用require() 方法的()中可以寫:

        // 1. node自帶的模塊,如fs、http
        require('fs');
        // 2. 通過路徑引入的自己的js文件
        require('./foo.js');
    

    注意事項

    require() 加載模塊是同步加載!!

    ② 引入自己的js文件時,路徑中的./或者../不能省略

    ③ 如果省略,require() 會把它當成是一個node自帶的模塊

    報錯信息不要害怕!!!一定要去看!!!

    ④ 模塊后綴.js 可以省略不寫

(3)導出模塊

模塊中定義的變量是局部的,

那如何在A模塊使用B模塊中定義好的變量呢?

這個問題, 就涉及到了模塊加載機制

導出模塊中的變量
  • 每個模塊中都有一個 module 對象
  • module對象中有一個exports對象
  • 在CommonJS 中規定:一個模塊返回數據可以通過module.exports和rexports兩個關鍵字來返回
  • 模塊最終返回的僅僅只是 modules.exports
  • exports 僅僅只是 modules.exports的一個引用
  • 我們可以把需要導出的成員都掛載到module.exports 對象上
驗證module.exports與exports的關系:
    1)console.log(module.exports === exports);
    2)exports.a = 123;
    3)exports = fucntion() {}
    4)exports = modules.exports =  fucntion() {}
導入模塊foo.js
    var a = 10;
    var b = 20;
    function add(){};

    module.exports.a = a;
    module.exports.b = b;
    module.exports.add = add;

    // 可以理解為在模塊的末尾有這樣一句代碼: return module.exports;
    // return module.exports;
導入模塊main.js
    // 1 導入模塊
    var foo = require('./foo.js');
    // 2 使用模塊中的成員                  
    console.log(foo.add);
    console.log(foo.a);

(4)模塊加載機制

  • require關鍵字:

    • 可以幫助一個模塊加載另一個模塊
  • 優先從緩存中加載

    • Node 加載模塊時,如果這個模塊已經被加載過了,則會直接緩存起來,將來再次引用時不會再次加加載這個模塊(即:如果一個模塊被加載兩次,則模塊中的代碼只會被執行一次)
    • 加載模塊本質上是加載模塊中的modules.exports值
  • 核心模塊

    • 先去緩存中看下是否存在,如果有,直接拿來使用
    • 如果沒有,則加載
  • 自定義模塊

    • 以 './' 或者 '../' 或者 'c:/xxx' 類似于這樣的標識路徑作為加載名
  • 第三方模塊:包

    • 先在當前文件的模塊所屬目錄去找 node_modules目錄
    • 如果找到,則去該目錄中找 moment 目錄
    • 如果找到 moment 目錄, 則找該目錄中的 package.json文件
    • 如果找到 package.json 文件,則找該文件中的 main屬性
    • 如果找到main 屬性,則拿到該屬性對應的文件
    • 如果找到 moment 目錄之后,
      • 沒有package.json
      • 或者有 package.json 沒有 main 屬性
      • 或者有 main 屬性,但是指向的路徑不存在
      • 則 node 會默認去看一下 moment 目錄中有沒有 index.js ,index.node, index.json 文件
    • 如果找不到index 或者 找不到 moment 或者找不到 node_modules
    • 則進入上一級目錄找 node_moudles 查找(規則同上)
    • 如果上一級還找不到,繼續向上,一直到當前文件所屬磁盤的根目錄
    • 如果到磁盤概目錄還沒有找到,直接報錯
    核心模塊:
      由 Node 本身提供,通過唯一的模塊標識名進行加載
      核心模塊本質上也是文件模塊,它已經被編譯到可執行文件中了
    第三方模塊
    用戶自定義模塊
    
  • 模塊的兼容處理

    • 有些模塊即能在瀏覽器端使用,又能在服務器端使用,是因為它們作了兼容處理(如:moment)

(5)模塊分類及第三方模塊(包)的使用

  • 核心模塊
    • Node 本身提供,例如fshttp
  • 自定義(用戶)模塊
    • 自己寫的.js文件,按照路徑來加載,注意 ./ 或者 ../ 不能省略
  • 第三方模塊
    1. npm網站找包(模塊)
    2. 打開cmd, 使用命令 npm install 包名 安裝包
    3. 在需要使用的位置, 通過require('第三方包名') 加載包
    4. 看文檔調用 API

4、art-template 第三方包

(1)使用art-template

    // 1、遍歷對象
    // var template = require('art-template');
    // // 有html結構
    // var str = '<ul><li><%=name%></li><li>{{age}}</li></ul>';
    // // 也有數據
    // var obj = {
    //     name: '張三',
    //     age: 18
    // };
    // // 結合
    // //  render可以結合html結構和數據
    // //      str:結構
    // //      obj:數據
    // var htmlStr = template.render(str, obj);
    // console.log(htmlStr);

    // 2、遍歷數組
    var template = require('art-template');
    // 準備結構
    var str = '<ul>{{each arr}}<li>{{$index}}----{{$value.name}}----{{$value.age}}</li>{{/each}}</ul>';
    // 數據
    var arr = [
        {name: '張三', age: 18},
        {name: '李四', age: 30}
    ]
    // 結合
    var htmlStr = template.render(str, {
        arr: arr
    });
    console.log(htmlStr);

    //需要結合
    //  art-template的使用步驟:
    //  1、下載
    //  2、在html結構中書寫art-template的語法
    //      輸出:
    //          {{name}}
    //      條件:
    //          {{if 條件}} ... {{else if 條件}}... {{else}}
    //      循環
    //          {{each 數據}} {{$index}} {{$value}}  {{/each}}
    //  3、通過nodejs代碼將結構與數據結合起來

(2)使用url核心模塊來將參數轉為對象

    // url核心模塊可以幫助我們將路徑后面的參數得到
    //  url: http://192.168.11.1:8080/abc/index.html?name=abc&age=18

    // 引用
    var url = require('url');
    // pase路徑
    var strUrl = 'http://192.168.11.1:8080/abc/index.html?name=abc&age=18';
    var urlObj = url.parse(strUrl, true);
    console.log(urlObj);

5、npm包管理器

(1) npm 是什么

  • npm 全稱 Node Package Manager,它的誕生是為了解決 Node 中第三方包共享的問題。 和瀏覽器一樣,由于都是 JavaScript,所以前端開發也使用 npm 作為第三方包管理工具。 例如大名鼎鼎的 jQuery、Bootstrap 等都可以通過 npm 來安裝。 所以官方把 npm 定義為 JavaScript Package Manager

  • yarn也是包管理器

(2)npm 命令行工具

  • 只要你安裝了 node 就已經安裝了 npm,可以在cmd中進行npm的指令操作

        // 檢驗npm是否安裝成功
        npm --version
    
  • 常用命令

        // 在項目中初始化一個 package.json 文件,凡是使用 npm 來管理的項目都會有這么一個文件
        // 只敲一次就可以!
        npm init   
    
        // 跳過向導,快速生成 package.json 文件,簡寫是npm init -y
        npm init --yes
    
        // 一次性安裝 dependencies 中所有的依賴項,簡寫是 npm i
        npm install
    
        // 安裝指定的包,可以簡寫為 npm i 包名
        // npm 5 以前只下載,不保存依賴信息,如果需要保存,則需要加上 --save 選項
        // npm 5 以后就可以省略 --save 選項了
        npm install 包名
    
        // 一次性安裝多個指定包
        npm install 包名 包名 包名 ...
    
        // 安裝指定版本的包!!!
        npm install jquery@版本號
        // 如果不指定版本號,默認安裝最新穩定版本
    
        // 卸載指定的包
        npm uninstall 包名
    
        // 查看使用幫助
        npm help
    
        // 查看某個命令的使用幫助
        // 例如我忘記了 uninstall 命令的簡寫了,這個時候,可以輸入 npm uninstall --help 來查看使用幫助
        npm 命令 --help
    

(3)package.json

  • 我們的項目會放到云端的倉庫中,例如 github ,第三方包沒有上傳的意義,我們只需要把我們的源碼放到云端倉庫,node_modules 目錄中存儲的就是第三方包(不用擔心丟失問題),如果沒有 package.json 文件則你就找不回來了。

  • 建議每一個項目都要有一個 package.json 文件(包描述文件,就像產品的說明書一樣),給人踏實的感覺最重要的就是保存這個項目的第三方依賴信息(因為我們不需要提交第三方包到我們的云端倉庫,只需要提交我們自己的代碼),有了這個文件中的依賴信息結合 npm install 命令我們就可以放心了。

  • 這個文件可以通過 npm init 的方式來自動初始化出來。

      npm init
    
  • 對于咱們目前來講,最有用的是那個 dependencies 選項,可以用來幫我們保存第三方包的依賴信息。

  • 如果你的 node_modules 刪除了也不用擔心,我們只需要:npm install 就會自動把 package.json 中的 dependencies 中所有的依賴項都下載回來。

  • 建議每個項目的根目錄下都有一個 package.json 文件

    • 不同的項目有不同依賴,各自保存各自的
  • 執行 npm install 包名的的時候可以加上--save

      --save
    

    這個選項,目的是用來保存依賴項信息

    • npm 5 以前不會自動保存依賴信息到 package.json 文件中,必須手動加 --save 選項才可以
    • npm 5 以后不需要加 --save 選項了,因為它會自動保存依賴項

(4) package-lock.json

  • npm 5 以前是不會有 package-lock.json 這個文件的。
  • 當你安裝包的時候,npm 都會生成或者更新 package-lock.json 這個文件。
  • npm 5 以后的版本安裝包不需要加 --save 參數,它會自動保存依賴信息
  • 當你安裝包的時候,會自動創建或者是更新 package-lock.json 這個文件
  • package-lock.json 這個文件會保存 node_modules 中所有包的信息(版本、下載地址)
    • 這樣的話重新 npm install 的時候速度就可以提升

三、ECMAScript 6

一般情況下不建議在window環境下面使用ES6

1、 嚴格模式

    'use strict'

2、 申明一個變量(let)

  • let 申明的變量不存在變量提升

        //console.log(a); // undefined
        //var a = 123;
        //console.log(a); // 123
        //等同于
        var a;
        console.log(a);
        a = 123;
        console.log(a);
        console.log(a);
        let a = 123;
        console.log(a);
    
  • let 申明的變量不允許重復聲明

        var a = 123;
        var a = 456;
        console.log(a);
        let a = 123;
        let a = 456;
        console.log(a);
    
  • let 申明的變量存在塊級作用域

    • 可以用來解決閉包問題
        // 塊級作用域:來自于后臺
        //  特點:凡是大括號包裹的部分都是一個單獨的作用域,我們把這個作用域叫做塊級作用域, 塊級作用域之間不會相互影響
        if (true) {
            let a = 123;
            console.log(a);
        } 
        // if (true) {
        //     console.log(a);
        // }
        console.log(a);
    

3、 申明一個常量 (const)

  • contst申明的常量:一旦設置無法修改

    • 可以改變對象中的屬性
  • contst申明的常量:一旦設置不可重復聲明

        // const a = 123;
        // a = 456;
        // console.log(a);
        // 值類型(簡單類型)
        //  簡單類型的常量一旦設置無法修改
        // 引用類型(復雜類型)
        //  一旦賦值只要不是改變引用還是可以修改的
        const obj = {
            name: "張三"   
        }
        // 修改引用類型的引用地址
        // obj = {};// 如果修改引用地址會報錯
        obj.name = "李四";// 修改的是地址中對應對象的屬性:
    
        console.log(obj);
    

4、 字符串的一些擴展方法的使用

  • includes() :返回布爾值,表示是否找到了參數字符串

  • startsWith() :返回布爾值,表示是否找到了參數字符串

  • endsWith() :返回布爾值,表示參數字符串是否在源字符串的尾部

  • repeat():返回一個新字符串,表示將原字符串重復n次

        var s = "hello world";
        s.startsWith('hello') //true
        s.startsWith('world', 6) //true   ,表示從第6個開始后面的字符是 world
        s.endWith('hello', 5) //true ,表示前5個字符是hello
        'x'.repeat(2)  // “xx”
        'hello'.repeat(2)  // “hellohello”
        'ivan'.repeat(0)  // “”
    

5、 模板字符串

  • 使用“`”來定義模板字符串

  • 在模板字符串中可以保持變量的結構,可以識別換行

  • 在模板字符串中可以直接使用js代碼格式:${ code }

        var obj = {
            name: '張三',
            age: 18
        }
        // var a = '<ul><li>' + obj.name + '</li><li>' + obj.age + '</li></ul>';
        // console.log(a);
    
        // 模板字符串:
        //  1.0 拼接字符:輸出變量
        // var a = `<ul><li> ${obj.name} </li><li> ${obj.age} </li></ul>`;
        // console.log(a);
        //  2.0 識別換行
        var a = `<ul>
            <li>${obj.name}</li> 
            <li>${obj.age}</li>
        </ul>`;
        console.log(a);
    

6、 解構賦值

(1) 字符串的解構賦值

  • var [a,b,c] = 'xyz';
    const [a, b, c, d, e] = 'hello';
    a // "h"
    b // "e"
    c // "l"
    d // "l"
    e // "o"

(2)對象解構

  • var {name, age} = {name: 'zs', age: 18}
  • 給對象屬性設置別名
    • var {name: myName, age} = {name: 'zs', age: 18}
  • 屬性設置默認值(如果對旬中有這個屬性,并且值不為undefined,那么這個屬性的值為對象中
    • var {name='ls', age} = {name: 'zs', age: 18}的值,如果不存在或者是值為undefined,那么值為:默認值)
    var obj = {
      name: '張三',
      age: 18
    }
    var {name, age} = obj;
    name // 張三
    age  // 18      
  • 無順序問題

(3) 數組的解構

  • var [x,y,z] = [1,2,3];
    var arr = ['a', 'b', 'c']
    var [x, y, z] = arr;
    x // 'a'
    y // 'b'
    z // 'c'
  • 有順序問題
  • 被賦值的變量和數組中的元素個數可以不相等

(4)其它特點

  • 可以指定默認值
  • 解構出來的元素不一定要與前面的對象一一對應
  • 可以給解構出來的元素設置別名

7、 箭頭函數的推演

  • 作用:在nodejs中我們大量使用了回調函數,每次回調函數都用匿名函數來實現,ES6中為了能夠讓我們書寫匿名函數速度加快,就提出了箭頭函數,用來簡化匿名函數

        寫法1: arr.sort(function(x, y){return x - y ;});
        寫法2:arr.sort((x, y) => {return x - y ;});
        寫法3:arr.sort((x, y) => x - y);
    
  • 箭頭函數的其它寫法

        如果參數只有一個,可以將()省略    // arr.map(c => c + 1);
        如果沒有參數,則一定能要寫上()     // ()=> console.log(‘a’)
        如果多于一個參數,每個參數之間用逗號分隔   (x, y) => { ... }
        如果方法體只有一句代碼,可以省略{} 和分號,如果有返回可以省略return
        如果方法體多于一句代碼,則不能省略{} ,每句代碼使用 分號分隔
    
  • 箭頭函數的一些特點

    • 箭頭函數沒有自己的this,函數體內部寫的this,指向的是外層代碼塊的this

          //匿名函數
          var obj = {
              name: '張三',
              age: 18,
              sayHi: function(){
                  console.log(`大家好,我叫${this.name},今年${this.age}歲了`);
              }
          }
          obj.sayHi();
          // 箭頭函數
          this.name = '李四';
          this.age = 20;
          // window
          var obj = {
              name: '張三',
              age: 18,
              sayHi: () => {// => goes to
                  console.log(`大家好,我叫${this.name},今年${this.age}歲了`);
              }
          }
          obj.sayHi();
      
    • 箭頭函數內部的this是定義時所在的對象,而不是使用時所在的對象并且不會改變

          // 匿名函數
          var obj = {
              name: '張三',
              age: 18,
              sayHi: function(){
                  console.log(`大家好,我叫${this.name},今年${this.age}歲了`);
              }
          }
          var newObj = {
              name: '李四',
              age: 40
          }
          // 可以通過apply和call動態改變this的指向
          obj.sayHi.apply(newObj);
          // 箭頭函數
          this.name = '李四';
          this.age = 30;
          var obj = {
              name: '張三',
              age: 18,
              sayHi: ()=>{
                  console.log(`大家好,我叫${this.name},今年${this.age}歲了`);
              }
          }
          var newObj = {
              name: '范冰冰',
              age: 40
          }
          // 函數在定義時this指向的是obj上一層中的this name = '王五'
          // obj.sayHi();
          // 可以通過apply和call動態改變this的指向
          // 調用時使用newObj來調用,并沒有改變this的指向
          obj.sayHi.apply(newObj);
      
    • 箭頭箭頭函數不能用作構造函數

          // 普通構造函數
          var GirlFirend = function(name, age){
              this.name = name;
              this.age = age;
          }
          var g1 = new GirlFirend('張三', 30);
          console.log(g1.name, g1.age);
      
          var GirlFirend = (name, age) => {
              this.name = name;
              this.age = age;
          }
          var g2 = new GirlFirend('李四', 39);
          console.log(g2.name, g2.age);
      
    • 箭頭函數內部不存在arguments,箭頭函數體中使用的arguments其實指向的是外層函數的arguments

          //  什么是arguments:函數在使用時的實參
          // // 普通函數
          // var fn = function () {
          //     // 將所有的傳入的實參打印出來
          //     console.log('-----fn-------');
          //     console.log(arguments);
          //     console.log('-----fn-------');
          //     return function () {
          //         console.log('-----fn1-------');
          //         console.log(arguments);
          //         console.log('-----fn1-------');
          //     }
          // }
          // var fn1 = fn(1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
          // fn1(1, 2, 3, 4, 5);
          // // fn 1,2,3,4,5,6,7,8,9,0
          // // fn1 1,2,3,4,5
          // 箭頭函數
          var fn = function () {
              // 將所有的傳入的實參打印出來
              console.log('-----fn-------');
              console.log(arguments);
              console.log('-----fn-------');
              return  () => {
                  console.log('-----fn1-------');
                  console.log(arguments);
                  console.log('-----fn1-------');
              }
          }
          var fn1 = fn(1,2,3,4,5,6,7,8,9,0);
          fn1(1,2,3,4,5);
      

8、 對象中屬性的簡寫

    //es5中對象: {add:add, substrict:substrict}
    //es6中對象: {add, substrict}  注意這種寫法的屬性名稱和值變量是同一個名稱才可以簡寫,否則要想es5那樣的寫法,例如: {addFun:add}
    var name = '李四';
    var age = 20;
    // ES6之前
    var obj = {
        name: name,
        age: age
    }
    //ES6之后
    var obj = {
        name,
        age
    }
    console.log(obj.name, obj.age);

9、 對象中方法的簡寫

    //es5中對象: {add:function(){}, substrict:function(){}}
    //es6中對象: {add(){}, substrict(){}}
    //ES6之前
    var obj = {
        name,
        age,
        sayHi: function(){
            console.log(`大家好,我叫${this.name}, 今年${this.age}歲了`);
        }
    }
    // ES6之后
    var obj = {
        name,
        age,
        sayHi(){
            console.log(`大家好,我叫${this.name}, 今年${this.age}歲了`);
        }
    }
    obj.sayHi();

10、promise

  • 含義:承諾(答應去做,但是還沒有做)

  • 來由:ES6之前是沒有promise的,這個玩意是由前端社區提出,在ES6中成為一個標準

  • 作用:它是用來簡化回調函數的多層嵌套

  • 使用:

      1)要使用promise必須先根據promise構造函數創建一個函數對象
          在promise的構造函數中有兩個參數:resolve  reject    這兩個參數是兩個函數
              這兩個參數是與promise中的狀態對應:
                  pending(進行中)
                  fulfilled(已成功)
                  rejected(已失敗)
              狀態的切換有兩種方式:
                  pending---->fulfilled 操作成功   --->會執行resolve中的方法
                  pending---->rejected 操作失敗    --->會執行reject中的方法
          在使用promise的時候可以在后面接無數個then方法,then方法可以傳遞參數
              參數一般分為三種類型:
                  1)undefined
                  2)普通數據:字符串,數字,boolean,數組,對象
                  3)promise對象
      2)使用then方法執行promise
      3)在promise中提供一個統一處理錯誤的方法:catch
    
    • 創建一個promise 對象

          var p = new Promise(function(resolve, reject){
              if () {
                  // 如果成功執行reject
              } else{
                  // 如果失敗執行resolve
              }
          });
      
      • resolve: 成功后執行
      • reject: 失敗后執行

      執行promise的代碼:使用p.then()方法執行

      • then(resolve, reject ) 方法可以執行promise的代碼
        • 特點:可以無限調用then方法
      • 統一處理捕獲異常的方法: catch()
    • then方法返回值:

      • 每個then方法執行完成以后,會將返回值交給下一個then方法,如果沒有返回值,其實返回的是undefined
      • 沒有返回值
        • 如果沒有返回值,返回值為undefined
      • 返回值為:數字,字符串,對象,數組....
        • 下面的then方法可以直接通過resolve方法的參數接收到
      • 返回值為一個新的promise對象:
        • 將來下面緊跟的then方法其實是執行上面返回的promise對象的then方法
    // 需求:依次將三個文件中的內容讀取出來并且輸出: 0 ~ 1 ~ 2
    var fs = require('fs');
    // 讀取0文件
    fs.readFile('./file/0.txt', function (err0, data0) {
        if (err0) {
            return console.log(err0.message);
        }
        console.log(data0.toString());
        // 讀取1文件
        fs.readFile('./file/1.txt', function (err1, data1) {
            if (err1) {
                return console.log(err1.message);
            }
            console.log(data1.toString());
            // 讀取2文件
            fs.readFile('./file/2.txt', function (err2, data2) {     
                if (err2) {
                    return console.log(err2.message);
                }
                console.log(data2.toString());
            });
        });
    });
    // 回調地獄

使用promise解決回調地獄

    // var p = new Promise(function(reject, resolve){
    //     if () {
    //         // 如果成功執行reject
    //     } else{
    //         // 如果失敗執行resolve
    //     }
    // });

    // // 需求:依次將三個文件中的內容讀取出來并且輸出: 0 ~ 1 ~ 2
    // 創建一個promise對象讀取0文件
    var fs = require('fs');
    // 一旦Promise被創建:
    //      promise的狀態為: pending 等待
    //      promise執行以后,狀態會發生改變
    //          pending -- fulfilled 說明執行成功  執行resolve方法
    //          pending -- rejected 說明執行失敗  執行reject
    //          一旦對應的狀態確定,就無法改變
    var p0 = new Promise(function (resolve, reject) {
        fs.readFile('./file/0.txt', function (err, data) {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
    // 讀取1文件
    var p1 = new Promise(function (resolve, reject) {
        fs.readFile('./file/1.txt', function (err, data) {
            if (err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
    // 調用:than
    p0
        .then(data => {
            console.log(data.toString());
            // 不返回值任何內容:返回值 為undefined
        })
        .then((res) => {
            console.log(res); // undeinfed
            console.log('第二個then方法'); 
            // 返回值為非promise對象
            return 'abc';
        })
        .then((str) => {
            console.log(str);// abc
            console.log('第三個then方法');
            // 返回值為promise對象
            return p1;
        })
        .then((data) => {
            console.log(data.toString());
            console.log('第四個then方法');
        });

四、Express

1、起步

安裝:

    npm install --save express

hello world:

    // 引入express第三方包
    var express = require('express')
    // 創建一個express對象(express用來搭建web服務:相當于創建一了個服務器對象)
    var app = express()

    // 將來瀏覽器發送get請求,請求網站的根目錄,會執行后面的回調函數
    //  回調函數有兩個參數:
    //      req:請求對象
    //      res:響應對象
    app.get('/', function (req, res) {
      res.send('Hello World!')
    })

    // 開啟監聽:監聽3000商品,
    // 當開啟成功以后會執行后面的回調函數
    app.listen(3000, function () {
      console.log('Example app listening on port 3000!')
    })

基本路由:

    app.get('/', function (req, res) {
      res.send('Hello World!')
    })

    app.post('/', function (req, res) {
      res.send('Got a POST request')
    })

    app.put('/user', function (req, res) {
      res.send('Got a PUT request at /user')
    })

    app.delete('/user', function (req, res) {
      res.send('Got a DELETE request at /user')
    })

2、Express 中外置路由使用

router.js 文件代碼如下:

    // 1. 加載外置路由 express 模塊
    var express = require('express')

    // 2. 調用 express.Router() 方法,得到一個路由容器實例
    var router = express.Router()

    // 3. 為 router 添加不同的路由
    router.get('/', function (req, res) {
      res.send('hello express')
    })

    router.get('/add', function (req, res) {
      res.send('add')
    })

    // 4. 將 router 路由容器導出(路由對象暴露給外界)
    module.exports = router

在 app.js 文件中:

    // 使用express搭建服務
    var express = require('express')

    // 1. 加載路由模塊
    var router = require('./router')

    var app = express()

    // 2. 將路由模塊導出的路由容器 router 通過 app.use() 方法掛載到 app 實例上
    //    這樣的話 app 實例程序就擁有了 router 的路由了
    app.use(router)

    // 把所有的路由添加到一個新的文件中(外置路由)

    // 開啟監聽
    app.listen(3000, function () {
      console.log('running...')
    })

3、res.render方法

  • 使用:
    • 1.0 下載一個第三方包express-art-template(art-template)
    • 2.0 將這個第三方包注冊到express中:
      • app.engine('html', require('express-art-template'));
    • 3.0 可以直接使用render方法了
    • 注意點:
      • 1.0 render方法渲染的靜態文件默認會去views文件夾下去找,所以存放靜態文件的文件夾,一定要取名叫做views
      • 2.0 默認情況下render渲染的靜態文件后綴為art,但是在注冊art-template時可以修改為html
  • 注意點:
    • res中雖然已經有了render方法但是不能夠直接使用,它需要配合一些第三方包來進行使用(art-template的來使用)

index.js

    // res.render方法用來渲染html文件的
    var express = require('express');

    var app = express();
    // 注冊 art-template
    app.engine('html', require('express-art-template'));

    // 注冊路由
    app.get('/', (req,res) => {
        // 將靜態頁面響應回瀏覽器
        // 特點:它在渲染的時候會去一個固定的路徑下面去找對應的art文件
        res.render('index.html', {
            name: '張三',
            age: 18
        });
    })

    app.listen(3000, () => {
        console.log('running');
    });

index.html

    <body>
        <ul>
            <li>{{name}}</li>
            <li>{{age}}</li>
        </ul>
    </body>

4、在 Express 中處理靜態資源

  • 默認情況下express沒有幫助我們處理靜態文件,如果要處理靜態文件,我們需要在express中進行設置
  • 設置:
    • 訪問是不帶路徑
      • app.use(express.static('public'))
        • public 是靜態文件存放的位置
    • 按照原路徑來訪問
      • app.use('/imgs', express.static('public'))
      • 在訪問時必須帶上/imgs才能訪問到圖片
    • 訪問的路徑自己來設置
      • app.use('/abc', express.static('public'))
    app.use(express.static('public'))
    app.use(express.static('files'))

    app.use('/public', express.static('public'))
    app.use('/aaa', express.static('public'))

    app.use('/static', express.static(path.join(__dirname, 'public')))

index.js

    // 引入express第三方包
    var express = require('express');
    // 創建一個express對象(express用來搭建web服務:相當于創建一了個服務器對象)
    var app = express();

    // 設置靜態文件的路徑
    // app.use(express.static('imgs')); // 設置完成以后,將來可以訪問imgs中的靜態文件
    // app.use('/imgs', express.static('imgs'));
    app.use('/abc', express.static('imgs'));


    // 注冊模板引擎
    app.engine('html', require('express-art-template'));

    // 渲染靜態文件
    app.get('/', function (req, res) {
        res.render('index.html');
    });

    app.listen(3000, function () {
        console.log('running');
    });

index.html

    <body>
        <img src="/abc/4.jpg" alt="">
    </body>

5、express 的中間件

  • 什么是中間件:

    • 在express已有的結構上再加入一些自己的代碼
  • 參數:

    • req:請求報文對象
    • res:響應報文對象
    • next:函數,如果調用這個函數才會繼續的執行后續的流程代碼
  • 應用中間件

    • app.use()
      • app.use(function(){})
        • 無論發送任何請求都會執行的中間件
      • app.use('/path', function(){})
        • 只要在請求path路由時才會執行的中間件(無論GET/POST)
    • app.method()
      • app.get()
        • 在get請求時會執行的中間件
      • app.post()
        • 在post請求時會執行的中間件
        var express = require('express');
        var app = express();
    
        // 1、app.use 中間件
        //    特點:由于在設置的時候沒有加上任何參數,所以在這里任意請求過來都會執行這個中間件
        app.use(function(req, res, next){
            console.log('我是app.use');
            // 調用next執行后續的邏輯代碼
            next();
        });
    
        // 2、app.use 的變形
        //     特點:只要在請求/add的時候才會執行的中間件(不管是GET/POST)
        app.use('/add', function(req, res, next){
            console.log('我是app.use /add');
            next();
        });
    
        // 3、app.METHOD  = GET / POST
        //  (1)app.get 在express中其實路由就是一種中間件
        app.get('/add', function(req, res, next){
            console.log('我是中間件app.get /add');
            next();
        })
        //  (2)app.post
        app.post('/add', function(req, res, next){
            console.log('我是中間件app.post /add');
            next();
        });
    
        app.get('/', function(req, res){
            console.log('根目錄');
        });
    
        // 設置的路由
        app.get('/add', function(req, res){
            console.log('add');
        });
    
        app.post('/add', function(req, res){
            console.log('add');
        });
    
        app.get('/del', function(req, res){
            console.log('del');
        });
    
        app.listen(3000, function() {
            console.log('running');
        });
    
  • 路由級中間件

    • 它的用法與應用級中間件完全一樣,唯一的區別是
      • 應用級中間件的中間件是掛載在app對象上
      • 路由級中間件的中間件是掛載在router對象上的

    app.js

        // 開戶服務器
        var express = require('express');
        var router = require('./router');
        var app = express();
    
        //使用外置路由
        app.use(router);
    
        app.listen(3000, () => {
            console.log('running');
        });
    

    router.js

        var express = require('express');
        var router = express.Router();
    
        router.use(function(req, res, next){
            console.log('router.use');
            next();
        });
        router.use('/add', function(req, res, next){
            console.log('router.use /add');
            next();
        });
        router.get('/add', function(req, res, next){
            console.log('router.get /add');
            next();
        });
    
        router.get('/', function(req, res){
            console.log('根目錄');
        });
        router.get('/add', function(req,res){
            console.log('get/add');
        });
    
        // 暴露到外界
        module.exports = router;
    
  • 錯誤處理中間件

    • 可以用來處理網站發生的所有錯誤
        var express = require('express');
        var fs = require('fs');
        var app = express();
    
        app.get('/', function(req, res){
            console.log('根目錄');
        });
    
        app.get('/add', function(req, res){
            // 會報錯
            res.render('add.html');
        });
    
        // 統一錯誤處理的中間件
        app.use((err, req, res, next) => {
            // console.log("err:" + err.message);
            fs.readFile('./404.html', (err, data) => {
                res.end(data);
            });
        });
    
        app.listen(3000, function() {
            console.log('running');
        });
    
  • 內置中間件(靜態資源處理)

    • express.static(root,option)

五、使用nodejs來操作數據庫

1、連接數據庫

    // 使用nodejs來鏈接mysql
    // 1、引包
    var mySql = require('mysql');
    // 2、設置連接參數
    var connection = mySql.createConnection({
        host: 'localhost',
        user: 'root',
        password: '123456',
        database: 'class_one'
    });
    // 3、開啟連接
    connection.connect();
    // 4、執行sql語句
    var strSql = 'SELECT * FROM person';
    connection.query(strSql, (err, result, fields) => {
        if (err) {
           return console.log(err.message);
        }
        console.log(result);
        // [ RowDataPacket { id: 1, name: '張三', age: 18, gender: '男' },
        // RowDataPacket { id: 3, name: '王五', age: 22, gender: '男' } ]
        console.log(fields);
        // 存儲的是表中字段的信息
    });


2、增刪改查

    // 引包
    var mysql = require('mysql');
    // 建立連接
    var connection = mysql.createConnection({
        host: 'localhost',
        user: 'root',
        password: '123456',
        database: 'class_one'
    });
    // 開啟連接
    connection.connect();
    // 執行sql說到語句
    // 查詢
    //      result: 就是數據表中的數據
    //      fields: 數據表中的字段的屬性說明
    var strSql = 'SELECT * FROM person';
    // 新增
    //      result: OkPacket對象,這是一個成功對象,說明對數據庫的操作成功了
    //          affactedRows: 受影響的行數
    //      fields: undeinfed
    var strSql = 'INSERT INTO person (name, age, gender) VALUES ("趙六", "33", "男")';
    // 修改
    //      result: OkPacket對象,這是一個成功對象,說明對數據庫的操作成功了
    //          affactedRows: 受影響的行數
    //      fields: undeinfed
    var strSql = 'UPDATE person SET name="無情", age="80" WHERE id = 1'
    // 刪除
    //      result: OkPacket對象,這是一個成功對象,說明對數據庫的操作成功了
    //          affactedRows: 受影響的行數
    //      fields: undeinfed
    var strSql = 'DELETE FROM person WHERE id = 5'
    connection.query(strSql, (err, result, fields) => {
        if (err) {
            return console.log(err.message);
        }
        console.log(result);
        console.log(fields);
    });

六、cookie和session

1、cookie

  • nodejs 中設置cookie
    • res.wirteHead(200, {'set-cookie', 'uName=admin'});
  • 如果要設置硬盤cookie可以使用第三方中間件express: cookie
    • 可以通過這個中間件來操作cookie
      • 設置過期時間
      • 設置起效果的域名
      • 。。。

server.js(服務器)

    var fs = require('fs');
    var express = require('express');
    var bodyParser = require('body-parser')
    var app = express();



    app.use(bodyParser.urlencoded({ extended: false }))
    app.use(bodyParser.json())

    // 設置靜態文件
    app.use('/node_modules', express.static('node_modules'));

    // 1.0 設置路由
    // 設置一個首頁
    app.get('/', function(req, res){
        // 請求頭會帶上一個cookie
        // console.log(req.headers.cookie);
        // 驗證用戶是否登錄:只要判斷請求頭中是否帶有cookie
        if (req.headers.cookie === "uName=admin") {
            // 說明登錄過:
            res.send('用戶已經登錄過了,直接訪問根上當');
        } else{
            res.send('<script>alert("用戶還沒有登錄");window.location="/login"</script>');
        }
    });

    // 得到登錄頁面
    app.get('/login', function(req, res){
        fs.readFile('./views/login.html', function(err, data){
            res.end(data);
        });
    });

    // 完成提交數據的邏輯
    app.post('/login', function(req, res){
        // body-parser
        var uName = req.body.uName;
        var pwd = req.body.pwd;
        // 判斷
        if (uName === "admin" && pwd === "888") {
            // 要將用戶的登錄信息保存起來:用cookie來保存
            // 向響應報文頭中加入一段內容:cookie
            res.writeHead(200, {
                'set-cookie': 'uName=' + uName
            });
            // 登錄成功
            // res.json({
            //     status: 200,
            //     msg: '成功'
            // });
            res.end(JSON.stringify({
                status: 200,
                msg: '成功'
            }));
        } else{
            // 登錄失敗
            res.json({
                status: 500,
                msg: '不成功'
            });
        }
    });
    app.listen(3000, function() {
        console.log('running');
    });

./views/login.html(客戶端)

        <style>
            table {
                width: 400px;
                height: 200px;
                border-collapse: collapse;
                margin: 0 auto;
            }

            td {
                border: 1px solid #ccc;
            }
        </style>
    <body>
        <form id="form">
            <table>
                <tr>
                    <td>用戶名:</td>
                    <td><input type="text" id="uName" name="uName"></td>
                </tr>
                <tr>
                    <td>密 碼:</td>
                    <td><input type="text" id="pwd" name="pwd"></td>
                </tr>
                <tr>
                    <td></td>
                    <td><input type="button" id="login" value="登錄"></td>
                </tr>
            </table>
        </form>
    </body>
    <script src="../node_modules/jquery/dist/jquery.min.js"></script>
    <script>
        // 給登錄按鈕注冊事件
        $("#login").on('click', function (e) {
            e.preventDefault();// 阻止默認事件
            // 得到參數
            var params = $("#form").serialize();
            // var data = `uName=${$('#uName').val()}&pwd=${$('pwd').val()}`;
            // 將參數提交到服務器
           var ajax = $.ajax({
                url: '/login',
                type: 'POST',
                data: params,
                dataType: 'JSON'
            });
            // promise
            ajax.then(data => {
                // 判斷狀態
                if (data.status === 200) {
                    alert(data.msg);
                    // 跳轉到首頁
                    window.location = '/'
                } else {
                    // 跳轉到登錄頁面
                    alert(data.msg);
                    window.location('/login');
                }
            });
        })
    </script>

2、session

  • nodejs中不能直接使用session,如果要使用必須借助express的第三方中間件:cookie-session

    • 使用步驟:

      • 下載:npm i cookie-session

      • 引用:var cookieSession = require('cookie-session');

      • 注冊:

        app.use(cookieSession({
            name: 'session',
            keys: ['key1', 'key2']
        }))
        
      • 使用

        • 設置: req.session.鍵= 值;
        • 取值: req.session.鍵

server.js(服務器)

    var fs = require('fs');
    var express = require('express');
    var bodyParser = require('body-parser')
    var cookieSession = require('cookie-session');
    var app = express();

    app.use(bodyParser.urlencoded({ extended: false }))
    app.use(bodyParser.json())

    // 設置靜態文件
    app.use('/node_modules', express.static('node_modules'));

    // 注冊cookiesession
    app.use(cookieSession({
        name: 'session',
        keys: ['key1', 'key2']
    }))

    // 1、設置路由
    // 設置一個首頁
    app.get('/', function(req, res){
       // 得到session
       // console.log(req.session.uName);
       var session = req.session.uName;
       if (session) {
           res.send('用戶已經登錄過一');
       } else {
           res.send('<script>alert("用戶還沒有登錄");window.location="/login"</script>');
       }
    });

    // 得到登錄頁面
    app.get('/login', function(req, res){
        fs.readFile('./views/login.html', function(err, data){
            res.end(data);
        });
    });

    // 完成提交數據的邏輯
    app.post('/login', function(req, res){
        // body-parser
        var uName = req.body.uName;
        var pwd = req.body.pwd;
        // 判斷
        if (uName === "admin" && pwd === "888") {
            // 要將用戶的登錄信息保存起來:用session來保存
            req.session.uName = uName;
            res.end(JSON.stringify({
                status: 200,
                msg: '成功'
            }));
        } else{
            // 登錄失敗
            res.json({
                status: 500,
                msg: '不成功'
            });
        }
    });
    app.listen(3000, function() {
        console.log('running');
    });

./views/index.html(客戶端)

    <body>
        <form id="form">
            <table>
                <tr>
                    <td>用戶名:</td>
                    <td><input type="text" id="uName" name="uName"></td>
                </tr>
                <tr>
                    <td>密 碼:</td>
                    <td><input type="text" id="pwd" name="pwd"></td>
                </tr>
                <tr>
                    <td></td>
                    <td><input type="button" id="login" value="登錄"></td>
                </tr>
            </table>
        </form>
    </body>
    <script src="../node_modules/jquery/dist/jquery.min.js"></script>
    <script>
        // 給登錄按鈕注冊事件
        $("#login").on('click', function (e) {
            e.preventDefault();// 阻止默認事件
            // 得到參數
            var params = $("#form").serialize();
            // var data = `uName=${$('#uName').val()}&pwd=${$('pwd').val()}`;
            // 將參數提交到服務器
           var ajax = $.ajax({
                url: '/login',
                type: 'POST',
                data: params,
                dataType: 'JSON'
            });
            // promise
            ajax.then(data => {
                // 判斷狀態
                if (data.status === 200) {
                    alert(data.msg);
                    // 跳轉到首頁
                    window.location = '/'
                } else {
                    // 跳轉到登錄頁面
                    alert(data.msg);
                    window.location('/login');
                }
            });
        })

    </script>
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Node.js是目前非常火熱的技術,但是它的誕生經歷卻很奇特。 眾所周知,在Netscape設計出JavaScri...
    w_zhuan閱讀 3,641評論 2 41
  • 概要 64學時 3.5學分 章節安排 電子商務網站概況 HTML5+CSS3 JavaScript Node 電子...
    阿啊阿吖丁閱讀 9,315評論 0 3
  • Node.js是目前非常火熱的技術,但是它的誕生經歷卻很奇特。 眾所周知,在Netscape設計出JavaScri...
    Myselfyan閱讀 4,101評論 2 58
  • 風霜滿面的將軍下馬問路邊茶娘:“大嬸,你知道附近那個說話很溫柔的賣茶姑娘住在哪嗎”茶娘笑笑:“她呀,嫁了個好人家,...
    Fatestaywhite閱讀 457評論 0 0
  • 隨著九年級的到來,壓力越來越大,作業越來越多,自然就會導致睡覺的時間越來越少。 “今天作業咋又這么多啊,看來明...
    y加油閱讀 321評論 1 0