新建的基于ejs模版的NodeJS项目.png
按照常规,去执行npm start
侧边栏左下角的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
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) { // ...});
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