Vue 版商城 (二)
Vue 版商城技术详情
Vue
Vue 配合 Vue-Router,Vuex
实现路由跳转,路由过渡,状态管理
Vue-lazyload
头像图片懒加载
Vue-infinite-scroll
滚动加载
Vue-pull-to-refresh
自己开发的下拉刷新(实现插件)
Axios
实现 api 请求等的异步处理
Express
后端服务器
获取数据 get 请求
操作数据 post 请求
利用中间件,路由实现后端逻辑
MongoDB
数据库
MongoDB 是一个基于文档的数据库,所有数据是从磁盘上进行读写的。
MongoDB 善长的是对无模式 JSON 数据的查询。
MongoDB 旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。
MongoDB 将数据存储为一个文档,数据结构由键值 (key=>value) 对组成。
MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
Robomongo
可视化 MongeDB 工具
Mongoose
连接数据库和 Express
使用 promise 的调用方式
const mongoose = require('mongoose')
const Schema = mongoose.Schema
mongoose.Promise = global.Promise
const productSchema = new Schema({
"productId": String,
"productName": String,
"salePrice": Number,
"productImage": String,
"checked": Number,
"productNum": Number
})
module.exports = mongoose.model('Good', productSchema)
Api
插入数据
save
更新数据
update
查找
find
查找并更新
findByIdAndUpdate
移除
remove
数量查询
count
根据_id 查询
findById
变量
$or 或关系
$nor 或关系取反
$gt 大于
$gte 大于等于
$lt 小于
$lte 小于等于
$ne 不等于
$in 在多个值范围内
$nin 不在多个值范围内
$all 匹配数组中多个值
$regex 正则,用于模糊查询
$size 匹配数组大小
$maxDistance 范围查询,距离(基于 LBS)
$mod 取模运算
$near 邻域查询,查询附近的位置(基于 LBS)
$exists 字段是否存在
$elemMatch 匹配内数组内的元素
$within 范围查询(基于 LBS)
$box 范围查询,矩形范围(基于 LBS)
$center 范围醒询,圆形范围(基于 LBS)
$centerSphere 范围查询,球形范围(基于 LBS)
$slice 查询字段集合中的元素(比如从第几个之后,第 N 到第 M 个元素)
使用
const Goods = require('../models/goods.js')
mongoose.Promise = global.Promise
var env = process.env.NODE_ENV || 'development'
if(env === 'development') {
console.log('NODE_ENV:', env)
mongoose.connect('mongodb://IP:port/shopdb')
}
else {
console.log('NODE_ENV:', env)
mongoose.connect('mongodb:// 用户. 数据库: password@IP:port/shopdb')
}
router.get('/list', (req, res, next) => {
const sort = req.query.sort
const page = req.query.page
const pageSize = req.query.pageSize
const priceChecked = req.query.priceChecked
let params = {}
let priceGt = '', priceLte =''
if(priceChecked !== 'all') {
switch(priceChecked) {
case '0': priceGt = 0; priceLte=500; break;
case '1': priceGt = 500; priceLte=1000; break;
case '2': priceGt = 1000; priceLte=2000; break;
case '3': priceGt = 2000; priceLte=4000; break;
}
params = {
salePrice: {
$gt: priceGt,
$lte: priceLte
}
}
console.log(params)
}
const skip = (page - 1) * pageSize;
const goodsModel = Goods.find(params).skip(skip).limit(parseInt(pageSize))
goodsModel.sort({'salePrice': sort})
goodsModel.exec((err, doc) => {
if(err) {
res.json({
status: '404',
msg: err.message
})
}
else {
res.json({
status: '200',
msg: 'OK',
result: {
count: doc.length,
list: doc
}
})
}
})
})
根据环境连接数据库
var env = process.env.NODE_ENV || 'development'
var sessionUrl = ''if(env ==='development') {
console.log('app.NODE_ENV:', env)
sessionUrl = 'mongodb://IP:port/shopdb'
}
else {
console.log('app.NODE_ENV:', env)
sessionUrl = 'mongodb:// 用户. 数据库: password@IP:port/shopdb'
}
Nginx
代理转发
upstream vue {
server 本地 IP:port;
}
server {
listen port;
# server_name *. 主域名;
server_name 外部 IP;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Nginx-Proxy true;
proxy_pass http://vue;
proxy_redirect off;
}
location /goods/ {
proxy_pass http:// 本地 IP:port/goods/;
}
location /users/ {
proxy_pass http:// 本地 IP:port/users/;
}
location /vueshop/ {
alias 路径;
}
location ~* ^.+\.(jpg|jpeg|gif|png|svg|ico|webp|css|js|map|pdf|txt)$ {
root 路径;
}
}
开启 gzip 压缩
sudo vi /etc/nginx/nginx.conf
##
# Gzip Settings
##
gzip on;
gzip_disable "msie6";
# 启用 gzip 压缩的最小文件,小于设置值的文件将不会压缩
gzip_min_length 1k;
# 是否在 http header 中添加 Vary: Accept-Encoding,建议开启
gzip_vary on;
# gzip_proxied any;
# gzip 压缩级别,1-10,数字越大压缩的越好,也越占用 CPU 时间
gzip_comp_level 4;
gzip_buffers 16 8k;
# gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
PM2
做守护进程, 发布更新项目
使用 ecosystem.json
pm2 deploy ecosystem.json start
可以配置服务器 ip,指定 github 仓库
实现代码更新后快速部署到服务器中
{
"apps":[
{
"name":"vueshop",
"script":"入口脚本",
"env": { // 传进去的变量
"COMMON_VARIABLE": "true"
},
"env_production": {
"NODE_ENV": "production"
}
}
],
"deploy": { // 部署任务
"start": { // 任务名
"user": "",// 服务器上用来发布应用的 user"host": ["IP"],"port":"port","ref":"origin/master","repo":"git 仓库 ","path":"", // 服务器项目位置
"ssh_options": "StrictHostKeyChecking=no", // 取消 key 校验
"post-deploy": "执行的命令行 && pm2 startOrRestart ../ecosystem.json --env production",
"env": {
"NODE_ENV": "production"
}
}
}
}
Echarts
数据可视化显示
显示价格趋势,数据来自数据库(自己编的)
显示订单详情
Svg-captcha
验证码
在考虑要使用验证码时, 发现许多相关的 npm 模块都要
安装其它支持库(C 或 C++ 编写,而且平台兼容性极差)
最终在 CNode 社区发现了以 svg 为基础的 Svg-captcha
内部使用 opentype.js , 把字符转换为 svg 路径,然后生成验证码
简单易用
const svgCaptcha = require('svg-captcha')
router.get('/captcha', (req, res, next) => {
const captcha = svgCaptcha.create({
background: "#3E96D6"
})
req.session.captcha = captcha.text
res.set('Content-Type', 'image/svg+xml')
res.json({
status: '200',
msg: 'OK',
result: captcha
})
})
router.get('/isCaptchaTrue', (req, res, next) => {
const captcha = req.query.captcha.toLowerCase()
if (req.session.captcha.toLowerCase() !== captcha) {
res.json({
status: '500',
msg: '验证码有错',
result: ''
})
return
} else {
res.json({
status: '200',
msg: '验证码正确',
result: ''
})
}
})
session
express 在 4.x 版本之后,session 管理和 cookies 等许多模块都不再直接包含在 express 中,
而是需要单独添加相应模块。
express4 中操作 cookie 使用 cookie-parser 模块
express4 中操作 session 使用 express-session 模块
session 的运作通过一个 session_id 来进行。session_id 通常是存放在客户端的 cookie 中,
比如在 express 中,默认是 connect.sid 这个字段,当请求到来时,
服务端检查 cookie 中保存的 session_id 并通过这个 session_id 与服务器端的 session data 关联起来,
进行数据的保存和修改。
这意思就是说,当你浏览一个网页时,服务端随机产生一个 1024 比特长的字符串,
然后存在你 cookie 中的 connect.sid 字段中。
当你下次访问时,cookie 会带有这个字符串,然后浏览器就知道你是上次访问过的某某某,然
后从服务器的存储中取出上次记录在你身上的数据。
由于字符串是随机产生的,而且位数足够多,所以也不担心有人能够伪造。
伪造成功的概率比坐在家里编程时被邻居家的狗突然闯入并咬死的几率还低。
session 可以存放在 1)内存、2)cookie 本身、3)redis 或 memcached 等缓存中,或者 4)数据库中。
使用 session 验证用户
session 存储在数据库中
app.js
配置
const session = require('express-session')
const mongoStore = require('connect-mongo')(session)
app.use(session({
secret: 'recommand 128 bytes random string', // 建议使用 128 个字符的随机字符串
// 作为服务器端生成 session cookie 的签名 ,防止篡改 ,
// 通过设置的 secret 字符串,来计算 hash 值并放在 cookie 中,使产生的 signedCookie 防篡改。
name: 'user',
// cookie 的名字 保存 session 的字段名称, 返回客户端的 key 的名称,默认为 connect.sid, 也可以自己设置。
resave: false,
// 强制保存 session 即使它并没有变化 (默认: true)
//saveUninitialized:
// 初始化 session 时是否保存到存储。默认为 true, 但是 (后续版本) 有可能默认失效,所以最好手动添加。
cookie: { maxAge: 60 * 1000 * 60 * 24 * 7 },
//session cookie 设置 设置返回到前端 key 的属性,默认值为{ path: ‘/’, httpOnly: true, secure: false, maxAge: null }。
//genid - 生成新 session ID 的函数 (默认使用 uid2 库)
//rolling: false,
// 在每次请求时强行设置 cookie,这将重置 cookie 过期时间(默认:false)
//proxy: true,
// 当设置了 secure cookies(通过”x-forwarded-proto” header )时信任反向代理。当设定为 true 时,
//”x-forwarded-proto” header 将被使用。
// 当设定为 false 时,所有 headers 将被忽略。当该属性没有被设定时,将使用 Express 的 trust proxy。
saveUninitialized: false,
// 强制将未初始化的 session 存储。当新建了一个 session 且未设定属性或值时,它就处于
// 未初始化状态。在设定一个 cookie 前,这对于登陆验证,减轻服务端存储压力,权限控制是有帮助的。(默认:true)
// unset
// 控制 req.session 是否取消(例如通过 delete,或者将它的值设置为 null)。这可以使 session 保持存储
// 状态但忽略修改或删除的请求(默认:keep)
//session 存储实例
store: new mongoStore({
// db: 'VueShop',
url: sessionUrl,
collection: 'sessions'
})
}))
使用
routes
user.js
// 登录
router.post('/login', (req, res, next) => {
const captcha = req.body.captcha.toLowerCase()
console.log(captcha)
console.log(req.session.captcha.toLowerCase())
if (req.session.captcha.toLowerCase() !== captcha) {
res.json({
status: '500',
msg: '验证码有错',
result: ''
})
return
}
console.log('param')
const param = {
userName: req.body.userName,
userPwd: req.body.userPwd
}
User.findOne(param)
.then(doc => {
if (doc) {
res.cookie("userId", doc.userId, {
path: '/',
maxAge: 1000 * 60 * 60 * 24 * 7
})
res.cookie("userName", doc.userName, {
path: '/',
maxAge: 1000 * 60 * 60 * 24 * 7
})
req.session.user = {
userName: doc.userName,
userId: doc.userId
}
res.json({
status: '200',
msg: 'OK',
result: {
userName: doc.userName
}
})
} else {
res.json({
status: '400',
msg: '用户名或者密码错误',
result: ''
})
}
})
.catch(err => {
res.json({
status: '404',
msg: err.message,
result: ''
})
})
})
许可协议: "署名-非商用-相同方式共享 4.0"
本文链接:http://ldq-first.github.io/2017/08/18/Vue版商城-二/