vue面试

199 阅读18分钟

Vue 请说下封装vue组件的过程 首先:组件可以提升整个项目的开发效率。能够把页面抽象成多个独立的模块,解决了我们传统项目开发:效率低、难维护、复用性等问题。 然后:使用Vue.extend方法创建一个组件,然后使用Vue.component方法注册组件。子组件需要数据,可以在props中接受定义。而子组件修改好数据后,想把数据传递给父组件。可以采用emit方法。 vue基本操作指令: v-once:该指令表示元素和组件只渲染一次,不会随着数据的改变而改变 v-html:该指令后面往往会跟上一个string类型,会将string的html解析出来并且进行渲染 v-text:v-text作用和Mustache一致,v-text通常情况下,接受一个string类型 v-pre:v-pre用于跳过这个元素和它子元素的编译过程,用于显示原本的Mustache语法 v-bind:v-bind用于绑定一个或多个属性值,或者向另一个组件传递props值 v-on:用一个监听按钮的点击事件 v-if:v-if当条件为false时,压根不会有对应的元素在DOM中,v-else-if那么,v-else否则 v-show:v-show当条件为false时,仅仅是将元素的display属性设置为none而已。 (当需要在显示与隐藏之间切片很频繁时,使用v-show,当只有一次切换时,通过使用v-if) v-for:v-for遍历数组,v-for遍历对象 key的使用:官方推荐我们在使用v-for时,给对应的元素或组件添加上一个:key属性 key的作用主要是为了高效的更新虚拟DOM

v-model:Vue中使用v-model指令来实现表单元素和数据的双向绑定。
  对象方法 v-bind:class="{'orange': isRipe, 'green': isNotRipe}"
  数组方法 v-bind:class="[class1, class2]"
  行内 v-bind:style="{color: color, fontSize: fontSize+'px' }"

v-model其实是一个语法糖,它的背后本质上是包含两个操作:
  1.v-bind绑定一个value属性
  2.v-on指令给当前元素绑定input事件
v-model的修饰符:
  lazy修饰符:lazy修饰符可以让数据在失去焦点或者回车时才会更新
  number修饰符:number修饰符可以让在输入框中输入的内容自动转成数字类型
  trim修饰符:trim修饰符可以过滤内容左右两边的空格
Vue的双向数据绑定原理是什么?
  vue.js是采用数据劫持发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
  具体步骤:
    第一步:需要observe的数据对象进行递归遍历,包括子属性对象的属性,都加上setter和getter,这样的话,给这个对象的某个值赋值,就会触发setter,那么就能监听到了数据变化。
    第二步:compile解析模块指令,将模块中的变量替换成数据,然后初始化渲染页面视图,并将每个指力对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图。
    第三步:Watcher订阅者是Observer和Compile之间通信的桥梁,主要做的事情是:
      1、在自身实例化时往属性订阅者(dep)里面添加自己
      2、自身必须有一个update()方法
      3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退
    第四步:MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化->视图跟新;视图交互变化(input)->数据model变更的双向绑定效果

v-bind的使用 对象: 用法一:直接通过{}绑定一个类

Hello World

用法二:也可以通过判断,传入多个值

Hello World

用法三:和普通的类同时存在,并不冲突 注:如果isActive和isLine都为true,那么会有title/active/line三个类

Hello World

用法四:如果过于复杂,可以放在一个methods或者computed中 注:classes是一个计算属性

Hello World

数组:
  用法一:直接通过{}绑定一个类
  <h2 :class="['active']">Hello World</h2>
  用法二:也可以传入多个值
  <h2 :class=“[‘active’, 'line']">Hello World</h2>
  用法三:和普通的类同时存在,并不冲突
  注:会有title/active/line三个类
  <h2 class="title" :class=“[‘active’, 'line']">Hello World</h2>
  用法四:如果过于复杂,可以放在一个methods或者computed中
  注:classes是一个计算属性
  <h2 class="title" :class="classes">Hello World</h2>

组件之间的传值
  (父子之间传递数据)通过props父组件向子组件传递数据(通过v-bind绑定):vue
  (子向父组件传递数据)子组件向父组件传递数据$emit发起数据
  兄弟之间传递数据:1、$dispatch用于向上级派发事件。2、$broadcast用于向下级广播事件(但是在vue2.0已经取消呢)
    1,一般可以使用子组件a传递给父组件,再由父组件传递给子组件b。但是一般较为麻烦,
    2,使用bus作为中介,来传递ab组件之间的值
    3、用sessionStorage实现页面之间的数据传输 存取方法
      sessionStorage主要含几种方法:

   //页面A:存放一个简单的字符串    sessionStorage.obj = '123';    //页面B:取到给obj    var str = sessionStorage.obj;    //类型地:    sessionStorage.setItem(key,value);    sessionStorage.gettItem(key,value);    sessionStorage.remove(key); 4、通过vuex的使用

组件之间的通信:
  父组件通过this.$children(可以获取全部子组件的方法和属性)。this.$refs(获取相应的子组件方法和属性)
  子组件通过this.$parent(访问父组件的属性和方法)  

vue中的ref的作用是什么?
  1/el-table中ref的用法:普通用法: ref=“refs”  查找时使用this.$refs.refs
  2/组件中ref的用法:<child ref="childRef"></child>查找时this.$refs.childRef找到的是子组件
    如果子组件里el-table不是唯一组件,this.$refs.childRef.$children[0]找到table组件

props的跳转有两种方式
路由跳转方式
1,<router-link to='home'> router-link标签会渲染为<a>标签,咋填template中的跳转都是
2,另一种是编程是导航 也就是通过js跳转 比如 router.push('/home')复制代码

MVVM
  M - Model,Model 代表数据模型,也可以在 Model 中定义数据修改和操作的业务逻辑
  V - View,View 代表 UI 组件,它负责将数据模型转化为 UI 展现出来
  VM - ViewModel,ViewModel 监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步 View 和 Model 的对象,连接 Model 和 View复制代码

computed和watch有什么区别?
computed:
    1. computed是计算属性,也就是计算值,它更多用于计算值的场景
    2. computed具有缓存性,computed的值在getter执行后是会缓存的,只有在它依赖的属性值改变之后,下一次获取computed的值时才会重新调用对应的getter来计算
    3. computed适用于计算比较消耗性能的计算场景复制代码
    watch:
    1. 更多的是「观察」的作用,类似于某些数据的监听回调,用于观察props $emit或者本组件的值,当数据变化时来执行回调进行后续操作
    2. 无缓存性,页面重新渲染时值不变化也会执行复制代码
小结:
1. 当我们要进行数值计算,而且依赖于其他数据,那么把这个数据设计为computed
2. 如果你需要在某个数据变化时做一些事情,使用watch来观察这个数据变化复制代码

key
key是为Vue中的vnode标记的唯一id,通过这个key,我们的diff操作可以 更准确、更快速
准确:
    如果不加key,那么vue会选择复用节点(Vue的就地更新策略),导致之前节点的状态被保留下来,会产生一系列的bug
快速:
    key的唯一性可以被Map数据结构充分利用

组件中的data为什么是函数?
  Vue 组件可能存在多个实例,如果使用对象的形式定义data, 则会导致它们公用一个data对象,那么状态变更会影响所有组件实例,这是不合理的;
  采用函数的形式定义,在initData时会将其作为工厂函数返回全新的data对象,有效的避免了多个实例之间的状态污染问题;
  而在Vue根实例创建过程中不存在该限制,也是因为根实例只能有一个,不需要担心这种情况
为什么组件中的data必须是一个函数,然后return一个对象,而new Vue实例里,data可以直接是一个对象?
// datadata() {
  return {
	message: "子组件",
	childName:this.name
  }
}

// new Vue
new Vue({
  el: '#app',
  router,
  template: '<App/>',
  components: {App}
})复制代码
因为组件是用来复用的,JS里对象是引用关系,这样作用域没有隔离,而new Vue的实例,是不会被复用的,因此不存在引用对象问题

Class 与 Style 如何动态绑定?
Class 可以通过对象语法和数组语法进行动态绑定:
对象语法
<div v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>

data: {
  isActive: true,
  hasError: false
}复制代码
数组语法
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>

data: {
  activeClass: 'active',
  errorClass: 'text-danger'
}复制代码
Style 也可以通过对象语法和数组语法进行动态绑定:
对象语法
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

data: {
  activeColor: 'red',
  fontSize: 30
}复制代码
数组语法
<div v-bind:style="[styleColor, styleSize]"></div>

data: {
  styleColor: {
     color: 'red'
   },
  styleSize:{
     fontSize:'23px'
  }
}复制代码

vue的单项数据流 所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解 额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改 有两种常见的试图改变一个 prop 的情形 : 这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用 在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值: props: ['initialCounter'], data: function () { return { counter: this.initialCounter } }复制代码 这个 prop 以一种原始的值传入且需要进行转换 在这种情况下,最好使用这个 prop 的值来定义一个计算属性 props: ['size'], computed: { normalizedSize: function () { return this.size.trim().toLowerCase() } }复制代码

router的使用: 安装npm install vue-router --save 在模块化工程中使用它(因为是一个插件, 所以可以通过Vue.use()来安装路由功能) 第一步:导入路由对象,并且调用 Vue.use(VueRouter) {import vue from 'vue'} 第二步:创建路由实例,并且传入路由映射配置 {import VUeRouter from 'vue-router'} 第三步:在Vue实例中挂载创建的路由实例 {Vue.use(VUeRouter)} 使用vue-router的步骤: 第一步: 创建路由组件 第二步: 配置路由映射: 组件和路径映射关系 第三步: 使用路由: 通过和

改变路径的方式有两种: URL的hash(默认情况下, 路径的改变使用的URL的hash.) HTML5的history (想要使用,在router的映射中使用mode:'history')

路由代码跳转方式(2种) 第一种: 使用首页 第二种:执行对应的JavaScript代码 <button @click="linkToHome">首页 linkToHome() {
this.$router.push('./home') },

动态路由的使用: 1、在router/index种加入 {path: '/user/:userId',(对应的组件) component: User} 2、在app.vue中写入用户 data() { return { userId: 'lisi' } }, 3、如果想要获取后面绑定的对象(zhangsan) 在对应的组件

{{userId}}

computed: { userId() { return this.$route.params.userId } }

routeroute和router的区别(所有的组件,继承于vue的原型) router:VueRouter实例,想要导航到不同URL,则使用router: 为VueRouter实例,想要导航到不同URL,则使用router.push方法 $route 为当前router跳转对象里面可以获取name、path、query、params等

嵌套路由的使用: 第一步: 创建路由组件 第二步: 配置路由映射: 组件和路径映射关系 (在对应的父组件下面children写上子组件) 第三步: 使用路由: 通过和 (在对应的父组件下面写上)

传递参数主要有两种类型: params和query params的类型:(动态路由的形式) 配置路由格式: /router/:id 传递的方式: 在path后面跟上对应的值 传递后形成的路径: /router/123, /router/abc

query的类型:(对象)
  配置路由格式: /router, 也就是普通配置
  传递的方式: 对象中使用query的key作为传递方式
  传递后形成的路径: /router?id=123, /router?id=abc

Object.defineProperty(obj,'age',18) 给对象obj增加一个属性

vue-router全局导航守卫的使用: 监听跳转的过程(在某一个函数里面做你想要做的事情)

(前置守卫guard)/(前置钩子)
router.beforeEach((to, from, next) =>{

})
to:即将要进入的目标的路由对象
from:当前导航即将要离开的路由对象
next:调用该方法后,才能进入下一个钩子

(后置钩子)不需要主动调用next()
router.afterEach((to, from) =>{

})

路由独享的守卫.
组件内的守卫.

keep-alive遇见vue-router 1、router-view 也是一个组件,如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存 2、keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染 它们有两个非常重要的属性: include - 字符串或正则表达,只有匹配的组件会被缓存 exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存 这两个函数,只有该组件被保持了状态使用了keep-alive时,才是有效的 activated(){ 函数的使用:当你的组件处于活跃状态 } deactivated() {

}

router.addRoutes的使用: router.addRoutes(routes: Array) 动态添加更多的路由规则。参数必须是一个符合 routes 选项要求的数组。 1、直接单独配置: export const asyncRoute = [ { path: '/users', name: 'Users', component: () => import('@/views/Users'), mets: { title: '用户管理', icon: 'user' } } ]

2、直接单独配置,然后确定用户是否拥有该权限,利用数组方法concat拼接到其他路由配置当中。
  let rs = routes
  if(+id === 1) { id为1时,该用户拥有所有权限
    rs = routes.concat(asyncRoute)
  }

3、亦或者,在meta属性里面添加参数以确定是否需要鉴权:
  [
    {
      path: '/home',
      name: '/Home',
      component: () => import('@/views/Home'),
      meta: {
        title: '首页',
        icon: 's-home'
      },
      {
        path: '/monitor',
        name: 'Monitor',
        component: () => import('@/views/Monitor/Ing'),
        meta: {
          title: '设备状态检测',
          icon: 'monitor'
        },
        {
          path: '/users',
          name: 'Users',
          component: () => import('@/views/Users'),
          meta: {
            title: '用户管理',
            icon: 'user',
            auth: true
          }
        }
      }
    }
  ]
  比如这种,然后在页面当中利用数组方法filter进行过滤。拥有所有权限,就直接使用,如果没有用户管理的权限,那就过滤掉auth为true的选项。
  const routes = [].filter(item => !item.meta.auth)
  不管怎么判断,只要区分出具体的权限,剩下的就是官方文档的示例了:
  Router.addRoutes([...routes])

keep-alive keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 ,其有以下特性: 一般结合路由和动态组件一起使用,用于缓存组件; 提供 include 和 exclude 属性,两者都支持字符串或正则表达式, include 表示只有名称匹配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高; 对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated。

vue-router有哪几种导航钩子 第一种:是全局导航钩子:router.beforeEach(to,from,next),作用:跳转前进行判断拦截 第二种:组件内的钩子 第三种:单独路由独享组件

vue插槽 单个插槽 当子组件模板只有一个没有属性的插槽时, 父组件传入的整个内容片段将插入到插槽所在的 DOM 位置, 并替换掉插槽标签本身复制代码 命名插槽 solt元素可以用一个特殊的特性name来进一步配置如何分发内容。 多个插槽可以有不同的名字。 这样可以将父组件模板中 slot 位置, 和子组件 slot 元素产生关联,便于插槽内容对应传递复制代码 作用域插槽 可以访问组件内部数据的可复用插槽(reusable slot) 在父级中,具有特殊特性 slot-scope 的