进阶

路由

认识路由

我们首先新创建一个项目,使用脚手架2,注意在选择的时候选择vue-router

什么是路由

路由是网络工程里面的一个专业术语,路由(routing)就是通过互联的网络将信息从源地址传输到目的地址的活动。

前端渲染和后端渲染

1、后端渲染:早期的JSP就是将页面写好,然后交给前端

2、前端渲染:将数据交给前端,然后前端写好页面

vue-router基本使用

目前前段的三大框架都有自己的路由实现:

1、Angular的ngRouter

2、React的ReactRouter

3、Vue的vue-router

路由框架的搭建

vue-router是Vue.js的官方插件,我们安装路由只需要npm来安装:

1、npm install vue-router --save

这一步之前在我们使用脚手架创建的时候已经完成了

2、在src下创建一个router文件夹,在router文件夹下面创建一个index.js,在JS文件中配置所有的路由信息

// 1、导入Vue
import Vue from 'vue'

// 2、导入VueRouter
import VueRouter from "vue-router";

// 3、Vue导入插件,这里导入的插件是VueRouter
Vue.use(VueRouter)

// 4、配置关系对象,描述路由和组件的关系
const routes = [

]

// 5、配置router对象,配置路由和组件之间的关系
const router = new VueRouter({
  routes
})

// 6、导出router,让Vue实例能够挂载router对象
export default router

3、在main.js中的vue实例中挂载vuerouter

import Vue from 'vue'
import App from './App'

// 1、我们导入一个文件夹之后,会默认导入文件夹下面的index文件
import router from './router'
Vue.config.productionTip = false

new Vue({
  el: '#app',
  // 2、Vue实例挂载router
  router,
  render: h => h(App)
})

使用路由

1、首先我们将App.vue中的全部内容都删掉,还他一个干净的页面

<template>
  <div id="app">
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style>
</style>

2、在components下面创建两个Vue页面,让这两个页面等会进行路由

  • Home.vue
<template>
  <div>
    <h2>首页</h2>
  </div>
</template>

<script>
export default {
name: "Home"
}
</script>

<style scoped>

</style>
  • About.vue
<template>
  <div>
    <h2>关于</h2>
  </div>
</template>

<script>
export default {
  name: "About"
}
</script>

<style scoped>

</style>

3、配置路由

import Vue from 'vue'
import VueRouter from "vue-router";

// 1、导入两个页面
import Home from '../components/Home'
import About from '../components/About'

Vue.use(VueRouter)


// 2、配置路由关系,一个对象就是一个路由的映射
const routes = [
  // 3、path是路由,component配置对应的页面
  {
    path: '/home',
    component: Home
  },
  {
    path: '/about',
    component: About
  }
]


const router = new VueRouter({
  routes
})


export default router

3、在App.vue中编写页面,关联对应的路由

<template>
  <div id="app">
    <!--  1、这个router-link是全局组件,可以在全局使用,最终会渲染称为a标签  -->
    <router-link to="/home">首页</router-link>
    <router-link to="/about">关于</router-link>

    <!--  2、这个router-view是表示router-link所链接的内容,将会渲染到这里  -->
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style>
</style>

路由的重定向,默认页面

用户进入一个页面之后,应该进入到一个默认的页面,所以我们应该再配置一个映射关系

import Vue from 'vue'
import VueRouter from "vue-router";

import Home from '../components/Home'
import About from '../components/About'

Vue.use(VueRouter)


const routes = [
  // redirect就是重定向,也就是说当path为空的时候,重定向到path为/home下
  {
    path: '',
    redirect: '/home'
  },
  // 重定向到这里,最终指向Home.vue
  {
    path: '/home',
    component: Home
  },
  {
    path: '/about',
    component: About
  }
]


const router = new VueRouter({
  routes
})


export default router

路由修改为history页面

我们的页面现在是一个hash的样式,有锚点,比较难看

下面我们改变为history样式,那么就比较好看

import Vue from 'vue'
import VueRouter from "vue-router";

import Home from '../components/Home'
import About from '../components/About'

Vue.use(VueRouter)


const routes = [
  {
    path: '',
    redirect: '/home'
  },
  {
    path: '/home',
    component: Home
  },
  {
    path: '/about',
    component: About
  }
]


const router = new VueRouter({
  routes,
  // 将默认样式改变为history样式
  mode: 'history'
})


export default router


之前我们使用了router-link这个标签,之前只用了to属性,其实还有其他属性

1、tag属性:可以将标签渲染为别的样式,比如button

<router-link to="/home" tag="button">首页</router-link>

2、replace属性:用户不能后退页面了,因为使用了这个结构之后,底层不再使用栈结构,而是直接替代当前页面,也就是说不能返回上一层页面了

<router-link to="/about" replace>关于</router-link>

3、router-link-active:这是一个样式,并不是一个属性,当某一个组件位于选中状态的时候,标签上会出现这个样式,我们可以做一些点击效果,比如点击就字体变红之类的

4、假如我们认为router-link-active名字太长,那么可以在路由选项中将这个名字更改为别的内容,比如active

import Vue from 'vue'
import VueRouter from "vue-router";

import Home from '../components/Home'
import About from '../components/About'

Vue.use(VueRouter)


const routes = [
  {
    path: '',
    redirect: '/home'
  },
  {
    path: '/home',
    component: Home
  },
  {
    path: '/about',
    component: About
  }
]


const router = new VueRouter({
  routes,
  mode: 'history',
  // 将router-link-active样式改为active
  linkActiveClass: 'active'
})


export default router

通过代码跳转路由

<template>
  <div id="app">

    <button @click="homeClick">首页</button>
    <button @click="aboutClick">关于</button>

    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    homeClick(){
      // 使用Vue的方式来跳转页面,同样会去寻找vue-router中的内容,最终会找到Home.vue
      this.$router.push('/home')
    },
    aboutClick(){
      this.$router.push('/about')
    }
  }
}
</script>

<style>
</style>

注意,我们的push()代表的是向栈中加入了当前页面,假如我们点击返回上一层页面,还是可以返回的

如果要替换当前页面,那么使用this.$router.replace()


动态路由

某些情况下,路由可能是不确定的,比如我进入/user/1或者/user/2,这个后面是用户的ID,这个我们不太确定

1、为了方便演示,我们首先创建一个页面

<template>
  <div>用户信息</div>
</template>

<script>
export default {
  name: "User"
}
</script>

<style scoped>

</style>

2、在router的配置中配置

import Vue from 'vue'
import VueRouter from "vue-router";

Vue.use(VueRouter)

import Home from '../components/Home'
import About from '../components/About'
import User from '../components/User'

const routes = [
  {
    // 注意,这里的:userId是不确定的
    path: '/user/:userId',
    component: User
  },
  {
    path: '',
    redirect: '/home'
  },
  {
    path: '/home',
    component: Home
  },
  {
    path: '/about',
    component: About
  }
]


const router = new VueRouter({
  routes,
  mode: 'history',
  linkActiveClass: 'active'
})


export default router

3、在Vue实例中编写

<template>
  <div id="app">

    <router-link :to="'/user/'+userId">用户</router-link>
    
    <!--  注意不是router是route,这个可以拿到我们路由中的参数  -->
    <h2>{{$route.params.userId}}</h2>

    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      userId: '1'
    }
  }
}
</script>

<style>
</style>

路由懒加载

当打包应用的时候,JS的包会非常大,假如我们一次性请求,可能会产生很长时间的停顿,让页面进入空白,这个是我们不想看到的

那么我们这个时候就需要路由的懒加载了:路由懒加载主要是将路由对应的组件打包为一个个的JS代码块,这样在请求内容的时候是分批请求,而不是一次性请求,这样访问速度大大提升

那么我们之前的路由懒加载和之前加载的区别:

import Vue from 'vue'
import VueRouter from "vue-router";

Vue.use(VueRouter)


const User = ()=>import('../components/User')
const Home = ()=>import('../components/Home')
const About = ()=>import('../components/About')

const routes = [
  {
    path: '/user/:userId',
    component: User
  },
  {
    path: '',
    redirect: '/home'
  },
  {
    path: '/home',
    component: Home
  },
  {
    path: '/about',
    component: About
  }
]


const router = new VueRouter({
  routes,
  mode: 'history',
  linkActiveClass: 'active'
})


export default router

路由嵌套

我们有可能会在一个路由中嵌套一个子路由,那么这就是嵌套路由

1、按照这种方式,我们创建这几个Vue文件,创建过程就不写了

2、在router中的JS文件中引入

import Vue from "vue";
import VueRouter from "vue-router";

Vue.use(VueRouter);

const User = () => import("../components/User");

const Home = () => import("../components/Home");
// 引入两个内容
const Message = () => import('../components/home/Message')
const News = () => import('../components/home/News')

const About = () => import("../components/About");

const routes = [
  {
    path: "/user/:userId",
    component: User
  },
  {
    path: "",
    redirect: "/home"
  },
  {
    path: "/home",
    component: Home,
    // 增加子路由
    children: [
      {
        path: "news",
        component: News
      },
      {
        path: "message",
        component: Message
      }
    ]
  },
  {
    path: "/about",
    component: About
  }
];

const router = new VueRouter({
  routes,
  mode: "history",
  linkActiveClass: "active"
});

export default router;

注意,我们的子路由是通过children这个数组来进行实现的

而且子路由并不需要加/home,只是home,前面会自动拼接

3、在父路由对应的Vue文件中,也就是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",
};
</script>

<style scoped></style>

注意,我们这里的路由是全路由,而不是一个单独的子路由


参数传递

我们肯定想在路由进行切换的时候顺便传递点消息

传递参数有两种方式:

1、params

2、query

params的类型

1、配置路由格式:/router/:id

import Vue from "vue";
import VueRouter from "vue-router";

Vue.use(VueRouter);

const User = () => import("../components/User");
const Home = () => import("../components/Home");
const About = () => import("../components/About");

const routes = [
  {
    path: "/user/:userId",
    component: User
  },
  // ...
];

const router = new VueRouter({
  routes,
  mode: "history"
});

export default router;

2、传递的方式:在path后面跟上对应的值

<template>
  <div id="app">

    <router-link to='/home'>首页</router-link>

    <router-link :to="'/user/'+userId">用户</router-link>

    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      userId: '1'
    }
  }
}
</script>

<style>
</style>

3、传递后形成的路径:/router/123

这种方式就是我们刚才在讲动态路由的时候的方式

4、取出内容

<template>
  <div>
    用户信息

    <br>
    {{ userId }}
  </div>

</template>

<script>
export default {
  name: "User",
  data(){
    return{
      userId: this.$route.params.userId
    }
  }
}
</script>

<style scoped>

</style>

query的类型

1、配置路由就是我们的普通配置

2、传递的时候,对象中使用query的key作为传递方式

<template>
  <div id="app">

    <router-link to='/home'>首页</router-link>


    <router-link :to="message">关于</router-link>


    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      userId: '1',
      message: {
        // 路径
        path: '/about',
        // 这个就是query,使用query中的name作为key传递
        query:
          {
            name: 'maple'
          }
      }
    }
  }
}
</script>

<style>
</style>

3、传递后形成的路径:router?id=123

4、取出路径中的内容

<template>
  <div>
    <h2>关于</h2>
    {{message}}
  </div>
</template>

<script>
export default {
  name: "About",
   data(){
    return{
      message: this.$route.query.name
    }
  }
}
</script>

<style scoped>

</style>

但是我们在进行参数传递的时候,总不是进行router-link的传递,那么我们应该这样传递:

<template>
  <div id="app">

    <!--    <router-link to='/home'>首页</router-link>-->

    <!--    <router-link :to="'/user/'+userId">用户</router-link>-->

    <!--    <router-link :to="message">关于</router-link>-->

    <button @click="toUser">用户</button>
    <button @click="toAbout">关于</button>

    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'App',
  data() {
    return {
      userId: '1',
      message: {
        name: 'maple'
      }
    }
  },
  methods: {
    toUser() {
      this.$router.push(`/user/${this.userId}`)
      // 这两种都可以,但是我们仍然选择使用上面的ES6语法
      // this.$router.push('/user/'+this.userId)
    },
    toAbout() {
      this.$router.push({
        path: '/about',
        query: this.message
      })
    }
  }
}
</script>

<style>
</style>

注意,这里我们将path和query分开了,不再一次性写到data中了


全局导航守卫

有的时候,我们在进行路由跳转的时候,我们需要在跳转过程中进行一些操作,所以我们需要导航守卫

比如控制访问权限就可以使用导航守卫来进行实现

首先我们来看一下几个生命周期的函数

<script>
export default {
  name: "Home",
  created() {
    console.log('组件创建的回调函数')
  },
  mounted() {
    console.log('组件挂载到dom上的回调函数')
  },
  updated() {
    console.log('页面改变的时候的回调函数')
  },
  activated() {
    console.log('页面进入活跃状态调用')
  },
  deactivated() {
    console.log('页面进入不活跃状态调用')
  }
};
</script>

上面的这几个生命周期函数,十分好用,当组件到达对应的生命周期,那么他就会调用对应的函数

但是这样做比较麻烦,我们必须在每一个vue页面上增加这样的代码,所以我们需要一个东西来监听全局的路由跳转

我们看一下router/index.js中的内容

import Vue from "vue";
import VueRouter from "vue-router";

Vue.use(VueRouter);

const User = () => import("../components/User");

const Home = () => import("../components/Home");

const About = () => import("../components/About");

const routes = [
  {
    path: "",
    redirect: "/home"
  },
  {
    path: "/user/:userId",
    component: User,
    meta: {
      title: '用户'
    }
  },
  {
    path: "/home",
    component: Home,
    meta: {
      title: '首页'
    }
  },
  {
    path: "/about",
    component: About,
    meta: {
      title: '关于'
    }
  }
];

const router = new VueRouter({
  routes,
  mode: "history"
});


// 路由跳转之前进行监听,类似拦截器,从from到to
router.beforeEach((to, from, next) => {

  /*
    to的类型是Route,也就是我们在上面定义的这些东西,所以可以直接获取meta数据,得到对应的内容
    我们这里直接就将下一个页面的内容转换为了我们在这里定义的内容
   */
  document.title = to.meta.title

  // 跳转到下一个页面
  next()
})

export default router;

next()有很多用法,假如传入false就是中断当前导航,可以传入一个对应的路径直接跳转到对应路径

我们刚才的这个东西叫做前置钩子,也就是在页面跳转之前进行拦截然后修改其中的内容

假如是后置钩子,就是router.afterEach((to,from)=>{})

我们上面的前置和后置钩子,叫做全局守卫,那么除了全局守卫之外,还有路由独享守卫和组件内的守卫

这些东西和全局导航守卫差不多,有需要可以直接去官网上查询


keep-alive

keep-alive是vue内置的组件,所有被keep-alive包裹的组件都会被缓存起来

等之后再次回到这个组件的时候,就会读取缓存内容,而不是重新创建一个新的组件

<template>
  <div id="app">
    <keep-alive>
      <router-view/>
    </keep-alive>
  </div>
</template>

Promise

Promise介绍

Promise是ES6新引入的一个语法,其实是异步编程的解决方案,比如网络请求

网络请求很复杂的时候,假如有多层嵌套,那么就会陷入回调地狱,但是Promise可以很优雅地解决这个问题

Promise基本语法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>


<script>
    // 1、使用一个异步操作
    setTimeout(() => {
        // 延迟一秒钟打印Hello World
        console.log('Hello World');

        // 打印完成之后,再次调用打印Hello Promise,假如这种情况比较多,那么就陷入了回调地狱
        setTimeout(() => {
            console.log('Hello Promise')

            setTimeout(() => {
                console.log('Hello Vue')
            }, 1000)

        }, 1000)
    }, 1000)


    /*
        2、使用Promise来对异步操作进行封装
        resolve:函数,调用了resolve之后,会直接调用then()
        reject:函数,调用了reject之后,会直接调用
     */
    new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('Hello World')
            resolve
        }, 1000)
    }).then(() => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('Hello Promise')
                resolve
            }, 1000)
        })
    }).then(() => {
        setTimeout(() => {
            console.log('Hello Vue')
        }, 1000)
    })
</script>
</body>
</html>

可以看到这种写法其实就是将树形结构变为了线性结构

那么我们什么时候需要使用到Promise呢?异步操作的时候,使用Promise对操作进行封装


Promise的三种状态

1、pending:等待状态,比如正在进行网络请求的时候,或者定时器没有到时间

2、fulfill:满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then

3、reject:拒绝状态,当我们主动回调了reject时,就处于该状态,并且回调.catch()

我们在上面是一种处理形式,但是我们还有另外的处理形式

/**
 * 使用Promise的另外的处理形式
 */
new Promise((resolve, reject) => {

}).then(
    // 处理数据
    data => {

    },
    // 处理错误信息
    err => {

    }
)

Promise的all方法

假如我们现在有一个需求,这个需求需要两次请求出来的数据拼接出来,那么我们这两个结果都有的情况下才能向下进行

但是我们本身网络请求是异步的,那么我们应该如何确定什么时候两个结果都拿到了呢?

那么这个时候我们就有一个方法,就是Promise.all()方法,这里面的参数是可迭代对象,也就是说可以遍历的对象,这些对象可以是很多的Promise

/**
 * 4、Promise all
 */
Promise.all([
    new Promise(((resolve, reject) => {
        $ajax({
            url: '',
            success: function (data) {
                resolve(data)
            }
        })
    })),
    new Promise(((resolve, reject) => {
        $ajax({
            url: '',
            success: function (data) {
                resolve(data)
            }
        })
    }))
]).then(results => {
    // 第一个请求的结果
    results[0]
    // 第二个请求的结果
    results[1]
})

Vuex

Vuex的概念

Vuex是官网为Vue应用程序开发的状态管理模式,其实就是一个状态管理工具而已

一般来说,有一些共享的状态需要放到Vuex里面

1、用户登录状态、名称、头像

2、地理位置

3、商品的收藏,购物车的物品

4、…….


Vuex的使用

1、npm install vuex --save

2、在src新建一个文件夹store,里面放置一些Vuex的内容

3、在store中创建index.js

import Vue from 'vue';

// 1、引入Vuex
import Vuex from "vuex";

// 2、使用Vuex
Vue.use(Vuex)

// 3、创建Vuex对象,但是我们创建的是vuex的store对象而不是vuex对象
const store = new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  getters: {},
  modules: {}
})

// 4、导出
export default store

其实我们可以看到store对象中的这些东西是比较固定的,这些就是我们使用的一些Vuex的一些状态

这些状态到后面会一一讲到,其中actions里面是用于异步操作的

4、在main.js中引入

import Vue from 'vue'
import App from './App'
import router from './router'

import store from "./store";

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  render: h => h(App)
})

之后的内容,我们就会在$store中拿到这个store中的内容

这个Vuex是一个全局的单例模式


state 和 mutations

在state,我们定义一些变量。对于mutations而言,我们一般是用于定义一些方法

1、在Vuex中定义一些变量和方法

import Vue from 'vue';

// 1、引入Vuex
import Vuex from "vuex";

// 2、使用Vuex
Vue.use(Vuex)

// 3、创建Vuex对象,但是我们创建的是vuex的store对象而不是vuex对象
const store = new Vuex.Store({
  state: {
    // state这里就是用于定义一些变量的
    couter: 0
  },
  mutations: {
    // 这个mutations用于定义一些方法,这些方法中的state就是我们这里store中的state
    increment(state) {
      state.couter++
    },
    decrement(state) {
      state.couter--
    }
  },
  actions: {},
  getters: {},
  modules: {}
})

// 4、导出
export default store

2、在我们普通的Vue文件中使用

<template>
  <div id="app">
    <!-- 这里直接使用$store来获取我们的vuex中store中的内容 -->
    <div>{{ $store.state.couter }}</div>

    <button @click="addNum">+</button>
    <button @click="lessNum">-</button>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    addNum() {
      // 这里注意了,我们调用方法不是直接使用mutations,而是要通过commit事件,传入要调用的方法名字调用
      this.$store.commit('increment')
    },
    lessNum() {
      this.$store.commit('decrement')
    }
  }
}
</script>

除了上面的基本使用,我们还有一些细节需要说明

1、只要是更改state中的内容,一定要经过mutations

2、mutations中定义的函数我们可以看为两部分:事件类型type、回调函数handler

比如我们的decrement(state) {state.couter--}中,decrement是事件类型,其余为回调函数

3、这样我们其实知道了this.$store.commit('decrement')中,decrement其实是一个事件类型

4、mutations中方法的参数除了state之外,还可以接受参数

import Vue from 'vue';

// 1、引入Vuex
import Vuex from "vuex";

// 2、使用Vuex
Vue.use(Vuex)

// 3、创建Vuex对象,但是我们创建的是vuex的store对象而不是vuex对象
const store = new Vuex.Store({
  state: {
    // state这里就是用于定义一些变量的
    couter: 1
  },
  mutations: {
	// 注意这里,我们接受的类型除了state还有data
    decrement(state, data) {
      state.couter -= data
    }
  },
  actions: {},
  getters: {
    power(state) {
      return Math.abs(state.couter)
    }
  },
  modules: {}
})

// 4、导出
export default store
<template>
  <div id="app">
    <div>{{ $store.getters.power }}</div>
    <button @click="lessNum(5)">-</button>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    lessNum(data) {
      // 这里直接传递参数
      this.$store.commit('decrement',data)
    }
  }
}
</script>

5、事件的提交方式:事件的提交方式有两种,之前我们都是第一种提交方式,下面我说第二种

<template>
  <div id="app">
    <!-- 这里直接使用$store来获取我们的vuex中store中的内容 -->
    <div>{{ $store.getters.power }}</div>

    <button @click="addNum">+</button>
    <button @click="lessNum(5)">-</button>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    lessNum(data) {
      // 1、第一种提交方式,注意这里的data就是传递过来的值data
      // this.$store.commit('decrement',data)

      // 2、第二种提交方式,注意这里的data不仅仅是data,而是整个对象
      this.$store.commit({
        type: 'decrement',
        // 假如是整个对象的话,我们有一个专业的名称:载荷 payload
        data,
        // 载荷的意思整个对象,也就是当前的commit中的所有内容都会提交过去
        // 所以也包括这个18
        age: 18
      })
    }
  }
}
</script>
import Vue from 'vue';

// 1、引入Vuex
import Vuex from "vuex";

// 2、使用Vuex
Vue.use(Vuex)

// 3、创建Vuex对象,但是我们创建的是vuex的store对象而不是vuex对象
const store = new Vuex.Store({
  state: {
    // state这里就是用于定义一些变量的
    couter: 1
  },
  mutations: {
    // 这里第二个参数是整个对象,那么我们就使用专业的名称载荷payload来接受,可以接受所有参数
    decrement(state, payload) {
      // 可以接受所有的内容参数
      console.log(payload.age)
      state.couter -= payload.data
    }
  },
  actions: {},
  getters: {
    power(state) {
      return Math.abs(state.couter)
    }
  },
  modules: {}
})

// 4、导出
export default store

一般来说我们使用第二种方式传递参数


getter

getter,简单来说就是对state中的某些内容做个封装,然后传出去,比如对数据乘二,或者做一些别的处理

import Vue from 'vue';

// 1、引入Vuex
import Vuex from "vuex";

// 2、使用Vuex
Vue.use(Vuex)

// 3、创建Vuex对象,但是我们创建的是vuex的store对象而不是vuex对象
const store = new Vuex.Store({
  state: {
    // state这里就是用于定义一些变量的
    couter: 1
  },
  mutations: {
    // 这个mutations用于定义一些方法,这些方法中的state就是我们这里store中的state
    increment(state) {
      state.couter++
    },
    decrement(state) {
      state.couter--
    }
  },
  actions: {},
  getters: {
    power(state) {
      return Math.abs(state.couter)
    }
  },
  modules: {}
})

// 4、导出
export default store
<template>
  <div id="app">
    <!-- 这里之前是state,这里改成了getters -->
    <div>{{ $store.getters.power }}</div>

    <button @click="addNum">+</button>
    <button @click="lessNum">-</button>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    addNum() {
      // 这里注意了,我们调用方法不是直接使用mutations,而是要通过commit事件,传入要调用的方法名字调用
      this.$store.commit('increment')
    },
    lessNum() {
      this.$store.commit('decrement')
    }
  }
}
</script>

这里返回的时候,除了一个结果,甚至还可以是一个函数等,一般来说我们返回函数是很有用的


actions

Action类似Mutation,但是Action是用于异步操作的,所以Action可以替代Mutation进行异步操作

import Vue from 'vue';

// 1、引入Vuex
import Vuex from "vuex";

// 2、使用Vuex
Vue.use(Vuex)

// 3、创建Vuex对象,但是我们创建的是vuex的store对象而不是vuex对象
const store = new Vuex.Store({
  state: {
    couter: 1
  },
  mutations: {
    increment(state) {
      state.couter++
    },
  },
  actions: {
    // context是上下文的意思,上下文的意思是该有的东西它全都有
    up(context) {
      // 可以使用上下文直接调用mutation的内容,进而修改state中的内容
      // 模拟异步操作
      setTimeout(() => context.commit('increment'), 1000)
    }
  },
  getters: {
    power(state) {
      return Math.abs(state.couter)
    }
  },
  modules: {}
})

// 4、导出
export default store
<template>
  <div id="app">
    <!-- 这里直接使用$store来获取我们的vuex中store中的内容 -->
    <div>{{ $store.getters.power }}</div>

    <button @click="addNum">+</button>
    <button @click="lessNum(5)">-</button>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    addNum() {
		// 这里变为了dispatch,而不是commit,因为我们调用的是actions中的方法
      this.$store.dispatch('increment')
    }
  }
}
</script>

除了这个dispatch之外,其余的没有啥区别


modules

modules的用法就是,可以将store中的内容进行模块的划分,一看你就懂了

import Vue from 'vue';

// 1、引入Vuex
import Vuex from "vuex";

// 2、使用Vuex
Vue.use(Vuex)

// 3、创建Vuex对象,但是我们创建的是vuex的store对象而不是vuex对象
const store = new Vuex.Store({
  state: {
  },
  mutations: {
  },
  actions: {
  },
  getters: {
  },
  modules: {
    A: {
      state: {
        couter: 1
      },
      mutations: {},
      actions: {},
      getters: {},
      modules: {}
    },
    B: {
      state: {},
      mutations: {},
      actions: {},
      getters: {},
      modules: {}
    }
  }
})

// 4、导出
export default store

其实是很简单的,怕一个Vuex里面的内容太复杂,所以开始套娃

但是注意了,我们modules中的内容最终是放到了state里面的,所以我们在vue页面取得其实是从state中取得内容

<template>
  <div id="app">
    <!-- 这里直接使用$store来获取我们的vuex中store中的内容 -->
    <div>{{ $store.state.A.couter }}</div>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
  }
}
</script>

Axios

Axios基本使用

Axios支持多种方式:

  • axios(config)
  • axios.request(config)
  • axios.get(url[,config])
  • axios.delete(url[,config])
  • axios.head(url[,config])
  • axios.post(url[,data[,config]])
  • axios.put(url[,data[,config]])
  • axios.patch(url[,data[,config]])

1、安装:npm install axios --save

2、在main.js中引入

import Vue from 'vue'
import App from './App'
import router from './router'

// 1、导入axios
import axios from "axios";

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  render: h => h(App)
})

// 2、对axios做配置,这个配置是一个对象,可以直接访问
axios({
  url: '目标的访问地址'
})
  .then(res => {
    console.log(res)
  })

上面是最基本的使用,但是假如我们想要以get方式发送请求数据,那么我们可以使用下面的两种方式:

1、在method中写为get

import Vue from 'vue'
import App from './App'
import router from './router'

// 1、导入axios
import axios from "axios";

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  render: h => h(App)
})

// 2、对axios做配置,这个配置是一个对象,可以直接访问
axios({
  url: 'xxx/home/data?type=sell&page=3',
  method: 'get'
}).then(res => {
  console.log(res)
})

axios.get({
  url: 'xxx/home/data',
  params: {
    type: 'sell',
    page: 3
  }
}).then(res => {
  console.log(res)
})

可以看到上面的请求两种方式,都是传递参数的,第一个传递的参数只不过是url拼接,第二个则是使用的params

最终拼成了和第一种的一样的效果,而params是专门针对于get的参数,其他的之后再讲


Axios发送并发请求

有的时候,我们一个页面的渲染需要多个数据,而这多个数据需要多次网络请求,那么这种情况Axios也有处理方式

我们之前讲的Promise有一个all方法,那么Axios其实也有一个all方法

import Vue from 'vue'
import App from './App'
import router from './router'

// 1、导入axios
import axios from "axios";

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  render: h => h(App)
})

// 2、对axios做配置,这个配置是一个对象,可以直接访问
axios.all([
  axios.get({
    url: 'http://123.207.32.32:8000/home/data',
    params: {
      type: 'sell',
      page: 3
    }
  }),
  axios.get({
    url: 'http://123.207.32.32:8000/home/multidata'
  })
]).then(res => {
  console.log(res)
})

假如我们想要将数组进行延展分割的时候,也就是说两个结果分开,那么

axios.all([
  axios.get({
    url: 'http://123.207.32.32:8000/home/data',
    params: {
      type: 'sell',
      page: 3
    }
  }),
  axios.get({
    url: 'http://123.207.32.32:8000/home/multidata'
  })
]).then(axios.spread((res1, res2) => {
  console.log(res1);
  console.log(res2);
}))

axios.spread


配置信息相关

这里有一些我们常用的配置信息

// 基本的URL
axios.defaults.baseURL = 'xxxx'
// 请求头
axios.defaults.headers.post['Content-Type'] = 'application/X-www-form-urlencoded'
// 超时时间
axios.defaults.timeout = 5000
// 常用的数据响应格式:json / blob / documet / arraybuffer / text / stream
responseType: 'json'
// 跨域是否带token
withCredentials: false
// 身份验证
auth: {
    uname: '',
	pwd: ''
}
// 常见的请求方式:get、post、patch、delete
method: 'get'
// URL查询对象,当method为get的时候,我们传params
params: {
    id: 22
}
// 请求体,当我们method设置为post的时候,我们传data
data: {
    key: ''
}

Axios其实我们的一些URL前缀或者一些其他的东西是重复的,那么这些东西就需要抽出来,也就是Axios的全局配置

这些东西全部都可以抽出来,甚至可以单独成立一个文件


Axios实例和模块封装

之前我们都是使用的全局的配置,但是我们一般在开发中是要使用Axios实例+模块封装的方式来进行网络请求

Axios实例

import Vue from 'vue'
import App from './App'
import router from './router'

Vue.config.productionTip = false
new Vue({
  el: '#app',
  router,
  render: h => h(App)
})


// 1、创建一个Axios的实例
const instance1 = axios.create({
  baseURL: 'http://xxx:8000',
  timeout: 5000
})

// 2、使用实例的基础配置,加上真正的封装,形成真正的请求接口
instance1({
  url: '/home/multidata',
}).then(res => {
  console.log(res)
})

// 第二个网络请求
instance1({
  url: '/home/data',
  params: {
    type: 'pop',
    page: 1
  }
})

封装

为了防止代码冗余,封装肯定是需要的,我们的组件抽离是这样做的:

1、在src下面新建立一个文件夹network

2、在network中新建一个JS文件request.js

// 1、导入axios
import axios from "axios";

// 2、因为考虑到可能有多个服务器的配置,所以不是导出的default
export function request(config) {

  // 3、新建一个axios的实例
  const instance = axios.create({
    baseURL: 'http://123.207.32.32:8000',
    timeout: 5000
  })

  // 4、因为这个axios默认是实现了promise的,所以直接搞一个返回,让调用者去处理
  return instance(config)
}


// 5、可能是有其他的配置
export function request2(config) {

}

3、在main.js中引入

import Vue from 'vue'
import App from './App'
import router from './router'

Vue.config.productionTip = false
new Vue({
  el: '#app',
  router,
  render: h => h(App)
})


// 1、导入request
import {request} from './network/request'

// 2、使用最终的配置
request({
  url: '/home/data',
  params: {
    type: 'pop',
    page: 1
  }
}).then(res => {
  console.log(res)
})

Axios拦截器

在发送网络请求,我们可能需要拦截来做一些内容:

1、请求拦截

  • 请求成功拦截
  • 请求失败拦截

2、响应拦截

  • 响应成功拦截
  • 响应失败拦截

Axios的全局拦截和实例拦截

Axios分为实例拦截和全局拦截

import axios from "axios";

/*
  1、Axios的全局拦截器
    - axios.interceptors.request:拦截请求
    - axios.interceptors.response:拦截响应
 */
export function request(config) {

  const instance = axios.create({
    baseURL: 'http://123.207.32.32:8000',
    timeout: 5000
  })

  /*
    2、Axios的实例拦截器,我们以实例拦截器为例子进行讲解
      使用 use()来进行使用
   */

  // 请求拦截
  instance.interceptors.request.use(
    // 这个拦截下来的就是我们定义的配置,也就是instance里面加上config的所有内容,我们可以在这里进行更改
    config => {
      // 中间对config进行一些处理,比如携带token,更改url地址,显示网络请求的加载动画等
      // 我们拦截到config之后,还要进行放行
      return config
    },
    // 请求发送失败来到这里
    err => {

    })

  // 响应拦截
  instance.interceptors.response.use(
    // 拿到结果
    res => {

      // 放行结果,其实我们想要的一般就是服务器中返回的结果,所以其他的信息都不要
      return res.data
    },
    // 响应错误时来到这里
    err => {

    })

  return instance(config)
}

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。