Vue常见题
- Vue请求一般放在哪个生命周期内
- 可以在钩子函数 created、beforeMount、mounted 中进行调用,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。
- 但是推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:
- 能更快获取到服务端数据,减少页面loading 时间;
- ssr不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;
- v-model原理
- v-model是value+input事件的语法糖,可以通过model配置对象prop+event来进行自定义
- Vue事件绑定原理
- 原生事件通过addEventListener进行绑定,组件事件通过Vue自定义的$on进行绑定
- Vue中组件生命周期调用顺序说一下
- 组件的调用顺序都是先父后子,渲染完成的顺序是先子后父
- 组件的销毁操作都是先父后子,销毁完成的顺序是先子后父
- 加载渲染过程:父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
- 销毁过程:父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
- 子组件更新过程:父beforeUpdate->子beforeUpdate->子updated->父updated
- 父组件更新过程:父beforeUpdate->父updated
- Vue2.x组件通信方式
- 父子通信
- props、$parent、$children、$ref、$on、$emit
- 兄弟组件通信
- vuex、event bus、
- 跨级组件通信
- inject、provide、vuex、$attrs、$listeners
- 父子通信
- 在Vue方面做过哪些性能优化:优化是一个大专栏,很多内容
- 减少data中的属性,data中的属性会增加getter、setter,一般配置数据从文件引入或在当前文件定义变量
- for循环列表中如果有事件处理,则采用事件委托进行代理处理
- SPA页面采用Keep-alive缓存组件
- key值设置为唯一值
- 细分Vue组件,提高Vue组件渲染速度
- 图片资源按需加载:v-lazy以及监听鼠标事件,滚动到指定位置再进行加载
- 页面加载loading
- 选择性使用v-if、v-show
- 代码按需加载
- 全局引入SASS变量,node-sass,sass-loader,vue-cli3通过css.loaderOptions.sass进行配置
- v-if和v-show的区别
- v-if会在切换过程中对组件或元素进行销毁和重建,如果初始为false则不会进行任何操作,v-show不管条件如果都会创建,然后通过display:none进行显隐
- $on的原理
- 将监听的事件绑定在vm._events对象上,将事件函数收集到数组中,_events是在Vue init初始化时创建的
- $emit将事件对象上绑定的函数依次循环调用
- $forceUpdate
- 手动执行vm._watcher.update()方法
- Vue销毁组件流程
- 清除当前组件与父组件的联系,通过$children删除当前组件实例
- 实例的teardown清除实例绑定的相关状态
- Vue状态发生变更时,watcher会收到通知,然后触发虚拟DOM更新
Vue的选项初始化顺序
过滤器保存在this.$options.filters中,通过resolveFilter函数将过滤器名称传入进去,查找到对应的过滤器函数再进行执行,将变量作为参数传递给函数,最后编译为文本交给页面渲染
虚拟DOM
- 虚拟DOM是根据状态生成一棵虚拟状态树,根据虚拟树进行渲染,当状态发生了变更,进行虚拟节点对比,只更新变更的节点
- jQuery命令式操作DOM,框架帮助我们声明式操作DOM,我们只需要维护状态,DOM映射由框架帮助我们完成
- Vue中模块转换成视图的过程:
- 模块编译为render函数
- 根据模板标签解析成为AST语法树
- 根据AST语法树生成代码
- (_c(‘div’,{id:”app”,a:”1”,style:{“color”:”red”}},_v(“hello”+_s(arr)+”world”)))
- 代码通过new Function + with解析为函数返回render函数
- 根据模板标签解析成为AST语法树
- 执行render函数得到虚拟DOM
- 虚拟DOM生成DOM
- 虚拟DOM与AST语法树的区别为可以增加自定义属性
- 模块编译为render函数
- 状态发生变更,通过patch对比newVnode、oldVnode,得出需要变更的dom
- patch是虚拟DOM对比最核心的算法,提供两个虚拟DOM的差异,dom对比其实就是增删改
- 之所以不直接根据状态直接生成DOM,需要借助虚拟DOM,因为可以缓存数据,通过对比差异可以减少操作DOM
虚拟DOM的好处
- template变成render函数
- render函数返回一个虚拟DOM
- 让Vue的性能更快,其实是JS对象,将真实DOM的映射
- 让虚拟DOM具备跨平台的能力,比如weex
- template -> render -> 虚拟DOM(JS对象)-> 真实DOM -> 渲染页面
Diff算法
- 暴击比对:生成两个新老DOM,新节点直接替换老节点
- 对比直接节点、属性、子节点
- 由易到难:
- 一个有子元素,一个无子元素
- 两个元素都有子元素(双指针对比)
- 为什么需要key
- diff算法可以通过key进行方便比较,可以不需要创建元素,达到复用元素的作用
-
视图更新
通过观察者模式进行数据观察,当数据发生变化,观察都做刷新视图的变化
实现原理
Vue中模板编译原理
第一步:parserHTML-先将模板解析成为AST语法树,并打上标签tag
{
attrs: (3) [{…}, {…}, {…}]
children: [{…}]
parent: null
tag: "div"
type: 1
}
第二步:genCode-将AST语法树生成字符串代码
_c('div',{id:"app",a:"1",style:{"color":"red","background":"blue"}},_v("hello"+_s(arr)+"world"))
第三步:new Function + with将字符串代码执行,返回render函数
anonymous() {
with(this){return _c('div',{id:"app",a:"1",style:{"color":"red","background":"blue"}},_v("hello"+_s(arr)+"world"))}
}
如何将render函数转换为视图
- render函数调用后生成虚拟dom
b. 通过调用patch方法,将虚拟dom使用原生createElement创建对应的元素,挂载到页面{
children: [{…}]
data: {id: "app", a: "1", style: {…}}
el: div
key: undefined
tag: "div"
text: undefined
vm: Vue {$options: {…}, _data: {…}, $el: div#app}
}
let elm = createElm(vnode); //根据虚拟节点 创建元素
parentElm.insertBefore(elm, oldVnode.nextSibling);
parentElm.removeChild(oldVnode);
- render函数调用后生成虚拟dom
$watch:在对象上配置watch选项与vm.$watch没有任何区别,对象上配置的三种写法,结果也是也是调用的原型上$watch方法
-
Vue-Router
hash模式和history模式是如何实现的
- hash模式:地址后面hash值的变化,不会导致浏览器向服务器发出请求,浏览器不发出请求,就不会刷新页面,同时监听hashchange事件可以知道hash发生了哪些变化,根据hash变化来实现更新页面内容
- history模式:history是采用H5新的API-history来实现,主要有pushState、replaceState等API,这两个API可以在改变url,但是不会发送请求,也是通过监听url变化来实现更新页面
- hash模式与history模式的区别
- hash后面带有#号字符
- hash模式兼容性更好,history模式需要服务端支持
- 有哪些导航守卫
- 全局导航守卫:beforeEach、afterEach、beforeResolve
- beforeEach做了哪些事
- 关闭模态框、验证访问权限、动态添加模块路由、验证用户token
- 组件内的守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
- 组件配置路由时守卫:beforeEnter
- 完整的导航流程:整体是先离开的组件再全局守卫再局部组件守卫再全局完成
- 全局导航守卫:beforeEach、afterEach、beforeResolve
- 为什么要use Router、use做了什么
- 为什么要放到router选项中
- 为什么要放到router-view中
响应式的数据发生变化,使用属性的render函数就会重新执行
Vuex
- muations里为什么只能包含同步操作
Vue服务端渲染
- 主要解决两个问题
- 首屏白屏时间过长
- SEO
- 服务端渲染的缺陷
- 需要占用服务的CPU和内存
- 前端框架中的很多生命周期无法使用
接口使用优化
- 接口数据缓存:使用Vuex统一缓存接口数据
- 页面切换接口清除:在axios请求时创建token存储到vuex,统一在vuex清除,在全局路由钩子清除
- 节约流量
- 取消不再处理的接口请求
- 接口列表缓存:在axios请求时创建映射对象,返回时清空接口列表
- 页面请求时可以添加Loading,页面所有请求返回时取消Loading
- 异步组件,可以在组件加载时创建loading效果,在弱网情况下很有效
接口loading:在axios创建请求时为接口创建loading
组件设计
功能上拆分层次
- 尽量让组件原子化
-
项目流程
项目分多人、多角色参与
- 项目计划与执行
开发阶段:写出开发文档、单元测试、Mock API、Code Review
Vue3
变更
slot变化
- 全局API区别
- 增加teleport
- Composition API
- 指定变化,.sync去掉,统一使用v-model
- non-props特性
- 增加emits:[‘add’, ‘test’]
- modelValue新双向绑定,也可重新命名
-
Vue3生命周期钩子
实例生成之前调用的钩子
- 实例生成之后调用的钩子
- 当有挂载元素时,在组件内容被渲染到页面之前执行的函数
- 在组件内容被渲染到页面之后执行的函数
- 当data中的数据发生改变时执行的函数
- 当data中的数据发生改变,同时页面完成更新后,执行的函数
- 当Vue实例销毁时执行的函数
- 当Vue实例和DOM完全销毁完毕时执行的函数
- 每次渲染后重新收集响应式依赖
- 每次触发页面重新渲染时自动执行
Vue事件
- 可以当时绑定多个事件
- self修饰符只自身触发
- once修饰符事件只执行一次
- passive修饰符提升滚动性能
事件修饰符
- stop, prevent, capture, self, once, passive
按键修饰符
- enter, tab, delete, esc, up, down, left, right
鼠标修饰符
- left, right, middle
精确修饰符
- exact
- @click.ctrl.exact只有按ctrl时再点击才会触发事件,不加exact,当ctrl结合其它键也会触发事件
表单修饰符
- lazy, number, trim,
Vue3相关原理
- toRefs原理:将proxy({name: ‘jack’})转换为{ name: proxy: ({value: ‘yang’}) }
- refs原理:将基础类型数据通过proxy代理,当数据发生变化时进行响应式更新proxy({value: ‘jack’})
- toRef:为初始化时未定义的属性定义一个响应式
Vue3监听
- watchEffect立即执行,相当于immediate,不需要传递监听内容
- watch惰性执行,首次不会执行,配置immediate可以实现非惰性
Vue3 minixs优先级
- 组件data, methods优先级高于 mixins data, methods
- 生命周期函数,先执行mixix里面,再执行组件的
- 自定义属性,组件属性优先于mixins
- 可自定义属性的合并策略,optionsMergeStrategies
Vue动画与过渡
- 动画是运动帧,从上到下,从左到右,弹跳等
- 过渡是从一个状态过渡到另一个状态
- Vue列表动画
- Vue组件动画
- Vue状态动画
- Vue单元素动画
Vue项目
- 组件name还有一个作用就是用于dev tools
- 项目合在一起写代码量少点,分开写未来扩展性更好
- 自己写toast
- 阻止Chrome自动填写密码:autocomplete=”new-password”
- 异步组件是谁实现的?webpack
- ?.使用
- template模板内少写JS逻辑
- 本地开发使用localhost,不要使用ip,以免影响热更新
登录JWT
- jwt:JSON Web Tokens
- 什么是jwt
- jwt解决了什么问题
- jwt原理
- 服务器认证后,生成一个JSON对象,后续通过json进行通信
- jwt数据结构
jwt使用方式
Vue3采用函数式编程,所以支持tree-shaking,不使用就不会打包
- Vue3采用TS开发,Vue2使用Flow开发
-
内部代码优化
Vue3劫持数据采用proxy Vue2劫持数据采用defineProperty,有性能问题和缺陷
- Vue3中对模板编译进行了优化,编译时生成了Block tree,可以对子节点的动态节点进行收集,可以减少比较,并且采用了patchFlag标记动态节点
- Vue2 Diff算法在对比时是两棵树进行对比,Vue3是将动态节点收集成数组进行对比
- 如果使用JSX语法则无法模板优化,还是使用传统的两棵树Diff递归对比
- Vue3采用compositionApi进行组织功能,解决反复横跨,优化复用逻辑
Vue3增加Fragment,Teleport,Suspense组件
Vue3项目结构
compiler-core:与平台无关的编译器核心
- compiler-dom:针对浏览器的编译模块
- compiler-sfc:针对单文件解析
- compiler-ssr:针对服务端渲染的编译模块
- reactivity:响应式系统
- ref-transform
- runtime-core:与平台无关的运行时核心(可以创建针对特定平台的运行时-自定义渲染器)
- runtime-dom:针对浏览器的运行时。包括DOM API,属性,事件处理等
- runtime-test:用于测试
- server-renderer:用于服务端渲染
- sfc-playground
- shared:多个包之间共享的内容
- size-check:用来测试代码体积
- template-explorer:用于调试编译器输出的开发工具
- vue-compat
- vue:完整版本,包括运行时和编译器
- 包含compiler-dom、compiler-core
- (dom基于core,相当于针对浏览器扩展了一些,dom是让template编译成render函数,如何不使用template,则可以不使用这个模块,只使用render函数开发)
- 包含runtime-core、runtime-dom、reactivity
- runtime-dom封装了浏览器操作DOM的API
- runtime-core封装了虚拟DOM的生成,如何调度的核心,不依赖于平台
- reactivity封装了响应式模块
- 包含compiler-dom、compiler-core