Aller au contenu

Feuille de chaleur de la parcelle

Parcel - L'outil de construction de configuration zéro

Parcel est un groupeur d'applications web, différencié par son expérience de développeur. Il offre des performances rapides en utilisant le traitement multicore, et nécessite une configuration zéro.

Copier toutes les commandes Générer PDF

Sommaire

  • [Installation] (#installation)
  • [Pour commencer] (#getting-started)
  • [Structure du projet] (#project-structure)
  • Serveur de développement
  • [Bâtiment pour la production] (#building-for-production)
  • [Types d'ensemble] (#asset-types)
  • [Transformations] (#transformations)
  • [Doublure du code] (#code-splitting)
  • [Remplacement du module hôte] (#hot-module-replacement)
  • [variables environnementales] (#environment-variables)
  • [Plugins] (#plugins)
  • [Configuration] (#configuration)
  • [Optimisation] (#optimization)
  • [Objectifs] (#targets)
  • [Cachage] (#caching)
  • Cartes des sources
  • [Analyse préliminaire] (#bundle-analysis)
  • [Migration] (#migration)
  • [Meilleures pratiques] (#best-practices)

Installation

Installation mondiale

# Install Parcel globally
npm install -g parcel

# Or with Yarn
yarn global add parcel

# Check version
parcel --version

Installation locale (recommandée)

# Initialize npm project
npm init -y

# Install Parcel as dev dependency
npm install --save-dev parcel

# Or with Yarn
yarn add --dev parcel
```_

### Paquet.json Scripts
```json
{
  "scripts": {
    "start": "parcel src/index.html",
    "dev": "parcel src/index.html --open",
    "build": "parcel build src/index.html",
    "clean": "rm -rf dist .parcel-cache"
  }
}
```_

## Commencer

### HTML de base Point d'entrée
```html
<!-- src/index.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>My Parcel App</title>
</head>
<body>
  <div id="app"></div>
  <script type="module" src="./index.js"></script>
</body>
</html>

Basic JavaScript Entrée

// src/index.js
import './styles.css';

console.log('Hello from Parcel!');

// Import and use a module
import { greet } from './utils';
greet('World');

CSS de base

/* src/styles.css */
body {
  font-family: Arial, sans-serif;
  margin: 0;
  padding: 20px;
  background-color: #f0f0f0;
}

.container {
  max-width: 800px;
  margin: 0 auto;
  background: white;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}

Exécuter le serveur de développement

# Start development server
npm start

# Or with specific port
parcel src/index.html --port 3000

# Open browser automatically
parcel src/index.html --open

Structure du projet

Projet de parcelle typique

my-parcel-app/
├── src/
   ├── index.html
   ├── index.js
   ├── styles.css
   ├── components/
      ├── Header.js
      └── Footer.js
   ├── assets/
      ├── images/
      └── fonts/
   └── utils/
       └── helpers.js
├── dist/           # Build output
├── .parcel-cache/  # Parcel cache
├── package.json
└── .gitignore

Points d'entrée multiples

{
  "scripts": {
    "start": "parcel src/*.html",
    "build": "parcel build src/*.html"
  }
}

Structure du projet de bibliothèque

my-library/
├── src/
   ├── index.js
   ├── components/
   └── utils/
├── package.json
└── .parcelrc

Serveur de développement

Serveur de développement de base

# Start dev server
parcel src/index.html

# Custom port
parcel src/index.html --port 8080

# Custom host
parcel src/index.html --host 0.0.0.0

# HTTPS
parcel src/index.html --https

# Open browser
parcel src/index.html --open

Options de serveur de développement

# Disable HMR
parcel src/index.html --no-hmr

# Disable source maps
parcel src/index.html --no-source-maps

# Custom dist directory
parcel src/index.html --dist-dir build

# Watch additional files
parcel src/index.html --watch-dir src/data

Configuration de proxy

// package.json
{
  "scripts": {
    "start": "parcel src/index.html --port 3000"
  },
  "@parcel/resolver-default": {
    "packageExports": true
  }
}

Bâtiment pour la production

Construction de production de base

# Build for production
parcel build src/index.html

# Build with custom output directory
parcel build src/index.html --dist-dir build

# Build without source maps
parcel build src/index.html --no-source-maps

# Build without optimization
parcel build src/index.html --no-optimize

Configuration de construction

{
  "scripts": {
    "build": "parcel build src/index.html",
    "build:analyze": "parcel build src/index.html --reporter @parcel/reporter-bundle-analyzer",
    "build:stats": "parcel build src/index.html --reporter @parcel/reporter-bundle-buddy"
  }
}

Optimisations de la production

# Enable scope hoisting
parcel build src/index.html --experimental-scope-hoisting

# Custom public URL
parcel build src/index.html --public-url /my-app/

# Detailed bundle report
parcel build src/index.html --detailed-report

Types d'actifs

JavaScript et TypeScript

// ES6 modules
import { helper } from './utils/helper.js';

// CommonJS
const lodash = require('lodash');

// Dynamic imports
const module = await import('./dynamic-module.js');

// TypeScript
import { Component } from './Component.ts';

CSS et préprocesseurs

/* CSS imports */
@import './normalize.css';
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap');

/* CSS modules */
.container {
  composes: flex from './layout.css';
  background: var(--primary-color);
}
// SCSS
@import './variables';

.component {
  background: $primary-color;

  &:hover {
    background: darken($primary-color, 10%);
  }
}
// Less
@import './variables.less';

.component {
  background: @primary-color;

  &:hover {
    background: darken(@primary-color, 10%);
  }
}

Images et biens

// Import images
import logo from './assets/logo.png';
import icon from './assets/icon.svg';

// Use in HTML
document.getElementById('logo').src = logo;

// CSS background images
.hero {
  background-image: url('./assets/hero.jpg');
}

Polices

/* Font imports */
@font-face {
  font-family: 'CustomFont';
  src: url('./assets/fonts/custom-font.woff2') format('woff2'),
       url('./assets/fonts/custom-font.woff') format('woff');
}

body {
  font-family: 'CustomFont', sans-serif;
}

JSON et fichiers de données

// Import JSON
import data from './data.json';
import config from './config.json';

// Import text files
import template from './template.html';
import shader from './shader.glsl';

Transformations

Configuration de Babel

// .babelrc
{
  "presets": [
    ["@babel/preset-env", {
      "targets": {
        "browsers": ["> 1%", "last 2 versions"]
      }
    }],
    "@babel/preset-react"
  ],
  "plugins": [
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-syntax-dynamic-import"
  ]
}

PostCSS Configuration

// .postcssrc
{
  "plugins": {
    "autoprefixer": {
      "grid": true
    },
    "cssnano": {
      "preset": "default"
    }
  }
}

Configuration du script de type

// tsconfig.json
{
  "compilerOptions": {
    "target": "es2018",
    "module": "esnext",
    "lib": ["dom", "dom.iterable", "es6"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": [
    "src"
  ]
}

Réagir à la configuration

// React component
import React from 'react';
import './Component.css';

export default function Component({ title }) {
  return (
    <div className="component">
      <h1>{title}</h1>
    </div>
  );
}

Configuration de la vue

<!-- Vue component -->
<template>
  <div class="component">
    <h1>{{ title }}</h1>
  </div>
</template>

<script>
export default {
  props: ['title']
}
</script>

<style scoped>
.component {
  padding: 20px;
}
</style>

Fractionnement du code

Importations dynamiques

// Dynamic import for code splitting
async function loadModule() {
  const { default: Module } = await import('./heavy-module.js');
  return new Module();
}

// React lazy loading
import React, { Suspense } from 'react';

const LazyComponent = React.lazy(() => import('./LazyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  );
}

Séparation des ensembles

// Vendor bundle splitting
import React from 'react';
import ReactDOM from 'react-dom';
import lodash from 'lodash';

// App code
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));

Séparation par parcours

// React Router with code splitting
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { Suspense, lazy } from 'react';

const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Contact = lazy(() => import('./pages/Contact'));

function App() {
  return (
    <Router>
      <Suspense fallback={<div>Loading...</div>}>
        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/about" component={About} />
          <Route path="/contact" component={Contact} />
        </Switch>
      </Suspense>
    </Router>
  );
}

Remplacement du module à chaud

HMR en JavaScript

// Enable HMR for a module
if (module.hot) {
  module.hot.accept('./component.js', function() {
    // Re-render component
    render();
  });
}

// HMR with state preservation
if (module.hot) {
  module.hot.accept();

  if (module.hot.data) {
    // Restore state
    restoreState(module.hot.data.state);
  }

  module.hot.dispose((data) => {
    // Save state
    data.state = getCurrentState();
  });
}

Réagir

// React Fast Refresh (automatic with Parcel)
import React from 'react';

function App() {
  const [count, setCount] = React.useState(0);

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
}

export default App;

HMR CSS

/* CSS changes are automatically hot reloaded */
.component {
  background: blue; /* Change this and see instant update */
  padding: 20px;
  border-radius: 8px;
}

Variables d'environnement

Utilisation des variables d'environnement

// Access environment variables
const apiUrl = process.env.API_URL || 'http://localhost:3000';
const isDevelopment = process.env.NODE_ENV === 'development';

console.log('API URL:', apiUrl);
console.log('Development mode:', isDevelopment);

.env Fichiers

# .env
API_URL=https://api.example.com
FEATURE_FLAG=true
DEBUG=false
# .env.development
API_URL=http://localhost:3000
DEBUG=true
# .env.production
API_URL=https://api.production.com
DEBUG=false

Environnement Constructions

{
  "scripts": {
    "start": "NODE_ENV=development parcel src/index.html",
    "build": "NODE_ENV=production parcel build src/index.html",
    "build:staging": "NODE_ENV=staging parcel build src/index.html"
  }
}

Greffons

Installation des plugins

# Install common plugins
npm install --save-dev @parcel/transformer-sass
npm install --save-dev @parcel/transformer-less
npm install --save-dev @parcel/transformer-stylus
npm install --save-dev @parcel/transformer-typescript

Configuration du plugin

// .parcelrc
{
  "extends": "@parcel/config-default",
  "transformers": {
    "*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"],
    "*.scss": ["@parcel/transformer-sass"]
  },
  "reporters": ["@parcel/reporter-dev-server", "@parcel/reporter-bundle-analyzer"]
}

Plugins personnalisés

// parcel-plugin-custom.js
const { Transformer } = require('@parcel/plugin');

module.exports = new Transformer({
  async transform({ asset }) {
    const code = await asset.getCode();

    // Transform the code
    const transformedCode = customTransform(code);

    asset.setCode(transformedCode);
    return [asset];
  }
});

Greffons populaires

# Bundle analyzer
npm install --save-dev @parcel/reporter-bundle-analyzer

# Service worker
npm install --save-dev parcel-plugin-sw-precache

# Compression
npm install --save-dev parcel-plugin-compress

# Clean dist
npm install --save-dev parcel-plugin-clean-dist

Configuration

Base .parcelrc

{
  "extends": "@parcel/config-default",
  "transformers": {
    "*.{js,mjs,jsm,jsx,es6,cjs,ts,tsx}": [
      "@parcel/transformer-js",
      "@parcel/transformer-react-refresh-wrap"
    ]
  }
}

Configuration avancée

{
  "extends": "@parcel/config-default",
  "resolvers": ["@parcel/resolver-default"],
  "transformers": {
    "*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"],
    "*.{js,jsx}": ["@parcel/transformer-js"],
    "*.{css,scss,sass}": ["@parcel/transformer-sass", "@parcel/transformer-css"],
    "*.{html,htm}": ["@parcel/transformer-html"]
  },
  "bundler": "@parcel/bundler-default",
  "namers": ["@parcel/namer-default"],
  "runtimes": ["@parcel/runtime-js", "@parcel/runtime-browser-hmr"],
  "optimizers": {
    "*.{js,mjs,jsm,jsx,ts,tsx}": ["@parcel/optimizer-terser"],
    "*.{css,scss,sass}": ["@parcel/optimizer-css"]
  },
  "packagers": {
    "*.html": "@parcel/packager-html",
    "*.{js,mjs,jsm,jsx,ts,tsx}": "@parcel/packager-js",
    "*.{css,scss,sass}": "@parcel/packager-css"
  },
  "compressors": {
    "*": ["@parcel/compressor-gzip"]
  },
  "reporters": ["@parcel/reporter-dev-server"]
}

Configuration de la cible

// package.json
{
  "targets": {
    "default": {
      "distDir": "dist"
    },
    "modern": {
      "engines": {
        "browsers": "Chrome 80"
      }
    },
    "legacy": {
      "engines": {
        "browsers": "> 1%"
      }
    }
  }
}

Optimisation

Optimisations de la production

# Build with optimizations
parcel build src/index.html

# Disable optimizations
parcel build src/index.html --no-optimize

# Enable experimental optimizations
parcel build src/index.html --experimental-scope-hoisting

Optimisation de la taille des ensembles

// Tree shaking (automatic with ES modules)
import { debounce } from 'lodash-es';

// Avoid importing entire libraries
import debounce from 'lodash/debounce';

// Use dynamic imports for large dependencies
async function loadChart() {
  const { Chart } = await import('chart.js');
  return Chart;
}

Optimisation de l'image

<!-- Automatic image optimization -->
<img src="./image.jpg" alt="Optimized image" />

<!-- WebP support -->
<picture>
  <source srcset="./image.webp" type="image/webp">
  <img src="./image.jpg" alt="Image with WebP fallback">
</picture>

CSS Optimisation

/* Automatic CSS optimization */
.component {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

/* PostCSS plugins handle vendor prefixes */
.modern-feature {
  backdrop-filter: blur(10px);
}

Objectifs

Objectifs du navigateur

// package.json
{
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead"
  ]
}

Node.js Cible

{
  "targets": {
    "node": {
      "context": "node",
      "engines": {
        "node": ">= 12"
      }
    }
  }
}

Objectifs de la bibliothèque

{
  "targets": {
    "library": {
      "source": "src/index.js",
      "distDir": "lib"
    }
  }
}

Cibles multiples

{
  "targets": {
    "modern": {
      "engines": {
        "browsers": "Chrome 80"
      },
      "distDir": "dist/modern"
    },
    "legacy": {
      "engines": {
        "browsers": "> 1%"
      },
      "distDir": "dist/legacy"
    }
  }
}

Cache

Construire le cache

# Cache location
.parcel-cache/

# Clear cache
rm -rf .parcel-cache

# Disable cache
parcel build src/index.html --no-cache

Cachement HTTP

// Automatic content hashing for long-term caching
// Output: main.a1b2c3d4.js

// Service worker for caching
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js');
}

Configuration de cache

{
  "cacheDir": ".cache",
  "cache": true
}

Cartes sources

Configuration de la carte source

# Enable source maps (default in development)
parcel src/index.html

# Disable source maps
parcel src/index.html --no-source-maps

# Production source maps
parcel build src/index.html --source-maps

Types de cartes sources

// .parcelrc
{
  "extends": "@parcel/config-default",
  "optimizers": {
    "*.js": {
      "sourceMap": {
        "inline": false,
        "inlineSources": true
      }
    }
  }
}

Analyse de l'ensemble

Analyseur de groupe

# Install bundle analyzer
npm install --save-dev @parcel/reporter-bundle-analyzer

# Build with analyzer
parcel build src/index.html --reporter @parcel/reporter-bundle-analyzer

Groupe Buddy

# Install bundle buddy
npm install --save-dev @parcel/reporter-bundle-buddy

# Generate bundle buddy report
parcel build src/index.html --reporter @parcel/reporter-bundle-buddy

Analyse personnalisée

// analyze-bundle.js
const fs = require('fs');
const path = require('path');

function analyzeBundles() {
  const distDir = path.join(__dirname, 'dist');
  const files = fs.readdirSync(distDir);

  files.forEach(file => {
    const filePath = path.join(distDir, file);
    const stats = fs.statSync(filePath);
    console.log(`${file}: ${(stats.size / 1024).toFixed(2)} KB`);
  });
}

analyzeBundles();

Migrations

À partir de Webpack

// webpack.config.js (remove this file)
// Parcel handles most webpack functionality automatically

// Update package.json scripts
{
  "scripts": {
    "start": "parcel src/index.html",
    "build": "parcel build src/index.html"
  }
}

De l'application Create React

# Remove react-scripts
npm uninstall react-scripts

# Install Parcel
npm install --save-dev parcel

# Update scripts
{
  "scripts": {
    "start": "parcel public/index.html",
    "build": "parcel build public/index.html"
  }
}

De Rollup

// Remove rollup.config.js
// Update package.json

{
  "source": "src/index.js",
  "main": "dist/index.js",
  "scripts": {
    "build": "parcel build"
  }
}

Liste de contrôle des migrations

  • Supprime les anciens fichiers de configuration des paquets
  • Mettre à jour les scripts paquet.json
  • Installer la parcelle et enlever l'ancien paqueteur
  • Mettre à jour les chemins d'importation si nécessaire
  • Configurer .parcelrc si des transformations personnalisées sont nécessaires
  • Développement d'essais et constructions de production
  • Mettre à jour les pipelines CI/CD

Meilleures pratiques

Organisation du projet

# Recommended structure
src/
├── index.html          # Entry point
├── index.js           # Main JavaScript
├── styles/            # Global styles
├── components/        # Reusable components
├── pages/            # Page components
├── utils/            # Utility functions
├── assets/           # Static assets
└── types/            # TypeScript types

Meilleures pratiques en matière de rendement

  • Utiliser les importations dynamiques pour le fractionnement des codes
  • Optimiser les images avec les formats appropriés
  • ** Minimiser la taille du faisceau** avec tremblement d'arbre
  • Contraction automatique pour les constructions de production
  • Utilisez le hachage du contenu pour le cache

Meilleures pratiques de développement

  • Utiliser HMR pour un développement plus rapide
  • Activer les cartes sources pour le débogage
  • Configurer le lintage avec ESLint et Prettier
  • Utilisez TypeScript pour une meilleure expérience de développement
  • Set up testing avec Jest ou d'autres cadres

Meilleures pratiques de production

  • Optimiser pour les navigateurs cibles avec la liste des navigateurs
  • Activer toutes les optimisations dans les constructions de production
  • Utiliser les variables d'environnement pour la configuration
  • ** Taille du faisceau de moniteur** avec outils d'analyse
  • Construits d'essai avant déploiement

Résumé

Parcel est un outil de construction de configuration zéro qui fournit une excellente expérience de développeur avec une configuration minimale. Les principales caractéristiques sont les suivantes :

  • ** Configuration de Zero**: Fonctionne hors de la boîte pour la plupart des projets
  • Fast Builds: Utilise le traitement et la mise en cache multicore
  • Remplacement du module hôte: mises à jour instantanées pendant le développement
  • Manipulation de l'ensemble: Prend en charge tous les types de fichiers courants
  • Doublure du code: fractionnement automatique et manuel du code
  • Tree Shaking: supprime automatiquement le code inutilisé
  • Cartes d'origine: prise en charge de la carte d'origine intégrée
  • Optimisation: Optimisations automatiques de la production

En tirant parti de la simplicité et de la puissance de Parcel, vous pouvez vous concentrer sur la construction de votre application plutôt que sur la configuration des outils de construction, tout en ayant la flexibilité de personnaliser au besoin.