vue 一百问

请说一下响应式数据的理解?

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

Vue 如何检测数组变化?

  • 初始化 instance ->_init->initState->initData->observe->new Observer(value)
  • 在构造函数里区分是否是Array
  • 给Array重写变异方法
    • push
    • pop
    • shift
    • unshift
    • splice
    • sort
    • reverse
重写Array的方法
  • 调用位置
export class Observer {
  value: any;
  dep: Dep;
  vmCount: number; // number of vms that have this object as root $data

  constructor (value: any) {
    this.value = value
    this.dep = new Dep()
    this.vmCount = 0
    def(value, '__ob__', this)
    if (Array.isArray(value)) {
      if (hasProto) {
        protoAugment(value, arrayMethods)
      } else {
        copyAugment(value, arrayMethods, arrayKeys)
      }
      this.observeArray(value)
    } else {
      this.walk(value)
    }
  }

  /**
   * Walk through all properties and convert them into
   * getter/setters. This method should only be called when
   * value type is Object.
   */
  walk (obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }

  /**
   * Observe a list of Array items.
   */
  observeArray (items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }
}










 
 
 
 
 
 
 


























1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  • 垫片 根据不同的浏览器环境做不同的操作
function protoAugment (target, src: Object) {
  /* eslint-disable no-proto */
  target.__proto__ = src
  /* eslint-enable no-proto */
}

function copyAugment (target: Object, src: Object, keys: Array<string>) {
  for (let i = 0, l = keys.length; i < l; i++) {
    const key = keys[i]
    def(target, key, src[key])
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
  • observer/array.js 重写机制
/*
 * not type checking this file because flow doesn't play well with
 * dynamically accessing methods on Array prototype
 */

import { def } from '../util/index'

const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)

const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]

/**
 * Intercept mutating methods and emit events
 */
methodsToPatch.forEach(function (method) {
  // cache original method
  const original = arrayProto[method]
  def(arrayMethods, method, function mutator (...args) {
    const result = original.apply(this, args)
    const ob = this.__ob__
    let inserted
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2)
        break
    }
    if (inserted) ob.observeArray(inserted)
    // notify change
    ob.dep.notify()
    return result
  })
})










































 




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

vue.set 方法是如何实现的?

  • Vue.prototype.$set = set
  • set 方法内部实现方式
    • defineReactive
    • ob.dep.notify() ob 通过Observe 给__ob__赋值 dep在get里赋值 notice 会调用 watcher的update 方法
set方法的实现路径
  • 初始化
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'

function Vue (options) {
  if (process.env.NODE_ENV !== 'production' &&
    !(this instanceof Vue)
  ) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options)
}

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue

















 




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  • $set赋值
export function stateMixin (Vue: Class<Component>) {
  // flow somehow has problems with directly declared definition object
  // when using Object.defineProperty, so we have to procedurally build up
  // the object here.
  const dataDef = {}
  dataDef.get = function () { return this._data }
  const propsDef = {}
  propsDef.get = function () { return this._props }
  if (process.env.NODE_ENV !== 'production') {
    dataDef.set = function () {
      warn(
        'Avoid replacing instance root $data. ' +
        'Use nested data properties instead.',
        this
      )
    }
    propsDef.set = function () {
      warn(`$props is readonly.`, this)
    }
  }
  Object.defineProperty(Vue.prototype, '$data', dataDef)
  Object.defineProperty(Vue.prototype, '$props', propsDef)

  Vue.prototype.$set = set
  Vue.prototype.$delete = del

  Vue.prototype.$watch = function (
    expOrFn: string | Function,
    cb: any,
    options?: Object
  ): Function {
    const vm: Component = this
    if (isPlainObject(cb)) {
      return createWatcher(vm, expOrFn, cb, options)
    }
    options = options || {}
    options.user = true
    const watcher = new Watcher(vm, expOrFn, cb, options)
    if (options.immediate) {
      const info = `callback for immediate watcher "${watcher.expression}"`
      pushTarget()
      invokeWithErrorHandling(cb, vm, [watcher.value], vm, info)
      popTarget()
    }
    return function unwatchFn () {
      watcher.teardown()
    }
  }
}























 

























1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  • set 方法的实现 需要注意__ob__的来源
export function set (target: Array<any> | Object, key: any, val: any): any {
  if (process.env.NODE_ENV !== 'production' &&
    (isUndef(target) || isPrimitive(target))
  ) {
    warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)
  }
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.length = Math.max(target.length, key)
    target.splice(key, 1, val)
    return val
  }
  if (key in target && !(key in Object.prototype)) {
    target[key] = val
    return val
  }
  const ob = (target: any).__ob__
  if (target._isVue || (ob && ob.vmCount)) {
    process.env.NODE_ENV !== 'production' && warn(
      'Avoid adding reactive properties to a Vue instance or its root $data ' +
      'at runtime - declare it upfront in the data option.'
    )
    return val
  }
  if (!ob) {
    target[key] = val
    return val
  }
  defineReactive(ob.value, key, val)
  ob.dep.notify()
  return val
}















 











 
 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  • defineReactive
export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  const dep = new Dep()

  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    return
  }

  // cater for pre-defined getter/setters
  const getter = property && property.get
  const setter = property && property.set
  if ((!getter || setter) && arguments.length === 2) {
    val = obj[key]
  }

  let childOb = !shallow && observe(val)
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        dep.depend()
        if (childOb) {
          childOb.dep.depend()
          if (Array.isArray(value)) {
            dependArray(value)
          }
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      const value = getter ? getter.call(obj) : val
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      /* eslint-enable no-self-compare */
      if (process.env.NODE_ENV !== 'production' && customSetter) {
        customSetter()
      }
      // #7981: for accessor properties without setter
      if (getter && !setter) return
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      childOb = !shallow && observe(newVal)
      dep.notify()
    }
  })
}





















 







 
 
 
 
 
 

























1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
  • __ob__的注入值的位置
export class Observer {
  value: any;
  dep: Dep;
  vmCount: number; // number of vms that have this object as root $data

  constructor (value: any) {
    this.value = value
    this.dep = new Dep()
    this.vmCount = 0
    def(value, '__ob__', this)
    if (Array.isArray(value)) {
      if (hasProto) {
        protoAugment(value, arrayMethods)
      } else {
        copyAugment(value, arrayMethods, arrayKeys)
      }
      this.observeArray(value)
    } else {
      this.walk(value)
    }
  }

  /**
   * Walk through all properties and convert them into
   * getter/setters. This method should only be called when
   * value type is Object.
   */
  walk (obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }

  /**
   * Observe a list of Array items.
   */
  observeArray (items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }
}









 

































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

nextTick 在哪里使用?原理是?

根据浏览器的不同所所以兼容的API也不一样

  • 兼容顺序
    • Promise
    • MutationObserver
    • setImmediate
    • setTimeout
nextTick实现原理
/* @flow */
/* globals MutationObserver */

import { noop } from 'shared/util'
import { handleError } from './error'
import { isIE, isIOS, isNative } from './env'

export let isUsingMicroTask = false

const callbacks = []
let pending = false

function flushCallbacks () {
  pending = false
  const copies = callbacks.slice(0)
  callbacks.length = 0
  for (let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}

// Here we have async deferring wrappers using microtasks.
// In 2.5 we used (macro) tasks (in combination with microtasks).
// However, it has subtle problems when state is changed right before repaint
// (e.g. #6813, out-in transitions).
// Also, using (macro) tasks in event handler would cause some weird behaviors
// that cannot be circumvented (e.g. #7109, #7153, #7546, #7834, #8109).
// So we now use microtasks everywhere, again.
// A major drawback of this tradeoff is that there are some scenarios
// where microtasks have too high a priority and fire in between supposedly
// sequential events (e.g. #4521, #6690, which have workarounds)
// or even between bubbling of the same event (#6566).
let timerFunc

// The nextTick behavior leverages the microtask queue, which can be accessed
// via either native Promise.then or MutationObserver.
// MutationObserver has wider support, however it is seriously bugged in
// UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
// completely stops working after triggering a few times... so, if native
// Promise is available, we will use it:
/* istanbul ignore next, $flow-disable-line */
if (typeof Promise !== 'undefined' && isNative(Promise)) {
  const p = Promise.resolve()
  timerFunc = () => {
    p.then(flushCallbacks)
    // In problematic UIWebViews, Promise.then doesn't completely break, but
    // it can get stuck in a weird state where callbacks are pushed into the
    // microtask queue but the queue isn't being flushed, until the browser
    // needs to do some other work, e.g. handle a timer. Therefore we can
    // "force" the microtask queue to be flushed by adding an empty timer.
    if (isIOS) setTimeout(noop)
  }
  isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
  isNative(MutationObserver) ||
  // PhantomJS and iOS 7.x
  MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
  // Use MutationObserver where native Promise is not available,
  // e.g. PhantomJS, iOS7, Android 4.4
  // (#6466 MutationObserver is unreliable in IE11)
  let counter = 1
  const observer = new MutationObserver(flushCallbacks)
  const textNode = document.createTextNode(String(counter))
  observer.observe(textNode, {
    characterData: true
  })
  timerFunc = () => {
    counter = (counter + 1) % 2
    textNode.data = String(counter)
  }
  isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  // Fallback to setImmediate.
  // Technically it leverages the (macro) task queue,
  // but it is still a better choice than setTimeout.
  timerFunc = () => {
    setImmediate(flushCallbacks)
  }
} else {
  // Fallback to setTimeout.
  timerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
}

export function nextTick (cb?: Function, ctx?: Object) {
  let _resolve
  callbacks.push(() => {
    if (cb) {
      try {
        cb.call(ctx)
      } catch (e) {
        handleError(e, ctx, 'nextTick')
      }
    } else if (_resolve) {
      _resolve(ctx)
    }
  })
  if (!pending) {
    pending = true
    timerFunc()
  }
  // $flow-disable-line
  if (!cb && typeof Promise !== 'undefined') {
    return new Promise(resolve => {
      _resolve = resolve
    })
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111

Vue 父组件和子组件生命周期执行顺序

  • 加载渲染过程 父先创建,才能有子;子创建完成,父才完整。

    • 父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
  • 子组件更新过程 子组件更新 影响到 父组件的情况。

    • 父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
  • 子组件更新 不影响到 父组件的情况。

    • 子 beforeUpdate -> 子 updated
  • 父组件更新过程父组件更新 影响到 子组件的情况。

    • 父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
  • 父组件更新 不影响到 子组件的情况。

    • 父 beforeUpdate -> 父 updated
  • 销毁过程

    • 父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed

Vue 组件间通讯方式

  • 父子组件通讯
    • props 与 $emit
    • parent与children
  • 隔代组件通讯
    • attrs与listeners
    • provide 和 inject
  • 父子、兄弟、隔代组件通讯
    • EventBus
    • Vuex

Vue 中模板编译原理?

解析器(parser)的作用是将 模板字符串 转换成 element ASTs。 优化器(optimizer)的作用是找出那些静态节点和静态根节点并打上标记。 代码生成器(code generator)的作用是使用 element ASTs 生成 render 函数代码

Vue 组件间传值的方式及之间的区别

$attrs 是为了解决什么问题出现的?应用场景有哪些?

生命周期钩子是如何实现的?

生命周期描述
beforeCreatevue实例初始化后,数据观测(data observer)和事件配置之前。data、computed、watch、methods都无法访问。
createdvue实例创建完成后立即调用 ,可访问 data、computed、watch、methods。未挂载 DOM,不能访问el、ref。
beforeMount在 DOM 挂载开始之前调用。
mountedvue实例被挂载到 DOM。
beforeUpdate数据更新之前调用,发生在虚拟 DOM 打补丁之前。
updated数据更新之后调用。
beforeDestroy实例销毁前调用。
destroyed实例销毁后调用 。

vue 生命周期的hook 分为两种

  • 在对象里的常见的生命周期函数

    ::: details vue 生命周期hook实现原理
    
    ```js
      //src/core/instance/init.js
    
      Vue.prototype._init = function (options?: Object) {
        //something ...
    
        initLifecycle(vm)
        initEvents(vm)
        initRender(vm)
        callHook(vm, 'beforeCreate')
        initInjections(vm) // resolve injections before data/props
        initState(vm)
        initProvide(vm) // resolve provide after data/props
        callHook(vm, 'created')
    
        //something ...
      }
    ```
    
    ```js
    ///src/core/instance/lifecycle.js
    export function callHook (vm: Component, hook: string) {
      // #7573 disable dep collection when invoking lifecycle hooks
      pushTarget()
      const handlers = vm.$options[hook]
      const info = `${hook} hook`
      if (handlers) {
        for (let i = 0, j = handlers.length; i < j; i++) {
          invokeWithErrorHandling(handlers[i], vm, null, vm, info)
        }
      }
      if (vm._hasHookEvent) {
        vm.$emit('hook:' + hook)  //这里会触发第二种
      }
      popTarget()
    }
    
    ```
    
    :::
    
  • this.$on("hook:beforeCreate",()=>{})

    vue 生命周期hook实现原理
    ```js {19}
    /// src/core/instance/index.js 
    import { initMixin } from './init'
    import { stateMixin } from './state'
    import { renderMixin } from './render'
    import { eventsMixin } from './events'
    import { lifecycleMixin } from './lifecycle'
    import { warn } from '../util/index'
    
    function Vue (options) {
      if (process.env.NODE_ENV !== 'production' &&
        !(this instanceof Vue)
      ) {
        warn('Vue is a constructor and should be called with the `new` keyword')
      }
      this._init(options)
    }
    
    initMixin(Vue)
    stateMixin(Vue)
    eventsMixin(Vue)
    lifecycleMixin(Vue)
    renderMixin(Vue)
    export default Vue
    ```
    
    ```js  {2,13-15}
    export function eventsMixin (Vue: Class<Component>) {
      const hookRE = /^hook:/
      Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
        const vm: Component = this
        if (Array.isArray(event)) {
          for (let i = 0, l = event.length; i < l; i++) {
            vm.$on(event[i], fn)
          }
        } else {
          (vm._events[event] || (vm._events[event] = [])).push(fn)
          // optimize hook:event cost by using a boolean flag marked at registration
          // instead of a hash lookup
          if (hookRE.test(event)) {
            vm._hasHookEvent = true
          }
        }
        return vm
      }
    }
    ```
    

Vue 的组件渲染流程?

Vue 中组件的 data 为什么是一个函数?

一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝

Vue.mixin 的使用场景和原理?

Vue 中的 diff 原理

Vue 的生命周期方法有哪些?一般在哪一步

provide/inject 不能解决它能解决的问题吗?

请说下 v-if 和 v-show 的区别

v-if:如果为 false 默认不渲染 切换时会进行创建和销毁 会触发生命周期钩子 v-show:默认渲染 css 切换

Vue.use 是干什么的?原理是什么?

vue 安装的组件类型必须为 Function 或者是 Object 如果是个对象,必须提供 install 方法 如果是一个函数,会被直接当作 install 函数执行

vue-router 两种模式的区别?

hash 模式背后的原理是 onhashchange 事件,可以在 window 对象上监听这个事件: window.onhashchange = function(event){ console.log(event.oldURL, event.newURL); let hash = location.hash.slice(1); document.body.style.color = hash; }

history 模式

因为 HTML5 标准发布,多了两个 API,pushState() 和 replaceState()。 通过这两个 API (1)可以改变 url 地址且不会发送请求, (2)不仅可以读取历史记录栈,还可以对浏览器历史记录栈进行修改。 除此之外,还有 popState().当浏览器跳转到新的状态时,将触发 popState 事件. 修改历史状态 包括了 pushState,replaceState 两个方法,这两个方法接收三个参数:stateObj,title,url window.history.pushState(stateObject, title, URL) window.history.replaceState(stateObject, title, URL)

函数式组件的优势及原理

v-if 与 v-for 的优先级

vue2 v-for 优先级高 vue3 v-if 优先级高

Vue 事件修饰符有哪些?其实现原理是什么?

Vue.directive 源码实现?

如何理解自定义指令?

谈一下你对 vuex 的个人理解

Vue 中 slot 是如何实现的?什么时候使用它?

keep-alive 平时在哪里使用?原理是?

$refs 是如何实现的?

Vue 中使用了哪些设计模式?

谈谈 Vue3 和 Vue2 的区别?

vue 如何做权限检验

虚拟 dom的理解

  1. 为函数式的 UI 编程方式打开了大门;
  2. 可以渲染到 DOM 以外的 backend 比如 ReactNative。 https://www.zhihu.com/question/31809713/answer/53544875在新窗口打开

为什么 vue 或者 react 要求 key 值唯一

没有 Key 值的问题 老集合中包含节点:A、B、C、D,更新后的新集合中包含节点:B、A、D、C,此时新老集合进行 diff 差异化对比,发现 B != A,则创建并插入 B 至新集合,删除老集合 A;以此类推,创建并插入 A、D 和 C,删除 B、C 和 D。 因为这些都是相同的节点,但由于位置发生变化,导致需要进行繁杂低效的删除、创建操作,其实只要对这些节点进行位置移动即可。 针对这一现象,提出优化策略:允许开发者对同一层级的同组子节点,添加唯一 key 进行区分,虽然只是小小的改动,性能上却发生了翻天覆地的变化!

vue 有哪些全局组件

component

  • Props

    • is - string | ComponentDefinition | ComponentConstructor
    • inline-template - boolean
  • 用法

    渲染一个“元组件”为动态组件。依 is 的值,来决定哪个组件被渲染。

    <!-- 动态组件由 vm 实例的 `componentId` property 控制 -->
    <component :is="componentId"></component>
    
    <!-- 也能够渲染注册过的组件或 prop 传入的组件 -->
    <component :is="$options.components.child"></component>
    
    1
    2
    3
    4
    5

transition

  • Props

    • name - string,用于自动生成 CSS 过渡类名。例如:name: 'fade' 将自动拓展为 .fade-enter.fade-enter-active 等。默认类名为 "v"
    • appear - boolean,是否在初始渲染时使用过渡。默认为 false
    • css - boolean,是否使用 CSS 过渡类。默认为 true。如果设置为 false,将只通过组件事件触发注册的 JavaScript 钩子。
    • type - string,指定过渡事件类型,侦听过渡何时结束。有效值为 "transition""animation"。默认 Vue.js 将自动检测出持续时间长的为过渡事件类型。
    • mode - string,控制离开/进入过渡的时间序列。有效的模式有 "out-in""in-out";默认同时进行。
    • duration - number | { enter: number, leave: number } 指定过渡的持续时间。默认情况下,Vue 会等待过渡所在根元素的第一个 transitionendanimationend 事件。
    • enter-class - string
    • leave-class - string
    • appear-class - string
    • enter-to-class - string
    • leave-to-class - string
    • appear-to-class - string
    • enter-active-class - string
    • leave-active-class - string
    • appear-active-class - string
  • 事件

    • before-enter
    • before-leave
    • before-appear
    • enter
    • leave
    • appear
    • after-enter
    • after-leave
    • after-appear
    • enter-cancelled
    • leave-cancelled (v-show only)
    • appear-cancelled
  • 用法

    <transition> 元素作为单个元素/组件的过渡效果。<transition> 只会把过渡效果应用到其包裹的内容上,而不会额外渲染 DOM 元素,也不会出现在可被检查的组件层级中。

    <!-- 简单元素 -->
    <transition>
      <div v-if="ok">toggled content</div>
    </transition>
    
    <!-- 动态组件 -->
    <transition name="fade" mode="out-in" appear>
      <component :is="view"></component>
    </transition>
    
    <!-- 事件钩子 -->
    <div id="transition-demo">
      <transition @after-enter="transitionComplete">
        <div v-show="ok">toggled content</div>
      </transition>
    </div>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    new Vue({
      ...
      methods: {
        transitionComplete: function (el) {
          // 传入 'el' 这个 DOM 元素作为参数。
        }
      }
      ...
    }).$mount('#transition-demo')
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

transition-group

  • Props

    • tag - string,默认为 span
    • move-class - 覆盖移动过渡期间应用的 CSS 类。
    • 除了 mode,其他 attribute 和 <transition> 相同。
  • 事件

    • 事件和 <transition> 相同。
  • 用法

    <transition-group> 元素作为多个元素/组件的过渡效果。<transition-group> 渲染一个真实的 DOM 元素。默认渲染 <span>,可以通过 tag attribute 配置哪个元素应该被渲染。

    注意,每个 <transition-group> 的子节点必须有独立的 key,动画才能正常工作

    <transition-group> 支持通过 CSS transform 过渡移动。当一个子节点被更新,从屏幕上的位置发生变化,它会被应用一个移动中的 CSS 类 (通过 name attribute 或配置 move-class attribute 自动生成)。如果 CSS transform property 是“可过渡”property,当应用移动类时,将会使用 FLIP 技术在新窗口打开使元素流畅地到达动画终点。

    <transition-group tag="ul" name="slide">
      <li v-for="item in items" :key="item.id">
        {{ item.text }}
      </li>
    </transition-group>
    
    1
    2
    3
    4
    5

keep-alive

  • Props

    • include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
    • exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
    • max - 数字。最多可以缓存多少组件实例。
  • 用法

    <keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition> 相似,<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。

    当组件在 <keep-alive> 内被切换,它的 activateddeactivated 这两个生命周期钩子函数将会被对应执行。

    在 2.2.0 及其更高版本中,activateddeactivated 将会在 <keep-alive> 树内的所有嵌套组件中触发。 主要用于保留组件状态或避免重新渲染。

    <!-- 基本 -->
    <keep-alive>
      <component :is="view"></component>
    </keep-alive>
    
    <!-- 多个条件判断的子组件 -->
    <keep-alive>
      <comp-a v-if="a > 1"></comp-a>
      <comp-b v-else></comp-b>
    </keep-alive>
    
    <!-- 和 `<transition>` 一起使用 -->
    <transition>
      <keep-alive>
        <component :is="view"></component>
      </keep-alive>
    </transition>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

    注意,<keep-alive> 是用在其一个直属的子组件被开关的情形。如果你在其中有 v-for 则不会工作。如果有上述的多个条件性的子元素,<keep-alive> 要求同时只有一个子元素被渲染。

函数式和响应式的理解

你说你看过 Vue 源码,能不能介绍一下 Vuex 的 Mutation 和 Action 的区别吗?

定位不同 Mutation:专注于修改 State,理论上是修改 State 的唯一途径。 Action:业务代码、异步请求。 角色不同,二者有不同的限制 Mutation:必须同步执行。 Action:可以异步,但不能直接操作 State。

上次更新: 2023/3/7 07:16:31
贡献者: wangdapan, wangdapan