美文网首页
node.js 笔记

node.js 笔记

作者: 梁同桌 | 来源:发表于2017-11-30 18:59 被阅读32次

    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]
    
    

    相关文章

      网友评论

          本文标题:node.js 笔记

          本文链接:https://www.haomeiwen.com/subject/izhbbxtx.html