Vue电商项目实战(一)

一、开发前准备

1.1 使用脚手架新建vue项目

vue create vue-mart

1.2 安装cube-ui

cube-ui 是滴滴2017年底开源的一款基于 Vue.js 2.0 的移动端组件库,主要核心目标是做到体验极致、灵活性强、易扩展以及提供良好的周边生态—后编译。

Cubi-ui快速入门:https://didi.github.io/cube-ui/#/zh-CN/docs/quick-start

如果使用的是vue3.0以上版本,可以在项目目录下执行一下命令安装cube-ui插件。

vud add cube-ui

在这里插入图片描述在这里插入图片描述
安装成功后,在项目中可以看到cube-ui.js文件。该文件把cube-ui的所有组件一次性导入。
在这里插入图片描述

1.3 安装axios和vuex

vue add axios
vue add vuex

二、用户登录

2.1 构建登录页

修改router/index.js文件,添加登录页的路由配置。

{
  path: '/login',
  name: 'login',
  component: Login,
},

然后在views目录中新建Login.vue。然后使用cube-form组件定义表单。

cube-form定义格式:

<cube-form
  :model="model"
  :schema="schema"
  :immediate-validate="false"
  :options="options"
  @validate="validateHandler"
  @submit="submitHandler"
  @reset="resetHandler"></cube-form>

其中,model就是整个表单需要的数据源,schema 就是生成表单所定义的模式,submit 校验成功后提交事件,validate 每次有数据校验更新的事件。

登录表单完整代码:

<template>
    <div>
        <cube-form :model="model"
            :schema="schema"
            @submit="handleLogin"
            @validate="handleValidate"></cube-form>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                model: { // 定义数据模型
                    username: '',
                    password: '',
                },
                schema: { // 定义表单结构
                    fields: [
                        {
                            type: 'input',
                            modelKey: 'username',
                            label: '用户名',
                            props: { // INPUT标签属性
                                placeholder: '请输入用户名'
                            },
                            rules: { // 校验规则
                                required: true
                            },
                            trigger: 'blur', // 触发器
                            messages: {
                                required: '用户名不能为空'
                            }
                        },
                        {
                            type: 'input',
                            modelKey: 'password',
                            label: '密码',
                            props: { // INPUT标签属性
                                placeholder: '请输入密码',
                                type: 'password',
                                eye: { // 小眼睛
                                    open: false, // 关闭眼睛
                                }
                            },
                            rules: { // 校验规则
                                required: true
                            },
                            trigger: 'blur', // 触发器
                            messages: {
                                required: '密码不能为空'
                            },
                        },
                        {
                            type: 'submit',
                            label: '登录',
                        },
                    ]
                }
            }
        },  
        methods: {
            async handleLogin(e) {
                console.log('登录');
            },
            handleValidate(ret) {
                console.log('校验结果:', ret);
            },
        }
    }
</script>

<style lang="scss" scoped>

</style>

cube-form的详细用法:https://didi.github.io/cube-ui/#/zh-CN/docs/form

2.2 登录事件处理

在main.js文件中定义全局的axios。

Vue.prototype.$http = axios

在Login.vue中实现handleLogin方法。

async handleLogin(e) {
    e.preventDefault();
    // 发送登录请求
    const res = await this.$http.get('/api/login', {
        params: {
            username: this.model.username,
            password: this.model.password,
        }
    });
    // 把响应结果结构出来
    const {code, token, message} = res.data;
    if (code == 0) {
        // 登录成功
        localStorage.setItem('token', token); // 把token存储在本地
        this.$store.commit('setToken', token); // 更新state

        // 登录成功后回调到指定页面
        const redirect = this.$route.query.redirect || '/';
        this.$router.push(redirect);
    } else {
        // 登录失败
        const toast = this.$createToast({
            time: 2000,
            txt: message || '登录失败',
            type: 'error'
        });
        toast.show();
    }
},

2.3 后台登录实现

后台登录返回结果的数据格式:

{
	code: ''
	token: ''
	message: ''
}

code:登录状态码,0代表登录成功,-1代表登录失败,可自行定义;
token:登录成功后生成的令牌;
message:登录失败的信息;

在vue.config.js中mock登录数据:

// mock数据
devServer: {
  before(app) {
    // 登录
    app.get('/api/login', function(req, res) {
      const {username, password} = req.query; // post请求在body中取出参数
      if (username === 'admin' && password === '123') {
        res.json({
          code: 0,
          token: 'jilie',
        });
      } else {
        res.json({
          code: -1,
          message: '用户名或密码错误',
        });
      }
    });
  }
}

三、定义store

在store中存储用户token。

export default new Vuex.Store({
  state: {
    token: localStorage.getItem('token') || '', // 令牌
  },
  mutations: {
    setToken(state, token) {
      state.token = token;
    }
  },
  getters: {
    isLogin(state) {
      return !!state.token; 
    }
  },
  actions: {
  },
  modules: {
  }
})

上面在getters中定义了isLogin方法,用户判断用户是否登录。!!state.token中的双感叹号表示把state.token转换为布尔值。如果token为空,则转换为false,否则返回true。

四、首页登录注销按钮

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link> |
      <router-link to="/login" v-if="!isLogin">Login</router-link>
      <a v-if="isLogin" @click="logout">logout</a>
    </div>
    <router-view/>
  </div>
</template>

<script>
import {mapGetters} from 'vuex';

export default {
  computed: {
    ...mapGetters(['isLogin'])
  },
  methods: {
    async logout() {
      await this.$http.get('/api/logout');
    }
  }
}
</script>

<style>

</style>

上面增加了登录和注销按钮的逻辑判断。如果state的isLogin方法返回true,则显示Logout;否则显示Login。

五、拦截器

5.1 创建拦截器

第一步:在src目录下新建http-interceptor.js文件。

第二步:在main.js文件引入http-interceptor。

import interceptor from './http-interceptor' // 引入拦截器,只会执行一次

5.1 请求拦截器

请求拦截器用于在发送请求前加入token参数到请求头中。

import axios from 'axios';
import store from './store';
import router from './router';

axios.interceptors.request.use(
    config => {
        if (store.state.token) {
            // 如果token存在,则放入请求头中
            config.headers.token = store.state.token;
        }
        return config;
    }
);

5.2 响应拦截器

响应拦截器用于提前预处理响应。

axios.interceptors.response.use(
    response => {
        // 如果code等于-1,代表用户已注销或token已过期
        // 此时需要重新登录,并且清除本地的缓存信息。
        if (response.status == 200) {
            if (response.data.code == -1) {
                clearHandler();
            }
            return response;
        }
    }
);

// 清除用户登录状态,并跳转登录页
function clearHandler() {
    store.commit('setToken', ''); // 清除store中的token
    localStorage.removeItem('token'); // 清除localStorage中的token
    // 跳转到登录页面
    router.push({
        path: '/login',
        redirect: router.currentRoute.path, // 登陆后重定向回当前path
    });
}

六、用户注销

在vue.config.js文件中模拟/api/logout。

// 注销
app.get('/api/logout', function(req, res) {
  res.json({code: -1});
});

七、添加守护

7.1 路由前守护

修改index.js文件,调用router.beforeEach函数,该函数在访问路由之前进行过滤操作。

import store from '../store'

// 守护
router.beforeEach((to, from, next) => {
  if (to.meta.auth) {
    // 需要认证,则检查令牌
    if (store.state.token) { // 如果存在令牌则放行
      next();
    } else { // 如果不存在令牌,则重定向到登录页面
      next({
        path: '/login',
        query: {
          redirect: to.path
        }
      });
    }
  } else {
    next();
  }
});

修改router/index.js文件,在菜单路由配置项中指定meta属性。如果meta.auth为false,代表该路由不需要认证;如果meta.auth为true,代表该路径需要认证。

const routes = [
  {
    path: '/',
    name: 'home',
    component: Home,
    meta: {
      auth: false,
    },
  },
  {
    path: '/login',
    name: 'login',
    component: Login,
    meta: {
      auth: false,
    },
  },
  {
    path: '/about',
    name: 'about',
    meta: {
      auth: true,
    },
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

7.2 响应前守护

在vue.config.js中定义服务端中间件,该中间件在响应数据前先被调用。

app.use(function(req, res, next) {
  // 检查token
  if (/^\/api/.test(req.path)) {
    if (req.path === '/api/login' || req.headers.token) {
      next();
    } else {
      // 提示用户登录
      res.sendStatus(401); 
    }
  } else {
    next();
  }
});

修改http-interceptor.js文件,配置响应拦截器的失败处理事件。

axios.interceptors.response.use(
    response => {
        ...
    }, err => { // 服务器响应失败
        if (err.response.status === 401) {
            console.log('用户未认证');
            clearHandler();
        }
    }
);
发布了111 篇原创文章 · 获赞 41 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/zhongliwen1981/article/details/103346532