1、前言
接下來,我們來完成vueblog前端的部分功能。可能會使用的到技術如下:
- vue
- element-ui
- axios
- mavon-editor
- markdown-it
- github-markdown-css
直接使用 npm install element-ui 、npm install github-markdown-css ......
2、環境準備
1)首先要先安裝node、下載node地址:
Node.js官方安裝包及源碼下載地址:http://nodejs.org/download/
安裝node完成之後,查看版本信息(如果提示出錯,配置一下環境變量,將nodejs的路徑加入path中)
2)安裝vue的環境
# 安裝淘寶npm
npm install -g cnpm --registry=https://registry.npm.taobao.org
# vue-cli 安裝依賴包
cnpm install --g vue-cli
3、新建項目
進入指定的文件夾下,執行:vue init webpack vue-blog-front ;這裏的vue-blog-front是項目名稱。
這裏創建項目需要等待一會兒0.0,我這邊使用的vscode,導入剛剛創建的項目
1.先執行npm install,下載該項目需要的依賴。
目錄結構大致解釋
├── README.md 項目介紹
├── index.html 入口頁面
├── build 構建腳本目錄
│ ├── build-server.js 運行本地構建服務器,可以訪問構建後的頁面
│ ├── build.js 生產環境構建腳本
│ ├── dev-client.js 開發服務器熱重載腳本,主要用來實現開發階段的頁面自動刷新
│ ├── dev-server.js 運行本地開發服務器
│ ├── utils.js 構建相關工具方法
│ ├── webpack.base.conf.js wabpack基礎配置
│ ├── webpack.dev.conf.js wabpack開發環境配置
│ └── webpack.prod.conf.js wabpack生產環境配置
├── config 項目配置
│ ├── dev.env.js 開發環境變量
│ ├── index.js 項目配置文件
│ ├── prod.env.js 生產環境變量
│ └── test.env.js 測試環境變量
├── mock mock數據目錄
│ └── hello.js
├── package.json npm包配置文件,裏面定義了項目的npm腳本,依賴包等信息
├── src 源碼目錄
│ ├── main.js 入口js文件
│ ├── app.vue 根組件
│ ├── components 公共組件目錄
│ │ └── title.vue
│ ├── assets 資源目錄,這裏的資源會被wabpack構建
│ │ └── images
│ │ └── logo.png
│ ├── routes 前端路由
│ │ └── index.js
│ ├── store 應用級數據(state)狀態管理
│ │ └── index.js
│ └── views 頁面目錄
│ ├── hello.vue
│ └── notfound.vue
├── static 純靜態資源,不會被wabpack構建。
└── test 測試文件目錄(unit&e2e)
└── unit 單元測試
├── index.js 入口腳本
├── karma.conf.js karma配置文件
└── specs 單測case目錄
└── Hello.spec.js
2.使用npm安裝Router、Vuex、element-ui
3.在src目錄下的main.js,引入element-ui依賴。
import Element from 'element-ui'
import "element-ui/lib/theme-chalk/index.css"
// 引入element
Vue.use(Element)
接下來就可以在頁面上使用element-ui組件了。
4.npm安裝axios
npm install axios --save
然後同樣我們在main.js中全局引入axios。
import axios from 'axios'
// 全局引用axios
Vue.prototype.$axios = axios
組件中,我們就可以通過this.$axios.get()來發起我們的請求了哈。
5.頁面路由
我們在views文件夾下定義幾個頁面:
- BlogDetail.vue(博客詳情頁)
- BlogEdit.vue(編輯博客)
- Blogs.vue(博客列表)
- Login.vue(登錄頁面)
這裏只說明BlogDetail.vue(博客詳情頁),其他頁面都類似(當然你得會些許前端的知識)
頁面的結構是:
<template>
<div>
這裏就是實現頁面的內容
</div>
</template>
<script>
這裏面就是js了,一些定義的變量,引入的js、模塊、一些訪問後端的方法
</script>
<style scoped>
/* scoped 代表該css只在該頁面有效 */
這裏放該頁面的樣式css
</style>
<template>
<div>
<Header></Header>
<div class="mblog">
<h2> {{ blog.title }}</h2>
<el-link icon="el-icon-edit" v-if="ownBlog">
<router-link :to="{name: 'BlogEdit', params: {blogId: blog.id}}" >
編輯
</router-link>
</el-link>
<el-divider></el-divider>
<div class="markdown-body" v-html="blog.content"></div>
</div>
</div>
</template>
<script>
import 'github-markdown-css'
import Header from "../components/Header";
export default {
name: "BlogDetail.vue",
components: {Header},
data() {
return {
blog: {
id: "",
title: "",
content: ""
},
ownBlog: false
}
},
created() {
const blogId = this.$route.params.blogId
console.log(blogId)
const _this = this
this.$axios.get('/blog/' + blogId).then(res => {
const blog = res.data.data
_this.blog.id = blog.id
_this.blog.title = blog.title
var MardownIt = require("markdown-it")
var md = new MardownIt()
var result = md.render(blog.content)
_this.blog.content = result
_this.ownBlog = (blog.userId === _this.$store.getters.getUser.id)
})
}
}
</script>
<style scoped>
.mblog {
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
width: 100%;
min-height: 700px;
padding: 20px 15px;
}
</style>
頁面中引入了一個公共的組件,頁面的頭部內容,這樣抽取成一個組件,需要用到該頭部內容的頁面直接引入該組件即可。
然後再路由中心配置:
- router\index.js
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
接下來我們去開發我們的頁面。其中,帶有meta:requireAuth: true說明是需要登錄字後才能訪問的受限資源,後面我們路由權限攔截時候會用到。
登錄頁面
Login.vue
<template>
<div>
<el-container>
<el-header>
<img class="mlogo" src="https://www.markerhub.com/dist/images/logo/markerhub-logo.png" alt="">
</el-header>
<el-main>
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="用戶名" prop="username">
<el-input v-model="ruleForm.username"></el-input>
</el-form-item>
<el-form-item label="密碼" prop="password">
<el-input type="password" v-model="ruleForm.password"></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-main>
</el-container>
</div>
</template>
<script>
export default {
name: "Login",
data() {
return {
ruleForm: {
username: 'frank',
password: '111111'
},
rules: {
username: [
{ required: true, message: '請輸入用戶名', trigger: 'blur' },
{ min: 3, max: 15, message: '長度在 3 到 15 個字符', trigger: 'blur' }
],
password: [
{ required: true, message: '請選擇密碼', trigger: 'change' }
]
}
};
},
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
const _this = this
this.$axios.post('/login', this.ruleForm).then(res => {
console.log(res.data)
const jwt = res.headers['authorization']
const userInfo = res.data.data
// 把數據共享出去
_this.$store.commit("SET_TOKEN", jwt)
_this.$store.commit("SET_USERINFO", userInfo)
// 獲取
console.log(_this.$store.getters.getUser)
_this.$router.push("/blogs")
})
} else {
console.log('error submit!!');
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
}
}
}
</script>
<style scoped>
.el-header, .el-footer {
background-color: #B3C0D1;
color: #333;
text-align: center;
line-height: 60px;
}
.el-aside {
background-color: #D3DCE6;
color: #333;
text-align: center;
line-height: 200px;
}
.el-main {
/*background-color: #E9EEF3;*/
color: #333;
text-align: center;
line-height: 160px;
}
body > .el-container {
margin-bottom: 40px;
}
.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
line-height: 260px;
}
.el-container:nth-child(7) .el-aside {
line-height: 320px;
}
.mlogo {
height: 60%;
margin-top: 10px;
}
.demo-ruleForm {
max-width: 500px;
margin: 0 auto;
}
</style>
從返回的結果請求頭中獲取到token的信息,然後使用store提交token和用戶信息的狀態。完成操作之後,我們調整到了/blogs路由,即博客列表頁面。
const token = res.headers['authorization']
_this.$store.commit('SET_TOKEN', token)
_this.$store.commit('SET_USERINFO', res.data.data)
_this.$router.push("/blogs")
token的狀態同步
store/index.js
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: {
}
})
存儲token,我們用的是localStorage,存儲用戶信息,我們用的是sessionStorage。畢竟用戶信息我們不需要長久保存,保存了token信息,我們隨時都可以初始化用戶信息。
定義全局axios攔截器
點擊登錄按鈕發起登錄請求,成功時候返回了數據,如果是密碼錯誤,我們是不是也應該彈窗消息提示。爲了讓這個錯誤彈窗能運用到所有的地方,所以我對axios做了個後置攔截器,就是返回數據時候,如果結果的code或者status不正常,那麼我對應彈窗提示。
import axios from 'axios'
import Element from 'element-ui'
import router from './router'
import store from './store'
axios.defaults.baseURL = "http://localhost:8088"
// 前置攔截
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)
}
)
路由權限攔截
permission.js
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()
}
})
通過之前我們再定義頁面路由時候的的meta信息,指定requireAuth: true,需要登錄才能訪問,因此這裏我們在每次路由之前(router.beforeEach)判斷token的狀態,覺得是否需要跳轉到登錄頁面。
{
path: '/blog/add', // 注意放在 path: '/blog/:blogId'之前
name: 'BlogAdd',
meta: {
requireAuth: true
},
component: BlogEdit
}
然後我們再main.js中import我們的permission.js
import './permission.js' // 路由攔截
最後啓動項目測試:
1.npm install
2.npm run serve
啓動成功。
自此vue的前端開發完成。前端代碼