koa+mysql實現(xiàn)增刪改查-全棧之路(001)

Date: 2020-4-23

以前很少寫文章澄暮,從今天開始我要挑戰(zhàn)一下自己,連續(xù)輸出100篇技術(shù)類文章炕舵。這100篇文章我盡量以實戰(zhàn)案例為主栋荸。

如果你覺得本文還不錯菇怀,記得關(guān)注或者給個 star凭舶,你們的贊和 star 是我編寫更多更精彩文章的動力!
GitHub 地址

本文重點內(nèi)容

  • 從 0 到 1 集成 node + mysql + ejs 用戶管理系統(tǒng)
  • 上手 sequelize 不使用sql操作數(shù)據(jù)庫
  • 熟悉 MVC 開發(fā)模式

成品演示

關(guān)鍵技術(shù)點

  • 1.1 數(shù)據(jù)庫操作
  • 1.2 MVC 模式是什么爱沟?

1.1 數(shù)據(jù)庫操作

// 使用 sequelize 代理數(shù)據(jù)庫操作
const { Sequelize, Model, DataTypes } = require('sequelize');
const config = require('./config')

// 配置數(shù)據(jù)庫連接
const sequelise = new Sequelize(
    dbName,
    username, password,
    {
        host: host,
        dialect: 'mysql', // 配置方言
})
class User extends Model {}

// 創(chuàng)建表
User.init({
  username: DataTypes.STRING,
  birthday: DataTypes.DATE
}, { sequelize, modelName: 'user' });

sequelize.sync() // 生成數(shù)據(jù)表
  .then(() => User.create({ // 插入數(shù)據(jù)
    username: 'janedoe',
    birthday: new Date(1980, 6, 20)
  }))
  .then(jane => {
    console.log(jane.toJSON());
  });

1.2 MVC模式是什么帅霜?

MVC即Model、View呼伸、Controller即模型身冀、視圖、控制器

Module     - 對象和業(yè)務(wù)邏輯
View       - 用戶界面
Controller - 用來調(diào)度 View 和 Model

開始擼代碼

第一步 初始化目錄

先來初始化下目錄結(jié)構(gòu):

$ mkdir demo_001 && cd demo_001
$ npm init -y
$ npm i -s nodemon better-npm-run
$ npm i -s koa koa-views @koa/router koa-bodyparser
$ npm i -s ejs sequelize mysql2

各個庫的版本號為:
@koa/router: 9.0.0, better-npm-run: 0.1.1括享,ejs: 3.0.2搂根,koa: 2.11.0,koa-views:6.2.1铃辖,sequelize:5.21.6剩愧,koa-bodyparser:4.3.0,koa-static:5.0.0娇斩,mysql2:2.1.0隙咸,nodemon:2.0.3

添加 npm scripts 到 package.json:

    "scripts": {
      "start": "npm run dev",
      "dev": "better-npm-run dev",
      "prd": "better-npm-run prd"
    },
    "betterScripts": {
      "dev": {
        "command": "nodemon app.js",
        "env": {
          "NODE_ENV": "development"
        }
      },
      "prd": {
        "env": {
          "NODE_ENV": "production"
        },
        "command": "pm2 start app.js -n demo_001"
      }
    },

第二步 實現(xiàn) view 層

新建 app.js

// app.js 代碼
const Koa = require('koa');
const views = require('koa-views');
const path = require('path');
const bodyparser = require('koa-bodyparser');

const app = new Koa();

app.keys = ['my keys'];

app.use(bodyparser());
app.use(views(path.join(__dirname, './views'), { extension: 'ejs' }));

app.listen(3000, () => {
    console.log('server is running', new Date());
});

讓代碼跑起來,之后修改代碼不用頻繁的重啟服務(wù)成洗。因為開發(fā)環(huán)境是用 nodemon 托管的

$ npm start

新建 views 目錄結(jié)構(gòu)

  demo_001
  ├── router
  │   └── index.js
  ├── views
  │   ├── index.ejs
  │   ├── header.ejs
  │   ├── create.ejs
  │   └── edit.ejs
  └── app.json
  └── package.json

view 層核心代碼:

<!-- views/header.ejs -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>node + mysql 實現(xiàn)增刪改查</title>
</head>
<body>
<!-- views/index.ejs -->
<% include('./header.ejs') %>
    <h1>
        <%= title %>
        <small>實現(xiàn)增刪改查</small>
    </h1>
    <a href="/user/create">添加用戶</a>
    <style>
        table{
            border-color: #ccc;
        }
        table td, th{
            background: #fff;
        }
    </style>
    <table cellspacing="1"  cellpadding="15" bgcolor="#000"  >
        <thead>
            <tr>
                <th>username</th>
                <th>pwd</th>
                <th>phone</th>
                <th>age</th>
                <th>gender</th>
                <th>操作</th>
            </tr>
        </thead>
        <% for (const user of users) { %>
            <tr>
                <td><%= user.username %></td>
                <td><%= user.pwd %></td>
                <td><%= user.phone %></td>
                <td><%= user.age %></td>
                <td><%= user.gender %></td>
                <td>
                    <a href="/user/edit?id=<%= user.id %>">修改</a>
                    <a href="/user/del/<%= user.id %>">刪除</a>
                </td>
            </tr>
        <% } %>
    </table>
</body>
</html>
<!-- views/create.ejs -->
<% include('./header.ejs') %>
    <style>
        label{
            width: 80px;
            display: inline-block;
            text-align: right;
            padding-right: 10px;
        }
    </style>
    <h1>
        <%= title %> <small><a href="/">返回首頁</a></small>
    </h1>
    <form action="/user/create" method="POST" >
        <fieldset>
            <label>username</label>
            <input value="" name="username" />
        </fieldset>
        <fieldset>
            <label>pwd</label>
            <input value="" name="pwd" />
        </fieldset>
        <fieldset>
            <label>phone</label>
            <input value="" name="phone" />
        </fieldset>
        <fieldset>
            <label>age</label>
            <input value="" name="age" />
        </fieldset>
        <fieldset>
            <label>gender</label>
            <input value="" name="gender" />
        </fieldset>
        <fieldset>
            <button type="submit">Submit</button>
        </fieldset>
    </form>
</body>
</html>
<!-- views/edit.ejs -->
<% include('./header.ejs') %>
    <style>
        label{
            width: 80px;
            display: inline-block;
            text-align: right;
            padding-right: 10px;
        }
    </style>
    <h1>
        <%= title %> <small><a href="/">返回首頁</a></small>
    </h1>
    <form action="/user/edit" method="POST" >
        <input value="<%= user.id %>" name="id" type="hidden" />
        <fieldset>
            <label>username</label>
            <input value="<%= user.username %>" name="username" />
        </fieldset>
        <fieldset>
            <label>pwd</label>
            <input value="<%= user.pwd %>" name="pwd" />
        </fieldset>
        <fieldset>
            <label>phone</label>
            <input value="<%= user.phone %>" name="phone" />
        </fieldset>
        <fieldset>
            <label>age</label>
            <input value="<%= user.age %>" name="age" />
        </fieldset>
        <fieldset>
            <label>gender</label>
            <input value="<%= user.gender %>" name="gender" />
        </fieldset>
        <fieldset>
            <button type="submit">Submit</button>
        </fieldset>
    </form>
</body>
</html>

路由部分核心代碼:

const Router = require('@koa/router');
const router = new Router()

// 首頁,查詢所有用戶
router.get('/', async ctx => {
    let users = []
    console.log('查詢所有用戶')
    await ctx.render('index', { title: 'node + mysql ', users });
});
// 增加
router.get('/user/create', async ctx => {
    await ctx.render('create', { title: '添加用戶', method: 'add' })
})
router.post('/user/create', async ctx => {
    console.log('添加用戶:',ctx.request)
    ctx.redirect('/')
})
// 修改
router.get('/user/edit', async ctx => {
    const codition = { id: ctx.query.id }
    console.log('查詢要修改的用戶',codition)
    await ctx.render('edit', { title: '修改用戶', method: 'edit', user: {} })
})
router.get('/user/edit', async ctx => {
    console.log('要修改的用戶:', ctx.request)
    ctx.redirect('/')
})
// 刪除
router.get('/user/del/:id', async ctx => {
    console.log('刪除用戶id藏否,', ctx.params.id)
    ctx.redirect('/')
})

module.exports = router;

目前為止所有的路由已經(jīng)準(zhǔn)備好了瓶殃,需要掛載到 koa 實例上

// 修改 app.js
// 引入路由部分
const indexRouter = require('./router/index')
// 掛載到 koa 實例
app.use(indexRouter.routes(), indexRouter.allowedMethods());

到這一步我們已經(jīng)把頁面做好了

打開瀏覽器輸入 http://localhost:3000

到此為止,頁面已經(jīng)可以訪問了

[圖片上傳失敗...(image-3e862-1587690648262)]


查詢頁面

第三步 實現(xiàn) module 層

新建 module 目錄結(jié)構(gòu)

  demo_001
  ├── config
  │   ├── dev.js
  │   ├── prd.js
  │   └── index.js
  ├── modules
  │   └── user.js
  ├── router
  ├── views
  ├── db.js
  ├── app.json
  └── package.json

我們先要配置數(shù)據(jù)庫連接:

// config/index.js
if (process.env.NODE_ENV === 'production') {
    module.exports = require('./prd')
} else {
    module.exports = require('./dev')
}

dev 和 prd 分別對應(yīng)不同環(huán)境的數(shù)據(jù)庫連接副签。這里都寫成你自己的數(shù)據(jù)庫地址即可

// config/prd.js, config/dev.js
module.exports = {
    db: {
        // host
        host: '127.0.0.1',
        // 數(shù)據(jù)庫名
        dbName: 'xxxx',
        // 用戶名
        username: 'xxxx',
        // 密碼
        password: 'xxxx'
    }
}

連接數(shù)據(jù)庫 db.js

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

const { dbName, username, password, host } = config.db;

const sequelise = new Sequelize(
    dbName,
    username, password,
    {
        host: host,
        dialect: 'mysql',
        // 配置連接池
        pool: {
            max: 5,
            min: 0,
            acquire: 30000,
            idle: 10000
        }
})

sequelise
    .authenticate()
    .then(() => {
        console.log('數(shù)據(jù)庫連接成功')
    })
    .catch(err => {
        throw new Error('數(shù)據(jù)庫連接失敗', err)
    })

module.exports = sequelise

創(chuàng)建 module

// modules/user.js
const Sequelize = require('sequelize')

const sequelize = require('../db');

const User = sequelize.define('user', {
    username: { type: Sequelize.STRING },
    pwd: { type: Sequelize.STRING, },
    phone: { type: Sequelize.STRING, },
    age: { type: Sequelize.STRING, },
    gender: { type: Sequelize.INTEGER, },
});

module.exports.add = async (data) => await User.create(data)
module.exports.del = async (id) => await User.destroy({ where: { id } })

module.exports.update = async (data) => {
    let newUser = {...data}
    delete newUser['id']
    return await User.update({ ...newUser }, {
        where: { id: data.id }
    })
}

module.exports.find = async (condition) => {
    if (Object.keys(condition).length) {
        return await User.findAll({ where: { ...condition } })
    } else {        
        return await User.findAll();
    }
}

第四步 控制層調(diào)度

現(xiàn)在要通 修改 過路由遥椿,把視圖和 module 結(jié)合起來。

// router/index.js
// 引入
const UserMudule = require('../modules/user')

// 修改路由:增加
router.post('/user/create', async (ctx) => {
    const {
        username, pwd, phone, age, gender
     } = ctx.request.body
     await UserMudule.add({
        username, pwd, phone, age, gender
     })
    ctx.redirect('/')
})
// 修改路由:首頁淆储,查詢所有用戶
router.get('/', async ctx => {
    let users = await UserMudule.find(ctx.query)
    await ctx.render('index', { title: 'node + mysql ', users });
});
此時已經(jīng)實現(xiàn)了增加和查詢冠场,你可以測試一下這部分功能。

接下來實現(xiàn)修改和刪除的代碼

// router/index.js
// 修改路由:delete
router.get('/user/del/:id', async ctx => {
    await UserMudule.del(ctx.params.id)
    ctx.redirect('/')
})

// 修改路由:update
router.get('/user/edit', async ctx => {
    const codition = { id: ctx.query.id }
    // 修改前先查詢出 User 對象
    let user = await UserMudule.find(codition)
    await ctx.render('edit', { title: '修改用戶', method: 'edit', user: user[0] })
})

// 修改路由:update
router.post('/user/edit', async ctx => {
    const {
        username, pwd, phone, age, gender, id
    } = ctx.request.body
    //  接受參數(shù)本砰,執(zhí)行修改
    await UserMudule.update({
    id, username, pwd, phone, age, gender
    })
    ctx.redirect('/')
})

來碴裙,測試一下!

新增和查詢

[圖片上傳失敗...(image-b16129-1587690648262)]

總結(jié)

到此你已經(jīng)掌握了簡單的 nodejs 服務(wù)器開發(fā)点额,下一篇文章我繼續(xù)帶你一步步的上線一個 nodejs 項目

所以舔株,如果你看完真覺得不錯,那就給個 star 吧还棱!你的贊和 star 是我編寫更多精彩文章的動力GitHub 地址

my_wechart.jpg

share.jpg

后記

這篇文章我花了6個小時载慈,寫代碼,錄gif 脖子和胳膊都酸了~~
希望小伙伴們給我一點點打賞珍手,鼓勵我寫成更多干貨文章

微信

[圖片上傳失敗...(image-c9d4c6-1587690648262)]

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末办铡,一起剝皮案震驚了整個濱河市辞做,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌寡具,老刑警劉巖秤茅,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異晒杈,居然都是意外死亡嫂伞,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門拯钻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來帖努,“玉大人,你說我怎么就攤上這事粪般∑从啵” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵亩歹,是天一觀的道長匙监。 經(jīng)常有香客問我,道長小作,這世上最難降的妖魔是什么亭姥? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮顾稀,結(jié)果婚禮上达罗,老公的妹妹穿的比我還像新娘。我一直安慰自己静秆,他們只是感情好粮揉,可當(dāng)我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著抚笔,像睡著了一般扶认。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上殊橙,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天辐宾,我揣著相機與錄音,去河邊找鬼膨蛮。 笑死螃概,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的鸽疾。 我是一名探鬼主播吊洼,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼制肮!你這毒婦竟也來了冒窍?” 一聲冷哼從身側(cè)響起递沪,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎综液,沒想到半個月后款慨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡谬莹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年檩奠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片附帽。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡埠戳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蕉扮,到底是詐尸還是另有隱情整胃,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布喳钟,位于F島的核電站屁使,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏奔则。R本人自食惡果不足惜蛮寂,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望易茬。 院中可真熱鬧共郭,春花似錦、人聲如沸疾呻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽岸蜗。三九已至,卻和暖如春叠蝇,著一層夾襖步出監(jiān)牢的瞬間璃岳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工悔捶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留铃慷,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓蜕该,卻偏偏與公主長得像犁柜,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子堂淡,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容