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')
- 核心模塊是由 Node 提供的一個(gè)個(gè)的具名的模塊,它們都有自己特殊的名稱標(biāo)識橡娄,例如
2. 安裝Node環(huán)境
- 查看Node環(huán)境的版本號
- 下載:https://nodejs.org/en/
- 安裝:
- 傻瓜式安裝匠童,一路
next
- 安裝過再次安裝會(huì)升級
- 傻瓜式安裝匠童,一路
- 確認(rèn)Node環(huán)境是否安裝成功
- 查看node的版本號:
node --version
- 或者
node -v
- 查看node的版本號:
- 配置環(huán)境變量
3. 解析執(zhí)行JavaScript
- 創(chuàng)建編寫JavaScript腳本文件
- 打開終端,定位腳本文件的所屬目錄
- 輸入
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è)作用:
- 加載文件模塊并執(zhí)行里面的代碼
- 拿到被加載文件模塊中的
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 = {};
- 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í)例對象))才能使用 - 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)