使用Vue和Node.js实现一个简单的商城系统
前端: Vue2.0、ES6语法
后端: Node.js、Express框架
数据库: MongoDB
该项目需要axios、ES6等前置知识:
将头部、面包屑、底部拆分出来,写成组件的形式,放在components
中,以供多页面复用,商品列表页GoodsList
则放在views
中。
-
安装
express
、express-generator(全局)
,然后在项目下输入命令express server
, 可以看到会生成一个目录名为server
,然后进入该目录并npm install
,安装框架所需的依赖包。 -
一个最简单的项目框架搭建成功,我们可以使用命令
node server/bin/www
来启动项目。 这里更推荐使用的项目启动方式是pm2
,它可以让项目运行在一个单独的进程中,不妨碍我们其他的操作,甚至可以同时启动多个项目,管理也很方便(但有时候看不到详细的报错信息)。 可以npm install pm2 -g
来安装,并使用pm2 start server/bin/www
启动,使用pm2 stop server/bin/www
或接all
停止项目。 -
在
server
目录下新建一个models目录,用来存放数据模型。如goods.js
,存放商品数据字段约束和mongodb的Schema
模型并向外暴露,会自动在数据库中关联商品集合goods
。 -
在
server/routes
目录下新建一个goods.js
,引入数据模型,写入路由信息,并把数据的获取接口等写在里面,记得向外暴露,然后在app.js
中导入并use
。 -
由于后端数据接口和前端页面的端口号不同,因此存在跨域问题。
axios
插件不支持jsonp
请求,我们可以在config
下的index.js
中找到proxyTable
,使用代理转发请求解决跨域问题。
-
在后台通过mongoose写好访问
mongodb
的接口,参数有page
、pageSize
、sort
、skip
等,在前台访问数据接口时只需带上这些参数就可获取到符合规则的数据, 直接渲染即可,用户可以通过点击价格区间、升降序按钮来控制查询参数。 -
这里商品分页使用到了一个类似瀑布流加载的插件:
vue-infinite-scroll
,详细用法去npm官网查看即可,注意使用busy
防止无限重复加载请求。 -
对于每个商品的加载,使用了插件
vue-lazyload
,可以选择某图案在商品加载完毕之前显示。这里遇到一个小坑,当页面发出排序过滤请求,数据发生变化,需要动态切换图片时,由于插件的原因,图片并不会发生改变,只是价格名称等信息变化。
这时需要给图片标签中加上一个
:key
属性,值也为图片路径,才可以正常切换显示。经测试,1.0.4的版本不会出现这个问题,但是1.1/1.2的版本都有这个问题。
-
当前台点击加入购物车按钮,会将商品id和用户id数据post到后端接口,后端获取到数据,到
mongodb
的users
集合中查找对应用户的cartList
; -
再到
cartList
中看是否已存在productId
:-
如果已存在,就在原来的数量上加1;
-
不存在则用
productId
去goods
集合中拿到数据,并在此基础上添加productNum
和checked
字段,插入到cartList
。
-
-
注意这里,用
mongoose
获取到的doc数据,是属于document
对象,如果直接用doc.checked=1
的形式来添加属性,必须save()
才能添加成功。为了不影响
goods
中的原有数据,同时更新users
的cartList
数据,需要用doc.toObject()
方法转成普通JS对象,添加数据,再把该对象push
进入cartList
并save
即可。
-
用户相关的所有数据存放在
mongodb
中的users
集合中,当用户输入账号密码,使用post
请求传到后台;后台使用
mongoose
的findOne()
方法,将账号密码和数据库中数据进行匹配,如果匹配成功,设置好cookie,返回用户名等信息。 -
前台用获取到的用户数据渲染页面,把顶部的
login
替换掉,显示用户名。为了合理利用cookie,每次刷新重新加载页面时,mounted
中要请求接口数据,看是否已登录。 -
登出,将cookie的值设为空,将maxAge设为-1,即清除掉用户cookie。
-
在后台
app.js
中添加登录拦截,如果用户未登录,页面中除商品数据、登录、登出的接口外,都不可访问,弹出登录提示框。 -
对于页面常用的部分,可以抽取封装成组件,比如加入购物车时出现的模态框,注意父子组件的数据传递。
-
这里的购物车列表显示,当
mounted
状态时,自动调取post请求获取数据渲染页面。 -
商品数据中的
checked
字段派上用场,当点击勾选按钮,首先将checkFlag
值取反,同时icon-ok
图标的class
状态会切换,然后发出请求,相应改变数据库中该字段的值。 -
对于全选的按钮,使用
computed
计算属性,自动判断勾选的选项数量是否等于所有项目数量,然后变化checkAllFlag
的值。 -
这里用到了过滤器
currency
,网上有写好的可以引过来用。局部使用写在组件的filters
中,全局引用通过Vue.filter
写在main.js
中。 -
购物车中删除商品,用到了我们之前定义的模态框组件。
-
为下一步进入地址和订单界面做准备,如果计算属性
checkedCount
大于0,才可以进行按钮的点击跳转,这里用到了编程式路由this.$router.push()
跳转。
-
在购物车界面中点击跳转,到收货地址界面,调用
mounted
中定义的方法,从数据库users
集合的addressList
中获取数据,并渲染页面。 -
这里有设置默认地址的选项,当在某个地址选项框点击
Set Default
,会将该地址的addressId
传到后台接口,后台会把该地址的isDefault
属性设为true
,将其他地址置为false
。 -
每次加载页面会自动选中(
class
为check
)默认的地址框,这里用计算属性来处理,自动判断地址数据中的default
字段,获取值为true
地址的索引值和id
,可以在init
函数时使用。 -
对于计算属性,已经使用多次,实用性不言而喻。注意它的
return
,不能直接在子作用域中(如forEach中)返回,否则无法取值,需要先声明一个temp值来传递。 -
这里删除地址确认时,同样也用到模态框组件。在点击下一步跳转时,可以使用命名路由跳转
router-link
,如果需要做一些较复杂的操作建议使用编程式路由跳转。
-
在上一步收货地址确认后,会把
addressId
参数随路由传过来,在订单确认页面时,只是把购物车选中的数据进行展示,并给出商品总额和运费等相关费用。 -
订单确认页点击下一步后,会将
addressId
参数和totalPrice
一起传给后端接口,在后端会把订单号(根据时间戳生成),生成时间,订单商品,订单状态,地址等信息组合成一个订单对象,并保存至数据库中。 -
后台生成订单数据后,同时会返回订单号和总金额给前端,方便下一步操作(如跳转支付界面)。
-
这里由于订单的支付要调用第三方接口,不便模拟,所以后台清空购物车选中的商品,并把订单数据传给前台,前台直接使用编程式路由,把订单号传递到订单成功页。
-
在订单成功页,直接显示订单号和总支付金额,并提供两个选项,查看购物车,回到商品列表。