本文的面试题整理自 B站 前端杨村长的 Vue面试专题,题解结合了个人的理解和思考,供大家参考。
个人觉得村长的题目选的都挺好的,而且题解也很全面,就是题解偏书面,大家实际面试的时候可以尽量口语化,按照答题思路组织语言,把答案附上自己的风格说出来,有些话复述不出来还可以通过举例的方式表达等等。
题目的顺序是按照观众点赞评论收藏等数据计算出的评分按大到小排名的,排在前面的题目相对更重要观众更感兴趣
花了挺大劲整理的,面试前可以看看 就酱~ (我恨八股文)
答题思路:
Vue
生命周期是什么?Vue
生命周期有哪些阶段?Vue
生命周期的流程?Vue3
变化 Vue
生命周期的变化回答范例:
生命周期这个词应该是很好理解的,在我们生活中就会常常碰到,比如谈到一个人的生命周期,我们会说人这一生会经历婴儿、儿童、少年、青年、中年、老年这几个阶段。
而 Vue
的生命周期也是如此,在 Vue
中的每个组件都会经历从创建到挂载到更新再到销毁这几个阶段,而在这些阶段中,Vue
会运行一种叫做生命周期钩子的函数,方便我们在特定的阶段有机会添加上我们自己的代码。
Vue 生命周期总共可以分为 8
各阶段:创建前后、挂载前后、更新前后、销毁前后,以及一些特殊场景的生命周期(keep-alive
激活时、捕获后代组件错误时)。Vue3
中还新增了三个用于调试和服务端渲染场景。
这几个阶段对应的钩子函数 API依次为:beforeCreate
create
beforeMount
mounted
beforeUpdate
updated
activated(keep-alive 激活时调用)
deactivated(keep-alive 停用时调用)
beforeDestory
destoryed
errorCaptured(捕获子孙组件错误时调用)
。
在 Vue3 中的变化 绝大多数只要加上前缀 on 即可,比如 mounted
变为 onMounted
,除了 beforeDestroy
和 destroyed
被重新命名为 beforeUnmount
和 unMounted
(这样与前面的 beforeMount
和 mounted
对应,强迫症表示很赞🤣)
beforeCreate
在组件创建前调用,通常用于插件开发中执行一些初始化任务;created
组件创建完毕调用,可以访问各种数据,请求接口数据等;mounted
组件挂载时调用 可以访问数据、dom
元素、子组件等;beforeUpdate
更新前调用 此时 view
层还未更新,可用于获取更新前的各种状态;updated
完成更新时调用 此时view层已经完成更新,所有状态已经是最新的了;beforeUnmount
实例被销毁前调用,可用于一些定时器或订阅的取消;unMounted
销毁一个实例时调用 可以清理与其他实例的链接,解绑它的全部指令以及事件监听器。
在 Vue3 中: setup
是比 created
先执行的; 而且没有 beforeCreate
和 created
。
权限管理一般需求就是对页面权限和按钮权限的管理
具体实现的时候分前端实现和后端实现两种方案:
前端方案会把所有路由信息在前端配置,通过路由守卫要求用户登录,用户登录后根据角色过滤出路由表,然后在动态添加路由。比如我会配置一个 asyncRoutes
数组,需要认证的页面在路由的 meta
中添加一个 roles
字段,等获取用户角色之后取两者的交集,若结果不为空则说明可以访问。过滤结束后剩下的路由就是用户能访问的页面,最后通过 router.addRoutes(accessRoutes)
方式动态添加路由即可。
后端方案会把所有页面路由信息存在数据库中,用户登录的时候根据其角色查询得到其能访问的所有路由信息返回给前端,前端再通过 addRoute
动态添加路由信息。
按钮权限的控制通常会实现一个指令,例如 v-permission
,将按钮要求角色通过值传给 v-permission
指令,在指令的 mounted
钩子中可以判断当前用户角色和按钮是否存在交集,有就保留按钮,没有就移除按钮。
纯前端方案的优点是实现简单,不需要额外权限管理页面,但是维护起来问题比较大,有新的页面和角色需求就要修改前端代码和重新打包部署;服务端方案就不存在这个问题,通过专门的角色和权限管理页面,配置页面和按钮权限信息到数据库,应用每次登陆时获取的都是最新的路由信息。
自己的话:
权限管理一般分页面权限和按钮权限,而具体实现方案又分前端实现和后端实现,前端实现就是会在前端维护一份动态的路由数组,通过用户登录后的角色来筛选它所拥有权限的页面,最后通过 addRoute
将动态添加到 router
中;而后端实现的不同点就是这些路由是后端返回给前端,前端再动态添加进去的。
按钮权限一般会实现一个 v-permission
,通过判断用户有没有权限来控制按钮是否显示。
纯前端方案的优点是实现简单,但是维护问题大,有新的页面和角色需求都需要改代码重新打包部署,服务端则不存在这个问题。
回答思路:
回答:
v-model
,它可以绑定一个响应式数据到视图,同时视图中变化也能改变该值。v-model
是一个语法糖,它的原理(默认请情况下)就是通过 :value
将变量挂到 dom
上,再通过 input
事件 监听 dom
的变化改变变量的值。使用 v-model
的好处就是方便呀,减少了大量的繁琐的事件处理,提高开发效率。v-model
,还可以在自定义组件上使用,表示某个值得输入和输出控制。modelValue
的 属性 和 update:modelValue
的 事件; 在 Vue3 中还可以用参数形式指定多个不同的绑定,如 v-model:foo
这个时候就相当于 给了子组件一个 foo
的 属性 和 update:foo
的事件。v-model
作为一个指令,它的原理就是 Vue 编译器会把它转换成 value属性绑定和input的监听事件,上面说过是默认情况下,实际上编译器会根据表单元素的不同分配不同的事件,比如 checkbox
和 radio
类型的input
会转换为 checked
和 change
事件。Vue 组件之间通信有以下这么几种:
props
$emit
、$on
、$off
、$once
(后三者在Vue3中已被废除)$children
(Vue3中废除)、$parent
$attrs
、$listeners
(Vue3中废除)ref
$root
eventbus
(Vue3中不好使了,需要自己封装)vuex
、pinia
provide + inject
以上的方法长按使用场景可以分为:
父子组件之间可以使用
props
/$emit
/ $parent
/ ref
/$attrs
兄弟组件之间可以使用
$parent
/ $root
/ eventbus
/ vuex
跨层及组件之间可以使用
eventbus
/ vuex pinia
/ provide + inject
App
尺寸,访问时才异步加载jsconst router = createRouter({
routes: [
{ path : '/foo', component: () => import('./foo.vue)}
]
})
keep-alive
缓存页面:避免重复创建组件实例,且能保存缓存组件状态html<keep-alive>
<router-view v-if="$route.meta.keepAlive == true"></router-view>
</keep-alive>
<router-view v-if="$route.meta.keepAlive != true"></router-view>
v-show
复用 DOM
:避免重复创建组件v-for
遍历避免同时使用 v-if
(实际上这在 Vue3 中是错误的写法)v-once
和 v-memo
: 不再变化的数据使用 v-once
;按条件跳过更新时使用 v-memo
vue-virtual-scroller
/ vue-virtual-scroll-grid
)v-lazy
指令 (参考项目:vue-lazyload
)element-plus
避免体积太大SSR
服务端渲染 解决首屏渲染慢的问题思路:
回答:
localStorage
存储 Vuex
的状态,store
中把值取出来作为 state
的初始值,提交 mutation
的时候就存入 localStorage
。vuex-persist
、vuex-persistedstate
这种插件,可以通过插件选项控制哪些需要持久化。内部的原理就是通过订阅 mutation
变化做统一处理。localStorage
怎么办?那我 Vuex
里的状态不是也改变了?二是由于 localStorage API
的原因只能存储字符串,所以我们只能将数据通过 JSON.stringify
转换为字符串,而当我们存储的数据为 Map
、Set
、Function
这种引用类型的数据时,JSON.stringify
转换后会变味 {}
而丢失。对应第一个问题我的解决方法是可以通过 监听 storage
事件来清除数据
window.addEventListener("storage", function () { localStorage.clear(); window.location.href = '/login' console.error("不要修改localStorage的值~~~"); });
对于第二个问题没办法了,只能选择不适用 Map
和 Set
这种引用类型。
思路:
回答:
JS
中做属性拦截常见的方式有三种:defineProperty
、getter/setters
和 Proxy
Vue2
中使用 defineProperty
的原因是, 2013 年只能使用这种方式,由于该 API
存在一些局限性,比如对于数组的拦截有问题,为此 Vue
需要专门为数组响应式做一套实现。另外不能拦截那些新增、删除属性;最后 defineProperty
方案在初始化时需要深度递归遍历处理对象才能对它进行完全拦截,明显增加了初始化的时间。Proxy
出现后迎刃而解,不仅可以对数组实现拦截,还能对 Map
、Set
实现拦截;另外 Proxy
的拦截也是懒处理行为,如果用户没有访问嵌套对象,那么也不会实施拦截,这就让初始化的速度和内存占用改善了。Proxy
有兼容性问题,完全不支持IE思路:
回答:
component
选项配置一个返回 Promise组件的函数就可以定义懒加载路由.例如:js{
path: '/login',
component: () => import('../views/login/Login.vue')
},
js{
path: '/login',
component: () => import(/* webpackChunkName: "login" */'../views/login/Login.vue')
},
vite中结合rollupOptions定义分块 5. 路由中不能使用异步组件
#
,这种方式使用和部署都较简单;history模式url看起来更优雅没关,但是应用在部署时需要做特殊配置,web服务器需要做回退处理,否则会出现刷新页面404的问题。hash
模式是监听hashchange
事件触发路由跳转,history
模式是监听popstate
事件触发路由跳转。在 Vue
中 nextTick
是等待下一次 DOM
更新刷新的工具方法。
Vue
有一个异步更新策略,意思是如果数据变化,Vue
不会立刻更新 DOM
,而是开启一个队列,把组件更新函数保存在队列中,在同一时间循环中发生的所有数据变更会异步的批量更新。这一策略导致我们对数据的修改不会立刻体现在 DOM
上,此时如果想要获取更新后的 DOM
状态,就需要使用 nextTick
nextTick
接受一个函数,我们可以在这个函数内部访问最新的 DOM
状态
在开发时,有两个场景我们会用到 nextTick
:
created
中想要获取 DOM
;DOM
更新后的状态;nextTick
的原理:在 Vue
内部,nextTick
之所以能够让我们看到 DOM
更新后的结果,是因为我们传入的 callback
会被添加到队列刷新函数的后面,这样等队列内部的更新函数都执行完毕,所有 DOM
操作也就结束了,callback
自然能够获取最新的 DOM
值。
先回答答案:
在 vue2
中, v-for
的优先级更高
但是在 vue3
中, v-if
的优先级更高
拓展:
无论什么时候,我们都不应该把 v-for
和 v-if
放在一起,
怎么解决呢?一是可以定义一个计算属性,让 v-for
遍历计算属性。二是可以把 if
移到内部容器里(ul
ol
)或者把v-for
移植外部容器(template
)中
watch
方式,可以以字符串形式监听 $store.state.xx
; subscribe
方法参数是一个回调函数,回调函数接受mutation
对象和 state
对象,可以通过 mutation.type
判断监听的目标。
wtach 方法更简单好用, subscribe
会略繁琐,一般用 vuex
插件中(可以提一下vuex的持久化插件vuex-persist
、vuex-persistedstate
)
ts
(或者说很不友好)vue3 + pinia 会是更好的组合。
ref
返回的是一个响应式Ref
对象, reactive
返回的是响应式代理对象。ref
通常是处理单值得响应式,reactive
用于处理对象类型的数据响应式ref
需要通过 .value
访问, 在视图中会自动脱 ref
,不需要 .value
,ref
可以接收对象或数组但内部依然是 reactive
实现的;reactive
如果接收 Ref
对象会自动脱 ref
;使用展开运算符展开 reactive
返回的响应式对象会使其失去响应性,可以结合 toRefs()
将值转换为 Ref
对象后再展开。reactive
内部使用 Prxoy
代理拦截对象各种操作,而 ref
内部封装一个 RefImpl
类,设置 get value/set value
,拦截用户对值得访问。mixins
、extends
、composition api
:mixins
很灵活,但是会冲突很混乱。extends
是一个不太常用的选项,更 mixins
的不同是它只能扩展单个对象,优先级比 mixins
高。
混入的数据和方法 不能明确判断来源 而且可能和当前组件内变量 产生命名冲突,composition api 可以很好解决这些问题,利用独立出来的响应式模块可以很方便的编写独立逻辑并提供响应式数据局,增强代码的可读性和维护性。
扩展:Vue.mixin(全局混入) Vue.extend(有点像是 类/组件的继承 创建一个子类)
vue-loader
是用于处理单文件组件(SFC)的webpack loadervue-loader
,我们才能用 .vue
文件形式编写代码,将代码分割为 template
script
style
webpack
在打包的时候,会以 loader
的方式调用 vue-loader
vue-loader
被执行时,它会对 SFC
中的每个语言块用单独的 loader
链处理,最后将这些单独的块装配成最终的组件模块不能直接改。
组件化开发中有一个单向数据流原则,不在子组件修改父组件数据是个常识
如果你确实需要改,请通过emit向父组件发送一个事件,在父组件中修改
我么可以在路径中使用一个动态字段来实现,例如/users/:id
其中 :id
就是路径参数。
可以通过 this.$route.parmas
获取,参数还可以有多个, $route
对象还公开了其他有用的信息如 query
hash
等。
思路:
回答:
vue
中要解决的一个核心问题就是连接数据层和视图层,通过数据变化驱动视图更新,要做到这点就需要对数据做响应式处理。DOM
和 patch
算法,我们只需要操作数据,关心业务,完全不需要接触繁琐的 DOM
操作,打打提升了开发效率,降低开发难度。vue2
中实现数据响应式的核心就是通过 Object.defineProperty()
方法对数据进行拦截,当 get
数据时做依赖收集 set
数据时做更新通知。这种机制很好的及绝了数据响应式的问题,但是实际使用也存在缺点,比如在初始化时的递归遍历会造成性能损失;无法监听新增或删除属性,在 vue
中要通过像 Vue.set/delete
这种特定的 API
才能实现对对象数组属性的添加和删除,而且也不支持 Ma
、Set
这些数据结构,Vue3
重写了这部分实现,利用的是 ES6
中的 Proxy
代理要响应化的数据。它有很多好处,初始化性能和内存都大幅改善,也不需要特殊的 API
,但是不支持 IE
浏览器。问 template
到 render
的过程其实是问的 vue 编译器
工作原理。
思路:
回答:
Vue
中有个独特的编译模块,称为 compiler
,它的主要作用是将 template
编译为 js
可执行的 render
函数HTML
来编写视图,直观且高效。手写 render
函数不仅效率低下,而且失去了被编译器的优化能力。Vue
编译器 首先会对 template进行解析
( Parse
),结束后会得到一个抽象语法树AST
,然后对 AST
进行深加工转换(transform
),最后将得到的 AST
生成为 js
代码,也就是 render
函数缓存组件可以使用 keep-alive
组件,include 和 exclude 可以指定包含不包含哪些组件。
Vue3
结合 vue-router
使用变化非常大,之前是 keep-alive
包含 router-view
,现在是 router-view
包含 keep-alive
缓存后如果想要获取数据可以使用 actived
钩子 或者 beforeRouteEnter
( vue-router
的一个守卫)
keep-alive
是一个通用组件,它内部定义了一个 map
,缓存创建过的组件实例,它返回的渲染函数内部会查找内嵌的 component
组件对应组件的 vnode
,如果改组件在map中存在就直接返回它。由于 component
的 is
属性是一个响应式数据,因此只要它变化,keep-alive
的 render
函数就会重新执行。
DOM
是什么?
虚拟 DOM
的本质就是一个 Javascript
对象。DOM
?(好处)
它能有效减少操作 DOM
的次数,方便实现跨平台compiler
编译器会把 template
模版编译成渲染函数,接下来在 mount
挂载的过程会调用这个渲染函数,返回的对象就是 虚拟DOM
。挂载结束后,会进入更新流程。如果某些响应式数据发生变化,将会引起组件重新 render
,此时会生成新的 虚拟DOM
,和上次渲染结果做 diff
操作,最小量的操作 dom
,从而高效更新视图。defineAsyncComponet
指定一个 loader
函数,结合 ES 模块 动态导入函数 import
可以快速实现。Vue3
还可以结合 Suspense
组件使用异步组件。Vue
框架,处理路由组件加载的是 vue-router
。但是可以在懒加载的路由组件中使用异步组件。v-once
方式只渲染一次懒加载
方式,在用户需要的时候在加载数据。computed
是计算属性,watch
是侦听器。computed
通常用于处理模版中复杂的逻辑,而 watch
通常用于需要监听一个响应式对象的变化而做一些操作的时候watch
可以进行异步操作,computed
不行。set
和 get
两个选项,是它称为即可读又可写的计算属性,如果传递的是函数的话默认就是 get
选项,watch
可以传递一个对象,设置deep、immediate等选项vue3
中 watch
发生了一些变化,例如不能再侦测一个点操符之外的字符串表达式,reactivity API
中新出的 watch
、watchEffect
可以完全替代 watch
选项,而且功能更加强大SPA
(Single Page Application)是单页面应用。一般也称为客户端渲染,简称 CSR
。SSR(Server Side Render) 即服务端渲染。一般也称为多页面应用(Mulpile Page Application),简称 MPA。
SPA
只会首次请求 html
文件,后续只需要请求 JSON
数据即可,因此用户体验更好,节约流量,服务端压力也较小。但是首屏加载的时间会变长,而且 SEO
不友好。为了解决以上缺点,就有了 SSR
方案,由于 HTML
内容在服务器一次性生成出来,首屏加载快,搜索引擎也可以很方便的抓取页面信息。但同时 SSR
方案也会有性能,开发受限等问题。
选择上,如果有首屏加载优化需求,SEO需求时,就可以考虑SSR。
但并不是只有这一种替代方案,比如对一些不常变化的静态网站,SSR反而浪费资源,我们可以考虑预渲染的方案。另外 nuxt.js/next.js
中给我们提供了SSG静态网站生成方案也是很好的静态站点解决方案,结合一些CI手段,可以起到很好的优化效果。
回答思路:
回答:
Vue
中的 diff
算法称为 patching
算法,虚拟DOM要想转化为真实DOM就需要通过 patch
方法转换。
最初 Vue1.x
视图中农每个依赖均有更新函数对应,可以做到精确更新,因此不需要 虚拟DOM
和 patching
算法支持,但是这样粒度过细导致 Vue1.x
无法承载较大应用;Vue2.x
中为了降低 Watcher
粒度,每个组件只有一个 Watcher
与之对应,此时就需要引入 patching
算法才能精确找到发生变化的地方并高效更新。
vue
中 diff
执行的时刻是组件内响应式数据变更触发实例执行其更新函数时,更新函数会再次执行 render函数
获得最新的 虚拟DOM
,然后执行 patch函数
,对比新旧虚拟DOM,将其转化为对应的 DOM
操作。
patch
过程是一个递归过程,遵循深度优先、同层比较的策略;以 vue3
的patch
为例:
vue3
中引入的更新策略:编译期优化 patchFlags
、block
等
diff+ |- /src
+ |- /assets 存放资源
+ |- /img
+ |- /css
+ |- /font
+ |- /data
+ |- base-ui 存放多个项目中都会用到的公共组件
+ |- components 存放这个项目用到的公共组件
+ |- hooks 存放自定义hook
+ |- views 视图
+ |- store 状态管理
+ |- router 路由
+ |- service 网络请求
+ |- utils 工具
+ |- global 全局注册、全局常量..
一个 SPA
应用的路由需要解决的问题时页面跳转内容改变同时不刷新,同时路由还需要已插件形式存在,所以:
createRouter
函数,返回路由器实例,实例内部做几件事;
hash
或者 popstate
事件path
匹配对应路由router
定义成一个 Vue
插件,即实现 install
方法,内部做两件事:
router-link
和 router-view
,分别实现页面跳转和内容显示$router
和 $route
,组件内可以访问当前路由和路由器实例modules
选项组织起来:createStore({modules: {...}})
getters
、mutations
和 actions
又在全局空间中,使用方式和之前一样。如果要做到完全拆分,需要在子模块加上 namespace
选项,此时再访问它们就要加上命名空间前缀。vue2
中组件确实只能有一个跟,但 vue3
中组件已经可以多根组件了。vdom
是一颗单根树形结构,patch
方法在遍历的时候从根节点开始遍历,它要求只有一个根节点。组件也会转换为一个 vdom
,自然应该满足这个要求。vue3
中之所以可以写多个根节点,是因为引入了 Fragment
的概念,这是一个抽象的节点,如果发现组件时多根的,就创建一个 Fragment
节点,把多个根节点作为它的 children
。将来 pathch
的时候,如果发现是一个 Fragment
节点,则直接遍历 children
创建或更新。v-once
是 vue
的内置指令,作用是仅渲染指定组件或元素一次,并跳过未来对其更新。v-once
,这样哪怕这些数据变化,vue
也会跳过更新,是一种代码优化手段。v-once
即可。补充:
vue3.2
之后,又增加了 v-memo
,这个指令可以有条件的缓存模板并控制他们的更新。v-once
的原理:编译器发现有 v-once
时,会将首次计算结果存入缓存对象,组件再次渲染时就会从缓存获取,从而避免再次计算。url
各部分通常对应某个嵌套的组件,vue-router
中可以使用嵌套路由表示这种关系。view-router
,从而形成物理上的嵌套,和逻辑上的嵌套对应起来。定义嵌套路由时使用 children
属性组织嵌套关系router-view
组件内部判断其所处嵌套的深度,将这个深度作为匹配组件数组 matched
的索引,获取对应渲染组件并渲染之。如果你说不出来,可以直接举例子。当我开发一个页面时,如果需要显示一个顶部导航栏,通过导航栏跳转到不同的页面,而顶部的导航栏又必须要在每个页面显示时,就可以使用嵌套路由;还可以举例,当我需要查看某个列表的详情页面时,往往需要嵌套路由 (detail/:id
)
watch
方式,可以以字符串形式监听 $store.state.xx
; subscribe
方法参数是一个回调函数,回调函数接受mutation
对象和 state
对象,可以通过 mutation.type
判断监听的目标。
wtach 方法更简单好用, subscribe
会略繁琐,一般
patch
将前面获得vnode
转换为 dom
;同时会创建它内部响应式数据和组件更新函数之间的依赖关系,这使得以后数据变化时会执行对应的更新函数。key
的作用主要是为了更高效的更新虚拟 DOM
。key
是 vue
在 patch
过程中判断两个节点是否是相同节点的关键条件(另一个是元素类型),如果不设置 key
,它的值就是 undefined
,vue
则可能永远认为这是两个相同节点,只能去做更新操作,这造成了大量的 dom
更新操作,明显是不可取的。key
,而且应该尽量避免使用数组索引,这可能导致一些隐藏 bug
。watchEffect
立即运行函数,被动地追踪它的依赖,传入的函数即是依赖收集的数据源,也是回调函数;watch
侦测一个或多个响应式数据源,在数据源变化时调用一个回调函数,通过 immediate
选项也可以设置立即执行一次。watchEffect
是一种特殊的 watch
。如果不关心响应式数据前后的值,可以使用 watchEffect
。其他情况都可以用 watch
。parent created -> child created -> child mounted -> parent mounted
原因:Vue
创建是一个递归的过程,先创建父组件,有子组件就会创建子组件,因此创建时先有父组件再有子组件;子组件首次创建时会添加 Mounted
钩子到队列,等到 patch
结束再执行它们,可见子组件的 mounted
钩子是选进入到队列中的,因此等到 patch
结束执行这些钩子时也先执行。
Tree
、Menu
这类组件,它们的节点往往包含子节点,子节点结构和父节点往往是相同的。这类组件的数据往往也是树形结构,这种都是使用递归组件的典型场景。使用自定义指令分为定义、注册、和使用
定义有两种方式,对象和函数形式,前者类似组件定义,有各种生命周期;后者只会在 mounted
和 updated
时执行
注册:可以使用 app.directive
全局注册 也可以通过选项局部注册
使用时在注册名称前加上 v-即可。
v-copy
复制粘贴
v-lazy
图片懒加载
v-debounce
防抖
v-permission
按钮权限
v-longpress
长按
API 层面
Composition API
setup
语法糖Teleport
传送门Fragments
可以多个根节点Emits
createRenderer
自定义渲染器SFC
状态驱动 css
变量 (v-bind in <style>
)此外,Vue3在框架层面也有很多两点和改进
DOM
重写patchFlags
、block
等Proxy
的响应式系统最大设计目标就是替代 Vue2
,为了实现这一点,Vue3
在以下几个方面做了很大改进,如:易用性,框架性能、扩展性、可维护性、开发体验等
API
简化 v-model
变成了 v-model
和 sync
修饰符的结合体。类似的还有 h(type,props,children)
函数中的 props
不用考虑区分属性、特性、事件等,框架替我们判断,易用性增。Teleport
Fragment
Suspense
等都会简化特定场景的代码编写。 setup
语法糖更是极大提升了我们的开发体验。reactivity
模块,custom render API
等Composition API
,更容易编写高复用性的业务逻辑。还有对TS支持的提升。Proxy
的响应式系统。Proxy
实现,初始化事件和内存占用均大幅改进;tree-shaking
,因此体积更小,加载更快.(因为vue3 所有的API都通过ES6模块化的方式引入,这样就能让webpack或rollup等打包工具在打包时对没有用到API进行剔除,最小化bundle体积)$attrs
和 $listeners
是做什么的?$attrs
获取没有在 props
中定义的属性,v-bind="$attrs"
可以用于属性透传
$listeners
用于获取事件,vue3
中已经移除合并到 attrs
中,使用起来更方便
Composition API
是一组API,包括 Reactivity API
、生命钩子、依赖注入,使用户可以通过导入函数方式编写组件,而 Options API
则通过声明组件选项的对象形式编写组件。
Composition API
更简洁、逻辑复用更高效。解决的过去 Options API
中 mixins
的各种缺点(会冲突很混乱);另外 Composition API
更自由,没有 Options API
那样固定的写法,并且可以更有效的将逻辑代码组织在一起,而不用东一块西一块搞得很混乱,最后 Composition API
拥有更好的类型推断,对 ts
支持友好。
编码风格方面:
性能方面:
SSR
减少首屏加载事件v-once
v-memo
shallowRef
或 shallowReactive
降低开销mutation
用于修改 state
action
用于提交一个 mutation
,而且 action
可以包含异步操作
Store
存储全局状态commit({type, payload})
, dispatch(type,payload)
实现 Store
,可以定义 Store
类,构造函数接受选项 options
,设置属性 state
对外暴露状态,提供 commit
和 dispatch
修改属性。这里需要设置 state
为响应式对象,同时将 Store
定义为一个 Vue
插件(install方法)。
commit
可以获取用户传入 mutations
并执行它,这样可以按用户提供的方法修改状态,dispatch
类似,但是 dispatch
需要返回一个 Promise
给用户用于处理异步结果。
本文作者:叶继伟
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!