Vue常见面试题

Vue常见问题

1、Vue中props是单向数据流 子组件不能直接修改父组件的值

  • 具体原因如下
    • 源码里更新组件调用initProps方法时,会执行defineReactive,这个方法会调用Object.definePropertyget/set方法,调用set方法时会做校验是不是根组件,是不会更新子组件的
    • 最重要的一个原因单向数据流为了不引起数据混乱

2、组件传值方法

  • this.$attrs:子组件中通过v-bind="Name"来传入组件内部,通过this.$attrs[dataName]来获取所有除了不是pros的数据;禁用继承实例中 inheritAttrs: false;
    • 注意 class跟style不属于属性值
  • Vue3.0中被移除的 this.$listeners:子组件中通过v-on="$listeners"来传入组件内部,使用this.$listeners[methodeName]来获取方法;
  • this.$children:获取子组件集合(类似于原生获取子元素集合children);this.$parent:直接获取父元素,可以拿方法和数据;
  • this.$refs.name:获取该节点的方法和数据;
  • 使用provide(一个函数或者对象)和inject(一个数组或者字符串)实现父组件对 子孙组件 注入一个依赖,不管层次多深都可以拿到。
    // 父组件 和data() 同级
    data() {
      return {
        name: 'Derry父组件',
        age: 9999,
      }
    },
    created() {
      const timer = setTimeout(() => {
        this.age += 1;
        clearTimeout(timer);
      }, 1000)
    },
    // 第一种对象的写法
    //provide: {
    //  title: '测试provide'
    //},    
    // 第二种函数的形式 用函数的形式可以使用data中的数据 否则将报错
    provide(): {
      return {
        // 用函数的形式可以使用data中的数据
        childAge: this.age.length,
        title: () => this.age,
      }
    },
    // 如果需要响应式,添加 Vue.computed(() => `${this.name.length}测试provide`)和watch中使用监听, 也可以使用对象;
    // -----------------
    // 子孙组件
    data() {
      return {
        name: 'Jerry子孙组件',
      }
    },
    inject: ['title'],
    created() {
      console.log(this.title); // 测试provide
    },
    computed: {
      ageChange() {
        // 这里需要使用调用
        return this.title();
      },
    },
    // 再用watch监听
    watch: {
      ageChange(val) {
        console.log('祖组件值发生变化', val);
      },
    },
    
    • 运用new Vue()一个新实例导入导出作为桥梁来通信,再需要的传值的两个组件里引入空实例通信;

3、Vue中如何实现单页应用的

  • new Router({})配置 mode 参数,mode值可选hash或者History

  • hash模式

    • hash(#)URL的锚点,同时每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置;但不会对服务端请求数据;
    • 使用hashchange()监听hash值变化,使用window.location.href重新赋值;
      • 用法实例
      // 对hash值变化进行监听
      window.onhashchange = function(e) {
        // 实现页面显示隐藏
        switch(e) {
          case e === 'index':
            window.location.href = 'index.html';
            break;
          ...
          default:
            console.log(`Sorry, we are out of ${e}.`);
        }
      }
      
  • History模式

    • 使用 HTML5 History 中提供的一种功能,能在不刷新页面的情况下修改URL, 使用History.pushState()实现URL跳转无需刷新。
      • pushState新建历史记录 和replaceState修改历史记录 两个API以及浏览器的 popState事件监听 历史栈的改变,只要历史栈有信息发生改变,就会触发该事件。这种模式同样也是不会向后端发起请求的。
      • 用法实例
      // History变化事件监听
      window.onpopstate = function(e) {
        alert(2);
      }
      
      // 状态对象
      let stateObj = {
          foo: "bar",
      };
      
      /*
      * @description pushState - 新建历史记录 replaceState - 修改历史记录
      * @params stateObj {Object} 状态对象state是一个JavaScript对象
      * @params title {String} 标题
      * @params URL {String} URL地址 新URL必须与当前URL同源
      */
      history.pushState(stateObj, "page 2", "bar.html");
      
      history.replaceState(stateObj, "page 3", "bar2.html");
      
      • window.history.forward() == window.history.go(1); -- 前进;
      • window.history.back() == window.history.go(-1); -- 后退;
    • 但当用户直接在用户栏输入地址并带有参数时
      • Hash模式:xxx.com/#/id=5 请求地址为 xxx.com,没有问题;

      • History模式: xxx.com/id=5 请求地址为 xxx.com/id=5,如果后端没有对应的路由处理,就会返回404错误;

      • 前端解决办法: 在路由拦截(router.beforEach(async(to, from, next)=>{}))里判断,如果没有匹配的路由,跳转默认的index.html页面。

        • 有个问题不再返回 404 页面,所以后端处理比较好。
      • 后端解决办法:

        • 在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。
        • 给个警告,因为这么做以后,你的服务器就不再返回 404 错误页面,因为对于所有错误路径都会返回 index.html 文件。为了避免这种情况,你应该在 Vue 应用里面覆盖所有的路由情况,然后再给出一个 404 页面。或者,如果你使用 Node.js 服务器,你可以用服务端路由匹配到来的 URL,并在没有匹配到路由的时候返回 404,以实现回退。
  • Remark-单页应用缺点:对SEO不友好

    • 单页应用实际是把视图(View)渲染从Server交给浏览器,Server只提供JSON格式数据,视图和内容都是通过本地JavaScript来组织和渲染。而搜索搜索引擎抓取的内容,需要有完整的HTML和内容,单页应用架构的站点,并不能很好的支持搜索。

4、Vuex为什么刷新后 数据就清空了

  • 因为JS数据保存在浏览器的堆栈内存里面,所以一刷新浏览器就把堆栈内存清空,所以就Vuex没有了数据。
  • 解决方法:使用cookie \ localStorage \ sessionStroage
    Remark:1、sessionStroage没有过期时间限制,数据会在浏览器窗口关闭时清空。2、localStorage也不能设置过期时间,只能手动删除。

5、Vue中如何实现路由缓存,原理是什么

  • 使用keep-alive
    • /*
      * @description 第一种
      * @attr :max="缓存的最大数量"
      * @attr :include="符合条件的JS数组或者正则表达式"
      * @attr :exclude="不符合条件的JS数组或者正则表达式"
      */
      <keep-alive :exclude="不符合条件的JS数组或者正则表达式">
        <router-view />
      </keep-alive>
      /*
      * 第二种可以直接使用路由中的缓存字段
      */
      <keep-alive>
        <router-view v-if="符合条件的JS表达式" />
      </keep-alive>
      <router-view v-if="!符合条件的JS表达式">
      
    • 总结:keep-alive 组件是抽象组件,在对应父子关系时会跳过抽象组件,它只对包裹的子组件做处理,主要是根据LRU策略缓存组件 VNode,最后在 render 时返回子组件的 VNode。缓存渲染过程会更新 keep-alive 插槽,重新再 render 一次,从缓存中读取之前的组件 VNode 实现状态缓存。

6、Vue实现异步刷新的原理

  • 从eventLoop角度出发
    • 例如for循环从 1 到 100;结果只监听到 100;为什么中间2-99没有变化?其实是for循环是栈里面的事件,优先执行完后,再看任务队列中的watch事件,所以for执行完之后就是100了,再被监听到就是100;
  • this.$nextTick()原理:
    • Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。
    • Vue 在修改数据后,视图不会立刻更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。
    • 首先修改数据,这是同步任务。同一事件循环的所有的同步任务都在主线程上执行,形成一个执行栈,此时还未涉及 DOM 。
    • 然后Vue 开启一个异步队列,并缓冲在此事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被推入到队列中一次。
    • vue的降级策略(兼容)
      • promise -> MutationObserver ->(macro-task) setTimeout 创建一个新的任务,优先使用 Promise,如果浏览器不支持,再尝试MutationObserver。又不支持,只能用 setTimeout 创建 task 了。MutationObserver 是 h5 新加的一个功能,其功能是监听dom节点的变动,在所有 dom 变动完成后,执行回调函数。

7、Vue中使用环境变量和使用插值

  • public文件夹中 使用插值

    • <%= VALUE %> 用来做不转义插值;
    • <%- VALUE %> 用来做 HTML 转义插值;
    • <% expression %> 用来描述 JavaScript 流程控制。
    • 除了被 html-webpack-plugin 暴露的默认值之外,所有客户端环境变量也可以直接使用。例如,BASE_URL 的用法:
    <!-- index文件中 设置页签图标 -->
    <link rel="ico" href="<%= BASE_URL %>favicon.ico"></link>
    
  • 你可以在你的项目根目录中放置下列文件来指定环境变量:

     .env                # 在所有的环境中被载入
     .env.local          # 在所有的环境中被载入,但会被 git 忽略
     .env.[mode]         # 只在指定的模式中被载入
     .env.[mode].local   # 只在指定的模式中被载入,但会被 git 忽略
    
    • 一个环境文件只包含环境变量的“键=值”对:VUE_APP_NOT_SECRET_CODE=some_value

    • 请注意,只有 NODE_ENVBASE_URL 和以 VUE_APP_ 开头的变量将通过 webpack.DefinePlugin 静态地嵌入到客户端侧的代码中。这是为了避免意外公开机器上可能具有相同名称的私钥。

    • 代码中访问变量例如console.log(process.env.VUE_APP_HELLO)

    • 也可以再vue.config.js中设置环境变量

      // vue.config.js
      process.env.VUE_APP_VERSION = require('./package.jon').version;
      
    • 除了 VUE_APP_* 变量之外,在你的应用代码中始终可用的还有两个特殊的变量:

      • NODE_ENV - 会是 "development"、"production" 或 "test" 中的一个。具体的值取决于应用运行的模式(可以不用设置)。
      • BASE_URL - 会和 vue.config.js 中的 publicPath 选项相符,即你的应用会部署到的基础路径。
      // vue.config.js
      Module.exports = {
        publicPath: process.env.NODE_ENV !== "development" ? '/pollution-ses/' : '/',
      }
      

8、Vue事件$emit发送后$on监听失效

  • 发送$emit('handleEven'),接收 @handle-even="handle"
    • 失效原因 html 对大小写不敏感,并且 v-on 事件监听器中都会转成全小写(转成@handleeven),监听不到handleEven,所以不存在驼峰式写法。
    • 推荐始终使用 kebab-case 的事件名。

9、Vue中使用原生事件失效

  • 主要原因是未使用 .native 事件修饰符。
  • 如果添加 .native 还是静默失效,那么可能是组件根元素的问题
  • 解决方法:可以使用 v-on="$listeners"$listeners"它是一个对象,里面包含了作用在这个组件上的所有监听器;

10、Vue中Router异步组件和添加处理加载状态

  • 工厂函数中返回一个 Promise,所以把 webpack 2 和 ES2015 语法加在一起,我们可以这样使用动态导入:
Vue.component(
  'async-webpack-example',
  // 这个动态导入会返回一个 `Promise` 对象。
  () => import('./my-async-component')
)
// 或者局部注册组件中使用键值对方式
new Vue({
  // ...
  components: {
    'my-component': () => import('./my-async-component')
  }
})
  • 异步组件工厂函数可以返回一个如下格式的对象:
onst AsyncComponent = () => ({
  // 需要加载的组件 (应该是一个 `Promise` 对象)
  component: import('./MyComponent.vue'),
  // 异步组件加载时使用的组件
  loading: LoadingComponent,
  // 加载失败时使用的组件
  error: ErrorComponent,
  // 展示加载时组件的延时时间。默认值是 200 (毫秒)
  delay: 200,
  // 如果提供了超时时间且组件加载也超时了,
  // 则使用加载失败时使用的组件。默认值是:`Infinity`
  timeout: 3000
})

11、elementUI 设置表头高度: 利用diff算法, 添加动态 key值, 刷新表格.

<el-table
  :data="
    tableData.slice((currentPage - 1) * pageSize, currentPage * pageSize)
  "
  style="width: 100%"
  :header-cell-style="{
      textAlign: 'center',
      height: '40px',
  }"
  :row-style="{height: '60px'}"
  stripe
>
<el-table-column label="ID"         align="center" prop="id" width="50"></el-table-column>
</el-table>
<!-- 以上并无效果 添加动态利用diff算法,动态刷新表格 添加动态 key 值才生效  -->
<el-table
  :data="
    tableData.slice((currentPage - 1) * pageSize, currentPage * pageSize)
  "
  style="width: 100%"
  :key="Math.random()"
  stripe
>
<el-table-column label="ID"         align="center" prop="id" width="50"></el-table-column>
</el-table>
posted @ 2022-06-24 15:31  lutwelve  阅读(88)  评论(0编辑  收藏  举报