前言
原项目使用微信小程序配合ThinkPHP5.0打造的微信小程序商城,作为一名“中端工程师”来讲能够使用javascript前后一起梭也是工作之余非常愉快的事情。所以如果你也想梭一把,那么可以继续往下看。
github项目地址
项目不足
1.前端部分没有100%还原设计稿,因为压根就没有设计稿。唯一的标准就是自己的“像素眼”。
2.作为WEB项目自然而然就阉割了微信的登录与支付体系。当然登录体系也许会加上。
3.后端部分没有做严格的容错处理,这里更多的是提供一些问题的解决方法和分享自己遇到的坑儿。
项目运行效果
前端部分
前端部分好像没有什么好说的了,网上vue全家桶项目一搜一大把。这里主要分享一下从前端角度如何分解产品设计稿以及css模块化的处理。本篇文章将重点放在服务端上。
分解一款产品
不知道其他小伙伴拿到设计稿是如何开始的,记得才开始写前端的时候也是根据设计稿从上到下,从左到右一步一步实现。不过往往这样的开发流程遇到大的需求变更,或是产品同学提不切实际需求的时候是非常头痛的。这里提一句,前端小伙伴一定要多多参加产品需求会,一方面可以增加对公司业务的理解,另一方面可以把产品同学不切实际的需求扼杀在摇篮中,防止拍脑袋决策出现。
分解设计稿
“零食商贩”项目虽然页面有多个,但是我们分解设计稿就会发现其实该项目由这几个部分组成。
Header与Footer
这一部分称为公共部分。基本上每个页面都由header、footer以及中间主要内容组成。
layout
这一部分称为“基模块”。也就是页面大部分都是由该模块组合而成,无非是一些内容的增减,实际开发中完全可以通过数据以及css达到各个页面个性化定制需求。
以上三个页面都一样,只是换了马甲。
结构抽象出来应该就是这样:
//vue模板
{{ title }}
{{ item.name }}
{{ item.price }}
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并使用
//$style部分将会替换为'[name]---[local]---[hash:base64:5]'
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的安装
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. 定义模型
我们的模型长这个样子:
//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语句:
其他更详细的内容可以查看mini-shop!