一. 模塊系統(tǒng)
1.模塊作用域:
即使你加載我 你也不能訪問我,
除非我想讓你訪問(exports暴露)
2. exports
exports是一個對象,通過這個對象暴露屬性
exports.add=add
或者
moudle.exports={ add:add }
引用模塊的腳本可以直接這樣用:
var 模塊別名 require('模塊名') 模塊別名.方法
如果想直接暴露一個方法直接用 不用 模塊名.方法 的話
moudle.exports=add
因為,每一個文件模塊都有一個moudle對象,他有一個成員叫exports, 又聲明了一個exports對象讓他等于moudle.exports
var exports=moudle.exports
所以,我們用exports=add 無法暴露.只能用moudle.exports=add;
3.require
var 模塊別名 require('模塊名')
require兩個作用:1. 執(zhí)行模塊中的代碼 2.導(dǎo)出模塊中的暴露對象
4.require緩存規(guī)則
(1). 優(yōu)先加載緩存中的模塊 //如果已經(jīng)加載過的模塊又被加載,會直接從緩存中加載
(2). 判斷模塊標(biāo)識
①核心文件本質(zhì)也是文件,已經(jīng)被編譯成二進(jìn)制文件了,可以直接調(diào)用
②用戶模塊 require('路徑')
③第三方模塊
不可能有第三方包和核心模塊同名
調(diào)用時,先找當(dāng)前模塊目錄下的node_moudles文件夾
再去找該文件夾下的package.json文件
在文件中找main,main屬性就記錄了當(dāng)前模塊的入口
如果找不到package.json或者main,則node會自動找該目錄下的index.js
如果以上操作都不成立,則進(jìn)入上一級目錄的node_moudles文件夾尋找
如果還沒有,再進(jìn)入上一級,直到根目錄,然后報錯
注意,一個項目只有一個node_moudles,存放于根目錄
二.npm相關(guān)
1.npm官方網(wǎng)站
npm | build amazing things?www.npmjs.com

我們可以再網(wǎng)站中搜索包
2. npm常用命令
①版本
npm -v
②升級
npm install --global npm
③常用命令
npm init //初始化npm 生成package.json文件
npm init -y //快速初始化npm ,跳過向?qū)?
npm install 包名 //下載包
npm i 包名 //同上
npm install 包名 --save //下載包,并記錄在package.json文件中
npm i 包名 -S //同上
npm install //一次性將記錄在package.json文件中的包全部下載下來
npm i //同上
npm uninstall 包名 //卸載包
npm un 包名 //同上
npm uninstall 包名 --save //卸載包,同時撤銷在package.json文件中的記錄
npm un 包名 -S //同上
npm --help //幫助
3、npm被墻問題
淘寶 NPM 鏡像?npm.taobao.org
npm install --global cnpm
然后把之前的指令中的npm改為cnpm即可
4、描述文件
建議每一個項目要有一個 package.json文件(包描述文件)
我們想弄清楚項目的依賴包情況,可以訪問這個package.json文件
這個文件可以用以下命令自動創(chuàng)建:
npm init
這是一個詢問式的命令,會詢問你一些項目基本信息,按需填寫就可以

此時已經(jīng)生成了package.json

然后
npm install 包名 --save
//備注:npm 5.x以后不寫save也可以自動保存了
此時json文件會記錄下我們安裝的包


如果node_moudles被刪除了,我們可以直接執(zhí)行以下命令一口氣裝好
npm install //此命令會重新下載package.json中的dependencies中的包
三、express模塊初步使用
原生的http表現(xiàn)不足,需要使用框架加快效率,也讓代碼高度統(tǒng)一
Express - 基于 Node.js 平臺的 web 應(yīng)用開發(fā)框架?www.expressjs.com.cn
1、安裝方式
npm install express --save
2、hello world
var express = require('express') var app = express();//實例化一個服務(wù)程序 //當(dāng)服務(wù)器收到請求"/"時的邏輯
app.get('/', (req, res) => { res.send('hhello express!你好!') })
app.listen(3000,()=>{ //讓服務(wù)器程序在3000端口監(jiān)聽
console.log('app is listening at Port 3000'); })
我們發(fā)現(xiàn)了express的幾個好處:
①響應(yīng)不用end了 ②url不用自己解析了 ③中文不用自己轉(zhuǎn)碼了
3、基本路由
get:
app.get('/', (req, res) => { res.send('hello express!你好!') })
post
app.post('/',(req,res)=>{ res.send(req); })
注:
比如一個ajax請求要求發(fā)json, express提供一個方法 res.json(對象) 它會自動將對象轉(zhuǎn)為JSON對應(yīng)的字符串發(fā)送給瀏覽器
如果將來安裝了art-template,可以用 res.render('頁面',模板規(guī)則對象)來發(fā)送頁面
4、靜態(tài)服務(wù) //指定公開目錄
app.use('/public/',express.static('./public/'))
這樣就公開了public中的所有資源,可以訪問了

此外,我們可以省略第一個參數(shù),這時url也省略對應(yīng)的層
app.use(express.static('./public/'))

其實,我們可以給文件路徑起url別名,例如:app.use('/a/',express.static('./public/')),
上面這種做法其實是起別名的一種特殊情況
5、解析get請求中的數(shù)據(jù)
req.query //會直接返回一個對象
例如:我們的get請求如下:
http://127.0.0.1:3000/commit?name=zhangsan&age=30&sex=male
app.get('/commit',(req,res)=>{ console.log(req.query); })

6、在express中配置使用art-template模板引擎
(1).安裝art-template
cnpm i art-template -S cnpm i express-art-template -S
(2).使用
①、指定后綴名的文件用art-template渲染:
app.engine('html', require('express-art-template'))
當(dāng)渲染以.html結(jié)尾的文件時,使用art-template模板引擎
express-art-template是用來把a(bǔ)rt-template整合在Express中的
雖然不需要加載art-template但它是express-art-template的依賴,所以必須安裝
②、使用respone.render(views目錄下文件,渲染參數(shù))來進(jìn)行渲染
express有一個約定,希望開發(fā)人員把頁面文件都放在views目錄及其子目錄下,
同時,express為responce提供了對應(yīng)的render方法,此方法默認(rèn)不可用,當(dāng)配置了模板引擎時,可用!第一個參數(shù)不能寫路徑(但可以寫views的子目錄),默認(rèn)會去views路徑下查找模板
var express = require('express')
var app = express();//實例化一個服務(wù)程序
app.engine('html', require('express-art-template'))
//當(dāng)渲染以.html結(jié)尾的文件時,使用art-template模板引擎
//express-art-template是用來把a(bǔ)rt-template整合在Express中的
//雖然不需要加載art-template但它是express-art-template的依賴
//所以必須安裝
app.get('/', (req, res) => {
res.render('index.html', {
name: 'lili',
age: 18,
sex: 'girl'
})
//express為responce提供了對應(yīng)的render方法,此方法默認(rèn)不可用
//當(dāng)配置了模板引擎時,可用.
//express有一個約定,希望開發(fā)人員把頁面文件都放在views目錄及其子目錄下
//第一個參數(shù)不能寫路徑(但可以寫views的子目錄),默認(rèn)會去views路徑下查找模板
})
app.get('/admin', (req, res) => {
res.render('admin/adminIndex.html', {//render里可以寫views的子目錄
admin: '莉莉'
})
})
app.listen(3000, () => { //讓服務(wù)器程序在3000端口監(jiān)聽
console.log('app is listening at Port 3000');
})


③、如果想修改目錄(一般不需要)則:
app.set('views','新的路徑')
7. 重定向
res.redirect('/')
8、解析POST請求中的數(shù)據(jù)
需要借用一個插件 body-parser
(1)安裝
cnpm install body-parser --save
(2)引包
var bodyParser = require('body-parser');
(3)配置
配置 body-parser后,會在req對象上增加一個屬性叫body
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true }));
(4) 此時,res.body就可以用了
var express = require('express')
var app = express();//實例化一個服務(wù)程序
var bodyParser = require('body-parser');
app.engine('html', require('express-art-template'))
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true }));
var user;//創(chuàng)建一個對象接收用戶信息
app.get('/', (req, res) => {
res.render('index.html', user)
})
app.get('/admin', (req, res) => {
res.render("admin/adminIndex.html")
})
app.post('/post', (req, res) => { //post到/post頁面
console.log(req.body) //req.body可用了
user = req.body //把它賦給user
res.redirect("/") //重定向到首頁
})
app.listen(3000, () => {
console.log('app is listening at Port 3000');
})
9.設(shè)置狀態(tài)碼
res.status(500).send("讀取文件錯誤:") //設(shè)置了狀態(tài)碼,還返回了錯誤信息
10.操作文件中的數(shù)據(jù)
express中沒有專門的文件讀寫API,所以還是使用fs.readFile()
此外注意讀取時
app.get('/', (req, res) => {
fs.readFile('./views/db.json', 'utf8', (err, data) => {
//第二個參數(shù)加'utf8'會讀出utf8字符串
if (err) { res.status(500).send("讀取文件錯誤:" + err); };
res.render("index.html", {
food: ["apple", 'orange', 'banana'],
students: JSON.parse(data).students
//文件讀出的是字符串,所以應(yīng)該轉(zhuǎn)換成對象,再調(diào)用屬性
})
})
})
11.路由模塊的提取
(1).引用與暴露
實際工作中我們應(yīng)該將工程模塊化,不應(yīng)將所有邏輯卸載一個app.js中,這就涉及了文件的引用與參數(shù)的暴露問題
我們常用的方法是:
在app.js中 require 一個模塊 ,在模塊中又要引用app對象,所以我們在模塊中封裝一個函數(shù)并暴露,這個函數(shù)有一個形參app
app.js引入后調(diào)用時,把自己的app對象傳遞給這個函數(shù)
舉例如下:
在app.js中
var express = require('express')
var router=require('./router')
var app = express()
var port = 3000
app.engine('html', require('express-art-template'))
app.use('/public/', express.static('./public/'))
app.use('/node_modules/', express.static('./node_modules/'))
router(app)//調(diào)用router函數(shù),同時把a(bǔ)pp對象傳給他
app.listen(port, () => console.log(`app listening on port` + port))
在router.js中
var fs = require('fs')
module.exports = (app) => { //封裝一個函數(shù)并暴露,需要形參app
app.get('/', (req, res) => {
fs.readFile('./views/db.json', 'utf8', (err, data) => {
if (err) { res.status(500).send("讀取文件錯誤:" + err); };
res.render("index.html", {
food: ["apple", 'orange', 'banana'],
students: JSON.parse(data).students
})
})
})
}
但是,express提供了更好的方法:
(2) express路由模塊
①.引用express,實例化其中的Router路由容器對象
var express =require('express') //1.創(chuàng)建一個路由容器 var router=express.Router()
②.把請求都掛載在router這個對象中
③.把router這個對象導(dǎo)出
④app.js引入這個模塊,同時獲得router這個對象
⑤app對象使用use方法使用router這個對象
例子:
router.js
var fs = require('fs')
var express = require('express')
//1.創(chuàng)建一個路由容器
var router = express.Router()
//2.把請求都掛在router中
router.get('/', (req, res) => {
fs.readFile('./views/db.json', 'utf8', (err, data) => {
if (err) { res.status(500).send("讀取文件錯誤:" + err); };
res.render("index.html", {
food: ["apple", 'orange', 'banana'],
students: JSON.parse(data).students
})
})
})
//3.把router導(dǎo)出
module.exports = router
app.js中
var express = require('express')
var router=require('./router') //4.app引用router
var app = express()
var port = 3000
app.engine('html', require('express-art-template'))
app.use('/public/', express.static('./public/'))
app.use('/node_modules/', express.static('./node_modules/'))
app.use(router) //5. app使用router
app.listen(port, () => console.log(`app listening on port` + port))
13.封裝異步回調(diào)函數(shù)
如果獲取一個函數(shù)中異步操作的結(jié)果,則必須通過回調(diào)函數(shù)來獲取
先看一個錯誤的代碼: 由于return不會等待fs.readFile的執(zhí)行結(jié)果,所以obj=undefined
exports.findAll=function findAll() {
var obj
fs.readFile(dbPath, 'utf8', (err, data) => {
obj=JSON.parse(data).students
})
return obj
} //錯!!!!!由于異步,這種函數(shù)是無法正確返回返回值的!!!!!!
要得到返回值,則應(yīng)采用回調(diào)的方式
exports.findAll = (callback) => {
//如果有人想調(diào)用findAll,則必須傳入一個回調(diào)函數(shù)
fs.readFile(dbPath, 'utf8', (err, data) => {
//這個回調(diào)函數(shù)要有err,data兩個形參
if(err){//如果readFile失敗了 這個err是readFile的
callback(err)//則callback會傳遞err給回調(diào)函數(shù)
}else{
callback(null,JSON.parse(data).students)
//否則callback會傳遞data給回調(diào)函數(shù) 這個data是readFile的
}
})
}
如果有人想調(diào)用findAll
router.get('/students', (req, res) => {
students.findAll((err, students) => { //調(diào)用findAll,必須傳入一個回調(diào)函數(shù)
if(err) return res.status.send("讀取文件錯誤"+err)
//如果callback傳出的err不是null,則報錯
res.render("index.html",{
food:['apple','orange'],
students:students //接收到callback傳遞的students(data)
})
})
這種方式與其說是把數(shù)據(jù)傳出來處理,不如說是使用者把邏輯傳入了封裝好的函數(shù)中處理邏輯
關(guān)鍵是轉(zhuǎn)變?yōu)?上層定義下層調(diào)用的思路 具體應(yīng)用詳見綜合練習(xí).
14.JSON處理
(1)將字符串轉(zhuǎn)換為JSON對象
students = JSON.parse(data)
(2)將JSON對象轉(zhuǎn)換為字符串
str= JSON.stringify(students ) //我們還可以順便調(diào)整字符串內(nèi)容 str = JSON.stringify({ students: students })
15.如何自己編寫服務(wù)器渲染及數(shù)據(jù)處理的后端app
- 處理模板
- 配置開放靜態(tài)資源
- 配置模板引擎
- 簡單路由表設(shè)計
- 路由設(shè)計
- 提取路由模塊
- 由于我們需要操作文件或數(shù)據(jù)庫,所以要封裝一個.js專門處理他們
- 實現(xiàn)具體功能
16.小技巧
(1)如何快速找到一個不錯的HTML頁面?
建議去bootstrap看看
起步 · Bootstrap v3 中文文檔?v3.bootcss.com
Examples?v4.bootcss.com
全局 CSS 樣式 · Bootstrap v3 中文文檔?v3.bootcss.com
17. express如何處理404頁面??
需要使用中間件
app.use(function(req,res){})
18.綜合練習(xí)
此練習(xí)為一個迷你學(xué)生信息管理系統(tǒng)

https://github.com/zfdok/node_stu_mgr?github.com
運(yùn)行時先執(zhí)行 npm install 重裝一下依賴庫
再用 nodemon app 啟動服務(wù)
輸入 127.0.0.1/3000訪問首頁