面试题-Vue

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')

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容