node.js(1)

1. Node.js是什么

1.1 概述

官網(wǎng):https://nodejs.org

  • Node 不適合從來沒有接觸過服務(wù)端的人學(xué)習(xí)
  • 如果想要真正的學(xué)好服務(wù)端,還是老牌的 Java、PHP 這些平臺
  • Node 不是特別適合入門服務(wù)端艇肴,但不代表 Node 不強(qiáng)大
  • Node 很厲害瞻讽,具有經(jīng)驗(yàn)的人可以玩兒的非常的牛
  • 不適合新手的原因就在于比較偏底層蚂会、而且太靈活
  • Java蚤霞、PHP 好入門的原因就在于:這些平臺屏蔽了一些底層
Node.js?是基于Chrome的V8 JavaScript引擎構(gòu)建的JavaScript運(yùn)行時(shí)(官方)
  • Node.js是JavaScript 運(yùn)行時(shí)
  • 通俗易懂的講东且,Node.js是JavaScript的運(yùn)行平臺(運(yùn)行環(huán)境)
  • Node.js既不是語言埃跷,也不是框架,它是一個(gè)平臺

1.2 特征:

1>構(gòu)建于Chrome的V8引擎之上

  • 代碼只是具有特定格式的字符串
  • 引擎可以認(rèn)識它心赶,幫你解析和執(zhí)行
  • Google Chrome的V8引擎是目前公認(rèn)的解析執(zhí)行JavaScript代碼最快的
  • Node.js的作者把Google Chrome中的V8引擎移植出來扣讼,開發(fā)了一個(gè)獨(dú)立的JavaScript運(yùn)行時(shí)環(huán)境

2>Node.js uses an envent-driven,non-blocking I/O mode that makes it lightweight and efficent.

  • envent-driven 事件驅(qū)動(dòng)
  • non-blocking I/O mode 非阻塞I/O模型(異步)
  • ightweight and efficent. 輕量和高效

3>Node.js package ecosystem,npm,is the larget scosystem of open sourcr libraries in the world

  • npm 是世界上最大的開源生態(tài)系統(tǒng)
  • 絕大多數(shù)JavaScript相關(guān)的包都存放在npm上,這樣做的目的是為了讓開發(fā)人員更方便的去下載使用
  • npm install jquery

1.3 瀏覽器中的JavaScript

  • EcmaScript
    • 基本語法
    • if
    • var
    • function
    • Object
    • Array
  • Bom
  • Dom

1.4 Node.js中的JavaScript

  • 沒有Bom缨叫,Dom
//報(bào)錯(cuò)
console.log(window)
console.log(document)
  • EcmaScript
    • 變量
    • 方法
    • 數(shù)據(jù)類型
    • 內(nèi)置對象
    • Array
    • Object
    • Date
    • Math
    • 模塊系統(tǒng)
      • 在 Node 中沒有全局作用域的概念
      • 在 Node 中椭符,只能通過 require 方法來加載執(zhí)行多個(gè) JavaScript 腳本文件
      • require 加載只能是執(zhí)行其中的代碼荔燎,文件與文件之間由于是模塊作用域,所以不會(huì)有污染的問題
        • 模塊完全是封閉的
        • 外部無法訪問內(nèi)部
        • 內(nèi)部也無法訪問外部
      • 模塊作用域固然帶來了一些好處销钝,可以加載執(zhí)行多個(gè)文件湖雹,可以完全避免變量命名沖突污染的問題
      • 但是某些情況下,模塊與模塊是需要進(jìn)行通信的
      • 在每個(gè)模塊中曙搬,都提供了一個(gè)對象:exports
      • 該對象默認(rèn)是一個(gè)空對象
      • 你要做的就是把需要被外部訪問使用的成員手動(dòng)的掛載到 exports 接口對象中
      • 然后誰來 require 這個(gè)模塊摔吏,誰就可以得到模塊內(nèi)部的 exports 接口對象
      • 還有其它的一些規(guī)則,具體后面講纵装,以及如何在項(xiàng)目中去使用這種編程方式征讲,會(huì)通過后面的案例來處理
    • 核心模塊
      • 核心模塊是由 Node 提供的一個(gè)個(gè)的具名的模塊,它們都有自己特殊的名稱標(biāo)識橡娄,例如
        • fs 文件操作模塊
        • http 網(wǎng)絡(luò)服務(wù)構(gòu)建模塊
        • os 操作系統(tǒng)信息模塊
        • path 路徑處理模塊
        • 诗箍。。挽唉。滤祖。
      • 所有核心模塊在使用的時(shí)候都必須手動(dòng)的先使用 require 方法來加載,然后才可以使用瓶籽,例如:
        • var fs = require('fs')

2. 安裝Node環(huán)境

  • 查看Node環(huán)境的版本號
  • 下載:https://nodejs.org/en/
  • 安裝:
    • 傻瓜式安裝匠童,一路next
    • 安裝過再次安裝會(huì)升級
  • 確認(rèn)Node環(huán)境是否安裝成功
    • 查看node的版本號:node --version
    • 或者node -v
  • 配置環(huán)境變量

3. 解析執(zhí)行JavaScript

  1. 創(chuàng)建編寫JavaScript腳本文件
  2. 打開終端,定位腳本文件的所屬目錄
  3. 輸入node 文件名執(zhí)行對應(yīng)的文件

注意:文件名不要用node.js來命名塑顺,也就是說除了node這個(gè)名字隨便起汤求,最好不要使用中文。

例如 node helloworld.js執(zhí)行該文件

4. 文件的讀寫

文件讀取:

  • 瀏覽器中的JavaScript是沒有文件操作能力的
  • 但是Node中的JavaScript具有文件操作能力

fs核心模塊(所有文件操作API都在該模塊):

  • fs是file-system的簡寫严拒,就是文件系統(tǒng)的意思

  • 在fs這個(gè)核心模塊中扬绪,就提供了所有文件操作相關(guān)的API

    • 例如 fs.readFile就是用來讀取文件的
    • fs.writeFile 用來寫文件
//  1.使用fs核心模塊
var fs = require('fs');
// 2.讀取文件
//    第一個(gè)參數(shù)就是要讀取的文件路徑
//    第二個(gè)參數(shù)是一個(gè)回調(diào)函數(shù)
fs.readFile('./data/a.txt',function(err,data){
   if(err){
        console.log('文件讀取失敗');
       // console.log(error)
       // 在這里就可以通過判斷 error 來確認(rèn)是否有錯(cuò)誤發(fā)生
   }
    else{
         console.log(data.toString()); //data為文件二進(jìn)制數(shù)據(jù)
    }
    // console.log(data);
})

  // <Buffer 68 65 6c 6c 6f 20 6e 6f 64 65 6a 73 0d 0a>
  // 文件中存儲(chǔ)的其實(shí)都是二進(jìn)制數(shù)據(jù) 0 1
  // 這里為什么看到的不是 0 和 1 呢裤唠?原因是二進(jìn)制轉(zhuǎn)為 16 進(jìn)制了
  // 但是無論是二進(jìn)制01還是16進(jìn)制挤牛,人類都不認(rèn)識
  // 所以我們可以通過 toString 方法把其轉(zhuǎn)為我們能認(rèn)識的字符

  
//  success :
//              data    數(shù)據(jù)
//              error   null
//  error :
//              data    null
//              error   就是錯(cuò)誤對象

文件寫入:

var fs = require('fs')

// $.ajax({
//   ...
//   success: function (data) {
    
//   }
// })

// 第一個(gè)參數(shù):文件路徑
// 第二個(gè)參數(shù):文件內(nèi)容
// 第三個(gè)參數(shù):回調(diào)函數(shù)

//    成功:
//      文件寫入成功
//      error 是 null
//    失敗:
//      文件寫入失敗
//      error 就是錯(cuò)誤對象
fs.writeFile('./data/你好.md', '大家好种蘸,給大家介紹一下墓赴,我是Node.js', function (error) {
  // console.log('文件寫入成功')
  // console.log(error)
  if (error) {
    console.log('寫入失敗')
  } else {
    console.log('寫入成功了')
  }
})

5. http

最簡單的http服務(wù):

  • 你可以使用 Node 非常輕松的構(gòu)建一個(gè) Web 服務(wù)器
  • 在 Node 中專門提供了一個(gè)核心模塊:http
  • http 這個(gè)模塊的職責(zé)就是幫你創(chuàng)建編寫服務(wù)器的
// 1. 加載 http 核心模塊
var http = require('http')

// 2. 使用 http.createServer() 方法創(chuàng)建一個(gè) Web 服務(wù)器
//    返回一個(gè) Server 實(shí)例
var server = http.createServer()

// 3. 服務(wù)器要干嘛?
//    提供服務(wù):對 數(shù)據(jù)的服務(wù)
//    發(fā)請求
//    接收請求
//    處理請求
//    給個(gè)反饋(發(fā)送響應(yīng))
//    注冊 request 請求事件
//    當(dāng)客戶端請求過來劈彪,就會(huì)自動(dòng)觸發(fā)服務(wù)器的 request 請求事件竣蹦,然后執(zhí)行第二個(gè)參數(shù):回調(diào)處理函數(shù)
server.on('request', function () {
  console.log('收到客戶端的請求了')
})

// 4. 綁定端口號顶猜,啟動(dòng)服務(wù)器
server.listen(3000, function () {
  console.log('服務(wù)器啟動(dòng)成功了沧奴,可以通過 http://127.0.0.1:3000/ 來進(jìn)行訪問')
})

發(fā)送響應(yīng)

var http = require('http')

var server = http.createServer()

// request 請求事件處理函數(shù),需要接收兩個(gè)參數(shù):
//    Request 請求對象
//        請求對象可以用來獲取客戶端的一些請求信息长窄,例如請求路徑
//    Response 響應(yīng)對象
//        響應(yīng)對象可以用來給客戶端發(fā)送響應(yīng)消息
server.on('request', function (request, response) {
  // http://127.0.0.1:3000/ /
  // http://127.0.0.1:3000/a /a
  // http://127.0.0.1:3000/foo/b /foo/b
  console.log('收到客戶端的請求了滔吠,請求路徑是:' + request.url)

  // response 對象有一個(gè)方法:write 可以用來給客戶端發(fā)送響應(yīng)數(shù)據(jù)
  // write 可以使用多次纲菌,但是最后一定要使用 end 來結(jié)束響應(yīng),否則客戶端會(huì)一直等待
  response.write('hello')
  response.write(' nodejs')

  // 告訴客戶端疮绷,我的話說完了翰舌,你可以呈遞給用戶了
  response.end()
})

server.listen(3000, function () {
  console.log('服務(wù)器啟動(dòng)成功了,可以通過 http://127.0.0.1:3000/ 來進(jìn)行訪問')
})

根據(jù)不同請求路徑冬骚,返回不同數(shù)據(jù)

var http = require('http')

// 1. 創(chuàng)建 Server
var server = http.createServer()

// 2. 監(jiān)聽 request 請求事件椅贱,設(shè)置請求處理函數(shù)
server.on('request', function (req, res) {
  console.log('收到請求了,請求路徑是:' + req.url)
  console.log('請求我的客戶端的地址是:', req.socket.remoteAddress, req.socket.remotePort)

  // res.write('hello')
  // res.write(' world')
  // res.end()

  // 上面的方式比較麻煩只冻,推薦使用更簡單的方式庇麦,直接 end 的同時(shí)發(fā)送響應(yīng)數(shù)據(jù)
  // res.end('hello nodejs')

  // 根據(jù)不同的請求路徑發(fā)送不同的響應(yīng)結(jié)果
  // 1. 獲取請求路徑
  //    req.url 獲取到的是端口號之后的那一部分路徑
  //    也就是說所有的 url 都是以 / 開頭的
  // 2. 判斷路徑處理響應(yīng)

  var url = req.url

  if (url === '/') {
    res.end('index page')
  } else if (url === '/login') {
    res.end('login page')
  } else if (url === '/products') {
    var products = [{
        name: '蘋果 X',
        price: 8888
      },
      {
        name: '菠蘿 X',
        price: 5000
      },
      {
        name: '小辣椒 X',
        price: 1999
      }
    ]

    // 響應(yīng)內(nèi)容只能是二進(jìn)制數(shù)據(jù)或者字符串           數(shù)字  對象  數(shù)組  布爾值
    res.end(JSON.stringify(products))
  } else {
    res.end('404 Not Found.')
  }
})

// 3. 綁定端口號,啟動(dòng)服務(wù)
server.listen(3000, function () {
  console.log('服務(wù)器啟動(dòng)成功喜德,可以訪問了山橄。。舍悯。')
})

6. node中js核心模塊

開發(fā)時(shí)參考官方API 航棱,

// 用來獲取機(jī)器信息的
var os = require('os')

// 用來操作路徑的
var path = require('path')

// 獲取當(dāng)前機(jī)器的 CPU 信息
console.log(os.cpus())

// memory 內(nèi)存
console.log(os.totalmem())

// 獲取一個(gè)路徑中的擴(kuò)展名部分
// extname extension name
console.log(path.extname('c:/a/b/c/d/hello.txt'))

7. IP地址和端口號

  • ip 地址用來定位計(jì)算機(jī)
  • 端口號用來定位具體的應(yīng)用程序
  • 所有需要聯(lián)網(wǎng)通信的應(yīng)用程序都會(huì)占用一個(gè)端口號
var http = require('http')

var server = http.createServer()

// 2. 監(jiān)聽 request 請求事件,設(shè)置請求處理函數(shù)
server.on('request', function (req, res) {
  console.log('收到請求了萌衬,請求路徑是:' + req.url)
  console.log('請求我的客戶端的地址是:', req.socket.remoteAddress, req.socket.remotePort)

  res.end('hello nodejs')
})

server.listen(5000, function () {
  console.log('服務(wù)器啟動(dòng)成功饮醇,可以訪問了。秕豫。驳阎。')
})

// require
// 端口號

var http = require('http')

var server = http.createServer()

server.on('request', function (req, res) {
  // 在服務(wù)端默認(rèn)發(fā)送的數(shù)據(jù),其實(shí)是 utf8 編碼的內(nèi)容
  // 但是瀏覽器不知道你是 utf8 編碼的內(nèi)容
  // 瀏覽器在不知道服務(wù)器響應(yīng)內(nèi)容的編碼的情況下會(huì)按照當(dāng)前操作系統(tǒng)的默認(rèn)編碼去解析
  // 中文操作系統(tǒng)默認(rèn)是 gbk
  // 解決方法就是正確的告訴瀏覽器我給你發(fā)送的內(nèi)容是什么編碼的
  // 在 http 協(xié)議中馁蒂,Content-Type 就是用來告知對方我給你發(fā)送的數(shù)據(jù)內(nèi)容是什么類型
  // res.setHeader('Content-Type', 'text/plain; charset=utf-8')
  // res.end('hello 世界')

  var url = req.url

  if (url === '/plain') {
    // text/plain 就是普通文本
    res.setHeader('Content-Type', 'text/plain; charset=utf-8')
    res.end('hello 世界')
  } else if (url === '/html') {
    // 如果你發(fā)送的是 html 格式的字符串呵晚,則也要告訴瀏覽器我給你發(fā)送是 text/html 格式的內(nèi)容
    res.setHeader('Content-Type', 'text/html; charset=utf-8')
    res.end('<p>hello html <a href="">點(diǎn)我</a></p>')
  }
})

server.listen(3000, function () {
  console.log('Server is running...')
})

8. Content-Type (響應(yīng)內(nèi)容類型)

解決node中亂碼問題 HTTP Content-type對照表

  • 在服務(wù)端默認(rèn)發(fā)送的數(shù)據(jù),其實(shí)是 utf8 編碼的內(nèi)容沫屡,但是瀏覽器不知道你是 utf8 編碼的內(nèi)容
  • 瀏覽器在不知道服務(wù)器響應(yīng)內(nèi)容的編碼的情況下會(huì)按照當(dāng)前操作系統(tǒng)的默認(rèn)編碼去解析饵隙, 中文操作系統(tǒng)默認(rèn)是 gbk
  • 解決方法就是正確的告訴瀏覽器我給你發(fā)送的內(nèi)容是什么編碼的
  • 在 http 協(xié)議中,Content-Type 就是用來告知對方我給你發(fā)送的數(shù)據(jù)內(nèi)容是什么類型
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
res.end('hello 世界')
  • Content-Type
    • 服務(wù)器最好把每次響應(yīng)的數(shù)據(jù)是什么內(nèi)容類型都告訴客戶端沮脖,而且要正確的告訴
    • 不同的資源對應(yīng)的 Content-Type 是不一樣金矛,具體參照:http://tool.oschina.net/commons
    • 對于文本類型的數(shù)據(jù),最好都加上編碼勺届,目的是為了防止中文解析亂碼問題
    • 通過網(wǎng)絡(luò)發(fā)送文件
      • 發(fā)送的并不是文件驶俊,本質(zhì)上來講發(fā)送是文件的內(nèi)容
      • 當(dāng)瀏覽器收到服務(wù)器響應(yīng)內(nèi)容之后,就會(huì)根據(jù)你的 Content-Type 進(jìn)行對應(yīng)的解析處理
// 1. 結(jié)合 fs 發(fā)送文件中的數(shù)據(jù)
// 2. Content-Type
//    http://tool.oschina.net/commons
//    不同的資源對應(yīng)的 Content-Type 是不一樣的
//    圖片不需要指定編碼
//    一般只為字符數(shù)據(jù)才指定編碼

var http = require('http')
var fs = require('fs')

var server = http.createServer()

server.on('request', function (req, res) {
  // / index.html
  var url = req.url

  if (url === '/') {
    // 肯定不這么干
    // res.end('<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>Document</title></head><body><h1>首頁</h1></body>/html>')

    // 我們要發(fā)送的還是在文件中的內(nèi)容
    fs.readFile('./resource/index.html', function (err, data) {
      if (err) {
        res.setHeader('Content-Type', 'text/plain; charset=utf-8')
        res.end('文件讀取失敗免姿,請稍后重試饼酿!')
      } else {
        // data 默認(rèn)是二進(jìn)制數(shù)據(jù),可以通過 .toString 轉(zhuǎn)為咱們能識別的字符串
        // res.end() 支持兩種數(shù)據(jù)類型,一種是二進(jìn)制故俐,一種是字符串
        res.setHeader('Content-Type', 'text/html; charset=utf-8')
        res.end(data)
      }
    })
  } else if (url === '/xiaoming') {
    // url:統(tǒng)一資源定位符
    // 一個(gè) url 最終其實(shí)是要對應(yīng)到一個(gè)資源的
    fs.readFile('./resource/ab2.jpg', function (err, data) {
      if (err) {
        res.setHeader('Content-Type', 'text/plain; charset=utf-8')
        res.end('文件讀取失敗想鹰,請稍后重試!')
      } else {
        // data 默認(rèn)是二進(jìn)制數(shù)據(jù)药版,可以通過 .toString 轉(zhuǎn)為咱們能識別的字符串
        // res.end() 支持兩種數(shù)據(jù)類型辑舷,一種是二進(jìn)制,一種是字符串
        // 圖片就不需要指定編碼了槽片,因?yàn)槲覀兂Uf的編碼一般指的是:字符編碼
        res.setHeader('Content-Type', 'image/jpeg')
        res.end(data)
      }
    })
  }
})

server.listen(3000, function () {
  console.log('Server is running...')
})

8. Node中的模塊系統(tǒng)

使用Node編寫應(yīng)用程序主要就是在使用:

在 Node 中何缓,模塊有三種:

  • 1 核心模塊

    • 文件操作的fs
    • http服務(wù)操作的http
    • url路徑操作模塊
    • path路徑處理模塊
    • os操作系統(tǒng)信息
  • 2 第三方模塊

    • art-template
    • 必須通過npm來下載才可以使用
  • 3 用戶自己編寫的文件模塊

    • 自己創(chuàng)建的文件

    • 相對路徑必須加 ./ ,可以省略后綴名

      相對路徑中的 ./ 不能省略还栓,否則報(bào)錯(cuò)

什么是模塊化

  • 文件作用域(模塊是獨(dú)立的歌殃,在不同的文件使用必須要重新引用)【在node中沒有全局作用域,它是文件模塊作用域】
  • 也就是說不引用蝙云,作用域只在文件內(nèi)生效氓皱,外部訪問不到內(nèi)部,內(nèi)部也訪問不了外部
  • 通信規(guī)則
    • 加載require
    • 導(dǎo)出exports

CommonJS模塊規(guī)范

在Node中的JavaScript還有一個(gè)重要的概念勃刨,模塊系統(tǒng)波材。

  • 模塊作用域

  • 使用require方法來加載模塊

  • 使用exports接口對象來導(dǎo)出模板中的成員

加載require

語法:

  var 自定義變量名 = require('模塊')
require 方法有兩個(gè)作用:
  1. 加載文件模塊并執(zhí)行里面的代碼
  2. 拿到被加載文件模塊中的exports導(dǎo)出的接口對象
//假裝同一目錄下有  a.js  b.js   c.js  三個(gè)文件
//a.js
console.log('a start')
console.log('./b.js')
console.log('a end')
//======================
//b.js
console.log('b start')
console.log('./c.js')
console.log('b end')
//======================
//c.js
console.log('ccc')

執(zhí)行

//執(zhí)行a.js
a start
b start
ccc
b end
a end
//執(zhí)行b.js
b start
ccc
b end

導(dǎo)出exports

  • Node中是模塊作用域,默認(rèn)文件中所有的成員只在當(dāng)前模塊有效

  • 對于希望可以被其他模塊訪問到的成員身隐,我們需要把這些公開的成員都掛載到exports接口對象中就可以了

  • exports 默認(rèn)是一個(gè)空對象

導(dǎo)出多個(gè)成員(必須在對象中):

exports.a = 123;
exports.b = function(){
    console.log('bbb')
};
exports.c = {
    foo:"bar"
};
exports.d = 'hello';

導(dǎo)出單個(gè)成員(拿到的就是函數(shù)廷区,字符串):

 module.exports = 'hello';

以下情況會(huì)覆蓋:

module.exports = 'hello';
//后者會(huì)覆蓋前者
module.exports = function add(x,y) {
    return x+y;
}

也可以通過以下方法來導(dǎo)出多個(gè)成員:

module.exports = {
    foo = 'hello',
    add:function(){
        return x+y;
    }
};
var ret = require('./b')
console.log(ret)   

console.log(export) //{}

export.foo = 'hello'
console.log(export)
// require 方法有兩個(gè)作用:
//    1. 加載文件模塊并執(zhí)行里面的代碼
//    2. 拿到被加載文件模塊導(dǎo)出的接口對象
//    
//    在每個(gè)文件模塊中都提供了一個(gè)對象:exports
//    exports 默認(rèn)是一個(gè)空對象
//    你要做的就是把所有需要被外部訪問的成員掛載到這個(gè) exports 對象中
var bExports = require('./b')
var fs = require('fs')

console.log(bExports.foo)  //hello
console.log(bExports.add(10, 30))  //40
console.log(bExports.age)   //18

bExports.readFile('./a.js')  //

fs.readFile('./a.js', function (err, data) {
  if (err) {
    console.log('讀取文件失敗')
  } else {
    console.log(data.toString())
  }
})

var foo = 'bbb'
// console.log(exports)

exports.foo = 'hello'

exports.add = function (x, y) {
  return x + y
}

exports.readFile = function (path, callback) {
  console.log('文件路徑:', path)
}

var age = 18
exports.age = age

function add(x, y) {
  return x - y
}

模塊原理

exports和module.exports的一個(gè)引用:

console.log(exports === module.exports);    //true

exports.foo = 'bar';

//等價(jià)于
module.exports.foo = 'bar';

//當(dāng)給exports重新賦值后,exports贾铝!= module.exports.
//最終return的是module.exports,無論exports中的成員是什么都沒用隙轻。
//真正去使用的時(shí)候:
//  導(dǎo)出單個(gè)成員:exports.xxx = xxx;
//  導(dǎo)出多個(gè)成員:module.exports 或者 modeule.exports = {};
  1. jQuery中的each 和 原生JavaScript方法forEach的區(qū)別:
    提供源頭:
    原生js是es5提供的(不兼容IE8),
    jQuery的each是jQuery第三方庫提供的(如果要使用需要用2以下的版本也就是1.版本),它的each方法主要用來遍歷jQuery實(shí)例對象(偽數(shù)組),同時(shí)也可以做低版本forEach的替代品,jQuery的實(shí)例對象不能使用forEach方法,如果想要使用必須轉(zhuǎn)為數(shù)組([].slice.call(jQuery實(shí)例對象))才能使用
  2. exports和module.exports的區(qū)別:
    每個(gè)模塊中都有一個(gè)module對象垢揩, module對象中有一個(gè)exports對象
    我們可以把需要導(dǎo)出的成員都掛載到module.exports接口對象中玖绿,也就是module.exports.xxx = xxx的方式
    但是每次寫太多了就很麻煩,所以Node為了簡化代碼叁巨,就在每一個(gè)模塊中都提供了一個(gè)成員叫exports
    exports === module.exports結(jié)果為true,所以完全可以exports.xxx = xxx 當(dāng)一個(gè)模塊需要導(dǎo)出單個(gè)成員的時(shí)候必須使用module.exports = xxx的方式斑匪,=,使用exports = xxx不管用,因?yàn)槊總€(gè)模塊最終return的是module.exports,而exports只是module.exports的一個(gè)引用,所以exports即使重新賦值,也不會(huì)影響module.exports
    有一種賦值方式比較特殊:exports = module.exports這個(gè)用來新建立引用關(guān)系的锋勺。

9. http 路徑跳轉(zhuǎn)改寫

var http = require('http')
var fs = require('fs')

// 1. 創(chuàng)建 Server
var server = http.createServer()

// 2. 監(jiān)聽 Server 的 request 請求事件蚀瘸,設(shè)置請求處理函數(shù)
//    請求
//      處理
//    響應(yīng)
//    一個(gè)請求對應(yīng)一個(gè)響應(yīng),如果在一個(gè)請求的過程中庶橱,已經(jīng)結(jié)束響應(yīng)了贮勃,則不能重復(fù)發(fā)送響應(yīng)。
//    沒有請求就沒有響應(yīng)苏章。
// 
// 咱們以前使用過 Apache 服務(wù)器軟件寂嘉,這個(gè)軟件默認(rèn)有一個(gè) www 目錄,所有存放在 www 目錄中的資源都可以通過網(wǎng)址來瀏覽
// 127.0.0.1:80/a.txt
// 127.0.0.1:80/index.html
// 127.0.0.1:80/apple/login.html

//    假裝這里有一個(gè)目錄
//      D:/Movie/www
//          index.html
//          a.txt
//          apple
//              login.html

var wwwDir = 'D:/Movie/www'

server.on('request', function (req, res) {
  var url = req.url
  // / index.html
  // /a.txt wwwDir + /a.txt
  // /apple/login.html wwwDir + /apple/login.html
  // /img/ab1.jpg wwwDir + /img/ab1.jpg
  if (url === '/') {
    fs.readFile(wwwDir + '/index.html', function (err, data) {
      // if (err) {
      //   res.end('404 Not Found.')
      // } else {
      // }

      if (err) {
        // return 有兩個(gè)作用:
        //  1. 方法返回值
        //  2. 阻止代碼繼續(xù)往后執(zhí)行
        return res.end('404 Not Found.')
      }
      res.end(data)
    })
  } else if (url === '/a.txt') {
    fs.readFile(wwwDir + '/a.txt', function (err, data) {
      if (err) {
        return res.end('404 Not Found.')
      }
      res.end(data)
    })
  } else if (url === '/index.html') {
    fs.readFile(wwwDir + '/index.html', function (err, data) {
      if (err) {
        return res.end('404 Not Found.')
      }
      res.end(data)
    })
  } else if (url === '/apple/login.html') {
    fs.readFile(wwwDir + '/apple/login.html', function (err, data) {
      if (err) {
        return res.end('404 Not Found.')
      }
      res.end(data)
    })
  }
})

// 3. 綁定端口號,啟動(dòng)服務(wù)
server.listen(3000, function () {
  console.log('running...')
})

url 固定部分替換

var http = require('http')
var fs = require('fs')

var server = http.createServer()

var wwwDir = 'D:/Movie/www'

server.on('request', function (req, res) {
  var url = req.url
  // / index.html
  // /a.txt wwwDir + /a.txt
  // /apple/login.html wwwDir + /apple/login.html
  // /img/ab1.jpg wwwDir + /img/ab1.jpg
  
  var filePath = '/index.html'
  if (url !== '/') {
    filePath = url
  }

  fs.readFile(wwwDir + filePath, function (err, data) {
    if (err) {
      return res.end('404 Not Found.')
    }
    res.end(data)
  })
})

// 3. 綁定端口號垫释,啟動(dòng)服務(wù)
server.listen(3000, function () {
  console.log('running...')
})

10. 案例之簡易目錄列表

[圖片上傳失敗...(image-d64eb-1603337864181)]

var http = require('http')
var fs = require('fs')

var server = http.createServer()

var wwwDir = 'D:/Movie/www'

server.on('request', function (req, res) {
  var url = req.url
  fs.readFile('./template.html', function (err, data) {
    if (err) {
      return res.end('404 Not Found.')
    }
    // 1. 如何得到 wwwDir 目錄列表中的文件名和目錄名
    //    fs.readdir
    // 2. 如何將得到的文件名和目錄名替換到 template.html 中
    //    2.1 在 template.html 中需要替換的位置預(yù)留一個(gè)特殊的標(biāo)記(就像以前使用模板引擎的標(biāo)記一樣)
    //    2.2 根據(jù) files 生成需要的 HTML 內(nèi)容
    // 只要你做了這兩件事兒,那這個(gè)問題就解決了
    fs.readdir(wwwDir, function (err, files) {
      if (err) {
        return res.end('Can not find www dir.')
      }

      // 2.1 生成需要替換的內(nèi)容
      var content = ''
      files.forEach(function (item) {
        // 在 EcmaScript 6 的 ` 字符串中撑瞧,可以使用 ${} 來引用變量
        content += `
          <tr>
            <td data-value="apple/">
              <a class="icon dir" href="/D:/Movie/www/apple/">${item}/</a>
           </td>
            <td class="detailsColumn" data-value="0">1024kb</td>
            <td class="detailsColumn" data-value="1509589967">2017/11/2 上午10:32:47</td>
          </tr>
        `
      })

      // 2.3 替換
      data = data.toString()
      data = data.replace('^_^', content)

      // 3. 發(fā)送解析替換過后的響應(yīng)數(shù)據(jù)
      res.end(data)
    })
  })
})
server.listen(3000, function () {
  console.log('running...')
})

template.html

<html dir="ltr" lang="zh" i18n-processed="">
<head>
  <meta charset="utf-8">
  <meta name="google" value="notranslate">
  <title id="title">D:\Movie\www\ 的索引</title>
  <style>假裝這里有樣式</style>
</head>

<body>
  <div id="listingParsingErrorBox">
      糟糕棵譬!Google Chrome無法解讀服務(wù)器所發(fā)送的數(shù)據(jù)。請
      <a >報(bào)告錯(cuò)誤</a>
      预伺,并附上
      <a href="LOCATION">原始列表</a>订咸。
  </div>
  <h1 id="header">D:\Movie\www\ 的索引</h1>
  <div id="parentDirLinkBox" style="display:none">
    <a id="parentDirLink" class="icon up">
    <span id="parentDirText">[上級目錄]</span>
  </a>
  </div>
  <table>
    <thead>
      <tr class="header" id="theader">
        <th onclick="javascript:sortTable(0);">名稱</th>
        <th class="detailsColumn" onclick="javascript:sortTable(1);">
          大小
        </th>
        <th class="detailsColumn" onclick="javascript:sortTable(2);">
          修改日期
        </th>
      </tr>
    </thead>
    <tbody id="tbody">^_^</tbody>
  </table>
</body>

</html>

11. 讀取目錄 readdir

格式:

readdir(‘讀取文件的文件目錄路徑’,function(err,files){})讀取目錄
var fs = require('fs')

fs.readdir('D:/Movie/www', function (err, files) {
  if (err) {
    return console.log('目錄不存在')
  }
  console.log(files)
})
//得到一個(gè)數(shù)組例如  ['a.txt','apple','img']

12. 在瀏覽器中使用art-template

  • 導(dǎo)入后使用即可
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>在瀏覽器中使用art-template</title>
</head>
<body>
  <!-- 
    注意:在瀏覽器中需要引用 lib/template-web.js 文件

    強(qiáng)調(diào):模板引擎不關(guān)心你的字符串內(nèi)容酬诀,只關(guān)心自己能認(rèn)識的模板標(biāo)記語法脏嚷,例如 {{}}
    {{}} 語法被稱之為 mustache 語法,八字胡啊瞒御。
   -->
  <script src="node_modules/art-template/lib/template-web.js"></script>
  
  <script type="text/template" id="tpl">
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Document</title>
    </head>
    <body>
      <p>大家好父叙,我叫:{{ name }}</p>
      <p>我今年 {{ age }} 歲了</p>
      <h1>我來自 {{ province }}</h1>
      <p>我喜歡:{{each hobbies}} {{ $value }} {{/each}}</p>
    </body>
    </html>
  </script>
  
  <script>
    var ret = template('tpl', {
      name: 'Jack',
      age: 18,
      province: '北京市',
      hobbies: [
        '寫代碼',
        '唱歌',
        '打游戲'
      ]
    })

    console.log(ret)
  </script>
</body>
</html>

13. 在node中使用模板引擎

在 Node 中使用 art-template模板引擎
模板引起最早就是誕生于服務(wù)器領(lǐng)域,后來才發(fā)展到了前端肴裙。

art-template 不僅可以在瀏覽器使用趾唱,也可以在 node 中使用

  • 1、安裝:
    • npm install art-template
    • 該命令在哪執(zhí)行就會(huì)把包下載到哪里蜻懦。默認(rèn)會(huì)下載到 node_modules 目錄中
    • node_modules 不要改甜癞,也不支持改。
  • 2宛乃、使用:
    • 在需要使用的文件模塊中加載 art-template
    • 只需要使用 require 方法加載就可以了:require('art-template')
    • 參數(shù)中的 art-template 就是你下載的包的名字
    • 也就是說你 install 的名字是什么悠咱,則你 require 中的就是什么
  • 3、查文檔征炼,使用模板引擎的 API

tel.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>{{ title }}</title>
</head>
<body>
  <p>大家好析既,我叫:{{ name }}</p>
  <p>我今年 {{ age }} 歲了</p>
  <h1>我來自 {{ province }}</h1>
  <p>我喜歡:{{each hobbies}} {{ $value }} {{/each}}</p>
  <script>
    var foo = '{{ title }}'
  </script>
</body>
</html>

使用


var template = require('art-template')
var fs = require('fs')

// 這里不是瀏覽器
// template('script 標(biāo)簽 id', {對象})

// var tplStr = `
// <!DOCTYPE html>
// <html lang="en">
// <head>
//   <meta charset="UTF-8">
//   <title>Document</title>
// </head>
// <body>
//   <p>大家好,我叫:{{ name }}</p>
//   <p>我今年 {{ age }} 歲了</p>
//   <h1>我來自 {{ province }}</h1>
//   <p>我喜歡:{{each hobbies}} {{ $value }} {{/each}}</p>
// </body>
// </html>
// `

fs.readFile('./tpl.html', function (err, data) {
  if (err) {
    return console.log('讀取文件失敗了')
  }
  // 默認(rèn)讀取到的 data 是二進(jìn)制數(shù)據(jù)
  // 而模板引擎的 render 方法需要接收的是字符串
  // 所以我們在這里需要把 data 二進(jìn)制數(shù)據(jù)轉(zhuǎn)為 字符串 才可以給模板引擎使用
  var ret = template.render(data.toString(), {
    name: 'Jack',
    age: 18,
    province: '北京市',
    hobbies: [
      '寫代碼',
      '唱歌',
      '打游戲'
    ],
    title: '個(gè)人信息'
  })

  console.log(ret)
})

14. http案例加入art-template

var http = require('http')
var fs = require('fs')
var template = require('art-template')

var server = http.createServer()

var wwwDir = 'D:/Movie/www'

server.on('request', function (req, res) {
  var url = req.url
  fs.readFile('./template-apache.html', function (err, data) {
    if (err) {
      return res.end('404 Not Found.')
    }
    // 1. 如何得到 wwwDir 目錄列表中的文件名和目錄名
    //    fs.readdir
    // 2. 如何將得到的文件名和目錄名替換到 template.html 中
    //    2.1 在 template.html 中需要替換的位置預(yù)留一個(gè)特殊的標(biāo)記(就像以前使用模板引擎的標(biāo)記一樣)
    //    2.2 根據(jù) files 生成需要的 HTML 內(nèi)容
    // 只要你做了這兩件事兒谆奥,那這個(gè)問題就解決了
    fs.readdir(wwwDir, function (err, files) {
      if (err) {
        return res.end('Can not find www dir.')
      }

      // 這里只需要使用模板引擎解析替換 data 中的模板字符串就可以了
      // 數(shù)據(jù)就是 files
      // 然后去你的 template.html 文件中編寫你的模板語法就可以了
      var htmlStr = template.render(data.toString(), {
        title: '哈哈',
        files: files
      })

      // 3. 發(fā)送解析替換過后的響應(yīng)數(shù)據(jù)
      res.end(htmlStr)
    })
  })
})
server.listen(3000, function () {
  console.log('running...')
})

template-apache.html

<html dir="ltr" lang="zh" i18n-processed="">

<head>
  <meta charset="utf-8">
  <meta name="google" value="notranslate">
  
  <style>假裝這里有樣式</style>
  <title id="title">{{ title }}</title>
</head>

<body>
  <div id="listingParsingErrorBox">
      糟糕渡贾!Google Chrome無法解讀服務(wù)器所發(fā)送的數(shù)據(jù)。請
      <a >報(bào)告錯(cuò)誤</a>
      雄右,并附上<a href="LOCATION">原始列表</a>空骚。
  </div>
  <h1 id="header">D:\Movie\www\ 的索引</h1>
  <div id="parentDirLinkBox" style="display:none">
    <a id="parentDirLink" class="icon up">
    <span id="parentDirText">[上級目錄]</span>
  </a>
  </div>
  <table>
    <thead>
      <tr class="header" id="theader">
        <th onclick="javascript:sortTable(0);">名稱</th>
        <th class="detailsColumn" onclick="javascript:sortTable(1);">
          大小
        </th>
        <th class="detailsColumn" onclick="javascript:sortTable(2);">
          修改日期
        </th>
      </tr>
    </thead>
    <tbody id="tbody">
      {{each files}}
      <tr>
        <td data-value="apple/">
            <a class="icon dir" href="/D:/Movie/www/apple/">{{$value}}/</a>
        </td>
        <td class="detailsColumn" data-value="0"></td>
        <td class="detailsColumn" data-value="1509589967">2017/11/2 上午10:32:47</td>
      </tr>
      {{/each}}
    </tbody>
  </table>
</body>

</html>

15. 客戶端渲染和移動(dòng)端渲染

  • 服務(wù)端渲染

    • 說白了就是在服務(wù)端使用模板引擎
    • 模板引擎最早誕生于服務(wù)端,后來才發(fā)展到了前端
  • 服務(wù)端渲染和客戶端渲染的區(qū)別

    • 客戶端渲染不利于 SEO 搜索引擎優(yōu)化

    • 服務(wù)端渲染是可以被爬蟲抓取到的擂仍,客戶端異步渲染是很難被爬蟲抓取到的

    • 所以你會(huì)發(fā)現(xiàn)真正的網(wǎng)站既不是純異步也不是純服務(wù)端渲染出來的

    • 而是兩者結(jié)合來做的

    • 例如京東的商品列表就采用的是服務(wù)端渲染囤屹,目的了為了 SEO 搜索引擎優(yōu)化

    • 而它的商品評論列表為了用戶體驗(yàn),而且也不需要 SEO 優(yōu)化逢渔,所以采用是客戶端渲染

16. parse 和query

cmd終端輸入:node + 回車可進(jìn)入node控制臺

var url = require('url')

var obj = url.parse('/pinglun?name=的撒的撒&message=的撒的撒的撒', true)

console.log(obj)
console.log(obj.query)

下一章node.js(2)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末肋坚,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌智厌,老刑警劉巖诲泌,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異铣鹏,居然都是意外死亡敷扫,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門诚卸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來葵第,“玉大人,你說我怎么就攤上這事合溺∽涿埽” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵棠赛,是天一觀的道長哮奇。 經(jīng)常有香客問我,道長睛约,這世上最難降的妖魔是什么屏镊? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮痰腮,結(jié)果婚禮上而芥,老公的妹妹穿的比我還像新娘。我一直安慰自己膀值,他們只是感情好棍丐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著沧踏,像睡著了一般歌逢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上翘狱,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天秘案,我揣著相機(jī)與錄音,去河邊找鬼潦匈。 笑死阱高,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的茬缩。 我是一名探鬼主播赤惊,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼凰锡!你這毒婦竟也來了未舟?” 一聲冷哼從身側(cè)響起圈暗,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎裕膀,沒想到半個(gè)月后员串,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡昼扛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年寸齐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片野揪。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡访忿,死狀恐怖瞧栗,靈堂內(nèi)的尸體忽然破棺而出斯稳,到底是詐尸還是另有隱情,我是刑警寧澤迹恐,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布挣惰,位于F島的核電站,受9級特大地震影響殴边,放射性物質(zhì)發(fā)生泄漏憎茂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一锤岸、第九天 我趴在偏房一處隱蔽的房頂上張望竖幔。 院中可真熱鬧,春花似錦是偷、人聲如沸拳氢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽馋评。三九已至,卻和暖如春刺啦,著一層夾襖步出監(jiān)牢的瞬間留特,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工玛瘸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蜕青,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓糊渊,卻偏偏與公主長得像市咆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子再来,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355