анализ исходного кода element-ui, знаете ли вы, как реализована v-загрузка?

Vue.js

предисловие

Я считаю, что все должны были использовать element-ui внутриv-loadingнаписать загрузку, но если бы вы написали ее, как бы вы ее написали?

Как мы все знаем, фреймворк element-uiv-loadingЕсть два способа его использования, один из них — использовать его непосредственно на этикетке, которую необходимо загрузить.:v-loading='true', этот метод официально называется инструкцией, а другой - использоватьthis.$loading(options)Чтобы позвонить, этот метод официально называется сервисом.

Люди всегда будут иметь склонность к замечательным вещам, я так не думаю, не говорите много (пердеть, вы подойдете к вам), прямо выберите исходный код.

Некоторые люди от природы подходят вам, какой-то код от природы подходит для чтения, а отличные проекты с открытым исходным кодом вот такие, надеюсь, мой анализ заставит вас это осознать, а потом хихикнуть, так оно и есть.

текст

Как использовать

инструкция

пересматриватьv-loadingКак использовать команду

<template>
  <div :v-loading.fullscreen="true">全屏覆盖</div>
</template>

Служить

Давайте посмотрим, как используется сервис

mounted() {
  let loading = this.$loading({ fullscreen: true })
  setTimeout(() => { loading.close() }, 1000)
}

Чтобы оставить небольшое впечатление, давайте будем простыми и грубыми, и сразу перейдем к исходному коду.

отправная точка

Откройте свойnode_modules,Цельelement-ui,существуетsrcв каталогеindex.jsЗдесь представлены все компоненты, позвольте мне посмотреть, какие две милашки будут раздеты сегодня.

// element-ui\src\index.js
// ...
// directive 指令装载
Vue.use(Loading.directive)
// prototype 服务装载
Vue.prototype.$loading = Loading.service
// ...

В соответствии с принципом пошагового выполнения, начнем с использования дополнительных инструкций.

Vue.use()Эта директива используется Vue для установки плагинов.Если входящий параметр является объектом, объект должен предоставитьinstallметод, если функция, функция рассматривается какinstallметод, вinstallКогда метод вызывается, онVueПередать в качестве параметра.

Чтобы узнать больше о плагинах,Нажмите здесь, чтобы узнать больше о плагинах, Вы не устанете читать официальные документы университета.

Но босс! Какие две вещи под загрузкой!

Приходите, посмотрим здесь.

import directive from './src/directive'
import service from './src/index'

export default {
  // 这里为什么有个 install 呢
  // 当你使用单组件单注册的时候就会调用这里了
  // 效果和下面一样,挂载指令,挂载服务
  install(Vue) {
    Vue.use(directive)
    Vue.prototype.$loading = service
  },
  // 就是上面的 Loading.directive
  directive,
  // 就是上面的 Loading.service
  service
}

Далее нам, наконец, нужно погрузиться в исходный код! докидоки...

v-loadingРазбор инструкций

Чашка воды, подавленное волнение, мы открываемpackagesвнизloading\index.js, видно, что он выставлен наружуdirectiveИнструкция, давай, в путь, посмотрим его исходники.

Кажется, там сотни строк кода, но не переживайте, я упрощу для вас и вставлю основной код, для удобства объяснения возьмем толькоfullscreenмодификаторы.

import Vue from 'vue'
// 这里就是我们写的比较多的单 .vue 文件了,不拓展开了
// 值得注意的是在这个单文件里面的 data() {} 声明的值
// 我们下面会碰到
import Loading from './loading.vue'
// 老板!Vue.extend() 是什么!
// 代码片段之后我会简单介绍
const Mask = Vue.extend(Loading)

const loadingDirective = {}
// 还记得 Vue.use() 的使用方法么?若传入的是对象,该对象需要一个 install 属性
loadingDirective.install = Vue => {
  // 这里处理显示、消失 loading
  const toggleLoading = (el, binding) => {
    // 若绑定值为 truthy 则插入 loading 元素
    // binding 值为 directive 的几个钩子中会接受到的参数
    if (binding.value) {
      if (binding.modifiers.fullscreen) {
        insertDom(document.body, el, binding)
      }
      // 不然则将其设为不可见
      // 从上往下读我们是第一次看到 visible 属性
      // 别急,往下看,这个属性可以其实就是单文件 loading.vue 里面的
      // data() { return { visible: false } }
    } else {
      el.instance.visible = false
    }
  }

  const insertDom = (parent, el, binding) => {
    // 将 loading 设为可见
    el.instance.visible = true
    // appendChild 添加的元素若为同一个,则不会重复添加
    // 我们 el.mask 所指的为同一个 dom 元素
    // 因为我们只在 bind 的时候给 el.mask 赋值
    // 并且在组件存在期间,bind 只会调用一次
    parent.appendChild(el.mask)
  }
  // 在此注册 directive 指令
  Vue.directive('loading', {
    bind: function(el, binding, vnode) {
      // 创建一个子组件,这里和 new Vue(options) 类似
      // 返回一个组件实例
      const mask = new Mask({
        el: document.createElement('div'),
        // 有些人看到这里会迷惑,为什么这个 data 不按照 Vue 官方建议传函数进去呢?
        // 其实这里两者皆可
        // 稍微做一点延展好了,在 Vue 源码里面,data 是延迟求值的
        // 贴一点 Vue 源码上来
        // return function mergedInstanceDataFn() {
        //   let instanceData = typeof childVal === 'function'
        //     ? childVal.call(vm, vm)
        //     : childVal;
        //   let defaultData = typeof parentVal === 'function'
        //     ? parentVal.call(vm, vm)
        //     : parentVal;
        //   if (instanceData) {
        //     return mergeData(instanceData, defaultData)
        //   } else {
        //     return defaultData
        //   }
        // }
        // instanceData 就是我们现在传入的 data: {}
        // defaultData 就是我们 loading.vue 里面的 data() {}
        // 看了这段代码应该就不难理解为什么可以传对象进去了
        data: {
          fullscreen: !!binding.modifiers.fullscreen
        }
      })
      // 将创建的子类挂载到 el 上
      // 在 directive 的文档中建议
      // 应该保证除了 el 之外其他参数(binding、vnode)都是只读的
      el.instance = mask
      // 挂载 dom
      el.mask = mask.$el
      // 若 binding 的值为 truthy 运行 toogleLoading
      binding.value && toggleLoading(el, binding)
    },
    update: function(el, binding) {
      // 若旧不等于新值得时候(一般都是由 true 切换为 false 的时候)
      if (binding.oldValue !== binding.value) {
        // 切换显示或消失
        toggleLoading(el, binding)
      }
    },
    unbind: function(el, binding) {
      // 当组件 unbind 的时候,执行组件销毁
      el.instance && el.instance.$destroy()
    }
  })
}
export default loadingDirective

Vue.extendчто

В обычном коде мы не вызываем этот метод активно, но когда мы регистрируем компонент, например,Vue.component('my-component', options), на этот раз автоматически вызоветVue.extend, перейти сразу к исходнику, код есть при вызовеVue.component()будет казнен.

ps: сделайте небольшое расширение, для.vueОдин файл, каждый может догадаться, как это работает, в первую очередь<template>Содержимое этикетки преобразуется вrender()функция, говоря об этомrender()функция, я хочу, чтобы Amway еще одна волна JSX (стоп!), а затем идтиVue.component()Эта строка регистрирует компонент.

...
if (type === 'component' && isPlainObject(definition)) {
  definition.name = definition.name || id
  definition = this.options._base.extend(definition)
}
...

пока вextendЧто там происходит? Очень хочется вставить исходники прямо для анализа, но это выходит за рамки того, о чем мы сегодня поговорим.Vue.extendпринимает параметры и возвращает конструктор,newЭтот конструктор может возвращать экземпляр компонента.

Я считаю, что на данный момент все уже поняли, как достичьv-loadingС некоторым пониманием, старательные друзья уже засучили рукава и принялись за работу, но статья еще не закончена.После разбора режима инструкции, надо посмотреть режим обслуживания, передохнуть и в путь.

Нажмите здесь, чтобы узнать больше о Vue.extend, по-прежнему является официальным документом, я только что взорвал документ Vue (сломанный звук).

Служить

Если вы включаете режим разработчика и видите дваloadingКстати, вы должны заметить разницу между командным режимом и сервисным режимом, наиболее интуитивно понятным является то, что если естьfullscreenпараметры, сгенерированные не удаляются в командном режимеdomэлемент, а в сервисном режиме удаляет сгенерированныйdomэлемент.

У меня слишком много ерунды, поэтому перейду сразу к исходникам.Аналогичным образом я извлеку фрагменты кода, на которые нужно обратить внимание для облегчения анализа.На основе режима инструкций нетрудно увидеть режим обслуживания .

import Vue from 'vue'
import loadingVue from './loading.vue'
// 和指令模式一样,创建实例构造器
const LoadingConstructor = Vue.extend(loadingVue)
// 定义变量,若使用的是全屏 loading 那就要保证全局的 loading 只有一个
let fullscreenLoading
// 这里可以看到和指令模式不同的地方
// 在调用了 close 之后就会移除该元素并销毁组件
LoadingConstructor.prototype.close = function() {
  setTimeout(() => {
    if (this.$el && this.$el.parentNode) {
      this.$el.parentNode.removeChild(this.$el)
    }
    this.$destroy()
  }, 3000)
}

const Loading = (options = {}) => {
  // 若调用 loading 的时候传入了 fullscreen 并且 fullscreenLoading 不为 falsy
  // fullscreenLoading 只会在下面赋值,并且指向了 loading 实例
  if (options.fullscreen && fullscreenLoading) {
    return fullscreenLoading
  }
  // 这里就不用说了吧,和指令中是一样的
  let instance = new LoadingConstructor({
    el: document.createElement('div'),
    data: options
  })
  let parent = document.body
  // 直接添加元素
  parent.appendChild(instance.$el)
  // 将其设置为可见
  // 另外,写到这里的时候我查阅了相关的资料
  // 自己以前一直理解 nextTick 是在 dom 元素更新完毕之后再执行回调
  // 但是发现可能并不是这么回事,后续我会继续研究
  // 如果干货足够的话我会写一篇关于 nextTick ui-render microtask macrotask 的文章
  Vue.nextTick(() => {
    instance.visible = true
  })
  // 若传入了 fullscreen 参数,则将实例存储
  if (options.fullscreen) {
    fullscreenLoading = instance
  }
  // 返回实例,方便之后能够调用原型上的 close() 方法
  return instance
}
export default Loading

о кодеfullscreenLoadingпеременная, основанная наelement-uiМы должны быть в состоянии понять официальное описание. Это необходимо для того, чтобы был только один экземпляр загрузки, который покрывает всю страницу. Официальная документация выглядит следующим образом.

Следует отметить, что полноэкранная загрузка, вызываемая как служба, является одноэлементной: если полноэкранная загрузка вызывается повторно до того, как предыдущая полноэкранная загрузка будет закрыта, новый экземпляр загрузки не будет создан, а существующий полный -screen Загрузка экземпляра будет возвращена.

послесловие

На этом основное содержание статьи закончилось, похоже, это толькоv-loadingФункция леопарда, но расширяет много контента.Также я упростил много кода, так что это лишь малая часть контента леопарда.Для большего контента рекомендую всем прочитать исходный код, и вы найдете другой мир.

Некоторые люди говорили мне, что когда вы смотрите на исходный код, вы смотрите в начало, там около дюжины внедренных модулей, а когда вы смотрите на компоненты, там сотни строк, вы можете начать с некоторых функций, о которых вы знаете. , Такие какv-loading, когда вы видите это, вы знаете, чтоdirective, обращайте внимание на ключевые моменты при просмотре исходного кода, такие какbinding,el,vnode, смысл кода скоро станет ясен.

Если вокруг вас есть люди получше, это нормально, вы можете ориентироваться на него, но когда вы выделяетесь, люди вокруг вас не так хороши, как вы, и вы не знаете, как взрослеть, вы должны смотреть на источник код в настоящее время, из исходного кода Учитесь и улучшайте себя из исходного кода.

Как следует относиться к чтению? На деньги одного дня еды можно купить чьи-то жизненные усилия, какой ценный бизнес. --- Аноним

Хотя исходный код нельзя назвать тяжким трудом всей жизни, но представьте себе, что процесс чтения исходного кода подобен разговору с автором с глазу на глаз, как устроить этот модуль, как оптимизировать алгоритм, видетьVueИсходный код . (Краем глаза, почему? Потому что я так тронут)

В этой статье я просто делюсь с вами тяжелой работой некоторых замечательных людей в сочетании с некоторыми своими собственными знаниями.Я надеюсь, что после прочтения всей статьи вы сможете получить некоторые достижения и некоторые осадки.Конечно, засучите рукава и напишите сами прямо.v-custom-loadingдаже лучше.

В процессе просмотра исходного кода я найду много интересного кода, если вам интересно, вы можете зайти на мой проект и посмотреть, я написал его сам.v-custom-loading, а также объединить некоторые из моих собственных идей, например, при монтировании экземпляра компонента я рекомендую следующий способ написания.

Мой vue-tiny-code приветствует звезду

const context = '@@loadingContext'
...
el[context] = { instance: mask }
...

Причина этого в том, чтобы не загрязнятьelАтрибуты самого элемента, ведь возможно, что атрибуты, определенные вами, будутdomИсходный атрибут конфликтует.

Также яvue-element-adminИменно так pr упоминается в проекте. В частности, вы можете увидетьvue-element-admin issue 1704а такжеvue-element-admin issue 1705.

ps: В последнее время очень много вопросов на собеседовании.Я думаю, если заголовок изменить на "Анализ вопросов на собеседовании, знаете ли вы, как реализовать v-загрузку?" 》Будет ли больше людей обращать внимание? (Собачья голова в безопасности)

нижний колонтитул

Код — это жизнь, и я люблю его.

Технология постоянно меняется Мозг был в сети Фронтальная дорога Мы видим следующий период

от --- промежность трио

я здесьgayhub@jsjzhДобро пожаловать, чтобы прийти и играть со мной.

Добро пожаловать, друзья, чтобы добавить меня напрямую, привлечь вас в группу, чтобы делать что-то вместе, не забудьте отметить, где вы прочитали статью.

ps: если изображение недействительно, вы можете добавить меня в wechat: kimimi_king