vue入门–基础命令+axios+案例练习
vue入门–vue常用属性、生命周期、计算属性、过滤器、组件、虚拟DOM、数组的响应式方法、页面闪烁、ES6简单语法增强
vue入门–js高阶函数(箭头函数)、v-model数据绑定、组件化、父子组件通信及访问
vue入门–插槽(具名、匿名、作用域插槽)+ES6模块化导入导出+webpack的使用(基本使用+配置使用+如何一步步演化成cli脚手架)+webpack插件使用(搭建本地服务器、配置文件分离)
vue-cli脚手架2版本及3+版本安装、目录解析、only和compiler的区别、3+版本如何改配置、箭头函数及this的指向
vue-router基本使用、路由传参、懒加载、嵌套路由、导航守卫、keep-alive
Promise基本使用、三种状态、链式调用及简写、all方法
Vuex的作用、使用、核心概念(State、Mutations、Getters、Actions、Modules)、文件抽离
axios网络请求基本使用、配置使用(全局axios和局部axios实例)、模块封装、axios拦截器
vue路由 vue-router
路由有一个非常重要的概念叫路由表,路由表本质上就是一个映射表,决定了数据包的指向,可以进行路由和转发。
前后端分离:
后端只负责提供数据,不负责任何界面的内容。通常情况下,我们会把前端打包后的资源放置到一个静态资源服务器上,如Nginx。然后还会有一个后端服务器为客户端提供API接口。
我们只需要使用浏览器访问静态资源服务器,然后从静态服务器上拉取到HTML+CSS+JS,浏览器再去执行js代码,一般会去访问后端服务器提供的API接口,返回数据给客户端(浏览器)后再进行页面数据的渲染。
前端渲染,后端渲染、前端路由、后端路由:
- 前端渲染:浏览器中显示的页面中的大部分内容,都是由前端写的js代码在浏览器(客户端)中执行,最后渲染出来的网页页面。
- 前端路由:
SPA页面(单页面富应用)
,在前后端分离阶段中,增加了一层前端路由,html只有一个,通过不同的路由,将不同的组件展示到一个html中,整个应用中只有一个html页面。通过映射关系实现。前端来管理URL和页面之间的映射关系。 - 后端渲染:也叫作服务器端渲染,之前会采用jsp技术,当请求发送到服务器(后端),由服务器进行查询数据,并且通过jsp技术将页面数据渲染好,直接返回给浏览器一个 HTML+CSS,因为是完全后端渲染,还未用到js、ajax等
- 后端路由:后端服务器来处理URL和页面之间的映射关系。
url的hash和HTML5的history
既然说到了前端路由,当我浏览器更改url时,如何不让浏览器再去访问静态资源服务器呢,可以使用url的hash或html5的history。
-
url的hash:url的hash也就是锚点(#),本质上是改变window.location.href的属性。可以通过location.hash改变href,但是页面不会发生刷新,且url中多了一个
#
锚点 -
html5的history:通过history的pushState也可以做到
// 入栈,入栈后浏览器会记录历史,可操作上一步,下一步 history.pushState({ },'','home') // 返回 history.back() // 替换,不会记录历史 history.replaceState({ }, '', 'demo')
基本使用
首先安装 vue-router
npm install vue-router --save
然后再模块化工程中使用它,由于vue-router是一个插件,所以可以通过Vue.use()
来使用
创建组件 Home.vue
<template>
<div>
<h2>首页</h2>
</div>
</template>
<script>
export default {
name: "Home"
}
</script>
<style>
</style>
创建组件 About.vue
<template>
<div>
<h2>关于</h2>
</div>
</template>
<script>
export default {
name: "About"
}
</script>
<style>
</style>
在src目录中有一个router
的目录,然后创建一个index.js
文件,内容如下:
// 配置路由相关的信息
import Vue from 'vue'
import VueRouter from 'vue-router'
// 导入组件
import Home from '../components/Home'
import About from '../components/About'
// 1.通过Vue.use(VueRouter),使用这个插件
Vue.use(VueRouter)
// 2.创建路由对象
const routes = [
{
path: "/home",
component: Home
},
{
path: "/about",
component: About
}
]
const router = new VueRouter({
routes
});
//3.将router传给vue实例
export default router
在src目录中的main.js入口文件中,创建vue实例并且使用路由对象。
import Vue from 'vue'
import App from './App'
import router from './router' // 当from文件夹时,它会自动找文件夹下的index.js入口文件
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
render: h => h(App)
})
在App.vue入口组件中使用这些
router-link和router-view配合使用
<template>
<div id="app">
<router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link>
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
路由默认路径
const routes = [
{
// 匹配到 / 时(默认值),直接重定向到 /home路由
path: "/",
redirect: '/home'
}
]
路由改为history模式(去掉#锚点)
创建vue-router实例时,指定mode为history
const router = new VueRouter({
routes,
mode: 'history'
});
router-link属性
- to : 点击时router-view中替换为相应路由下的组件
- tag : router-link渲染到html中会被渲染为
a标签
,可以根据tag属性更改为其他标签,如div,button
<router-link to="/home" tag="button">首页</router-link>
-
replace ,增加此属性后,跳页面底层用的是 replaceState(),不会存在历史记录,不能点左右箭头
-
active-class : 当router-link处于激活状态时(被点击),增加一个类属性(不写时默认为
router-link-active
)
<router-link to="/home" tag="button" active-class="active">首页</router-link>
<router-link to="/about" active-class="active">关于</router-link>
<style>
.active{
color: red;
}
</style>
实现后,点击哪个 router-link,哪个router-link中的文本就会变成红色的
但是假如有多个 router-link,难道一个一个加 active-class属性吗,当然不用。我们可以在实例化路由时进行全局化配置:
const router = new VueRouter({
routes,
mode: 'history',
linkActiveClass: 'active'
});
通过代码跳转路由
当我们不使用 router-link进行路由跳转时,还可以通过代码进行路由跳转。
<template>
<div id="app">
<!-- <router-link to="/home" tag="button" >首页</router-link>
<router-link to="/about" >关于</router-link> -->
<button @click="homeClick">首页</button>
<button @click="aboutClick">关于</button>
<router-view/>
</div>
</template>
<script>
export default {
name: 'App',
methods: {
homeClick(){
// 从 data()中默认返回的一个 $router对象
this.$router.push('/home') // 有历史记录
//this.$router.replace('/home') // 直接替换,无历史记录
console.log('homeClick')
},
aboutClick(){
this.$router.push('/about')
console.log('aboutClick')
}
},
// 注意,在vue中,所有的vue组件都会默认创建一个$router的属性,这里不需要写,是默认的,只是为了说明 $router是从哪里来的
data(){
return {
$router : ...
}
}
}
</script>
那么这种方式如果连续点击几次后发现报错了,那么可以给push或replace函数加一个异常捕获,我们直接把异常给吃掉。
this.$router.push('/home').catch(err => {
})
// 或
this.$router.replace('/home').catch(err => {
})
动态路由
如访问到某一个用户id为2的详细信息,路径就是变成 /user/2
配置路由时,在path上面加传值匹配,匹配路由时 /user/xxx才会被匹配到,否则无法匹配到就找不到路由
const routes = [
......
{
path: "/user/:userId",
component: User
}
]
使用时(App.vue文件中),将具体的值拼接在path上面即可
<router-link to="/user/2">用户</router-link>
<router-view/>
但是实际使用时那个id是需要从后端服务器动态获取来的,假如这么一个userId变量为2
export default {
name: 'App',
data() {
return {
userId: 2
}
}
}
那么如何拼接上这个变量呢?需要给router-link绑定上to属性
<router-link :to="'/user/' + userId">用户</router-link>
<router-view/>
实际项目中,我们需要从url中拿到我们path上面拼接的userId,并且在组件中做一个展示,那么我们如何做呢?
User.vue
<template>
<div>
<h2>用户界面</h2>
<h2>{
{
userId}}</h2>
<h2>{
{
$route.params.userId}}</h2> <!-- 和{
{
userId}}一样的效果 -->
</div>
</template>
<script>
export default {
name: "User",
// 我们可以创建一个计算属性,里面去 $route中获取参数列表中的 userId变量(path中拼接的那个值),参数列 表中的 变量名都是在 路由配置时定义的
computed:{
userId(){
return this.$route.params.userId;
}
}
}
</script>
那么前面我们学到的 $router
,这里又来了一个 $route
,这两个是什么意思呢?
$router:是获取到的我们创建的 vue-router的实例
const router = new VueRouter({
routes,
mode: 'history'
});
r o u t e : 是 我 们 获 取 到 的 处 于 活 跃 状 态 的 一 个 路 由 , 比 如 在 A p p . v u e 中 , 显 示 的 是 U s e r 组 件 , 那 么 这 个 route:是我们获取到的处于活跃状态的一个路由,比如在App.vue中,显示的是 User组件,那么这个 route:是我们获取到的处于活跃状态的一个路由,比如在App.vue中,显示的是User组件,那么这个route就是获取的 User组件相关的路由。
const routes = [
......
// User组件的相关路由
{
path: "/user/:userId", //:userId,是在参数列表中定义了一个 userId的变量,组件中可以取出来
component: User
}
]
vue-router打包文件解析
我们先执行 npm run build
打包一下项目,然后打开dist目录下的js目录,会发现:
路由懒加载
当打包构建应用时,JavaScript包会变得非常大,影响页面的加载。如果我们能把不同路由对象的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样会很高效。
由上面打包解析可知,为我们模块导入导出做支撑的代码和第三方插件代码一定要先加载,不然会导致功能不可用。那么我们可以懒加载入手的就是我们的业务层代码
,需要用到时再加载某个业务代码。
如何使用?
在配置路由的时候,不直接配置组件,而是配置一个箭头函数,然后导入相关的组件,这样子就能够每一个路由生成一个js文件,当浏览器访问时,不会把所有的资源js文件都下载到本地,而是用到某一个路由的时候,再把相应的路由js文件下载到浏览器执行。
-
方式一:AMD写法
const About = resolve => require(['../components/Abount.vue'],resolve)
-
方式二:ES6写法(推荐)
const Home = () => import('../components/Home.vue')
详细router下的index.js配置
// 配置路由相关的信息
import Vue from 'vue'
import VueRouter from 'vue-router'
// 路由懒加载方式
const Home = () => import('../components/Home')
const About = () => import('../components/About')
const User = () => import('../components/User')
Vue.use(VueRouter)
// 2.创建路由对象
const routes = [
{
path: "/",
redirect: '/home'
},
{
path: "/home",
component: Home
},
{
path: "/about",
component: About
},
{
path: "/user/:userId",
component: User
}
]
const router = new VueRouter({
routes,
mode: 'history'
});
//3.将router传给vue实例
export default router
然后执行打包npm run build
,打包后的js目录中,可以看到一个懒加载路由对应一个js文件。
嵌套路由
假如Home主页组件中又有两个子组件要分别展示,那么就需要用到嵌套路由。实现嵌套路由有两个步骤:
- 创建对应的子组件,并且在路由映射配置对应的子路由
- 在组件内部使用<router-view>标签
定义两个组件
HomeNews.vue
<template>
<div>
<ul>
<li>新闻1</li>
<li>新闻2</li>
<li>新闻3</li>
</ul>
</div>
</template>
<script>
export default {
name: "HomeNews"
}
</script>
HomeMessage.vue
<template>
<div>
<ul>
<li>消息1</li>
<li>消息2</li>
<li>消息3</li>
</ul>
</div>
</template>
<script>
export default {
name: "HomeMessage"
}
</script>
在Home路由中配置子路由,HomeNews和HomeMessage
// 采用懒加载路由方式
const Home = () => import('../components/Home')
const HomeNews = () => import('../components/HomeNews')
const HomeMessage = () => import('../components/HomeMessage')
const routes = [
{
path: "/",
redirect: '/home'
},
{
path: "/home",
component: Home,
children:[ // 在Home路由下,配置子路由
{
path: 'news', // 子路由中的path 不能加 /,直接定义path就行
component: HomeNews
},
{
path: 'message',
component: HomeMessage
}
]
},
]
在Home.vue中使用标签,展示子组件
<template>
<div>
<h2>首页</h2>
<!-- 这里 to路径一定要写全,否则找不到 -->
<router-link to="/home/news">新闻</router-link>
<router-link to="/home/message">消息</router-link>
<router-view></router-view>
</div>
</template>
同样可以在子组件中定义一个默认路由:
children:[
{
// path直接为空即可
path: '',
// 重定向到 news
redirect: 'news'
},
{
path: 'news',
component: HomeNews
},
{
path: 'message',
component: HomeMessage
}
]
参数传递
传递参数主要有两种类型:params和query
params方式
配置动态路由的时候,在path上拼接对应的值,然后在组件中可以this.$route.params.xxx
获取到这个值。
query方式
普通方式配置路由,传递时使用对象的方式来进行传值,路径上会变成问号传值的方式把数据展示在url上面。
比如配置一个Profile.vue组件:
<template>
<div>
<h2>我是Profile组件</h2>
</div>
</template>
<script>
export default {
name: "Profile"
}
</script>
配置路由:
{
path: "/profile",
component: Profile
}
具体传值方式:直接绑定 to属性,给传入一个对象。path就是跳转路径,query中写的是拼接的k-v
<router-link :to="{path:'/profile', query: {name:'wlh', age:21}}">档案</router-link>
在Profile.vue中如何取出这些数据呢?
使用 $route.query可以获取到一个对象,$route.query.xxx获取到具体的属性的值
<template>
<div>
<h2>我是Profile组件</h2>
<h2>{
{
$route.query.name}}</h2>
<h2>{
{
$route.query.age}}</h2>
</div>
</template>
如果不使用router-link,使用代码方式呢?
$router.push方式进行跳转,push中可以直接传递一个对象。
<template>
<div id="app">
<router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link>
<button @click="userClick">用户</button>
<button @click="profileClick">档案</button>
<router-view/>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
userId: 2
}
},
methods: {
userClick(){
this.$router.push('/user/'+ this.userId)
},
profileClick(){
this.$router.push({
path: "/profile",
query:{
name: 'wlh',
age: 21
}
})
}
}
}
</script>
导航守卫
监听从某一个组件跳到另一个组件时,我们可以做些事情。比如,从一个组件跳到另一个组件时,把当前的title标题改一下。
再来复习下vue的生命周期
- created() : 当vue实例创建完之后,会发生回调
- mounted() : 当template挂载到整个dom后回调
- update() : 页面发生变化时,回调
那么在vue-router中如何全局监听路由的跳转呢?
前置钩子(监听回调函数) 监听跳转前
前置钩子,或者叫前置守卫。
router实例中有一个 beforeEach()的方法,需要在此方法中进行操作。我们可以在路由配置时给每个route对象加上一些元数据
const routes = [
...
{
path: "/about",
component: About,
meta:{
title:"关于"
}
},
{
path: "/user/:userId",
component: User,
meta:{
title:"用户"
}
},
{
path: "/profile",
component: Profile,
meta:{
title:"档案"
}
}
]
const router = new VueRouter({
routes,
mode: 'history'
});
// 使用 router的beforeEach方法 (前置钩子函数)
router.beforeEach((to, from, next) => {
// 从 to中(要跳转到的route中) 找到元数据,给当前title赋值
document.title = to.matched[0].meta.title;
// 放行,注意如果不放行,路由就不会跳转了
next()
});
to(也就是route对象)中有个matched的列表,这里存放的是所有有关联的route,一般是父子路由信息展示为一个列表,只找第一个(父路由)即可。
后置钩子 (监听跳转后)(后置守卫)
既然有前置钩子,那么同样有后置钩子,感觉跟spring的拦截器很像,方法执行前拦截和方法执行后拦截。。
如果是后置钩子afterEach函数,不需要调用next()函数,因为next()已经执行过了
router.afterEach((to, from) => {
console.log('--------------')
});
next()
守卫中有个next()函数,这个函数有几种传参方式:
- next() : 无参,执行下一步跳转到该跳转的路由下
- next(false) : 直接中断跳转
- next(‘/login’) 或者next({path:‘/login’}),跳到别的路由(多用于判断登录)
那么上面说的都是全局守卫,能否搞一些局部守卫呢?当然可以
局部守卫
-
路由独享守卫, beforeEnter函数
const routes = [ { path: '/users/:id', component: UserDetails, beforeEnter: (to, from) => { // reject the navigation return false } }, ]
beforeEnter
守卫 只在进入路由时触发,不会在params
、query
或hash
改变时触发。例如,从/users/2
进入到/users/3
或者从/users/2#info
进入到/users/2#projects
。它们只有在 从一个不同的 路由导航时,才会被触发。 -
组件内守卫(见名知意,是在组件中创建的)
beforeRouteEnter
beforeRouteUpdate
beforeRouteLeave
const UserDetails = { template: `...`, beforeRouteEnter(to, from) { // 在渲染该组件的对应路由被验证前调用 // 不能获取组件实例 `this` ! // 因为当守卫执行时,组件实例还没被创建! }, beforeRouteUpdate(to, from) { // 在当前路由改变,但是该组件被复用时调用 // 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候, // 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。 // 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this` }, beforeRouteLeave(to, from) { // 在导航离开渲染该组件的对应路由时调用 // 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this` }, }
不过,我们可以通过传一个回调给 next
来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数:
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
}
keep-alive
- keep-alive是vue内置的一个组件,可以使被包含的组件保留状态,避免重新被渲染
- router-view也是一个组件,如果直接被包在 keep-alive中,所有路径匹配到的试图组件都会被渲染。
实际上,我们前面的路由跳转,每次跳转时,上一次的路由的组件都会被销毁,跳转到一个组件时再创建这个需要跳转到的vue组件。但是实际项目中可能存在需求:要保留我上次组件状态
使用keep-alive标签后,里面的路由跳转时都不会被销毁,而是保存在内存中。
keep-alive使用后,只有被包含在keep-alive中的组件,可以使用 activated()和deactivated(),来监听当前组件是否是活跃状态.需要注意的是:这个被保存的组件如果下面有子组件,那么子组件是不会被保存起来的
App.vue中,使用keep-alive,也就是里面的所有组件都只被创建一次后会被保存到内存中。
<template>
<div id="app">
<router-link to="/home">首页</router-link>
<router-link to="/about">关于</router-link>
<!-- <router-link :to="'/user/' + userId">用户</router-link>
<router-link :to="{path:'/profile', query: {name:'wlh', age:21}}">档案</router-link> -->
<button @click="userClick">用户</button>
<button @click="profileClick">档案</button>
<keep-alive>
<router-view/>
</keep-alive>
</div>
</template>
在Home.vue子组件中:
<template>
<div>
<h2>首页</h2>
<router-link to="/home/news">新闻</router-link>
<router-link to="/home/message">消息</router-link>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "Home",
data(){
return {
path: '/home/news'
}
},
activated() {
this.$router.push(this.path)
},
// 在导航离开渲染该组件的对应路由时调用
beforeRouteLeave (to, from, next) {
// this.path = from.path
this.path = this.$route.path;
next()
}
}
</script>
其实keep-alive中,有两个很重要的属性
- include : 字符串或者正则表达式,只有匹配的组件才会被缓存
- exclude : 字符串或者正则表达式,匹配的组件不被缓存
<keep-alive exclude="Profile,User">
<router-view/>
</keep-alive>