node.js 的 基础。
node.js 基础
全局变量
有包括 console(输出),__dirname(文件目录位置),__filename(文件位置) 等全局对象,具体可看官方文档。
模块
node.js 的导入导出与 JavaScript 不同,导出使用 module.exports,导入使用 require()
单个 导出/导入
- 导出
1 | let count = (arr) => { |
- 导入
1 | var counter = require('./count') |
多个 导出/导入
- 导出
1 | let count = (arr) => { |
上面的写法相当于暴露了一个对象,等同于:
1 | let count = (arr) => { |
- 导入
1 | var stuff = require('./count') |
这里就是接收了一个对象,然后调用里面的方法,等同于:
1 | var counter = require('./count').counter |
事件
基础
node.js 写事件的方法:
1 | // 导入事件库 |
首先导入事件库,然后新增事件,之后绑定监听函数,这里和 JavaScript 一样,都是一个事件名称加一个回调函数。
触发事件的时候我们用代码触发,直接加上事件的名称,会自动去找事件然后触发。
进阶
1 | // 导入事件库 |
文件系统(同步、异步)
读取文件内容
- 同步
简单读取同目录下的文件 ReacMe.txt:
1 | // 导入文件系统库 |
现在就可以打印出当前的文件内容了。
- 异步
1 | let readme = fs.readFile('readMe.txt','utf8',function(err,data){ |
fs.readFile 是异步的,它的第一个参数是文件目录,第二个参数是一个回调函数:
1 | fs.readFile('<目录>', (err, data) => { |
node.js 有一个事件队列,执行当异步操作时,会去事件队列中注册一个事件,这时主线程不会立即执行它,而是分配一个线程去执行它,主线程继续执行其他的同步操作,当分线程执行完毕后,会在主线程空闲时把执行结果反馈给主线程,主线程在来执行异步操作的回调函数。
写入文件内容
- 同步
1 | let fs = require('fs'); |
执行完毕后,会在当前目录下创建一个文件:writeMe.txt(因为当前没有这个文件,有就不会创建),并写入 readMe 这个变量的内容。
- 异步
1 | fs.writeFile('writeMe.txt',readMe,function(err,data){ |
删除文件
- 同步
1 | fs.unlinkSync('readMe.txt') |
- 异步
1 | fs.unlink('writeMe.txt', (err) => { |
综合
创建一个目录,并且把当前目录下的一个文件复制到新创建的目录中去:
1 | let fs = require('fs'); |
流和管道
在 node.js 中,数据的请求和响应对应的就是输入和输出的流,webpack,gulp 等也大量运用了流,文件的打包压缩也是通过流来实现的。
读取数据的流
1 | let fs = require('fs') |
以上就实现了一个简单的读取 readMe.txt 中数据的流,它就相当于上面的文件系统中的读取文件内容。
写入数据的流
1 | let fs = require('fs') |
上面的代码在读取的基础上做了一些修改,我们创建了一个输出了流,在读取了 readMe.txt 后,把 readMe.txt 的内容给了 writeMe.txt,它就相当于上面的文件系统中的写入文件内容。
再来试一遍写入的流:
1 | let fs = require('fs') |
使用管道实现:
1 | let fs = require('fs') |
可以看到,使用管道的话,只要一句话就可以读出 readMe.txt 的内容,然后给 writeMe.txt,结束后打印 ‘done’。
HTTP
响应纯文本
先来实现一个 server
1 | let http = require('http') |
此时打开浏览器,在 3000 端口上就可以看见响应的内容,在谷歌调试工具的 Network 可以看见请求信息。
在终端中可以看见启动服务后打印了 Server started on localhost port 3000,请求了一次 3000 端口后打印了 Request received,多次请求可以看见多次打印。
这样我们就创建了一个简单的服务器,并且响应了一个纯文本给浏览器。
响应 JSON
响应 JSON 很简单,只要把响应内容的格式改成 application/json 就可以了:
1 | let http = require('http') |
上面就响应了一个 JSON 给浏览器,同样的,浏览器可以在 3000 端口查看相应内容。
响应 HTML
响应 html,把响应内容的格式改成 text/html 就可以了:
1 | let http = require('http') |
用流的形式读取页面并响应:
1 | let http = require('http') |
上面就是读取了 index.html 页面,然后响应给了服务器。
如果我们在响应的内容格式那里写 texl/plain,则浏览器不会解析,而是直接显示。
模块化重构
把上面的内容打包成一个模块:
1 | let http = require('http') |
这个模块的文件名称是 server.js,然后我们可以在 app.js 中调用它:
1 | let server = require('./server') |
在终端中执行
1 | node app |
即可在浏览器的 3000 端口看到 index.html 的内容。
路由
所谓的路由就是根据请求的 url 的不同,响应不同的页面。
基础
我们可以通过 request.url 来获取请求的 url 并进行判断,重构一下之前的代码:
1 | let http = require('http') |
上面就对请求的 url 进行了判断,当请求不同的 url 的时候,会返回不同的内容。
重构路由
上面的路由太繁琐了,严重影响阅读,我们可以对它进行一下重构。
首先,我们写一个 handler.js 模块:
1 | const fs = require('fs') |
这个模块就是把上面路由对应的判断写了进来,针对对应的 request.url 进行对应的返回,注意这里没有 404 的判断。我们把写的每一个判断都导出。
我们再写一个 router.js 模块来进行判断:
1 | const fs = require('fs') |
这个模块会接收 handle,pathname,response,其中的 handle 是我们在 app.js 中对 handle.js 进行处理后传递过来的;pathname 就是 server.js 里面的 request.url;response 就是 server.js 里面的 response。
这里就是对 server 传递过来的 request.url 进行判断,如果在 handle 中有对应的 function,就执行,没有,就返回 404。
我们返回 app.js,修改如下:
1 | let server = require('./server') |
app.js 里面就导入了 handle 模块 和 router 模块,我们会在这里对 handle 模块进行处理。
定义了一个空对象 handle,然后把每一个 handler 对应的 function 都一一对应到 handle 中,把处理好的 handle 和 route 传递给 server 的 startServer 方法。
修改 server.js 如下:
1 | let http = require('http') |
可以看到,现在的 server 就是接收 route 和 handle,所有的判断都交给 route 来做,对应的返回在 handle 中查找。
这样看起来我们的路由就很简单了,可读性也非常好。
使用 Get 和 Post
Get 方法
使用 Get 方法传递数据,是会把数据放在 url 后面,所有我们前面的路由那里也需要做一些处理,不能直接给 route 传递 request.url。
我们使用 node.js 的 url 库。
这个库就是操作 url 的,我们可以通过它对 url 进行取值处理。
对 server.js 中的 onRequest 进行修改:
1 | let url = require('url') |
1 | let onRequest = function (request, response) { |
url.parse(request.url,true).query 的第一个参数是 url;第二个参数是 true,代表解析这个 url 并返回一个对象,如果是 false 就直接输出一个字符串。
对 router.js 进行修改:
1 | const fs = require('fs') |
在 handler.js 中新建一个 function:
1 | function api_pass(response,params) { |
修改 app.js 的 handle:
1 | let server = require('./server') |
启动服务,在浏览器中输入:
就可以看见返回的结果:
1
2 >{"id":"4","age":"25"}
>
POST 方法
上面的方法只能获取 Get 请求的数据,我们修改一些 server.js 以获取 post 请求的数据:
1 | let querystring = require('querystring') |
1 | let onRequest = function (request, response) { |
这里是对 request 进行了一下判断,监听了 error,如果没有错误,就通过 data 获取数据,然后在 end 时调用了 route。
这里还对 data 做了一下处理,如果不这样处理,后面接收到的就是一个字符串,处理过后,接收到的就是一个 json 数据。
去修改一下 index.html:
1 |
|
加了一个表单,提交的路由是 /api/v1/records,所有修改一下 handler.js 的 api_records:
1 | function api_records(response,params) { |
这样我们在主界面填写表单提交后就会在 http://localhost:3000/api/v1/records 页面看见表单的内容。
判断 Get 或 Post 请求
上面能处理 post 但不能处理 get,所有我们这里判断一下不同的情况来进行不同的处理:
1 | let onRequest = function (request, response) { |
这里就判断了一下 post,实际情况肯定不止这两种,还有 put 等等。
优化:
1 | let onRequest = function (request, response) { |
package.json
我们使用一个项目的时候,如果没有 package.json 文件,可以通过
1 | $ npm init |
来生成 package.json 文件。
其中的 dependencies 是所有安装的包的信息,通过
1 | $ npm install --save <包名> |
安装的包会被记录在这里面。
其中的 devDependencies 是开发环境下需要使用的包的信息,通过
1 | $ npm install --save-dev <包名> |
安装的包会被记录在这里面。
其中的 scripts 是定义的运行对应脚本的命令,例如:
1 | "scripts": { |
通过在端上运行 app.js 就是:
1 | $ npm run start |
有了 package.json,其他人如果用你的项目,通过运行
1 | $ npm install |
可以下载项目需要的依赖的包,然后通过
1 | $ npm run start |
运行。
当然,上面的都是 npm,现在也有用 yarn 的,方法都类似。
nodemon
安装 nodemon
1 | $ npm install -g nodemon |
它的作用是监控所有的文件,当你做了修改后自动重启服务器,这样就不用每次都自己手动重启服务器了。
使用 nodeman 来启动服务:
1 | $ nodeman app |
启动后会显示
1 | [nodemon] 1.19.3 |
其中的最后一句(Server started on localhost port 3000)是我们定义在 server.js 中启动服务后打印的内容。
这样代码有改变的时候它会自动重启。
修改一下 package.json:
1 | "scripts": { |
这样我们就可以通过 npm run start 来使用 nodemon 启动服务了。
这个工具在开发环境中使用,不要在生产环境中使用。