Learning Parcel: una alternativa de Webpack para proyectos pequeños





¡Buen dia amigos!



El propósito principal de un creador de módulos o un paquete como Webpack o Parcel es garantizar que todos los módulos necesarios para que la aplicación se ejecute se incluyan en el orden correcto en un script minificado (para la compilación de producción) que se incluye en el índice. html.



De hecho, los constructores, como regla, pueden optimizar no solo JS, sino también HTML, archivos CSS, pueden convertir Less, Sass a CSS, TypeScript, React y Vue (JSX) a JavaScript, trabajar con imágenes, audio, video y otros. formatos de datos, y también proporcionan capacidades adicionales, tales como: crear un mapa de recursos o fuentes (usados) (mapa fuente), representación visual del tamaño de todo el paquete y sus partes individuales (módulos, bibliotecas), dividir el código en partes (fragmentos), incluyendo number, con fines de reutilización (por ejemplo, las bibliotecas que se usan en varios módulos se eliminan en un archivo separado y se cargan solo una vez), carga inteligente de paquetes desde npm (por ejemplo, cargando solo la localización rusa desde moment.js), todo tipo de complementos para resolver tareas específicas etc.



En este sentido, el liderazgo, por supuesto, pertenece a Webpack. Sin embargo, ¿qué pasa si estamos desarrollando un proyecto en el que la mayor parte de la funcionalidad que brinda esta gran herramienta no es necesaria? ¿Existen alternativas a esta tecnología que sean más fáciles de aprender y usar? Para mí, la respuesta a esta pregunta fue Parcel . Por cierto, si está interesado en aprender sobre Webpack, le recomiendo ver este video . Mi archivo con la configuración de Webpack para este tutorial se encuentra aquí .



















Con su permiso, no volveré a contar la documentación en mis propias palabras, especialmente porque está disponible en ruso, pero me enfocaré en el componente práctico, a saber: usando cadenas de plantillas e importación dinámica, crearemos un SPA que consta de tres páginas en JavaScript, diseñar la aplicación con CSS, escribir una función simple en TypeScript, importarla a la aplicación, diseñar el contenedor para los resultados de esta función usando Sass y construir la aplicación usando Parcel en los modos de desarrollo y producción.



El código del proyecto está aquí .



Si estás interesado, sígueme.



solicitud



Listo? Entonces vamos.



Cree el directorio de parcela-tutorial.



Entramos en él e inicializamos el proyecto usando npm init -y.



Cree el archivo index.html. Usaremos una de las plantillas estándar de Bootstrap Cover:



<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>


Vaya al sitio web oficial de Bootstrap , vaya a la sección Ejemplos, busque la Portada en los componentes personalizados, presione Ctrl + U (Cmd + U) para ver el código de la página.











Cree el directorio src, y hay dos carpetas más en él: js y css.



Cree los siguientes archivos en el directorio js: header.js, footer.js, home.js, projects.js y contact.js. Estos son módulos o, si lo desea, componentes de nuestra aplicación: encabezado, pie de página, contenido de la página principal y otras páginas.



En el directorio css, cree un archivo style.css.



Por el momento, la estructura del proyecto se ve así:



-- parcel-tutorial
    -- src
        -- css
            -- style.css
        -- js
            -- contact.js
            -- footer.js
            -- header.js
            -- home.js
            -- projects.js
    -- index.html
    -- index.js
    -- package.json


Volver a Bootstrap.



Copie y pegue el código de la página en los módulos correspondientes con cambios menores.



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()


Tenga en cuenta que hemos cambiado el href a nombre en los enlaces.



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()


proyectos.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()


No olvide copiar los estilos de cover.css a style.css.



Abra index.js.



Importamos el encabezado, pie de página y estilos:



import header from './src/js/header.js'
import footer from './src/js/footer.js'
import './src/css/style.css'


El contenido de la página principal y otras se cargará dinámicamente cuando se haga clic en el enlace, por lo que creamos un objeto como este:



const pages = {
    home: import('./src/js/home.js'),
    projects: import('./src/js/projects.js'),
    contact: import('./src/js/contact.js')
}


El nombre de propiedad de este objeto es la página correspondiente (solicitada por el usuario).



Generamos la página de inicio utilizando los componentes de encabezado y pie de página importados previamente:



// 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()


El contenido de las páginas se mostrará en el elemento principal, así lo definimos:



const mainEl = document.querySelector('main')


Cree una función de representación de página:



const renderPage = async name => {
    const template = await pages[name]
    mainEl.innerHTML = template.default
}


Tenemos que esperar a que se cargue el módulo correspondiente, por lo que usamos async / await (la palabra clave await detiene la ejecución de la función). La función toma el nombre de la página solicitada (nombre) y lo utiliza para acceder a la propiedad correspondiente del objeto de páginas (páginas [nombre]). Luego insertamos la plantilla resultante en mainEl. De hecho, await devuelve un objeto Module que contiene la plantilla. Por lo tanto, al insertar una plantilla como marcado en mainEl, debe consultar la propiedad predeterminada del objeto Módulo (los módulos se exportan de manera predeterminada); de lo contrario, obtendremos un error: es imposible convertir el objeto a HTML.



Renderizar la página principal:



renderPage('home')


El enlace activo correspondiente a la página actual tiene la clase activa. Necesitamos cambiar de clase al renderizar una nueva página. Implementemos la función auxiliar:



const toggleClass = (activeLink, currentLink) => {
    if (activeLink === currentLink) {
        return;
    } else {
        activeLink.classList.remove('active')
        currentLink.classList.add('active')
    }
}


La función toma dos argumentos: un enlace con la clase activa (activeLink) y el enlace en el que se hizo clic (currentLink). Si los enlaces especificados coinciden, no hacemos nada. De lo contrario, cambiamos las clases.



Finalmente, necesitamos agregar un controlador de clic de enlace. Implementemos una función auxiliar más:



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


En esta función, primero encontramos el elemento nav. Luego, a través de la delegación, procesamos los clics en los enlaces: si el elemento de destino es la etiqueta A, obtenemos el enlace activo (enlace con la clase activa), el enlace actual (el enlace en el que se hizo clic), cambiamos las clases y renderizamos la página. El valor del atributo de nombre del enlace actual se pasa como argumento renderPage.



Casi hemos terminado con la aplicación. Sin embargo, antes de proceder a construir un proyecto usando Parcel, es necesario tener en cuenta lo siguiente: hoy en día, el soporte para módulos dinámicos según ¿Puedo usar datos es del 90%? Eso es mucho, pero no estamos preparados para perder el 10% de nuestros usuarios. Por lo tanto, nuestro código debe convertirse a una sintaxis menos moderna. Babel se utiliza para la transpilación. Necesitamos conectar dos módulos adicionales:



import "core-js/stable";
import "regenerator-runtime/runtime";


Tenga en cuenta que no instalamos estos paquetes con npm.



Además, implementemos inmediatamente una función en TypeScript, algo muy simple, como una función para sumar dos números.



Cree un archivo index.ts en el directorio js con el siguiente contenido:



export const sum = (a: number, b: number): number => a + b


La única diferencia con JavaScript, además de la extensión del archivo (.ts), es que especificamos explícitamente los tipos de valores aceptados y devueltos por la función, en este caso, el número. De hecho, podríamos limitarnos a definir el tipo de retorno, TypeScript es lo suficientemente inteligente como para saber que si el valor de retorno es un número, entonces los argumentos pasados ​​deben ser números. No importa.



Importemos esta función en index.js:



import { sum } from './src/js/index.ts'


Y llámalo con los argumentos 1 y 2 en renderPage:



const renderPage = async name => {
    // ...
    mainEl.insertAdjacentHTML('beforeend', `
        <output>Result of 1 + 2 -> <span>${sum(1, 2)}<span></output>
    `)
}


Aplicar estilo al contenedor de resultados de la función con Sass. En la carpeta css, cree un archivo style.scss con el siguiente contenido:



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


Importemos estos estilos en index.js:



import './src/css/style.scss'


Tenga en cuenta nuevamente que no estamos instalando TypeScript y Sass con npm.



Hemos terminado con la aplicación. Pasando a Parcel.



Paquete o empaquetar



Para instalar Parcel globalmente, debe ejecutar el comando npm i parcel-bundler -gen la terminal.



Abra package.json y configure Parcel para que se ejecute en los modos de desarrollo y producción:



"scripts": {
    "dev": "parcel index.html --no-source-maps --open",
    "pro": "parcel build index.html --no-source-maps --no-cache"
  },


El equipo npm run devcomienza a construir el proyecto para el desarrollo y el equipo comienza npm run proa producir. Pero, ¿qué significan todas estas banderas? ¿Y por qué no instalamos Babel, TypeScript y Sass a través de npm?



El hecho es que Parcel instala automáticamente todas las dependencias cuando detecta su importación o uso en la aplicación. Por ejemplo, si Parcel ve hojas de estilo importadas de un archivo .scss, instala Sass.



Ahora sobre los equipos y las banderas.



Para construir el proyecto en modo de desarrollo, use el comando parcel index.html, donde index.html es el punto de entrada de la aplicación, es decir un archivo que contiene un enlace al script o scripts principales. Además, este comando inicia un servidor local en localhost: 1234.



La bandera --no-source-mapssignifica que no necesitamos mapas de recursos.



Bandera--openle dice a Parcel que abra index.html después de compilar en un navegador en el servidor local.



Para construir un proyecto en modo de producción, use el comando parcel build index.html. Este ensamblaje asume la minificación de archivos JS, CSS y HTML.



La bandera --no-cachesignifica deshabilitar el almacenamiento en caché de recursos. El almacenamiento en caché proporciona una alta velocidad de construcción y reconstrucción del proyecto en tiempo real. Esto es relevante al desarrollar, pero no demasiado al ensamblar un producto terminado.



Un punto más: Parcel coloca los archivos generados en la carpeta dist de forma predeterminada, que se crea si falta. El problema es que al reconstruir, los archivos antiguos no se eliminan. Para eliminar estos archivos, necesita un complemento especial, por ejemplo, parcel-plugin-clean-easy .



Instale este complemento usandonpm i parcel-plugin-clean-easy -D y agregue lo siguiente a package.json:



"parcelCleanPaths": [
    "dist",
    ".cache"
  ]


parcelCleanPaths son directorios que se eliminarán durante la reconstrucción.



El paquete ahora está completamente configurado. Abra una terminal, escriba npm run dev, presione enter.















Parcel crea el proyecto en modo de desarrollo, inicia el servidor local y abre la aplicación en un navegador. Excelente.



Ahora intentemos armar un proyecto para producción.



Ejecutamos el comando npm run pro.











Lanzamos la aplicación en el navegador.







Vaya, parece que algo salió mal.



Echemos un vistazo al index.html generado. ¿Qué vemos ahí? Sugerencia: preste atención a las rutas en el enlace y las etiquetas del script. No sé exactamente con qué está conectado, pero Parcel convierte los enlaces relativos a "/ ruta a archivo" y el navegador no lee dichos enlaces.



Para resolver este problema, debe agregar la bandera "--public-url" al script "pro".



Empezamos a reconstruir.



Las rutas relativas son correctas y la aplicación funciona. Frio.







Eso es todo para mi. Gracias por su atención.



All Articles