面试题

100 阅读11分钟

1、v-model是什么? vue中标签怎么绑定事件?

答:v-model这个指令只能用在表单元素上,可以用他进行双向数据绑定。绑定事件:<input @click=doLog() />

2、mvvm框架是什么?说说对双向数据绑定的理解?它和其它框架(jquery)的区别是什么?哪些场景适合?

答:mvvmm模型就是用来定义驱动的数据、v经过数据改变后的html、vm就是连接数据和视图,用来实现双向绑定

双向绑定:一个变了另外一个跟着变了,例如:视图一个绑定了模型的节点有变化,模型对应的值会跟着变

区别:vue数据驱动,通过数据来显示视图层而不是节点操作。

场景:数据操作比较多的场景,更加便捷

 

3、自定义指令的方法有哪些?它有哪些钩子函数?还有哪些钩子函数参数?

答:全局定义指令:在vue对象的directive方法里面有两个参数,一个是指令名称,另外一个是函数。组件内定义指令(局部定义指令):directives

钩子函数:bind(绑定事件触发)、inserted(节点插入的时候触发)、update(组件内相关更新)、componentUpdated(被绑定元素所在模板完成一次更新周期时调用)、unbind(只调用一次,指令与元素解绑时调用)

钩子函数参数:el、binding

4、说出至少4种vue当中的指令和它的用法?

答:v-if:判断是否隐藏;v-for:数据循环出来;v-bind:class:绑定一个属性;v-model:实现双向绑定

 # vue组件通信方式

方法①、props/$emit

父组件向子组件传值

父组件通过props向下传递数据给子组件。注:组件中的数据共有三种形式:data、props、computed

子组件向父组件传值(通过事件形式)

子组件通过events给父组件发送消息,实际上就是子组件把自己的数据发送到父组件。

**方法②、emit/emit/emit/on **

**这种方法通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,巧妙而轻量地实现了任何组件间的通信,包括父子、兄弟、跨级。当我们的项目比较大时,可以选择更好的状态管理解决方案vuex。
**

方法③、vuex

方法④、attrs/attrs/attrs/listeners

方法⑤、provide/inject

方法⑥、parent / parent / parent / children与 ref

总结:常见使用场景可以分为三类:

父子通信:

父向子传递数据是通过 props,子向父是通过 events(emit);通过父链/子链也可以通信(emit);通过父链 / 子链也可以通信(emit);通过父链/子链也可以通信(parent / children);ref也可以访问组件实例;provide/injectAPI;children);ref 也可以访问组件实例;provide / inject API;children);ref也可以访问组件实例;provide/injectAPI;attrs/$listeners

兄弟通信:

Bus;Vuex

跨级通信:

Bus;Vuex;provide / inject API、attrs/attrs/attrs/listeners

5、请详细说下你对vue生命周期的理解?

总共分为8个阶段创建前/后,载入前/后,更新前/后,销毁前/后。

创建前/后: 在beforeCreated阶段,vue实例的挂载元素el和数据对象data都为undefined,还未初始化。在created阶段,vue实例的数据对象data有了,el和数据对象data都为undefined,还未初始化。在created阶段,vue实例的数据对象data有了,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结构依然存在

 

6、请说下 vue 组件的优点,以及注册使用的过程?

答:首先,组件可以提升整个项目的开发效率。能够把页面抽象成多个相对独立的模块,解决了我们传统项目开发:效率低、难维护、复用性等问题。

使用Vue.component方法注册组件。子组件需要数据,可以在props中接受定义。而子组件修改好数据后,想把数据传递给父组件。可以采用emit方法。

7、Vue.js内置的指令,用什么开头?

v-开头的 16、动态路由传参2种方式params与query 一、格式的区别

1)、params

声明式: User 编程式: $router.push({ name: 'user', params: { id: '123' }}) 接

//模板里的写法: $route.params.参数名

//脚本里的写法: this.$route.params.参数名 动态路由匹配也行。

1)、路由配置:{ path: '/user/:id', component: User }

2)、传参:

//声明式 用户01001的信息 //编程式 $router.push("/user/01001");

3)、接值:

//模板里的写法: $route.params.参数名

//脚本里的写法: this.$route.params.参数名

2)、query

传:

// 带查询参数,变成 /register?plan=private $router.push({ path: '/register', query: { plan: 'private' }}) 注意:如果提供了 path,那么params 会被忽略 接:

//模板里的写法: route.query.参数名//脚本里的写法:this.route.query.参数名 //脚本里的写法: this.route.query.参数名//脚本里的写法:this.route.query.参数名 二、使用场景的区别:

1、params:在传递一个参数时使用,如果参数多的话,地址栏上不利于阅读

2、query:在传递多个参数时使用,地址栏比params好阅读

17、vue实例和vue组件写法的区别 1、 data是个函数(面试题) 一个组件的 data 选项必须是一个函数,且要有返回object,只有这样,每个实例(vue组件对象)就可以维护一份被返回对象的独立的拷贝,否则组件复用时,数据相互影响,也就是说,组件的作用域是独立的。

2、组件模板(html代码)只能有一个根标签

3、组件名不可和html官方的标签名同名

4、组件没有el选项,只有根实例存在el

5、书写:组件名如果驼峰,那么使用时,用短横线(羊肉串的写法)

18、谈谈你对vueX的理解 1、vueX是干什么的

vuex能够保存全局数据,供整个应用使用,可以在组件之间传递数据。

vuex保存的数据是响应式的

vuex保存的数据可以跟踪状态的变化

2、vueX的核心概念

state : 数据仓库 ,存储所有的 共享数据 ,相当于vue组件里的data

getter : 在state的基础上 派生的数据, 相当于vue组件里 computed

mutation:修改state的数据时,用mutation,这与跟踪状态 有关系

action:解决mutation里只能有同步代码的问题,action里可以有异步代码

3、vueX的数据流

组件里 dispatch(派发)vueX中的 action,action里commit(提交)mutation,mutation里修改state。state被修改后,会响应式到组件上。

4、辅助函数

现在的面试,都会问的比较多。而且在项目中大部分都会使用辅助函数来简化项目中的代码。

有了这些辅助函数后,在组件里,不用再写$store了。

mapState:把vuex中state(状态)映射(合并)到组件对象的computed上。直接使用计算属性就可以拿到vuex的状态

mapGetters:把vueX的getters映射到组件的computed上。

mapMutations:把vueX的mutations映射到组件的methods上。

mapActions: 把vueX的actions映射到组件的methods上。

示例代码

//1、vueX export default new vueX.Store({ state:{ count:10 } })

//2、组件里 import { mapState } from "vuex";

export default { name: "Store01", data() { return {}; }, computed:{
...mapState(['count']), //把vuex的state映射到组件的计算属性上 a:function(){ return 250; }, } }; 5、modules

   当项目比较大时,所有的全局数据存放在state里,会非常混乱,怎么办?使用module,把数据分门别类的进行处理,即:模块化。 每个模块是一个独立的store。然后由总体的store引入所有的分模块store。
复制代码

示例代码:

//1、moduleA.js 此模块里管理的数据是 count。

export default { namespaced:true, //这个命名空间是区分不同模块的 state:{ count:1 }, mutations:{ …………………… }, actions:{
incCount(context){ console.log("moduleA的action"); setTimeout(()=>{ context.commit("incCount"); },2000); } } }

2、创建vueX.store对象

import moduleA from "./moduleA";

export default new vueX.Store({ modules:{ moduleA:moduleA } //简写: modules:{ moduleA,moduleB } });

//2、组件里派发action时,加上namespaced,用来区分不同的模块

this.$store.dispatch('moduleA/incCount');//moduleA就是模块名。保证派发的是moduleA里的incCount。 19、vue路由懒加载 vue的SPA(Single Page Application)应用里,当(webpack)打包构建时,会把所有的js打在一起,JavaScript 包会变得非常大,并在第一次请求时全部下载完毕,影响页面加载(性能)。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。

目前有三种方式实现路由组件的懒加载,分别是:

vue异步组件

es 的 import()

webpack的require.ensure()

1)、 vue异步组件

把路由配置,进行修改

{ ​ path: '/shopcar', ​ name: 'shopcar', ​ component: resolve => require(['@/pages/ShopCar'],resolve) ​ },

1)、运行是打开chrome的network,就会看到进入路由 /shopcar 时,会多出另外 一个js文件。一般是0.js或者1.js

2)、用npm run build打包时,wepback就会多打了一个 js文件(如:0.b5a82d6947b2e30edcc8.js),这个js文件就是把ShopCar文件进行了单独打包。同样的在network里,就会看到进入路由 /shopcar 时,多出一个单独的js文件的请求

注:这种方式,webpack会把每个异步组件单独打成一个js文件。

2)、es的import()

主要是把原来的引入方式进行修改 ,路由配置就不用改了:

1、不指定webpackChunkName,每个组件单独打一个js文件

原来是:import ShopCar from '@/pages/ShopCar'

修改后:const ShopCar = () => import('@/pages/ShopCar');

修改后的做法是定义了一个函数,由于函数不调用不执行,所有,一开始时,并不会引入该组件,只有路由跳转时才会调用该函数。

2、指定webpackChunkName,相同的webpackChunkName就会打在同一个js文件里

1)、以下两个组件的webpackChunkName相同,所以,打在一个js文件里

const ShopCar = () => import(/* webpackChunkName: 'demot' / '@/pages/ShopCar'); ​ const GoodsDetailPage = () => import(/ webpackChunkName: 'demot' */ '@/pages/GoodsDetailPage');

2)、下面这个组件的webpackChunkName和上面两个不一样,所以,单独打在一个js文件里

const Login = () => import(/* webpackChunkName: 'demoty' */ '@/pages/Login');

3)、webpack的require.ensure()

这种方式,只改路由配置即可。

如:

{ ​ path: '/GoodsDetailPage', ​ name: 'GoodsDetailPage', ​ component: r => require.ensure([], () => r(require('@/pages/GoodsDetailPage')), 'demot') ​ }, ​ { ​ path: '/Login', ​ name: 'Login', ​ component: r => require.ensure([], () => r(require('@/pages/Login')), 'demot') ​ }, ​ { ​ path: '/shopcar', ​ name: 'shopcar',

component: r => require.ensure([], () => r(require('@/pages/ShopCar')), 'demoty')
复制代码

​ },

以上代码中,我把Login和GoodsDetailPage使用了相同的chunkName

20、MV*(MVC,MVP,MVVM) 答:

这是项目的架构模式。优点:耦合度低、重用性高、生命周期成本低、部署快、可维护性高、有利软件工程化管理。

1、MVC是从后端演变后的项目架构模式。

M:model,模型,主要完成业务功能,在数据库相关的项目中,数据库的增删改查属于模型(重点)。 V:view,视图,主要负责数据的显示 C:controller,控制器,主要负责每个业务的核心流程,在项目中体现在路由以及中间件上。

2、MVP

MVP是由MVC演变过来的。

P:Presenter 代替里C。

在MVP中View并不直接使用Model,而在MVC中View可以绕过Controller从直接Model中读取数据。

3、MVVM

MVVM是Model-View-ViewModel的缩写,MVVM模式把Presenter改名为ViewModel,基本与MVP模式相似。 唯一区别是:MVVM采用数据双向绑定的方式

在做vue的开发时,程序员写的代码和vue框架本身合起来是属于MVVM模式。

21、你了解Vue.js吗? 这种题,一般是比较难以回答的,问得很模糊,

如果不想多说,那就直接回答:了解并做过五个项目。

如果想回答详细的话,参考思路如下:

1)、vueJS是基于MVVM的JS框架

2)、有(常见的)13个指令:

3)、有(常见的)8个配置项:el,data,computed,watch,components,filter,directives,mixins

4)、vue实例生命周期分为四个阶段,八个生命周期函数

5)、vue做项目时会用到的全家桶技术:vueJS框架,vueX,vue-router,aixos,vant组件库等等

6)、我用vue框架一共做过五个项目。

………………………………

22、vue-router的两种路由模式的区别 路由模式分为两种:hash和history;通过设置vueRouter对象的mode属性来完成修改。

区别:

1)、外观上

hash模式时,路径上有#。

history模式时,路径上没有#。

2)、原理上

hash模式通过修改location.href来完成

使用锚点连接的思路,使用hash模式不会给后端发请求。当然,在hash值变化时,会同时触发window对象的onhashchange事件,并可以通过事件对象的oldURL属性和newURL属性 得到新旧URL。

p01
p02
p03







我是第一个p





我是第二个p





我是第三个p





2)、通过修改history.pushState来完成

如:

window.history.pushState(null,null,"p01.html"); window.location.reload();//想测试的话,尽量加上这句话,要不然,地址变了,但是页面并没有出现。 history模式会给后端发请求(如果刷新当前页面的话),一旦,后端和前端提供了同样的路径,那么,浏览器的请求结果就会有问题,到底是后端的资源还是前端的资源(不同的后端处理思路不停),还好,我们一般在后端apiserver的请求路径的前面习惯性的有个 /api。

所以,由于不同服务端的处理思路不同。所以,需要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面(单页面),这个页面就是你 app 依赖的页面。否则,就会返回404。

你可以改成history的模式,测试一下,如果刷新当前页面,那么,浏览器会朝后端发送请求(给当前路径)。

23、Vue路由守卫的三种方式,及其钩子函数和参数 1)、全局守卫

全局守卫有前置守卫和后置守卫,是vueRouter对象的两个钩子函数,分别是 beforeEach和afterEach。

前置守卫:

router.beforeEach((to, from, next) => { // to: 目标路由 // from: 当前路由 ​ // next() 跳转 一定要调用 next(false);//不让走 next(true);//继续前行 next('/login')//走哪 next({path:'/detail/2',params:{},query:{}})//带点货 } 后置守卫:

router.afterEach((to,from)=>{ //全局后置守卫业务 }) 如果能够回答上过程,肯定有加分:

//过程: 1、请求一个路径:如:/Index 2、经历前置守卫 决定了能去哪个路径 3、根据去的路径,找对应component(路由配置) 4、经过后置守卫 5、创建组件

2)、路由独享守卫

写在路由配置里。钩子函数名:beforeEnter,只有前置守卫

如:

// src/router/index.js { path: '/user', component: User, beforeEnter: (to,from,next)=>{ //路由独享守卫 前置 console.log('路由独享守卫'); if(Math.random()<.5){ next() }else{ next('/login') } } } 3)、组件内部守卫

写在组件对象里。分别有前置守卫,后置守卫,路由改变守卫(当前组件被复用的情况,不是路径改变)三个钩子函数。

export default{ data(){return {}} ……………………
​ //组件内部钩子 beforeRouteEnter (to, from, next) {//前置 // 不!能!获取组件实例 this // 因为当守卫执行前,组件实例还没被创建 }, beforeRouteUpdate (to, from, next) { // 在当前路由改变,但是该组件被复用时调用 // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候, // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。 // 可以访问组件实例 this }, beforeRouteLeave (to, from, next) {//后置 // 导航离开该组件的对应路由时调用 // 可以访问组件实例 this }