zoukankan      html  css  js  c++  java
  • 【interview】vue面试题

    1. 什么是 RESTful ? (粗略过一遍 有个印象即可 知道是个什么东东)

    面试回答:“是一个api的标准,无状态请求,请求的路由地址是固定的。 restful:给用户一个 url,根据method不同在后端做不同处理:比如post 创建数据,get 获取数据,put和patch修改数据,delete删除数据”

    REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful

    RESTful 即 RESTful 架构

    每一个 URI 代表一种资源

    客户端和服务器之间,传递这种资源的某种表现层

    客户端通过四个 HTTP 动词,对服务器端资源进行操作,实现 "表现层状态转化"

    REST,即 Representational State Transfer的缩写。我对这个词组的翻译是"表现层 状态 转化"

    --------(Resources)资源的表现层状态转化

    • 资源:

    所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。

    它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。

    你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。

    要获取这个资源,访问它的 URI 就可以,因此 URI 就成了每一个资源的地址或独一无二的识别符。

    所谓"上网",就是与互联网上一系列的"资源"互动,调用它的 URI 。

    • 表现层(Representation)

    "资源"是一种信息实体,它可以有多种外在表现形式。

    我们把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)。

    比如,文本可以用 txt 格式表现,也可以用 HTML 格式、XML格式、JSON 格式表现,甚至可以采用二进制格式。

    图片可以用 JPG 格式表现,也可以用 PNG 格式表现。

    在 HTTP 请求的头信息中用 Accept 和 Content-Type 字段指定,这两个字段才是对"表现层"的描述。

    • 状态转化(State Transfer)

    访问一个网站,就代表了客户端和服务器的一个互动过程。

    在这个过程中,势必涉及到数据和状态的变化。

    联网通信协议HTTP协议,是一个无状态协议。

    这意味着,所有的状态都保存在服务器端。

    因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生 "状态转化"(State Transfer)。

    而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。

    客户端用到的手段,只能是 HTTP 协议。

    具体来说,就是 HTTP 协议里面,四个表示操作方式的动词 POST、DELETE、GET、PUT

    它们分别对应四种基本操作:

    • 增:POST 用来新建资源(也可以用于更新资源)
    • 删:DELETE 用来删除资源。
    • 查:GET 用来获取资源
    • 改:PUT 用来更新资源

    2. 双向绑定

    参考:https://www.cnblogs.com/kidney/p/6052935.html?utm_source=gold_browser_extension

    •  双向数据绑定描述的就是: 组件 data 中的状态数据流
    • 用户操作导致状态数据的改变,这是一个方向 由页面流入 data
    • 状态数据改变以后,自动渲染页面最新状态数据,这是另一个方向 由 data 流向页面
    • 双向绑定原理 (Object.defineProperty() 属性劫持 和 发布订阅模式)
    • 利用了 Object.defineProperty() 这个方法,去为组件 data 中的每个属性,设置 set 和 get,通常分别称之为 ‘setter’ 和 ‘getter’,这一步操作,我们称之为,数据劫持(劫持了组件 data 中的每个属性)

    这是为了: 在给 data 中的属性赋值操作时,不是简单的赋值,还要执行 “自动渲染 属性相关的最新数据 到页面中”  的逻辑

    • 然后 data 中的每一个属性 都会有一个 主题对象 dep ,我们可以称之为发布者

    在 new Vue 的时候,会 Compile 编译 html 创建一个虚拟 dom,解析到 属性相关的元素,就为这个元素绑定为 data 中属性相关的 watcher,理解为 发布者

    一个发布者,进行数据的改变,会引起 data 中属性的改变,由于数据劫持触发 setter,同时也会让发布者派发一个消息给页面所有相关的 watcher 订阅者

    3. v-if 和 v-show 的区别

    • v-show 只是在 display: non e和 display: block 之间切换,只需要切换 CSS,DOM还是一直保留着

    v-show 在初始渲染时有更高的开销,但是切换开销很小,更适合频繁切换的场景

    • v-if 涉及到 vue 底层的编译,当属性初始为 false 时组件不会被渲染,直到条件为 true

    切换条件时会触发销毁/挂载组件,切换时开销更高,更适合不经常切换的场景

    3.2 聊聊你对Vue.js的template编译的理解

    先转化成AST树,再得到 render函数 返回VNode(Vue的虚拟DOM节点)

    详细步骤:

    首先通过compile编译器把template编译出AST语法树

    然后AST会经过generate(将AST语法树转化成render function字符串的过程)得到render函数,render的返回值是VNode

    VNode是Vue的虚拟DOM节点,里面有标签名,子节点,文本等

    4. active-class 是哪个组件的属性?

    • active-class 是 vue-router 模块中 router-link 组件的属性

    路由的模糊匹配

    • <router-link to="/article" active-class="router-active"></router-link>

    当用户访问 /article/1 时会被激活为:

    • <a href="#/article" class="router-active" rel="nofollow"></a>
    • exact-active-class 也是 vue-router 模块中 router-link 组件的属性

    路由的全匹配

    • <router-link to="/article" exact-active-class="router-active"></router-link>

    当用户访问 /article/1 时,不会激活这个 link 的 class:

    • <a href="#/article" rel="nofollow"></a>

    5. 嵌套路由怎么定义

    使用 children 定义嵌套路由

    5.2. 对 keep-alive 的了解

    keep-alive 是一个 vue 内置组件,可使被包含的组件保留状态或避免重新渲染,有 include(包含的组件缓存)和 exclude(排除的组件不缓存)两个属性

    6. 对 vue 生命周期的理解

    生命周期共分为 8 个阶段

    创建前/后

    在 beforeCreate 阶段,vue 实例的挂载元素 el  数据对象 data 都为 undefined,还未初始化

    created 阶段,vue 实例的数据对象 data 有了,el 还没有。

    载入前后

    在 beforeMount 阶段,vue 实例的 el 和 data 都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。

    在 mounted 阶段,vue 实例挂载完成,data.message 成功渲染

    更新前/后:当 data 变化时,会触发 beforeUpdate 和 updated 方法

    销毁前/后

    beforeDestroy 在实例销毁前调用,实例仍然完全可用

    destroyed 在实例销毁之后调用,调用后所有事件监听器会被移除,所有子实例也会被销毁。

    7. vue 常用修饰符(参考:https://www.cnblogs.com/gitByLegend/p/10835064.html

    • 事件修饰符

    .prevent

    阻止默认行为、相当于在事件处理程序中调用 event.preventDefault()

    • <form @submit.prevent></form>

    .stop

    阻止单击事件冒泡

    • <a v-on:click.stop="doThis"></a>

    .self 

    当事件发生在该元素本身而不是子元素的时候触发

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

    .capture

    元素自身触发的事件先在此处处理,然后才交由内部元素进行处理

    • <div @click.capture="doThis">...</div>
    • v-model 修饰符

    .lazy

    在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步

    你可以添加 lazy 修饰符,从而转变为使用 change 事件进行同步:

    • <!-- 在“change”时而非“input”时更新 -->
      <input v-model.lazy="msg" >

    .number

    如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符

    • <input v-model.number="age" type="number">

    .trim

    如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:

    • <input v-model.trim='trim'>
    • 按键修饰符

    .enter .tab .delete .esc .space .up .down .left .right

    .ctrl .alt .shift .meta

    • <!-- Alt + C -->
      <input @keyup.alt.67="clear">

    8. 常见指令及作用

     v-text 数据绑定标签,将 vue 对象 data 中的属性绑定给对应的标签作为内容显示出来,类似 js 的 text 属性;

    • 复制代码
      <div id="app">
          <div v-text="h1"></div>
      </div>
      
      <script>
          new Vue({
              el: "#app",
              data:{ 
                  h1:'<h1>完善<h1>' 
              }
          }
      </script>
      复制代码

    v-html 类似 v-text 标签,他是将 data 的属性作为 html 语法输出,类似 js 中的 innerHtml 属性;

    v-show 实际是控制了dom的css的display属性

    v-if 、v-else-if

    v-for

    v-on

    v-bind

    v-model

    v-slot 插槽名(默认default)

    8.2 插槽内容 slot 

    父组件 向 子组件传递"标签数据"

    应对: 不仅数据是动态的,结构也是动态的

    • App.vue 父组件 使用子组件时,声明用哪些槽:

    <Child>

    </Child>

    • 子组件Child 定义槽:

    <template>

    </template>

    插槽显示的顺序,由子组件编码决定

    细节: 在写公共组件 Child 时,先不写槽,直接写正常的代码,将样式搞定,再抽出槽

    9. 自定义指令

    分为两种: 创建局部指令、创建全局指令

    使用:

    • <div v-lower-text></div>
    • 局部指令
    • 复制代码
      new Vue({
          el: "test",
          directive:{
              "lower-text": function(el, binding){
                  el.innerHTML = binding.value.toLowerCase();
              }
          }
      })
      复制代码
    • 全局指令
    • 复制代码
      /********* js *********/
      Vue.directive(
         "upper-text",
          function(el, binding){
              el.innerHTML = binding.value.toUpperCase()
          }
      );
      复制代码

    10. 自定义一个过滤器?

    比如 moment 自定义过滤器 - 日期处理 /src/filters/index.js

    • 复制代码
      import Vue from 'vue'
      import moment from "moment"
      
      
      Vue.filter(
          'stick-format',
          funtion (vue, formatStr="YYYY-MM-DD HH:mm:ss") {
              return moment().format(formatStr)
          }
      )
      复制代码

    11. 什么是 MVVM

    MVVM 是 Model-View-ViewModel 的缩写。
    • Model代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。
    • View 代表UI 组件,它负责将数据模型转化成UI 展现出来。
    • ViewModel 监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步 View 和 Model 的对象,连接 Model 和 View。
    在 MVVM 架构下,View 和 Model 之间并没有直接的联系,而是通过 ViewModel 进行交互
    Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
    ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而 View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。

    12. vue 中的 MVVM 分别是?

     

    13. 引入三方库的时候 Vue.use(MyPlugin);        // 声明使用 vue 插件 MyPlugin,内部会自调用方法加载 MyPlugin.install(Vue); 

    14. 导航守卫(全局守卫,类似 react 封装的那个组件,判断是否已经登录并认证,然后 render 不同的组件)

    参考:https://www.cnblogs.com/tianxiaxuange/p/10418315.html

    是 vue-router 提供的 下面 2 个方面的功能

    • 监视路由跳转
    • 控制路由跳转

    应用

    • 在跳转到界面之前,对用户进行检查限制
    • 在界面离开之前进行收尾工作

    分类:

    • 全局守卫 ---- 针对任意路由跳转
    • 复制代码
      import Vue from "vue";
      import VueRouter from "vue-router";
      
      const Home = ()=>import("../pages/Home/Home")
      const Search = ()=>import("../pages/Search/Search")
      const Order = ()=>import("../pages/Order/Order")
      const Personal = ()=>import("../pages/Personal/Personal")
      
      
      Vue.use(VueRouter);
      const router = new VueRouter({
        mode: "history",
        routes: [
          {path:"/home", component: Home, meta:{showFooter: true}},
          {
            path:"/shop",
            component: Shop,
            children: [
              {path:"/shop/goods", component: Goods},
              {path:"/shop/ratings", component: Ratings},
              {path:"/shop/info", component: Info},
              {path:"/shop", redirect: "/shop/goods"},
            ]
          },
          
          {path:"/search", component: Search, meta:{showFooter: true}},
          {path:"/order", component: Order, meta:{showFooter: true}},
          {path:"/personal", component: Personal, meta:{showFooter: true}},
          
          {path:"/login_register", component: LoginRegister},
          
          {path:"/", redirect: "/home"},
        ]
      })
      
      /*************************************************/
      const paths =  ["/login_register"]
      router.beforeEach((to, from, next)=>{    // 设置全局守卫
        const path = to.path
        if(paths.indexOf(path)>=0){
          if(Vue.store.state.user.userInfo._id){    // 在 main.js 中保存 store
            return next("/personal")
          }
        }
        next()    // 其他路由请求,放行
      })
      /***********************

              to ---- 目标路由

              from ---- 当前路由

              next ---- 函数

              next() ---- 执行下一个守卫回调,如果没有,就跳转到目标路由

              next(false) ---- 不继续执行,跳转流程在此处中断,不会跳转到目标路由组件

              next(path) ---- 跳转到指定的另一个路由

              next() ---- 传入的回调函数会在组件对象创建后对象,即延后执行回调 - 且将组件对象传入回调函数 即 this 

      **************************/
      
      export default router
      复制代码
    • 全局前置守卫: 在准备跳转到某个路由组件之前 (在开发中用的较多)
    • router.beforeEach((to, from, next)=>{
      })
    • 全局后置守卫: 在跳转到某个路由组件之后
    • router.afterEach((to, from)=>{
      })
    • 组件守卫
    • 复制代码
      beforeRouteEner(){   // 在跳转到当前组件之前,无法访问这个组件的组件对象  this
      }
      
      beforeRouteUpdate(){   // 在当前组件显示更新前调用,可以访问 this
      }
      
      beforeRouteLeave(){   // 在当前组件离开前调用,可以访问 this
      }
      复制代码

    15. computed 和 watch 区别

    • computed 是计算属性,依赖其他属性计算值,并且 computed 的值有缓存

    只有当计算值变化才会返回内容

    一般来说需要依赖别的属性来动态获得值的时候可以使用 computed

    • watch 监听到值的变化就会执行回调,在回调中可以进行一些逻辑操作。

    对于监听到值的变化需要做一些复杂业务逻辑的情况可以使用 watch 

    值得注意的是,watch 还可以配置 deep 属性为 true ,实现对对象的深层次属性监控

    16. 组件继承 extend 能做什么?

    扩展组件

    • 复制代码
      // 创建组件构造器
      let Component = Vue.extend({
          template: '<div>test</div>'
      })
      
      // 扩展已有组件
      let SuperComponent = Vue.extend(Component)
      new SuperComponent({
          created(){
              console.log(1);
          }
      })
      new SuperComponent().$mount('#app')
      复制代码

      实现了子组件 Compont,增加一个生命周期函数 created 并在期间打印 1

    17. 其实 vue 提供的两个都可以理解为继承

    • mixins 接收对象数组(可理解为多继承)
    • extends 接收的是对象或函数(可理解为单继承)

    优先调用 extends 和 mixins 继承的父类

    extends 触发的优先级更高,相对于是队列

    • 复制代码
      const extend = {
        created () {
          console.log('extends created')
        }
      }
      const mixin1 = { created () { console.log('mixin1 created') } }
      const mixin2 = { created () { console.log('mixin2 created') } }
      export default { extends: extend, mixins: [mixin1, mixin2], name: 'app', created () { console.log('created') } }
      复制代码

      控制台会打印

      extends created
      mixin1 created
      mixin2 created
      created

    18. 说说你所了解的 Vue.nextTick() 即 this.$nextTick()

    参考: https://mp.weixin.qq.com/s/mCcW4OYj3p3471ghMBylBw

    在 DOM 更新完毕之后执行一个回调

    尽管 MVVM 框架并不推荐访问 DOM,但有时候确实会有这样的需求,尤其是和第三方插件进行配合的时候,免不了要进行DOM操作

    而 nextTick 就提供了一个桥梁,确保我们操作的是更新后的 DOM

    19. 项目中遇到的问题:

    (1)beforeCreate 和 created 生命周期发送ajax到后台,数据返回的时候发现 DOM 节点还未生成无法操作节点,那要怎么办呢?

    原因: created()钩子函数执行的时候DOM并未进行任何渲染
    解决: 使用 this.$nextTick(() => { //操作节点 }) 确保 DOM 已经更新完成

    (2)

    (3)

    (4)

    20. 组件间的通信

    (1)父子组件通信

    • 父组件通过 props 传递数据给子组件
    • 复制代码
      // 父组件
      <div>
          <child :data="child" @send="getFromChild"></child>
      </div>
      
      data(){
          return{
              toChild: '大儿子',
              fromChild: ''
          }
      },
      methods: {
          getFromChild(val) {
              this.fromChild = val
          }
      }
      复制代码
    • 子组件通过 emit 发送事件传递给父组件
    • 复制代码
      // 子组件
      <div @click="toParent">{{data}}</div>
      
      props:[data],
      
      methods: {
          toParent(){
              this.$emit('send', '给父亲')
          }
      }
      复制代码
    • 在父组件使用 $children 访问子组件
    • 复制代码
      / 父组件
      <child />
      
      data(){
          return {
              msg: '父组件数据'
          }
      },
      methods: {
          test(){
              console.log('我是父组件的方法,被执行')
          }
      },
      mounted(){
          console.log(this.$children[0].child_msg); // 打印子组件 data 的属性
      }
      复制代码
    • 在子组件中使用 $parent 访问父组件
    • 复制代码
      // 子组件渲染父组件 data 中的属性
      <div>{{$parent.msg}}</div>
      
      data(){
          return{
              child_msg: '子组件数据'
          }
      },
      mounted(){
          // 子组件执行父组件方法
          this.$parent.test(); 
      }
      复制代码

    (2)兄弟组件通信

    可以通过查找公共父组件中的子组件实现

    this.$parent.$children 在 $children 中可以通过组件 name 查询到需要的组件实例,然后进行通信

    (3)跨多层次组件通信

    provide/inject 文档中不推荐直接使用在业务中

    • 复制代码
      // 父组件A
      export default{
          provide: {
              data: 1
          }
      }
      // 子组件B
      export default{
          inject: ['data'],
          mounted(){
              // 无论跨几层都能获取父组件的data属性
              console.log(this.data); // 1
          }
      }
      复制代码

    (4)任意组件

    • Event Bus

    1. 新建一个bus.js文件

    • // bus.js
      import Vue from 'vue';
      export default new Vue();

    2. 使用它

    组件A

    • 复制代码
      <div @click="addCart">添加</div>
      
      import Bus from 'bus.js';
      export default{
          methods: {
              addCart(event){
                  Bus.$emit('getTarget', event.target)
              }
          }
      }
      复制代码

      Bus.$emit('消息名称', 传参);

    组件B

    • 复制代码
      export default{
          created(){
              Bus.$on('getTarget', target =>{
                  console.log(target)
              })
          }
      }
      复制代码

      Bus.$on('消息名称', 处理函数);

    • Vuex 集中管理组件的公共状态数据

    就是用于 任意组件 间的通信

    核心概念: state 状态机、mutation、action

    流程:

    操作的数据流:

    • 组件逻辑中会 this.$store.dispatch() 一个 action,进行必要的数据处理,可能包含异步操作
    • 然后 action 函数会 commit 一个 mutation-type 触发对应的 mutation
    • 然后 mutation 会直接操作 状态机 state 改变对应的状态数据

    接下来就是数据渲染:

    在组件中,会通过 mapState 的方式将需要的公共状态映射到 computed 上,然后实时渲染页面(猜测也是发布订阅者的模式,没有验证)

    而 computed 的属性又被挂载到了 this 上,所以可以通过 this 访问到最新的 vuex 状态数据,从而进行逻辑流程

    21. 单页面应用 SPA 的核心之一是:更新视图而不重新请求页面,

    vue-router 路由模式 分为

    • hash 模式 : vue-router 中默认使用的是 hash 模式

    URL 中带有 # 号

    背后的原理是 onhashchange 事件

    • history模式

    随着 history api的到来,前端路由开始进化了,history api 给了前端完全的自由 history api 可以分为两大部分:切换和修改

    两个方法,这两个方法接收三个参数:stateObj,title,url

    pushState

    把页面的状态保存在 state 对象中,当页面的 url 再变回这个url时,可以通过 event.state 取到这个state对象,从而可以对页面状态进行还原

    这里的页面状态就是页面字体颜色,其实滚动条的位置,阅读进度,组件的开关的这些页面状态都可以存储到state的里面

    replaceState

    缺点:

    当刷新时,如果服务器中没有相应的响应或者资源,会分分钟刷出一个404来

    vue-router 路由 分为

    • 普通路由
    • this.$router.push('home');
      this.$router.push({path: 'home');
    • 命名路由
    • 复制代码
      const router=new VueRouter({
          routes: [{
              path: '/user',
              name: 'user',
              component: User
          }]
      })
      复制代码
      <router-link :to="{name: 'user'}"></router-link>

      this.$router.push({ name: 'user' });

    • 动态路由匹配(路由参数 params)
    • 复制代码
      const router = new VueRouter({
          routes: [{
              path: '/user/:id',
              component: User
          }]
      })
      复制代码
      <div>{{$route.params.id}}</div>

      this.$router.push({name:'user',params: {id: 123});

    • 嵌套路由
    • 复制代码
      const router = new VueRouter({
          routes: [{
              path: '/user/:id',
              children: [{
                  // 当/user/:id
                  path: '',
                  component: UserHome
              },{
                  // 当/user/:id/profile
                  path: 'profile',
                  component: UserProfile
              }]
          }]
      })
      复制代码
      <h2>{{$route.params.id}}</h2>
      <router-view></router-view>
    • 请求参数的路由
    • this.$router.push({path:'register',query:{plan:'private'});
  • 相关阅读:
    Unique path
    *Jump Game
    Valid Palindrome
    *Reverse Words in a String
    Min Stack
    [?]*Simplify Path
    *Valid Parentheses
    *Sqrt(x)
    String to Integer (atoi)
    Add Digits
  • 原文地址:https://www.cnblogs.com/mailyuan/p/13381802.html
Copyright © 2011-2022 走看看