Node.js学习笔记
Node.js简介
- 简单的说 Node.js 就是运行在服务端的 JavaScript。
Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。
Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。
Node.js第一个应用
- 创建一个http服务器
const http = require('http')
const url=require('url')
const app = http.createServer()
app.on('request', (req, res) => {
let {query,pathname}=url.parse(req.url,true)
console.log("路径名为"+pathname)
res.writeHead(200,{
'content-type':'text/html;charset=utf-8'
})
res.end("<h1>请求信息成功</h1>")
}).listen(3000)
console.log('服务器正在监听3000端口')
- 引入http模块,创建完成后,运行,即可在3000端口访问到服务器
Node.js异步
避免回调地狱的两种方法
- promise对象,使用promise对象的reject方法导出错误信息,resolve方法导出正确信息
const fs=require('fs')
new Promise((resolve, reject)=>{
fs.readFile('./1.txt','utf8',(err,data)=>{
if (err){
reject(err)
}else {
resolve(data)
}
})
}).then(result=>console.log(result));`
- async关键字,在函数名前加上async关键字使函数变为异步函数,使用await关键字使异步函数阻塞运行
const fs=require('fs')
const promisify=require('util').promisify //改造现有api
const read = promisify(fs.readFile)
async function run() {
let r1=await read('./1.txt','utf8')
let r2=await read('./2.txt','utf8')
let r3=await read('./3.txt','utf8')
console.log(r1)
console.log(r2)
console.log(r3)
}
run()
Node.js模块化开发
- 使用require引入模块:
const http = require('http')
require方法中可以传入,相对路径,绝对路径
- 使用module.exports将自己创建的模块开放出去供其他模块使用:
module.exports={开放的内容} //开放的内容可以是一个函数,一个对象都可
exports.开放的内容=开放的内容
两种方式都可导出模块
不建议同时使用 exports 和 module.exports
如果先使用 exports 对外暴露属性或方法,再使用 module.exports 暴露对象,会使得 exports 上暴露的属性或者方法失效。
原因在于,exports 仅仅是 module.exports 的一个引用。
Node.js模块系统
- 文件系统模块:
读取文件:
let fs = require("fs");
// 异步读取
fs.readFile('input.txt', (err, data)=> {
if (err) {
return console.error(err);
}
console.log("异步读取: " + data.toString());
});
// 同步读取
let data = fs.readFileSync('input.txt');
console.log("同步读取: " + data.toString());
写入文件:
fs.writeFile("./input.txt","hello",err=>{
if (err!=null){
return console.log(err.toString())
}
console.log("文件写入成功!")
})
- path模块:因为各个操作系统的路径分隔符不同,模块可以使用当前操作系统的路径分隔符拼接路径
const path=require('path')
path.join(__dirname, 'public')
__dirname为全局对象,用于获取当前文件的绝对路径
- config模块:可以读取配置文件的内容,当拼接数据库连接url时可以使用这个模块获取配置信息
//导入config模块
const config=require('config')
const url=`mongodb://${config.get("db.user")}:${config.get("db.pwd")}@${config.get("db.host")}:${config.get("db.port")}/${config.get("db.name")}`
配置文件内容为:
{
"db": {
"user": "blog", //用户名
"host": "localhost", //密码
"port": "27017", //端口号
"name": "blog" //数据库名称
}
}
{
"db": {
"pwd": "APP_PASSWORD" //APP_PASSWORD是系统环境变量的一个值,将密码保存在环境变量中,增加安全性
}
}
- serve-static模块,开放静态资源的模块:
const serveStatic=require('serve-static') //引入模块
const serve=serveStatic(path.join(__dirname,'public')) //告诉静态资源的位置
serve(req,res,()=>{}) //开放静态资源
- dateformat模块:时间格式化模块,可以在模板中格式化时间:
const dateFormat=require('dateformat')
dateFormat(需要格式化的时间,'yyyy年mm月dd日')
- gulp模块,对完成的文件进行压缩等操作:
const gulp = require('gulp')
const htmlmin = require('gulp-htmlmin')
const fileinclude = require('gulp-file-include')
const less = require('gulp-less')
const csso = require('gulp-csso')
const babel = require('gulp-babel')
const uglify = require('gulp-uglify')
//对html文件压缩
gulp.task('htmlmin', () => {
return gulp.src('./src/*.html')
.pipe(fileinclude())
.pipe(htmlmin({collapseWhitespace: true}))
.pipe(gulp.dest('./dist/'))
})
//对css文件压缩
gulp.task("cssmin", () => {
return gulp.src(['./src/css/*.less', './src/css/*.css'])
.pipe(less())
.pipe(csso())
.pipe(gulp.dest('./dist/css'))
})
//对js文件压缩
gulp.task('jsmin', () => {
return gulp.src('./src/js/*.js')
.pipe(babel({
presets: ['@babel/env']
}))
.pipe(uglify())
.pipe(gulp.dest('./dist/js'))
})
//文件的复制
gulp.task('copy', async () => {
await gulp.src('./src/images/*')
.pipe(gulp.dest('./dist/images'))
gulp.src('./src/fonts/*')
.pipe(gulp.dest('./dist/fonts'))
})
gulp.task('default', gulp.series(gulp.parallel('htmlmin', 'cssmin', 'jsmin', 'copy')));
queryString模块,可以将提交的表单数据转化为对象
const queryString=require('querystring')
let student=queryString.parse(formData) //formData为表单数据,student为转换的对象
Node.js路由
- 使用路由模块,可以直接调用get,post方法来响应请求,而不再需要我们自己判断
const getRouter=require('router') //引入路由模块
const router=getRouter() //创建路由
//响应get请求
router.get('/add',(req,res)=>{
const index=template('index',{}) //渲染模板
res.end(index) //响应信息
})
//响应post请求
router.post('/add',(req,res)=>{
let formData=''
req.on('data',param=>{
formData+=param
})
req.on('end',async()=>{
let student=queryString.parse(formData)
await Student.create(student)
res.writeHead(301,{
Location:'/list'
})
res.end()
})
})
Node.js全局对象
- __dirname:当前文件所在目录
- setTimeout():定时器
- clearTimeout()清除定时器
- setInterval()开启一个循环的定时器
function printHello(){
console.log( "Hello, World!");
}
// 两秒后执行以上函数
setTimeout(printHello, 2000);
// 清除定时器
clearTimeout(t);
function printHello(){
console.log( "Hello, World!");
}
// 两秒后执行以上函数
setInterval(printHello, 2000);
- process进程对象:
//通过进程对象获取当前的系统环境输出不同的值
if (process.env.NODE_ENV==='development'){
//开发环境
app.use(morgan('dev'))
}else {
//生产环境
console.log('生产环境')
}
Node.js模板引擎
- art-template模板引擎:
//引入模板
const template = require('art-template')
//引入时间格式化模块
const dateFormat = require('dateformat')
//引入路径拼接模块
const path = require('path')
//将时间格式化模块导入到模板中,使所有模板都可以使用
template.defaults.imports.dateFormat = dateFormat
//设置要渲染的模板的目录
template.defaults.root = path.join(__dirname, '../', 'views')
//设置要渲染的模板的后缀名
template.defaults.extname = '.art'
//创建一个方法封装模板的渲染,
path为渲染的模板的路径,res为响应对象,object为要渲染的数据
module.exports=(path,res,object)=>{
res.writeHead(200, {'content-type': 'text/html;charset=utf8'})
res.end(template(path, object))
}
- 模板语法
– each循环:
{{each students}}
<tr>
<td>{{$value.name}}</td>
<td>{{$value.age}}</td>
<td>{{$value.sex=='0'?'男':'女'}}</td>
<td>{{$value.email}}</td>
<td>
{{each $value.hobbies}}
<span>{{$value}}</span>
{{/each}}
</td>
<td>{{$value.collage}}</td>
<td>{{dateFormat($value.enterTime,'yyyy年mm月dd日')}}</td>
</tr>
{{/each}}
– if…else写法:
{{if admin}}
{{include 'admin_content'}}
{{each list}}
<div>{{$index}}. {{$value.user}}</div>
{{/each}}
{{/if}}
– include语句:可以导入模板中相同的部分,例如html的头部和尾部
{{include './common/header.art'}}
– block语句:可以创建一个块供其他模板在继承之后填入内容
extend语句:用于继承模板
父模板的html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<title>Blog - Content Manager</title>
<link rel="stylesheet" href="/admin/lib/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="/admin/css/base.css">
{{block 'link'}}{{/block}}
</head>
<body>
<!-- 创建一个块main,供其他模板填入内容 -->
{{block 'main'}}{{/block}}
<!-- /删除确认弹出框 -->
<script src="/admin/lib/jquery/dist/jquery.min.js"></script>
<script src="/admin/js/common.js"></script>
<script src="/admin/lib/bootstrap/js/bootstrap.min.js"></script>
<!-- 创建script块,供其他模板填入script内容 -->
{{block 'script'}}{{/block}}
</body>
</html>
子模版的html:
{{extend './common/layout.art'}} //继承父模板
{{block 'main'}} //填充main中的内容
{{include './common/header.art'}} //引入头部模块
<!-- 主体内容 -->
<div class="content">
<!-- 侧边栏 -->
{{include './common/aside.art'}} //引入侧边栏模块
主体内容部分....
{{/block}}
//填充script块的内容
{{block 'script'}}
<script>
$("#delete").on('click', function () {
var id = $(this).attr('#data-id')
// alert(id)
$('#deleteId').val(id)
})
</script>
{{/block}}
Node.js连接MongoDB数据库
- 连接数据库:
//导入config模块
const config=require('config')
//导入mongoose模块
const mongoose=require('mongoose')
const url=`mongodb://${config.get("db.user")}:${config.get("db.pwd")}@${config.get("db.host")}:${config.get("db.port")}/${config.get("db.name")}`
mongoose.connect(url,{ useNewUrlParser: true , useUnifiedTopology: true,useCreateIndex:true })
.then(()=>console.log('数据库连接成功'))
.catch(()=>console.log("数据库连接失败"))
- 创建集合规则:
const mongoose = require('mongoose') //引入mongoose模块
const Joi = require('joi') //引入joi模块,用于表单的验证
//创建集合规则
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
minlength: 2,
maxlength: 20
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
role: {
type: String,
required: true
},
state: {
type: Number,
default: 0
}
})
//使用model方法创建用户集合
const User = mongoose.model('User', userSchema)
//创建判断用户信息有效性的方法
const validateUser = user => {
const schema = {
username: Joi.string().min(2).max(10).required().error(new Error('用户名不符合规则')),
email: Joi.string().email().required().error(new Error('邮箱格式不符合要求')),
password: Joi.string().required().regex(/^[a-zA-Z0-9]{3,30}$/).error(new Error('密码不符合规则')),
role: Joi.string().valid('normal', 'admin').required().error(new Error('角色不合法')),
state: Joi.number().valid(0, 1).required().error(new Error('状态值不合法'))
}
return Joi.validate(user, schema)
}
//导出创建的用户集合和方法供其他模块使用
module.exports = {User,validateUser}
- mongoose添加数据:
User.create(添加的数据对象).then(result => console.log(result))
.catch(err=>{
console.log(err.message)
})
- mongoose删除数据:
User.deleteMany(查询条件).then(data=>console.log(data))
User.findOneAndDelete({
_id:'5c09f267aeb04b22f8460968'
}).then(result=>console.log(result))
- mongoose查询数据:
//分页查询
User.find().skip(2).limit(3).then(data=>console.log(data))
//查询后排序
User.find().sort('-age').then(data=>console.log(data))
User.find(查询条件).then(data=>{
console.log(data)
})
- mongoose修改数据:
//修改多条数据
User.updateMany({},{age:25}).then(result=>console.log(result))
//修改一条数据
User.updateOne({
name:'上官婉儿'
},{name:'张良'}).then(result=>console.log(result))
Node.js与express框架
- express框架的应用:
//导入express框架
const express = require('express')
//创建服务器
const app = express()
//导入封装post请求体的模块
const bodyParser = require('body-parser')
//使用session
const session = require('express-session')
app.use(session({
resave: false, //添加 resave 选项
saveUninitialized: false, //添加 saveUninitialized 选项
secret: 'secret key',
cookie: {maxAge: 24 * 60 * 60 * 1000}
}));
//开放静态资源
app.use(express.static(path.join(__dirname, 'public')))
//设置模板的默认路径
app.set('views', path.join(__dirname, 'views'))
//设置模板的默认后缀
app.set('view engine', 'art')
//根据后缀设置渲染的模板引擎
app.engine('art', require('express-art-template'))
//拦截所有请求对请求体进行封装
app.use(bodyParser.urlencoded({extended: false}))
//错误处理中间件
app.use((err, req, res) => {
console.log(err)
})
//监听端口
app.listen(80)
console.log('服务器启动完成')
- express路由模块:
const express = require('express')
const admin = express.Router()
//渲染登录页面
admin.get('/login',(req, res) => {
res.render('admin/login')
})
//用户登录操作
admin.post('/login', async (req, res) => {
let {email, password} = req.body
if (email.trim().length === 0 || password.trim().length === 0)
return res.status(400).render('admin/error', {msg: '邮件地址或密码错误!'})
let user = await User.findOne({email})
if (user) {
if (!bcryptjs.compareSync(password, user.password)) {
res.status(400).render('admin/error', {msg: '邮箱地址或密码错误,三秒后返回登录页面'})
} else {
//保存用户在session中
req.session.username = user.username
//把用户给其他模板
req.app.locals.userInfo = user
template.defaults.imports.userInfo=user
//保存用户角色到session
req.session.role=user.role
if (user.role==='admin'){
res.redirect('/admin/user?page=1')
}else {
res.redirect('/home/')
}
}
} else {
res.status(400).render('admin/error', {msg: '邮箱地址或密码错误,三秒后返回登录页面'})
}
})