1、vue的生命周期钩子函数:
,
钩子函数 | 触发的行为 | 在此阶段可以做的事情 |
beforeCreadted | vue实例的挂载元素$el和数据对象data都为undefined,还未初始化。 | 加loading事件 |
created | vue实例的数据对象data有了,$el还没有 结束loading、 | 请求数据为mounted渲染做准备 |
beforeMount | vue实例的$el和data都初始化了,但还是虚拟的dom,节点具体的data.filter还未替换。 | |
mounted | vue实例挂载完成,data.filter成功渲染 | 配合路由钩子使用 |
beforeUpdate | data更新时触发 | |
updated | data更新时触发 数据更新时,做一些处理(此处也可以用watch进行观测) | |
beforeDestroy | beforeDestroy 组件销毁时触发 |
数据更新时,做一些处理(此处也可以用watch进行观测) |
destroyed | 组件销毁时触发,vue实例解除了事件监听以及和dom的绑定(无响应了),但DOM节点依旧存在 组件销毁时进行提示 | 组件销毁时进行提示 |
2、v-show 与 v-if 区别
- v-hsow和v-if的区别:
v-show是css切换,v-if是完整的销毁和重新创建。 - 使用
频繁切换时用v-show,运行时较少改变时用v-if - v-if=‘false’ v-if是条件渲染,当false的时候不会渲染。
3、计算属性和 watch 的区别
计算属性是自动监听依赖值的变化,从而动态返回内容,监听是一个过程,在监听的值变化时,可以触发一个回调,并做一些事情。
所以区别来源于用法,只是需要动态值,那就用计算属性;需要知道值的改变后执行业务逻辑,才用 watch,用反或混用虽然可行,但都是不正确的用法。
说出一下区别会加分
computed 是一个对象时,它有哪些选项?
computed 和 methods 有什么区别?
computed 是否能依赖其它组件的数据?
watch 是一个对象时,它有哪些选项?
- 有get和set两个选项
- methods是一个方法,它可以接受参数,而computed不能,computed是可以缓存的,methods不会。
- computed可以依赖其他computed,甚至是其他组件的data
- watch 配置
handler
deep 是否深度
immeditate 是否立即执行
总结
当有一些数据需要随着另外一些数据变化时,建议使用computed。
当有一个通用的响应数据变化的时候,要执行一些业务逻辑或异步操作的时候建议使用watcher
4、事件修饰符:
.prevent 是阻止事件本身行为,如阻止超链接的点击跳转,form表单的点击提交
.self 是只有是自己触发的自己才会执行,如果接受到内部的冒泡事件传递信号触发,会忽略掉这个信号
.capture 是改变js默认的事件机制,默认是冒泡,capture功能是将冒泡改为倾听模式
.once 是将事件设置为只执行一次,如 .click.prevent.once 代表只阻止事件的默认行为一次,当第二次触发的时候事件本身的行为会执行
5、组件中 data 为什么是函数
为什么组件中的 data 必须是一个函数,然后 return 一个对象,而 new Vue 实例里,data 可以直接是一个对象?
因为组件是用来复用的,JS 里对象是引用关系,这样作用域没有隔离,而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题。
6、组件间的通信
- 父子 props/event $parent/$children ref provide/inject
- 兄弟 bus vuex
- 跨级 bus vuex provide.inject
7、路由的跳转方式
一般有两种
<router-link to='home'> router-link
标签会渲染为<a>
标签,咋填template中的跳转都是这种;- 另一种是编程是导航 也就是通过js跳转 比如
router.push('/home')
8、Vue.js 2.x 双向绑定原理
这个问题几乎是面试必问的,回答也是有深有浅。基本上要知道核心的 API 是通过 Object.defineProperty()
来劫持各个属性的 setter / getter
,在数据变动时发布消息给订阅者,触发相应的监听回调,这也是为什么 Vue.js 2.x 不支持 IE8 的原因(IE 8 不支持此 API,且无法通过 polyfill 实现)。
9、什么是 MVVM,与 MVC 有什么区别
- MVC 是后端的分层开发概念;
-
MVVM是前端视图层的概念,主要关注于 视图层分离,也就是说:MVVM把前端的视图层,分为了 三部分 Model, View , VM ViewModel
- 数据劫持: 使用
Object.defineProperty(obj, 'property',{})
来定义属性,在定义时传入一些参数,包括set()
和get()
函数,分别在设置和获取该对象的该属性时调用执行。
function MVVM(option = {}) { this.$option = option let data = this._data = this.$option.data // 对data进行数据劫持 observe(data) // 将数据代理到this中,在this中对this._data操作 for (let key in data) { Object.defineProperty(this, key, { enumerable: true, get () { return this._data[key] }, set (newVal) { this._data[key] = newVal } }) } } function Observe(data) { // 这里写逻辑,方便递归 for (let key in data) { // 把data上的属性通过defineProperty定义 let val = data[key] observe(val) // 递归 Object.defineProperty(data, key, { enumerable: true, get () { // 获取该属性 return val }, set (newVal) { // 更改这个属性 if (val === newVal) return // 值不改变 val = newVal observe(newVal) } }) } } // 给对象增加 Object.defineProperty function observe(data) { if(typeof data != "object") return return new Observe(data) }
从代码看出,this
代理了this._data
(被劫持的数据),所以我们在用this.未定义属性
时不会加上setter
和getter
,应该去报错
- 编译页面
function VM(options = {}) { // MVVM框架核心类 this.$options = options let data = this._data = this.$options.data observe(data) // 对data进行数据劫持 for (let key in data) { // 将数据代理到this中,在this中对this._data操作 Object.defineProperty(this, key, { enumerable: true, get () { return this._data[key] }, set (newVal) { this._data[key] = newVal } }) } new Compile(options.el, this) // 编译页面 } function Observe(data) { // 这里写逻辑,方便递归 for (let key in data) { // 把data上的属性通过defineProperty定义 let val = data[key] observe(val) // 递归 Object.defineProperty(data, key, { enumerable: true, get () { // 获取该属性 return val }, set (newVal) { // 更改这个属性 if (val === newVal) return // 值不改变 val = newVal observe(newVal) } }) } } function observe(data) { // 给对象增加 Object.defineProperty if(typeof data != "object") return return new Observe(data) } function Compile (el, vm) { vm.$el = document.querySelector(el) // 获取到dom容器 // 新建一个文档碎片,将容器中的dom放入到文档碎片中(内存中),操作文档碎片编译(高效) let fragment = document.createDocumentFragment() while(child = vm.$el.firstChild) { fragment.appendChild(child) } replace(fragment) function replace (fragment) { Array.from(fragment.childNodes).forEach(node => { let text = node.textContent let reg = /\{\{(.*)\}\}/ if (node.nodeType === 3 && reg.test(text)) { // 处理模板 文本节点 let arr = RegExp.$1.split(".") let val = vm arr.forEach(key => {