前端系统学习 10. Vue高级用法

Vue 高级用法

动画特效

  1. transition 实现路由切换动画
  • App.vue
  • Home -> List -> Detail 页面从右往左出现
  • Detail -> List -> Home 页面从左往右出现

插槽 - Slot

当某些组件或页面的宏观布局确定,局部子组件需要经常变化的时候,十分适合使用 Slot

父组件和子组件的变量作用域都是创建时的作用域,但是父组件可以向 slot 子组件传递数据

  • 默认 slot
  • 具名 slot
  • 作用域 slot
  • 语法糖:#。#defaut
  1. slot 实现原理

slot 本质上是一个返回 VNode 的函数,一般情况下,Vue 中的组件要渲染到页面上需要经过 template -> render function -> VNode -> DOM 的过程

比如一个带 slot 的组件

Vue.component('button-counter', {
  template: '
我是默认内容
' }) new Vue({ el: '#app', template: '我是slot传入内容', components:{buttonCounter} })

经过 vue 编译,组件渲染函数会变成

(function anonymous(
) {
with(this){
  return _c('div',[_t("default",[_v("我是默认内容")])],2)}
})

而这个_t就是 slot 渲染函数

function resolveSlots (
    children,
    context
  ) {
    if (!children || !children.length) {
      return {}
    }
    var slots = {};
    for (var i = 0, l = children.length; i < l; i++) {
      var child = children[i];
      var data = child.data;
      // remove slot attribute if the node is resolved as a Vue slot node
      if (data && data.attrs && data.attrs.slot) {
        delete data.attrs.slot;
      }
      // named slots should only be respected if the vnode was rendered in the
      // same context.
      if ((child.context === context || child.fnContext === context) &&
        data && data.slot != null
      ) {
        // 如果slot存在(slot="header") 则拿对应的值作为key
        var name = data.slot;
        var slot = (slots[name] || (slots[name] = []));
        // 如果是tempalte元素 则把template的children添加进数组中,这也就是为什么你写的template标签并不会渲染成另一个标签到页面
        if (child.tag === 'template') {
          slot.push.apply(slot, child.children || []);
        } else {
          slot.push(child);
        }
      } else {
        // 如果没有就默认是default
        (slots.default || (slots.default = [])).push(child);
      }
    }
    // ignore slots that contains only whitespace
    for (var name$1 in slots) {
      if (slots[name$1].every(isWhitespace)) {
        delete slots[name$1];
      }
    }
    return slots
}

Mixin

本质就是一个 js 对象,它可以包含我们组件中任意功能选项,如 data、components、methods、created、computed等等

我们只要将公用的功能以对象的方式传入 mixins 选项中是,当组件使用 mixins 对象时所有 mixins 对象的选项都将被混入该组件本身的选项中来

在 Vue 中我们可以局部混入和全局混入,全局混入常用来编写插件。

  • 当组件存在与 mixin 对象同名的数据的时候,进行递归合并的时候组件的数据会覆盖 mixin 的数据
  • 如果相同数据为声明周期钩子的时候,会合并成一个数组,先执行 mixin 钩子,在执行组件的钩子。(mounted / beforeDestroy 都是一样的)
  1. mixin 实战
  2. mixin 实现原理
  • 优先递归处理 mixins
  • 先遍历合并 parent 中的 key,调用 mergeField 方法进行合并,然后再保存在变量 options
  • 再遍历 child,合并补上 parent 中没有的 key,调用 mergeField 方法进行合并,保存在变量 options
  • 通过 mergeField 函数进行了合并
export function mergeOption (
  parent: Object,
  child: Object,
  vm?: Component
): Object {
  // 判断有有没有 mixin 也就是 mixin里面挂 mixin 的情况,有的化递归合并
  if (child.mixins) {
    for (let i = 0, l = child.mixins.length; i < l; i++) {
      parent = mergeOptions(parent, child.mixins[i], vm)
    }
  }

  const options = {}
  let key
  for (key in parent) {
    // 先遍历 parent 的 key 调对应的 strats[xxx]方法进行合并
    mergeField(key)
  }

  for (key in child) {
    if (!hasOwn(parent, key)) {
      处理 parent 没有处理的 key
      mergeField(key)
    }
  }

  function mergeField (key) {
    const strat = strats[key] || defaultStrat
    options[key] = strat(parent[key], child[key], vm, key)
  }

  return options
}

其主要逻辑就是合并 mixin 和当前组件的各种数据,细分为四种策略:

  • 替换型策略 - 同名的 props、methods、inject、computed 会被后来者替代

  • 合并型策略 - data,通过 set 方法进行合并和重新赋值

  • 队列型策略 - 生命周期钩子、watch。将函数存入一个数组,顺序执行

  • 叠加型策略 - components、directives、filters,通过原型链层层叠加

插件

插件就是指对 Vue 的功能的增强或补充

  1. 什么是插件?如何编写一个插件
MyPlugin.install = function (Vue, options) {
  // 1. 添加全局方法或 property
  Vue.myGlobalMethod = function () {}

  // 2. 添加全局资源
  Vue.directive('my-directive', {
    bind (el, binding, vnode, oldVNode) {}
  })

  // 3. 注入组件选项
  Vue.mixin({
    created: function () {}
  })

  // 4. 添加实例方法
  Vue.prototype.$myMethod = function (options) {

  }

}

Vue.use(plugin, options)
  1. Vue.use做了什么
  • 判断当前插件是否已经安装过,防止重复安装
  • 处理参数,调用插件的 install 方法。第一个参数是 Vue 实例。
Vue.use = function (plugin, options) {
  const installedPlugins = this._installedPlugins || (this._installedPlugins = [])

  if (installedPlugins.indexOf(plugin) > -1) return this

  // init plugin.install()
  const args = Array.prototype.slice.call(arguments, 1)
  args.unshift(this)

  if (typeof plugin.install === 'function') {
    plugin.install.apply(plugin, args)
  } else if (typeof plugin === 'function') {
    plugin.apply(null, args)
  }

  installedPlugins.push(plugin)
  return this
}

你可能感兴趣的:(前端系统学习 10. Vue高级用法)