继续创造,加快成长!这是我参与「日新方案 6 月更文应战」的第30天,点击查看活动概况

前语

小伙伴们好,我是村长,一个爱同享的老前端。本月恰逢「日新方案 6 月更文应战」,我抉择每天一题完毕《50+经典Vue面试题分析》系列的后半部分。时间匆促,或许有考虑不周的当地,但我确保每题都是原创,纯手敲非常辛苦,希望我们多多点赞保藏注重。有差错和遗失欢迎我们留言交流!本文的上半部分在此:

历时一个月,2.6W字!50+Vue经典面试题源码级详解,你值得保藏!

学习群

我安排了一个面试学习群,注重村长群众号村长学前端,回复“加群”,我们一起卷~

相关学习资源

本系列有配套视频思维导图开源项目,我们学习一起千万不要忘了三连 + 注重 + 同享,有道是喝水不忘挖井人~

  • 视频教程:56道经典Vue面试题详解
  • 思维导图:Vue面试专题
  • 配套代码:vue-interview

24-Vue 3.0的规划政策是什么?做了哪些优化?

分析

仍是问新特性,陈说典型新特性,分析其给你带来的改动即可。


思路

从以下几方面分门别类论说:易用性、功用、扩展性、可保护性、开发领会等


模范

  1. Vue3的最大规划政策是替代Vue2(皮一下),为了完毕这一点,Vue3在以下几个方面做了很大改进,如:易用性、结构功用、扩展性、可保护性、开发领会等
  2. 易用性方面首要是API简化,比如v-model在Vue3中变成了Vue2中v-modelsync修饰符的结合体,用户不用差异两者不同,也不用选择困难。类似的简化还有用于烘托函数内部生成VNode的h(type, props, children),其间props不用考虑差异特色、特性、作业等,结构替我们判别,易用性大增。
  3. 开发领会方面,新组件Teleport传送门、FragmentsSuspense等都会简化特定场景的代码编写,SFC Composition API语法糖更是极大进步我们开发领会。
  4. 扩展性方面进步如独立的reactivity模块,custom renderer API等
  5. 可保护性方面首要是Composition API,更简略编写高复用性的业务逻辑。还有对TypeScript支撑的进步。
  6. 功用方面的改进也很明显,例如编译期优化、根据Proxy的照应式系统
  7. 。。。

或许的诘问

  1. Vue3做了哪些编译优化?
  2. ProxydefineProperty有什么不同?

25-你了解哪些Vue功用优化方法?

分析

这是一道归纳实践标题,写过必定数量的代码之后小伙伴们天然会初步注重一些优化方法,答得越多肯定实践经验也越丰富,是很好的标题。

答题思路:

根据标题描绘,这儿首要讨论Vue代码层面的优化


答复模范

  • 我这儿首要从Vue代码编写层面说一些优化方法,例如:代码切开、服务端烘托、组件缓存、长列表优化等

  • 最常见的路由懒加载:有用拆分App标准,访问时才异步加载

    const router = createRouter({
     routes: [
      // 凭仗webpack的import()完毕异步组件
       { path: '/foo', component: () => import('./Foo.vue') }
      ]
    })
    

  • keep-alive缓存页面:避免重复创建组件实例,且能保存缓存组件情况

    <router-view v-slot="{ Component }">
        <keep-alive>
        <component :is="Component"></component>
     </keep-alive>
    </router-view>
    

  • 运用v-show复用DOM:避免重复创建组件

    <template>
     <div class="cell">
      <!-- 这种情况用v-show复用DOM,比v-if作用好 -->
      <div v-show="value" class="on">
       <Heavy :n="10000"/>
      </div>
      <section v-show="!value" class="off">
       <Heavy :n="10000"/>
      </section>
     </div>
    </template>
    

  • v-for 遍历避免一起运用 v-if:实践上在Vue3中现已是个差错写法

    <template>
      <ul>
       <li
        v-for="user in activeUsers"
        <!-- 避免一起运用,vue3中会报错 -->
        <!-- v-if="user.isActive" -->
         :key="user.id">
        {{ user.name }}
       </li>
      </ul>
    </template>
    <script>
     export default {
      computed: {
       activeUsers: function () {
        return this.users.filter(user => user.isActive)
        }
       }
      }
    </script>
    

  • v-once和v-memo:不再改动的数据运用v-once

    <!-- single element -->
    <span v-once>This will never change: {{msg}}</span>
    <!-- the element have children -->
    <div v-once>
     <h1>comment</h1>
     <p>{{msg}}</p>
    </div>
    <!-- component -->
    <my-component v-once :comment="msg"></my-component>
    <!-- `v-for` directive -->
    <ul>
     <li v-for="i in list" v-once>{{i}}</li>
    </ul>
    

    按条件跳过更新时运用v-memo:下面这个列表只会更新选中情况改动项

    <div v-for="item in list" :key="item.id" v-memo="[item.id === selected]">
     <p>ID: {{ item.id }} - selected: {{ item.id === selected }}</p>
     <p>...more child nodes</p>
    </div>
    

    vuejs.org/api/built-i…


  • 长列表功用优化:假设是大数据长列表,可选用虚拟翻滚,只烘托少部分区域的内容

    <recycle-scroller
     class="items"
     :items="items"
     :item-size="24"
    >
     <template v-slot="{ item }">
      <FetchItemView
       :item="item"
       @vote="voteItem(item)"
      />
     </template>
    </recycle-scroller>
    

    一些开源库:

    • vue-virtual-scroller
    • vue-virtual-scroll-grid

  • 作业的销毁:Vue 组件销毁时,会自动解绑它的悉数指令及作业监听器,但是仅限于组件本身的作业。

    export default {
     created() {
      this.timer = setInterval(this.refresh, 2000)
      },
     beforeUnmount() {
      clearInterval(this.timer)
      }
    }
    

  • 图片懒加载

    关于图片过多的页面,为了加快页面加载速度,所以许多时分我们需求将页面内未出现在可视区域内的图片先不做加载, 比及翻滚到可视区域后再去加载。

    <img v-lazy="/static/img/1.png">
    

    参阅项目:vue-lazyload


  • 第三方插件按需引进

    element-plus这样的第三方组件库可以按需引进避免体积太大。

    import { createApp } from 'vue';
    import { Button, Select } from 'element-plus';
    ​
    const app = createApp()
    app.use(Button)
    app.use(Select)
    

  • 子组件切开战略:较重的情况组件合适拆分

    <template>
     <div>
      <ChildComp/>
     </div>
    </template><script>
    export default {
     components: {
      ChildComp: {
       methods: {
        heavy () { /* 耗时任务 */ }
        },
       render (h) {
        return h('div', this.heavy())
        }
       }
      }
    }
    </script>
    

    但一起也不宜过度拆分组件,特别是为了所谓组件笼统将一些不需求烘托的组件特意抽出来,组件实例消耗远大于纯dom节点。参阅:vuejs.org/guide/best-…


  • 服务端烘托/静态网站生成:SSR/SSG

    假设SPA运用有首屏烘托慢的问题,可以考虑SSR、SSG方案优化。参阅SSR Guide


26-Vue组件为什么只能有一个根元素?

这题现在有些掉队,vue3现已不用一个根了。因此这标题很有说头!


领会一下

vue2直接报错,test-v2.html

new Vue({
 components: {
  comp: {
   template: `
    <div>root1</div>
    <div>root2</div>
   `
   }
  }
}).$mount('#app')

又一个月,1.5W字!50+Vue经典面试题源码级详解,完毕篇!


vue3中没有问题,test-v3.html

Vue.createApp({
 components: {
  comp: {
   template: `
    <div>root1</div>
    <div>root2</div>
   `
   }
  }
}).mount('#app')

又一个月,1.5W字!50+Vue经典面试题源码级详解,完毕篇!


答复思路

  • 给一条自己的结论
  • 解释为什么会这样
  • vue3处理方法原理

模范

  • vue2中组件确实只能有一个根,但vue3中组件现已可以多根节点了。
  • 之所以需求这样是由于vdom是一颗单根树形结构,patch方法在遍历的时分从根节点初步遍历,它要求只要一个根节点。组件也会转换为一个vdom,天然应该满足这个要求。
  • vue3中之所以可以写多个根节点,是由于引进了Fragment的概念,这是一个笼统的节点,假设发现组件是多根的,就创建一个Fragment节点,把多个根节点作为它的children。将来patch的时分,假设发现是一个Fragment节点,则直接遍历children创建或更新。

知其所以然

  • patch方法接收单根vdom:

    github1s.com/vuejs/core/…

    // 直接获取type等,没有考虑数组的或许性
    const { type, ref, shapeFlag } = n2
    
  • patch方法对Fragment的处理:

    github1s.com/vuejs/core/…

    // a fragment can only have array children
    // since they are either generated by the compiler, or implicitly created
    // from arrays.
    mountChildren(n2.children as VNodeArrayChildren, container, ...)
    

27-你有运用过vuex的module吗?

这是底子运用才能查询,稍微上点规划的项目都要拆分vuex模块便于保护。


领会

vuex.vuejs.org/zh/guide/mo…

const moduleA = {
 state: () => ({ ... }),
 mutations: { ... },
 actions: { ... },
 getters: { ... }
}
const moduleB = {
 state: () => ({ ... }),
 mutations: { ... },
 actions: { ... }
}
const store = createStore({
 modules: {
  a: moduleA,
  b: moduleB
  }
})
store.state.a // -> moduleA 的情况
store.state.b // -> moduleB 的情况
store.getters.c // -> moduleA里的getters
store.commit('d') // -> 能一起触发子模块中同名mutation
store.dispatch('e') // -> 能一起触发子模块中同名action

思路

  1. 概念和必要性
  2. 怎样拆
  3. 运用细节
  4. 优缺点

模范

  1. 用过module,项目规划变大之后,单独一个store政策会过于巨大臃肿,通过模块方法可以拆分开来便于保护
  2. 可以按之前规则单独编写子模块代码,然后在主文件中通过modules选项安排起来:createStore({modules:{...}})
  3. 不过运用时要留心访问子模块情况时需求加上注册时模块名:store.state.a.xxx,但一起gettersmutationsactions又在全局空间中,运用方法和之前相同。假设要做到完全拆分,需求在子模块加上namespace选项,此时再访问它们就要加上命名空间前缀。
  4. 很显然,模块的方法可以拆分代码,但是缺点也很明显,就是运用起来比较繁琐凌乱,简略犯错。并且类型系统支撑很差,不能给我们带来帮助。pinia显然在这方面有了很大改进,是时分切换以前了。

或许的诘问

  1. 用过pinia吗?都做了哪些改进?

28-怎样完毕路由懒加载呢?

分析

这是一道运用题。当打包运用时,JavaScript 包会变得非常大,影响页面加载。假设我们能把不同路由对应的组件切开成不同的代码块,然后当路由被访问时才加载对应组件,这样就会愈加高效。

// 将
// import UserDetails from './views/UserDetails'
// 替换为
const UserDetails = () => import('./views/UserDetails')
​
const router = createRouter({
 // ...
 routes: [{ path: '/users/:id', component: UserDetails }],
})

参阅router.vuejs.org/zh/guide/ad…


思路

  1. 必要性
  2. 何时用
  3. 怎样用
  4. 运用细节

答复模范

  1. 当打包构建运用时,JavaScript 包会变得非常大,影响页面加载。运用路由懒加载我们能把不同路由对应的组件切开成不同的代码块,然后当路由被访问的时分才加载对应组件,这样会愈加高效,是一种优化方法。

  2. 一般来说,对全部的路由都运用动态导入是个好主意。

  3. component选项配备一个回来 Promise 组件的函数就可以定义懒加载路由。例如:

    { path: '/users/:id', component: () => import('./views/UserDetails') }

  4. 结合注释() => import(/* webpackChunkName: "group-user" */ './UserDetails.vue')可以做webpack代码分块

    vite中结合rollupOptions定义分块

  5. 路由中不能运用异步组件


知其所以然

component (和 components) 配备假设接收一个回来 Promise 组件的函数,Vue Router 只会在第一次进入页面时才会获取这个函数,然后运用缓存数据。

github1s.com/vuejs/route…


29-ref和reactive异同

这是Vue3数据照应式中非常重要的两个概念,天然的,跟我们写代码联络也很大。


领会

ref:vuejs.org/api/reactiv…

const count = ref(0)
console.log(count.value) // 0
​
count.value++
console.log(count.value) // 1

reactive:vuejs.org/api/reactiv…

const obj = reactive({ count: 0 })
obj.count++

答复思路

  1. 两者概念
  2. 两者运用场景
  3. 两者异同
  4. 运用细节
  5. 原理

答复模范

  1. ref接收内部值(inner value)回来照应式Ref政策,reactive回来照应式署理政策
  2. 从定义上看ref一般用于处理单值的照应式,reactive用于处理政策类型的数据照应式
  3. 两者均是用于结构照应式数据,但是ref首要处理原始值的照应式问题
  4. ref回来的照应式数据在JS中运用需求加上.value才华访问其值,在视图中运用会自动脱ref,不需求.value;ref可以接收政策或数组等非原始值,但内部依然是reactive完毕照应式;reactive内部假设接收Ref政策会自动脱ref;运用翻开运算符(…)翻开reactive回来的照应式政策会使其失掉照应性,可以结合toRefs()将值转换为Ref政策之后再翻开。
  5. reactive内部运用Proxy署理传入政策并阻挠该政策各种操作(trap),然后完毕照应式。ref内部封装一个RefImpl类,并设置get value/set value,阻挠用户对值的访问,然后完毕照应式。

知其所以然

reactive完毕照应式:

github1s.com/vuejs/core/…

ref完毕照应式:

github1s.com/vuejs/core/…


30-watch和watchEffect异同

我们常常性需求侦测照应式数据的改动,vue3中除了watch之外又出现了watchEffect,不少同学会稠浊这两个api。


领会

watchEffect当即作业一个函数,然后被动地追踪它的依托,当这些依托改动时从头实行该函数。

Runs a function immediately while reactively tracking its dependencies and re-runs it whenever the dependencies are changed.

const count = ref(0)
​
watchEffect(() => console.log(count.value))
// -> logs 0
​
count.value++
// -> logs 1

watch侦测一个或多个照应式数据源并在数据源改动时调用一个回调函数。

Watches one or more reactive data sources and invokes a callback function when the sources change.

const state = reactive({ count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
  /* ... */
  }
)

思路

  1. 给出两者定义
  2. 给出场景上的不同
  3. 给出运用方法和细节
  4. 原理论说

模范

  1. watchEffect当即作业一个函数,然后被动地追踪它的依托,当这些依托改动时从头实行该函数。watch侦测一个或多个照应式数据源并在数据源改动时调用一个回调函数。
  2. watchEffect(effect)是一种特别watch,传入的函数既是依托收集的数据源,也是回调函数。假设我们不关心照应式数据改动前后的值,仅仅想拿这些数据做些作业,那么watchEffect就是我们需求的。watch更底层,可以接收多种数据源,包括用于依托收集的getter函数,因此它完全可以完毕watchEffect的功用,一起由于可以指定getter函数,依托可以控制的更精确,还能获取数据改动前后的值,因此假设需求这些时我们会运用watch。
  3. watchEffect在运用时,传入的函数会马上实行一次。watch默许情况下并不会实行回调函数,除非我们手动设置immediate选项。
  4. 从完毕上来说,watchEffect(fn)适当于watch(fn,fn,{immediate:true})

知其所以然

watchEffect定义:github1s.com/vuejs/core/…

export function watchEffect(
 effect: WatchEffect,
 options?: WatchOptionsBase
): WatchStopHandle {
 return doWatch(effect, null, options)
}

watch定义如下:github1s.com/vuejs/core/…

export function watch<T = any, Immediate extends Readonly<boolean> = false>(
 source: T | WatchSource<T>,
 cb: any,
 options?: WatchOptions<Immediate>
): WatchStopHandle {
 return doWatch(source as any, cb, options)
}

很明显watchEffect就是一种特其他watch完毕。


31-SPA、SSR的差异是什么

我们现在编写的Vue、React和Angular运用大多数情况下都会在一个页面中,点击链接跳转页面一般是内容切换而非页面跳转,由于出色的用户领会逐步成为干流的开发方法。但一起也会有首屏加载时间长,SEO不和睦的问题,因此有了SSR,这也是为什么面试中会问到两者的差异。

思路分析

  1. 两者概念
  2. 两者优缺点分析
  3. 运用场景差异
  4. 其他选择

答复模范

  1. SPA(Single Page Application)即单页面运用。一般也称为 客户端烘托(Client Side Render), 简称 CSR。SSR(Server Side Render)即 服务端烘托。一般也称为 多页面运用(Mulpile Page Application),简称 MPA。
  2. SPA运用只会初度央求html文件,后续只需求央求JSON数据即可,因此用户领会更好,节约流量,服务端压力也较小。但是首屏加载的时间会变长,并且SEO不和睦。为了处理以上缺点,就有了SSR方案,由于HTML内容在服务器一次性生成出来,首屏加载快,搜索引擎也可以很便当的抓取页面信息。但一起SSR方案也会有功用,开发受限等问题。
  3. 在选择上,假设我们的运用存在首屏加载优化需求,SEO需求时,就可以考虑SSR。
  4. 但并不是只要这一种替代方案,比如对一些不常改动的静态网站,SSR反而浪费资源,我们可以考虑预烘托(prerender)方案。其他nuxt.js/next.js中给我们供应了SSG(Static Site Generate)静态网站生成方案也是很好的静态站点处理方案,结合一些CI方法,可以起到很好的优化作用,且能节约服务器资源。

知其所以然

内容生成上的差异:

SSR

又一个月,1.5W字!50+Vue经典面试题源码级详解,完毕篇!


SPA

又一个月,1.5W字!50+Vue经典面试题源码级详解,完毕篇!


安置上的差异

又一个月,1.5W字!50+Vue经典面试题源码级详解,完毕篇!


32-vue-loader是什么?它有什么作用?

分析

这是一道东西类的原理标题,适当有深度,具有不错的人才差异度。


领会

运用官方供应的SFC playground可以很好的领会vue-loader

sfc.vuejs.org


有了vue-loader加持,我们才华够以SFC的方法快速编写代码。

<template>
 <div class="example">{{ msg }}</div>
</template><script>
export default {
 data() {
  return {
   msg: 'Hello world!',
   }
  },
}
</script><style>
.example {
 color: red;
}
</style>

思路

  • vue-loader是什么东东
  • vue-loader是做什么用的
  • vue-loader何时收效
  • vue-loader怎样作业

答复模范

  1. vue-loader是用于处理单文件组件(SFC,Single-File Component)的webpack loader
  2. 由于有了vue-loader,我们就可以在项目中编写SFC格式的Vue组件,我们可以把代码切开为<template>、<script>和<style>,代码会失常明晰。结合其他loader我们还可以用Pug编写<template>,用SASS编写<style>,用TS编写<script>。我们的<style>还可以单独作用其时组件。
  3. webpack打包时,会以loader的方法调用vue-loader
  4. vue-loader被实行时,它会对SFC中的每个言语块用单独的loader链处理。毕竟将这些单独的块装配成毕竟的组件模块。

知其所以然

  1. vue-loader会调用@vue/compiler-sfc模块解析SFC源码为一个描绘符(Descriptor),然后为每个言语块生成import代码,回来的代码类似下面:
// source.vue被vue-loader处理之后回来的代码// import the <template> block
import render from 'source.vue?vue&type=template'
// import the <script> block
import script from 'source.vue?vue&type=script'
export * from 'source.vue?vue&type=script'
// import <style> blocks
import 'source.vue?vue&type=style&index=1'
​
script.render = render
export default script

  1. 我们想要script块中的内容被作为js处理(当然假设是<script lang="ts">被作为ts处理),这样我们想要webpack把配备中跟.js匹配的规则都运用到形如source.vue?vue&type=script的这个央求上。例如我们对全部*.js配备了babel-loader,这个规则将被克隆并运用到地址Vue SFC的
import script from 'source.vue?vue&type=script'

将被翻开为:

import script from 'babel-loader!vue-loader!source.vue?vue&type=script'

类似的,假设我们对.sass文件配备了style-loader + css-loader + sass-loader,对下面的代码:

<style scoped lang="scss">

vue-loader将会回来给我们下面作用:

import 'source.vue?vue&type=style&index=1&scoped&lang=scss'

然后webpack会翻开如下:

import 'style-loader!css-loader!sass-loader!vue-loader!source.vue?vue&type=style&index=1&scoped&lang=scss'
  1. 当处理翻开央求时,vue-loader将被再次调用。这次,loader将会注重那些有查询串的央求,且仅针对特定块,它会选中特定块内部的内容并传递给后边匹配的loader。
  2. 关于<script>块,处理到这就可以了,但是<template><style>还有一些额定任务要做,比如:
  • 需求用Vue 模板编译器编译template,然后得到render函数
  • 需求对<style scoped>中的CSS做后处理(post-process),该操作在css-loader之后但在style-loader之前

完毕上这些附加的loader需求被注入到现已翻开的loader链上,毕竟的央求会像下面这样:

// <template lang="pug">
import 'vue-loader/template-loader!pug-loader!source.vue?vue&type=template'// <style scoped lang="scss">
import 'style-loader!vue-loader/style-post-loader!css-loader!sass-loader!vue-loader!source.vue?vue&type=style&index=1&scoped&lang=scss'

33-你写过自定义指令吗?运用场景有哪些?

分析

这是一道API题,我们或许写的自定义指令少,但是我们用的多呀,多举几个比如就行。


领会

定义一个包括类似组件生命周期钩子的政策,钩子函数会接收指令挂钩的dom元素:

const focus = {
 mounted: (el) => el.focus()
}
​
export default {
 directives: {
  // enables v-focus in template
  focus
  }
}
<input v-focus />
<input v-focus />

思路

  1. 定义
  2. 何时用
  3. 怎样用
  4. 常用指令
  5. vue3改动

答复模范

  1. Vue有一组默许指令,比如v-model或v-for,一起Vue也答运用户注册自定义指令来扩展Vue才能

  2. 自定义指令首要完毕一些可复用低层级DOM操作

  3. 运用自定义指令分为定义、注册和运用三步:

    • 定义自定义指令有两种方法:政策和函数方法,前者类似组件定义,有各种生命周期;后者只会在mounted和updated时实行
    • 注册自定义指令类似组件,可以运用app.directive()全局注册,运用{directives:{xxx}}部分注册
    • 运用时在注册称谓前加上v-即可,比如v-focus
  4. 我在项目中常用到一些自定义指令,例如:

    • 复制粘贴 v-copy
    • 长按 v-longpress
    • 防抖 v-debounce
    • 图片懒加载 v-lazy
    • 按钮权限 v-premission
    • 页面水印 v-waterMarker
    • 拖拽指令 v-draggable
  5. vue3中指令定义产生了比较大的改动,首要是钩子的称谓坚持和组件一起,这样开发人员简略记忆,不易犯错。其他在v3.2之后,可以在setup中以一个小写v开头便当的定义自定义指令,更简略了!


知其所以然

编译后的自定义指令会被withDirective函数装饰,进一步处理生成的vnode,增加到特定特色中。

sfc.vuejs.org/#eyJBcHAudn…


34-说下$attrs和$listeners的运用场景

分析

API查询,但$attrs和$listeners是比较少用的边界常识,并且vue3有改动,$listeners现已移除,仍是有细节可说的。


思路

  1. 这两个api的作用
  2. 运用场景分析
  3. 运用方法和细节
  4. vue3改动

领会

一个包括组件透传特色的政策。

An object that contains the component’s fallthrough attributes.

<template>
    <child-component v-bind="$attrs">
     将非特色特性透传给内部的子组件
  </child-component>
</template>

模范

  1. 我们或许会有一些特色和作业没有在props中定义,这类称为非特色特性,结合v-bind指令可以直接透传给内部的子组件。
  2. 这类“特色透传”常常用于包装高阶组件时往内部传递特色,常用于爷孙组件之间传参。比如我在扩展A组件时创建了组件B组件,然后在C组件中运用B,此时传递给C的特色中只要props里面声明的特色是给B运用的,其他的都是A需求的,此时就可以运用v-bind=”$attrs”透传下去。
  3. 最常见用法是结合v-bind做翻开;$attrs本身不是照应式的,除非访问的特色本身是照应式政策。
  4. vue2中运用listeners获取作业,vue3中已移除,均合并到listeners获取作业,vue3中已移除,均合并到attrs中,运用起来更简略了。

原理

查看透传特色foo和普通特色bar,发现vnode结构完全相同,这说明vue3中将分辨两者作业由结构完毕而非用户指定:

<template>
 <h1>{{ msg }}</h1>
 <comp foo="foo" bar="bar" />
</template>
<template>
 <div>
  {{$attrs.foo}} {{bar}}
 </div>
</template>
<script setup>
defineProps({
 bar: String
})
</script>
_createVNode(Comp, {
  foo: "foo",
  bar: "bar"
})

sfc.vuejs.org/#eyJBcHAudn…


34-v-once的运用场景有哪些?

分析

v-once是Vue中内置指令,很有用的API,在优化方面常常会用到,不过小伙伴们平常或许简略疏忽它。


领会

仅烘托元素和组件一次,并且跳过未来更新

Render the element and component once only, and skip future updates.

<!-- single element -->
<span v-once>This will never change: {{msg}}</span>
<!-- the element have children -->
<div v-once>
 <h1>comment</h1>
 <p>{{msg}}</p>
</div>
<!-- component -->
<my-component v-once :comment="msg"></my-component>
<!-- `v-for` directive -->
<ul>
 <li v-for="i in list" v-once>{{i}}</li>
</ul>

思路

  1. v-once是什么
  2. 什么时分运用
  3. 怎样运用
  4. 扩展v-memo
  5. 探索原理

答复模范

  1. v-once是vue的内置指令,作用是仅烘托指定组件或元素一次,并跳过未来对其更新。
  2. 假设我们有一些元素或许组件在初始化烘托之后不再需求改动,这种情况下合适运用v-once,这样哪怕这些数据改动,vue也会跳过更新,是一种代码优化方法。
  3. 我们只需求作用的组件或元素上加上v-once即可。
  4. vue3.2之后,又增加了v-memo指令,可以有条件缓存部分模板并控制它们的更新,可以说控制力更强了。
  5. 编译器发现元素上面有v-once时,会将初度核算作用存入缓存政策,组件再次烘托时就会从缓存获取,然后避免再次核算。

知其所以然

下面比如运用了v-once:

<script setup>
import { ref } from 'vue'const msg = ref('Hello World!')
</script><template>
 <h1 v-once>{{ msg }}</h1>
 <input v-model="msg">
</template>

我们发现v-once出现后,编译器会缓存作用元素或组件,然后避免今后更新时从头核算这一部分:

// ...
return (_ctx, _cache) => {
 return (_openBlock(), _createElementBlock(_Fragment, null, [
  // 从缓存获取vnode
  _cache[0] || (
   _setBlockTracking(-1),
   _cache[0] = _createElementVNode("h1", null, [
    _createTextVNode(_toDisplayString(msg.value), 1 /* TEXT */)
    ]),
   _setBlockTracking(1),
   _cache[0]
   ),
// ...

35-什么是递归组件?举个比如说明下?

分析

递归组件我们用的比较少,但是在Tree、Menu这类组件中会被用到。


领会

组件通过组件称谓引证它自己,这种情况就是递归组件。

An SFC can implicitly refer to itself via its filename.

<template>
 <li>
  <div> {{ model.name }}</div>
  <ul v-show="isOpen" v-if="isFolder">
   <!-- 留心这儿:组件递归烘托了它自己 -->
   <TreeItem
    class="item"
    v-for="model in model.children"
    :model="model">
   </TreeItem>
  </ul>
 </li>
<script>
export default {
 name: 'TreeItem',
 // ...
}
</script>

思路

  • 下定义
  • 运用场景
  • 运用细节
  • 原理论说

答复模范

  1. 假设某个组件通过组件称谓引证它自己,这种情况就是递归组件。
  2. 实践开发中类似Tree、Menu这类组件,它们的节点往往包括子节点,子节点结构和父节点往往是相同的。这类组件的数据往往也是树形结构,这种都是运用递归组件的典型场景。
  3. 运用递归组件时,由于我们并未也不能在组件内部导入它自己,所以设置组件name特色,用来查找组件定义,假设运用SFC,则可以通过SFC文件名揣度。组件内部一般也要有递归完毕条件,比如model.children这样的判别。
  4. 查看生成烘托函数可知,递归组件查找时会传递一个布尔值给resolveComponent,这样实践获取的组件就是其时组件本身。

知其所以然

递归组件编译作用中,获取组件时会传递一个标识符 _resolveComponent("Comp", true)

const _component_Comp = _resolveComponent("Comp", true)

就是在传递maybeSelfReference

export function resolveComponent(
 name: string,
 maybeSelfReference?: boolean
): ConcreteComponent | string {
 return resolveAsset(COMPONENTS, name, true, maybeSelfReference) || name
}

resolveAsset中毕竟回来的是组件本身:

if (!res && maybeSelfReference) {
  // fallback to implicit self-reference
  return Component
}

github1s.com/vuejs/core/…

github1s.com/vuejs/core/…

sfc.vuejs.org/#eyJBcHAudn…


36-异步组件是什么?运用场景有哪些?

分析

由于异步路由的存在,我们运用异步组件的次数比较少,因此仍是有必要两者的不同。

领会

大型运用中,我们需求切开运用为更小的块,并且在需求组件时再加载它们。

In large applications, we may need to divide the app into smaller chunks and only load a component from the server when it’s needed.

import { defineAsyncComponent } from 'vue'
// defineAsyncComponent定义异步组件
const AsyncComp = defineAsyncComponent(() => {
 // 加载函数回来Promise
 return new Promise((resolve, reject) => {
  // ...可以从服务器加载组件
  resolve(/* loaded component */)
  })
})
// 凭仗打包东西完毕ES模块动态导入
const AsyncComp = defineAsyncComponent(() =>
 import('./components/MyComponent.vue')
)

思路

  1. 异步组件作用
  2. 何时运用异步组件
  3. 运用细节
  4. 和路由懒加载的不同

模范

  1. 在大型运用中,我们需求切开运用为更小的块,并且在需求组件时再加载它们。
  2. 我们不只可以在路由切换时懒加载组件,还可以在页面组件中继续运用异步组件,然后完毕更细的切开粒度。
  3. 运用异步组件最简略的方法是直接给defineAsyncComponent指定一个loader函数,结合ES模块动态导入函数import可以快速完毕。我们甚至可以指定loadingComponent和errorComponent选项然后给用户一个很好的加载反应。其他Vue3中还可以结合Suspense组件运用异步组件。
  4. 异步组件简略和路由懒加载稠浊,实践上不是一个东西。异步组件不能被用于定义懒加载路由上,处理它的是vue结构,处理路由组件加载的是vue-router。但是可以在懒加载的路由组件中运用异步组件。

知其所以然

defineAsyncComponent定义了一个高阶组件,回来一个包装组件。包装组件根据加载器的情况抉择烘托什么内容。

github1s.com/vuejs/core/…


37-你是怎样处理vue项目中的差错的?

分析

这是一个归纳运用标题,在项目中我们常常需求将App的失常上报,此时差错处理就很重要了。

这儿要差异差错的类型,针对性做收集。

然后是将收集的的差错信息上报服务器。


思路

  1. 首要差异差错类型
  2. 根据差错不同类型做相应收集
  3. 收集的差错是怎样上报服务器的

答复模范

  1. 运用中的差错类型分为”接口失常“和“代码逻辑失常
  2. 我们需求根据不同差错类型做相应处理:接口失常是我们央求后端接口进程中产生的失常,或许是央求失败,也或许是央求获得了服务器照应,但是回来的是差错情况。以Axios为例,这类失常我们可以通过封装Axios,在阻挠器中一起处理整个运用中央求的差错。代码逻辑失常是我们编写的前端代码中存在逻辑上的差错构成的失常,vue运用中最常见的方法是运用全局差错处理函数app.config.errorHandler收集差错。
  3. 收集到差错之后,需求一起处理这些失常:分析差错,获取需求差错信息和数据。这儿应该有用差异差错类型,假设是央求差错,需求上报接口信息,参数,情况码等;关于前端逻辑失常,获取差错称谓和概况即可。其他还可以收集运用称谓、环境、版别、用户信息,地址页面等。这些信息可以通过vuex存储的全局情况和路由信息获取。

实践

axios阻挠器中处理捕获失常:

// 照应阻挠器
instance.interceptors.response.use(
  (response) => {
  return response.data;
  },
  (error) => {
  // 存在response说明服务器有照应
  if (error.response) {
   let response = error.response;
   if (response.status >= 400) {
    handleError(response);
    }
   } else {
   handleError(null);
   }
  return Promise.reject(error);
  },
);

vue中全局捕获失常:

import { createApp } from 'vue'const app = createApp(...)
​
app.config.errorHandler = (err, instance, info) => {
 // report error to tracking services
}

处理接口央求差错:

function handleError(error, type) {
 if(type == 1) {
  // 接口差错,从config字段中获取央求信息
  let { url, method, params, data } = error.config
  let err_data = {
    url, method,
    params: { query: params, body: data },
    error: error.data?.message || JSON.stringify(error.data),
   })
  }
}

处理前端逻辑差错:

function handleError(error, type) {
 if(type == 2) {
  let errData = null
  // 逻辑差错
  if(error instanceof Error) {
   let { name, message } = error
   errData = {
    type: name,
    error: message
    }
   } else {
   errData = {
    type: 'other',
    error: JSON.strigify(error)
    }
   }
  }
}

38-假设让你从零初步写一个vuex,说说你的思路

思路分析

这个标题很有难度,首要考虑vuex处理的问题:存储用户全局情况并供应办理情况API。

  • vuex需求分析
  • 怎样完毕这些需求

答复模范

  1. 官方说vuex是一个情况办理方法和库,并确保这些情况以可预期的方法改动。可见要完毕一个vuex

    • 要完毕一个Store存储全局情况
    • 要供应批改情况所需API:commit(type, payload), dispatch(type, payload)
  2. 完毕Store时,可以定义Store类,结构函数接收选项options,设置特色state对外露出情况,供应commit和dispatch批改特色state。这儿需求设置state为照应式政策,一起将Store定义为一个Vue插件。

  3. commit(type, payload)方法中可以获取用户传入mutations并实行它,这样可以按用户供应的方法批改情况。 dispatch(type, payload)类似,但需求留心它或许是异步的,需求回来一个Promise给用户以处理异步作用。


实践

Store的完毕:

class Store {
  constructor(options) {
    this.state = reactive(options.state)
    this.options = options
   }
  commit(type, payload) {
    this.options.mutations[type].call(this, this.state, payload)
   }
}

知其所以然

Vuex中Store的完毕:

github1s.com/vuejs/vuex/…

39-vuex中actions和mutations有什么差异?

标题分析

mutationsactionsvuex带来的两个一起的概念。新手程序员简略稠浊,所以面试官喜欢问。

我们只需记住批改情况只能是mutationsactions只能通过提交mutation批改情况即可。


领会

看下面比如可知,Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接改动情况。
  • Action 可以包括任意异步操作。
const store = createStore({
 state: {
  count: 0
  },
 mutations: {
  increment (state) {
   state.count++
   }
  },
 actions: {
  increment (context) {
   context.commit('increment')
   }
  }
})

答题思路

  1. 给出两者概念说明差异
  2. 举例说明运用场景
  3. 运用细节不同
  4. 简略论说完毕上差异

答复模范

  1. 官方文档说:更改 Vuex 的 store 中的情况的仅有方法是提交 mutationmutation 非常类似于作业:每个 mutation 都有一个字符串的类型 (type)和一个 回调函数 (handler)Action 类似于 mutation,不同在于:Action可以包括任意异步操作,但它不能批改情况, 需求提交mutation才华改动情况。
  2. 因此,开发时,包括异步操作或许凌乱业务组合时运用action;需求直接批改情况则提交mutation。但由于dispatch和commit是两个API,简略引起稠浊,实践中也会选用一起运用dispatch action的方法。
  3. 调用dispatch和commit两个API时几乎完全相同,但是定义两者时却不甚相同,mutation的回调函数接收参数是state政策。action则是与Store实例具有相同方法和特色的上下文context政策,因此一般会解构它为{commit, dispatch, state},然后便当编码。其他dispatch会回来Promise实例便于处理内部异步作用。
  4. 完毕上commit(type)方法适当于调用options.mutations[type](state)dispatch(type)方法适当于调用options.actions[type](store),这样就很简略了解两者运用上的不同了。

知其所以然

我们可以像下面这样简略完毕commitdispatch,然后区分两者不同:

class Store {
  constructor(options) {
    this.state = reactive(options.state)
    this.options = options
   }
  commit(type, payload) {
    // 传入上下文和参数1都是state政策
    this.options.mutations[type].call(this.state, this.state, payload)
   }
  dispatch(type, payload) {
    // 传入上下文和参数1都是store本身
    this.options.actions[type].call(this, this, payload)
   }
}

40-运用vue烘托许多数据时应该怎样优化?说下你的思路!

分析

企业级项目中烘托许多数据的情况比较常见,因此这是一道非常好的归纳实践标题。

思路

  1. 描绘大数据量带来的问题
  2. 分不同情况做不同处理
  3. 总结一下

答复

  1. 在大型企业级项目中常常需求烘托许多数据,此时很简略出现卡顿的情况。比如大数据量的表格、树。

  2. 处理时要根据情况做不通处理:

    • 可以采用分页的方法获取,避免烘托许多数据
    • vue-virtual-scroller等虚拟翻滚方案,只烘托视口规划内的数据
    • 假设不需求更新,可以运用v-once方法只烘托一次
    • 通过v-memo可以缓存作用,结合v-for运用,避免数据改动时不用要的VNode创建
    • 可以选用懒加载方法,在用户需求的时分再加载数据,比如tree组件子树的懒加载
  3. 总之,仍是要看具体需求,首要从规划上避免大数据获取和烘托;真实需求这样做可以选用虚表的方法优化烘托;毕竟优化更新,假设不需求更新可以v-once处理,需求更新可以v-memo进一步优化大数据更新功用。其他可以选用的是交互方法优化,无线翻滚、懒加载等方案。

41-怎样监听vuex数据的改动?

分析

vuex数据情况是照应式的,所以情况变视图跟着变,但是有时仍是需求知道数据情况变了然后做一些作业。

既然情况都是照应式的,那天然可以watch,其他vuex也供应了订阅的API:store.subscribe()


思路

  • 总述知道的方法
  • 分别论说用法
  • 选择和场景

答复模范

  • 我知道几种方法:

    • 可以通过watch选项或许watch方法监听情况
    • 可以运用vuex供应的API:store.subscribe()
  • watch选项方法,可以以字符串方法监听$store.state.xx;subscribe方法,可以调用store.subscribe(cb),回调函数接收mutation政策和state政策,这样可以进一步判别mutation.type是否是等待的那个,然后进一步做后续处理。

  • watch方法简略好用,且能获取改动前后值,首选;subscribe方法会被全部commit行为触发,因此还需求判别mutation.type,用起来略繁琐,一般用于vuex插件中。

实践

watch方法

const app = createApp({
  watch: {
   '$store.state.counter'() {
    console.log('counter change!');
    }
   }
  })

subscribe方法:

 store.subscribe((mutation, state) => {
  if (mutation.type === 'add') {
   console.log('counter change in subscribe()!');
   }
  })

42-router-link和router-view是怎样起作用的?

分析

vue-router中两个重要组件router-linkrouter-view,分别起到导航作用和内容烘托作用,但是答复怎样收效还真有必定难度哪!

思路

  • 两者作用
  • 论说运用方法
  • 原理说明

答复模范

  • vue-router中两个重要组件router-linkrouter-view,分别起到路由导航作用和组件内容烘托作用
  • 运用中router-link默许生成一个a标签,设置to特色定义跳转path。实践上也可以通过custom和插槽自定义毕竟的展示方法。router-view是要闪现组件的占位组件,可以嵌套,对应路由配备的嵌套联络,合作name可以闪现具名组件,起到更强的布局作用。
  • router-link组件内部根据custom特色判别怎样烘托毕竟生成节点,内部供应导航方法navigate,用户点击之后实践调用的是该方法,此方法毕竟会批改照应式的路由变量,然后从头去routes匹配出数组作用,router-view则根据其所处深度deep在匹配数组作用中找到对应的路由并获取组件,毕竟将其烘托出来。

知其所以然

  • RouterLink定义

github1s.com/vuejs/route…

  • RouterView定义

github1s.com/vuejs/route…

43-Vue-router 除了 router-link 怎样完毕跳转

分析

vue-router导航有两种方法:声明式导航编程方法导航


领会

声明式导航

<router-link to="/about">Go to About</router-link>

编程导航

// literal string path
router.push('/users/eduardo')
​
// object with path
router.push({ path: '/users/eduardo' })
​
// named route with params to let the router build the url
router.push({ name: 'user', params: { username: 'eduardo' } })

思路

  • 两种方法
  • 分别论说运用方法
  • 差异和选择
  • 原理说明

答复模范

  • vue-router导航有两种方法:声明式导航编程方法导航
  • 声明式导航方法运用router-link组件,增加to特色导航;编程方法导航愈加灵敏,可传递调用router.push(),并传递path字符串或许RouteLocationRaw政策,指定path、name、params等信息
  • 假设页面中简略标明跳转链接,运用router-link最便当,会烘托一个a标签;假设页面是个凌乱的内容,比如商品信息,可以增加点击作业,运用编程式导航
  • 实践上内部两者调用的导航函数是相同的

知其所以然

github1s.com/vuejs/route…

routerlink点击跳转,调用的是navigate方法

又一个月,1.5W字!50+Vue经典面试题源码级详解,完毕篇!

navigate内部依然调用的push

44-Vue3.0 功用进步体现在哪些方面?

分析

vue3在规划时有几个政策:更小、更快、更和睦,这些多数合适功用相关,因此可以环绕介绍。


思路

  • 总述和功用相关的新特性
  • 逐一说细节
  • 能说点原理更佳

答复模范

  • 我分别从代码、编译、打包三方面介绍vue3功用方面的进步
  • 代码层面功用优化首要体现在全新照应式API,根据Proxy完毕,初始化时间和内存占用均大幅改进;
  • 编译层面做了更多编译优化处理,比如静态进步、动态符号、作业缓存,区块等,可以有用跳过许多diff进程;
  • 打包时更好的支撑tree-shaking,因此全体体积更小,加载更快

领会

通过playground领会编译优化:sfc.vuejs.org

image-20220626184606427


知其所以然

为什么根据Proxy更快了:初始化时懒处理,用户访问才做阻挠处理,初始化更快:

github1s.com/vuejs/core/…

轻量的依托联络保存:运用WeakMap、Map和Set保存照应式数据和副作用之间的依托联络

github1s.com/vuejs/core/…

45-Vue3.0里为什么要用 Proxy 替代 defineProperty ?

分析

Vue3中最严峻的更新之一就是照应式模块reactivity的重写。首要的批改就是Proxy替换defineProperty完毕照应式。

此改动首要是从功用方面考量。

思路

  • 特色阻挠的几种方法
  • defineProperty的问题
  • Proxy的长处
  • 其他考量

答复模范

  • JS中做特色阻挠常见的方法有三:: defineProperty,getter/setters 和Proxies.
  • Vue2中运用defineProperty的原因是,2013年时只能用这种方法。由于该API存在一些局限性,比如关于数组的阻挠有问题,为此vue需求专门为数组照应式做一套完毕。其他不能阻挠那些新增、删除特色;毕竟defineProperty方案在初始化时需求深度递归遍历待处理的政策才华对它进行完全阻挠,明显增加了初始化的时间。
  • 以上两点在Proxy出现之后便当的处理,不只可以对数组完毕阻挠,还能对Map、Set完毕阻挠;其他Proxy的阻挠也是懒处理行为,假设用户没有访问嵌套政策,那么也不会施行阻挠,这就让初始化的速度和内存占用都改进了。
  • 当然Proxy是有兼容性问题的,IE完全不支撑,所以假设需求IE兼容就不合适

知其所以然

Proxy特色阻挠的原理:运用get、set、deleteProperty这三个trap完毕阻挠

function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {},
    set(target, key, val) {},
    deleteProperty(target, key){}
   })
}

Object.defineProperty特色阻挠原理:运用get、set这两个trap完毕阻挠

function defineReactive(obj, key, val) {
  Object.defineReactive(obj, key, {
    get(key) {},
    set(key, val) {}
   })
}

很简略看出两者的差异!


46-History方法和Hash方法有何差异?

分析

vue-router有3个方法,其间两个更为常用,那就是history和hash。

两者不同首要在闪现方法和安置上。


领会

vue-router4.x中设置方法现已改动:

const router = createRouter({
 history: createWebHashHistory(), // hash方法
 history: createWebHistory(),   // history方法
})

用起来一模相同

<router-link to="/about">Go to About</router-link>

差异只在url方法

// hash
// 浏览器里的形状:http://xx.com/#/about
// history
// 浏览器里的形状:http://xx.com/about

思路

  • 差异
  • 具体论说
  • 完毕

答复模范

  • vue-router有3个方法,其间history和hash更为常用。两者不同首要在闪现方法、搜索引擎优化和安置上。
  • hash方法在地址栏闪现的时分是已哈希的方法:#/xxx,这种方法运用和安置简略,但是不会被搜索引擎处理,搜索引擎优化有问题;history方规律主张用在大部分web项目上,但是它要求运用在安置时做特别配备,服务器需求做回退处理,否则会出现改写页面404的问题。
  • 底层完毕上其实hash是一种特其他history完毕。

知其所以然

hash是一种特其他history完毕:

github1s.com/vuejs/route…


47-在什么场景下会用到嵌套路由?

分析

运用的有些界面是由多层级组件组合而来的,这种情况下,url各部分一般对应某个嵌套的组件,vue-router中就可以运用嵌套路由标明这种联络:router.vuejs.org/guide/essen…

又一个月,1.5W字!50+Vue经典面试题源码级详解,完毕篇!


领会

定义嵌套路由,对应上图嵌套联络:

const routes = [
  {
  path: '/user/:id',
  component: User,
  children: [
    {
    // UserProfile 会被烘托在 User 组件中的 <router-view> 里
    path: 'profile',
    component: UserProfile,
    },
    {
    // UserPosts 会被烘托在 User 组件中的 <router-view> 里
    path: 'posts',
    component: UserPosts,
    },
   ],
  },
]

思路

  • 概念和运用场景
  • 运用方法
  • 完毕原理

答复模范

  • 平常开发中,运用的有些界面是由多层级组件组合而来的,这种情况下,url各部分一般对应某个嵌套的组件,vue-router中可以运用嵌套路由标明这种联络
  • 体现方法是在两个路由间切换时,它们有公用的视图内容。此时一般提取一个父组件,内部放上,然后构成物理上的嵌套,和逻辑上的嵌套对应起来
  • 定义嵌套路由时运用children特色安排嵌套联络
  • 原理上是在router-view组件内部判别其时router-view处于嵌套层级的深度,讲这个深度作为匹配组件数组matched的索引,获取对应烘托组件,烘托之

知其所以然

router-view获取自己地址的深度:默许0,加1之后传给子孙,一起根据深度获取匹配路由。

又一个月,1.5W字!50+Vue经典面试题源码级详解,完毕篇!


48-页面改写后vuex的state数据丢掉怎样处理?

分析

这是一道运用标题,很简略想到运用localStorage或数据库存储并还原情况。

但是怎样典雅编写代码仍是能体现认知水平。


领会

可以从localStorage中获取作为情况初始值:

const store = createStore({
 state () {
  return {
   count: localStorage.getItem('count')
   }
  }
})

业务代码中,提交批改情况一起保存最新值:虽说完毕了,但是每次还要手动改写localStorage不太典雅

store.commit('increment')
localStorage.setItem('count', store.state.count)

思路

  • 问题描绘
  • 处理方法
  • 谈个人了解
  • 三方库原理讨论

答复模范

  • vuex仅仅在内存保存情况,改写之后就会丢掉,假设要耐久化就要存起来。
  • localStorage就很合适,提交mutation的时分一起存入localStorage,store中把值取出作为state的初始值即可。
  • 这儿有两个问题,不是全部情况都需求耐久化;假设需求保存的情况许多,编写的代码就不可典雅,每个提交的当地都要单独做保存处理。这儿就可以运用vuex供应的subscribe方法做一个一起的处理。甚至可以封装一个vuex插件以便复用。
  • 类似的插件有vuex-persist、vuex-persistedstate,内部的完毕就是通过订阅mutation改动做一起处理,通过插件的选项控制哪些需求耐久化

知其所以然

可以看一下vuex-persist内部确实是运用subscribe完毕的

github.com/championswi…


49-你觉得vuex有什么缺点?

分析

相较于redux,vuex现已适当简练好用了。但模块的运用比较繁琐,对ts支撑也欠好。


领会

运用模块:用起来比较繁琐,运用方法也不一起,底子上得不到类型系统的任何支撑

const store = createStore({
 modules: {
  a: moduleA
  }
})
store.state.a // -> 要带上 moduleA 的key,内嵌模块的话会很长,不得不合作mapState运用
store.getters.c // -> moduleA里的getters,没有namespaced时又变成了全局的
store.getters['a/c'] // -> 有namespaced时要加path,运用方法又和state不相同
store.commit('d') // -> 没有namespaced时变成了全局的,能一起触发多个子模块中同名mutation
store.commit('a/d') // -> 有namespaced时要加path,合作mapMutations运用感觉也没简化

思路

  • 先夸再贬
  • 运用感受
  • 处理方案

答复模范

  • vuex运用照应式,运用起来现已适当便当便当了。但是在运用进程中感觉模块化这一块做的过于凌乱,用的时分简略犯错,还要常常查看文档
  • 比如:访问state时要带上模块key,内嵌模块的话会很长,不得不合作mapState运用,加不加namespaced差异也很大,getters,mutations,actions这些默许是全局,加上之后有必要用字符串类型的path来匹配,运用方法不一起,简略犯错;对ts的支撑也不和睦,在运用模块时没有代码提示。
  • 之前Vue2项目中用过vuex-module-decorators的处理方案,虽然类型支撑上有所改进,但又要学一套新东西,增加了学习本钱。pinia出现之后运用领会好了许多,Vue3 + pinia会是更好的组合。

知其所以然

下面我们来看看vuex中store.state.x.y这种嵌套的途径是怎样搞出来的。

首要是子模块设备进程:父模块情况parentState上面设置了子模块称谓moduleName,值为其时模块state政策。放在上面的比如中适当于:store.state['x'] = moduleX.state。此进程是递归的,那么store.state.x.y设备时就是:store.state['x']['y'] = moduleY.state

if (!isRoot && !hot) {
  // 获取父模块state
  const parentState = getNestedState(rootState, path.slice(0, -1))
  // 获取子模块称谓
  const moduleName = path[path.length - 1]
  store._withCommit(() => {
    // 把子模块state设置到父模块上
    parentState[moduleName] = module.state
   })
}

这下我们了解了吧!

源码地址:github1s.com/vuejs/vuex/…


50-Composition API 与 Options API 有什么不同

分析

Vue3最重要更新之一就是Composition API,它具有一些列长处,其间不少是针对Options API露出的一些问题量身打造。是Vue3引荐的写法,因此掌握好Composition API运用对掌握好Vue3至关重要。

又一个月,1.5W字!50+Vue经典面试题源码级详解,完毕篇!

vuejs.org/guide/extra…


领会

Composition API能更好的安排代码,下面这个代码用options api完毕

又一个月,1.5W字!50+Vue经典面试题源码级详解,完毕篇!

假设用composition api可以提取为useCount(),用于组合、复用

又一个月,1.5W字!50+Vue经典面试题源码级详解,完毕篇!

又一个月,1.5W字!50+Vue经典面试题源码级详解,完毕篇!


思路

  • 总述不同点
  • composition api动机
  • 两者选择

答复模范

  • Composition API是一组API,包括:Reactivity API、生命周期钩子、依托注入,运用户可以通过导入函数方法编写vue组件。而Options API则通过声明组件选项的政策方法编写组件。
  • Composition API最首要作用是可以简练、高效复用逻辑。处理了以前Options APImixins的各种缺点;其他Composition API具有愈加灵敏的代码安排才能,许多用户喜欢Options API,以为全部东西都有固定位置的选项放置代码,但是单个组件增长过大之后这反而成为限制,一个逻辑注重点涣散在组件各处,构成代码碎片,保护时需求重复横跳,Composition API则可以将它们有用安排在一起。毕竟Composition API具有更好的类型揣度,对ts支撑更和睦,Options API在规划之初并未考虑类型揣度要素,虽然官方为此做了许多凌乱的类型体操,确保用户可以在运用Options API时获得类型揣度,但是仍是没方法用在mixins和provide/inject上。
  • Vue3首推Composition API,但是这会让我们在代码安排上多花点心思,因此在选择上,假设我们项目归于中低凌乱度的场景,Options API仍是一个好选择。关于那些大型,高扩展,强保护的项目上,Composition API会获得更大收益。

或许的诘问

  • Composition API能否和Options API一起运用?

51-vue-router中怎样保护路由?

分析

路由保护在运用开发进程中非常重要,几乎每个运用都要做各种路由权限办理,因此适当查询运用者底子功。


领会

全局护卫:

const router = createRouter({ ... })
​
router.beforeEach((to, from) => {
 // ...
 // 回来 false 以吊销导航
 return false
})

路由独享护卫:

const routes = [
  {
  path: '/users/:id',
  component: UserDetails,
  beforeEnter: (to, from) => {
   // reject the navigation
   return false
   },
  },
]

组件内的护卫:

const UserDetails = {
 template: `...`,
 beforeRouteEnter(to, from) {
  // 在烘托该组件的对应路由被验证前调用
  },
 beforeRouteUpdate(to, from) {
  // 在其时路由改动,但是该组件被复用时调用
  },
 beforeRouteLeave(to, from) {
  // 在导航脱离烘托该组件的对应路由时调用
  },
}

思路

  • 路由护卫的概念
  • 路由护卫的运用
  • 路由护卫的原理

  • vue-router中保护路由的方法叫做路由护卫,首要用来通过跳转或吊销的方法护卫导航。
  • 路由护卫有三个等级:全局,路由独享,组件级。影响规划由大到小,例如全局的router.beforeEach(),可以注册一个全局前置护卫,每次路由导航都会通过这个护卫,因此在其内部可以参与控制逻辑抉择用户是否可以导航到政策路由;在路由注册的时分可以参与单路由独享的护卫,例如beforeEnter,护卫只在进入路由时触发,因此只会影响这个路由,控制更精确;我们还可以为路由组件增加护卫配备,例如beforeRouteEnter,会在烘托该组件的对应路由被验证前调用,控制的规划更精确了。
  • 用户的任何导航行为都会走navigate方法,内部有个guards行列按次序实行用户注册的护卫钩子函数,假设没有通过验证逻辑则会吊销原有的导航。

知其所以然

runGuardQueue(guards)链式的实行用户在各等级注册的护卫钩子函数,通过则继续下一个级其他护卫,不通过进入catch流程吊销原本导航。

又一个月,1.5W字!50+Vue经典面试题源码级详解,完毕篇!

github1s.com/vuejs/route…