五、过渡内容
-
模块系统
- 核心模块
- 第三方模块
- art-template
- 必须通过npm来下载才可以使用
- 自己写的模块
-
npm -
package.json -
Express
- 第三方Web开发框架
- 高度封装了http模块
- 更加专注于业务,而非底层
-
增删改查
-
MongoDB
-
什么是模块化
-
CommonJS模块规范 在Node中的JavaScript还有一个很重要的概念,模块系统
- 模块作用域
- 使用require方法来加载模块
- 使用exports接口对象来导出模块中的成员
-
加载require 语法: var 自定义变量名称 = require('模块')
两个作用:
- 执行被加载模块中的代码
- 得到被加载模块中的exports导出接口对象
-
导出exports
-
Node中是模块作用域,默认文件中所有的成员只在当前文件模块中有效 -
对于希望可以被其他模块访问的成员,我们需要把这些公开的成员挂载到exports接口对象 -
导出多个成员(必须在对象中) exports.a = 123
exports.b = 'hello'
exports.c = function () {
console.log('ccc')
}
exports.d = {
foo: 'bar'
}
-
导出单个成员(拿到的就是:函数、字符串) module.exports = 'hello'
以下情况会覆盖: module.exports = 'hello'
module.exports = {
add: function() {
return x + y
}
str: 'hello'
}
-
也可以这样来导出多个成员 module.exports = {
add: function() {
return x + y
},
str: 'hello'
}
-
原理解析 exports和module.exports是一个引用 console.log(exports === module.exports)
exports.foo = 'bar'
module.exports.foo = 'bar'
-
exports与module.exports的一些注意点
- exports === module.exports结果为true
- 对于
module.exports.xxx=xxx 的方式完全可以使用exports.xxx=xxx - 当一个模块需要导出单个成员时,这个时候必须使用
module.exports=xxx 的方式 - 不要使用
exports=xxx ,不管用 - 每个模块向外
return 的是module.exports exports 只是module.exports 的一个引用- 即便为
exports=xx 重新赋值,也不会影响module.exports - 但是有一种赋值方式比较特殊:
exports=module.exports ,这个用来重新建立引用关系 -
require加载规则
-
package.json
-
我们建议每一个项目都要有一个package.json 文件(包描述文件,就像产品说明书一样) -
这个文件可以通过npm init 的方式初始化
- 对于咱们目前来说,最有用的是
dependencies 选项,可以帮我们保存第三方包的依赖信息 - 如果node_module删除了也不用担心,
npm install 会自动把package.json 中的dependencies -
示例 C:\Users\zc\Desktop\npm-demo>npm init
This utility will walk you through creating a package.json file.
version: (1.0.0)
See `npm help init` for definitive documentation on these fields
and exactly what they do.
Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.
version: (1.0.0) 0.0.1
description: 这是一个测试项目
entry point: (index.js) main.js
test command:
git repository:
keywords:
author: zc
license: (ISC)
About to write to C:\Users\zc\Desktop\npm-demo\package.json:
{
"name": "npm-demo",
"version": "0.0.1",
"description": "这是一个测试项目",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "zc",
"license": "ISC"
}
Is this OK? (yes) yes
对于目前来说,最有用的文件是package.json,可以用来保存第三方包的依赖信息
- 建议每个项目的根目录下都有一个
package.json 文件 - 建议执行
npm install 包名 的时候都加上--save 这个选项,目的是保存依赖项信息 - 如果
node_modules 丢失了也不用担心,我们只需要执行npm install ,就会自动把package.json 中的dependencies 中所有的依赖项都下载回来 -
package.json与package-lock.json 注意:npm 5以前是不会有package-lock.json 这个文件的 npm 5以后才加入了这个文件 安装包时,npm都会生成或更新package-lock.json 这个文件
-
npm 5以后的版本安装包不需要加--save 参数,它会自动保存依赖信息 -
当你安装包的时候,会自动创建或更新package-lock.json 这个文件 -
package-lock.json 这个文件会保存node_modules 中所有包的信息(版本、下载地址)
- 这样的话重新
npm install 的时候速度就会提升 -
从文件来看,有一个lock 称之为锁
-
npm常用命令
-
解决npm被墙问题 npm存储包文件的服务器在国外,有时候会很慢,需要解决这个问题 cnpm:http://npm.taobao.org # 在任意目录下执行都可以
# --global表示安装到全局,而非当前目录
# --global不能省略,否则不管用
npm install --global cnpm
把之前的npm替换为cnpm 举个例子
npm install jquery
cnpm install jquery
如果不想安装cnpm又想使用淘宝的服务器来下载 npm install jquery --registry=https://registry.npm.taobao.org
但是每次手动这样加参数很麻烦,所以我们可以把这个选项加入配置文件中 npm config set registry https://registry.npm.taobao.org
npm config list
只要经过了上面命令的配置,则以后所有的npm install都会默认通过淘宝的服务器来下载 -
path路径操作模块
- path.basename获取一个路径的文件名(默认包含扩展名)
- path.dirname获取一个路径中的目录部分
- path.extname获取一个路径中的扩展名部分
- path.parse把一个路径转为对象
- root 根路径
- dir 目录
- base 包含后缀名的文件名
- ext 后缀名
- name 不包含后缀名的文件名
- path.join需要使用路径拼接时推荐这个方法
- path.isAbsolute判断一个路径是否是绝对路径
> path.basename('c:/a/b/c/index.js')
'index.js'
> path.basename('c:/a/b/c/index.js', '.js')
'index'
> path.basename('c:/a/b/c/index.js', '.html')
'index.js'
> path.dirname('c:/a/b/c/index.js')
'c:/a/b/c'
> path.extname('c:/a/b/c/index.js')
'.js'
> path.extname('c:/a/b/c/index.html')
'.html'
> path.isAbsolute('c:/a/b/c/index.html')
true
> path.isAbsolute('c/index.html')
false
> path.isAbsolute('./c/index.html')
false
> path.isAbsolute('/c/index.html')
true
> path.parse('c:/a/b/c/index.html')
{
root: 'c:/',
dir: 'c:/a/b/c',
base: 'index.html',
ext: '.html',
name: 'index'
}
> path.join('c:/a','b')
'c:\\a\\b'
> var str = ''c:a/' + '/b'
var str = ''c:a/' + '/b'
^
Uncaught SyntaxError: Unexpected identifier
> var str = 'c:a/' + '/b'
undefined
> str
'c:a//b'
> var str = 'c:a' + 'b'
undefined
> str
'c:ab'
> path.join('c:/a/', '/b')
'c:\\a\\b'
> path.join('c:/a/', '/b', 'c')
'c:\\a\\b\\c'
> path.join('c:/a/', '/b', 'c', 'd')
'c:\\a\\b\\c\\d'
> path.join('c:/a/', '/b', 'c', 'd', 'e')
'c:\\a\\b\\c\\d\\e'
> path.join('c:/a/', '/b', 'c', 'd', 'e', 'f')
'c:\\a\\b\\c\\d\\e\\f'
-
Node中的其他成员 在每个模块中,除了require 、exports 等模块相关API之外,还有两个特殊的成员:
__dirname 动态获取可以用来获取当前文件模块所属模块的绝对路径__filename 动态获取可以用来获取当前文件的绝对路径__dirname 和__filename 是不受执行node命令所属路径影响的 在文件操作中,使用相对路径是不可靠的,因为在Node中文件操作的路径被设计为执行node命令所处的路径。 为了解决这个问题,需要把相对路径转变为绝对路径。 这里我们可以使用__dirname 或者__filename 来帮我们解决这个问题 在拼接路径的过程中,为了避免手动拼接带来的一些低级错误,所以推荐多使用:path.join() 来辅助拼接 为了尽量避免这个问题,在文件操作中使用的相对路径统一转换为动态的绝对路径
补充:模块中的路径标识和这里的路径没关系,不受影响(相对于文件模块)
六、Express
6.1 起步
原生的http在某些方面表现不足以应对我们的开发需求,所以我们需要使用框架来加快开发效率,框架的目的就是提高效率,让我们的代码高度统一。
在Node中,有很多Web开发框架,这里我们以Express为主。
- 官网:http://expressjs.com
- 安装:
npm install --save express
6.2 修改完代码自动重启
这里使用一个第三方命名工具,nodemon 来帮我们解决频繁修改代码重启服务器问题
nodemon 是一个基于Node.js开发的一个第三方命令行工具,我们使用的时候需要独立安装
npm install --global nodemon
安装完毕之后,使用
node app.js
nodemon app.js
只要是通过nodemon app.js启动的服务,它会监视你的文件变化,当文件发生变化的时候,自动帮你重启服务器
6.3 基本路由
路由器
get:
app.get('/', function(req, res) {
res.send('Hello World!')
})
post:
app.post('/', function(req, res) {
res.send('Got a POST request')
})
6.4 静态服务
app.use(express.static('public'))
app.use(express.static('files'))
app.use('/static', express.static('public'))
app.use('/static', express.static(path.join(__dirname, 'public')))
6.5 在Express中配置使用art-template模板引擎
安装:
npm install --save art-template
npm install --save express-art-template
配置:
app.engine('art', require('express-art-template'))
使用:
app.get('/', function(req, res) {
// express默认会去项目中的views目录中找index.html
res.render('index.html', {
title: 'hello world'
})
})
如果希望修改默认的views 视图渲染目录,可以:
app.set('views', 目录路径)
6.6 在Express中获取表单GET请求体数据
Express内置了一个API,可以通过req.query 来获取
req.query
6.7 在Express获取表单POST请求体数据
在Express中没有内置获取表单POST请求体的API,这里我们需要使用一个第三方包:body-parser
安装:
npm install --save body-parser
配置:
var express = require('express')
var bodyParser = require('body-parser')
var app = express()
app.use(bodyParser.urlencoded({extended: false}))
app.use(bodyParser.json())
使用:
app.use(function(req, res) {
res.setHeader('Content-Type', 'text/plain')
res.write('you posted:\n')
res.end(JSON.stringify(req.body, null, 2))
})
6.8 提取路由模块
请求方法 | 请求路径 | get参数 | post参数 | 备注 |
---|
GET | /students | | | 渲染首页 | GET | /students/new | | | 渲染添加学生页面 | POST | /students | | name/age/gender/hobbies | 处理添加学生请求 | GET | /students/edit | id | | 渲染编辑 | POST | /students/edit | | id/name/age/gender/hobbies | 处理编辑请求 | GET | /students/delete | id | | 处理删除请求 |
var express = require('express')
var router = express.Router()
router.get('/students', function (req, res) {
})
router.get('/students/new', function (req, res) {
})
router.post('/students/new', function (req, res) {
})
router.get('/students/edit', function (req, res) {
})
router.post('/students/edit', function (req, res) {
})
router.get('/students/delete', function (req, res) {
})
module.exports = router
6.9 设计操作数据的API文件模块
exports.find = function () {
}
exports.save = function () {
}
exports.update = function() {
}
exports.delete = function() {
}
6.10 自己编写的步骤
- 处理模板
- 配置开放静态资源
- 配置模板引擎
- 简单路由:/students渲染静态也处理
- 路由设计
- 提取路由模块
- 由于接下来一系列的业务操作都需要处理文件数据,所以要封装student.js
- 先写好student.js文件结构
- 查询所有学生列表的API find
- findById
- save
- updateById
- deleteById
- 实现具体功能
- 通过路由收到请求
- 接收请求中的数据(get、post)
- 调用数据操作API处理数据
- 根据操作结果给客户端发送响应
- 业务功能顺序
- find
- findIndex
七、MongoDB
参考https://www.runoob.com/mongodb/mongodb-tutorial.html
7.1 关系型数据库和非关系型数据库
表就是关系,或者说表与表之间存在关系
- 所有的关系型数据库都需要通过
SQL 语言来操作 - 所有的关系型数据库在操作之前都需要设计表结构
- 而且数据表还支持约束
- 非关系型数据库非常灵活
- 有的非关系型数据库就是key-value对
- 但是MongoDB是长得最像关系型数据库的非关系型数据库
- 数据库->数据库
- 数据表->集合(数组)
- 表记录->文档对象
- MongoDB不需要设计表结构
- 你可以任意地存放数据
7.2 下载与安装
下载地址
7.3 启动和关闭数据库
启动:
mongod
如果想要修改默认的数据存储目录,可以:
mongod --dbpath=数据存储目录路径
停止:
在开启服务的控制台,直接Ctrl+C即可停止
或直接关闭开启服务的控制台也可以
7.4 连接数据库
连接:
mongo
退出:
exit
7.5 基本命令
show dbs
db
use 数据库名称
- 插入数据
- db.students.insertOne({“name”: “Jack”})
- show collections
- db.students.find()
7.6 在Node中如何操作MongoDB数据库
-
使用官方的mongodb 包来操作:http://github.com/mongodb/node-mongodb-native -
使用第三方mongoose 来操作MongoDB数据库
mongoose 基于MongoDB官方的mongodb 包再一次做了封装- 网址:http://mongoosejs.com
- 官方指南:http://mongoosejs.com/docs/guide.html
- 官方API文档:http://mongoosejs.com/docs/api.html
-
MongoDB数据库的基本概念
- 可以有多个数据库
- 一个数据库中可以有多个集合
- 文档结构很灵活,没有任何限制
- MongoDB非常灵活,不需要像MySQL一样事先创建数据库、表、设计表结构
- 在这里只需要当你插入数据时指定往哪个数据库的哪个集合插入数据即可,一切都由MongoDB来帮你自动完成建库建表这件事
{
qq: {
users: [
{name: '张三', age: 15},
{name: '李四', age: 15},
{name: '王五', age: 15},
{name: '张三123', age: 15},
{name: '张三321', age: 18}
],
products: [
],
...
},
taobao: {
},
baidu: {
}
}
-
增加数据 var admin = new User({
username: 'zs',
password: '123456',
email: 'admin@admin.com'
})
admin.save(function (err, ret) {
if (err) {
console.log('保存失败')
} else {
console.log('保存成功')
console.log(ret)
}
})
-
查询数据 查询所有: User.find(function (err, ret) {
if (err) {
console.log('查询失败')
} else {
console.log(ret)
}
})
按条件查询所有: User.find({
username: 'zs'
}, function (err, ret) {
if (err) {
console.log('查询失败')
} else {
console.log(ret)
}
})
按条件查询单个: User.findOne({
username: 'zs'
}, function (err, ret) {
if (err) {
console.log('查询失败')
} else {
console.log(ret)
}
})
-
删除数据 根据条件删除所有: User.remove({
username: 'zs'
}, function (err, ret) {
if (err) {
console.log('删除失败')
} else {
console.log('删除成功')
console.log(ret)
}
})
根据条件删除一个: Model.findOneAndRemove(conditions, [options], [callback])
根据id删除一个: Model.findByIdAndRemove(id, [options], [callback])
-
更新数据 根据条件更新所有: Model.update(conditions, doc, [options], [callback])
根据指定条件更新一个: Model.findOneAndUpdate([conditions], [update], [options], [callback])
根据id更新一个: User.findByIdAndUpdate('5a001b23d219eb00c8581184', {
password: '123'
}, function (err, ret) {
if (err) {
console.log('更新失败')
} else {
console.log('更新成功')
}
})
参考链接
|