如何使用Webpack复制静态文件以建立目录?


330

我正尝试从Gulp移至Webpack。在Gulp我的任务中,将所有文件和文件夹从/ static /文件夹复制到/ build /文件夹。怎么做Webpack呢?我需要一些插件吗?


2
Gulp很好理解。只需从gulpfile.js调用webpack
Baryon Lee

如果您使用Laravel混合,laravel.com/docs/5.8/mix#copying-files-and-directories可用。
瑞安

Answers:


179

您无需复制任何内容,Webpack的工作原理与gulp不同。Webpack是一个模块捆绑程序,您在文件中引用的所有内容都将包括在内。您只需要为此指定一个加载程序。

因此,如果您写:

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

Webpack首先将尝试将引用的文件解析为JavaScript(因为这是默认设置)。当然,那将失败。这就是为什么您需要为该文件类型指定加载程序。该文件 -或者URL装载机例如采用引用的文件,把它变成的WebPack的输出文件夹(这应该是build你的情况),并返回混编网址该文件。

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

通常,加载器是通过webpack配置应用的:

// webpack.config.js

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

当然,您需要先安装文件加载器以使此工作。


42
当然,您需要首先安装文件加载器才能进行此工作。在此处链接到上述“文件加载器” 。而这里是如何安装和使用它。
Nate

21
您仍然有HTML文件的问题,并且其中的所有引用均未加载。
kilianc 2015年

125
是的,如果您想了解Webpack插件,可以使用文件加载器,css加载器,样式加载器,URL加载器...,然后可以花很多时间按照需要配置它和谷歌搜索和不睡觉:),或者您可以使用copy-webpack-plugin来完成工作...
KamilTomšík16年

11
@KamilTomšík因此,您的建议是我们应该使用webpack插件来避免使用webpack插件?(开个玩笑。我明白你的意思。)
Konrad Viltersten

12
好的,所有图像的大部分都在css和html中。所以我应该使用require('img.png')在我的JS文件中要求所有这些图像吗?使它与该文件加载器一起工作?那真是太疯狂了。
Rantiev

579

使用文件加载器模块来请求资产是打算使用webpack的方式(来源)。但是,如果您需要更大的灵活性或想要一个更简洁的界面,也可以使用my copy-webpack-pluginnpmGithub)直接复制静态文件。为了您staticbuild例如:

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

module.exports = {
    context: path.join(__dirname, 'your-app'),
    plugins: [
        new CopyWebpackPlugin([
            { from: 'static' }
        ])
    ]
};

11
当您要复制整个目录(即静态html和其他样板图像)时,这要简单得多!
Arjun Mehta

6
做了把戏,谢谢:)在几次失败的尝试之后,放弃了文件加载器,使它执行一个非常简单的命令。您的插件首次使用。
arcseldon

3
@Yan插件会在文件更改时重新复制文件(dev-server或webpack --watch)。如果不是为您复制,请提出问题。
2013年

2
我是webpack的新手,但是我很难理解为什么我们需要使用file-loader / url-loader / img-loader ...而不是仅仅复制它们?使用文件加载器进行此操作有什么好处?
BreakDS

2
由于您是插件作者。没有更好的步伐来问这个问题。使用“ copy-webpack-plugin”插件...我可以从源目录中过滤文件,以便仅复制具有特定文件扩展名的文件。仅复制“ .html”?问候
DevWL

56

如果要复制静态文件,则可以通过以下方式使用文件加载器:

对于html文件:

在webpack.config.js中:

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

在您的js文件中:

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

./static/相对于您的js文件所在的位置。

您可以对图像或其他内容进行相同的操作。上下文是探索的强大方法!


3
与copy-webpack-plugin模块相比,我更喜欢这种方法。另外,我无需在Webpack配置中使用“&context =。/ app / static”就可以使其正常运行。我只需要require.context行。
戴夫·兰德里

2
我正在尝试,这看起来很棒,但是我遇到一个小问题,那就是它将我index.html放入要创建的子目录_(下划线),这是怎么回事?
克里斯,2016年

2
当您说“在您的js文件中”是什么意思?如果没有JS文件怎么办?
evolutionxbox

绝对。输入脚本中的这一行,即main.js正在导入static文件夹中的所有内容:require.context("./static/", true, /^.*/);
Mario

2
这是一个很好的技巧,但是如果您复制了过多的文件,则会耗尽内存。
汤姆(Tom)

18

前面没有提到的copy-webpack-plugin带来的一个好处是,这里提到的所有其他方法仍然将资源捆绑到捆绑文件中(并要求您“需要”或“将其导入”某处)。如果我只想移动一些图像或某些模板部分,我不想在无用的引用中弄乱我的javascript捆绑文件,而只想在正确的位置发出这些文件。我在webpack中还没有找到其他方法可以做到这一点。诚然,这并不是webpack最初设计的目的,但绝对是当前的用例。(@BreakDS,我希望这能回答您的问题-如果您愿意,这只是一种好处)


7

以上建议是好的。但是,要尝试直接回答您的问题,我建议cpy-cli您在中定义的脚本中使用package.json

此示例期望node在您的路径上。安装cpy-cli为开发依赖项:

npm install --save-dev cpy-cli

然后创建几个nodejs文件。一个用于复制,另一个用于显示选中标记和消息。

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;

在中添加脚本package.json。假设脚本在<project-root>/scripts/

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

要运行摘要:

npm run copy


3
OP是否想完成文件在webpack中的移动,而不是使用npm脚本?
威廉·S

即使OP想要在webpack中解决此问题,他也有可能通过npm运行webpack,因此他可以将其添加到运行webpack的构建脚本中
Piro说,Reinstate Monica

5

最有可能您应该使用kevlened答案中提到的CopyWebpackPlugin。另外,对于.html.json之类的文件,您也可以使用raw-loader或json-loader。通过进行安装npm install -D raw-loader,然后只需要向我们的webpack.config.js文件中添加另一个加载器即可。

喜欢:

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

注意:重新启动webpack-dev-server,使所有配置更改生效。

现在,您可以要求使用相对路径的html文件,这使移动文件夹变得更加容易。

template: require('./nav.html')  

5

我加载静态images和的方式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/'
          },
        }
    ]
}

别忘了安装file-loader才能正常工作。


您如何处理重复的文件名?或者更好的是,您知道将原始路径保留在新的输出目录中的任何方法吗?
cantuket

您的项目中不应有重复的文件名,且文件名应具有相同的扩展名。如果内容相同,保留重复项又有什么意义呢?如果不是,则根据其内容使用不同的名称。但是,如果您想将内容保留在原始路径中,为什么还要使用webpack?如果只需要JS翻译,那么Babel应该足够了。
RegarBoy

1
如果您要实现基于组件的开发(其主要原理之一是封装,并且在这种情况下更具体地讲是信息隐藏,那么您提到的内容都不相关。例如,当有人向程序中添加新组件时,他们不必检查是否存在另一个名为的图像,logo.png也不必创建一个钝的和“希望的”唯一文件名来避免全局冲突。同样的原因,我们使用CSS模块
cantuket

1
至于为什么我要图像保持原始路径和文件名;主要是进行调试,与您使用sourcemaps相同的原因,但也包括SEO。无论如何,我的问题的答案实际上非常简单... [path][name].[ext]并且提供了足够的灵活性来针对特定环境或用例进行修改... 文件加载器
cantuket

1
话虽这么说,我们确实实现了您的示例的变体,所以谢谢您提供!
cantuket

3

您可以在package.json中编写bash:

# package.json
{
  "name": ...,
  "version": ...,
  "scripts": {
    "build": "NODE_ENV=production npm run webpack && cp -v <this> <that> && echo ok",
    ...
  }
}

1
在Windows中,只需使用xcopy而不是cp:"build": "webpack && xcopy images dist\\images\\ /S /Y && xcopy css dist\\css\\ /S /Y"
SebaGra

7
是的,因此您的解决方案是为每个操作系统使用不同的脚本?
Maciej Gurban '17

是的,对我来说,每个操作系统的脚本都是可以接受的(实际上是unix / non-unix,因为Linux上的脚本将在Darwin或其他POSIX * nix上运行)
Victor Pudeyev

该Windows示例也无法将PowerShell作为默认外壳使用。
朱利安·奈特

与CopyWebpackPlugin不同,此选项保留文件日期。对于开放源代码,操作系统问题可能会出现问题,但对于较小的团队,可以使用Windows bash或使用cp.bat包装xcopy轻松管理。
外星科技

2

我也被困在这里。copy-webpack-plugin对我有用。

但是,在我的情况下,“ copy-webpack-plugin”不是必需的(我以后会学习)。

webpack忽略根路径
示例

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

因此,要在不使用“ copy-webpack-plugin”的情况下进行此项工作,请在路径中使用“〜”

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

“〜”告诉webpack将“图像”视为模块

注意:您可能必须在以下位置添加图片目录的父目录

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

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


2

webpack配置文件(在webpack 2中)允许您导出承诺链,只要最后一步返回webpack配置对象即可。请参阅Promise配置文档。从那里:

webpack现在支持从配置文件返回Promise。这允许在配置文件中进行异步处理。

您可以创建一个简单的递归复制功能来复制文件,并且仅在此之后触发webpack。例如:

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

1

假设您所有的静态资产都位于根目录下的“静态”文件夹中,并且您想将其复制到维护子文件夹结构的build文件夹中,然后将其复制到条目文件中)

//index.js or index.jsx

require.context("!!file?name=[path][name].[ext]&context=./static!../static/", true, /^\.\/.*\.*/);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.