为了账号安全,请及时绑定邮箱和手机立即绑定

NodeJS项目基础结构简单介绍

标签:
JavaScript

webp

新建的基于ejs模版的NodeJS项目.png

按照常规,去执行npm start

webp

侧边栏左下角的npm命令.png

或者在Terminal中手动输入命令,结果如下:

C:\Subversion\test>npm start

> test@0.0.0 start C:\Subversion\test> node ./bin/www

可以看到是执行了test/package.json文件中的start命令,初始packagejson.json文件内容如下:

{  "name": "test",  "version": "0.0.0",  "private": true,  "scripts": {    "start": "node ./bin/www"
  },  "dependencies": {    "cookie-parser": "~1.4.3",    "debug": "~2.6.9",    "ejs": "~2.5.7",    "express": "~4.16.0",    "http-errors": "~1.6.2",    "morgan": "~1.9.0"
  }
}

执行start,对应的命令为node ./bin/www并在命令行执行。现在项目已经启动成功了,可以打开浏览器输入localhost:3000查看一下,但是本次侧重点在于项目启动时候发生的完整的过程。所以……

那么我们需要看一下 ./bin/www 文件

#!/usr/bin/env node/**
 * Module dependencies.
 */var app = require('../app');var debug = require('debug')('test:server');var http = require('http');/**
 * Get port from environment and store in Express.
 */var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);/**
 * Create HTTP server.
 */var server = http.createServer(app);/**
 * Listen on provided port, on all network interfaces.
 */server.listen(port);
server.on('error', onError);
server.on('listening', onListening);/**
 * Normalize a port into a number, string, or false.
 */function normalizePort(val) {  var port = parseInt(val, 10);  if (isNaN(port)) {    // named pipe
    return val;
  }  if (port >= 0) {    // port number
    return port;
  }  return false;
}/**
 * Event listener for HTTP server "error" event.
 */function onError(error) {  if (error.syscall !== 'listen') {    throw error;
  }  var bind = typeof port === 'string'
    ? 'Pipe ' + port
    : 'Port ' + port;  // handle specific listen errors with friendly messages
  switch (error.code) {    case 'EACCES':      console.error(bind + ' requires elevated privileges');
      process.exit(1);      break;    case 'EADDRINUSE':      console.error(bind + ' is already in use');
      process.exit(1);      break;    default:      throw error;
  }
}/**
 * Event listener for HTTP server "listening" event.
 */function onListening() {  var addr = server.address();  var bind = typeof addr === 'string'
    ? 'pipe ' + addr
    : 'port ' + addr.port;
  debug('Listening on ' + bind);
}

通过 require() 来引入本地安装的包,这里加载了三个包 app、debug 、http ,主要关注一下app.js

var createError = require('http-errors');var express = require('express');var path = require('path');var cookieParser = require('cookie-parser');var logger = require('morgan');var indexRouter = require('./routes/index');var usersRouter = require('./routes/users');var app = express();// view engine setupapp.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/users', usersRouter);// catch 404 and forward to error handlerapp.use(function(req, res, next) {
  next(createError(404));
});// error handlerapp.use(function(err, req, res, next) {  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

可以看到app加载的为该项目基础组件,核心为 var app = express(); 看一下express.js

var bodyParser = require('body-parser')var EventEmitter = require('events').EventEmitter;var mixin = require('merge-descriptors');var proto = require('./application');var Route = require('./router/route');var Router = require('./router');var req = require('./request');var res = require('./response');/**
 * Expose `createApplication()`.
 */exports = module.exports = createApplication;/**
 * Create an express application.
 *
 * @return {Function}
 * @api public
 */function createApplication() {  var app = function(req, res, next) {
    app.handle(req, res, next);
  };

  mixin(app, EventEmitter.prototype, false);
  mixin(app, proto, false);  // expose the prototype that will get set on requests
  app.request = Object.create(req, {    app: { configurable: true, enumerable: true, writable: true, value: app }
  })  // expose the prototype that will get set on responses
  app.response = Object.create(res, {    app: { configurable: true, enumerable: true, writable: true, value: app }
  })

  app.init();  return app;
}/**
 * Expose the prototypes.
*/exports.application = proto;
exports.request = req;
exports.response = res;/**
 * Expose constructors.
 */exports.Route = Route;
exports.Router = Router;/**
 * Expose middleware
 */exports.json = bodyParser.json
exports.query = require('./middleware/query');
exports.static = require('serve-static');
exports.urlencoded = bodyParser.urlencoded

将app用到的中间件等暴露出来,所以 app = express() 实际上就是加载应用基础组件,生成项目的根节点(当然这个节点是复合的)。所以在app.js中,进行的是加载应用基础组件并进行应用设置初始化(包含了视图目录设置、页面模板引擎设置、中间件的设置、静态资源目录设置以及错误捕捉相关内容)。

继续回到 www.js,同样将debug配置、http相关组件进行加载。继而是默认端口号的设定。通过http.createServer(app),创建应用服务相关信息,并对端口进行监听。

浏览器输入localhost:3000

webp

express框架项目跑起来了.png

控制台显示:

GET / 304 3.389 ms - -
GET /stylesheets/style.css 304 0.680 ms - -

说明在以GET方式请求URI为"/"的资源,并以GET方式请求/stylesheets/style.css资源,这个发生在app.js中我们设定的资源请求。

var indexRouter = require('./routes/index');var usersRouter = require('./routes/users');

app.use('/', indexRouter);
app.use('/users', usersRouter);

我们来看下 ./routes/index 文件

var express = require('express');var router = express.Router();/* GET home page. */router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});module.exports = router;

即定义网站主页的路由

使用 express.Router 类创建模块化、可挂载的路由句柄(简称:路由模块)。Router 实例是一个完整的中间件和路由系统。最终,在应用中加载路由模块app.use('/', indexRouter);

通过 res.render 对视图进行渲染,上述为对index.ejs的渲染。ps:在app.js中已经设定了views的所在目录。语法:res.render(view [,locals] [,callback])

  • locals 定义视图的局部变量。局部变量cache启用视图缓存。将其设置为true,以在开发期间缓存视图; 默认情况下,生产中启用了视图缓存。

  • callback 定义回调函数。如果存在,则该方法返回可能的错误和异常信息,将不执行自动相应。发生错误时,该方法在next(err)内部调用

示例:

// send the rendered view to the clientres.render('index');// if a callback is specified, the rendered HTML string has to be sent explicitlyres.render('index', function(err, html) {
  res.send(html);
});// pass a local variable to the viewres.render('user', { name: 'Tobi' }, function(err, html) {  // ...});

webp

res响应方法.png


区别:路由句柄

为请求处理提供多个回调函数,其行为类似 中间件。唯一的区别是这些回调函数有可能调用 next('route') 方法跳至下一个同路由的回调函数而略过其他路由回调函数。路由句柄有多种形式,可以是一个函数、一个函数数组,或者是两者混合。

使用一个回调函数处理路由:

app.get('/example/a', function (req, res) {
  res.send('Hello from A!');
});

使用多个回调函数处理路由(记得指定 next 对象):

app.get('/example/b', function (req, res, next) {  console.log('response will be sent by the next function ...');
  next();
}, function (req, res) {
  res.send('Hello from B!');
});

使用回调函数数组处理路由:

var cb0 = function (req, res, next) {  console.log('CB0');
  next();
}var cb1 = function (req, res, next) {  console.log('CB1');
  next();
}var cb2 = function (req, res) {
  res.send('Hello from C!');
}
app.get('/example/c', [cb0, cb1, cb2]);

混合使用函数和函数数组处理路由:

var cb0 = function (req, res, next) {  console.log('CB0');
  next();
}var cb1 = function (req, res, next) {  console.log('CB1');
  next();
}
app.get('/example/d', [cb0, cb1], function (req, res, next) {  console.log('response will be sent by the next function ...');
  next();
}, function (req, res) {
  res.send('Hello from D!');
});

区别:路由方法

一个路由示例:

var express = require('express');var app = express();// respond with "hello world" when a GET   request is made to the homepageapp.get('/', function(req, res) {
  res.send('hello world');
});



作者:miku设定
链接:https://www.jianshu.com/p/f3896653bbb3


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消