
Bom dia amigos!
O objetivo principal de um construtor de módulo ou empacotador como Webpack ou Parcel é garantir que todos os módulos necessários para que o aplicativo seja executado sejam incluídos na ordem correta em um script reduzido (para construção de produção) que é incluído no índice. html.
Na verdade, os construtores, como regra, podem otimizar não apenas JS, mas também HTML, arquivos CSS, podem converter Less, Sass para CSS, TypeScript, React e Vue (JSX) para JavaScript, trabalhar com imagens, áudio, vídeo e outros formatos de dados e também fornecem recursos adicionais, como: criação de um mapa de recursos ou fontes (usados) (mapa de origem), representação visual do tamanho de todo o pacote e suas partes individuais (módulos, bibliotecas), dividindo o código em partes (pedaços), incluindo número, para fins de reutilização (por exemplo, as bibliotecas que são usadas em vários módulos são retiradas em um arquivo separado e carregadas apenas uma vez), carregamento inteligente de pacotes de npm (por exemplo, carregando apenas localização russa de moment.js), todos os tipos de plug-ins para resolver tarefas específicas etc.
Nesse aspecto, a liderança, é claro, pertence à Webpack. Porém, e se estivermos desenvolvendo um projeto em que a maior parte das funcionalidades oferecidas por esta ótima ferramenta não sejam necessárias? Existem alternativas para essa tecnologia que são mais fáceis de aprender e usar? Para mim, a resposta a essa pergunta foi Parcel . A propósito, se você estiver interessado em aprender sobre o Webpack, recomendo assistir a este vídeo . Meu arquivo com as configurações do Webpack para este tutorial está localizado aqui .




Com sua permissão, não vou recontar a documentação em minhas próprias palavras, especialmente porque está disponível em russo, mas vou me concentrar no componente prático, a saber: usando strings de modelo e importação dinâmica, criaremos um SPA consistindo de três páginas em JavaScript, defina o estilo do aplicativo com CSS, escreva uma função simples em TypeScript, importe-o no aplicativo, defina o estilo do contêiner para os resultados dessa função usando Sass e crie o aplicativo usando Parcel nos modos de desenvolvimento e produção.
O código do projeto está aqui .
Se você estiver interessado, por favor me siga.
inscrição
Pronto? Então vamos.
Crie o diretório parcel-tutorial.
Vamos lá e inicializamos o projeto usando
npm init -y
.
Crie o arquivo index.html. Usaremos um dos modelos padrão de capa de bootstrap:
<head>
...
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
</head>
<!-- Bootstrap class -->
<body class="text-center">
<! -- Main script -->
<script src="index.js"></script>
</body>
Vá para o site oficial do Bootstrap , vá para a seção Exemplos, encontre a Capa em componentes personalizados, pressione Ctrl + U (Cmd + U) para visualizar o código da página.


Crie o diretório src e haverá mais duas pastas nele - js e css.
Crie os seguintes arquivos no diretório js: header.js, footer.js, home.js, projects.js e contact.js. São módulos ou, se preferir, componentes de nossa aplicação: cabeçalho, rodapé, conteúdo da página principal e outras páginas.
No diretório css, crie um arquivo style.css.
No momento, a estrutura do projeto se parece com esta:
-- parcel-tutorial
-- src
-- css
-- style.css
-- js
-- contact.js
-- footer.js
-- header.js
-- home.js
-- projects.js
-- index.html
-- index.js
-- package.json
De volta ao Bootstrap.
Copie e cole o código da página nos módulos correspondentes com pequenas alterações.
header.js:
export default `
<header class="masthead mb-auto">
<div class="inner">
<h3 class="masthead-brand">Parcel Tutorial</h3>
<nav class="nav nav-masthead justify-content-center">
<a class="nav-link active" name="home">Home</a>
<a class="nav-link" name="projects">Projects</a>
<a class="nav-link" name="contact">Contact</a>
</nav>
</div>
</header>
`.trim()
Observe que alteramos o href para name nos links.
footer.js:
export default `
<footer class="mastfoot mt-auto">
<div class="inner">
<p>© 2020. All rights reserved.</p>
</div>
</footer>
`.trim()
home.js:
export default `
<h1 class="cover-heading">Home page</h1>
<p class="lead">Home page content</p>
`.trim()
projects.js:
export default `
<h1 class="cover-heading">Projects page</h1>
<p class="lead">Projects page content</p>
`.trim()
contact.js:
export default `
<h1 class="cover-heading">Contact page</h1>
<p class="lead">Contact page content</p>
`.trim()
Não se esqueça de copiar os estilos de cover.css para style.css.
Abra index.js.
Vamos importar o cabeçalho, rodapé e estilos:
import header from './src/js/header.js'
import footer from './src/js/footer.js'
import './src/css/style.css'
O conteúdo da página principal e de outras páginas será carregado dinamicamente quando o link for clicado, então criamos tal objeto:
const pages = {
home: import('./src/js/home.js'),
projects: import('./src/js/projects.js'),
contact: import('./src/js/contact.js')
}
O nome da propriedade deste objeto é a página correspondente (solicitada pelo usuário).
Geramos a página inicial usando os componentes de cabeçalho e rodapé importados anteriormente:
// Bootstrap classes
document.body.innerHTML = `
<div class="cover-container d-flex w-100 h-100 p-3 mx-auto flex-column">
${header}
<main role="main" class="inner cover"></main>
${footer}
</div>
`.trim()
O conteúdo das páginas será exibido no elemento principal, por isso o definimos:
const mainEl = document.querySelector('main')
Crie uma função de renderização de página:
const renderPage = async name => {
const template = await pages[name]
mainEl.innerHTML = template.default
}
Precisamos esperar o módulo correspondente carregar, então usamos async / await (a palavra-chave await pausa a execução da função). A função obtém o nome da página solicitada (nome) e o utiliza para acessar a propriedade correspondente do objeto de páginas (páginas [nome]). Em seguida, inserimos o modelo resultante em mainEl. Na verdade, await retorna um objeto Module que contém o modelo. Portanto, ao inserir um modelo como marcação em mainEl, é necessário referir-se à propriedade padrão do objeto Módulo (os módulos são exportados por padrão), caso contrário, receberemos um erro - é impossível converter o objeto para HTML.
Renderize a página principal:
renderPage('home')
O link ativo correspondente à página atual contém a classe ativa. Precisamos trocar de classe ao renderizar uma nova página. Vamos implementar a função auxiliar:
const toggleClass = (activeLink, currentLink) => {
if (activeLink === currentLink) {
return;
} else {
activeLink.classList.remove('active')
currentLink.classList.add('active')
}
}
A função leva dois argumentos - um link com a classe ativa (activeLink) e o link que foi clicado (currentLink). Se os links especificados corresponderem, não faremos nada. Caso contrário, mude as classes.
Finalmente, precisamos adicionar um manipulador de clique em link. Vamos implementar mais uma função auxiliar:
const initClickHandlers = () => {
const navEl = document.querySelector('nav')
navEl.addEventListener('click', ev => {
if (ev.target.tagName === 'A') {
const activeLink = navEl.querySelector('.active')
const currentLink = ev.target
toggleClass(activeLink, currentLink)
renderPage(currentLink.name)
}
})
}
Nesta função, primeiro encontramos o elemento nav. Então, por delegação, processamos os cliques nos links: se o elemento alvo for a tag A, obtemos o link ativo (link com a classe ativa), o link atual (o link que foi clicado), mudamos as classes e renderizamos a página. O valor do atributo name do link atual é passado como o argumento renderPage.
Estamos quase terminando o aplicativo. Porém, antes de prosseguir com a construção de um projeto no Parcel, é necessário observar o seguinte: hoje, o suporte para módulos dinâmicos de acordo com Can I use data é de 90%. Isso é muito, mas não estamos prontos para perder 10% de nossos usuários. Portanto, nosso código precisa ser convertido para uma sintaxe menos moderna. Babel é usado para transpilação. Precisamos conectar dois módulos adicionais:
import "core-js/stable";
import "regenerator-runtime/runtime";
Observe que não instalamos esses pacotes com o npm.
Além disso, vamos implementar imediatamente uma função em TypeScript, algo muito simples, como uma função para adicionar dois números.
Crie um arquivo index.ts no diretório js com o seguinte conteúdo:
export const sum = (a: number, b: number): number => a + b
A única diferença do JavaScript, além da extensão do arquivo (.ts), é que especificamos explicitamente os tipos de valores aceitos e retornados pela função - neste caso, número. Na verdade, poderíamos nos limitar a definir o tipo de retorno. O TypeScript é inteligente o suficiente para entender que, se o valor de retorno for um número, os argumentos passados devem ser números. Deixa pra lá.
Vamos importar esta função para index.js:
import { sum } from './src/js/index.ts'
E chame-o com os argumentos 1 e 2 em renderPage:
const renderPage = async name => {
// ...
mainEl.insertAdjacentHTML('beforeend', `
<output>Result of 1 + 2 -> <span>${sum(1, 2)}<span></output>
`)
}
Estilo do contêiner de resultado da função usando Sass. Na pasta css, crie um arquivo style.scss com o seguinte conteúdo:
$color: #8e8e8e;
output {
color: $color;
border: 1px solid $color;
border-radius: 4px;
padding: .5rem;
user-select: none;
transition: transform .2s;
& span {
color: #eee;
}
&:hover {
transform: scale(1.1);
}
}
Vamos importar esses estilos para index.js:
import './src/css/style.scss'
Observe novamente que não estamos instalando TypeScript e Sass com npm.
Concluímos o aplicativo. Seguindo para o Parcel.
Parcela
Para instalar o Parcel globalmente, você precisa executar o comando
npm i parcel-bundler -g
no terminal.
Abra o package.json e configure o Parcel para ser executado nos modos de desenvolvimento e produção:
"scripts": {
"dev": "parcel index.html --no-source-maps --open",
"pro": "parcel build index.html --no-source-maps --no-cache"
},
A equipe
npm run dev
começa a construir o projeto para desenvolvimento e a equipe começa npm run pro
a produção. Mas o que todas essas bandeiras significam? E por que não instalamos Babel, TypeScript e Sass via npm?
O fato é que o Parcel instala automaticamente todas as dependências ao detectar sua importação ou uso na aplicação. Por exemplo, se Parcel vê folhas de estilo importadas de um arquivo .scss, ele instala o Sass.
Agora sobre as equipes e bandeiras.
Para construir o projeto em modo de desenvolvimento, use o comando
parcel index.html
, onde index.html é o ponto de entrada do aplicativo, ou seja, um arquivo que contém um link para o script ou scripts principais. Além disso, este comando inicia um servidor local em localhost: 1234.
A bandeira
--no-source-maps
significa que não precisamos de mapas de recursos.
Bandeira
--open
diz ao Parcel para abrir index.html após compilar em um navegador no servidor local.
Para construir um projeto em modo de produção, use o comando
parcel build index.html
. Esta montagem pressupõe a minimização de arquivos JS, CSS e HTML.
O sinalizador
--no-cache
significa desabilitar o cache de recursos. O cache fornece alta velocidade de construção e reconstrução do projeto em tempo real. Isso é relevante no desenvolvimento, mas não muito na montagem de um produto acabado.
Mais um ponto: por padrão, o Parcel coloca os arquivos gerados na pasta dist, que é criada se estiver faltando. O problema é que, ao reconstruir, os arquivos antigos não são excluídos. Para remover esses arquivos, você precisa de um plugin especial, por exemplo parcel-plugin-clean-easy .
Instale este plugin usando
npm i parcel-plugin-clean-easy -D
e adicione o seguinte ao package.json:
"parcelCleanPaths": [
"dist",
".cache"
]
parcelCleanPaths são diretórios a serem removidos na reconstrução.
Parcel agora está totalmente configurado. Abra um terminal, digite
npm run dev
e pressione Enter.



O Parcel cria o projeto no modo de desenvolvimento, inicia o servidor local e abre o aplicativo em um navegador. Excelente.
Agora vamos tentar montar um projeto para produção.
Nós executamos o comando
npm run pro
.


Lançamos o aplicativo no navegador.

Ops, parece que algo deu errado.
Vamos dar uma olhada no index.html gerado. O que vemos lá? Dica: preste atenção aos caminhos nas tags de link e script. Não sei exatamente com o que está conectado, mas o Parcel converte links relativos em "/ caminho para arquivo" e o navegador não lê esses links.
Para resolver este problema, você precisa adicionar o sinalizador "--public-url." Ao script "pro".
Começamos a reconstruir.
Os caminhos relativos estão corretos e o aplicativo funciona. Legal.

Isso é tudo para mim. Obrigado pela atenção.