内容简介:本文介绍如何使用安装好mongodb 并启动, 新建一个后端项目目录, 目录结构如下后,安装需要用到的库
本文介绍如何使用 MERN
技术栈开发一个前后端分离的电商项目,水平有限,不足之处,请指出, Github 。
后端开发
安装好 mongodb 并启动, 新建一个后端项目目录, 目录结构如下
当然也可以按自己的方式建立, 执行npm init
后,安装需要用到的库
npm i express mongoose multer validator jsonwebtoken dotenv cors bcrypt -S
图片上传 multer
, 验证表单数据 validator
, 配置环境变量 dotenv
, 跨域处理 cors
新建 .env
文件,在里面配置数据库等参数
DB_HOST=localhost DB_PORT=27017 DB_NAME=cake-shop JWT_KEY=my_jwt_key PORT=9090 HOSTNAME=http://localhost 复制代码
接着在 models 目录下定义数据模型 product.js
代表产品,其他同理
// product.js const mongoose = require('mongoose') const Schema = mongoose.Schema const productSchema = new Schema( { name: { type: String, required: true, default: '' }, description: { type: String, default: '' }, price: { type: Number, required: true }, stock: { type: Number, default: 0 }, imgList: { type: Array, default: '' }, category: { type: Array }, top: { type: Boolean, default: false }, rate: { type: Number, default: 5.0 }, publish: { type: Boolean, default: false } }, { timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' } } ) module.exports = mongoose.model('Product', productSchema) 复制代码
在 routes 目录下建立 product.js 用来定义产品相关的路由,建立 index.js
将 product.js
路由导出, 然后在 controllers 目录下建立 product.js 用来处理产品相关的路由, 然后在入口文件中使用即可
// routes/product.js const express = require('express') const router = express.Router() const auth = require('../middleware/auth') const controller = require('../controllers/product') router.get('/', controller.getAllProducts) router.get('/top', controller.getTopProducts) router.get('/recommend', controller.getRecommendProducts) router.get('/detail/:productId', controller.getProductDetail) router.get('/:sort', controller.getProducts) router.post('/', controller.addProduct) router .route('/:productId') .put(auth, controller.updateProduct) .delete(auth, controller.deleteProduct) module.exports = router 复制代码
// index.js const routes = require('./routes') ..... app.use('/api/product', routes.product) 复制代码
在 controller 里面编写路由对应的逻辑代码,用 Postman 将代码逻辑跑通,用户注册时要对字段进行验证,以及需要对用户密码加密,用户登录时要将 jsonwentoken
生成的token 返回给前端
// controllers/user.js // 注册 async function signUp(req, res, next) { let { name, email, password } = req.body if (!isVerifiedField(name)) { return res.status(400).json({ success: false, message: '请输入字符长度大于4的用户名' }) } if (!isVerifiedEmail(email)) { return res.status(400).json({ success: false, message: '请输入正确的邮箱地址' }) } if (!isVerifiedField(password)) { return res.status(400).json({ success: false, message: '请设置长度不小于4个字符的密码' }) } const oldUser = await User.findOne({ email }).exec() // 检验用户是否已存在 if (oldUser !== null) { return res.status(409).json({ success: false, message: '用户已存在' }) } password = await bcrypt.hash(password, 10) // 对用户密码加密 const newUser = new User({ name, email, password }) newUser .save() .then(result => { return res.status(201).json({ success: true, result }) }) .catch(error => { return res.status(500).json({ success: false, error }) }) } // 登录 async function login(req, res, next) { const { name, email, password } = req.body if (name) { checkField({ name }) } if (email) { checkField({ email }) } async function checkField(field) { const user = await User.findOne(field).exec() if (user === null) { return res.status(404).json({ success: false, message: '用户不存在' }) } const isMatch = await bcrypt.compare(password, user.password) if (isMatch) { const token = jwt.sign({ field, id: user._id }, process.env.JWT_KEY) // 生成token return res.status(200).json({ success: true, message: '登录成功', token }) // 返回token } else { return res.status(401).json({ success: false, message: '密码错误' }) } } } 复制代码
管理后台
使用 create-react-app
建立项目,使用 yarn
安装需要的依赖包
npx create-react-app you-project
yarn add antd react-router-dom axios
根据 create-react-app
的 User Guide 配置 CSS 预处理器等
建立项目目录结构
配置 axios
import axios from 'axios' const token = localStorage.getItem('CAKE_SHOP_AUTH_TOKEN') const Request = axios.create({ baseURL: 'http://localhost:9090/api', timeout: 5000, headers: { authorization: token ? token : '' // 如果有token就在请求headers里面带上 } }) export default Request 复制代码
在 pages 目录下建立管理员注册,登录等页面,用户登录后把后端返回的token放到 localStorage
里面,然后跳转到工作台首页
// pages/Login import React, { Component } from 'react' import { withRouter } from 'react-router-dom' import { Layout, Form, Icon, Input, Button, Checkbox, message as Message } from 'antd' import '../account.css' import { ManagerContext } from '../../store/manager' import ManagerService from '../../services/manager' import { CAKE_SHOP_AUTH_TOKEN, CAKE_SHOP_USER_INFO } from '../../constant' const { Item } = Form const { Content, Footer } = Layout class Login extends Component { handleSubmit = e => { e.preventDefault() this.props.form.validateFields(async (err, values) => { if (!err) { const { name, password } = values ManagerService.login(name, password) // 请求登录接口 .then(res => { const { history, login } = this.props // history 对象来自于 react-router-dom const { message, token, manager } = res.data Message.success(message) localStorage.setItem(CAKE_SHOP_AUTH_TOKEN, `Bearer ${token}`) localStorage.setItem(CAKE_SHOP_USER_INFO, JSON.stringify(manager)) login(manager) history.push('/dashboard') }) .catch(error => { const { data } = error.response Message.error(data.message) }) } }) } render() { const { getFieldDecorator } = this.props.form return ( <ManagerContext.Consumer> {login => ( <Layout className="account" login={login}> <Content className="account__content"> <h1 className="account__title">店铺管理系统</h1> <sub className="account__sub-title">登录</sub> <Form className="account__form" onSubmit={this.handleSubmit}> <Item> {getFieldDecorator('name', { rules: [{ required: true, message: '请输入你的用户名!' }] })( <Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username admin" /> )} </Item> <Item> {getFieldDecorator('password', { rules: [{ required: true, message: '请输入你的密码!' }] })( <Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password admin" /> )} </Item> <Item> <Button type="primary" htmlType="submit" block> 登录 </Button> </Item> <Item> {getFieldDecorator('remember', { valuePropName: 'checked', initialValue: true })(<Checkbox>记住我</Checkbox>)} </Item> </Form> </Content> <Footer className="account__footer"> <a className="account__link" href="https://xrr2016.github/io" target="_blank" rel="noopener noreferrer" > <Icon type="github" /> </a> <a className="account__link" href="mailto:xiaoranran1993@outlook.com"> <Icon type="mail" /> </a> </Footer> </Layout> )} </ManagerContext.Consumer> ) } } export default withRouter(Form.create()(Login)) 复制代码
在 routes 目录下建立工作台里面的子路由页面,编写对应逻辑, 处理不同的业务, 最后在入口文件中定义 react-router-dom
的路由
前端页面
同样使用 create-react-app
建立项目,使用 yarn
安装需要的依赖包
npx create-react-app you-project
yarn add antd-mobile react-router-dom axios
建立项目目录结构
这里的逻辑跟管理后台主要的区别在于请求的数据接口不同,以及页面的UI不同,具体实现,UI交互等按个人而定。
部署
功能开发完毕后,使用 yanr build
将前端以及管理后台项目打包,将代码传到服务器上,配置不同的域名,使用 nginx
进行反向代理,防止刷新浏览器后404。
location / { try_files $uri $uri/ /index.html; } 复制代码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 「Flask实战」鱼书项目实战一
- 「Flask实战」鱼书项目实战三
- 「Flask实战」鱼书项目实战四
- 「Flask实战」鱼书项目实战六
- 「Flask实战」flask鱼书项目实战二
- go语言实战教程:Redis实战项目应用
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
JS 压缩/解压工具
在线压缩/解压 JS 代码
在线进制转换器
各进制数互转换器