本文是主要内容为转载,对其中部分内容进行了代码解析
只为记录学习过程,共享学习
1.性能的提升
打包大小减少41%
初次渲染快55%, 更新渲染快133%
内存减少54%
…
2.源码的升级
使用Proxy代替defineProperty实现响应式
重写虚拟DOM的实现和Tree-Shaking
…
3.拥抱TypeScript
4.新的特性
Composition API(组合API)
新的内置组件
其他改变
1.使用 vue-cli 创建
## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create vue_test
## 启动
cd vue_test
npm run serve
点击跳转
shallowReactive:
shallowReactive
用于创建一个浅层响应式对象,即只有对象的第一层属性是响应式的,而深层嵌套的对象不会被转换成响应式对象。这可以用于避免深层对象的监听,提高性能。
import { shallowReactive } from 'vue';
const obj = shallowReactive({
nested: {
count: 0
},
message: 'Hello'
});
// 只有第一层的属性是响应式的
console.log(obj.nested); // 没有响应式
console.log(obj.message); // 有响应式
// 修改第一层的属性,触发响应
obj.message = 'Updated';
// 修改第二层的属性,不触发响应
obj.nested.count++;
shallowRef:
shallowRef
用于创建一个浅层响应式引用,它与ref
相似,但只对引用类型(对象、数组等)进行浅层的响应式处理
import { shallowRef } from 'vue';
const refObj = { count: 0 };
const shallowObj = shallowRef(refObj);
// 修改引用对象的属性,触发响应
refObj.count++;
// 修改引用对象的属性,触发响应
shallowObj.value.count++;
// 修改引用对象,触发响应
shallowObj.value = { count: 1 };
总结一下,shallowReactive
适用于创建一个对象,只使对象的第一层属性响应式,而 shallowRef
适用于创建一个引用,对引用类型进行浅层响应式处理。使用这两者之一取决于你的具体需求和数据结构。
readonly
readonly
用于创建一个完全的只读对象,该对象及其嵌套的属性都是不可修改的。这意味着无论是对象的属性值还是嵌套对象,都不能被修改。如果你尝试修改 readonly
对象的属性,Vue 会发出警告。
import { readonly } from 'vue';
const obj = readonly({
prop1: 'value1',
nested: {
prop2: 'value2'
}
});
// 下面的操作会触发警告
obj.prop1 = 'new value';
obj.nested.prop2 = 'new value';
shallowReadonly:
shallowReadonly
用于创建一个浅层只读对象,即只有对象的第一层属性是只读的,而深层嵌套的对象仍然可以被修改。这允许你修改嵌套对象的属性,但不能修改嵌套对象本身。
import { shallowReadonly } from 'vue';
const obj = shallowReadonly({
prop1: 'value1',
nested: {
prop2: 'value2'
}
});
// 修改第一层的属性,允许
obj.prop1 = 'new value';
// 修改第二层的属性,会触发警告
obj.nested.prop2 = 'new value';
总结:
readonly
创建的对象是完全只读的,包括对象的所有嵌套属性。shallowReadonly
创建的对象只有第一层属性是只读的,允许修改嵌套对象的属性,但不允许修改嵌套对象本身。toRaw
import { reactive, toRaw } from 'vue';
const obj = reactive({
prop1: 'value1',
prop2: 'value2'
});
const rawObj = toRaw(obj);
console.log(rawObj === obj); // false
console.log(rawObj.prop1); // 'value1'
markRaw
import { reactive, markRaw } from 'vue';
const obj = reactive({
prop1: 'value1',
prop2: markRaw({
nestedProp: 'value2'
})
});
console.log(obj.prop1); // 'value1'
// obj.prop2 是一个不可代理的对象
console.log(obj.prop2.nestedProp); // 'value2'
// 尝试对 obj.prop2 进行响应式操作会引发警告
obj.prop2.nestedProp = 'new value';
总结:
toRaw
用于获取一个响应式对象的原始版本。markRaw
用于标记一个对象,使其成为不可代理的对象。customRef
在 Vue 3 中的应用场景主要是用于创建具有自定义行为的响应式对象。这个功能允许你手动控制对象的响应式,适用于一些特殊的需求。以下是一些 customRef
的应用场景:
延迟更新: 你可以使用 customRef
来实现一个具有延迟更新的响应式属性。例如,你可能希望在用户输入停止一段时间后再更新状态,而不是每次输入都立即更新。
import { customRef } from 'vue';
function useDebouncedRef(value, delay = 200) {
let timeout;
return customRef((track, trigger) => {
return {
get() {
track();
return value;
},
set(newValue) {
clearTimeout(timeout);
timeout = setTimeout(() => {
value = newValue;
trigger();
}, delay);
}
};
});
}
export default {
setup() {
const inputValue = useDebouncedRef('', 500);
return {
inputValue
};
}
};
异步更新: 你可以使用 customRef
来实现具有异步更新的响应式属性。例如,当某个异步操作完成后再更新状态。
import { customRef } from 'vue';
function useAsyncRef(asyncGetter) {
let value;
let isPending = true;
return customRef((track, trigger) => {
return {
get() {
track();
return value;
},
async set(newValue) {
isPending = true;
value = await asyncGetter(newValue);
isPending = false;
trigger();
}
};
});
}
export default {
setup() {
const asyncValue = useAsyncRef(async (inputValue) => {
// 模拟异步操作
await new Promise(resolve => setTimeout(resolve, 1000));
return inputValue.toUpperCase();
});
return {
asyncValue
};
}
};
自定义依赖追踪: 如果你希望在特定条件下追踪依赖,可以使用 customRef
实现自定义依赖追踪的逻辑。
import { customRef } from 'vue';
function useConditionalRef(initialValue) {
let value = initialValue;
return customRef((track, trigger) => {
return {
get() {
if (value > 0) {
track();
}
return value;
},
set(newValue) {
value = newValue;
trigger();
}
};
});
}
export default {
setup() {
const conditionalValue = useConditionalRef(1);
return {
conditionalValue
};
}
};
这些是一些 customRef
的应用场景示例。总体来说,customRef
提供了更灵活的方式来处理响应式对象,使得你能够根据具体需求实现自定义的响应逻辑。
作用:实现祖与后代组件间通信
套路:父组件有一个 provide 选项来提供数据,后代组件有一个 inject 选项来开始使用这些数据
provide
provide
是一个在父组件中使用的函数,用于提供数据给其子组件。它接收两个参数,第一个是键名,用于在子组件中识别数据,第二个是提供给子组件的数据。
// ParentComponent.vue
<script>
import { provide } from 'vue';
export default {
setup() {
const sharedData = 'Shared Data';
provide('sharedKey', sharedData);
return {
// other setup logic
};
}
};
</script>
inject
inject
是一个在子组件中使用的函数,用于接收来自父组件的提供的数据。它接收一个键名数组或对象,键名对应了在父组件中使用 provide
时设置的键名。
// ChildComponent.vue
<script>
import { inject } from 'vue';
export default {
setup() {
const sharedData = inject('sharedKey', 'Default Value');
console.log(sharedData); // 'Shared Data'
return {
// other setup logic
};
}
};
</script>
在上面的例子中,inject
接收了一个键名 'sharedKey'
,它会在父组件中查找是否有与之对应的数据,如果找到,则返回该值,否则返回提供的默认值 'Default Value'
。
注意事项:
provide
和 inject
是成对使用的,子组件通过 inject
获取提供的值。inject
中没有找到对应的键名,它会使用提供的默认值。provide
和 inject
不仅限于直接父子关系,也可以用于祖先和后代组件之间的通信。这一对 API 提供了一种简单而强大的方式,在组件树
中共享数据,避免了通过 props 一层层传递数据的繁琐。
isRef
: 检查一个值是否为一个 ref 对象isReactive
: 检查一个对象是否是由 reactive 创建的响应式代理isReadonly
: 检查一个对象是否是由 readonly 创建的只读代理isProxy
: 检查一个对象是否是由 reactive 或者 readonly 方法创建的代理在Vue2中: 组件必须有一个根标签
在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
好处: 减少标签层级, 减小内存占用
Teleport
将组件的内容渲染到任意的 DOM 元素上,而不受父组件的限制。
Teleport 在处理弹出层、模态框等情况时非常有用,因为它可以将组件的内容渲染到页面的其他位置,而不仅仅是组件所在的父组件内。
<template>
<div>
<button @click="toggleModal">Open Modal</button>
<!-- 使用 Teleport 渲染到 id 为 "modal-container" 的元素中 -->
<teleport to="#modal-container">
<Modal v-if="showModal" @close="closeModal" />
</teleport>
</div>
</template>
<script>
import Modal from './Modal.vue';
export default {
data() {
return {
showModal: false
};
},
methods: {
toggleModal() {
this.showModal = !this.showModal;
},
closeModal() {
this.showModal = false;
}
},
components: {
Modal
}
};
</script>
在上面的例子中,
标签将 Modal
组件的内容渲染到页面中 id
为 “modal-container” 的元素上。这样,无论 Modal
组件在组件树中的位置如何,它的内容都会被移动到指定的 DOM 元素上。
注意:
组件和 to
属性。to
属性指定了目标容器的选择器。to
属性可以是一个字符串,也可以是一个函数。函数的参数是 teleport 目标元素的引用,返回值是一个 DOM 元素,表示 Teleport 的目标容器。
是一个用于处理异步组件加载的特殊组件。它允许你在异步组件加载完成之前展示一些备用内容(例如 loading 状态),以提供更好的用户体验。
<template>
<div>
<Suspense>
<!-- 异步组件的引入 -->
<AsyncComponent />
<!-- 备用内容,可自定义 loading 状态 -->
<template #fallback>
<p>Loading...</p>
</template>
</Suspense>
</div>
</template>
<script>
// 异步组件的定义
const AsyncComponent = () => import('./AsyncComponent.vue');
export default {
components: {
AsyncComponent
}
};
</script>
在上面的例子中,
是一个异步组件,通过使用 import()
动态导入。当这个异步组件加载时,
将显示备用内容,直到异步组件加载完成。
是
的一个插槽,用于定义备用内容,即在异步组件加载过程中显示的内容。
注意:
可以包裹多个异步组件,而不仅仅是一个。
的插槽 fallback
中的内容可以是任何有效的 Vue 模板内容,因此你可以在加载过程中展示任何自定义的 loading 状态。import()
来异步加载,确保只在需要时才加载对应的 JavaScript。
的引入使得在处理异步操作时能够更好地控制用户体验,通过展示备用内容,提高了用户对于页面加载过程的感知。
2.x 全局 API(Vue) | 3.x 实例 API (app) |
---|---|
Vue.config.xxxx | app.config.xxxx |
Vue.config.productionTip | 移除 |
Vue.component | app.component |
Vue.directive | app.directive |
Vue.mixin | app.mixin |
Vue.use | app.usex |
Vue.prototype | app.config.globalProperties |