美文网首页VUE
面试题-Vue

面试题-Vue

作者: 艾萨克菊花 | 来源:发表于2019-03-04 18:29 被阅读243次

    Vue八个生命周期

    beforeCreate【创建前】    created【创建后】

    beforeMount【载入前】    mounted【载入后】

    beforeUpdate【更新前】    updated【更新后】

    beforeDestroy【销毁前】    destroyed【销毁后】

    vue生命周期的作用:

    它的生命周期有多个事件钩子,让我们控制Vue实例的过程时形成更好的逻辑


    Vue、React、Angular之间的区别【题目根据此文章内容整理】

    MVX框架模式:MVC+MVP+MVVM

    1、MVC:Model【模型】+View【视图】+Controller【控制器】,只要基于分层的目的,让彼此的指责分开

    View通过Controller和Model联系,Controller是View和Model的协调者,View和Model不直接联系,基本联系都是单向的;

    MVC模式

    用户通过控制器Controller来操作模版Model从而达到视图View的变化。

    2、MVP是从MVC模式演变出来的,都是通过Controller/Presenter负责逻辑的处理+Model提供数据+View负责显示

    在MVP中,Presenter完全把View和Model进行了分离,主要的程序逻辑在Presenter中实现。

    MVP模式

    3、MVVM

    MVVM是把MVC里面的Controller和MVP里的Presenter改成了ViewModel。Model+View+ViewModel

    View变化会自动更新到ViewModel,ViewModel的变化也会自动同步到View显示。

    这种自动同步是因为ViewModel的属性实现了Observer,当属性变更时都能出发对应的操作。

    MVVM模式

    Vue实现数据双向绑定的原理

    采用数据劫持结合发布者+订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter、getter,在数据变动时发布消息给订阅者,触发相应监听回调。当把一个普通JS对象传给Vue实例,来作为它的data选项时,Vue将遍历它的属性,用Object.defineProperty()将他们转化为getter/setter。用户看不到getter/setter,但是在内部他们让Vue追踪依赖,在属性被访问和修改时通知变化。

    vue的数据双向绑定,将MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模版指令(Vue是用来解析{{}}),最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化=>视图更新;视图监护变化(input)=>数据model变更双向绑定效果。


    Vue组件间的参数传递

    1、父组件与子组件传值

    父组件=>子组件:子组件通过props方法接受数据;

    子组件=>父组件:$emit方法传递参数;

    2、兄弟组件传值

    eventBus,就是创建一个事件中心,相当于中转站,可以用它来传递事件和接受事件。【项目比较小时用这个比较合适,Vuex适用于中大型项目】


    Vue解决跨域问题


    Vue虚拟DOM传送门

    浏览器渲染引擎工作流程

    创建DOM树=>创建StyleRules=>创建Render树=>布局Layout=>绘制Painting

    1、用html分析器,分析html元素,构建一颗DOM树

    2、用CSS分析器,分析css文件和元素上的inline样式,生成页面的样式表

    3、将DOM树和样式表关联起来,构建一颗Render树每个DOM节点都有attach方法,接受样式信息,返回一个render对象,这些render对象最终会被构建成一颗Render树;

    4、有了Render树,浏览器开始布局,为每个Render的节点确定一个在显示屏上出现的精确坐标;

    5、Render树和显示坐标节点都有了,就调用每个节点paint方法。把他们绘制出来。

    JS操作真实DOM的代价

    用我们传统模式开发,原生js或者jq操作dom时,浏览器会从构建dom树开始从头到尾执行一次,在第一次操作中,我们需要更新10个DOM节点,浏览器收到第一个DOM请求后会立即执行,并不知道之后还有九次,最终执行10次,因此js直接操作真实DOM节点会付出很大的代价。

    为什么需要虚拟DOM

    web界面由DOM树来构建,当其中一部分发生变化时,其实就是对应某个DOM节点发生了变化。

    虚拟DOM就是为了解决浏览器性能问题而被设计出来的;

    创建DOM树=>创建StyleRules=>创建Render树【10次DOM操作=>虚拟DOM=>将十次更新的diff内容存储到本地的js对象上=>将这个js对象一次性attch到DOM树上】=>布局Layout=>绘制Painting


    Vue异步更新队列

    获取DOM内部内容

    运行后会抛出错误:Cannot read property 'innnerHTML of null,意思就是获取不到div元素。这里就涉及到一个vue重要的概念:异步更新队列

    Vue会根据当前浏览器环境优先使用原生的Promise.then和MutationObserver,如果都不支持,就会采用setTimeout代替。【Vue异步更新DOM

    Vue在观察数据变化时,并不是直接更新DOM,而是开启一个队列,并缓冲同一个事件循环中发生的所有数据变化。在缓冲过程中会去除所有重复数据,从而避免不必要的计算和DOM操作。

    $nextTick知道什么时候DOM更新完成

    vue-router链接

    vue-router是vue官方的路由管理器。他和vue的核心深度集成让构建单页面应用变得易如反掌。

    使用vue,我们通过组合组件来组成应用程序,当把vue-router添加进来,需要将组件映射到路由,然后告诉vue-router哪里渲染他们。

    通过注入路由器,我们可以在任何组件内通过this.$router访问路由器。

    this.$router和router使用起来完全不一样,我们使用this.$router的原因是我们并不想在每个独立需要封装路由的组件中都导入路由。

    router:

    //使用router-link组件来导航,通过to属性指定链接

    <router-link to="/foo"></router-link>    //默认渲染为a标签

    //路由出口      路由匹配到的组件将在这里渲染

    <router-view></router-view>  

    1、定义组件

    var Foo = {template: '<div>foo</div>'};

    2、定义路由

    每个路由应该映射一个组件

    通过vue.extend()创建组件构造器

    const routes = [{"foo", component: Foo}]

    3、创建router实例,然后传routes配置

    const router = new VueRouter({

        routes

    })

    4、创建和挂载根实例

    通过router配置参数注入路由,从而让整个应用都有路由功能

    const app = new Vue({

        router

    }).$mount("#app")

    动态路径参数

    const Foo = {template: '<div></div>'};

    const router = new VueRouter({

        routes: [

            {path: '/user/:id', component: Foo}

        ]

    })

    一个路径参数使用冒号:标记。当匹配到一个路由时,参数值会被设置到this.$router.params

    响应路由参数的变化

    当使用路由参数时,原来的组件实例会被复用,不过这也意味着组件的生命周期钩子不会再被调用

    复用组件时,相对路由参数的变化作出响应的话,可以watch $route对象

    const Foo = {

        template: '...',

        watch:{

            '$route'(to, from){

                对路由变化作出相应

            }

        }

    }

    或者使用2.2中引入的beforeRouteUpdate导航守卫

    const Foo = {

        template: '...',

        beforeRouteUpdate(to,from,next){

        }

    }

    捕获所有路由以及404路由

    常规参数只会匹配被/分隔url片段中的字符。如果想匹配任意字符,我们可以使用通配符*。

    当使用通配符路由时,请确保路由的顺序是正确的,也就是含有通配符的路由应该放到最后。路由{path:'*'}通常用于客户端404错误,。如果你使用了history模式,请确保正确配置你的服务器。

    当使用通配符时,$route.params会自动添加一个名为pathMatch的参数。它包含了通配符匹配的部分。

    高级匹配模式

    匹配优先级【谁先定义的,谁的优先级就最高】

    嵌套路由

    <div id="app"><router-link></router-link></div>

    const User = {

        template: '<div>{{$router.params.id}}<router-view></vouter-view/></div>'

    }

    const router = new VueRouter({

        routes:[

            {path: '/user/:id', component: User,

            children:[{

                当路由匹配到‘/user/:id/post’时

                path:''post",

                component:post

            }]

            }

        ]

    })

    要注意,以/开头的嵌套路由会被当做根路径。这让你充分的使用全套组件而无需设置嵌套的路径。

    编程式导航

    在vue实例内部,你可以通过$router访问到路由实例。因此你可以调用this.$router.push。

    想要导航到不同的url,则可以使用router.push方法。这个方法会向history栈添加一个新的记录,所以当用户点击浏览器后退按钮时,则回到之前的url。

    点击<router-link to=""></router-link>相当于调用router.psuh(...)

    声明式/编程式 编程式路由

    同样的规则也适用于router-link组件的to属性。

    在2.2.0+,可选的在router.push或router.replace中提供onComplete和onAbort回调作为第二个和第三个参数。这些回调将会在导航完成(所有的异步钩子被解析之后)或终止(导航到相同的路由、或在当前导航完成以前导航到另一个不同的路由)的时候进行响应的调用。

    router.replace(location,onComplete,onAbout)

    跟router.push很像,唯一的不同就是他不会向history添加新记录,跟他的方法命一样,替换掉当前的history记录。

    router.go()

    操作history

    router.push()/router.replace()/router.go()与window.history.pushState()/window.history.replaceState()/window.history.go相似,实际上他们确实消防了window.historyAPI.

    命名路由

    通过一个名称来标示路由,连接一个路由或执行跳转的时候。可以在创建Router实例的时候,在routes配置中给某个路由设置名称。

    const router = new VueRouter({

        routes:[{

            path: '/user/:id',

            name: 'user',

            component: User

        }]

    })

    要链接一个命名路由,可以给router-link的to属性传一个对象:

    <router-link :to="{name:'user',params:{userId123}}"></router-link>

    这跟代码调用router.push()一样

    router.push({name:"user",params:{userId:123}})    //     '/user/123'

    命名视图

    同时【同级】展示多个视图,而不是嵌套展示。例如创建一个布局,有sidebar(侧导航)和main(主内容)两个视图,这个时候命名视图就派上用场了。可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果router-view没有设置名字,那么默认default。

    <router-view name="a"></router-view>

    <router-view name="b"></router-view>

    <router-view></router-view>

    一个视图使用一个组件渲染,因此对于同个路由多个视图就需要多个路由。

    const router = new VueRouter({

        routes: [{

            path: '/user',

            components: {

                default: Foo,

                a:Bar,

                b: Baz

            }

        }]

    })

    嵌套命名路由

    const router  = new VueRouter({

        routes:[{

            path: '/',

            component: Foo,

            children: [{

                path: '/',

                component: User

            },{

                path: '/',

                components:{

                    a: A,

                    b: B,

                    default: C

                }

            }]

        }]

    })

    重定向

    重定向也是通过routes配置完成的:

    const router = new VueRouter({

        routes: [

            {path: '/a',redirect: '/b'}

        ]

    })

    重定向的目标也可以是一个命名的路由:

    const router = new VueRouter({

        routes: [

            {path: '/a',redirect: {name: 'foo'}}

        ]

    })

    或者一个动态方法,动态返回重定向目标:

    const router = new VueRouter({

        routes: [

            {path: '/a', redirect: to=> {

                方法接收目标路由作为参数

                return 重定向的 字符串路径路径对象

            }}

        ]

    })

    路由组件传参

    const User = {

        props: ['id'],

        template: '<div>{id}</div>'

    }

    const router = new VueRouter({

        routes: [

            {path: '/user/:id', component: User, props: true}

        ]

    })

    hash模式与history模式区别:【vue-router核心:改变视图的同时不会想后端发送请求】【单页面应用】

    声明式导航=>hash模式、编程式导航=>history模式

    1、hash-即地址栏url中的#符号

    特点:hash虽然出现在url中,但不会被包括在http请求中对后端完全没有影响,因此改变hash不会重新加载页面

    2、history-利用了h5 history interface 中新增的window.history.pushState()以及window.history.replaceState()方法

    因此hash模式以及history模式都是属于浏览器的特性,vue-router只是利用了这两种特性来实现前端路由。

    优缺点:

    1⃣️history模式设置的新的url可以是与当前url同源的任意url,而hash模式只能修改#后面的部分;

    2⃣️history模式可以设置与当前url相同,也可以把记录添加到栈,而hish模式设置的url必需与原来url不一样,才能把记录添加到栈;

    3⃣️history模式通过stateObject参数可以添加任意类型的数据到记录中,而hash模式只可以添加短字符;

    4⃣️history模式可以额外设置title属性提供后续使用;

    5⃣️hash模式,只有hash之前的url被包含在请求中,所以对后端来说,即使没有做到对路由的全覆盖,也不会出现404的情况;history模式下,前端的url必需和后端的url一致。如:'http:www.baidu.com/book/id'如果后端缺少对'/book/id'路由的处理,则会出现404情况。

    导航守卫

    全局前置守卫:router.beforeEach

    const router = new VueRouter({})

    router.beforeEach((to, form, next)=>{

        //to即将进入的路由目标对象

        //from当前导航正要离开的路由

        //next【function】一定要调用该方法来resolve这个钩子。执行效果依赖next方法的调用参数。

    })

    全局后置守卫:router.afterEach((to, from) => {})

    路由独享的守卫:beforeEnter

    组件内的守卫:beforeRouterEnter/beforeRouterUpdate/beforeRouterLeave

    完整的导航解析流程:

    1、导航被触发;

    2、在失活的组件里调用离开守卫;

    3、调用全局的beforeEach守卫;

    4、在重用的组件里调用beforeRouterUpdate守卫;

    5、在路由配置里靠用befroeEnter;

    6、解析异步路由组件;

    7、再被激活的组件里调用beforeRouterEnter;

    8、调用全局的beforResolve守卫;

    9、导航被确认;

    10、触发dom刷新;

    11、用创建好的实例调用beforeRouterEnter传给next回调函数;

    路由的懒加载

    当打包构建应用时,js包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。

    结合vue的异步组件webpack的代码分割功能轻松实现路由组件的懒加载。

    1、将异步组件定义为返回一个Promise的工厂函数

    const Foo = () => Promise.resolve({组件定义对象})

    2、在webpack2中,我们可以使用动态import语法来定义代码分块点

    import('./Foo.vue')

    相关文章

      网友评论

        本文标题:面试题-Vue

        本文链接:https://www.haomeiwen.com/subject/ddvxuqtx.html