前端系统学习 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: '<div> <slot>我是默认内容</slot></div>'
})
 
new Vue({
    el: '#app',
    template: '<button-counter><span>我是slot传入内容</span></button-counter>',
    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
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容