Some time ago, I was discussing his career path with a colleague in the company who had switched from technology to product, and he said something that I deeply believed in:
"Don't confine yourself to a certain field. The transformation from technology to product is first of all a change in thinking. You have been working on the front end. When interacting with data, you only know how to enter it, but you don't know how it comes out. This is a limitation. ”
It was like an enlightenment. When I was learning Vue, I saw a registration and login project. I simply followed suit and started a Vue project, introduced koa and mongodb, and implemented client submission-server reception return-input. The entire database process.
This project is built based on vue-cli, uses token method to verify user login, and implements functions such as registering into the database, reading users, and deleting users. The article assumes that readers have a certain foundation in node and vue, so the basic parts will not be described in detail.
System environment: MacOS 10.13.3
Install using Taobao mirror
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
Then change all npm install to cnpm install
In order to make the project ideas and selected technologies clearer, a diagram is drawn for easy understanding.
1.Initialize the project
$ npm install
2. Start the project
$ npm run dev
3. Start MongoDB
$ mongod --dbpath XXX
xxx is the path to the data folder in the project (you can also create a new one, the database is used to store data), or you can drag it directly into the terminal.
4. Start the server
$ node server.js
I chose Ele.me’s Element-UI as my preferred UI library for vue. Others such as iview and vue-strap don’t seem to be as comprehensive as ele.
$ npm i element-ui -S
//在项目里的mian.js里增加下列代码
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
Use the tab switching in the UI to switch between the registration and login interfaces, use the login component as the main interface of the entire login system, and the register component as an independent component. Please refer to the official website for the composition method of Element-UI, form validation and other APIs.
//login组件
<template>
<div class="login">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="登录" name="first">
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="名称" prop="name">
<el-input v-model="ruleForm.name"></el-input>
</el-form-item>
<el-form-item label="密码" prop="pass">
<el-input type="password" v-model="ruleForm.pass" auto-complete="off"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">登录</el-button>
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="注册" name="second">
<register></register>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import register from '@/components/register'
export default {
data() {
var validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入密码'));
} else {
if (this.ruleForm.checkPass !== '') {
this.$refs.ruleForm.validateField('checkPass');
}
callback();
}
};
return {
activeName: 'first',
ruleForm: {
name: '',
pass: '',
checkPass: '',
},
rules: {
name: [
{ required: true, message: '请输入您的名称', trigger: 'blur' },
{ min: 2, max: 5, message: '长度在 2 到 5 个字符', trigger: 'blur' }
],
pass: [
{ required: true, validator: validatePass, trigger: 'blur' }
]
},
};
},
methods: {
//选项卡切换
handleClick(tab, event) {
},
//重置表单
resetForm(formName) {
this.$refs[formName].resetFields();
},
//提交表单
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$message({
type: 'success',
message: '登录成功'
});
this.$router.push('HelloWorld');
} else {
console.log('error submit!!');
return false;
}
});
},
},
components: {
register
}
}
</script>
<style rel="stylesheet/scss" lang="scss">
.login {
width: 400px;
margin: 0 auto;
}
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
.el-tabs__item {
text-align: center;
width: 60px;
}
</style>
Next is to register the component
//register组件
<template>
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="名称" prop="name">
<el-input v-model="ruleForm.name"></el-input>
</el-form-item>
<el-form-item label="密码" prop="pass">
<el-input type="password" v-model="ruleForm.pass" auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="确认密码" prop="checkPass">
<el-input type="password" v-model="ruleForm.checkPass" auto-complete="off"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">注册</el-button>
<el-button @click="resetForm('ruleForm')">重置</el-button>
</el-form-item>
</el-form>
</template>
<script>
export default {
data() {
var validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入密码'));
} else {
if (this.ruleForm.checkPass !== '') {
this.$refs.ruleForm.validateField('checkPass');
}
callback();
}
};
var validatePass2 = (rule, value, callback) => {
if (value === '') {
callback(new Error('请再次输入密码'));
} else if (value !== this.ruleForm.pass) {
callback(new Error('两次输入密码不一致!'));
} else {
callback();
}
};
return {
activeName: 'second',
ruleForm: {
name: '',
pass: '',
checkPass: '',
},
rules: {
name: [
{ required: true, message: '请输入您的名称', trigger: 'blur' },
{ min: 2, max: 5, message: '长度在 2 到 5 个字符', trigger: 'blur' }
],
pass: [
{ required: true, validator: validatePass, trigger: 'blur' }
],
checkPass: [
{ required: true, validator: validatePass2, trigger: 'blur' }
],
}
};
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$message({
type: 'success',
message: '注册成功'
});
// this.activeName: 'first',
} else {
console.log('error submit!!');
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
}
}
</script>
vue-router is the core of Vue to create single-page projects. Applications can be composed by combining components. What we have to do is to map components to routes and then tell vue-router where to render them. The above code already involves some routing switching. Let’s improve the routing now:
$ cnpm i vue-router
import Router from 'vue-router'
Vue.use(Router)
Create a new router (folder)/index.js under the src folder. We have introduced three components:
HelloWorld display page after login
login login main interface
register register component
Use router.beforeEach routing guard to set the page that needs to be logged in first. Use the requiresAuth field to determine whether the route requires login permissions. Routes that require permissions will be intercepted, and then it will be determined whether there is a token (token will be discussed below). If so, log in directly. If not, jump to the login page.
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import login from '@/components/login'
import register from '@/components/register'
Vue.use(Router)
const router = new Router({
mode: 'history',
routes: [{
path: '/',
name: 'home',
component: HelloWorld,
meta: {
requiresAuth: true
}
},
{
path: '/HelloWorld',
name: 'HelloWorld',
component: HelloWorld,
},
{
path: '/login',
name: 'login',
component: login,
},
{
path: '/register',
name: 'register',
component: register,
},
]
});
//注册全局钩子用来拦截导航
router.beforeEach((to, from, next) => {
//获取store里面的token
let token = store.state.token;
//判断要去的路由有没有requiresAuth
if (to.meta.requiresAuth) {
if (token) {
next();
} else {
next({
path: '/login',
query: { redirect: to.fullPath } // 将刚刚要去的路由path作为参数,方便登录成功后直接跳转到该路由
});
}
} else {
next();
}
});
export default router;
We can see that the token in the routing guard is obtained from the store, which means that we store the various states of the token in the store and perform operations such as retrieval, update, and deletion. This requires the introduction of vuex state management.
Explain why a simple registration and login page requires the use of vuex: The operation of each of our components in the project basically requires obtaining a token for verification. If component A stores a token, component B's acquisition of the token involves component communication. , which would be very tedious. With the introduction of vuex, it is no longer the communication between components, but the communication between the components and the store, which is simple and convenient.
$ cnpm i vuex --S
Introduce store in main.js, and add store to vue instance.
//引入store
import store from './store'
Then introduce it in the components that need to use vuex
//store index.js
import Vuex from 'vuex'
Vue.use(Vuex)
Create a new store (folder)/index.js under the src folder
//store index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
//初始化时用sessionStore.getItem('token'),这样子刷新页面就无需重新登录
const state = {
token: window.sessionStorage.getItem('token'),
username: ''
};
const mutations = {
LOGIN: (state, data) => {
//更改token的值
state.token = data;
window.sessionStorage.setItem('token', data);
},
LOGOUT: (state) => {
//登出的时候要清除token
state.token = null;
window.sessionStorage.removeItem('token');
},
USERNAME: (state, data) => {
//把用户名存起来
state.username = data;
window.sessionStorage.setItem('username', data);
}
};
const actions = {
UserLogin({ commit }, data){
commit('LOGIN', data);
},
UserLogout({ commit }){
commit('LOGOUT');
},
UserName({ commit }, data){
commit('USERNAME', data);
}
};
export default new Vuex.Store({
state,
mutations,
actions
});
You can see that we submit mutations through actions, change tokens, clear tokens, and store usernames.
When you start the project at this time, you can see the preliminary registration and login interface. Click the registration or login button to switch to the corresponding interface, and there is basic form verification. After logging in, you will enter the helloworld page.
We have written the basic interface, and the next step is to send the form data to the background and perform a series of processing. It doesn’t matter if there is no back-end interface yet. Let’s write the front-end axios request first.
Vue's communication used vue-resource before, and there were many pitfalls. Until vue2.0 comes, just abandon vue-resource and use axios .
Encapsulate ajax, used to send requests and obtain data asynchronously. Promise-based HTTP client, suitable for: browser and node.js.
Specific API description in Chinese: https://www.kancloud.cn/yunye/axios/234845
$ cnpm i -S axios
import axios from 'axios'
In the part of setting up vue-router, routing guards are added to intercept routes that require login, but this method is only a simple front-end routing control and cannot really prevent users from accessing routes that require login permissions. When the token expires, the token is still saved locally. At this time, when you access a route that requires login permissions, you should actually ask the user to log in again. At this time, interceptors + the http status code returned by the backend interface are needed to judge.
Create a new axios.js under the src folder (same level as App.vue)
//axios.js
import axios from 'axios'
import store from './store'
import router from './router'
//创建axios实例
var instance = axios.create({
timeout: 5000, //请求超过5秒即超时返回错误
headers: { 'Content-Type': 'application/json;charset=UTF-8' },
});
//request拦截器
instance.interceptors.request.use(
config => {
//判断是否存在token,如果存在的话,则每个http header都加上token
if (store.state.token) {
config.headers.Authorization = `token ${store.state.token}`;
}
return config;
}
);
//respone拦截器
instance.interceptors.response.use(
response => {
return response;
},
error => { //默认除了2XX之外的都是错误的,就会走这里
if (error.response) {
switch (error.response.status) {
case 401:
router.replace({ //跳转到登录页面
path: 'login',
query: { redirect: router.currentRoute.fullPath } // 将跳转的路由path作为参数,登录成功后跳转到该路由
});
}
}
return Promise.reject(error.response);
}
);
export default {
//用户注册
userRegister(data){
return instance.post('/api/register', data);
},
//用户登录
userLogin(data){
return instance.post('/api/login', data);
},
//获取用户
getUser(){
return instance.get('/api/user');
},
//删除用户
delUser(data){
return instance.post('/api/delUser', data);
}
}
The code finally exposes four request methods, which correspond to register, login, obtain (user), and delete (delUser) users, and they are all under /api. The four request interfaces are:
http://localhost:8080/api/login
http://localhost:8080/api/register
http://localhost:8080/api/user
http://localhost:8080/api/delUser
Later we will use these four methods to write the corresponding backend interface.
The article starts from here on the server side. Since the server side needs to be built together with the database and http secure communication (jwt), please read this section in conjunction with the database and jwt chapters below.
koa2 can use async/await syntax to avoid repeated and cumbersome callback function nesting, and use ctx to access the Context object.
Now we use koa2 to write the project's API service interface.
$ cnpm i koa
$ cnpm i koa-router -S //koa路由中间件
$ cnpm i koa-bodyparser -S //处理post请求,并把koa2上下文的表单数据解析到ctx.request.body中
const Koa = require('koa');
Create a new server.js under the project root directory as the startup entry for the entire server.
//server.js
const Koa = require('koa');
const app = new Koa();
//router
const Router = require('koa-router');
//父路由
const router = new Router();
//bodyparser:该中间件用于post请求的数据
const bodyParser = require('koa-bodyparser');
app.use(bodyParser());
//引入数据库操作方法
const UserController = require('./server/controller/user.js');
//checkToken作为中间件存在
const checkToken = require('./server/token/checkToken.js');
//登录
const loginRouter = new Router();
loginRouter.post('/login', UserController.Login);
//注册
const registerRouter = new Router();
registerRouter.post('/register', UserController.Reg);
//获取所有用户
const userRouter = new Router();
userRouter.get('/user', checkToken, UserController.GetAllUsers);
//删除某个用户
const delUserRouter = new Router();
delUserRouter.post('/delUser', checkToken, UserController.DelUser);
//装载上面四个子路由
router.use('/api',loginRouter.routes(),loginRouter.allowedMethods());
router.use('/api',registerRouter.routes(),registerRouter.allowedMethods());
router.use('/api',userRouter.routes(),userRouter.allowedMethods());
router.use('/api',delUserRouter.routes(),delUserRouter.allowedMethods());
//加载路由中间件
app.use(router.routes()).use(router.allowedMethods());
app.listen(8888, () => {
console.log('The server is running at http://localhost:' + 8888);
});
As can be seen in the code, both obtaining users and deleting users require verification tokens (see the jwt chapter below for details), and we have hung the four interfaces on /api, which is consistent with the previous request path of axios.
In addition, since our project startup port is 8080 and the port monitored by the koa interface is 8888, we need to add the following to the dev configuration in the config/index.js file:
proxyTable: {
'/api': {
target: 'http://localhost:8888',
changeOrigin: true
}
},
JWT can help us perform identity authentication during HTTP communication.
For specific API details, please see: https://segmentfault.com/a/1190000009494020
1. The client logs in to the server through user name and password;
2. The server verifies the client’s identity;
3. The server generates a Token for the user and returns it to the client;
4. The client saves the token to the local browser, usually in a cookie (this article uses sessionStorage, it depends on the situation);
5. When the client initiates a request, it needs to carry the Token;
6. After receiving the request, the server first verifies the Token and then returns the data. The server does not need to save the Token, it only needs to verify the information carried in the Token. No matter which server the client accesses in the background, as long as it can pass the verification of user information.
In the server folder, create a new /token (folder) below and add checkToken.js and createToken.js to place methods for checking and adding tokens respectively.
$ cnpm i jsonwebtoken -S
const jwt = require('jsonwebtoken');
module.exports = function(user_id){
const token = jwt.sign({user_id: user_id}, 'zhangzhongjie', {expiresIn: '60s'
});
return token;
};
When creating the token, we use the username as an attribute of the JWT Payload, set the key to 'zhangzhongjie', and set the token expiration time to 60s. This means that after logging in, refreshing the page within 60 seconds does not require logging in again.
const jwt = require('jsonwebtoken');
//检查token是否过期
module.exports = async ( ctx, next ) => {
//拿到token
const authorization = ctx.get('Authorization');
if (authorization === '') {
ctx.throw(401, 'no token detected in http headerAuthorization');
}
const token = authorization.split(' ')[1];
let tokenContent;
try {
tokenContent = await jwt.verify(token, 'zhangzhongjie');//如果token过期或验证失败,将抛出错误
} catch (err) {
ctx.throw(401, 'invalid token');
}
await next();
};
Get the token first and then use jwt.verify to verify it. Note that the key must correspond to the key 'zhangzhongjie' of createToken.js. If the token is empty, expires, or fails verification, a 401 error will be thrown, requiring you to log in again.
MongoDB is a document-oriented database management system designed to provide scalable, high-performance data storage solutions for WEB applications. It is very convenient to use node to connect to MongoDB.
$ cnpm i mongoose -S
There are several ways to connect to MongoDB. Here we use connection. connection is the default reference of the mongoose module and returns a Connection object.
Create a new db.js in the server folder as the database connection entry.
//db.js
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/vue-login');
let db = mongoose.connection;
// 防止Mongoose: mpromise 错误
mongoose.Promise = global.Promise;
db.on('error', function(){
console.log('数据库连接出错!');
});
db.on('open', function(){
console.log('数据库连接成功!');
});
//声明schema
const userSchema = mongoose.Schema({
username: String,
password: String,
token: String,
create_time: Date
});
//根据schema生成model
const User = mongoose.model('User', userSchema)
module.exports = User;
In addition to the connection we use, there are also *connect() and createConnection()* connection methods.
Schema defines the template of the table so that this type of document has a specific composition and storage mode in the database. But it only defines what the Document looks like. As for generating the document and performing various operations on the document (adding, deleting, modifying, and checking), it is done through the corresponding model. Then we need to convert the userSchema into something we can use. Model, that is to say, model is the handle that we can operate on.
After compiling the model, we get a model named User .
Pay attention to the schema table you define here. The data storage needs to correspond to this table when writing and registering it into the database later.
Create a new controller (folder)/user.js under the server folder to store the operation methods of the database.
Install some functional plug-ins first
$ cnpm i moment -s //用于生成时间
$ cnpm i objectid-to-timestamp -s //用于生成时间
$ cnpm i sha1 -s //安全哈希算法,用于密码加密
//user.js
const User = require('../db.js').User;
//下面这两个包用来生成时间
const moment = require('moment');
const objectIdToTimestamp = require('objectid-to-timestamp');
//用于密码加密
const sha1 = require('sha1');
//createToken
const createToken = require('../token/createToken.js');
//数据库的操作
//根据用户名查找用户
const findUser = (username) => {
return new Promise((resolve, reject) => {
User.findOne({ username }, (err, doc) => {
if(err){
reject(err);
}
resolve(doc);
});
});
};
//找到所有用户
const findAllUsers = () => {
return new Promise((resolve, reject) => {
User.find({}, (err, doc) => {
if(err){
reject(err);
}
resolve(doc);
});
});
};
//删除某个用户
const delUser = function(id){
return new Promise(( resolve, reject) => {
User.findOneAndRemove({ _id: id }, err => {
if(err){
reject(err);
}
console.log('删除用户成功');
resolve();
});
});
};
//登录
const Login = async ( ctx ) => {
//拿到账号和密码
let username = ctx.request.body.name;
let password = sha1(ctx.request.body.pass);//解密
let doc = await findUser(username);
if(!doc){
console.log('检查到用户名不存在');
ctx.status = 200;
ctx.body = {
info: false
}
}else if(doc.password === password){
console.log('密码一致!');
//生成一个新的token,并存到数据库
let token = createToken(username);
console.log(token);
doc.token = token;
await new Promise((resolve, reject) => {
doc.save((err) => {
if(err){
reject(err);
}
resolve();
});
});
ctx.status = 200;
ctx.body = {
success: true,
username,
token, //登录成功要创建一个新的token,应该存入数据库
create_time: doc.create_time
};
}else{
console.log('密码错误!');
ctx.status = 200;
ctx.body = {
success: false
};
}
};
//注册
const Reg = async ( ctx ) => {
let user = new User({
username: ctx.request.body.name,
password: sha1(ctx.request.body.pass), //加密
token: createToken(this.username), //创建token并存入数据库
create_time: moment(objectIdToTimestamp(user._id)).format('YYYY-MM-DD HH:mm:ss'),//将objectid转换为用户创建时间
});
//将objectid转换为用户创建时间(可以不用)
user.create_time = moment(objectIdToTimestamp(user._id)).format('YYYY-MM-DD HH:mm:ss');
let doc = await findUser(user.username);
if(doc){
console.log('用户名已经存在');
ctx.status = 200;
ctx.body = {
success: false
};
}else{
await new Promise((resolve, reject) => {
user.save((err) => {
if(err){
reject(err);
}
resolve();
});
});
console.log('注册成功');
ctx.status = 200;
ctx.body = {
success: true
}
}
};
//获得所有用户信息
const GetAllUsers = async( ctx ) => {
//查询所有用户信息
let doc = await findAllUsers();
ctx.status = 200;
ctx.body = {
succsess: '成功',
result: doc
};
};
//删除某个用户
const DelUser = async( ctx ) => {
//拿到要删除的用户id
let id = ctx.request.body.id;
await delUser(id);
ctx.status = 200;
ctx.body = {
success: '删除成功'
};
};
module.exports = {
Login,
Reg,
GetAllUsers,
DelUser
};
The above methods form the core of database operations in the project. Let’s analyze them.
First, three common basic methods are defined: findUser, findAllUsers, and delUser. Among them, findUser needs to pass in the username parameter, and delUser needs to pass in the id parameter.
Get the form information submitted by the user post, the new User that was previously designed according to the database and compiled into a model, and the obtained user name, password (needs to be encrypted with sha1 hash), token (using the previously created createToken method, And use the user name as the payload parameter of jwt), and save the generation time.
At this time, you must first search the database to see if the user name exists. If it exists, it will return failure. Otherwise, the user will be stored in the database and success will be returned.
Get the form information of the user's post, username and password (the registration is hashed and needs to be decrypted at this time). Search the username from the database to determine whether the username exists. If there is no return error, determine whether the password stored in the database is consistent with the password submitted by the user. If they are consistent, a new token will be generated for the user and stored in the database. Return success.
Just encapsulate the public findAllUsers method above and put the information in the result, so that the helloworld page can obtain this data and display it later.
Note that you must first get the user ID that needs to be deleted and pass it in as a parameter.
After writing these methods, you can improve the registration and login functions that were not perfect before.
When we complete the registration and the data is stored in the database, we want to check the data just registered and stored in the database, and we need to use database visualization tools. I use MongoBooster , which is easy to operate.
As you can see from the figure below, the two pieces of data registered in the example include id, username, password, token, and time. That long string of passwords is compiled due to hash encryption.
Add the following code after form validation in register.vue
//register.vue
if (valid) {
axios.userRegister(this.ruleForm)
.then(({}) => {
if (data.success) {
this.$message({
type: 'success',
message: '注册成功'
});
} else {
this.$message({
type: 'info',
message: '用户名已经存在'
});
}
})
}
We did not submit any data before in the login component. Now we add a series of methods to complete the login operation after successful verification: Introducing axios
import axios from '../axios.js'
Then add the following code after form verification in login.vue
//login.vue
if (valid) {
axios.userLogin(this.ruleForm)
.then(({ data }) => {
//账号不存在
if (data.info === false) {
this.$message({
type: 'info',
message: '账号不存在'
});
return;
}
//账号存在
if (data.success) {
this.$message({
type: 'success',
message: '登录成功'
});
//拿到返回的token和username,并存到store
let token = data.token;
let username = data.username;
this.$store.dispatch('UserLogin', token);
this.$store.dispatch('UserName', username);
//跳到目标页
this.$router.push('HelloWorld');
}
});
}
Submit the form data to the background, return the data status, and determine whether the account exists or not. After successful login, you need to get the returned token and username, save them in the store, and jump to the target HelloWorld page.
After successfully registering and logging in, we finally arrived at the actual display page—helloworld!
Let's improve this component so that it displays all currently registered user names and gives a delete button.
//Helloworld.vue
<template>
<div class="hello">
<ul>
<li v-for="(item,index) in users" :key="item._id">
{{ index + 1 }}.{{ item.username }}
<el-button @click="del_user(index)">删除</el-button>
</li>
</ul>
<el-button type="primary" @click="logout()">注销</el-button>
</div>
</template>
<script>
import axios from '../axios.js'
export default {
name: 'HelloWorld',
data () {
return {
users:''
}
},
created(){
axios.getUser().then((response) => {
if(response.status === 401){
//不成功跳转回登录页
this.$router.push('/login');
//并且清除掉这个token
this.$store.dispatch('UserLogout');
}else{
//成功了就把data.result里的数据放入users,在页面展示
this.users = response.data.result;
}
})
},
methods:{
del_user(index, event){
let thisID = {
id:this.users[index]._id
}
axios.delUser(thisID)
.then(response => {
this.$message({
type: 'success',
message: '删除成功'
});
//移除节点
this.users.splice(index, 1);
}).catch((err) => {
console.log(err);
});
},
logout(){
//清除token
this.$store.dispatch('UserLogout');
if (!this.$store.state.token) {
this.$router.push('/login')
this.$message({
type: 'success',
message: '注销成功'
})
} else {
this.$message({
type: 'info',
message: '注销失败'
})
}
},
}
}
</script>
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
.hello {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
width: 400px;
margin: 60px auto 0 auto;
}
</style>
The output page is relatively simple. Here are a few key points:
1. The getUser() interface must be requested immediately after the instance is created ( created() ). If the request fails, the token must be cleared. If the request is successful, the return data must be put into the user for page rendering.
2. thisID must be written in object format, otherwise an error will be reported
3. Clear the token when logging out
Changing people's thinking is indeed the most difficult thing. According to the process, koa should design the interface first, and then the front-end will make requests based on this interface. But in reverse, I write the front-end requests first, and then formulate the interface based on this request.
Of course, I also encountered many difficulties: when I finished the front-end display page and axios was written, I was stuck for a long time in using koa to write the interface. I had no idea at all. As mentioned in the preface, "I only know how to enter the data, not how to enter it." Know how to get out.” Then I encountered an interface error of 500 and debugged it for a long time. The main reason was that I had no concept of debugging the interface. In the end, the company's boss Langya helped solve the problem. Thank you.