1.v-show与v-if的区别
控制手段:`v-show`隐藏则是为该元素添加`css--display:none`,`dom`元素依旧还在。`v-if`显示隐藏是将`dom`元素整个添加或删除
编译过程:`v-if`切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;`v-show`只是简单的基于css切换
编译条件:`v-if`是真正的条件渲染,它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。只有渲染条件为假时,并不做操作,直到为真才渲染
* `v-show` 由`false`变为`true`的时候不会触发组件的生命周期
* `v-if`由`false`变为`true`的时候,触发组件的`beforeCreate`、`create`、`beforeMount`、`mounted`钩子,由`true`变为`false`的时候触发组件的`beforeDestory`、`destoryed`方法
性能消耗:`v-if`有更高的切换消耗;`v-show`有更高的初始渲染消耗;
2.v-if和v-for的优先级,为什么不能一起用
* `v-for`优先级比`v-if`高
* 永远不要把 v-if 和 v-for 同时用在同一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断)
* 如果避免出现这种情况,则在外层嵌套template(页面渲染不生成dom节点),在这一层进行v-if判断,然后在内部进行v-for循环
3.为什么data属性是一个函数而不是一个对象?
* `vue`组件可能会有很多个实例,采用函数返回一个全新`data`形式,使每个实例对象的数据不会受到其他实例对象数据的污染
* 根实例对象data可以是对象也可以是函数(根实例是单例),不会产生数据污染情况
* 组件实例对象data必须为函数,目的是为了防止多个组件实例对象之间共用一个data,产生数据污染。采用函数的形式,initData时会将其作为工厂函数都会返回全新data对象
4.Vue中给对象添加新属性界面不刷新?
* 在一个组件实例中,只有在data里初始化的数据才是响应的,Vue不能检测到对象属性的添加或删除,没有在data里声明的属性不是响应的。
* Vue不允许在已经创建的实例上动态添加根级响应式属性,但是可以使用$set方法将相应属性添加到嵌套的对象上
* 数组数据变动,使用某些方法操作数组,变动数据时,有些方法无法被vue监测
push(),pop(),splice(),sort(),reverse()可被vue检测到filter(), concat(), slice()。这些不会改变原始数组,但总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组。
vue不能检测以下变动的数组:
1、当你利用索引直接设置一个项时,vm.items[indexOfItem]=newValue
2、当你修改数组的长度时,例如:vm.items.length = newLength
对象属性的添加或删除
由于Vue会在初始化实例时对属性执行getter/setter转化过程,所以属性必须在data对象上存在才能让Vue转换它,这样才能让它是响应的。
若想实现数据与视图同步更新,可采取下面三种解决方案:
* Vue.set()
* Object.assign()
* $forcecUpdated()
5.组件间通信的方案?
1. 通过 props 传递,通过 $emit 触发自定义事件
2. 使用 ref,EventBus
3. $parent 或$root
4. attrs 与 listeners
5. Provide 与 Inject
* Vuex
State -- 存储状态信息 Getters -- 状态信息的计算属性 Mutations -- 修改状态信息 Actions -- 状态信息异步处理 Modules -- store对象模块化
6.Computed和Watch?
computed 本质是一个具备缓存的watcher,依赖的属性发生变化就会更新视图。适用于计算比较消耗性能的计算场景。当表达式过于复杂时,在模板中放入过多逻辑会让模板难以维护,可以将复杂的逻辑放入计算属性中处理。
watch 没有缓存性,更多的是观察的作用,可以监听某些数据执行回调。
* 当我们需要深度监听对象中的属性时,可以打开 deep: true 选项,这样便会对对象中的每一项进行监听。这样会带来性能问题,优化的话可以使用 字符串形式 监听,如果没有写到组件中,不要忘记使用 unwatch手动注销 哦。
* 将immediate属性设置为true,我们告诉Vue在初始化时立即执行handler函数。
7.vue 插槽slot?
在Vue中,插槽(Slot)是组件模板中的一个占位符,用于插入子组件中的内容。通过使用插槽,父组件可以将子组件的内容嵌入到自己的模板中,实现更灵活的组件组合和内容传递。
`slot`可以分来以下三种:
* 默认插槽(Default Slot):当组件没有具名插槽时,所有没有被包裹在具名插槽中的内容都会被放置在默认插槽中。在组件模板中使用<slot>标签来表示默认插槽。
* 具名插槽(Named Slot):具名插槽是使用slot元素并指定name属性的插槽,用于在父组件中插入特定的内容。在子组件中使用<slot>元素并指定name属性来定义具名插槽,父组件通过使用<template v-slot:name>来访问具名插槽并插入内容。
* 作用域插槽(Scoped Slot):作用域插槽是一种特殊的插槽,允许将子组件中的数据传递给插槽内容。通过使用v-slot指令和作用域插槽,可以实现更灵活的组件组合和数据传递。
8.vue 路由守卫?
Vue路由守卫是一种用于控制用户访问权限的技术,它通过在路由跳转前后执行一些钩子函数来实现。路由守卫可以帮助我们验证用户是否登录、检查用户是否有权限浏览某个页面等。
Vue路由守卫主要包括以下三种类型:
* 全局路由守卫:全局路由守卫是在路由跳转前或跳转后执行的钩子函数,它可以用于全局拦截或处理路由导航。全局路由守卫包括全局前置守卫和全局后置守卫。
全局前置守卫用于在路由导航前进行拦截和处理,通常用于验证用户是否登录、检查权限等。全局后置守卫则用于在路由导航后进行一些处理,例如记录日志、统计访问次数等。
* 独享路由守卫:独享路由守卫是针对某个具体路由的守卫,它会在该路由被渲染时执行。独享路由守卫通常用于检查当前用户是否有权限访问该路由,或者对该路由的页面进行一些特定的处理。
* 组件内路由守卫:组件内路由守卫是针对组件内部的路由导航进行的守卫,它会在组件内部的路由导航发生时执行。组件内路由守卫通常用于对组件内部的路由进行一些特定的处理,例如加载数据、初始化组件等。
在使用Vue路由守卫时,可以根据实际需求选择合适的守卫类型,并在守卫中执行相应的逻辑来控制用户的访问权限。同时,还可以结合Vuex等其他技术来实现更复杂的权限控制和数据管理。
9.vue-route两种模式:history模式和hash模式?
* hash模式(vue-router默认hash模式)
hash模式背后的原理是onhashchange事件。
由于hash发生变化的url都会被浏览器记录下来,使得浏览器的前进后退都可以使用了,尽管浏览器没有亲求服务器,但是页面状态和url关联起来。后来人们称其为前端路由,成为单页应用标配。
比如http://www.abc.com/#/index,hash值为#/index。hash模式的特点在于hash出现在url中,但是不会被包括在HTTP请求中,对后端没有影响,不会重新加载页面。
* history模式
history模式利用了HTML5 History Interface中新增的pushState()和replaceState()方法。MDN相关介绍(需要特定浏览器支持)。这两个方法应用于浏览器的历史记录栈,提供了对历史记录进行修改的功能。只是当他们进行修改时,虽然修改了url,但浏览器不会立即向后端发送请求。
当使用history模式时,url就像正常的url,例如http://abc.com/user/id相比hash模式更加好看。特别注意,history模式需要后台配置支持。如果后台没有正确配置,访问时会返回404。
通过history api,我们丢弃了丑陋的#,但是有一个缺点,当刷新时,如果服务器中没有相应的相应或者资源,会分分钟刷出一个404来(刷新需要请求服务器)。所以history模式不怕前进,不怕后退,就怕刷新。
* hash模式和history模式对比
pushState()设置新的url可以是和当前url**同源的任意url;hash只可修改#后面的部分,只能设置当前url同文档**的url。
pushState()设置的新url可与当前url一致,这样也会把记录添加到栈中;hash必须设置与当前url不同的url的,才会触发动作将记录添加到栈中。
pushState()通过stateObject参数可以添加任意类型的数据到记录中;hash只可添加短字符串。
pushState()可额外设置title属性供后续使用。
* hash模式下,仅hash符号之前的url会被包含在请求中,后端如果没有做到对路由的全覆盖,也不会返回404错误。
* history模式下,前端的url必须和实际向后端发起请求的url一致,如http://abc.com/user/id,后端如果没有对user/id的路由处理,将返回404错误。
10. Promise和async&await?
Promise对象有以下两个特点。
* 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
* 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
Async&Await语法糖
在主体函数之前使用了async关键字。在函数体内,使用了await关键字。当然await关键字只能出现在用async声明的函数体内。该函数会隐式地返回一个Promise对象,函数体内的return值,将会作为这个Promise对象resolve时的参数。可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句
总结:
* 简约而干净Concise and clean
我们看一下上面两处代码的代码量,就可以直观地看出使用Async/await对于代码量的节省是很明显的。对比Promise,我们不需要书写.then,不需要新建一个匿名函数处理响应,也不需要再把数据赋值给一个我们其实并不需要的变量。同样,我们避免了耦合的出现。这些看似很小的优势其实是很直观的,在下面的代码示例中,将会更加放大。
* 错误处理Error handling
Async/await使得处理同步+异步错误成为了现实。我们同样使用try/catch结构,但是在promises的情况下,try/catch难以处理在JSON.parse过程中的问题,原因是这个错误发生在Promise内部。想要处理这种情况下的错误,我们只能再嵌套一层try/catch。Async/await是近些年来JavaScript最具革命性的新特性之一,提供了代替Promise的方案。