vue学习4


配置代理

96集:首先,找到文件,然后cmd启动这里是引用
得到服务器1的地址 http://localhost:5000/students
得到服务器2的地址 http://localhost:5001/cars

下载库npm i axios
引入库import axios from 'axios'

一开始出现了一个错误,跨域:请求发送了,服务器收齐并返回,但是我们获取不到(代理服务器 )
在这里插入图片描述
配置好之后要重新启动npm run serve
在这里插入图片描述

  • 代理服务器不会把所有的请求都转发给5000,当8080服务器本身就具有请内的内容的时候,public文件中内容是8080服务器所有的
  • 不能灵活的控制走不走代理服务器
  • 只能配置一个代理,只能转发给5000请求
    - 修改了代理服务器,一定要重新开启npm run serve!!!

方法一:

在这里插入图片描述

Vue.config.js

里面的内容是在这里找到的 链接https://cli.vuejs.org/config/#devserver-proxy

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave: false,
  //开启代理服务器(是因为服务器1 student的端口号时是5000)
  devServer: {
    proxy: 'http://localhost:5000'
  }

})

App.vue
<template>
  <div><button @click="getStudents">获取学生信息</button></div>
</template>

<script>
import axios from "axios";
export default {
  name: "App",
  methods: {
    getStudents() {
      //请求之后返回的是promise值
      axios.get("http://localhost:8080/students").then(
        response => {
          console.log("请求成功了", response.data);
        },
        error => {
          console.log("请求失败了", error.message);
        }
      );
    },
  },
};
</script>


方法二:

可以配置多个代理,且可以灵活的控制请求是否走代理
配置略微繁琐,请求资源时必须加前缀

1.关于vue中proxy代理的一些理解 !!!加深理解
在这里插入图片描述
2.关于axios介绍和使用–面试题
axios的另一篇教程
跨域

  • Axios是一个基于promise的HTTP库,是目前最流行的ajax封装库之一,用于很方便地实现ajax请求的发送。

  • 支持的功能:
    从浏览器发出 XMLHttpRequests请求。
    支持 Promise API。
    能拦截请求和响应。
    能转换请求和响应数据。
    取消请求。

可以使用axios里面配置参数

 document.getElementById("btn1").onclick = function(){
       axios({
        method:"GET",
        url:"http://localhost:3000/posts/1"
       }).then(response=>{
           console.log(response);
       })
    };

也可以直接像APP中一样,使用axios.get方法
点我查看axios请求方法: get,post,put,patch,delete

get:获取数据
post:提交数据(表单提交+文件上传)
put:更新数据(所有数据推送到后端)
patch:更新数据(只将修改的数据推送到后端)
delete:删除数据

Vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  lintOnSave: false,
  //开启代理服务器(是因为服务器1 student的端口号时是5000)
  /*devServer: {
    proxy: 'http://localhost:5000'
  }*/

  devServer: {
    proxy: {
      // '^/api': {
      //   target: 'http://localhost:5000',//接口地址
      //   // ws: true,//如果代理webSocket配置这个参数
      //   // changeOrigin: true//是否跨域
      // },
      // '^/foo': {
      //   target: '<other_url>'
      // }

      '/atguigu':{
        target: 'http://localhost:5000',
        pathRewrite:{'^/atguigu':''},
         ws: true,//用于支持websocket
        changeOrigin: false//用于控制请求头中的host值
      },
      '/demo':{
        target: 'http://localhost:5001',
        pathRewrite:{'^/demo':''},
         ws: true,//用于支持websocket
        changeOrigin: false
      },
    }
  }

})

APP.vue
<template>
  <div><button @click="getStudents">获取学生信息</button>
  
  <button @click="getCars">获取汽车信息</button></div>
 
</template>

<script>
import axios from "axios";
export default {
  name: "App",
  methods: {
    getStudents() {
      
      //前缀加载端口号后面
      axios.get("http://localhost:8080/atguigu/students").then(
        response => {
          console.log("请求成功了", response.data);
        },
        error => {
          console.log("请求失败了", error.message);
        }
      );
    },
getCars(){
    
      axios.get("http://localhost:8080/demo/cars").then(
        response => {
          console.log("请求成功了", response.data);
        },
        error => {
          console.log("请求失败了", error.message);
        }
      );
}

  },
};
</script>


在这里插入图片描述


跨域

跨域相关问题
在这里插入图片描述

github案例

  • 对所有的进行拆分,记得引入bootstrap.css的写法
  • search进行发送请求
  • 把search中的数据在list中显示(全局事件总线/消息订阅与发布/交给app )
    • 遍历 / 显示加载中
      在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

App.vue

<template>
  <div class="container">
    <Search />
    <List />
  </div>
</template>

<script>
import List from "./components/List.vue";
import Search from "./components/Search.vue";

export default {
  name: "App",
  components: {
    List,
    Search,
  },
};
</script>

<style >
</style>

Search.vue

<template>
  <div>
    <section class="jumbotron">
      <h3 class="jumbotron-heading">Search Github Users</h3>
      <div>
        <input
          type="text"
          placeholder="enter the name you search"
          v-model="keyWord"
        />&nbsp;<button @click="searchUsers">Search</button>
      </div>
    </section>
  </div>
</template>

<script>
import axios from "axios";
export default {
  name: "Search",
  data() {
    return {
      keyWord: "",
    };
  },
  methods: {
    searchUsers() {
        //请求前更新List数据(发送数据)
      this.$bus.$emit('updateListInfo',{isFirst:false,isLoading:true,errMsg:'',users:[]});
    //怎么在字符串里引用关键字变量,不会将变量认错成字符串(模板字符串)
      axios.get(`https://api.github.com/search/users?q=${this.keyword}`).then(
       
        (response) => {
         //查看收到的数据是什么样的(下面的item就是这么写出来的,还有List组件data中的内容也是靠这个判断的)
          console.log("请求成功了", response.data);
          //不需要加载中那些花里胡哨,只传这一句也是可以的
          // this.$bus.$emit('updateListInfo',{users:response.data.items})
            //请求成功后更新List数据
          this.$bus.$emit('updateListInfo',{isLoading:false,errMsg:'',users:response.data.items});
         
        },
        (error) => {
              //请求失败后更新List数据
          console.log("请求失败了", {isLoading:false,errMsg:error.message,users:[]});
        }
      );
    },
  },
};
</script>

<style>
</style>

List.vue

<template>
  <div class="row">
    <!-- 展示用户列表 -->
    <div
      v-show="info.users.length"
      class="card"
      v-for="user in info.users"
      :key="user.login"
    >
      <a :href="user.html_url" target="_blank">
        <img :src="user.avatar_url" style="width: 100px" />
      </a>
      <p class="card-text">{{ user.login }}</p>
    </div>
    <!-- 展示欢迎词 -->
    <h1 v-show="info.isFirst">欢迎使用!</h1>
    <!-- 展示加载中 -->
    <h1 v-show="info.isLoading">加载中....</h1>
    <!-- 展示错误信息 -->
    <h1 v-show="info.errMsg">{{ info.errMsg }}</h1>
  </div>
</template>

<script>
export default {
  name: "List",
  data() {
    return {
      info: {
        isFirst: true,
        isLoading: false,
        errMsg: "",
        users: [],
      },
    };
  },

  mounted() {
    /* 
    一开始是没使用对象的
    this.$bus.$on("updateListInfo", (isFirst, isLoading, errMsg, users) => {
      console.log("我是List组件,收到了数据");
      this.isFirst = isFirst;
      this.isLoading = isLoading;
      this.errMsg = errMsg;
      this.users = users;
    });*/
    //接收数据
    this.$bus.$on("updateListInfo", (dataObj) => {
      console.log("我是List组件,收到了数据");
      console.log(dataObj);
      //   this.info = dataObj; 没写的isFirst属性丢了
      this.info = { ...this.info, ...dataObj };//es6语法拼串
    });
  },
};
</script>

<style scoped>
.album {
  min-height: 50rem; /* Can be removed; just added for demo purposes */
  padding-top: 3rem;
  padding-bottom: 3rem;
  background-color: #f7f7f7;
}

.card {
  float: left;
  width: 33.333%;
  padding: 0.75rem;
  margin-bottom: 2rem;
  border: 1px solid #efefef;
  text-align: center;
}

.card > img {
  margin-bottom: 0.75rem;
  border-radius: 100px;
}

.card-text {
  font-size: 85%;
}
</style>

Vue-resource

npm i vue-resource
在这里插入图片描述

main.js

import Vue from 'vue'
import App from './App.vue'
//引入插件
import vueResource from 'vue-resource'

Vue.config.productionTip = false
//使用插件
Vue.use(vueResource)
const vm = new Vue({
  el: '#app',
  render: h => h(App),
  //全局事件总线
 beforeCreate() {
   Vue.prototype.$bus=this
 },

})

插槽

让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于父组件 >>> 子组件

默认插槽

App.vue

<template>
  <div class="container">
    <Category title="美食" >
      <img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
    </Category>
    <Category title="游戏" >
      <ul>
          <li v-for="(g,index) in games" :key="index">{{g}}</li>
      </ul>
    </Category>
    <Category title="电影" >
      <video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
    </Category>
  
  </div>
</template>

<script>
import Category from "./components/Category.vue";
export default {
  name: "App",
  components: {
   Category,
  },
    data(){
     return{
       foods:['火锅','烧烤','小龙虾','牛排'],
       games:['红色警戒','穿越火线','劲舞团','超级玛丽'],
       films:['教父','拆弹专家','你好','盖茨比'],
     }
   },
};
</script>

<style >
.container{
  display: flex;
  justify-content: space-around;
}
img{
  width:100%;
}
video{
  width:100%;
}
</style>

category.vue

<template>
  <div class="category">
      <h3>{{title}}</h3>
      <!-- 定义一个插槽 (挖个坑,等着组件的使用者填空) -->
      <slot>我是一个默认值,当使用者没有传递具体结构时,我会出现</slot>
      
  </div>
</template>

<script>
export default {
name:"Category",
props:['title']
}
</script>

<style>
.category{
    background-color: rgb(69, 155, 216);
    width:200px;
    height:300px;
}
h3{
    background-color: orange;
    text-align: center;
}
</style>

具名插槽

App.vue

<template>
  <div class="container">
    <Category title="美食">
      <img
        slot="center"
        src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg"
        alt=""
      />
      <a slot="footer" href="http://www.atguigu.com">更多美食</a>
    </Category>
    <Category title="游戏">
      <ul slot="center">
        <li v-for="(g, index) in games" :key="index">{{ g }}</li>
      </ul>
      <div class="foot" slot="footer">
        <a href="http://www.atguigu.com">单机游戏</a>
        <a href="http://www.atguigu.com">网络游戏</a>
      </div>
    </Category>
    <Category title="电影">
      <video
        slot="center"
        controls
        src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"
      ></video>
      <!-- <template slot="footer"> 两种都可以 -->
        <template v-slot:footer>
        <div class="foot">
          <a href="http://www.atguigu.com">经典</a>
          <a href="http://www.atguigu.com">热门</a>
          <a href="http://www.atguigu.com">推荐</a>
        </div>
        <h4>欢迎来到影院观影</h4>
      </template>
    </Category>
  </div>
</template>

<script>
import Category from "./components/Category.vue";
export default {
  name: "App",
  components: {
    Category,
  },
  data() {
    return {
      foods: ["火锅", "烧烤", "小龙虾", "牛排"],
      games: ["红色警戒", "穿越火线", "劲舞团", "超级玛丽"],
      films: ["教父", "拆弹专家", "你好", "盖茨比"],
    };
  },
};
</script>

<style >
.container {
  display: flex;
  justify-content: space-around;
}
img {
  width: 100%;
}
video {
  width: 100%;
}
.foot {
  display: flex;
  justify-content: space-around;
}
h4{
  text-align: center;
}
</style>

Category.vue

<template>
  <div class="category">
      <h3>{{title}}</h3>
      <!-- 定义一个插槽 (挖个坑,等着组件的使用者填空) -->
      <slot name="center">我是一个默认值,当使用者没有传递具体结构时,我会出现1</slot>
       <slot name="footer">我是一个默认值,当使用者没有传递具体结构时,我会出现2</slot>
  </div>
</template>

<script>
export default {
name:"Category",
props:['title']
}
</script>

<style>
.category{
    background-color: rgb(69, 155, 216);
    width:200px;
    height:300px;
}
h3{
    background-color: orange;
    text-align: center;
}
</style>

作用域插槽

数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)
App.vue

<template>
  <div class="container">
    <!-- App使用Category组件,atguigu收到的是Category传来的所有,所以atguigu是一个对象 -->
    <Category title="游戏">
      <template scope="atguigu">
        <ul>
          <li v-for="(g, index) in atguigu.liuyue" :key="index">{{ g }}</li>
        </ul>
      </template>
    </Category>

    <Category title="游戏">
      <template scope="{liuyue}">
        <!-- es6解构赋值 把atguigu写成liuyue-->
        <ol>
          <li v-for="(g, index) in liuyue" :key="index">{{ g }}</li>
        </ol>
      </template>
    </Category>
    <Category title="游戏">
      <!-- 写成scope也可以-->
      <template slot-scope="{ msg }">
        <h4>
          <li v-for="(g, index) in msg" :key="index">{{ g }}</li>
        </h4>
      </template>
    </Category>
  </div>
</template>

<script>
import Category from "./components/Category.vue";
export default {
  name: "App",
  components: {
    Category,
  },
};
</script>

<style >
.container {
  display: flex;
  justify-content: space-around;
}
img {
  width: 100%;
}
video {
  width: 100%;
}
.foot {
  display: flex;
  justify-content: space-around;
}
h4 {
  text-align: center;
}
</style>

Category.vue

<template>
  <div class="category">
    <h3>{{ title }}</h3>
    <!-- 把games传给插槽的使用者 -->
    <slot :liuyue="games" msg="DF">我是一个默认值,当使用者没有传递具体结构时,我会出现1</slot>
    
  </div>
</template>

<script>
export default {
  name: "Category",
  props: ["title"],
  data() {
    return {
      games: ["红色警戒", "穿越火线", "劲舞团", "超级玛丽"],
    };
  },
};
</script>

<style>
.category {
  background-color: rgb(69, 155, 216);
  width: 200px;
  height: 300px;
}
h3 {
  background-color: orange;
  text-align: center;
}
</style>

Vuex

vuex理解

专门在 Vue 中实现集中式状态(数据)管理的一个 Vue 插件,对 vue 应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。

Vuex Github 网址
在这里插入图片描述
在这里插入图片描述

什么时候使用Vuex
1.多个组件依赖于同一状态
2.来自不同组件的行为需要变更同一状态

求和案例_纯vue版本

Count.vue

<template>
  <div>
    <h1>{{ sum }}</h1>
    <select v-model="n">
      <!-- 第一种方法:加上冒号,就当成纯粹的js表达式解析,就是数字
      不加冒号,就是字符串,所以加减法的时候3+2="32"(字符串)
      第二种方法:v-model.number="n",类型转换
       -->
      <option :value="1">1</option>
      <option :value="2">2</option>
      <option :value="3">3</option>
    </select>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
    <button @click="incrementOdd">当前求和为奇数再加</button>
    <button @click="incrementWait">等一等再加</button>
  </div>
</template>

<script>
export default {
  name: "Count",
  data() {
    return {
      sum: 0, //当前的和
      n: 1, //用户选择的数字
    };
  },
  methods: {
    increment() {
      this.sum += this.n;
    },

    decrement() {
      this.sum -= this.n;
    },
    incrementOdd() {
      if (this.sum % 2) {
        this.sum += this.n;
      }
    },
    incrementWait() {
      setTimeout(() => {
        this.sum += this.n;
      }, 500);
    },
  },
};
</script>

<style>
button {
  margin: auto 5px;
}
</style>

App.vue

<template>
  <div >
 <Count/>
  </div>
</template>

<script>
import Count from "./components/Count.vue";
export default {
  name: "App",
  components: {
    Count,
  },
};
</script>

<style >

</style>

Vuex环境

在这里插入图片描述
任何一个组件,都可以调用dispatch和Commit,他们都在store身上,Actions,Mutations,State都是对象{}

Vue2==>vuex3
Vue3==>vuex4
首先下载npm i vuex@3
在这里插入图片描述

index.js
与components同级创建一个store文件夹,里面有index.js

//该文件用于创建Vuex中最为核心的store

import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)

//准备actions,用于响应组件中的动作
const actions={}
//准备mutations,用于操作数据(state)
const mutations={}
//准备state,用于存储数据
const state={}


//创建store
const store=new Vuex.Store({

    actions:actions,//对象的key和保存对应值的变量重名了,可以简写
    mutations:mutations,
    state:state,
})

//暴露store,只有暴露了才可以引入
export default store
// export default new Vuex.Store {(...)} 也是可以的

main.js

import Vue from 'vue'
import App from './App.vue'
import store from "./store"
//引入Vuex
//import Vuex from 'vuex'

Vue.config.productionTip=false;
//Vue.use(Vuex)
new Vue({
  el:"#app",
  render:h=>h(App),
  store,
})
  1. 直接引入store就行,不需要写store下的index.js
  2. 注掉并将Vue.use(Vuex)写在index文件是因为在引入store的时候,我们需要先使用Vuex,而且import不论写的顺序在哪,引入store一定是先执行的,所以为了能使用store,我们将Vue.use(Vuex)写在store中,这样就是先使用Vuex,再引入store。

求和案例_Vuex版本

对外暴露

// 对外暴露实例
export default new Vuex.Store()

App.vue

<template>
  <div >
   
 <Count/>
  </div>
</template>

<script>
import Count from "./components/Count.vue";
export default {
  name: "App",
  components: {
    Count,
  },
};
</script>

main.js

import Vue from 'vue'
import App from './App.vue'
//引入插件
import vueResource from 'vue-resource'
//引入Vuex
import Vuex from 'vuex'

import store from './store/index'


Vue.config.productionTip = false
//使用插件
Vue.use(vueResource)

const vm = new Vue({
  el: '#app',
  render: h => h(App),
  beforeCreate() {
    Vue.prototype.$bus = this
  },
  store,
})

index.js

//该文件用于创建Vuex中最为核心的store

import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)

//准备actions,用于响应组件中的动作
//content代表上下文
const actions = {
  /*  jia(context, value) {
        console.log("action中的jia被调用了");
        console.log(context, value);//可以查看一下输出的上下文是什么,以及在哪里找到sum
        context.commit('JIA', value)
    },
    jian(context, value) {
        console.log("action中的jian被调用了");
        console.log(context, value);
        context.commit('JIAN', value)
    },*/
    jiaOdd(context, value) {
        console.log("action中的jiaOdd被调用了");
        if (context.state.sum%2)
            context.commit('JIA', value)
    },
    jiawait(context, value) {
        console.log("action中的jiawait被调用了");

        setTimeout(()=>{
            context.commit('JIA', value)
        },500);
        
    },
}
//准备mutations,用于操作数据(state)
const mutations = {
    JIA(state, value) {
        console.log("mutations中的JIA被调用了");
        state.sum += value;
    },
    JIAN(state, value) {
        console.log("mutations中的JIAN被调用了");
        state.sum -= value;
    },
}
//准备state,用于存储数据
const state = {
    sum: 0, //当前的和
}


//创建store
const store = new Vuex.Store({

    actions: actions,//对象的key和保存对应值的变量重名了,可以简写
    mutations: mutations,
    state: state,
})

//暴露store,只有暴露了才可以引入
export default store
// export default new Vuex.Store {(...)} 也是可以的

Count.vue

<template>
  <div>
    <h1>{{ $store.state.sum }}</h1>
    <select v-model="n">
      <!-- 第一种方法:加上冒号,就当成纯粹的js表达式解析,就是数字
      不加冒号,就是字符串,所以加减法的时候3+2="32"(字符串)
      第二种方法:v-model.number="n",类型转换
       -->
      <option :value="1">1</option>
      <option :value="2">2</option>
      <option :value="3">3</option>
    </select>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
    <button @click="incrementOdd">当前求和为奇数再加</button>
    <button @click="incrementWait">等一等再加</button>
  </div>
</template>

<script>
export default {
  name: "Count",
  data() {
    return {
      n: 1, //用户选择的数字
    };
  },
  methods: {
    increment() {
      // this.$store.dispatch("jia", this.n);
      this.$store.commit("JIA", this.n);
    },

    decrement() {
      this.$store.commit("JIAN", this.n);
    },
    incrementOdd() {
     /* if (this.$store.state.num % 2) {
        this.$store.dispatch("jia", this.n);
      }*/
      
      this.$store.dispatch("jiaOdd", this.n);
    },
    incrementWait() {
      setTimeout(() => {
        this.$store.dispatch("jiawait", this.n);
      }, 500);
    },
    mounted() {
      console.log(this);//直接可以查看store,进而判断属性方法如何调用
    },
  },
};
</script>

<style>
button {
  margin: auto 5px;
}
</style>

在这里插入图片描述
请添加图片描述

Getters和四种map方法

在这里插入图片描述

…mapState({
FloorList:(state)=>{return state.home.FloorList}
})

在这里插入图片描述
在这里插入图片描述

Count.vue

<template>
  <div>
    <!-- <h1>当前求和为:{{ $store.state.sum }}</h1> -->
    <h1>当前求和为:{{ he }}</h1>
    <h1>当前求和放大10:{{ bigSum }}</h1>
    <!-- <h3>我在{{ $store.state.school }},学习{{$store.state.subject}}</h3> -->
    <h3>我在{{ xuexiao }},学习{{ subject }},这是第二种写法</h3>
    <select v-model="n">
      <option :value="1">1</option>
      <option :value="2">2</option>
      <option :value="3">3</option>
    </select>
    <button @click="increment(n)">+</button>
    <button @click="decrement(n)">-</button>
    <button @click="incrementOdd(n)">当前求和为奇数再加</button>
    <button @click="incrementWait(n)">等一等再加</button>
  </div>
</template>

<script>
import { mapGetters, mapState, mapMutations, mapActions } from "vuex";

export default {
  name: "Count",
  data() {
    return {
      n: 1, //用户选择的数字
    };
  },
  methods: {
    /* increment() {
      // this.$store.dispatch("jia", this.n);
      this.$store.commit("JIA", this.n);
    },

    decrement() {
      this.$store.commit("JIAN", this.n);
    },
*/

    /* 方法不写小括号,不代表不传参数,而是默认的事件对象event
   所以使用时,上面的方法要加参数
   借助mapMutations生成对应的方法,方法会调用commit去联系mutations(对象写法)
*/
    ...mapMutations({ increment: "JIA", decrement: "JIAN" }),
    /*数组写法,但是记得上面的方法名字也要改成JIA,否则错误 
  ...mapMutations(['JIA','JIAN']),
  
*/

    /*  incrementOdd() {
 
      this.$store.dispatch("jiaOdd", this.n);
    },
    incrementWait() {
      setTimeout(() => {
        this.$store.dispatch("jiawait", this.n);
      }, 500);
    }
     借助mapActions生成对应的方法,方法会调用dispatch去联系actions(对象写法)
    */
    ...mapActions({ incrementOdd: "jiaOdd", incrementWait: "jiawait" }),
    // ...mapActions(['jiaOdd','jiawait'])
  },
  computed: {
    /*这是程序员自己亲自去写的计算属性
   he() {
      return this.$store.state.sum;
    },*/

    /*1. mapState是一个对象,computed也是一个对象,我们在{}中再写{},是错的
    解决方法,里面写成...{}  es6语法,把每一对key value都展开
    借助mapState生成计算属性,从State中读取属性

    你给他起啥名字,就调用哪个名字(key)值
    */
    ...mapState({ he: "sum", xuexiao: "school", subject: "subject" }),
    /*2.借助mapState生成计算属性(数组写法)保证调用的名字和属性的名字一致
      ...mapState(['sum','school','name']), */
    ...mapGetters(["bigSum"]),
  },

  mounted() {
    const x = mapState({ he: "sum", xuexiao: "school", subject: "subject" });
    console.log("我是验证时候的x,通过查看,我是一个对象", x);
  },
};
</script>

<style>
button {
  margin: auto 5px;
}
</style>

index.js

//该文件用于创建Vuex中最为核心的store

import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)

//准备actions,用于响应组件中的动作
//content代表上下文
const actions = {
  /*  jia(context, value) {
        console.log("action中的jia被调用了");
        console.log(context, value);//可以查看一下输出的上下文是什么,以及在哪里找到sum
        context.commit('JIA', value)
    },
    jian(context, value) {
        console.log("action中的jian被调用了");
        console.log(context, value);
        context.commit('JIAN', value)
    },*/
    jiaOdd(context, value) {
        console.log("action中的jiaOdd被调用了");
        if (context.state.sum%2)
            context.commit('JIA', value)
    },
    jiawait(context, value) {
        console.log("action中的jiawait被调用了");

        setTimeout(()=>{
            context.commit('JIA', value)
        },500);
        
    },
}
//准备mutations,用于操作数据(state)
const mutations = {
    JIA(state, value) {
        console.log("mutations中的JIA被调用了",value);
   
        state.sum += value;
    },
    JIAN(state, value) {
        console.log("mutations中的JIAN被调用了");
        state.sum -= value;
    },
}
//准备state,用于存储数据
const state = {
    sum: 0, //当前的和
    school:'尚硅谷',
    subject:"计网",
}
//准备getters,用于将state中的数据进行加工
const getters={
    bigSum(state){
        return state.sum*10;
    }
}

//创建store
const store = new Vuex.Store({

    actions: actions,//对象的key和保存对应值的变量重名了,可以简写
    mutations: mutations,
    state: state,
    getters,
})

//暴露store,只有暴露了才可以引入
export default store
// export default new Vuex.Store {(...)} 也是可以的

main.js 和 App.vue同上

在项目中的应用

1.首先在api中写请求接口
在仓库中,可以新建一个模块文件夹,引入接口,四步,暴露出来
在index.vue中发起请求 dispatch

多组件共享数据

APP.vue

<template>
  <div class="container">
    <Count />
    <hr />
    <Person />
  </div>
</template>

<script>
import Count from "./components/Count";
import Person from "./components/Person";

export default {
  name: "App",
  components: { Count, Person },
};
</script>

index.js

就是加了一个ADD_PERSON方法和PersonList属性

import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)

//准备actions,用于响应组件中的动作
const actions = {
    jiaOdd(context, value){
        console.log("actions+jiaOdd被调用了");
        if(state.sum%2){
            context.commit("JIA", value);
        }
    },
    jiawait(context, value){
        console.log("actions+jiawait被调用了");
        context.commit("JIA", value);
    }
}
//准备mutations,用于操作数据state
const mutations = {
    JIA(context, value) {
        console.log("mutations中的JIA被调用了");
        state.sum += value
    },
    JIAN(context, value){
        console.log("mutations中的JIAN被调用了");
        state.sum-=value
    },
    ADD_PERSON(state, value) {
        console.log("mutations中的value(personObj)被调用了");
     state.personList.unshift(value)
    }

}
//准备state,用于存储数据
const state = {
    sum: 0,
    personList:[
		{id:'001',name:'JOJO'}
	],
}

const getters={
    bigSum(state){
        return state.sum*10
    }
}
//创建store
export default new Vuex.Store({
    actions,
    mutations,
    state,
    getters,
})

Count.vue

<template>
  <div>
    <h1>当前求和为:{{ sum }}</h1>
    <h1>{{ bigSum }}</h1>
    	<h3 style="color:red">Person组件的总人数是:{{personList.length}}</h3>
    <select v-model="n">
      <option :value="1">1</option>
      <option :value="2">2</option>
      <option :value="3">3</option>
    </select>
    <button @click="increment(n)">+</button>
    <button @click="decrement(n)">-</button>
    <button @click="incrementOdd(n)">当前求和为奇数再加</button>
    <button @click="incrementWait(n)">等一等再加</button>
  </div>
</template>

<script>
import { mapGetters, mapState, mapMutations, mapActions } from "vuex";
export default {
  name: "Count",
  data() {
    return {
      n: 1,
    };
  },
  methods: {
    ...mapMutations({ increment: "JIA", decrement: "JIAN" }),
    ...mapActions({ incrementOdd: "jiaOdd", incrementWait: "jiawait" }),
  },
  computed: {
    ...mapState([ "sum","personList" ]),
    ...mapGetters(["bigSum"]),
  },
};
</script>

<style>
button {
  margin: 5px;
}
</style>

Person.vue

<template>
  <div>
      <h1>人员列表</h1>
      <h3 style="color:red">Count组件求和为:{{sum}}</h3>
      <input type="text" placeholder="请输入姓名" v-model="name">
      <button @click="add">提交</button>
      <ul>
          <li v-for="p in $store.state.personList" :key="p.id">{{p.name}}</li>
      </ul>
  </div>
</template>

<script>
import {nanoid} from "nanoid"
export default {
name:"Person",
data(){
    return{
        name:"",
    }
},
computed:{
    sum(){
        return this.$store.state.sum
    }
},
methods:{
    add(){
        const personObj={id:nanoid(),name:this.name}
        this.$store.commit("ADD_PERSON",personObj)
        this.name=''
    }
}
}
</script>

<style>

</style>

Vue模块化+命名空间

让代码更好维护,让多种数据分类更加明确

main.js

import Vue from 'vue'
import App from './App.vue'
//引入插件
import vueResource from 'vue-resource'
//引入Vuex
import Vuex from 'vuex'

import store from './store/index'


Vue.config.productionTip = false
//使用插件
Vue.use(vueResource)

const vm = new Vue({
  el: '#app',
  render: h => h(App),
  beforeCreate() {
    Vue.prototype.$bus = this
  },
  store,
})

App.vue

<template>
  <div>
    <Count />
    <Person />
  </div>
</template>

<script>
import Count from "./components/Count.vue";
import Person from "./components/Person.vue";
export default {
  name: "App",
  components: {
    Count,
    Person,
  },
};
</script>


index.js

//该文件用于创建Vuex中最为核心的store

import Vuex from 'vuex'
import Vue from 'vue'
import axios from 'axios'
import {nanoid} from 'nanoid'
Vue.use(Vuex)

//求和相关的配置
const countOptions = {
    //开启命名空间
    namespaced: true,
    actions: {
        jiaOdd(context, value) {
            console.log("action中的jiaOdd被调用了");
            if (context.state.sum % 2)
                context.commit('JIA', value)
        },
        jiawait(context, value) {
            console.log("action中的jiawait被调用了");

            setTimeout(() => {
                context.commit('JIA', value)
            }, 500);

        },
    },
    mutations: {
        JIA(state, value) {
            console.log("mutations中的JIA被调用了", value);

            state.sum += value;
        },
        JIAN(state, value) {
            console.log("mutations中的JIAN被调用了");
            state.sum -= value;
        },
    },
    state: {
        sum: 0, //当前的和
        school: '尚硅谷',
        subject: "计网",
    },
    getters: {
        bigSum(state) {
            return state.sum * 10;
        }
    },
}
//人员管理相关的配置
const personOptions = {
    namespaced: true,
    actions: {
        addPersonWang(context, value) {
            //value是person对象,可以log查看一下
            if (value.name.indexOf('王') === 0) {
                context.commit("ADD_PERSON", value)
            } else {
                alert("添加的人必须姓王!");
            }
        },
        //联系服务器要某人名字
        addPersonServer(context){
            axios.get('http://api.uixsj.cn/hitokoto/get?type=social').then(
                response=>{  
                    context.commit('ADD_PERSON',{id:nanoid(),name:response.data})
                },
                error=>{ alert(error.message)}
            )
        }
    },
    mutations: {
        ADD_PERSON(state, value) {
            console.log("mutations中的value(personObj)被调用了");
            state.personList.unshift(value)
        }
    },

    state: {
        personList: [{ id: "001", name: "张三" }],
    },
    getters: {
        //这个地方的state是自己的state,而不是总的state
        firstPersonName(state) {
            return state.personList[0].name;
        }
    },
}

//创建store
export default new Vuex.Store({

    modules: {
        countAbout: countOptions,
        personAbout: personOptions,
    }
})


person.vue


<template>
  <div>
    <h1>人员列表</h1>
    <h3 class="cnt">Count组件的求和为{{ sum }}</h3>
    <h3>列表中第一个人的名字是:{{ firstPersonName }}</h3>
    <input type="text" placeholder="请输入名字" v-model="name" />
    <button @click="add">添加</button>
    <button @click="addWang">添加一个姓王的人</button>
   <button @click="addPersonServer">添加一个人,名字随机</button>
    <ul>
      <li v-for="p in personList" :key="p.id">{{ p.name }}</li>
    </ul>
  </div>
</template>

<script>
import { mapState } from "vuex";
import { nanoid } from "nanoid";
export default {
  name: "Person",
  data() {
    return {
      name: "",
    };
  },
  computed: {
    //这两种写法都可以
    //  ...mapState(["personList","sum"]),
    personList() {
      return this.$store.state.personAbout.personList;
    },
    sum() {
      return this.$store.state.countAbout.sum;
    },
    firstPersonName() {
      return this.$store.getters["personAbout/firstPersonName"];
    },
  },
  methods: {
    add() {
      const personObj = { id: nanoid(), name: this.name };
      console.log(personObj);
      this.$store.commit("personAbout/ADD_PERSON", personObj);
      this.name = "";
    },
    addWang() {
      //commit联系mutation,dispatch联系actions
      const personObj = { id: nanoid(), name: this.name };
      this.$store.dispatch("personAbout/addPersonWang", personObj);
      this.name = "";
    },
    addPersonServer(){
         this.$store.dispatch("personAbout/addPersonServer");
    }
  },
};
</script>

<style>
</style>

Count.vue

<template>
  <div>
    <h1>当前求和为:{{ sum }}</h1>
    <h1>当前求和放大10:{{ bigSum }}</h1>

    <h3>我在{{ school }},学习{{ subject }},这是第二种写法</h3>
    <h3 class="cnt">下方组件的总人数是:{{ personList.length }}</h3>
    <select v-model="n">
      <option :value="1">1</option>
      <option :value="2">2</option>
      <option :value="3">3</option>
    </select>
    <button @click="increment(n)">+</button>
    <button @click="decrement(n)">-</button>
    <button @click="incrementOdd(n)">当前求和为奇数再加</button>
    <button @click="incrementWait(n)">等一等再加</button>
  </div>
</template>

<script>
import { mapGetters, mapState, mapMutations, mapActions } from "vuex";

export default {
  name: "Count",
  data() {
    return {
      n: 1, //用户选择的数字
    };
  },
  methods: {
    ...mapMutations("countAbout", { increment: "JIA", decrement: "JIAN" }),
    ...mapActions("countAbout", {
      incrementOdd: "jiaOdd",
      incrementWait: "jiawait",
    }),
  },
  computed: {
    //未开启命名空间 ...mapState({ countAbout:"countAbout",personAbout:"personAbout" }),
    ...mapState("countAbout", ["sum", "school", "subject"]),
    ...mapState("personAbout", ["personList"]),
    ...mapGetters("countAbout", ["bigSum"]),
  },

  mounted() {
    // const x = mapState({ he: "sum", xuexiao: "school", subject: "subject" });
    // console.log("我是验证时候的x,通过查看,我是一个对象", x);
    console.log(this.$store);
  },
};
</script>

<style>
button {
  margin: auto 5px;
}
.cnt {
  background-color: rgb(236, 153, 154);
  width: 300px;
}
</style>

单独拆分

也可以吧person和count单独拆分成.js
注意写法上请添加图片描述

修改store.js:

const countAbout = {
	namespaced:true,//开启命名空间
	state:{x:1},
    mutations: { ... },
    actions: { ... },
  	getters: {
    	bigSum(state){
       		return state.sum * 10
    	}
  	}
}

const personAbout = {
  	namespaced:true,//开启命名空间
  	state:{ ... },
  	mutations: { ... },
  	actions: { ... }
}

const store = new Vuex.Store({
  	modules: {
    	countAbout,
    	personAbout
  	}
})

开启命名空间后,组件读取state数据

//方式一:自己直接读取
this.$store.state.personAbout.list
//方式二:借助mapState读取:
...mapState('countAbout',['sum','school','subject']),

开启命名空间后,组件读取state数据

//方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName']
//方式二:借助mapGetters读取:
...mapGetters('countAbout',['bigSum'])

开启命名空间后,组件中调用dispatch:

//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions:
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})

开启命名空间后,组件中调用commit:

//方式一:自己直接commit
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二:借助mapMutations:
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),

路由

在这里插入图片描述

  1. 什么是路由?

    • 一个路由就是一组映射关系(key - value)
    • key 为路径,value 可能是 function 或 component(组件)
  2. 后端路由:

    • 理解:value 是 function,用于处理客户端提交的请求
    • 工作过程:服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据
  3. 前端路由:

    • 理解:value 是 component(组件),用于展示页面内容
    • 工作过程:当浏览器的路径改变时,对应的组件就会显示
      请添加图片描述
      请添加图片描述

路由基本使用

  • 首先把index.html中的bootstrap.css使用打开

  • 注意新建一个router文件夹,把index.js放入

  • 新建一个pages文件夹,把About和Home放入

  • 在路由配置中是routes,component,而不是 routers,components

  • 下载vue-router:npm i vue-router@3
    在这里插入图片描述

main.js

import Vue from 'vue'
//引入vue-router
import VueRouter from 'vue-router'

import App from './App.vue'
//引入路由文件
import router from './router'
Vue.config.productionTip = false
//应用插件
Vue.use(VueRouter)

const vm = new Vue({
  el: '#app',
  render: h => h(App),
  router,
})

bootstrap.css

App.vue

<template>
  <div>
    <div class="row">
      <div class="col-xs-offset-2 col-xs-8">
这个地方是有一个标题内容的
      </div>
    </div>
    <div class="row">
      <div class="col-xs-2 col-xs-offset-2">
        <div class="list-group">
          <!-- 原始html中,我们使用a标签实现页面的跳转 -->
          <!-- <a class="list-group-item active" href="./about.html">About</a>
          <a class="list-group-item" href="./home.html">Home</a> -->

          <!-- Vue中借助router-link标签实现路由的切换 -->
          <router-link class="list-group-item " active-class="active" to="/about">About</router-link>
          <router-link class="list-group-item"  active-class="active" to="/home">Home</router-link>
        </div>
      </div>
      <div class="col-xs-6">
        <div class="panel">
          <div class="panel-body">
            <!-- 此处到底展示什么组件,得看用户点击的是哪个导航项 找清这是哪个地方,可以给它加上个样式判断-->
            <!-- 指定组件的呈现位置 -->
            <router-view></router-view>
            
				
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

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


About.vue

<template>
  <h2>我是About的内容</h2>
</template>

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

}
</script>
<style>

</style>

Home.vue

<template>
    <h2>我是Home的内容</h2>
</template>

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

<style>

</style>

index.js

//该文件用于创建整个应用的路由器
import VueRouter from "vue-router"
//引入组件
import About from "../pages/About"
import Home from "../pages/Home"
//创建并暴露一个路由器
export default new VueRouter({
    routes: [
        {
        path: '/about',
        component: About
    },
    {
        path: '/home',
        component: Home
    },
]
})


一般组件,路由组件

  • 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载

  • 挂载和销毁在整个过程中都是不断重复的

  • 每个组件都有自己的$route属性,里面存储着自己的路由信息

  • 整个应用只有一个router,可以通过组件的$router属性获取到

  • 一般组件写在components中,路由组件写在pages中

router和route

$router:进行编程式导航的路由跳转
this.$router.push|this.$router.replace
$route:可以获取路由的信息|参数
this.$route.path
this.$route.params|query
this.$route.meta

在这里插入图片描述

嵌套路由(多级路由)

分布情况

main.js

直接使用上面的即可

index.js

//该文件用于创建整个应用的路由器
import VueRouter from "vue-router"
//引入组件
import About from "../pages/About"
import Home from "../pages/Home"
import News from '../pages/News'
import Message from '../pages/Message'
//创建并暴露一个路由器
export default new VueRouter({
    routes: [
        //一级路由
        {
            path: '/about',
            component: About
        },
        {
            path: '/home',
            component: Home,
            //二级路由(home的子路由)
            children:[
                {
                path: "news",//二级路由不要加/了
                component: News,
            },
            {
                path: "message",
                component: Message,
            },
        ]
        },
    ]
})

Home.vue

<template>
        <div class="panel-body">
            <div>
              <h2>Home组件内容</h2>
              <div>
                <ul class="nav nav-tabs">
                  <li>
                    <!-- 子级路由,需要带着父级的路径 -->
                    <router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>
                  </li>
                  <li>
                    <router-link class="list-group-item "active-class="active" to="/home/message">Message</router-link>
                  </li>
                </ul>
               <!-- ??? -->
               <router-view></router-view>
               
              </div>
            </div>
          </div>
</template>

<script>
export default {
name:"Home",/*
beforeDestroy(){
  console.log("home组件即将被销毁了");
},
mounted(){
    console.log("home组件挂载完毕",this);
},*/
}
</script>

<style>

</style>

About.vue

用不到,和上面的一样即可

Message.vue

<template>
  <div>
    <ul>
      <li><a href="/message1">message001</a>&nbsp;&nbsp;</li>
      <li><a href="/message2">message002</a>&nbsp;&nbsp;</li>
      <li><a href="/message/3">message003</a>&nbsp;&nbsp;</li>
    </ul>
  </div>
</template>

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

<style>
</style>

News.vue

<template>
  <div>
    <ul>
      <li>news001</li>
      <li>news002</li>
      <li>news003</li>
    </ul>
  </div>
</template>

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

<style>
</style>

App.vue

<template>
  <div>
    <div class="row">
      <div class="col-xs-offset-2 col-xs-8">
       <Banner/>
      </div>
    </div>
    <div class="row">
      <div class="col-xs-2 col-xs-offset-2">
        <div class="list-group">
          <!-- 原始html中,我们使用a标签实现页面的跳转 -->
          <!-- <a class="list-group-item active" href="./about.html">About</a>
          <a class="list-group-item" href="./home.html">Home</a> -->

          <!-- Vue中借助router-link标签实现路由的切换 -->
          <router-link class="list-group-item " active-class="active" to="/about">About</router-link>
          <router-link class="list-group-item"  active-class="active" to="/home">Home</router-link>
        </div>
      </div>
      <div class="col-xs-6">
        <div class="panel">
          <div class="panel-body">
            <!-- 此处到底展示什么组件,得看用户点击的是哪个导航项 -->
            <!-- 指定组件的呈现位置 -->
            <router-view></router-view>
            
				
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import Banner from "./components/Banner.vue"
export default {
  name: "App",
  components:{
    Banner,
  }
};
</script>


Banner.vue

<template>
   <div class="page-header"><h2>Vue Router Demo</h2></div>
</template>

<script>
export default {
name:"Banner",
}
</script>

<style>

</style>

路由的重定向

  • path中的内容指的是,我在URL路径中输入的信息,只要我输入*,就会跳转到指定是路径
  • path中的参数还可以是/
  • redirect中指的就是要跳转的地址
  • 重定向的目标可以是一个对象,一个路由地址,一个命名的路由或者一个方法
  • 1.路由地址在这里插入图片描述
    2、重定向的目标也可以是一个对象,包含路由地址:
const router = new Router({
  routes: [
    { path: '*', 
      redirect: {path:'/home'}
   }
  ]
})

3、重定向的目标也可以是
一个命名的路由:

const router = new Router({
  routes: [
    { path: '*', 
      redirect: {name:'Home'}
   }
  ]
})

4、重定向的目标可以是一个方法,动态返回重定向目标:

const router = new Router({
  routes: [
    { path: '*', 
      redirect:(to)=>{
        //to目标路由对象,就是访问的路径的路由信息
        if(to.path==='/123'){
          return '/about';
        }else if(to.path=='/456'){
          return '/document'
        }else{
          return '/home';
        }
 
      }
   }
  ]
})

路由跳转传参query+路由命名

跳转的时候带着参数过去
在这里插入图片描述
通过这个,才可以把问号??替换掉
这种写法,一点不打扰路由的配置

Detail.vue

<template>
  <ul>
      <li>消息编号:{{$route.query.id}}</li>
      <li>消息标题:{{$route.query.title}}</li>
  </ul>
</template>

<script>
export default {
    name:"Detail",
    mounted(){
        //得到组件相关的路由信息
        console.log("我是detail中输出的",this.$route);
    }

}
</script>

<style>

</style>

index.js

//该文件用于创建整个应用的路由器
import VueRouter from "vue-router"
//引入组件
import About from "../pages/About"
import Home from "../pages/Home"
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from "../pages/Detail"
//创建并暴露一个路由器
export default new VueRouter({
    routes: [
        //一级路由
        {
            name:"guanyu",
            path: '/about',
            component: About
        },
        {
            path: '/home',
            component: Home,
            //二级路由(home的子路由)
            children:[
                {
                path: "news",//二级路由不要加/了
                component: News,
            },
            {
                path: "message",
                component: Message,
                children:[
                    {  name:"xiangqing",
                        path:'detail',
                        component:Detail,
                    }
                ]
            },
        ]
        },
    ]
})


先把detail中的内容,使用 <router-view></router-view>给呈现出来
先使用普通的路由,再使用特殊的

Message.vue

<template>
  <div>
    <ul>
      <li v-for="m in messageList" :key="m.id">
          <!--跳转路由携带query参数,to的字符串写法 模板字符串混着js的东西 -->
        <!-- <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{ m.title }}</router-link>&nbsp; -->
             <!--跳转路由携带query参数,to的对象写法 模 -->
             <router-link :to="{
                 path:'/home/message/detail', //(要用单引号,双引号会出错,因为外面有一个双引号了
                 //name:'xiangqing',//找到她的名字,呼唤她(path 和name写一个就行)
                 query:{
                     id:m.id,
                     title:m.title
                 },
             }">
                {{m.title}}
             </router-link>
             

      </li>
    </ul>
    <hr>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: "Message",
  data() {
    return {
      messageList: [
        { id: "001", title: "消息001000" },
        { id: "002", title: "消息002" },
        { id: "003", title: "消息003" },
      ],
    };
  },
};
</script>

<style>
</style>

App.vue

<template>
  <div>
    <div class="row">
      <div class="col-xs-offset-2 col-xs-8">
       <Banner/>
      </div>
    </div>
    <div class="row">
      <div class="col-xs-2 col-xs-offset-2">
        <div class="list-group">
          <!-- 原始html中,我们使用a标签实现页面的跳转 -->
          <!-- <a class="list-group-item active" href="./about.html">About</a>
          <a class="list-group-item" href="./home.html">Home</a> -->

          <!-- Vue中借助router-link标签实现路由的切换 -->
          <router-link class="list-group-item " active-class="active" :to="{path:'/about'}">About</router-link>
           <router-link class="list-group-item " active-class="active" :to="{name:'guanyu'}">About</router-link>
          <router-link class="list-group-item"  active-class="active" to="/home">Home</router-link>
        </div>
      </div>
      <div class="col-xs-6">
        <div class="panel">
          <div class="panel-body">
            <!-- 此处到底展示什么组件,得看用户点击的是哪个导航项 -->
            <!-- 指定组件的呈现位置 -->
            <router-view></router-view>
            
				
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import Banner from "./components/Banner.vue"
export default {
  name: "App",
  components:{
    Banner,
  }
};
</script>

About.vue

<template>
  <h2>我是About的内容</h2>
</template>

<script>
export default {
name:"About",
beforeDestroy(){
  console.log("about组件即将被销毁了");
},
mounted(){
    console.log("about组件挂载完毕",this);
    //验证一下router是一样的,但是route不是
    window.aboutRoute=this.$route
      window.aboutRouter=this.$router

},
}
</script>

<style>

</style>

路由跳转传参 params

需要修改的地方

  1. 路由声明的地方(使用占位符)
{
	path:'/home',
	component:Home,
	children:[
		{
			path:'news',
			component:News
		},
		{
			component:Message,
			children:[
				{
					name:'xiangqing',
					path:'detail/:id/:title', //使用占位符声明接收params参数
					component:Detail
				}
			]
		}
	]
}

  1. Message传递参数的时候
<!-- 跳转并携带params参数,to的字符串写法 -->
<router-link :to="`/home/message/detail/${m.id}/${m.title}`">跳转</router-link>
				
<!-- 跳转并携带params参数,to的对象写法 -->
<router-link 
	:to="{
		name:'xiangqing',
		params:{
		   id:m.id,
           title:m.title
		}
	}"
>跳转</router-link>

路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!

  1. Detail接收参数:
$route.params.id
$route.params.title

Message.vue

<template>
  <div>
    <ul>
      <li v-for="m in messageList" :key="m.id">
          <!--跳转路由携带query参数,to的字符串写法 模板字符串混着js的东西 -->
        <!-- <router-link :to="`/home/message/detail/${m.id}/${m.title}`">{{ m.title }}</router-link>&nbsp; -->
             <!--跳转路由携带query参数,to的对象写法 模 -->
          
             <router-link :to="{
                name:'xiangqing',//使用params,一定用的是name,不能用path
                 params:{
                     id:m.id,
                     title:m.title
                 },
             }">
                {{m.title}}
             </router-link>
             

      </li>
    </ul>
    <hr>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: "Message",
  data() {
    return {
      messageList: [
        { id: "001", title: "消息001000" },
        { id: "002", title: "消息002" },
        { id: "003", title: "消息003" },
      ],
    };
  },
};
</script>

<style>
</style>

Detail.vue
自行更改,只需要把上一个query全部替换为params
index.js
自己更改吧,只需要加上第一步的占位符

路由的props配置

Message.vue
可以直接使用上面的 ,需要注意的是,query参数和params参数的更替。
index.js

//该文件用于创建整个应用的路由器
import VueRouter from "vue-router"
//引入组件
import About from "../pages/About"
import Home from "../pages/Home"
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from "../pages/Detail"
//创建并暴露一个路由器
export default new VueRouter({
    routes: [
        //一级路由
        {
            name: "guanyu",
            path: '/about',
            component: About
        },
        {
            path: '/home',
            component: Home,
            //二级路由(home的子路由)
            children: [
                {
                    path: "news",//二级路由不要加/了
                    component: News,
                },
                {
                    path: "message",
                    component: Message,
                    children: [
                        {
                            name: "xiangqing",
                            path: 'detail/:id/:title',//占位符
                           
                            component: Detail,
                            /*props的第一种写法,值为对象,
                            该对象中的所有key-value都会以props的形式传给Detail组件
                            props: { a: 999, b: "liuyue" }
                            */
                            /*2.值为布尔值,
                            若布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传给Detail组件
                            path: 'detail',
                             props:true,
                            */
                           /*3.值为函数*/
                           props($route){
                               return{
                                   id:$route.params.id,
                                   title:$route.params.title
                                   /*如果Message中使用的query传值,那么这个地方可以更正为
                                    id:$route.query.id,
                                   title:$route.query.title
                                   */
                               }
                           },
                           /* 
                           解构赋值
                              props({query}){
                               return{
                                   id:query.id,
                                   title:query.title
                               }
                           }
                           
                           解构赋值的连续写法
                           props({query:{id,title}}){
                               return{
                                   id:id,
                                   title:title
                               }
                           }
                           */
                           
                           
                        }
                    ]
                },
            ]
        },
    ]
})


Detail.vue

<template>
  <ul>
      <li>消息编号:{{$route.params.id}}</li>
      <li>消息标题:{{$route.params.title}}</li>
      <li>a:{{a}}</li>
      <li>b:{{b}}</li>
       <li>消息编号:{{id}}</li>
      <li>消息标题:{{title}}</li>
  </ul>
</template>

<script>
export default {
    name:"Detail",
    props:['a','b',"id","title"],
    mounted(){
        //得到组件相关的路由信息
        console.log(this.$route);
    }

}
</script>

声明式路由导航router-link

  1. 作用:控制路由跳转时操作浏览器历史记录的模式
  2. 浏览器的历史记录有两种写入方式:push和replace,其中push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push方式
  3. 开启replace模式:<router-link replace ...>News</router-link>
    4.加上了replace属性之后,点击某一个,他不会在历史记录里留下她的上一个,也就是说直接把上一个链接替换掉,没办法返回到上一个页面

请添加图片描述

编程式路由导航this.$router.push

不借助router-link的导航
注意,我将所有的params都换成了query

 goSearch() {
      //获取input搜索框内的内容
      // this.$router.push({ name: "search", query: { keyword: this.keyword } });
      let location = { name: "search", params: { keyword: this.keyword } };
      if (this.$route.query) {
        location.query = this.$route.query;
      }
      // console.log(location);
      this.$router.push(location);
    },

  sendGoodId(id){
      this.$router.push({name:"detail",query:{skuId:id}})
    }

在这里插入图片描述

Banner.vue

<template>
  <div class="page-header">
    <h2>Vue Router Demo</h2>

    <button @click="back">后退</button>
    <button @click="forward">前进</button>
    <button @click="test">测试一下go</button>
  </div>
</template>

<script>
export default {
  name: "Banner",
  methods:{
     back(){
      //   console.log(this.$router);//还是在原型里面
      this.$router.back()
     },
     forward(){
           this.$router.forward()
     },
     test(){
        //往前走几步
        this.$router.go(3)
     }
  }
};
</script>

<style>
</style>

Detail.vue

<template>
  <ul>
      <li>消息编号:{{$route.query.id}}</li>
      <li>消息标题:{{$route.query.title}}</li>
      <li>a:{{a}}</li>
      <li>b:{{b}}</li>
       <li>消息编号:{{id}}</li>
      <li>消息标题:{{title}}</li>
  </ul>
</template>

<script>
export default {
    name:"Detail",
    props:['a','b',"id","title"],
    mounted(){
        //得到组件相关的路由信息
        console.log(this.$route);
    }

}
</script>

<style>

</style>

Message.vue

<template>
  <div>
    <ul>
      <li v-for="m in messageList" :key="m.id">
        

        <router-link
          :to="{
            name: 'xiangqing', //使用params,一定用的是name,不能用path
            query: {
              id: m.id,
              title: m.title,
            },
          }"
        >
          {{ m.title }}
        </router-link>
        <button @click="pushShow(m)">push查看</button>
        <button @click="replaceShow(m)">replace查看</button>
        
      </li>
    </ul>
    <hr />
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: "Message",
  data() {
    return {
      messageList: [
        { id: "001", title: "消息001000" },
        { id: "002", title: "消息002" },
        { id: "003", title: "消息003" },
      ],
    };
  },

  methods: {
    pushShow(m) {
      // console.log(this.$router);//输出之后,在原形中找到push/replace
      this.$router.push({
        name: "xiangqing", //使用params,一定用的是name,不能用path
        query: {
          id: m.id,
          title: m.title,
        },
      });
    },
    replaceShow(m){
       this.$router.replace({
        name: "xiangqing", //使用params,一定用的是name,不能用path
        query: {
          id: m.id,
          title: m.title,
        },
      });
    }
  },
};
</script>

<style>
</style>

index.js

//该文件用于创建整个应用的路由器
import VueRouter from "vue-router"
//引入组件
import About from "../pages/About"
import Home from "../pages/Home"
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from "../pages/Detail"
//创建并暴露一个路由器
export default new VueRouter({
    routes: [
        //一级路由
        {
            name: "guanyu",
            path: '/about',
            component: About
        },
        {
            path: '/home',
            component: Home,
            //二级路由(home的子路由)
            children: [
                {
                    path: "news",//二级路由不要加/了
                    component: News,
                },
                {
                    path: "message",
                    component: Message,
                    children: [
                        {
                            name: "xiangqing",
                            path: 'detail',//占位符
                           
                            component: Detail,
                          
                           props($route){
                               return{
                                    id:$route.query.id,
                                   title:$route.query.title
                               }
                           },
                          
                           
                           
                        }
                    ]
                },
            ]
        },
    ]
})


缓存路由组件

让不展示的路由组件保持挂载,不被销毁
如果不写的话,就是都缓存

//缓存一个路由组件
<keep-alive include="News"> //include中写想要缓存的组件名,不写表示全部缓存
    <router-view></router-view>
</keep-alive>

//缓存多个路由组件
<keep-alive :include="['News','Message']"> 
    <router-view></router-view>
</keep-alive>

记住,对news进行缓存,是要写在home组件中,想实现在input中输入内容,切换回来之后,内容依旧存在。在这里插入图片描述

在这里插入图片描述

News.vue

<template>
  <div>
    <ul>
      <li>news001</li><input type="text">
      <li>news002</li><input type="text">
      <li>news003</li><input type="text">
    </ul>
  </div>
</template>

<script>
export default {
  name: "News",
  beforeDestroy(){
    console.log("News组件即将被销毁了");
    //如果被销毁了,是会显示这句话的
  }
};
</script>

<style>
</style>

两个新的生命周期钩子

Home.vue和上面的一样,加上一个keep-alive即可
News.vue

<template>
  <div>
    <ul>
      <li :style="{ opacity }">欢迎学习Vue</li>
      <li>news001</li>
      <input type="text" />
      <li>news002</li>
      <input type="text" />
      <li>news003</li>
      <input type="text" />
    </ul>
  </div>
</template>

<script>
export default {
  name: "News",
  data() {
    return {
      opacity: 1,
    }
  },
  /*  mounted(){
    this.timer=setInterval(()=>{
    
      this.opacity-=0.01
      if(this.opacity<=0) 
      this.opacity=1
    },30)
  },
*/
  // beforeDestroy(){
  //   console.log("News组件即将被销毁了");
  //   //如果被销毁了,是会显示这句话的
  // },

  activated() {
    //激活
    console.log("News组件被激活了");
  },
  deactivated() {
     console.log("News组件被失活了");
  }, 
   

};
</script>

<style>
</style>

activated和deactivated是路由组件所独有的两个钩子,用于捕获路由组件的激活状态
activated路由组件被激活时触发
deactivated路由组件失活时触发

全局前置

首先设置,可以将代码中的School更改一下,查看

在这里插入图片描述

index.js

//该文件用于创建整个应用的路由器
import VueRouter from "vue-router"
//引入组件
import About from "../pages/About"
import Home from "../pages/Home"
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from "../pages/Detail"
//创建并暴露一个路由器
const router = new VueRouter({
    routes: [
        //一级路由
        {
            name: "guanyu",
            path: '/about',
            component: About
        },
        {
            name: "zhuye",
            path: '/home',
            component: Home,
            //二级路由(home的子路由)
            children: [
                {
                    name: "xinwen",
                    path: "news",//二级路由不要加/了
                    component: News,
                },
                {
                    name: "xiaoxi",
                    path: "message",
                    component: Message,
                    children: [
                        {
                            name: "xiangqing",
                            path: 'detail',

                            component: Detail,

                            props($route) {
                                return {
                                    id: $route.query.id,
                                    title: $route.query.title
                                }
                            },



                        }
                    ]
                },
            ]
        },
    ]
})
//全局前置路由守卫,初始化的时候+每一次路由切换之前,调用函数
router.beforeEach((to, from, next) => {
    // console.log("#");
    console.log(to);
    //先判断去的是哪里
    // if (to.name === 'xinwen' || to.name === 'xiaoxi') {
    if (to.path == '/home/news'||to.path == '/home/message') {
            if (localStorage.getItem('school') === 'atguigu') {
                next()//正确的时候,才会放行
            }
            else {
                alert("学校名不对,无权限查看")
            }
           
    } else{//不是上面那两个,就放行
        next()
    }
   

})
export default router

后置路由守卫

有一个小bug,index.html这个地方的名字没有改,所以切换到 时候可能会出错
还可以把这个地方名字改了

在这里插入图片描述

//该文件用于创建整个应用的路由器
import VueRouter from "vue-router"
//引入组件
import About from "../pages/About"
import Home from "../pages/Home"
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from "../pages/Detail"
//创建并暴露一个路由器
const router = new VueRouter({
    routes: [
        //一级路由
        {
            name: "guanyu",
            path: '/about',
            component: About,
            meta: {
                isAuth:true,
                title: "关羽",
            }
        },
        {
            name: "zhuye",
            path: '/home',
            component: Home,
            //二级路由(home的子路由)
            meta: {
                title: "主页",
            },
            children: [
                {
                    name: "xinwen",
                    path: "news",//二级路由不要加/了
                    component: News,
                    meta: {
                        isAuth: true,
                        title: "新闻",
                    }
                },
                {
                    name: "xiaoxi",
                    path: "message",
                    component: Message,
                    meta: {isAuth: true,title: "消息",},
                    children: [
                        {
                            name: "xiangqing",
                            path: 'detail',//占位符
                            component: Detail,
                            meta: {isAuth: true,title: "详情",},
                            props($route) {
                                return {
                                    id: $route.query.id,
                                    title: $route.query.title
                                }
                            },



                        }
                    ]
                },
            ]
        },
    ]
})
//全局前置路由守卫,初始化的时候+每一次路由切换之前,调用函数
router.beforeEach((to, from, next) => {
    // console.log("#");
    console.log("前置路由守卫", to);
 
    if (to.meta.isAuth) {//判断是否需要鉴权
        if (localStorage.getItem('school') === 'atguigu') {
            
            next()//正确的时候,才会放行
        }
        else {
            alert("学校名不对,无权限查看")
        }

    } else {//不是上面那两个,就放行
        next()
    }

})

/*后置路由守卫,初始化的时候+每一次路由切换之后,调用函数*/
router.afterEach((to,from)=>{
    console.log("后置路由守卫",to,from);
       document.title = to.meta.title|| "硅谷系统";
})

export default router

独享路由守卫

给哪个组件添加就把这个加在组件里,可以和后置路由搭配使用,可以随意搭配

请添加图片描述

       //独享路由(需求,只对新闻这个路由做出响应)
                    beforeEnter: (to, from, next) => {
                        console.log("独享路由守卫", to);

                        if (to.meta.isAuth) {//判断是否需要鉴权
                            if (localStorage.getItem('school') === 'atguigu') {

                                next()//正确的时候,才会放行
                            }
                            else {
                                alert("学校名不对,无权限查看")
                            }

                        } else {//不是上面那两个,就放行
                            next()
                        }
                    }

组件内路由守卫

index.js用上面的

About.vue

<template>
  <h2>我是About的内容</h2>
</template>

<script>
export default {
  name: "About",
  beforeDestroy() {
    console.log("about组件即将被销毁了");
  },

  //通过路由规则(如果一开始通过新增一个组件,就不是通过规则进入的),进入该组件时被调用
  beforeRouteEnter(to, from, next) {
    console.log("beforeRouteEnter,进入了About", to);
    if (to.meta.isAuth) {
      //判断是否需要鉴权
      if (localStorage.getItem("school") === "atguigu") {
        next(); //正确的时候,才会放行
      } else {
        alert("学校名不对,无权限查看");
      }
    } else {
      //不是上面那两个,就放行
      next();
    }
  },
  //通过路由规则,离开该组件时被调用
  beforeRouteLeave(to, from, next) {
   console.log("beforeRouteLeave,离开了About", to);
   next()
  },
};
</script>

<style>
</style>

history和hash 路由器的工作模式

对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值

hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器

hash模式:

  • 地址中永远带着#号,不美观
  • 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法
  • 兼容性较好

history模式:

  • 地址干净,美观
  • 兼容性和hash模式相比略差
  • 应用部署上线时需要后端人员去解决刷新页面服务端404的问题

新开一个终端,生成一个dist文件夹,npm run build,这个文件就是后端需要的东西。

新建一个文件夹,比如叫demo,

在终端输入npm init变成一个合法的包,在中断中会出现一个起名字,atguigu_test_server 回车回车 (若干)
输入npm i express

左边新建一个server.js,在里面写入代码

再新建一个static文件夹,把脚手架生成的东西加进去,引入到server,然后停到服务器,重新启动,可以在网站上直接访问
至于一些错误,后端解决,播放链接
https://www.bilibili.com/video/BV1Zy4y1K7SH?p=133
请添加图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值