vue
vue 作为近几年很流行的一个前端框架,现在国内有大量的前端项目基于vue开发,因此求职面试一般都会问一些与Vue相关的问题。
如何让CSS只在当前组件中起作用?
在当前组件的style中加入scoped:<style scoped>
,scoped 有限制在某范围的含义,如果不设置,容易引起样式错乱!
v-if 和v-show的异同?
- v-if指令是直接销毁和重建DOM达到让元素显示和隐藏的效果,在切换时开销更高,更适合不经常切换的场景;
- v-show指令是通过修改元素的display CSS属性让其显示或者隐藏,在初始渲染时有更高的开销,但是切换开销很小,更适合于频繁切换的场景。
computed 和 watch 区别?
- watch 监听到值的变化就会执行回调,在回调中可以进行一些逻辑操作,一般在监听到值的变化需要做一些复杂业务逻辑的情况下使用;
- computed 是计算属性,依赖其他属性计算值,并且 computed 的值有缓存,只有当计算值变化才会返回内容,一般在需要依赖别的属性来动态获得值的时候使用。
组件之间的如何传值?
- 父子组件通信
- 父组件通过标签上面定义传值
- 子组件通过props方法接受数据
- 子组件通过$emit方法传递参数
- 兄弟组件通信:通过查找父组件中的子组件实现,也就是 this.$parent.$children,在 $children 中可以通过组件 name 查询到需要的组件实例,然后进行通信。
- 任意组件通信:可以通过 Vuex 或者 Event Bus 解决。
vue-cli中怎样使用自定义的组件?
- 在components目录新建你的组件文件;
- 在需要用的页面(组件)中导入;
- 注入到vue的组件的components属性上面;
- 在template视图view中使用。
请谈谈Vue生命周期钩子函数?
- vue 实例从创建到销毁的过程,就是生命周期;
- 创建前/后: 在beforeCreate阶段,vue实例的挂载元素el和数据对象data都为undefined,还未初始化。在created阶段,vue实例的数据对象data有了,el还没有;
- 载入前/后:在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染;
- 更新前/后:当data变化时,会触发beforeUpdate和updated方法;
- 销毁前/后:在执行destroy方法后,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在;
- vue生命周期中的事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑。
为什么避免 v-if 和 v-for 用在一起?
当 Vue 处理指令时,v-for 比 v-if 具有更高的优先级,这意味着 v-if 将分别重复运行于每个 v-for 循环中。通过v-if 移动到容器元素,不会再重复遍历列表中的每个值。取而代之的是,我们只检查它一次,且不会在 v-if 为否的时候运算 v-for。
vue-loader是什么?使用它的用途有哪些?
- 根据官网的定义,vue-loader 是 webpack 的一个 loader,用于处理 .vue 文件。
- 使用vue-cli脚手架,作者已经配置好了基本的配置,开箱及用,你需要做的就是npm install 安装下依赖,然后就可以开发业务代码了。
请说出vue.cli项目中src目录每个文件夹和文件的用法?
- assets文件夹是放静态资源;
- components是放组件;
- router是定义路由相关的配置;
- view视图;app.vue是一个应用主组件;
- main.js是入口文件
vue数据双向绑定的实现原理
双向绑定是对表单来说的,表单的双向绑定,说到底不过是 value 的单向绑定 + onChange 事件侦听的一个语法糖。
利用数据劫持结合发布订阅模式实现的数据双向绑定:
- observer用来对初始数据通过Object.defineProperty添加setter和getter,当取数据(即调用get)的时候添加订阅对象(watcher)到数组里, 当给数据赋值(即调用set)的时候就能知道数据的变化,此时调用发布订阅中心的notify,从而遍历当前这个数据的订阅数组,执行里面所有的watcher,通知变化update。
- compiler是用来把data编译到dom中;
- watcher是oberver和compiler之间通信的桥梁;
vue 为何用 proxy代替 defineProperty
Object.defineProperty是ES5中的方法,它可以直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
vue2利用Object.defineProperty来劫持data数据的getter和setter操作,动态更新绑定的template模块。
然而Object.defineProperty有先天缺陷——无法监听数组变化和只能劫持对象的属性,所以对于数组的监听需要hack它的8种方法,而当属性值也是对象则需要深度遍历,对性能损耗很大。
而proxy一是可以直接监听数组的变化;二是可以直接监听对象而非属性,同时,Proxy作为新标准将受到浏览器厂商重点持续的性能优化,唯一的劣势就是兼容性问题,而且无法用polyfill实现。
通过 Proxy 实现vue 的数据响应
Proxy 是 ES6 中新增的功能,它可以用来自定义对象中的操作,在 Vue3.0 中已经通过 Proxy 来替换原本的 Object.defineProperty 来实现数据响应式。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
let p = new Proxy(target, handler)
Proxy 对象的所有用法,都是上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
接下来我们通过 Proxy 来实现一个数据响应式
const onWatch = (obj, setLog, getLog) => {
const handler = {
get(target, property, receiver) {
getLog(target, property)
return Reflect.get(target, property, receiver);
},
set(target, property, value, receiver) {
setLog(value, property);
return Reflect.set(target, property, value);
}
}
return new Proxy(obj, handler);
}
const obj = { a: 1 }
const p = onWatch(
obj,
(v, property) => {
console.log(`监听到属性${property}改变为${v}`)
},
(target, property) => {
console.log(`'${property}' = ${target[property]}`)
}
)
我们通过自定义 set 和 get 函数的方式,在原本的逻辑中插入了我们的函数逻辑,实现了在对对象任何属性进行读写时发出通知。
当然这是简单版的响应式实现,如果需要实现一个 Vue 中的响应式,需要我们在 get 中收集依赖,在 set 派发更新。
vue3 中为何使用function base api 而不是class api?
- 更灵活的逻辑复用能力;
- 更好的TS类型推导;
- 更好的性能;
- tree-shaking 更好
- 代码更容易被压缩
总结
以上面试题,我都在面试过程中问过很多人,而自己去面试也经常被面试官问到,因为自己面试的岗位基本都是架构师或负责人岗位,所以vue的双向绑定原理被问到的最多。