- template:
- 5.容器与实例对应规则
- 7.模板语法
- 8.数据绑定
- 9.el和data的两种写法
- 10.MVVM
- 11.Object.defineProperty()
- 12.数据代理(劫持):通过一个对象代理对另一个对象属性的操作
- 13.vue中的数据代理
- 14.事件处理
- 15.事件修饰符
- 16.键盘事件
- 19-20.计算属性
- 22-24.监视属性
- 25.watch vs computed
- 26.class的绑定样式
- 27.style的绑定样式
- 28.条件渲染 v-show / v-if
- 29-32.遍历渲染 v-for
- 33-37 数据监视原理 / 数据劫持
- 38.各种表单的v-model使用
- 39.过滤器
- 40-41.v-text v-html
- 42.v-cloak
- 43.v-once
- 44.v-pre
- 45-47.自定义指令
- 48.生命周期
- 53.模块、组件
- 54-55.vue的组件
- 57.VueComponent的构造函数
- 59.原型链继承
- 61-63.脚手架
- 65.Vue的ref属性
- 66.Vue的props属性
- 67.mixin混合
- 68.插件
- 69.样式的scoped属性
- 78.webStorage
- 80-82.自定义事件
- 84-85.全局事件总线
- 90.$nextTick
- 91-95.Vue封装的过度与动画
- 96-97.配置代理
- 101.vue-resource
- 102-104.插槽
- 105-116.vuex
- 117-133.路由
- 133.node!!
- 138.vite
- 139.工程结构
- 141.setup
- 142-143.引用对象
- 144.reactive函数
- 147.ref/reactive对比
- 145.Vue2响应式
- 146-147.Vue3响应式
- 149.setup的两个注意点
- 150.计算属性
- 151-153.监视
- 154.新增:watchEffect函数
- 155.生命钩子
- 156.hook
- 157.toRef / toRefs
- 158.shallowReactive与shallowRef
- 159.readonly与shallowReadonly
- 160.toRaw与markRaw
- 161.customRef
- 162.provide与inject
- 163.判断响应式数据
- 164.组合式API的优点
- 166.teleport
- 167.suspense
- 168.全局API的转移
template:
/*main.js*/Vue.config.productionTip = false //关闭提示Vue.filter('过滤器名',function(){}) //配置全局过滤器Vue.directive('自定义指令名',{}) //配置全局自定义指令名Vue.directive('自定义指令名',function(){}) //配置全局自定义指令名/*组件中的script标签*/export default{component:{School,}//数据根目录data(){return {}},//计算属性computed:{computed1(){ return 'xxx' },computed2:{get(){},set(){}}},//方法methods:{showInfo(){}},//局部过滤器filters:{filter1(){}},//局部自定义指令directives:{directive1(){}, //简写directive2:{bind(){},inserted(){},update(){}}},//接收传递的属性props:[],//混合mixins:[],}
5.容器与实例对应规则
容器和vue实例一一对应,一个实例对应多个容器则只操作第一个容器,多个实例对应一个容器则接受第一个容器
真实开发中只有一个vue实例,并且配合着组件一起使用
7.模板语法
两大类
1.插值语法:用于解析标签体内容
写法:{{xxx}},xxx是js表达式,读取data中所有属性
2.指令语法:用于解析标签(包括标签属性、标签体内容、绑定事件)
写法:v-bind:href=’xxx’ / :href=’xxx’
xxx是js表达式,读取data中所有属性
<!-- 这里的url不再是字符串,而是js表达式,去实例的data中寻找变量--!><a v-bind:href='url'/><a :href='url'>
8.数据绑定
1.单向绑定(v-bind):数据只能从data流向页面
2.双向绑定(v-model):数据双向流动
双向绑定只能用在表单类元素上(带value属性的元素),且v-model绑定的是value值,所以v-model:value=’xxx’可以简写为v-model:’xxx’
9.el和data的两种写法
el的另外一种写法:vue实例的原型上的$mount方法绑定el
/*main.js*/new Vue({el:'#root', //vue实例掌管的容器data:{name:'...'} //对象式})const v = new Vue({data(){return{name:'xxx'}} //函数式})//v.__proto__.$mount('#root')v.$mount('#root') //同等与在vue配置对象设置el
data必须使用函数式,避免组件复用时变量存在引用关系,且只能写一般函数,函数内部this指向vue实例,说明是vue实例调用了data函数
10.MVVM
M:模型(Model):data中的数据
V:视图(View):模板代码
VM:视图模型(ViewModel):Vue实例 (其中有dom listeners和data bindings)
1.data中所有的属性,最后都出现在了vm身上(数据代理)
2.vm身上所有的属性及vue原型上所有属性,在Vue模板中都可以直接使用
11.Object.defineProperty()
方法接收三个属性:对象、属性、配置项
Object.defineProperty(person,'age',{value:18,enumerable:true, //属性是否可以遍历,默认值falsewritable:true, //属性是否可以修改,默认值falseconfigurable:true //属性是否可以删除,默认值false})
高级属性:getter、setter
//需求:age的值取决于number变量,且number变量会改变,需要age不重新设置则随之改变Object.defineProperty(person,'age',{//当person的age属性被读取时,get函数(getter)就会被调用,且返回值就是age的值get(){//实现age读取number的值return number},//当person的age属性被修改时,set函数(setter)就会被调用,且会受到修改的具体值set(value){//实现age被修改时,会同时改变numbernumber = value}})
12.数据代理(劫持):通过一个对象代理对另一个对象属性的操作
13.vue中的数据代理
1.Vue中的数据代理:通过vm对象来代理data对象中的属性的操作
2.Vue中数据代理的好处:更加方便地操作data中的数据
3.基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上,为每一个添加到vm上的属性,都指定一个getter/setter,在getter/setter内部去操作data中对应的属性
14.事件处理
1.使用v-on:click=’xxx’或@click=’xxx’ 绑定事件,xxx是回调;
2.事件的回调需要配置在methods对象中,最终会直接复制到vm上;(data做数据代理,methods没有)
3.methods中配置的函数,是被Vue所管理的函数,一般函数this指向是vm或组件实例对象;
4.@click=’demo’ 和 @click=’demo($event)’ 效果一致,但后者可以传参
15.事件修饰符
跟在事件后.调用,修饰事件,示例:@click.prevent
1.prevent:阻止默认事件(常用)
2.stop:阻止事件冒泡(常用)
3.once:事件只触发一次(常用)
4.capture:使用事件的捕获模式
5.self:只有event.target是当前操作的元素时才触发事件
6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕
16.键盘事件
@keyup @keydown两个事件监听键盘,一般使用@keyup
1.vue中常用的按键别名(事件修饰符):
回车 enter
删除 delete
退出 esc
空格 space
换行 tab (特殊,需要配合keydown使用)
上 up
下 down
左 left
右 right
2.vue未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)
3.系统修饰键(用法特殊):ctrl、alt、shift、meta
配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发
配合keydown使用:正常触发事件
4.Vue.config.keyCodes.自定义键名 = 键码,自定义按键别名
19-20.计算属性
1.定义:要用的属性不存在,需要通过已有属性计算(包括vm外的数据,但是此时数据不能管理)
2.原理:底层借助了Object.defineProperty方法的getter和setter
3.get函数执行时机:
初次读取时执行一次
依赖数据发生改变时会被再次调用
4.优势:与methods实现相比,内部又缓存机制(复用),效率更高,调试方便
5.备注:
计算属性最终会出现在vm上,直接读取即可
如果计算属性要被修改,必须写setter,且setter修改依赖数据(从再次执行get得到计算属性,不能直接修改计算属性)
//完整:new Vue({computed:{computed1:{get(){},set(){}}}})//只有getter的简写:new Vue({computed:{computed1(){}}})
22-24.监视属性
1.当被监视的属性变化时,handler回调函数自动调用
2.监视的两种写法:
new Vue时传入watch配置
通过vm.$watch方法监视
3.watch默认监视一层(优化性能),通过配置对象中设置deep:true开启深度监视监视多层数据结构
4.简写:配置对象中只有handler时,可以使用handler函数直接替代配置对象的位置。函数必须为一般函数。
new Vue({watch:{isHot:{immediate:true, //初始化调用handlerdeep:true, //开启深度监视handler(newValue,oldValue){}}}})vm.$watch({handler(){}})
25.watch vs computed
1.computed更轻便,watch更灵活(异步任务等)
两个原则:
1.vue管理的函数最好写成普通函数,this指向是vm 或 组件实例对象
2.不被vue管理的函数最好写成箭头函数,更深层次的函数的this是vm 或 组件实例对象
26.class的绑定样式
1.字符串写法:样式类名不确定,需要动态指定
2.数组写法:要绑定的样式个数不确定,名字也不确定
3.对象写法:要绑定的样式个数确定,名字确定,但要动态决定用不用
<div :class='mood' @click='changeMood'/><div :class='classArr'/><div :class='classObj'/>/*classArr = ['atguigu1','atguigu2']classObj = {atguigu1:false,atguigu2:true}*/
27.style的绑定样式
两种方式:对象式、数组式
对象式:jsx的双括号,外层的括号改成引号
数组式:多个style对象放到一个数组里
28.条件渲染 v-show / v-if
v-show设置display:none进行隐藏,适用于切换频率高的场景
v-if挂载/卸载节点,还有v-else-if v-else,但是需要连续使用,不能中途插入其他节点
template(空节点)配合v-if
29-32.遍历渲染 v-for
v-for= ‘(a,b) in xxx’ 可遍历数组、对象、字符串和指定次数
需要使用:key指定key,没有指定key则自动指定index为key
列表过滤:使用computed重新计算属性实现过滤
33-37 数据监视原理 / 数据劫持
1.Vue会监视data中所有层次的数据
2.监测对象数据
通过setter实现监视,且要在new Vue时就传入要监测的数据
如需给后添加的属性做响应式,需要用到如下API
Vue.set(taget,propertyName/index,value) 或
vue.$set(taget,propertyName/index,value)
3.监测数组数据
通过包裹更改数组的方法实现,本质是:(1)调用原生api更改数组,(2)重新解析模板,进而更新页面
4.vue更新数组的方法
APIs:push() pop() unshift() shift() splice() sort() reverse()
Vue.set() vue.$set()
数组替换
注意:Vue.set()和vue.$set()不能给vm或vm的根数据对象(vm._data)添加属性
(后续可以用Vue.set做子传父的props?类似this.setState?)
响应式:数据变化带动页面变化
Vue的数据劫持:创建Vue实例的配置对象的data和vm的根数据对象并非直接赋值,而是经过加工后赋值,加工的过程称为数据劫持。vue的数据劫持过程模拟如下
//模拟实例化Vue配置对象的数据let data = {name:'xxx',age:18}//第一步:数据劫持的模拟const obs = new Observer(data)//模拟实例化let vm = {}vm._data = data = obs //第二步:赋值给vm._data//构造函数模拟劫持function Observer(obj){const keys = Object.keys(obj)keys.forEach(k => {Object.defineProperty(this,k,{get(){return obj[k]},set(value){console.log('此处模拟解析模板,生成虚拟dom,对比算法')obj[k] = value}})})}
38.各种表单的v-model使用
1.text框:v-model收集的是value值,用户输入也是value值
2.radio框:v-model收集的是value值,需要给标签配置value值,且同组radio配置相同name
3.checkbox框:
没有配置value属性,那么收集的是checked值
配置了value属性:
v-model初始值是非数组,收集的还是checked值
v-model初始值是数组,收集的是value值的数组
4.v-model的三个修饰符:
lazy:失去焦点再收集数据,适用于textarea
number:输入字符串转为有效数字,适用于text=number
trim:输入首尾空格过滤
39.过滤器
定义:对要显示的数据进行特定格式化再显示(适用于一些简单逻辑的处理)
语法:
1.注册过滤器:Vue.filter(name,callback) 或 new Vue({filters:{}})
2.使用过滤器 {{XXX | 过滤器名}} 或 v-bind:属性 = ‘xxx | 过滤器名’
备注:
1.过滤器也可以接收额外参数,多个过滤器也可以串联
2.并没有改变原本的数据,是产生新的对应的数据
20211114补充:fliter和computed都不能接收参数,如格式化(判断)需要用到结构中的数据(v-for中item)还是得用到methods。
40-41.v-text v-html
1.作用:v-html向指定节点中渲染包含html结构的内容,v-text则不包含结构
2.与插值语法的区别
v-html/v-text会替换掉节点的所有内容,插值语法则不会
v-html可以识别html结构
3.v-html有安全性问题
网站上动态渲染任意HTML是危险的,容易导致XSS攻击
一定要在可信的内容上使用v-html,不要在用户提交内容上使用
cookie:本质是字符串,格式是对象
42.v-cloak
1.本质是一个特殊属性(没有值),Vue实例创建完毕并接管容器后会删掉v-cloak属性
2.使用css配合v-cloak可以解决网速慢页面展示出{{xxx}}的问题
43.v-once
1.v-once(没有值)所在节点在初次动态渲染后,就视为静态内容了
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能
区别于事件修饰符.once : 只能触发一次事件
44.v-pre
跳过其所在节点的vue解析过程,加快编译速度,可用于没有指定语法和插值语法的节点
45-47.自定义指令
1.两种定义语法与配置对象的三个回调:
//局部new Vue({directives:{directive1(){}, //bind和update时调用directive2:{bind(){}, //指令与元素成功绑定时调用inserted(){}, //指令所在元素被插入页面时调用update(){} //指令所在模板结构被重新解析时调用}}})
2.指令定义时不加v-,使用时要加v-;指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名
48.生命周期
环节1.初始化生命周期、监听事件、渲染方法,但数据代理还没开始
钩子1:beforeCreate
环节2.初始化:数据监测、数据代理
钩子2:created
(这里的create是指create数据监测、数据代理,这里的this是vm)
环节3.检查el(有则继续无则挂起)、template(有则template替换#root,无则使用#root);vue开始解析模板,生成虚拟DOM,页面还不能显示分析好的内容,而是显示原始内容
钩子3:beforeMount (此阶段对DOM的操作最终都被覆盖)
环节4.虚拟DOM转为真实DOM并把真实DOM存到vm.$el,
重要钩子4:mounted
遇到更新数据的情况
钩子5:beforeUpdate(数据与页面不同步)
环节:生成新虚拟dom,新旧虚拟DOM比较,完成页面更新,即model -> view的更新
钩子6:updated(数据与页面同步)
vm.$destory()用于销毁vm实例(但真实DOM还会保留)
重要钩子7:beforeDestory(此时vm的data、methods、指令都处于可用状态,但不再触发更新流程,只做收尾工作)
环节:vm实例卸载
钩子8:destoryed
钩子9:this.$nextTick-下次DOM渲染之后执行其中操作
钩子10:activated
钩子11:deactivated
53.模块、组件
模块定义:向外提供特定功能的js程序,是js文件。作用:复用js,简化js编写,提高js运行效率
组件定义:实现应用中局部功能代码和资源的集合。作用:复用编码,简化项目编码,提高运行效率
54-55.vue的组件
/*第一步:创建组件1.el不要写:最终所有的组件都经过vm的管理,由vm中的el决定服务哪个容器2.data必须写成函数:避免组件被复用时,数据存在引用关系(独立)const school = Vue.extend(options)可简写为const school = options*/const school = Vue.extends({template:``,data(){return {}}})//第二步:局部注册组件new Vue({el:'#root',components:{school}})//第二步:全局注册组件Vue.component('school',school)//第三步:引用组件-以标签的形式放入#root中,如<school></school>
组件名:
一个单词:首字母大小写均可
两个单词:kebab-case CamelCase均可
57.VueComponent的构造函数
1.school组件本质是一个名为VueComponent的构造函数,且不是程序员定义的,是Vue.extend生成的
2.我们只需写
3.特别注意:每次调用Vue.extend 返回的都是一个全新的VueComponent
4.关于this指向:
(1)组件配置中:data函数、methods中的函数、watch中的函数、computed中的函数,它们的this均是 VueComponent实例对象;
(2)new Vue(options)配置中:data函数、methods中的函数、watch中的函数、computed中的函数,它们的this均是 Vue实例对象
59.原型链继承
VueComponent.prototype.proto === Vue.prototype
组件实例对象沿着原型链可以找到VueComponent.prototype,进而找到Vue.prototype,再进而找Object.prototype,由此组件实例对象可以访问到Vue原型的属性和方法
组件实例对象是小型的vm,和vm相比,组件实例对象的配置对象不能写el,data必须写函数形式
61-63.脚手架
npm i -g @vue/cli //安装脚手架
vue create xxx //创建脚手架
render函数:
1.vue.js与vue.runtime.xxx.js的区别:
vue.js是完整版的Vue,包含:核心功能+模板解析器
vue.runtime.xxx.js是运行版的Vue,只包含:核心功能,没有模板解析器
通过import Vue from ‘Vue’引入的是vue.runtime.xxx.js,可以在vue的package.js中module属性查看引入的文件
2.因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容
65.Vue的ref属性
用作给元素或子组件注册引用信息,获取到元素的真实DOM元素或子组件的组件实例对象(虚拟DOM)
字符串的形式标识,通过this.$refs获取
66.Vue的props属性
传递:标签内传递属性
接收:
//1.简单接收props:['name']//2.限制类型props:{name:String,age:Number}//3.限制类型、必要性,指定默认值props:{name:{type:string,require:true,default:'Tom' //必要性和默认值同时使用无意义}}
props是只读的(之前的react项目做得挺有问题的),vue底层会监测对props的修改(浅层监测,只监测引用),修改则警告。如业务需求,则应该复制props到data中再修改data(props的声明优先级高于data)
props也有数据劫持
67.mixin混合
功能:可以把多个组件共用的配置提取成一个混入对象
全局:Vue.mixin(xxx)
局部:通过配置项的mixins属性: mixins:[‘xxx’]
68.插件
功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据
//定义插件:plugins.jsexport default {install(){//添加全局过滤器,添加全局指令,配置全局混入,添加实例方法等}}//使用:在main.js创建vm之前引用plugins.jsVue.use(plugins)
69.样式的scoped属性
限定样式为局部样式,仅应用于当前组件
npm view webpack versions //查看webpack的所有版本
78.webStorage
1.存储内容大小一般支持5MB(不同浏览器不同)
2.浏览器通过window.sessionStorage和window.localStorage属性实现本地存储机制
3.相关API:
xxxxStorage.setItem(‘key’,’value’)
xxxxStorage.getItem(‘key’)
xxxxStorage.removeItem(‘key’)
xxxxStorage.clear()
4.备注:
1.sessionStorage存储的内容会随着浏览器窗口关闭而消失
80-82.自定义事件
1.一种组件间通信的方式,适用于:子组件===>父组件
2.使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)
3.绑定自定义事件:
第一种方式:在父组件中,
第二种方式:在父组件中,给组件上ref,在mounted钩子里 this.$refs.xxx.$on(‘atguigu’,this.test)
若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法
4.触发自定义事件 this.$emit(‘atguigu’,数据)
数据可以传多个,接收可以以 …arg的形式接收为数组
5.解绑自定义事件 this.$off(‘atguigu’)
6.组件上也可以绑定事件默认是绑定自定义事件,如绑定原生DOM事件需要.native修饰符
7.注意:通过this.$ref.xxx.$on(‘atguigu’,回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出现问题。
谁绑定的事件,谁提供回调。绑定给谁的,谁调用
应用:用于替代父传子的函数,原先需要调用函数的地方改为触发自定义事件
84-85.全局事件总线
1.一种组件间通信的方式,适用于任意组件间通信。
原理:
存在一个变量,所有组件实例对象都能对其操作(绑定事件及触发事件),其自身也有$on $emit $off等方法。
当需要跨组件通信时,接收者对其绑定自定义事件,发送者触发其自定义事件,则能通过其进行通信。
2.安装全局事件总线
new Vue({...beforeCreate(){Vue.prototype.$bus = this //安装全局事件总线}})/*条件1:所有组件实例和Vue实例都能调用 ===> 将其定义在Vue原型条件2:其能调用$on $emit $off等在Vue原型的方法 ===> 让其成为组件实例或Vue实例(此处为Vue实例)*/
3.使用事件总线
1.接收数据 在mounted钩子中,this.$bus.$on(‘xxx’,()=>{})
2.提供数据 this.$bus.$emit(‘xxx’,data)
4.最好在接收数据(绑定事件)的组件中,用beforeDestory钩子中,用$off去解绑当前组件所用到的事件
90.$nextTick
语法:this.$nextTick(回调函数)
作用:在下一次DOM更新结束后执行其指定的回调
场景:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。(因为Vue会将整个回调函数的语句都执行完再进行模板的重新渲染)
例如:新出现的input框获取焦点:this.$refs.inputTiltie.focus()
91-95.Vue封装的过度与动画
1.作用:再插入、更新或移除DOM元素时,在合适的时候给元素添加样式类名
2.写法
1.准备好样式:三方库、动画、过渡
/*三方库:npm i animate.css --save只需在template设置name和进场离场过程样式即可*/<template><transitionname='animate_animated animate_bounce'enter-active-class='xx'leave-active-class='xx'><h1 v-show='isShow'>xxx</h1></transition></template>/*动画*/<style>.name-enter-active{animation:atguigu 1s}.name-leave-active{animation:atguigu 1s reverse}@keyframe atguigu{from{transform:translateX(-100%)}to{transform:translateX(0)}}</style>/*过渡*/<style>.name-enter-active,.name-leave-active{animation:1s}.name-enter,.name-leave-to{transform:translateX(-100%)}.name-leave,.name-enter-to{transform:translateX(0)}</style>
元素进入/离开的样式:
v-enter/leave:进入/离开的起点,只有短暂的一帧生效
v-enter/leave to:进入/离开的终点,过程持续生效
v-enter/leave-active:进入/离开的过程,一般用来指定时间
v-enter-to:进入的终点
2.使用transition包裹过渡元素并配置name
<transition><h1 v-show='isShow'>xxx</h1></transition>
3.若有多个元素则应该使用transition-group标签,且每个元素配置key属性
appear属性表示初次渲染执行进场动画
96-97.配置代理
方法一:在vue.config.js中添加如下配置:
devServer:{proxy:"http://localhost:5000"}
优点:配置简单,请求资源时直接发给前端(8080)即可
缺点:不能配置多个代理,不能灵活控制请求是否走代理
工作方式:优先匹配前端资源,前端不存在则转发请求给服务器
方法二:
devServer:{'/atguigu':{target:'http://localhost:5000',pathRewrite:{'^/atguigu':''},ws:true, //websocketchangeOrigin:true //用于控制请求头中的host值}}
优点:可以配置多个代理,且可以灵活的控制请求是否走代理
缺点:配置略微繁琐,请求资源时必须加前缀
React是在package.json中配置代理
101.vue-resource
vue1.0使用的一个用于发送ajax请求的插件库,现在不推荐使用,推荐使用axios
import vueResource from 'vue-resource'Vue.use(vueResource)//由此Vue实例和组件实例多了一个$http属性,通过$http.get() $http.post()发送请求
102-104.插槽
1.作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信方式,适用于父组件===>子组件,使得组件的复用更灵活
2.分类:默认插槽、具名插槽、作用域插槽
3.使用方式
1.默认插槽:
//父组件:<Category><div>html结构</div></Category>//子组件:<template><slot>插槽默认内容</slot></template>
2.具名插槽:具有名字的插槽
//父组件,template包裹多个结构一并传入,或分别传入,分别传入用v-slot:xxx<Category><template v-slot:footer><div>html结构</div></template></Category>//子组件<template><slot name='footer'>插槽默认内容</slot></template>
3.作用域插槽:数组在组件自身,但根据数据生成的结构在父组件中
//父组件:Vue3把scope='footer'替换成v-slot='footer',接收到一个对象<Category><template slot-scope='anywords'><div>html结构</div></template></Category>//子组件 把data中的games往父组件中自己的起始标签和结束标签中传<template><slot :games='games'>插槽默认内容</slot></template>
105-116.vuex
1.搭建环境
//创建store/index.jsimport Vue from 'vue'import Vuex from 'vuex'const actions = {incrementOdd(context,value){ //context是精简版的stroe,内含dispatch,commit,state等if(context.state.sum % 2){context.commit('INCREMENT',value)}}}const mutations = {INCREMENT(state,value){state.sum += value}}const state = {sum:0}const getters = {bigSum(state){return state.sum*10}}export default Vuex.Store({actions,mutations,state,getters})//main.js中配置storeimport store from './store'new Vue({el:'#root',....store,})
2.组件中读取vuex中的数据:this.$store.state.xxx / $store.state.xxx
3.组件中修改vuex中的数据:this.$store.dispatch(‘action方法名’,value) / this.$store.commit(‘mutation方法名’,value)
4.当state中的数据需要经过加工后再使用,可以使用getters加工,读取时向getters读取:this.$store.getters.xxx / $store.getters.xxx
5.四个map的方法
1.mapState
2.mapGetters
3.mapActions
4.mapMutations
/*mapState / mapGetters 方法:映射state和getters中的数据,方便读取数据生成一个对象,里面对应每个值为mappedState/mappedGetters函数mapActions / mapMutations 方法:生成和actions/mutations沟通的dispatch/commit方法即...mapActions({increment:'JIA'})生成increment(value){this.$store.dispatch('JIA',value)}此时注意value,需要绑定事件时传入value,否则value是event*/import { mapState, mapGetters, mapActions, mapMutations } = 'vuex'computed:{//对象式写法,从state的sum中读取属性到computed的he...mapState({he:'sum',xuexiao:'school',xueke:'subject'})//数组式写法,当state和computed属性同名...mapState(['sum','school','subject'])...mapGetters({dahe:'bigSum'})...mapGetters(['bigSum'])}methods:{//同样有对象写法和数组写法...mapActions({})...mapActions([])...mapMutations({})...mapMutations([])}
6.模块化vuex
/*store/index.js*/const personOptions = {namespaced:true,actions = {},mutations = {},state = {},getters = {}}export default Vuex.Store({modules:{personAbout:personOptions}})/*组件:四个map方法,方法新增第一个参数:vuex模块化的名字*/import { mapState, mapGetters, mapActions, mapMutations } = 'vuex'computed:{...mapState('personAbout',[])...mapGetters('personAbout',['bigSum'])}methods:{...mapActions('personAbout',{})...mapMutations('personAbout',{})}/*组件普通写法:getter写成'personAbout/personList',其他均多一级即可*/
117-133.路由
路由器router监视路径。路由是一组映射关系,key为路径,value为function(后端)或component(前端)
基本使用
/*安装vue-router插件编写router/index.js 用于配置路由关系*/import VueRouter from 'vue-router'import...export default new VueRouter({//mode:'history', //默认hash模式routes:[{path:'/home',component:Home},{path:'/about',component:About,children:[{path:'news', //二级路由path不写斜杠component:News}]},]})/*main.js*/import VueRouter from 'vue-router'import router from './router'Vue.use(VueRouter)new Vue({...,router,})/*React中的Link组件,在Vue中是<router-link>,to属性需要把多级路径写全,可添加replace属性:表示历史记录replaceReact中的Route组件,在Vue中是<router-view>,且不需要额外配置关系*/<router-link class='' active-class='' to='/home'/><router-view>/* vue3 *///router.jsimport { createRouter } from 'vue-router'const routes = [{},{}]const router = createRouter({routes})export default router//main.jsimport router from './router'createApp(App).use(router).mount(#app)
注意:
1.被切走的组件实际是卸载了
2.每个组件实例都有自己的$route属性,存储着自己的路由信息,且有共同的$router属性
传参1:query传参:跳转的参数在实例的$route.query属性中
//跳转路由并携带query参数,to的字符串写法<router-link :to='`/home/message/detail?id=${m.id}&title=${m.title}`'/>//跳转路由并携带query参数,to的对象写法<router-link:to="{path:'/home/message/detail',query:{id:m.id,title:m.title}}"/>
路由命名:to的对象写法path可以使用name替代,但需要事先在router/index.js配置name。
传参2:params传参:跳转的参数在实例的$route.params属性中
/*在router/index.js配置路径时需要声明接收params参数*/{name:'xiangqing',path:'detail/:id/:title', //使用占位符接收params参数component:Detail}/*传参跳转to的对象式写法不能用path,必须用name*/<route-link :to='`/home/message/detail/${m.id}/${m.title}`'/><route-link:to="{name:'xiangqing',params:{id:m.id,title:m.title}}"/>
传参的使用:
1.在模板中直接$route.query/params使用,没有响应式(也不需要),但刷新会丢失;
2.传到data里,会多了响应式(vue3可以解决)
以props的形式接收路由传参:在router/index.js配置props属性
方法一:值为对象,该对象中所有的key-value都会以props的形式传给Detail组件(写死)
props:{a:1,b:’hello’}
方法二:值为布尔值,若布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传给Detail组件
方法三:props为函数,return一个对象,对象中的属性会以props分别传给组件,且函数接收$route
編程式路由:方法均在VueRouter的原型上,this.$router是VueRouter的实例。push/replace均传入配置对象,即route-link中to的对象式写法的对象
this.$router.push()
this.$router.replace()
this.$router.back()
this.$router.forward()
this.$router.go(n) //跳转n步
缓存路由组件:让不展示的路由保持挂载,不被销毁
<keep-alive :include='["News"]'> //News是组件名,即实例中配置对象的name属性。写一个直接写字符串<router-view /></keep-alive>
两个生命周期钩子:用于捕获路由组件的激活状态
activated:路由组件被激活时触发
deactivated:路由组件失活时触发
路由守卫:全局守卫、独享守卫、组件内守卫
beforeEach全局前置路由守卫——初始化的时候,每次路由切换之前被调用,用于检验权限
afterEach全局前置路由守卫——初始化的时候,每次路由切换之后被调用,用于跳转后修改标题
beforeEnter独享路由守卫:在单个route的配置对象中设置,只有前置
beforeRouteEnter组件内守卫进入前,写在组件实例的配置根目录
beforeRouteLeave组件内守卫离开前,写在组件实例的配置根目录
/*router/index.js*/import VueRouter from 'vue-router'const router = new VueRouter({routes:[{...beforeEnter:(to,from,next)=>{}, //独享路由守卫,只有前置}]})//全局前置路由守卫router.beforeEach((to,from,next)=>{//to/from是vueRouter实例,next函数表示通行})//全局后置路由守卫router.afterEach((to,from)=>{})export default router/*组件内路由:组件实例*/<script>export default {beforeRouteEnter(to,from,next){},beforeRouteLeave(to,from,next){}}</script>
133.node!!
138.vite
npm init vite-app <project-name>cd <project-name>npm installnpm run dev
139.工程结构
vue3的main.js,对比p9
//不再引入vue构造函数,而是createApp工厂函数。(构造函数new调用,工厂函数不用)import { createApp } from 'vue'import App from './App.vue'//创建应用实例对象——app(类似于之前Vue2中的vm,单app比vm更“轻”)const app = createApp(App)app.mount('#app')/*vue2的写法*/new Vue({render: h => h(App)}).mount('#app')
141.setup
vue3的一个新配置,值为一个函数。函数内部写compositionAPI(组合API)以及数据、方法等。
setup函数的返回值:
1.对象,对象中的属性、方法都可以在模板中直接使用
2.渲染函数,自定义渲染内容,渲染内容覆盖模板 (jsx?)
注意尽量不要与vue2混用,也不能是async函数
142-143.引用对象
引用实现(RefImpl)的实例,用来定义一个响应式的数据(固定的数据直接写?)
import { ref } from 'vue'const xxx = ref(initValue)xxx.value = ''
创建一个包含响应式数据的引用对象(reference对象),操作数据不能直接操作,需要通过value属性操作,但模板中读取则直接读取xxx
ref函数可以接收基本类型和对象对象,对于基本类型,响应式的实现基于Object.defineProperty()的get/set;对于对象类型,则基于reactive函数(RefImpl的value是一个Proxy实例)
144.reactive函数
作用:定义一个对象类型的响应式数据(基本类型用ref函数)
语法:const 代理对象= reactive(被代理对象) 接收一个对象(数组),返回一个代理器对象(proxy对象)
reactive定义的响应式数据是深层次的,内部基于ES6的Proxy实现,通过代理对象操作源对象内部数据都是响应式的
147.ref/reactive对比
定义数据:ref用于定义基本类型数据,reactive用于定义引用类型数据
原理:ref通过Oject.defineProperty()的get和set实现响应式(数据劫持),reactive通过Proxy实现响应式(数据劫持),并通过Reflect操作源对象内部的数据
使用角度:ref操作数据需要.value,模板读取不需要;reactive均不需要
145.Vue2响应式
存在问题:
1.新增、删除属性界面不更新。解决:通过this.$set/$delete Vue.set/delete对响应式数据做修改
2.直接通过下标修改数组,界面不会更新。解决:数组7个api,this.$set
146-147.Vue3响应式
通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写,属性的添加、删除等
通过Reflect对象(反射):对源对象的属性进行操作
/*模拟proxy实现响应式*/new Proxy(data,{//读取属性值触发get(target,propName){//进行响应式操作return Reflect.get(target,propName)},//修改属性值,或新增属性触发set(target,propName,value){//进行响应式操作Reflect.set(target,propName,value)},//删除属性触发deleteProperty(target,propName){//进行响应式操作Reflect.deleteProperty(target,propName)}})
149.setup的两个注意点
setup执行的时机,在beforeCreate之前执行,this是undefined
setup的参数:
props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性(在setup外用props生命)
context:上下文对象
attrs:值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性,相当于this.$attrs
slots:收到的插槽内容,相当于this.$slot
emit:分发自定义事件的函数,相当于this.$emit(可能需要emits声明接收自定义事件)
150.计算属性
引入computed函数,setup内部调用computed函数,参数为函数(简写)或对象(完整写法),computed函数返回计算属性的值。
151-153.监视
引入watch函数,setup内部调用watch函数。参数为三个,第一个是监视的目标;第二个是触发监听的回调(回调默认接收newValue、oldValue),第三个(选配)为配置对象。
情况一:监听ref所定义的一个响应式数据
情况二:监听ref所定义的多个响应式数据,则第一个参数改为数组,数组内每个元素为监听的目标
情况三:监听reactive所定义的一个响应式数据,此时无法获得正确的oldValue,且强制开启deep监听
情况四:监听reactive所定义的响应式数据中的某个属性,则第一个参数改为回调,回调返回某个属性
情况五:监听reactive所定义的响应式数据中的某些属性,则第一个参数改为数组,数组内每个元素为回调,回调返回某个属性
情况六:监听reactive所定义的响应式数据中的某个引用型数据,此时配置对象的深度监听有效,但无法获得正确的oldValue
监听的目标是RefImpl / Proxy对象(而不是值),所以ref定义的基本类型数据是监听变量本身(RefImpl对象),而定义的引用类型数据是变量的value属性(Proxy对象)。
154.新增:watchEffect函数
watchEffect函数传入两个参数:回调、配置对象
watchEffect和computed的比较:
两者都能自动追踪需要监听的变量
computed注重的计算结果,所以回调函数必须有返回值,而watchEffect更注重过程,所以不用写返回值。而且watchEffect很像React的useEffect
155.生命钩子
beforeDestory ===》beforeUnmount
Destoryed ===》unmounted
通过配置项的形式写生命钩子:写在setup外面。
通过组合式API形式的生命周期钩子:从vue解构引入,写在setup里面:
没有了beforeCreate / created 对应API钩子
beforeMount /mounted / beforeUpdate / updated / beforeUnmount / unmounted 改成组合式API形式的生命钩子:都在前面加上on
同时使用两个形式,组合式API的调用早一些
156.hook
组合API:API指ref、reactive、计算属性与监视、生命周期等。组合是指允许在hook对其进行组合
hook:一个函数,把setup函数中舒勇的compositionAPI进行封装
自定义hook优势:复用代码,让setup逻辑更清楚易懂
157.toRef / toRefs
作用:创建一个ref对象,其value值指向另外一个对象中的某个属性
语法:const name = toRef(person,'name')
应用:要将响应式对象中的某个属性单独提供给外部使用,此时直接传递会丢失响应式,新建ref会丢失映射关系,toRef相当于建立新变量和原变量的映射关系且新变量是响应式的。
拓展:toRefs与toRef功能一致,但可以批量创建多个ref对象(只创建一层),语法:toRefs(person) 函数返回一个对象,里面每组key-value是原对象的属性和ObjectRefImpl
158.shallowReactive与shallowRef
shallowReactive只处理对象最外层属性的响应式(浅响应式)
shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理
应用场景:
shallowReative:对象数据结构比较深,但只变化外层属性
shallowRef:后续不会改变对象数据的属性,而是直接替换新的对象
159.readonly与shallowReadonly
readonly:让一个响应式数据变为只读的(深只读)
shallowReadonly:让一个响应式数据变为只读的(浅只读)
应用场景:不希望数据被修改
与非响应式区别:非响应式是能够修改但不会重新渲染页面,对比toRaw和markRaw
160.toRaw与markRaw
toRaw函数接收一个reactivce生成的响应式数据,返回对应的普通对象。对普通对象进行任何操作都不会引起页面更新
markRaw函数接收一个数据,返回永不会被生成响应式的数据(即使后续交由reactive也不会被生成响应式)。
应用场景:数据源不可变时,避免生成响应式可以提高性能。
161.customRef
自定义ref,允许自己创建一个容器(代替ref包裹数据),容器内调用customRef实现响应式的逻辑。
customRef是一个函数,内部接收一个函数,内部函数接收track、trigger,返回一个对象,对象包含getter/setter。track函数用于getter通知追踪value,trigger函数用于setter通知实例重新解析模板
function myRef(value){return customRef((track,trigger)=>{return {get(){track() //通知get追踪valuereturn value},set(newValue){value = newValuetrigger() //通知重新解析模板}}})}
162.provide与inject
用于祖先与后代的通信方式,祖先发布,后代订阅。provide和inject都是函数,provide接收两个变量:标识、数据,inject接收一个参数:标识,返回数据。
163.判断响应式数据
isRef:检查一个值是否为一个ref对象
isReative:检查一个对象是否是由reactive创建的响应式代理
isReadonly:检查一个对象是否是由readonly创建的只读代理
isProxy:检查一个对象是否是由reactive或readonly方法创建的代理
164.组合式API的优点
vue2的配置式API缺点:相同功能的data、methods、computed、watch需要分配写在配置API里,改动一个功能需要改动多个地方。
组合式API允许把一个功能的相关代码抽离成单独的函数(hook),甚至是文件,在setup中进行组合。
166.teleport
teleport标签内的结构可以传送到页面的任何地方,属性to=’’,可以填入标签名及css选择器
167.suspense
import { lazy, Suspense } from 'react'import Loading from './Loading'/*异步组件*/const Login = lazy(() => import('./Pages/login'))const Admin = lazy(() => import('./Pages/admin'))/*在 render 里*/render() {return (<Suspense fallback={<Loading/>}><Switch><Route path='/login' component={Login} /><Route path='/' component={Admin} /></Switch></Suspense>)}
/*v-slot:default代表默认的内容v-slot:fallback代表备用的内容*/<template><Suspense><template v-slot:default><Child/></template><template v-slot:fallback>加载中...</template></Suspense></template><script>import { defineAsyncComponent } from 'vue'const Child = defineAsyncComponent(() => import('./Child'))</script>
异步引入的子组件(Child组件)的setup函数可以是async函数,返回值可以是Promise
168.全局API的转移
1.从Vue身上转到app组件身上了,同样写在main.js里
Vue.prototype.xxx ===> app.config.globalProperties
2.移除keyCode作为v-on的修饰符,兼容性。
3.移除v-on.native vue3父组件对子组件绑定自定义事件,子组件需要在配置api中使用emits进行声明,不声明则默认是原生事件
export default {emits:['close']}
4.移除过滤器,使用computed代替
