vue面试题总结

这些是在最近面试中问到的一些问题

Vue 双向绑定的实现

v-model的双向绑定使用Object.defineProperty()实现双向绑定,这个方法重新定义了对象属性的 set、get方法,以此实现监听数据改变视图。

需要注意的是vue3.0使用了proxy对象实现双向绑定。

两者的区别在于,前者只能劫持对象的属性值进行操作。后者proxy是劫持整个对象,并返回一个新的对象进行操作

    //Object.defineProperty的用法
    let obj = {};
    let name = '';
    Object.defineProperty(obj, 'name', {
      set: function (value) {
        name = value;
        console.log('name' + value);
      },
      get: function () {
        return '《' + name + '》'
      }
    })
    obj.name = 'ZJINH';  // 设置为ZJINH
    console.log(obj.name);  // 《ZJINH》

    //proxy的用法
    let obj1 = {};
    let handler = {
        get(target, property) {
            console.log(`${property} 被读取`);
            return property in target ? target[property] : 3;
        },
        set(target, property, value) {
            console.log(`${property} 被设置为 ${value}`);
            target[property] = value;
        }
    }
    let p = new Proxy(obj1, handler);
    p.name = 'ZJINH'; //name 被设置为 ZJINH
    p.age; //age 被读取 3

Vue路由有哪些模式,有什么不同,用什么监听改变

vue的路由一共有两种模式historyhash

hisoty模式需要服务器配合,明显的特征是浏览器地址栏没有/#/,vue路由使用popstate监听实现路由功能,这种模式充分利用 history.pushState API 来完成 URL 跳转

hash模式则是使用哈希,明显的特征是浏览器地址栏有/#/,vue路由使用onhashchange监听实现路由功能,使用URL的 hash来模拟一个完整的URL,于是当URL改变时,页面不会重新加载

Vue路由跳转的几种方式

router-link跳转组件,to参数为跳转地址

this.$router.push该方法内部传去对象,对象内path为路由,但这个方法会向history栈添加一个记录,点击后退会返回到上一个页面。

this.$router.replace()该方法内部传去对象,对象内path为路由,跳转到指定的url,但是这个方法不会向history里面添加新的记录,点击返回,会跳转到上上一个页面。上一个记录是不存在的。

this.$router.go(n)该方法和history.go用法类似,可以前进后退n个页面。

this.$router.reslove()该方法返回一个地址,可以使用window.open打开新标签。

Vue router-link的用法

    // 字符串
    <router-link to="apple"> to apple</router-link>
    // 对象
    <router-link :to="{path:'apple'}"> to apple</router-link>
    // 命名路由
    <router-link :to="{name: 'applename'}"> to apple</router-link>
    //直接路由带查询参数query,地址栏变成 /apple?color=red
    <router-link :to="{path: 'apple', query: {color: 'red' }}"> to apple</router-link>
    // 命名路由带查询参数query,地址栏变成/apple?color=red
    <router-link :to="{name: 'applename', query: {color: 'red' }}"> to apple</router-link>
    //直接路由带路由参数params,params 不生效,如果提供了 path,params 会被忽略
    <router-link :to="{path: 'apple', params: { color: 'red' }}"> to apple</router-link>
    // 命名路由带路由参数params,地址栏是/apple/red
    <router-link :to="{name: 'applename', params: { color: 'red' }}"> to apple</router-link>
    tag属性
    <router-link :to="{name:'schedule',query:{aa:'dsd'}}" tag="li">跳转</router-link>
    event属性
    <router-link to="/document" event="mouseover">document</router-link>

基本属性有to 、replace、 append、 tag、 active-class、 exact 、 event、 exact-active-class

router-link标签会自动渲染成a标签,如果不想渲染为a标签,只需要使用tag属性例如 tag='button',那么就会渲染为button。

和a标签一样加上target属性为"_blank",依然可以打开一个新的页面。

event属性,那么默认情况下是当我们点击document的时候,跳转到相应的页面,但当我们加上event的时候,就可以改变触发导航的事件,比如鼠标移入事件。

append属性 类型: boolean 默认值: false 设置 append 属性后,则在当前 (相对) 路径前添加基路径。

active-class 类型: string 默认值: "router-link-active",选中的classname,默认值可以通过路由的构造选项 linkActiveClass 来全局配置。

exact-active-class 类型: string 默认值: "router-link-exact-active",配置当链接被精确匹配的时候应该激活的 class,默认值也是可以通过路由构造函数选项 linkExactActiveClass 进行全局配置的。

路由的构造选项全局配置示例

    export default new Router({
      mode:'history',
      linkActiveClass:'is-active-route',
      routes: [
        {
          path:'/about',
          component:about
        }
    ]
    })

Vue修改数组某一对象能否触发视图更新

答案是不能的,vue只对数组的push、splice等进行hack实现视图改变,针对这种arr[0].name='ZJINH'是无法监听的。 前面提到了Object.defineProperty()实现双向绑定,这个方法只能监听对象的某一个key,未来使用proxy实现的双向绑定,由于是监听一整个对象,所以可以触发视图更新

v-if v-show的区别 什么时候使用

v-if是通过vue底层实现的,效果类似于注释和取消注释

v-show是通过改变display实现元素显示隐藏

在v-for的时候不推荐使用v-if,每次循环都会在v-if里再次循环,有事件绑定的不推荐使用v-if。

vue的生命周期顺序

beforeCreate 创建之前初始化 事件和生命周期

created 创建完成

beforeMount 渲染

mounted dom结构渲染完成

beforeUpdate 视图更新前

updated 视图更新后

actived 这个只在使用了 keep-alive会出现,组件被激活

deactived 这个只在使用了 keep-alive会出现,组件取消激活(不可见)

beforeDestroy 组件销毁前,期间注销监听等

destroyed 组件销毁完毕

如何在created生命周期里获取组件的dom

上面提到了在mounted之前dom结构不会渲染出来,自然无法获取到。 但我们可以用以下两种方法获取。

一种使用子组件,在mounted的时候发送消息给父组件,父组件接受消息获取dom 当子组件已经渲染完成,便可发送消息事件通知父组件获取dom

    method:{
        getElm(){
            document.getElementById()
        }
    }

另外一种使用this.nextTick()的回调函数获取

    created(){
       this.nextTick(()=>{
           document.getElementById()
       })
    }

Sync修饰符是什么作用

这个修饰符在vue里其实是允许子组件修改传入的props参数,但这是不推荐使用的,修改参数还是建议使用emit对父组件发送消息进行参数修改。

Vue的 mixins 作用及用法

mixins 选项接收一个混入对象的数组。这些混入对象可以像正常的实例对象一样包含实例选项,这些选项将会被合并到最终的选项中,使用的是和 Vue.extend() 一样的选项合并逻辑。也就是说,如果你的混入包含一个created 钩子,而创建组件本身也有一个,那么两个函数都会被调用。

以上是vue的官方文档

简单来说类似于Object.assign,但在vue使用的时候,这个混入的对象,会和该组件内的方法合并执行

如何使用原生JS实现变量只允许改变一次

这个问题的前提是不存在ES6语法,实现var name=123只可以被修改一次

这里我使用的是Object.defineProperty实现的,原理很简单,修改的时候记录次数,如果是第一次修改则允许。

    var name=123;
    var temp=""
    var count=0
    Object.defineProperty(window, 'name',{
        get:function(){
            return temp;
        },
        set:function(val) {
            if(count===0){
                temp=val;      
                count++;
            }else{
                throw Error('只能赋值一次')        
            }
        }
    })

for foreach map 区别 如何停止跳出

这道题我的解答是,foreach map都是用回调进行,无法跳出,map会返回新对象。for循环可以用break跳出

跨域的解决方法

jsonp 利用了script标签无跨域,实现参数返回。

nginx 使用nginx反向代理到同域下

跨域头 在后端服务配置加上允许跨域请求头

如何对一个对象的key按字母排序并返回排序后的对象

var obj = {name: "ZJINH", age: 18, ace: 5};//要排序的对象
function objKeySort(obj) {//排序的函数
    var newkey = Object.keys(obj).sort();
  //先用Object内置类的keys方法获取要排序对象的属性名,再利用Array原型上的sort方法对获取的属性名进行排序,newkey是一个数组
    var newObj = {};//创建一个新的对象,用于存放排好序的键值对
    for (var i = 0; i < newkey.length; i++) {//遍历newkey数组
        newObj[newkey[i]] = obj[newkey[i]];//向新创建的对象中按照排好的顺序依次增加键值对
    }
    return newObj;//返回排好序的新对象
}
objKeySort(obj)