Vue(2.x), Storybook(5.x), 웹 구성 요소 e nient'altro


  • Intro
  • Cos'è Vue.js?
  • Cosa sono i Web Components?
  • Cos'è Storybook?

  • Definizione problema
  • Creazione progetto di test
  • Aggiunta di Storybook
  • Creazione di un Web Component
  • Problema con gli stili
  • Ipotesi sulla soluzione

  • implementazione di una soluzione
  • vue_config.js
  • Includere il web component nella story
  • Registrare il componente
  • Integrare l'interfaccia delle stories
  • Conclusioni e credits
  • 소개

    CosèVue。js?

    Vediamo cosa dice la documentazione:

    Vue (pronounced /vjuː/, like view) is a progressive framework for building user interfaces. Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable. The core library is focused on the view layer only, and is easy to pick up and integrate with other libraries or existing projects. On the other hand, Vue is also perfectly capable of powering sophisticated Single-Page Applications when used in combination with modern tooling and supporting libraries.
    [...]

    In atre parole, Vue è framework javascript da utilizzarsi nella realizzazione di frontend. Dalla sua ha la semplicità d'uso e di setup, il template code richiesto è minimo ed è comunque performante, tanto da essere riuscito a ritagliarsi nel tempo un proprio rispettabilissimo spazio accanto a framework decisamente molto più noti ed utilizzati (sì ovviamente sto parlando di Angular e React). Niente di più, niente di meno.

    Cosa sono i 웹 구성 요소?

    Se ne è scritto e tutt'ora se ne scrive (e spero se ne continuerà a scrivere) tantissimo, mi limiterò a fornire una piccola sintesi: i web components, in breve, non sono altro che componenti frontend i quali, una volta registrati dal browser e quindi da questo riconosciuti, possono essere usati come normali tag html con i propri attributi, parametri e comportamento peculiare.
    Possono essere definiti tramite classi in js vanilla o usando un framework che li supporti, nello specifico, come è facile intuire, in questo articolo si parlerà di web component definiti utilizzando Vue.js

    이야기책

    Storybook è un eccellente tool per il testing visuale di componenti UI, compatibile con tutti i maggiori framework js ed utilizzabile anche con js vanilla. Tutto quello che si deve fare è specificare quale componente renderizzare, fornire dei mock data e lasciare che storybook istanzi il nostro componente in un proprio iframe e il gioco è fatto. La criticità con vue nasce dalla difficoltà di poter istanziare semplici web components senza utilizzare altre dipendenze.


    문제 정의

    Creazione progetto di 시험

    Creare web components con Vue non è un problema, tanto che la sua cli permette di specificare un target apposito per questo compito e,con alcuni accorgimenti,èpossibile testarli anche con il server di sviluppo.
    Cerchiamo ora di andare un po'piùnel dettaglio,la procedura per definire un web component in Vueèdecisamente banale,partiamo da un normale progetto Vue:
    vue create vue-webcomponent-storybook-test
    
    la mia configurazione customèstata typescript,babel,scss(dart sass)e basic linter on save.
    Ciòche si otterrásaráun'alberatura di questo tipo:
    ├── dist
    ├── node_modules
    ├── public
    │   ├── favicon.ico
    │   └── index.html
    ├── src
    │   ├── assets
    │   │   └── logo.png
    │   ├── components
    │   │   └── HelloWorld.vue
    │   ├── App.vue
    │   ├── main.ts
    │   ├── shims-tsx.d.ts
    │   └── shims-vue.d.ts
    ├── .gitignore
    ├── babel.config.js
    ├── package.json
    ├── README.md
    ├── tsconfig.json
    ├── vue.config.js
    └── yarn.lock
    
    Se tuttoèandato liscio,da terminale,lanciando yarn serve,potremo vedere la nostra app con il componente HelloWorld.vue di test,fare bella mostra di Se su http://localhost:8080/.

    Aggiunta di 이야기책

    Il secondo passo consiste nell'installare Storybook attraverso il plugin manager di Vue,anche qui l'operazione nonèparticolarmante impegnativa:
    vue add storybook
    
    이야기책 aggiunger á alcuni file e cartelle:
    ├── config
    │   └── storybook
    │       └── storybook.js
    ├── dist
    ├── node_modules
    ├── public
    │   ├── favicon.ico
    │   └── index.html
    ├── src
    │   ├── assets
    │   │   └── logo.png
    │   ├── components
    │   │   ├── Helloworld.vue
    │   │   └── MyButton.vue
    │   ├── stories
    │   │   ├── index.stories.js
    │   │   └── index.stories.mdx
    │   ├── App.vue
    │   ├── main.ts
    │   ├── shims-tsx.d.ts
    │   └── shims-vue.d.ts
    ├── .gitignore
    ├── babel.config.js
    ├── package.json
    ├── README.md
    ├── tsconfig.json
    ├── vue.config.js
    └── yarn.lock
    
    Possiamo eliminare Tranquilamente il componente src/components/MyButton.vue e la story src/stories/index.stories.mdx,non saranno necessari al nostro progetto.
    All'interno di src/stories/index.stories.js creiamo una story per il nostro componente App.vue:

    Ora lanciando il task storybook:serve,si avvieráun server di test che permetterádi eseguire storybook e testare il nostor component:


    npm run storybook:serve
    


    (Al momento della scrittura sembra che avviare 이야기책 con yarn non sia Possible)


    Creazione di un Web구성 요소

    Il secondo passo consiste nel wrappare il nostro componente (lavoreremo con il componente root di default, App.vue , questo ci permetterà di vedere l'inclusione di altri componenti e come si comportano i loro stili, tutto è ovviamente replicabile per qualunque componente) all'interno di una classe che estende HTMLElement (link approfondimento), questo non verrà fatto direttamente da noi, ma attraverso una api fornita da Vue. Alla fine di questo step il file main.ts avrà questo aspetto:

    customElements.define fa parte dell'api js che materialmente permette di registrare il nostro componente al browser con il tag name my-web-component .
    Una piccola nota, se usate typescript come il sottoscritto, potreste dover aggiungere al file shim-vue.d.ts la definizione del modulo per @vue/web-component-wrapper :

    declare module '@vue/web-component-wrapper';
    

    Questo per evitare l'errore Could not find a declaration file for module '@vue/web-component-wrapper'. che su ide come IntelliJ e simili potrebbe essere apparso, strano non sia presente un d.ts già preinstallato che risolve il problema.

    A questo punto nell' index.html del nostro progetto (in public/index.html ) dovremo liberarci del root component predefinito (il div con id="app" ) e sostituirlo con il nostro componente appena registrato. Il nostro index, quindi, sarà:


    conglistili 문제

    Lanciando ora il comando yarn serve vedremo in nostro componente funzionare alla grande, no?


    베노


    Cioèsì...말은 진리에 있어서 전유가 아니다...비둘기 diavolo sono finiti gli stili


    Il guaioèche Vue ha incluso gli stili nel tag <head> della pagina come farebbe normalmente,ma Il nostro component eèrinchiuso dentro ad uno shadow dom(https://w3c.github.io/webcomponents/spec/shadow/),una sorta di orizzonte degli eventi attraverso cuièdifficile(non impossible,qualcosa passa tutto sommato)far passare informazione


    Con Storybook invece?bhe le cose non migliorano di molto,anzi il problema si ripropone.Modificando il nostro index.stories.js





    Regindo il componente prima di utilizzarlo(이야기책attualmente sembra non utilizzare quanto definito nel main.ts), èpossibile renderizzarlo, ma non-sono applicationgli stili:







    Ipotesi sulla soluzione

    Una possibile soluzione è descritta qui,a quanto pare l'opzione shodowMode di vue-loaderèsettata a false di default,da qui il cursioso comportamente riscontrato.A questo punto mettere A true quella proprietádovrebbe risolvere il problema




    vue_ 구성.js

    Tutto ciò di cui abbiamo bisogno ora è il file vue.config.js nella root del nostro progetto;se ancora non esiste,creiamolo


    sapere con cosa riempire il nostro file è necessario ispezionare la configurazione webpack del nostro progetto con il comando:


    vue inspect
    

    Il risultato assomiglieráa questo:





    Se guardiamo attentamente questo output,possiamo notare alcuni commenti interestsanti,per esempio:


    /* config.module.rule('css').oneOf('vue').use('vue-style-loader') */
    

    che illustrano l'api necessaria a generare quel determino pezzetto di configurazione,questa api,infatti,èparte di webpack-chain(https://github.com/neutrinojs/webpack-chain) 도구 실용 프로그램은 웹 페이지마다 편리한 도구 lastesura di file di configurazione입니다.Visto cheègiáinstallato nel nostro progetto,lo possiamo usare a nostro favore


    Ovviamente le parti della configurazione che interestsano a noi sono quelle in cui appare la proprietáshadowmode: false,qui sotto l'estratto delle parti interestate:





    Ora,ciòche mettiamo nel vue_config.js verráintercentato da webpack e integrato nel processo di compileazione,e alla fine dovrebbe essere una cosa di questo genre:





    questo script aggiunge shadowMode=false ovunque sia necessario e permette a webpack di procedere con la compileazione,finalmente quello che si avrásaráun web component corrette renderizato che incapsula tutti suoi stili:





    Includere il 웹 구성 요소 nella story()

    Se lanciamo storybook ora, vedremo che anche lì il nostro componente sarà corretamente renderizzato, tuttavia l'api di storybook in questo caso non ci aiuta: come facciamo a passare dati al nostro componente in modo efficiente? Se questi dati sono oggetti complessi? Come si può interfacciare il nostro web component con l'api esposta dall'addon knobs?

    Ok andiamo con ordine:

    )
    등록 il 구성 요소

    Questa è facile, ogni componente deve essere registrato come abbbiamo detto, un a possibilità è implementare una funzione che controlli se già il componetne non sia stato registrato e in caso contrario proceda di conseguenza, qualcosa del genere:

    Davvero molto semplice, gli elementi non registrati hanno come constructor HTMLElement() , è sufficente fare un check e il gioco è fatto.

    Successivamente, il componente va registrato:

    anche qui nulla di nuovo, la procedura è quella vista poco sopra, soltanto chiusa dentro una funzione.


    Integrare l'interfaccia delle stories

    Ora dobbiamo fare in modo di poter usare l' addon-knobs per poter passare dati al nostro componente e renderli reattivi ai cambiamenti che possiamo fare durante i test, la mia soluzione è stata costruire una funzione che restituisse un componente e succesivamente ne recuperasse il riferimento per potergli passare eventuali dati:

    Cerchiamo di capire cosa questo script effettivamente fa:

    export const webComponentWrapper = ({props, template}) => {
    ...
    

    In ingresso ci si aspetta un oggetto, per esempio:

    props: {
      test: [
        ['test', true, 'GROUP-ID1'],
        boolean
      ],
    },
    template: '<test-component></test-component>'
    

    formato dalla proprietà props che sarà un altro oggetto, i suoi elementi avranno come chiave il nome della proprietà del nostro componente e per valore un array dove il primo elemento sarà un ulteriore array formato da

    • nome proprietà (sì c'è un po' di ridondanza di cui è possibile liberarsi),
    • valore che si dovrà considerare
    • e l'etichetta che vogliamo dare al gruppo di dati di quello specifico knob.

    Il secondo valore, invece, la funzione dell'addon knobs che verrà utilizzata per trattare quello specifico tipo di dato (in questo caso boolean ).
    template invece è una stringa che rappresenta il nostro componente e quello che contiene.


    ...
    const id = generateRandomNumber(0, 10 ** 16);
    ...
    

    Qui generiamo un id casuale che verrà utilizzato poi per applicarlo al componente e recuperarne il riferimento, io ho creato una funzione apposta, ma in effetti può essere benissimo un timestamp qualunque.


    ...
    for (const key in props) {
      if (Object.hasOwnProperty.call(props, key)) {
    
        const old = key + 'Old' + id;
        const value = key + 'Value' + id;
    
        props[old] = null;
        props[value] = () => (props[old] !== null) ? props[old] : props[key][0][1];
      }
    }
    ...
    

    Ora cominciamo a lavorare sui dati da passare al componente: prima di tutto prendiamo la proprietà props e ne scorriamo il contenuto, per ogni elemento preso in considerazione, lo arricchiamo di altre due proprietà (le variabili old e value ), alla prima diamo null alla seconda una funzione che ritornerà il vecchio valore ( old ) o quello di 'default' passato assieme alle proprietà (per capirci, il valore true nel ['test', true, 'GROUP-ID1'] di cui abbiamo parlato più su) a seconda che il vecchio valore esista o meno.

    Ogni volta che in Storybook selezioniamo un certo componente questo viene reinizializzato, con questo sistema riusciamo a passare sempre l'ultimo valore usato nei knobs, diversamente ritornando su un componente perderemmo le modifiche fatte durante i nostri test e vedremmo sempre il primo valore passato.


    return () => {
      setTimeout(() => {
    
        const root = document.getElementById(id.toString());
        const old = 'Old' + id;
        const value = 'Value' + id;
    
        for (const key in props) {
    
          if (Object.prototype.hasOwnProperty.call(props, key) && !key.includes(old) && !key.includes(value)) {
    
            const knobsParams = props[key][0];
            const knobsFunction = props[key][1];
            const tagElem = props[key][2];
    
            knobsParams[1] = props[key + value]();
            props[key + old] = props[key][1](...knobsParams);
    
            if (tagElem) {
              const elems = root.getElementsByTagName(tagElem)
              elems.forEach((item) => {
                item[key] = props[key + old];
              })
            }
            else {
              root[key] = props[key + old];
            }
          }
        }
    
      });
    
      return newTemplate;
    }
    

    la funzione ritornata è quella che verrà eseguita da Storybook ogni volta che si selezionerà quel determinato componente.

    Prima che questa ritorni il template (nulla più che una stringa del tipo <my-web-component></my-web-component> ), si esegue un timeout privo dei milliscondi di durata, questo permette all'handler di rientare nella coda del loop event appena sarà possibile (più informazioni qui), in questo caso appena il template diventa un elemento della pagina.

    Viene recuperato il riferimento del componente tramite l'id calcolato prima, dopo di che vengono recuperati i dati dall'oggetto passato alla funzione e passati al componente. Come detto prima, il dato viene salvato nella proprietà aggiunta prima (qui props[key + old] = props[key][1](...knobsParams); ).



    결론 I e credits

    E questo è quanto, mettendo tutto assieme, si riesce ad avere un progetto Vue per testare web components (e non solo normali classi Vue) con Storybook e il dev server incluso. Qui trovate un repository con un progetto di test completo e funzionante


    봉티:


    좋은 웹페이지 즐겨찾기