Open oliver1204 opened 5 years ago
chrome DevTools: vue: 周活跃 90万 react: 周活跃 160万
vue 3.0 目标: 更快、更小、支持TypeScript
Object.defineProperty劫持 转换 Object 属性是一个昂贵的过程,浏览器的Javascript引擎 喜欢比较稳定的 Object 结构,而Proxy 真正做到了对原始 Object 的代理,初始化的性能得到了很好的提升。
Vue 3.0 的整个 Virtual DOM 用 TypeScript 重写了。完美的支持了TypeScript。很多人认为我们使用 vdom 目的是让节目变化的更快,但其实Virtual DOM 核心价值就是用 纯Javascript 描述界面渲染结构。于此同时其付出的代价是:每一次更新的时候,都需要从头开始遍历整个结构,一次比对,找到变化的部分。 虽然 Vue 能保证触发更新到组件,但是组件的颗粒度还是比较大的,在组件内部也需要遍历整个 vdom 树。例如下面的例子:
vdom
<template> <div id="content"> <p class="text">Lorem ipsum</p> <p class="text">Lorem ipsum</p> <p class="text">{{ message }}</p> <p class="text">Lorem ipsum</p> <p class="text">Lorem ipsum</p> </div> </template>
由上面的例子我们可以看出,整个组件中变化的部门只有{{ message }} 这一部分,但是我们却需要遍历整个 vdom。
{{ message }}
传统的 vdom 的性能跟模版大小正相关,跟动态节点的数量无关。在一些组件整个模版内只有少量动态节点的情况下,这些遍历都是性能的浪费。
虽然在大多数的情况下,很多更新是可以在16毫秒内完成的, 但是当项目足够的大时候,16毫秒并不是不够的。
那么传统的 vdom 为什么要用这种不效率的算法呢?究其原因就是:最初的vdom 并不是由模版编译而来的。比如 react 它是由 JSX 编译而来的,JSX 是 Javascript 的一种语法延伸,它具备 Javascript 的一切动态性。
react
JSX
Javascript
JSX 和 手写的 render function 由于是完全动态的,这种过度的灵活性导致运行时可以用于优化的信息缺失。
function render() { const children = [] for(let i = 0; i < 5; i++) { children.push(h('p', { class: 'text' }, i === 2 ? this.message : 'Lorum ipsum')) } return h('div', { id: 'content' }, children) }
比如,上面的 render function 我们很难单独的从这段代码中获取到唯有当 i === 2 时动态渲染 this.message 否则渲染 Lorum ipsum。
this.message
Lorum ipsum
反之,从上一个模版例子中,我们可以清楚的发现只有 this.message 是动态变化的。
所以,这就是包含的可以用于优化的信息的不同。react 的本质就是,只要你用 JSX 来写,我们就没有办法向用模版一样的来推测出可以优化的信息。
react 的优化方法是: 时间分片。将更新划分为一帧一帧的。react 秉承的观念是,既然伤害已经造成了,那么我们能做的就是将伤害减少到最底。
vue 的优化方法是: 认为整个 dom树都是静态的,然后从中间提炼出只变化的部分,Block tree(区块树),对比只变化的部分进行更新。
<template> <div> <p class="text">Lorem ipsum</p> <p v-if="ok"> <span>{{ message }}</span> <span>Lorem ipsum</span> </p> </div> </template>
v-if 外部,只有 v-if 是动态节点 v-if 内部,只有 {{ message }} 是动态节点
<template> <div> <p class="text">Lorem ipsum</p> <p v-for="item in list"> <span>{{ item.message }}</span> <span>Lorem ipsum</span> </p> </div> </template>
v-for 外部,只有 v-for 是动态节点 v-for 内部,只有 {{ item.message }} 是动态节点
新策略的 vdom 更新性能由与模版整体大小相关提升为与动态内容的数量相关。
render function 可以实现很多高级场景,但是使用 render function 不可避免的就要用到 Virtual DOM。 当然兼容 2.x 也是必须的。
引入 Class API 原本的目的是更好的支持 TS,但是 发现props 和其他插件的注入依然会导致类型问题,而且 Class API 除了支持类型外,似乎没有带来任何优势。
import { value, computed, watch, onMounted } from 'vue' const App = { template: ` <div> <span>count is {{ count }}</span> <span>plusOne is {{ plusOne }}</span> <button @click="increment">count++</button> </div> `, setup() { // reactive state const count = value(0) // computed state const plusOne = computed(() => count.value + 1) // method const increment = () => { count.value++ } watch(() => count.value * 2, val => { console.log(`count * 2 is ${val}`) }) onMounted(() => { console.log(`mounted`) }) // 暴露给模版使用的值 return { count, plusOne, increment } } }
优势:
在 3.0 之前逻辑复用的方式需要有 Mixins、Higher-order Components、Renderless Components 几种。
当使用大量的 Mixins 时会造成命名冲突以及模版数据来源不清楚等问题
Mixins
const mousePositionMixin = { data() { return { x: 0, y: 0 } }, mounted() { window.addEventListener('mousemove', this.update) }, destroyed() { window.addEventListener('mousemove', this. update) }, methods: { update(e) { this.x = e.pageX this.y = e.pageY } } }
高阶组件就是用一个父组件来承载逻辑内容,让父组件把最终的数据以Props 的形式传给里面的组件,当使用该组件的时候,就用此高阶组件把真正要写的组件包一下,真正要写的组件就以Props的形式接收外面传进来的数据。
const demo = withMousePosition({ props: ['x', 'y'], template: `<div> Mouse position: x {{x}} / y {{y}}</div>` })
大量使用时问题:
<mouse v-slot="{ x, y }"> Mouse position: x {{x}} / y {{y}} </mouse>
slot 没有命名空间冲突和数据来源不清楚的问题,但是依旧会造成额外的组件实例性能消耗。
组件部分:
function useMousePosition () { const x = value(0) const y = value(0) const update = e => { x.value = e.pageX y.value = e.pageY } onMounted(() => { window.addEventListener('mousemove', this. update) }) onUnmounted(() => { window.addEventListener('mousemove', this. update) }) return { x, y } }
模版部分:
new Vue({ https://github.com/olifer655/randomNotes/issues/93template: ` <div> Mouse position: x {{x}} / y {{y}} </div> `, data() { const { x, y } = useMousePosition() return { x, y } } })
React Hooks 存在的问题:
vue 完美的规避了上面的问题。
chrome DevTools: vue: 周活跃 90万 react: 周活跃 160万
vue 3.0 目标: 更快、更小、支持TypeScript
更快
1. Object.defineProperty —> Proxy
Object.defineProperty劫持 转换 Object 属性是一个昂贵的过程,浏览器的Javascript引擎 喜欢比较稳定的 Object 结构,而Proxy 真正做到了对原始 Object 的代理,初始化的性能得到了很好的提升。
2. Virtual DOM 重构
Vue 3.0 的整个 Virtual DOM 用 TypeScript 重写了。完美的支持了TypeScript。很多人认为我们使用
vdom
目的是让节目变化的更快,但其实Virtual DOM 核心价值就是用 纯Javascript 描述界面渲染结构。于此同时其付出的代价是:每一次更新的时候,都需要从头开始遍历整个结构,一次比对,找到变化的部分。 虽然 Vue 能保证触发更新到组件,但是组件的颗粒度还是比较大的,在组件内部也需要遍历整个 vdom 树。例如下面的例子:由上面的例子我们可以看出,整个组件中变化的部门只有
{{ message }}
这一部分,但是我们却需要遍历整个vdom
。传统的
vdom
的性能跟模版大小正相关,跟动态节点的数量无关。在一些组件整个模版内只有少量动态节点的情况下,这些遍历都是性能的浪费。3. 传统的
vdom
的性能瓶颈虽然在大多数的情况下,很多更新是可以在16毫秒内完成的, 但是当项目足够的大时候,16毫秒并不是不够的。
那么传统的
vdom
为什么要用这种不效率的算法呢?究其原因就是:最初的vdom
并不是由模版编译而来的。比如react
它是由JSX
编译而来的,JSX
是Javascript
的一种语法延伸,它具备Javascript
的一切动态性。JSX
和 手写的 render function 由于是完全动态的,这种过度的灵活性导致运行时可以用于优化的信息缺失。比如,上面的 render function 我们很难单独的从这段代码中获取到唯有当 i === 2 时动态渲染
this.message
否则渲染Lorum ipsum
。反之,从上一个模版例子中,我们可以清楚的发现只有
this.message
是动态变化的。所以,这就是包含的可以用于优化的信息的不同。react 的本质就是,只要你用
JSX
来写,我们就没有办法向用模版一样的来推测出可以优化的信息。react 的优化方法是: 时间分片。将更新划分为一帧一帧的。react 秉承的观念是,既然伤害已经造成了,那么我们能做的就是将伤害减少到最底。
vue 的优化方法是: 认为整个 dom树都是静态的,然后从中间提炼出只变化的部分,Block tree(区块树),对比只变化的部分进行更新。
v-if 外部,只有 v-if 是动态节点 v-if 内部,只有 {{ message }} 是动态节点
v-for 外部,只有 v-for 是动态节点 v-for 内部,只有 {{ item.message }} 是动态节点
4. Block tree(区块树)
新策略的
vdom
更新性能由与模版整体大小相关提升为与动态内容的数量相关。5. 为什么不能抛弃 Virtual DOM
render function 可以实现很多高级场景,但是使用 render function 不可避免的就要用到 Virtual DOM。 当然兼容 2.x 也是必须的。
TypeScript
1. 取消 Class API 取而代之的是 Function-based API
引入 Class API 原本的目的是更好的支持 TS,但是 发现props 和其他插件的注入依然会导致类型问题,而且 Class API 除了支持类型外,似乎没有带来任何优势。
2. Function-based API
优势:
逻辑复用
在 3.0 之前逻辑复用的方式需要有 Mixins、Higher-order Components、Renderless Components 几种。
1. Mixins
当使用大量的
Mixins
时会造成命名冲突以及模版数据来源不清楚等问题2. Higher-order Components 高阶组件
高阶组件就是用一个父组件来承载逻辑内容,让父组件把最终的数据以Props 的形式传给里面的组件,当使用该组件的时候,就用此高阶组件把真正要写的组件包一下,真正要写的组件就以Props的形式接收外面传进来的数据。
大量使用时问题:
3. Renderless Components 作用域插槽
slot 没有命名空间冲突和数据来源不清楚的问题,但是依旧会造成额外的组件实例性能消耗。
Function-based API
组件部分:
模版部分:
对比 React Hooks
React Hooks 存在的问题:
vue 完美的规避了上面的问题。