/components用来存放小组件
//template,可以把列表项放入template标签中,然后进行批量渲染
//class属性代表i一个选择器,可以理解为一个标识用来识别特定标签
欢迎来到MarkerHub的博客
//el-avatar,头像组件
{{ user.username }}
主页
发表博客
登录
退出
//
//template,可以把列表项放入template标签中,然后进行批量渲染
/class属性代表一个选择器,可以理解为一个标识用来识别特定标签
< scrtpt>
Header.vue的代码比较简单,一个标题标签显示欢迎内容,一个头像组件显示头像,一个小标题标签显示名字;用两个el-divider组件分隔开了三个el-link文字标签
主页标签的超链接是/blogs 博客列表
发表文章的超链接是/blogs/add
退出这个超链接,在!haslogin时显示为登录,绑定的请求是/login,haslogin时绑定的超链接是/logout
值得注意的是
methods: {
logout() {
const _this = this
_this.$axios.get("/logout", {
headers: {
"Authorization": localStorage.getItem("token")
}
}).then(res => {
_this.$store.commit("REMOVE_INFO")
_this.$router.push("/login")
})
}
},
- 这个方法中,通过axios发出get请求,在headers中设置了一个字段Authorization": localStorage.getItem("token“)
- 发送完请求后再store执行REMOVE_INFO,删除掉用户信息,再页面路由到登录界面
存放路由脚本文件(配置路由 url链接 与 页面组件的映射关系)
vue-router配置路由,使用vue的异步组件技术,可以实现按需加载
每一个路由规则都是一个对象
表示当前的路由信息(表示一条路由),包含了当前URL解析得到的信息(包含当前路径,参数,query对象等)
1.$route.path** 字符串,对应当前路由的路径,总是解析为绝对路径,如"/foo/bar"。
2.$route.params** 一个 key/value 对象,包含了 动态片段 和 全匹配片段, 如果没有路由参数,就是一个空对象。
3. r o u t e . q u e r y ∗ ∗ 一 个 k e y / v a l u e 对 象 , 表 示 U R L 查 询 参 数 。 例 如 , 对 于 路 径 / f o o ? u s e r = 1 , 则 有 route.query** 一个 key/value 对象,表示 URL 查询参数。 例如,对于路径 /foo?user=1,则有 route.query∗∗一个key/value对象,表示URL查询参数。例如,对于路径/foo?user=1,则有route.query.user == 1, 如果没有查询参数,则是个空对象
一组路由,把上面的每一条路由组合起来,形成一个数组
$router对象是全局路由的实例,是router构造方法的实例,实例方法包括:
push(和< router-link :to="…">是等同的)
字符串this.$router.push(‘home’)
对象this.$router.push({path:‘home’})
go( 页面路由跳转 )
前进或者后退this.$router.go(-1) // 后退
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../views/Login.vue'
import Blogs from '../views/Blogs.vue'
import BlogEdit from '../views/BlogEdit.vue'
import BlogDetail from '../views/BlogDetail.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Index',
redirect: {name: "Blogs"}
},
{
path: '/blogs',
name: 'Blogs',
component: Blogs
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/blog/add',
name: 'BlogAdd',
component: BlogEdit,
meta: {
requireAuth: true
}
},
{
path: '/blog/:blogId',
name: 'BlogDetail',
component: BlogDetail
},
{
path: '/blog/:blogId/edit',
name: 'BlogEdit',
component: BlogEdit,
meta: {
requireAuth: true
}
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
路由中心的配置
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
token: '',
userInfo: JSON.parse(sessionStorage.getItem("userInfo"))
},
mutations: {
// set
SET_TOKEN: (state, token) => {
state.token = token
localStorage.setItem("token", token)
},
SET_USERINFO: (state, userInfo) => {
state.userInfo = userInfo
sessionStorage.setItem("userInfo", JSON.stringify(userInfo))
},
REMOVE_INFO: (state) => {
state.token = ''
state.userInfo = {}
localStorage.setItem("token", '')
sessionStorage.setItem("userInfo", JSON.stringify(''))
}
},
getters: {
// get
getUser: state => {
return state.userInfo
}
},
actions: {
},
modules: {
}
})
存放页面(视图)组件
立即创建
重置
{{blog.title}}
{{blog.description}}
引入Header.vue,用一个时间线组件做样式和分页显示,用el-card显示v-for标签遍历出的每一个博客对象,卡片上还放置了一个router-link标签页面路由到博客详情
script中methods发起了通过axios做了get请求然后将结果中的内容赋值,底层用了分页插件PageHelper
page(currentPage) {
const _this = this
_this.$axios.get("/blogs?currentPage=" + currentPage).then(res => {
console.log(res)
_this.blogs = res.data.data.records
_this.currentPage = res.data.data.current
_this.total = res.data.data.total
_this.pageSize = res.data.data.size
})
}
{{ blog.title }}
编辑
编辑
//-prop属性用来接收父组件传给子组件的数据,可以在组件上注册一些自定义特性,当一个值传递给一个prop特性的时候,它就变成那个组件实例的一个属性
立即创建
重置
博客编辑页面引入Header.vue后,放置了三个表单分别输入标题,摘要和内容,注意在内容输入时进入了mavon-editor标签用于博客内容的编辑,还引入了一个”立即创建“按钮,和”重置“按钮
script中methods做了表单提交(axios.post)这里需要拿到jwt设置在请求头中,提交成功时会弹窗提示失败做了全局asios处理
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
const _this = this
//请求中将表单的内容也提交了
this.$axios.post('/blog/edit', this.ruleForm, {
headers: {
"Authorization": localStorage.getItem("token")
}
}).then(res => {
console.log(res)
_this.$alert('操作成功', '提示', {
confirmButtonText: '确定',
callback: action => {
_this.$router.push("/blogs")
}
});
})
} else {
console.log('error submit!!');
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
},
数据的双向绑定在这段代码里体现的格外明显,template中标题,摘要,内容通过prop属性绑定为title,description,content,在- 生命周期函数created()中根据动态路由参数的blogid先去后台查询如果有则显示在页面上(通过data()中的return),没有则显示为空,这样的操作正好实现了逻辑上无论是发表博客还是修改博客的请求都可以由一个请求实现
全局脚本文件(项目的入口)项目中所有的页面都会加载main.js
1.项目中所有的页面都会加载main.js
2.放置项目中经常会用到的插件和CSS样式。例如: 网络请求插件:axios和vue-resource、图片懒加载插件:vue-lazyload
3.存储全局变量
import Vue from ‘vue’
import App from ‘./App.vue’
import router from ‘./router’
import store from ‘./store’
import Element from ‘element-ui’
import axios from ‘axios’
import mavonEditor from ‘mavon-editor’
import “element-ui/lib/theme-chalk/index.css”
import ‘mavon-editor/dist/css/index.css’
import “./axios”
import “./permission”
Vue.use(Element)
Vue.use(mavonEditor)
Vue.config.productionTip = false
Vue.prototype.$axios = axios
new Vue({
router,
store,
render: h => h(App)
}).$mount(’#app’)
根组件,所有页面都是在App.vue下进行切换的(可以理解为所有的路由router也是App.vue的子组件)
一定要引入 < router-view/> 标签
项目中在axios.js中做全局axios拦截
实现了前置拦截,后置拦截(根据返回结果数据中json中的code(代表操作是否合法,此时请求时完成的)和返回头的status(代表的请求是否正常)进行错误原因判断和显示以达到更好的用户体验)
在项目目录下(注意层级)的axios.js要在main.js中import 才生效
import axios from 'axios'
import Element from 'element-ui'
import router from './router'
import store from './store'
axios.defaults.baseURL = "http://localhost:8081"
// 前置拦截
axios.interceptors.request.use(config => {
return config
})
axios.interceptors.response.use(response => {
let res = response.data;
console.log("=================")
console.log(res)
console.log("=================")
if (res.code === 200) {
return response
} else {
Element.Message.error('错了哦,这是一条错误消息', {duration: 3 * 1000})
return Promise.reject(response.data.msg)
}
},
error => {
console.log(error)
if(error.response.data) {
error.message = error.response.data.msg
}
if(error.response.status === 401) {
store.commit("REMOVE_INFO")
router.push("/login")
}
Element.Message.error(error.message, {duration: 3 * 1000})
return Promise.reject(error)
}
)
主要作用时做路由权限拦截
进行路由判断登录,每次路由时先判断该路由元数据meta中的requireAuth是否为true,是则从localStorage中取出token,存在则继续业务逻辑,否则页面路由到登录页面
import router from "./router";
// 路由判断登录 根据路由配置文件的参数
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requireAuth)) { // 判断该路由是否需要登录权限
const token = localStorage.getItem("token")
console.log("------------" + token)
if (token) { // 判断当前的token是否存在 ; 登录存入的token
if (to.path === '/login') {
} else {
next()
}
} else {
next({
path: '/login'
})
}
} else {
next()
}
})