卷不动也得继续学!紧跟vue3的步伐,再来get一波进阶新特性!
Posted 星期一研究室
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了卷不动也得继续学!紧跟vue3的步伐,再来get一波进阶新特性!相关的知识,希望对你有一定的参考价值。
之前写了一篇文章谈论
vue3
的新特性,然鹅……周一最近又
get
到了几个比较进阶的新特性,
比如:
vue2
用
watch
,
vue3
为什么用
watchEffect
。还有
vue3
为什么比
vue2
快,
vite
为什么启动会非常快,以及
vue3
对全局注册
API
做出的重大改变。
一起来了解一波 vue3
新特性📷
一、📗watch和watchEffect
1、watch和watchEffect的区别
我们在 vue2
时,经常用 watch
来监听数据。但现在的 vue3
已经改用 watchEffect
来进行数据监听了。这两者具体有以下区别:
- 两者都可以监听
data
属性变化; watch
需要明确监听哪个属性;- 而
watchEffect
会根据其中的属性,自动监听其变化。
2、举个例子
(1)wtach监听
我们用 watch
来监听数据。具体代码如下:
<template>
<!-- <p>watch vs watchEffect</p> -->
<p>{{numberRef}}</p>
<p>{{name}} {{age}}</p>
</template>
<script>
import { reactive, ref, toRefs, watch, watchEffect } from 'vue'
export default {
name: 'Watch',
setup() {
const numberRef = ref(100)
const state = reactive({
name: 'monday',
age: 18
})
watch(numberRef, (newNumber, oldNumber) => {
console.log('ref watch', newNumber, oldNumber)
}
, {
immediate: true // 初始化之前就监听,可选
}
)
setTimeout(() => {
numberRef.value = 200
}, 1500)
watch(
// 第一个参数,确定要监听哪个属性
() => state.age,
// 第二个参数,回调函数
(newAge, oldAge) => {
console.log('state watch', newAge, oldAge)
},
// 第三个参数,配置项
{
immediate: true, // 初始化之前就监听,可选
// deep: true // 深度监听
}
)
setTimeout(() => {
state.age = 25
}, 1500)
setTimeout(() => {
state.name = 'mondayLab'
}, 3000)
return {
numberRef,
...toRefs(state)
}
}
}
</script>
此时浏览器的显示效果如下:
综上,我们可以知道,当使用 watch
进行属性监听时,需要明确要监听哪一个属性,并且如果想要在初始化时就被监听,需要加上第三个可选参数 immediate:true
。这样看来,如果我们要监听多个属性时,那就要写很多个 watch
,属实有点麻烦。所以, vue3
就引进了 watchEffect
来解决这几个问题。
(2)watchEffect监听
我们用 watchEffect
来监听数据。具体代码如下:
<template>
<!-- <p>watch vs watchEffect</p> -->
<p>{{numberRef}}</p>
<p>{{name}} {{age}}</p>
</template>
<script>
import { reactive, ref, toRefs, watch, watchEffect } from 'vue'
export default {
name: 'Watch',
setup() {
const numberRef = ref(100)
const state = reactive({
name: 'monday',
age: 18
})
watchEffect(() => {
// 初始化时,一定会先执行一次(收集要监听的数据)
console.log('numberRef', numberRef.value)
console.log('state.age', state.age)
console.log('state.name', state.name)
})
setTimeout(() => {
numberRef.value = 2000
}, 1000)
setTimeout(() => {
state.age = 25
}, 1500)
setTimeout(() => {
state.name = 'mondayLab'
}, 3000)
return {
numberRef,
...toRefs(state)
}
}
}
</script>
此时浏览器的显示效果如下:
从上图中可以看到, watchEffect
只要做一次监听,就可以同时监听到三个属性。同时,值得注意的是, watchEffect
需要初始化,且初始化时一定会先执行一次,这个初始化的目的在于收集要监听的数据。所以,控制台打印的第一组数据就是初始化时的数据。
第一次收集到它要监听这三个属性后,在此之后呢,这三个属性也相应地拥有了响应式的功能。相对应的三个计时器再打印出三组数据出来,所以一共是四组数据。
二、📘setup如何获取组件实例
(1)为什么需要获取组件实例
刚听到这个概念时,我其实时有点懵的。为什么要用setup来获取组件的实例?其实说的就是一个this的指向问题。
在 vue2
中, Options API
可以使用 this
来获取组件的实例,但是到现在的 vue3
,已经被摒弃掉了。在 setup
和其他 Composition API
中没有 this
,但是它提供了一个 getCurrentInstance
来获取当前的实例。
(2)举个例子
我们先用 Options API
来获取实例。具体代码如下:
<template>
<p>get instance</p>
</template>
<script>
import { onMounted, getCurrentInstance } from 'vue'
export default {
name: 'GetInstance',
data() {
return {
x: 1,
y: 2
}
},
mounted() {
console.log('this2', this)
console.log('x', this.x, 'y', this.y)
}
}
</script>
此时浏览器的显示效果如下:
正如我们所想的,用 options API
,具体的实例都可以如期的被调用出来。
下面我们用 Composition API
来看看,是否可以调用出来。具体代码如下:
<template>
<p>get instance</p>
</template>
<script>
import { onMounted, getCurrentInstance } from 'vue'
export default {
name: 'GetInstance',
data() {
return {
x: 1,
y: 2
}
},
setup() {
//无法获取this实例
console.log('this1', this)
const instance = getCurrentInstance()
console.log('instance', instance)
onMounted(() => {
//无法获取this实例
console.log('this in onMounted', this)
//通过getCurrentInstance获取this实例
console.log('x', instance.data.x)
})
}
}
</script>
此时浏览器的显示效果如下:
通过上图我们可以知道,如果用 Composition API
来获取组件实例,是没有办法获取的。需要通过 getCurrentInstance
方法来获取当前的组件实例。
三、📒 Vue3为何比Vue2快
有一次看面经的时候发现有一道题:Vue3为何比Vue2快。当时我也挺纳闷的,那个时候我的心里🤯: vue3
的出现不就是因为更好才出现嘛?不是更好难道还能更差?
事实证明……是我孤陋寡闻了。 Vue3 比 Vue2 快的原因主要体现在以下6个方面:
- Proxy响应式
- PatchFlag
- hoistStatic
- cacheHandler
- SSR优化
- tree-shaking
接下来就让我们一起来了解一下吧🙋
1、Proxy响应式
vue3
中实现响应式的 Proxy
会比 vue2
中的 Object.defineProperty
快。具体原因可翻看我的另外一篇文章,这里不再讲述。
2、PatchFlag
(1)什么是PatchFlag
- 在编译模板时,使用动态节点做标记;
- 标记,分为不同的类型,如
TEXT
、PROPS
;有的是直接获取text
,有的则是修改props
; diff
算法比较时,可以区分静态节点,以及不同类型的动态节点。此处要注意的是,patchflag
并不是专门对diff
算法做优化,而是在输入上做一些变更和做一些标记,从而达到对diff
算法的优化。
(2)举个例子🌰
我们用一个在线网站来演示 patchflag
,在线网站网址为https://vue-next-template-explorer.netlify.app/,大家可以根据需要自行演示~
具体使用方式如下图所示:
接下来我们来演示 patchflag
,此时右边的 options
不做选择。我们在左边的框输入下面代码:
<div id="app">
<span>hello vue3</span>
<span>{{msg}}</span>
<span :class="name">monday</span>
<span :id="name">monday</span>
<span :id="name">{{mag}}</span>
<span :id="name" :msg="msg">monday</span>
</div>
此时右边的框显示如下:
import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", { id: "app" }, [
_createVNode("span", null, "hello vue3"),
_createVNode("span", null, _toDisplayString(_ctx.msg), 1 /* TEXT */),
_createVNode("span", { class: _ctx.name }, "monday", 2 /* CLASS */),
_createVNode("span", { id: _ctx.name }, "monday", 8 /* PROPS */, ["id"]),
_createVNode("span", { id: _ctx.name }, _toDisplayString(_ctx.mag), 9 /* TEXT, PROPS */, ["id"]),
_createVNode("span", {
id: _ctx.name,
msg: _ctx.msg
}, "monday", 8 /* PROPS */, ["id", "msg"])
]))
}
// Check the console for the AST
大家可以看到,除了第一个是静态节点以外,其他都是动态节点。此时模板编译后的结果,在最后边有对应的数字出现,这个数字就是标记。 vue3
通过给每个动态节点做数字标记,达到优化 diff
算法的效果。
3、hoistStatic
(1)什么是hoistStatic
- 将静态节点的定义,提升到父作用域上,并缓存起来;
- 多个相邻的静态节点,会被合并起来;
- 典型的拿空间换时间的优化策略。
(2)举个例子🌰
我们同样用在线网站来做一个演示。此时我们在左边框输入以下代码:
<div id="app">
<span>monday</span>
<span>monday</span>
<span>monday</span>
<span>{{msg}}</span>
</div>
此时右边的框显示如下:
import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"
const _hoisted_1 = { id: "app" }
const _hoisted_2 = /*#__PURE__*/_createVNode("span", null, "monday", -1 /* HOISTED */)
const _hoisted_3 = /*#__PURE__*/_createVNode("span", null, "monday", -1 /* HOISTED */)
const _hoisted_4 = /*#__PURE__*/_createVNode("span", null, "monday", -1 /* HOISTED */)
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", _hoisted_1, [
_hoisted_2,
_hoisted_3,
_hoisted_4,
_createVNode("span", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)
]))
}
// Check the console for the AST
通过以上代码可以发现, vue3
在每一个静态节点的外部都定义了父节点。这样看好像更冗余了一点,原因在于现在节点还比较少。
下面我们来演示更多的节点,此时右边的 options
选择 hoistStatic
。我们在左边框输入以下代码:
<div id="app">
<span>monday</span>
<span>monday</span>
<span>monday</span>
<span>monday</span>
<span>monday</span>
<span>monday</span>
<span>monday</span>
<span>monday</span>
<span>monday</span>
<span>monday</span>
<span>{{msg}}</span>
</div>
此时右边的框显示如下:
import { createVNode as _createVNode, toDisplayString as _toDisplayString, createStaticVNode as _createStaticVNode, openBlock as _openBlock, createBlock as _createBlock } from "vue"
const _hoisted_1 = { id: "app" }
const _hoisted_2 = /*#__PURE__*/_createStaticVNode("<span>monday</span><span>monday</span><span>monday</span><span>monday</span><span>monday</span><span>monday</span><span>monday</span><span>monday</span><span>monday</span><span>monday</span>", 10)
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", _hoisted_1, [
_hoisted_2,
_createVNode("span", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)
]))
}
// Check the console for the AST
此时可以看到, vue3
把所有的静态节点都包围成一个父节点了,就好像 vue3
跟它的甲方爸爸说,要不这样吧,我帮你做一个静态节点的集合,帮你把所有内容都定义到一起。
4、cacheHandler
(1)什么是cacheHandler
cacheHandler
,指缓存事件的意思。
(2)举个例子🌰
我们同样用在线网站来做一个演示,此时右边的 options
选择 cacheHandler
。我们在左边框输入以下代码:
<div id="app">
<span @click="clickHandler">
monday
</span>
</div>
此时右边的框显示如下:
import { createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from "vue"
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", { id: "app" }, [
_createVNode("span", {
onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.clickHandler && _ctx紧跟月影大佬的步伐,一起来学习如何写好JS(下)