vue3中Composition API 的优势?
1.了解 Options Api
Options API,即大家常说的选项API,即以vue为后缀的文件,通过定义methods,computed,watch,data等属性与方法,共同处理页面逻辑。
Composition
在 Vue3 Composition API 中,组件根据逻辑功能来组织的,一个功能所定义的所有 API 会放在一起(更加的高内聚,低耦合),即使项目很大,功能很多,我们都能快速的定位到这个功能所用到的所有 API,Compositon API,将某个逻辑关注点相关的代码全都放在一个函数里,这样当需要修改一个功能时,就不再需要在文件中跳来跳去。
composition APi:
- setup
Vue3.0中一个新的配置向,值为一个函数 setup是所有Composition API(组合API) ”表演的舞台“ 组件中所有用到的:数据、方法等等,均要配置在setup中
- setup语法糖
<script setup> 语法糖里面的代码会被编译成组件setup()函数的内容,不需要通过return 暴露声明的变量、函数以及import引入的内容,即可在<template/>使用,并且不需要 export default{}
- ref函数
作用:定义一个响应式的数据。
基本类型的数据:响应式依然是靠Object.defineProperty()的get和set完成的
对象类型的数据:内部“求助”了Vue3.0的一个新的函数------reactive函数 - reactive函数
作用:定义一个对象类型的响应式数据 - defineEmits
setup语法糖中引入defineEmits方法,传入数组,数组里面则是父组件传递的自定义事件名。
- 自定义hooks
ve3中还可以自定义 hooks, 将单个功能封装成单个的函数,在集成到组件中去复用! 将功能进行解耦,方便维护、复用、可读性强
2.shallowReactive和shallowRef的区别?
shallowReactive:只处理对象最外层的响应式(浅响应式)。
shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理。
使用场景:
如果有一个对象数据,结构比较深,但变化时候,只是外层属性变化,使用shallowReactive。 如果有一个对象数据,后续功能不会修改改对象中的属性,而是生成新的对象来替换,使用shallowRef。
3.provide与inject如何使用?
概述:父子组件传参可以通过props和emit来实现,但是当组件的层次结构比较深时,props和emit就没什么作用了。vue为了解决这个提出了Provide / Inject;provider/inject:简单的来说就是在父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量.
作用:实现祖和后代组件间通信。
例如:
provide()函数
定义:提供一个值,可以被后代组件注入。
具体实现:
父组件有一个provide选项提供数据,后代组件有一个inject选项来开始使用这些数据。
provide(第一个参数是要注入的key,它可以是一个字符串或一个symbol;第二个参数是要注入的值(具体要传递给子孙组件的数据)。
import {reactive,provide} from "vue"; let person = reactive({name: '张三',age: 14}); provide('person',person);
inject()函数
定义: 注入一个由祖先(父)组件或整个应用提供的值
实现: 接收父(祖)组件传递过来的值.
具体实现
inject(第一个参数,第二个参数(可选)):第一个参数是注入的key,来自父(祖)组件,它们两者是需要保持一致的
Vue会遍历父组件链,通过匹配key来确定所提供的值,如果父组件链上多个组件对同一个key提供了之,那么离得更近的将会覆盖链上更远的组件所提供的值
如果没有能通过key匹配到的值,inject()函数将返回undefined,除非提供一个默认值
第二个参数是可选的,即没有匹配到key时,使用默认值,它也可以是一个函数,用来返回某些创建起来比较复杂的值,如果默认值本身就是一个函数
那么必须将false作为第三个参数传入,表明这个函数就是默认值,而不是一个工厂函数。
与注册生命周期钩子的API类似,inject()必须在组件的setup()阶段同步调用
import {inject,toRefs} from "vue"; const person = inject('person'); // 若是使用解构,则会丢失响应式,修改数据时,页面不会更新,具体解决,可以引入toRef或toRefs函数 const {name,website} = toRefs(person); 模板使用 {{person.name}}---{{person.website}}
总结
provide/inject 做全局状态管理的原则:
多人协作时,做好作用域隔离;
尽量使用一次性数据作为全局状态
4.toRaw 与 markRaw是什么作用?
- toRaw
作用:将一个由reactive生成的响应式对象转化为普通对象。 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新.
- markRaw:
作用:标记一个对象,使其永远不会再成为响应式对象。 应用场景: 1.有些值不应被设置为响应式的,例如复杂的第三方类的库。 2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
5.readonly 与 shallowReadonly谈谈你对他的理解?
- readonly:让一个响应式数据变为只读的(深只读)。
readonly是一个函数,他会接收一个响应式的数据,如上定义的name变量,将该变量加工一番readonly函数 包裹后后重新返回一个name,这个重新返回的name变量中的所有的东西都不允许修改,所以我们现在再 点击上面的修改按钮 name 变量也不会有任何改变,并且控制台会有警告。 <template> <h1>{{ name }}</h1> <h1>{{ age }}</h1> <button @click="age++">age+1</button> </template> <script setup> import { ref, reactive, readonly, shallowReadonly, toRefs} from 'vue' let name = ref("张三") let age = ref(18) let job = reactive({ salary:{ value:70 } }) name = readonly(name) age = readonly(age) job = readonly(job) </script>
- shallowReadonly:让一个响应式数据变为只读的(浅只读)。
shallowReadonly只限制对象中的第一层数据(不能改动,如:salary),但是嵌套的深层次的value属性值 是可以更改的,我们点击更改按钮测试就能发现,被shallowReadonly包裹的对象的深层次值改变了。 <template> <h1>{{ job }}</h1> <!--> 不可以修改 <--> <button @click="job.salary = { value: 80 }">修改salary</button> <!--> 可以修改 <--> <button @click="job.salary.value = 100">修改value</button> </template> <script setup> import { ref, reactive, readonly, shallowReadonly, toRefs} from 'vue' let job = reactive({ salary:{ value:70 } }) job = shallowReadonly(job) </script>
6.自定义hook是什么?
在 Vue 3 中,Hook 是通过 Vue 3 新特性的最重要的部分——组合式 API 来实现的.
本质是一个函数。自定义hook,就是将setup中使用过的组合式函数进行封装,实现代码复用。
把数据、方法、和生命周期钩子封装在一个js文件。
在src目录下新建子目录hooks,并在hooks目录下新建文件:usePoint.js
7.虚拟DOM一定更快吗?
虚拟DOM/domDiff
我们常说的虚拟DOM是通过JS对象模拟出来的DOM节点,domDiff是通过特定算法计算出来一次操作所带来的DOM变化。react和vue中都使用了虚拟DOM,我们借着react聊聊虚拟DOM。
react中涉及到虚拟DOM的代码主要分为以下三部分,其中核心是第二步的domDiff算法:
把render中的JSX(或者createElement这个API)转化成虚拟DOM
状态或属性改变后重新计算虚拟DOM并生成一个补丁对象(domDiff)
通过这个补丁对象更新视图中的DOM节点
虚拟DOM不一定更快
干前端的都知道DOM操作是性能杀手,因为操作DOM会引起页面的回流或者重绘。相比起来,通过多一些预先计算来减少DOM的操作要划算的多。
但是,“使用虚拟DOM会更快”这句话并不一定适用于所有场景。例如:一个页面就有一个按钮,点击一下,数字加一,那肯定是直接操作DOM更快。使用虚拟DOM无非白白增加了计算量和代码量。即使是复杂情况,浏览器也会对我们的DOM操作进行优化,大部分浏览器会根据我们操作的时间和次数进行批量处理,所以直接操作DOM也未必很慢。
那么为什么现在的框架都使用虚拟DOM呢?因为使用虚拟DOM可以提高代码的性能下限,并极大的优化大量操作DOM时产生的性能损耗。同时这些框架也保证了,即使在少数虚拟DOM不太给力的场景下,性能也在我们接受的范围内。
而且,我们之所以喜欢react、vue等使用了虚拟DOM框架,不光是因为他们快,还有很多其他更重要的原因。例如react对函数式编程的友好,vue优秀的开发体验等,目前社区也有好多比较这两个框架并打口水战的,我觉着还是在两个都懂的情况下多探究一下原理更有意义一些
8.vue路由中,history和hash两种模式有什么区别?
- hash 模式
hash 模式是一种把前端路由的路径用井号 # 拼接在真实 URL 后面的模式。当井号 # 后面的路径发生变化时,浏览器并不会重新发起请求,而是会触发 hashchange 事件。
<script> <a href="#/a">A页面</a> <a href="#/b">B页面</a> <div id="app"></div> <script> function render() { app.innerHTML = window.location.hash } window.addEventListener('hashchange', render) render() </script>
在上面的例子中,我们利用 a 标签设置了两个路由导航,把 app 当做视图渲染容器,当切换路由的时候触发视图容器的更新,这其实就是大多数前端框架哈希路由的实现原理。
hash 模式的优缺点:
优点:浏览器兼容性较好,连 IE8 都支持
缺点:路径在井号 # 的后面,比较丑。
- history 模式
history API 是 H5 提供的新特性,允许开发者直接更改前端路由,即更新浏览器 URL 地址而不重新发起请求。
<a href="javascript:toA();">A页面</a> <a href="javascript:toB();">B页面</a> <div id="app"></div> <script> function render() { app.innerHTML = window.location.pathname } function toA() { history.pushState({}, null, '/a') render() } function toB() { history.pushState({}, null, '/b') render() } window.addEventListener('popstate', render) </script>
history API 提供了丰富的函数供开发者调用:
history.replaceState({}, null, '/b') // 替换路由 history.pushState({}, null, '/a') // 路由压栈 history.back() // 返回 history.forward() // 前进 history.go(-2) // 后退2次
history 模式的优缺点:
优点:路径比较正规,没有井号 #
缺点:兼容性不如 hash,且需要服务端支持,否则一刷新页面就404了。