大家好,欢迎来到IT知识分享网。
一、前言
笔者之前是Vue2+React开发者,因项目需要直接上手Vue3,所以快速学习一下,中间会对比一些和React相关的区别。阅读前提:已经上手了Vue2的开发
,本文主要聊的问题:
- Vue3的全新特性
- Vue2和Vue3的一些区别
- Vue2开发者如何快速上手Vue3
- Vue3和React的简单比对
- 使用Vue3编写组件库
- 后续关于Vue3的一些学习、源码、面试题等
【俊劫的学习基地】 更多前端学习资源等你来拿(有摸鱼群🐳),
点击关注公众号,一起学习打卡!兄弟姐妹帮小弟点个赞
强烈推荐小满ZS的B站教程:Vue3 + vite + Ts + pinia + 实战 + 源码 +全栈
二、Vue2 vs Vue3
2.1 简单点说
- Vue2只支持
单节点
,Vue3 template支持多节点
,类似react fragments 变化
基本都在script中(Option api -> Composition api)不会再看见满屏的this了!!!- style支持
v-bind
- Proxy代替defineProperty
- defineProperty无法实现对数组对象的深层监听,
Proxy
是浏览器最新api,功能更加强大。 - 不再支持IE,Vue2想享受Vue3带来的部分更新,可考虑升级
Vue2.7
版本
- defineProperty无法实现对数组对象的深层监听,
- TypeScript的支持
- Vue2采用的是Facebook的
Flow
,没法完美支持TypeScript
(所以项目初期技术选型很重要) - Vue3 TypeScript
完全重写
,提供和React一样的TS支持
- Vue2采用的是Facebook的
- 全新生态
- 基本还是vue周边伴随Vue3升级那一套,但是状态管理推荐,由原来的Vuex变为
Pina
- 全新的
vite
支持,包括vitest
等,官方提供周边工具更多了
- 基本还是vue周边伴随Vue3升级那一套,但是状态管理推荐,由原来的Vuex变为
- 其它优化
性能更好,体积更小
就不用说了事件监听缓存
,类似@click绑定的函数,无需多次创建,放在了cacheHandler缓存中SSR
:Vue 3.0 会将静态标签直接转化为文本,相比 React 先将 JSX 转化为虚拟 DOM,再将虚拟 DOM 转化为 HTML,这一点Vue3的速度要快很多Use Hooks
放弃过去的mixins,使用Hooks解决过去mixins的一些缺点
2.2 源码
了解的不多,后续再补充
diff算法的优化
- 不再和vue2一样,完全对比两棵虚拟DOM树,Vue3采用
动静结合
的方法,优化diff性能 - 通过编译阶段对静态模板进行分析,编译生成 Block tree。更新性能由
模版整体大小相关
=》与动态内容的数量相关
,这是一个非常大的性能突破。将代码提升到渲染函数之外,这样可以避免在每次渲染时重新创建这些对象,从而大大提高内存使用率并减少垃圾回收的频率。
源码管理
- vue2
poly-repo
- vue2.x的源码托管在src目录中,然后依据功能拆分出了complier(模板编译的相关代码),core(与平台无关的通用运行时代码),platforms(平台专有代码),server(服务端渲染的相关代码)、sfc(.vue 单文件解析相关代码)、shared(共享工具代码) 等目录
- vue3
mono-repo
- package可以
独立于
vue.js去使用,这样例如用户想要使用vue3.0的响应式,可以单独依赖reactive
,而不必依赖整个vue.js,减少引用包的体积,而vue2.x却做不到这一点。
- package可以
- 源码结构对比
三、全新的API
什么是组合式 API?- Vue官方
- 解决了过去组件过长时,optionsApi带来的难以维护的问题
- 逻辑可以整块复用
- 所有API都是import引入的,对Tree- shaking很友好,没用到功能,打包的时候会被清理掉,减小包的大小
3.1 setup
- 新的
setup
选项在组件被创建之前执行,一旦props
被解析完成,它就将被作为组合式 API 的入口。 - 可以当做Vue2的beforeCreate和create生命周期用
- 可直接写
await语法
- SFC单文件组件中直接使用
<script lang="ts" setup>
即可,或者也可以结合export default
使用
<script lang="ts" setup>
const result = await Http.get('/getUserInfo')
</script>
// or
export default {
setup(props, context) {
// Attribute (非响应式对象,等同于 $attrs)
console.log(context.attrs)
// 插槽 (非响应式对象,等同于 $slots)
console.log(context.slots)
// 触发事件 (方法,等同于 $emit)
console.log(context.emit)
// 暴露公共 property (函数)
console.log(context.expose)
}
}
IT知识分享网
3.2 ref
- ref 用来创建
基础类型
的响应式数据 - template中默认调用value显示数据,script中需要使用
.value
调用 - 和react ref差不多,react是.current获取值,vue3是.value。
- Ref的本质是通过Reactive创建的,Ref(10)=>Reactive({value:10})
有一定的心智负担,尤大也明确说了不会支持script中直接访问。究其原因,还是基础类型无法拦截它的变化,当然也有大哥提出用new String(‘foo’)类似的语法对基础类型进行包装。个人感觉直接拿已支持的reactive来搞也不错
相关api
Ref
ts定义import { type Ref } from 'vue';
isRef
判断是否为ref对象。一般是ref,toRef,toRefs创建的变量toRefs
将reactive对象
解构为单个响应式对象shallowRef
创建一个跟踪自身 .value 变化的 ref,但不会使其值也变成响应式的,简单理解为创建一个和ref相同结构的非响应式变量triggerRef
强制更新页面DOM。即使创建的ref没有变,想更新dom可以用customRef
提供类似于computed的get和set,可自定义ref行为
3.3 reactive
- reactive 用来创建
引用类型
的响应式数据 - reactive的本质是将每一层的数据都解析成
proxy对象
- reactive 的响应式默认都是
递归的
,改变某一层的值都会递归的调用一遍,重新渲染dom。 直接解构
,响应性会丢失,需要用toRefs
包裹。引用类型直接改变引用地址也会导致响应式丢失
相关api
readonly
将reactive的值更改为只读shallowReactive
只能对浅层的数据响应 如果是深层的数据只会改变值 不会改变视图
IT知识分享网import { reactive, toRefs } from 'vue'
const book = reactive({
author: 'Vue Team',
year: '2020',
title: 'Vue 3 Guide',
description: 'You are reading this book right now ;)',
price: 'free'
})
let { author, title } = toRefs(book)
title.value = 'Vue 3 Detailed Guide' // 我们需要使用 .value 作为标题,现在是 ref
console.log(book.title) // 'Vue 3 Detailed Guide'
3.4、生命周期
区别不大,把setup当created用,其它就当改了个名
<script setup lang="ts"> import { onMounted } from 'vue'; const getUserInfo = () => { console.log('获取用户信息'); }; onMounted(getUserInfo); </script>
3.5 watch & watchEffect
watch
- 功能和vue2一致
- watch(监听参数,变化回调,配置参数)
- 注意监听对象的单个属性:
watch(() => articleInfo.author, (newVal) => {})
,第一个参数为箭头函数返回要监听的目标属性
IT知识分享网import { ref, reactive, watch } from 'vue'
const counter1 = ref(0)
const counter2 = ref(0)
// 监听多个
watch([counter1, counter2], (newValue, oldValue) => {
console.log('The new counter1 value is: ' + counter1.value)
console.log('The new counter2 value is: ' + counter2.value)
})
const obj = reactive({
name: 'JJ',
age: 18
})
// 深度监听对象
watch(obj, (newValue, oldValue) => {
console.log('The new obj value is: ' + obj.value)
}, {
deep: true,
immediate: true
})
// watch监听单个属性
watch(() => obj.name, (newValue, oldValue) => {
console.log('The new obj value is: ' + obj.value)
}, {
deep: true,
immediate: true
})
watchEffect
- 类似React useEffect,但是不需要写依赖项,只要我们回调中引用了
响应式的属性
- 和watch的区别:
- 同一个功能的两种不同形态,底层的实现是一样的
- watch 可以获取到新值与旧值(更新前的值),而
watchEffect
是拿不到的。 - watch –
显式
指定依赖源,watchEffect –自动
收集依赖源 - watchEffect 在组件初始化的时候就会执行一次用以收集依赖,watch指定了依赖,所以不需要。
可以理解为watchEffect 就是配置了{ immediate: true } 的watch
- 使用场景:
antfu小哥
:推荐在大部分时候用 watch 显式的指定依赖以避免不必要的重复触发,也避免在后续代码修改或重构时不小心引入新的依赖。watchEffect 适用于一些逻辑相对简单,依赖源和逻辑强相关的场景(或者懒惰的场景 )。
const stopWatch = watchEffect(
(oninvalidate): void => {
oninvalidate(() => {
console.log("前置校验函数");
});
// 引用了响应式的属性 count
console.log("watchEffect count变化", count.value);
},
{
// 副作用刷新时机 flush 一般使用post
// 可选:pre(组件更新前执行)/sync(强制效果始终同步触发)/post(组件更新后执行)
flush: "post",
// 开发调试
onTrigger() {
console.log("开发调试");
},
}
);
3.6 computed
- 更加灵活,可以在定义响应式变量时声明
- 作用和vue2无差异
import { ref, computed } from 'vue'
const counter = ref(0)
const twiceTheCounter = computed(() => counter.value * 2)
// get set写法
const plusOne = computed({
get: () => counter.value + 1,
set: (val) => {
counter.value = val - 1
},
})
plusOne.value = 1
console.log(counter.value) // 0
counter.value++
console.log(counter.value) // 1
console.log(twiceTheCounter.value) // 2
四、组件
4.1 异步组件
- 通过进行引入defineAsyncComponent
- 可配合Suspense 进行更多操作,可用于loading和骨架屏相关,和react Suspense基本一致。不过react Suspense基本一致这个属性都不太好用,vue的不清楚实际场景咋样
// template
<Suspense>
<template #default> <AsyncComponent></AsyncComponent> </template>
<template #fallback> <div>loading...</div> </template>
</Suspense>
// script
const AsyncComponent = defineAsyncComponent(() => import('./asyncComponent.vue'))
4.2 Teleport传送组件
Teleport 是一种能够将我们的模板渲染至指定DOM节点,不受父级style、v-show等属性影响,但data、prop数据依旧能够共用的技术;类似于 React 的 Portal
。之前写react是不怎么用这个属性,vue3这个估计也没多大用。
主要解决的问题 因为Teleport节点挂载在其他指定的DOM节点下,完全不受父级style样式影响
to 属性 插入指定元素位置,body,html,自定义className等等
<Teleport to="body">
<Loading></Loading>
</Teleport>
4.3 keep-alive缓存组件
- 作用和vue2还是一样的,生命周期名称变了
- 初次进入时:onMounted> onActivated
- 退出后触发 deactivated
- 再次进入:只会触发 onActivated
4.4 组件通信
defineXxxx
defineXxxx 无需import即可直接使用
- defineProps 代替过去的props
- defineEmits 代替过去的$emit
- defineOptions 自定义一些组件属性,比如组件名称(需要插件支持)
- defineComponent 用于render函数、TSX、IDE提醒等
- defineExpose 子组件声明的数据,暴露给父组件直接用
provide/inject
和vue2一致
vuex & pina
两者用法,除了pina没有Mutations,差别不大。但是官方推荐的东西,自然有它的各种优点
- Vuex: State、Gettes、Mutations(同步)、Actions(异步)
- Pinia: State、Gettes、Actions(同步异步都支持)
- Vuex4 用于 Vue3 ,Vuex3 用于 Vue2
- Pinia2.x 即支持 Vue2 也支持 Vue3
五、TS支持
- 可以让写react的兄弟,快速上手写vue3
- react中 {{}} => {}
- 兼容的指令:v-model,v-if,v-show
import { ref } from 'vue'
let v = ref<string>('')
const renderDom = () => {
return (
<> <input v-model={v.value} type="text" /> <div> {v.value} </div> </>
)
}
export default renderDom
六、插件
6.1 开源插件
unplugin-auto-import/vite
无需导入xxx,import { reactive,ref } from "vue";
,只需要用即可
unplugin-vue-define-options
自定义组件名称,需要引入插件unplugin-vue-define-options
,并在vite中配置
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import DefineOptions from 'unplugin-vue-define-options/vite';
export default defineConfig({
plugins: [vue(), DefineOptions()],
});
不使用插件,也可以通过多写一个script标签来单独写options
<script lang="ts">
export default {
name: "TButton",
};
</script>
<script lang="ts" setup> defineOptions({ name: 'TButton', }); </script>
6.2 vscode插件
volar vscode
- vetur只支持vue2,volar只支持vue3,两者冲突。
- 建议禁用vetur,格式化代码使用prettier,本地使用volar做代码高亮。
- 或者通过项目配置,指定相关插件配置
七、指令
7.1 v-model
- 底层语法糖时间改变,之前vue2是update:input,vue3 是update:modelValue
- 支持多个v-model
- 支持自定义修饰符
- 弃用.sync等
7.2 自定义指令
生命周期(和vue3一致)
- created 元素初始化的时候
- beforeMount 指令绑定到元素后调用 只调用一次
- mounted 元素插入父级dom调用
- beforeUpdate 元素被更新之前调用
- update 这个周期方法被移除 改用updated
- beforeUnmount 在元素被移除前调用
- unmounted 指令被移除后调用 只调用一次
自定义拖拽指令v-move
- 比如这个v-move 封装自定义拖拽指令
import { Directive } from "vue";
const vMove: Directive = {
mounted(el: HTMLElement) {
let moveEl = el.firstElementChild as HTMLElement;
const mouseDown = (e: MouseEvent) => {
//鼠标点击物体那一刻相对于物体左侧边框的距离=点击时的位置相对于浏览器最左边的距离-物体左边框相对于浏览器最左边的距离
console.log(e.clientX, e.clientY, "-----起始", el.offsetLeft);
let X = e.clientX - el.offsetLeft;
let Y = e.clientY - el.offsetTop;
const move = (e: MouseEvent) => {
el.style.left = e.clientX - X + "px";
el.style.top = e.clientY - Y + "px";
console.log(e.clientX, e.clientY, "---改变");
};
document.addEventListener("mousemove", move);
document.addEventListener("mouseup", () => {
document.removeEventListener("mousemove", move);
});
};
moveEl.addEventListener("mousedown", mouseDown);
},
};
八、Hook
用了react hook的人都知道很香,vue3支持这个相当不错,能解决很多业务场景的封装
8.1 自定义Hook
可以当做mixins写
// useWindowResize
import { onMounted, onUnmounted, ref } from "vue";
function useWindowResize() {
const width = ref(0);
const height = ref(0);
function onResize() {
width.value = window.innerWidth;
height.value = window.innerHeight;
}
onMounted(() => {
window.addEventListener("resize", onResize);
onResize();
});
onUnmounted(() => {
window.removeEventListener("resize", onResize);
});
return {
width,
height
};
}
export default useWindowResize;
8.2 hook库
- vueuse 官方,感谢评论区@rogepi 小哥的提醒
- ahooks-vue
- v3hooks
8.3 react vs vue3
- Vue3 究竟好在哪里?(和 React Hook 的详细对比)ssh-晨曦时梦见兮
九、结语
- Vue3 的依赖追踪是全自动的,不需要担心传了错误的依赖数组给 useEffect/useMemo/useCallback 从而导致回调中- 使用了过期的值
- Vue3 Hook也没React Hook那么多限制,后续用用看怎么样
- 个人比较喜欢SFC语法,html、js、css分离开
笔者vue3也刚用不久,如有错误,还请兄弟们指出
本文所有demo都在该仓库中JJ-UI 一款Vue3组件库,参考大佬文章刚刚搭建好,后续会基于这个架子开发自己的vue3组件库
十、参考文章
- 小满zs vue3讲解系列 B站有视频
- Vue3.0中Ref与Reactive的区别是什么
- vue3.0的优化
- (建议收藏)Vue3 对比 Vue2.x 差异性、注意点、整体梳理,与React hook比又如何?(面试热点)
- 为什么 Vue3 的 ref 让很多大佬操碎了心?
十一、往期推荐
- 聊一聊web图片小知识 50+ 👍🏿
- 【逃离一线】被裁后的面经与感慨 350+ 👍🏿
- 一篇搞定【web打印】知识点 90+ 👍🏿
- 一篇够用的TypeScript总结 800+ 👍🏿
- 一名 vueCoder 总结的 React 基础 240+ 👍🏿
- Vue 转 React不完全指北 900+ 👍🏿
- 跳槽人速来,面经&资源分享 1300+ 👍🏿
- 一年半前端人的求职路 350+ 👍🏿
- vue2.x高阶问题,你能答多少 500+ 👍🏿
- 聊一聊前端性能优化 1700+ 👍🏿
- Egg + Puppeteer 实现Html转PDF(已开源) 70+ 👍🏿
十二、求赞、求三连
我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/9338.html