vue面试题
(掌握)简单说下setTimeout(fn, 0)中的0代表的是什么?如果我想在vue实现此功能可以用什么方法代替
js运行是基于单线程的,意味着一段代码执行时,其他代码将进入队列等待,一旦线程有空闲就执行后续代码。如果代码中设定了一个setTimeout
,那么浏览器便会在合适的时间,将代码插入任务队列,如果这个时间设为 0,就代表立即插入队列,但并不是立即执行,仍然要等待前面代码执行完毕。
所以setTimeout
并不能保证执行的时间,是否及时执行取决于 JavaScript 线程是拥挤还是空闲。
可以用Vue中的nextTick是Vue的DOM更新队列的下一次更新完成时触发,也是能够确保DOM更新完毕,如果环境不支持的时候其实就是setTimeout(fn, 0)。
#(掌握)用Vue实现样式绑定,可以用class或者内联样式,最少写出2个?
<!-- 第一种绑定class -->
<div :class="['classA', 'classB']"></div>
<!-- 第二种绑定class -->
<div :class="{'classA': true, 'classB' : false}"></div>
<!-- 第一种绑定style -->
<div :style="{fontSize: '16px', color: 'red'}"></div>
<!-- 第二种绑定style -->
<div :style="[{fontSize: '16px', color: 'red'}]"></div>
#(掌握)el的用法
ref 有三种用法:
1、ref 加在普通的元素上,用this.$refs(ref值) 获取到的是dom元素。
2、ref 加在子组件上,用 this.refs.XX。
vm.$el:
获取Vue实例关联的DOM元素;
比方说我这里想获取自定义组件tabControl,并获取它的OffsetTop,就需要先获取该组件。
在组件内设置 属性 ref='一个名称(tabControl2)', 然后 this.$refs.tabControl2,就拿到了该组件
切记:ref属性,而获取组件的时候要用$refs
获取 OffsetTop,组件不是DOM元素,是没有OffsetTop的,无法通过.OffsetTop来获取的。就需要通过$el来获取组件中的DOM元素:
this.tabOffsetTop=this.$refs.tabControl2.$el.offsetTop
#(掌握)一、简述Vue的生命周期
常用的有8个(生命周期不仅仅是8个),都是成对出现。分别是: beforeCreate阶段、created阶段; beforeMount阶段、mounted阶段; beforeUpdate阶段、updated阶段; beforeDestroy阶段、destroyed阶段; 分别为:创建前/后,载入前/后,更新前/后,销毁前/后。
#(掌握)二、第一次页面加载会触发哪几个钩子?
beforeCreate, created, beforeMount, mounted
#(掌握)三、Vue实现数据双向绑定的原理
vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时,Vue 将遍历它的属性,用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。
vue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer,Compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令(vue中是用来解析 {{}}),最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化 —>视图更新;视图交互变化(input)—>数据model变更双向绑定效果。
#(掌握)四、简单实现双向数据绑定
<body>
<div id="app">
<input type="text" id="txt">
<p id="show"></p>
</div>
</body>
<script type="text/javascript">
var obj = {}
Object.defineProperty(obj, 'txt', {
get: function () {
return obj
},
set: function (newValue) {
document.getElementById('txt').value = newValue
document.getElementById('show').innerHTML = newValue
}
})
document.getElementById('txt').addEventListener('keyup', function (e) {
obj.txt = e.target.value
})
</script>
#(掌握)六、简单讲述vuex
vuex是专门为vue.js提供的一种状态管理模式,它采用的是集中式储存和管理所有组件的状态和数据,方便使用。
imageVex有5种属性,分别是 state、getter、mutation、action、module:
1、state
Vuex 使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据。
2、getters
类似vue的计算属性,主要用来过滤一些数据。
3、mutations
mutations定义的方法动态修改Vuex 的 store 中的状态或数据。
4、actions
actions可以理解为通过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。view 层通过 store.dispath 来分发 action。
const store = new Vuex.Store({ //store实例
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
5、modules
项目特别复杂的时候,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
#(掌握)七、vue如何自定义一个过滤器
1、局部过滤器
Html:
<div id="app">
<input type="text" v-model="msg" />
{{msg| 过滤器名称 }}
</div>
JS:
var vm=new Vue({
el:"#app",
data:{
msg:''
},
filters: {
过滤器名称: function (value) {
if (!value) return ''
...
return value
}
}
})
2、全局过滤器
Vue.filter('过滤器名称', function (value) {
if (!value) return ''
...
return value
})
#(掌握)八、对keep-alive 的了解?
keep-alive
包裹动态组件时,会缓存不活动的组件实例,主要用于保留组件状态或避免重新渲染。
使用方法:
<keep-alive>
<component>
<!-- 该组件将被缓存! -->
</component>
</keep-alive>
#(掌握)十一、Vue 组件 data 为什么必须是函数
每个实例可以维护一份被返回对象的独立的拷贝。data是一个函数的话,这样每复用一次组件,就会返回一份新的data
,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而单纯的写成对象形式,就使得所有组件实例共用了一份data
,就会造成一个变了全都会变的结果。
#(掌握)十二、Vue 中怎么自定义指令
1、全局注册
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
2、局部注册
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
#(掌握)十三、nextTick 是做什么的
$nextTick
是在下次 DOM
更新循环结束之后执行延迟回调,在修改数据之后使用 $nextTick
,则可以在回调中获取更新后的DOM。
#(掌握)十四、$route
和 $router
的区别
$router
为 VueRouter
实例,想要导航到不同 URL
,则使用 $router.push
方法
$route
为当前 router
跳转对象里面可以获取 name
、 path
、 query
、 params
等
#(掌握)十五、vue-cli生成的项目文件夹assets和public(static) 的区别
vue-cli2生成的项目目录是static
vue-cli3以上生成的项目目录是public
- 相同点:
assets和public(static) 都是存放静态资源的文件。项目所需要的静态文件、图片、样式文件都可以放在这两个文件夹下
- 不同点:
在assets中存放的静态资源,在项目打包时会将这些静态资源与代码、index.html文件一起一同进行打包压缩,上传到服务器
在public(static)中存放的静态资源,不会被打包压缩格式化等流程,而是直接进入打包好的目录,直接上传到服务器
#(掌握)十六、DOM渲染在哪个周期中就已经完成
DOM 渲染在 mounted 中就已经完成了。
#(掌握)十七、VNode是什么?虚拟DOM是什么?
Vue在页面上渲染的节点,及其子节点称为“虚拟节点 (Virtual Node)”,简写为“VNode”。“虚拟 DOM”是由 Vue 组件树建立起来的整个 VNode 树的称呼。
#(掌握)十八、什么是RESTful API?怎么使用?
是一个api的标准,无状态请求。请求的路由地址是固定的,如果是tp5则先路由配置中把资源路由配置好。标准有:.post .put .delete
#(掌握)二十、如何让CSS只在当前组件中起作用?
将当前组件的<style>
修改为 <style scoped>
#(掌握)二十一、 修改第三方库的样式
/deep/
>>>
#(掌握)二十二 、那你能讲一讲MVVM吗?
MVVM是Model-View-ViewModel
缩写,也就是把MVC
中的Controller
演变成ViewModel
。Model层代表数据模型,View代表UI组件,ViewModel是View和Model层的桥梁,数据会绑定到viewModel层并自动将数据渲染到页面中,视图变化的时候会通知viewModel层更新数据。
#(掌握)单向数据流和双向数据流
单向数据流(Unidirectional data flow)方式使用一个上传数据流和一个下传数据流进行单向数据通信,两个数据流之间相互独立。单向数据流指只能从一个方向来修改状态。vuex(组件data -> action -> state->组件data)
与单向数据流对对应的是双向数据流(也叫双向绑定)。在双向数据流中,Model(可以理解为状态的集合) 中可以修改自己或其他Model的状态, 用户的操作(如在输入框中输入内容)也可以修改状态。这使改变一个状态有可能会触发一连串的状态的变化,最后很难预测最终的状态是什么样的。使得代码变得很难调试。
#(掌握)说一下MVC的理解?
mvc是模型(model)-视图(view)-控制器(controller)`的缩写,一种软件设计典范使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。`MVC对应Html,CSS,js。
#(掌握)二十三、那你知道Vue3.x响应式数据原理吗?
Vue3.x改用Proxy
替代Object.defineProperty。因为Proxy可以直接监听对象和数组的变化,并且有多达13种拦截方法。并且作为新标准将受到浏览器厂商重点持续的性能优化
Proxy只会代理对象的第一层,那么Vue3又是怎样处理这个问题的呢?(拓展)
判断当前Reflect.get的返回值是否为Object,如果是则再通过reactive
方法做代理, 这样就实现了深度观测。
监测数组的时候可能触发多次get/set,那么如何防止触发多次呢?(拓展)
我们可以判断key是否为当前被代理对象target自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行trigger。
#(掌握)二十四、再说一下vue2.x中如何监测数组变化
使用了函数劫持的方式,重写了数组的方法,Vue将data中的数组进行了原型链重写(push,pop,shift,unshift,splice...),指向了自己定义的数组原型方法。这样当调用数组api时,可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。这样就实现了监测数组变化。
#(掌握)二十五、Computed和Watch
Computed
本质是一个具备缓存的watcher,依赖的属性发生变化就会更新视图。 适用于计算比较消耗性能的计算场景。当表达式过于复杂时,在模板中放入过多逻辑会让模板难以维护,可以将复杂的逻辑放入计算属性中处理。
Watch
没有缓存性,更多的是观察的作用,可以监听某些数据执行回调。当我们需要深度监听对象中的属性时,可以打开deep:true
选项,这样便会对对象中的每一项进行监听。这样会带来性能问题,优化的话可以使用字符串形式
监听。
#(掌握)二十六.再说一下虚拟Dom以及key属性的作用
由于在浏览器中操作DOM是很昂贵的。频繁的操作DOM,会产生一定的性能问题。这就是虚拟Dom的产生原因
。
Vue2的Virtual DOM借鉴了开源库snabbdom
的实现。
Virtual DOM本质就是用一个原生的JS对象去描述一个DOM节点。是对真实DOM的一层抽象。
(也就是源码中的VNode类,它定义在src/core/vdom/vnode.js中。)
VirtualDOM映射到真实DOM要经历VNode的create、diff、patch等阶段。
「key的作用是尽可能的复用 DOM 元素。」
新旧 children 中的节点只有顺序是不同的时候,最佳的操作应该是通过移动元素的位置来达到更新的目的。
需要在新旧 children 的节点中保存映射关系,以便能够在旧 children 的节点中找到可复用的节点。key也就是children中节点的唯一标识。
#(掌握)二十七.Vue中组件生命周期调用顺序说一下
组件的调用顺序都是先父后子
,渲染完成的顺序是先子后父
。
组件的销毁操作是先父后子
,销毁完成的顺序是先子后父
。
加载渲染过程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount- >子mounted->父mounted
子组件更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated
父组件更新过程
父 beforeUpdate -> 父 updated
销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
#(了解)二十八.SSR了解吗?
SSR也就是服务端渲染,也就是将Vue在客户端把标签渲染成HTML的工作放在服务端完成,然后再把html直接返回给客户端
。
SSR有着更好的SEO、并且首屏加载速度更快等优点。不过它也有一些缺点,比如我们的开发条件会受到限制,服务器端渲染只支持beforeCreate
和created
两个钩子,当我们需要一些外部扩展库时需要特殊处理,服务端渲染应用程序也需要处于Node.js的运行环境。还有就是服务器会有更大的负载需求。
#(了解)在vuejs中实现防止模板渲染时闪烁的方法
[v-cloak] {
display: none;
}
<div v-cloak>
{{ message }}
</div>
#(了解)十九、聊聊你对Vue.js的template编译的理解?
简而言之,就是先转化成AST树,再得到的render函数返回VNode(Vue的虚拟DOM节点)。
详情步骤:
首先,通过compile编译器把template编译成AST语法树(abstract syntax tree 抽象语法树 即 源代码的抽象语法结构的树状表现形式),compile是createCompiler的返回值,createCompiler是用以创建编译器的。另外compile还负责合并option。
然后,AST会经过generate(将AST语法树转化成render funtion字符串的过程)得到render函数,render的返回值是VNode,VNode是Vue的虚拟DOM节点,里面有(标签名、子节点、文本等等)
#(了解)谈谈Vue.extend和Vue.component
Vue.extend
返回的是一个扩展实例构造器,也就是预设了部分选项的Vue实例构造器。其主要用来服务于Vue.component
。
Vue.component()`会注册一个全局的组件,其会自动判断第二个传进来的是Vue继续对象(`Vue.extend`)还是普通对象(`{...}`),如果传进来的是普能对象的话会自动调用`Vue.extend
#(了解)九、vue常用的修饰符?
.prevent: 提交事件不再重载页面;
.stop: 阻止单击事件冒泡;
.self: 当事件发生在该元素本身而不是子元素的时候会触发;
.capture: 事件侦听,事件发生的时候会调用;
#(了解)十、什么是vue的计算属性?
在模板中放入太多的逻辑会让模板过重且难以维护,在需要对数据进行复杂处理,且可能多次使用的情况下,尽量采取计算属性的方式。好处:
① 使得数据处理结构清晰;
② 依赖于数据,数据更新,处理结果自动更新;
③ 计算属性内部this指向vm实例;
④ 在template调用时,直接写计算属性名即可;
⑤ 常用的是getter方法,获取数据,也可以使用set方法改变数据;
⑥ 相较于methods,不管依赖的数据变不变,methods都会重新计算,但是依赖数据不变的时候computed从缓存中获取,不会重新计算。
#(了解)五、Vue的路由实现
1、hash模式
在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取; 特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。 hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://www.xxx.com (opens new window),因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。
2、history模式
history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。 history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.xxx.com/items/id。后端如果缺少对 /items/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”
#(了解)vue-router 有几种导航钩子?
1、全局守卫: router.beforeEach
2、全局解析守卫: router.beforeResolve
3、全局后置钩子: router.afterEach
4、路由独享的守卫: beforeEnter
5、组件内的守卫: beforeRouteEnter、beforeRouteUpdate (2.2 新增)、beforeRouteLeave