上接vue-cli4下创建前端项目(路由、vuex与axios应用),本篇解决了一些编码过程中遇到的问题。
1.设置全局模板
全局模板的作用是为了让所有的页面都按照某种布局来进行展示,比如两边固定是广告或者操作表盘等,只有中间是实际展示的内容,这里我用bootstrap来实现布局。
1.1.引用bootstrap
在index.html中直接引用CDN的css和js文件,这样应该可以作用在全局上。
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<link href="https://cdn.jsdelivr.net/npm/@bootcss/v3.bootcss.com@1.0.13/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@bootcss/v3.bootcss.com@1.0.13/assets/css/ie10-viewport-bug-workaround.css" rel="stylesheet">
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
</body>
<script src="https://cdn.jsdelivr.net/npm/@bootcss/v3.bootcss.com@1.0.13/assets/js/ie-emulation-modes-warning.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@bootcss/v3.bootcss.com@1.0.13/dist/js/bootstrap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@bootcss/v3.bootcss.com@1.0.13/assets/js/ie10-viewport-bug-workaround.js"></script>
</html>
1.2.增加全局模板文件
在views下新建layout.vue,写入如下内容,这里只需要把中间设置为router-view用于展示子页面
<template>
<div class="col-md-1" style="font-size: 30px;">
>_:
</div>
<div class="col-md-10">
<router-view></router-view>
</div>
<div class="col-md-1"></div>
</template>
<script>
export default {
name: "layout"
}
</script>
<style>
</style>
1.3.修改路由
增加之后启动项目的效果就是当访问localhost:8080的时候,会得到一个嵌套在layout下的note/list,即把真正的note/list的内容注入到Layout页面的<router-view>中。
import {createRouter, createWebHistory} from "vue-router";
import Layout from '@/views/layout/layout.vue'
const constantRouterMap = [
{
path: '',
redirect: '/note/list'
},
{
path: '/note',
component: Layout,
name: 'note',
meta: { title: '文章列表' },
children: [
{
path: 'list',
name: 'list',
component: () => import('@/views/note/list.vue')
}
]
},
{
path: '/detail/:id',
component: () => import('@/views/detail.vue')
},
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
component: () => import('@/views/notFound.vue')
}
]
export default new createRouter({
history: createWebHistory(),
routes: constantRouterMap
});
2.引用静态资源(图片和CSS等)
首先把静态资源放到assets中。
对于图片可以这样引用:
<img class="icon" src="@/assets/icon/文件夹.svg" v-if="node.dir">
<img class="icon" src="@/assets/icon/文件.svg" v-if="!node.dir">
对于CSS可以这样引用
<style>
@import "assets/css/zekdot.css";
</style>
3.给页面传参
首先在对应的页面,这里是detail.vue上定义props
export default {
name: 'list',
props: ['address'],
data() {
return {
nodes : [{"address":"/nginx/负载均衡实现.md","dir":false,"name":"负载均衡实现.md"},{"address":"/nginx/url.txt","dir":false,"name":"url.txt"},{"address":"/nginx/note.md","dir":false,"name":"note.md"},{"address":"/nginx/test","dir":true,"name":"test"}],
}
},
created() {
console.log(this.address)
}
}
然后修改对应的路由,设置props为true。
{
path: '/note',
component: Layout,
name: 'note',
meta: { title: '文章列表' },
children: [
...
{
path: 'detail/:address',
name: 'detail',
component: () => import('@/views/note/detail.vue'),
props: true
},
]
},
然后访问localhost:8080/note/detail/test.txt,就可以在控制台中打印出test.txt了。
4.设置页面访问权限
4.1.实现登录请求方法
首先需要实现一个登录的请求方法,调用时会进行登录,并且获取登录成功的令牌。
import request from '@/utils/request';
export function login(user) {
return request({
url: '/user/login',
method: 'post',
data: user
})
}
export function isLogin() {
return request({
url: '/user/isLogin',
method: 'get'
})
}
4.2.实现user的store
这里由于使用了session作为登录凭证,浏览器会自动进行管理,所以不用专门去存储令牌相关的内容。
actions.js:
import { login }from "@/api/user";
export const actions = {
async login({ commit }, user) {
try {
await login(user);
commit('SET_USERNAME', user.username);
}catch (e) {
return Promise.reject(e);
}
}
};
getter.js:
export const getters = {
username: state => state.username
};
mutations.js:
export const mutations = {
SET_USERNAME: (state, username) => {
state.username = username;
}
};
index.js:
import { getters } from './getters';
import { actions } from './actions';
import { mutations } from './mutations';
export const state = {
username: ''
};
const namespaced = true;
export const note = {
namespaced,
state,
getters,
actions,
mutations
};
接下来只需要在对应的地方去调用actions和commit即可。
methods: {
confirm: function() {
try {
let user = {'username' : this.userInput, 'password' : this.passInput};
let that = this;
this.$store.dispatch('user/login', user).then(() => {
that.showLogin = false;
});
} catch (err) {
alert(err);
}
},
logout: function() {
this.$store.commit('user/SET_USERNAME', null);
}
}
4.3.设置未登录自动页面跳转
有些页面需要登录之后才能访问,如果没登录就需要让他直接跳转到登录页面,这是一个常见的操作,之前都是直接if加window.location.href来实现的,但是在目前的框架下,我们可以通过vue-router的元数据配置以及导航守卫来实现这一需求,这里以一个笔记编辑页面editNode.vue为例,只有登录的用户可以访问这个页面,否则就跳转到笔记列表去。
首先在router.js中配置这一路由,并为其增加一个元数据requireLogin表明访问这个页面需要登录。
path: '/note',
component: Layout,
name: 'note',
meta: { title: '笔记列表' },
children: [
...
{
path: 'editNote/:address',
name: 'editNote',
component: () => import('@/views/note/editNote.vue'),
meta: { requireLogin: true },
props: true
}
]
然后来编写过滤的逻辑,这里我们判断是否登录是根据store中的user.uesrname是否为空,如果在router.js中的话是无法获取到store的引用的(可能也可以,但是我目前不知道怎么做),因此我把过滤的逻辑写到了main.js中,那里有一个现成的store实例:
const app = createApp(App)
router.beforeEach((to) => {
if(to.meta.requireLogin && !store.state.user.username) {
return {
path: '/note/list'
}
}
});
app.use(router);
app.use(store);
app.mount('#app');
这样实现的效果就是,如果在未登录的情况下去访问localhost:8080/note/editNote会直接跳转会/note/list。
5.引用marked.js和highlight.js
这两个可能不是特别通用的技术,我的项目中使用到了,前者可以将markdown文本解析为html文本,后者可以将html中的代码字段进行高亮显示,所以我觉得两者搭配使用的情况倒是应该很多。
5.1.安装
安装很简单,在项目根目录下使用如下命令:
npm install marked --save
npm install highlight --save
5.2.使用
以下代码应该是在组件的vue文件中的<script>部分。
import marked from "marked";
import hljs from 'highlight.js';
import 'highlight.js/styles/atom-one-dark.css'
var rendererMD = new marked.Renderer();
marked.setOptions({
renderer: rendererMD,
gfm: true,
tables: true,
breaks: false,
pedantic: false,
sanitize: false,
smartLists: true,
smartypants: false
});
marked.setOptions({
highlight: function (code) {
return hljs.highlightAuto(code).value;
}
});
export default {
name: 'list',
props: ['address'],
data() {
return {
detail: '',
picSrc: ''
}
},
created() {
let that = this;
getNoteDetail(this.address).then(res => {
rendererMD.image = function(href) {
return '<image src="noteraw/' + that.address.substr(0, that.address.lastIndexOf('/')) + '/' + href.replace("./", "") + '">'
};
that.detail = marked(res);
})
}
}
|