VUE3 module
1. What are the advantages of VUE3 over VUE2?
2. VUE3 life cycle
3. Examples of ref, toRef, reactive, toRefs
ref example
<template>
<p>ref demo {
{
ageRef }}{
{
state.name }}</p>
</template>
<script>
import {
reactive, ref } from 'vue'
export default {
name: 'Ref',
setup() {
const ageRef = ref(20) // 值类型 响应式
const nameRef = ref('ref的使用')
const state = reactive({
name: nameRef,
})
setTimeout(() => {
console.log('ageRef', ageRef.value) // 1.5s后 ageRef 25
ageRef.value = 25 // .value 修改值
nameRef.value = '除template模版和reactive中,其他地方都使用.value'
}, 1500)
return {
ageRef,
state,
}
},
}
</script>
toRef example
<template>
<p>toRef demo --- {
{
ageRef }} -- {
{
state.name }} {
{
state.age }}</p>
</template>
<script>
import {
toRef } from 'vue'
export default {
name: 'toRef',
setup() {
const state = reactive({
// 普通对象想实现响应式用 reactive
age: 20,
name: 'toRef的使用',
})
// toRef 如果用于普通对象(非响应式对象),产出的结果不具备响应式
// const state = {
// age: 20,
// name: '普通对象,不具备响应式',
// }
// reactive 中的某一个属性想要 单独拿出来 实现响应式 使用 toRef
const ageRef = toRef(state, 'age') // 引用 state 中的 age属性
setTimeout(() => {
state.age = 25
}, 1500)
setTimeout(() => {
ageRef.value = 30 // .value 修改值
}, 3000)
return {
ageRef,
state,
}
}
}
</script>
toRefs example
<template>
<p>toRefs demo {
{
age }}{
{
name }}</p>
</template>
<script>
import {
toRefs ,reactive} from 'vue'
export default {
name: 'toRefs',
setup() {
const state = reactive({
age: 20,
name: 'toRefs示例',
})
const stateAsRefs = toRefs(state) // 将响应式对象,变成普通对象
// 每个属性,都是 ref对象。 因此取出 age 要使用 Ref 结尾命名
// const { age: ageRef, name: nameRef } = stateAsRefs
// return {
// 然后 return 上面命名的 ageRef, 模版中使用 {
{ageRef}}
// ageRef,
// nameRef,
// }
// 此时一旦 state中的属性多了,需要给每一个都命名,不利用维护.
setTimeout(() => {
console.log('如果直接...state,会失去响应式。而使用toRefs没事')
state.age = 25
}, 1500)
// 因此这样 更加方便直观
return {
// ...state | 如果直接使用 ...state ,模版中 {
{name}} 会丢失响应式
// state | 如果这样使用 state,模版中 需要{
{state.name}} 不会丢失响应式,但是很繁琐
...stateAsRefs, // = return stateAsRefs 这种写法
}
},
}
</script>
-
toRef 是针对 state中的 某一个属性
-
toRefs 是针对 state 中的 所有属性
4. Advanced, in-depth understanding of ref
Why is ref needed?
Not responsive - example
<template>
<p>
why ref demo {
{
age1 }}-[...state会丢失响应式],|
{
{
state.age }}-[具备响应式]
</p>
</template>
<script>
import {
reactive } from 'vue'
export default {
name: 'WhyRef',
setup() {
// Proxy 对象(针对对象才具备响应式)
const state = reactive({
age: '20',
name: 'reactive针对对象,ref针对普通类型',
})
let age1 = 20 // 普通值类型,不具备响应式
setTimeout(() => {
console.log('普通值类型数据,并不具备响应式')
age1 = 25 // 普通值类型 不具备响应式
state.age = 25 // = let age = 25 不具备响应式
}, 1500)
return {
age1, // 普通值类型 不具备响应式
...state, // 如果直接使用 ...state ,模版中 {
{age}} 会丢失响应式
}
},
}
</script>
Responsive - Example
<template>
<p>
why ref demo
{
{
state.age }}-[具备响应式] ,| {
{
age1 }}-[具备响应式]
</p>
</template>
<script>
import {
reactive } from 'vue'
export default {
name: 'WhyRef',
setup() {
// Proxy 对象(针对对象才具备响应式)
const state = reactive({
age: '20',
name: 'reactive针对对象,ref针对普通类型',
})
const age1 = computed(() => {
return state.age + 1
})
// computed 返回的也是一个类似 ref的值,类似ref的机制
// 也可以通过 .value来获取值,但是不要修改,computed的值不太适合修改
console.log('age1.value', age1.value)
setTimeout(() => {
state.age = 25 // = let age = 25 不具备响应式
}, 1500)
return {
state, // 模版使用 state.age具备响应式
age1, // 具备响应式
}
},
}
</script>
<template>
<p>{
{
x }}{
{
y }} -- 此时具备响应式</p>
</template>
<script>
import {
reactive, toRefs } from 'vue'
function useFeatureX() {
const state = reactive({
x: 1,
y: 2,
})
return toRefs(state)
// return state 模版中{
{ state1.y }} - 不进行结构需要这种方式写
}
export default {
name: 'WhyRef',
setup() {
const {
x, y } = useFeatureX()
// const state1 = useFeatureX() 不进行结构
return {
// state1, // 不进行结构
}
},
}
</script>
5. Why is .value needed?
6. What important functions have been upgraded in VUE3?
7. How to use Composition API to implement logic reuse?
8. How does VUE3 implement responsiveness?
9. In-depth proxy, see the packaged proxy below
// 创建响应式
function reactive(target = {
}) {
if (typeof target !== 'object' || target == null) {
// 不是对象或数组,则返回
return target
}
// 代理配置
const proxyConf = {
get(target, key, receiver) {
// 只处理本身(非原型的) 属性
const ownKeys = Reflect.ownKeys(target)
if (ownKeys.includes(key)) {
console.log('get 获取', key) // 监听
}
const result = Reflect.get(target, key, receiver)
//深度监听
// 如果直接返回 retun result , data中的info 会被上面 if判断返回
return reactive(result)// 返回结果
},
set(target, key, val, receiver) {
// 重复的数据,不处理
if (val === target[key]) {
return true
}
// 只处理本身(非原型的) 属性
const ownKeys = Reflect.ownKeys(target)
if (ownKeys.includes(key)) {
console.log('已有的 key', key) // 监听
} else {
console.log('新增的 key', key);
}
const result = Reflect.set(target, key, val, receiver)
console.log('set 设置', key, val);
// console.log('result 是否设置成功',result); // true
return result // 返回结果
},
deleteProperty(target, key) {
const result = Reflect.defineProperty(target, key)
console.log('delete property', key);
// console.log('result 是否设置成功',result) // true
return result // 返回结果
}
}
// 生成代理对象
const observed = new Proxy(target, proxyConf)
return observed
}
// 测试数据
const data = {
name: 'proxy实现响应式', // proxyData.name | 会 get name 获取到name名
age: 20,
info: {
// proxyData.info.city | get info 并没有 get到city
city: '北京,需要深度监听', // 在get 时返回 reactive(result) 可以get 到 city
a: {
// 如果访问 a , a具有响应式
b: 100 // b未被访问,b不具备响应式
}
}
}
// 将要实现响应式的 data对象 传入 reactive函数中
const proxyData = reactive(data)
- How is the performance improved?
- Oject.defineProperty is recursive from the beginning, recursively to the end
- When does the Proxy get and when does it recurse, and if it doesn’t get, it doesn’t recurse
10. V3 removes the .sync attribute and replaces it with v-model
The following is a sample code, which includes a parent component and a child component, which realize the two-way binding of parent-child data through v-bind and $emit:
<!-- 父组件 TextDocument.vue -->
<template>
<div>
<h1>{
{
doc.title }}</h1>
<title-editor v-bind:title="doc.title" v-on:update:title="updateTitle"></title-editor>
</div>
</template>
<script>
import TitleEditor from './TitleEditor.vue'
export default {
components: {
TitleEditor
},
data() {
return {
doc: {
title: "默认标题"
}
}
},
methods: {
updateTitle(newTitle) {
this.doc.title = newTitle;
}
}
};
</script>
<!-- 子组件 TitleEditor.vue -->
<template>
<div>
<input type="text" v-bind:value="title" v-on:input="updateTitle($event.target.value)">
</div>
</template>
<script>
export default {
props: ['title'],
methods: {
updateTitle(newTitle) {
this.$emit('update:title', newTitle);
}
}
}
</script>
11. What is the difference between watch and watchEffect?
watch monitors ref changes
<!-- 先看watch 监听 ref-->
<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) // watch监听
watch(
numberRef,
(newNumber, oldNumber) => {
// watch 不需要使用.value
console.log('ref watch', newNumber, oldNumber)
// 调用setTimeout 打印结果 200 100
},
{
immediate: true, // 初始化之前就监听,可选
// 打印结果 ref watch 100 undefined
// 为什么是undefined,因为没有修改值,监听初始化就是undefined
}
)
// setTimeout(() => {
// numberRef.value = 200
// }, 1500)
return {
numberRef
}
},
}
</script>
watch monitors reactive changes
<template>
<p>watch vs watchEffect</p>
<p>{
{
numberRef }}</p>
<p>{
{
name }}{
{
age }}</p>
</template>
<script>
import {
reactive, ref, toRefs, watch } from 'vue'
export default {
name: 'watch',
setup() {
const numberRef = ref(100) // watch监听
const state = reactive({
name: 'watch 监听 reactive',
age: 20,
})
watch(
() => state.age,
// 第二个参数,回调函数
(newAge, oldAge) => {
console.log('state watch', newAge, oldAge)
},
// 第三个参数,配置项
{
immediate: true, // 初始化之前就监听,可选
deep: true, // 深度监听,
// 如果age是一个对象或数组,就可以深度监听 其中的变化
}
)
setTimeout(() => {
state.age = 25
}, 1500)
setTimeout(() => {
state.name = '是否会监听到'
}, 3000)
return {
numberRef,
...toRefs(state),
}
},
}
</script>
watchEffect monitors ref and reactive
The state data in setup is the same as the watch monitoring code above
12. How to get component instance in setup
const instance = getCurrentInstance() is the instance of the component
13. Why is VUE3 faster than VUE2? The compilation optimization is as follows
PatchFlag static flag
template template
<div>
<span>hello vue3</span>
<span>{
{
msg}} 使用TEXT标记</span>
<span :id="name">使用ClASS标记</span>
<span :id="name">{
{
msg}} 使用TEXT 和 PROPS标记</span>
<span :id="name" :msg="msg">使用PROPS 放入数组中标记多个</span>
</div>
After the patchFlag tag
import {
createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock("div", null, [
_createElementVNode("span", null, "hello vue3"),
_createElementVNode("span", null, _toDisplayString(_ctx.msg) + " 使用TEXT标记", 1 /* TEXT */),
_createElementVNode("span", {
id: _ctx.name }, "使用ClASS标记", 8 /* PROPS */, ["id"]),
_createElementVNode("span", {
id: _ctx.name }, _toDisplayString(_ctx.msg) + " 使用TEXT 和 PROPS标记", 9 /* TEXT, PROPS */, ["id"]),
_createElementVNode("span", {
id: _ctx.name,
msg: _ctx.msg
}, "使用PROPS 放入数组中标记多个", 8 /* PROPS */, ["id", "msg"])
]))
}
// Check the console for the AST
1 is TEXT, indicating that the text content of VNode has changed, and the view needs to be updated
hostStatic cache merge
template template
<div>
<span>hello vue3</span>
<span>hello vue3</span>
<span>hello vue3</span>
<span>{
{
msg}}</span>
</div>
hoistStatic uses -1 /* HOISTED */ is caching and promotion
import {
createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
const _hoisted_1 = /*#__PURE__*/_createElementVNode("span", null, "hello vue3", -1 /* HOISTED */)
const _hoisted_2 = /*#__PURE__*/_createElementVNode("span", null, "hello vue3", -1 /* HOISTED */)
const _hoisted_3 = /*#__PURE__*/_createElementVNode("span", null, "hello vue3", -1 /* HOISTED */)
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock("div", null, [
_hoisted_1,
_hoisted_2,
_hoisted_3,
_createElementVNode("span", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)
]))
}
// Check the console for the AST
If adjacent nodes >= 10, they will be merged into one node
import {
createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, createStaticVNode as _createStaticVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
const _hoisted_1 = /*#__PURE__*/_createStaticVNode("<span>hello vue3</span><span>hello vue3</span><span>hello vue3</span><span>hello vue3</span><span>hello vue3</span><span>hello vue3</span><span>hello vue3</span><span>hello vue3</span><span>hello vue3</span><span>hello vue3</span>", 10)
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock("div", null, [
_hoisted_1,
_createElementVNode("span", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)
]))
}
// Check the console for the AST
cacheHandler cache event
import {
createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createElementBlock("div", null, [
_createElementVNode("span", {
onClick: _cache[0] || (_cache[0] = (...args) => (_ctx.clickHandler && _ctx.clickHandler(...args)))
}, "hello vue3")
]))
}
// Check the console for the AST
SSR optimization
After checking SSR, when it renders, it renders a string, and it can be done without vdom conversion
<div>
<span>hello vue3</span>
<span>hello vue3</span>
<span>hello vue3</span>
<span>{
{
msg }}</span>
</div>
import {
mergeProps as _mergeProps } from "vue"
import {
ssrRenderAttrs as _ssrRenderAttrs, ssrInterpolate as _ssrInterpolate } from "vue/server-renderer"
export function ssrRender(_ctx, _push, _parent, _attrs, $props, $setup, $data, $options) {
const _cssVars = {
style: {
color: _ctx.color }}
_push(`<div${
_ssrRenderAttrs(_mergeProps(_attrs, _cssVars))
}><span>hello vue3</span><span>hello vue3</span><span>hello vue3</span><span>${
_ssrInterpolate(_ctx.msg)
}</span></div>`)
}
// Check the console for the AST
tree shaking
When compiling, different APIs are introduced according to different situations. It doesn't pull in all of them, but on demand.
- According to the SSR code above, import whatever you need
- Dynamically go to import to introduce, don't import if you don't need it
Summarize