(1)监测机制的改变
- 3.0 将带来基于代理 Proxy的 observer 实现,提供全语言覆盖的反应性跟踪。
消除了 Vue 2 当中基于 Object.defineProperty 的实现所存在的很多限制:
(2)只能监测属性,不能监测对象
- 检测属性的添加和删除;
- 检测数组索引和长度的变更;
- 支持 Map、Set、WeakMap 和 WeakSet。
(3)模板
- 作用域插槽,2.x 的机制导致作用域插槽变了,父组件会重新渲染,而 3.0 把作用域插槽改成了函数的方式,这样只会影响子组件的重新渲染,提升了渲染的性能。
同时,对于 render 函数的方面,vue3.0 也会进行一系列更改来方便习惯直接使用 api 来生成 vdom 。
(4)对象式的组件声明方式
- vue2.x 中的组件是通过声明的方式传入一系列 option,和 TypeScript 的结合需要通过一些装饰器的方式来做,虽然能实现功能,但是比较麻烦。
3.0 修改了组件的声明方式,改成了类式的写法,这样使得和 TypeScript 的结合变得很容易
(5)其它方面的更改
支持自定义渲染器,从而使得 weex 可以通过自定义渲染器的方式来扩展,而不是直接 fork 源码来改的方式。
支持 Fragment(多个根节点)和 Protal(在 dom 其他部分渲染组建内容)组件,针对一些特殊的场景做了处理。
基于 tree shaking 优化,提供了更多的内置功能。
Composition API详解
Vue3.0已经发布了,其中最大的亮点就是新引入了Composition API。它使得组件内部逻辑更加清晰、可组合性更强,让我们能够更方便地重用代码、提高代码的可读性和可维护性。Composition API是什么?
Composition API是一种新的API风格,它允许将组件逻辑进行分离,从而使得组件更容易被理解和测试。在Composition API中,我们可以将相关的函数和状态放在一起,其实就是将Vue2.x中的Options API进行了拆分。使用Composition API的好处
更加灵活:在Vue 2.x中,我们只能通过生命周期来管理组件的状态,但是生命周期很难管理所有的组件行为,这时候Composition API就能够帮助我们更好地组织业务逻辑。
更加直观:使用Composition API,我们可以更加直观地看到某个方法会影响哪些状态,使得代码更加易于理解。
更加简洁:使用Composition API可以大幅度减少代码重复,提高代码的可读性和可维护性。
总结
Composition API是Vue3.0中最重要的新特性之一,它使得组件内部逻辑更加清晰、可组合性更强,让我们能够更方便地重用代码、提高代码的可读性和可维护性。虽然Composition API还不是很成熟,但是它已经得到了越来越多开发者的认可和使用,相信在未来的开发中,它会变得更加完善和强大。
区别:
一、vue2(Options API):
在vue2中,编写组件的方式是Options API特点:
在对应的属性中编写对应的功能模块,比如在data属性中定义数据,methods中定义属性,computed中定义计算属性,watch中监听属性改变,还有一些声明周期钩子
弊端:
1.当实现一个功能的时候,我们就要将这个功能的对应逻辑代码拆分到各个属性中
2.项目一庞大,组件变得更大、更复杂,代码就会非常多,同一个功能的逻辑就会被拆分的很散,当想要修改一个功能逻辑代码的时候,找起来就非常的麻烦
3.当别人阅读你的代码的时候,这个组件会变得难以阅读和理解
二、vue3(Composition API):
为了解决Options API的弊端,vue3的Composition API为了解决这种碎片化代码,避免一个功能的逻辑代码太分散,于是增添了一个setup函数,这个选项可以用来替代之前所编写的大部分其他选项,比如data的属性,methods,computed,watch,一些生命周期钩子函数。
{{ message }}
从本质上来说,Virtual Dom是一个JavaScript对象,通过对象的方式来表示DOM结构。将页面的状态抽象为JS对象的形式,配合不同的渲染工具,使跨平台渲染成为可能。通过事务处理机制,将多次DOM修改的结果一次性的更新到页面上,从而有效的减少页面渲染的次数,减少修改DOM的重绘重排次数,提高渲染性能。
虚拟DOM是对DOM的抽象,这个对象是更加轻量级的对 DOM的描述。它设计的最初目的,就是更好的跨平台,比如Node.js就没有DOM,如果想实现SSR,那么一个方式就是借助虚拟DOM,因为虚拟DOM本身是js对象。 在代码渲染到页面之前,vue会把代码转换成一个对象(虚拟 DOM)。以对象的形式来描述真实DOM结构,最终渲染到页面。在每次数据发生变化前,虚拟DOM都会缓存一份,变化之时,现在的虚拟DOM会与缓存的虚拟DOM进行比较。在vue内部封装了diff算法,通过这个算法来进行比较,渲染时修改改变的变化,原先没有发生改变的通过原先的数据进行渲染。
另外现代前端框架的一个基本要求就是无须手动操作DOM,一方面是因为手动操作DOM无法保证程序性能,多人协作的项目中如果review不严格,可能会有开发者写出性能较低的代码,另一方面更重要的是省略手动DOM操作可以大大提高开发效率。
首先对将要插入到文档中的 DOM 树结构进行分析,使用 js 对象将其表示出来,比如一个元素对象,包含 TagName、props 和 Children 这些属性。然后将这个 js 对象树给保存下来,最后再将 DOM 片段插入到文档中。
当页面的状态发生改变,需要对页面的 DOM 的结构进行调整的时候,首先根据变更的状态,重新构建起一棵对象树,然后将这棵新的对象树和旧的对象树进行比较,记录下两棵树的的差异。
最后将记录的有差异的地方应用到真正的 DOM 树中去,这样视图就更新了。
(1)保证性能下限,在不进行手动优化的情况下,提供过得去的性能
看一下页面渲染的流程:解析HTML -> 生成DOM -> 生成 CSSOM -> Layout -> Paint -> Compiler
下面对比一下修改DOM时真实DOM操作和Virtual DOM的过程,来看一下它们重排重绘的性能消耗∶
真实DOM∶ 生成HTML字符串+重建所有的DOM元素
虚拟DOM∶ 生成vNode+ DOMDiff+必要的dom更新
Virtual DOM的更新DOM的准备工作耗费更多的时间,也就是JS层面,相比于更多的DOM操作它的消费是极其便宜的。尤雨溪在社区论坛中说道∶ 框架给你的保证是,你不需要手动优化的情况下,依然可以给你提供过得去的性能。(2)跨平台
Virtual DOM本质上是JavaScript的对象,它可以很方便的跨平台操作,比如服务端渲染、uniapp等。
- 首次渲染大量DOM时,由于多了一层虚拟DOM的计算,会比innerHTML插入慢。
- 正如它能保证性能下限,在真实DOM操作的时候进行针对性的优化时,还是更快的。
在 Vue 中,key 是一个特殊的属性,用于给每个节点(组件)设置唯一的标识符。它在 Vue 的虚拟 DOM 的渲染和更新过程中发挥重要作用,主要有以下两个方面的作用:
1.提高渲染效率 当 Vue 在进行虚拟 DOM 的 diff 算法比较新旧节点时,如果节点具有相同的 key,则 Vue会认为它们是相同的节点,不会进行重新渲染,从而提高渲染效率。
2.保持组件状态 在使用 v-for 指令渲染列表时,每个列表项都应该拥有唯一的 key,这样可以在列表项顺序改变时,Vue可以准确地判断哪些列表项是新添加的,哪些列表项是已存在但位置改变的,哪些列表项是被删除的,从而保持组件状态的正确性
因为每当在列表中增删数据时key都会发生改变
当key绑定是index(数组所在位置索引)时,增删数据时,数据中的key也会发生改变,这也失去了key所存在的意义
当key值发生改变,会重新渲染key所绑定的对应数据,是将旧数据删除并插入新的数据
①vue-router使用pushState进行路由更新,静态跳转,页面不会重新加载;location.href会触发浏览器,页面重新加载一次
②vue-router使用diff算法,实现按需加载,减少dom操作
③vue-router是路由跳转或同一个页面跳转;location.href是不同页面间跳转;
④vue-router是异步加载this.$nextTick(()=>{获取url});location.href是同步加载
query语法:
this.$router.push({path:"地址",query:{id:"123"}}); 这是传递参数
this.$route.query.id; 这是接受参数
params语法:
this.$router.push({name:"地址",params:{id:"123"}}); 这是传递参数
this.$route.params.id; 这是接受参数
1.写法的不同
query的语法用于path编写传参地址
params的语法用于name编写传参地址
2.接收方式不同
接受参数的时候用this.$route.params.name或者this.$route.query.name
3.两者中query在刷新页面的时候参数不会消失 但params在刷新页面的时候参数会消失 可以考虑本地存储解决此问题
4.query传过来的参数会显示到地址栏中 而params传过来的参数不会显示到地址栏中 直白的来说 query相当于get请求,而params相当于post请求
一、全局守卫
1、全局前置守卫router.beforeEach
const router = createRouter({ ... })
// to:即将要进入的目标, from:当前导航正要离开的路由,next:可选参数
router.beforeEach((to, from, next) => {
// ...
// next() 放行,执行管道中的下一个钩子
// 返回 false 以取消导航
return false
})
2、全局解析守卫router.beforeResolve
router.beforeResolve
和 router.beforeEach
类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用
3、全局后置钩子 router.afterEach
全局后置钩子和守卫不同的是,这些钩子不会接受 next
函数也不会改变导航本身
router.afterEach((to, from) => {
// ...
})
它们对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用。
二、单个路由独享守卫
在路由配置上直接定义beforeEnter,beforeEnter
守卫 只在进入路由时触发。
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
三、组件内的守卫
可以在路由组件内直接定义以下路由导航守卫
beforeRouteEnter
beforeRouteUpdate
(2.2 新增)
beforeRouteLeave
const Foo = {
template: `...`,
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}