Storybook: histórias individuais estão puxando as folhas de estilo de todas as outras histórias?

Criado em 21 mar. 2017  ·  67Comentários  ·  Fonte: storybookjs/storybook

Oi,

Estou tentando descobrir por que estou tendo esse problema específico. Estou carregando histórias dinamicamente como esta:

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

e cada story.js arquivo tem um respectivo arquivo sass ou css que é importado para ele (para fins de estilos específicos de história que estão fora do componente que story.js importaria, para exibir:

import './story.sass';

Tenho cerca de 4 histórias agora e esta é a fonte de cada iframe de história ... carregando cada folha de estilo:

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

Este é um comportamento normal ...?

-

demo

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

bug todo

Comentários muito úteis

Criar uma raiz sombria em torno das histórias resolveria instantaneamente este problema sem as preocupações de desempenho de @ndelangen

Todos 67 comentários

Além disso, o webpack está emitindo todas essas histórias, então estou me perguntando se é assim que configurei o webpack.

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

Tentei usar um decorador também, e acabou ficando meio quebrado, pois consegui pelo menos isolar a folha de estilo adicional da história, mas conforme eu atravessava outras, esses estilos quebrariam a menos que eu fizesse uma atualização completa.

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

@arunoda @mnmtanish

Interessante !, você tem um repo que demonstra isso?

@ndelangen fará um em breve

Ainda estou tentando encontrar tempo para verificar isso ..

obrigado; D é estranho. Já dei uma olhada, mas não sei por onde começar.

Então, o que eu acho que está acontecendo é que todos os arquivos CSS estão sendo pegos pelo webpack style-loader e apenas injetados na cabeça. se eles são usados ​​ou não.

Mas como consertar?

O que fiz para o meu CSS foi usar módulos CSS. e importe os nomes de classe gerados em meu JS. Mesmo que todos os CSS sejam injetados juntos no cabeçote, não importa, porque são classes / seletores exclusivos.

Isso não resolve exatamente o problema que você está tendo. Mas eu acho que esse é o comportamento pretendido do carregador de estilo.

isso é verdade. i (e minha empresa) estamos definindo o escopo com css, mas estamos fazendo um grande redesenvolvimento de código, portanto, compartilhamos estilos, portanto, uma combinação de styleName e className . então o que acaba afetando o livro de histórias são aqueles arquivos css "estranhos".

Vou examinar o código do livro de histórias novamente esta noite, depois de tomar algumas cervejas, e descobrir como talvez resolver isso, em termos de implementação.

@moimikey para o seu problema, acho que você tem que pular o carregador de estilos e carregar os arquivos css manualmente com um decorador. Algo assim talvez:

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

Uau .... isso nunca passou pela minha cabeça ... vou tentar isso.

ick, mas, novamente, usando sass ... x_x. Eu até tentei um requerer em linha. mas não tive muita sorte. isso seria uma excelente solução para CSS direto :)

então @mnmtanish. obrigado por sua orientação. Eu resolvi meu problema com sua inspiração:

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

mmm então o único problema agora é que quando você navega de uma história para outra, está empilhando os estilos :(

Talvez o carregador de estilos possa ser configurado para inseri-lo em algum lugar diferente do HEAD para que possa ser removido. Eu não fiz isso, então não tenho certeza se é possível. Verifique isso .

Não é responsabilidade do storybook react carregar o componente em completo isolamento e, portanto, executar apenas JavaScript / CSS que esteja relacionado à história selecionada no momento?

Isso está relacionado a https://github.com/storybooks/react-storybook/issues/686?

@ConneXNL sim. bom ponto. isso é verdade ...; X

@mnmtanish minha próxima resposta, é claro, leva a outro problema, insertAt pode ser útil, mas está disponível apenas na versão mais recente de style-loader , que não é possível usar com o livro de histórias, porque usa [email protected] . livro de histórias ainda está usando 0.x . a última versão possível que podemos usar é [email protected] : (...

Ei @moimikey, talvez você possa tentar a abordagem mencionada por último com a versão alfa?

Não é melhor / mais seguro criar um novo elemento iframe ao trocar de histórias?

Isso seria seguro e também ruim para o desempenho.

O novo iframe teria que analisar o javascript, analisar o CSS, se conectar ao canal postmessage, reconectar ao websocket e provavelmente mais coisas.

Talvez a remoção de <style> elementos na troca de história seja suficiente para resolver esse problema?

É claro que não há estilos globais e tudo tem um namespace apropriado, isso não seria um problema direto. Pensamento positivo, eu acho. 😃

Se alguém pode provar que as afirmações acima são falsas ou pode demonstrar que não há consequências negativas, sou todo ouvidos!

Eu fiz alguns testes hoje. O padrão do decorador funcionou bem, de modo que os estilos só são injetados quando você muda para a história. No entanto, tive o mesmo problema em que os estilos não são removidos ao trocar de histórias.

Eu brinquei com a remoção de estilos em um decorador, mas parece que o estilo necessário é aplicado apenas uma vez. É possível disparar novamente um require ()? Tentei usar singleton: false, mas não resolveu o problema.

Estou quase hesitante em até sugerir isso, mas você pode tentar invadir o cache do webpack:
https://webpack.github.io/docs/api-in-modules.html#advanced

Este é o webpack 1 docs, mas ainda pode funcionar.

Ideia: você poderia escrever um decorador que remova todos os <style>...</style> ao trocar de história. Para limpar os estilos não relevantes para a história atual.

Estou quase lá. Na configuração do webpack que uso
'style-loader/useable' instead of 'style-loader',

Isso adiciona uma API para você trabalhar. .use () para adicionar os estilos, unuse () para removê-los. No meu arquivo de histórias eu uso o decorador como:

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

Usando o seguinte Componente React para adicionar e remover os estilos.

import * as React from '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;
    }
}

Alterar a folha de estilo corretamente recarrega os estilos a quente. Alternando para uma história diferente, unuse () é chamado e as folhas de estilo são limpas. No entanto, o método é interrompido ao adicionar novamente os estilos, uma vez que anuncia a versão não atualizada hrm da folha de estilo. Qualquer alteração depois disso também gera este erro:

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>

Não tenho certeza de como atualizar a declaração de requerer para apontar para a versão mais recente de HRM.

Trabalho de investigação fantástico! Procurei algo assim antes e não consegui encontrar.

Podemos fazer alguma coisa neste lado para ajudar @ConneXNL ?

as soluções estão chegando perto. a ideia de stylesheet.use() e unuse era estranha para mim, mas parece que está indo no caminho certo.

isso também é outra coisa interessante para o livro de histórias de sandbox https://github.com/Wildhoney/ReactShadow

Olá a todos! Parece que não tem havido muito nesta edição ultimamente. Se ainda houver perguntas, comentários ou bugs, sinta-se à vontade para continuar a discussão. Infelizmente, não temos tempo para abordar todos os problemas. Estamos sempre abertos a contribuições, portanto, envie-nos uma solicitação de pull se quiser ajudar. Os problemas inativos serão fechados após 60 dias. Obrigado!

@ConneXNL para finalizar esta edição, você acha que poderia nos ajudar a melhorar a documentação a esse respeito?

Se você não conseguir encontrar um bom lugar para isso, basta colocá-lo em algum lugar no formato markdown. Vou cuidar de colocá-lo.

🙇

Olá a todos! Parece que não tem havido muito nesta edição ultimamente. Se ainda houver perguntas, comentários ou bugs, sinta-se à vontade para continuar a discussão. Infelizmente, não temos tempo para abordar todos os problemas. Estamos sempre abertos a contribuições, portanto, envie-nos uma solicitação de pull se quiser ajudar. Os problemas inativos serão fechados após 60 dias. Obrigado!

Olá, sou eu de novo! Vou encerrar este problema para ajudar nossos mantenedores a se concentrarem no atual roteiro de desenvolvimento. Se o problema mencionado ainda é uma preocupação, abra um novo tíquete e mencione o antigo. Parabéns e obrigado por usar o Storybook!

Mesmo problema aqui, o livro de histórias não isola cada história, tornando-o inutilizável para qualquer teste visual / teste de aceitação.

Criar uma raiz sombria em torno das histórias resolveria instantaneamente este problema sem as preocupações de desempenho de @ndelangen

@bennypowers interessante! você teria um exemplo de código sobre como fazer isso? 🙇

Pode ser interessante para @shilman também.

Oi. Também estou tendo esse problema
Isso foi corrigido ou existe alguma solução alternativa?

@ndelangen

O caminho mais rápido para a satisfação aqui é provavelmente 's sugestão @moimikey ao uso ReactShadow

A estratégia a ser seguida pode ser envolver a raiz em um componente ReactShadow e, em seguida, trazer os estilos com adoptedStyleSheets (ou um elemento <style> para navegadores sem suporte)

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

Abra novamente, este problema torna extremamente difícil adicionar estilos personalizados por história. Eu tenho histórias MDX totalmente separadas que implementam estilos personalizados para seus exemplos e, globalmente, incluir todos os estilos de cada história torna esse caso de uso insustentável.

editar: Obrigado !!!

Espero que isso seja resolvido até 21 de março de 2020.

@moimikey Tem interesse em fazer isso? A melhor maneira de garantir que seja feito até uma determinada data ... 😉

IMO devemos adicionar parâmetros ou addon para lidar com esse recurso em vez de adicionar uma funcionalidade especial apenas para folhas de estilo. Isso causará um comportamento inconsistente entre as folhas de estilo e os scripts. Mas talvez seja um bom momento para considerar como o "isolamento" deve ser?

Eu escrevi um PoC rápido para a abordagem do addon, para quem se pergunta que é possível.
https://github.com/pocka/storybook-addon-css

@pocka Você é INCRÍVEL! 💯

yazzzzz. cheers @pocka

Vale a pena notar que a solução de @pocka não funciona se você estiver usando histórias MDX por causa de mdx-js / mdx # 894.

Edit: Meu mal, definitivamente faz! Você precisa ter o style-loader 1.x + e fazer algo assim:

--- 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
Obrigado por apontar isso, esqueci completamente o MDX: no_mouth:
Eu atualizei o PoC e adicionei exemplos de MDX .

Com a abordagem addon (por estilos de história), em contraste com a abordagem de escopo de arquivo (por estilos de arquivo), a guia Docs obterá todas as folhas de estilo de cada história. No meu exemplo , a história "foo" e "baz" importando foo.css e a história "bar" importando bar.css , então a guia Docs obtém foo.css e bar.css . Acho que isso é inevitável e não sei se isso é aceitável ou não.

@pocka Acho que essa abordagem pode funcionar bem com https://github.com/storybookjs/storybook/tree/next/addons/cssresources WDYT?

@ndelangen
Ah, isso mesmo!

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

Uma preocupação: se um usuário importar folhas de estilo muito grandes, a guia "Recursos CSS" ficará confusa.

@pocka certo, acho que o complemento cssresources pode ser amplamente melhorado nesse departamento. Você quer assumir isso?

@ndelangen
Sim: sorridente:

A propósito, o que você acha de permitir que os usuários escrevam uma consulta webpack bruta? (como !to-string-loader!... ) Acho que precisamos de muita magia negra no código do complemento se quisermos nos livrar disso ...

Acho que damos suporte a macros babel por padrão, então os usuários também podem usar macro-prevalentes para injetar conteúdo de arquivo no pacote?

Eu não sabia disso, logo darei uma olhada!

Olá, estou tendo o mesmo problema.

Para quem está enfrentando o problema, tente esta solução alternativa . (É necessário um pouco de conhecimento profundo do webpack)


@ndelangen Dei uma olhada na macro, mas o que ela permite é "carregar arquivos CSS do sistema de arquivos", certo? Acho que muitos usuários querem importar arquivos SASS, Less, Stylus, CSS com PostCSS ou etc, de forma que essa abordagem não satisfaça as necessidades. Minha ideia atual é o addon CSSResource adicionar uma regra que importa o arquivo CSS com to-string-loader (ou file-loader ) para que o usuário possa importar um arquivo CSS e usá-lo para o addon.

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

Para pré-processadores, uma opção para personalizar test e use também é necessária. É possível escolher uma regra para o arquivo de estilo e modificá-la com oneOf mas seria tão complicado ...

O que você acha?

@pocka sim, parece um conceito interessante!

Olá, quer saber se isso ainda está sendo resolvido? Também estou enfrentando o problema, estou ciente da solução alternativa, mas gostaria de saber se uma correção estará disponível em breve.

Olá, você pode fornecer um exemplo de solução alternativa com uma história Vue.?

Pelo que eu posso dizer, a solução alternativa ainda resulta no empilhamento de folhas de estilo após a visualização inicial, pelo menos se você estiver usando o complemento do Docs. 😕

Sem a intenção de desrespeitar, acho que a abordagem de addon de @pocka é bastante deficiente, já que não isola estilos que foram importados em arquivos de componentes em oposição aos arquivos de história, que acho que é o padrão mais comum. Meu desejo pessoal - eu suspeito que isso pode ser compartilhado por outras pessoas neste tópico - é ser capaz de ter um import './Button.css' dentro de Button.jsx que só é usado em arquivos de história onde Button.jsx é importado. O estilo por história, da maneira que @pocka forneceu, não é tão importante para mim quanto garantir que nenhum componente que não importe Button (direta ou indiretamente) não seja afetado por quaisquer regras CSS de Button.css . (A preocupação aqui é ter certeza de que OtherWidget.css , digamos, não faltem algumas regras que inadvertidamente acabaram em Button.css vez disso - talvez eles tenham sido esquecidos em uma refatoração ou algo assim - e não isso porque as histórias para OtherWidget obtêm todos os CSS importados estaticamente de todo o aplicativo, então OtherWidget ainda parece bom no Storybook.)

O que eu acho que faria em vez disso é mudar todos os carregadores CSS para injetar lazyStyleTag e, em seguida, usar a API webpack para gerar um novo módulo que agrupa os módulos CSS pelos arquivos de história que os requerem e ouve a mudança de história eventos para ativar e desativar os módulos apropriados.
Essa abordagem já foi considerada e descartada ou há algum problema com ela que você vê agora? Acho que posso fazer tudo em um complemento Storybook, mas pode ser mais limpo se integrado ao Storybook como um recurso principal.

Se você deseja um encapsulamento de estilo forte, os navegadores o acompanham. Correndo o risco de expor minha própria ignorância aqui, ainda não tenho certeza de por que todo esse JavaScript userland (com seus custos de complexidade associados) é necessário para realizar algo que está integrado e pronto para funcionar.

Por que não apenas tornar o DOM de cada história em uma raiz de sombra com algo assim

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;
  }
});

e sempre que a história muda

encapsulatedStory.storyHTML = theStoryDOMStringWithAllStyleTags;

E feito? Aqui, theStoryDOMStringWithAllStyleTags é apenas a concatenação do HTML de uma história com todos os estilos associados concatenados <style> tags :host , normalmente.

Esse é um ponto de partida mínimo, que poderia ser construído posteriormente, talvez com algum código de biblioteca, mas pelo menos alcançará o objetivo de um encapsulamento de estilo forte sem a necessidade de todas essas novas APIs propostas.

Mas como isso funciona com o webpack? Webpack empacota tudo em um pacote JavaScript, não uma string DOM; e a forma como o webpack está configurado atualmente, ele empacota todas as histórias em um único pacote. Não acho que uma raiz de sombra ajude quando o JavaScript sendo (hot-re) carregado insere estilos diretamente no cabeçalho do documento. Você precisa fazer algumas configurações complicadas do webpack de uma forma ou de outra para mudar isso.

Usar o DOM shadow para isolar completamente cada história também significaria replicar as tags de estilo compartilhadas por muitas histórias em cada uma; usar um estilo compartilhado junto com o webpack será mais eficiente. Talvez não o suficiente para fazer uma grande diferença, mas possivelmente o suficiente para compensar os benefícios, se houver, de usar o DOM de sombra em vez de usar lazyStyleTag (que eu acho que é a única parte da complexidade que as raízes de sombra poderiam salvar você).

Também estou interessado em ver isso corrigido mais cedo ou mais tarde.

Isso seria seguro e também ruim para o desempenho.

O novo iframe teria que analisar o javascript, analisar o CSS, se conectar ao canal postmessage, reconectar ao websocket e provavelmente mais coisas.

@ndelangen, desculpe citá-lo de 2017, mas essa ainda é sua perspectiva, que recarregar um iframe é muito caro? Os navegadores fazem esse tipo de coisa constantemente; eles provavelmente estão muito otimizados para isso. Nesse caso, seria ainda mais rápido do que um carregamento de página normal, uma vez que não há solicitações de rede envolvidas.

Para mim, o benefício supera o custo, porque eu preferiria muito mais um iframe novo para cada história. Eu toleraria um atraso de até 600 ms para tal luxo.

(Meu caso de uso é que estou tentando renderizar alguns componentes do angularjs legados no livro de histórias, e vamos apenas dizer que os componentes não são muito puros. Eles têm muito estado; eles têm efeitos colaterais e usam serviços do angularjs que são também com estado. As coisas quebram de maneiras inesperadas

Uma ideia para uma superfície de API é configurar o livro de histórias em .storybook/manager.js .

addons.setConfig({
  refreshBetweenStories: true,
})

Isso pode ser considerado uma configuração de IU se você inclinar a cabeça da maneira certa.

Não haveria custo de tempo de execução para as pessoas que não habilitarem este sinalizador, e para aqueles que o habilitarem, eles _realmente_ precisam dele, então qualquer atraso seria tolerável.

Se você deseja que isso seja corrigido, vote positivamente adicionando um 👍 à descrição do problema. Usamos isso para ajudar a priorizar!

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

OI!!!!
onde posso colocar esse aqui? !!!

Esta página foi útil?
0 / 5 - 0 avaliações