IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 从源码看vue(v2.7.10)中的slot的原理 -> 正文阅读

[JavaScript知识库]从源码看vue(v2.7.10)中的slot的原理

// app.vue
<template>
  <div>
    <bb>
      <template v-slot:header="user">
        {{user}}
      </template>
    </bb>
  </div>
</template>

<script>
import bb from './bb.vue'
export default {
  name: "app",
  components: {
    bb
  }
};
</script>
// b.vue
<template>
  <div>
    <slot name="header" :user="user"></slot>
  </div>
</template>

<script>
export default {
  name: 'bb',
  data() {
    return {
      user: '王哥'
    }
  }
}
</script>

app.vue的渲染函数:

var render = function render() {
  var _vm = this,
    _c = _vm._self._c
  return _c(
    "div",
    [
      _c("bb", {
        scopedSlots: _vm._u([
          {
            key: "header",
            fn: function (user) {
              return [_vm._v("\n      " + _vm._s(user) + "\n    ")]
            },
          },
        ]),
      }),
    ],
    1
  )
}

可以看到v-slot:header="user"被渲染成一个名为scopedSlots的对象,{{user}}被放入函数中,传入的user就是v-slot:header="user"的value。然后执行_vm._u方法:

function resolveScopedSlots(fns, res, 
  // the following are added in 2.6
  hasDynamicKeys, contentHashKey) {
      res = res || { $stable: !hasDynamicKeys };
      for (var i = 0; i < fns.length; i++) {
          var slot = fns[i];
          if (isArray(slot)) {
              resolveScopedSlots(slot, res, hasDynamicKeys);
          }
          else if (slot) {
              // marker for reverse proxying v-slot without scope on this.$slots
              // @ts-expect-error
              if (slot.proxy) {
                  // @ts-expect-error
                  slot.fn.proxy = true;
              }
              res[slot.key] = slot.fn;
          }
      }
      if (contentHashKey) {
          res.$key = contentHashKey;
      }
      return res;
  }

该函数返回一个对象:
在这里插入图片描述
最后执行_c方法生成bb组件的vnode:
在这里插入图片描述
可以看出来插槽传值的原理,其实就是把你传入的值当做函数变量,插槽里面的值就是函数体。当初始化bb.vue组件的时候会执行initRender(vm)方法,该方法主要执行normalizeScopedSlot方法:

for (var key_1 in scopedSlots) {
    if (scopedSlots[key_1] && key_1[0] !== '$') {
        res[key_1] = normalizeScopedSlot(ownerVm, normalSlots, key_1, scopedSlots[key_1]);
    }
}
function normalizeScopedSlot(vm, normalSlots, key, fn) {
      var normalized = function () {
          var cur = currentInstance;
          setCurrentInstance(vm);
          var res = arguments.length ? fn.apply(null, arguments) : fn({});
          res =
              res && typeof res === 'object' && !isArray(res)
                  ? [res] // single vnode
                  : normalizeChildren(res);
          var vnode = res && res[0];
          setCurrentInstance(cur);
          return res &&
              (!vnode ||
                  (res.length === 1 && vnode.isComment && !isAsyncPlaceholder(vnode))) // #9658, #10391
              ? undefined
              : res;
      };
      // this is a slot using the new v-slot syntax without scope. although it is
      // compiled as a scoped slot, render fn users would expect it to be present
      // on this.$slots because the usage is semantically a normal slot.
      if (fn.proxy) {
          Object.defineProperty(normalSlots, key, {
              get: normalized,
              enumerable: true,
              configurable: true
          });
      }
      return normalized;
  }

该方法是个闭包,返回一个函数,这个函数我们后面会用到。然后赋值给bb组件的vnode。
在这里插入图片描述

我们继续看bb.vue组件的渲染函数:

var render = function render() {
  var _vm = this,
    _c = _vm._self._c
  return _c("div", [_vm._t("header", null, { user: _vm.user })], 2)
}

可以看到name="header"被当做_t函数的参数传入,:user="user"作为第三个参数传入。继续看看_t函数:

function renderSlot(name, fallbackRender, props, bindObject) {
    var scopedSlotFn = this.$scopedSlots[name];
    var nodes;
    if (scopedSlotFn) {
        // scoped slot
        props = props || {};
        if (bindObject) {
            if (!isObject(bindObject)) {
                warn$2('slot v-bind without argument expects an Object', this);
            }
            props = extend(extend({}, bindObject), props);
        }
        nodes =
            scopedSlotFn(props) ||
                (isFunction(fallbackRender) ? fallbackRender() : fallbackRender);
    }
    else {
        nodes =
            this.$slots[name] ||
                (isFunction(fallbackRender) ? fallbackRender() : fallbackRender);
    }
    var target = props && props.slot;
    if (target) {
        return this.$createElement('template', { slot: target }, nodes);
    }
    else {
        return nodes;
    }
}

该函数主要执行scopedSlotFn(props)方法:
在这里插入图片描述

function normalizeScopedSlot(vm, normalSlots, key, fn) {
    var normalized = function () {
        var cur = currentInstance;
        setCurrentInstance(vm);
        var res = arguments.length ? fn.apply(null, arguments) : fn({});
        res =
            res && typeof res === 'object' && !isArray(res)
                ? [res] // single vnode
                : normalizeChildren(res);
        var vnode = res && res[0];
        setCurrentInstance(cur);
        return res &&
            (!vnode ||
                (res.length === 1 && vnode.isComment && !isAsyncPlaceholder(vnode))) // #9658, #10391
            ? undefined
            : res;
    };
   ...
}

这里的fn如下图:
在这里插入图片描述
这里会执行该函数并传入需要的参数user然后返回vnode。从这里我们可以看到,slot的值取决于传入的props和fn的返回值。

总结

我们在组件里面写的<template v-slot:header="user"> {{user}} </template>
会被渲染成一个对象,该对象里面的参数就是header后面的值,template里面的表达式就是函数返回值,header会被当做函数的key。当渲染到<slot name="header" :user="user"></slot>时,会先根据name找到key为header函数,然后将props的值{user:'王哥'}当做参数传给该函数并执行返回一个vnode。

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-10-31 11:46:33  更:2022-10-31 11:48:50 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 19:04:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码