node进阶第一天总结
目标
什么是nodejs的模块化
npm的使用
使用第三方模块
node中的自定义模块
模块查找规则
模块化开发:MVC架构
程序开发中的模块及好处
封装到一个单独的文件中去,这些单独抽离出来的代码文件叫做程序中的模块化。
好处:保证了每个文件的功能(职能)单一;需要什么特定的功能,直接调用某一个特定的模块;对将来程序开发和维护都有好处!
Node 中的模块的分类
node由三部分组成:ECMAScript + 核心模块 + 第三方模块
核心模块
模块,编译成了二进制可执行文件,打包到了Node的安装包(随着node安装到本地)。
使用核心模块使用require(‘核心模块的名称’);
第三方模块
别人做好的我们可以下载这些模块开发中会使用很多第三方模块
npm的使用
(俗称下包)通过npm我们可以下载项目中所需要的第三方模块
nrm(换镜像源提高效率)
1.安装:
npm install -g nrm
2.查看当前及所有可用的源
$ nrm ls
// * 代码当前源
* npm ----- https://registry.npmjs.org/
yarn ----- https://registry.yarnpkg.com
cnpm ---- http://r.cnpmjs.org/
taobao -- https://registry.npm.taobao.org/
nj ------ https://registry.nodejitsu.com/
skimdb -- https://skimdb.npmjs.com/registry
3.切换当前的源
$ nrm use cnpm // 切换到cnpm
Registry has been set to: http://r.cnpmjs.org/
npm使用流程(开发是查找对应的文档)
- 初始化:在当前nodejs项目中执行终端命名:
npm init -y
- 作用:生成一个
pachage.json
文件,帮你记录当前项目安装了哪些第三方模块及对应的版本号
- 作用:生成一个
- 安装:在当前nodejs项目中执行终端命名:
npm install 模块名
- 安装之后,你的项目目录会新增两个文件
node_modules
与package-lock.json
- node_modules:npm会自动将所有的第三方模块放入这个文件夹中。类似于前端的
lib文件夹
- package.json:npm会自动记录第三方模块的下载地址,下一次安装或更新的时候直接从这个地址下载,速度更快(只是影响以后更新速度,不影响开发)
- node_modules:npm会自动将所有的第三方模块放入这个文件夹中。类似于前端的
- 安装之后,你的项目目录会新增两个文件
第三方模块的使用
三步走
1先使用npm下载这个模块
2使用require导入这个第三方模块!(安装导入名字应该相同)
3通过官方文档,试着去使用这个第三方模块!
注意:无论是核心模块、还是第三方模块,都是通过 require来引用这个模块的!
5.一个简单的第三方模块使用的案例
// 这个文件演示一下如果使用第三方模块moment实现日期数据的格式化处理
// 1. 引入moment(引入之前确保这个模块已经正确的下载哦)
const moment = require('moment')
// 2.定义一个日期,现在日期值是未格式化的日期数据
let timeNow = new Date()
// 3.调用moment实现对日期数据的格式化处理
console.log(moment(timeNow).format('YYYY-MM-DD HH:mm:ss')
node中的自定义模块(重点)
定义用户模块(自己添加所需要的)
导出用户模块中的成员(exports)
它是一个对象,我们可以在这个对象上挂载你想暴露的成员。
使用.语法在exports上挂载成员
1定义用户模块
// 我们所创建的js文件就可以认为是一个自定义模块
let obj = {
name:'jack',
age:20
}
function sayHi(){
console.log('我的名字叫:'+obj.name+",我的年龄是:"+obj.age)
}
// 在模块中有一个默认的对象exports,这个对象在在这个模块被引入的时候会自动的返回
// 我们暴露成员的方式就是在这个对象上挂载你想暴露的成员
exports.sayHi = sayHi
exports.aa = obj
2.使用用户模块
// 如何使用用户自定义模块:
// 所有模块想使用都要先引入
// 如果引入一个模块,那么这个模块默认会返回一个对象
let umodule = require('你想引入的模块路径')
umodule.sayHi()
module.exports
这是个对象,可以在对象上挂载你想暴露的成员。
语法:module.exports上挂载成员。
module.exports重置为一个新的对象,对象中就包含你想暴露的成员。
1.定义用户模块
// 我们所创建的js文件就可以认为是一个自定义模块
let obj = {
name:'jack',
age:20
}
function sayHi(){
console.log('我的名字叫:'+obj.name+",我的年龄是:"+obj.age)
}
// 在模块中有一个默认的对象module.exports,这个对象在在这个模块被引入的时候会自动的返回
// 我们暴露成员的方式就是在这个对象上挂载你想暴露的成员
// module.exports.sayHi = sayHi
// module.exports.aa = obj
// 也可以直接使用你想暴露的成员覆盖module.exports
module.exports = sayHi
2.使用用户模块
// 如何使用用户自定义模块:
// 所有模块想使用都要先引入
// 如果引入一个模块,那么这个模块默认会返回一个对象
let umodule = require('你想引入的模块路径')
umodule.sayHi()
global(了解)
相当于浏览器中的window,global是一个全局对象
- 全局变量污染
- 暴露的成员不明确:不知道成员是从哪个模块中暴露出去的
- 成员可能被覆盖
- 不建议使用
- exports 和 module.exports 的区别(建议使用module.exports)
- 通过 module.exports 可以使用 . 的形式追加属性,也可以使用 等号 直接赋值的形式导出成员;
- exports 只能通过 . 的形式追加属性;不能使用 等号 直接赋值的形式!
- 注意: 在一个 module 中,最终向外暴露的成员,以 module.exports 指向的对象为准!
- 在一个模块中,不要混合使用
module.exports
和exports
模块查找(加载)规则(了解)
-
优先从缓存中加载
-
加载核心模块:优先从缓存中加载;如果缓存中没有的话,再去执行加载核心模块!
-
用户自定义模块:优先从缓存中加载;如果缓存中没有的话,再去执行加载用户模块!
用户模块的查找规则: 如果不写后缀名,则先严格按照给定的文件名去查找模块并加载执行;
index -> index.js -> index.json -> index.node(主要看这几步即可)
第三方模块查找规则:
4. 首先,查看项目根目录中有没有 `node_modules` 文件夹
5. 查找 `node_modules` 文件夹中,有没有和第三方模块名称一致的文件夹
6. 在模块对应的文件夹中,查找有没有 `package.json` 这个文件
7. 在 `package.json` 文件中,查找有没有 `main` 属性
8. 如果有 `main` 属性,并且 `main` 属性指向的路径存在,那么就尝试加载这个路径指定的文件!
9. 如果 `package.json` 文件中,没有 `main` 属性,或者 `main` 属性指向的路径不存在,或者没有`package.json` 文件, 那么,Node尝试加载 模块根目录中 `index` 相关文件:`index.js` -> `index.json` -> `index.node`
10. 如果在`node_modules`文件夹中,找不到对应的模块文件夹,或者在项目根目录中根本没有`node_modules`文件夹,则向上一层文件夹中去查找,查找规则同上!
11. 如果上一层目录中也没有查找到,则再向上翻一层去查找,直到找到当前项目所在的盘符根目录为止!
12. 如果找到当前盘符根目录还找不到,则报错:***cannot find module***
案例
搭建案例目录结构
**#### 在app.js中完成所有的功能
创建服务器**
// 1.引入协议
const http = require('http')
// 2.创建服务器
const server = http.createServer()
// 3.添加对端口的监听
server.listen(3004, function () {
console.log('http://127.0.0.1:3004')
})
目中需要使用到的核心模块和第三方模块
const fs = require('fs')
const mime = require('mime')
const querystring = require('querystring')
##### 添加用户请求的监听
完成注册页面的读取并返回
完成静态资源的处理
完成注册功能
html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户注册</title>
<link rel="stylesheet" href="/css/index.css">
<script src="/js/jquery.js"></script>
</head>
<body>
<div class="register">
<form id="ajaxForm">
<ul>
<li>
<label for="">用户名</label>
<input type="text" name="username" class="name">
</li>
<li>
<label for="">密码</label>
<input type="password" name="password" class="pass">
</li>
<li>
<label for="">手机号</label>
<input type="text" name="phone" class="mobile">
</li>
<li>
<label for=""></label>
<input type="button" class="submit " value="立即注册">
</li>
</ul>
</form>
<img src="/images/timg.gif" alt="">
</div>
<script>
$('.submit').on('click', function () {
$.ajax({
type: 'post',
url: 'http://127.0.0.1:3004/register',
data: $('#ajaxForm').serialize(),
dataType: 'json',
success: function (res) {
console.log(res)
if (res.code == 200) {
alert(res.msg)
// 跳转到列表页
} else {
alert(res.msg)
}
}
})
})
</script>
</body>
</html>
app.js页面
// 一般没有特别的需要,服务器的入口文件都是叫app.js
// 引入核心模块
const http = require('http')
const fs = require('fs')
const path = require('path')
// 引入第三方模块
const mime = require('mime')
// 引入自定义模块
const usermodule = require('./js/common')
const server = http.createServer()
server.listen('3004', function () {
console.log('http://127.0.0.1:3004')
})
server.on('request', function (req, res) {
// 约定请求形式和请求url
// 1.响应注册页面 get /register
// 2.实现注册功能 post /register
// 获取用户请求方式
let method = req.method
// 获取用户请求url
let myurl = req.url
console.log(method, myurl)
// 进行用户请求方式和请求url的判断,从而实现相应的响应
if (method.toLowerCase() == 'get' && myurl == '/register') {
// 读取注册页面并响应
fs.readFile(path.join(__dirname, '/views/register.html'), (err, data) => {
if (err) {
res.end('404')
} else {
res.end(data)
}
})
}
// 处理静态资源 /css/reset.css
// indexOf:查找指定字符串在源字符串第一次出现的索引位置,如果找不到就返回-1
// myurl.indexOf('/css/') != -1:说明当前的url有/css/,意味着它是一个css请求
else if (method.toLowerCase() == 'get' && (myurl.indexOf('/css/') != -1 || myurl.indexOf('/js/') != -1 || myurl.indexOf('/images/') != -1)) {
console.log(path.join(__dirname, myurl))
// 读取静态资源文件
fs.readFile(path.join(__dirname, myurl), (err, data) => {
if (err) {
res.end('404')
} else {
// 响应之前,进行资源的mime类型的设置,明确的告诉客户端文件的类型是什么,方便客户端的处理
// getType:根据文件的扩展名获取文件的mime类型
res.setHeader('Content-Type', mime.getType(myurl))
res.end(data)
}
})
}
else if (method.toLowerCase() == 'post' && myurl == '/register') {
// 1.接收参数:node支持传递大容量的参数,为了节省服务器的内存开销,node实行分批接收参数,接收的参数是字符串类型
let str = '' // 定义一个参数用于存储接收到的参数字符串
// 1.1 添加data事件,用于分批接收参数--拼接,每次接收到参数就会自动的触发data事件,所以我们可以在data事件拼接接收到的参数.它有一个回调函数,每次接收到参数就会调用这个函数,同时将接收到的参数赋值给chunk
req.on('data', (chunk) => {
str += chunk
})
// 1.2 添加end事件,当所有参数接收完毕的时候,就会触发end事件,在参数接收完毕之后进行下一步的处理
req.on('end', () => {
console.log(str) // username=saaa&password=sdsafds&phone=fasdfasdfas
// 2.将参数转换为对象
let obj = usermodule.getParameter(str) // {}
console.log(obj)
// 3.将数据添加到json文件
// 3.1 读取json文件
fs.readFile(path.join(__dirname, '/data/users.json'), 'utf-8', (err, data) => {
if (err) {
// 这里返回了json格式的字符串
let ret = {
code: 404,
msg: '注册失败'
}
res.end(JSON.stringify(ret))
} else {
// 3.2 将内容转换为数组
let arr = JSON.parse(data)
// 3.3 将新新增数据添加到数组中
arr.push(obj)
console.log(arr)
// 3.4 将添加了新数据的数组重新转换为字符串
// 3.5 将字符串写入到json文件中
fs.writeFile(path.join(__dirname, '/data/users.json'), JSON.stringify(arr, null, ' '), (err) => {
if (err) {
// 这里返回了json格式的字符串
let ret = {
code: 404,
msg: '注册失败'
}
res.end(JSON.stringify(ret))
} else {
let ret = {
code: 200,
msg: '注册成功'
}
res.end(JSON.stringify(ret))
}
})
}
})
})
}
})
common.js(用来去除一些符号直接拿到对象)
module.exports = {
getParameter: function (str) { // ?id=7&name=jack
// 删除?
str = str.replace('?', '') // id=7&name=jack
// 分割字符串
var arr = str.split('&') // ["id=7","name=jack"]
// 循环遍历再次分割
var obj = {}
for (var i = 0; i < arr.length; i++) { // 1.id=7
var temp = arr[i].split('=') // ["id",7]
// 将数据添加到对象
obj[temp[0]] = temp[1] // {id:7}
}
return obj
}
}