前言
原項目使用微信小程序配合ThinkPHP5.0打造的微信小程序商城,作爲一名“中端工程師”來講能夠使用javascript前後一起梭也是工作之餘非常愉快的事情。所以如果你也想梭一把,那麼可以繼續往下看。
項目不足
1.前端部分沒有100%還原設計稿,因爲壓根就沒有設計稿。唯一的標準就是自己的“像素眼”。
2.作爲WEB項目自然而然就閹割了微信的登錄與支付體系。當然登錄體系也許會加上。
3.後端部分沒有做嚴格的容錯處理,這裏更多的是提供一些問題的解決方法和分享自己遇到的坑兒。
項目運行效果
前端部分
前端部分好像沒有什麼好說的了,網上vue全家桶項目一搜一大把。這裏主要分享一下從前端角度如何分解產品設計稿以及css模塊化的處理。本篇文章將重點放在服務端上。
分解一款產品
不知道其他小夥伴拿到設計稿是如何開始的,記得纔開始寫前端的時候也是根據設計稿從上到下,從左到右一步一步實現。不過往往這樣的開發流程遇到大的需求變更,或是產品同學提不切實際需求的時候是非常頭痛的。這裏提一句,前端小夥伴一定要多多參加產品需求會,一方面可以增加對公司業務的理解,另一方面可以把產品同學不切實際的需求扼殺在搖籃中,防止拍腦袋決策出現。
分解設計稿
“零食商販”項目雖然頁面有多個,但是我們分解設計稿就會發現其實該項目由這幾個部分組成。
Header與Footer
這一部分稱爲公共部分。基本上每個頁面都由header、footer以及中間主要內容組成。
layout
這一部分稱爲“基模塊”。也就是頁面大部分都是由該模塊組合而成,無非是一些內容的增減,實際開發中完全可以通過數據以及css達到各個頁面個性化定製需求。
以上三個頁面都一樣,只是換了馬甲。
結構抽象出來應該就是這樣:
//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項目,只需要兩步即可開啓:
進入文件夾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來編寫。
項目結構
- server:根目錄
- api:存放的是我們項目的數據接口
- database:數據庫腳本文件,可以直接導入到navicate中使用
- dbs:數據庫配置以及數據模型(sequelize)
- public:一些資源圖片就存放在這裏
- view:視圖模板(本項目不需要)
- app.js:koa主文件
- 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的安裝
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!