说明

更新时间:2020/9/10 11:03,更新了对应的项目名
更新时间:2020/7/19 22:11,更新了Vuex剩下的相关知识
更新时间:2020/7/18 21:26,更新了Vue Router的剩下的内容和部分Vuex的知识,修改了整体排版
更新时间:2020/7/17 22:46,更新了动态路由到导航守卫的相关内容
更新时间:2020/7/16 23:12,更新了整体vue-cli安装、快速原型开发、创建vue-cli项目以及动态路由的(1)(2)点

本文主要基于vue-cli官网进行学习,同时综合了网上的很多相关教程,本文会持续更新,不断地扩充

本文仅为记录学习轨迹,如有侵权,联系删除

注意:本文主要基于vue-cli官网进行学习,地址:https://cli.vuejs.org/zh/guide/prototyping.html

一、入门系列

建议在观看这篇文章前先花几个小时了解一下Vue的基础语法和部分ES6相关的知识,也可以看一下本人的相关博文菜鸟的Vue基础快速入门菜鸟的ES6与JavaScript学习总结

当然没有Vue的基础和ES6也基本上能看懂这篇博文,里面的代码都有详细贴出来,玩一下就大概上手了

(1)vue-cli安装

在安装之前需要先确保有安装node和npm
在这里插入图片描述
输入安装命令,然后静静等待安装完毕即可

npm i @vue/cli -g

安装完输入vue --version查看安装的版本号
在这里插入图片描述

(2)快速原型开发

参考vue-cli官网的快速原型开发,创建第一个项目
在这里插入图片描述

新建空文件夹用于项目文件(cli-01),用vscode打开项目
在这里插入图片描述
在终端输入命令npm install -g @vue/cli-service-global
在这里插入图片描述
创建App.vue,输入测试的模板内容,在终端输入vue serve运行整个项目
在这里插入图片描述
访问结果
在这里插入图片描述
注意:运行的命令vue serve默认启动的App.vue,如果是其他vue组件的话需要在vue serve后面加上要启动的组件,例如:vue serve Hello.vue
在这里插入图片描述

具体的使用可以参考官网的介绍。

(3)创建vue-cli项目

  • 输入vue create 项目名称创建vue项目,选择第二个,手动配置选型,第一个是默认配置选项,回车
    在这里插入图片描述

  • 选项如图所示,可以上下移动,按空格进行选择,带*号的都是已选的配置,为了简单可以先选择如图所示的选项即可,然后回车
    在这里插入图片描述
    选项说明
    Babel: 将 ES6 编 译 成 ES5
    TypeScript:使 用 TypeScript
    Router和Vuex:路由和状态管理
    Linter/ Formatter:代码检查工具
    CSS Pre-processors:css预编

  • 选择ESLint with error prevention only在这里插入图片描述
    选项说明
    eslint w… : 只进行报错提醒;
    eslint + A… : 不严谨模式;
    eslint + S…:正常模式;
    eslint + P… :严格模式;

  • Pick additionall intfeatures:代码检查方式, 选择Lint on save保存时检查
    在这里插入图片描述

  • 选择配置信息存放位置:单独存放或者并入package.json ,选择In dedicated configfiles
    在这里插入图片描述

  • 是否保存当前预设,下次构建无需再次配置
    在这里插入图片描述

  • 直接回车
    在这里插入图片描述

  • 项目创建完成
    在这里插入图片描述

  • 运行
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

关于创建的项目的目录结构说明和运行流程介绍,可以查看我的这一篇博文:Vue-Cli 4.x目录结构学习总结

(4)Vue的生命周期

对应项目cli-07
其实这个知识点应该记录在我的另一篇Vue基础那一篇博文上,不过在这里记录也可以。
生命周期

生命周期说明
(初始化)beforeCreate整个页面创建之前触发
(初始化)created实例创建完之后触发
(初始化)beforeMount开始挂载之前触发
(初始化)mounted挂载成功之后触发
(运行中)beforeUpdate组件数据更新前触发
(运行中)updated组件数据更新后触发
(销毁)beforeDestroy组件销毁前触发
(销毁)destroy组件销毁后触发

直接上例子说明
随便新建组件(Test01.vue),并且在方法里面将上面的生命周期相关的方法全写上

<template>
  <div>
    <div>{{data}}</div>
    <button @click="change">点击更新组件数据</button>
  </div>
</template>

<script>
export default {
    methods: {
        change(){
            this.data = "修改后的测试数据,"
        }
    },
  data() {
    return {
      data: "测试数据"
    };
  },

  //整个页面创建之前触发
  beforeCreate() {
    console.log("beforeCreate");
  },

  //实例创建完之后触发
  created() {
    console.log("created");
  },

  //开始挂载之前触发
  beforeMount() {
    console.log("beforeMount");
  },

  //挂载成功之后触发
  mounted() {
    console.log("mounted");
  },

  //组件数据更新前触发
  beforeUpdate() {
    console.log("beforeUpdate");
  },

  //组件数据更新后触发
  updated() {
    console.log("updated");
  },

  //组件销毁前触发
  beforeDestroy() {
    console.log("beforeDestroy");
  },

  //组件销毁后触发
  destroyed() {
    console.log("destroyed");
  }
};
</script>

<style>
</style>

运行
在这里插入图片描述
如上图,第一次加载Test01页面,按照顺序触发4个生命周期函数,用于初始化数据
在这里插入图片描述
如上图,点击按钮后,组件数据进行更新,触发运行中的两个生命周期函数
在这里插入图片描述
如上图,点击About跳转页面,触发销毁的两个生命周期函数

(5)Axios

对应项目cli-08
参考网址:http://www.axios-js.com/zh-cn/docs/index.html

Axios 是一个基于 promise 的 HTTP 库,简单的讲就是可以发送get、post等请求,当然这也可以用jq来实现这些请求,实际上,Axios跟jq也挺类似的。

下面直接上案例
安装

npm install --save axios vue-axios

配置,将下面这些代码加入主入口文件

import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'

Vue.use(VueAxios, axios)

在这里插入图片描述
新建Test01.vue

<template>
  <div>
    <table border="1" style="margin:0 auto">
      <tr>
        <th>id</th>
        <th>名字</th>
        <th>性别</th>
        <th>出生年月</th>
      </tr>

      <tr>
        <td>{{user.id}}</td>
        <td>{{user.name}}</td>
        <td>{{user.age}}</td>
        <td>{{user.bir}}</td>
      </tr>
    </table>

    <button @click="addUser">添加用户</button>
    <p>{{msg}}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      user: {
        id: null,
        name: null,
        age: null,
        bir: null
      },
      msg: null
    };
  },
  methods: {
    //发送post请求
    addUser() {
      this.$http
        .post("http://rap2.taobao.org:38080/app/mock/253047/user/add", {
          name: "张三",
          age: 10,
          bir: "2012-12-12"
        })
        .then(resp => {
          this.msg = resp.data;
        })
        .catch(error => {
          alert(error);
        });
    },

    //发送get请求
    findUser() {
      this.$http
        .get("http://rap2.taobao.org:38080/app/mock/253047/user/findOne?id=3")
        .then(resp => {
          this.user = resp.data;
        })
        .catch(error => {
          alert(error);
        });
    }
  },

  created() {
    this.findUser();
  }
};
</script>

<style>
</style>

运行结果
在这里插入图片描述

二、路由Vue Router

注意:Vue Router主要根据Vue Router官网进行相应的学习,官网地址:https://router.vuejs.org/zh/guide/advanced/meta.html
上面创建的项目是没有配置路由的,这里简单说一下,页面的路由需要配置路由插件,下面创建新的项目cli-03,进行路由的配置

(1)路由配置

项目的创建基本和上面的创建流程一样,唯一不同的是在手动配置的时候,把路由的配置也选上,如下图所示
在这里插入图片描述
选上Router路由配置,其他的创建方式不变,创建成功后可以在项目的根目录下的package.json里面查看项目的配置信息,如下图所示,路由已经配置
在这里插入图片描述
关于创建的项目的目录结构说明和运行流程介绍,可以查看我的这一篇博文:Vue-Cli 4.x目录结构学习总结,建议先看一下,如果连目录结构都不知道的话,就更别说使用该项目了。

(2)简单路由

对应项目cli-03
需求:通过多个自定义组件,组成一个新的组件,并且添加到路由里面

components包存放公共组件
在这里插入图片描述
Head.vue代码如下

<template>
  <div>头部信息:{{data}}</div>
</template>

<script>
export default {
    name:"Head",
      props:{
        data:String
    }

}
</script>

<style>

</style>

Content.vue代码如下

<template>
  <ul class="list">
    <li v-for="item in link" :key="item">
      <a href="#">{{item}}</a>
    </li>
  </ul>
</template>
<script>
export default {
  name: "Content",
  data() {
    return { link: ["首页", "资讯", "图文", "关于"] };
  }
};
</script>
<style scoped>
.list a {
  color: brown;
}
</style>

Foot.vue代码如下

<template>
  <div>版权所有</div>
</template>
<script>
export default {
  name: "Foot"
};
</script>
<style scoped>
</style>

view包存放页面组件
在这里插入图片描述
Test.vue代码如下

<template>
<div>
  <Head data="Hello"></Head><!--使用子组件Head,并且向Head传值-->
  <Content></Content><!--使用子组件Content-->
  <Foot></Foot><!--使用子组件Foot-->
</div>
</template>

<script>
// 引入子组件(公共组件)
import Head from '../components/Head'
import Content from '../components/Content'
import Foot from '../components/Foot'

export default {
    name:"Test",
    components:{//组成引入的公共组件
        Head,Content,Foot
    }
};
</script>

<style>
</style>

在Rount包下注册路由
在这里插入图片描述

在App.vue根节点添加导航
在这里插入图片描述
注意:< router-view/>表示路由的添加,不加上去的话将不会有路由的页面跳转。

运行结果
在这里插入图片描述
通过这个例子可以大概体会到vue的组件开发的模式,将功能写成一个个的组件,需要的,通过组件的方式进行开发,即所谓的组件式开发,上面的例子涉及到知识点有,组件的创建,组件的赋值,组件间的传值等。

(3)动态路由

对应项目cli-03
所谓动态路由匹配:就是将不确定的参数进行路由映射到同一个组件上去;比如经典的:user?id=5,这个 5 就是动态的数值,最终路径:user/5,可以看一下官网给出的例子,链接地址:https://router.vuejs.org/zh/guide/essentials/dynamic-matching.html#响应路由参数的变化
在这里插入图片描述
动态路由案例
这里给出动态路由的两种方法,$ route.query和$ route.params,更多的方法可以查看router的api,地址:router的api
在这里插入图片描述
$ route.query
在views包下新建User.vue,代码如下

<template>
  <div>url传递的用户参数id = {{$route.query.id}}  </div>
  
</template>

<script>
export default {
    name:"User"

}
</script>

<style>

</style>

添加路由
在这里插入图片描述
运行结果
在这里插入图片描述

$ route.params
在views包下新建User2.vue,代码如下

<template>
  <div>
  url传递的用户id = {{$route.params.id}}
  </div>
</template>

<script>
export default {
    name:"User2"

}
</script>

<style>

</style>

添加路由
在这里插入图片描述
运行结果
在这里插入图片描述
路由对象属性除了上面的两个之外还有$ route.hash等,具体可以查看官网

通配符*
"*"可以匹配所有的路径
在这里插入图片描述
如是上图所示,输入任意路径都会跳转到About页面
在这里插入图片描述

(4)嵌套路由

对应项目cli-03
实际应用场景中,可能存在多种嵌套组件的应用界面,类似栏目分类; 比如:新闻板块下有国内新闻、国外新闻、体育新闻、音乐新闻等; 音乐板块下有流行音乐、古典音乐等;下面是嵌套路由的匹配方式,第一种固定路由,二三两种动态路由;

模式匹配路径
/ music / : id/ popMusic/ music / 5 /popMucsic (注意,这里假设id为5)
/ music / : id / classicalMusic/ music / 5 / classicalMusic (注意,这里假设id为5c)

也可以查看官网的说明
在这里插入图片描述
嵌套路由案例

在views包下新建一个Music文件夹,用于存放音乐板块,里面新建Music.vue、PopMusic.vue和ClassicalMusic.vue三个组件
在这里插入图片描述
PopMusic.vue代码如下

<template>
  <div>这是流行音乐</div>
</template>

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

<style>

</style>

ClassicalMusic.vue代码如下

<template>
  <div>这是古典音乐</div>
</template>

<script>
export default {
  name: "ClassicalMusic"
};
</script>

<style>
</style>

Music.vue代码如下

<template>
  <div>
  <h1>音乐模块</h1><br>
  <router-link to="/music/5/popMusic">流行音乐</router-link> |
  <router-link to="/music/5/classicalMusic">古典音乐</router-link>

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

<script>
export default {
    name:"Music"

}
</script>

<style>

</style>

注册路由
在这里插入图片描述
运行结果
在这里插入图片描述
注意:注册路由时,子路由需要通过children来进行注册。

(5)编程式导航

对应项目cli-03
< router-link>组件标签可以直接进行导航,但缺乏编程性,无法各种逻辑判断;插件提供了编程式的导航,让我们自行定义我们的导航的方法; 比如,我们自行创建一个按钮,通过执行函数的方式,去导航,可编程性就提高了

可以看一下官方给出的解释
在这里插入图片描述
编程式导航案例
使用router.push方法,主要有如下两种方式,分别对应get和post

//对应get请求,其中不传参时,query可以省略
router.push({ path: '/register', query: { plan: 'private' }})

//对应post请求,其中不传参时,params可以省略
router.push({ name: 'User', params: { userId: '123' }})

新建Navigation.vue组件,代码如下

<template>
  <div>
    <!--通过path+query的方式:get -->
    <span>通过path+query的方式(get):</span>
    <button @click="gotoAbout()">跳转到About页面</button>
    <button @click="gotoUser(30)">跳转到User页面(传参:30)</button>
    <br />
    <br />

    <!--通过name+params的方式:post -->
    <span>通过name+params的方式(psot):</span>
    <button @click="gotoUser3()">跳转到User3页面</button>
    <button @click="gotoUser03(20)">跳转到User3页面(传参:20)</button>
  </div>
</template>

<script>
export default {
  name: "Navigation",
  methods: {
    //path+query,对应get请求,其中不传参时,query可以省略
    gotoAbout() {
      //这里可以做相应的逻辑处理
      this.$router.push({ path: "/about" });
    },
    //通过path+query进行传参,对应get请求
    gotoUser(id) {
      //这里可以做相应的逻辑处理
      alert("后台传入的id值为:" + id);

      //注意:这里只能式name和params搭配,name对应路由的对应组件的name
      this.$router.push({ path: "/user", query: { id } }); //相当于user2/20
    },
    //name+params,对应post请求,其中不传参时,params可以省略
    gotoUser3() {
      //这里可以做相应的逻辑处理

      //注意:这里只能式name和params搭配,name对应路由的对应组件的name
      this.$router.push({ name: "User3", params: { id: "11" } }); //相当于user2/11
    },
    //通过name+params进行传参,对应post请求
    gotoUser03(id) {
      //这里可以做相应的逻辑处理
      alert("后台传入的id值为:" + id);

      //注意:这里只能式name和params搭配,name对应路由的对应组件的name
      this.$router.push({ name: "User3", params: { id } }); //相当于user2/20
    }
  }
};
</script>

<style>
</style>

运行结果
在这里插入图片描述
为什么说一个对应get,一个对应post呢,先点击get的两个按钮
在这里插入图片描述
从上图可以看到这种传参的方式是直接在url后面拼接字符串实现的传参,跟get对应。最后点击post对应的按钮
在这里插入图片描述
从上图可以看到这种传参的方式url后面没有拼接字符串,参数也跟着传过去了,跟post对应。

其余方法
编程式导航还有其他方法,像router.replace(location, onComplete?, onAbort?)和this.$router.go()等,具体可以查看官网,地址:https://router.vuejs.org/zh/guide/essentials/navigation.html

(6)命名路由

对应项目cli-03
有时候,通过一个名称来标识一个路由显得更方便一些,特别是在链接一个路由,或者是执行一些跳转的时候。你可以在创建 Router 实例的时候,在 routes 配置中给某个路由设置名称。

这个相对简单,直接上代码

      <router-link v-bind:to="{ name: 'User3', params: { id: '11' } }">User3</router-link> |
      <router-link v-bind:to=" { path: '/user', query: { id:'21' } }">User</router-link>

这个跟上面的编程式导航的功能是一样的。

(7)命名视图

对应项目cli-03
是视图标签,组件通过这个标签来渲染组件的内容;上面的所有内容都通过这个标签加载组件的模版内容; 但如果,我们在不同的区域做了不同的设计,不同的组件需要在不同的区域渲染;这个时候,就需要通过命名视图来指定渲染的组件了;

来看一个例子
在这里插入图片描述
这种情况下,对应的路径下的组件被渲染了3次
在这里插入图片描述
在这里插入图片描述
命名路由案例
需求:加载about的时候,还需要另外加载 Head.vue 组件和 Footer.vue 组件

在跟组件修改几行代码,有3个路由视图,如图
在这里插入图片描述

在about路由注册那里增加点东西
在这里插入图片描述
其中Head.vue和Foot.vue是自定义组件
在这里插入图片描述

当然更多的玩法可以查看官网。

(8)重定向

先看一下官方的说明
在这里插入图片描述
这里重新开一个项目cli-04进行演示,重定向的存操作跟java还是挺像的
在这里插入图片描述
运行结果
在这里插入图片描述
运行结果
在这里插入图片描述
查看控制台还可以查看代码中的to是什么
在这里插入图片描述
在这里插入图片描述

(9)打包部署

首先明确两种路由的模式history模式和hash模式,先来看history模式,这是创建项目时默认的模式
history模式
在这里插入图片描述
在该模式下执行打包命令:npm run build,会自动生成一个dist文件,里面是打包好的文件
在这里插入图片描述
打包后,我们想要在静态服务器上测试,先要安装静态服务器

 npm i serve -g //安装 
 serve dist -s //运行dist 目录,注意-s不能省略,不然后面会出现404错误

在这里插入图片描述在这里插入图片描述
访问链接,各种功能都正常
在这里插入图片描述

hash模式
在这里插入图片描述
接下来的命令都差不多的,唯一不同的就是运行dist目录时不需要-s

 npm run build//大包
 npm i serve -g //安装 
 serve dist //运行dist 目录,注意这里不需要-s

在这里插入图片描述
运行结果,一切正常
在这里插入图片描述

(10)导航守卫

导航守卫的作用就是跳转或取消跳转的导航功能,比如登录跳转方案,导航守卫还包括前置导航守卫和后置导航守卫等,直接上官网瞄一眼
在这里插入图片描述

前置导航守卫
按照官方的说法,在路由下新建一个前置导航守卫
在这里插入图片描述
代码

//模拟登录状态 
const flag = true

//全局前置导航守卫
router.beforeEach((to,from,next)=>{
  //to: Route: 即将要进入的目标 路由对象
  //from: Route: 当前导航正要离开的路由
  //.next:这是个钩子函数

  console.log('开始loading...')
  if(flag){//已经登录
    // 如果是已经登录状态,再访问登录页面直接跳转到首页
    if(to.name === 'Login'){
      next('/')//跳转到首页
    }else{
      next()//直接放行
    }
    
  }else{//未登录
    //next('/login')//如果直接这样写会出现无限递归错误

    if(to.name === 'Login'){//如果是已经跳转到登录页面的话直击放行
      next()
    }else{//如果跳转页面不是登录页面则继续跳转登录页面
      next('/login')
    }
  }
})

运行flag为true,已登录状态,一切正常访问,访问login路径时自动跳转到首页
在这里插入图片描述
运行flag为false,未登录状态,一切正常访问,不管输入什么路径都会跳转到登录页面
在这里插入图片描述

守卫的参数参考官网给出的说明
在这里插入图片描述

后置导航守卫
在这里插入图片描述
代码

//全局后置导航守卫
router.afterEach(()=>{
  console.log("关闭loading...")
})

也可以传参进去,进行相应的操作,参数跟前置导航守卫的参数一致

路由独享的守卫
你可以在路由配置上直接定义 beforeEnter 守卫:

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

组件内的守卫

const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

beforeRouteEnter 守卫 不能 访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。

beforeRouteEnter (to, from, next) {
  next(vm => {
    // 通过 `vm` 访问组件实例
  })
}

注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了。

beforeRouteUpdate (to, from, next) {
  // just use `this`
  this.name = to.params.name
  next()
}

这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。

beforeRouteLeave (to, from, next) {
  const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
  if (answer) {
    next()
  } else {
    next(false)
  }
}

(11)元信息和过渡动效

元信息
可以给路由配置一个 meta 属性,这个属性一般设置对象即可;这个属性是固定的,设置别的属性比如:abc是无法获得内容的;
在这里插入图片描述
运行结果
在这里插入图片描述
过渡动效
过渡效果,字面意思就是基础 Vue 课程中的转场特效,支持路由切换;

将要再如的视图组件标签使用< transition>包裹住实现特效,标签以css样式的首个字符串命名

<transition name="fade"> <router-view/> </transition>

拷贝转场动效的 CSS3

.fade-enter-active,
.fade-leave-active {
  transition: opacity 5s;
}
.fade-enter,
.fade-leave-to {
  opacity: 0;
}
.fade-leave-to {
  display: none;
}

在这里插入图片描述
这样在路由跳转时会有一个转场的特效,这里是缓慢转场(大概是这个意思)

(12)路由数据获取

获取数据,可以有两种方式:导航完成后获取和导航完成前获取;

导航完成后获取

<template>
  <div class="post">
    <div v-if="loading">Loading...</div>

    <div v-if="post">
      <h2>{{post.title}}</h2>
      <p>{{post.body}}</p>
    </div>
  </div>
</template>
<script>
export default {
  name: "Test02",
  data() {
    return {
      loading: false,
      post: null
    };
  },
  //当组件创建完毕后执行
  created() {
    this.getData();
  },
  //方法
  methods: {
    getData() {
      //数据获取前先loading
      this.loading = true;
      //模拟获取的延迟过程
      setTimeout(() => {
        this.loading = false;
        this.post = { title: "标题", body: "内容..." };
      }, 2000);
    }
  }
};
</script>

导航完成前获取

<template>
    <div class="post">
        <div v-if="post">
            <h2>{{post.title}}</h2>
            <p>{{post.body}}</p>
        </div>
        <div v-if="flag">
        <h1>{{text}}</h1>
        </div>
    </div>
</template>

<script>
    export default {
        name: "Test03",
        data() {
            return {
                post : null,
                flag:false,
                text:''
            }
        },
        //导航完成之前
        beforeRouteEnter(to, from, next) {
            setTimeout(() => {
                next(vm => {
                    vm.getData()
                })
            }, 2000);

        },
        methods: {
            //获取数据
            getData() {
                this.post = {
                    title : '标题',
                    body  : '内容...'
                }
                this.flag = true
                this.text='文本'
            }
        }
    }
</script>

<style scoped>

</style>

三、Vuex

注意:这一部分的关于Vuex主要根据Vuex官网进行学习,官网地址:https://vuex.vuejs.org/zh/

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。

Vuex 主要功能是用于解决组件与组件之间共享的状态,集中存储管理所有组件状态

(1)入门引子

对应项目cli-06
来看一个计数的例子,也是官网给出的例子

<template>
  <div><button @click="Add">按钮的次数:{{msg}}</button></div>
</template>

<script>
export default {
    name:'Count',
    data() {
        return {
            msg:0
        }
    },
    methods: {
        Add(){
            this.msg++
        }
    },

}
</script>

<style>

</style>

运行结果
在这里插入图片描述
貌似没啥问题,但是一旦点击其他页面后再跳回计数页面,发现按钮的次数就又变为了0,有没有办法让按钮的次数不变呢,这就要用的Vuex了

(2)Store 模式

对应项目cli-06
store 模式支持极少的共享数据,按照官网给出的说法,如果不是大型的单页面应用,使用 Vuex 会繁琐冗余; 也就是说,你的应用特别简单,不需要真么重的插件,可以使用 store 模式; store 模式支持你在极少的共享数据中,就好比你只近视 50 度,可以不戴眼镜

在src目录下新建store/index.js文件用于存储共享数据
在这里插入图片描述

修改上一节的Count.vue文件,通过将store引入并且使用里面index.js的共享数据的方式进行数据的操作

<template>
  <div>
    <button @click="Add1">(not store)按钮的次数:{{msg1}}</button>
    <button @click="Add2">(store)按钮的次数:{{storeState.msg}}</button>
  </div>
</template>

<script>
//引入store
import store from "@/store"

export default {
  name: "Count",
  data() {
    return {
      storeState: store.state,//使用store下的state
      msg1:0
    };
  },
  methods: {
    Add1() {
        this.msg1++
    },
    Add2() {
      store.Add();//调用store下的Add()方法
    }
  }
};
</script>

<style>
</style>

运行结果,同样增加到10次,点击Home页面后再跳回Count页面发现使用了store的按钮次数还是10,没有使用store的则直接被清0了
在这里插入图片描述

(3)Vuex的安装与基本使用

对应项目cli-06
上面的Store的方式是通过自己手动新建一个store/index.js的方式进行数据的共享,这种方式适合少量数据的共享,如果大量数据的话需要安装Vuex,使用配置好的Vuex

安装
安装的方式也简单,只需要在新建项目的时候,勾上Vuex的选项即可
在这里插入图片描述
安装完之后发现,在src下多了一个store文件夹,里面有一个index.js文件,里面有配置好一些数据
在这里插入图片描述
简单认识一下里面的配置
在这里插入图片描述

使用
还是以上面的计数的例子,在store下的index.js增加状态值和修改状态值的方法

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  //状态值
  state: {
    msg: 0
  },

  //修改状态值
  mutations: {
    Add(state) {
      state.msg++
    }
  },
  actions: {
  },
  modules: {
  }
})

新建Count.vue组件,添加相应的路由

<template>
  <div>
    <button @click="Add1">(not store)按钮的次数:{{msg1}}</button>

    <!--通过$store.state.msg调用里面的msg数据-->
    <button @click="Add2">(store)按钮的次数::{{$store.state.msg}}</button>
  </div>
</template>

<script>
export default {
  name: "Count",
  data() {
    return {
      msg1:0
    };
  },
  methods: {
    Add1() {
        this.msg1++
    },
    Add2() {
      //注意::$store 是插件挂载在 Vue 实例上的,在实例内访问用 this.$store,否则会出错
      this.$store.commit('Add')//修改状态执行mutation 里的下的Add()方法
    }
  }
};
</script>

<style>
</style>

运行的效果跟上面用store模式实现的一样
在这里插入图片描述

(4)State状态设置

对应项目cli-06
这个模块主要解决设置多个状态在赋值取值繁琐的问题。

传统的State多状态设置
在store/index.js里面设置name,age等多个状态值

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  //状态值
  state: {
    //设置多个状态值
    msg: 0,
    name: '灰太狼',
    age: 3,
    sex: '雄',
    place: '狼堡'
  },

  //修改状态值
  mutations: {
    Add(state) {
      state.msg++
    },
    SetPlace(state, value) {
      state.place = value
    }
  },
  actions: {
  },
  modules: {
  }
})

创建User01.vue组件,代码如下

<template>
  <div>
    <!--除了用$store.state.属性的方式获取外,还可以用下面的计算属性的方式获取-->
    <p>姓名:{{name}}</p>
    <p>年龄:{{age}}</p>
    <p>性别:{{sex}}</p>
    <p>住址:{{place}}</p>
    <input type="text" v-model="place" />
  </div>
</template>

<script>
export default {
  name: "User01",

  //计算属性
  computed: {
    name() {
      return this.$store.state.name;
    },
    age() {
      return this.$store.state.age;
    },
    sex() {
      return this.$store.state.sex;
    },
    place: {
      //get方法用于获取值
      get() {
        return this.$store.state.place;
      },
      //set用于修改值
      set(value) {
        this.$store.commit("SetPlace", value);
      }
    }
  }
};
</script>

<style>
</style>

运行结果,信息都正确显示,住址跟着输入框改变而改变
在这里插入图片描述
这种传统的获取和修改state状态值的方式在状态值多的时候就会显得代码很冗余,重复代码量会增多,于是又了下面的简洁版的写法

辅助函数写法
实现同样的功能,这种方式显得更简洁
新建一个User02.vue组件库,store里面的index.js的写法不变,主要区别就是组件里面状态值的获取方式和修改方式

<template>
  <div>
    <!--除了用$store.state.属性的方式获取外,还可以用下面的计算属性的方式获取-->
    <p>姓名:{{name}}</p>
    <p>年龄:{{age}}</p>
    <p>性别:{{sex}}</p>
    <p>住址:{{place}}</p>
    <input type="text" @input="SetPlace" :value="place" />

    <p>测试:{{test}}</p>
  </div>
</template>

<script>
//导入辅助函数
import { mapState } from "vuex";

export default {
  name: "User02",
  computed: {
      //其余计算属性操作
    test() {
      return "这是一个测试";
    },
    
    //辅助函数操作store.state
    ...mapState({
      name: "name", //等于name:state=>state.name,state表示store下的state
      age: "age",
      sex: "sex",
      place(state) {
        return state.place;
      }
    })
  },

  methods: {
    SetPlace(e) {
      this.$store.commit("SetPlace", e.target.value);
    }
  }
};
</script>

<style>
</style>

运行结果,这种方式也可以实现上面的功能,而且写法更简洁
在这里插入图片描述

(5)Getter

在获取 state 状态时,有时我们需要对这个原生的值进行处理;处理完之后再从store中返回给组件使用, 处理采用计算属性或辅助 mapState 均可,但每个组件都要进行处理就繁琐了 ;我们可以使用 getters 派生来设置需要处理的 state 状态

先在store里面新增两个状态值,爱好和身高,用来做不传参和传参的使用,同时新建getters

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  //状态值
  state: {
    //设置多个状态值
    msg: 0,
    name: '灰太狼',
    age: 3,
    sex: '雄',
    place: '狼堡',
    hobby: '抓羊',
    height:'3米'
  },
  getters: {
    //无传参形式
    getHobby:(state)=>{
      //可以进行对原值的逻辑操作
      return '灰太狼:'+state.hobby
    },

    //传参形式:参数为id
    getHeight:(state)=>(id)=>{
      //可以进行对原值的逻辑操作
      return state.height+id
    }

  },


  //修改状态值
  mutations: {
    Add(state) {
      state.msg++
    },
    SetPlace(state, value) {
      state.place = value
    }
  },
  actions: {
  },
  modules: {
  }
})

在这里插入图片描述

在原来的User02.vue组件上进行操作

<template>
  <div>
    <!--除了用$store.state.属性的方式获取外,还可以用下面的计算属性的方式获取-->
    <p>姓名:{{name}}</p>
    <p>年龄:{{age}}</p>
    <p>性别:{{sex}}</p>
    <p>住址:{{place}}</p>

    <!--Getter-->
    <p>爱好:{{hobby}}</p>
    <p>身高:{{getHeight('--巨人')}}</p>
    <input type="text" @input="SetPlace" :value="place" />

    <p>测试:{{test}}</p>
  </div>
</template>

<script>
//导入辅助函数和mapGetters辅助函数
import { mapState, mapGetters } from "vuex";

export default {
  name: "User02",
  computed: {
    //其余计算属性操作
    test() {
      return "这是一个测试";
    },

    //mapGetters辅助函数
    ...mapGetters({
      hobby: 'getHobby',
      getHeight:'getHeight'
    }),

    //辅助函数操作store.state
    ...mapState({
      name: "name", //等于name:state=>state.name,state表示store下的state
      age: "age",
      sex: "sex",
      place(state) {
        return state.place;
      }
    })
  },

  methods: {
    SetPlace(e) {
      this.$store.commit("SetPlace", e.target.value);
    }
  }
};
</script>

<style>
</style>

在这里插入图片描述
运行结果
在这里插入图片描述

(6)Mutations

关于Mutations的使用在上面计数的那个例子里面已经使用过了,下面用辅助函数的方式进行操作

还是以计数为例,在store/index.js中增加状态值msg2,同时在Mutations中增加AddMsg2方法

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  //状态值
  state: {
    //设置多个状态值
    msg2:0,
    msg: 0,
    name: '灰太狼',
    age: 3,
    sex: '雄',
    place: '狼堡',
    hobby: '抓羊',
    height:'3米'
  },
  getters: {
    //无传参形式
    getHobby:(state)=>{
      //可以进行对原值的逻辑操作
      return '灰太狼:'+state.hobby
    },

    //传参形式:参数为id
    getHeight:(state)=>(id)=>{
      //可以进行对原值的逻辑操作
      return state.height+id
    }

  },
  //修改状态值
  mutations: {
    Add(state) {
      state.msg++
    },
    SetPlace(state, value) {
      state.place = value
    },

    AddMsg2(state){
      state.msg2++
    }


  },
  actions: {
  },
  modules: {
  }
})

在Count.vue中增加按钮,同时导入mapMutations辅助函数

<template>
  <div>
    <button @click="Add1">(not store)按钮的次数:{{msg1}}</button>

    <!--通过$store.state.msg调用里面的msg数据-->
    <button @click="Add2">(store)按钮的次数:{{$store.state.msg}}</button>
    <button @click="addMsg2">(store 辅助函数)按钮的次数:{{$store.state.msg2}}</button>
  </div>
</template>

<script>
//导入辅助函数
import {mapMutations} from 'vuex'

export default {
  name: "Count",
  data() {
    return {
      msg1:0
    };
  },
  methods: {
     ...mapMutations({
      addMsg2:'AddMsg2'
    }),
    
    Add1() {
        this.msg1++
    },
    Add2() {
      //注意::$store 是插件挂载在 Vue 实例上的,在实例内访问用 this.$store,否则会出错
      this.$store.commit('Add')//修改状态执行mutation 里的下的Add()方法
    }
  }
};
</script>

<style>
</style>

运行结果在这里插入图片描述

(7)Actions

上节课我们说 Mutations 只能是同步函数,那异步操作可以交给Actions,直接上例子
在store/index.js中的mutations里面写要异步执行的代码,然后在Actions异步调用,为了模拟异步效果,可以在state里面设置一个状态值info
在这里插入图片描述
新建一个User03.vue组件异步调用信息

<template>
<div>
<h1>{{$store.state.info}}</h1>
<button @click="setInfo('异步:hello')">点击执行异步方法</button>
</div>  
</template>

<script>
//导入辅助函数
import { mapActions } from 'vuex'

export default {
methods: {
    //传统使用异步方法
    // setInfo(){
    //     this.$store.dispatch('changeInfo')
    // },
    
    //采用辅助函数的方式使用异步方法
    ...mapActions({
        setInfo:"changeInfo"
    })
    
},
}
</script>

<style>

</style>

运行结果
在这里插入图片描述

(8)module

当项目越来越大的时候,如果所有的共享状态都存放在一个index.js文件里的时候,整文件就会显得很臃肿,于是就可以用模块化的方式来进行共享状态,直接上例子

在store文件下新建一个module文件,用来存放各种模块,再新建一个module01.js

export default{
    namespaced: true,//为了隔离主状态和模块状态名称冲突,需要设置一个命名空间
    //状态值
    state: {
        info: 'modules下的初始值'
    },
    //修改状态值
    mutations: {
        //等待被异步调用的方法
        setInfo(state, value) {
            state.info = value
        }

    },
    modules: {
    }
}

然后将该模块导入store/index.js里面
在这里插入图片描述
引入后就可以直接使用了,新建User04.vue组件

<template>
<div>
<h1>info = {{$store.state.module01.info}}</h1>
<button @click="getStore">获取$store.state</button>
</div>  
</template>

<script>
export default {
methods: {
    getStore(){
        console.log(this.$store.state)
    }
},
}
</script>

<style>

</style>

运行的时候打开控制台,查看$store.state变量
在这里插入图片描述

在这里插入图片描述

下面演示调用模块的函数

修改module01.js的代码

export default{
    namespaced: true,//为了隔离主状态和模块状态名称冲突,需要设置一个命名空间
    //状态值
    state: {
        info: 'module01下的初始值'
    },
    //修改状态值
    mutations: {

        setInfo(state, value) {
            console.log("这是模块module01.js")
            state.info = value
        }

    },
    modules: {
    }
}

在这里插入图片描述
修改User04.vue演示函数调用

<template>
<div>
<h1>info = {{$store.state.module01.info}}</h1>
<button @click="getStore">获取$store.state</button>
<button @click="setInfo('你好')">修改info</button>
</div>  
</template>

<script>
import {mapMutations} from "vuex"

export default {
methods: {
    getStore(){
        console.log(this.$store.state)
    },
    ...mapMutations({
        setInfo:'module01/setInfo'
    })
},
}
</script>

<style>

</style>

运行结果,可以看见调用的确实module01模块下的函数
在这里插入图片描述

Logo

前往低代码交流专区

更多推荐