WEB实战:使用MERN技术栈开发项目

栏目: Node.js · 发布时间: 8年前

内容简介:本文介绍如何使用安装好mongodb 并启动, 新建一个后端项目目录, 目录结构如下后,安装需要用到的库

本文介绍如何使用 MERN 技术栈开发一个前后端分离的电商项目,水平有限,不足之处,请指出, Github 。

后端开发

安装好 mongodb 并启动, 新建一个后端项目目录, 目录结构如下

WEB实战:使用MERN技术栈开发项目
当然也可以按自己的方式建立, 执行 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 预处理器等

建立项目目录结构

WEB实战:使用MERN技术栈开发项目

配置 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 的路由

WEB实战:使用MERN技术栈开发项目

前端页面

同样使用 create-react-app 建立项目,使用 yarn 安装需要的依赖包

npx create-react-app you-project

yarn add antd-mobile react-router-dom axios

建立项目目录结构

WEB实战:使用MERN技术栈开发项目

这里的逻辑跟管理后台主要的区别在于请求的数据接口不同,以及页面的UI不同,具体实现,UI交互等按个人而定。

源码地址

部署

功能开发完毕后,使用 yanr build 将前端以及管理后台项目打包,将代码传到服务器上,配置不同的域名,使用 nginx 进行反向代理,防止刷新浏览器后404。

location / {
  try_files $uri $uri/ /index.html;
}
复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

复杂网络理论及其应用

复杂网络理论及其应用

汪小帆、李翔、陈关荣 / 清华大学出版社 / 2006 / 45.00元

国内首部复杂网络专著 【图书目录】 第1章 引论 1.1 引言 1.2 复杂网络研究简史 1.3 基本概念 1.4 本书内容简介 参考文献 第2章 网络拓扑基本模型及其性质 2.1 引言 2.2 规则网络 2.3 随机图 2.4 小世界网络模型 2.5 无标度网络模型 ......一起来看看 《复杂网络理论及其应用》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码