Storybook: отдельные истории втягивают таблицы стилей всех остальных историй?

Созданный на 21 мар. 2017  ·  67Комментарии  ·  Источник: storybookjs/storybook

Привет,

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

function loadStories() {
    const req = require.context('../components', true, /story.js$/);
    req.keys().forEach(filename => req(filename));
}
configure(loadStories, module);

и каждый файл story.js имеет соответствующий файл sass или css, который импортируется в него (для целей специфичных для истории стилей, которые находятся за пределами компонента, который story.js будет импортировать, для отображения:

import './story.sass';

У меня сейчас около 4 историй, и это источник каждой истории iframe ... загрузка каждой таблицы стилей:

screen shot 2017-03-21 at 9 56 22 am

Это нормальное поведение ...?

-

демо

https://github.com/moimikey/729-single-stories-pulling-all-stylesheets

bug todo

Самый полезный комментарий

Создание теневого корня вокруг историй мгновенно решит эту проблему без проблем с производительностью

Все 67 Комментарий

Кроме того, веб-пакет излучает все эти истории, поэтому мне интересно, как я настроил веб-пакет?

screen shot 2017-03-21 at 11 27 10 am

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

.addDecorator((getStory) => {
  require('./story.sass');
  return getStory();
})

@arunoda @mnmtanish

Интересно! У вас есть репо, которое это демонстрирует?

@ndelangen скоро сделает его

Все еще пытаюсь найти время, чтобы это проверить ...

спасибо; D это странно. Я посмотрел, но не знаю, с чего начать.

Итак, я думаю, что все файлы CSS подхватываются загрузчиком стилей webpack и просто вводятся в голову. используются они или нет.

Но как исправить?

Для своего CSS я использовал CSS-модули. и импортировать сгенерированные имена классов в мой JS. Даже если весь CSS вводится вместе в голову, это не имеет значения, потому что это гарантированно будут уникальные классы / селекторы.

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

это правда. Я (и моя компания) изучают css, но мы делаем огромную переработку кода, поэтому у нас есть общие стили, то есть комбинация styleName и className . Итак, что в конечном итоге влияет на сборник рассказов, так это «посторонние» файлы css.

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

@moimikey для вашей проблемы, я думаю, вам нужно пропустить загрузчик стилей и загрузить файлы css вручную с помощью декоратора. Возможно, что-то вроде этого:

.addDecorator(getStory => (
  <div>
    <link ... />
    {getStory()}
  </div>
))

Вау .... это никогда не приходило мне в голову ... Я попробую.

ick, но опять же, используя sass ... x_x. я даже пробовал inline require. но мне не повезло. это было бы отличным решением для прямого css :)

так что @mnmtanish. спасибо за ваше руководство. Я решил свою проблему с вашим вдохновением:

.addDecorator((getStory) => {
  require('./story.sass');
  return getStory();
})

ммм, так что единственная проблема сейчас в том, что когда вы переходите от истории к истории, стили складываются :(

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

Разве в обязанности React Storybook входит загрузка компонента в полной изоляции и, следовательно, запуск только JavaScript / CSS, связанных с текущей выбранной историей?

Связано ли это с https://github.com/storybooks/react-storybook/issues/686?

@ConneXNL да. хорошая точка зрения. это правда ...; X

@mnmtanish мой следующий ответ, конечно же, приводит к другой проблеме, insertAt может быть полезным, но он доступен только в последней версии style-loader , который нельзя использовать с сборником рассказов, потому что он использует [email protected] . Сборник рассказов все еще использует 0.x . последняя возможная версия, которую мы можем использовать, - это [email protected] : (...

Привет, @moimikey, может, ты попробуешь последний подход, упомянутый в альфа-версии?

Разве не лучше / безопаснее создать новый элемент iframe при переключении историй?

Это было бы безопасно, а также плохо для производительности.

Новый iframe должен будет анализировать javascript, анализировать CSS, подключаться к каналу postmessage, повторно подключаться к веб-сокету и, возможно, многое другое.

Может быть, для решения этой проблемы достаточно удаления элементов <style> при переключении истории?

Конечно, нет глобальных стилей и все правильно распределено по именам, это не будет проблемой. Принятие желаемого за действительное, я думаю. 😃

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

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

Я поигрался с удалением стилей в декораторе, но кажется, что требуемый стиль применяется только один раз. Можно ли повторно запустить require ()? Я пробовал использовать singleton: false, но это не устранило проблему.

Я почти не решаюсь даже предлагать это, но вы можете попробовать очистить кеш веб-пакетов:
https://webpack.github.io/docs/api-in-modules.html#advanced

Это документы webpack 1, но они могут работать.

Идея: вы могли бы написать декоратор, который удаляет все <style>...</style> при переключении историй. Чтобы очистить стили, не относящиеся к текущей истории.

Я почти у цели. В конфигурации веб-пакета я использую
'style-loader/useable' instead of 'style-loader',

Это добавляет API, с которым вы можете работать. .use () для добавления стилей, unuse () для их удаления. В моем файле историй я использую декоратор, например:

.addDecorator((c) => <ReactStylesheet stylesheets={[require('./stories.scss')]}>{ c() }</ReactStylesheet> )

Использование следующего компонента React для добавления и удаления стилей.

import * как React из 'react';

export class ReactStylesheet extends React.Component{

    componentWillUnmount(){
        let stylesheets = Array.isArray(this.props.stylesheets) ? this.props.stylesheets : [this.props.stylesheets];
        stylesheets.forEach((stylesheet) => {
            console.log("Unmounting....");
            stylesheet.unuse();
        });

    }

    componentDidMount(){
         let stylesheets = Array.isArray(this.props.stylesheets) ? this.props.stylesheets : [this.props.stylesheets];
        stylesheets.forEach((stylesheet) => {
            console.log("Mounting....");
            stylesheet.use();
        });
    }

    render(){
        return this.props.children;
    }
}

При правильном изменении таблицы стилей происходит горячая перезагрузка стилей. Вызывается переключение на другую историю unuse () и очищаются таблицы стилей. Однако метод не работает при повторном добавлении стилей, поскольку он рекламирует не обновленную версию таблицы стилей. Выполнение любых изменений после этого также вызывает эту ошибку:

Uncaught (in promise) TypeError: Cannot read property 'refs' of undefined
    at update (webpack:///./~/style-loader/addStyles.js?:63:4)
    at eval (webpack:///./src/Component/stories.scss?:32:4)
    at Object.hotApply [as apply] (http://dev.test:6006/static/preview.bundle.js:499:14)
    at cb (webpack:///(webpack)-hot-middleware/process-update.js?:52:36)
    at eval (webpack:///(webpack)-hot-middleware/process-update.js?:68:13)
    at <anonymous>

Я не знаю, как обновить инструкцию require, чтобы указать на последнюю версию HRM.

Фантастическая следственная работа! Раньше я искал что-то подобное и не мог найти.

Что мы можем сделать с этой стороны, чтобы помочь @ConneXNL ?

решения приближаются. идея stylesheet.use() и unuse была мне чужда, но похоже, что она идет в правильном направлении.

это еще одна интересная вещь для сборника рассказов в песочнице https://github.com/Wildhoney/ReactShadow

Всем привет! Похоже, что в последнее время в этом вопросе особо ничего не происходит. Если все еще есть вопросы, комментарии или ошибки, пожалуйста, продолжайте обсуждение. К сожалению, у нас нет времени разбираться в каждой проблеме. Мы всегда открыты для участия, поэтому, если вы хотите помочь, отправьте нам запрос на включение. Неактивные вопросы будут закрыты через 60 дней. Благодаря!

@ConneXNL, чтобы закончить эту проблему, как вы думаете, могли бы вы помочь нам улучшить документацию в этом отношении?

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

🙇

Всем привет! Похоже, что в последнее время в этом вопросе особо ничего не происходит. Если все еще есть вопросы, комментарии или ошибки, пожалуйста, продолжайте обсуждение. К сожалению, у нас нет времени разбираться в каждой проблеме. Мы всегда открыты для участия, поэтому, если вы хотите помочь, отправьте нам запрос на включение. Неактивные вопросы будут закрыты через 60 дней. Благодаря!

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

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

Создание теневого корня вокруг историй мгновенно решит эту проблему без проблем с производительностью

@bennypowers интересно! есть ли у вас пример кода, как этого добиться? 🙇

Возможно, это будет интересно и

Привет. У меня тоже возникла эта проблема
Было ли это исправлено или есть какое-то решение?

@ndelangen

Самый быстрый путь к удовлетворению здесь, вероятно , @moimikey «s предложение использовать ReactShadow

Стратегия, которую следует предпринять, может заключаться в том, чтобы обернуть корень в компоненте ReactShadow, а затем добавить стили с помощью adoptedStyleSheets (или элемента <style> для не поддерживающих браузеров)

https://github.com/storybookjs/storybook/blob/ba74d889fcfd87849a6ae9369f5e4176e8149d33/lib/core/src/client/preview/start.js#L253

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

редактировать: Спасибо !!!

Надеюсь, что это будет решено к 21 марта 2020 года.

@moimikey Есть интерес в этом? Лучший способ сделать это к определенной дате ... 😉

ИМО, мы должны добавить параметры или надстройку для обработки этой функции вместо добавления специальных функций только для таблиц стилей. Это приведет к несогласованному поведению таблиц стилей и скриптов. Но, может быть, пора задуматься, какой должна быть «изоляция»?

Я написал быстрый PoC для подхода к аддону, и кому интересно, что это возможно.
https://github.com/pocka/storybook-addon-css

@pocka Ты УДИВИТЕЛЬНЫЙ! 💯

yazzzzz. ура @pocka

Стоит отметить, что решение выражений из-за mdx-js / mdx # 894.

Изменить: Плохо, это определенно! Вам нужно иметь style-loader 1.x +, а затем сделать что-то вроде этого:

--- a/components/grid/GridChild.stories.mdx
+++ b/components/grid/GridChild.stories.mdx
@@ -1,7 +1,9 @@
 import { Meta, Story, Preview } from '@storybook/addon-docs/blocks';
 import { GridContainer, GridRow, GridChild } from './';
 import '../../shared/critical-path.scss';
-import 'o-grid/demos/src/scss/_demos.scss';
+import demoStylesModule from '!!style-loader?injectType=lazyStyleTag!css-loader!sass-loader!o-grid/demos/src/scss/_demos.scss?story';
+
+export const demoStyles = Promise.resolve(demoStylesModule);

 <Meta title="Core|Grid/GridChild" component={GridChild} />

@@ -37,7 +39,12 @@ You supply it a `colspan` prop in one of the following formats:
     ```

 <Preview>
-  <Story name="Default unresponsive columns">
+  <Story
+    name="Default unresponsive columns"
+    parameters={{
+      styles: [demoStyles],
+    }}
+  >
     <GridContainer>
       <GridRow>
         <GridChild colspan="1">

@aendrew
Спасибо, что указали на это, я совершенно забыл о MDX: no_mouth:
Я обновил PoC и добавил примеры MDX .

При использовании аддонного подхода (по стилям истории), в отличие от подхода с масштабированием файлов (по стилям файлов), на вкладке «Документы» будут представлены все таблицы стилей для каждой истории. В моем примере история "foo" и "baz" импортирует foo.css а история "bar" импортирует bar.css , тогда вкладка Документы получает как foo.css и bar.css . Я думаю, что это неизбежно, и я не знаю, приемлемо это или нет.

@pocka Я думаю, что этот подход может хорошо сработать с https://github.com/storybookjs/storybook/tree/next/addons/cssresources WDYT?

@ndelangen
Ах, верно!

foo.story = {
  parameters: {
    cssresources: [
      {
        id: 'foo',
        code: `<style>${require('!to-string-loader!css-loader!./foo.css')}</style>`,
        picked: true
      }
    ]
  }
}

Одна проблема: если пользователь импортирует очень большие таблицы стилей, вкладка «Ресурсы CSS» будет беспорядочной.

@pocka правильно, я думаю, что аддон cssresources можно значительно улучшить в этом отделе. Вы хотите взять это на себя?

@ndelangen
Да: смайлик:

Кстати, что вы думаете о том, чтобы позволить пользователям писать raw-webpack-query? (например, !to-string-loader!... ) Я думаю, нам нужно много черной магии в коде аддона, если мы хотим избавиться от этого ...

Я думаю, мы поддерживаем макросы babel по умолчанию, поэтому пользователи могут использовать macro-preval для вставки содержимого файла в пакет?

Я этого не знала, скоро посмотрю!

Привет, у меня та же проблема.

Для тех, кто сталкивается с проблемой, попробуйте этот обходной путь . (Однако для этого нужно немного глубоких знаний о веб-пакете).


@ndelangen Я взглянул на макрос, но он позволяет «загружать файлы CSS из файловой системы», верно? Я думаю, что многие пользователи хотят импортировать файлы SASS, Less, Stylus, CSS с PostCSS и т. Д., Чтобы такой подход не удовлетворял потребности. Моя текущая идея - надстройка CSSResource, добавляющая правило, которое импортирует файл CSS с to-string-loader (или file-loader ), чтобы пользователь мог импортировать файл CSS, а затем использовать его для надстройки.

// adding a rule like this
{
  test: /\.css$/,
  resourceQuery: /cssresources/,
  use: ['to-string-loader', 'css-loader', 'postcss-loader']
}

Для препроцессоров также необходима возможность настройки test и use . Можно выбрать правило для файла стиля, а затем изменить его с помощью oneOf но это будет так сложно ...

Что вы думаете?

@pocka да, это звучит как интересная концепция!

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

Привет, не могли бы вы привести пример обходного пути с помощью Vue Story?

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

Не проявляя неуважения, я считаю, что @pocka довольно недостаточен, поскольку он не изолирует стили, которые были импортированы в файлы компонентов, а не в файлы историй, что, на мой взгляд, является более распространенным шаблоном. Мое личное желание - я подозреваю, что это может разделяться и другими в этой теме - иметь возможность иметь import './Button.css' внутри Button.jsx которое используется только в файлах историй, где Button.jsx импортирован. Построение для каждой истории, как это предусмотрено Button (прямо или косвенно), не подвергался воздействию любые правила CSS из Button.css . (Здесь проблема состоит в том, чтобы убедиться, что, скажем, в OtherWidget.css отсутствуют некоторые правила, которые случайно оказались в Button.css - может быть, они были упущены из виду при рефакторинге или чем-то еще - и отсутствуют это потому, что истории для OtherWidget получают весь статически импортированный CSS всего приложения, поэтому OtherWidget по-прежнему отлично выглядит в Storybook.)

Вместо этого, я думаю, я бы изменил все загрузчики CSS на инъекцию с помощью lazyStyleTag , а затем использовал API веб-пакетов для создания нового модуля, который группирует модули CSS по файлам историй, которые в конечном итоге их требуют, и прослушивает изменение истории. события для включения и выключения соответствующих модулей.
Был ли этот подход уже рассмотрен и отвергнут, или есть какие-то проблемы, которые вы видите сейчас? Я думаю, что могу сделать все это в надстройке Storybook, но он мог бы быть чище, если бы он был интегрирован в Storybook в качестве основной функции.

Если вам нужна строгая инкапсуляция стиля, браузеры поставляются с ней. Рискуя раскрыть здесь свое собственное невежество, я все еще не уверен, почему весь этот пользовательский JavaScript (с соответствующими затратами на сложность) необходим для выполнения чего-то встроенного и готового к работе.

Почему бы просто не преобразовать DOM каждой истории в теневой корень с чем-то вроде этого

customElements.define('encapsulated-story', class EncapsulatedStory extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
  }

  /* not sure why we'd need this getter, but let's say */
  get storyHTML() {
    return this.shadowRoot.innerHTML;
  }

  set storyHTML(string) {
    this.shadowRoot.innerHTML = storyHTML;
  }
});

и всякий раз, когда история меняется

encapsulatedStory.storyHTML = theStoryDOMStringWithAllStyleTags;

и готово? Здесь theStoryDOMStringWithAllStyleTags - это просто конкатенация HTML-кода истории со всеми связанными стилями, объединенными как встроенные теги <style> . Story, как обычно, может стилизовать элемент хоста с помощью селектора :host .

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

Но как это работает с webpack? Webpack упаковывает все в пакет JavaScript, а не в строку DOM; и способ, которым в настоящее время сконфигурирован webpack, объединяет все истории в один пакет. Я не думаю, что теневой корень помогает, когда загружаемый (горячий) JavaScript вставляет стили непосредственно в заголовок документа. Чтобы это изменить, вам нужно каким-то образом изменить конфигурацию веб-пакета.

Использование теневой модели DOM для полной изоляции каждой истории также означало бы репликацию тегов стиля, общих для многих историй, в каждую из них; использование общего стиля, связанного с webpack, будет более эффективным. Может быть, недостаточно, чтобы иметь огромное значение, но, возможно, достаточно, чтобы компенсировать преимущества, если таковые имеются, от использования теневого DOM вместо использования lazyStyleTag (что, я думаю, является единственной частью сложности, которую теневые корни могли бы спасти тебя).

Мне также интересно увидеть, как это исправят рано или поздно.

Это было бы безопасно, а также плохо для производительности.

Новый iframe должен будет анализировать javascript, анализировать CSS, подключаться к каналу postmessage, повторно подключаться к веб-сокету и, возможно, многое другое.

@ndelangen, извините, что цитирую вас из 2017 года, но вы все еще думаете, что перезагрузка iframe слишком дорога? Браузеры делают такие вещи постоянно; они, вероятно, очень оптимизированы для этого. В этом случае это будет даже быстрее, чем обычная загрузка страницы, поскольку нет сетевых запросов.

Для меня выгода перевешивает стоимость, потому что я бы предпочел свежий iframe для каждой истории. Я бы допустил задержку в 600 мс за такую ​​роскошь.

(Мой вариант использования заключается в том, что я пытаюсь отобразить некоторые устаревшие компоненты angularjs в сборнике рассказов, и позвольте просто сказать, что компоненты не очень чистые. Они очень сохраняют состояние; они имеют побочные эффекты и используют службы angularjs, которые также очень запоминающийся. Вещи ломаются неожиданным образом.)

Одна из идей для поверхности API - настроить сборник рассказов в .storybook/manager.js .

addons.setConfig({
  refreshBetweenStories: true,
})

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

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

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

.addDecorator ((getStory) => {
требуется ('./ story.sass');
return getStory ();
})

ПРИВЕТ!!!!
где я могу поставить это? !!!

Была ли эта страница полезной?
0 / 5 - 0 рейтинги