nodejs基础知识笔记
Node介绍
为什么要学习Node.js
- 企业需求
- 具有服务端开发经验更改
- front-end
- back-end
- 全栈开发工程师
- 基本的网站开发能力
- 服务端
- 前端
- 运维部署
- 多人社区
Node.js是什么
- Node.js是JavaScript 运行时
- 通俗易懂的讲,Node.js是JavaScript的运行平台
- Node.js既不是语言,也不是框架,它是一个平台
- 浏览器中的JavaScript
- EcmaScript
- 基本语法
- if
- var
- function
- Object
- Array
- Bom
- Dom
- EcmaScript
- Node.js中的JavaScript
- 没有Bom,Dom
- EcmaScript
- 在Node中这个JavaScript执行环境为JavaScript提供了一些服务器级别的API
- 例如文件的读写
- 网络服务的构建
- 网络通信
- http服务器
- 构建与Chrome的V8引擎之上
- 代码只是具有特定格式的字符串
- 引擎可以认识它,帮你解析和执行
- Google Chrome的V8引擎是目前公认的解析执行JavaScript代码最快的
- Node.js的作者把Google Chrome中的V8引擎移植出来,开发了一个独立的JavaScript运行时环境
- Node.js uses an envent-driven,non-blocking I/O mode that makes it lightweight and efficent.
- envent-driven 事件驱动
- non-blocking I/O mode 非阻塞I/O模型(异步)
- ightweight and efficent. 轻量和高效
- Node.js package ecosystem,npm,is the larget scosystem of open sourcr libraries in the world
- npm 是世界上最大的开源生态系统
- 绝大多数JavaScript相关的包都存放在npm上,这样做的目的是为了让开发人员更方便的去下载使用
- npm install jquery
Node能做什么
- web服务器后台
- 命令行工具
- npm(node)
- git(c语言)
- hexo(node)
- …
- 对于前端工程师来讲,接触最多的是它的命令行工具
- 自己写的很少,主要是用别人第三方的
- webpack
- gulp
- npm
起步
安装Node环境
- 查看Node环境的版本号
- 下载:https://nodejs.org/en/
- 安装:
- 傻瓜式安装,一路
next
- 安装过再次安装会升级
- 傻瓜式安装,一路
- 确认Node环境是否安装成功
- 查看node的版本号:
node --version
- 或者
node -v
- 查看node的版本号:
- 配置环境变量
解析执行JavaScript
- 创建编写JavaScript脚本文件
- 打开终端,定位脚本文件的所属目录
- 输入
node 文件名
执行对应的文件
注意:文件名不要用node.js
来命名,也就是说除了node
这个名字随便起,最好不要使用中文。
文件的读写
文件读取:
1 | //浏览器中的JavaScript是没有文件操作能力的 |
文件写入:
1 | // 1.使用fs核心模块 |
http
服务器:
1 | // 1.加载http核心模块 |
Node中的模块系统
使用Node编写应用程序主要就是在使用:
-
EcmaScript语言
- 和浏览器一样,在Node中没有Bom和Dom
-
核心模块
- 文件操作的fs
- http服务操作的http
- url路径操作模块
- path路径处理模块
- os操作系统信息
-
第三方模块
- art-template
- 必须通过npm来下载才可以使用
-
自己写的模块
- 自己创建的文件
什么是模块化
- 文件作用域(模块是独立的,在不同的文件使用必须要重新引用)【在node中没有全局作用域,它是文件模块作用域】
- 通信规则
- 加载require
- 导出exports
CommonJS模块规范
在Node中的JavaScript还有一个重要的概念,模块系统。
-
模块作用域
-
使用require方法来加载模块
-
使用exports接口对象来导出模板中的成员
加载
require
语法:
1
var 自定义变量名 = require('模块')
作用:
- 执行被加载模块中的代码
- 得到被加载模块中的
exports
导出接口对象
导出
exports
-
Node中是模块作用域,默认文件中所有的成员只在当前模块有效
-
对于希望可以被其他模块访问到的成员,我们需要把这些公开的成员都挂载到
exports
接口对象中就可以了导出多个成员(必须在对象中):
1
2
3
4
5
6
7
8exports.a = 123;
exports.b = function(){
console.log('bbb')
};
exports.c = {
foo:"bar"
};
exports.d = 'hello';导出单个成员(拿到的就是函数,字符串):
1
module.exports = 'hello';
以下情况会覆盖:
1
2
3
4
5module.exports = 'hello';
//后者会覆盖前者
module.exports = function add(x,y) {
return x+y;
}也可以通过以下方法来导出多个成员:
1
2
3
4
5
6module.exports = {
foo = 'hello',
add:function(){
return x+y;
}
};
模块原理
exports和module.exports
的一个引用:
1 | console.log(exports === module.exports); //trueexports.foo = 'bar';//等价于module.exports.foo = 'bar'; |
当给exports重新赋值后,exports!= module.exports.
最终return的是module.exports,无论exports中的成员是什么都没用。
1 | 真正去使用的时候: 导出单个成员:exports.xxx = xxx 或者 modeule.exports = xxx; 导出多个成员:module.exports = {} |
总结
1 | // 引用服务var http = require('http');var fs = require('fs');// 引用模板var template = require('art-template');// 创建服务var server = http.createServer();// 公共路径var wwwDir = 'D:/app/www';server.on('request', function (req, res) { var url = req.url; // 读取文件 fs.readFile('./template-apche.html', function (err, data) { if (err) { return res.end('404 Not Found'); } fs.readdir(wwwDir, function (err, files) { if (err) { return res.end('Can not find www Dir.') } // 使用模板引擎解析替换data中的模板字符串 // 去xmpTempleteList.html中编写模板语法 var htmlStr = template.render(data.toString(), { title: 'D:/app/www/ 的索引', files:files }); // 发送响应数据 res.end(htmlStr); }) })});server.listen(3000, function () { console.log('running....');}) |
1 | 1.jQuery中的each 和 原生JavaScript方法forEach的区别: 提供源头: 原生js是es5提供的(不兼容IE8), jQuery的each是jQuery第三方库提供的(如果要使用需要用2以下的版本也就是1.版本),它的each方法主要用来遍历jQuery实例对象(伪数组),同时也可以做低版本forEach的替代品,jQuery的实例对象不能使用forEach方法,如果想要使用必须转为数组([].slice.call(jQuery实例对象))才能使用2.模块中导出多个成员和导出单个成员3.301和302的区别: 301永久重定向,浏览器会记住 302临时重定向4.exports和module.exports的区别: 每个模块中都有一个module对象 module对象中有一个exports对象 我们可以把需要导出的成员都挂载到module.exports接口对象中 也就是`module.exports.xxx = xxx`的方式 但是每次写太多了就很麻烦,所以Node为了简化代码,就在每一个模块中都提供了一个成员叫`exports` `exports === module.exports`结果为true,所以完全可以`exports.xxx = xxx` 当一个模块需要导出单个成员的时候必须使用`module.exports = xxx`的方式,=,使用`exports = xxx`不管用,因为每个模块最终return的是module.exports,而exports只是module.exports的一个引用,所以`exports`即使重新赋值,也不会影响`module.exports`。 有一种赋值方式比较特殊:`exports = module.exports`这个用来新建立引用关系的。 |
require的加载规则
-
核心模块
- 模块名
-
第三方模块
- 模块名
-
用户自己写的
- 路径
require的加载规则:
-
优先从缓存加载
-
判断模块标识符
- 核心模块
- 自己写的模块(路径形式的模块)
- 第三方模块(node_modules)
- 第三方模块的标识就是第三方模块的名称(不可能有第三方模块和核心模块的名字一致)
- npm
- 开发人员可以把写好的框架库发布到npm上
- 使用者通过npm命令来下载
- 使用方式:
var 名称 = require('npm install【下载包】 的包名')
- node_modules/express/package.json main
- 如果package.json或者main不成立,则查找被选择项:index.js
- 如果以上条件都不满足,则继续进入上一级目录中的node_modules按照上面的规则依次查找,直到当前文件所属此盘根目录都找不到最后报错
1 | // 如果非路径形式的标识// 路径形式的标识: // ./ 当前目录 不可省略 // ../ 上一级目录 不可省略 // /xxx也就是D:/xxx // 带有绝对路径几乎不用(D:/a/foo.js)// 首位表示的是当前文件模块所属磁盘根目录// require('./a'); // 核心模块// 核心模块本质也是文件,核心模块文件已经被编译到了二进制文件中了,我们只需要按照名字来加载就可以了require('fs'); // 第三方模块// 凡是第三方模块都必须通过npm下载(npm i node_modules),使用的时候就可以通过require('包名')来加载才可以使用// 第三方包的名字不可能和核心模块的名字是一样的// 既不是核心模块,也不是路径形式的模块// 先找到当前文所述目录的node_modules// 然后找node_modules/art-template目录// node_modules/art-template/package.json// node_modules/art-template/package.json中的main属性// main属性记录了art-template的入口模块// 然后加载使用这个第三方包// 实际上最终加载的还是文件// 如果package.json不存在或者mian指定的入口模块不存在// 则node会自动找该目录下的index.js// 也就是说index.js是一个备选项,如果main没有指定,则加载index.js文件// // 如果条件都不满足则会进入上一级目录进行查找// 注意:一个项目只有一个node_modules,放在项目根目录中,子目录可以直接调用根目录的文件var template = require('art-template'); |
模块标识符中的/
和文件操作路径中的/
文件操作路径:
1 | // 咱们所使用的所有文件操作的API都是异步的// 就像ajax请求一样// 读取文件// 文件操作中 ./ 相当于当前模块所处磁盘根目录// ./index.txt 相对于当前目录// /index.txt 相对于当前目录// /index.txt 绝对路径,当前文件模块所处根目录// d:express/index.txt 绝对路径fs.readFile('./index.txt',function(err,data){ if(err){ return console.log('读取失败'); } console.log(data.toString());}) |
模块操作路径:
1 | // 在模块加载中,相对路径中的./不能省略// 这里省略了.也是磁盘根目录require('./index')('hello') |
npm
- node package manage(node包管理器)
- 通过npm命令安装jQuery包(npm install --save jquery),在安装时加上–save会主动生成说明书文件信息(将安装文件的信息添加到package.json里面)
npm网站
npmjs.com 网站 是用来搜索npm包的
npm命令行工具
npm是一个命令行工具,只要安装了node就已经安装了npm。
npm也有版本概念,可以通过npm --version
来查看npm的版本
升级npm(自己升级自己):
1 | npm install --global npm |
常用命令
- npm init(生成package.json说明书文件)
- npm init -y(可以跳过向导,快速生成)
- npm install
- 一次性把dependencies选项中的依赖项全部安装
- 简写(npm i)
- npm install 包名
- 只下载
- 简写(npm i 包名)
- npm install --save 包名
- 下载并且保存依赖项(package.json文件中的dependencies选项)
- 简写(npm i 包名)
- npm uninstall 包名
- 只删除,如果有依赖项会依然保存
- 简写(npm un 包名)
- npm uninstall --save 包名
- 删除的同时也会把依赖信息全部删除
- 简写(npm un 包名)
- npm help
- 查看使用帮助
- npm 命令 --help
- 查看具体命令的使用帮助(npm uninstall --help)
解决npm被墙问题
npm存储包文件的服务器在国外,有时候会被墙,速度很慢,所以需要解决这个问题。
https://developer.aliyun.com/mirror/NPM?from=tnpm淘宝的开发团队把npm在国内做了一个镜像(也就是一个备份)。
安装淘宝的cnpm:
1 | npm install -g cnpm --registry=https://registry.npm.taobao.org; |
1 | 在任意目录执行都可以#--global表示安装到全局,而非当前目录#--global不能省略,否则不管用npm install --global cnpm |
安装包的时候把以前的npm
替换成cnpm
。
1 | 走国外的npm服务器下载jQuery包,速度比较慢npm install jQuery;#使用cnpm就会通过淘宝的服务器来下载jQuerycnpm install jQuery; |
如果不想安装cnpm
又想使用淘宝的服务器来下载:
1 | npm install jquery --registry=https://npm.taobao.org; |
但是每次手动加参数就很麻烦,所以我们可以把这个选项加入到配置文件中:
1 | npm config set registry https://npm.taobao.org;#查看npm配置信息npm config list; |
只要经过上面的配置命令,则以后所有的npm install
都会通过淘宝的服务器来下载
package.json
每一个项目都要有一个package.json
文件(包描述文件,就像产品的说明书一样)
这个文件可以通过npm init
自动初始化出来
1 | D:\code\node中的模块系统>npm initThis utility will walk you through creating a package.json file.It only covers the most common items, and tries to guess sensible defaults.See `npm help json` for definitive documentation on these fieldsand exactly what they do.Use `npm install <pkg>` afterwards to install a package andsave it as a dependency in the package.json file.Press ^C at any time to quit.package name: (node中的模块系统)Sorry, name can only contain URL-friendly characters.package name: (node中的模块系统) clsversion: (1.0.0)description: 这是一个测试项目entry point: (main.js)test command:git repository:keywords:author: xiaochenlicense: (ISC)About to write to D:\code\node中的模块系统\package.json:{ "name": "cls", "version": "1.0.0", "description": "这是一个测试项目", "main": "main.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "xiaochen", "license": "ISC"}Is this OK? (yes) yes |
对于目前来讲,最有用的是dependencies
选项,可以用来帮助我们保存第三方包的依赖信息。
如果node_modules
删除了也不用担心,只需要在控制面板中npm install
就会自动把package.json
中的dependencies
中所有的依赖项全部都下载回来。
- 建议每个项目的根目录下都有一个
package.json
文件 - 建议执行
npm install 包名
的时候都加上--save
选项,目的是用来保存依赖信息
package.json和package-lock.json
npm 5以前是不会有package-lock.json
这个文件
npm5以后才加入这个文件
当你安装包的时候,npm都会生成或者更新package-lock.json
这个文件
- npm5以后的版本安装都不要加
--save
参数,它会自动保存依赖信息 - 当你安装包的时候,会自动创建或者更新
package-lock.json
文件 package-lock.json
这个文件会包含node_modules
中所有包的信息(版本,下载地址。。。)- 这样的话重新
npm install
的时候速度就可以提升
- 这样的话重新
- 从文件来看,有一个
lock
称之为锁- 这个
lock
使用来锁版本的 - 如果项目依赖了
1.1.1
版本 - 如果你重新install其实会下载最细版本,而不是
1.1.1
package-lock.json
的另外一个作用就是锁定版本号,防止自动升级
- 这个
path路径操作模块
- path.basename:获取路径的文件名,默认包含扩展名
- path.dirname:获取路径中的目录部分
- path.extname:获取一个路径中的扩展名部分
- path.parse:把路径转换为对象
- root:根路径
- dir:目录
- base:包含后缀名的文件名
- ext:后缀名
- name:不包含后缀名的文件名
- path.join:拼接路径
- path.isAbsolute:判断一个路径是否为绝对路径
Node中的其它成员(__dirname,__filename)
在每个模块中,除了require
,exports
等模块相关的API之外,还有两个特殊的成员:
-
__dirname
,是一个成员,可以用来动态获取当前文件模块所属目录的绝对路径 -
__filename
,可以用来动态获取当前文件的绝对路径(包含文件名) -
__dirname
和filename
是不受执行node命令所属路径影响的
==在文件操作中,使用相对路径是不可靠的,因为node中文件操作的路径被设计为相对于执行node命令所处的路径。==(node xxx.js)
所以为了解决这个问题,只需要把相对路径变为==绝对路径==(绝对路径不受任何影响)就可以了。
就可以使用==__dirname
==或者==__filename
==来帮助我们解决这个问题
在拼接路径的过程中,为了避免手动拼接带来的一些低级错误,推荐使用path.join()
来辅助拼接
1 | var fs = require('fs');var path = require('path');// console.log(__dirname + 'a.txt');// path.join方法会将文件操作中的相对路径都统一的转为动态的绝对路径fs.readFile(path.join(__dirname + '/a.txt'),'utf8',function(err,data){ if(err){ throw err } console.log(data);}); |
补充:模块中的路径标识和这里的路径没关系,不受影响(就是相对于文件模块)
注意:
模块中的路径标识和文件操作中的相对路径标识不一致
模块中的路径标识就是相对于当前文件模块,不受node命令所处路径影响
Express(快速的)
作者:Tj
原生的http在某些方面表现不足以应对我们的开发需求,所以就需要使用框架来加快我们的开发效率,框架的目的就是提高效率,让我们的代码高度统一。
在node中有很多web开发框架。主要学习express
-
http://expressjs.com/
,其中主要封装的是http。 -
// 1 安装// 2 引包var express = require('express');// 3 创建服务器应用程序// 也就是原来的http.createServer();var app = express();// 公开指定目录// 只要通过这样做了,就可以通过/public/xx的方式来访问public目录中的所有资源// 在Express中开放资源就是一个API的事app.use('/public/',express.static('/public/'));//模板引擎在Express中开放模板也是一个API的事// 当服务器收到get请求 / 的时候,执行回调处理函数app.get('/',function(req,res){ res.send('hello express');})// 相当于server.listenapp.listen(3000,function(){ console.log('app is runing at port 3000');})
1
2
3
4
5
6
7
8
9
10
### 学习Express
#### 起步
##### 安装:
```javascript
cnpm install express
hello world:
1 | // 引入expressvar express = require('express');// 1. 创建appvar app = express();// 2. app.get('/',function(req,res){ // 1 // res.write('Hello'); // res.write('World'); // res.end() // 2 // res.end('hello world'); // 3 res.send('hello world');})app.listen(3000,function(){ console.log('express app is runing...');}) |
基本路由
路由:
-
请求方法
-
请求路径
-
请求处理函数
get:
1 | //当你以get方法请求/的时候,执行对应的处理函数app.get('/',function(req,res){ res.send('hello world');}) |
post:
1 | //当你以post方法请求/的时候,执行对应的处理函数app.post('/',function(req,res){ res.send('hello world');}) |
Express静态服务API
1 | // app.use不仅仅是用来处理静态资源的,还可以做很多工作(body-parser的配置)app.use(express.static('public'));app.use(express.static('files'));app.use('/stataic',express.static('public')); |
1 | // 引入expressvar express = require('express');// 创建appvar app = express();// 开放静态资源// 1.当以/public/开头的时候,去./public/目录中找对应资源// 访问:http://127.0.0.1:3000/public/login.htmlapp.use('/public/',express.static('./public/')); // 2.当省略第一个参数的时候,可以通过省略/public的方式来访问// 访问:http://127.0.0.1:3000/login.html// app.use(express.static('./public/')); // 3.访问:http://127.0.0.1:3000/a/login.html// a相当于public的别名// app.use('/a/',express.static('./public/')); // app.get('/',function(req,res){ res.end('hello world');});app.listen(3000,function(){ console.log('express app is runing...');}); |
在Express中配置使用art-templete
模板引擎
- art-template官方文档
- 在node中,有很多第三方模板引擎都可以使用,不是只有
art-template
- 还有ejs,jade(pug),handlebars,nunjucks
安装:
1 | npm install --save art-templatenpm install --save express-art-template//两个一起安装npm i --save art-template express-art-template |
配置:
1 | app.engine('html', require('express-art-template')); |
使用:
1 | app.get('/',function(req,res){ // express默认会去views目录找index.html res.render('index.html',{ title:'hello world' });}) |
如果希望修改默认的views
视图渲染存储目录,可以:
1 | // 第一个参数views千万不要写错app.set('views',目录路径); |
在Express中获取表单请求数据
获取get请求数据:
Express内置了一个api,可以直接通过req.query
来获取数据
1 | // 通过requery方法获取用户输入的数据// req.query只能拿到get请求的数据 var comment = req.query; |
获取post请求数据:
在Express中没有内置获取表单post请求体的api,这里我们需要使用一个第三方包body-parser
来获取数据。
安装:
1 | npm install --save body-parser; |
配置:
// 配置解析表单 POST 请求体插件(注意:一定要在 app.use(router) 之前 )
1 | var express = require('express')// 引包var bodyParser = require('body-parser')var app = express()// 配置body-parser// 只要加入这个配置,则在req请求对象上会多出来一个属性:body// 也就是说可以直接通过req.body来获取表单post请求数据// parse application/x-www-form-urlencodedapp.use(bodyParser.urlencoded({ extended: false }))// parse application/jsonapp.use(bodyParser.json()) |
使用:
1 | app.use(function (req, res) { res.setHeader('Content-Type', 'text/plain') res.write('you posted:\n') // 可以通过req.body来获取表单请求数据 res.end(JSON.stringify(req.body, null, 2))}) |
在Express中配置使用express-session
插件操作
安装:
1 | npm install express-session |
配置:
1 | //该插件会为req请求对象添加一个成员:req.session默认是一个对象//这是最简单的配置方式//Session是基于Cookie实现的app.use(session({ //配置加密字符串,他会在原有的基础上和字符串拼接起来去加密 //目的是为了增加安全性,防止客户端恶意伪造 secret: 'keyboard cat', resave: false, saveUninitialized: true,//无论是否适用Session,都默认直接分配一把钥匙 cookie: { secure: true }})) |
使用:
1 | // 读//添加Session数据//session就是一个对象req.session.foo = 'bar';//写//获取session数据req.session.foo//删req.session.foo = null;delete req.session.foo |
提示:
默认Session数据时内存储数据,服务器一旦重启,真正的生产环境会把Session进行持久化存储。
利用Express实现ADUS项目
模块化思想
模块如何划分:
- 模块职责要单一
javascript模块化:
- Node 中的 CommonJS
- 浏览器中的:
- AMD require.js
- CMD sea.js
- es6中增加了官方支持
起步
- 初始化
- 模板处理
路由设计
请求方法 | 请求路径 | get参数 | post参数 | 备注 |
---|---|---|---|---|
GET | /students | 渲染首页 | ||
GET | /students/new | 渲染添加学生页面 | ||
POST | /students/new | name,age,gender,hobbies | 处理添加学生请求 | |
GET | /students/edit | id | 渲染编辑页面 | |
POST | /students/edit | id,name,age,gender,hobbies | 处理编辑请求 | |
GET | /students/delete | id | 处理删除请求 |
提取路由模块
router.js:
1 | /** * router.js路由模块 * 职责: * 处理路由 * 根据不同的请求方法+请求路径设置具体的请求函数 * 模块职责要单一,我们划分模块的目的就是增强代码的可维护性,提升开发效率 */var fs = require('fs');// Express专门提供了一种更好的方式// 专门用来提供路由的var express = require('express');// 1 创建一个路由容器var router = express.Router();// 2 把路由都挂载到路由容器中router.get('/students', function(req, res) { // res.send('hello world'); // readFile的第二个参数是可选的,传入utf8就是告诉他把读取到的文件直接按照utf8编码,直接转成我们认识的字符 // 除了这样来转换,也可以通过data.toString()来转换 fs.readFile('./db.json', 'utf8', function(err, data) { if (err) { return res.status(500).send('Server error.') } // 读取到的文件数据是string类型的数据 // console.log(data); // 从文件中读取到的数据一定是字符串,所以一定要手动转换成对象 var students = JSON.parse(data).students; res.render('index.html', { // 读取文件数据 students:students }) })});router.get('/students/new',function(req,res){ res.render('new.html')});router.get('/students/edit',function(req,res){ });router.post('/students/edit',function(req,res){ });router.get('/students/delete',function(req,res){ });// 3 把router导出module.exports = router; |
app.js:
1 | var router = require('./router');// router(app);// 把路由容器挂载到app服务中// 挂载路由app.use(router); |
设计操作数据的API文件模块
es6中的find和findIndex:
find接受一个方法作为参数,方法内部返回一个条件
find会便利所有的元素,执行你给定的带有条件返回值的函数
符合该条件的元素会作为find方法的返回值
如果遍历结束还没有符合该条件的元素,则返回undefined
1 | /** * student.js * 数据操作文件模块 * 职责:操作文件中的数据,只处理数据,不关心业务 */var fs = require('fs'); /** * 获取所有学生列表 * return [] */exports.find = function(){ } /** * 获取添加保存学生 */exports.save = function(){ }/** * 更新学生 */exports.update = function(){ } /** * 删除学生 */exports.delete = function(){ } |
步骤
-
处理模板
-
配置静态开放资源
-
配置模板引擎
-
简单的路由,/studens渲染静态页出来
-
路由设计
-
提取路由模块
-
由于接下来的一系列业务操作都需要处理文件数据,所以我们需要封装Student.js’
-
先写好student.js文件结构
- 查询所有学生列别哦的API
- findById
- save
- updateById
- deleteById
-
实现具体功能
- 通过路由收到请求
- 接受请求中的参数(get,post)
- req.query
- req.body
- 调用数据操作API处理数据
- 根据操作结果给客户端发送请求
-
业务功能顺序
- 列表
- 添加
- 编辑
- 删除
子模板和模板的继承(模板引擎高级语法)【include,extend,block】
注意:
模板页:
1 | <!DOCTYPE html><html lang="zh"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>模板页</title> <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css"/> {{ block 'head' }}{{ /block }}</head><body> <!-- 通过include导入公共部分 --> {{include './header.html'}} <!-- 留一个位置 让别的内容去填充 --> {{ block 'content' }} <h1>默认内容</h1> {{ /block }} <!-- 通过include导入公共部分 --> {{include './footer.html'}} <!-- 公共样式 --> <script src="/node_modules/jquery/dist/jquery.js" ></script> <script src="/node_modules/bootstrap/dist/js/bootstrap.js" ></script> {{ block 'script' }}{{ /block }}</body></html> |
模板的继承:
header页面:
1 | <div id=""> <h1>公共的头部</h1></div> |
footer页面:
1 | <div id=""> <h1>公共的底部</h1></div> |
模板页的使用:
1 | <!-- 继承(extend:延伸,扩展)模板也layout.html --><!-- 把layout.html页面的内容都拿进来作为index.html页面的内容 -->{{extend './layout.html'}}<!-- 向模板页面填充新的数据 --><!-- 填充后就会替换掉layout页面content中的数据 --><!-- style样式方面的内容 -->{{ block 'head' }} <style type="text/css"> body{ background-color: skyblue; } </style>{{ /block }}{{ block 'content' }} <div id=""> <h1>Index页面的内容</h1> </div>{{ /block }}<!-- js部分的内容 -->{{ block 'script' }} <script type="text/javascript"> </script>{{ /block }} |
最终的显示效果:
MongoDB
关系型和非关系型数据库
关系型数据库(表就是关系,或者说表与表之间存在关系)。
- 所有的关系型数据库都需要通过
sql
语言来操作 - 所有的关系型数据库在操作之前都需要设计表结构
- 而且数据表还支持约束
- 唯一的
- 主键
- 默认值
- 非空
非关系型数据库
- 非关系型数据库非常的灵活
- 有的关系型数据库就是key-value对儿
- 但MongDB是长得最像关系型数据库的非关系型数据库
- 数据库 -》 数据库
- 数据表 -》 集合(数组)
- 表记录 -》文档对象
一个数据库中可以有多个数据库,一个数据库中可以有多个集合(数组),一个集合中可以有多个文档(表记录)
1 | { qq:{ user:[ {},{},{}... ] }} |
- 也就是说你可以任意的往里面存数据,没有结构性这么一说
安装
-
下载
-
安装
1
npm i mongoose
-
配置环境变量
-
最后输入
mongod --version
测试是否安装成功
启动和关闭数据库
启动:
1 | mongodb 默认使用执行mongod 命令所处盘符根目录下的/data/db作为自己的数据存储目录# 所以在第一次执行该命令之前先自己手动新建一个 /data/dbmongod# 开启服务net start mongodb |
如果想要修改默认的数据存储目录,可以:
1 | mongod --dbpath = 数据存储目录路径 |
停止:
1 | 在开启服务的控制台,直接Ctrl+C;或者直接关闭开启服务的控制台。 |
连接数据库
连接:
1 | # 该命令默认连接本机的 MongoDB 服务mongo |
退出:
1 | # 在连接状态输入 exit 退出连接exit |
基本命令
show dbs
- 查看数据库列表(数据库中的所有数据库)
db
- 查看当前连接的数据库
use 数据库名称
- 切换到指定的数据库,(如果没有会新建)
show collections
- 查看当前目录下的所有数据表
db.表名.find()
- 查看表中的详细信息
在Node中如何操作MongoDB数据库
使用官方的MongoDB
包来操作
使用第三方包mongoose
来操作MongoDB数据库
第三方包:mongoose
基于MongoDB官方的mongodb
包再一次做了封装,名字叫mongoose
,是WordPress项目团队开发的。
学习指南(步骤)
官方学习文档:https://mongoosejs.com/docs/index.html
设计Scheme 发布Model (创建表)
1 | // 1.引包// 注意:按照后才能require使用var mongoose = require('mongoose');// 拿到schema图表var Schema = mongoose.Schema;// 2.连接数据库// 指定连接数据库后不需要存在,当你插入第一条数据库后会自动创建数据库mongoose.connect('mongodb://localhost/test');// 3.设计集合结构(表结构)// 用户表var userSchema = new Schema({ username: { //姓名 type: String, require: true //添加约束,保证数据的完整性,让数据按规矩统一 }, password: { type: String, require: true }, email: { type: String }});// 4.将文档结构发布为模型// mongoose.model方法就是用来将一个架构发布为 model// 第一个参数:传入一个大写名词单数字符串用来表示你的数据库的名称// mongoose 会自动将大写名词的字符串生成 小写复数 的集合名称// 例如 这里会变成users集合名称// 第二个参数:架构// 返回值:模型构造函数var User = mongoose.model('User', userSchema); |
添加数据(增)
1 | // 5.通过模型构造函数对User中的数据进行操作var user = new User({ username: 'admin', password: '123456', email: 'xiaochen@qq.com'});user.save(function(err, ret) { if (err) { console.log('保存失败'); } else { console.log('保存成功'); console.log(ret); }}); |
删除(删)
根据条件删除所有:
1 | User.remove({ username: 'xiaoxiao'}, function(err, ret) { if (err) { console.log('删除失败'); } else { console.log('删除成功'); console.log(ret); }}); |
根据条件删除一个:
1 | Model.findOneAndRemove(conditions,[options],[callback]); |
根据id删除一个:
1 | User.findByIdAndRemove(id,[options],[callback]); |
更新(改)
更新所有:
1 | User.remove(conditions,doc,[options],[callback]); |
根据指定条件更新一个:
1 | User.FindOneAndUpdate([conditions],[update],[options],[callback]); |
根据id更新一个:
1 | // 更新 根据id来修改表数据User.findByIdAndUpdate('5e6c5264fada77438c45dfcd', { username: 'junjun'}, function(err, ret) { if (err) { console.log('更新失败'); } else { console.log('更新成功'); }}); |
查询(查)
查询所有:
1 | // 查询所有User.find(function(err,ret){ if(err){ console.log('查询失败'); }else{ console.log(ret); }}); |
条件查询所有:
1 | // 根据条件查询User.find({ username:'xiaoxiao' },function(err,ret){ if(err){ console.log('查询失败'); }else{ console.log(ret); }}); |
条件查询单个:
1 | // 按照条件查询单个,查询出来的数据是一个对象({})// 没有条件查询使用findOne方法,查询的是表中的第一条数据User.findOne({ username: 'xiaoxiao'}, function(err, ret) { if (err) { console.log('查询失败'); } else { console.log(ret); }}); |
使用Node操作MySQL数据库
文档:https://www.npmjs.com/package/mysql
安装:
1 | npm install --save mysql |
1 | // 引入mysql包var mysql = require('mysql');// 创建连接var connection = mysql.createConnection({ host : 'localhost', //本机 user : 'me', //账号root password : 'secret', //密码12345 database : 'my_db' //数据库名}); // 连接数据库 (打开冰箱门)connection.connect(); //执行数据操作 (把大象放到冰箱)connection.query('SELECT * FROM `users` ', function (error, results, fields) { if (error) throw error;//抛出异常阻止代码往下执行 // 没有异常打印输出结果 console.log('The solution is: ',results);});//关闭连接 (关闭冰箱门)connection.end(); |
异步编程
回调函数
不成立的情况下:
1 | function add(x,y){ console.log(1); setTimeout(function(){ console.log(2); var ret = x + y; return ret; },1000); console.log(3); //到这里执行就结束了,不会i等到前面的定时器,所以直接返回了默认值 undefined}console.log(add(2,2));// 结果是 1 3 undefined 4 |
使用回调函数解决:
回调函数:通过一个函数,获取函数内部的操作。(根据输入得到输出结果)
1 | var ret;function add(x,y,callback){ // callback就是回调函数 // var x = 10; // var y = 20; // var callback = function(ret){console.log(ret);} console.log(1); setTimeout(function(){ var ret = x + y; callback(ret); },1000); console.log(3);}add(10,20,function(ret){ console.log(ret);}); |
注意:
凡是需要得到一个函数内部异步操作的结果(setTimeout,readFile,writeFile,ajax,readdir)
这种情况必须通过 回调函数 (异步API都会伴随着一个回调函数)
ajax:
基于原生XMLHttpRequest封装get方法:
1 | var oReq = new XMLHttpRequest();// 当请求加载成功要调用指定的函数oReq.onload = function(){ console.log(oReq.responseText);}oReq.open("GET", "请求路径",true);oReq.send(); |
1 | function get(url,callback){ var oReq = new XMLHttpRequest(); // 当请求加载成功要调用指定的函数 oReq.onload = function(){ //console.log(oReq.responseText); callback(oReq.responseText); } oReq.open("GET", url,true); oReq.send();}get('data.json',function(data){ console.log(data);}); |
Promise
callback hell(回调地狱):
文件的读取无法判断执行顺序(文件的执行顺序是依据文件的大小来决定的)(异步api无法保证文件的执行顺序)
1 | var fs = require('fs');fs.readFile('./data/a.text','utf8',function(err,data){ if(err){ // 1 读取失败直接打印输出读取失败 return console.log('读取失败'); // 2 抛出异常 // 阻止程序的执行 // 把错误信息打印到控制台 throw err; } console.log(data);});fs.readFile('./data/b.text','utf8',function(err,data){ if(err){ // 1 读取失败直接打印输出读取失败 return console.log('读取失败'); // 2 抛出异常 // 阻止程序的执行 // 把错误信息打印到控制台 throw err; } console.log(data);}); |
通过回调嵌套的方式来保证顺序:
1 | var fs = require('fs');fs.readFile('./data/a.text','utf8',function(err,data){ if(err){ // 1 读取失败直接打印输出读取失败 return console.log('读取失败'); // 2 抛出异常 // 阻止程序的执行 // 把错误信息打印到控制台 throw err; } console.log(data); fs.readFile('./data/b.text','utf8',function(err,data){ if(err){ // 1 读取失败直接打印输出读取失败 return console.log('读取失败'); // 2 抛出异常 // 阻止程序的执行 // 把错误信息打印到控制台 throw err; } console.log(data); fs.readFile('./data/a.text','utf8',function(err,data){ if(err){ // 1 读取失败直接打印输出读取失败 return console.log('读取失败'); // 2 抛出异常 // 阻止程序的执行 // 把错误信息打印到控制台 throw err; } console.log(data); }); });}); |
为了解决以上编码方式带来的问题(回调地狱嵌套),所以在EcmaScript6新增了一个API:
-
Promise:承诺,保证 (深度变长度)
-
Promise本身不是异步的,但往往都是内部封装一个异步任务
-
Promise 容器:存放了一个异步任务,有两种状态(Pending):
-
Resolved:解决
-
Rejected:失败
-
两种状态只能变成其中一种
-
容器一旦创建,就开始执行里面的代码
-
基本语法:
1 | // 在EcmaScript 6中新增了一个API Promise// Promise 是一个构造函数var fs = require('fs');// 1 创建Promise容器 resolve:解决 reject:失败var p1 = new Promise(function(resolve, reject) { fs.readFile('./a.text', 'utf8', function(err, data) { if (err) { // console.log(err); // 把容器的Pending状态变为rejected reject(err); } else { // console.log(data); // 把容器的Pending状态变为resolve resolve(1234); } });});// 当p1成功了,然后就(then)做指定的操作// then方法接收的function就是容器中的resolve函数// 当你 return 123 后面就接收到 123,没有 return 后面收到的就是 undefined// 真正有用的是:我们可以 return 一个 Promise 对象p1 .then(function(data) { console.log(data); }, function(err) { console.log('读取文件失败了', err); }); |
链式循环:png
封装Promise的readFile
:
1 | var fs = require('fs');function pReadFile(filePath) { return new Promise(function(resolve, reject) { fs.readFile(filePath, 'utf8', function(err, data) { if (err) { reject(err); } else { resolve(data); } }); });}pReadFile('./a.txt') .then(function(data) { console.log(data); return pReadFile('./b.txt'); }) .then(function(data) { console.log(data); return pReadFile('./a.txt'); }) .then(function(data) { console.log(data); }) |
mongoose所有的API都支持Promise:
1 | // 查询所有User.find() .then(function(data){ console.log(data) }) |
注册:
1 | User.findOne({username:'admin'},function(user){ if(user){ console.log('用户已存在') } else { new User({ username:'aaa', password:'123', email:'fffff' }).save(function(){ console.log('注册成功'); }) }}) |
1 | User.findOne({ username:'admin'}) .then(function(user){ if(user){ // 用户已经存在不能注册 console.log('用户已存在'); } else{ // 用户不存在可以注册 return new User({ username:'aaa', password:'123', email:'fffff' }).save(); } }) .then(funciton(ret){ console.log('注册成功'); }) |
Generator
async函数
其他
修改完代码自动重启
我们在这里可以使用一个第三方命名行工具:nodemon
来帮助我们解决频繁修改代码重启服务器的问题。
nodemon
是一个基于Node.js开发的一个第三方命令行工具,我们使用的时候需要独立安装:
1 | #在任意目录执行该命令都可以#也就是说,所有需要 --global安装的包都可以在任意目录执行npm install --global nodemonnpm install -g nodemon#如果安装不成功的话,可以使用cnpm安装cnpm install -g nodemon |
安装完毕之后使用:
1 | node app.js#使用nodemonnodemon app.js |
只要是通过nodemon
启动的服务,则他会监视你的文件变化,当文件发生变化的时候,会自动帮你重启服务器。
封装异步API
回调函数:获取异步操作的结果
1 | function fn(callback){ // var callback = funtion(data){ console.log(data); } setTimeout(function(){ var data = 'hello'; callback(data); },1000);}// 如果需要获取一个函数中异步操作的结果,则必须通过回调函数的方式来获取fn(function(data){ console.log(data);}) |
数组的遍历方法,都是对函数作为一种参数
EcmaScript 6
项目案例
目录结构
1 | .├- app.js 项目入口├- controllers/├- models/ 存储数据模型(mongoose) ﹂ comment.js 评论数据模型 ﹂ topic.js 话题数据模型 ﹂ user.js 用户数据模型├- node_modules/ 第三方包├- package.json 包描述文件├- package-lock.json 第三方包版本锁定文件├- public/ 公共静态资源 ﹂ css/ 样式资源 · login.css · main.css · markdown-github.css · setting.css ﹂ img/ 媒体资源 ﹂ js/ 脚本├- routes/ 路由文件 ﹂ session.js ﹂ topic.js┖- views/ 存储视图目录 ﹂ _layouts/ · home.html 主页面 ﹂ _partials/ 公共模板 · header.html 公共头部模板 · footer.html 公共页脚模板 · settings-nav.html 公共设置导航条模板 ﹂ settings/ · admin.html · profile.html ﹂ topic/ 操作页面 · edit.html 编辑 · new.html 添加 · show.html 展示 ﹂ index.html 内容主体 ﹂ login.html 登陆页面 ﹂ register.html 注册页面 |
模板页
- 子模板
- 模板继承
路由设计
路由 | 方法 | get参数 | post参数 | 是否需要登录 | 备注 |
---|---|---|---|---|---|
/ | get | 否 | 渲染首页 | ||
/register(登录) | get | 否 | 渲染注册页面 | ||
/register | post | email,nickname,password | 否 | 处理注册请求 | |
/login | get | 是 | 渲染登陆界面 | ||
/login | post | email,password | 处理登录请求 | ||
/loginout | get | 处理退出请求 | |||
模型设计
功能实现
步骤
- 创建目录结构
- 整合静态页-模板页
- include
- block
- extend
- 设计用户登陆,退出,注册的路由
- 用户注册
- 先处理客户端页面的内容(表单控件的name,收集表单数据,发起请求)
- 服务端
- 获取从客户端收到的数据
- 操作数据库
- 如果有错,发送500告诉客户端服务器错了‘
- 其他的根据业务发送不同的响应数据
- 登录
- 退出
Express中间件
中间件的概念
中间件:把很复杂的事情分割成单个,然后依次有条理的执行。就是一个中间处理环节,有输入,有输出。
说的通俗易懂点儿,中间件就是一个(从请求到响应调用的方法)方法。
把数据从请求到响应分步骤来处理,每一个步骤都是一个中间处理环节。
1 | var http = require('http');var url = require('url');var cookie = require('./expressPtoject/cookie');var query = require('./expressPtoject/query');var postBody = require('./expressPtoject/post-body');var server = http.createServer(function(){ // 解析请求地址中的get参数 // var obj = url.parse(req.url,true); // req.query = obj.query; query(req,res); //中间件 // 解析请求地址中的post参数 req.body = { foo:'bar' }});if(req.url === 'xxx'){ // 处理请求 ...}server.listen(3000,function(){ console.log('3000 runing...');}); |
同一个请求对象所经过的中间件都是同一个请求对象和响应对象。
1 | var express = require('express');var app = express();app.get('/abc',function(req,res,next){ // 同一个请求的req和res是一样的, // 可以前面存储下面调用 console.log('/abc'); // req.foo = 'bar'; req.body = { name:'xiaoxiao', age:18 } next();});app.get('/abc',function(req,res,next){ // console.log(req.foo); console.log(req.body); console.log('/abc');});app.listen(3000, function() { console.log('app is running at port 3000.');}); |
中间件的分类:
1.1 应用程序级别的中间件
万能匹配(不关心任何请求路径和请求方法的中间件):
1 | app.use(function(req,res,next){ console.log('Time',Date.now()); next();}); |
关心请求路径和请求方法的中间件:
1 | app.use('/a',function(req,res,next){ console.log('Time',Date.now()); next();}); |
路由级别的中间件
严格匹配请求路径和请求方法的中间件
get:
1 | app.get('/',function(req,res){ res.send('get');}); |
post:
1 | app.post('/a',function(req,res){ res.send('post');}); |
put:
1 | app.put('/user',function(req,res){ res.send('put');}); |
delete:
1 | app.delete('/delete',function(req,res){ res.send('delete');}); |
总
1 | var express = require('express');var app = express();// 中间件:处理请求,本质就是个函数// 在express中,对中间件有几种分类// 1 不关心任何请求路径和请求方法的中间件// 也就是说任何请求都会进入这个中间件// 中间件本身是一个方法,该方法接收三个参数// Request 请求对象// Response 响应对象// next 下一个中间件// // 全局匹配中间件// app.use(function(req, res, next) {// console.log('1');// // 当一个请求进入中间件后// // 如果需要请求另外一个方法则需要使用next()方法// next();// // next是一个方法,用来调用下一个中间件// // 注意:next()方法调用下一个方法的时候,也会匹配(不是调用紧挨着的哪一个)// });// app.use(function(req, res, next) {// console.log('2');// });// // 2 关心请求路径的中间件// // 以/xxx开头的中间件// app.use('/a',function(req, res, next) {// console.log(req.url);// });// 3 严格匹配请求方法和请求路径的中间件app.get('/',function(){ console.log('/');});app.post('/a',function(){ console.log('/a');});app.listen(3000, function() { console.log('app is running at port 3000.');}); |
错误处理中间件
1 | app.use(function(err,req,res,next){ console.error(err,stack); res.status(500).send('Something broke');}); |
配置使用404中间件:
1 | // 放在最后,如果前面没有任何中间件能匹配处理,则进入这个404app.use(function(req,res){ res.render('404.html');}); |
配置全局错误处理中间件:
1 | app.get('/a', function(req, res, next) { fs.readFile('.a/bc', funtion() { if (err) { // 当调用next()传参后,则直接进入到全局错误处理中间件方法中(4个参数) // 当发生全局错误的时候,我们可以调用next传递错误对象 // 然后被全局错误处理中间件匹配到并进行处理 // 就不需要每处理一次业务都要判断err next(err); } })});//全局错误处理中间件app.use(function(err,req,res,next){ res.status(500).json({ err_code:500, message:err.message });}); |
内置中间件
- express.static(提供静态文件)
第三方中间件
- body-parser
- compression
- cookie-parser
- mogran
- response-time
- server-static
- session
项目2.0
目录结构
1 | .├- app.js 项目入口├- controllers/├- models/ 存储数据模型(mongoose) ﹂ comment.js 评论数据模型 ﹂ topic.js 话题数据模型 ﹂ user.js 用户数据模型├- node_modules/ 第三方包├- package.json 包描述文件├- package-lock.json 第三方包版本锁定文件├- public/ 公共静态资源 ﹂ css/ 样式资源 · login.css · main.css · markdown-github.css · setting.css ﹂ img/ 媒体资源 ﹂ js/ 脚本资源├- routes/ 路由文件 ﹂ session.js ﹂ topic.js┖- views/ 存储视图目录 ﹂ _layouts/ · home.html 主页面 ﹂ _partials/ 公共模板 · header.html 公共头部模板 · footer.html 公共页脚模板 · settings-nav.html 共设置导航条模板 ﹂ settings/ · admin.html · profile.html ﹂ topic/ 操作页面 · edit.html 编辑 · new.html 添加 · show.html 展示 ﹂ index.html 内容主体 ﹂ login.html 登陆页面 ﹂ register.html 注册页面 |
模板页
- 子模板
- 模板继承
路由设计
路由 | 方法 | get参数 | post参数 | 是否需要登录 | 备注 |
---|---|---|---|---|---|
/ | get | 否 | 渲染首页 | ||
/register(登录) | get | 否 | 渲染注册页面 | ||
/register | post | email,nickname,password | 否 | 处理注册请求 | |
/login | get | 是 | 渲染登陆界面 | ||
/login | post | email,password | 处理登录请求 | ||
/loginout | get | 处理退出请求 | |||
模型设计
阶段一
-
页面设计
- 一体页
功能实现
NodeJS2.0
补充
命令行窗口
-
cmd、终端、shell、dos
-
常用指令
-
dir
- 列出当前文件夹下所有文件
-
cd
-
md 目录名
- 创建文件夹
-
rd 目录名
- 删除文件夹
-
文件名
- 打开文件
-
-
环境变量
-
即windows系统的变量
-
path
-
系统寻找文件的顺序(dos)
- 当前目录下
- →环境变量path
-
类似作用域链
-
-
修改环境变量后需要重启dos
-
进程与线程
-
进程
-
负责为程序的运行提供必备的环境
- 相当于工厂车间
-
-
线程
-
计算机中最小的计算单位,负责执行进程中的程序
- 相当于车间里的工人
-
单线程
-
多线程
- 并发问题
-
关于性能
-
客户端
-
服务器端
-
多线程
-
nodejs
- 单线程
-
-
数据库
- 通过磁盘调用操作数据库
简介
定义
- nodejs是能在服务器端运行js的开放源代码、跨平台的js运行环境
特点
- 采用Google开发的V8引擎运行js代码
- 单线程服务器
- 使用事件驱动
- 非阻塞
- 异步I/O模型
作用
-
web服务API
- REST
-
实时多人游戏
关于node版本
- 奇数为开发版,偶数为稳定版
操作
使用node执行js文件
-
进入当前文件所在目录
-
快捷方法
- 在文件地址栏输入 cmd
- node 文件名
-
-
在编译器终端
- node 文件名
-
编译器直接run
模块化
简介
-
特点
- 大化小
- 避免耦合
- 增加复用
-
ES标准缺陷
- 没有模块系统(ES5)
- 标准库较少
- 没有标准接口
- 缺乏管理系统
CommonJS规范
-
简介
-
弥补当前js没有标准的缺陷
-
对模块的定义
- 模块引用
- 模块定义
- 模块标识
-
-
定义模块
- 创建js文件
- 在node中一个js文件就是一个模块
-
模块标识
-
定义
- 找到模块的方式
- 使用require引入外部模块时,使用的就是模块标识,通过模块标识来找到指定的标识
-
模块分类
-
核心模块
-
由node引擎提供的模块
-
模块标识
- 模块名
-
-
文件模块
-
由用户自己创建的模块
-
模块标识
- 文件路径
-
-
-
-
引用模块
-
require()
-
参数
- 文件路径
-
注意
- 相对路径必须以 ./或 …/开头
-
返回值
- 该函数会返回一个对象,该对象代表引入的模块
-
-
在node中,每一个文件中的js代码都是独立运行在一个函数中且传入了5个实参,而不是全局作用域
-
所以一个模块中的变量和函数在其他模块中无法访问
-
console.log(arguments)//证明一个模块的确是运行在函数里
-
arguments.callee
- 该属性保存的是当前正在执行的函数对象
-
-
node执行模块中的代码时
-
最顶部添加
- function (exports, require, module, __filename, __dirname) {
-
最底部添加
- }
-
-
五个参数
-
exports
-
该对象用来向外部暴露属性或方法
-
语法
- exports . x
-
-
require
- 函数,用来引入外部的模块
-
module
-
代表的是当前模块本身
-
exports就是module的属性
- 即module . exports = exports,本质相同
-
-
__filename
- 当前文件完整路径
-
__dirname
- 当前模块所在文件夹的完整路径
-
-
关于
-
module .exports
-
批量导入
- module.exports = {
}
- module.exports = {
-
可以通过 . 的形式,也可以直接赋值
- module.exports = { }
- model.exports . xxx = xxx
-
-
exports
-
通过exports只能使用 . 的方式向外暴露内部变量
- exports . xxx = xxx
-
改变量,不是改对象
-
-
-
-
global
-
定义
- node的全局对象,与window类似
- 在全局中创建的变量/函数都会作为global的属性/方法保存
-
创建
- 去掉var
-
包package
简介
-
规范的模块
- 将一组相关的模块组合到一起
-
本质:文件夹
CommonJS的包规范组成
-
包结构
-
用于组织包中的各种文件(压缩文件)
-
规范目录组成
-
package.json
- 描述文件(说明书)
-
bin
- 可执行二进制文件
-
lib
- js代码
-
doc
- 文档
-
test
- 单元测试
-
-
-
包描述文件
-
表达非代码的相关信息,放在包的根目录下
-
package.json
- name、version、description…
- 不能写注释
-
npm
-
简介
- Node Package Manager包管理工具
- CommonJS包规范是理论,npm是其实践之一
-
功能
- 帮助node完成第三方模块的发布、安装和依赖等
-
命令
-
npm -v
- 查npm版本
-
npm -version
- 所有模块版本
-
npm search 包名
- 搜索包
-
npm init
- 初始化,创建package.json文件
-
npm remove / r 包名
- 删除包
-
npm install / i 包名
- 安装包
-
npm install 包名 --save
- 安装包并添加到依赖
- 将包版本直接添加到package.json文件的dependencies中
-
npm install
- 下载当前项目所依赖的包
- 通过package.josn文件的dependencies寻找
-
npm install 包名 -g
-
全局安装包
-
一般是一些工具
- 用于计算机中,而不是项目中
-
-
npm install 包名 -registry=地址
- 从镜像原安装
-
npm config set registry 地址
- 设置镜像源
-
配置cnpm
-
切换为国内镜像源
- 淘宝npm镜像
-
npm install -g cnpm --registry=https://registry.npm.taobao.org
-
其他命令与npm相似
补充
-
通过npm下载的包都放到node_module文件夹中
- 直接通过包名引入即可
-
node搜索包的流程
- 通过包名引入时,首先在当前目录的node_module中寻找
- 如果没有则去上一级的node_module寻找
- 直到找到磁盘的根目录,如果依然没有,报错
Buffer缓冲区
简介
-
概念
-
buffer结构与数组相似,操作也类似
-
专门用来存储二进制文件的数组,以16进制显示
-
元素范围:00 ~ ff
-
实际范围:00000000~11111111
- 0~255(十进制)
-
-
-
作用
- 接收用户发送的数据,暂存
-
本质
-
直接操作内存空间
- 开辟连续的内存空间
-
操作
-
创建
-
Buffer.from
- 保存字符串到buffer中
- var buf = Buffer.from(str)
-
new Buffer(1024)
- 创建一个指定长度的buffer
- 尽量不用此构造函数
-
Buffer.alloc(1024)
- 创建指定字节的buffer
-
Buffer.allocUnsafe(10)
-
特性
- 可能含有敏感数据
- 分配空间时,不会清空之前的数据
-
缺点
- 可能泄露数据
-
优点
- 性能好
-
-
…
-
-
添加
- buf [0] = 254
-
读取
-
buf[0]
-
只要数字在控制台输出或页面中输出一定是10进制
-
若需要转换成其他
- toString(16)
-
-
-
遍历
- 同数组
属性
-
length
-
占用内存的大小(字节数)
-
区别字符串length
- 表示字符串的长度
-
fs 文件系统
简介
- 文件系统简单来说就是通过Node操作系统中的文件
- 该模块提供了一些标准文件访问API来打开、读取、写入文件,以及与其交互
特点
- fs模块所有操作都有同步与异步可选择
使用
-
引入
- var fs = require(‘fs’)
分类
-
文件写入
-
同步文件写入
-
同步文件系统会阻塞程序的执行
- 除非操作完毕,否则不会向下执行代码
-
步骤
-
打开文件
-
fs.openSync()
-
参数
-
path:要打开文件的路径
-
flags:打开文件要做的操作的类型(w,r)
-
w
- 覆盖写
-
a
- 追加写
-
r+
- 读写文件,追加,文件不存在则报错
-
-
[mode]:设置文件的操作权限,windows一般不写
-
-
返回值
- 该方法会返回一个文件的描述符
- 可通过该描述符对文件进行操作
-
-
-
写入内容
-
fs.writeSync()
-
参数
-
fd
- 文件描述符
-
string
- 要写入的内容
-
mode
- 写入的起始位置
-
encoding
- 写入的编码
-
-
-
-
保存并关闭文件
-
fs.closeSync(3)
-
参数
-
fd
- 文件描述符
-
-
-
-
-
-
异步文件写入
-
异步文件系统不会阻塞程序的执行,而是在操作完成时,通过回调函数将结果返回
-
步骤
-
打开文件
-
open
-
参数
-
path:路径
-
flags:操作类型
-
callback:回调函数
-
参数
-
err:错误对象
- 没有错误则为null(错误优先思想)
-
fd:文件描述符
-
-
-
-
返回值
- 异步方法没有返回值
- 异步调用的方法,结果都是通过回调函数返回的
-
open时,会将该任务交给后台线程池,继续异步执行open之后的代码,当open操作完成后,通过回调函数返回
-
-
-
写入文件
-
write
-
在回调函数里面操作
-
参数
-
fd
-
string:内容
-
callback:回调
-
参数
- err
- written:被写入的字符串的字节数
- string:写入的内容
-
-
-
-
-
关闭文件
-
close
-
参数
-
fd
-
callback
-
参数
- err
-
-
-
-
-
-
-
简单文件写入
-
步骤
-
异步
-
fs.writeFile()
-
参数
-
file:操作文件路径
-
data:指定要写入的数据
-
【options】
-
选项,配置信息,对写入进行一些设置
-
一般需要一个对象作为参数
- encoding:编码
- mode:0o666
- flag:‘w’
-
-
callback
-
写入完成以后执行的操作
-
参数
-
err
- 错误对象
-
-
-
-
-
-
同步
-
fs.writeFileSync()
-
参数
-
file:操作文件路径
-
data:指定要写入的数据
-
【options】
- 选项,配置信息,对写入进行一些设置
-
-
-
-
-
-
流式文件写入
-
概念
-
特点
- 非一次性写入文件
- 避免占用内存
- 适用大型文件
-
对比其他文件写入
- 容易内存溢出
- 性能差
-
-
步骤
-
创建一个可写流(接通水管)
-
fs.createWriteStream()
-
参数
- path
- 【option】
-
返回值
-
-
-
写入内容
-
write()
-
参数
- 字符串
-
-
-
关闭流
-
通过监听流的open和close事件来监听流的打开和关闭
-
补充
-
once(事件字符串,回调函数)
- 为对象绑定一个一次性的事件,触发后自动失效
-
on(事件字符串,回调函数)
- 绑定一个事件
-
-
end()
- 输送完后断“流”
-
close()
- 可能没送完就断“流”(旧版本)
-
-
-
-
-
文件读取
-
同步文件读取
-
异步文件读取
-
简单文件读取
-
同步
-
fs.readFile()
-
参数
- path,【options,】callback(err,data)
-
返回值
-
返回一个buffer
- 提高通用性
- 比如读取一张图片
-
-
-
-
异步
-
fs.readFileSync()
-
参数
- path,【options,】callback
-
-
-
-
流式文件读取
-
特点
- 适用于大文件
- 分多次读取文件
-
步骤
-
创建一个可读流
- var rs = fs.createReadStream(“hello2.txt”)
-
监听流的开启和关闭
-
读取
- 如果要读取一个可读流中的数据,必须为可读流绑定一个data事件,data事件绑定完毕,会自动开始读取数据
-
-
pipe()
-
将可读流中的内容直接输出到可写流中
-
简写(复制文件)
- var fs = require(‘fs’)
//创建一个可读流
var rs = fs.createReadStream(“hello2.txt”)
//创建一个可写流
var ws = fs.createWriteStream(‘a.txt’)
rs.pipe(ws)
- var fs = require(‘fs’)
-
-
-
其他方法
-
existsSync()
- 验证文件路径是否存在
- 只有同步(立即获取结果)
-
stat( path, callback(err,stat))
-
获取文件状态
-
返回一个对象,保存当前对象状态的信息
-
属性
- size
- …
-
-
unlink(path,callback)/unlinkSync(path)
- 删除文件
-
readdir(path[, option], callback(err, files))
-
读取一个目录的 目录结构
-
回调参数
-
files
- 是一个字符串数组,每一个元素就是一个文件夹或文件
-
-
-
truncate(path , len, callback)
- 截断文件
- len:截断后的文件大小
-
mkdir(path[,mode],callback)
- 创建目录
-
rmdir(path)
- 删除文件夹
-
rename(oldpath,newpath,callback)
- 重命名文件
- 改变路径
-
watchFile(filename[,optin], listener)
-
监视文件的修改,耗性能
-
参数
-
文件名
-
配置选项
-
interval:1000
- 监听频率(1s刷新一次)
-
-
回调函数
-
当文件发送变化时执行
-
参数
-
curr
- 当前文件状态stat
-
prev
- 修改前文件状态stat
-
-
-
-
XMind: ZEN - Trial Version
Tips: Please indicate the source and original author when reprinting or quoting this article.