博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Node中间层实践(二)——搭建项目框架
阅读量:7039 次
发布时间:2019-06-28

本文共 6640 字,大约阅读时间需要 22 分钟。

版权声明:更多文章请访问我的个人站 ,转载请注明出处。

项目地址:

脚手架?

使用过angular2,vue2的同学都清楚,官方推荐的安装方式是通过专用的angular-cli、vue-cli来快速搭建一个由webpack编译、打包的项目框架。

受这两个工具的启发,我在项目之初开发一个基于webpack的项目框架:

  • 使用express搭载webpack-dev-middleware和webpack-hot-middleware进行热加载
  • 内置dev(开发环境)和 production(生产环境)两种启动方式

技术选型

node开发框架

express和koa二选一。

  • express的更贴近Web Framework这一概念;
  • express功能极简,完全是由路由和中间件构成一个的 web 开发框架:从本质上来说,一个 Express 应用就是在调用各种中间件;
  • koa使用co作为底层运行框架,使用它完全忘记了什么时候回调函数或者callbackshell;
  • express历史更久,装机量大,文档完整,第三方的中间件也很多;

我选择了express作为node开发框架。

模板引擎

由于之前用node写过restFul的网站(详见我的博客-),比较熟悉jade,所以这次直接选择了pug作为服务器端模板引擎。

pug是一个比较灵活的服务器端模板,express配置起来也很简单,在根目录的app.js文件中配置

// 设置模板引擎的类型
app.set('view engine', 'pug');
// 设置模板文件路径
app.set('views', path.resolve(__dirname, 'src/Views'));

这样,在express渲染时候使用 res.render('Home/index', {}),指定对应的页面模板路径就行了,第二个参数 是要在pug页面使用的数据内容,json格式。

中间层与后端的异步通信

其实就是个ajax库,所以我选择了axios,vue2官方推荐的ajax库,内置的axios.all(),可以在中间层代理多个后端请求一并返回,轻松实现Bigpipe。

模块化

因为是网站前台页面,需要考虑SEO,无法使用前端框架,所以换个思路,使用webpack+es6/AMD来实现模块化。

于是,使用webpack作为打包机+View层的模块化,从前端层面实现了模块化和组件化
es6用在Contorl层的模块化,在node中间层实现了模块化

环境配置(启动文件)

app.js作为项目的启动文件,是express的配置文件,同时也是运行开发环境 / 生产环境的配置文件。

通过node启动时配置的命令参数,由app.js接收参数,然后运行对应的环境

我们需要安装cross-env这个插件

npm i --save-dev cross-env

在package.json的scripts中配置:

..."scripts": {    "start": "cross-env NODE_ENV=production node app.js",    "dev": "cross-env NODE_ENV=dev supervisor -w app.js app",    "build": "webpack --config build/webpack.prod.config.js"  }...

这样,使用 process.env.NODE_ENV 就可以获取node启动的环境参数。

这里我配置了 开发环境(dev) 和 生产环境(production)

  • 开发环境: 不压缩代码,开启devtool生成source-map,便于调试代码;
  • 生产环境: 是用webpack进行打包、压缩、混淆操作,将最终完整的代码(应用)输出至dist目录中,然后再启动node服务器,运行应用;

上述代码中还用到了 supervisor插件,这是监听node配置文件(app.js)的更改,并自动重启node的。

npm i --save-dev supervisor

前端层面使用webpack-dev-middleware和webpack-hot-middleware,实现了热加载。

这样,从中间层到前端都实现了热加载。

node中间层配置

在根目录下新建了config文件夹,将一些常用的配置/参数、方法抽出来,写在了config/config.js 和 config/common.js中。

例如,中间层启动的端口号、应用的根目录(path.resolve(__dirname,'../'))、后端服务器的ip、前端SEO的description和keywords等参数,都放在了config.js中,通过AMD规范在所有使用的文件中统一引入,修改起来也方便。

app.js

var express = require('express');var cookieParser = require('cookie-parser');var path = require('path');var localOptions = require('./build/localOptions');var config = require('./config/config');var bodyParser = require('body-parser');var auth = require('./middleware/auth');var log4js = require('./config/log4js');process.env.NODE_ENV = process.env.NODE_ENV ? process.env.NODE_ENV : 'production';var isDev = process.env.NODE_ENV === 'dev';var app = express();var port = config.port;app.set('view engine', 'pug');// 设置模板文件路径if (isDev){    app.set('views', path.resolve(__dirname, 'src/Views'));}else {    app.set('views', path.resolve(__dirname, 'dist/Views'));}// app.locals定义的键值对能在模板中直接访问app.locals.env = process.env.NODE_ENV || 'dev';app.locals.reload = true;app.use(cookieParser());app.use(bodyParser.urlencoded({extended: false}));if (isDev) {    // app.locals.pretty = true;    // 开发环境,静态文件使用热插拔    var webpack = require('webpack');    var webpackDevMiddleware = require('webpack-dev-middleware');    var webpackHotMiddleware = require('webpack-hot-middleware');    var webpackDevConfig = require('./build/webpack.dev.config.js');    var compiler = webpack(webpackDevConfig);    // 热插拔    app.use(webpackDevMiddleware(compiler, {        publicPath: webpackDevConfig.output.publicPath,        noInfo: true,        stats: 'errors-only'    }))    app.use(webpackHotMiddleware(compiler, {        heartbeat: 1000,        noInfo: true,    }));    // 不能热插拔的往下执行    var reload = require('reload');    var http = require('http');    var server = http.createServer(app);    // reload(server, app);    reload(app);    server.listen(port, () => {        console.log('App【dev】 is now running on port ' + port + '!');    });    // 静态目录设置必须有,开发环境读取的vendor.js不是内存文件;    // 静态目录设置必须放在reload后面,避免页面引入reload.js报错    app.use(express.static(path.join(config.root, 'src')));    app.use('/', require(path.join(config.configRoot,'/routes')));    }else {    // 线上环境不需要监听,只需开启node服务即可    // 设置node的静态文件目录    app.use(express.static(path.join(config.root, 'dist')));    app.use('/',require(path.join(config.configRoot,'/routes')));    // app.listen(process.env.PORT, () => {    app.listen(port, () => {        console.log('App【production】 is now running on port ' + port + '!');    })}// 捕捉 404错误 传给 error路由app.use('*', auth, (req, res, next) => {    let err = new Error('Not Found');    err.status = 404;    next(err);});// 捕获 error,跳转至error页面app.use((err, req, res, next) => {    const sc = err.status || 500;    if(err.status == 405){        res.redirect(config.cndesignOrigin + '/Home/GuideAuthentication');        return;    }    res.status(sc);    // 此处需写入日志    log4js.error('\r\n Status: '+ sc + "\r\n Message: " + err.message + "\r\n Href: " + localOptions.baseUrl + req.originalUrl + "\r\n User-agent: " + req.headers['user-agent']);    res.render('Error/404', {        error: err,        status: sc,        message: err.message,        userInfo: req.userInfo_ || { hasLogin: false }    });});

注意Error Handle的挂载的顺序,一般挂载到app.use()下,且放在最后。

因为Error Handle的参数为4个,第一个参数err可以通过路由中的next(err)抛出,这样所有的路由都可以随时抛出error,让Error Handle来捕获。

来看一下目录结构:

App├─ .babelrc                         // babel的配置├─ ReadMe                           ├─ app.js                           // 启动文件├─ build                            // webpack的配置文件│    ├─ entrys.js                   // webpack打包js和css的入口文件│    ├─ localOptions.js             // 前端开发者本地配置文件,本地ip等站点配置│    ├─ postcss.config.js           // postcss的配置文件│    ├─ webpack.dev.config.js       // 开发环境的webpack打包配置│    ├─ webpack.dll.config.js       // webpack.DllReferencePlugin插件的配置文件│    └─ webpack.prod.config.js      // 生产环境的webpack打包配置├─ config                           // Model层文件:包括node服务器的配置、路由和代理接口│    ├─ common.js                   // 中间层逻辑处理的公用方法│    ├─ config.js                   // node服务器的配置│    └─ routes                      // 路由和代理接口配置│           ├─ default.js│           ├─ designers.js│           ├─ home.js│           └─ index.js             // 路由和接口配置的入口文件├─ package.json└─ src                              // View层文件       ├─ Components                // 公用组件       │    ├─ base       │    ├─ home       │    ├─ index       │    ├─ message       │    ├─ modals       │    ├─ page       ├─ Views                     // 页面       │    ├─ Default       │    ├─ Error       │    ├─ Home       │    ├─ Include       │    ├─ Messages       │    └─ Shared       └─ assets                    // 静态文件目录              ├─ Jcrop              ├─ es5-shim-master              ├─ images              ├─ jquery-1.10.2.min.js              ├─ jquery-ui-1.8.24.min.js              └─ layui

欢迎继续关注本博的更新

你可能感兴趣的文章
Objective-C实例变量
查看>>
容器技术毫无悬念主宰本次Amazon Re:Invent
查看>>
内存泄露从入门到精通三部曲之常见原因与用户实践
查看>>
js基础笔记-类型装换
查看>>
win7-x64上MySql的初次安装
查看>>
自定义初学1——扩展系统控件
查看>>
docker进阶与实战 1 docker简介
查看>>
日常环境莫名宕机的处理
查看>>
获5.3亿美金融资,亚马逊、红杉入局,自动驾驶“梦之队”Aurora还藏了哪些秘招? ...
查看>>
PreparedStatement 基于mysql数据库做分页查询和统计查询 ...
查看>>
机器人军团和傅盛的答案
查看>>
阿里云年营收突破200亿 成亚洲最大云服务商
查看>>
移动数据安全怎么防护?
查看>>
新功能: 阿里云爬虫风险管理利器 - 实时日志分析 (一)
查看>>
如何优化Spring Cloud微服务注册中心架构?
查看>>
C#面向服务WebService从入门到精通
查看>>
PHP实现全家、好德各大便利店大润发超市饭店微信扫码支付代码
查看>>
一个简单的弹性返回顶部JS代码实现介绍
查看>>
#region的快捷键 小工具的快捷键
查看>>
Unicode in RAD Studio
查看>>