Vue 面试题(持续更新)

文章目录

      • 一、Vue是什么,Vue的优势有哪些?
      • 二、什么是MVVM
      • 三、Vue的双向绑定原理
      • 四、Object.defineProperty 和 Proxy 的区别
      • 五、Vue生命周期
      • 六、第一次加载页面会触发那几个钩子函数
      • 七、Vue 组件的封装过程?
      • 八、Vue 组件如何进行传值的?
      • 九、组件中的name有什么作用
      • 十、data 为什么必须是函数
      • 十一、组件命名规范
      • 十二、怎么在组件中监听路由参数的变化?
      • 十三、如何捕获Vue组件的错误信息
      • 十四、Vue组件里的定时器要怎么销毁
      • 十五、Vue组件使用流程
      • 十六、Vue 中 solt 以及 solt 作用域插槽的使用方式
        • 插槽的使用
        • 作用域插槽的使用
      • 十七、什么是动态组件,如何实现一个动态组件
      • 十八、Vue 该如何实现组件缓存?
      • 十九、跟 keep-alive 有关的生命周期是哪些?
      • 二十、Vue 常用的修饰符都有哪些?
        • 表单修饰符
        • 事件修饰符
        • 鼠标按钮修饰符
      • 二十一、Vue 常用的指令都有哪些?并且说明其作用
      • 二十二、自定义指令
        • 钩子函数
        • 钩子函数参数
        • 动态指令参数
      • 二十三、指令 v-el 和 v-ref
      • 二十四、v-show 和 v-if 指令的共同点和不同点?
        • 相同点:
        • 不同点:
      • 二十五、为什么避免 v-if 和 v-for 用在一起
      • 二十六、watch、methods 和 computed 的区别?
        • 三者的加载顺序
      • 二十七、怎么在 watch 监听开始之后立即被调用?
      • 二十八、watch 怎么深度监听对象变化?
      • 二十九、computed 中的属性名和 data 中的属性名可以相同吗?
      • 三十、什么是 Vue 的计算属性
      • 三十一、Vue 中 key 值的作用是什么?
      • 三十二、Vue-loader 是什么?使用它的用途有哪些?
      • 三十三、Vue 中怎么自定义过滤器
    • Vuex
      • 三十四、你是怎么认识 Vuex 的?
      • 三十五、Vuex 的 5 个核心属性是什么?
      • 三十六、Vuex 的出现解决了什么问题?
      • 三十七、简述 Vuex 的数据传递流程
      • 三十八、Vuex 的 Mutation 和 Action 之间的区别是什么?
    • Vue-router
      • 三十九、Vue-router 是干什么的,原理是什么?
      • 四十、配置路由的流程
      • 四十一、路由之间是怎么跳转的?有哪些方式?
      • 四十二、导航守卫
          • 全局前置守卫
          • 全局解析守卫
          • 全局后置钩子
          • 路由独享的守卫
          • 组件内的守卫
          • 完整的导航解析流程
      • 四十三、路由传值的方式有哪几种
          • 编程式的导航 router.push
          • 声明式导航
      • 四十四、JQuery 和 params 之间的区别是什么?
      • 四十五、$ route 和$ router 的区别是什么?
      • 四十六、请说出路由配置项常用的属性及作用
      • 四十七、Vue 怎么实现跨域
      • 四十八、Vue 中动画如何实现
      • 四十九、Vue2.0 兼容 IE 哪个版本以上吗?
      • 五十、Vue 项目优化的解决方案都有哪些?
      • 五十一、怎样理解 Vue 的单向数据流?
      • 五十二、说说你对 SPA 单页面的理解,它的优缺点分别是什么?
      • 五十三、scoped 属性的原理
          • 什么是 scoped
      • 五十四、请说出 Vue.cli 项目中 src 目录每个文件夹和文件的用法?
      • 五十五、使用 Vue 的时候一下加载造成页面卡顿,该如何解决?
          • 使用路由懒加载

一、Vue是什么,Vue的优势有哪些?

Vue.js是一款轻量级前端框架,双向数据绑定、组件化开发、数据和结构分离、虚拟DOM、运行速度快等特点,作者是中国的尤雨溪,对应的API文档对国内开发者非常友好,做为国内前端开发人员的首选入门框架。
1、Vue.js最突出的优势在于可以对数据进行双向绑定;
2、Vue.js可以进行组件化开发,同一组件多处复用,大大减少代码的重复编写。封装组件后代码结构更加清晰,读者更加易于理解;
3、使用Vue.js编写出的代码本身就是响应式的,这使得网页在各种设备商都能显示出非常好看的效果;
4、Vue是单页面应用,使用路由进行页面的切换和跳转、相比于传统的服务器返回整个页面内容,Vue.js只刷新页面一部分内容,减少了DOM元素的重复渲染,大大加快了访问速度和用户体验;
5、Vue.js有大量的第三方组件库,例如element-ui、iView等,使用这些组件库可以节省大量的开发时间,大大加快开发效率;

二、什么是MVVM

MVVM即Model-View-ViewModel的简写,即模型-视图-视图模型。模型(model)指的是后端传递的数据、视图(View)指的是所看到的前端页面、视图模型(ViewModel)是MVVM的核心,它是连接View和Model的桥梁,它分为两个方向:
1、将模型(Model)转换成视图(View)
2、将视图(View)转换成模型(Model)
这两个方向都实现,我们称之为数据的双向绑定。

三、Vue的双向绑定原理

Vue.js是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()劫持data中的属性以及属性中的子属性的getter、setter,在数据发生变化(属性被访问或者修改时)发布者发送消息给订阅者,触发相应的监听回调。Vue 面试题(持续更新)_第1张图片
observe 对数据对象中的属性以及子属性对象的属性进行递归遍历,给他们都体检上setter和getter,当给某一属性赋值时,就会触发setter,从而监听数据的变化。
compile 解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的接点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,就会更新视图。
watcher 订阅者是observe和conpile之间的通讯桥梁,
(1)在资深实例化时向订阅器(dep)中添加自己;
(2)自身必须有一个update()方法;
(3)当属性变动depnotice()通知时,能调用自身的update()方法,并触发compile中绑定的回调函数;
通过Observer来监听model数据变化,Compile解析编译模板指令,最终利用Watcher作为Observer 和 Compile 之间的通信桥梁,达到数据双向绑定效果。

四、Object.defineProperty 和 Proxy 的区别

1、Proxy可以直接监听对象而非属性
2、Proxy可以直接监听数组的变化
3、Proxy有多重拦截方法,不限于apply、ownKeys、deleteProperty、has 等是Object.defineProperty 不具备的
4、Proxy返回的是一个新对象,我们可以只操作新的对象达到目的,而Object.defineProperty只能便利对象属性直接修改
5、Proxy作为新标准将收到浏览器厂商重点持续的性能优化,即新标准性能红利
6、Object.defindProperty兼容性好,支持IE9,而Proxy存在浏览器兼容性问题,而且无法用polyfill磨平,因此vue在3.0版本使用Proxy重写

五、Vue生命周期

1、beforeCreate
实例初始化之后被调用,
2、created
实例创建完成立即调用,此时data中的属性和methods中的方法已经可以调用,然而挂载阶段还没开始,$el属性不可见。通常在此处发起数据请求。
3、beforeMount
在挂载之前呗调用,相关的render(渲染)函数首次被调用
4、mounted
el被新创建的vm. $el替换,并挂载到实例上之后调用该钩子,如果root实例挂在了一个文档内元素,当mounted被调用时、vm. $el也在文档内
5、beforeUpdate
数据更新是调用,发生在虚拟DOM打补丁之前,这里适合在更新之前访问现有的DOM,比如手动移除已添加的事件监听器,该钩子在服务器端渲染期间不被调用,因为只有初次渲染才会在服务端进行。
6、updated
由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。
7、beforeDestroy
实例销毁之前被调用,在这一步,实例仍然完全可用,该钩子在服务端渲染期间不被调用
8、destroyed
Vue实例销毁后调用,调用后,Vue实例知识的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
9、activated
keep-alive组件激活时调用
10、deactivated
keep-alive组件停用时呗调用
11、errorCaptured
当捕获一个来自子组件的错误时被调用,此钩子会受到三个参数:错误对象,发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回false以组织该错误继续向上传播

六、第一次加载页面会触发那几个钩子函数

beforeCreate, created, beforeMount, mounted 这几个钩子函数

七、Vue 组件的封装过程?

1、分析项目需求,把页面中可复用结构、样式以及功能单独抽离出来。
2、具体步骤,使用Vue.extend方法创建一个组件,然后使用Vue.commponent方法注册组件。

八、Vue 组件如何进行传值的?

1、父组件向子组件传递数据
在父组件中的子组件标签上用v-bind绑定一个自定义属性,并把数据绑定在自定义属性上,子组件使用props接收。
2、子组件向父组件传递数据
子组件通过Vue实例的$emit触发,并且可以携带参数。父组件监听使用@(v-on)进行监听,然后进行方法处理.
3、非父子组件之间数据传递
(1)使用事件总线,

// 添加事件总线对象
Vue.prototype.$bus = new Vue()

(2)在需要接收消息的组件的created钩子中订阅方法,$ bus.on(‘方法名’, 传递的参数)
(3)在需要传递消息的组件的motheds中添加触发函数,在函数中发布订阅的方法,$ bus.$emit(‘方法名’)
(4)在传递消息的组件中绑定事件触发

九、组件中的name有什么作用

1、当项目使用keep-alive标签时,可搭配组件 name 进行缓存过滤
2、DOM 做递归组件时需要调用自身 name
3、vue-devtools 调试工具里显示的组见名称是由 vue 中组件 name 决定的
Vue 面试题(持续更新)_第2张图片

十、data 为什么必须是函数

1、每个组件都是Vue的实例
2、当组件被复用,当data的值是同一个引用类型的值时,改变其中一个会影响其他数据。
3、data是函数时,数据以函数返回值的形式定义,这样每复用一次组件,就会返回一个新的data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。

十一、组件命名规范

1、组件文件命名单词大写开头 (PascalCase),要么始终是横线连接 (kebab-case)。

components/
|- MyComponent.vue
components/
|- my-component.vue

2、应用特定样式和约定的基础组件 (也就是展示类的、无逻辑的或无状态的组件) 应该全部以一个特定的前缀开头,比如 Base、App 或 V。

components/
|- BaseButton.vue
|- BaseTable.vue
|- BaseIcon.vue
components/
|- AppButton.vue
|- AppTable.vue
|- AppIcon.vue
components/
|- VButton.vue
|- VTable.vue
|- VIcon.vue

3、只使用一次,不接受任何prop的组件,应该以The前缀命名,以示其唯一性。
这不意味着组件只可用于一个单页面,而是每个页面只使用一次。这些组件永远不接受任何 prop,因为它们是为你的应用定制的,而不是它们在你的应用中的上下文。如果你发现有必要添加 prop,那就表明这实际上是一个可复用的组件,只是目前在每个页面里只使用一次。

components/
|- TheHeading.vue
|- TheSidebar.vue

4、和父组件紧密耦合的子组件应该以父组件名作为前缀命名。
如果一个组件只在某个父组件的场景下有意义,这层关系应该体现在其名字上。因为编辑器通常会按字母顺序组织文件,所以这样做可以把相关联的文件排在一起。

components/
|- TodoList.vue
|- TodoListItem.vue
|- TodoListItemButton.vue
components/
|- SearchSidebar.vue
|- SearchSidebarNavigation.vue

5、在组件使用时

在这里插入代码片
<!-- 在单文件组件和字符串模板中 -->
<MyComponent/>
<!-- 在 DOM 模板中 -->
<my-component></my-component>
或者

<!-- 在所有地方 -->
<my-component></my-component>

十二、怎么在组件中监听路由参数的变化?

有两种方式可以监路由参数的变化,但是只能在包含了< router-view >标签的组件内使用
1、会用watch

watch: {
    '$route': (to, from) => {
      console.log('to', to)
      console.log('from', from)
    }
  },

Vue 面试题(持续更新)_第3张图片
在这里插入图片描述
2、当使用路由参数时,例如从 /user/foo 导航到 /user/bar,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。所以该钩子只在复用同一组件时才被触发。

beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },

十三、如何捕获Vue组件的错误信息

1、errorCaptured 是组件内部钩子,当捕获一个来自子孙组件的错误时被调用,接收
error、vm、info 三个参数,return false 后可以阻止错误继续向上抛出
子组件直接打印一个未定义的变量a
在这里插入图片描述
父组件生命周期函数errorCaptured
Vue 面试题(持续更新)_第4张图片
Vue 面试题(持续更新)_第5张图片
2、errorHandler 为全局钩子,使用 Vue.config.errorHandler 配置,接收参数与 errorCaptured 一致,2.6 后可捕捉 v-on 与 promise 链的错误,可用于统一错误处理与错误兜底
在这里插入图片描述
main.js 中 errorHandler全局钩子
在这里插入图片描述
Vue 面试题(持续更新)_第6张图片

十四、Vue组件里的定时器要怎么销毁

如果有多个定时器,可以在data中 创建一个对象timer,给每个定时器取个名字一一映射在timer中,当组件销毁时,触发生命周期函数beforeDestroy,在beforeDestroy中循环timer对象,

for(let timerItem in this.timer) {
  clearInterval(timerItem)
}

如果只有一个定时器

const timer = setInterval(() =>{}, 500);
this.$once('hook:beforeDestroy', () => {
 clearInterval(timer);
})

十五、Vue组件使用流程

1、在components 目录创建两个文件,一个common目录用于存放通用组件,一个content目录,用于存放该项目组件,新建组件文件MyConponents.vue。script export default {}
2、在需要的页面(组件)导入,import MyComponents from ‘@/components/content/MyConponents.vue’
3、将组件注入到vue的components属性上,components: { MyComponents }
4、在tempalte的视图View上使用组件,< MyCompoent />或者< my-component />

十六、Vue 中 solt 以及 solt 作用域插槽的使用方式

插槽的使用

slot 可以用来接受组件标签包裹的内容,给solt 标签添加 name 属性,可以标识要替换那一个插槽

// 子组件
<template>
  <div class="tab-bar-item" @click="itemClick">
    <div ><slot name="tab-icon" ></slot></div>
    <div ><slot name="tab-icon-action"></slot></div>
    <div ><slot name="tab-text"></slot></div>
  </div>
</template>
// 父组件
<template>
  <tab-bar>
    <tab-bar-item path="/Home" activeColor="#1296db">
        <img slot="tab-icon" src="~assets/img/tabbar/shouye.png" alt="">
        <img slot="tab-icon-action" src="~assets/img/tabbar/shouye-active.png" alt="">
        <div slot="tab-text">首页</div>
    </tab-bar-item>
  </tab-bar>
</template>
作用域插槽的使用

作用域插槽其实就是带数据的插槽,父组件接收来自子组件的 slot 标签上通过 v-bind
绑定进而传递过来的数 据,父组件通过 scope 来进行接受子组件传递过来的数据

<el-table-column label="序号" width="50">
  <template slot-scope="scope">
    <span style="margin-left: 10px">{{ scope.$index + 1 }}</span>
  </template>
</el-table-column>

十七、什么是动态组件,如何实现一个动态组件

在一个多标签的界面里,有时会需要在组件之间进行动态切换,例如一个tab的切换功能。
vue要实现这个功能,通畅用两种方式:
一种是使用vue自身的内置组件,通过 Vue 的 < component > 元素加一个特殊的 is attribute 来实现:

<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>
在上述示例中,currentTabComponent 可以包括已注册组件的名字,或一个组件的选项对象

另一种是使用v-if进行判断

十八、Vue 该如何实现组件缓存?

在面对要切换组件时,动态组件在切换的过程中,组件的实例都是重新创建的,而我们需要保留组件的状态,为了解决这个问题,需要使用到 vue 中内置组件< keep-alive >
< keep-alive >< /keep-alive > 包裹动态组件时,会缓存不活动的组件实例,主要用于保留组
件状态或避免重新渲染
简答的说: 比如有一个列表和一个详情,那么用户就会经常执行打开详情=>返回列表=>打
开详情…这样的话列表和详情都是一个频率很高的页面,那么就可以对列表组件使用
< keepalive >< /keep-alive >进行缓存,这样用户每次返回列表的时候,都能从缓存中快速渲染,而不是重新渲染

十九、跟 keep-alive 有关的生命周期是哪些?

在被 keep-alive 包含的组件/路由中,会多出两个生命周期的钩子activated 与 deactivated。
activated 钩子:
在在组件第一次渲染时会被调用,之后在每次缓存组件被激活时调用。
activated钩子调用时机:
第一次进入缓存路由/组件,在 mounted 后面,beforeRouteEnter 守卫传给 next 的回调函数之前调用,并且不会触发beforeCreate created beforeMount mounted 这些钩子函数
deactivated钩子:
组件被停用(离开路由)时调用:deactivated 钩子调用时
机:使用 keep-alive 就不会调用 beforeDestroy(组件销毁前钩子)和 destroyed(组件销毁)

二十、Vue 常用的修饰符都有哪些?

表单修饰符

1.lazy
在我们填完信息,光标离开标签的时候,才会将值赋予给value,也就是在change事件之后再进行信息同步

<input type="text" v-model.lazy="value">
<p>{{value}}</p>

2.trim
自动过滤用户输入的首空格字符,但中间的空格不会过滤

<input type="text" v-model.trim="value">

3.number
自动将用户的输入值转为数值类型,但如果这个值无法被parseFloat解析,则会返回原来的值

<input v-model.number="age" type="number">
事件修饰符

1.stop
阻止了事件冒泡,相当于调用了event.stopPropagation方法
event.stopPropagation() 方法阻止事件冒泡到父元素,阻止任何父事件处理程序被执行。

<div @click="func(2)">
  <button @click.stop="func(1)">ok</button>
</div>

func(number) {
  console.log(number)
}
//只输出1

2.prevent
阻止了事件的默认行为,相当于调用了event.preventDefault方法
preventDefault() 方法阻止元素发生默认的行为(例如,当点击提交按钮时阻止对表单的提交)。

<html>
<head>
<script type="text/javascript" src="/jquery/jquery.js"></script>
<script type="text/javascript">
$(document).ready(function(){
  $("a").click(function(event){
    event.preventDefault();
  });
});
</script>
</head>
<body>
<a href="http://w3school.com.cn/">W3School</a>
<p>preventDefault() 方法将防止上面的链接打开 URL。</p>
</body>
</html>

3.self
只当在 event.target 是当前元素自身时触发处理函数

<div v-on:click.self="doThat">...</div>

使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用
v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击
4.once
绑定了事件以后只能触发一次,第二次就不会触发

<button @click.once="shout(1)">ok</button>

5.capture
使事件触发从包含这个元素的顶层开始往下触发


<div @click.capture="func(1)">
  obj1
  <div @click.capture="func(2)">
    obj2
    <div @click="func(3)">
      obj3
      <div @click="func(4)">
        obj4
      </div>
    </div>
  </div>
</div>

func(number) {
  console.log(number)
}
结果是  1 2 4 3

6.native
让组件监听根元素的原生事件

<my-component v-on:click.native="doSomething"></my-component>

使用.native修饰符来操作普通HTML标签是会令事件失效的

鼠标按钮修饰符

left 左键点击
right 右键点击
middle 中键点击

<button @click.left="func(1)">left</button>
<button @click.right="func(1)">right</button>
<button @click.middle="func(1)">middle</button>

二十一、Vue 常用的指令都有哪些?并且说明其作用

1、v-model 多用于表单元素实现双向数据绑定(同 angular 中的 ng-model)
2、v-for 格式: v-for=“字段名 in(of) 数组 json” 循环数组或 json(同 angular 中的 ngrepeat),需要注意从 vue2 开始取消了$index
3、v-show 显示内容 (同 angular 中的 ng-show)
4、v-hide 隐藏内容(同 angular 中的 ng-hide)
5、v-if 显示与隐藏 (dom 元素的删除添加 同 angular 中的 ng-if 默认值为 false)velse-if 必须和 v-if 连用 v-else 必须和 v-if 连用 不能单独使用 否则报错 模板编译错误
6、v-bind 动态绑定 作用: 及时对页面的数据进行更改
7、v-on:click 给标签绑定函数,可以缩写为@,例如绑定一个点击函数 函数必须写在
methods 里面
8、v-text 解析文本
9、v-html 解析 html 标签
10、v-bind:class 三种绑定方法 1、对象型 ‘{red:isred}’ 2、三元型 ‘isred?“red”:“blue”’ 3、
数组型 ‘[{red:“isred”},{blue:“isblue”}]’
11、v-once 进入页面时 只渲染一次 不在进行渲染
12、v-cloak 防止闪烁
13、v-pre 把标签内部的元素原位输出

二十二、自定义指令

这一部分官网有很详细的介绍
除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令。
当页面加载时,该元素将获得焦点 (注意:autofocus 在移动版 Safari 上不工作)。事实上,只要你在打开这个页面后还没点击过任何内容,这个输入框就应当还是处于聚焦状态。现在让我们用指令来实现这个功能:

// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})

如果想注册局部指令,组件中也接受一个 directives 的选项

directives: {
  focus: {
    // 指令的定义
    inserted: function (el) {
      el.focus()
    }
  }
}

然后你可以在模板中任何元素上使用新的 v-focus property,如下:

<input v-focus>
钩子函数

一个指令定义对象可以提供如下几个钩子函数 (均为可选):

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。

  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。

  • unbind:只调用一次,指令与元素解绑时调用。

接下来我们来看一下钩子函数的参数 (即 el、binding、vnode 和 oldVnode)。

钩子函数参数

指令钩子函数会被传入以下参数:

  • el:指令所绑定的元素,可以用来直接操作 DOM。
  • binding:一个对象,包含以下 property:

name:指令名,不包括 v- 前缀。
value:指令的绑定值,例如:v-my-directive=“1 + 1” 中,绑定值为 2。
oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
expression:字符串形式的指令表达式。例如 v-my-directive=“1 + 1” 中,表达式为 “1 + 1”。
arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 “foo”。
modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。

  • vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
  • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
    除了 el 之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行。

这是一个使用了这些 property 的自定义钩子样例:

<div id="hook-arguments-example" v-demo:foo.a.b="message"></div>
Vue.directive('demo', {
  bind: function (el, binding, vnode) {
    var s = JSON.stringify
    el.innerHTML =
      'name: '       + s(binding.name) + '
'
+ 'value: ' + s(binding.value) + '
'
+ 'expression: ' + s(binding.expression) + '
'
+ 'argument: ' + s(binding.arg) + '
'
+ 'modifiers: ' + s(binding.modifiers) + '
'
+ 'vnode keys: ' + Object.keys(vnode).join(', ') } }) new Vue({ el: '#hook-arguments-example', data: { message: 'hello!' } })

Vue 面试题(持续更新)_第7张图片

动态指令参数

指令的参数可以是动态的。例如,在 v-mydirective:[argument]=“value” 中,argument 参数可以根据组件实例数据进行更新!这使得自定义指令可以在应用中被灵活使用。

例如你想要创建一个自定义指令,用来通过固定布局将元素固定在页面上。我们可以像这样创建一个通过指令值来更新竖直位置像素值的自定义指令:

<div id="baseexample">
  <p>Scroll down the page</p>
  <p v-pin="200">Stick me 200px from the top of the page</p>
</div>
Vue.directive('pin', {
  bind: function (el, binding, vnode) {
    el.style.position = 'fixed'
    el.style.top = binding.value + 'px'
  }
})

new Vue({
  el: '#baseexample'
})

这会把该元素固定在距离页面顶部 200 像素的位置。但如果场景是我们需要把元素固定在左侧而不是顶部又该怎么办呢?这时使用动态参数就可以非常方便地根据每个组件实例来进行更新。

<div id="dynamicexample">
  <h3>Scroll down inside this section ↓</h3>
  <p v-pin:[direction]="200">
    I am pinned onto the page at 200px to the left.
  </p>
</div>
Vue.directive('pin', {
  bind: function (el, binding, vnode) {
    el.style.position = 'fixed'
    var s = (binding.arg == 'left' ? 'left' : 'top')
    el.style[s] = binding.value + 'px'
  }
})

new Vue({
  el: '#dynamicexample',
  data: function () {
    return {
      direction: 'left'
    }
  }
})

二十三、指令 v-el 和 v-ref

通过v-el我们可以获取到DOM对象。
通过v-ref获取到整个组件(component)的对象。

二十四、v-show 和 v-if 指令的共同点和不同点?

相同点:

v-show 和 v-if 都能控制元素的显示和隐藏。

不同点:
  • 实现本质方法不同
    v-show 本质就是通过设置 css 中的 display 设置为 none,控制隐藏
    v-if 是动态的向 DOM 树内添加或者删除 DOM 元素
  • 性能
    v-show 只编译一次,后面其实就是控制 css
    v-if 不停的销毁和创建,故 vshow 性能更好

二十五、为什么避免 v-if 和 v-for 用在一起

v-for 比 v-if 具有更高的优先级,两者一起使用时,v-if判断为否的也会被运行。将v-if放到外层先进行判断,就不会在 v-if 为否的时候运算 v-for。

二十六、watch、methods 和 computed 的区别?

computed
计算属性将被混入到 Vue 实例中,所有 getter 和 setter 的 this 上下文自动地绑定为 Vue 实例
methods
methods 将被混入到 Vue 实例中。可以直接通过 VM 实例访问这些方法,或者在指令表达式中使用。方法中的 this 自动绑定为 Vue 实例。
watch
观察和响应 Vue 实例上的数据变动,一个对象,键是需要观察的表达式,值是对应回调函数,值也可以是方法名,或者包含选项的对象,Vue 实例将会在实例化时调,$watch(),遍历 watch 对象的每一个属性

三者的加载顺序

1.computed 是在 HTML DOM 加载后马上执行的,如赋值;(属性将被混入到 Vue 实
例)
2.methods 则必须要有一定的触发条件才能执行,如点击事件,watch 呢?它用于观
察 Vue 实例上的数据变动,
3.默认加载的时候
先 computed 再 watch,不执行 methods;
4.触发某一事件后
先 computed 再 methods 再到 watch,computed 属性 vs method 方,computed 计算属性是基于它们的依赖进行缓存的

二十七、怎么在 watch 监听开始之后立即被调用?

在选项参数中指定 immediate: true 将立即以表达式的当前值触发回调

二十八、watch 怎么深度监听对象变化?

有个原则监听谁,写谁的名字,然后是对应的执行函数, 第一个参数为最新的改变值,第二
个值为上一次改变的值, 注意: 除了监听 data,也可以监听计算属性 或者一个 函数的计算结果
启用深度监听对象

watch:{
 a:{
   handler:function(val,oldval){ 
 },
 deep:true
 }
}

二十九、computed 中的属性名和 data 中的属性名可以相同吗?

不能同名,因为不管是 computed 属性名还是 data 数据名还是 props 数据名都会被挂载在
vm 实例上,因此这三个都不能同名

三十、什么是 Vue 的计算属性

在模板中放入太多的逻辑会让模板过重且难以维护,可读性差。这时在需要对数据进行复杂处理,并且可能多次使用的情况下,就可以使用计算属性或者methods方法的方式
1、计算属性内部 this 指向 vm 实例
2、在 template 调用时,直接写计算属性名即可
3、常用的是 getter 方法,获取数据,也可以使用 set 方法改变数据
4、相较于 methods,不管依赖的数据变不变,methods 都会重新计算,但是依赖数据不变的时候 computed 从缓存中获取,不会重新计算

三十一、Vue 中 key 值的作用是什么?

Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果
数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处
每个元素,并且确保它在特定索引下显示已被渲染过的每个元素,key 的作用主要是为了高效的更新虚拟 DOM

三十二、Vue-loader 是什么?使用它的用途有哪些?

三十三、Vue 中怎么自定义过滤器

Vuex

三十四、你是怎么认识 Vuex 的?

vuex 可以理解为一种开发模式或框架。通过状态(数据源)集中管理驱动组件的变化(好比 spring 的 IOC 容器对 bean 进行集中管理)
1、应用级的状态集中放在 store 中
2、改变状态的方式是提交 mutations,这是个同步的事务
3、异步逻辑应该封装在 action 中

三十五、Vuex 的 5 个核心属性是什么?

分别是 State、 Getter、Mutation 、Action、 Module
1、state
state 为单一状态树,在 state 中需要定义我们所需要管理的数组、对象、字符串等等,
只有在这里定义了,在 vue.js 的组件中才能获取你定义的这个对象的状态
2、getter
getter 有点类似 vue.js 的计算属性,当我们需要从 store 的 state 中派生出一些状态,
那么我们就需要使用 getter,getter 会接收 state 作为第一个参数,而且 getter 的返回值会
根据它的依赖被缓存起来,只有 getter 中的依赖值(state 中的某个需要派生状态的值)发
生改变的时候才会被重新计算
3、mutation
更改 store 中 state 状态的唯一方法就是提交 mutation,就很类似事件。每个 mutation
都有一个字符串类型的事件类型和一个回调函数,我们需要改变 state 的值就要在回调函
数中改变。我们要执行这个回调函数,那么我们需要执行一个相应的调用方法
store.commit
4、action
action 可以提交 mutation,在 action 中可以执行 store.commit,而且 action 中可以有
任何的异步操作。在页面中如果我们要嗲用这个 action,则需要执行 store.dispatch
5、module
module 其实只是解决了当 state 中很复杂臃肿的时候,module 可以将 store 分割成
模块,每个模块中拥有自己的 state、mutation、action 和 getter

三十六、Vuex 的出现解决了什么问题?

主要解决了以下两个问题
1、多个组件依赖于同一状态时,对于多层嵌套的组件的传参将会非常繁琐,并且对于兄
弟组件间的状态传递无能为力
2、来自不同组件的行为需要变更同一状态。以往采用父子组件直接引用或者通过事件来
变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码

三十七、简述 Vuex 的数据传递流程

当组件进行数据修改的时候我们需要调用 dispatch 来触发 actions 里面的方法。actions 里
面的每个方法中都会有一个
1、commit 方法,当方法执行的时候会通过 commit 来触 mutations 里面的方法进行数据
的修改
2、mutations 里面的每个函数都会有一个 state 参数,这样就可以在 mutations 里面进行
state 的数据修改 ,当数据修改完毕后,会传导给页面,页面的数据也会发生改变

三十八、Vuex 的 Mutation 和 Action 之间的区别是什么?

  • 流程顺序
    将“相应视图—>修改 State”拆分成两部分,视图触发 Action,Action 再触发 Mutation
  • 角色定位
    Mutation:专注于修改 State,理论上是修改 State 的唯一途径
    Action:业务代码、异步请求
  • 限制
    Mutation:必须同步执行
    Action:可以异步,但不能直接操作 State

Vue-router

三十九、Vue-router 是干什么的,原理是什么?

Vue-router 是 Vue.js 官方的路由插件,它和 vue.js 是深度集成的,适合用于构建单页面应用,vue 的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来,传统的页面应用,是用一些超链接来实现页面切换和跳转的,在 vue-router 单页面应用中,则是路径之间的切换,也就是组件的切换,路由模块的本质 就是建立起 url 和页面之间的映射关系
1、利用 URL 中的 hash(“#”)
2、利用 History interface 在 HTML5 中新增的方法

四十、配置路由的流程

1、安装
npm install --save vue-router
2、引用
import VueRouter from ‘vue-router’
3、配置路由

const routes = [
  {
    path: '/Home',
    component: Home
  }
]

const router = new Router({
  routes
  // mode: 'history'
})

new Vue({
  el: '#app',
  router,
  components: { App },
  template: ''
})

4、视图加载的位置
默认 App.vue 文件中加< router-view >< /router-view >
5、路由跳转

四十一、路由之间是怎么跳转的?有哪些方式?

1、< router-link to=“需要跳转到页面的路径”>
2、this.$ router.push()跳转到指定的 url,并在 history 中添加记录,点击回退返回到上一个
页面
3、this.$ router.replace()跳转到指定的 url,但是 history 中不会添加记录,点击回退到上上
个页面
4、this.$ touter.go(n)向前或者后跳转 n 个页面,n 可以是正数也可以是负数
5、this.$ router.back() 后退
6、this.$ router.forward() 前进

四十二、导航守卫

“导航”表示路由正在发生改变。

全局前置守卫

你可以使用 router.beforeEach 注册一个全局前置守卫:

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // ...
})

当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。

  • to: Route: 即将要进入的目标 路由对象
  • from: Route: 当前导航正要离开的路由
  • next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
    next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
    next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
    next('/') 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: ‘home’ 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。
    next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
全局解析守卫

在 2.5.0+ 你可以用 router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。

全局后置钩子

你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:

router.afterEach((to, from) => {
  // ...
})
路由独享的守卫

你可以在路由配置上直接定义 beforeEnter 守卫:

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})
组件内的守卫
const Foo = {
  template: `...`,
  beforeRouteEnter(to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

beforeRouteEnter 守卫 不能 访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。

完整的导航解析流程

导航被触发。
在失活的组件里调用 beforeRouteLeave 守卫。
调用全局的 beforeEach 守卫。
在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
在路由配置里调用 beforeEnter。
解析异步路由组件。
在被激活的组件里调用 beforeRouteEnter。
调用全局的 beforeResolve 守卫 (2.5+)。
导航被确认。
调用全局的 afterEach 钩子。
触发 DOM 更新。
调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

四十三、路由传值的方式有哪几种

Vue-router 传参可以分为两大类,分别是编程式的导航 router.push 和声明式的导航

编程式的导航 router.push
  • 字符串:直接传递路由地址,但是不能传递参数
    this.$router.push(“home”)
  • 对象:
    命名路由 这种方式传递参数,目标页面刷新会报错
    this.$ router.push({name:“news”,params:{userId:123})
    查询参数 和 name 配对的式 params,和 path 配对的是 query
    this.$ router.push({path:"/news’,query:{uersId:123})
    接收参数
    this.$ route.query
声明式导航

字符串 < router-link to:“news”>< /router-link>
命名路由 < router-link :to:"{name:‘news’,params:{userid:1111}}">< /route-link>
查询参数 < router-link :to="{path:’/news’,query:{userId:1111}}">< /router-link>

四十四、JQuery 和 params 之间的区别是什么?

1、query 要用 path 来引入,params 要用 name 来引入
2、接收参数时,分别是 this.$ route.query.name 和 this.$ route.params.name(注意:是
$ route 而不是$ router
3、query 更加类似于我们 ajax 中 get 传参,params 则类似于 post,前者在浏览器的地址
栏中显示,params 不显示
4、params 传值一刷新就没了,query 传值刷新还存在

四十五、$ route 和$ router 的区别是什么?

$ route 是“路由信息对象”,包括 path,params,hash,query,fullPath,matched,
name 等路由信息参数
$ router 为 VueRouter 的实例,相当于一个全局的路由器对象,里面含有很多属性和子对
象,例如 history 对象,经常用的跳转链接就可以用 this.router.push 会往 history 栈中添加一个
新的记录。返回上一个 history 也是使用$ router.go 方法

四十六、请说出路由配置项常用的属性及作用

1、path : 跳转路径
2、component : 路径相对于的组件
3、name:命名路由
4、children:子路由的配置参数(路由嵌套)
5、props:路由解耦
6、redirect : 重定向路由

四十七、Vue 怎么实现跨域

1、什么是跨域
跨域指浏览器不允许当前页面的所在的源去请求另一个源的数据。源指协议,端口,域名。只要这3 个中有一个不同就是跨域
2、使用 vue-cli 脚手架搭建项目时 proxyTable 解决跨域问题
打开 config/index.js,在 proxyTable 中添写如下代码:
proxyTable: {
‘/api’: { //使用"/api"来代替"http://f.apiplus.c"
target: ‘http://f.apiplus.cn’, //源地址
changeOrigin: true, //改变源
pathRewrite: {
‘^/api’: ‘http://f.apiplus.cn’ //路径重写
}
3、使用 CORS(跨域资源共享)
3.1)前端设置,vue 设置 axios 允许跨域携带 cookie(默认是不带 cookie)
axios.defaults.withCredentials = true;
3,2)后端设置:
3.2.1)跨域请求后的响应头中需要设置
3.2.2)Access-Control-Allow-Origin 为发起请求的主机地址
3.2.3)Access-Control-Allow-Credentials,当它被设置为 true 时,允许跨域
带 cookie,但此时 Access-Control- Allow-Origin 不能为通配符*
3.2.4)Access-Control-Allow-Headers,设置跨域请求允许的请求头
3.2.5)Access-Control-Allow-Methods,设置跨域请求允许的请求方式

四十八、Vue 中动画如何实现

1、哪个元素需要动画就给那个元素加 transition 标签
2、进入时 class 的类型分为以下几种
< name>-enter < name>-enter-active < name>-enter-to
3、离开时 class 的类型分为以下几种
< name>-leave < name>-leave-active < name>-leave-to
如果需要一组元素发生动画需要用标签< transition-group>< transition-group>

四十九、Vue2.0 兼容 IE 哪个版本以上吗?

不支持 ie8 及以下,部分兼容 ie9 ,完全兼容 10 以上,因为 vue 的响应式原理是基于 es5
的 Object.defineProperty(),而这个方法不支持 ie8 及以下

五十、Vue 项目优化的解决方案都有哪些?

1、 使用 mini-css-extract-plugin 插件抽离 css
2、 配置 optimization 把公共的 js 代码抽离出来
3、 通过 webpack 处理文件压缩
4、 不打包框架、库文件,通过 cdn 的方式引入
5、 小图片使用 base64
6、 配置项目文件懒加载
7、 UI 库配置按需加载
8、 开启 Gzip 压缩

五十一、怎样理解 Vue 的单向数据流?

1、数据从父级组件传递给子组件,只能单向绑定
2、子组件内部不能直接修改从父级传递过来的数据
3、所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新
会向下流动到子组件中,但是反过来则不行,这样会防止从子组件意外改变父级组件的状态,
从而导致你的应用的数据流向难以理解。
4、每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值,这意味着
你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警

5、子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件
修改

五十二、说说你对 SPA 单页面的理解,它的优缺点分别是什么?

单页 Web 应用 (single-page application 简称为 SPA)
优点:
1,1)无刷新界面,给用户体验原生的应用感觉
1,2)节省原生(android 和 ios)app 开发成本
1,3)提高发布效率,无需每次安装更新包
1,4)容易借助其他知名平台更有利于营销和推
1,5)符合 web2.0 的趋势
缺点:
1) 效果和性能确实和原生的有较大差距
2) 各个浏览器的版本兼容性不一样
3) 业务随着代码量增加而增加,不利于首屏优化
4) 某些平台对 hash 有偏见,有些甚至不支持 pushstate
5) 不利于搜索引擎抓取

五十三、scoped 属性的原理

什么是 scoped

在 Vue 组件中,为了使样式私有化(模块化),不对全局造成污染,可以在 style 标
签上添加 scoped 属性以表示它的只属于当下的模块,局部有效。
2、scoped 的实现原理:
Vue 中的 scoped 属性的效果主要通过 PostCSS 转译实现,如下是转译前的 Vue 代码:

<template>
  <div>Vue.js scoped</div>
</template>
<style scoped>
  .scoped {font-size:14px;}
</style>

浏览器渲染后的代码:

<div data-v-fed36922>Vue.js scoped</div>
.scoped[data-v-fed36922]{font-size:14px;}

即:PostCSS 给所有 dom 添加了一个唯一不重复的动态属性,然后,给 CSS 选择器额外添加一个对应的属性选择器来选择该组件中 dom,这种做法使得样式私有化

五十四、请说出 Vue.cli 项目中 src 目录每个文件夹和文件的用法?

1、assets 文件夹是放静态资源
2、components 是放组件
3、router 是定义路由相关的配置
4、view 视图
5、app.vue 是一个应用主组件
6、main.js 是入口文件

五十五、使用 Vue 的时候一下加载造成页面卡顿,该如何解决?

使用路由懒加载

像 vue 这种单页面应用,如果没有应用懒加载,运用 webpack 打包后的文件将会异常的大,造成进入首页时, 需要加载的内容过多,时间过长,会出现长时间的白屏,即使做了 loading 也是不利于用户体验,而运用懒加载 则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时。

const Foo = () => import('./Foo.vue')

const router = new VueRouter({
  routes: [{ path: '/foo', component: Foo }]
})

你可能感兴趣的:(前端学习,vue.js,前端)