¿Cómo copiar archivos estáticos para crear un directorio con Webpack?

331

Estoy tratando de pasar de Gulpa Webpack. En GulpTengo una tarea que copia todos los archivos y carpetas de / static / folder a / build / folder. ¿Cómo hacer lo mismo con Webpack? ¿Necesito algún complemento?

Vitalii Korsakov
fuente
2
Gulp es genial de entender. simplemente llame a webpack desde gulpfile.js si lo desea
Baryon Lee
Si está utilizando Laravel Mix, laravel.com/docs/5.8/mix#copying-files-and-directories está disponible.
Ryan

Respuestas:

179

No necesita copiar cosas, el paquete web funciona de manera diferente que el trago. Webpack es un paquete de módulos y se incluirá todo lo que haga referencia en sus archivos. Solo necesita especificar un cargador para eso.

Entonces si escribes:

var myImage = require("./static/myImage.jpg");

Webpack primero intentará analizar el archivo referenciado como JavaScript (porque ese es el valor predeterminado). Por supuesto, eso fallará. Es por eso que necesita especificar un cargador para ese tipo de archivo. El archivo , o el cargador de url, por ejemplo, toma el archivo al que se hace referencia, lo coloca en la carpeta de salida del paquete web (que debería estar builden su caso) y devuelve la url hash para ese archivo.

var myImage = require("./static/myImage.jpg");
console.log(myImage); // '/build/12as7f9asfasgasg.jpg'

Por lo general, los cargadores se aplican a través de la configuración del paquete web:

// webpack.config.js

module.exports = {
    ...
    module: {
        loaders: [
            { test: /\.(jpe?g|gif|png|svg|woff|ttf|wav|mp3)$/, loader: "file" }
        ]
    }
};

Por supuesto, primero debe instalar el cargador de archivos para que esto funcione.

Johannes Ewald
fuente
42
" Por supuesto, primero debe instalar el cargador de archivos para que esto funcione " . Enlace al mencionado "cargador de archivos" aquí . Y aquí está cómo instalarlo y usarlo.
Nate
21
Aún tiene el problema de que los archivos HTML y todas las referencias en ellos no se cargan.
kilianc
126
sí, si quieres meterte en el infierno de los complementos de paquete web, puedes usar el cargador de archivos, el cargador de CSS, el cargador de estilos, el cargador de URL, ... y luego puedes pasar un buen rato configurándolo de la manera que lo necesites y googlear y no dormir :) o puede usar copy-webpack-plugin y hacer su trabajo ...
Kamil Tomšík
11
@ KamilTomšík ¿Entonces su recomendación es que usemos un complemento de paquete web para evitar los complementos de paquete web? (Es broma.
Entendí
12
Ok, la mayor parte de todas las imágenes están en CSS y HTML. Entonces, ¿debería requerir todas estas imágenes en mis archivos JS usando require ('img.png'); hacer que funcione con ese cargador de archivos? Eso es una locura.
Rantiev
581

Requerir activos utilizando el módulo cargador de archivos es la forma en que se pretende usar el paquete web ( fuente ). Sin embargo, si necesita una mayor flexibilidad o desea una interfaz más limpia, también puede copiar archivos estáticos directamente usando my copy-webpack-plugin( npm , Github ). Para su staticde buildejemplo:

const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
    context: path.join(__dirname, 'your-app'),
    plugins: [
        new CopyWebpackPlugin([
            { from: 'static' }
        ])
    ]
};
kevlened
fuente
11
¡Esto es mucho más simple cuando desea copiar un directorio completo (es decir, html estático y otras imágenes repetitivas)!
Arjun Mehta
66
Hice el truco, gracias :) abandonó el cargador de archivos después de varios intentos fallidos de hacer que hiciera un comando muy simple. Tu plugin funcionó por primera vez.
arcseldon
3
@Yan El complemento vuelve a copiar archivos si cambian (dev-server o webpack --watch). Si no está copiando para usted, presente un problema.
kevlened
2
Soy nuevo en webpack, pero me cuesta entender por qué necesitamos usar file-loader / url-loader / img-loader ... en lugar de simplemente copiarlos. ¿Cuál es el beneficio que obtenemos al hacer esto con, por ejemplo, el cargador de archivos?
BreakDS
2
Como eres el autor del complemento. No hay mejor ritmo para hacer esta pregunta. Utilizando el complemento "copy-webpack-plugin" ... ¿puedo filtrar los archivos del directorio de origen para que solo copie el archivo con cierta extensión de archivo? copiar solo ".html"? Saludos
DevWL
56

Si desea copiar sus archivos estáticos, puede usar el cargador de archivos de esta manera:

para archivos html:

en webpack.config.js:

module.exports = {
    ...
    module: {
        loaders: [
            { test: /\.(html)$/,
              loader: "file?name=[path][name].[ext]&context=./app/static"
            }
        ]
    }
};

en su archivo js:

  require.context("./static/", true, /^\.\/.*\.html/);

./static/ es relativo a donde está su archivo js.

Puedes hacer lo mismo con imágenes o lo que sea. ¡El contexto es un método poderoso para explorar!

Moussa Dembélé
fuente
3
Prefiero este método sobre el módulo copy-webpack-plugin. Además, pude hacerlo funcionar sin usar "& context =. / App / static" en la configuración de mi paquete web. Solo necesitaba la línea require.context.
Dave Landry
2
Estoy intentando esto, parece genial, pero por un pequeño problema que estoy teniendo, es que me está poniendo index.htmlen un subdirectorio que está creando llamado _(subrayado), ¿qué está pasando?
kris
2
Cuando dices "en tu archivo js", ¿qué quieres decir? ¿Qué pasa si no tengo un archivo JS?
evolutionxbox
absolutamente. Esta línea en el script de entrada, main.jses decir, está importando todo dentro de la staticcarpeta:require.context("./static/", true, /^.*/);
Mario
2
Este es un buen truco, pero si está copiando demasiados archivos, se quedará sin memoria.
Tom
18

Una ventaja que trae el plugin copy-webpack-plugin mencionado anteriormente que no se ha explicado antes es que todos los otros métodos mencionados aquí aún agrupan los recursos en sus archivos de paquete (y requieren que los "requiera" o "importe" en alguna parte). Si solo quiero mover algunas imágenes o algunos parciales de plantilla, no quiero saturar mi paquete de javascript con referencias inútiles, solo quiero que los archivos se emitan en el lugar correcto. No he encontrado ninguna otra forma de hacer esto en webpack. Es cierto que no es para lo que se diseñó originalmente el paquete web, pero definitivamente es un caso de uso actual. (@BreakDS Espero que esto responda a su pregunta, solo es un beneficio si lo desea)

steev
fuente
7

Las sugerencias anteriores son buenas. Pero para tratar de responder a su pregunta directamente, le sugiero que use cpy-clien un script definido en su package.json.

Este ejemplo espera nodeen algún lugar de tu camino. Instalar cpy-clicomo una dependencia de desarrollo:

npm install --save-dev cpy-cli

Luego cree un par de archivos nodejs. Uno para hacer la copia y el otro para mostrar una marca de verificación y un mensaje.

copy.js

#!/usr/bin/env node

var shelljs = require('shelljs');
var addCheckMark = require('./helpers/checkmark');
var path = require('path');

var cpy = path.join(__dirname, '../node_modules/cpy-cli/cli.js');

shelljs.exec(cpy + ' /static/* /build/', addCheckMark.bind(null, callback));

function callback() {
  process.stdout.write(' Copied /static/* to the /build/ directory\n\n');
}

checkmark.js

var chalk = require('chalk');

/**
 * Adds mark check symbol
 */
function addCheckMark(callback) {
  process.stdout.write(chalk.green(' ✓'));
  callback();
}

module.exports = addCheckMark;

Agregue el script en package.json. Suponiendo que los scripts están en<project-root>/scripts/

...
"scripts": {
  "copy": "node scripts/copy.js",
...

Para ejecutar el sript:

npm run copy

RnR
fuente
3
OP quería lograr que el archivo se moviera dentro del paquete web, ¿no usa scripts npm?
William S
Incluso cuando OP quería resolver esto dentro de webpack, es posible que esté ejecutando webpack a través de npm, por lo que podría agregarlo a su script de compilación donde se ejecuta webpack
Piro dice Reinstate Monica
5

Lo más probable es que deba usar CopyWebpackPlugin, que se mencionó en la respuesta kevlened. Alternativamente, para algún tipo de archivo como .html o .json , también puede usar raw-loader o json-loader. Instálelo a través de npm install -D raw-loadery luego lo único que debe hacer es agregar otro cargador a nuestro webpack.config.jsarchivo.

Me gusta:

{
    test: /\.html/,
    loader: 'raw'
}

Nota: Reinicie el servidor webpack-dev-server para que los cambios de configuración surtan efecto.

Y ahora puede requerir archivos html usando rutas relativas, esto hace que sea mucho más fácil mover carpetas.

template: require('./nav.html')  
Andurit
fuente
5

La forma en que cargo estática imagesy fonts:

module: {
    rules: [
      ....

      {
        test: /\.(jpe?g|png|gif|svg)$/i,
        /* Exclude fonts while working with images, e.g. .svg can be both image or font. */
        exclude: path.resolve(__dirname, '../src/assets/fonts'),
        use: [{
          loader: 'file-loader',
          options: {
            name: '[name].[ext]',
            outputPath: 'images/'
          }
        }]
      },
      {
        test: /\.(woff(2)?|ttf|eot|svg|otf)(\?v=\d+\.\d+\.\d+)?$/,
        /* Exclude images while working with fonts, e.g. .svg can be both image or font. */
        exclude: path.resolve(__dirname, '../src/assets/images'),
        use: [{
          loader: 'file-loader',
          options: {
            name: '[name].[ext]',
            outputPath: 'fonts/'
          },
        }
    ]
}

No olvides instalar file-loaderpara que funcione.

RegarBoy
fuente
¿Cómo manejas los nombres de archivo duplicados? O mejor aún, ¿conoce alguna forma de preservar la ruta original en el nuevo directorio de salida?
cantuket
No debe tener un nombre de archivo duplicado con el mismo nombre de extensión en su proyecto. ¿Qué sentido tiene mantener duplicados si su contenido es idéntico? De lo contrario, asígneles un nombre diferente según su contenido. ¿Pero por qué usarías webpack si quieres mantener tus cosas en el camino original? Si solo quieres la traducción JS, entonces Babel debería ser suficiente.
RegarBoy
1
Si está implementando un desarrollo basado en componentes (uno de los principios principales de los cuales es la encapsulación y, más específicamente, en este caso, la ocultación de información ) , entonces nada de lo que mencionó es pertinente. es decir, cuando alguien agrega un nuevo componente al programa, no debería tener que verificar si hay otra imagen nombrada logo.pngni tener que crear un nombre de archivo obtuso y "con suerte" único para evitar una colisión global. La misma razón por la que usamos módulos CSS .
cantuket
1
En cuanto a por qué quiero que las imágenes mantengan la ruta original y el nombre del archivo; depuración principalmente, la misma razón por la que usarías mapas de origen, pero también SEO . De todos modos, la respuesta a mi pregunta fue realmente muy simple ... [path][name].[ext]y hay mucha flexibilidad para modificar esto para un entorno específico o caso de uso ... cargador de archivos
cantuket
1
Dicho esto, implementamos una variación de su ejemplo, ¡así que gracias por proporcionarnos!
Cantuket
3

Puedes escribir bash en tu package.json:

# package.json
{
  "name": ...,
  "version": ...,
  "scripts": {
    "build": "NODE_ENV=production npm run webpack && cp -v <this> <that> && echo ok",
    ...
  }
}
Victor Pudeyev
fuente
1
En Windows, solo use xcopy en lugar de cp:"build": "webpack && xcopy images dist\\images\\ /S /Y && xcopy css dist\\css\\ /S /Y"
SebaGra
77
Bien, ¿entonces su solución es tener un script diferente para cada sistema operativo?
Maciej Gurban
Sí, para mí un script para cada sistema operativo es aceptable (es realmente unix / non-unix, ya que un script en Linux se ejecutará en Darwin u otro POSIX * nix)
Victor Pudeyev
Y ese ejemplo de Windows tampoco funcionará con PowerShell como shell predeterminado.
Julian Knight el
A diferencia de CopyWebpackPlugin, esta opción mantiene las fechas de los archivos. El problema del sistema operativo puede ser problemático para el código abierto, pero para equipos más pequeños se maneja fácilmente con Windows bash o envolviendo xcopy con cp.bat.
Alien Technology
2

Estaba atrapado aquí también. copy-webpack-plugin funcionó para mí.

Sin embargo, 'copy-webpack-plugin' no era necesario en mi caso (lo supe más tarde).

webpack ignora el
ejemplo de rutas raíz

<img src="/images/logo.png'>

Por lo tanto, para que esto funcione sin usar 'copy-webpack-plugin' use '~' en las rutas

<img src="~images/logo.png'>

'~' le dice a webpack que considere las 'imágenes' como un módulo

nota: es posible que deba agregar el directorio principal del directorio de imágenes en

resolve: {
    modules: [
        'parent-directory of images',
        'node_modules'
    ]
}

Visite https://vuejs-templates.github.io/webpack/static.html

techNik
fuente
2

El archivo de configuración de webpack (en webpack 2) le permite exportar una cadena de promesa, siempre que el último paso devuelva un objeto de configuración de webpack. Consulte los documentos de configuración de promesa . Desde allí:

webpack ahora admite devolver una Promesa desde el archivo de configuración. Esto permite realizar un procesamiento asíncrono en su archivo de configuración.

Puede crear una función de copia recursiva simple que copie su archivo, y solo después de eso se activa el paquete web. P.ej:

module.exports = function(){
    return copyTheFiles( inpath, outpath).then( result => {
        return { entry: "..." } // Etc etc
    } )
}
Mentor
fuente
1

supongamos que todos sus activos estáticos están en una carpeta "estática" en el nivel raíz y desea copiarlos en la carpeta de compilación manteniendo la estructura de la subcarpeta, luego en su archivo de entrada) simplemente coloque

//index.js or index.jsx

require.context("!!file?name=[path][name].[ext]&context=./static!../static/", true, /^\.\/.*\.*/);
abhisekpaul
fuente