2022-09-16Vue00

如果让你从零开始写一个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的实现:

https://github1s.com/vuejs/vuex/blob/HEAD/src/store.js#L19-L20

2022-09-16Vue00

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)
    }
}
2022-09-16Vue00

使用vue渲染大量数据时应该怎么优化?说下你的思路!

分析

企业级项目中渲染大量数据的情况比较常见,因此这是一道非常好的综合实践题目。

思路

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

回答

  1. 在大型企业级项目中经常需要渲染大量数据,此时很容易出现卡顿的情况。比如大数据量的表格、树。
  2. 处理时要根据情况做不通处理:
    • 可以采取分页的方式获取,避免渲染大量数据
    • vue-virtual-scroller等虚拟滚动方案,只渲染视口范围内的数据
    • 如果不需要更新,可以使用v-once方式只渲染一次
    • 通过v-memo可以缓存结果,结合v-for使用,避免数据变化时不必要的VNode创建
    • 可以采用懒加载方式,在用户需要的时候再加载数据,比如tree组件子树的懒加载
  3. 总之,还是要看具体需求,首先从设计上避免大数据获取和渲染;实在需要这样做可以采用虚表的方式优化渲染;最后优化更新,如果不需要更新可以v-once处理,需要更新可以v-memo进一步优化大数据更新性能。其他可以采用的是交互方式优化,无线滚动、懒加载等方案。
2022-09-16Vue00

怎么监听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()!');
    }
  })
2022-09-16Vue00

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定义

https://github1s.com/vuejs/router/blob/HEAD/src/RouterLink.ts#L184-L185

  • RouterView定义

https://github1s.com/vuejs/router/blob/HEAD/src/RouterView.ts#L43-L44