Balíček prohlížeče Meteor a Node-Stubs – pozor na to, co importujete

Meteor vám nabízí přímou zkušenost s používáním balíčků NPM v prohlížeči, které jsou cílené na platformu uzlů.

To se provádí pomocí balíčku meteor-node-stub-package.

Činí tak skenováním vašich importů v době sestavování a řeší závislosti tak, aby poskytla náhradu vhodnou pro prohlížeč. Někteří z vás možná znáte tento vzor ze slavného balíčku browserify a ve skutečnosti používá některé ze svých balíčků jako náhrady, jak můžete vidět v mapovacím souboru:

{
  "assert": "assert/",
  "buffer": "buffer/",
  "child_process": null,
  "cluster": null,
  "console": "console-browserify",
  "constants": "constants-browserify",
  "crypto": "../wrappers/crypto.js",
  "dgram": null,
  "dns": null,
  "domain": "domain-browser",
  "events": "events/",
  "fs": null,
  "http": "stream-http",
  "https": "https-browserify",
  "module": "../wrappers/module.js",
  "net": null,
  "os": "os-browserify/browser.js",
  "path": "path-browserify",
  "process": "process/browser.js",
  "punycode": "punycode/",
  "querystring": "querystring-es3/",
  "readline": null,
  "repl": null,
  "stream": "stream-browserify",
  "_stream_duplex": "readable-stream/lib/_stream_duplex.js",
  "_stream_passthrough": "readable-stream/lib/_stream_passthrough.js",
  "_stream_readable": "readable-stream/lib/_stream_readable.js",
  "_stream_transform": "readable-stream/lib/_stream_transform.js",
  "_stream_writable": "readable-stream/lib/_stream_writable.js",
  "string_decoder": "string_decoder/",
  "sys": "util/util.js",
  "timers": "timers-browserify",
  "tls": null,
  "tty": "tty-browserify",
  "url": "url/",
  "util": "util/util.js",
  "vm": "vm-browserify",
  "zlib": "browserify-zlib"
}

Zkuste to sami

Můžete to sami otestovat vytvořením nového projektu Meteor a importem balíčku specifického pro uzel do klienta:

client/main.js

import { Buffer } from 'buffer'

Meteor.startup(() => {
  console.log(Buffer.from('Buffer on the client')) // Uint8Array(20) [ 66, 117, 102, 102, 101, 114, 32, 111, 110, 32, … ]
})

To je skvělé, protože nemusíte nic konfigurovat, aby to fungovalo. Nyní je zde problém, proč to může snadno nafouknout váš balíček klientů.

Dynamicky roste

Pokud není potřeba blokovat balíček uzlů, meteor-node-stubs balíček má pouze asi 3,61 kB ve velikosti. Je to proto, že dělení kódu Meteoru v době sestavování zjistí, zda je balíček uzlu importován do klienta nebo ne.
Proto meteor-node-stubs balíček "roste", když skutečně importujete modul uzlu na klienta.

Například naše buffer zvětšil velikost balíčku stub o 23,89 kB (detekováno pomocí Meteor's bundle-visualizer).

Jak vidíte, může se vám to snadno vymknout z rukou! Pokud například použijete crypto balík na klientovi, vaše uzlové pahýly budou muset používat crypto-browserify což přidává asi 630 kB klientovi, pokud má být použita celá krypto knihovna.

Dejte si pozor na to, co importujete

V tuto chvíli jste si již měli uvědomit, že jednoduchý import čehokoli na klienta může vést k nafouknutým balíčkům, a tím k velmi dlouhým dobám načítání a značnému zpoždění v interakci.

Před importem si promyslete

Je vaší odpovědností analyzovat, který balíček chcete použít a jak jej využít.

Opravdu potřebujete Buffer na klientovi? Opravdu potřebujete kryptoměnu na klientovi nebo můžete místo toho použít Web Crypto API?

Analyzujte spoluzávislosti

Kromě balíčků jádra uzlů existují také balíčky NPM. které se konkrétně zaměřují na prostředí Node. Buďte si této skutečnosti vědomi a zkontrolujte její závislosti. Pokud balíček závisí na path například potom meteor-node-stubs obratem přidá path-browserify a pokud to závisí na stream , pak budou útržky obsahovat stream-browserify .

Jak se vyhnout nafouknutým balíkům klientů

1. Využijte dělení kódu

Meteor umožňuje psát izomorfní kód a meteor-node-stubs hraje v něm důležitou roli. Můžete tedy napsat kód jednou a použít jej na serveru i na klientovi stejným způsobem.

To je úplně v pořádku, pokud je to to, co jste zamýšleli. Pokud jste nezamýšleli, ale omylem importovali kód uzlu do klienta (například kvůli těsnému propojení nebo špatnému návrhu importů), skončíte se zvětšenou, ale nevyužitou velikostí klientského balíčku.

Abychom to vyřešili, podívejme se na krátký příklad, kde chceme vytvořit výtah SHA512 pomocí crypto na serveru a Web Crypto API v prohlížeči.

Nejprve vytvořte funkci createSHA512 pod cestou /imports/api/sha512/server/createSHA512.js . Toto je funkce našeho serveru:

import crypto from 'crypto'

export const createSHA512 = async input => await crypto.createHash('sha512').update(input).digest('base64')

Nyní to přidejte do exportu, řekněme SHA512 ale pouze na serveru. Pojďme ve skutečnosti použít Web Crypto API na klientovi:

import { Meteor } from 'meteor/meteor'

export const SHA512 = {}

if (Meteor.isServer) {
  SHA512.create = async input => {
    import { createSHA512 } from './server/createSHA512'
    return createSHA512(input)
  }
}

if (Meteor.isClient) {
  SHA512.create = async input => {
    const encoder = new TextEncoder()
    const data = encoder.encode(input)
    const hash = await window.crypto.subtle.digest({ name: 'SHA-512' }, data)
    const buffer = new Uint8Array(hash)
    return window.btoa(String.fromCharCode.apply(String, buffer))
  }
}

Funkce se bude chovat stejně na serveru i na klientovi a oba ji mohou importovat bez potřeby útržku:

/client/main.js a/nebo
/server/main.js :

import { SHA512 } from '../imports/api/sha512/SHA512'

SHA512.create('The quick brown fox jumps over the lazy dog')
  .catch(e => console.error(e))
  .then(hashed => console.debug(hashed))

Výše uvedený kód vytiskne pro server i klienta stejný výsledek, B+VH2VhvanP3P7rAQ17XaVEhj7fQyNeIownXhUNru2Quk6JSqVTyORJUfR6KO17W4b/XCXghIz+gU489uFT+5g== . Pod kapotou však používá dvě různé implementace a klientský balíček nemusí stubovat crypto balík. Uloženo 630 kB 🎉

2. Použijte dynamické importy

Pokud nemůžete na klientovi vynechat určitý balíček cílený na uzel a nepotřebujete ho hned při spuštění aplikace, měli byste použít dynamic-import odložit import modulů na pozdější dobu.

Stále se tím zvýší množství dat odesílaných klientovi, ale počáteční velikost balíčku zůstane malá, aby bylo zajištěno rychlé načítání stránky a doba interakce.

3. Pro ukládání do mezipaměti používejte ServiceWorkers

Meteor podepisuje balíčky pomocí hash, takže můžete pomocí ServiceWorkers zabránit opětovnému načítání stejného kódu pokaždé. To je ještě výkonnější v kombinaci s dynamickými importy.

Můžete to nastavit podle mého návodu "Three step Meteor PWA"".

Souhrn

Meteor node stub je skvělá funkce a důležitá součást systému sestavování Meteoru. Jeho síla se však může snadno změnit v problém, pokud se podrobně nepodíváte na to, které balíčky kam importujete a proč.


Pravidelně zde na dev.to publikuji články o Meteoru a JavaScript . Pokud se vám líbí, co čtete a chcete mě podpořit, můžete mi poslat tip přes PayPal.

Můžete mě také najít (a kontaktovat) na GitHubu, Twitteru a LinkedIn.

Sledujte nejnovější vývoj na Meteoru tím, že navštívíte jejich blog, a pokud jste do Meteoru stejní jako já a chcete to ukázat světu, měli byste se podívat do obchodu Meteor s merch.