vue2与vue3的差异(总结)?
Posted 短暂又灿烂的
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue2与vue3的差异(总结)?相关的知识,希望对你有一定的参考价值。
vue作者尤雨溪在开发 vue3.0 的时候开发的一个基于浏览器原生 ES imports 的开发服务器(开发构建工具)。那么我们先来了解一下vite
Vite
Vite,一个基于浏览器原生 ES imports 的开发服务器。利用浏览器去解析 imports,在服务器端按需编译返回,完全跳过了打包这个概念,服务器随起随用。同时不仅有 Vue 文件支持,还搞定了热更新,而且热更新的速度不会随着模块增多而变慢。针对生产环境则可以把同一份代码用 rollup 打。虽然现在还比较粗糙,但这个方向我觉得是有潜力的,做得好可以彻底解决改一行代码等半天热更新的问题。它做到了本地快速开发启动, 用 vite 文档上的介绍,它具有以下特点:
- 快速的冷启动,不需要等待打包操作;
- 即时的热模块更新,替换性能和模块数量的解耦让更新飞起;
- 真正的按需编译,不再等待整个应用编译完成;
使用 npm:
# npm 7+,需要加上额外的双短横线
$ npm init vite@latest <project-name> -- --template vue
$ cd <project-name>
$ npm install
$ npm run dev
或者 yarn:
$ yarn create vite <project-name> --template vue
$ cd <project-name>
$ yarn
$ yarn dev
概览
- 速度更快
- 体积减少
- 更易维护
- 更接近原生
- 更易使用
- 重写了虚拟Dom实现
diff算法优化
<div>
<span/>
<span> msg </span>
</div>
被编译成:
import createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock from "vue"
export function render(_ctx, _cache)
return (_openBlock(), _createBlock("div", null, [
_createVNode("span", null, "static"),
_createVNode("span", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)
]))
首先静态节点进行提升,会提升到 render 函数外面,这样一来,这个静态节点永远只被创建一次,之后直接在 render 函数中使用就行了。
Vue在运行时会生成number(大于0)值的PatchFlag,用作标记,仅带有PatchFlag标记的节点会被真正追踪,无论层级嵌套多深,它的动态节点都直接与Block根节点绑定,无需再去遍历静态节点,所以处理的数据量减少,性能得到很大的提升。
- 事件监听缓存:cacheHandlers
<div>
<span @click="onClick">
msg
</span>
</div>
优化前:
import toDisplayString as _toDisplayString, createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock from "vue"
export function render(_ctx, _cache)
return (_openBlock(), _createBlock("div", null, [
_createVNode("span", onClick: _ctx.onClick , _toDisplayString(_ctx.msg), 9 /* TEXT, PROPS */, ["onClick"])
]))
onClick会被视为PROPS动态绑定,后续替换点击事件时需要进行更新。
优化后:
import toDisplayString as _toDisplayString, createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock from "vue"
export function render(_ctx, _cache)
return (_openBlock(), _createBlock("div", null, [
_createVNode("span",
onClick: _cache[1] || (_cache[1] = $event => (_ctx.onClick($event)))
, _toDisplayString(_ctx.msg), 1 /* TEXT */)
]))
会自动生成一个内联函数,这个内联函数里面再去引用当前组件最新的onclick,然后把这个内联函数cache起来,第一次渲染的时候会创建内联函数并且缓存,后续的更新就直接从缓存里面读同一个函数,既然是同一个函数就没有再更新的必要,就变成了一个静态节点
3. SSR速度提高
当有大量静态的内容时,这些内容会被当做纯字符串推进一个buffer里面,即使存在动态的绑定,会通过模板 插值嵌入进去,这样会比通过虚拟dom来渲染的快很多。vue3.0 当静态文件大到一定量的时候,会用_ceratStaticVNode方法在客户端去生成一个static node, 这些静态node,会被直接innerhtml,就不需要创建对象,然后根据对象渲染
- tree-shaking
tree-shakinng 原理
主要依赖es6的模块化的语法,es6模块依赖关系是确定的,和运行时的状态无关,可以进行可靠的静态分析,
分析程序流,判断哪些变量未被使用、引用,进而删除对应代码
前提是所有的东西都必须用ES6 module的import来写
按照作者的原话解释,Tree-shaking其实就是:把无用的模块进行“剪枝”,很多没有用到的API就不会打包到最后的包里
在Vue2中,全局 API 如 Vue.nextTick() 是不支持 tree-shake 的,不管它们实际是否被使用,都会被包含在最终的打包产物中。
而Vue3源码引入tree shaking特性,将全局 API 进行分块。如果你不使用其某些功能,它们将不会包含在你的基础包中
5. compositon Api
没有Composition API之前vue相关业务的代码需要配置到option的特定的区域,中小型项目是没有问题的,但是在大型项目中会导致后期的维护性比较复杂,同时代码可复用性不高
compositon api提供了以下几个函数:
vue2使用全局api 如 Vue.component, Vue.mixin, Vue.use等,缺点是会导致所创建的根实例将共享相同的全局配置(从相同的 Vue 构造函数创建的每个根实例都共享同一套全局环境。这样就导致一个问题,只要某一个根实例对 全局 API 和 全局配置做了变动,就会影响由相同 Vue 构造函数创建的其他根实例。)
vue3 新增了createApp,调用createApp返回一个应用实例,拥有全局API的一个子集,任何全局改变 Vue 行为的 API 现在都会移动到应用实例上
2. 组件挂载
-
setup (入口函数,接收两个参数(props,context))
-
ref (将一个原始数据类型转换成一个带有响应式特性)
-
reactive (reactive 用来定义响应式的对象)
-
watchEffect
-
watch
-
computed
-
toRefs (解构响应式对象数据)
-
生命周期的hooks
如果用ref处理对象或数组,内部会自动将对象/数组转换为reactive的代理对象
ref内部:通过给value属性添加getter/setter来实现对数据的劫持
reactive内部:通过使用proxy来实现对对象内部所有数据的劫持,并通过Reflect反射操作对象内部数据
ref的数据操作:在js中使用ref对象.value获取数据,在模板中可直接使用import useRouter from 'vue-router' import reactive, onMounted, toRefs from 'vue' // setup在beforeCreate 钩子之前被调用 // setup() 内部,this是undefined,因为 setup() 是在解析其它组件选项之前被调用的,所以 setup() 内部的 this 的行为与其它选项中的 this 完全不同。这在和其它选项式 API 一起使用 setup() 时可能会导致混淆 // props 是响应式的,当传入新的 prop 时,它将被更新(因为props是响应式的,所以不能使用 ES6 解构,因为它会消除 prop 的响应性。) // props参数:包含组件props配置声明且传入了的所有props的对象 // attrs参数:包含没有在props配置中声明的属性对象,相当于this.$attrs // slots参数:包含所有传入的插槽内容的对象,相当于this.$slots // emit参数:可以用来分发一个自定义事件,相当于this.$emit setup (props, attrs, slots, emit) const state = reactive( userInfo: ) const getUserInfo = async () => state.userInfo = await GET_USER_INFO(props.id) onMounted(getUserInfo) // 在 `mounted` 时调用 `getUserInfo` // setup的返回值 // 一般都是返回一个对象,为模板提供数据,就是模板中可以直接使用此对象中所有属性/方法 // 返回对象中的属性会与data函数返回对象的属性合并成为组件对象的属性 // 返回对象中的方法会与methods中的方法合并成组件对象的方法 // 若有重名,setup优先 return ...toRefs(state), getUserInfo
灵活的逻辑组合与复用
可与现有的Options API一起使用
与选项API最大的区别的是逻辑的关注点
选项API这种碎片化使得理解和维护复杂组件变得困难,在处理单个逻辑关注点时,我们必须不断地上下翻找相关代码的选项块。
compositon API将同一个逻辑关注点相关代码收集在一起
6. Fragment(碎片)<template> <header>...</header> <main v-bind="$attrs">...</main> <footer>...</footer> </template>
Vue 3不再限于模板中的单个根节点,它正式支持了多根节点的组件,可纯文字,多节点,v-for等
render 函数也可以返回数组
7. Teleport(传送门)这个组件的作用主要用来将模板内的 DOM 元素移动到其他位置。
允许我们控制在 DOM 中哪个父节点下渲染了 HTML<teleport to="body"> <div v-if="modalOpen" class="modal"> <div> I'm a teleported modal! (My parent is "body") <button @click="modalOpen = false"> Close </button> </div> </div> </teleport>
-
更好的Typescript支持
vue3是基于typescipt编写的,可以享受到自动的类型定义提示 -
自定义渲染 API
vue官方实现的 createApp 会给我们的 template 映射生成 html 代码,但是要是你不想渲染生成到 html ,而是要渲染生成到 canvas 之类的不是html的代码的时候,那就需要用到 Custom Renderer API 来定义自己的 render 渲染生成函数了。
意味着以后可以通过 vue, Dom 编程的方式来进行canvas、webgl 编程
默认的目标渲染平台自定义目标渲染平台
-
响应原理的变化
vue2对象响应化:遍历每个key,通过 Object.defineProperty API定义getter,setter 进而触发一些视图更新
数组响应化:覆盖数组的原型方法,增加通知变更的逻辑
vue2响应式痛点
递归,消耗大
新增/删除属性,需要额外实现单独的API
数组,需要额外实现
Map Set Class等数据类型,无法响应式
修改语法有限制
vue3响应式方案: 使用ES6的Proxy进行数据响应化,解决上述vue2所有痛点,Proxy可以在目标对象上加一层拦截/代理,外界对目标对象的操作,都会经过这层拦截。Proxy可以在目标对象上加一层拦截/代理,外界对目标对象的操作,都会经过这层拦截,相比 Object.defineProperty ,Proxy支持的对象操作十分全面
以上是关于vue2与vue3的差异(总结)?的主要内容,如果未能解决你的问题,请参考以下文章
转载vue 3.x 如何有惊无险地快速入门 —— 一文扫遍 vue2 与 3 的差异点
转载vue 3.x 如何有惊无险地快速入门 —— 一文扫遍 vue2 与 3 的差异点
Vue2.7正式发布,终于可以在Vue2项目中使用Vue3的特性了,真香~