nodejs笔记

nodejs笔记

简介

node.js不是语言,也不是框架,而是一个基于chrome的JavaScript运行平台,是运行在服务端的JavaScript,所以nodejs更浏览器一样,也是单线程的。

安装:

  1. 官网下载安装包安装
  2. 修改安装目录下./node_modules/npm/npmrc的文件内容为(修改npm全局安装路径):
prefix = D:/soft/nodejs
cache = D:/soft/nodejs/npm_global_cache
  1. 查看当前安装版本:node -v

demo

编写文件:hello.js

console.log("hello world!");

解析执行:node hello.js

文件操作

浏览器中的JavaScript是没有文件操作能力的,但是在node中可以,且文件操作都是异步的(一般参数中有回调函数的函数都是异步函数)。

读取文件

// 加载fs核心模块
// 这里的./是相对于执行node命令的相对路径而不是相对当前文件的相对路径
var fs = require('fs');
fs.readFile("./1.txt", function(error, data){
    console.log(error);
    console.log(data);
});

写文件

var fs = require('fs');
fs.writeFile('./1.txt', 'hello Node.js', function(error) {
    if (error) {
        console.log('写入失败')
    } else {
        console.log('写入成功了')
    }
});

HTTP

demo

该模块可以创建和编辑服务器,示例代码:

// 加载http模块
var http = require('http');
var url = require('url');
// 创建一个Web服务器
var server = http.createServer();
// 当客户端发送请求,会执行回调函数
server.on('request', function(req, res) {
    console.log('请求地址:', req.socket.remoteAddress, req.socket.remotePort);
    console.log('请求路径:' + req.url);
    console.log('请求参数:', url.parse(req.url, true));
    // 设置编码和响应类型,避免乱码
    res.setHeader('Content-Type', 'text/html; charset=utf-8')
    res.write('hello ');
    res.write('nodejs');
    // 结束响应,也可以直接使用:res.end("hello nodejs");
    // 另外响应只能是字符串,其他对象需要使用JSON.stringify(obj)转化为字符串
    // 返回重定向:res.statusCode=302;res.setHeader('Location', '/');
    res.end();
});
// 绑定端口号,启动服务器
server.listen(8080, function() {
  console.log('启动成功,访问地址:http://127.0.0.1:8080/');
});

据此,对于静态文件,直接读取文件然后响应给客户端就完成了静态web服务器的功能。

根据路径返回文件内容

var http = require('http')
var fs = require('fs')
var server = http.createServer()
var filePath = 'E:/img'
server.on('request', function(req, res) {
	fs.readdir(filePath + req.url, function(error, filesArr){
        if(!error){
			filesArr.forEach(function(data, index){
				res.write(`<a href='${req.url}${data}'>${data}</a><br>`)
			})
            return res.end()
		}
    })
	fs.readFile(filePath + req.url, function(err, data){
		if(err){
			return res.end('404: ' + err)
		}
		res.end(data)
	})
}).listen(8080, function() {
  console.log('启动成功,访问地址:http://127.0.0.1:8080/')
})

Path

path是nodejs中路径操作的核心模块;

常用api示例

  • path.basename('D:/data/index.js', '.js'):返回:index
  • path.basename('D:/data/index.js'):返回:index.js
  • path.dirname('D:/data/index.js'):返回:D:/data
  • path.extname('D:/data/index.js'):放回:.js
  • path.parse('D:/data/index.js'):返回:{root: 'D:/', dir: 'D:/data', base: 'index.js', ext: '.js', name: 'index'}
  • path.join('D:/data/', '/a', 'index.js'):返回:D:/data/a/index.js

加载与导出

require与export

加载:

  • nodejs中通过require加载执行模块(即加载执行文件,nodejs中文件的加载个人认为可以将模块文件当作一个无参函数,加载时相当于执行这个函数),如:
console.log('a start');
// 可以省略后缀名.js
var b = require('./b');
console.log('a end');
// 执行顺序是a start -> b start -> a end
  • node会优先从缓存加载,如:a加载b,a加载c,b加载c,此时c中代码只在b中执行一次,a中只是拿到c的返回对象,但不会执行c中的代码;
  • 加载第三方包会从上一级目录下的node_modules目录中找,若没有则继续上一级目录找,到根目录都没有则报错。

导出:

  • 在node中没有全局作用域,即内部不能访问外部,外部也不能访问内部;
  • nodejs在完成模块加载时,会返回module.exports对象;
  • node内部还定义了exports=module.exports,所以也可以在内部设置exports的属性给外部使用;
  • 外部模块可以通过require获取内部模块返回的module.exports对象。

由上面规则知道,直接给exports或者module.exports赋值,容易混淆返回值,但最终外部获取的对象应该以module.exports的值为准。

__dirname与__filename

在nodejs中,./所代表的相对路径是相对于执行node命令的而不是相对于当前文件的,对此在nodejs的每个模块中有了以下两个特殊成员:

  • __dirname:可以用来获取当前文件模块所属目录的绝对路径
  • __filename:可以用来获取当前文件的绝对路径

art-template

art-template是模板引擎,可以在node和浏览器中执行。
安装:npm install art-template --save(会安装到执行该命令的目录)

文档:http://aui.github.io/art-template/zh-cn/docs/index.html

在浏览器中使用

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>在浏览器中使用art-template</title>
</head>
<body>
    ·
  <!-- 注意:在浏览器中需要引用 lib/template-web.js 文件 -->
  <script src="node_modules/art-template/lib/template-web.js"></script>
  <script type="text/template" id="tpl">
      引入公有页面:{{include './header.html'}}
      <p>大家好,我叫:{{ name }}</p>
      <p>我今年 {{ age }} 岁了</p>
      <h1>我来自 {{ province }}</h1>
      <p>我喜欢:{{each hobbies}} {{ $value }} {{/each}}</p>
  </script>
  <script>
    var ret = template('tpl', {
      name: 'Jack',
      age: 18,
      province: '北京市',
      hobbies: ['写代码', '唱歌', '打游戏']
    })
    console.log(ret)
  </script>
</body>
</html>

在nodejs中使用

// 在node中使用需要配置环境变量或者在art-template的安装目录下执行
var template = require('art-template')
res = template.render(`
    <p>大家好,我叫:{{ name }}</p>
    <p>我今年 {{ age }} 岁了</p>
    <h1>我来自 {{ province }}</h1>
    <p>我喜欢:{{each hobbies}} {{ $value }} {{/each}}</p>
`, {
      name: 'Jack',
      age: 18,
      province: '北京市',
      hobbies: ['写代码', '唱歌', '打游戏']
    })
console.log(res);

npm

npm网站:https://www.npmjs.com,所有通过npm命令下载的包都是从这个网站下载的,另外也可以在该网站查看部分npm包的使用文档。

npm常用命令:

  • npm init:生成package.json文件,可以加选项:-y,快速生成;
  • npm install:根据package.json安装第三方包(install可以简写为i);
  • npm install 包名:安装第三方包;
  • npm install 包名 --save:安装第三方包,并添加到package.json的dependencies中;
  • npm install 包名 --save-dev:安装第三方包,并添加到package.json的devDependencies中;
  • npm uninstall 包名:删除包但保留依赖(uninstall可以简写为un);
  • npm uninstall 包名 --save:删除包,同时删除依赖;
  • npm install --global 包名:全局安装;
  • npm uninstall --global 包名:全局卸载;
  • npm install --global npm:升级npm;

说明:

  • npm5版本以后,不需要–save参数,也会添加依赖,同时还会生成package-lock.json文件;
  • package.json文件只会列出当前项目的依赖,且每次重新安装时会自动升级新版本,而不是文件中写的版本;
  • package-lock.json会列出当前项目依赖项的依赖,即所有依赖,同时还会列出依赖项的下载地址、版本号,且每次重新安装会使用文件中的版本,不会自动升级。

使用淘宝npm镜像(http://npm.taobao.org)解决npm很慢的问题:

  • 方式一:
    • 安装淘宝npm:npm install --global cnpm
    • 然后将原来的npm命令改为cnpm来执行;
  • 方式二:
    • 配置全局参数:npm config set registry https://registry.npm.taobao.org/
    • 查看配置信息:npm config list(默认的官方配置为:https://registry.npmjs.org/);
    • 然后执行npm命令时就会使用淘宝镜像了。

nrm:nrm提供了一些常用的npm包镜像地址,能够让我们快速的切换包镜像服务器地址;使用方式:

  • npm install nrm -g:安装;
  • nrm ls:查看镜像地址列表,列表中带*的就是正在使用的;
  • nrm use 镜像名:使用指定镜像地址。

express

express是基于nodejs的开源的轻量级的web框架;

安装:npm install express --save

demo

该demo具有路由模块原则,还整合了art-template以及get、post请求参数的获取:

main.js

包含以下模块的配置示例:

  • art-template模板引擎配置
  • body-parser中间件配置
  • 静态资源路径配置
  • session模块配置
  • 两种路由使用方式的配置
  • express中的中间件的使用
  • 404处理
  • 全局错误处理
var express = require('express')
var app = express()
// 使用模板引擎art-template,模板默认路径在views/下
app.engine('html', require('express-art-template'))
// 修改模板默认路径views/修改为html/
// app.set('views', 'html/')
// 配置 body-parser 中间件(插件,专门用来解析表单 POST 请求体,使用方法:req.body)
var bodyParser = require('body-parser')
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())
// 指定静态资源路径
app.use('/public', express.static('./public/'))
// 引入并配置session,使用方法:req.session.attr1=value1
session = require('express-session')
app.use(session({secret: 'xxx', resave: false, saveUninitialized: true}))
// 指定处理具体请求的router,这个需要放在app的配置后面
var router = require('./router.js')
// 方式一(推荐)
// router中配置的请求,本质上也是中间件
app.use(router)
// 方式二
// router(app)
// 中间件:满足规则的所有url都会进入中间件方法
// 第一个参数可以限定中间件匹配的url前缀,没有则不限定url
// 多个中间件按代码顺序匹配
// 多个中间件使用的req和res是同一个对象
// app.get()与app.post()方法本质上也是中间件
app.use(function(req, res, next){
    console.log("中间件1")
    //next()  //调用next方法才会执行后面的中间件
})
// 一般最后一个不限定url的中间件用于处理404
app.use(function(req, res, next){
    console.log("中间件2")
    res.send("404")
})
// 统一异常处理中间件,4个参数一个不能少
// 在上面的中间件中调用next(err)方法,会跳过上面匹配的中间件而直接进入该方法
app.use(function(err, req, res, next){
    res.send('出错了')
})
app.listen(8080, function(){
    console.log('启动成功,访问地址:http://127.0.0.1:8080/')
})
router.js

两种路由实现方式,与上面的main.js中的两种方式相对应,还包含:

  • get方式参数的获取
  • post方式参数的获取
  • art-template模板引擎的使用
  • 以下代码,本质上也是中间件
// 方式一(推荐)
var express = require('express')
var router = express.Router()
router.get('/', function(req, res, next){
    res.render('index.html', {name: '首页'})
}).get('/get', function(req, res){
    res.send(req.query)
}).post('/post', function(req, res){
    res.send(req.body)
})
module.exports = router
// 方式二
/*module.exports = function(app){
    // 方法名对应请求方式的get、post,第一个参数是访问路径
    app.get('/', function(req, res){
        res.render('index.html', {name: '首页'})
    }).get('/get', function(req, res){
        res.send(req.query)
    }).post('/post', function(req, res){
        res.send(req.body)
    })
}*/
index.html

art-template要渲染的模板内容,该文件路径为:./views/index.html

<html>
    <head>
        <meta http-equiv="content-type" content="text/html;charset=utf-8">
        <title>index</title>
    </head>
    <body>
        <h1>这是{{name}}</h1>
        <a href="/get?name=张三&age=22">get请求</a><br>
        <form action="/post" method="post">
            <input type="text" name="name" value="李四"/><br>
            <input type="number" name="age" value="3"/><br>
            <input type="submit" value="post提交"/><br>
        </form>
    </body>
</html>

热部署插件

nodemon会监视文件变化并自动重启服务器,使用方法如下:

  • npm install --global nodemon:安装第三方命令行工具;
  • 之后将原来的执行命令node xxx.js改为nodemon xxx.js

连接MongoDB

原生操作

文档:https://github.com/mongodb/node-mongodb-native/

安装:npm install mongodb --save

mongoose

英文文档:https://mongoosejs.com/ 中文文档:http://www.mongoosejs.net

安装:npm install mongoose --save

示例代码:

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test01', {useNewUrlParser: true, useUnifiedTopology: true});

var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
    // 这两行必须在调用model方法前面
    var StuSchema = new mongoose.Schema({name: {type: String, required: true}, age: Number});
    StuSchema.methods.say = function(){console.log(this.name + ": " + this.age);};
    var Stu = mongoose.model('stus', StuSchema);
    // var s1 = new Stu({name: "张三", age: 28});
    // s1.save(function(err, s1){
    //     if(err){
    //         return console.error(err);
    //     }
    //     console.log("保存成功: " + s1);
    // });
    Stu.find(function(err, stus){
        if(err){
            return console.error(err);
        }
        console.log(stus);
        stus.forEach(function(v, i){v.say();});
    });
});

express-mongoose

示例代码:

main.js

var express = require('express')
var app = express()
app.engine('html', require('express-art-template'))
var bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({extended: false}))
app.use(bodyParser.json())
app.use('/public', express.static('./public/'))
var router = require('./router.js')
app.use(router)
app.listen(8080, function(){
    console.log('启动成功,访问地址:http://127.0.0.1:8080/')
})

router.js

var express = require('express')
var router = express.Router()
var Stu = require('./mongoose_stu.js')
/**
 * 首页,所有学生列表
 */
router.get('/', function(req, res){
    Stu.find(function(err, stus){
        if(err){
            res.render('index.html', {datas: [], msg: err})
        }else{
            res.render('index.html', {datas: stus, msg: ''})
        }
    });
})
/**
 * 添加
 */
router.post('/save', function(req, res){
    new Stu(req.body).save(function(err, stu){
        if(err){
            res.render("index.html", {datas: [], msg: err})
        }else{
            res.redirect("/")
        }
    })
})
/**
 * 删除
 */
router.get('/delete', function(req, res){
    Stu.findByIdAndRemove(req.query.id, function(err){
        if(err){
            res.render('index.html', {datas: [], msg: err})
        }else{
            res.redirect("/")
        }
    })
})
/**
 * 更新
 */
router.post('/update', function(req, res){
    Stu.findByIdAndUpdate(req.body.id, req.body, function(err){
        if(err){
            res.render('index.html', {datas: [], msg: err})
        }else{
            res.redirect("/")
        }
    })
})
module.exports = router

mongoose_stu.js

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test01', {useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false});
// 这两行必须在调用model方法前面
var StuSchema = new mongoose.Schema({name: {type: String, required: true}, age: Number});
StuSchema.methods.say = function(){
    console.log(this.name + ": " + this.age);
};
var Stu = mongoose.model('stus', StuSchema);
module.exports = Stu;

index.html

<html>
    <head>
        <meta http-equiv="content-type" content="text/html;charset=utf-8">
        <title>index</title>
    </head>
    <body>
        <a href="/">首页</a>
        <p>{{msg}}</p>
        {{each datas}}
        <li>{{$value.id}}===>{{$value.name}}===>{{$value.age}}<a href="/delete?id={{$value.id}}">删除</a></li>
        {{/each}}
        <br><br>
        <form action="/save" method="post">
            name: <input type="text" name="name"/><br>
            age:  <input type="text" name="age"/><br>
            <input type="submit" value="添加"/>
        </form>

        <form action="/update" method="post">
            id:   <input type="text" name="id"/><br>
            name: <input type="text" name="name"/><br>
            age:  <input type="text" name="age"/><br>
            <input type="submit" value="修改"/>
        </form>
    </body>
</html>

package.json

{
  "name": "express_mongoose_demo",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "art-template": "^4.13.2",
    "body-parser": "^1.19.0",
    "express": "^4.17.1",
    "express-art-template": "^1.0.1",
    "mongoose": "^5.8.0"
  }
}

Promise

回调地狱:JavaScript编程中,异步代码不能保证执行顺序,要保证执行顺序需要在异步代码的回调函数编写后序执行的代码,即回调嵌套,也叫回调地狱。这种代码难以维护,所以在EcmaScript6中添加了Promise API。

Promise API示例代码(mongoose的所有数据库操作API都支持promise):

var fs = require('fs')
// 异步任务1
var p1 = new Promise(function (resolve, reject) {
    fs.readFile('./data/a.txt', 'utf8', function (err, data) {
        if (err) {
            reject(err)
        } else {
            resolve(data)
        }
    })
})
// 异步任务2
var p2 = new Promise(function (resolve, reject) {
    fs.readFile('./data/b.txt', 'utf8', function (err, data) {
        if (err) {
            reject(err)
        } else {
            resolve(data)
        }
    })
})
// 顺序执行1、2,这里then方法第一个参数是上面的resolve,第二个参数是上面的reject
p1.then(function (data) {
    console.log(data)
    //return一个Promise 对象的时候,以便后续异步任务执行
    return p2
}, function (err) {
    console.log('读取文件失败了', err)
}).then(function (data) {
    console.log(data)
})

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值