如何理解MVVM
数据驱动视图:
- 传统组件只是静态渲染,更新还要依赖于操作DOM
- 数据驱动视图 - Vue - MVVM
- 数据驱动视图 - React - setState
监听data变化的核心API是什么
Vue2.0:Object.defineProperty
Vue3.0:Proxy(有兼容性问题,且无法用polyfill)
Object.defineProperty 缺点:(不要把缺点故意放大)
- 深度监听需要递归到底,一次性计算量大
- 新增或删除属性不能监听到,所以需要Vue.set / Vue.delete
- 无法原生监听数组,需要特殊处理
虚拟DOM
本质是将计算放到 js 中,以提升计算性能,节约dom开销
- vdom是实现vue和react的基石
- diff算法是vdom的核心
HTML是一种XML语言,所有的XML语言都可以用一种JSON的方式表示:
diff 算法概述
最直接的体现:key
- 树的diff算法时间复杂度 o ( n ) 3 o(n)^{3} o(n)3,第一遍历tree1,第二遍历tree2,第三排序
优化时间复杂度到 o ( n ) o(n) o(n)
- 只比较同一层级,不跨级比较
- tag不同,直接删掉重建,不再深度比较
- tag 和 key 两者都相同,则认为是相同节点,不再深度比较(因为只比较同层)
diff 算法的时间复杂度
o ( n ) o(n) o(n)
diff 算法的过程
Vue 模板编译(组件渲染更新过程)
vue-template-compiler 模板编译为render函数,执行render函数返回vnode;基于vnode再执行patch和diff
使用webpack vue-loader 会在开发环境下编译模板;但直接引入vue是在浏览器运行时编译的
- with语法:
改变 {} 内自由变量的查找规则,当做obj的属性来查找;如果找不到匹配的属性择报错;(with要慎用,它打破了作用于规则,易读性差)
- vue template compiler 将模板编译为 render 函数
const template = `<p>{{flag ? message : 'no message found'}}</p>` with(this){return _c('p',[_v(_s(flag ? message : 'no message found'))])}
- 执行 render 函数生成 vnode
Vue组件是如何渲染更新的
初次渲染过程:
- 解析模板为 render 函数(或在开发环境中已完成,vue-loader)
- 触发响应式,监听data属性getter/setter
- 执行 render 函数,生成 vnode,patch(ele,vnode)
更新过程:
- 修改data,触发setter(此前在getter中已被监听)
- 重新执行 render 函数,生成newVnode
- patch(vnode,newVnode) ----> patch中的diff算法会算出最小差异然后更新到dom上
异步渲染
- $nextTick 在DOM更新完之后触发回调
- 汇总data修改,一次性更新视图
- 减少dom操作次数,提高性能
Vue Router 原理
hash的特点:
- hash变化会触发网页跳转,即浏览器前进后退
- hash变化不会刷新页面,SPA必须的特点
- hash永远不会提交到server端
H5 history(需后端配合)
- 用url规范的路由,跳转时不刷新页面
- history.pushState()
- window.onpopstate ---- 浏览器前进后退
总结:
- hash — window.onhashchange
- H5 history — history.pushState() & window.onpopstate
- history需要后端支持
两者选择:
- 2B 系统推荐用hash,简单易用,对url不敏感
- 2C 系统可以考虑H5 history,但需要服务端支持
为何v-for中要使用key
diff算法中通过 tag 和 key 来判断是否是 sameNode
作用:减少渲染次数,提升渲染性能
Vue组件的声明周期(父子组件)
组件实例的创建过程是从上而下
组件实例的挂载过程是从下而上
Vue组件如何通讯
- 父子组件:props 和 this.$emit
- 自定义事件:event.$on event.$off event.$emit
- vuex
双向数据绑定v-model的原理
- v-model 只是一个语法糖,是v-bind和input事件的组合
- 将value绑定data中的值,通过事件去更新value,从而data更新触发re-render
computed 有何特点
- 缓存,data不会重新计算,提高性能
组件data必须是一个函数
vue-template编译出来实际上是一个class,在每个地方使用组件的时候相当于实例化class,在实例化class时去执行data,如果data不是函数的话,那么每一个实例的数据都会是一样的
组件化机制
- 组件化可以让我们方便的把页面拆分成多个可重用的组件
- 组件是独立的,系统内可重用,组件之间可以嵌套
- 有了组件可以像搭积木一样开发网页
- 组件实例的创建过程是从上而下(父组件=>子组件)
- 组件实例的挂载过程是从下而上(子组件=>父组件)
ajax应该在哪个生命周期
mounted
- js是单线程的,ajax是异步的,放在mounted之前没什么用,只会让逻辑更加混乱
如何将组件所有的props传递给子组件
- $props
<User v-bind="$props" />
何时使用beforeDestory
- 解绑自定义事件 event.$off
- 清除定时器
- 解绑自定义的DOM事件(用vue定义的事件不用自己解除,vue会自己解除)
什么是作用于插槽
vuex中 action 和 mutation 有何区别
- action 用于处理异步,mutation 不可以处理异步
- mutation 做原子操作
- action 可以整合多个 mutation
vue-router异步加载
箭头函数import
监听data变化的核心API
- Object.defineProperty(注意深度监听&监听数组)
- 重新定义原型,重写push,pop等方法
- proxy可以原生支持监听数组变化
描述响应式原理
- 监听data变化
- 组件渲染和更新的流程
Vue常见性能优化
- 合理使用 v-show 和 v-if
- 合理使用 computed
- v-for 加 key
- v-for 和 v-if 避免同时使用(2.x中v-for优先级更高,每次 v-for 的时候 v-if 都会重新渲染;3.x中 v-if 优先级更高,v-if没有权利访问v-for中的变量)
- 自定义事件,dom事件及时销毁
- 合理使用异步组件
- 合理使用keep-alive
- data层级不要太深(deep深度监听时需要一次性遍历完成)
- webpack层面
- 前端通用–图片懒加载等等
- ssr