一. 模块系统
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. 执行模块中的代码 2.导出模块中的暴露对象
4.require缓存规则
(1). 优先加载缓存中的模块 //如果已经加载过的模块又被加载,会直接从缓存中加载
(2). 判断模块标识
①核心文件本质也是文件,已经被编译成二进制文件了,可以直接调用
②用户模块 require('路径')
③第三方模块
- 不可能有第三方包和核心模块同名
- 调用时,先找当前模块目录下的node_moudles文件夹
- 再去找该文件夹下的package.json文件
- 在文件中找main,main属性就记录了当前模块的入口
- 如果找不到package.json或者main,则node会自动找该目录下的index.js
- 如果以上操作都不成立,则进入上一级目录的node_moudles文件夹寻找
- 如果还没有,再进入上一级,直到根目录,然后报错
- 注意,一个项目只有一个node_moudles,存放于根目录
二.npm相关
1.npm官方网站
npm | build amazing things我们可以再网站中搜索包
2. npm常用命令
①版本
npm -v
②升级
npm install --global npm
③常用命令
npm init //初始化npm 生成package.json文件
npm init -y //快速初始化npm ,跳过向导
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 install --global cnpm
然后把之前的指令中的npm改为cnpm即可
4、描述文件
建议每一个项目要有一个 package.json文件(包描述文件)
我们想弄清楚项目的依赖包情况,可以访问这个package.json文件
这个文件可以用以下命令自动创建:
npm init
这是一个询问式的命令,会询问你一些项目基本信息,按需填写就可以
![v2-bcc3366070a1bb57453034134987ee4a_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/1ea4461fea7976e4a5517fa24eb48a76.jpeg)
此时已经生成了package.json
![v2-3d578665bf3c6d8eedb28de2a43df5f5_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/73cbfb6b198f019a8475d4d3588023b8.png)
然后
npm install 包名 --save
//备注:npm 5.x以后不写save也可以自动保存了
此时json文件会记录下我们安装的包
![v2-4e1bc0319f89dd9e1da02f648ab4a89b_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/85a93638ffbfba94f372a09939d63cd4.png)
![v2-dd964299fb3d5a14a8fb14c13d60116c_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/f466f92720039d42d0dc9dfd03d6fc61.jpeg)
如果node_moudles被删除了,我们可以直接执行以下命令一口气装好
npm install //此命令会重新下载package.json中的dependencies中的包
三、express模块初步使用
原生的http表现不足,需要使用框架加快效率,也让代码高度统一Express - 基于 Node.js 平台的 web 应用开发框架
1、安装方式
npm install express --save
2、hello world
var express = require('express')
var app = express();//实例化一个服务程序
//当服务器收到请求"/"时的逻辑
app.get('/', (req, res) => {
res.send('hhello express!你好!')
})
app.listen(3000,()=>{ //让服务器程序在3000端口监听
console.log('app is listening at Port 3000');
})
我们发现了express的几个好处:
①响应不用end了 ②url不用自己解析了 ③中文不用自己转码了
3、基本路由
get:
app.get('/', (req, res) => {
res.send('hello express!你好!')
})
post
app.post('/',(req,res)=>{
res.send(req);
})
4、静态服务 //指定公开目录
app.use('/public/',express.static('./public/'))
这样就公开了public中的所有资源,可以访问了
![v2-989f6272148a7c3cd930b7b4aa677631_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/0c45f883038b89ce764ae6ad17a44980.png)
此外,我们可以省略第一个参数,这时url也省略对应的层
app.use(express.static('./public/'))
![v2-c8a0c4c4c0bef82cf5fff3433e156c8c_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/10c16a66f61ec7eb4b4fb8f188fe833d.jpeg)
其实,我们可以给文件路径起url别名,例如:app.use('/a/',express.static('./public/')),
上面这种做法其实是起别名的一种特殊情况
5、解析get请求中的数据
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);
})
![v2-27d90b25aa3cf6576b06c32fe4fed4e9_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/0af9bf82de4cd73dd1a5b1ae829e0802.png)
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'))
当渲染以.html结尾的文件时,使用art-template模板引擎
express-art-template是用来把art-template整合在Express中的
虽然不需要加载art-template但它是express-art-template的依赖,所以必须安装
②、使用respone.render(views目录下文件,渲染参数)来进行渲染
express有一个约定,希望开发人员把页面文件都放在views目录及其子目录下,
同时,express为responce提供了对应的render方法,此方法默认不可用,当配置了模板引擎时,可用!第一个参数不能写路径(但可以写views的子目录),默认会去views路径下查找模板
var express = require('express')
var app = express();//实例化一个服务程序
app.engine('html', require('express-art-template'))
//当渲染以.html结尾的文件时,使用art-template模板引擎
//express-art-template是用来把art-template整合在Express中的
//虽然不需要加载art-template但它是express-art-template的依赖
//所以必须安装
app.get('/', (req, res) => {
res.render('index.html', {
name: 'lili',
age: 18,
sex: 'girl'
})
//express为responce提供了对应的render方法,此方法默认不可用
//当配置了模板引擎时,可用.
//express有一个约定,希望开发人员把页面文件都放在views目录及其子目录下
//第一个参数不能写路径(但可以写views的子目录),默认会去views路径下查找模板
})
app.get('/admin', (req, res) => {
res.render('admin/adminIndex.html', {//render里可以写views的子目录
admin: '莉莉'
})
})
app.listen(3000, () => { //让服务器程序在3000端口监听
console.log('app is listening at Port 3000');
})
![v2-7d0838c4972646928da82039d7be3c37_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/5a945a51814a7a203bfcee0dca3c8e1a.png)
![v2-1a986ae4f36a6ed43ecaa125640890b0_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/a179c260332e819b3e7f520fb0e23461.png)
③、如果想修改目录(一般不需要)则:
app.set('views','新的路径')
7. 重定向
res.redirect('/')
8、解析get请求中的数据
需要借用一个插件 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();//实例化一个服务程序
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;//创建一个对象接收用户信息
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.设置状态码
res.status(500).send("读取文件错误:") //设置了状态码,还返回了错误信息
10.操作文件中的数据
express中没有专门的文件读写API,所以还是使用fs.readFile()
此外注意读取时
app.get('/', (req, res) => {
fs.readFile('./views/db.json', 'utf8', (err, data) => {
//第二个参数加'utf8'会读出utf8字符串
if (err) { res.status(500).send("读取文件错误:" + err); };
res.render("index.html", {
food: ["apple", 'orange', 'banana'],
students: JSON.parse(data).students
//文件读出的是字符串,所以应该转换成对象,再调用属性
})
})
})
11.路由模块的提取
(1).引用与暴露
实际工作中我们应该将工程模块化,不应将所有逻辑卸载一个app.js中,这就涉及了文件的引用与参数的暴露问题
我们常用的方法是:
在app.js中 require 一个模块 ,在模块中又要引用app对象,所以我们在模块中封装一个函数并暴露,这个函数有一个形参app
app.js引入后调用时,把自己的app对象传递给这个函数
举例如下:
在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)//调用router函数,同时把app对象传给他
app.listen(port, () => console.log(`app listening on port` + port))
在router.js中
var fs = require('fs')
module.exports = (app) => { //封装一个函数并暴露,需要形参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.创建一个路由容器
var router=express.Router()
②.把请求都挂载在router这个对象中
③.把router这个对象导出
④app.js引入这个模块,同时获得router这个对象
⑤app对象使用use方法使用router这个对象
例子:
router.js
var fs = require('fs')
var express = require('express')
//1.创建一个路由容器
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导出
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.封装异步回调函数
如果获取一个函数中异步操作的结果,则必须通过回调函数来获取
先看一个错误的代码: 由于return不会等待fs.readFile的执行结果,所以obj=undefined
exports.findAll=function findAll() {
var obj
fs.readFile(dbPath, 'utf8', (err, data) => {
obj=JSON.parse(data).students
})
return obj
} //错!!!!!由于异步,这种函数是无法正确返回返回值的!!!!!!
要得到返回值,则应采用回调的方式
exports.findAll = (callback) => {
//如果有人想调用findAll,则必须传入一个回调函数
fs.readFile(dbPath, 'utf8', (err, data) => {
//这个回调函数要有err,data两个形参
if(err){//如果readFile失败了 这个err是readFile的
callback(err)//则callback会传递err给回调函数
}else{
callback(null,JSON.parse(data).students)
//否则callback会传递data给回调函数 这个data是readFile的
}
})
}
如果有人想调用findAll
router.get('/students', (req, res) => {
students.findAll((err, students) => { //调用findAll,必须传入一个回调函数
if(err) return res.status.send("读取文件错误"+err)
//如果callback传出的err不是null,则报错
res.render("index.html",{
food:['apple','orange'],
students:students //接收到callback传递的students(data)
})
})
这种方式与其说是把数据传出来处理,不如说是使用者把逻辑传入了封装好的函数中处理逻辑
关键是转变为:上层定义下层调用的思路 具体应用详见综合练习.
14.JSON处理
(1)将字符串转换为JSON对象
students = JSON.parse(data)
(2)将JSON对象转换为字符串
str= JSON.stringify(students )
//我们还可以顺便调整字符串内容
str = JSON.stringify({
students: students
})
15.如何自己编写服务器渲染及数据处理的后端app
- 处理模板
- 配置开放静态资源
- 配置模板引擎
- 简单路由表设计
- 路由设计
- 提取路由模块
- 由于我们需要操作文件或数据库,所以要封装一个.js专门处理他们
- 实现具体功能
16.小技巧
(1)如何快速找到一个不错的HTML页面?
建议去bootstrap看看
起步 · Bootstrap v3 中文文档 Examples 全局 CSS 样式 · Bootstrap v3 中文文档17. express如何处理404页面??
需要使用中间件
app.use(function(req,res){})
18.综合练习
此练习为一个迷你学生信息管理系统
![v2-e2bfdd7bbf4c4adcd6b57a0fc843c33f_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/30591d7631f3ee5098478888a979e6f9.png)
运行时先执行 npm install 重装一下依赖库
再用 nodemon app 启动服务
输入 127.0.0.1/3000访问首页