webpack配置整理


转载自 皮皮坤666  webpack配置整理

开始准备

 npm init -y    #简单初始化一个项目
    npm install webpack webpack-cli --save-dev  #安装webpack相关开发依赖

    npx webpack --config webpack.config.js #可以将脚本作为入口起点,然后输出为main.js,如果webpack.config.js存在,将默认使用它,这里使用--config是为了表明可以传递任何名称的配置文件。

    #上面的方式是通过cli运行本地webpack,为了方便我们可以在package.json添加一个npm 脚本 :
    "scripts":{
        "build":"webpack"
    }
    #这时我们就可以使用npm run build命令来替代我们之前使用的npx命令。

为了从JavaScript模块中import一个CSS文件,需要安装并添加style-loader和css-loader并在webpack.config.js文件的中进行配置,并引入规则。同样的道理,加载图片也需要使用图片加载器file-loader。

//npm install --save-dev style-loader css-loader file-loader
    
    module.exports = {
        entry: './src/index.js',
        output:{
            filename:'bundle.js',
            path:path.resolve(__dirname,'dist')
        },
        module:{
            rules:[
                {
                    test:/\.css$/,
                    use:[
                        'style-loader',
                        'css-loader'
                    ]
                },{
                    test:/\.(png|svg|jpg|gif)$/,
                    use:[
                        'file-loader'
                    ]
                }
            ]
        }
    }

    //webpack根据正则表达式,来确定应该查找哪些文件,并将其提供给指定的loader。

入口分离

在entry添加新的入口起点,然后修改output,则会根据入口生成多个js文件。

 const path = require('path');
    module.exports = {
        entry:{
            app:'./src/index.js',
            print:'./src/print.js'
        },
        output:{
            filename:'[name].bundle.js',
            path:path.resolve(__dirname,'dist')
        }
    }

自动创建并更新html文件

HtmlWebpackPlugin简化html文件的创建。自动匹配不断变化的文件,省去了手动维护。

 let HtmlWebpackPlugin = require('html-webpack-plugin');
    const path = require('path');

    module.exports = {
        entry:'index.js',
        output:{
            path:path.resolve(__dirname,'./dist'),
            filename:'[name].js'
        },
        plugins:[
            new HtmlWebpackPlugin()
        ]
    }

HtmlWebpackPlugin配置

nametypedefaultdescription
title string Webpack App 用于生成html文档的标题
filename string index.html 要写入html的文件,默认为'index.html',在这里可以指定一个目录(eg:assets/admin.html)
template string `` webpack需要模板的路径
templateParameters Boolean/Object/Function `` 允许重写template中引用的参数
templateContent string/function `` 一个String/Function包含template内容的字符串。template和templateContent不可同时使用
inject Boolean/String true true/head/body/false将所有的assets注入到给定的template或templateContent。当传入true/body时,所有JavaScript资源将放置在body的底部。传入head将把脚本放在head中
favicon String `` 向html输出facicon路径
meta Object {} 允许注入meta标签
minify Boolean/Object false 将html-minifiler的配置作为对象来缩小输出
hash Boolean false 如果为true,则将唯一的webpack编译hash追加到所有包含js和css文件中。这对于缓存崩溃是有用的
cache Boolean true 仅在文件被更改时才发出该文件
showErrors Boolean true 错误详情将被写到html页面
chunks {?} ? 允许只添加一些chunks
chunksSortMode String/Function auto 允许控制chunks被包含到html之前应该如何排序。允许值:none/auto/dependency/manual/function
excludeChunks Array `` 允许跳过一些chunks
xhtml Boolean false 如果为true设置link标签为自动关闭

清理dist文件夹,clean-webpack-plugin

由于过去的代码会遗留在dist中,导致dist目录非常混乱,webpack无法追踪到哪些文件是实际在项目中用到的。因此推荐在每次构建前清理dist文件夹,如此便只会生成用到的文件,下面的插件可以实现这个功能:

npm i clean-webpack-plugin --save-dev

const CleanWebpackPlugin = require('clean-webpack-plugin');
    //webpack config
    {
        plugins:[
            new CleanWebpackPlugin(paths[,{options}])
        ]
    }
        
    //options and defaults(Options),下面是该插件的一些配置选项,不需要再项目中都配置。
    {
        //wepack根文件夹的绝对路径,默认:package的根目录
        root:__dirname,
        //向console写日志
        werbose:true,
        //使用'true'来测试或仿真删除(将不会移除文件)
        //默认值:'false',移除文件
        dry:false,
        //如果为'true',则删除文件重新编译
        watch:false,
        // Instead of removing whole path recursively,
        // remove all path's content with exclusion of provided immediate children.
        // Good for not removing shared files from build directories.
        exclude: [ 'files', 'to', 'ignore' ],
        // 允许插件清理webpack目录以外的文件夹
        allowExternal:false,
        //在文件被发送的output文件夹之前执行清理
        beforeEmit:false
    }

使用source map

当webpack打包源代码时,可能会很难追踪到错误和警告在源代码中的原始位置。例如,如果将三个源文件(a.js,b.js和c.js)打包到一个bundle中,而其中一个源文件包含一个错误,那么堆栈跟踪就会简单地指向到bundle.js。这并没有太多帮助,因为你可能需要准确地知道错误来自于哪个源文件。

为了更容易地追踪错误和警告,JavaScript提供了source map功能,将编译后的代码映射回原始源代码。

source map有很多不同的选项可用,如下:

devtool构建速度重新构建速度生产环境品质
(none) +++ +++ yes 打包后的代码
eval +++ +++ no 生成后的代码
eval-source-map -- + no 原始源代码
cheap-eval-source-map + ++ no 转换过的代码(仅限行)
cheap-module-eval-source-map o ++ no 原始源代码(仅限行)
source-map -- -- yes 原始源代码
inline-source-map -- -- no 原始源代码
hidden-source-map -- -- yes 原始源代码
nosources-source-map -- -- yes 无源代码内容
cheap-source-map + o no 转换过的代码(仅限行)
cheap-module-source-map o - no 原始源代码(仅限行)
inline-cheap-source-map + o no 转换过的代码(仅限行)
inline-cheap-module-source-map o - no 原始源代码(仅限行)

注: +++ 非常快,++ 快速,+ 比较快,o 中等,- 比较慢,--

其中一些值适用于开发环境,一些适用于生产环境。对于开发环境,通常希望更快的source map,需要添加到bundle中以增加体积为代价,但是对于生产环境,则希望更精确的source map,需要从bundle中分离并独立存在。

品质说明

打包后的代码-将所有生成的代码视为一大块代码,你看不到相互分离的模块。
生成后的代码 - 每个模块相互分离,并用模块名称进行注释。可以看到webpack生成的代码。示例:你会看到类似 var module_WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(42);module__WEBPACK_IMPORTED_MODULE_1__.a(); 而不是 import {test} from "module"; test();
转换过的代码 - 每个模块相互分离,并用模块名称进行注释。可以看到webpack转换前、loader转译后的代码。示例:会看到类似import {test} from "module"; var A = function(_test) { ... }(test); 而不是 import {test} from "module"; class A extends test {}
原始源代码 - 每个模块相互分离,并用模块名称进行注释。可以看到转译之前的代码,正如编写它时。这取决于loader支持。
无源代码内容 - source map中不包含源代码内容。浏览器通常会尝试从web服务器或文件系统加载源代码。必须保证正确设置output.devtoolModuleFilenameTemplate,以匹配源代码的url。
(仅限行) - source map 被简化为每行执行一个映射。这通常意味着每个语句只有一个映射。这会妨碍你在语句级别上调试执行,也会妨碍你在每行的一些列上设置断点。与压缩后的代码组合后,映射关系是不可能实现的,因为压缩工具通常只会输出一行。

对于开发环境,以下选项非常适合开发环境:

eval - 每个模块都是用eval()执行,并且都有//@ sourceURL 。此选项会非常快地构建。主要缺点是:由于会映射到转换后的代码,而不是映射到原始代码(没有从loader中获取source map),所以不能正确的显示行数。
eval-source-map - 每个模块使用eval()执行,并且source map转换为DataUrl后添加到eval()中。初始化source map时比较慢,但是会在重新构建时提供比较快的速度,并且生成实际的文件。行数能够正确映射,因为会映射到原始代码中。它会生成用于开发环境的最佳品质的source map。
cheap-eval-source-map - 类似eval-source-map,每个模块使用eval()执行。这是"cheap(低开销)"的source map,因为它没有生成列映射,只是映射行数。它会忽略源自loader的source map,并且仅显示转译后的代码,就像evaldevtool.
cheap-module-eval-source-map - 类似 cheap-eval-source-map ,并且在这种情况下,源自loader的source map会得到更好的处理结果。然而,loader source map会被简化为每行一个映射。

对于生产环境,以下选项通常用在生产环境中:

(none) - 不生成source map。这是一个不错的选择。
source-map - 整个source map 作为一个独立的文件生成。它为bundle添加了一个引用注释,以便开发工具知道到在哪里可以找到它。注:此时应该将服务器设置为不允许普通用户访问srouce map 文件!
hidden-soucr-map - 与source-map相同,但不会为bundle添加引用注释。如果你只想source map映射那些源自错误报告的错误堆栈跟踪信息源自错误报告的堆栈跟踪信息,但不想为浏览器开发工具暴露你的source map,这个选项会很有用。
nosources-srouce-map - 创建的source map不包含sroucesContent(源代码内容)。它可以映射客户端上的堆栈跟踪,而无需暴露所有的源代码。可以将source map 文件部署到web服务器。注:这仍然会暴露反编译后的文件名和结果,但它不会暴露原始代码。

特殊场景

以下选项对于开发环境和生产环境并不理想。他们是一些特定场景下需要的,例如,针对一些第三方工具。

inline-source-map - source map转换为dataUrl后添加到bundle中。
cheap-source-map - 没有列映射的source map,忽略loader source map 。
inline-cheap-source-map - 类似cheap-srouce-map,但是source map转换为DataUrl后添加到bundle中。
cheap-module-source-map - 没有列映射的source map,将loader source map简化为每行一个映射。
inline-cheap-module-source-map - 类似cheap-module-source-map,但是source map转换为DataUrl添加到bundle中。

使用观察者模式

上面的每次修改我们都需要运行npm run build来重新编译项目,观察者模式会自动为我们监听文件的修改,不过不会触发浏览器刷新,还是需要我们手动刷新页面。

    //安装开发依赖 npm install --save-dev webpack-dev-server
    //webpack.config.js,告诉dev server在哪里查找文件
    module.exports = {
        devServer:{
            contentBase:'./dist'
        }
    }
    //package.json
    {
        "start":"webpack-dev-server --open"
    }

上面的配置告诉webpack-dev-server,在localhost:8080下建立服务器,将dist目录下的文件作为可访问文件。此时文件更改不仅受到监听,同时还能触发浏览器的自动刷新。

webpack-dev-server带有许多可配置的选项。

//安装开发依赖:npm install --save-dev express webpack-dev-middleware
    //注:安装过webpack-dev-server的不需要再安装webpack-dev-middleware,而且实际测验中安装webpack-dev-middleware后开发中会报错,语法不够新
    // 配置webpack.config.js
    const path = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const CleanWebpackPlugin = require('clean-webpack-plugin');

    module.exports = {
        mode:'development',
        entry:{
            app:'./src/index.js',
            print:'./src/print.js'
        },
        output:{
            filename:'[name].bundle.js',
            path:path.resolve(__dirname,'dist'),
            publicPath:'/'//publicPath也会在服务器脚本用到,以确保文件资源能够在http://localhost:3000下正确访问。
        },
        devtool:'inline-source-map',
        plugins:[
            new HtmlWebpackPlugin({
                title:'hello world'
            }),
            new CleanWebpackPlugin(['dist'])
        ],
        module:{
            rules:[
                {
                    test:/\.css$/,
                    use:['style-loader','css-loader']
                }
            ]
        }
    }

    // 配置server.js服务器文件,需要自己创建
    const express = require('express');
    const webpack = require('webpack');
    const webpackDevMiddleware = require('webpack-dev-middleware');

    const app = express();
    const config = require('./webpack.config.js');
    const compiler = webpack(config);

    // Tell express to use the webpack-dev-middleware and use the webpack.config.js
    // configuration file as a base.
    app.use(webpackDevMiddleware(compiler, {
      publicPath: config.output.publicPath
    }));

    // Serve the files on port 3000.
    app.listen(3000, function () {
      console.log('Example app listening on port 3000!\n');
    });
    //添加一个npm script,package.json
    {
        "scripts":{
            ...
            "server:express":"node server.js"
        }
    }
    //运行npm run server:express 便一个在浏览器中通过访问localhost:3000访问到该服务器

热模块替换(Hot Module Replacement 或 HRM)

HRM是webpack提供的最有用的功能之一。它允许运行时更新各种模块,而无需进行完全刷新。

//webpack.config.js
    const path = require('path');
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const CleanWebpackPlugin = require('clean-webpack-plugin');
    const webpack = require('webpack');

    module.exports = {
        mode:'development',
        entry:{
            app:'./src/index.js',
        },
        output:{
            filename:'[name].bundle.js',
            path:path.resolve(__dirname,'dist'),
            publicPath:'/'//publicPath也会在服务器脚本用到,以确保文件资源能够在http://localhost:3000下正确访问。
        },
        devtool:'inline-source-map',
        devServer:{
            contentBase:'./dist',
            hot:true
        },
        plugins:[
            new HtmlWebpackPlugin({
                title:'hello world'
            }),
            new CleanWebpackPlugin(['dist']),
            new webpack.NamedModulesPlugin(),
            new webpack.HotModuleReplacementPlugin(),
        ],
        module:{
            rules:[
                {
                    test:/\.css$/,
                    use:['style-loader','css-loader']
                }
            ]
        }
    }

HotModuleReplacementPlugin插件,开启热模块替换插件,也被称为HMR,永远不要在生产环境下启用HMR。

启用HMR非常简单,在大多数情况下也不需要配置

    //index.js
    import _ from 'lodash';
    import printMe from './print';
    import './style.css'

    function component(){
        let element = document.createElement('div');
        element.innerHTML = _.join(['hello','world'],' ');
        element.classList.add('red_text');

        let btn = document.createElement('button');
        btn.innerHTML = 'click to print something';
        btn.onclick = printMe;

        element.appendChild(btn);

        return element;
    }

    let element = component();
    document.body.appendChild(element);

    if(module.hot){
        module.hot.accept('./print.js',function(){
            console.log('Accepting the updated printMe module!');
            document.body.removeChild(element);
            element = component();
            document.body.appendChild(element);
        })
    }

通过Node.js API

当使用webpack dev server 和 Node.js API 时,不要将dev server选项放在webpack配置对象中。而是,在创建选项时,将其作为第二个参数传递。例如:

new webpackDevServer(compiler,options)

想要启用 HMR,还需要修改 webpack 配置对象,使其包含 HMR 入口起点。webpack-dev-server package 中具有一个叫做 addDevServerEntrypoints 的方法,你可以通过使用这个方法来实现。这是关于如何使用的一个小例子:

 //dev-server.js
    const webpackDevServer = require('webpack-dev-server');
    const webpack = require('webpack');

    const config = require('./webpack.config.js');
    const options = {
        contentBase:'./dist',
        hot:true,
        host:'localhost'
    }

    webpackDevServer.addDevServerEntrypoints(config,options);
    const compiler = webpack(config);
    const server = new webpackDevServer(compiler,options);

    server.listen(5000,'localhost',()=>{
        console.log('dev server listening on port 5000');
    })