一、webpack引入bootstrap
首先bootstrap是依赖于jquery的,所以需要安装jquery。
1、在下载好jquery之后,需要引入jquery。
在webpack.base.conf.js模块中,使用webpack的内置模块ProvidePlugin,可以自动加载模块,而不需要使用import等。
const webpack = require('webpack')
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery"
})
]
//添加好之后,可以直接找一个页面测试
//例在HelloWorld.vue添加,审查元素,body中有没有添加成功
//如果成功者说明引入成功
$("body").append("<div>1</div>");
2、安装bootstrap,特别提醒如果你安装jquery的版本是3.3.1,那么如果安装的bootstrap的版本是4以上的那么2者是不兼容的bootstrap的样式会出现问题,所以个人建议安装bootstrap3.3.7的。
安装:npm install --save bootstrap@3.3.7,当然你也可以直接到官网里下载。
引用:如果是用npm方式安装的,那么记住这个是在node_modules文件中的。
在main.js头上中引入:
import 'bootstrap/css/bootstrap.min.css'
import 'bootstrap/js/bootstrap.min.js'
这个时候可能会报Popper.js文件找不到的错误,因为这里bootstrap依赖Popper.js文件,用npm install --save Popper.js
安装一下就可以了。
刚刚不是说也可以直接在官网上下载下来的,如果是直接下载的。将下载包移动到你放置静态文件的文件夹中解压出来(我比较推荐这种做法,因为这样比较好管理),一般不会怎么变化的静态文件我会存放在“static”目录下。
//在`webpack.base.conf.js文件`中alias模块
//这里的resolve是一个方法
function resolve (dir) {
//join方法用于将多个字符串结合成一个路径字符串
//path在node中会经常用到可以仔细了解一下path的各种方法
//__dirname:获取当前文件所在目录的完整绝对路径
return path.join(__dirname, '..', dir)
}
alias: {
'bootstrap':resolve('static/bootstrap'),
}
//main.js
//这里的bootstrap指代的就是上面的../static/bootstrap
import 'bootstrap/css/bootstrap.min.css'
import 'bootstrap/js/bootstrap.min.js'
到此开发时的基本样式框架已经准备完毕。
二、登入页面
最终结果:
问题:我们需要设计如果没有登入话跳转登入页面,如果已经登入过则跳转home页面。
首先这个问题需要分2中情况进行考虑:1、客户进行访问时是基于登入的前提下进行的,如管理系统等;2、这个系统不需要一开始就进行登入,只有在特殊的页面才需要进行登入验证,例如博客。
如果“这个系统不需要一开始就进行登入,只有在特殊的页面才需要进行登入验证”。可以用vue-router的导航守卫来实现。导航守卫分为:全局、单个路由和组件级的。关于各种级别的守卫在官网里已经写的很详细了,可以自己去看。在这里我们着重了解一下每个守卫方法接收三个参数:to, from, next。
如果是第一种情况,可以用钩子函数created进行判断。登入的流程:
- 判断是否需要登入验证,如何验证?通过cookie获取session。验证不通过跳转登入页面。
- 点击登入按钮,先保证用户名和密码不为空。然后用ajax请求后台,验证是否存在该用户,密码是否正确。如果正确则保存一些用户的基础信息和保存cookie。当然这里也应考虑跨域的事情。
//这是一些路由代码,下面的参数说明都是依照这段代码的
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import Login from '@/login'
Vue.use(Router)
const router = new Router({
mode: 'history',
routes: [
{
path: '/',
redirect: '/home' //重定向
},
{
path: '/home',
name: 'HelloWorld',
component: HelloWorld
},
{
path: '/login',
name: 'login',
component: Login
}
]
});
- to:即将进入的路由目标(例如你的地址是/home,那路由目标(to)中包括home的所有信息如name等)。具体to中包括的信息可以参考路由信息对象。如何使用,比如我们需要知道这个路由的名称:to.name。
- from:与to正好相反,是当前导航正要离开的路由。因为2者都是路由对应所以他们包括的内容和用法都是一样的,不一样的只是他们代表的路由目标不一样。例如你是从login到home的,那么to代表home,from代表login。
- next:是一个函数,控制导航的跳转或者终止。具体用法官网写的是否清楚。
next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。1、next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。2、next(false): 中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。3、next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: 'home' 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。3、next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。确保要调用 next 方法,否则钩子就不会被 resolved
在vue中请额外注意箭头函数的运用:注意箭头行数this指向的是执行时的上下文。例如我们在methods中定义一个方法,然后这里面需要访问data里面的数据,如果你用同步的方法可以用this.x访问到data中的数据,因为这时候this指向的是vue中的上下文,其可以访问data定义的数据。如果该方法用箭头函数定义且你在外边调用,那么这时候this指向的是执行时的上下文,这是this并不能访问定义在vue内部的data。
//html
<button class="btn loginBtn" data-loading-text="Loading..." type="button" @click="doLogin()"> LOGIN</button>
//js
<script>
export default {
name: 'login',
data () {
return {
msg: 'Welcome to Login page'
}
},
methods: {
doLogin() {
var self = this;
console.info(self.msg);//正常访问,内容为“Welcome to Login page”
}
}
}
//如果为箭头函数
export default {
name: 'login',
data () {
return {
msg: 'Welcome to Login page'
}
},
methods: {
doLogin:() => {
var self = this;
console.info(self.msg);//不能访问,控制台打印出“undefined”
}
}
}
</script>
三、全局守卫
前面我们说到,在某些页面需要进行登入验证。这里我们可以借助router导航守卫中的全局守卫beforeEach
router.beforeEach((to, from, next)=> {
......
})
但这里我们需要区别跳转页面时,那个页面是需要验证的那个是不要的。我们可以直接在定义路由的时候就指出需要验证的地址,设置meta:{requireAuth: true },然后在beforeEach中判断to.meta.requireAuth是否为true,如果为true则需要验证。代码如下:
//router文件下的index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'//这里特别说明一下@这个东西,在webpack配置文件中的resolve下面alias设置了@代表‘src’路径。具体webpack配置的问题我在前面的几章也说过。
import Login from '@/login'
Vue.use(Router)
const router = new Router({
mode: 'history',
routes: [
{
path: '/',
redirect: '/home' //重定向
},
{
path: '/home',
name: 'HelloWorld',
component: HelloWorld,
meta:{requireAuth: true }
},
{
path: '/login',
name: 'login',
component: Login
}
]
});
router.beforeEach((to, from, next)=> {
if(to.meta.requireAuth) {
....
}
next();
})
export default router;
然后知道那个页面需要验证后我们需要通过cookie来获取session判断是否已经登入过。首先我们需要封装一下cookie。在src下新建文件夹util,在util下新建cookie.js
//获取cookie
export function getCookie(name) {
var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)")
if(arr = document.cookie.match(reg)) {
return arr[2];
}else {
return null;
}
}
//设置cookie
export function setCookie(c_name, value, expiredays) {
const exdate = new Date();
exdate.setDate(exdate.getDate() + expiredays);
document.cookie = c_name + '=' + escape(value) + ((expiredays == null) ? "": ';expires='+exdate.toGMTString())
}
//删除cookie
export function delCookie (name) {
var exp = new Date();
exp.setTime(exp.getTime() - 1);
var cval = getCookie(name);
if (cval != null)
document.cookie = name + "=" + cval + ";expires=" + exp.toGMTString();
}
然后在router/index.js引入cookie.jsimport {delCookie,getCookie} from '@/util/cookie'
,最终router/index.js变成
......
import {delCookie,getCookie} from '@/util/cookie'
......
router.beforeEach((to, from, next)=> {
if(to.meta.requireAuth) {
if(getCookie('session')) {
next();
}else {
next({ path: '/login' });
}
}
next();
})
然后在main.js中引入router
import Vue from 'vue'
import App from './App'
import router from './router'
import 'bootstrap/css/bootstrap.min.css'//这里的bootstrap同上的@
import 'bootstrap/js/bootstrap.min.js'
Vue.config.productionTip = false
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
四、登入页面
我默认你知道如何操作vuex,如果不知道的可以看这里,我觉得写得十分通俗易懂,我就不具体说了。在src下建好store之后需要在main.js中引用并使用。
//main.js
......
import store from '@/store'
......
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
//login.vue html
<template>
<div class="login">
<div class="logo">
<img src="./assets/bog-logo.png" height="200px">
<h1>{{ msg }}</h1>
</div>
<div class="content">
<form class="loginForm">
<input type="text" v-model= "loginForm.userName" class="form-control" placeholder="Plass enter your username">
<input type="password" v-model= "loginForm.password" class="form-control" placeholder="Plass enter your password">
<button class="btn loginBtn" data-loading-text="Loading..." type="button" @click="doLogin()"> LOGIN</button>
<button class="btn enrollBtn" data-loading-text="Enroll..." type="button"> 注册</button>
</form>
</div>
</div>
</template>
<style scoped>
.login {
width: 100%;
height: 100%;
}
.logo {color: #53538f;}
.content {margin:0 auto;background: #e2e2f6;width: 50%;;max-width: 500px;padding: 30px;
border-radius: 10px}
.loginForm {margin:0 auto;}
.loginForm .form-control {margin:10px 0;height: 50px;}
.loginForm .loginBtn,.enrollBtn {background: #53538F;color: #fff;font-size: 20px;}
.enrollBtn {background: #f5c7bc;font-size: 12px;}
</style>
//js
export default {
name: 'login',
data () {
return {
msg: 'Welcome to Login page',
loginForm: {
userName: '',
password: ''
}
}
},
methods: {
doLogin() {
const _self = this;
const loginForm = _self.loginForm;
const api = _self.$store.state.api;//这里的api就是定义在vuex中,这样所有的组件都可以访问,并且以后修改的话只需要修改一处就行
if(loginForm.userName == '' || loginForm.password == '') {
alert("用户名或密码不能为空!")
return ;
}
$(".loginBtn").button('loading');
$(".enrollBtn").hide(500);
$.post(api + '/doLogin', loginForm, function(result){
if(!result.success) {
alert(result.msg);
$(".loginBtn").button('reset');
$(".enrollBtn").show(500);
return ;
}
});
}
}
}
</script>
这里我是用node做了一个简单的后台服务器,所以涉及到跨域的问题,我先用了极其简单的方法解决。后台代码都放在了serve文件夹中。在app.js中设置header和访问路径,并返回结果。
..........
//设置跨域访问
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
res.header("X-Powered-By",' 3.2.1')
res.header("Content-Type", "application/json;charset=utf-8");
next();
});
........
app.post('/doLogin',function(req,res){
const user = {
"admin":'123456'
};
const vUser = req.body;
var result = {};
if(!user.hasOwnProperty(vUser.userName)) {
result["success"] = false
result["msg"] = "不存在该用户!"
}else {
result["success"] = true
result["msg"] = "登入成功!"
if(user[vUser.userName] != vUser.password){
result["success"] = false
result["msg"] = "密码不正确!"
}
}
res.send(result);
}) ;
登入成功后,利用crypto加密用户名,生成token返回前端,存储到cookie中。因为模块有可能会比较多,所以我就将user模块分离出来了。最后服务器代码,先在serve下建一个名叫routes的文件夹,所有路径都放在这里。先建一个index.js
const photos = require('./photos');
const user = require('./user');//引入user相关的操作
/*
* GET home page.
*/
exports.index = function(req, res){
res.render('index', { title: 'Express' });
};
/*
* GET photo page.
*/
exports.photos = photos;
exports.user = user;
具体的crypto属性方法可以参考官网。但这里我发现一个问题,我也不知道为什么。如果createHmac写在上头第二次操作的时候就会报“HashUpdate fail”。如果有知道的请告知我一下。
//新建user.js文件
const crypto = require('crypto');//nodejs的内置模块 加密
//const hmac = crypto.createHmac('sha256', 'blogsss');如果这样写,再第二次操作的时候就会报”HashUpdate fail“
/*
* GET users listing.
*/
const user = {
"admin":'123456'
};
exports.doLogin = function(req, res, next){
const vUser = req.body;
var result = {};
if(!user.hasOwnProperty(vUser.userName)) {
result["success"] = false
result["msg"] = "不存在该用户!"
}else {
result["success"] = false
result["msg"] = "密码不正确!"
if(user[vUser.userName] == vUser.password){
//const hash = hmac.update(vUser.userName+"date="+new Date()).digest('hex');
const hash = crypto.createHmac('sha256', 'blogsss').update(vUser.userName+"date="+new Date()).digest('hex');
result["success"] = true
result["msg"] = "登入成功!"
result["token"] = hash
}
}
res.send(result);
};
登入成功之后除了要将返回的token存储以外还需将vuex的一些用户信息修改,并且跳转home页面。这里需要修改的时候,要在login页面引入3个mapGetters, mapMutations, mapActions。但这里我只需要mapActions所以只引入了mapActions。
//login.vue
<script>
import {mapActions} from 'vuex';
import {getCookie, setCookie} from '@/util/cookie'
export default {
....
methods: {
...mapActions([
'updateUserInfo'//注意这里需要加单引号,之前参考的文档中没有,所以一直报updateUserInfo未定义的错误
]),
doLogin() {
const _self = this;
const loginForm = _self.loginForm;
const api = _self.$store.state.api;
if(loginForm.userName == '' || loginForm.password == '') {
alert("用户名或密码不能为空!")
return ;
}
$(".loginBtn").button('loading');
$(".enrollBtn").hide(500);
$.post(api + '/doLogin', loginForm, function(result){
if(!result.success) {
alert(result.msg);
$(".loginBtn").button('reset');
$(".enrollBtn").show(500);
return ;
}else {
setCookie("session", result.token, 2);
_self.updateUserInfo({//修改vuex的值
name:loginForm.userName,
login:true
});
_self.$router.push('/home');
}
});
}
}
}
</script>
这里需要注意的是vuex的actions,它只能传一个参数,如果有2个后面的就undefined了,所以我这里解决的方法是传一个对象进去。
//store/actions.js
import * as types from './mutation-type.js';
export default {
updateUserInfo({commit}, obj) {
commit(types.SET_USERNAME, obj.name);
commit(types.SET_LOGIN, obj.login);
}
};
这样该存的存,该改的改之后用this.$router.push('/home')
我们跳转到home页面了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。