全栈开发入门实战:后台管理系统

本文首发于 GitChat 平台,免费 Chat,连接:全栈开发入门实战:后台管理系统javascript


感谢你打开了这篇 Chat,在阅读以前,须要让你了解一些事情。css

第一,本 Chat 虽然免费,不表明没有价值,我会将我的全栈开发的经历叙述给你,但愿对你有一些帮助;
第二,文中所使用的技术栈并不是最新,也并不是最优。后台管理系统更可能是 2B 端的产品,一般是业务优先。本 Chat 的目的是为了让你可以快速上手全栈开发。
第三,本 Chat 虽然名为全栈开发,可是不会带你作完一个完整的后台管理系统项目。一是因为篇幅有限,二是因为时间关系,我的精力也有限。html

正文

本 Chat 的内容,正如 Chat 简介中所描述,将分为如下 5 大块:前端

  1. 开发准备
  2. 前台样式
  3. 数据库链接
  4. 先后台交互
  5. 线上部署

你可能会发现,好像不知道要作什么,没错,后台管理系统通常都是企业内部定制开发,一般是对业务的数据管理,具体作什么功能由业务决定,但多数功能都是围绕着表格或者表单。java

上面列举的仅仅是全栈开发的大体流程。首先,要作一些准备工做,例如:开发环境、编辑器环境以及依赖包配置等等工做;其次,咱们要选定一个后台模版样式,快速套用,实现业务功能。(固然,你要本身开发也行,但不建议这么作,除非为了学习);而后,根据业务作数据库的设计,编写后台数据处理逻辑,以及先后台数据交互等功能;最后,测试并部署上线。node

这里的示例,将实现一个学生数据的在线管理需求,其实就是一个在线表格,包括添加,删除功能,系统层面包括登陆退出功能。麻雀虽小,五脏俱全,总体架子搭好了,再在上面添加功能就简单多了。好了,如今就开始全栈之旅吧。mysql

开发准备

启动项目

首先要作的是,开发环境的安装,这里就很少说了,关于 Node 环境的安装,默认你已经搞定了。nginx

既然采用 Express 做为 Web 框架,Express 也是要安装的,有了 Node 环境,安装 Express 就简单多了。咱们直接上手 Express 的脚手架,全栈开发关键要速度。git

npm install express-generator -g

必定记得是全局安装。安装完成以后,输入 express -h 能够查看帮助。这里选用 ejs 模版引擎,为何?由于我顺手而已,这个不重要。找个合适的目录,运行下面命令:es6

express -e node-web-fullstack-demo

生成项目目录以后,首先要安装依赖,以下命令:

cd example-node-web-fullstack
npm install

等待安装完成,咱们就能够启动项目了,使用命令 npm start ,去浏览器中,打开网址:http://localhost:3000,看到写着 Express 的首页,表明你的项目启动成功了。

编辑器环境配置

一个好的编码环境,可让你项目开发效率加倍。

首先介绍一个编辑器配置 EditorConfig,这是一个编辑器的小工具。它有什么做用呢?简而言之,就是让你能够在不一样的编辑器上,得到相同的编码风格,例如:空格缩进仍是 Tab 缩进?缩进几个空格?

你可能以为诧异,这个不是在编辑器上设置就能够了吗?没错,假设你从始至终都是在同一个电脑同一个编辑器上编码,那么能够忽略它。若是存在多电脑配合,亦或是多个编辑器配合,那么它就是神器。它几乎支持全部的主流编辑器,不用单独去编辑器中设置,配置文件就在项目中,用那个编辑器打开项目,都能得到一致的编码风格。

使用方法很简单,在根目录中新建 .editorconfig 文件便可。

# EditorConfig is awesome: https://EditorConfig.org

# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true

# Matches multiple files with brace expansion notation
# Set default charset
[*.*]
charset = utf-8

# 4 space indentation
[*.js]
indent_style = space
indent_size = 4

# Indentation override for all JS under lib directory
[views/**.ejs]
indent_style = space
indent_size = 2

上面这个示例,设置了 js, ejs 的缩进,并经过 insert_final_newline 设置了文档末尾默认插入一个空行。还有一些有意思配置,建议你查看一下官方文档,就明白了,很是好用的一个小插件。

代码检查工具

使用 js 编码,建议最好使用一款代码检查的工具,否则写到后面就会很尴尬,每每一个小的语法错误,会让你抓狂很久。代码检查工具推荐 ESLint

使用方法也很简单,首先安装要它,npm install eslint --save-dev,代码检查工具一般只须要安装在开发依赖里便可。紧接着你应该设置一个配置文件:./node_modules/.bin/eslint --init,而后,你就能够在项目根目录下运行 ESLint:./node_modules/.bin/eslint yourfile.js 用来校验代码了。若是你使用的是 IDE 编码,通常都会有 ESLint 的插件来校验代码,例如在 VS Code 中安装 eslint 插件,就能够实时校验正在编辑的文件了。

配置文件里的设置参数就多了,建议自行查看官方文档。以下示例:

{
    "rules": {
        "semi": ["error", "always"],
        "no-undef": "off",
        "no-unused-vars": "off",
        "no-console": "off"
    },
    "env": {
        "node": true,
        "es6": true
    },
    "extends": "eslint:recommended",
    "globals": {
        "Atomics": "readonly",
        "SharedArrayBuffer": "readonly"
    },
    "parserOptions": {
        "ecmaVersion": 2018,
        "sourceType": "module"
    }
}

有时候你可能会以为很烦,默认推荐的 eslint 校验规则管得太多了,上述中我就手动关掉了「不容许使用 console」「不容许存在未定义变量」等配置。有些人可能干脆关掉了校验,可是,我仍是强烈建议启用,毕竟规范的编码效率会更高。

前台样式

前面作了那么多工做,但彷佛一点效果也看不到,别着急,这一部分的内容,就能让你直接看到效果。

前端的工做实际上是很是费时的,它包括页面设计及布局等等,每每一个页面光调样式就要花费很长的时间。假如你是想快速搭建一个后台管理系统,不建议本身去写前端页面代码。颇有可能,页面样式尚未出来,就已经放弃了。除非你是单纯想要学习。

建议的作法是,快速套用一个前端模版库,将大致的页面结构搭建出来,而后在上面添加业务功能。 这里采用 AdminLTE 这套模版库,大概效果图以下:

它是基于 bootstrap3 的后台模版,几乎再也不须要作 CSS 的开发,一会儿就解决了前端界面设计的大问题。下面来看看如何套用它。

下载安装

推荐采用 bower 包管理器来安装,安装命令:bower install adminlte 等待下载就能够了。下载完成后,你会发现bower_components/ 目录里多了好多目录,这些都是它的依赖包。

接下来,咱们还要将整个项目下载下来,获得它的示例代码,打开其中的 index.html 文件,分析一下它的页面结构,好便于后面的拆解。以下图:

拆解页面结构

这是后台首页的一个基本结构,分为:main-headermain-sidebarcontent-wrappermain-footercontrol-sidebar ,因而,咱们按照这几大模块,拆分页面,分别保存为 layout 模版。在项目 views 下新建两个目录:backend 以及 frontend ,分别对应后台页面以及前台页面。

backend 里再新建 layout 目录以及 index.ejs 文件,layout 中用来保存通用的页面代码,index.ejs 则表明后台首页。拆解完的 index.ejs 代码以下:

<% include ./layout/head.ejs %>
<!-- custom css files -->

<% include ./layout/header.ejs %>
<% include ./layout/main-sidebar.ejs %>

<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
  ... 页面代码 ...
</div>

<% include ./layout/control-sidebar.ejs %>
<% include ./layout/footer.ejs %>

<!-- custom javascript files -->

<% include ./layout/foot.ejs %>

相信你一眼就能看出上面这段代码的意思,将一个页面拆分后,再重组,这就是最终的首页。其余的全部页面均可以经过这样的方式,进行重组,咱们要写的代码,仅仅只是修改 <div class="content-wrapper"></div> 里的页面便可。

配置静态资源目录

如今的页面,咱们还不能访问,由于页面中连接的 CSS 以及 JS 文件路径都不对,模版里引用的都是相对路径,咱们须要将它改成本项目里的绝对路径。打开 layout/head.ejs 文件,部分代码以下:

<!-- Bootstrap 3.3.7 -->
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.min.css">
<!-- Font Awesome -->
<link rel="stylesheet" href="bower_components/font-awesome/css/font-awesome.min.css">
<!-- Ionicons -->
<link rel="stylesheet" href="bower_components/Ionicons/css/ionicons.min.css">
<!-- Theme style -->
<link rel="stylesheet" href="dist/css/AdminLTE.min.css">

套用模块的好处就是,这些连接库的地址基本上没太大的变化,咱们只须要修改一下根目录就能够了。修改以前,咱们须要先配置一下项目的静态资源目录,打开项目根目录下的 app.js 文件,加上一行代码以下:

...
app.use(express.static(path.join(__dirname, 'public')));
// 新添静态资源目录 bower_componennts
app.use(express.static(path.join(__dirname, 'bower_components')));
...

新加的这句代码意思是,将项目中 bower_components/ 目录设置为静态资源访问的根目录,那么以上的那些静态资源,咱们就知道怎么引入了,修改以下:

<!-- Bootstrap 3.3.7 -->
<link rel="stylesheet" href="/bootstrap/dist/css/bootstrap.min.css">
<!-- Font Awesome -->
<link rel="stylesheet" href="/font-awesome/css/font-awesome.min.css">
<!-- Ionicons -->
<link rel="stylesheet" href="/Ionicons/css/ionicons.min.css">
<!-- Theme style -->
<link rel="stylesheet" href="/admin-lte/dist/css/AdminLTE.min.css">

一样的,JS 文件的引用路径也要进行修改,打开 footer.ejs 文件,以 bower_components/ 为根目录,修改对应的 JS 文件路径便可。好了,该页面全部引用的静态资源路径,都已经修改正确了,接下来就是编写页面路由了。

PS. 页面中的 JS 库并不是都与本身的项目相匹配,后续要根据实际状况,进行增减。

编写页面路由

页面准备好了,咱们须要去编写访问路由,在这点上,Node 就与 PHP 和 Java 有所区别了,Node Web 开发不须要再搭建一个 Web 服务器,PHP 须要 Apache ,Java 须要 Tomcat,而 Node 把它交给你,让你去自定义。咱们打开项目 routes/index.js 路由文件,编写代码以下:

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

// My backend index page
router.get('/backend', function(req, res, next){
  res.render('backend/index.ejs', {});
});

module.exports = router;

咱们加了一个路由 /backend ,渲染了上面准备好的 index.ejs 页面,好了,咱们重启一下项目,哦,对,若是已经安装了 supervisor ,不须要手动重启了,直接访问 http://localhost:3000/backend

细心的你可能发现,这个页面有些元素显示不正常,这是由于有些静态资源,咱们没有安装而致使的加载失败,这个无需担忧,由于实际页面还须要不少的时间去打磨,这个要根据实际业务来决定页面的内容,这里就不去展开了。

接下来的事情,就是改写页面代码了,总体的样式结构都有了,剩下就是对页面的删删减减了。

数据库链接

根据上面的内容,相信你可以添加完页面路由。当所有页面改写完毕以后,咱们就至关于获得了,一个静态的后台管理系统。要达到的效果就是:点击左侧相应的导航,能够实现页面跳转,可是没有实际的数据,页面中的数据都是写「死的」,由于咱们尚未链接数据库,接下来的工做就涉及到数据库的链接了。

设计数据表

无论你选择哪个数据库,首先都要作设计数据表,对于数据表的设计以及业务埋点等内容,均可以看成一本书来说,这里不会有那么详细的内容,来教你如何设计数据表,以及业务功能埋点。但有个建议须要提一下:不要着急去学习 SQL 语言,你可能会说,不学习 SQL 怎么设计数据表呢?怎么操做数据库呢?

这里须要强调的是,不是不须要学,而是开始的时候,不用着急去学。全栈开发重要的是快,否则你全栈干啥呢?在实际的场景中,业务才是最重要的。

首先打开你的 Excel(或在线表格应用) 把表的设计作出来再说。例以下图是我当时一个项目的数据表设计:

最重要的是要去作这个事情,而不是去学数据库,别担忧作得不够好,一次一次的实战会让你愈来愈熟练。作完这个工做,全栈开发基本算是完成了 40%,在这个过程当中,必定要深刻去分析业务流程,把该用到的不应用到的都要考虑进去,前期能够不作开发,可是必要的字段必定要预留。

编写数据库模块

一般状况下,最好找一个趁手的数据库 GUI 工具,把数据库以及相关的表建立出来。这一步作完以后就是编写链接数据数据的代码了,建议在项目的根目录中,新建名为 db 的目录,用来存放全部关于数据库操做的代码。

这里咱们选用 MySQL 数据库,Node 链接 MySQL 还须要安装一个数据库模块。在项目目录,执行命令 npm install --save mysql 便可,安装完成以后,就能够经过下面的示例,尝试链接数据库了。

var mysql = require('mysql');
var connection = mysql.createConnection({
  host: 'localhost',
  user: 'me',
  password : 'secret',
  database : 'my_db'
});
connection.connect();
connection.query('SELECT 1 + 1 AS solution', function(err, rows, fields) {
  if (err) throw err;
  console.log('The solution is: ', rows[0].solution);
});
connection.end();

这是官方提供的一个简单例子,从上面的例子能够得出:使用createConnection(option)方法建立一个链接对象,而后链接对象的connect()方法建立链接,最后使用query()方法执行SQL语句,返回结果做为回调函数的参数rows返回,rows为数组类型。

一般状况下,咱们会使用链接池进行链接,链接池具体实现原理就很少展开了,能够查看一下官方文档。下面直接给出使用示例。

// connect.js 使用getConnection方法
var mysql = require('mysql');
var config = require('./config.json');
// 将配置写入配置文件中
var pool = mysql.createPool(config.mysql);
exports.querySQL = function(sql, callback){
    pool.getConnection(function(err,conn){
        conn.query(sql,function(err,rows,fields){
            callback(err,rows,fields); 
            conn.release();   // 不要忘了释放
        });        
    });
}

使用的时候,直接使用querySQL方法便可,以下:

// db.js 查询用户信息
var connect = require('./connect.js');
exports.getUser = function(username,callback){
    var sql = 'select * from user where username = "' + username + '"';
    connect.querySQL(sql,function(err,rows,fields){
        callback(err,rows,fields);        
    });
};

上面示例中,直接将 sql 语句写进代码中了,经过执行 sql 语句来得到数据库数据。

这种方式优势是比较直观,可是缺点就太多了。不安全拼接字符串容易错可扩展性差等等。在实际项目中,咱们每每都会选用一款 ORM 框架来链接数据库。

使用 ORM 框架

关于 Node 使用 ORM 框架的介绍,我以前单独写了一篇 Chat,一样是免费的,这里我就再也不赘述了。

请点击连接查看:如何使用 Sequelize 框架快速进行 Node Web 开发

先后台交互

一般状况下,先后台交互的开发会涉及到多个部门多个岗位协做完成,除了先后台,近几年也分离出中台的概念,专门提供接口服务。

而对于全栈开发来讲,这个环节显然要简单得多了,虽然工做流程能够省,可是,必要的前中后台的框架仍是要有的,分离开的好处是尽可能下降功能耦合性,便于后期维护。

前台页面获取动态数据一般有的两种方式:

  • 一是,被动获取,页面被渲染,后台传参给前台;
  • 二是,主动获取,前台主动请求,异步获取数据。

后台传参方式

关于页面展现,前面已经讲过了,根据模块改写页面,而后编写路由,就能够进行访问了。但问题是,目前页面上的一些内容都是静态的,例如:站点的名称,Logo以及标题等等。

一般状况下,这些内容不该该是写死在页面中的,而应该是动态的,从后台传来的,甚至应该是写入数据库的,未来作个修改页面,就能够随时修改网站标题以及 Logo 等信息。

这里就涉及到后台传参的相关知识点了。Express 框架给咱们提供了三种传参的方式:

(1)应用级别的传参

使用 app.locals 定义参数。例如:在 app.js 文件中,routes(app); 语句以前加入 app.locals.hello = "Node",咱们在任何页面中,使用 hello 这个参数都是没问题的。如何使用?建议先了解下模版引擎的用法,这里是 ejs。

(2)路由级别的传参

使用 res.locals 定义参数。例以下面例子:

app.use(function (req, res, next) {
    res.locals.web = {
        title: 'STU INFO',
        name: '数据管理',
        desc: '学生数据管理',
        verifier: 0
    };
    res.locals.userInfo = {
        username: 'admin',
        title: '管理员'
    };
    res.locals.url = req.url.split('?')[0];
    next();
});

经过一个中间件,将站点信息写入 res.locals 对象中,因而咱们在对应的页面中,就可使用 web 以及 userInfo 等对象参数了。例以下面登录页面部分代码(注意:这里是 ejs 模版引擎的写法):

<div class="login-logo">
    <a href="/" title="<%= web.desc %>"><b><%= web.title %> </b><%= web.name %></a>
</div>

(3)页面级别的传参

使用 res.render() 方法定义参数,这应该是最经常使用的一种方式,例如页面的 title 属性,每一个页面都是不同的,因此在渲染模版的时候,传入 title 参数,是最合适的。例如登录页面的路由:

router.get('/login', function(req, res, next) {
  res.render('login', {
      pageInfo:{
          title: '登录页面',
      }
  });
});

示例中,定义了一个 pageInfo 的对象,将其看成参数传输到了前台。

前台异步获取

前台主动获取数据的例子不少,一般都是经过 Ajax 异步的方式。典型的例子就是在线表格功能,由于后台管理系统几乎离不开在线表格的功能。

关于在线表格的内容,我以前写过一篇 Chat,建议阅读,一样是免费的。连接:如何快速将线下表格数据线上化

这里我重点介绍下,后台数据如何产出?首先要明白,数据确定从数据库中得到,咱们编写数据库 API 的时候,就应该将对应的方法封装好,前台经过访问数据路由,路由调用对应的数据库 API 便可得到数据,并返回前台。

下面看下路由的代码示例:

// api
const studentHandler = require('../../db/handler/studentHandler.js');
// 当前学生列表
router.get('/list', function(req, res, next) {
    let state = 0 ; // 表明当前在校
    studentHandler.getStudentList(state, function(p){
        res.send(p);
    });
});

对应的数据 API 封装在了 db/handler 目录中,路由经过调用 getStudentList 方法,便可得到对应的学生列表,并经过 res.send(p) 方法返回给前台。

再来看看数据库 API 是如何编写的?代码示例以下:

const { Student } = require('../relation.js');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
module.exports = {
    // 根据状态查询学生列表
    getStudentList: function(state, callback){
        Student.findAll({
            where: {
                state: state
            }
        }).then(function(data){
            callback(data);
        }).catch(function(err){
            callback(err);
        });
    },
    // 批量添加学生
    uploadStudent: function(data, callback){
        Student.bulkCreate(data).then(function(){
            callback();
        }).catch(function(err){
            callback(err);
        });
    }
};

重点看下 module.exports 对外的接口,篇幅缘由,这里只提供了两个调用方法。若是这里的代码看不明白,请翻回去查看「使用 ORM 框架」这节内容中,推荐的那篇 Chat。

一般状况下,大部分功能都是经过「后台传参」以及「异步获取」这两种方式配合来实现的。

有时候甚至是取代关系,你可能会发现有些后台管理系统,进去以后,从始至终 URL 就没有变过,这一类的后台管理系统的功能实现,所有采用前台异步获取的方式。我本人不太推荐这种方式,由于想要单独展示某个页面的时候,就很是的尴尬,同时也很是不利于页面维护。

线上部署

当项目开发完成,本地测试完毕以后。下一步,就是部署上线了。上线部署本来是个比较繁琐的工做流程,特别针对多系统联动的状况。这里咱们不考虑复杂的状况,单纯讲独立开发的后台管理系统如何快速部署到线上?

你可能会说,这有啥好讲的,把本地代码同步到服务器上,而后在服务器上启动不就得了。没错,大致是这么个意思,可是其中仍是有一些点须要注意的。

使用进程管理工具启动项目

这里我推荐使用 supervisor,也没其余缘由,顺手而已。它是一个进程管理工具,当线上的 Web 应用崩溃的时候,它能够帮助你从新启动应用,让应用一直保持线上状态。

安装方法很简单,在命令行中,输入 npm install -g supervisor 便可。安装完成以后,咱们须要修改一下项目的启动命令,打开 package.json 文件,编辑以下:

...
"scripts": {
  "start": "supervisor --harmony -i views/,public/ ./bin/www",
},
...

supervisor --help 查看使用方式,以上命令,配置了 --harmony 模式启动 Node ,同时,使用 -i 参数忽略了 views/ 以及 public/ 目录。

修改完成后,咱们依然使用 npm start 启动项目,不同的是,当咱们修改了除 views/ 以及 public/ 目录之外的文件后,服务将会自动重启,以确保线上一直运行的是最新版项目。

使用 Git 同步代码

将代码从本地拷贝到线上,有不少种办法。这里推荐使用 Git 工具,若是没有本身的 Git 服务器,那么就使用 GitHub 类公共 Git 服务平台。大可没必要担忧代码泄露的问题,GitHub 不是已经提供私有仓库免费的功能了嘛,没事,放心用吧。

具体操做我就再也不赘述了。

使用 Nginx 反向代理服务器

为了确保性能,不建议直接在线上环境,经过 npm 或 supervisor 直接启动项目,虽然 node 自己的性能并不差,建议仍是在 node 服务中间,再加一层 Web 服务器看成方向代理,与 Node 最配的固然是 Nginx 了。

安装 Nginx 没必要多说,下面贴出 Nginx 对应的配置文件内容供参考。

server {
    listen       80;
    server_name xxx.com

    charset utf-8;

    #此处配置你的访问日志,请手动建立该目录:
    access_log  /var/log/nginx/js/access.log;

    location / {
        try_files /_not_exists_ @backend;
    }

    # 这里为具体的服务代理配置
    location @backend {
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host            $http_host;
        proxy_set_header   X-Forwarded-Proto $scheme;

        #此处配置程序的地址和端口号
        proxy_pass http://127.0.0.1:8080;
    }
}

关键记住最后一句:proxy_pass 代理的是哪一个程序地址和端口号。

最后,启动 Node 服务,启动 Nginx 服务,而后访问对应服务地址,大功告成。

总结

这篇 Chat 从 5 个方面讲述了全栈开发一个后台管理系统的大体流程以及部分实战内容。因为内容量远超出了预期,在写的过程当中,不断的删减了不少篇幅。使得 Chat 的总体效果,没有达到个人预期。

也许这个内容,经过一篇 Chat 确实很难容下吧。因此,我计划写完整个课程,内容包括 Node 基础、进阶应用以及Web 实战等。从最基本的原理开始介绍,到最后全栈开发实战。

课程以开源免费的形式公开,GitHub 仓库地址:Node 全栈开发入门课程,更新频率不定,欢迎关注。

同时,也能够关注个人微信公众号:我的学习,以便了解最新进展。

谢谢观看~

相关文章
相关标签/搜索