是什么
vue2 的升级版, 使用 ts 重构了代码, 带来了 Composition API RFC。 类似于 react hook 的写法。
- ts 重构,代码可读性更强
- vue3.x 使用 Proxy 取代 Vue2.x 版本的 Object.defineProperty
- 实现了 TreeShaking (当 Javascript 项目达到一定体积时,将代码分成模块会更易于管理。但是,当这样做时,我们最终可能会导入实际上未使用的代码。Tree Shaking 是一种通过消除最终文件中未使用的代码来优化体积的方法。)
- 支持 hook 写法 CompositionAPI。受 ReactHook 启发
- 支持 jsx
- Vue 3 的 Template 支持多个根标签,Vue 2 不支持
- 对虚拟 DOM 进行了重写、对模板的编译进行了优化操作
- 在 Vue2.x 中具名插槽和作用域插槽分别使用 slot 和 slot-scope 来实现, 在 Vue3.0 中将 slot 和 slot-scope 进行了合并 v-slot
- 在 Vue 3 中对自定义指令的 API 进行了更加语义化的修改,名称和组件生命周期名称相同
- v-model 变更:在自定义组件上使用 v-model 时,同一组件可以同时设置多个 v-model, 开发者可以自定义 v-model 修饰符
学到什么
- vue3 与 vue2 的核心区别
- Tree-Shaking
- 函数 setup()
- 函数 ref()
- 函数 isRef()
- 函数 toRefs()
- 函数 reactive()
- 函数 computed()、watch()
- LifeCycle Hooks(新的生命周期)
- Template refs
- vue3 的全局配置
- vue3 组件模板结构
- 实现 自定义 Hook
- 组件 teleport 任意门
- 组件 异步组件
vue3 与 vue2 的核心区别
- vue3.x 将使用 Proxy 取代 Vue2.x 版本的 Object.defineProperty
- Object.defineProperty 只能劫持对象的属性, 而 Proxy 是直接代理对象,由于 Object.defineProperty 只能劫持对象属性,需要遍历对象的每一个属性,如果属性值也是对象,就需要递归进行深度遍历。但是 Proxy 直接代理对象,不需要遍历操作
- Object.defineProperty 对新增属性需要手动进行 Observe
Tree-Shaking
当 Javascript 项目达到一定体积时,将代码分成模块会更易于管理。但是,当这样做时,我们最终可能会导入实际上未使用的代码。Tree Shaking 是一种通过消除最终文件中未使用的代码来优化体积的方法。
为什么:因为 Vue 实例是作为单个对象导出的,打包器无法分辨出代码中使用了对象的哪些属性。所以,我们需要单独引用
抽离了 一部分 vue2 中的公用函数,需要单独引用。以前的全局 API 现在只能通过具名导入,这一更改会对以下 API 有影响:
- Vue.nextTick
- Vue.observable(用 Vue.reactive 替换)
- Vue.version
- Vue.compile(仅限完整版本时可用)
- Vue.set(仅在 2.x 兼容版本中可用)
- Vue.delete(与上同)
setup 函数
组件提供的新属性,为了使用 vue3 CompositionAPI 新特性而启用的函数,它有自己独立的生命周期
vue3 取消了 beforeCreate 、created 两个钩子函数,统一用 setup 代替
- props 用来接收 props 数据
- context 上下文对象
- return 返回模板中需要使用的函数
setup(props, context) {
context.attrs
context.slots
context.emit
return {
}
}
ref() 函数
组件提供的新特性函数
创建一个响应式的数据对象,这个对象是响应式的,只返回一个 { value: ""} 值
import { defineComponent, ref } from "vue";
export default defineComponent({
setup() {
const name = ref < string > "hello";
// 在js 中获取ref 中定义的值, 需要通过value属性
console.log(name.value);
return {
name,
};
},
});
isRef() 函数
组件提供的新特性函数
isRef() 用来判断某个值是否为 ref() 创建出来的对象
import { defineComponent, isRef, ref } from "vue";
export default defineComponent({
setup(props, context) {
const name: string = "vue";
const age = ref(18);
console.log(isRef(age)); // true
console.log(isRef(name)); // false
return {
age,
name,
};
},
});
toRefs() 函数
组件提供的新特性函数
toRefs() 函数可以将响应式对象,转换为普通的对象。只不过,这个对象上的每个属性节点,都是 ref() 类型的响应式数据
import { defineComponent, reactive, ref, toRefs } from "vue";
export default defineComponent({
setup(props, context) {
let state = reactive({
name: "hello",
});
const age = ref(18);
return {
...toRefs(state),
age,
};
},
});
reactive() 函数
组件提供的新特性函数
reactive() 函数接收一个普通对象,返回一个响应式的数据对象, 想要使用创建的响应式数据也很简单,创建出来之后,在 setup 中 return 出去,直接在 template 中调用即可
import { defineComponent, reactive, ref, toRefs } from "vue";
export default defineComponent({
setup(props, context) {
let state = reactive({
name: "hello",
});
return state;
},
});
computed()、watch() 函数
组件提供的新特性函数
computed() 函数 ,用来计算属性,返回的值是一个 ref 对象。
import { computed, defineComponent, ref } from "vue";
export default defineComponent({
setup(props, context) {
const age = ref(18);
const computedAge = computed({
get: () => age.value + 1,
set: (value) => age.value + value,
});
// 为计算属性赋值的操作,会触发 set 函数, 触发 set 函数后,age 的值会被更新
age.value = 100;
return {
age,
computedAge,
};
},
});
watch() 函数,用来监听属性, 当数据源变化的时候才会被执行。
import { computed, defineComponent, reactive, toRefs, watch } from "vue";
interface Person {
name: string;
age: number;
}
export default defineComponent({
setup(props, context) {
const state = reactive({ name: "vue", age: 10 });
watch(
[() => state.age, () => state.name],
([newName, newAge], [oldName, oldAge]) => {
console.log(newName);
console.log(newAge);
console.log(oldName);
console.log(oldAge);
}
);
// 修改age 时会触发watch 的回调, 打印变更前后的值, 此时需要注意, 更改其中一个值, 都会执行watch的回调
state.age = 100;
state.name = "vue3";
return {
...toRefs(state),
};
},
});
LifeCycle Hooks 生命周期
组件提供的新特性函数
生命周期组件中新的写法
import { set } from "lodash";
import {
defineComponent,
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
onErrorCaptured,
onMounted,
onUnmounted,
onUpdated,
} from "vue";
export default defineComponent({
setup(props, context) {
onBeforeMount(() => {
console.log("beformounted!");
});
onMounted(() => {
console.log("mounted!");
});
onBeforeUpdate(() => {
console.log("beforupdated!");
});
onUpdated(() => {
console.log("updated!");
});
onBeforeUnmount(() => {
console.log("beforunmounted!");
});
onUnmounted(() => {
console.log("unmounted!");
});
onErrorCaptured(() => {
console.log("errorCaptured!");
});
return {};
},
});
模板 Template refs
组件提供的新特性函数
通过 refs 来回去真实 dom 元素,onMounted 中可以得到 ref 的 RefImpl 的对象, 通过.value 获取真实 dom
hello
vue 的全局配置
Vue3 可以在组件用通过 getCurrentInstance() 来获取全局 globalProperties 中配置的信息
const app = Vue.createApp({});
app.config.globalProperties.$http = "axios";
setup( ) {
const { ctx } = getCurrentInstance();
ctx.$http
}
vue3 组件模板结构
{{ name }}
{{ count }}
- {{ item.name }}
实现 自定义 Hook
功能性组件可以封装成 hook, 以 use 作为前缀,和普通的函数区分
import { ref, Ref, computed } from "vue";
type CountResultProps = {
count: Ref;
multiple: Ref;
increase: (delta?: number) => void;
decrease: (delta?: number) => void;
};
export default function useCount(initValue = 1): CountResultProps {
const count = ref(initValue);
const increase = (delta?: number): void => {
if (typeof delta !== "undefined") {
count.value += delta;
} else {
count.value += 1;
}
};
const multiple = computed(() => count.value * 2);
const decrease = (delta?: number): void => {
if (typeof delta !== "undefined") {
count.value -= delta;
} else {
count.value -= 1;
}
};
return {
count,
multiple,
increase,
decrease,
};
}
使用 hook
count: {{ count }}
倍数: {{ multiple }}
组件 teleport 任意门
目的: 即希望继续在组件内部使用 Dialog, 又希望渲染的 DOM 结构不嵌套在组件的 DOM 中
场景: 弹框
我们可以用
// Dialog.vue
{{ title }}
// Footer.vue 子组件
组件 异步组件
Vue3 中 使用 defineAsyncComponent 定义异步组件,配置选项 component 替换为 loader ,Loader 函数本身不再接收 resolve 和 reject 参数,且必须返回一个 Promise。
参考
- vue3 官网 https://v3.vuejs.org/
- 构建 vite https://github.com/vitejs/vite
- vue3 源码 https://github.com/vuejs/vue-next
- vue-cli https://cli.vuejs.org/
- vue-router https://github.com/vuejs/vue-router-next