Vue2.js核心
1. Vue的特点
- 采用 组件化 模式,提高代码复用率、且让代码更好维护
- 声明式编码,让编码人员无需直接DOM,提高开发效率。(js原生是命令式编码)
- 使用虚拟DOM+Diff算法,尽量复用DOM节点。
- 流程:数据—>虚拟DOM—>真实DOM
- Diff(差异算法),复用DOM节点
2. Vue模板语法有两大类
- 差值语法:
- 功能:用于解析标签体内容
- 写法:
{{xxx}}
,“xxx”是 js表达式,且直接读取data
中的所有属性。
- 指令语法:
- 功能:用于解析标签(包括:标签属性、标签体内容、绑定事件…)
- 例如:
v-bind:href="xxx"
或简写为::href="xxx"
,“xxx”是 js表达式,且直接读取data
中的所有属性。 - 备注:Vue中有很多的指令,且形式都是:
v-???
,此时我们只是那v-bind
举个例子。
3. Vue中两种数据绑定的方法
- 单项绑定(
v-bind
):数据只能从data
流向页面。 - 双向绑定(
v-model
):数据不仅能从data
流向页面,还可以从页面流向data
。注:- 双向绑定一般是应用在 表单 类元素上(
input、select
等) v-model:value
可以简写成v-model
,因为v-model
默认收集的就是value
值。
- 双向绑定一般是应用在 表单 类元素上(
4. data
和el
的两种写法
-
el
有两种写法:new Vue
时配置el
属性- 先创建Vue实例,随后在通过
vm.$mount('#root')
指定el
的值
-
data
有两种写法- 对象式
- 函数式
如何选择:当用组件时,
data
必须使用函数式,否则会报错。 -
一个重要的原则:
由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,
this
就不再指向Vue实例了。
5. MVVM模型
-
MVVM:
- M:模型(model):对应
data
中的数据 - V:视图(view):模板代码
- VM:视图模型(ViewModel):Vue实例对象
- M:模型(model):对应
-
备注:
data
中所有的属性,最后都出现在了vm
身上。vm
身上所有的属性及Vue原型上所有属性,在Vue模板中都可以直接使用。
6. 数据代理
-
Vue中的数据代理
通过
vm
对象代理data
对象中属性的操作(读、写) -
Vue中数据代理的好处
更加方便的操作
data
中的数据 -
基本原理
- 通过
Object.defineProperty()
把data
对象中所有的属性添加到vm
上 - 为每个添加到
vm
上的属性,都指定一个getter/setter
- 在
getter/setter
内部去操作(读、写)data
中对应的属性。
- 通过
7. 事件处理
- 使用
v-on:xxx
或@xxx
绑定事件,其中xxx是事件名 - 事件的回调函数需要配置在
methods
对象中,最终会在vm
上 methods
中配置的函数,不要使用箭头函数!否则this
指向就不是vm
了;而是window
methods
中配置的函数,都是被Vue所管理的函数,this
的指向是vm
或组件实例对象。@click="demo"
和@click="demo($event)"
效果一样,但后者可以传参。
8. 事件修饰符
prevent
:阻止默认事件(常用);stop
:阻止事件冒泡(常用);once
:事件只触发依次(常用);capture
:使用事件的捕获模式;self
:只有event.target
是当前操作的元素时才触发事件;passive
:事件的默认行为立即执行,无需等待事件回调执行完毕。- 使用方法:例如
@click:pervent="xxx"
;
9. 键盘事件
-
用法:
keyup
或keydown
,例如:@keyup.enter="xxx"
-
Vue中常用的按键别名:
-
回车 =>
enter
-
删除 =>
delete
(捕获“删除”和“退格”键) -
退出 =>
esc
-
空格 =>
space
-
换行 =>
tab
-
上 =>
up
-
下 =>
down
-
左 =>
left
-
右 =>
right
-
10. 计算属性 (computed
)
- 定义:要用的属性不存在,要通过已有的属性计算得来。
- 原理:底层借助了
Object.defineProperty
方法提供的getter
和setter
. get
函数什么时候执行?- 初次读取时会执行一次
- 当依赖的数据变化时会被再次调用
- 优势:与
methods
实现相比,内部有缓存机制(复用),效率更高,调试方便。 - 备注:
- 计算属性最终会出现在
vm
上,直接读取使用即可。 - 如果计算属性要被修改,那必须写
set
方法去响应修改,且set
中要引起计算时依赖的数据发生改变。
- 计算属性最终会出现在
11. 监视属性(侦听属性)(watch
)
- 当被监视的属性发生变化时,回调函数自动调用,进行相关操作
- 监视的属性必须存在,才能进行监视
- 监视的两种写法:
new Vue()
时传入watch
配置- 通过
vm.$watch()
监视
- 深度监视:
Vue
中的watch
默认不监视对象内部值得改变(一层)- 配置
deep:true
可以监视对象内部值得改变(多层)
- 备注:
- Vue自身可以监视对象内部值的改变,但Vue提供的
watch
默认不可以 - 使用
watch
时根据数据的具体结构,决定是否采用深度监视
- Vue自身可以监视对象内部值的改变,但Vue提供的
12. computed
和watch
之间的区别
computed
能完成的功能,watch
都可以完成watch
能完成的功能,computed
不一定能完成,例如:watch
可以进行异步操作- 两个重要的原则:
- 所被
Vue
管理的函数,最好写成普通函数(function(){}
),这样this
的指向才是vm
或组件实例对象 - 所有不被
Vue
所管理的函数(如:定时器、ajax
的回调函数、Promise
的回调函数),最好写成箭头函数,这样this
的指向才是vm
或组件实例对象。
- 所被
13. 绑定样式
class
样式- 写法:
class="xxx","xxx"
可以是字符串、对象、数组。 - 字符串写法适用于:类名不确定,要动态获取
- 对象写法适用于:要绑定多个样式,个数不确定,名字不确定。
- 数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用
- 写法:
style
样式- 写法:
:style="{fontSize:xxx}"
,其中xxx
是动态值 style={a,b}
其中a,b
是样式对象
- 写法:
14. 条件渲染
-
v-if
-
写法:
v-if = "表达式" // 表达式 === ture/false v-else-if="表达式" v-else
-
适用于:切换频率较低的场景
-
特点:不展示的DOM元素直接被删除
-
注意:
v-if
可以和:v-else-if、v-else
一起使用,但要求结构不能被 打断
-
-
v-show
- 写法:
v-show="表达式"
- 适用于:切换频率较高的场景。
- 特点:不展示的DOM元素未被移除,仅仅是使用样式被隐藏
- 写法:
-
备注:使用
v-if
时,元素可能无法获取到,而使用v-show
一定能获取到
15. 列表渲染
v-for
指令- 用于展示列表数据
- 语法:
v-for="(item,index) in xxx" :key="yyy"
- 可遍历:数组、对象、字符串、指定次数。
- 面试题:
react、vue
中的key
有什么作用?(key
的内部原理)- 虚拟DOM中
key
的作用key
是虚拟DOM对象的标识,当数据发生变化时,Vue会根据 新数据生成 新的虚拟DOM,随后Vue进行 新虚拟DOM与 旧虚拟DOM的差异比较,如下:
- 对比规则:
- 旧虚拟DOM中找到了与新虚拟DOM相同的
key
:- 若虚拟DOM中的内容没变,直接使用之前的真实DOM
- 若虚拟DOM中的内容变了,则生成新的真实DOM,随后替换掉页面之前的真实DOM
- 旧虚拟DOM中未找到与新虚拟DOM相同的
key
- 创建新的真实DOM,随后渲染到页面
- 旧虚拟DOM中找到了与新虚拟DOM相同的
- 用
index
作为key
可能引发的问题:- 若对数据进行:逆序添加、逆序删除操作,会产生没有必要的真实DOM更新 ===> 界面效果没问题,但效率低
- 如果结构中还包含输入类的DOM:会产生错误的DOM更新 ===> 界面有问题
- 开发中如何选择
key
:- 最好使用每条数据的唯一标识作为
key
,如id
. - 如果仅用于列表展示而没有唯一标识,可使用
index
作为key
- 最好使用每条数据的唯一标识作为
- 虚拟DOM中
16. Vue监视数据的原理
-
Vue会监视
data
中所有层次的数据 -
如何监视对象中的数据?
-
通过
setter
实现数据监视,且要在new Vue
时就传入要监视的数据。- 对象中后加的属性,Vue默认不做响应式处理
- 如需给后加的属性做响应式,使用如下API:
Vue.set(target,propertyName/index,value) //或 vm.$set(target,propertyName/index,value)
-
-
如何监视数组中的数据:
- 通过包裹数组更新元素的方法实现,本质就是做两件事:
- 调用原生对应的方法对数组进行更新。
- 重新解析模板,进而更新页面
- 通过包裹数组更新元素的方法实现,本质就是做两件事:
-
在Vue修改数组中的某个元素一定要用如下方法:
- 使用API:
push()/pop()/shift()/unshift()/splice()/sort()/reverse()
- 应用
Vue.set()或vm.$set()
- 使用API:
-
注意:
Vue.set()或vm.$set()
不能给vm
及根数据对象添加属性
17. 收集表单数据
<input type="text"/>
,v-model
收集的是value
值,用户输入的就是value
值。<input type="radio"/>
,v-model
收集的是value
值,且要给标签配置value
值。<input text="checkbox"/>
:- 若没有配置
input
的value
属性,那么收集的就是checked
(勾选/未勾选,是布尔值) - 若配置
input
的value
属性:v-model
的初始值是非数组,那么收集的就是checked
(勾选/未勾选,是布尔值)v-model
的初始值是数组,那么收集的就是value
组成的数组
- 若没有配置
- 备注:
v-model
的三个修饰符:lazy
:失去焦点再收集数据namber
:输入字符串转为数字trim
:输入首位空格过滤
18. 过滤器
- 定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)
- 语法:
- 注册过滤器:
Vue.filter(name,callback)
或new Vue(filter:{})
- 使用过滤器:
{{xxx | 过滤器名}}
或v-bind:属性='xxx' | 过滤器名
- 注册过滤器:
- 备注:
- 过滤器也可以接受额外的参数,多个过滤器也可以串联
- 过滤并没有改变原本的数据,是产生新的对应的数据
19. Vue的内置指令集
v-bind
:单向绑定解析表达式,简写:xxx
v-model
:双向数据绑定v-for
:遍历数组、对象、字符串v-on
:绑定事件监听,简写@
v-if/v-else-if/v-else
:条件渲染(动态控制节点是否存在)v-show
:条件渲染(动态控制节点是否展示)v-text
:向其所在的节点中渲染文本内容,并会替换节点中的内容。v-html
:向指定节点中包含html
结构的内容,并会替换节点中的内容。注:存在安全性问题,不要用在用户提交的内容上,容易导致XSS攻击v-cloak
:本质上是特殊属性,Vue实例创建完成并接管容器后,会删除v-cloak
属性。使用css
配合v-cloak
可以解决网速变慢时页面展示出{{xxx}}
的问题v-once
:是所在节点在初次动态渲染后,就视为静态内容了,以后数据的改变将不会引起所在结构的更新,可用于优化性能。v-pre
:跳过其所在节点的编译过程,可用于:没有使用指令语法及插值语法的节点而加快编译。
20. 自定义指令
-
定义语法:
-
局部指令:
new Vue({ directives:{指令名:配置对象} }) // 或 new Vue({ directives:{指令名:回调函数} })
-
全局指令:
Vue.directives(指令名,配置对象) // 或 Vue.directives(指令名,回调函数)
-
-
配置对象中常见的3个回调函数
bind
:指令与元素成功绑定时调用inserted
:指令所在元素插入页面时调用update
:指令所在模板结构被重新解析时调用。
-
备注:
- 指令定义时不加
v-
,但使用时要加v-
- 指令名如果是多个单词,要使用
kabab-case
命名方式,不要使用camelCase
命名
- 指令定义时不加
21. 生命周期函数
- 常用的生命周期钩子:
mounted
:发送ajax
请求、启动定时器、绑定自定义事件、订阅消息等 初始化操作beforDestroy
:清除定时器、解绑自定义事件、取消订阅消息等 收尾工作
- 关于销毁Vue实例:
$destroy()
- 销毁后自定义事件会失效,但原生DOM事件依然有效
- 一般不会在
beforDestroy
操作数据,因为即使操作数据,也不会再触发更新数据了。
22. ref
属性
-
被用来给元素或子组件注册引用信息(id 的代替者)
-
应用于:
html
标签上获取真实DOM
元素 或 组件标签上获取组件实例对象(vc) -
使用方式:
// 打标识 <h1 ref="xxx"></h1> //或 <School ref="xxx" /> // 获取 this.$ref.xxx
23. 组件的自定义事件
-
一种组件间通信的方式,适用于:子组件 ===> 父组件
-
使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
-
绑定自定义事件:
- 第一种方式,在父组件中:
<Demo @atguigu="test"/>
或<Demo v-on:atguigu="test"/>
- 第二种方式,在父组件中:
- 第一种方式,在父组件中:
<Demo ref="demo"/>
......
mounted(){
this.$refs.xxx.$on('atguigu',this.test)
}
-
若想让自定义事件只能触发一次,可以使用
once
修饰符,或$once
方法。 -
触发自定义事件:
this.$emit('atguigu',数据)
-
解绑自定义事件
this.$off('atguigu')
-
组件上也可以绑定原生DOM事件,需要使用
native
修饰符。 -
注意:通过
this.$refs.xxx.$on('atguigu',回调)
绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!
24. 全局事件总线(GlobalEventBus)
-
一种组件间通信的方式,适用于任意组件间通信。
-
安装全局事件总线:
new Vue({ ...... beforeCreate() { Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm }, ...... })
-
使用事件总线:
-
接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。
methods(){ demo(data){......} } ...... mounted() { this.$bus.$on('xxxx',this.demo) }
-
提供数据:
this.$bus.$emit('xxxx',数据)
-
-
最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
25. 消息订阅与发布(pubsub)
-
一种组件间通信的方式,适用于任意组件间通信。
-
使用步骤:
-
安装pubsub:
npm i pubsub-js
-
引入:
import pubsub from 'pubsub-js'
-
接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
methods(){ demo(data){......} } ...... mounted() { this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息 }
-
提供数据:
pubsub.publish('xxx',数据)
-
最好在beforeDestroy钩子中,用
PubSub.unsubscribe(pid)
去取消订阅。
-
26. nextTick
-
语法:
this.$nextTick(回调函数)
-
作用:在下一次 DOM 更新结束后执行其指定的回调。
-
什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。
27. webStorage
-
存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
-
浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
-
相关API:
xxxxxStorage.setItem('key', 'value');
该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。xxxxxStorage.getItem('person');
该方法接受一个键名作为参数,返回键名对应的值。xxxxxStorage.removeItem('key');
该方法接受一个键名作为参数,并把该键名从存储中删除。xxxxxStorage.clear()
该方法会清空存储中的所有数据。
-
备注:
- SessionStorage存储的内容会随着浏览器窗口关闭而消失。
- LocalStorage存储的内容,需要手动清除才会消失。
xxxxxStorage.getItem(xxx)
如果xxx对应的value获取不到,那么getItem的返回值是null。JSON.parse(null)
的结果依然是null。
28. vue脚手架配置代理
-
方法一
-
在vue.config.js中添加如下配置:
devServer:{ proxy:"http://localhost:5000" }
-
说明:
- 优点:配置简单,请求资源时直接发给前端(8080)即可。
- 缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
- 工作方式:若按照上述配置代理,当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)
-
-
方法二
- 编写vue.config.js配置具体代理规则:
module.exports = { devServer: { 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设置为false时,服务器收到的请求头中的host为:localhost:8080 changeOrigin默认值为true */
-
说明:
- 优点:可以配置多个代理,且可以灵活的控制请求是否走代理。
- 缺点:配置略微繁琐,请求资源时必须加前缀。
29. 插槽
-
作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===> 子组件
-
分类:默认插槽、具名插槽、作用域插槽
-
使用方式:
- 默认插槽:
父组件中: <Category> <div>html结构1</div> </Category> 子组件中: <template> <div> <!-- 定义插槽 --> <slot>插槽默认内容...</slot> </div> </template>
- 具名插槽:
父组件中: <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>
- 作用域插槽:
- 理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(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 slot-scope="scopeData"> <!-- 生成的是h4标题 --> <h4 v-for="g in scopeData.games" :key="g">{{g}}</h4> </template> </Category> 子组件中: <template> <div> <slot :games="games"></slot> </div> </template> <script> export default { name:'Category', props:['title'], //数据在子组件自身 data() { return { games:['红色警戒','穿越火线','劲舞团','超级玛丽'] } }, } </script>
30. props配置项
-
功能:让组件接收外部传过来的数据
-
传递数据:
<Demo name="xxx"/>
-
接收数据:
-
第一种方式(只接收):
props:['name']
-
第二种方式(限制类型):
props:{name:String}
-
第三种方式(限制类型、限制必要性、指定默认值):
props:{ name:{ type:String, //类型 required:true, //必要性 default:'老王' //默认值 } }
-
备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。
31. mixin
(混入)
-
功能:可以把多个组件共用的配置提取成一个混入对象
-
使用方式:
第一步定义混合:
{ data(){....}, methods:{....} .... }
第二步使用混入:
全局混入:
Vue.mixin(xxx)
局部混入:mixins:['xxx']
32. 插件
-
功能:用于增强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 = xxxx }
-
使用插件:
Vue.use()
Vue3.js核心
1. 拉开序幕的setup
- 理解:Vue3的一个新的配置项,值是一个函数。
setup
是所有Composition API(组合API)“表演的舞台”。- 组件中所用到的:数据、方法等等,均要配置在
setup
中。 setup
函数的两种返回值:- 若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用。(重点)
- 又返回一个渲染函数:则可以自定义渲染内容。(了解)
- 注意:
- 尽量不要与Vue2.x配置混用
- Vue2.x配置(
data、methods、computed...
)中可以访问setup
中的属性、方法。 - 但在
setup
中不能访问到Vue2.x配置(data、methods、computed
). - 如果有重名,
setup
优先。
- Vue2.x配置(
setup
不能是一个async
函数,因为返回值不再是return
的对象,而是promise
,模板看不到return
对象中的属性。(后期也可以返回一个Promise
实例,但需要Suspense
和异步组件的配合)。
- 尽量不要与Vue2.x配置混用
2. ref
函数
- 作用:定义一个响应式函数
- 语法:
const xxx = ref(initValue)
- 创建一个包含响应式的引用对象(
reference
对象,简称ref
对象) JS
中操作数据:xxx.value
- 模板中读取数据:不需要
value
,直接<div>{{xxx}}</div>
- 创建一个包含响应式的引用对象(
- 备注:
- 接收的数据可以使:基本类型、也可以是对象类型。
- 基本类型的数据:响应式依然是靠
Object.defineProperty()
的get
与set
完成的。 - 对象类型的数据:内部 *“求助”*了Vue3.0中的一个新的函数——
reactive
函数。
3. reactive
函数
- 作用:定义一个对象类型的响应式数据(基本类型不要用它,要用
ref
函数) - 语法:
const 代理对象= reactive(源对象)
接收一个对象(或数组),返回一个代理对象(Proxy
的实例对象,简称proxy
对象) reactive
定义的响应式数据是*“深层次的”*- 内部基于
ES6
的Proxy
实现,通过代理对象操作源对象内部数据进行操作。
4. Vue3.0中的响应式原理
-
Vue2.x的响应式
-
对象类型:通过
Object.defineProperty()
对属性的读取、修改进行拦截(数据劫持)。 -
数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
Object.defineProperty(data,'count',{ get(){}, set(){} })
-
存在问题:
- 新增属性、删除属性,界面不会更新。
- 直接通过下标修改数组,界面不会自动更新。
-
-
Vue3.0的响应式
-
通过
Proxy
(代理):拦截对象中任意属性的变化,包括:属性值的读写、属性的添加、属性的删除等。 -
通过
Reflect
(反射):对源对象的属性进行操作。-
MDN
文档中描述的Proxy
与Reflect
-
Proxy:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
-
Reflect:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
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) } })
-
-
-
5. reactive
对比ref
- 从定义数据角度比较:
ref
用来定义:基本类型数据。reactive
用来定义:对象(或数组)类型数据。- 备注:
ref
也可以用来定义对象(或数组)类型数据,它内部会自动通过reactive
转为代理对象。
- 从原理角度对比:
ref
通过Object.defineProperty()
的get
与set
来实现响应式(数据劫持)。reactive
通过使用Proxy
来实现响应式(数据劫持),并通过Reflect
操作源对象内部的数据。
- 从使用角度对比:
ref
定义的数据:操作数据需要.value
,读取数据时模板中直接读取不需要.value
。reactive
定义的数据:操作数据与读取数据:均不需要.value
。
6. setup
的两个注意点
setup
执行的时机- 在
beforCreate
之前执行一次,this
是undefined
。
- 在
setup
的参数props
:值为对象,包括:组件外部传递过来,且组件内部声明接收了的属性。context
:上下文对象attrs
:值为对象,包含:组件外部传递过来,但没有在props
配置中声明的属性,相当于this.$attrs
。slots
:收到的插槽内容,相当于this.$slots
。emit
:分发自定义事件的函数,相当于this.$emit
。
7. 计算属性与监视属性
-
computed
函数-
与Vue2.x中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] } }) }
-
-
watch
函数-
与Vue2中
watch
配置功能一致 -
两个小坑:
- 监视
reactive
定义的响应式数据时:oldValue
无法正确获取、并强制开启了深度监视(deep
配置失效) - 监视
reactive
定义的响应式数据中某个属性时:响应式数据中某个属性时**:deep
配置有效**
//情况一:监视ref定义的响应式数据 watch(sum,(newValue,oldValue)=>{ console.log('sum变化了',newValue,oldValue) },{immediate:true}) //情况二:监视多个ref定义的响应式数据 watch([sum,msg],(newValue,oldValue)=>{ console.log('sum或msg变化了',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}) //情况五:监视reactive定义的响应式数据中的某些属性 watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{ console.log('person的job变化了',newValue,oldValue) },{immediate:true,deep:true}) //特殊情况 watch(()=>person.job,(newValue,oldValue)=>{ console.log('person的job变化了',newValue,oldValue) },{deep:true}) //此处由于监视的是reactive素定义的对象中的某个属性,所以deep配置有效
- 监视
-
-
watchEffect
函数-
watch
的套路是:既要指明监视的属性,也要指明监视的回调。 -
watchEffect
的套是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。 -
watchEffect
有点像computed
:- 但
computed
注重的计算出来的值(回调函数的返回值),所以必须要写返回值。 - 而
watchEffect
更注重的是过程(回调函数额函数体),所以不用写返回值。
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。 watchEffect(()=>{ const x1 = sum.value const x2 = person.age console.log('watchEffect配置的回调执行了') })
- 但
-
8. 生命周期
-
Vue2
-
Vue3:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-55ezalDt-1654075906024)(https://md-1257824135.cos.ap-nanjing.myqcloud.com/uPic/lifecycle-20220529180910316.svg)]
- Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
beforeDestroy
改名为beforeUnmount
destroyed
改名为unmounted
- Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
beforeCreate
===>setup()
created
====>setup()
beforeMount
===>onBeforeMount
mounted
====>onMounted
beforeUpdate
===>onBeforeUpdate
updated
====>onUpdated
beforeUnmount
==>onBeforeUnmount
unmounted
====>onUnmounted
- Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
9. 自定义hook
函数
- 什么是
hook?
——本质是一个函数,把setup
函数中使用的Composition API
进行了封装。 - 类似于Vue2中的
mixin
。 - 自定义
hook
的优势:复用代码,让setup
中的逻辑更清楚易懂。
10. toRef
- 作用:创建一个
ref
对象,其value
值指向另一个对象中的某个属性。 - 语法:
const name = toRef(person,'name')
- 应用:要将响应式对象的某个属性单独提供给外部使用时。
- 扩展:
toRefs
与toRef
功能一致,但可以批量创建多个ref
对象,语法:toRef(person)
11. shallowReactive
与shallowRef
shallowReactive
:只处理对象最外层属性的响应式(浅响应式)shallowRef
:只处理基本数据类型的响应式,不进行对象的响应式处理。- 什么时候使用?
- 如果有一个对象数据,结构比较深,但变化时只是外层属性变化 ====>
shallowReactive
。 - 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生成新的对象来替换 ====>
shallowRef
- 如果有一个对象数据,结构比较深,但变化时只是外层属性变化 ====>
12. readonly
与shallowReadonly
readonly
:让一个响应式数据变为只读(深只读)shallowReadonly
:让一个响应式数据变为只读的(浅只读)- 应用场景:不希望数据被修改时。
13. toRaw
与markRaw
toRaw
:- 作用:将一个由
reactive
生成的响应式对象转为普通对象。 - 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
- 作用:将一个由
markRaw
:- 作用:标记一个对象,使其永远不会在成为响应式对象。
- 应用场景:
- 有些不应被设置为响应式的,例如复杂的第三方类库等。
- 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
14. customRef
-
作用:创建一个自定义的
ref
,并对其依赖项跟踪和更新触发进行显式控制。 -
实现防抖效果:
<template> <input type="text" v-model="keyword"> <h3>{{keyword}}</h3> </template> <script> import {ref,customRef} from 'vue' export default { name:'Demo', setup(){ // let keyword = ref('hello') //使用Vue准备好的内置ref //自定义一个myRef function myRef(value,delay){ let timer //通过customRef去实现自定义 return customRef((track,trigger)=>{ return{ get(){ track() //告诉Vue这个value值是需要被“追踪”的 return value }, set(newValue){ clearTimeout(timer) timer = setTimeout(()=>{ value = newValue trigger() //告诉Vue去更新界面 },delay) } } }) } let keyword = myRef('hello',500) //使用程序员自定义的ref return { keyword } } } </script>
15. provide
与inject
![](https://md-1257824135.cos.ap-nanjing.myqcloud.com/uPic/components_provide.png?watermark/2/text/eWlnZTAwMQ==/fontsize/30/dissolve/40/dx/40/dy/40&imageMogr2/format/webp&watermark/3/type/3/text/eWlnZQ==)
-
作用:实现祖与后代组件间的通信
-
套路:父组件有一个
provide
选项来提供数据,后代组件有一个inject
选项来开始使用这些数据 -
具体写法:
-
祖组价中:
setup(){ ..... let car = reactive({name:'奔驰',price:'40万'}) provide('car',car) ..... }
-
后代组件中:
setup(){ ...... const car = inject('car') return {car} ....... }
-
16. 响应式数据的判断
isRef
:检查一个值是否为一个ref
对象isReactive
:检查一个对象是否有一个reactive
创建的响应式代理isReadonly
:检查一个对象是否由readonly
创建的只读代理isPorxy
:检查一个对象是否有一个reactive
或者readonly
方法创建的代理
17. Composition API
的优势
Options API
存在的问题(Vue2)
-
Composition API
的优势我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起
18. 新的组件
1. Fragment
- 在Vue2中: 组件必须有一个根标签
- 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
- 好处: 减少标签层级, 减小内存占用
2. 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>
3. Suspense
-
等待异步组件时渲染一些额外内容,让应用有更好的用户体验
-
使用步骤:
-
异步引入组件
import {defineAsyncComponent} from 'vue' const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
-
使用
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>
-
19. 其他
1. 全局API的转移
-
Vue 2.x 有许多全局 API 和配置。
-
例如:注册全局组件、注册全局指令等。
//注册全局组件 Vue.component('MyButton', { data: () => ({ count: 0 }), template: '<button @click="count++">Clicked {{ count }} times.</button>' }) //注册全局指令 Vue.directive('focus', { inserted: el => el.focus() }
-
-
Vue3.0中对这些API做出了调整:
-
将全局的API,即:
Vue.xxx
调整到应用实例(app
)上2.x 全局 API( Vue
)3.x 实例 API ( app
)Vue.config.xxxx app.config.xxxx Vue.config.productionTip 移除 Vue.component app.component Vue.directive app.directive Vue.mixin app.mixin Vue.use app.use Vue.prototype app.config.globalProperties
-
2. 其他改变
-
data选项应始终被声明为一个函数。
-
过度类名的更改:
-
Vue2.x写法
.v-enter, .v-leave-to { opacity: 0; } .v-leave, .v-enter-to { opacity: 1; }
-
Vue3.x写法
.v-enter-from, .v-leave-to { opacity: 0; } .v-leave-from, .v-enter-to { opacity: 1; }
-
-
移除keyCode作为 v-on 的修饰符,同时也不再支持
config.keyCodes
-
移除
v-on.native
修饰符-
父组件中绑定事件
<my-component v-on:close="handleComponentEvent" v-on:click="handleNativeClickEvent" />
-
子组件中声明自定义事件
<script> export default { emits: ['close'] } </script>
-
-
移除过滤器(filter)
过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性去替换过滤器。
-
…