随着移动互联网时代的生长,,,,许多古板电商都有了自己的小程序商城,,,,那么关于新手来说怎样简朴开发一款小程序商城系统呢。。

前端: 微信小程序、mpvue
后端:koa
数据库:mongodb 数据库可视化工具:Robo3T
一个基本的商城小程序,,,,包括了前端的首页、分类、购物车、我的(订单)四个tab页,,,,后端的数据界说、分类、和存取。。各有其色,,,,我在下面就响应介绍一些主要功效、比照原生小程序和vue.js所踩到的坑尚有后端数据库的功效应用。。 想相识或者有何问题都可以去 作品源码 中相识哦。。
1. 谈组件封装
举个栗子说,,,,首页由三部分组成:头部轮播推荐+中心横向滑动推荐+纵向转动商品list。。这三部分,,,,险些是所有商城类app必需的功效了。。头部的轮播推荐、中心的横向滑动式推荐的封装,,,,我们都知道,,,,诸云云类的功效组件,,,,在各app上基本都少不了,,,,最初学vue最先有所体会的,,,,即是组件代码复用性高的特点,,,,在举行一些组件复用迁徙至别的组件或页面时,,,,可能都不需要改动代码或者改动少量代码就可以直接使用,,,,可以说是相当利便了,,,,关于mpvue组件内仍然支持原生小程序的swiper与scroll,,,,两者兼容后,,,,关于熟知小程序和vue的开发者,,,,这项功效可以很高效率地完成。。
最后主页面文件就是由一个个组件组成,,,,可读性很强了,,,,关于初学者来说,,,,????榉庾暗耐纺允鞘紫染偷镁弑傅牧。。
<template>
<div class="container" @click="clickHandle('test click', $event)">
<div class="swiperList">
<swiper :text="motto" :swiperList="swiperlist"></swiper>
</div>
<div class="navTab">
<div class="recTab">
<text> —— 为你推荐 ——</text>
</div>
</div>
<scroll></scroll>
<div class="hot">
<span> —— 热门商品 ——</span>
</div>
<hot :v-text="motto"></hot>
<div class="fixed-img">
<img :src="fixImg" </div>
</div>
</template>
复制代码
不过关于组件封装与组合的问题,,,,由于最近有研究vue性能优化和用户体验的一些知识点,,,,思量了一个较量严肃的问题:
先看一下常见的vue写法:在html里放一个app组件,,,,app组件里又引用了其他的子组件,,,,形成一棵以app为根节点的组件树:
<body>
<app></app>
</body>
复制代码
而这种做法就引发了性能问题,,,,要初始化一个父组件,,,,必定需要先初始化它的子组件,,,,而子组件又有它自己的子组件。。那么要初始化根标签,,,,就需要从底层最先冒泡,,,,将页面所有组件都初始化完。。以是金狮贵宾会页面会在所有组件都初始化完才最先显示。。
这个效果显然不是我们要的,,,,用户每次点开页面,,,,还要面临一阵子的空缺和响应,,,,由于页面启动后不止要响应初始化页面的组件,,,,尚有包括在app里的其他组件,,,,这样严重拖慢了页面翻开的速率。。
更好的效果是页面可以从上到下按顺序流式渲染,,,,这样可能总体时间增添了,,,,但首屏时间缩减,,,,在用户看来,,,,页面翻开速率就更快了。。网上一些步伐大同小异,,,,各有优弱点,,,,以是...自己也在疯狂试验中,,,,静待好新闻。。
在差别父组件中引用统一子组件时,,,,可是各自需要吸收绑定的动态样式去泛起差别的样式,,,,在绑定css style样式这一关上,,,,踩了个大坑:mpvue居然不支持用object的形式传style,,,,起先处于样式一直上不去的抓狂当中,网上关于mpvue这方面的细节也少之又少,,,,厥后查找了许多地方,,,,发明class和style的绑建都是不支持classObj和styleObj形式,,,,就实验用了字符串,,,,果真...改代码改到嫌疑人生,,,,效果你告诉我人生起步就是过失,,,,怎能不心痛????...
解决:
<template>
<div class="swiper-list">
<d-swiper :swiperList="swiperlist" :styleObject="styleobject"></d-swiper>
</div>
</template>
<script>
data() {
return {
styleobject:'width:100%;height:750rpx;position:absolute;top:0;z-index:3'
}
}
</script>
复制代码
在做vue项目的时间难免会用到循环,,,,需要用到index索引值,,,,可是v-for在嵌套时index没步伐重复用,,,,内循环与外循环不可共用一个index。。
<swiper-item v-for="(items,index) in swiperList" :key="index">
<div v-for="item in items" class="swiper-info" :key="item.id" @click="choose" >
<image :src="item.url" class="swiper-image" :style="styleObject"/>
</div>
</swiper-item>
复制代码
而给内循环再加上另一个索引,,,,便没有报错:
<swiper-item v-for="(items,index) in swiperList" :key="index">
<div v-for="(item,i) in items" class="swiper-info" :key="i" @click="choose" >
<image :src="item.url" class="swiper-image" :style="styleObject"/>
</div>
</swiper-item>
复制代码
这是vue文档里的原话:All lifecycle hooks are called with their 'this' context pointing to the Vue instance invoking it.
意思是:在Vue所有的生命周期钩子要领(如created,,,,mounted,,,, updated以及destroyed)里使用this,,,,this指向挪用它的Vue实例,,,,即(new Vue)。。 mpvue里同理。。 我们都知道,,,,生命周期函数中的this都是指向Vue实例的,,,,因此我们就可以会见数据,,,,对属性和要领举行运算。。
props:{
goods:Array
},
mounted: function(options){
let category = [
{id: 0, name: '所有'},
{id: 1, name: 'JAVA'},
{id: 2, name: 'C++'},
{id: 3, name: 'PHP'},
{id: 4, name: 'VUE'},
{id: 5, name: 'CSS'},
{id: 6, name: 'HTML'},
{id: 7, name: 'JavaScript'}
]
this.categories = category
this.getGoodsList(0)
},
methods: {
getGoodsList(categoryId){
console.log(categoryId);
if(categoryId == 0){
categoryId = ''
}
wx.request({
url: 'http://localhost:3030/shop/goods/list',
data: {
categoryId: categoryId
},
method: 'POST',
success: res => {
console.log(res);
this.goods = res.data.data;
}
})
},
}
复制代码
通俗函数this指向这个函数运行的上下文情形,,,,也就是挪用它的上下文,,,,以是在这里,,,,关于生命周期函数用通俗函数照旧箭头函数着实并没有影响,,,,由于它的界讨情形与运行情形是统一个,,,,以是同样能取到vue实例中数据、属性和要领。。 箭头函数中,,,,this指向的是界说它的最外层代码块,,,,()=>{} 等价于 function(){}.bind(this);以是this虽然指向的是vue实例。。早先并没有思量到this指向的问题,,,,在wx.request({})中success用了通俗函数,,,,效果一直报错“goods is not defined”,,,,用了箭头函数才解决,,,,早先通俗函数的this指向 getGoodsList()的上下文情形,,,,以是一直没步伐取到值。。
在举行首页点击商品跳转到详情页时,,,,onLoad()无法获取更新数据。。
首先虽然onLoad: function (options) 这个是可以接受到值的,,,,可是这个只是加载一次,,,,不是我想要的效果,,,,我需要在本页面(不关闭的情形下)到另外一个页面在跳转进来,,,,吸收到对应商品的数据。。
以是需要将代码放在onshow内部,,,, 在每次页面加载的时间都会举行目今状态的盘问,,,,盘问对应数据的子工具,,,,更新渲染到详情页上。。
onShow: function(options){
// console.log(this.styleobject)
// console.log(options)
wx.getStorage({
key: 'shopCarInfo',
success: (res) =>{
// success
console.log(`initshopCarInfo:${res.data}`)
this.shopCarInfo = res.data;
this.shopNum = res.data.shopNum
}
})
wx.request({
url: 'http://localhost:3030/shop/goods/detail',//请求detail数据表的数据
method: 'POST',
data: {
id: options.id
},
success: res =>{
// console.log(res);
const dataInfo = res.data.data.basicInfo;
this.saveShopCar = dataInfo;
this.goodsDetail.name = dataInfo.name;
this.goodsDetail.minPrice = dataInfo.minPrice;
this.goodsDetail.goodsDescribe = dataInfo.characteristic;
let goodsLabel = this.goodsLabel
goodsLabel = res.data.data;
// console.log(goodsLabel);
this.selectSizePrice = dataInfo.minPrice;
this.goodsLabel.pic = dataInfo.pic;
this.goodsLabel.name = dataInfo.name;
this.buyNumMax = dataInfo.stores;
this.buyNumMin = (dataInfo.stores > 0) ? 1 : 0;
}
})
}
复制代码
相识小程序onLoad与onShow生命周期函数:
onLoad:生命周期函数–监听小程序初始化,,,,当小程序初始化完成时,,,,会触发 onLoadh(全局只触发一次)。。
onShow:生命周期函数–监听小程序显示,,,,当小程序启动,,,,或从后台进入前台显示,,,,会触发 onShow。。
在全局设置文件中: 1).引入koa并实例化
const Koa = require('koa');;;;;
const app = new Koa()
复制代码
2).app.listen(端口号):建设并返回 HTTP 服务器,,,,将给定的参数转达给Server#listen()。。
const Koa = require('koa');//引入koa框架
const app = new Koa();
app.listen(3000);
这里的app.listen()要领只是以下要领的语法糖:
const http = require('http');
const Koa = require('koa');
const app = new Koa();
http.createServer(app.callback()).listen(3000);
复制代码
这样基本的设置完毕,,,,我们就可以用“http://localhost3030+请求地点参数”获取到数据库的值了。。
koa-router 是常用的 koa 的路由库。。
若是依赖ctx.request.url去手动处理路由,,,,将会写许多处理代码,,,,这时间就需要对应的路由的中心件对路由举行控制,,,,这里介绍一个较量好用的路由中心件koa-router。。
以路由切换催动界面切换,,,,”数据化”界面。。
在构建函数库之前,,,,先来聊聊工具的建模。。
Mongoose是在node.js异步情形下对mongodb举行便捷操作的工具模子工具。。该npm包封装了操作mongodb的要领。。
Mongoose有两个特点:
1、通过关系型数据库的头脑来设计非关系型数
2、基于mongodb驱动,,,,简化操作
const mongoose = require('mongoose')
const db = mongoose.createConnection('mongodb://localhost/shop') //建设与shop数据库的毗连(shop是我外地数据库名)
复制代码
外地数据库shop中建了划分“地点治理”、“商品详情”、“订单详情”、“商品列表”、“用户列表”五个数据表:
Schema界面界说数据模子:
Schema用于界说数据库的结构。。类似建设表时的数据界说(不但仅可以界说文档的结构和属性,,,,还可以界说文档的实例要领、静态模子要领、复合索引等),,,,每个Schema会映射到mongodb中的一个collection,,,,可是Schema不具备操作数据库的能力。。
数据表跟工具的映射,同时具有检查效果,检查每组数据是否知足模子中界说的条件 同时,,,,每个工具映射成一个数据报表,,,,就可用该工具举行生涯操作,,,,等同操作数据表,,,,而非mysql下令行般繁琐的操作
以“商品列表”数据表为例:
// 模子通过Schema界面界说。。
var Schema = mongoose.Schema;
const listSchema = new Schema({
barCode: String,
categoryId: Number,
characteristic: String,
commission: Number,
commissionType: Number,
dateAdd: String,
dateStart: String,
id: Schema.Types.ObjectId,
logisticsId: Number,
minPrice: Number,
minScore: Number,
name: String,
numberFav: Number,
numberGoodReputation: Number,
numberOrders: Number,
originalPrice: Number,
paixu: Number,
pic: String,
pingtuan: Boolean,
pingtuanPrice: Number,
propertyIds: String,
recommendStatus: Number,
recommendStatusStr: String,
shopId: Number,
status: Number,
statusStr: String,
stores: Number,
userId: Number,
videoId: String,
views: Number,
weight: Number,
})
复制代码
界说了数据表中需要的数据项的类型,,,,数据表传入数据后会逐一对应:
const Router = require('koa-router')()//引入koa-router
const router = new Router();// 建设 router 实例工具
//注册路由
router.post('/shop/goods/list', async (ctx, next) => {
const params = ctx.request.body
//以‘listSchema’的模子去取到Goods的数据
const Goods = db.db.model('Goods', db.listSchema) //第一个‘db’是require来的自界说的,,,,第二个‘db’是取到毗连到mongodb的数据库,,,,model代指实体数据(凭证schema获取该字段下的数据,,,,然后传给Goods))
ctx.body = await new Promise((resolve, reject) => {//ctx.body是ctx.response.body的缩写,代指响应数据
//异步,,,,比及获取到数据之后再将body发出去
if (params.categoryId) {
Goods.find({categoryId: params.categoryId},(err, docs) => {
if (err) {
reject(err)
}
resolve({
code: 0,
errMsg: 'success',
data: docs
})
})
} else {
Goods.find((err, docs) => {
if (err) {
reject(err)
}
resolve({
code: 0,
errMsg: 'success',
data: docs
})
})
}
})
})
复制代码
所有的数据库操作都是异步的操作,,,,以是需要封装promise来实现,,,,由此通过POST “http://localhost3030/shop/goods/list”便可会见外地shop数据库了。。 这里顺便提一下“ctx”的使用,,,,ctx(context)上下文,,,,我们都知道有node.js 中有request(请求)工具和respones(响应)工具。。Koa把这两个工具封装在ctx工具中。。 参数ctx是由koa传入的封装了request和response的变量,,,,我们可以通过它会见request和response (前端通过ajax请求http获取数据) 我们可以通过ctx请求or获取数据库中的数据。。
Ctx.body 属性就是发送给用户的内容
body是http协议中的响应体,,,,header是指响应头
ctx.body = ctx.res.body = ctx.response.body
1).为什么要做数据缓存????
在这里不得不提一句数据缓存的主要性,,,,虽然我是从外地数据库获取的数据,,,,可是由于需要的数据量较多,,,,再者前面说的性能优化还未完成,,,,每次照旧有一定的请求时间,,,,没须要每次翻开都去请求一遍后端,,,,渲染页面较慢,,,,以是需要将需要经常用到的数据做外地缓存,,,,这样能大大提高页面渲染速率。。
2).设置模子层
setGoodsList: function (saveHidden, total, allSelect, noSelect, list) {
this.saveHidden = saveHidden,
this.totalPrice = total,
this.allSelect = allSelect,
this.noSelect = noSelect,
this.goodsList = list
var shopCarInfo = {};
var tempNumber = 0;
var list = [];
shopCarInfo.shoplist = list;
for (var i = 0; i < list.length; i++) {
tempNumber = tempNumber + list[i].number
}
shopCarInfo.shopNum = tempNumber;
wx.setStorage({
key: "shopCarInfo",
data: shopCarInfo
})
},
复制代码
将需要做外地存储数据的要领封装成一个要领模子,,,,当需要做外地存储时,,,,直接做引用,,,,现在vue、react中多用到的架构想想,,,,都对模子层封装有一定的要求。。
bindAllSelect() {
var list = this.goodsList;
var currentAllSelect = this.allSelect
if (currentAllSelect) {
list.forEach((item) => {
item.active = false
})
} else {
list.forEach((item) => {
item.active = true
})
}
this.setGoodsList(this.getSaveHide(), this.totalPrice(), !currentAllSelect, this.noSelect(), list);
},
复制代码
小程序工具开发公司长沙,,,,是一家有着十年手艺前沿的公司,,,,我们以先进手艺提供并解决各行业小程序开发,,,,操作简朴,,,,支持多种社群营销活动,,,,以及可视化模板操作,,,,大大镌汰人力物力本钱。。
小程序工具提供多类型商城/门店小程序制作,,,,可视化编辑 1秒天生5步上线。。通过拖拽、拼接????榻峁剐〕绦蛏坛且趁,,,,所看即所得,,,,只需要美工就能做出细腻商城。。更多小程序市肆请审查:小程序市肆
KESION 金狮贵宾会软件
KESION 金狮贵宾会软件是海内领先的在线教育软件及私域社交电商软件服务提供商,,,,恒久专注于为企业提供在线教育软件及社交电商SaaS平台解决方案。。
公司焦点产品云开店SaaS社交电商服务平台、在线教育SaaS服务平台、教育企业数字化SaaS云平台、企微营销助手、私有化自力安排品牌网校和在线教育咨询等。。KESION 一直通过手艺立异,,,,提供产品和服务,,,,助力企业向数字化转型,,,,通过科技驱动商业刷新,,,,让商业变得更智慧!
许多小程序都会用到相机,,,,那么相机组件是怎样实现的,,,,下面为各人介绍这篇文章。。...
微信小程序上线一年多的时间,,,,市面上有许多小程序应用应运而生,,,,价钱也是狼籍不齐,,,,有几千块的,,,,也有上万块的,,,,尚有人说300块就能做一个小程序了。。那么究竟广东惠州小程序开发...