一,Vue-Router 路由管理器
1.1,前端路由的概念及作用
需要大家注意的是,这里的路由可不是指我们日常生活中的路由器 😂 ,但是其实现原理基本相同。它代表一个 URL 与相应处理程序的映射关系。
用户在输入要访问的 URL 之后,路由会解析 URL 中的路径,之后根据映射表中的映射关系查找相应的预设函数,并将结果返回给用户,以此完成一次操作。
以上图为例,当一个用户使用 https://www.lanqiao.cn/a
来访问网页时,Web 服务会接收到这个请求,然后解析 URL 中的路径 /a
,在 Web 服务程序中该路径对应着响应的处理逻辑,程序会把请求交给路径所对应的处理逻辑,这样就完成了一次“路由分发”,这个分发就是通过“路由”来完成的。
简单地说,路由所做的工作就是:根据不同的 URL 地址展示不同的内容或页面。
前端路由不同于传统路由,它不需要服务器来进行解析,而是通过一个 hash 函数或者 H5 提供的 history API 来实现。一般使用前端路由的应用为不涉及到页面间跳转的单页面应用,也就是我们当下正在学习的。
1.2 Vue-Router
Vue-Router 是 Vue 的官方路由,专门为 Vue 的单页面应用量身打造。
其功能包括:
- 嵌套路由映射
- 动态路由选择
- 模块化、基于组件的路由配置
- 路由参数、查询、通配符
- 展示由 Vue 的过渡系统提供的过渡效果
- 细致的导航控制
- 自动激活 CSS 类的链接
- HTML5 history 模式或 hash 模式
- 可定制的滚动行为
- URL 的正确编码
我们通过一个单页面应用来看看 Vue-Router 的使用,其基本步骤如下所示:
- 使用
router-link
组件来导航,其通过to
属性来指定跳转链接(这相当于 HTML 中的 a 标签)。- 使用
router-view
组件定义路由出口,路由匹配到的组件将会渲染到此处。- 使用
const routes = [{ path, component }]
来定义路由(路径和组件名)。- 使用
const router = new VueRouter({})
来创建路由实例,在其中传入上一步定义的路由配置routes
。- 创建和挂载根实例,在
new Vue
中挂载上一步创建的路由实例router
。
步骤清楚了,我们来举个例子吧~
使用以下命令获取 Vue 和 Vue-Router 文件。
wget https://labfile.oss.aliyuncs.com/courses/1262/vue.min.js
wget https://labfile.oss.aliyuncs.com/courses/10532/vue-router.js
新建一个 index.html
文件,在文件中写入以下内容:
<!DOCTYPE html>
<html lang="en">
<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" />
<title>Document</title>
<script src="vue.min.js"></script>
<script src="vue-router.js"></script>
</head>
<body>
<div id="app">
<h1>路由的使用</h1>
<p>
<!-- 使用 router-link 组件来导航 -->
<router-link to="/home">首页</router-link>
<router-link to="/hot">热门</router-link>
<router-link to="/class">分类</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
<script>
const Home = { template: "<div>首页</div>" };
const Hot = { template: "<div>热门</div>" };
const Class = { template: "<div>分类</div>" };
// 定义路由
const routes = [
{ path: "/home", component: Home },
{ path: "/hot", component: Hot },
{ path: "/class", component: Class },
];
// 创建 router 实例,然后传 routes 配置
const router = new VueRouter({
routes,
});
// 创建和挂载根实例
const app = new Vue({
router,
}).$mount("#app");
</script>
</body>
</html>
效果如下所示:
二,Vuex 状态管理器
2.1什么是 Vuex
Vuex 是一个专门为 Vue 应用程序开发的状态管理模式。通过它可以将 Vue 应用中所有组件的共享状态储存管理起来,并以一定的规则保证这些状态以一种可预测的方式发生变化。
Vuex 应用场景及工作原理
通过前面的学习我们知道,组件之间的通讯有父子和兄弟这两层关系。其中兄弟组件之间的通信较为复杂,我们借助于它们共有的父组件来负责信息的传递。但实际上,随着业务逻辑的不断增多,难免会遇到相互之间没有任何联系的组件间共用响应式数据的情况。
针对这种情况,我们可以使用状态管理器来作为组件件沟通的桥梁。它就像所有组件共用的状态(即响应式数据)管理中心,可以将多个组件共用的状态存储起来,供它们访问或修改,所有组件都可以通过它上下平行地通知其它组件该状态发生了变化。如图所示:
2.2在 Vuex 中有五个核心概念
1, State
首先,在 main.js
文件中写入以下代码:
import Vue from "vue";
import App from "./App.vue";
import Vuex from "vuex"; // 导入 Vuex
Vue.use(Vuex); // 使用 Vuex,让 Vuex 可以访问到 Vue
Vue.config.productionTip = false;
// 创建 Store 实例
const store = new Vuex.Store({
state: {
count: 0, // 计数器的初始值
},
});
new Vue({
store, // 注入 Store
render: (h) => h(App),
}).$mount("#app");
有同学可能会问:为啥不叫 vuex 而是 store 呢?🤔
这是因为,Vuex 应用的核心就是 store(仓库)。它是一个用于存储组件共享状态(state)的容器,就像一个小型的数据仓库。它所有的功能和操作都是用于处理这个仓库中的状态而存在的,所以我们在创建 Vuex 配置的时候都是以 store 命名。
接下来,我们在 App.vue
中将计数器的状态展示出来,在文件中写入以下代码。
<template>
<div id="app">{{count}}</div>
</template>
<script>
export default {
name: "App",
// 通过计算属性来访问 count
computed: {
count() {
return this.$store.state.count;
},
},
};
</script>
来这里我们就可以在页面上访问到 count 的数据了,当前页面会显示 0。
2、Getters
getters
可以帮助我们缓存数据。
我们增加一个每次计数增加两倍的功能,在 main.js
中新增以下代码:
getters: {
doubleCount(state) {
return state.count * 2
}
}
然后在页面上获取数据,在 App
文件中新增以下代码:
{{$store.getters.doubleCount}}
这样,当点击 ++ 按钮时,计数会以乘 2 的形式增加。效果如下:
3、Mutations
在 App.vue
文件中定义一个按钮,新增代码如下:
<!--绑定一个点击事件,用 increment 来执行 count++ 的逻辑-->
<button @click="$store.commit('increment')">++</button>
我们在 main.js
文件中增加 mutations
,代码如下:
const store = new Vuex.Store({
// 此处省略 ...
mutations: {
increment(state) {
state.count++; // 执行 count++ 的操作
},
},
});
计数器的功能就实现啦~ 🎉 效果如下:
到此我们已经实现了一个最简单的 Vuex 状态管理,从上面的使用我们可以看出 state
就是用来存储和初始化状态。
通过上面简单的示例,我们知道了 Vuex 主要是用来存储并管理组件共享状态的。
4、Actions
有时候我们需要向后台发出一些异步请求,我们不能直接在 mutations
里进行操作,这时就可以在 actions
中定义一些异步操作。
下面我们来模拟一下异步操作,在页面上新增一个按钮,触发 count--
的操作。在 App.vue
中新增以下代码:
<button @click="$store.dispatch('decrement')">--</button>
注意哦!!! Actions 是通过
store.dispatch
方法来触发actions
更新state
状态。
在 main.js
文件中新增以下内容。
const store = new Vuex.Store({
mutations: {
decrement(state) {
state.count--;
},
},
actions: {
decrement({ commit }) {
setTimeout(() => {
// 通过 commit 交给 mutations 去处理
commit("decrement");
}, 500);
},
},
});
到这里我们 count--
的功能也实现了,效果如下:
actions 与 mutations 的区别。
actions
类似于mutations
,不同的是:
actions
中的更新函数最终仍是通过调用mutations
中的函数来更新状态的,不能通过它直接变更状态。- 与
mutations
不同,actions
中可以包含任意异步操作。
关于 mutations
、actions
等的用法还有其它形式,这些在官网上都有详细的 API,大家可以根据官网 API 对它们进行更多更深入的了解,这里就不再一一细说了。
5、 Modules
2.3Vuex 规则
在实际开发中 Vuex 并不会限制我们的代码结构。但是,它规定了一些需要遵守的规则:
-
应用层级的状态应该集中到单个 store 对象中。
-
提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
-
异步逻辑都应该封装到 action 里面。
只要你遵守以上规则,如何组织代码随你便。
不过随着业务的增多,我们可能会面临这样一个问题:由于 state
、mutations
、getters
、actions
存储的内容越来越多,会导致 store 文件及其庞大,开发和维护起来变得困难。没关系,学习过 ES6+ 的同学都知道模块化的概念,这里我们可以将它们作为单独的文件从 store 中分割出去。这样做对于大型应用开发来说在合适不过。😊
我们可以将 store 分割为如下所示的结构:
└── store
├── index.js # 我们组装模块并导出 store 的地方
├── state.js # 根级别的 state
├── getters.js # 根级别的 getters
├── actions.js # 根级别的 action
├── mutations.js # 根级别的 mutation