Геймплей с динамическими компонентами, о котором вы могли не знать 🍉

внешний интерфейс опрос
Геймплей с динамическими компонентами, о котором вы могли не знать 🍉

○ Фон

Если вы знаете, пожалуйста, слегка молотком.

Это преамбула основной статьи, подготовленная автором после того, как автор модернизировал структуру деятельности в компании.Поскольку эта статья является относительно самостоятельной, она извлечена и написана отдельно.

Заголовок — динамические компоненты, но для лучшего понимания его можно назвать удаленной загрузкой динамических компонентов, которые в дальнейшем будут именоваться «удаленными компонентами».

Как именно играет? Не волнуйтесь, слушайте меня медленно, после прочтения вы почувствуете, что компоненты Vue все еще могут играть так🐶, а также вы узнаете плагин Stylelint, оснащенный DEMO и скрытыми яйцами в конце.

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

Рассказчик: Удаленные компоненты фактически имеют аналогичные приложения в визуальных платформах с низким кодом, и мы также используем аналогичную идею здесь, чтобы разделить активную страницу и слой бомбы купона. Продолжение по теме ...

image.png

В предыдущей версии устаревшей системы действие было привязано к одному эластичному слою, а отношение связывания было 1-к-1.

image.png

Текущая сцена заключается в том, что активность может иметь разные стили всплывающих слоев, что должно освободить связь привязки. Нам нужно «многие ко многим», то есть одна страница события может соответствовать нескольким слоям рекламного купона-бомбы, или один слой рекламного купона-бомбы может соответствовать нескольким страницам событий.

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

И наше требование состоит в том, чтобы активная страница соответствовала бесчисленным типам слоев маркеров, а не нескольким, поэтому невозможно записать все слои маркеров локально. Так что делать?

image.png

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

Легко сказать, как это сделать?

○ Ядро удаленного компонента

Чистая версия

Если это пружинный слой, состоящий из чистого JS и CSS, мы, естественно, думаем, что пружинный слой может быть сформирован путем динамической вставки сценариев JS и CSS. Поэтому скомпилированные файлы JS и CSS можно хранить в удаленной CDN.

image.png

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

// CSS插入
<link rel="stylesheet" href="//yun.xxx.com/xxx.css">

// JS的动态插入

<script type="text/javascript">
  var oHead = document.querySelector('.modal-group');
  var oScript = document.createElement('script');
  oScript.type = "text/javascript";
  oScript.src = "//yun.xxx.com/xxx.js";
  oHead.appendChild(oScript);
</script>

Из вышеизложенного мы видим, что JS и CSS могут реализовать Pure-версию удаленных компонентов, но можно ли это реализовать в среде Vue? Если он динамически вставляется в активность Vue в соответствии с Pure JS и CSS, его также можно реализовать очень грубо.

Но есть ли более элегантный способ?

image.png

vue-версия

Выбор подробно не обсуждается, и в последующей основной статье будет рассказано о том, почему выбран Vue.

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

Давайте рассмотрим некоторые концепции Vue.

форма компонента

«Компоненты объекта»

Всплывающее окно, на самом деле, мы можем выразить это через компонент Vue, мы хотим поместить этот компонент в CDN, загрузить файл напрямую, а затем запустить его в среде браузера.Осуществимо ли это? Давай попробуем.

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

{
  mounted: () => {
   console.log('加载')
  },
  template: "<div v-bind:style=\"{ color: 'red', fontSize: '12' + 'px' }\">Home component</div>"
}

С помощью версии среды выполнения, включающей компилятор, мы можем обрабатывать шаблоны в строковой форме.

-- среда выполнения только для компилятора

Если вам нужно скомпилировать шаблон на стороне клиента (например, передать строку параметру Template или прикрепить ее к элементу и использовать HTML внутри его DOM в качестве шаблона), вам потребуется добавить компилятор, т.е. полная версия

Кажется, он нашел дверь в новый мир.

image.png

Мы действительно можем реализовать Шаблон, Сценарий и CSS в этой форме, но для студентов, изучающих разработку, Шаблон в виде строки и встроенный CSS не подходят для опыта разработки.

image.png

«Компонент одного файла»

В настоящее время естественно думать о SFC — Single File Component.

**однофайловые компоненты** с расширением файла .vue обеспечивают решение всех вышеперечисленных проблем — документация по Vue.image.png

Но как можно загрузить компонент .vue с удаленного компьютера и запустить его в текущей активной среде Vue? Это проблема, потому что браузеры не распознают файлы .vue, а файлы .js — нормально.

Давайте сначала подумаем, во что наконец конвертируется файл .vue?

image.png(Источник изображения:Преобразование файлов 1.03-Vue - 简 书)

Через преобразование он фактически становится объектом JS. Итак, как я могу преобразовать .vue в .js?

Есть два способа, один из них — преобразование во время выполнения, как мы обнаружили.http-vue-loader. Получите контент через Ajax, проанализируйте шаблон, CSS, скрипт и выведите объект JS.

image.png

И, учитывая производительность и совместимость, мы выбираем предварительную компиляцию с помощью препроцессора CSS и прекомпилятора шаблонов HTML.

Vue официально предоставляет vue-loader, который анализирует файл, извлекает каждый языковой блок, при необходимости обрабатывает его с помощью других загрузчиков и, наконец, собирает их в модуль ES, экспорт которого по умолчанию является объектом параметра компонента Vue.js. Что это значит? Официально предоставленоКомпонент в виде объекта опцийДЕМО.

С теоретической поддержкой, теперь надо рассмотреть практику, что использовать для компиляции?

image.png

как построить

Поскольку webpack после компиляции принесет много бесполезного кода, связанного с модуляризацией, обычно небольшие библиотеки выбирают rollup, здесь мы тоже выбираем rollup.

// rollup.config.js
import vue from 'rollup-plugin-vue'
import commonjs from 'rollup-plugin-commonjs'

export default {
  input: './skin/SkinDemo.vue',
  output: {
    format: 'iife',
    file: './dist/rollup.js',
    name: 'MyComponent'
  },
  plugins: [
    commonjs(),
    vue()
  ]
}

С помощью rollup-plugin-vue мы можем конвертировать файлы .vue в .js, Форма iife js выходных данных компиляции свертки.

image.png

Вы можете видеть, что скрипт, стиль и шаблон обрабатываются в соответствующие фрагменты соответственно.Благодаря интегрированному вычислению эти фрагменты сгенерируют объект JS и сохранят его в виде файла .js. Изображение ниже представляет собой объект параметра компонента.

image.png

Проекты могут быть:GitHub.com/fly0oh0/hotfilm…, попробуйте выполнить сборку в папке накопительного пакета, подробности см. в инструкциях README.

У нас уже есть объект опций компонента Vue.js, как смонтировать его в соответствующем приложении Vue?

image.png

Способ крепления

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

Динамические компоненты не могут исправлять определенные компоненты и заменять различные компоненты в соответствии с правилами. Из документации он поддерживает объект параметров для компонента.

наконец понял

Сначала вам нужно создать файл .vue, а затем загрузить удаленный JS через Ajax или динамический скрипт. Поскольку Ajax будет иметь междоменные ограничения, здесь мы выбираем динамическую форму Script для загрузки.

И способ, которым мы только что использовали Rollup для экспорта, заключается в монтировании содержимого в глобальную переменную. Затем вы знаете, что после того, как динамический скрипт вставлен, есть глобальная переменная MyComponent, которая монтируется на динамический компонент, и, наконец, компонент может отображаться на странице.

Как это работает? Каких шагов не хватает, для начала нам нужна функция, загружающая удаленный .js компонент.

// 加载远程组件js

function cleanup(script){
  if (script.parentNode) script.parentNode.removeChild(script)
  script.onload = null
  script.onerror = null
  script = null
}

function scriptLoad(url) {
  const target = document.getElementsByTagName('script')[0] || document.head

  let script = document.createElement('script')
  script.src = url
  target.parentNode.insertBefore(script, target)

  return new Promise((resolve, reject) => {
    script.onload = function () {
      resolve()
      cleanup(script)
    }
    script.onerror = function () {
      reject(new Error('script load failed'))
      cleanup(script)
    }
  })
}

export default scriptLoad

Затем смонтируйте загруженный компонент на соответствующий динамический компонент.

<!-- 挂载远程组件 -->

<template>
  <component
    class="remote-test"
    :is="mode">
  </component>
</template>

<script>
import scriptLoad from "./scriptLoad"

export default {
  name: "Remote",
  data() {
    return {
      mode: "",
    };
  },
  mounted() {
    this.mountCom(this.url)
  },
  methods: {
    async mountCom(url) {
      // 下载远程js
      await scriptLoad(url)

      // 挂载在mode
      this.mode = window.MyComponent

      // 清除MyComponent
      window.MyComponent = null
    },
  }
}
</script>

В основном реализован удаленный компонент Vue, но обнаружено, что проблема все еще существует.

image.png

Глобальная переменная MyComponent должна быть согласована, но для достижения лучшего опыта разработки согласование должно быть сведено к минимуму.

Метод экспорта

Как это решить? Поскольку мы используем метод IIFE для экспорта, Rollup также поддерживает метод UMD, включая Common JS и AMD.

Мы поддерживаем UMD, настроив Rollup.

// rollup.config.js
import vue from 'rollup-plugin-vue'
import commonjs from 'rollup-plugin-commonjs'

export default {
  input: './skin/SkinDemo.vue',
  output: {
    format: 'umd',
    file: './dist/rollup.js',
    name: 'MyComponent'
  },
  plugins: [
    commonjs(),
    vue()
  ]
}

Видно, что после завершения строительства поддерживаются три способа экспорта.image.png

Мы можем смоделировать среду узла, назвать глобальные переменные exports и module, а затем получить экспортированные компоненты в переменной module.exports.

image.png

Конкретная реализация основного кода выглядит следующим образом.

<!-- 挂载远程组件 -->

<template>
  <component
    class="remote-test"
    :is="mode">
  </component>
</template>

<script>
import scriptLoad from "./scriptLoad"

export default {
  name: "Remote",
  data() {
    return {
      mode: "",
    };
  },
  mounted() {
    this.mountCom(this.url)
  },
  methods: {
    async mountCom(url) {
      // 模拟node环境
      window.module = {}
      window.exports = {}

      // 下载远程js
      await scriptLoad(url)

      // 挂载在mode
      this.mode = window.module.exports

      // 清除
      delete window.module
      delete window.exports
    },
  }
}
</script>

Наконец-то разобрался, как загружается Vue-версия удаленного компонента.

image.png

Далее нам нужно подумать о том, как быть с дизайном удаленных компонентов (эластичных слоев).

резюме

Функциональность удаленного компонента реализована с использованием динамических компонентов Vue, заменяющих старую архитектуру.image.png

Вы можете попробовать всплывающий слой удаленного компонента по следующему адресу и следовать README проекта. Вы получите следующие оболочки удаленных компонентов.

адрес проекта:GitHub.com/fly0oh0/hotfilm…

image.png

○ Конструкция удаленного компонента (эластичный слой)

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

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

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

Во-первых, давайте посмотрим на повторное использование компонентов.

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

Но удалённый однокомпонентный код и код page-side разделены (его можно понимать как продукт, упакованный двумя записями вебпака), приходится думать, где разместить общие компоненты.

image.png

Повторное использование компонентов

Теперь мы можем найти, что есть три случая, давайте попробуем подумать об этом, используя метод перечисления.

Пакет

Публичные компоненты и удаленные компоненты упакованы вместе

Собирать их вместе определенно нецелесообразно, так как это не только приведет к увеличению размеров удаленных компонентов, но и не позволит повторно использовать другие удаленные компоненты. Рассмотрим далее ниже. ​

image.png

Общие компоненты упакованы отдельно

Удаленный компонент, общие компоненты упакованы отдельно, что неблагоприятно, так как удаленное разъединение сборки общего компонента меньше 5, а меньший объем кода, упакованного отдельно в виде слоя, многопостовые запросы повлияют на удаленную сборку. Первый раз показывают. ​

Продолжайте рассматривать и посмотреть на это.image.png Общие компоненты упакованы с базовой библиотекой страницы.

Общедоступные компоненты и основная библиотека страниц упакованы вместе, чтобы избежать перезагрузки удаленных компонентов при их использовании, что может повысить скорость отображения удаленных компонентов.image.png

Поэтому окончательный выбор был сделан окончательно, и общие компоненты и библиотека ядра страницы были упакованы вместе.

Если удаленный компонент component.js и общедоступный компонент разделены, как мы можем использовать общедоступный компонент? 😂

image.png

Зарегистрируйтесь 🔑

Оглядываясь назад на официальную документацию Vue, Vue.component предоставляет возможность регистрировать компоненты, на которые затем можно ссылаться глобально. Давай попробуем.

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

компонент кнопки

// 本地页面端(本地是相较于在远端CDN)

<!-- 按钮组件 -->
<template>
  <button type="button" class="btn" @click="use">
  </button>
</template>

<script>
export default {
  name: 'Button',
  inject: ['couponUseCallback'],
  methods: {
    use() {
      this.couponUseCallback && this.couponUseCallback()
    }
  }
}
</script>

компонент отключения

// 本地页面端(本地是相较于在远端CDN)

<!-- 关闭组件 -->
<template>
  <span @click="close" class="close"></span>
</template>

<script>
export default {
  name: "CouponClose",
  inject: ["couponCloseCallback"],
  methods: {
    close() {
      this.couponCloseCallback && this.couponCloseCallback();
    },
  },
};
</script>

<style lang="less" scoped>
.close {
  &.gg {
    background-image: url("//yun.tuisnake.com/h5-mami/dist/close-gg.png") !important;
    background-size: 100% !important;
    width: 92px !important;
    height: 60px !important;
  }
}
</style>

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

// 本地页面端(本地是相较于在远端CDN)

<script>
  Vue.component("CpButton", Button);
  Vue.component("CpClose", Close);
</script>

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

image.png

компонент связи

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

Инициирование события и обратный вызов могут быть достигнуты путем предоставления себя удаленному компоненту в основной библиотеке страницы и внедрения экземпляра действия в удаленный компонент.

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

image.png

Упаковка компонентов

Теперь есть проблема инкапсуляции компонентов.Давайте сначала рассмотрим пример, и у вас будет базовое понимание.

Есть 3 вложенных компонента, как показано ниже. Теперь вам нужно назначить счетчик нижнему компоненту RealComponent из компонента верхнего уровня Main.vue, а затем прослушать события входного компонента RealComponent.Если есть изменение, уведомить метод в Main.vue. Как это сделать?

image.png

Сколько существует вариантов межуровневого взаимодействия?

  1. Мы используем vuex для управления данными, который слишком тяжел для этого требования.
  2. Пользовательская шина событий vue bus (как упоминалось выше), передача сообщений без явных зависимостей, если передача реквизитов, требуемых компонентами, не подходит.
  3. Пропсы передаются слой за слоем, но есть много событий и свойств, которые необходимо передать, что увеличивает затраты на обслуживание.

И есть другой путь черезattrsа такжеатрибуты ислушателей для достижения «прозрачной передачи» свойств и событий между уровнями.

главный компонент

// Main.vue

<template>
<div>
  <h2>组件Main 数据项:{{count}}</h2>
  <ComponentWrapper @changeCount="changeCount" :count="count">
  </ComponentWrapper>
</div>
</template>
<script>
import ComponentWrapper from "./ComponentWrapper";
export default {
  data() {
    return {
      count: 100
    };
  },
  components: {
    ComponentWrapper
  },
  methods: {
    changeCount(val) {
      console.log('Top count', val)
      this.count = val;
    }
  }
};
</script>

компоненты для упаковки

Иногда, чтобы добавить некоторые функции к реальным компонентам, нам нужно использовать компоненты упаковки (особенно при упаковке сторонних библиотек компонентов).

// ComponentWrapper.vue

<template>
  <div>
    <h3>组件包裹层</h3>
    <RealComponent v-bind="$attrs" v-on="$listeners"></RealComponent>
  </div>
</template>
<script>
import RealComponent from "./RealComponent";
export default {
  inheritAttrs: false, // 默认就是true
  components: {
    RealComponent
  }
};
</script>

действительная составляющая

// RealComponent.vue

<template>
  <div>
    <h3>真实组件</h3>
    <input v-model="myCount" @input="inputHanlder" />
  </div>
</template>
<script>
export default {
  data() {
    return {
      myCount: 0
    }
  },
  created() {
    this.myCount = this.$attrs.count;  // 在组件Main中传递过来的属性
    console.info(this.$attrs, this.$listeners);
  },
  methods: {
    inputHanlder() {
      console.log('Bottom count', this.myCount)
      this.$emit("changeCount", this.myCount); // 在组件Main中传递过来的事件,通过emit调用顶层的事件
      // this.$listeners.changeCount(this.myCount) // 或者通过回调的方式
    }
  }
};
</script>

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

На самом деле существует два уровня удаленных компонентов: один — локальный (внутристраничный), а другой — удаленный (CDN). Локальный уровень используется только для инкапсуляции, которую можно понимать как уровень упаковки без реальных функций. В настоящее время можно понять, что локальный уровень компонентов является уровнем упаковки.Упаковочный слой в основном выполняет функцию импорта удаленных компонентов и не может быть удален.Необходимо использовать вышеуказанные характеристики для передачи информации удаленным компонентам.

иерархия стилей

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

Стандартный z-индекс

Поэтому деление между 0 и 90 разделено на десять слоев.Последующее значение может быть увеличено в соответствии с реальной ситуацией.Установлено, что каждый контейнер удаленного компонента может указывать z-индекс только в пределах указанного слоя.

// const.js
const FLOOR = {
  MAIN: 0,   // 主页面容器
  COUPON_MODAL: 20,  // 广告弹层
  OTHER_MODAL: 30, // 其他弹层
  ERROR_MODAL: 90,
  ...
}

Устанавливает слой-оболочку для каждого удаленного компонента, слой бомбы.


// CouponModalWrapper.vue
<script>
<template>
  <div class="modal-wrapper" :style="{'z-index': FLOOR.COUPON_MODAL}" @touchmove.prevent>
    <slot></slot>
  </div>
</template>

// OtherModalWrapper.vue
<template>
  <div class="modal-wrapper" :style="{'z-index': FLOOR.OTHER_MODAL}" @touchmove.prevent>
    <slot></slot>
  </div>
</template>

// 这里只是为了表意简单,实际上两个Wrapper.vue可以合并

Затем для каждой категории вводится соответствующий обертывающий слой эластичного слоя.

// 每类别公共组件有一个

// CouponModal2.vue  
<template>
  <CouponModalWrapper>
		...
  </CouponModalWrapper>
</template>
  
// OtherModal2.vue  
<template>
  <OtherModalWrapper>
		...
  </OtherModalWrapper>
</template>

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

image.png

Не волнуйтесь, есть способ.

со стилем

Идея такова, каждой категории удаленных компонентов соответствует отдельная основная папка, можно определить максимальный и наименьший допустимый z-индекс для этой папки, так как же это сделать?

Я не знаю, использовали ли вы плагины, которые автоматически добавляют -webkit и другие префиксы - автопрефиксера нет, на самом деле он основан на инструменте postcss. И stylelint, инструмент, который мы часто используем в качестве формата проверки css, также разработан на его основе.

В это время мы думали о том, можем ли мы использовать возможности stylelint для его ограничения, но обнаружили, что в официальной документации нет нужного нам API.

Нам нужно разработать плагин stylelint самостоятельно, давайте посмотрим на плагин для базового плагина stylelint.

image.png

stylelint принимает функцию и возвращает функцию через метод stylelint.createPlugin.

const stylelint = require('stylelint');
const ruleName = 'plugin/z-index-range-plugin';

function rule(options) {
  // options传入的配置
  return (cssRoot, result) => {
    // cssRoot即为postcss对象
   };
}

module.exports = stylelint.createPlugin(
  ruleName,
  rule
);

Вы можете получить объект PostCSS в функции, и вы можете использовать PostCSS для синтаксического анализа кода в AST, обхода, изменения и AST для изменения кода.

Нам доступны некоторые понятия. ​

  • правило, селектор, например .class {z-index: 99}.
  • decl, свойство, например z-index: 99.

Нам нужно проверить значение z-индекса, поэтому нам нужно пройти через CSS, чтобы проверить z-индекс. Мы можем вызвать cssRoot.walkDecls для обхода:

// 遍历
cssRoot.walkDecls((decl) => {
  // 获取属性定义
  if (decl) { 
    // ... 
  } 
});

Базовых знаний почти достаточно.

image.png

Предположим, мы хотим проверить, соответствует ли z-индекс файлов .css в одной или двух папках.

Мы устанавливаем диапазон z-index в файлах конфигурации stylelint двух модулей.

Здесь мы видим файл конфигурации stylelint, два файла css.

├── .stylelintrc.js
├── module1
│   └── index.css
├── module2
│   └── index2.css

конфигурационный файл stylelint

// .stylelintrc.js
module.exports = {
  "extends": "stylelint-config-standard",
  // 自定义插件
  "plugins": ["./plugin.js"],
  "rules": {
    // 自定义插件的规则
    "plugin/z-index-range-plugin": {
      // 设置的范围,保证各模块不重复
      "module1": [100, 199],
      "module2": [200, 299]
    }
  }
}

Тестовый файл CSS

/* module1/index.css */
.classA {
  color: red;
  width: 99px;
  height: 100px;
  z-index: 99;
}

/* module2/index.css */
.classB {
    color: red;
    width: 99px;
    height: 100px;
    z-index: 200;
}

Чего мы хотим добиться, так это того, что выполнение следующей команды приведет к тому, что module1/index.css сообщит об ошибке, в которой говорится, что z-index меньше ожидаемого.

npx stylelint "*/index.css"

Итак, мы завершили следующий код и достигли ожидаемой цели.

const stylelint = require('stylelint');
const ruleName = 'plugin/z-index-range-plugin';

function ruleFn(options) {
  return function (cssRoot, result) {

    cssRoot.walkDecls('z-index', function (decl) {
      // 遍历路径
      const path = decl.source.input.file
      // 提取文件路径里的模块信息
      const match = path.match(/module\d/)
      // 获取文件夹
      const folder = match?.[0]
      // 获取z-index的值
      const value = Number(decl.value);
      // 获取设定的最大值、最小值
      const params = {
        min: options?.[folder]?.[0],
        max: options?.[folder]?.[1],
      }

      if (params.max && Math.abs(value) > params.max) {
        // 调用 stylelint 提供的report方法给出报错提示
        stylelint.utils.report({
          ruleName,
          result,
          node: decl,
          message: `Expected z-index to have maximum value of ${params.max}.`
        });
      }

      if (params.min && Math.abs(value) < params.min) {
        // 调用 stylelint 提供的report方法给出报错提示
        stylelint.utils.report({
          ruleName,
          result,
          node: decl,
          message: `Expected z-index to have minimum value of ${params.min}.`
        });
      }
    });
  };
}

module.exports = stylelint.createPlugin(
  ruleName,
  ruleFn
);

module.exports.ruleName = ruleName;

Что попробовать:GitHub.com/fly0oh0/В мире то жарко, то холодно…, попробуйте и почувствуйте🐶.

Таким образом, проектирование базового дальнобойного бомбового заградителя завершено.

Но все же столкнулись с некоторыми проблемами, трудными.

image.png

○ Возникшие проблемы

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

Не паникуйте, сначала съешьте дыню, чтобы успокоиться. Что делает webpackJsonp?

Пример асинхронной загрузки

Давайте рассмотрим следующий пример. Test.js загружается с помощью функции импорта асинхронной загрузки по запросу. Следующий пример основан на Webpack3.

// 异步加载 test.js
import('./test').then((say) => {
  say();
});

Затем создается файл асинхронной загрузки 0.bundle.js.

// 异步加载的文件,0.bundle.js
webpackJsonp(
  // 在其它文件中存放着的模块的 ID
  [0],
  // 本文件所包含的模块
  [
    // test.js 所对应的模块
    (function (module, exports) {
      function ;(content) {
        console.log('i am test')
      }

      module.exports = say;
    })
  ]
);

и запустите входной файл bundle.js.

// 执行入口文件,bundle.js
(function (modules) {
  /***
   * webpackJsonp 用于从异步加载的文件中安装模块。
   *
   */
  window["webpackJsonp"] = function webpackJsonpCallback(chunkIds, moreModules, executeModules) {
    var moduleId, chunkId, i = 0, resolves = [], result;
    for (; i < chunkIds.length; i++) {
      chunkId = chunkIds[i];
      if (installedChunks[chunkId]) {
        resolves.push(installedChunks[chunkId][0]);
      }
      installedChunks[chunkId] = 0;
    }
    for (moduleId in moreModules) {
      if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
        modules[moduleId] = moreModules[moduleId];
      }
    }
    while (resolves.length) {
      resolves.shift()();
    }
  };

  // 模拟 require 语句
  function __webpack_require__(moduleId) {
  }

  /**
   * 用于加载被分割出去的,需要异步加载的 Chunk 对应的文件
   */
  __webpack_require__.e = function requireEnsure(chunkId) {
    // ... 省略代码
    return promise;
  };

  return __webpack_require__(__webpack_require__.s = 0);
})
(
  [
    // main.js 对应的模块
    (function (module, exports, __webpack_require__) {
      // 通过 __webpack_require__.e 去异步加载 show.js 对应的 Chunk
      __webpack_require__.e(0).then(__webpack_require__.bind(null, 1)).then((show) => {
        // 执行 show 函数
        show('Webpack');
      });
    })
  ]
);

Видно, что роль webpackJsonp заключается в загрузке файлов асинхронных модулей. Но зачем сообщать, что webpackJsonp не является функцией?

Начать устранение неполадок

Мы начали проверять собранный исходный код и обнаружили, что наш webpackJsonp — это не функция, а массив (теперь он известен как Webpack4, на момент расследования я его не знал). ​

Мы обнаружили, что когда асинхронный файл загружается, он становится массивом, а асинхронный модуль добавляется в систему посредством push.

// 异步加载的文件

(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[/* chunk id */ 0], {
  "./src/async.js": (function(module, __webpack_exports__, __webpack_require__) {

	//...

}))

И в файле записи выполнения также обнаруживается, что webpackJsonp определяется как массив.

// 执行入口文件,bundle.js中的核心代码  

var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
jsonpArray.push = webpackJsonpCallback;
jsonpArray = jsonpArray.slice();
for (var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
var parentJsonpFunction = oldJsonpFunction;

Это правда, что webpackJsonp созданного нами исходного кода представляет собой массив, а не функцию. Я чувствую, что нашел ключ к разгадке. Но почему webpackJsonp используется в функциональной форме? ​

Мы заподозрили, что возникла проблема с отчетом об ошибке, и начали исследовать отчет об ошибке и обнаружили, что соответствующий файл действительно вызывается webpackJsonp как функция. 🤔️ ​

В это время мы заметили, что сообщения об ошибках относятся ко всем удаленным компонентам под старой архитектурой.Есть ли какие-либо подсказки в проектах старой архитектуры? ​

Мы начали изучать старую архитектуру, на этот раз старая архитектура — WebPack3, а наша новая архитектура построена с использованием WebPack4. Это проблема? Бамбук ​

Итак, мы использовали webpack3 для перестроения удаленных компонентов старой архитектуры и обнаружили, что соответствующий webpackJsonp действительно является функцией, как показано в «Примере асинхронной загрузки» в предыдущем разделе.

Итак, причина найдена, webpack4 и webpack3 соответственно создают новые и старые асинхронные удаленные компоненты, webpackJsonp — это массив в версии 4 и функция в версии 3. ​

image.png

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

image.png

Если подумать, вариантов может быть несколько.

  1. Измените имя webpackJsonp в асинхронных компонентах, созданных webpack3 в пакетном режиме, а затем настройте функцию возможности асинхронной загрузки (функция webpackJsonp) в записи страницы контейнера.
  2. Повторно используйте WebPack4 для создания всех устаревших сборок для создания асинхронных компонентов.
  3. Ищите официальную поддержку, ведь это бред смена с webpack3 на webpack4.

Рабочая нагрузка первого решения немного велика, и как обеспечить синхронное изменение асинхронных компонентов и входного файла? Во втором решении тоже много работы, все асинхронные компоненты старой архитектуры приходится обновлять, а надежность после обновления вызывает беспокойство, в случае каких-либо упущений. Третий вариант кажется самым надежным.

image.png

Итак, в направлении третьего плана начались поиски. ​

Мы провели глобальный поиск webpackJsonp в исходном коде webpack4 и нашли jsonpFunction. В официальной документации я обнаружил, что jsonpFunction — это имя webpackJsonp, которое может настраивать webpack4. Например, его можно изменить на следующее.

output: {
  // 自定义名称
  jsonpFunction: 'webpack4JsonpIsArray'
},

После этого webpackJsonp не массив, а undefined. Поэтому нам необходимо предоставить определение версии функции webpackJsonp в нашей общедоступной кодовой базе. Как упоминалось в разделе примеров асинхронной загрузки.

// webpackJsonp函数版
!(function (n) {
  window.webpackJsonp = function (t, u, i) {
		//... 
  }
}([]))

Это дает возможность страницам входа загружать асинхронные файлы, созданные webpack3.

○ Эволюция

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

проблема со сжатием изображения

Купоны слоя купонов представлены в форматах PNG, JPG и GIF, которые требуют более высокой скорости отображения, поэтому мы сделали единый сервис для сжатия изображений.

image.png

стратегия обработки gif:GitHub.com/КО Р.Н. Эльски/…стратегия обработки png:pngquant.org

Вопросы эффективности

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

image.png

○ Расширенное чтение

удаленный компонент

yangjunlong/compile-vue-demo: компилировать однофайловые компоненты vue

Webpack загружается асинхронно

Webpack как это работает (два) - Знаешь почти

Принцип Webpack — Анализ выходных файлов — Tencent Web Front-end Сообщество команды IMWeb | Блог | Блог команды