Vue全家桶+koa2+MySql(sequelize)重構「零食商販」項目

前言

原項目使用微信小程序配合ThinkPHP5.0打造的微信小程序商城,做爲一名「中端工程師」來說可以使用javascript先後一塊兒梭也是工做之餘很是愉快的事情。因此若是你也想梭一把,那麼能夠繼續往下看。javascript

github項目地址css

項目不足

1.前端部分沒有100%還原設計稿,由於壓根就沒有設計稿。惟一的標準就是本身的「像素眼」。
2.做爲WEB項目天然而然就閹割了微信的登陸與支付體系。固然登陸體系也許會加上。
3.後端部分沒有作嚴格的容錯處理,這裏更多的是提供一些問題的解決方法和分享本身遇到的坑兒。html

項目運行效果

項目運行效果

前端部分

前端部分好像沒有什麼好說的了,網上vue全家桶項目一搜一大把。這裏主要分享一下從前端角度如何分解產品設計稿以及css模塊化的處理。本篇文章將重點放在服務端上。前端

分解一款產品

不知道其餘小夥伴拿到設計稿是如何開始的,記得纔開始寫前端的時候也是根據設計稿從上到下,從左到右一步一步實現。不過每每這樣的開發流程遇到大的需求變動,或是產品同窗提不切實際需求的時候是很是頭痛的。這裏提一句,前端小夥伴必定要多多參加產品需求會,一方面能夠增長對公司業務的理解,另外一方面能夠把產品同窗不切實際的需求扼殺在搖籃中,防止拍腦殼決策出現。vue

分解設計稿
「零食商販」項目雖然頁面有多個,可是咱們分解設計稿就會發現其實該項目由這幾個部分組成。java

Header與Footer
這一部分稱爲公共部分。基本上每一個頁面都由header、footer以及中間主要內容組成。
圖片描述node

layout
這一部分稱爲「基模塊」。也就是頁面大部分都是由該模塊組合而成,無非是一些內容的增減,實際開發中徹底能夠經過數據以及css達到各個頁面個性化定製需求。mysql

圖片描述
圖片描述
圖片描述

以上三個頁面都同樣,只是換了馬甲。
結構抽象出來應該就是這樣:ios

//vue模板
<template>
  <div>
    <!-- 小標題 -->
    <p>{{ title }}</p>
    <slot></slot>
    <div>
      <!-- 產品模塊 -->
      <div>
          <img src="" alt="" >
          <p >{{ item.name }}</p>
          <p >{{ item.price }}</p>
      </div>
    </div>
  </div>
</template>

css模塊化

雖然vue提供了scope來方便編寫組件內部的css,防止css名相互污染。但有時候scope形成的做用域問題不方便調試。因此這裏採用了一樣流行的CSS Modules
開啓方式也很簡單,若是是使用vue-cli方式構建的vue項目,只須要兩步便可開啓:git

進入文件夾build/vue-loader-conf.js

module.exports = {
  // css模塊化
  cssModules: {
    // 經過給類名加入惟一前綴防止類名衝突
    localIdentName: '[name]---[local]---[hash:base64:5]',
    camelCase: true
  }
}

在每一個組件中申明css modules並使用

<template>
   //$style部分將會替換爲'[name]---[local]---[hash:base64:5]'
  <div :class="$style.header"></div>
</template>

<style lang='scss' module>
.header{
  color:blue
}
</style>

css modules 的核心原理就是經過加入惟一的class類名從而防止css類名衝突。本質上的效果與scope是同樣的。

一款「異常簡陋」的輪子

項目寫到一半,才發現須要一款符合微信官方風格的UI組件,雖然官方UI組件顏值上並不高。可是爲了視覺上的統一,又苦於網上沒有找到過於簡陋的UI組件,因此本身封裝了一個。目前只有picker組件和基於picker組件的地址選擇組件。
npm 命令直接安裝,本項目默認是安裝好了的。由於簡陋因此也就不提供什麼文檔了...... 具體用法能夠看項目內部實現。

npm install only-ui --save

後端部分

項目後端部分採用koa2搭配mysql數據實現,koa2官網是這樣介紹的:

Koa 是一個新的 web 框架,由 Express 幕後的原班人馬打造, 致力於成爲 web 應用和 API 開發領域中的一個更小、更富有表現力、更健壯的基石。 經過利用 async 函數,Koa 幫你丟棄回調函數,並有力地加強錯誤處理。 Koa 並無捆綁任何中間件, 而是提供了一套優雅的方法,幫助您快速而愉快地編寫服務端應用程序。

正是由於koa2輕量,沒有一些官方性的約束,你能夠很方便搭建本身的前端項目。但同時也帶來了一些問題,「一千我的就有一千種koa MVC的寫法」,因此若是是大一點項目或是團隊項目仍是比較推薦egg.js來編寫。

項目結構

圖片描述

  1. server:根目錄
  2. api:存放的是咱們項目的數據接口
  3. database:數據庫腳本文件,能夠直接導入到navicate中使用
  4. dbs:數據庫配置以及數據模型(sequelize)
  5. public:一些資源圖片就存放在這裏
  6. view:視圖模板(本項目不須要)
  7. app.js:koa主文件
  8. index.js入口文件

初始化項目

//經過koa-generator快速搭建koa2服務
npm install -g koa-generator

//建立項目 輸入項目名稱
koa2 -e [項目名稱]

//安裝依賴
npm install

這樣咱們基本上建立了一個比較簡單的koa2項目,咱們來看一下如今已經安裝了哪些依賴

"dependencies": {
    "debug": "^2.6.3",
    "ejs": "~2.3.3", //ejs模板,由於咱們建立項目的時候用的ejs
    "koa": "^2.2.0",
    "koa-bodyparser": "^3.2.0", //解析request
    "koa-convert": "^1.2.0",
    "koa-json": "^2.0.2", //格式化json數據
    "koa-logger": "^2.0.1", //系統日誌
    "koa-onerror": "^1.2.1", //錯誤處理
    "koa-router": "^7.1.1", //路由
    "koa-static": "^3.0.0", //靜態資源處理
    "koa-views": "^5.2.1"//模板渲染
  },
  "devDependencies": {
    "nodemon": "^1.8.1" //能夠隨時監聽服務端文件改動,並更新
  }

前面說了,koa就像一塊電腦主板同樣,須要什麼東西本身能夠往上面加。這裏有更多中間件,若是仍是沒有你須要的,你徹底能夠本身寫一個造福社區。
只有以上的中間件仍是不夠的,好比我並不但願使用require語法導入模塊因此咱們換成 es6 modules方式導入。這裏還須要安裝:

"babel-core": "^6.26.3",
 "babel-preset-env": "^1.7.0",
 "babel-preset-es2015": "^6.24.1",
 "babel-register": "^6.26.0",

同時修改項目結構

//index.js
// 啓動文件
require('babel-register')
({
  'presets':['env']
})

require('./app.js')

這樣咱們就能夠在主文件中使用import導入咱們須要的模塊。(中間件的導入在在項目中有清晰的註釋)
圖片描述

咱們來嘗試啓動一下koa2服務,啓動以前要修改一下npm(你怕嗎) script

"scripts": {
    "start": "nodemon index.js", //使用nodemon 啓動index文件
},

數據庫

服務端開發怎麼能少了數據庫,不知道是否是錯覺,koa項目好像更多的是配合mongodb來使用。本項目使用mysql徹底是由於本身的習慣,再一個使用mongodb處理複雜一點的數據表間關係確實有點頭痛。。。
咱們在這裏引入sequelize來操做mysql,畢竟使用原生sql顯得不是那麼優雅!什麼是sequelize?

Sequelize 是一個基於 promise 的 Node.js ORM, 目前支持 Postgres, MySQL, SQLite 和 Microsoft SQL Server. 它具備強大的事務支持, 關聯關係, 讀取和複製等功能.

通俗一點說,就比如Java中的hibernate,mongodb中的mongoose。讓咱們以面向對象的方式操做數據庫。
如今讓咱們來安裝sequelize。mysql的安裝

sequelize中文文檔

1. 安裝sequelize

// 安裝sequelize
$ npm install --save sequelize
// 安裝驅動
$ npm install --save mysql2

2. 配置sequelize

既然咱們使用sequelize操做數據庫,那麼一番基本的配置必定是要有的。

//config.js
// sequelize配置文件
export default {
  // 數據庫名稱
  database: '',
  // 用戶名
  username: '',
  // 密碼
  password: '',
  // 地址
  host: '127.0.0.1',
  // 使用什麼數據庫
  dialect: 'mysql',
  // 鏈接池
  pool: {
    max: 5,
    min: 0,
    acquire: 30000,
    idle: 10000
  },
  // 數據表全局配置
  define:{
    //是否凍結表名,最好設置爲true,要不sequelize會自動給表名加上覆數s形成查詢數據失敗。
    //mongoose也有這樣的問題...
    freezeTableName:true,
    // 是否爲表添加 createdAt 和 updatedAt 字段
    // createdAt 記錄表的建立時間
    // updatedAt 記錄字段更新時間
    timestamps:false,
    // 是否爲表添加 deletedAt 字段
    // 在平常開發中刪除數據記錄是一大禁忌,所以咱們刪除數據並不會真正刪除,而是爲他添加
    // deletedAt字段
    paranoid:false,
    //是否開啓op
    operatorsAliases: false
  },
  // 時區
  timezone: '+08:00'
}

如此一番操做,sequelize已經與mysql創建起聯繫,但還沒法工做,咱們須要給數據表創建模型。創建模型以前咱們導入剛剛已經配置好的文件。以下圖
圖片描述
index.js中導入咱們的配置config.js文件,其餘的文件都是模型(能夠把它理解爲數據庫中的表,讓咱們更好操做它)。

3. 定義模型

咱們在navicat中看到的表長這個樣子
圖片描述

咱們的模型長這個樣子:

//banner.js
// banner 模型
export default (sequelize, DataTypes) => {
 //這裏的banner爲你的數據表名
  return sequelize.define('banner', {
    id: {
    //定義類型
      type: DataTypes.INTEGER(),
      //主鍵
      primaryKey: true
    },
    productsId: {
    //定義類型
      type: DataTypes.INTEGER(),
    },
    img_id: {
    //定義類型
      type: DataTypes.INTEGER(),
    }
  })
}

而後導入到index.js中統一管理(必定要讓你的全部模型在同一個sequelize實例下,曾經這個問題困擾了我好久。。。)

//index.js
import Sequelize from 'sequelize'
import config from '../config.js'

// 實例化sequelize
export const sequelize = new Sequelize(config)

// 導入模型統一管理(推薦使用官方方法)
export const Banner = sequelize.import(__dirname + '/banners.js')

4. 創建表與表之間關係

表與表之間無外乎:

一對一 belongsto
外鍵一對一 hasone
一對多 hasmany
多對多 belongsToMany

拿咱們項目中的banner與image來舉例,banner指向惟一image,image對應惟一banner那麼他們之間關係就爲一對一。

//定義關係
Banner.belongsTo(Image, {
  foreignKey: 'img_id',
  targetKey: 'id'
})

如今咱們創建了模型也定義了模型間關係,如今咱們開始來使用。

5.爲前端提供接口

仍是以banner爲例

//api/banner.js
// banner接口
import Router from 'koa-router'

// 引入用戶模型
import { Banner,Image } from '../dbs/models/index.js'
//定義接口前綴
let router = new Router({
  prefix:'/banner'
})

//暴露給前端的接口
router.get('/', async (ctx,next)=>{
  let banner = await Banner.findAll({
   //聲明要包含的模型,以前聲明的關係將在這裏發揮做用
    include:[{
      model:Image
    }],
    //過濾不須要的數據
    attributes:{
      exclude:['img_id']
    }
  })
//最終返回的數據
    ctx.body = {
      banner
    }
})

export default router

最後咱們須要把路由導入到主文件中。

//app.js
//引入
import banner from './api/banner.js'
//使用
app.use(banner.routes()).use(banner.allowedMethods())

如今你能夠在前端經過axios訪問你的數據接口了,咱們看一下最終執行效果。
sequelize已經自動幫咱們生成了sql語句:
圖片描述

postman中的數據:
圖片描述

其餘更詳細的內容能夠查看mini-shop

相關文章
相關標籤/搜索