node-js备忘(二)

一. 模块系统

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​www.npmjs.com

图标

我们可以再网站中搜索包

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.taobao.org

npm install --global cnpm

然后把之前的指令中的npm改为cnpm即可

4、描述文件

建议每一个项目要有一个 package.json文件(包描述文件)

我们想弄清楚项目的依赖包情况,可以访问这个package.json文件

这个文件可以用以下命令自动创建:

npm init

这是一个询问式的命令,会询问你一些项目基本信息,按需填写就可以

此时已经生成了package.json

然后

npm install 包名 --save

//备注:npm 5.x以后不写save也可以自动保存了

此时json文件会记录下我们安装的包

如果node_moudles被删除了,我们可以直接执行以下命令一口气装好

npm install //此命令会重新下载package.json中的dependencies中的包

三、express模块初步使用

原生的http表现不足,需要使用框架加快效率,也让代码高度统一

Express - 基于 Node.js 平台的 web 应用开发框架​www.expressjs.com.cn

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);  })

注:

  • 比如一个ajax请求要求发json, express提供一个方法 res.json(对象) 它会自动将对象转为JSON对应的字符串发送给浏览器

  • 如果将来安装了art-template,可以用 res.render('页面',模板规则对象)来发送页面

4、静态服务 //指定公开目录

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

这样就公开了public中的所有资源,可以访问了

image

此外,我们可以省略第一个参数,这时url也省略对应的层

app.use(express.static('./public/'))
image

其实,我们可以给文件路径起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);  })
image

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');
})
image
image

③、如果想修改目录(一般不需要)则:

app.set('views','新的路径')

7. 重定向

res.redirect('/')

8、解析POST请求中的数据

需要借用一个插件 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 中文文档​v3.bootcss.com

Examples​v4.bootcss.com
图标

全局 CSS 样式 · Bootstrap v3 中文文档​v3.bootcss.com

17. express如何处理404页面??

需要使用中间件

app.use(function(req,res){})

18.综合练习

此练习为一个迷你学生信息管理系统

image

https://github.com/zfdok/node_stu_mgrgithub.com

运行时先执行 npm install 重装一下依赖库

再用 nodemon app 启动服务

输入 127.0.0.1/3000访问首页

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容