0、Vue响应式原理
1、Object.defineProperty(监听对象的属性)
当传入一个数据时,Vue会遍历对象所有的property,并使用Object.defineProperty把这些property全部转为getter/setter,它们在内部能追踪依赖,读数据时候调用getter,修改数据调用setter,组件渲染的过程会把接触过的数据property记录为依赖,依赖项的setter触发会通知watcher,从而使它关联的组件重新渲染。
2、Proxy(监听对象和数组)
核心方法是
reactive
和effect
,其中reactive
方法是负责将数据变成响应式,effect
方法是根据数据变化去更新视图或调用函数
3、区别
- vue2不能监听新增或者删除属性
- vue2无法监听数组,需要特殊处理
- vue3可以返回一个新对象,可以直接操作新的对象,而vue2只能遍历对象属性直接修改
- vue2兼容IE9
一:Vue生命周期
1、生命周期的理解
- beforeCreate:数据和方法都不可以被访问
- created:实例创建完成,可以做一些初始数据的获取
- beforeMount:虚拟DOM创建完成,即将开始渲染
- mounted:真实DOM挂载完成,数据完成双向绑定,使用$refs属性对DOM进行操作
- beforeUpdate:更改数据不会造成重渲染
- updated:DOM已经完成更新,注意避免更改数据,可能导致无限循环更新
- beforeDestroy:进行善后收尾,清除定时器之类
- destroyed:子实例被销毁
- activated:组件被激活调用
- deactivated:组件被销毁调用
2、什么阶段访问操作DOM
可以在mounted中访问操作DOM,(mounted前)Vue此时已经将将编译好的模板挂载到页面上
3、接口请求放在哪个生命周期
在created、beforeMount、mounted中进行调用,data都已经创建,可以将服务端返回的数据进行赋值。但是推荐在created中调用异步请求,ssr服务器渲染不支持其他2个,并且能够更快得到服务端数据。
4、生命周期钩子是如何实现
Vue生命周期钩子就是回调函数,创建组件实例过程中会调用对应钩子函数
5、Vue父组件和子组件生命周期执行顺序
第一次页面 加载会触发beforeCreate,created,beforeMount,mounted
渲染过程:父beforeCreate->父created ->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
更新也是父要等到子完成更新
父beforeUpdate->子beforeUpdate->子updated->父updated
二:Vue-router路由
1、Vue-router的跳转原理
vue-router实现单页面路由跳转,由三种方式:hash、history
- hash:#号后面内容作为路由地址,可以通过?携带参数,监听 hashchange 事件可以知道hash的变化,从而更新页面内容
- history:主要是HTML5标准发布的两个API,pushState、replaceState,通过这两个API可以改变url,但不会向服务器发送请求,得以实现局部刷新。(history需要后端配置,否则返回404)
2、怎么定义Vue-router的动态路由?怎么获取传过来的值?
动态路由的创建,在使用path属性过程中,使用动态路径参数,以冒号开头
{path: '/details/:id'}
通过
this.$route.params
这个属性可以获取动态参数
console.log(this.$route.params.id)
3、路由之间的跳转
- 声明式:
router-link
- 编程式:
this.$router.push()
路由跳转传递参数
通过动态路由方法
// 需要在路由配置文件中,配置动态路由 {path: 'detail/:id', name: 'Detail', component: Detail} // 传递参数 this.$router.push('/detail/' + id) // 获取参数 this.$route.params.id
通过query属性传值
{path: '/detail', name: 'Detail', component: Detail} // 跳转传递参数 this.$router.push({ path: '/detail', query: { name: 'zs', id: 1 } }) // 获取参数 this.$route.query // $route是当前router跳转对象,可以获取属性
通过params属性传值
{path: '/detail', name: 'Detail', component: Detail}
3、组件缓存keep-alive
<keep-alive include="goods"> // 只对goods这个组件起路由缓存作用,需要在组件添加name属性,这样找到更快
<router-view></router-view>
</keep-alive>
// keep-alive有2个钩子函数,activated, deactivated
从其他页面进入keep-alive的缓存页面,会触发activated钩子函数,可以进行更新操作
从缓存页面离开,会触发deactivated
在缓存页面时候,点击刷新会触发:created -- mounted -- activated
4、vur-router由哪几种导航守卫
- 全局前置:beforeEach(to, from, next)
- 路由独享守卫:beforeEnter
- 组件内的守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
5、active-class是哪个组件的属性?
vue-router模块的router-link组件的属性,当router-link标签被激活时,会应用这个样式,需要在路由js文件配置
linkActiveClass: 'active'
6、 r o u t e 与 route与 route与router的区别?
r o u t e r 是 V u e R o u t e r 的 实 例 , 编 程 式 跳 转 路 由 使 用 : ‘ t h i s . router是VueRouter的实例,编程式跳转路由使用:`this. router是VueRouter的实例,编程式跳转路由使用:‘this.router.push()`
$route为当前router跳转对象,里面可以获取当前路由的一些属性,name、path、query、params
7、vue-router实现路由懒加载的方式?
路由懒加载:把不同路由对应的组件分割成不同的代码块,然后路由被访问的时候去加载对应的组件
- 路由懒加载 使用import
- vue异步组件技术,将异步组件定义为返回一个Promise的工厂函数
- 使用命名chunk,和webpack中的魔法注释就可以把某个路由下的所有组件都打包在同个异步块中
8、切换路由时需要保存草稿功能?
在组件切换的过程中,将状态保留在内存中,防止重复渲染DOM
使用$route.meta的keepAlive属性
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
routes: [
{
path: '/',
name: 'Hello', component: Hello,
meta:{keepAlive:true} //需要被缓存
}
]
三、Vuex状态管理
1、Vuex和全局对象区别
- vuex的状态存储是响应式的,store中的状态发生变化,相应的组件也会高效的变化
- Vuex不能直接修改store状态,只能通过(commit)mutation去修改
2、Vuex的mutation中不能做异步操作
- 异步操作通过Action来提交mutation实现,这样使得我们方便跟踪每一个状态的变化
- 如果mutation也支持异步,就没有办法知道状态是何时更新的,无法追踪状态
3、Vuex的action有返回值吗?是什么?
action的返回值是一个Promise
4、为什么不直接分发mutation,而通过分发action之后提交mutation变更状态
- mutation必须同步执行,action内部执行异步操作
- 可以进行一系列的异步操作,通过提交mutation来记录action产生的副作用
四、常规问题
1、理解Vue的单向数据流
- Vue提倡单向数据流,父级props的更新会流向子组件,反过来不行(单向数据流)
- 为了防止意外的改变父组件状态,使得数据流变得难以理解(子组件为何不能修改父组件)
- 破坏单向数据流,复杂时debug成本会非常高
2、Vue通信方式
props/$emit
:父子通信ref
与$parent/children
:父子通信EventBus($on/$emit)
:兄弟通信- Vuex
3、v-model原理
v-model本质是语法糖v-bind+v-on与属性value+input结合,通过model属性的props+event属性来自定义
4、$nextTick
因为vue是异步渲染的,data发生改变后,DOM节点不会立刻渲染,需要使用nextTick来获取更新后的DOM,主要使用了宏任务和微任务
5、不建议index作为key
不管数据如何变化,index都是从0开始排列,会导致Vue复用错误的旧子节点,做额外工作。
diff算法中通过tag和key来判断,是否是sameNode
减少渲染次数,提升渲染性能
6、vue检测数组变化
两种数组的变化是Vue检测不到的,也不会触发视图更新
- 通过索引直接设置项
arr[2] = 9
- 修改数组的长度
arr.length = 4
vue检测数组和对象需要:遍历数组和递归遍历对象
// 为了解决上面2个问题,vue提供下面方法
// 第一个问题
Vue.set(vm.items, indexOfItem, newValue) // Vue.set
vm.items.splice(indexOfItem, 1, newValue) // Array.prototype.splice
// 第二个问题
vm.items.splice(newLength) // Array.prototype.splice
7、mixins使用
mixins:混合,更加高效实现组件内容的复用
mixins与vuex:vuex用来状态管理,每个组件都可以修改和使用里面的变量,并且是响应式的变化;mixins可以定义变量,引用组件中后,每个变量是相互独立的,值的修改在组件中不会相互影响,方法和参数在组件不共享。
mixins与组件:组件相当于在父组件中给一片独立的空间给子组件使用,然后通过props传值,本质两者是相对独立的;而mixins是将引入组件与组件中的对象和方法进行合并,相当于扩展父组件的对象和方法
// mixins.js
export const myMixin = {
data(){return { number: 1 }},
created() { this.hello()},
methods: {hello() { console.log('hello word')}}
}
// home.vue 组件 引入mixins
import { myMixin } from '@/assets/mixins.js'
export default {
mixins: [myMixin],
data() {return {}}
}
7.2、使用beforeDestory
- 解绑自定义事件 event.$off
- 清除定时器
- 解绑自定义的DOM事件, window scroll
8、Vue性能优化
- 编译阶段
- v-for与v-if不能连用
- SPA页面使用keep-alive缓存组件
- key保证唯一
- 使用路由懒加载,异步组件(import)
- 防抖、节流
- 图片懒加载
- SEO优化
- 服务端渲染SSR(在服务端将标签渲染成html,然后将html返回给客户端)
- 打包优化
- 压缩代码
- 使用cdn加载第三方模块
- 多线程打包happypack
- splitChunks抽离公共文件
- 提取公共代码