Vue学习笔记(尚硅谷天禹老师)

1. Vue

1.1 Vue的特点

  1. 遵循MVVM模式
  2. 编码简洁,体积小,运行效率高,适合移动/PC端开发
  3. 它本身只关注UI,可以引入其它第三方库开发项目

1.2 与其他JS框架的关联

  1. 借鉴 Angular 的模板数据绑定技术
  2. 借鉴 React 的组件化虚拟DOM技术

1.3 数据绑定

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>数据绑定</title>
    <script src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        单向数据绑定:<input type="text" v-bind:value="name"><br>
        双向数据绑定:<input type="text" v-model:value="name">
    </div>

    <script>
        Vue.config.productionTip = false 
        new Vue({
            el:'#root', 
            data:{
                name:'JOJO'
            }
        })
    </script>
</body>
</html>

总结:

  • Vue中有2种数据绑定的方式:

    1. 单向绑定(v-bind):数据只能从data流向页面
    2. 双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data
  • 备注:

    1. 双向绑定一般都应用在表单类元素上(如:<input>、<select>、<textarea>等)
    2. v-model:value可以简写为v-model,因为v-model默认收集的就是value值

1.4 el与data的两种写法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>el与data的两种写法</title>
    <script src="../js/vue.js"></script>
</head>
<body>
    <div id="root">
        <h1>Hello,{{name}}!</h1>
    </div>

    <script>
        Vue.config.productionTip = false 
        //el的两种写法:
        // const vm = new Vue({
        //     // el:'#root', //第一种写法
        //     data:{
        //         name:'JOJO'
        //     }
        // })
        // vm.$mount('#root')//第二种写法

        //data的两种写法:
        new Vue({
            el:'#root', 
            //data的第一种写法:对象式
            // data:{
            //     name:'JOJO'
            // }
            //data的第二种写法:函数式
            data(){
                return{
                    name:'JOJO'
                }
            }
        })
    </script>
</body>
</html>

总结:

  1. el有2种写法:

    • 创建Vue实例对象的时候配置el属性
    • 先创建Vue实例,随后再通过vm.$mount('#root')指定el的值
  2. data有2种写法:

    • 对象式
    • 函数式
  3. 如何选择:目前哪种写法都可以,以后学到组件时,data必须使用函数,否则会报错

1.5 MVVM模型

在这里插入图片描述
MVVM模型:

  • M:模型(Model),data中的数据
  • V:视图(View),模板代码
  • VM:视图模型(ViewModel),Vue实例

1.6 Vue中的数据代理

在这里插入图片描述
总结:

  1. Vue中的数据代理通过vm对象来代理data对象中属性的操作(读/写)
  2. Vue中数据代理的好处:更加方便的操作data中的数据
  3. 基本原理:
    1. 通过object.defineProperty()把data对象中所有属性添加到vm上。
    2. 为每一个添加到vm上的属性,都指定一个getter/setter。
    3. 在getter/setter内部去操作(读/写)data中对应的属性。

1.7 监听属性 VS 计算属性

使用计算属性:

new Vue({
    el:'#root', 
    data:{ 
        firstName:'张',
        lastName:'三'
    },
    computed:{
    	fullName(){
		    return this.firstName + '-' + this.lastName
    	}
    }
})

使用监听属性:

new Vue({
	el:'#root',
	data:{
		firstName:'张',
		lastName:'三',
		fullName:'张-三'
	},
	watch:{
		firstName(val){
			setTimeout(()=>{
				this.fullName = val + '-' + this.lastName
			},1000);
		},
		lastName(val){
			this.fullName = this.firstName + '-' + val
		}
	}
})

总结:

  1. computedwatch之间的区别:

    • computed能完成的功能,watch都可以完成
    • watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作
  2. 两个重要的小原则:

    • 所有被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象
    • 所有不被Vue所管理的函数定时器的回调函数、ajax的回调函数等、Promise的回调函数),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。

1.8 绑定样式

<div id="root">
    <!-- 绑定class样式--字符串写法,适用于:样式的类名不确定,需要动态指定 -->
    <div class="basic" :class="mood" @click="changeMood">{{name}}</div> <br/><br/>

    <!-- 绑定class样式--数组写法,适用于:要绑定的样式个数不确定、名字也不确定 -->
    <div class="basic" :class="classArr">{{name}}</div> <br/><br/>

    <!-- 绑定class样式--对象写法,适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 -->
    <div class="basic" :class="classObj">{{name}}</div> <br/><br/>

    <!-- 绑定style样式--对象写法 -->
    <div class="basic" :style="styleObj">{{name}}</div> <br/><br/>

    <!-- 绑定style样式--数组写法 -->
    <div class="basic" :style="styleArr">{{name}}</div>
</div>

<script type="text/javascript">
	Vue.config.productionTip = false
		
    const vm = new Vue({
        el:'#root',
        data:{
            name:'尚硅谷',
            mood:'normal',
            classArr:['atguigu1','atguigu2','atguigu3'],
            classObj:{
                atguigu1:false,
                atguigu2:false,
            },
            styleObj:{
                fontSize: '40px',
                color:'red',
            },
            styleObj2:{
                backgroundColor:'orange'
            },
            styleArr:[
                {
                    fontSize: '40px',
                    color:'blue',
                },
                {
                    backgroundColor:'gray'
                }
            ]
        },
        methods: {
            changeMood(){
                const arr = ['happy','sad','normal']
                const index = Math.floor(Math.random()*3)
                this.mood = arr[index]
            }
        },
    })
</script>

1.9 key的作用与原理

在这里插入图片描述
在这里插入图片描述
面试题: reactvue中的key有什么作用?(key的内部原理)

  1. 虚拟DOM中key的作用:key是虚拟DOM中对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:

  2. 对比规则:

    • 旧虚拟DOM中找到了与新虚拟DOM相同的key:
      • 若虚拟DOM中内容没变, 直接使用之前的真实DOM
      • 若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
    • 旧虚拟DOM中未找到与新虚拟DOM相同的key:创建新的真实DOM,随后渲染到到页面
  3. 用index作为key可能会引发的问题:

    • 若对数据进行逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低
    • 若结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题
  4. 开发中如何选择key?

    • 最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值
    • 如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表,使用index作为key是没有问题的

1.10 过滤器

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>过滤器</title>
		<script type="text/javascript" src="../js/vue.js"></script>
		<script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.10.6/dayjs.min.js"></script>
	</head>
	<body>
		<div id="root">
			<h2>时间</h2>
            <h3>当前时间戳:{{time}}</h3>
            <h3>转换后时间:{{time | timeFormater()}}</h3>
			<h3>转换后时间:{{time | timeFormater('YYYY-MM-DD HH:mm:ss')}}</h3>
			<h3>截取年月日:{{time | timeFormater() | mySlice}}</h3>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false
		//全局过滤器
		Vue.filter('mySlice',function(value){
			return value.slice(0,11)
		})
		new Vue({
            el:'#root',
            data:{
                time:1626750147900,
            },
			//局部过滤器
            filters:{
                timeFormater(value, str="YYYY年MM月DD日 HH:mm:ss"){
                    return dayjs(value).format(str)
                }
            }
        })
	</script>
</html>

1.11 v-cloak指令

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>v-cloak指令</title>
		<style>
			[v-cloak]{
				display:none;
			}
		</style>
	</head>
	<body>
		<div id="root">
			<h2 v-cloak>{{name}}</h2>
		</div>
		<script type="text/javascript" src="../js/vue.js"></script>
	</body>
	
	<script type="text/javascript">
		Vue.config.productionTip = false
		
		new Vue({
			el:'#root',
			data:{
				name:'尚硅谷'
			}
		})
	</script>
</html>

总结:
v-cloak指令(没有值):

  1. 本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性
  2. 使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题

1.12 自定义指令

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>自定义指令</title>
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
    <!-- 
		需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍。
		需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点。
	-->
	<body>
		<div id="root">
			<h2>当前的n值是:<span v-text="n"></span> </h2>
			<h2>放大10倍后的n值是:<span v-big="n"></span> </h2>
			<button @click="n++">点我n+1</button>
			<hr/>
			<input type="text" v-fbind:value="n">
		</div>
	</body>
	
	<script type="text/javascript">
		Vue.config.productionTip = false

		new Vue({
			el:'#root',
			data:{
				n:1
			},
			// 第一种写法:directives:{指令名:配置对象}  
			// 第二种写法:directives:{指令名:回调函数}  
			directives:{
                //big函数何时会被调用?1.指令与元素成功绑定时(一上来) 2.指令所在的模板被重新解析时
				big(element,binding){
					console.log('big',this) //注意此处的this是window
					element.innerText = binding.value * 10
				},
				fbind:{
					//指令与元素成功绑定时(一上来)
					bind(element,binding){
						element.value = binding.value
					},
					//指令所在元素被插入页面时
					inserted(element,binding){
						element.focus()
					},
					//指令所在的模板被重新解析时
					update(element,binding){
						element.value = binding.value
					}
				}
			}
		})
	</script>
</html>

总结:

  1. 配置对象中常用的3个回调函数:
    • bind(element,binding):指令与元素成功绑定时调用
    • inserted(element,binding):指令所在元素被插入页面时调用
    • update(element,binding):指令所在模板结构被重新解析时调用
  2. 备注:
    • 指令定义时不加“v-”,但使用时要加“v-”
    • 指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名
    	new Vue({
    	el:'#root',
    	data:{
    		n:1
    	},
    	directives:{
    		'big-number'(element,binding){
    			element.innerText = binding.value * 10
    		}
    	}
    })
    

1.13 Vue生命周期

在这里插入图片描述

2. VueComponent

关于VueComponent

  1. school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的
  2. 我们只需要写或,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)
  3. 特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent
  4. 关于this指向:
    • 组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是VueComponent实例对象
    • new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数 它们的this均是Vue实例对象
  5. VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)
    Vue的实例对象,以后简称vm
<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8" />
		<title>一个重要的内置关系</title>
		<script type="text/javascript" src="../js/vue.js"></script>
	</head>
	<body>
		<div id="root">
			<school></school>
		</div>
	</body>

	<script type="text/javascript">
		Vue.config.productionTip = false
		Vue.prototype.x = 99

		const school = Vue.extend({
			name:'school',
			template:`
				<div>
					<h2>学校名称:{{name}}</h2>	
					<h2>学校地址:{{address}}</h2>	
					<button @click="showX">点我输出x</button>
				</div>
			`,
			data(){
				return {
					name:'尚硅谷',
					address:'北京'
				}
			},
			methods: {
				showX(){
					console.log(this.x)
				}
			},
		})

		const vm = new Vue({
			el:'#root',
			data:{
				msg:'你好'
			},
			components:{school}
		})
	</script>
</html>

在这里插入图片描述
总结:

  1. 一个重要的内置关系:VueComponent.prototype.__proto__ === Vue.prototype
  2. 为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue 原型上的属性、方法

3. 使用Vue CLI脚手架

Vue 脚手架隐藏了所有 webpack 相关的配置,若想查看具体的 webpakc 配置,请执行:
vue inspect > output.js

3.1 render函数

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

Vue.config.productionTip = false

new Vue({
    el:'#app',
    // 简写形式
	render: h => h(App),
    // 完整形式
	// render(createElement){
	//     return createElement(App)
	// }
})

总结:
关于不同版本的函数:

  1. vue.jsvue.runtime.xxx.js的区别:
    • vue.js 是完整版的 Vue,包含:核心功能+模板解析器
    • vue.runtime.xxx.js运行版的 Vue,只包含核心功能,没有模板解析器
  2. 因为 vue.runtime.xxx.js 没有模板解析器,所以不能使用 template 配置项,需要使用 render 函数接收到的createElement 函数去指定具体内容

3.2 plugin插件

src/plugin.js:

export default {
	install(Vue,x,y,z){
		console.log(x,y,z)
		//全局过滤器
		Vue.filter('mySlice',function(value){
			return value.slice(0,4)
		})

		//定义混入
		Vue.mixin({
			data() {
				return {
					x:100,
					y:200
				}
			},
		})

		//给Vue原型上添加一个方法(vm和vc就都能用了)
		Vue.prototype.hello = ()=>{alert('你好啊')}
	}
}

src/main.js:

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

Vue.config.productionTip = false
Vue.use(plugin,1,2,3)

new Vue({
    el:"#app",
    render: h => h(App)
})
<template>
    <div>
        <h2>姓名:{{name | mySlice}}</h2>
        <h2>性别:{{sex}}</h2> 
        <button @click="test">点我测试hello方法</button>  
    </div>
</template>

<script>
    export default {
        name:'Student',
        data() {
            return {
                name:'JOJO-fdsfdf',
				sex:'男'
            }
        },
        methods:{
            test() {
                this.hello()
            }
        }
    }
</script>

总结:
插件:

  1. 功能:用于增强Vue
  2. 本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据
  3. 定义插件:
    plugin.install = function (Vue, options) {
        // 1. 添加全局过滤器
        Vue.filter(....)
    
        // 2. 添加全局指令
        Vue.directive(....)
    
        // 3. 配置全局混入
        Vue.mixin(....)
    
        // 4. 添加实例方法
        Vue.prototype.$myMethod = function () {...}
        Vue.prototype.$myProperty = xxxx
    }
    
  4. 使用插件:Vue.use(plugin)

3.3 props

props适用于:

  1. 父组件 ==> 子组件 通信
  2. 子组件 ==> 父组件 通信(要求父组件先给子组件一个函数)
  3. 使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改
  4. props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做

3.4 自定义事件、解除自定义事件

<template>
    <div class="student">
        <h2>学生姓名:{{name}}</h2>
        <h2>学生性别:{{sex}}</h2>
        <button @click="sendStudentName">点我传递学生姓名</button> 
        <button @click="unbind">解绑自定义事件</button> 
    </div>
</template>

<script>
    export default {
        name:'Student',
        data() {
            return {
                name:'JOJO',
				sex:'男'
            }
        },
        methods:{
            sendStudentName(){
                this.$emit('jojo',this.name)
            },
            unbind(){
                // 解绑一个自定义事件
                // this.$off('jojo')
                // 解绑多个自定义事件
                // this.$off(['jojo'])
                // 解绑所有自定义事件
                this.$off()
            }
        }
    }
</script>

3.5 全局事件总线

全局事件总线是一种可以在任意组件间通信的方式,本质上就是一个对象。它必须满足以下条件:1. 所有的组件对象都必须能看见他 2. 这个对象必须能够使用$on、$emit和$off方法去绑定、触发和解绑事件

src/main.js:

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

Vue.config.productionTip = false

new Vue({
	el:'#app',
	render: h => h(App),
	beforeCreate() {
		Vue.prototype.$bus = this //安装全局事件总线
	}
})

在这里插入图片描述
在这里插入图片描述
总结:
全局事件总线(GlobalEventBus):

  1. 一种组件间通信的方式,适用于任意组件间通信
  2. main.js安装全局事件总线
  3. 使用事件总线:
    • mounted 中接收数据 this.$bus.$on('xxx',this.demo)
    • methods 提供数据 this.$bus.$emit('xxx',data)
    • 最好在beforeDestroy钩子中,用this.$bus.$off('demo')去解绑当前组件所用到的事件

3.6 消息的订阅与发布

消息订阅与发布(pubsub):

  1. 消息订阅与发布是一种组件间通信的方式,适用于任意组件间通信
  2. 使用步骤:
    1. 安装pubsub:npm i pubsub-js
    2. 引入:import pubsub from 'pubsub-js'
    3. 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身
    	export default {
        methods(){
            demo(data){...}
        }
        ...
        mounted() {
    		this.pid = pubsub.subscribe('xxx',this.demo)
        }
    }
    
    1. 提供数据:pubsub.publish('xxx',data)
    2. 最好在beforeDestroy钩子中,使用pubsub.unsubscribe(pid)取消订阅

3.7 $nextTick

$nextTick(回调函数)可以将回调延迟到下次 DOM 更新循环之后执行

<template>
  <div>
    <button v-show="!todo.isEdit" @click="handleEdit(todo)">编辑</button>
    <div>
      <span v-show="!todo.isEdit">{{ todo.title }}</span>
      <input
        type="text"
        v-show="todo.isEdit"
        :value="todo.title"
        @blur="handleBlur(todo, $event)"
        ref="inputTitle"
      />
    </div>
  </div>
</template>

<script>
export default {
  name: 'MyItem',
  data() {
    return {
      todo: {
        isEdit: false,
        title: '你好啊',
      },
    }
  },
  methods: {
    handleEdit(todo) {
      // 如果todo自身有isEdit属性就将isEdit改成true
      if (Object.prototype.hasOwnProperty.call(todo, 'isEdit')) {
        todo.isEdit = true
      } else {
        // 如果没有就向todo中添加一个响应式的isEdit属性并设为true
        this.$set(todo, 'isEdit', true)
      }
      // 当Vue重新编译模板之后执行$nextTick()中的回调函数

      // 当todo中数据发生改变后,基于在新的todo数据生成的新dom上操作,需要在nextTick的回调函数中执行
      // 如果直接写 this.$refs.inputTitle.focus() 无法在新的input上获取焦点
      this.$nextTick(function () {
        // 使input框获取焦点
        this.$refs.inputTitle.focus()
      })
    },
    // 当input框失去焦点时更新
    handleBlur(todo, event) {
      todo.isEdit = false
      if (!event.target.value.trim()) return alert('输入不能为空!')
      todo.title = event.target.value
    },
  },
}
</script>

总结:
$nextTick

  1. 语法:this.$nextTick(回调函数)
  2. 作用:在下一次 DOM 更新结束后执行其指定的回调
  3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行

3.8 slot插槽

总结:

  1. 作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于==父组件 > 子组件
  2. 分类:默认插槽、具名插槽、作用域插槽
  3. 作用域插槽:
    1. 理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)
    2. 具体编码:
    父组件中:
    	<Category>
    		<template scope="scopeData">
    			<!-- 生成的是ul列表 -->
    			<ul>
    				<li v-for="g in scopeData.games" :key="g">{{g}}</li>
    			</ul>
    		</template>
    	</Category>
    
    	<Category>
    		<template slot-scope="scopeData">
    			<!-- 生成的是h4标题 -->
    			<h4 v-for="g in scopeData.aaaa" :key="g">{{g}}</h4>
    		</template>
    	</Category>
    子组件中:
        <template>
            <div>
                <slot :aaaa="games"></slot>
            </div>
        </template>
    	
        <script>
            export default {
                name:'Category',
                props:['title'],
                //数据在子组件自身
                data() {
                    return {
                        games:['红色警戒','穿越火线','劲舞团','超级玛丽']
                    }
                },
            }
        </script>
    

4. Vuex

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

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

4.1 基本使用

<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('ADD',this.n)
			},
			decrement(){
				this.$store.commit('SUBTRACT',this.n)
			},
			incrementOdd(){
				this.$store.dispatch('addOdd',this.n)
			},
			incrementWait(){
				this.$store.dispatch('addWait',this.n)
			},
		},
	}
</script>

<style>
	button{
		margin-left: 5px;
	}
</style>
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
   
//准备actions对象——响应组件中用户的动作
const actions = {
    addOdd(context,value){
        console.log("actions中的addOdd被调用了")
        if(context.state.sum % 2){
            context.commit('ADD',value)
        }
    },
    addWait(context,value){
        console.log("actions中的addWait被调用了")
        setTimeout(()=>{
			context.commit('ADD',value)
		},500)
    },
}
//准备mutations对象——修改state中的数据
const mutations = {
    ADD(state,value){
        state.sum += value
    },
    SUBTRACT(state,value){
        state.sum -= value
    }
}
//准备state对象——保存具体的数据
const state = {
    sum:0 //当前的和
}
   
//创建并暴露store
export default new Vuex.Store({
    actions,
    mutations,
    state
})

4.2 四个map方法的使用

  1. mapState方法:用于帮助我们映射state中的数据
    computed: {
        //借助mapState生成计算属性:sum、school、subject(对象写法)
         ...mapState({sum:'sum',school:'school',subject:'subject'}),
             
        //借助mapState生成计算属性:sum、school、subject(数组写法)
        ...mapState(['sum','school','subject']),
    },
    
  2. mapGetters方法:用于帮助我们映射getters中的数据
    computed: {
        //借助mapGetters生成计算属性:bigSum(对象写法)
        ...mapGetters({bigSum:'bigSum'}),
    
        //借助mapGetters生成计算属性:bigSum(数组写法)
        ...mapGetters(['bigSum'])
    },
    
  3. mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数
    methods:{
        //靠mapActions生成:incrementOdd、incrementWait(对象形式)
        ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    
        //靠mapActions生成:incrementOdd、incrementWait(数组形式)
        ...mapActions(['jiaOdd','jiaWait'])
    }
    
  4. mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数
    methods:{
        //靠mapActions生成:increment、decrement(对象形式)
        ...mapMutations({increment:'JIA',decrement:'JIAN'}),
        
        //靠mapMutations生成:JIA、JIAN(对象形式)
        ...mapMutations(['JIA','JIAN']),
    }
    

4.3 模块化+命名空间

  1. 目的:让代码更好维护,让多种数据分类更加明确
  2. 修改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
      	}
    })
    
  3. 开启命名空间后,组件中读取数据:
    // 1、读取state数据:
    //方式一:自己直接读取
    this.$store.state.personAbout.list
    //方式二:借助mapState读取:
    ...mapState('countAbout',['sum','school','subject']),
    
    
    // 2、读取getters数据:
    //方式一:自己直接读取
    this.$store.getters['personAbout/firstPersonName']
    //方式二:借助mapGetters读取:
    ...mapGetters('countAbout',['bigSum'])
    
    
    // 3、调用dispatch:
    //方式一:自己直接dispatch
    this.$store.dispatch('personAbout/addPersonWang',person)
    //方式二:借助mapActions:
    ...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    
    
    // 4、调用commit:
    //方式一:自己直接commit
    this.$store.commit('personAbout/ADD_PERSON',person)
    //方式二:借助mapMutations:
    ...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
    

5. Vue Router

5.1 路由的理解

  1. 什么是路由?

    • 一个路由就是一组映射关系(key - value)
    • key 为路径,value 可能是 function 或 componen
  2. 路由分类

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

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

5.2 路由的使用

  1. 安装vue-router,命令:npm i vue-router
  2. 应用插件:Vue.use(VueRouter)
  3. 编写router配置项:
    //引入VueRouter
    import VueRouter from 'vue-router'
    //引入Luyou 组件
    import About from '../components/About'
    import Home from '../components/Home'
    
    //创建router实例对象,去管理一组一组的路由规则
    const router = new VueRouter({
    	routes:[
    		{
    			path:'/about',
    			component:About
    		},
    		{
    			path:'/home',
    			component:Home
    		}
    	]
    })
    
    //暴露router
    export default router
    
  4. 实现切换(active-class可配置高亮样式):
    <router-link active-class="active" to="/about">About</router-link>
    
  5. 指定展示位:<router-view></router-view>

5.3 路由的query参数

  1. 传递参数:
    <!-- 跳转并携带query参数,to的字符串写法 -->
    <router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>
    				
    <!-- 跳转并携带query参数,to的对象写法 -->
    <router-link :to="{
    	path:'/home/message/detail',
    	query:{
    		id:666,
            title:'你好'
    	}
    }">跳转</router-link>
    
  2. 接收参数:
    $route.query.id
    $route.query.title
    

5.4 命名路由

在这里插入图片描述

5.5 路由的params参数

在这里插入图片描述

5.6 路由的props配置

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

5.7 缓存路由组件

在这里插入图片描述

  1. activateddeactivated是路由组件所独有的两个钩子,用于捕获路由组件的激活状态
  2. 具体使用:
    • activated路由组件被激活时触发
    • deactivated路由组件失活时触发

5.8 路由守卫

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

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

  3. 全局守卫:

    //全局前置守卫:初始化时执行、每次路由切换前执行
    router.beforeEach((to,from,next)=>{
    	console.log('beforeEach',to,from)
    	if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
    		if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则
    			next() //放行
    		}else{
    			alert('暂无权限查看')
    		}
    	}else{
    		next() //放行
    	}
    })
    
    //全局后置守卫:初始化时执行、每次路由切换后执行
    router.afterEach((to,from) => {
    	console.log('afterEach',to,from)
    	if(to.meta.title){ 
    		document.title = to.meta.title //修改网页的title
    	}else{
    		document.title = 'vue_test'
    	}
    })
    
  4. 独享守卫:
    在这里插入图片描述

  5. 组件内守卫:
    在这里插入图片描述

原文链接:https://blog.csdn.net/qq_55593227/article/details/119717498

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值