Vue 后台管理简单框架(一) 中搭建出了后台管理的页面框架,但是还没有添加其他的功能,例如使用 Vuex 实现模块间数据共享、与服务器通讯、功能独立为模块、修改打包选项等,这一章主要的内容就是介绍这些功能的实现。
Vuex
Vuex 是什么?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。看不明白?其实就是保存数据的一个小工具,可以理解为和 localStorage 差不多,不过数据没有保存到硬盘上,一刷新页面就会没了。
什么时候使用 Vuex?
当我们的应用遇到多个组件共享状态时可以使用 Vuex,也就是多个组件共享变量时就可以使用 Vuex,共享数据变化时这些组件都同时更新。例如父子组建间通讯使用 props 和 emit,很多时候不够方便,如果这时使用 Vuex 的话,就会很简单(如果是提供给第三方使用的组件库的话,还是需要使用 props 和 emit)。
下面介绍 Vuex 的安装,注册和使用:
安装 Vuex
1
npm install vuex --save
创建 src/store/index.js 文件,存储共享数据变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
// state 中的 count 即是共享数据
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
increase(state) {
state.count++;
}
}
});main.js 中注册 Vuex 的 store
1
2
3
4
5
6
7
8
9
10...
import store from './store';
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App },
});new Vue()
中注册的 store, router 在 vue 文件中都可以使用 this 访问,例如this.$store.state.count
,this.$router.push('/hi')
。使用 store 访问共享变量,例如在 Hello.vue 中
1
2
3
4
5
6mounted() {
this.$store.commit('increase'); // 使用 commit 提交修改,不要直接修改 state 的状态
}
例如在其他 vue 文件中访问 count,因为我们已经引入了 iView,就用消息框吧:
this.$Message.info(`count value is ${this.$store.state.count}`);
与服务器通讯
vue-cli 推荐使用 Axios 与服务器通过 Ajax 通讯:
安装 Axios
1
npm install axios --save
在需要使用的 vue 页面引入 Axios 就可以使用了
1
2
3
4
5import axios from 'axios';
axios.get('/rest', {params: {name: 'Biao'}}).then((result) => {
console.log(result.data);
});
不过,Axios 默认使用 application/json 并把参数放到 request body 中发送,标准的 RESTful 的方式,由于服务器端有时候从 request body 中取参数不方便,例如在 Spring MVC 中我个人更愿意使用 @RequestParam 获取参数,这种用法需要对 Axios 封装一下,或则使用 jQuery 的 REST 插件。
Ajax 跨域
配置 proxyTable 实现 Ajax 跨域。
使用 vue-cli 开发的时候,与服务器通讯是跨域的 Ajax,要么服务器允许跨域,要么 vue 中配置 proxyTable 让 vue 代理访问实现跨域:
服务器允许跨域,但是有一个缺点,Ajax 的 url 必须要带上服务器的 IP、域名和端口
配置 proxyTable 让 vue 代理访问实现跨域,Ajax 的 url 不需要带上服务器的 IP、域名和端口,修改 config/index.js 中的
dev.proxyTable
1
2
3
4
5proxyTable: {
'/api': {
target: 'http://localhost:8080'
}
}这样我们在写url的时候,只用写成
/api/1
就可以代表http://localhost:8080/api/1
。
定义功能模块的模版
在一个大的 vue 页面中内容太多,可以把某些部分独立出来定义为一个小的模版,然后引用。例如 Big.vue 中有 10 个功能,全写在 Big.vue 中的话代码会比较多,模块不够清晰,可以把根据功能独立成模版,例如 module1.vue, module2.vue, …,然后在 Big.vue 中使用 module1.vue, module2.vue 中定义的模版
1 | <!-- 文件名: module1.vue --> |
1 | <!-- 文件名: Big.vue --> |
模版之间的通讯可以使用
props | slot | emit
或则上面的 vuex 的store
。
路由跳转
可以调用 router 的函数进行路由跳转
1 | this.$router.push('/'); // 跳转到首页 |
加载数据
mounted() 中从服务器加载数据。
一般应该在 mounted() 事件中加载数据,不应该在 created() 中,因为 created() 时 el 还没有被挂载,mounted() 时 el 被替换为 $el,数据就可以显示到界面上,而且在页面中只执行一次。下面以加载用户数据显示到 table 中为例,加载数据时显示 loading 状态,加载完成后用户数据显示到 table 中,loading 状态消失:
1 | <template> |
Build 配置
执行 npm run build 编译出发布的文件,默认中 dist 文件夹中,context path 为 /,但是默认的配置不一定符合项目的需求。
例如在 Spring MVC 中,context path 很多时候不是 /,而是项目名,vue 中的 index.html 要重命名为 admin.html 并且放到 static/html 目录下,通过配置 mvc:resources 来访问,如 http://host/html/admin.html,这些都可以通过配置 config/index.js 中的 build 来实现,例如:
1 | build: { |
- index: 生成的页面文件的路径
- assetsRoot: 编译输出的目录
- assetsSubDirectory: 编译输出的 js、img、css 等文件的文件夹名字
- assetsPublicPath: context path
引入静态文件
使用 ESJ 在 index.html 中判断不同的环境下使用不同的路径引入静态文件。
我们自己的静态文件 js、css、image 等都会放到 static 目录下(这里的文件 webpack 不会进行压缩打包),然后在 index.html 中引入
1 |
|
如果项目的 context path 是 /fox,修改 build.assetsPublicPath 为 /fox,但是编译后 js 引入部分仍然为:
1 | <script src="/static/lib/jquery.min.js" charset="utf-8"></script> |
实际需要:
1 | <script src="/fox/static/lib/jquery.min.js" charset="utf-8"></script> |
尝试修改 config/index.js 中 dev.assetsPublicPath 为 /fox,npm run dev
项目启动后却无法访问,可能是不能修改这个选项吧。考虑到可以使用 EJS 模版,于是修改 index.html 通过 if else 判断在开发环境和编译时使用不同的路径引入静态文件:
1 |
|
再次编译后看到引入静态文件时加上了 context path。
登陆
登陆就不在此 SPA 前端页面中做了,因为一个项目里不只是这一个单页面,所以可以由服务器端的 Spring Security 来控制权页面的限访问,还可以方便的实现各种登陆方式、remember me、验证码等,提供接口获取当前登陆用户的信息给前端 vue 使用,然后 vue 根据用户的权限信息显示不同的菜单。
可以在 main.js 中实现 router 的 beforeEach() 勾子函数,每次路由的时候使用 Ajax 在其中请求当前登陆用户的信息并保存到 store 中,然后 Home.vue 中根据用户的权限过滤显示菜单。
1 | // 每次路由都请求一下登陆用户信息 |
有可能你要问,每次路由都要请求一次登陆用户信息,效率是不是不高,能不能访问一次使用 sessionStorage 存储起来?
一般一个系统不可能就只有一个地方能够注销,例如:
- 点击页面 A、B、C 上的注销按钮进行注销
- 直接访问注销的 URL 进行注销
从其他地方注销后需要被监测到,把用户信息从 sessionStorage 里删除,关键就是怎么被监测到。使用 Websocket,定时轮训等?那还不如每次路由时请求一次呢,何况 Ajax 是很快的。
懒加载
打包后页面的 js 全打包到了 app.js 中,如果页面的模块非常多导致 app.js 很大,可以把每个模块的 js 单独打包,在访问具体模块的时候才加载它的 js,只需要在 router 引入 vue 时使用 require 加载即可:
1 | // 非懒加载 |
1 | export default new Router({ |