【前端笔记】Vue笔记

下载相关镜像-淘宝镜像:
http://www.npmmirror.com/?spm=a2c6h.14029880.0.0.3ba675d7ayxn4x

相关配置博客:
https://blog.csdn.net/qq_35275233/article/details/87900835?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164455854416780274110187%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=164455854416780274110187&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-2-87900835.pc_search_result_cache&utm_term=npm%E6%B7%98%E5%AE%9D%E9%95%9C%E5%83%8F%E5%AE%89%E8%A3%85&spm=1018.2226.3001.4187

一、数据绑定

Vue有两种数据绑定方法:
	1、单项绑定(v-bind):数据只能从data流向页面
	2、双向绑定(v-model):数据不经能从data流向页面,还可以从页面流向data
		备注:
			+双向绑定一般都应用在表单类i元素上,如input、select等
			+v-model:value可以简写为v-model,因为v-model默认收集的就是value的值

二、data与el的两种写法

1、el有2种写法
	(1)new Vue的时候配置el属性
	(2)先创建Vue实例,随后再通过vm.$mount('#root')挂载制定el值
2、data有2种写法
	(1)对象式
	data = {
		name:'jack',
		age: 18
	}·
	(2)函数式
	data(){
		return {
			name:'jack',
			age:18
		}
	}
	注意:
		在使用组件的时候,data必须使用函数式,否则会报错的
3、一个重要原则
	由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实列了

三、MVVM模型

1、M:模型(model):data中的数据
2、V:视图(view):模板代码
3、VM:视图模型(ViewModel):Vue实例
观察发现:
	1、data中所有的属性,最后都出现在了vm身上
	2、vm身上所有的属性及Vue原型身上的所有属性,在Vue模板中都可以直接使用

四、数据代理

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

五、事件处理

事件的基本使用
	1、使用v-on:xxx或者@xxx绑定事件,其中xxx是事件名
	2、事件回调需要配置在methods对象中,最终会在vm上
	3、methods中配置的函数,不要用箭头函数!否则this指向就是window而不是vm了
	4、methods中配置的函数,都是被Vue管理的函数,this的指向是vm或者组件实例对象
	5、@click="demo"和@click="demo($event)"效果一样,但是后者可以传参

六、事件修饰符

Vue中的事件修饰符:
	1、prevent:阻止默认事件(常用)——比如a的跳转等
	2、stop:组织时间冒泡(常用)
	3、once:时间只触发一次(常用)
	4、capture:使用事件的捕获模式
	5、self:只有event.target是当前操作的元素时才触发事件
	6、passive:事件的默认行为立即执行,无需等待时间回调执行完毕

七、键盘事件

1、Vue中常用的按键别名:
	回车 -- enter
	删除 -- delete
	退出 -- esc
	空格 -- space
	换行 -- tab(搭配keydown使用,因为tab本身就会导致光标变换位置)
	上 -- up
	下 -- down
	左 -- left
	右 --right
2、Vue未提供别名的按键,可以使用按键原始的key值去绑定,但是要注意转为短横线形式
3、系统修饰符(用法特殊):ctrl、alt、shift、meta
	(1)配合keyup使用:按下修饰键的同时,还需要按下其他键,随后释放其他键,事件才会被触发
	(2)配合keydown使用:正常触发事件
4、也可以使用keyCode去制定具体按键(不推荐,因为按键编号在不同的设备上可能不同)
5、Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名
	例如:Vue.congig.keyCodes.huiche = 13

八、计算属性

1、定义:要用的属性不存在,要通过已有属性计算得来
2、原理:底层借助了Object.defineproperty方法提供的getter和setter
3、get函数什么时候执行?
	(1)初次读取时会执行一次
	(2)当依赖的数据发生变化时会再次调用
4、优势:与methods相比,内部有缓存机制(复用),效率更高,调试更能方便
5、备注:
	1、计算属性最终会出现在vm上,直接读取使用即可
	2、如果计算属性要被修改,那必须写set函数去响应修改,且set中修改要引起计算时依赖的数据发生改变

九、深度监视

Vue中的watch默认不监测对象内部值的改变(一层)
配置deep:true可以监测对象内部值改变(多层)
备注:
	(1)Vue自身可以监测对象内部值的改变,但是Vue提供的watch默认不可以
	(2)使用watch时根据数据的具体结构,决定是否采用深度监视

computed和watch之间的区别

1、computed能完成的功能,watch都可以完成
2、watch能完成的功能,computed不一定能够能完成,例如:watch可以进行异步操作
两个重要的小原则:
	1、所有被Vue管理的函数,最好写成普通函数,这样this指向的才是vm或者组件实例对象
	2、所有不被Vue管理的函数(定时器的回调函数、ajax的回调函数、Promise的回调函数等),最好写成箭头函数,这样
	this的指向才是vm或者组件实例对象

十、绑定样式

1、class样式
	:class="xxx" xxx可以是字符串、数组、对象
	1、字符串写法,适用于样式的类名不确定,需要动态指定
	2、数组写法,适用于要绑定的样式个数不确定、名字不确定
	3、对象写法,适用于要绑定的样式个数确定、名字也确定,但要动态决定用不用
2、style样式
	:style="{fontSize: xxx}"其中xxx是动态值
	:style="[a,b]"其中a、b是样式对象

十一、条件渲染

1、v-if
	写法:
		(1)v-if="表达式"
		(2)v-else-if="表达式"
		(3)v-else="表达式"
	适用于:切换频率较低的场景
	特点:不展示DOM元素直接被删除
	注意:v-if和v-else-if、v-else一起使用,但是要求结构不能被“打断”
2、v-show
	写法:v-show="表达式"
	适用于:切换频率较高的场景
	特点:不展示的DOM元素不被一处,仅仅是使用样式隐藏起来
3、备注:使用v-if时,元素可能无法获取到,而使用v-show一定可以获取到 

十二、列表渲染

v-for指令
	1、用于展示列表元素
	2、语法:v-for="(item,index) in xxx" :key="yyy"
	3、可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)

十三、key的作用和原理

面试题:react、vue中的key有什么作用?(key的内部原理)
	1、虚拟DOM中的key的作用:
		key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】
		随后Vue尽心那个【新虚拟DOM】和【旧虚拟DOM】的差异比较,比较规则如下:
	2、对比规则:
		(1)旧虚拟DOM中找到了与新虚拟DOM相同的key:
			若虚拟DOM中的内容没有变化,直接使用之前的真实DOM
			若虚拟DOM中的内容变化了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM
		(2)就虚拟DOM中没有找到与新虚拟DOM中相同的key
			创建新的真实DOM,随后渲到页面
	3、用index作为key可能会引发的问题
		(1)若对数据进行:逆序添加、逆序删除等破环顺序的操作:
			会产生没有必要的真实DOM更新----页面效果没有问题,但是效率地下
		(2)若结构中还包含输入类的DOM:
			会产生错误的DOM更新----页面就会出现问题
	4、开发中如何选择key?
		(1)最好使用每条数据的唯一标识作为key,比如id、手机号、省份证号、学号等唯一值
		(2)如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅仅用于渲染列表展示,用index作为key还是可以的

十四、Vue监测数据的原理

1、Vue会监视data中所有层次的数据
2、如何监测对象中的数据?
	通过setter实时监控,且要在new Vue时就传入要监测的数据
	(1)对象中后续追加的属性,Vue默认不做响应式处理
	(2)如需给后续添加的属性做响应式,可以使用以下API:
		Vue.set(target,propertyName/index,value) 或者
		vm.$set(target,propertyName/index,value)
3、如何监测数组中的数据?
	通过包裹数据更新元素的方法实现,本质就是做两件事:
	(1)调用原生对应的方法对数组进行更新
	(2)重新解析模板,进而更新页面
4、在Vue中修改数据中的某个元素一定要用如下的方法:
	(1)使用这些API:push、pop、shift、unshift、splice、sort、reverse
	(2)Vue.set() 或 vm.$set()

注意:Vue.set() 和 vm.$set() 不能给vm或者vm的根数据对象(data)添加属性!!!

十五、收集表单数据

若:<input type="text"/>,则v-model收集的是value值,用户输入的就是value值
若:<input type="radio" />,则v-model收集的是value值,且要给标签配置value值
若:<input type="checkbox" />
	1、没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
	2、配置了input的value属性:
		(1)v-model的初始值是非数组,那么收集到的就是checked(勾选 or 未勾选,是布尔值)
		(2)v-model的初始值是数组,那么收集的就是value值组成的数组
备注:v-model的三个修饰符:
		lazy:失去焦点再收集数据
		number:输入字符串转为有效数字
		trim:输入首尾空格过滤

十六、过滤器

定义:对要显示的数据进行特定格式化之后再显示(适用于一些简单逻辑的处理)
语法:
	1、注册过滤器:Vue.filter(name,callback) 或者 new Vue({filters:{}})
	2、使用过滤器:{{xxx | 过滤器名}} 或 v-bind:属性 = "xxx | 过滤器名"
备注:
	1、过滤器也可以接受额外参数、多个过滤器可以串联
	2、过滤器本质就是函数,返回一个新的对应数据,并没有改变原本的数据

十七、目前学过的内置指令

v-bind:单项绑定解析表达式,可以简写为:xxx
v-model:双向数据绑定
v-for:遍历数组、对象、字符串等
v-on:绑定事件监听,可以简写为@
v-if:条件渲染(动态控制节点是否存在)
v-else:条件渲染(动态控制节点是否存在)
v-show:条件渲染(动态控制节点是否展示)
v-text指令:
	1、作用:向其所在的节点中渲染文本内容
	2、与插值语法的区别:v-text会替换掉节点中的内容,{{xxx}}则不会
v-html指令:
	1、作用:向指定节点中渲染包含html结构的内容
	2、与插值语法的区别:
		(1)v-html会替换掉节点中所有内容,插值语法不会
		(2)v-html可以识别html结构
	3、严重注意:v-html有安全性问题!!!
		(1)在网页上动态渲染任意的HTML是非常危险的,容易导致xss攻击
		(2)一定要在可信的内容上使用v-html,永远不要在用户提交的内容上!!!
v-cloak指令(没有值):
	1、本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性
	2、使用css配合v-cloak可以解决网速慢时页面展示{{xxx}}问题
	css里写
		[v-cloak]{
			display:none;
		}
v-once指令(没有值):
	1、v-once所在节点在初次动态渲染之后,就视为静态内容了
	2、以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能
v-pre指令:
	1、跳过其所在节点的编译过程
	2、可以利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译

十八、自定义指令

1、定义语法
	(1)局部指令
		new Vue({
			directives:{指令名:配置对象}
		})
		或
		new Vue({
			directives:{指令名:回调函数}
		})
	(2)全局指令
		Vue.directice(指令名 ,配置对象) 或 Vue.directice(指令名 ,回调函数)
2、配置对象中常用的三个回调
	(1)bind:指令与元素成功绑定时调用
	(2)inserted:指令所在元素被插入页面时调用
	(3)update:指令所在模板结构被重新解析时调用
3、备注:
	(1)指令定义的时候不加v-,但使用的时候要加v-
	(2)指令名如果是多个单词组成,要用短横线相连(kebab-case)命名方式,不要使用驼峰命名法;且在写配置的时候要用引号包裹

十九、生命周期

1、又名:生命周期回调函数、生命周期函数\生命周期钩子
2、是什么:Vue在关键的时刻帮我们调用的一些特殊名称的函数
3、生命周期函数的名字不可以更改,但是函数的具体内容是根据程序员的需要编写的
4、生命周期函数中的this指向是vm 或者 组件实例对象
常用的生命周期狗子:
	1、mounted:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】
	2、beforeDestroy:清除定时器、解绑自定义时间、取消订阅消息等【收尾工作】
关于销毁Vue实例:
	1、销毁后借助Vue开发者工具看不到任何信息
	2、销毁后自定义事件会消失,但是原生DOM时间依然有效
	3、一般不会在beforeDestroy操作数据,因为即将销毁,也不会在触发更新流程了

二十、组件

组件定义—— 实现应用中局部功能的代码和资源的集合

模块化:当应用中的js都以模块来编写的,那这个应用就是一个模块化应用
组件化:当应用中的功能都是多组件方式来编写的,那这个应用就是一个组件化的应用

Vue中使用组件的三大步骤:

1、定义组件
2、注册组件
3、使用组件(写组件标签)

1、如何定义一个组件?
	使用Vue.extend(options)创建,其中options与new Vue(options)时传入的那个options几乎一样,但是区别如下:
		(1)el不要写,为什么?—— 最终所有的组件都要经过一个vm管理,由vm中的el决定服务哪个容器
		(2)data必须写成函数,为什么?—— 避免组件复用时,数据存在引用关系
	备注:使用template可以配置组件结构
2、如何注册组件?
	(1)局部注册:靠new Vue时传入components选项
	(2)全局注册:靠Vue.component('组件名',组件)
3、编写组件标签:
	<school></school>

几个注意点

1、关于组件名:
	一个单词组成:
		第一种写法(首字母小写):school
		第二种写法(首字母大写):School
	多个单词组成:
		第一种写法(kabab-case命名);my-shcool(使用时需要用引号包裹)
		第二种写法(所有单词首字母都大写):MySchool(需要使用Vue脚手架支持)
	备注:
		(1)组件名尽可能回避HTML中已有的元素名称,例如h2、H2都不行
		(2)可以使用name配置项制定组件在开发者工具中呈现的名字
2、关于组件标签:
	第一种写法:<school></school>
	第二种写法:<school/>
	备注:不适用脚手架时,<school/>会导致后续组件不能渲染
3、一个简写方式
	const school = Vue.extend(options) 可以简写为: const school = options

关于VueComponent

1、school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,而是Vue.extend生成的
2、我们只需要写<school/>或者<school></school>,Vue解析的时候会帮我们创建school组件的实例对象
	即Vue帮我们执行的:new VueComponent(options)
3、特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!
4、关于this的指向
	(1)组件配置中:
		data函数、methods中的函数、watch中的函数、computed中的函数,他们的this均是【VueComponent实例对象】
	(2)new Vue(options)配置中:
		data函数、methods中的函数、watch中的函数、computed中的函数,他们的this均是【Vue实例对象】
5、VueComponent的实例对象,以后简称为vc(也可称之为:组件实例对象)
	Vue的实例对象,以后简称为vm

一个重要的内置关系

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

二十一、Vue脚手架

关于不同版本的Vue:

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

vue.config.js配置文件

1、使用vue.inspect > output.js可以查看Vue脚手架的默认配(但是只能看,改了也没有用)
2、使用vue.config.js可以对脚手架进行个性化定制,详情见:https://cli.vuejs.org/zh

二十二、ref属性

1、被用来给元素或者子组件注册引用i西南西(id的替代者)
2、应用在html标签上获取到的是真实的DOM元素,应用在组件标签上获取的是组件实例对象(vc)
3、使用方式
	打标识:<h1 ref='xxx'>...</h1> 或者 <School ref='xxx'></School>
	获取:this.$refs.xxx

二十三、配置项props

功能:让组件接受外部传过来的数据
(1)传递数据:
	<Demo name="xxx" :age="18">
	<!-- 其中对age前面加冒号,是为了v-bind:这样收集的时候就会当js表达式解析,读到数字18了 -->
(2)接收数据:
	第一种方式(只接收):
		props:['name','age']
	第二种方式(限制类型):
		props:{
			name:String,
			age:Number
		}
	第三种方式9(限制类型、限制必要性、指定默认值):
		props:{
			name:{
				type:String, //类型
				required:true, //必要性
			},
			age:{
				type:Number,
				default:18  //默认值
			}
		}
备注:props是只读的,Vue底层会监测你对props的修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容给data一份,然后去修改data中的数据

二十四、mixin(混入)

功能:可以把多个组件共用难过的配置提取成一个混入对象
使用方式:
	第一步定义混合,例如:
		{
			data(){...},
			methods:{...}
			...
		}
	第二步使用混入,例如:
		(1)全局混入:Vue.mixin(xxx)
		(2)局部混入:mixins:['xxx']

二十五、插件plugins

功能:用于增强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()

二十六、scoped样式

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

二十七、TodoList案例

第一版TodoList

1、组件化编码流程:
	(1)拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突
	(2)实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:
		-一个组件在用:放在组件自身即可
		-一些组件再用:放在他们共同的父组件上(状态提升)
	(3)实现交互:从绑定时间开始
2、props适用于:
	(1)父组件 ==> 子组件通信
	(2)子组件 ==> 父组件通信(要求父组件先给子组件一个函数,然后通过参数传值
3、使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!
4、props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但是不建议这样做

二十八、WebStorage

1、存储内容大小一般支持5M左右(不同浏览器可能还不一样)
2、浏览器通过Window.sessionStorage 和 Window.localStorage属性来实现本地存储机制
3、相关API:
	1、xxxStorage.setItem('key','value')
		该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值
	2、xxxStorage.getItem('key')
		该方法接受一个键名作为参数,返回键名对应的值
	3、xxxStorage.removeItem('key')
		该方法接受一个键名作为参数,并把该键名从存储中删除
	4、xxxStorage.clear()
		该方法会清空存储中的所有数据
4、备注:
	(1)SessionStorage存储的内容会随着浏览器窗口关闭而消失
	(2)LocalStorage存储的内容,需要手动清除才会消失
	(3)xxxStorage.getItem(xxx)如果xxx对应的value获取不到,那么getItem的返回值是null
	(4)JSON.parse(null)的结果依然是null

二十九、组件的自定义事件

1、一种组件间通信的方式,适用于:子组件 ==> 父组件
2、使用场景:A是父组件,B是子组件,B想给A传递数据,那么就在A中给B绑定自定义事件(实践的回调一定留在A中)
3、绑定自定义时间:
	(1)第一种方式,在父组件中 <Demo @atguigu="test"/> 或 <Demo v-on:atguigu="test">
	(2)第二种方式,在父组件中:
	<demo ref="demo>
	------
	mounted(){
		this.$refs.demo.$on('atguigu',this.test)
	}
	(3)若想让自定义时间只触发一次,可以使用once修饰符,或将$on替换成$once
4、触发自定义事件:this.$emit('atguigu',数据)
5、解绑自定义时间:this.$off('atguigu')
6、组件上也可以绑定原生DOM事件,需要使用native修饰符
7、注意:通过this.$refs.xxx.$on('atguigu',回调)绑定自定义事件时,回调要么配置在methods中,要么使用箭头函数,否则this的指向会有问题!!!

三十、全局事件总线

1、一种组件间通信的方式,适用于任意组件间通信
2、安装全局事件总线

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

3、使用事件总线:
(1)接收数据:A组件想接收数据,则在A组件中给KaTeX parse error: Expected '}', got 'EOF' at end of input: …ed() { this.bus.KaTeX parse error: Expected 'EOF', got '}' at position 23: …',this.demo) }̲ } (2)提供数据:th…bus. e m i t ( ′ x x x ′ , 数 据 ) 4 、 最 好 在 b e f o r e D e s t r o y 钩 子 中 , 用 emit('xxx',数据) 4、最好在beforeDestroy钩子中,用 emit(xxx,)4beforeDestroyoff去解绑当前组件所用到的事件

三十一、消息订阅与发布(pubsub)

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

三十一、消息订阅与发布(pubsub)

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

三十二、vue脚手架配置代理

方法一

在vue.config中添加如下配置
devServer:{
	proxy:'http:localhost:5000'
}
说明:
	1、优点:配置简单,请求资源时直接发给前端相同端口(8080)即可
	2、缺点:不能配置多个代理,不能灵活的控制请求是否走代理
	3、工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器(优先匹配前端资源)

方法二

编写vue.config.js配置具体代理规则:
module.exports = {
	deServer: {
		proxy: {
			'/api1':{ //匹配所有以'/api1'开头的请求路径
				target:'http:localhost:5000', //代理目标的基础路径
				changeOrigin:true,
				pathRewrite:{'^/api1',''}
			},
			'/api2':{//匹配所有以'/api2'开头的请求路径
				target:'http:localhost:5001',
				changeOrigin:true,
				pathRewrite:{'^/api2',''}
			}
		}
	}
}

changeOrigin设置为true时,服务器收到的请求头host为:localhost:5000
changeOrigin设置为true时,服务器收到的请求头host为:localhost:8080
changeOrigin默认为true
说明:
	1、优点:可以配置多个代理,且可以灵活的控制请求是否走代理
	2、缺点:配置略微繁琐,请求资源的时候必须加前缀

三十三、插槽

1、作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信方式
2、分类:默认插槽、具名插槽、作用域插槽
3、使用方式:
	(1)默认插槽
	父组件中:
		<Category>
			<div>html....</div>
		</Category>
	子组件中:
		<template>
			<div>
				<!-- 定义插槽 -->
				<slot>插槽默认内容。。。</slot>
			</div>
		</template>
	(2)具名插槽
	父组件中:
		<Category>
			<template slot='center'>
				<div>html结构1</div>
			</template>
			<template v-slot:'footer'>
				<div>html结构2</div>
			</template>
		</Category>
	子组件中:
	    <template>
			<div>
				<!-- 定义插槽 -->
				<slot name="center">插槽默认内容。。。</slot>
				<slot name="footer">插槽默认内容。。。</slot>
			</div>
		</template>
	(3)作用域插槽
		1、理解:理解:数据在组件自身,但根据数据生成的结构需要组件的使用者来决定(games数据在Category组件中,但是使用是在数据所遍历出来的结构有App组件决定)
		父组件中:
			<Category>
				<template scope="scopeData">
					<!-- 生成的是ul列表 -->
					<ul>
						<li v-for="g in scopeData.games" :key="g">{{g}}</li>
					</ul>
				</template>
			</Category>

			<Category>
				<template scope="scopeData">
					<!-- 生成的是ol列表 -->
					<ol>
						<li v-for="g in scopeData.games" :key="g">{{g}}</li>
					</ol>
				</template>
			</Category>
		子组件中:
		<template scope="scopeData">
			<slot :games="games"></slot>
		</template>

三十四、Vuex使用

1、组件中读取vuex中的数据:$store.state.xxx
2、组件中修改vuex的数据:$store.dispatch('actions中的方法名','数据') 或者 $store.commit('mutations中的方法名','数据')
备注:如果没有网络请求或者其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit

三十五、getters使用

1、概念:当state中的数据需要经过加工后再使用时,采用getters加工
2、在store.js中追加getters配置
const getters = {
	bigSum(state){
		return state.sum*10
	}
}
.......
创建并暴露store
export default new Vuex.Store({
	...
	getters
})
3、在组件中读取数据:$store.getters.bigSum

三十六、模块化+命名空间

1、目的:让代码更好维护,让多种数据分类更加明确
2、修改store.js
```
const CountAbout = {
	namespaced:ture, //开启命名空间
	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数据:
//方式一,自己直接读取
this.$store.state.personAbout.list
//方式二、借助mapState读取
...mapState('personAbout',['name','age'])

4、开启命名空间后,组件中读取getters数据
//方式一,自己直接读取
this.$store.gettters['personAbout/firstPersonName']
//方式二、借助mapGetters读取
...mapGetters('personAbout',['firstPersonName'])

5、开启命名空间后,组件中dispatch
//方式一,自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二、借助mapActions
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})

6、开启命名空间后,组件中commit
//方式一,自己直接commit
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二、借助mapMutations
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'})

三十七、路由

1、安装vue-router,命令:npm i vue-router
2、应用插件:Vue.use(VueRouter)
3、编写router配置项
```
//引入VueRouter
import VueRouter from 'vue-router'
//引入路由组件
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>

2、几个需要注意的点

1、路由组件通常存放在pages文件夹,一般组件通常存放在components中
2、通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载
3、每个组件都有自己的$route属性,里面存储着自己的路由信息
4、整个应用只有一个router,可以通过组件的$router属性获取到

3、多级路由

1、配置路由规则,使用children配置
```
routes:[
	{
		path:'/about',
		component:About
	},
	{
		path:'/home',
		component:Home,
		children:[ //通过children设置子路由
			{
				path:'news', //此处一定不要写斜杠/news
				component:News
			}
		]
	}
]
```
2、跳转(要写完整路径)
<router-link to='/home/news'>News</router-link>

4、路由的query参数
1、传递参数

```
跳转

<!-- 跳转并携带query参数,to的对象写法 -->
<router-link
	:to="{
		path:'home/message/detail',
		query:{
			id:item.id,
			title:item.title
		}
	}"
>跳转</router-link>
```
2、接收参数
$route.query.id
$route.$query.title

5、命名路由
在配置路由的时候加上name属性即可
6、采用params
1、配置路由

   routes:[
   	{
   		path:'/about',
   		component:About
   	},
   	{
   		path:'/home',
   		component:Home,
   		children:[ //通过children设置子路由
   			{
   				name:'xinwen',
   				path:'news', //此处一定不要写斜杠/news
   				component:News
   			}
   		]
   	}
   ]
   ```
   2、传递参数
   <!-- 跳转并懈怠query参数,to的字符串写法 -->
   ```
   <router-link :to="`/home/message/detail/${item.id}/${item.title}`">跳转</router-link>

   <!-- 跳转并携带query参数,to的对象写法 -->
   <router-link
   	:to="{
   		name:'xinwen',
   		query:{
   			id:item.id,
   			title:item.title
   		}
   	}"
   >跳转</router-link>
   ```
   特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置

**7、路由的props配置**
作用:让路由组件更方便的收到参数

{
name:‘xinwen’,
path:‘news’, //此处一定不要写斜杠/news
component:News,
//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给News组件
//props:{a:900}

//第二种,props为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给News
//props:true

//第三中,props值为函数,该函数返回的对象中每一组都会通过props传给News
props(route){
	return {
		id:route.query.id,
		title:route.query.title
	}
}

}

**8、<router-link>的replace属性**
	1、作用:控制路由跳转时操作浏览器历史记录的模式
	2、浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,路由跳转的时候默认为push
	3、如何开启replace模式:<router-link replace ...></router-link>

**9、编程式路由导航**

	1、组用:不借助<link-router>实现路由跳转,让路由跳转更加灵活
	2、具体编码:
	```
	//$router的两个API
	this.$router.push({
		name:'xiangqing',
		params:{
			id:xxx,
			title:xxx
		}
	})

	this.$router.replace({
		name:'xiangqinhg',
		params:{
			id:xxx,
			title:xxx
		}
	})
	```
	3、其他API
	this.$router.forward()  //前进
	this.$router.back()  //后退
	this.$router.go()  //参数为正就是前进,参数为负就是后退 

**10、缓存路由组件**

	1、作用:让不被展示的路由组件保持挂载,不被销毁
	2、具体编码
	```
	<keep-alive include="News">  //此处的News是只想要保持不被销毁的组件名
		<router-view></router-view>
	<keep-alive>
	```
**11、两个新的生命周期钩子**

	1、作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态
	2、具体名字:
		(1)activated路由组件被激活时触发
		(2)deactived路由组件失活时触发
**12、路由守卫**

	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、独享守卫
	独享守卫只有前置路由守卫,没有后置路由守卫
	```
	beforeEnter(to,from,next){
		console.log('beforeEnter',to,from)
		if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
			if(localStorage.getItem('school') === 'atguigu'){  //权限控制的具体规则 
				next() //放行
			}else{
				alert('暂无权限查看')
			}
		}else{
			next()
		}
	}
	```
	5、组件内守卫
	```
	//进入守卫,通过路由规则,进入该组件时被调用
	beforeRouteEnter(to,from,next){

	}
	//离开守卫,通过路由规则,离开组件时被调用
	beforeRouteLeave(to,from,next){

	}
**13、路由器的两种工作模式**

	1、对于一个url来说,什么是hash值?——#及其后面的内容都是hash值
	2、hash值不会包含在HTTP请求中,即:hash值不会带给服务器
	3、hash模式:
		(1)地址中永远带着#号,不美观
		(2)若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合格
		(3)兼容性较好
	4、history模式:
		(1)地址干净,美观
		(2)兼容性和hash模式相比略差
		(3)应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题

### 三十八、Vue3新特性
## 1、拉开序幕的setup
	1、理解:Vue3中的一个新配置项,值为一个函数
	2、setup是所有Composition AIP(组合API)的“表演舞台”
	3、组件中所用到的:数据、方法等等,均要配置在setup中
	4、setup函数的两种返回值:
		(1)若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用(重点关注)
		(2)若返回一个渲染函数,则可以自定义渲染内容(了解,因为渲染内容会直接覆盖模板里的内容)
	5、注意点
		(1)尽量不要与Vue2的配置混用
			Vue2配置(data、method、computed等)中可以访问到setup中的属性、方法
			但在setup中不能访问到Vue2的相关配置
			如有重名,以setup优先
## 2、ref函数
	1、作用:定义一个响应式的数据
	2、语法:const xxx = ref(initValue)
		创建一个包含响应式数据的引用对象(reference对象,简称ref对象)
		JS中操作数据:xxx.value
		模板中读取数据不需要.value,直接采用<div>{{xxx}}</div>
	3、备注:
		接受的数据可以是:基本类型,也可以是对象类型
		基本类型的数据:响应式依然是靠Object.defineProperty()的get与set完成的
		对象类型的数据:内部“求助”了vue3中的一个新函数——reactive函数
## 3、reactive函数
	1、作用:定义一个对象类型的响应式数据(基本类型别用它,用ref函数)
	2、语法:const 代理对象 = reactive(被代理对象)接受一个对象(或数组),返回一个代理对象(proxy对象)
	3、reactive定义的响应式数据是深层次的
	4、内部基于ES6的Proxy实现,通过代理对象操作源对象内部数据都是响应式的
## 4、Vue3的响应式
**实现原理:**

	1、通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写、属性的添加、属性的删除等
	2、通过Reflect(反射):对被代理对象的属性进行操作
	3、MDN文档中描述的Proxy与Refelct
	```
	new Proxy(data,{
		//拦截读取属性值
		get(target,prop){
			return Reflect.get(target,prop)
		},
		//拦截设置属性值或者添加新属性
		set(target,prop,value){
			return Reflect.set(target,prop,value)
		},
		//拦截删除属性
		deleteProperty(target,prop){
			return Reflect.deleteProperty(target,prop)
		}
	})

	proxy.name = 'tom
	```
## 5、reactive对比ref
	1、从定义数据角度对比:
		(1)ref用来定义:基本类型数据
		(2)reactive用来定义:对象(或数组)类型数据
		备注:ref也可以用来定义对象(或数组)类型数据,他内部会自动通过reactive转为代理对象
	2、从原理角度对比:
		(1)ref通过Object.defineProperty()和get和set来实现响应式(数据劫持)
		(2)reactive通过使用Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部数据
	3、从使用角度对比:
		(1)ref定义的数据:操作数需要.value,读取数据的模板中直接读取不需要.value
		(2)reactive定义的数据:操作数据与读取数据均不需要.value
## 6、setup的两个注意点
	1、setup执行的时机
		在beforeCreate之前执行一次,this是undefined
	2、setup的参数
		(1)props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性
		(2)context:上下文对象
			attrs:值为对象,包含:组件外部传递过来,但没有在props中声明的属性,相当于this.$attrs
			slots:收到插槽内容,相当于this.$slots
			emit:分发自定义事件的函数,相当于this.$emit
## 7、计算属性与监视
**1、computed函数**

	与Vue2中的computed的配置功能一致
	写法:
	```
	import {computed} from 'vue'
	setup(){
		...
		//计算属性简写
		let fullName = computed(()=>{
			return person.firstName + '-' + person.LastName
		})
		//计算属性完整版
		let fullName = computed({
			get(){
				return person.firstName + '-' + person.LastName
			},
			set(value){
				const nameArr = value.split('-')
				person.firstName = nameArr[0]
				person.lastName = nameArr[1]
			}
		})
	}
	```
**2、watch函数**

	与vue2中的watch配置一致
	有两个小坑:
		(1)监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失败)
		(2)监视reactive定义的响应式数据中的某个属性时:deep配置有效
	```
	//情况一:监视ref定义的响应式数据
	watch(sum,(newValue,oldValue)=>{
		console.log('sum变化了',newValue,oldValue)
	},{immediate:true})

	//情况二:监视多个ref定义的响应式数据
	watch([sum,msg],(newValue,oldValue)=>{
		console.log('sum变化了',newValue,oldValue)
	})

	//情况三:监视reactive定义的响应式数据
	//若watch监视的是reactive定义的响应式数据,则无法获得oldValue!!!
	//若watch监视的是reactive定义的响应式数据,则强制开启深度监视
	watch(person,(newValue,oldValue)=>{
		console.log('person变化了',newValue,oldValue)
	},{immediate:true,deep:false}) //此处的deep配置不再奏效

	//情况四:监视reactive定义的响应式数据中的某个属性
	watch(()=>person.job,(newValue,oldValue)=>{
		console.log('person的job变化了',newValue,oldValue)
	},{immediate:true,deep:true}) //此处的deep配置是奏效的

	//情况五:监视reactive定义的响应式数据中的某些属性
	watch([()=>person.job,()=>person.age],(newValue,oldValue)=>{
		console.log('person的或者age变化了',newValue,oldValue)
	},{immediate:true,deep:true}) //此处的deep配置是奏效的
	```
**3、watchEffect函数**

	(1)watch的套路是:既要知名监视的属性,也要知名监视的回调
	(2)watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,就监视哪个属性
	(3)watchEffect有点像computed:
		但computed注重的是计算出来的值(回调函数的返回值),所以必须要写返回值
		而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值
	```
	//watchEffect所制定的回调函数中用到的数据只要发生变化,则直接重新执行回调
	watchEffect(()=>{
		const x1 = sum.value
		const x2 = person.age
		console.log('watchEffect配置的回调函数执行了')
	})
## 9、自定义hook函数
	1、什么是hook?——本质是一个函数,把setup函数中的composition API进行了封装
	2、类似于vue2中的mixin
	3、自定义hook的优势,复用代码,让setup中的逻辑更清楚易懂

## 10、toRef
	1、作用:创建一个ref对象,其value值指向另一个对象中的某个属性值
	2、语法:const name = toRef(person,'name')
	3、应用:要将响应式对象中的某个属性单独提供给外面使用时
	4、扩展:toRefs与toRef功能一致,但可以批量创建多个ref对象,语法:toRefs(person)——只会把person最外层的那部分属性转变为ref对象,嵌套在深层的不会转化
## 11、其他组合API
**1、shallowReactive与shallowRef**

	(1)shallowReactive:只处理对象最外层属性的响应式(浅响应式)
	(2)shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理
	(3)什么时候使用:
		如果有一个兑现那个数据,结构比较深,但变化时只是外层属性变化===>shallowReactive
		如果有一个对象数据,后续功能不会修改对象中的属性,而是生新的对象来替换===>shallowRef
**2、readonly与shallowReadonly**

	(1)readonly:让一个响应式数据变为只读(深只读)
	(2)shallowReadonly:让一个响应式数据变为只读(浅只读,还可以改变对象里面的内容)
	(3)应用场景:不希望数据被修改时
**3、toRaw与markRaw**

	(1)toRaw:
		作用:将一个由reactive生成的响应式对象转为普通对象
		使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新
	(2)markRaw:
		作用:标记一个对象,使其永远不会再成为响应式对象
		当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能
**4、customRef**

作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显式控制
看官方那个输入防抖案例
**5、provide与inject**

	作用:实现组与后代组件间的通信
	套路:父组件有一个provide选项来提供数据,后代组件有一个inject选项来开始使用这些数据
	具体写法:
		(1)组组件中:
		```
		setup(){
			...
			let car = reactive({name:'奔驰',price:'40W'})
			provide('car',car)
			...
		}
		```
		(2)后代组件中
		```
		setup(props,context){
			...
			const car = inject('car')
			return {car}
			...
		}
		```
**6、响应式数据的判断**

	(1)isRef:检查一个值是否为一个ref对象
	(2)isReacitve:检查一个对象是否是由reactive创建的响应式代理
	(3)isReadonly:检查一个对象是否是由readonly创建的只读代理
	(4)isProxy:检查一个对象是否是由reactive或者readonly方法创建的代理
**7、新组件——Teleport**

	什么是Teleport?——Teleport是一种能够将我们的组件html结构移动到指定位置的技术
	```
	<teleport to="移动位置">
		<div v-if="isShow" class="mask">
			<div class="dialog">
				<h3>我是一个弹窗</h3>
				<button @click="isShow=false">关闭弹窗</button>
			</div>
		</div>
	</teleport>
	```
**7、新组件——Suspense**

	等待异步组件时渲染一些额外内容,让应用有更好的用户体验
	使用步骤:
		(1)异步引入组件
		```
		imoirt {defineAsyncComponent} from 'vue'
		const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
		```
		(2)使用Suspense包裹组件,并配置好default与fallback
		```
		<template>
			<div class="app">
				<h3>我是app组件</h3>
				<Suspense>
					<template v-slot:default>
						<Child/>
					</template>
					<template v-slot:fallback>
						<h3>加载中.......</h3>
					</template>
				</Suspense>
			</div>
		</template>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值