Galan小屋

welcome my blog

项目完整地址

前端界面制作

用vue-cli构建工具搭建一个简单的登录页面,一个列表页面

列表页面功能有删除,编辑,添加

简单介绍说下路由配置,做登录拦截

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

let router = new Router({
  routes: [
    {
      path: '/login',
      component: resolve => require(['../pages/Login.vue'], resolve)
    },
    {
      path: '/list',
      component: resolve => require(['../pages/List.vue'], resolve)
    },
    {
      path: '/',
      redirect: '/login'
    },
  ]
})

function getCookie(name){
   var arr = document.cookie.match(new RegExp("(^| )"+name+"=([^;]*)(;|$)"));
   if(arr != null){
     return unescape(arr[2]); 
   }else{
     return null;
   }
} 

router.beforeEach((to, from, next) => {
  const token = getCookie('username');
  if (to.path === '/login') { // 如果是跳转到登录页的
    next()
  } else {
    if (token !== 'null' && token !== null) {
      next() // 如果有token就正常转向
    } else {
      next('/login') // 否则跳转回登录页
    }
  }
})

export default router

后端koa以及mysql环境搭建

window上安装mysql,安装步骤可以自己去了解一下,安装完MySQL之后你需要设定一下root账户的密码。保证安全性。

因为我对MySQL的SQL语句不是很熟悉,所以我需要一个可视化的工具来操作MySQL。Windows上我用的是navicat for mysql。

然后我们可以用这些可视化工具连上MySQL的server(默认端口是3306)之后,创建一个新的数据库,叫做todolist。(当然你也可以用SQL语句:CREATE DATABASE todolist,之后不再赘述)。

接着我们可以来开始创建数据表了。

我们需要创建待办事项表。表名是lists

Sequelize

跟数据库打交道的时候我们都需要一个好的操作数据库的工具,能够让我们用比较简单的方法来对数据库进行增删改查。

对于Mongodb来说大家熟悉的是Mongoose以及使用一个相对更简单点的Monk。

对于MySQL,我选用的是Sequelize,它支持多种关系型数据库(Sqlite、MySQL、Postgres等),

它的操作基本都能返回一个Promise对象,这样在Koa里面我们能够很方便地进行”同步”操作。

更多关于Sequelize的用法,可以参考官方文档,以及这两篇文章——Sequelize中文API文档Sequelize和MySQL对照

安装命令

npm install sequelize mysql


在server目录下的config目录下我们新建一个db.js,用于初始化Sequelize和数据库的连接。

// config/db.js
const Sequelize = require('sequelize'); // 引入sequelize

// todolist:对应的数据库名称 root:对应的数据库账号 123456:对应的数据库密码
const Todolist = new Sequelize('todolist', 'root', '123456', {
  host: '127.0.0.1',
  port: 3306,
  dialect: 'mysql',
  logging: false,
    freezeTableName: true,
    operatorsAliases: false,
  pool:{
      max:5,
      min:0,
      idle:10000
  }
})


module.exports = {
  Todolist // 将Todolist暴露出接口方便Model调用
}

定义list的表结构

// mysql/list.js
module.exports = function(sequelize, DataTypes){
  return sequelize.define('lists', {
    user_id: {
      type: DataTypes.INTEGER(11),
      allowNull: true
    },
    content: {
      type: DataTypes.CHAR(255),
      allowNull: true
    },
    status: {
      type: DataTypes.INTEGER(1),
      allowNull: true
    }
  },{
        timestamps: false
    });
}

接着我们去model文件夹里将数据库和表结构文件连接起来。在这个文件夹下新建一个list.js的文件。我们先来写一个查询用户id的东西。

通常我们要查询一个用户id为1的数据,会很自然的想到类似如下的写法:

const list = lists.findOne({ where: { id: 1} }); // 查询
console.log(list); // 输出结果

完整的list数据库操作


// model/list.js
const db = require('../config/db.js'), 
      listModel = '../mysql/list.js'; // 引入list的表结构
const TodolistDb = db.Todolist; // 引入数据库

const List = TodolistDb.import(listModel); // 用sequelize的import方法引入表结构,实例化了User。

const getUserById = async function(id){
  const userInfo = await List.findOne({
    where: {
      id: id
    }
  });
  return userInfo // 返回数据
}

//返回列表
const getList = async function(){
  const list = await List.findAll();
  return list
}

//删除一条
const removeList = async function(data){
  const result = await List.destroy({
    where: {
      id: data.id,
      user_id: data.userId
    }
  })
  return true
}

//创建一条
const addList = async function(data){
  const result = await List.create({
    user_id: data.user_id,
    content: data.content,
    status:1,
  })
  return true
}

//更新一条
const updateList = async function(data){
  const result = await List.update(
    {
      content:data.content
    },
    {
      where: {
        id: data.id,
        user_id: data.userId
      }
    }
  )
  return true
}

module.exports = {
  getList,
  removeList,
  addList,
  updateList
}

接着我们在controller写一个list的controller,来执行这个方法,并返回结果。


// controller/list.js
const list = require('../model/list.js');

const getList = async function(ctx){
  const data = ctx.params.id;//get 获取url里传过来的参数
  const result = await list.getList();
  ctx.body = {
      code: 0,
      msg: 'success',
      data: result
  }
}

const removeList = async function(ctx){
  const data = ctx.request.body//post koa-bodyparser中间件可以把koa2上下文的formData数据解析到ctx.request.body中
  const result = await list.removeList(data);
  ctx.body = {
    code: 0,
      msg: '删除成功!'
  }
}

const addList = async function(ctx){
  const data = ctx.request.body;//post
  const result = await list.addList(data);
  ctx.body = {
    code: 0,
      msg: '添加成功!'
  }
}

const updateList = async function(ctx){
  const data = ctx.request.body;//post
  const result = await list.updateList(data);
  ctx.body = {
    code: 0,
      msg: '修改成功!'
  }
}

module.exports = {
  getList,
  removeList,
  addList,
  updateList
}

接着我们在controller写一个login的controller,来实现登录,并返回结果。


// controller/login.js
const login = async function(ctx){
  const data = ctx.request.body;//post
  if(data.username != 'admin'){
    ctx.body = {
      code: 1,
        msg: '账号不正确!'
    }
    ctx.cookies.set('username','',{signed:false,maxAge:0})
    return;
  }
  if(data.password != 123){
    ctx.body = {
      code: 1,
        msg: '密码不正确!'
    }
    ctx.cookies.set('username','',{signed:false,maxAge:0})
    return;
  }
  ctx.cookies.set(
      'username', 
      'admin',
      {
        //domain: 'localhost',  // 写cookie所在的域名
        //path: '/',       // 写cookie所在的路径
        maxAge: 1 * 24 * 60 * 60 * 1000, // cookie有效时长 1天
        expires: new Date('2018-02-15'),  // cookie失效时间
        httpOnly: false,  // 是否只用于http请求中获取
        overwrite: false  // 是否允许重写
      }
    )
  ctx.body = {
    code: 0,
      msg: '登录成功!'
  }
}

module.exports = {
  login
}

写完这个还不能直接请求,因为我们还没有定义路由,请求经过Koa找不到这个路径是没有反应的。

在routes文件夹下写一个all.js的文件。

// routes/all.js
const list = require('../controller/list.js');
const login = require('../controller/login.js');  
const router = require('koa-router')();

router.post('/list/add', list.addList);//添加记录
router.post('/list/remove', list.removeList);//删除记录
router.post('/list/update', list.updateList);//更新记录
router.get('/list/get', list.getList);//获取记录

router.post('/login', login.login);//登录


module.exports = router; // 把router规则暴露出去

至此我们已经接近完成我们的第一个API了,还缺最后一步,将这个路由规则“挂载”到Koa上去。

回到根目录的app.js,改写如下:

const appKoa = new require('koa')
  , koa = require('koa-router')()
  , path =require('path')
  , serve = require('koa-static')
  , list = require('./routes/all.js'); // 引入路由

app = new appKoa();

// 静态文件serve在koa-router的其他规则之上 
app.use(serve(path.resolve('dist'))); // 将webpack打包好的项目目录作为Koa静态文件服务的目录

app.use(require('koa-bodyparser')());

app.on('error', function(err, ctx){
  console.log('server error', err);
});

koa.use('/api', list.routes()); // 挂载到koa-router上,同时会让所有的list的请求路径前面加上'/api'的请求路径。

app.use(koa.routes()); // 将路由规则挂载到Koa上。

app.listen(3000,() => {
  console.log('Koa is listening in 3000');
});

module.exports = app;

打开你的控制台,输入node app.js,一切运行正常没有报错的话,大功告成,我们的第一个API已经构建完成!

API Test

接口在跟跟前端对接之前,我们应该先进行一遍测试,防止出现问题。在测试接口的工具上,我推荐Postman,这个工具能够很好的模拟发送的各种请求,方便的查看响应结果,用来进行测试是最好不过了。也可以在chrome浏览器安装插件



nginx配置

真正部署到服务器的时候,我们肯定不会让大家输入域名:3000这样的方式让大家访问。所以需要用Nginx监听80端口,把访问我们指定域名的请求引导转发给Koa服务端。

在服务器etc/nginx/conf.d文件夹创建一个nodejs.conf

nginx默认会引入这个文件夹下的配置

    #目录 ect/nginx/conf.d/nodejs.conf
    upstream nodejs {  
      server 127.0.0.1:3000;  
    }  
    server {  
            listen       80;  
            server_name  www.galan.cn;  
      
            #charset koi8-r;  
      
            #access_log  logs/host.access.log  main;  
            
            #缓存30
            location ~* ^.+\.(css|js|txt|xml|swf|wav)$ {
                access_log   off;
                expires      30d;
                proxy_pass   http://nodejs;
            }

            #代理3000端口
            location / {  
                proxy_pass   http://nodejs;  
                index  index.html index.htm;  
            }       
    } 
}

配置一下Nginx的Gzip,能让请求的JS\CSS\HTML等静态文件更小,响应速度更快些

目录 ect/nginx/conf.conf

  #目录 ect/nginx/conf.conf
  http {
      log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                        '$status $body_bytes_sent "$http_referer" '
                        '"$http_user_agent" "$http_x_forwarded_for"';

      access_log  /var/log/nginx/access.log  main;

      sendfile            on;
      tcp_nopush          on;
      tcp_nodelay         on;
      keepalive_timeout   65;
      types_hash_max_size 2048;

      include             /etc/nginx/mime.types;
      default_type        application/octet-stream;
    

      gzip on;
      gzip_min_length 1k;
      gzip_buffers 4 16k;
      #gzip_http_version 1.0;
      gzip_comp_level 2;
      gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png application/javascript;
      gzip_vary off;
      gzip_disable "MSIE [1-6]\.";

      # Load modular configuration files from the /etc/nginx/conf.d directory.
      # See http://nginx.org/en/docs/ngx_core_module.html#include
      # for more information.
      include /etc/nginx/conf.d/*.conf;

      server {
          listen       80 default_server;
          listen       [::]:80 default_server;
          server_name  _;
          root         /usr/share/nginx/html;

          # Load configuration files for the default server block.
          include /etc/nginx/default.d/*.conf;

          location / {
          }

          error_page 404 /404.html;
              location = /40x.html {
          }

          error_page 500 502 503 504 /50x.html;
              location = /50x.html {
          }
      }


  }

最后再放上本文项目的Github地址,如果这个项目对你有帮助,希望大家可以fork,给我提建议,如果再有时间,可以点个Star那就更好啦~