一、前言
按照惯例,先扯淡,就因为这货,现在才有了各大公司招聘的全栈工程师,正是因为它,让以前只会写前端的人也能写起后端服务器代码来了。所以呢,你招一个会NodeJs的前端,它都能把后端干了,一个人干了两个人的事,你说哪个公司不想要。但是我还是要同情一下前端的兄弟们,真是苦了你们了,以前你们只是写页面就完了,现在还得写后台,再加上各种前端框架,什么Vue,Angular,React啊,恭喜你们,现在微信又带了一波小程序的节奏,唉,可怜的娃啊。
NodeJs看着也带Js后缀,但是它和VueJs,Angular,ReactJs可不一样,后三个都是框架,而NodeJs是平台,运行时环境。怎么说呢,就是NodeJs之于JavaScript,就好比jvm之于Java,就好比CLR之于c#,而Vue,Angular,React之于JavaScript就好比于 SpringMVC 之于Java,Asp.Net MVC,这个栗子举得应该是十分清楚了吧。也就是说一般的js都是在浏览器上执行,能力也有限,只能操作浏览器上边的东西,不可以读写你操作系统的文件什么的,而配上node环境的js是在你电脑上执行,可以读写你操作系统的文件啥的,一不小心还能把你的操作系统中的东西都给你删除了,多可怕。
但凡一个东西火了,我们要用它,所以它必有过人之处,也必有好的地方。所以呢,NodeJs好的地方就是 :
1.用JavaScript作为服务器端语言,这东西火啊,谁都会,简直没有学习成本,只用背几个api就行了啊。
2.统一前后端啊,全栈开发只需要学习一门语言,老板高兴了,招几个前端啥都有了,你给我写写写写写(为前端同学默哀3分钟)。
3.擅长处理高并发,做网站和文件读写相关的应用很不错。
4.参看前三点。(暂时还想不起来,假装有第四点)
I am very 皮。
二、关于NodeJs的介绍
我们先来看NodeJs官方给的介绍
2.1 第一句话
Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. 说NodeJs是一个基于谷歌V8JavaScript解析引擎的一个JavaScript 运行时。(据说V8是目前最快的js 解析引擎,毕竟大厂出品,值得信赖)。
2.2 第二句话
Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. 说NodeJs采用了事件驱动,非阻塞的IO模型,所以很轻,效率也很高。(所以处理并发很给力)
2.3 第三句话
Node.js' package ecosystem, npm, is the largest ecosystem of open source libraries in the world. 说NodeJs的包生态系统,npm,是世界上最大的开源库生态系统。(厉害了,我的哥)
三、Node中的JavaScript
3.1 没有BOM和DOM
众所周知,as we know,浏览器中的JavaScript是有dom和bom的,因为这样才可以操作html和浏览器相关的东西,比如document对象,window对象的,但是在Node中的js是没有这些对象的,因为很显然,不需要操作html和浏览器,因为压根在服务器端就没有这些东西。
如图所示,在node中没有window和document对象,输出时就会报错。
3.2 提供了一些服务器级别的API
除了没有dom和bom一说,在语法上是一样的,都是基于ECMAScript的,另外的是Node提供了一些服务器级别的api,比如操作文件、网络请求,http什么的,这些在浏览器端的js是没有这些功能的。
3.3 Node中的模块系统
Node中没有全局作用于的概念,在 Node 中,只能通过 require 方法来加载执行多个 JavaScript 脚本文件,默认每个文件就是一个模块,两个模块中的变量不会有污染问题,也就是说两个文件中可以有相同的变量,但是在浏览器中的js是不允许这样做的。如果你同时在a.js 和b.js中声明 var foo = 'bar' ;这在Node中是允许的。但是在浏览器中就会报错。
注意:前端模块化有这么几种规范CommonJs,AMD,CMD,ES6,在每种标准中,模块化的写法不一样,实际上node中使用的模块化功能是CommomJS的规范,最新的官方标准模块化规范是在ES6中,也渐渐的被大家和主流的浏览器所支持。
可以看出,在a.js中输出foo,是a的值,在b.js中调用add方法,结果是b中的add函数在起作用,由此可以看出,模块之间不存在变量污染问题,每个模块都是独立的作用域。
Node中通过require加载并执行其中的代码,文件与文件之间由于是模块作用域,所以不会有全局污染的问题,模块是完全封闭的,外部无法访问内部,内部也无法访问外部。
但是模块之间是需要通讯的,Node为每个文件(也就是模块)提供了一个exports对象,默认情况下,该对象是一个空对象,你要做的就是把需要外部访问使用的成员手动挂载到exports接口对象中,然后谁来require这个模块,返回值就是模块内部的exports对象。
//a.js
var b = require('./b.js');
console.log(b);
//b.js
var foo = 'b foo';
function add(x, y) {
return x - y;
}
//运行后输出
{}
可以看出require的返回值是一个空对象,也就是b.js中的exports对象,接下来我们对exports对象进行操作,看看会有什么结果。
//a.js
var b = require('./b.js');
console.log(b);
//b.js
var foo = 'b foo';
function add(x, y) {
return x - y;
}
exports.foo = foo;
exports.add = add;
//运行后输出
{
foo:'b foo',
add:[Function:add]
}
可以看出返回的对象里边已经有了我们赋值的属性。这样,我们就可以使用这些属性和功能,就可以进行模块之间的通讯。
3.4 Node中的核心模块
核心模块是由Node提供的一个个具有名字的模块,它们都有自己特殊的名称标识,例如: fs 文件操作模块、http 网络服务构建模块、os 操作系统信息模块、path 路径处理模块等等。所有核心模块在使用的过程中必须手动的用require方法来加载,例如使用文件系统模块,var fs = require('fs');
var http = require('http');
var fs = require('fs');
var os = require('os');
var path = require('path');
console.log(http,fs,os,path);
四、传说中的四行代码开发Web服务器
var http = require('http'); //加载http模块
var server = http.createServer(); //创建服务器
server.on('request', function (request, response) { //注册request请求事件
response.end('hello,nodejs')
});
server.listen('4396', function () { //监听4396端口
console.log('服务器已经启动成功了');
});
浏览器访问:http://localhost:4396/ 响应如下
刚看到,感觉是真的厉害,就想问问Node 你究竟干了什么。
五、加载和导出的使用规则
1.require 加载方式
//加载方式有以下几种
// 1. 直接写模块名字
//系统核心模块,直接写名字就行,不需要路径。
var module = require('fs');
//第三方模块,也是直接写名字,但是名字绝对不可能与系统核心模块一样。你懂的。
var module = require('art-template');
// ./ 相对路径形式,用于自己写的模块,路径前必须加.,不然路径找的是当前文件的根目录路径
// 作过web开发的同学应该都知道,一般项目中从来都是禁止写绝对路径的
var module = require('../a.js');
var module = require('./b.js');
2.导出的规则
//每个页面(模块)默认有一个exports对象,想要导出什么就按照以下方式
exports.add = function (x, y) {
return x + y;
}
exports.foo = 'foo';
//但是这种方式是错误的,并不能导出
exports = {
add: function (x, y) {
return x + y;
},
foo: 'foo'
};
//内部原理module.exports,因为node内部默认是
exports = module.exports;
// 最终导出的还是module.exports;所以如果想导出一个整体,应该如下:
//体会以下引用的含义就明白为什么了。
module.exports = {
add: function (x, y) {
return x + y;
},
foo: 'foo'
}
六、包加载的方式
1.有路径的包
有路径的话是按照相对当前文件的路径找到对应的文件 直接执行就行了。
2.第三方的包(不带路径符号的)
2.1 找到当前文件中的node_modules,找到相应的模块名字,找到下面的package.json,里边有main,找到main的值,一般是index.js(这里就可以指定),然后node就去加载index.js文件 获得返回的exports对象,接着就想我们上边用模块一样了。
以art-template为例,第一步,找到package.json
第二步,找到里边的main.js的值,为index.js
第三步,node去加载index.js,下边是index.js里边的代码,可以看出是标准的node中的模块导出写法。
const template = require('./lib/index');
const extension = require('./lib/extension');
template.extension = extension;
require.extensions[template.defaults.extname] = extension;
module.exports = template;
3.关于package-lock.json
npm 版本 5.0以上会自动添加一个package-lock.json的文件,在该文件中保存了所有的包的依赖和下载地址,这样的话还原包的时候就不用一个个解析依赖,直接下载就行了,大大提高了效率。而且,有了这个文件,当你用npm还原包的时候还能锁定文件的版本,不至于还原的时候自动升级到最新版本。
七、npm常用命令
基本上就用到一下几个非常简单的命令
npm init //初始化一个基于node的app,这会让你一步一步输入你的app的配置
npm init -y //初始化一个基于node的app,使用默认生成配置
npm install 包名 // 安装一个包
npm install --save 包名 //安装一个包,并添加依赖信息到配置文件里边
npm install //根据依赖还原你所有依赖的包,万一不小心把node_modules文件夹删除了,那就用这个非常方便
npm remove 包名 //卸载一个包
npm remove --save 包名 //卸载一个包,并删除配置文件里相关的依赖配置信息
八、Express框架
var express = require('express'); //加载express模块
var app = express(); //启动服务器,相当于http.createServer()
app.use('/public/', express.static('./public/')); //设置静态文件目录
app.get('/', function (req, res) { //处理/的请求
res.end('hello,express');
})
app.listen(3000, function () { //开启监听3000端口
console.log('starting...');
})
九、框架搭建
9.1 app.js
var express = require('express');
var router = require('./router');
var bodyParser = require('body-parser');
var app = express();
app.engine('html', require('express-art-template')); //设置art-template的解析方式,为解析带html后缀的文件
app.use('/node_modules/', express.static('./node_modules/'));
app.use('/public/', express.static('./public/'));
app.use(bodyParser.urlencoded({ extended: false })); //使用body解析post数据库
app.use(bodyParser.json());
app.use(router); //使用路由
app.listen(3000, function () {
console.log('running...');
})
9.2 router.js
var express = require('express');
var student = require('./student');
var router = express.Router();
router.get('/add', function (req, res) {
res.render('add.html'); //默认去找views文件下的同名文件
});
router.post('/add', function (req, res) {
student.add(req.body, function (error, data) {
if (error) {
res.end('error');
} else {
res.redirect('/');
}
})
})
router.get('/', function (req, res) {
student.list(function (error, data) {
if (error) {
res.end('error');
} else {
res.render('index.html', {
students: data
});
}
})
})
router.get('/student', function (req, res) {
res.render('index.html')
})
router.post('/post', function (req, res) {
});
module.exports = router;
9.3 student.js
var fs = require('fs');
var student = {};
student.list = function (fn) {
fs.readFile('./student.json', function (error, data) {
if (error) {
fn(error);
} else {
fn(null, JSON.parse(data.toString()));
}
})
}
student.add = function (student, fn) {
fs.readFile('./student.json', function (error, data) {
if (error) {
fn(error);
} else {
var students = JSON.parse(data.toString());
students.push(student);
fs.writeFile('./student.json', JSON.stringify(students), function (error, data) {
if (error) {
fn(error);
} else {
fn(null, data);
}
});
}
})
}
module.exports = student;
十、数据访问(mongodb)
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
var catSchema = mongoose.Schema({
name: String
});
const cat = mongoose.model('Cat', catSchema);
const kitty = new Cat({ name: 'cat' });
kitty.save().then(() => console.log('meow'));
cat.find({ name: 'cat' }, function (error, cat) {
})
十、总结
总之,NodeJs开启了JavaScript的新的神秘之旅,目前正在探索中,不过前几天看到Node之父说NodeJs设计的出现了很大的失败,又出了个叫deno的东西,唉,前端兄弟们真是堪忧。。。。