Node_进阶_2

Posted eret9616

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Node_进阶_2相关的知识,希望对你有一定的参考价值。

第二天

 

一、复习:

Node.js开发服务器、数据、路由。本地关心效果,交互。

Node.js实际上是极客开发出的一个小玩具,不是银弹。有着别人不具备的怪异特点:

单线程、非阻塞I/O、事件驱动。 实际上是一个特点。

 

首先,Node不为每个用户开辟一个线程,所以非常极端的选择了单线程。

单线程,要照顾所有的用户,必须有非阻塞I/O否则一个人的I/O就把别人、自己都阻塞了。

 

一旦有非阻塞I/O,一个人如果I/O去了,就会放弃CPU的使用权,换成另一个人使用CPU(或者执行此人后面的语句)。所以CPU的利用率是100%。第一个人I/O结束了,就要用事件来通知线程,执行回调函数。此时必须有事件环,就有一个排队调度机制,Node中有超过半数的C++代码,在搭建事件环。

 

Node.js和别的老牌3P不一样:

1)     没有自己的语法,使用V8引擎,所以就是JS。V8引擎解析JS的,效率非常高,并且V8中很多东西都是异步的。Node就是将V8中的一些功能自己没有重写(别人做了,自己就站在巨人肩膀上),移植到了服务器上。

2)     没有web容器,就是安装配置完成之后,没有一个根目录。

 

 

汇编语言 暴露几个API => C语言 暴露几百个API => 高级语言 暴露更多API

 

 

系统中,80端口,就是默认的http端口,所以当没有端口号的时候就默认80端口。

(https是443)

 

之前写了一个静态服务器,但是其实不是很完善,虽然对mime类型有判断了,但是返回的都是200状态码。304状态码表示已经存在这个文件,实现的机制是用cookies来给文件打标记,302表示重定向。这些都没有实现.(顺带一提)

 

 

 

 

 

二、模块

模块的概念

·在Node.js中,以模块为单位划分所有功能,并且提供了一个完整的模块加载机制,这时的我们可以将应用程序划分为各个不同的部分。

不可能用一个js文件去写全部的业务,肯定要有MVC。

·狭义的说,每一个javascript文件都是一个模块,而多个JavaScript文件之间可以相互require,他们共同实现了一个功能,他们整体对外,又称为一个广义上的模块。

·Node.js中,一个JavaScript文件中定义的变量、函数,都只在这个文件有效。当需要从模块外部引用这些变量、函数时,必须使用exports对象进行暴露。使用者要用require()命令引用这个JS文件。

 

var msg = ‘你好’

var info = ‘呵呵’

 

exports.msg = msg

 

msg这个变量是一个js文件内部才有作用域的变量。

 

使用者:

var foo = require(‘./test/foo.js’)

console.log(foo.msg);

 

暴露与引用。js文件中可以用exports暴露很多东西,比如函数、变量。

 

相当于增加了顶层变量。所有的函数、变量都要从这个顶层变量走。

 

Node中,js文件和js文件,就是被一个个exports和require构建称为网状依赖的。

不是像html文件中从上往下依赖的。

 

在js中可以将一个类暴露出去,然后进行引用。

 

 

a.js

function People(name,sex,age)

{

this.name = name;

this.sex = sex;

this.age = age;

}

 

People.prototype = {

   SayHello:function(){

    console.log(this.name+this.sex+this.age);

}

}

 

//直接暴露出来

module.exports = People;

 

 

b.js:

var People = require(‘./a.js’);

 

var xiaoming = new People(‘xiaoming’,12,’男’);

xiaoming.sayHello();

 

 

文件夹模块和package文件

如果require(‘a.js’)这样不写出路径

会自动默认的加载node_modules文件夹中的内容。

如果当前层没有,他就逐级遍历哪一层有Node_modules文件夹。

甚至可以放到NODE_PATH环境变量的文件夹中。

 

计算机-高级系统设置-环境变量-新建系统遍历 名字叫NODE_PATH。

 

·也可以使用文件夹来管理模块,比如:

 var bar = require(‘bar’)

 那么Node.js将会去寻找node_modules目录下的bar文件夹中的Index.js去执行。

 可以自定义这个文件,需要建立package.json然后为main项设定。

这样require(‘bar’)文件夹的时候,就会去读取这个app.js

 

 

 

NPM

我们刚才学习了,模块就是一些功能的封装,所以一些成熟的、经常使用的功能,都有人封装成为了模块。

 

www.npmjs.com

 

去社区搜索需求,然后点进去看api

..

  1. 我们的依赖包,可能在随时更新,我们永远想保持更新。
  2. 项目越来越大的时候,给别人看的时候,没有必要再次共享我们引用的第三方模块。

 

 

所以我们用package.json来管理我们的依赖。

 

 

package.json的

‘dependency‘中的^符号确保更新时这一位版本不变,其他的位数在install时候会自动更新。因为社区开发者每个人对自己的版本更新都有不同的定义。有时虽然大版本没变,但是小版本号的更新也可能带来了巨大的变化。会导致程序挂掉。所以出现了packagelock.json来锁定到特定的版本。

 

 

路径

 

文件的操作一定要用绝对路径,因为如果在a.js中require了一个b.js,这个js中有用fs读取了一个相对路径,那么读取出来的路径会有错误,因为node a.js的时候是以a为相对目录的。

 

require()别的文件的时候,会执行这个js文件。

 

实际上他会做一次绑定,把对象中的export对象绑定给承接的变量。

 

注意:require()中的路径是从当前js文件出发找到别人。所以,桌面上有一个a.js,test文件夹中有一个b.js和c.js

a要引用b:

var a = require(‘./test/b.js‘);

b要引用c:

var b = require(‘./c.js’)

 

但是,fs等其他的模块用到路径的时候都是相对于入口文件的。

所以,如果test文件夹中,有一个1.txt,那么在b.txt中想读取这个文件,请使用绝对路径。

(使用__dirname)

 

狭义的讲,一个js文件就是一个模块。

广义的讲,一个文件夹(或者是带package.json的文件夹)也是一个模块,因为会去读取文件夹内的index.js(或者package.json中的main:’入口文件.js’)

 

三、post请求

 

post请求

示例:

(req.addListener == req.on)

 

 

a.js:

const http = require(\'http\');

const querystring = require(\'querystring\');

 

var server = http.createServer((req, res) => {

 

    //如果你的访问地址是这个,并且请求类型是post

    if (req.url == \'/dopost\' && req.method == \'POST\') {

 

        var alldata = "";

 

        //不断的往事件环里去加载这个事件

        //接受了一小段,可能就给别人去服务了。防止一个过大的表单阻塞了整个线程

        req.on(\'data\', (chunk) => {

            alldata += chunk;

        });

 

        req.on(\'end\', (chunk) => {

 

            var datastring = alldata.toString();

            res.end(\'success\');

 

            //将datastring转为一个对象

            var dataObj = querystring.parse(datastring, null, null,

            );

 

            console.log(dataObj);

            console.log(dataObj.name);

            console.log(dataObj.sex);

 

        })

 

    }

});



server.listen(3000);

 

aa.html:

<!DOCTYPE html>

<html lang="en">

 

<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>Document</title>

</head>

 

<body>

 

    <form action="http://127.0.0.1:3000/dopost" method="POST">

 

        <p>

            姓名:

            <input type="text" name=\'name\'>

        </p>

 

        <p>

            性别:

            <input type="radio" name=\'sex\' value=\'女\' id=\'female\'>

            <label for="female">女</label>

            <input type="radio" name=\'sex\' value=\'男\' id=\'male\'>

            <label for="male">男</label>

        </p>

 

        <p>

            爱好:

            <input type="checkbox" name=\'hobby\' value=\'睡觉\'>睡觉

            <input type="checkbox" name="hobby" value="吃饭">吃饭

            <input type="checkbox" name="hobby" value="足球">足球

        </p>

 

        <input type="submit" value="提交">

 

    </form>

 

</body>

 

</html>

 

 

 

如果这里有一个图片上传的input 最后alldata.toString()以后会是undefined ,所以,对于图片要做一个二进制的文件的处理。

 

formidable

formidable是一个node.js的表单模块

 

看以下npm.js网址上对这个模块的介绍:

Purpose

A Node.js module for parsing form data, especially file uploads.

This module was developed for Transloadit, a service focused on uploading and encoding images and videos. It has been battle-tested against hundreds of GB of file uploads from a large variety of clients and is considered production-ready.

(图为API的使用)

 

可以看到,把所有请求的表单域用fields(字段)表示,把所有的文件用files(文件)来表示。

 

 

aa.html:

<!DOCTYPE html>

<html lang="en">

 

<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>Document</title>

</head>

 

<body>

 

    <form action="http://127.0.0.1:3000/dopost" enctype="multipart/form-data" method="POST">

 

        <p>

            姓名:

            <input type="text" name=\'name\'>

        </p>

 

        <p>

            性别:

            <input type="radio" name=\'sex\' value=\'女\' id=\'female\'>

            <label for="female">女</label>

            <input type="radio" name=\'sex\' value=\'男\' id=\'male\'>

            <label for="male">男</label>

        </p>

 

        <p>

            爱好:

            <input type="checkbox" name=\'hobby\' value=\'睡觉\'>睡觉

            <input type="checkbox" name="hobby" value="吃饭">吃饭

            <input type="checkbox" name="hobby" value="足球">足球

        </p>

 

        <p>

            图片:

            <input type="file" name=\'tupian\'>

        </p>

 

        <input type="submit" value="提交">

 

    </form>

 

</body>

 

</html>

 

 

12.js:

//formidable

1.js:

const http = require(\'http\');

const querystring = require(\'querystring\');

const formidable = require(\'formidable\');

const util = require(\'util\');

 

var server = http.createServer((req, res) => {

 

    if (req.url == \'/dopost\' && req.method == \'POST\') {

 

        //create a new incoming form

        var form = new formidable.IncomingForm();

        //设置文件上传存放的地址

        form.uploadDir = "./uploads";

 

        //执行里面回调函数的时候,表单已经全部接收完毕了。

        form.parse(req, function (err, fields, files) {

 

            if (err) {

                throw err;

            }

 

            //所有的文本域、单选框,都在fileds里面存放;

            //所有的文件与,都在files内存放。

            //util.inspect 相当于console.log这个对象

            console.log(util.inspect({ fields: fields, files: files }));

 

            res.writeHead(200, { \'content-type\': \'text/html;charset=UTF-8\' });

            res.write(\'received upload:\\n\\n\');

            res.end(\'Success\');

 

        });

        return;

    }

}).listen(3000);

 

 

在这节课学到的一个比较重要的东西是如果上传图片的时候,在form标签里一定要加enctype="multipart/form-data"

 

 

 

上传改名

原生的POST比较复杂,要写两个监听。

所以用第三方的formidable比较方便。

 

下面继续上面的案例来做,上传的文件名改名为2018050812323(时间+随机数)这样。

 

这里顺带介绍了一下silly-datetime插件 可以格式化的生成时间

https://www.npmjs.com/package/silly-datetime

(非常好用)

 

formidable里面生成的fields与files的数据结构是这样的:

 

13.js:

//上传图片 文件名是当前时间

 

//formidable

 

const http = require(\'http\');

const querystring = require(\'querystring\');

const formidable = require(\'formidable\');

const util = require(\'util\');

const fs = require(\'fs\');

const sd = require(\'silly-datetime\');

const path = require(\'path\');

 

var server = http.createServer((req, res) => {

 

    if (req.url == \'/dopost\' && req.method == \'POST\') {

 

        //create a new incoming form

        var form = new formidable.IncomingForm();

        //设置文件上传存放的地址

        form.uploadDir = "./uploads";

 

        //执行里面回调函数的时候,表单已经全部接收完毕了。

        form.parse(req, function (err, fields, files) {

 

            if (err) {

                throw err;

            }

 

            //所有的文本域、单选框,都在fileds里面存放;

            //所有的文件与,都在files内存放。

            //util.inspect 相当于console.log这个对象

            console.log(util.inspect({ fields: fields, files: files }));

 

            //时间,使用的第三方模块,silly-datetime

            var ttt = sd.format(new Date(), \'YYYYMMDDHHmm\');

            var ran = parseInt(Math.random() * 89999 + 10000);

            var extname = path.extname(files.tupian.name);

 

            //执行该名

            var oldpath = __dirname + \'/\' + files.tupian.path;

            //新的路径由三个部分组成: 时间戳、随机数、扩展名

            var newpath = __dirname + \'/uploads/\' + ttt + ran + extname;

 

            //改名

            fs.rename(oldpath, newpath, (err) => {

 

                if (err) throw Error(\'改名失败\');

 

                res.writeHead(200, { \'content-type\': \'text/html;charset=UTF-8\' });

                res.end(\'成功\');

 

            });

        });

 

    } else if (req.url == \'/\') {

        //呈递form.html页面

        fs.readFile(\'./aa.html\', (err, data) => {

            res.writeHead(200, { \'Content-type\': \'text/html;charset=UTF-8\' });

            res.end(data);

        });

    } else {

        res.writeHead(404, { \'Content-type\': \'text/html;charset=UTF-8\' });

        res.end(\'404\');

    }

}).listen(3000);

 

 

aa.html:

<!DOCTYPE html>

<html lang="en">

 

<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>Document</title>

</head>

 

<body>

 

    <form action="http://127.0.0.1:3000/dopost" enctype="multipart/form-data" method="POST">

 

        <p>

            姓名:

            <input type="text" name=\'name\'>

        </p>

 

        <p>

            性别:

            <input type="radio" name=\'sex\' value=\'女\' id=\'female\'>

            <label for="female">女</label>

            <input type="radio" name=\'sex\' value=\'男\' id=\'male\'>

            <label for="male">男</label>

        </p>

 

        <p>

            爱好:

            <input type="checkbox" name=\'hobby\' value=\'睡觉\'>睡觉

            <input type="checkbox" name="hobby" value="吃饭">吃饭

            <input type="checkbox" name="hobby" value="足球">足球

        </p>

 

        <p>

            图片:

            <input type="file" name=\'tupian\'>

        </p>

 

        <input type="submit" value="提交">

 

    </form>

 

</body>

 

</html>

 

这个项目可以运行在服务器上,运行在局域网中,然后让别人给你的电脑上传文件,很棒。

 

 

四、模板引擎

ejs模板引擎

 

<a href="<%=url%>"><img src="<%=imageURL%>" alt=""></a>

数据绑定,就成为一个完整的html字符串了。

前台的模板,我们现在要学习的是后台的模板。

 

后台模板,著名的有两个,第一个叫做ejs,第二个叫做jade

 

是npm第三方的包

 

 

先说ejs

Embedded JavaScript templates

(嵌入式javascript模板引擎)

 

index.ejs:

<!DOCTYPE html>

<html lang="en">

 

<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>Document</title>

</head>

 

<body>

    <h1>好高兴啊,今天我买了一个iphone

        <%= a %>s</h1>

 

    <ul>

       <%for(var i = 0;i<news.length;i++){%> 

        <li><%= news[i]%></li>

        <% }%>

    </ul>

 

</body>

 

</html>

 

 

1.js:

const fs = require(\'fs\');

const ejs = require(\'ejs\');

const http = require(\'http\');

 

var server = http.createServer((req, res) => {

 

    fs.readFile(\'./views/index.ejs\', (err, data) => {

 

        //绑定模板

        var template = data.toString();

        var dictionary = {

            a: 6,

            news: [21, 4, \'哈哈\']

        };

        var html = ejs.render(template, dictionary);

 

        res.writeHead(200, { \'Content-type\': \'text/html;charset=UTF8\' });

        res.end(html);

    });

 

}).listen(2888);

 

 

 

(模板引擎这种在服务器上把语句组织起来的形式将逐渐被淘汰了,因为代码组织起来太杂糅了。这种时代一去不复返了。)

 

(未来后台只负责出数据)

 

jade模板引擎

ejs效率不高,因为是对字符串的处理 <% %>这种东西 下面介绍一下jade模板引擎

http://jade-lang.com/ (jade:玉)

www.npmjs.com/package/jade

(这个语法有点像Python)

这里不做介绍啦.

 

以上是关于Node_进阶_2的主要内容,如果未能解决你的问题,请参考以下文章

C++进阶AVL树

C++进阶二叉搜索树

C++进阶二叉搜索树

python_函数进阶

面对对象进阶2

Python中面向对象初识到进阶