Vue-CLI笔记

笔记

脚手架文件结构:

|—— node_modules
|—— public
|	|—— favicon.ico:页签图标
|  	|—— index.html:主页面
|—— src
|	|—— assets:存放静态资源
|	|	|—— logo.png
|	|—— component:存放组件
|	|	|—— HelloWorld.vue
|	|—— App.vue:汇总所有组件
|	|—— main.js:入口文件
|—— .gitignore:git版本管理忽略的配置
|—— babel.config.js:babel的配置文件
|—— package.json:应用包配置文件
|—— README.md:应用描述文件
|—— package-lock.json:包版本控制文件

关于不同版本的Vue

  • vue.js与vue.runtime.xxx.js的区别:

    (1)vue.js是完整的Vue,包含:核心功能+模板编译器

    (2)vue.runtime.xxx.js是运行版的Vue,只包含:核心功能,没有模板编译器

  • 因为veu.runtime.xxx.js没有模板编译器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容

vue.config.js配置文件

使用vue inspect>output.js可以查看脚手架的默认配置

使用veu.config.js可以对脚手架进行个性化定制,详情见:https://cli.vuejs.org/zh

ref属性

  1. 被用来给元素或子组件注册引用信息(id的替代者)
  2. 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
  3. 使用方式:
    打标识:


    获取:this.$refs.xxx

配置项props

功能:让组件接收外部传过来的数据
(1)传递数据:

(2)接收数据:
第一种方式(只接受):
props:[‘name’,‘age’]

第二种方式(限制类型):
  props:{
    name:String,
    age:Nember
  }

第三种方式(限制类型、限制必要性、指定默认值):
  props:{
    name:{
      type:String,
      required:true,
      default:'张三'
    }
  }

备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发生警告
如果你想要修改传过来的数据,可以赋值给data内,再修改data内的数据

mixin(混入)

功能:可以把多个组件共用的配置提取成一个混入对象
使用方式:
第一步定义混合,例如:
{
data(){…},
methods:{…}

}
第二步使用混入,例如:
(1)全局混入:Vue.mixin(xxx)
(2)局部混入:mixins:[‘xxx’]

插件

功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据
定义插件:
对象.install = function(Vue,options){
// 1.添加全局过滤器
Vue.filter(…)

 // 2.添加全局指令
 Vue.directive(...)

 // 3.配置全局混入(合)
 Vue.mixin(...)

 // 4.添加实例方法
 Vue.prototype.$myMethod = function(){...}
 Vue.prototype.$myProperty = xxx

}
使用插件:Vue.use(plugins)

scoped样式

作用:让样式在局部生效,防止冲突
写法:

Todo-List 案例

组件化编码流程

  1. 拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突
  2. 实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一堆组件再用
  • 一个组件在用:放在组件自身即可
  • 一堆组件在用:放在他们共同的父组件上(状态提升)
  1. 实现交互:从绑定事件开始

**props**适用于

​ a.父组件 ==> 子组件 通信

​ b.子组件 ==> 父组件 通信(要求父组件先給子组件一个函数)

使用**v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的,props传过来的值若是对象数据类型的值,修改对象中的属性时Vue**不会报错,但不推荐使用

src/App.vue

<template>
  <div id="root">
    <div class="todo-container">
      <div class="todo-wrap">
        <MyHeader :addTodo="addTodo"/>
        <MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>
        <MyFooter :todos="todos" 
                  :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/>
    	</div>
    </div>
  </div>
</template>

<script>
  import MyHeader from './components/MyHeader'
  import MyList from './components/MyList'
  import MyFooter from './components/MyFooter.vue'

  export default {
    name: 'App',
    components: { MyHeader, MyList, MyFooter },
    data() {
      return {
        // 由于todos是MyHeader组件和MyFooter组件都在使用,所以放在App中(状态提升)
        todos:[
          {id:'001',title:'抽烟',done:true},
          {id:'002',title:'喝酒',done:false},
          {id:'003',title:'开车',done:true}
        ]
      }
    },
    methods: {
      //添加一个todo
      addTodo(todoObj){
        this.todos.unshift(todoObj)
      },
      //勾选or取消勾选一个todo
      checkTodo(id){
        this.todos.forEach((todo)=>{
          if(todo.id === id) todo.done = !todo.done
        })
      },
      //删除一个todo
      deleteTodo(id){
        this.todos = this.todos.filter( todo => todo.id !== id )
      },
      //全选or取消全选
      checkAllTodo(done){
        this.todos.forEach((todo)=>{
          todo.done = done
        })
      },
      //清除所有已经完成的todo
      clearAllTodo(){
        this.todos = this.todos.filter((todo)=>{
          return !todo.done
        })
      }
    }
  }
</script>

<style>
  /*base*/
  body {background: #fff;}
  .btn {display: inline-block;padding: 4px 12px;margin-bottom: 0;font-size: 14px;
    line-height: 20px;text-align: center;vertical-align: middle;cursor: pointer;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
    border-radius: 4px;}
  .btn-danger {color: #fff;background-color: #da4f49;border: 1px solid #bd362f;}
  .btn-danger:hover {color: #fff;background-color: #bd362f;}
  .btn:focus {outline: none;}
  .todo-container {width: 600px;margin: 0 auto;}
  .todo-container .todo-wrap {padding: 10px;border: 1px solid #ddd;border-radius: 5px;}
</style>

src/components/MyHeader.veu

<template>
	<div class="todo-header">
		<input type="text" placeholder="请输入你的任务名称,按回车键确认" 
           v-model="title" @keyup.enter="add"/>
	</div>
</template>

<script>
	import {nanoid} from 'nanoid'
	export default {
		name:'MyHeader',
		props:['addTodo'],	// 接收从App传递过来的addTodo
		data() {
			return {
				title:''				// 收集用户输入的title
			}
		},
		methods: {
			add(){
				// 校验数据
				if(!this.title.trim()) return alert('输入不能为空')
				// 将用户的输入包装成一个todo对象
				const todoObj = { id:nanoid(), title:this.title, done:false }
				// 通知App组件去添加一个todo对象
				this.addTodo(todoObj)
				// 清空输入
				this.title = ''
			}
		},
	}
</script>

<style scoped>
	/*header*/
	.todo-header input {width: 560px;height: 28px;font-size: 14px;
    border: 1px solid #ccc;border-radius: 4px;padding: 4px 7px;}
	.todo-header input:focus {outline: none;border-color: rgba(82, 168, 236, 0.8);
		box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);}
</style>

src/components/MyList.vue

<template>
  <ul class="todo-main">
    <MyItem v-for="todoObj in todos":key="todoObj.id" 
            :todo="todoObj" :checkTodo="checkTodo":deleteTodo="deleteTodo"/>
  </ul>
</template>

<script>
  import MyItem from './MyItem'

  export default {
    name:'MyList',
    components:{MyItem},
    // 声明接收App传递的数据,其中todos是自己用的,checkTodo和deleteTodo是给子组件MyItem用的
    props:['todos','checkTodo','deleteTodo']
  }
</script>

<style scoped>
  /*main*/
  .todo-main {margin-left: 0px;border: 1px solid #ddd;border-radius: 2px;padding: 0px;}
  .todo-empty {height: 40px;line-height: 40px;border: 1px solid #ddd;
    border-radius: 2px;padding-left: 5px;margin-top: 10px;}
</style>

src/components/MyItem.vue

<template>
  <li>
    <label>
      <!-- 如下代码也能实现功能,但是不太推荐,因为有点违反原则,因为修改了props -->
      <!-- <input type="checkbox" v-model="todo.done"/> -->
      <input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/>  
      <span>{{ todo.title }}</span>
    </label>
    <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button>
  </li>
</template>

<script>
  export default {
    name:'MyItem',
    //声明接收todo、checkTodo、deleteTodo
    props:['todo','checkTodo','deleteTodo'],
    methods: {
      // 勾选or取消勾选
      handleCheck(id){
        this.checkTodo(id)	// 通知App组件将对应的todo对象的done值取反
      },
      // 删除
      handleDelete(id){
        if(confirm('确定删除吗?')){
          this.deleteTodo(id)	// 通知App组件将对应的todo对象删除
        }
      }
    },
  }
</script>

<style scoped>
  /*item*/
  li {list-style: none;height: 36px;line-height: 36px;padding: 0 5px;
    border-bottom: 1px solid #ddd;}
  li label {float: left;cursor: pointer;}
  li label li input {vertical-align:middle; margin-right:6px; position:relative;top: -1px;}
  li button {float: right;display: none;margin-top: 3px;}
  li:before {content: initial;}
  li:last-child {border-bottom: none;}
  li:hover{background-color: #ddd;}
  li:hover button{display: block;}
</style>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qLMaHPI5-1661310261360)(C:\Users\11465\AppData\Roaming\Typora\typora-user-images\image-20220814220407532.png)]

webStorage

1.存储内容大小一般支持5MB左右(不同浏览器可能还不一样)

2.浏览器端通过**Window.sessionStorageWindow.localStorage**属性来实现本地存储机制

3.相关API:

​ 1.xxxx.Storage.setItem('key','value);

​ 该方法接受一个键值对作为参数,会把键值对添加到存储中,如果键,名存在,则更新其对应的值

​ 2.xxxx.Storage.getItem('key');

​ 该方法接受一个键值对作为参数,返回键名对应的值

​ 3.xxxx.Storage.removeItem('key');

​ 该方法接受一个键值对作为参数,并把该键名从存储中删除

​ 4.xxxx.Storage.clear();

​ 该方法会清空存储中所有的数据

备注

1.sessionStorage存储的内容会随着浏览器窗口关闭而消失

2.localStorage存储的内容,需要手动清除才会消失

3.xxxxStorage.getItem('key')如果key对应的value获取不到,那么getItem的返回值是null

4.JSON.parse(null)的结果还是null

组件的自定义事件

1.一种组件间通信的方式,适用于:子组件 ----> 父组件

2.使用场景:子组件想给父组件传数据,那么就要在父组件中给子组件绑定自定义事件(事件的回调在A中)

3.绑定自定义事件

​ a.第一种方式:在父组件中**<Demo @事件名='回调'/><Demo v-on:事件名='回调'/>**

​ b.第二种方式:在父组件中this.$refs.demo.$on('事件','回调')

<Demo ref='demo'/>
    ...
mounted(){
    this.$ref.demo.$on('atguigu',this.test)
}

​ c.若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法

​ 1.触发自定义事件this.$emit('事件名','数据')

​ 2.解绑自定义事件this.$off('事件名')

​ 3.组件上也可以绑定原生DOM事件,需要使用native修饰符 @click.native='show'

上面绑定自定义事件,即使绑定的是原生事件也会被认为是自定义的,需要叫native,加了后就将此事件给组件的根元素

注意:通过this.$refs.demo.$on('事件名','回调')绑定自定义事件时,回调函调要么配置在methods中,要么用箭头函数,否则this指向会出问题

全局事件总线(GlobalEventBus)

  1. 一种组件间通信的方式,适用于任意组件间通信

  2. 安装全局事件总线:

    new Vue({
        .....
        beforeCreate(){
        Vue.prototype.$bus = this	//安装全局事件总线,$bus就是当前应用的vm
    	},
        .....
    })
    
  3. 使用事件总线:

    1. 接受数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。

      methods(){
      	demo(data){.....}
      }
      ......
      mounted(){
      	this.$bus.$on('xxx',this.demo)
      }
      
    2. 提供数据:this.$bus.$emit('xxx',数据)

  4. 最好在beforeDestroy钩子中,用$off去解绑当前组件所绑定的事件。

$nextTick

这是一个生命周期钩子

this.$nextTick(回调)在下一次DOM更新结束后执行其指定的回调

什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行

过渡与动画

Vue封装的过渡与动画:在插入、更新或移除DOM元素时,在合适的时候给元素添加样式类名

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E9mG1fjD-1661310261367)(C:\Users\11465\AppData\Roaming\Typora\typora-user-images\image-20220816104330293.png)]

写法

  1. 准备好样式
    • 元素进入的样式
      • v-enter 进入的起点
      • v-enter-active 进入过程中
      • v-enter-to 进入的终点
    • 元素离开的样式
      • v-leave 离开的起点
      • v-leave-active 离开过程中
      • v-leave-to 离开的终点
  2. 使用 包裹要过渡的元素,并配置 name属性,此时需要将上面样式名的v换为name
  3. 要让页面一开始就显示动画,需要添加appear
<transition name='hello' :appear='true'>
    <h1 v-show='isShow'>你好啊</h1>
</transition>

<style>
    .hello-enter-active {
        animation: hello 1s linear;
    }
    .hello-leave-active {
        animation: hello 1s linear reverse;
    }
    //动画帧
    @keyframes hello {
        from {
            transform: translateX(-100%);
        }
        to {
            transform: translateX(0px);
        }
    }
</style>
  1. 备注:若有多个元素需要过渡,则需要使用==,且每一个元素都要指定key==值

    <transition-group name='hello' :appear='true'>
        <h1 v-show='isShow' key='1'>你好啊</h1>
        <h1 v-show='!isShow' key='2'>尚硅谷</h1>
    </transition-group>
    
  2. 第三方动画库 Animate.css

Vue中的Ajax配置代理slot插槽

1.Vue脚手架配置代理

本案例需要下载 axiosnpm i axios

配置参考文档 Vue-Cli devServer.proxy

vue.config.js是一个可选的配置文件,如果项目的(和 package.json同级的)根目录中存在这个文件,那么它会被 @vue/cli-service自动加载。你也可以使用package.json中的Vue字段,但是注意这种写法需要你严格遵照JSON的格式来写

方法一

​ 在vue.config.js中添加如下配置

module.exports = {
    devServer:{
        proxy:'http://localhost:5000'
    }
}

说明

  1. 优点:配置简单,请求资源时直接发给前端(8080)即可
  2. 缺点:不能配置多个代理,不能灵活的控制请求是否走代理
  3. 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,才会将请求会转发给服务器(优先匹配前端资源)
方法二

​ 编写vue.config.js配置具体代理规则

module.exports = {
    devServer:{
		proxy:{
		'/api1':{						//匹配所有以'/api1'开头的请求路径
		target:'http://localhost:5000', //代理目标的基础路径
		pathRewrite:{'^/api1':''},      //代理往后端服务器的请求去掉 /api1 前缀
		ws:true,						//WebSocket
		changeOrigin:true,
		},
		'/api2':{
		target:'http://localhost:5001',
		pathRewrite:{'^/api2':''}, 
		ws:true,	
		changeOrigin:true,
		}
	}
   }
}
/*
	changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
	changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
	changeOrigin默认值为true
*/

说明

  1. 优点:可以配置多个代理,且可以灵活的控制请求是否走代理
  2. 缺点:配置略微繁琐,请求资源时必须加前缀
module.exports = {
	pages:{
		index:{
			entry:'src/main.js',
		}
	},
	lintOnsave:false,
	// 开启代理服务器(方式1)
	//  devServer:{
    //    proxy:'http://localhost:5000'
    // }
	// 开启代理服务器(方式2)
     devServer:{
            proxy:{
            '/api1':{					
            target:'http://localhost:5000', 
            pathRewrite:{'^/api1':''},     
            // ws:true,	//用于支持webSocket,默认值为true				
            // changeOrigin:true, //用于控制请求头中host值,默认值为true
            },
            '/api2':{
            target:'http://localhost:5001',
            pathRewrite:{'^/api2':''}, 
            }
        }
    }
}

src/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/demo/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>

。。。。。。。

Vuex

5.1.理解Vuex
5.1.1.Vuex是什么
  1. 概念:专门在Vue中实现集中式状态(数据)管理的一个Vue插件,对Vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信
  2. Vuex Github地址

img

img

5.1.2.什么时候使用 Vuex
  1. 多个组件依赖同一状态
  2. 来自不同组件的行为需要变更同一状态
5.1.3.Vuex工作原理图

image.png

5.2.求和案例
5.2.1.使用纯 vue 编写

src/App.vue

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

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

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

src/components/Count.vue

<template>
  <div>
    <h2>当前求和为:{{ sum }}</h2>
    <select 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-left: 5px;}
</style>
5.2.2.搭建 Vuex 环境
  1. 下载安装 vuex npm i vuex@3(适配vue2)

  2. 创建 src/store/index.js该文件用于创建 Vuex 中最核心的 store

    import Vue from 'vue'
    import Vuex from 'vuex'
    // 使用Vuex插件
    Vue.use(Vuex)
    
    const actions = {} // 准备actions--用于响应组件中的动作
    const nutations = {} // 准备mutations--用于操作数据
    const state = {} // 准备state--用于存储数据
    
    // 创建并暴露store
    export default new Vuex.store({
        actions,
        mutations,
        state
    })
    
    
  3. src/main.js中创建 vm 时传入 store 配置项

    import Vue from 'vue'
    import App from './App.vue'
    import store from './store' // 引入store
    
    Vue.config.productionTip = false // 关闭生产提示
    new Vue({
        el:'#app',
        render:h => h(App),
        store,
        beforeCreate(){
            Vue.prototype.$bus = this
        }
    })
    
5.2.3. 使用 Vuex 编写

Vuex的基本使用

  1. 初始化组件数据 state,配置 actionsmutations、操作文件 store.js

  2. 组件中读取 vuex 中的数据 $store.state.数据

  3. 组件中修改 vuex 中的数据 $store.dispatch('actions中的方法名',数据)$store.commit('mutations中的方法名',数据)

    若没有网络请求或其他业务需求,组件中也可以跳过 actions,即不写 dispatch,直接编写 commit

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

    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex) // 应用Vuex插件
    
    // 准备actions--用于响应组件中的动作
    const actions = {
        /* jia(context,value){
            console.log('actions中的jia被调用了')
            context.commit('JIA',value)
        },
        jian(context,value){
            console.log('actions中的jian被调用了')
            context.commit('JIAN',value)
        }, */
        jiaOdd(context,value){
            console.log('actions中的jiaOdd被调用了')
            if(value%2){
                context.commit('JIA',value)
            }
        },
        jiaWait(context,value){
            console.log('actions中的jiaWait被调用了')
            setTimeout(()=>{
                context.commit('JIA'.value)
            },500)
        }   
    }
    
    // 准备mutations--用于操作数据
    const mutations = {
        JIA(state,value){
            state.sum += value
        },
        JIAN(state,value){
            state.sum -= value
        }
    }
    
    // 准备state--用于存储数据
    const state = {
        sum:0,
    }
    
    // 创建并暴露store
    export default new Vuex.Store({
        actions,
        mutations,
        state
    }
    

    src/components/count.vue

    <template>
    	<div>
    		<h1>当前求和为:{{ $store.state.sum }}</h1>
    		<select 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.commit('JIA',this.n)
    			},
    			decrement(){
    				this.$store.commit('JIAN',this.n)
    			},
    			incrementOdd(){
    				this.$store.dispatch('jiaOdd',this.n)
    			},
    			incrementWait(){
    				this.$store.dispatch('jiaWait',this.n)
    			},
    		}
    	}
    </script>
    
    <style lang="css">button{margin-left: 5px;}</style>
    
    
5. 3. getters 配置项
  1. 概念:当 state 中的数据需要加工后使用时,可以使用 getters 加工,相当于 全局计算属性

  2. store.js 中追加 getters 配置

    .....
    
    const getters = {
        bigSum(){
            retuen state.sum * 10
        }
    }
    
    // 创建并暴露store
    export default new Vuex.Store({
        .....
        getters
    })
    
  3. 组件中读取数据 $store.getters.bigSum

image.png

src/store/index.js

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

// 准备actions--用于响应组件的动作
const actions = {
    addOdd(context,value){
        console.log('action中的addOdd被调用了')
        if(value%2){
            context.commit('JIA',value)
        }
    },
    addWait(context,value){
        console.log('actions中的addWait被调用了')
        setTimeout(()=>{
            context.commit('JIA',value)
        },500)
    }
}

// 准备mutations--用于操作数据
const mutations = {
    JIA(state,value){
        state.sum += value
    },
    JIAN(state,value){
        state.sum -= value
    }
}

// 准备state--用于存储数据
const state = {
    sum:0,
}

// 准备getters--用于将state中的数据进行加工
const getters = {
    bigSum(){
        return state.sum * 10
    }
}

// 创建并暴露store
export default new Vuex.Store({
    actions,
    mutations,
    state,
    getters
})

src/components/Count.vue

<template>
	<div>
		<h1>当前求和为:{{ $store.state.sum }}</h1>
		<h3>当前求和的10倍为:{{ $store.getters.bigSum }}</h3>
		<select 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.commit('JIA',this.n)},
			decrement(){this.$store.commit('JIAN',this.n)},
			incrementOdd(){this.$store.dispatch('addOdd',this.n)},
			incrementWait(){this.$store.dispatch('addWait',this.n)},
		},
	}
</script>

<style>button{margin-left: 5px;}</style>
5.4. 四个 map 方法的使用
  1. mapState方法:用于帮助映射 state 中的数据为计算属性

    src/components/Count.vue

    .....
    computed:{
    	/* sum(){
    		return $store.state.sum
    	},
    	school(){
    		return $store.state.school
    	},
    	subject(){
    		return $store.state.subject
    	}, */
    	// 借助mapState生成计算属性:sum、school、subject(对象写法)
    	...mapState({sum:'sum',school:'school',subject:'subject'}),
    	// 借助mapState生成计算属性:sum、school、subject(数组写法)
    	...mapState(['sum','school','subject']),
    }
    
  2. mapGetters方法:用于帮助映射 getters 中的数据为计算属性

    src/components/Count.vue

    .....
    computed:{
    	/* bigSum(){
    		return $store.getters.bigSum
    	} */
    	// 借助mapGetters生成计算属性:bigSum(对象写法)
    	...mapGetters({bigSum:'bigSum'}),
    	// 借助maGetters生成计算属性:bigSum(数组写法)
    	...mapState(['bigSum']),
    }
    
  3. mapActions方法:用于帮助生成与 actions 对话的方法,即包含 $store.dispatch(xxx) 的函数

    src/components/Count.vue

    .....
    methods:{
    	/* incrementOdd()
    		this.$store.dispatch('addOdd',this.n)
    	},
    	incrementWait(){
    		this.$store.dispatch('addWait',this.n)
    	}, */
    	// 借助mapActions生成:incrementOdd、incrementWait(对象形式)
    	...mapActions({incrementOdd:'addOdd',incrementWait:'addWait'}),
    	// 借助mapActions生成:incrementOdd、incrementWait(数组形式)
    	...mapActions(['addOdd','addWait']),
    }
    
  4. mapMutations方法:用于帮助生成与 mutations 对话的方法,即包含 $store.commit(xxx) 的函数

    src/components/Count.vue

    .....
    methods:{
    	/* increment(){
    		this.$store.commit('JIA',this.n)
    	},
    	decrement(){
    		this.$store.commit('JIAN',this.n)
    	}, */
    	// 借助mapMutations生成:increment、decrement(对象形式)
    	...mapMutations({increment:'JIA',decrement:'JIAN'}),
    	// 借助mapActions生成:increment、decrement(数组形式)
    	...mapMutations(['JIA','JIAN']),
    }
    

    注意:mapActionsmapMutations 使用时,若需要传递参数:在模板中绑定事件时传递好参数,否则参数是事件对象

    src/components/Count.vue

    <template>
    	<div>
    		<h1>当前求和为:{{ sum }}</h1>
    		<h3>当前求和的10倍为:{{ bigSum }}</h3>
    		<h3>我是{{ name }},我在{{ school }}学习</h3>
    		<select v-model.number="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="addOdd(n)">当前求和为奇数再加</button>
    		<button @click="addWait(n)">等一等再加</button>
    	</div>
    </template>
    
    <script>
        import {mapState,mapGetters,mapActions,mapMutations} from 'Vuex'
        
        export default {
            name:'App',
            data(){
                return {
                    n:0, //用户选择的数字
                }
            },
            computed:{
                ...mapState(['sum','school','name']),
                ...mapGetters(['bigSum'])
            }
            methods:{
       			...mapActions({incrementOdd:'addOdd',incrementWait:'addWait'}),
                ...mapMutations({increment:'JIA',decrement:'JIAN'}),
            }
        }
    </script>
    
    <style>
    	button{
    		margin-left: 5px;
    	}
    </style>
    
5.5. 多组共享数据案例

image.png

src/App.vue

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

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

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

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

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

Vue.use(Vuex)

const actions = {
	jiaOdd(context,value){
		console.log('actions中的jiaOdd被调用了')
		if(context.state.sum % 2){
			context.commit('JIA',value)
		}
	},
	jiaWait(context,value){
		console.log('actions中的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
	},
	ADD_PERSON(state,value){
		console.log('mutations中的ADD_PERSON被调用了')
		state.personList.unshift(value)
	}
}

//准备state——用于存储数据
const state = {
	sum: 0,
	school: '尚硅谷',
	subject: '前端',
	personList: []
}

//准备getters——用于将state中的数据进行加工
const getters = {
	bigSum(state){
		return state.sum*10
	}
}

//创建并暴露store
export default new Vuex.Store({
	actions,
	mutations,
	state,
	getters
})

src/components/Count.vue

<template>
	<div>
		<h1>当前求和为:{{ sum }}</h1>
		<h3>当前求和放大10倍为:{{ bigSum }}</h3>
		<h3>我在{{ school }},学习{{ subject }}</h3>
		<h3 style="color:red">Person组件的总人数是:{{ personList.length }}</h3>
		<select v-model.number="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 {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
	export default {
		name:'Count',
		data() {
			return {
				n:1, //用户选择的数字
			}
		},
		computed:{
			...mapState(['sum','school','subject','personList']),
			...mapGetters(['bigSum'])
		},
		methods: {
			...mapMutations({increment:'JIA',decrement:'JIAN'}),
			...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
		}
	}
</script>

<style lang="css">button{margin-left: 5px;}</style>

src/components/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 personList" :key="p.id">{{ p.name }}</li>
		</ul>
	</div>
</template>

<script>
	import {nanoid} from 'nanoid'
  import { mapState } from "vuex"
  
	export default {
		name:'Person',
		data() {
			return {
				name:''
			}
		},
		computed:{
			personList(){return this.$store.state.personList},
			sum(){return this.$store.state.sum}
		},
		methods: {
			add(){
        if (this.name === "") return
				const personObj = {id:nanoid(),name:this.name}
				this.$store.commit('ADD_PERSON',personObj)
				this.name = ''
			}
		},
	}
</script>
5.6. 模块化+命名空间
  1. 目的:让代码更好维护,让多种数据分类更加明确

  2. 修改 store.js

    为了解决不同模块命名冲突问题,将不同模块的 namespaced:true,之后在不同页面中引入 getters actions mutations 时,需要加上所属的模块名

    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
      }
    })
    
  3. 开启命名空间后,组件中读取 state 数据

    // 方式1:自己直接读取
    this.$store.state.personAbout.list
    // 方式2:借助mapState读取
    ...mapState('countAbout',['sum','school','subject'])
    
  4. 开启命名空间后,组件中读取 getters 数据

    // 方式1:自己直接读取
    this.$store.getters['personAbout/firstPersonName']
    // 方式2:借助mapGetters读取
    ...mapGetters('countAbout',['bigSum'])
    
  5. 开启命名空间后,组件中调用 dispatch

    // 方式1:自己直接dispatch
    this.$store.dispatch('personAbout/addPersonwang',person)
    // 方式2:借助mapActions
    ...mapActions('counAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    
  6. 开启命名空间后,组件中调用 commit

    // 方式1:自己直接commit
    this.$store.commit('personAbout/ADD_PERSON',person)
    // 方式2:借助mapcommit
    ...mapcommit('counAbout',{increment:'JIA',decrement:'JIAN'})
    

    img

Vue Router 相关理解 基本路由 多级路由

6.1 相关理解
6.1.1 vue-router 的理解
  • vue 的一个插件库,专门用来实现 SPA 应用
6.1.2 对SPA应用的理解
  1. 单页 Web 应用(single page web application,SPA

  2. 整个应用只有一个完整的页面

  3. 点击页面的导航链接不会刷新页面,只会做页面的局部更新

  4. 数据需要通过 ajax 请求获取

    image.png

6.1.3 路由的理解
  1. 什么是路由?

    a. 一个路由就是一组映射关系(key-value)

    b. key 为路径,value 可能是 functioncomponent

  2. 路由分类

    a. 后端路由

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

    b. 前端路由

    1. 理解:valuecomponent,用于展示页面内容
    2. 工作过程:当浏览器的路径改变时,对应的组件就会显示
6.2 基本路由
  1. 安装 vue-routernpm i vue-router@3(和vue2对应)

  2. 应用插件 Vue.use(VueRouter)

  3. 编写 router 配置项

    import VueRouter from 'vue-router'  // 引入VueRouter
    import About from '../components/About' // 路由组件
    import Home from '../components/About' // 路由组件
    
    // 创建router实例对象,去管理一组一组的路由规则
    const router = new VueRouter({
        routers:[
            {
                path:'/about',
                component:About
            },
            {
                path:'/home',
                component:Home
            }
        ]
    })
    
    // 暴露router
    export default router
    
  4. 实现切换

    <router-link></router-link> 浏览器会替换为 a 标签

    active-class 可配置高亮样式

    <router-link active-class='active' to='/about'>About</router-link>
    
  5. 指定展示位置 <router-view></router-view>

    image.png

src/router/index.js 该文件用于创建整个应用的路由器

import VueRoutre from 'vue-router'
import About from '../compontents/About'
import Home from '../components/Home'

export default new VueRouter({
    routers:[
        {
            path:'/about',
            compontent:About
        },
        {
            path:'/home',
            component:Home
        }
    ]
})

src/main.js

import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router' // 引入VueRouter插件
import router from './router' // 引入router路由器

Vue.config.productionTip = false

Vue.use(VueRouter) // 引用插件

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

src/App.vue

<template>
  <div>
    <div class="row">
      <div class="col-xs-offset-2 col-xs-8">
        <div class="page-header"><h2>Vue Router Demo</h2></div>
      </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>

src/components/Home.vue

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

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

src/components/About.vue

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

<script>
    export default {
        name:'About'
    }
</script>
6.3. 几个注意事项
  1. 路由组件通常存放在 pages 文件夹,一般组件通常存放在 components 文件夹

    比如上一节的案例就可以修改为

    src/pages/Home.vue

    src/pages/About.vue

    src/router/index.js

    src/compontents/Banner.vue

    src/App.vue

  2. 通过切换,”隐藏“了的组件,默认是被销毁掉的,需要的时候在去挂载

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

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

    src/router/index.js

    // 该文件专门用于创建整个应用的路由器
    import VueRouter from 'vue-router'
    import Home from '../pages/Home'
    import About from '../pages/About'
    
    export default new VueRouter({
        routers:[
            {
                path:'/home',
                componen:Home
            },
            {
                path:'/about',
                component:About
            }
          
        ]
    })
    

    src/components/Banner.vue

    <template>
    	<div class='col-xs-offset-2 col-xs-8'>
    		<div class='page-header'><h2>Vue Router Demo</h2></div>
    	</div>
    </template>
    
    <script>
    	export default {
    		name:'Banner',
    	}
    </script>
    

    src/App.vue

    <template>
      <div>
        <div class="row">
          <Banner/>
        </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>
    
6.4. 多级路由
  1. 配置路由规则,使用 children 配置项

    src/router/index.js

    import VueRouter from 'vue-router'
    import Home from '../pages/Home'
    import About from '../pages/About'
    import News from '../pages/News'
    import Message from '../pages/Message'
    
    export default new VueRouter({
        routers:[
            {
                path:'/home',
                component:Home,
                children:[  // 通过children配置子级路由
                    {
                        path:'news',  // 此处一定不要带 / ,因为会自动带上
                        component:News
                    },
                    {
                        path:'message',
                        component:Message
                    }
                ]
            },
            {
                path:'/about',
                component:About
            }
        ]
    })
    
  2. 跳转(要写完整路径)

    <router-link to='/home/news'>News</router-link>
    

image.png

src/pages/Home.vue

<template>
	<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>
</template>

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

src/pages/News.vue

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

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

src/pages/Message.vue

<template>
    <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>
</template>

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

src/router/index.js

import VueRouter from 'vue-router'
import Home from '../pages/Home'
import About from '../pages/About'
import News from '../pages/News'
import Message from '../pages/Message'

export default new VueRouter({
    routers:[
        {
            path:'/home',
            component:Home,
            children:[  // 通过children配置子级路由
                {
                    path:'news',  // 此处一定不要带 / ,因为会自动带上
                    component:News
                },
                {
                    path:'message',
                    component:Message
                }
            ]
        },
        {
            path:'/about',
            component:About
        }
    ]
})

Vue Router query 命名路由 params props

6.5. 路由的 query 参数
  1. 传递参数

    <!-- 跳转并携带query参数,to的字符串写法 -->
    <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">跳转</router-link>
    
    <!-- 跳转并携带query参数,to的对象写法(推荐)-->
    <router-link 
         :to="{
             path:'/home/message/detail',
             query:{
              	id:m.id,
              	title:m.title
              }}">跳转</router-link>
    
  2. 接收参数

    $route.query.id
    $route.query.title
    

image.png

src/router/index.js

import VueRouter from 'vue-router'
import Home from '../pages/Home'
import About from '../pages/About'
import News from '../pages/News'
import Message from '../pages/Message'
import Detail from '../pages/Detail'

export default new VueRouter({
    routes:[
        {
            path:'/about',
            component:About
        },
        {
            path:'/home',
            component:Home,
            children:[
                {
                    path:'news',
            		component:News
                },
                {
                    path:'message',
            		component:Message,
                    children:[
                        {
                            path:'detail',
                            component:Detail
                        }
                    ]
                }
            ]
        },
    ]
})

src/pages/Message.vue

<template>
	<div>
        <ul>
            <li v-for="m in messageList" :key="m.id">
                <!-- 跳转并携带query参数,to的字符串写法 -->
                <!-- <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{m.title}}</router-link>&nbsp;&nbsp; -->
                
                <!-- 跳转并携带query参数,to的对象写法 -->
                <router-link 
                        :to="{
                             path:'/home/message.detail',
                             query:{
                             		id:m.id,
                             		title:m.title
                             	}     
                             }">{{m.title}}</router-link>&nbsp;&nbsp;
    </li>
    </ul>
    <hr/>
        <router-view></router-view>
    </div>
</template>

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

src/pages/Detail.vue

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

<script>
    export default {
        name:'Detail'
    }
</script>
6.6. 命名路由

命名路由

  1. 作用:可以简化路由的跳转(to)

  2. 如何使用

    a. 给路口命名

    {
        path:'/demo',
        component:Demo,
        children:[
            {
                path:'test',
                component:Test,
                children:[
                    {
                        name:'hello',  // 给路由命名
                        path:'welcome',
                        component:Hello,
                    }               
                ]
            }       
        ]
    }
    

    b. 简化跳转

    <!-- 简化前,需要写完整的路径 -->
    <router-link to="/demo/test/welcome">跳转</router-link>
    
    <!-- 简化后,直接通过名字跳转 -->
    <router-link :to="{name:'hello'}">跳转</router-link>
    
    <!-- 简化写法配合传递参数 -->
    <router-link :to="{
                      name:'hello',
                      query:{
                      	id:666,
                      	title:'你好'
                      }
                      }">跳转
    </router-link>
    

    src/router/index.js

    import VueRouter from "vue-router";
    import Home from '../pages/Home'
    import About from '../pages/About'
    import News from '../pages/News'
    import Message from '../pages/Message'
    import Detail from '../pages/Detail'
    
    export default new VueRouter({
        routes:[
            {
                path:'/about',
                component:About
            },
            {
                path:'/home',
                component:Home,
                children:[
                    {
                        path:'news',
                        component:News
                    },
                    {
                        path:'message',
                        component:Message,
                        children:[
                            {
                                name:'detail',	// name配置项为路由命名
                                path:'detail',
                                component:Detail
                            }
                        ]
                    }
                ]
            }
        ]
    })
    

    src/pages/Message.vue

    <template>
        <div>
            <ul>
                <li v-for="m in messageList" :key="m.id">
                    <!-- 跳转路由并携带query参数,to的字符串写法 -->
                    <!-- <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">
                        {{m.title}}
                    </router-link>&nbsp;&nbsp; -->
    
                    <!-- 跳转路由并携带query参数,to的对象写法 -->
                    <router-link :to="{
                        name:'detail',	//使用name进行跳转
                        query:{
                            id:m.id,
                            title:m.title
                        }
                    }">
                        {{m.title}}
                    </router-link>&nbsp;&nbsp;
                </li>
            </ul>
            <hr/>
            <router-view></router-view>
        </div>
    </template>
    
    <script>
        export default {
            name:'News',
            data(){
                return{
                    messageList:[
                        {id:'001',title:'消息001'},
                        {id:'002',title:'消息002'},
                        {id:'003',title:'消息003'}
                    ]
                }
            }
        }
    </script>
    
    6.7. 路由的 params 参数
    1. 配置路由,声明接收 params 参数

      {
          path:'/home',
          component:Home,
          children:[
              {
                  path:'news',
             	 	component:News
              },
              {
                  path:'message',
                  component:Message,
                  children:[
                      {
                          name:'xiangqing',
                          path:'detail/:id/:title',  // 使用占位符声明接受params参数
                          component:Detail
                      }
                  ]
              }
              
          ]
      }
      
    2. 传递参数

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

      <!-- 跳转并携带params参数,to的字符串写法 -->
      <router-link :to="`/home/message/detail/666/你好`">跳转</router-link>
      
      <!-- 跳转并携带params参数,to的对象写法 -->
      <router-link
           :to="{
                name:'xiangqing',
                params:{
                	id:666,
                	title:'你好'
                }
                }">跳转
      </router-link>
      
    3. 接受参数

      $route.params.id
      $route.params.title
      

      src/router/index.js

      import VueRouter from "vue-router";
      import Home from '../pages/Home'
      import About from '../pages/About'
      import News from '../pages/News'
      import Message from '../pages/Message'
      import Detail from '../pages/Detail'
      
      export default new VueRouter({
          routes:[
              {
                  path:'/about',
                  component:About
              },
              {
                  path:'/home',
                  component:Home,
                  children:[
                      {
                          path:'news',
                          component:News
                      },
                      {
                          path:'message',
                          component:Message,
                          children:[
                              {
                                  name:'xiangqing',
                                  path:'detail/:id/:title',	// 使用占位符声明接收params参数
                                  component:Detail
                              }
                          ]
                      }
                  ]
              }
          ]
      })
      

      src/pages/Message.vue

      <template>
          <div>
              <ul>
                  <li v-for="m in messageList" :key="m.id">
                    
                      <!-- 跳转路由并携带params参数,to的字符串写法 -->
                      <!-- <router-link :to="`/home/message/detail/${m.id}/${m.title}`">
                          {{m.title}}
                      </router-link>&nbsp;&nbsp; -->
      
                      <!-- 跳转路由并携带params参数,to的对象写法 -->
                      <router-link :to="{
                          name:'xiangqing',
                          params:{
                              id:m.id,
                              title:m.title
                          }
                      }">
                          {{m.title}}
                      </router-link>&nbsp;&nbsp;
                  </li>
              </ul>
              <hr/>
              <router-view></router-view>
          </div>
      </template>
      
      <script>
          export default {
              name:'News',
              data(){
                  return{
                      messageList:[
                        {id:'001',title:'消息001'},
                        {id:'002',title:'消息002'},
                        {id:'003',title:'消息003'}
                      ]
                  }
              }
          }
      </script>
      

      src/pages/Detail.vue

      <template>
        <ul>
          <li>消息编号:{{ $route.params.id }}</li>
          <li>消息标题:{{ $route.params.title }}</li>
        </ul>
      </template>
      
      <script>
          export default {
              name:'Detail'
          }
      </script>
      
      6.8. 路由的 props 配置

      props 作用:让路由组件更方便的收到参数

      {
          name:'xiangqing',
          path:'detil/:id',
          component:Detail,
              
          // 第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
          // props:{a:900}
              
          // 第二种写法:props值为布尔值,为true时,则把路由收到的所有params参数通过props传给Detail组件
          // props:true
              
          // 第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
          props($route){
              return {
                  id:$route.query.id,
                  title:$route.query.title
              }
          }
      }
      

      src/router/index.js

      import VueRouter from "vue-router";
      import Home from '../pages/Home'
      import About from '../pages/About'
      import News from '../pages/News'
      import Message from '../pages/Message'
      import Detail from '../pages/Detail'
      
      export default new VueRouter({
        routes:[
          {
            path: '/about',
            component: About
          },
          {
            path:'/home',
            component: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:1,b:'hello'}
      
                    // props的第二种写法,值为布尔值,
                    // 若布尔值为真,会把该路由组件收到的所有params参数,以props的形式传给Detail组件
                    // props:true
      
                    // props的第三种写法,值为函数
                    props(params) { // 这里可以使用解构赋值
                      return {
                        id: params.id,
                        title: params.title,
                      }
                    }
                  }
                ]
              }
            ]
          }
        ]
      })
      

      src/pages/Message.vue

      <template>
      	<div>
          <ul>
            <li v-for="m in messageList" :key="m.id">
              <router-link :to="{
                      name:'xiangqing',
                      params:{
                          id:m.id,
                          title:m.title
                      }
               }">
                	{{m.title}}
        			</router-link>&nbsp;&nbsp;
        		</li>
        	</ul>
          <hr/>
          <router-view></router-view>
        </div>
      </template>
      
      <script>
          export default {
              name:'News',
              data(){
                  return{
                      messageList:[
                          {id:'001',title:'消息001'},
                          {id:'002',title:'消息002'},
                          {id:'003',title:'消息003'}
                      ]
                  }
              }
          }
      </script>
      

      src/pages/Detail.vue

      <template>
          <ul>
              <li>消息编号:{{ id }}</li>
              <li>消息标题:{{ title }}</li>
          </ul>
      </template>
      
      <script>
          export default {
              name:'Detail',
              props:[
                  'id','title'
              ]
          }
          </script>
      

Vue Router replace 编程式导航 缓存路由组件

6.9. 路由跳转的 replace 方法
  1. 作用:控制路由跳转时操作浏览器历史记录的模式

  2. 浏览器的历史记录有两种写入方式:pushreplace

    push 是追加历史记录

    replace 是替换当前记录,路由跳转时候默认是 push 方式

  3. 开启 replace 模式

    <router-link :replace="true" ...>News</router-link>

    简写 <router-link replace ...>News</router-link>

总结:浏览记录本质是一个栈,默认 push ,点开新页面就会在栈顶追加一个地址,后退,栈顶指针向下移动,改为 replace 就是不追加,而将栈顶地址替换

src/pages/Home.vue

<template>
  <div>
    <h2>Home组件内容</h2>
    <div>
      <ul class="nav nav-tabs">
        <li>
          <router-link replace class="list-group-item" active-class="active" 
                       to="/home/news">News</router-link>
    		</li>
        <li>
          <router-link replace class="list-group-item" active-class="active" 
                       to="/home/message">Message</router-link>
    		</li>
    </ul>
    <router-view></router-view>
    </div>
  </div>
</template>

<script>
  export default {
    name:'Home'
  }
</script>
6.10. 编程式路由导航(不用 <router-link>

作用:不借助 <router-link> 实现路由跳转,让路由跳转更加灵活

this.$router.push({}) 内传的对象与 <router-link> 中的 to 相同

this.$router.replace({})

this.$router.forward() 前进

this.$router.back() 后退

this.$router.go(n) 可前进也可后退,n(步数)为正是进,为负是退

this.$router.push({
    name:'xiangqing',
    params:{
        id:xxx,
        title:xxx
    }
})

this.$router.replace({
    name:'xiangqing',
    params:{
        id:xxx,
        title:xxx
    }
})

image.png

src/components/Banner.vue

<template>
	<div>
        <div>
            <h2>Vue Router Demo</h2>
            <button @click="back">后退</button>
            <button @click="forward">前进</button>
            <button @click="testGo">测试一下go</button>
    </div>
    </div>
</template>

<script>
    export default {
        name:'Banner',
        methods:{
            back(){
                this.$router.back()
        },
            forward(){
                this.$router.forward()
            }.
            test(){
        this.$router.go(3)
    	},
    }
        

src/pages/Message.vue

<template>
    <div>
        <ul>
            <li v-for="m in messageList" :key="m.id">
                <router-link :to="{
                    name:'xiangqing',
                    params:{
                        id:m.id,
                        title:m.title
                    }
                }">
                    {{m.title}}
                </router-link>
                <button @click="showPush(m)">push查看</button>
                <button @click="showReplace(m)">replace查看</button>
            </li>
        </ul>
        <hr/>
        <router-view></router-view>
    </div>
</template>

<script>
    export default {
        name:'News',
        data(){
            return{
                messageList:[
                    {id:'001',title:'消息001'},
                    {id:'002',title:'消息002'},
                    {id:'003',title:'消息003'}
                ]
            }
        },
        methods:{
            showPush(m){
                this.$router.push({
                    name:'xiangqing',
                    query:{
                        id:m.id,
                        title:m.title
                    }
                })
            },
            showReplace(m){
                this.$router.replace({
                    name:'xiangqing',
                    query:{
                        id:m.id,
                        title:m.title
                    }
                })
            }
        }
    }
</script>
6.11. 缓存路由组件

作用:让不展示的路由组件保持挂载,不被销毁

<keep-active inclue="News"><router-view></router-view></keep-active>

<keep-active inclue="['News','Message']"><router-view></router-view></keep-active>

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

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

image.png

src/pages/Home.vue

<template>
  <div>
    <h2>Home组件内容</h2>
    <div>
      <ul class="nav nav-tabs">
        <li><router-link replace class="list-group-item" active-class="active" 
                       to="/home/news">News</router-link></li>
        <li><router-link replace class="list-group-item" active-class="active" 
                       to="/home/message">Message</router-link></li>
    	</ul>
      <keep-alive include="News">
        <router-view></router-view>
    	</keep-alive>
    </div>
  </div>
</template>

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

Vue Router activated deactivated 路由守卫

6.12. activated deactivated

activateddeactivated 是路由组件所独有的两个钩子,用于捕获路由组件的激活状态

具体使用

  1. activated 路由组件被激活时触发
  2. deactivated 路由组件失活时触发

手风琴动画.gif

src/pages/News.vue

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

<script>
    export default {
        name:'News',
        data(){
            return {
                opacity:1
            }
        },
        activated(){
            console.log('News组件被激活了')
            this.timer = setInterval(()=>{
                this.opacity -= 0.01
                if(this.opacity <= 0) this.opacity = 1
            },16)
        },
        deactivated(){
            clearInterval(this.timer)
        }
    }
</script>
6.13. 路由守卫

作用:对路由进行权限控制

分类:全局守卫、独享守卫、组件内守卫

  1. 全局守卫

    ====

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值