node.js 笔记
https://github.com/nswbmw/N-blog/tree/master/book
启动服务
- mongod --config /usr/local/etc/mongod.conf
- cd /usr/local/bin 终端进去
- ./mongo 查看并且链接
终止服务
- pkill mongod
-
引入:
- import {firstName, lastName, year} from './profile';
-
输出:
- export {firstName, lastName, year};
-
初始化:
- npm init
-
下载模块:
- npm i express --save
var express = require('express');
var app = express();
app.use(express.bodyParser());//现实body
app.all('/', (req, res) => {
res.send(req.body.title + req.body.text);
});
app.listen(3000);
- res.send('Hello World') 比 res.end('Hello World')
- 省去诸如添加Content-Length之类的事情
- 每次修改代码保存后,就自自动启动
-
npm WARN checkPermissions Missing write access(权限问题
-
sudo npm install -g supervisor
-
运行 supervisor --harmony index 启动程序 cd进去
-
kill node 关闭
-
supervisor start app
-
停止进程: control+c
-
//localhost:3000/users/nswbmw
const express = require('express')
const app = express()
app.get('/',(req,res) => {
res.send('hello, express ')
})
app.get('/users/:name',(req,res) => {
res.send('hello' + req.params.name)
})
app.listen(3000)
- req.query: 解析后的 url 中的 querystring,如 ?name=haha,req.query 的值为 {name: 'haha'}
- req.params: 解析 url 中的占位符,如 /:name,访问 /haha,req.params 的值为 {name: 'haha'}
- req.body: 解析后请求体,需使用相关的模块,如 body-parser,请求体为 {"name": "haha"},则 req.body 为 {name: 'haha'}
模块加载
exports.Hello = Hello
var Hello = require('./hello').Hello 来获取
--
module.exports = Hello
var Hello = require('./hello')
路由
index.js
'use strict'
const express = require('express')
const app = express()
const indexRouter = require('./routes/index')
const userRouter = require('./routes/users')
app.use('/',indexRouter)
app.use('/users',userRouter)
app.listen(3000)
router index.js
'use strict'
const express = require('express')
const router = express.Router()
router.get('/', (req, res) => {
res.send('hello, express')
})
module.exports = router
router users.js
'use strict'
const express = require('express')
const router = express.Router()
router.get('/users/:name',(req,res) => {
res.send('hello, '+req.params.name)
})
module.exports = router
- npm i ejs --save
模版
index.js
'use strict'
const path = require('path')
const express = require('express')
const app = express()
const indexRouter = require('./routes/index')
const userRouter = require('./routes/users')
app.set('views', path.join(__dirname, 'views'))// 设置存放模板文件的目录,查找模版路径
app.set('view engine', 'ejs')// 设置模板引擎为 ejs
app.use('/',indexRouter)
app.use('/users',userRouter)
app.listen(3000)
routes/users.js
'use strict'
const express = require('express')
const router = express.Router()
router.get('/:name', (req, res) =>{
res.render('users', {
name: req.params.name
})
})
module.exports = router
//通过调用 res.render 函数渲染 ejs 模板,res.render 第一个参数是模板的名字,这里是 users 则会匹配 views/users.ejs,第二个参数是传给模板的数据,这里传入 name,则在 ejs 模板中可使用 name。res.render 的作用就是将模板和数据结合生成 html,同时设置响应头中的 Content-Type: text/html,告诉浏览器我返回的是 html,不是纯文本,要按 html 展示。现在我们访问 localhost:3000/users/haha
views/users.ejs
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
body {padding: 50px;font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;}
</style>
</head>
<body>
<h1><%= name.toUpperCase() %></h1>
<p>hello, <%= name %></p>
</body>
</html>
上面html name.toUpperCase()大写
<% code %>:运行 JavaScript 代码,不输出
<%= code %>:显示转义后的 HTML内容
<%- code %>:显示原始 HTML 内容
supplies: ['mop', 'broom', 'duster']
//模版拼接
<ul>
<% for(var i=0; i<supplies.length; i++) {%>
<li><%= supplies[i] %></li>
<% } %>
</ul>
//形成的样式
<ul>
<li>mop</li>
<li>broom</li>
<li>duster</li>
</ul>
模版引用
我们使用模板引擎通常不是一个页面对应一个模板,这样就失去了模板的优势,而是把模板拆成可复用的模板片段组合使用,如在 views 下新建 header.ejs 和 footer.ejs,并修改 users.ejs:
views/header.ejs
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
body {padding: 50px;font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;}
</style>
</head>
<body>
views/footer.ejs
</body>
</html>
views/users.ejs
拼接成网页<%-include('文件名字')%>
<%- include('header') %>
<h1><%= name.toUpperCase() %></h1>
<p>hello, <%= name %></p>
<%- include('footer') %>
中间件
index.js
const express = require('express');
const app = express();
app.use(function(req, res, next) {
console.log('1');
next();
});
app.use(function(req, res, next) {
console.log('2');
res.status(200).end();
});
app.listen(3000);
访问 localhost:3000,终端会输出:
1
2
错误处理
index.js
const express = require('express');
const app = express()
app.use(function(req, res, next) {
console.log('1')
next(new Error('haha'))
});
app.use(function(req, res, next) {
console.log('2')
res.status(200).end()
});
//错误处理
app.use(function(err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
app.listen(3000)
//此时访问 localhost:3000,命令行输出:1,浏览器会显示:Something broke!。
events 事件
var events = require('events');
var emitter = new events.EventEmitter()
emitter.on('someEvent', function(arg1, arg2) {
console.log('listener1', arg1, arg2)
})
emitter.on('someEvent', function(arg1, arg2) {
console.log('listener2', arg1, arg2)
})
emitter.emit('someEvent', 'byvoid', 1991)
//运行结果
//listener1 byvoid 1991
//listener2 byvoid 1991
以上例子中,emitter 为事件 someEvent 注册了两个事件监听器,然后发射了 someEvent 事件。运行结果中可以看到两个事件监听器回调函数被先后调用。这就是EventEmitter最简单的用法。接下来我们介绍一下EventEmitter常用的API。
EventEmitter.on(event, listener) 为指定事件注册一个监听器,接受一个字
符串event 和一个回调函数listener。
EventEmitter.emit(event, [arg1], [arg2], [...]) 发射 event 事件,传
递若干可选参数到事件监听器的参数表。
EventEmitter.once(event, listener) 为指定事件注册一个单次监听器,即
监听器最多只会触发一次,触发后立刻解除该监听器。
EventEmitter.removeListener(event, listener) 移除指定事件的某个监听
器,listener 必须是该事件已经注册过的监听器。
EventEmitter.removeAllListeners([event]) 移除所有事件的所有监听器, 如果指定 event,则移除指定事件的所有监听器。
fs.readfile
//异步的 fs.readFile()
//同步的 fs.readFileSync()
var fs = require('fs');
fs.readFile('content.txt', function(err, data) {
if (err) {
console.error(err);
} else {
console.log(data);
}
});
//假设 content.txt 中的内容是 UTF-8 编码的 Text 文本文件示例,运行结果如下:
//<Buffer 54 65 78 74 20 e6 96 87 e6 9c ac e6 96 87 e4 bb b6 e7 a4 ba e4 be 8b>
var fs = require('fs');
fs.readFile('content.txt', 'utf-8', function(err, data) {
if (err) {
console.error(err);
} else {
console.log(data);
}
});
//那么运行结果则是:
//Text 文本文件示例
npm
无参数的 npm install 的功能就是 检查当前目录下的 package.json,并自动安装所有指定的依赖。
express 架构分离
image- 这是一个典型的 MVC 架构,浏览器发起请求,由路由控制器接受,根据不同的路径定 向到不同的控制器。控制器处理用户的具体请求,可能会访问数据库中的对象,即模型部分。控制器还要访问模板引擎,生成视图的 HTML,最后再由控制器返回给浏览器,完成一次请求。
express 路径匹配
如我们想要展示一个用户的个人页面,路径为 /user/[username],可以用下面的方法定义路由规则:
app.get('/user/:username', function(req, res) {
res.send('user: ' + req.params.username);
});
REST风格
- 请求方式 安全 幂等
- GET 是 是
- POST 否 否
- PUT 否 是
- DELETE 否 是
- 所谓安全是指没有副作用,即请求不会对资源产生变动,连续访问多次所获得的结果不受访问者的影响。而幂等指的是重复请求多次与一次请求的效果是一样的,比如获取和更新操作是幂等的,这与新增不同。删除也是幂等的,即重复删除一个资源,和删除一次是 一样的。
Express 对每种 HTTP 请求方法绑定:
-
GET
app.get(path, callback) -
POST
app.post(path, callback) -
PUT
app.put(path, callback) -
DELETE
app.delete(path, callback) -
PATCH
app.patch(path, callback) -
TRACE
app.trace(path, callback) -
CONNECT
app.connect(path, callback) -
OPTIONS
app.options(path, callback) -
所有方法
app.all(path, callback)
-
Express 路由控制权转移
Express 提供了路由控制权转移的方法,即回调函数的第三个参数next,通过调用 next(),会将路由控制权转移给后面的规则,例如:
app.all('/user/:username', function(req, res, next) {
console.log('all methods captured');
next();
});
app.get('/user/:username', function(req, res) {
res.send('user: ' + req.params.username);
});
//如果调用next(“route”),则会跳过当前路由的其它中间件,直接将控制权交给下一个路由。
使用模版引擎
我们在 app.js 中通过以下两个语句设置了模板引擎和页面模板的位置:
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
表明要使用的模板引擎是 ejs,页面模板在 views 子目录下。在 routes/index.js 的 exports.index 函数中通过如下语句调用模板引擎:
res.render('index', { title: 'Express' });
res.render 的功能是调用模板引擎,并将其产生的页面直接返回给客户端。它接受 两个参数,第一个是模板的名称,即 views 目录下的模板文件名,不包含文件的扩展名;第 二个参数是传递给模板的数据,用于模板翻译。index.ejs 内容如下:
<h1><%= title %></h1>
<p>Welcome to <%= title %></p>
上面代码其中有两处 <%= title %>,用于模板变量显示,它们在模板翻译时会被替换 成 Express,因为 res.render 传递了 { title: 'Express' }。
ejs 的标签系统非常简单,它只有以下3种标签。
<% code %>:JavaScript 代码。
<%= code %>:显示替换过 HTML 特殊字符的内容。
<%- code %>:显示原始 HTML 内容。 我们可以用它们实现页面模板系统能实现的任何内容。
会话支持Cookie
为了在无状态的 HTTP 协议之上实现会话,Cookie 诞生了。Cookie 是一些存储在客户 端的信息,每次连接的时候由浏览器向服务器递交,服务器也向浏览器发起存储 Cookie 的 请求,依靠这样的手段服务器可以识别客户端。我们通常意义上的 HTTP 会话功能就是这样 实现的。具体来说,浏览器首次向服务器发起请求时,服务器生成一个唯一标识符并发送给 客户端浏览器,浏览器将这个唯一标识符存储在 Cookie 中,以后每次再发起请求,客户端 浏览器都会向服务器传送这个唯一标识符,服务器通过这个唯一标识符来识别用户。
methodOverride
因为我们单击出发的是post,而服务器上是put事件.
现在想让服务器端依然是put接受,html上依然是post请求. 加app.use(express.methodOverride());
http://www.bubuko.com/infodetail-503315.html
重定位
'use strict'
const express = require('express')
const app = express()
app.get('/', function (req, res) {
console.log('1')
});
app.get('/one', function(req, res) {
console.log('2')
return res.redirect('/');//是重定向功能,
})
//访问http://localhost:3000/one 会输出:2,1
connect-flash
const express = require('express')
const session = require('express-session')
const flash = require('connect-flash');
const app = express()
app.use(session({
secret: 'one', //通过设置 secret 来计算 hash 值并放在 cookie 中,使产生的 signedCookie 防篡改
cookie: {maxAge: 60000}, // 过期时间,过期后 cookie 中的 session id 自动删除
}))
app.use(flash());//中间件
app.get('/', function (req, res) {
//取出来,然后就消失了
console.log(req.flash('one'))
res.send('success')
})
app.get('/one', function(req, res) {
req.flash('one','1')//存 key value值
return res.redirect('/')//是重定向功能,
})
//这是session 设置详情
'use strict'
const express = require('express')
const session = require('express-session')
const flash = require('connect-flash')
const MongoStore = require('connect-mongo')(session);
const app = express()
app.use(session({
name: 'app'
secret: 'one', //通过设置 secret 来计算 hash 值并放在 cookie 中,使产生的 signedCookie 防篡改
cookie: {maxAge: 60000}, // 即60000后session和相应的cookie失效过期除
resave: false,//是指每次请求都重新设置session cookie,假设你的cookie是10分钟过期,每次请求都会再设置10分钟
saveUninitialized: true//是指无论有没有session cookie,每次请求都设置个session cookie ,默认给个标示为 connect.sid
//数据库储存
store: new MongoStore({ //创建新的mongodb数据库
host: 'localhost', //数据库的地址,本机的话就是127.0.0.1,也可以是网络主机
port: 27017, //数据库的端口号
db: 'test-app' //数据库的名称。
})
}))
app.use(flash());//中间件
app.get('/', function (req, res) {
//取出来,然后就消失了
console.log(req.flash('one'))
res.send('success')
});
app.get('/one', function(req, res) {
req.flash('one','1')//存 key value值
return res.redirect('/')//是重定向功能,
})
// 监听端口,启动程序
app.listen(3000, function () {
console.log(`success`)
})
require
我们要在 /home/byvoid/develop/foo.js 中使用 require('bar.js') 命令,Node.js会依次查找:
/home/byvoid/develop/node_modules/bar.js
/home/byvoid/node_modules/bar.js
/home/node_modules/bar.js
/node_modules/bar.js
循环便利,里面异步回调-有坑
var fs = require('fs');
var files = ['a.txt', 'b.txt', 'c.txt'];
for (var i = 0; i < files.length; i++) {
fs.readFile(files[i], 'utf-8', function(err, contents) {
console.log(files);
console.log(i);
console.log(files[i]);
});
}
运行结果如下:
[ 'a.txt', 'b.txt', 'c.txt' ]
3
undefined
[ 'a.txt', 'b.txt', 'c.txt' ]
3
undefined
[ 'a.txt', 'b.txt', 'c.txt' ]
3
undefined
//三次输出的 i 的值都是 3,超出了 files 数组的下标 范围,因此 files[i] 的值就是 undefined 了。
//现在问题就明朗了:原因是3次读取文件的回调函数事实上是同一个实例,退出循环时 i 的值就是 files.length 的值。既然 i 的值是 3,引用到的 i 值是上面循环执行结束后的值,因此不能分辨。
//解决方案
var fs = require('fs');
var files = ['a.txt', 'b.txt', 'c.txt'];
files.forEach(function(filename) {
fs.readFile(filename, 'utf-8', function(err, contents) {
console.log(filename + ': ' + contents);
});
});
构造函数
//创建复杂的对象。
function User(name, uri) {
this.name = name;
this.uri = uri;
this.display = function() {
console.log(this.name);
}
}
//以上是一个简单的构造函数,接下来用new 语句来创建对象:
var someuser = new User('byvoid', 'http://www.byvoid.com');
//然后就可以通过someuser 来访问这个对象的属性和方法了。
this指针
//this 指针不属于某个函数,而是函数调用时所属的对象。
var someuser = {
name: 'byvoid',
func: function() {
console.log(this.name);
}
};
var foo = {
name: 'foobar'
};
someuser.func(); // 输出 byvoid
foo.func = someuser.func;
foo.func(); // 输出 foobar
name = 'global';
func = someuser.func; func(); // 输出 global
call 和 apply
//call 和 apply 的功能是一致的,两者细微的差别在于 call 以参数表来接受被调用函 数的参数,而 apply 以数组来接受被调用函数的参数。
var someuser = {
name: 'byvoid',
display: function(words) {
console.log(this.name + ' says ' + words);
}
};
var foo = {
name: 'foobar'
};
someuser.display.call(foo, 'hello'); // 输出 foobar says hello
输出了 foobar.someuser.display 是 被调用的函数,它通过 call 将上下文改变为 foo 对象,因此在函数体内访问 this.name 时,实际上访问的是 foo.name,因而输出了foobar。
bind绑定
bind 方法来永久地绑定函数的上下文,使其无论被谁调用,上 下文都是固定的
var someuser = {
name: 'byvoid',
func: function() {
console.log(this.name);
}
};
var foo = {
name: 'foobar'
};
foo.func = someuser.func;
foo.func(); // 输出 foobar
foo.func1 = someuser.func.bind(someuser);
foo.func1(); // 输出 byvoid func = someuser.func.bind(foo);
func(); // 输出 foobar func2 = func;
func2(); // 输出 foobar
使用 bind 绑定参数表
bind 方法还有一个重要的功能:绑定参数表
var person = {
name: 'byvoid', 7 says: function(act, obj) {
console.log(this.name + ' ' + act + ' ' + obj); }
};
person.says('loves', 'diovyb'); // 输出 byvoid loves diovyb
byvoidLoves = person.says.bind(person, 'loves');
byvoidLoves('you'); // 输出 byvoid loves you
可以看到,byvoidLoves 将 this 指针绑定到了 person,并将第一个参数绑定到 loves,之后在调用 byvoidLoves 的时候,只需传入第三个参数。这个特性可以用于创建 一个函数的“捷径”.
原型链
两个特殊的对象: Object 与 Function,它们都是构造函数,用于生成对象。Object.prototype 是所有对象的祖先,Function.prototype 是所有函数的原型,包括构造函数。
我把 JavaScript 中的对象分为三类:
一类是用户创建的对象,用户创建的对象,即一般意义上用 new 语句显式构造的对象。
一类是构造函数对象,构造函数对象指的是普通的构造函数,即通过 new 调用生成普通对象的 函数。
一类是原型对象,原型对象特指构造函数 prototype 属性指向的 对象。
这三类对象中每一类都有一个 __proto__ 属 性,它指向该对象的原型,从任何对象沿着它开始遍历都可以追溯到 Object.prototype。
构造函数对象有 prototype 属性,指向一个原型对象,通过该构造函数创建对象时,被创 建对象的 __proto__ 属性将会指向构造函数的 prototype 属性。原型对象有 constructor 属性,指向它对应的构造函数。
function Foo() {
}
Object.prototype.name = 'My Object';
Foo.prototype.name = 'Bar';
var obj = new Object();
var foo = new Foo();
console.log(obj.name); // 输出 My Object
console.log(foo.name); // 输出 Bar
console.log(foo.__proto__.name); // 输出 Bar
console.log(foo.__proto__.__proto__.name); // 输出 My Object
console.log(foo.__proto__.constructor.prototype.name); // 输出 Bar
[图片上传失败...(image-badd9-1512039587570)]
对象定义
尽量将所有的成员函数通过原型定义,将属性在构造函数内定义,然后对构造函数使用 new 关键字创建对象。绝对不要把属性作为原型定义,因为当要定义的属性是一个对象的 时候,不同实例中的属性会指向同一地址。
正确:
function FooObj(bar) {
//在构造函数中初始化属性
this.bar = bar;
this.arr = [1, 2, 3];
}
//使用原型定义成员函数
FooObj.prototype.func = function() {
console.log(this.arr);
};
var obj1 = new FooObj('obj1');
var obj2 = new FooObj('obj2');
obj1.arr.push(4);
obj1.func(); // [1, 2, 3, 4]
obj2.func(); // [1, 2, 3]