资讯专栏INFORMATION COLUMN

为什么我们要做三份 Webpack 配置文件

silencezwm / 449人阅读

摘要:时至今日,已经成为前端工程必备的基础工具之一,不仅被广泛用于前端工程发布前的打包,还在开发中担当本地前端资源服务器模块热更新等角色,结合等代码检查工具,还可以实现在对源代码的严格校验检查。

时至今日,Webpack 已经成为前端工程必备的基础工具之一,不仅被广泛用于前端工程发布前的打包,还在开发中担当本地前端资源服务器(assets server)、模块热更新(hot module replacement)、API Proxy 等角色,结合 ESLint 等代码检查工具,还可以实现在对源代码的严格校验检查。

正如上文中提到的,前端从开发到部署前都离不开 Webpack 的参与,而 Webpack 的默认配置文件只有一个,即 webpack.config.js,那么问题来了,开发期和部署前应该使用同一份 Webpack 配置吗?答案肯定是否定的,既然 webpack.config.js 是一个 JS 文件,我们当然可以在文件里写 JavaScript 业务逻辑,通过读取环境变量 NODE_ENV 来判断当前是在开发(dev)时还是最终的生产环境(production),然而很多同学习惯把这两者的配置都混写在根目录下的 webpack.config.js,通过很多零散的 if…else 来“临时”决定某一个 plugin 或者某一个 loader 的配置项,随着 loaders 和 plugins 的不断增加,久而久之 webpack.config.js 变得原来越隆长,代码的可读性和可维护性也大大下降。

我想通过本文来介绍一种用 3 个 JS 文件来配置 Webpack 的方法,这里借鉴了很多开源项目的配置,同时也结合了我们自己在开发中碰到的种种问题解决方案。

本文中提及的配置基于 Webpack 2 或以上,建议使用 3.0 及以上版本

开发环境与生产环境的区别

  • 开发环境

    NODE_ENV 为 development
    启用模块热更新(hot module replacement)
    额外的 webpack-dev-server 配置项,API Proxy 配置项
    输出 Sourcemap

  • 生产环境

    NODE_ENV 为 production
    将 React、jQuery 等常用库设置为 external,直接采用 CDN 线上的版本
    样式源文件(如 css、less、scss 等)需要通过 ExtractTextPlugin 独立抽取成 css 文件
    启用 post-css
    启用 optimize-minimize(如 uglify 等)
    中大型的商业网站生产环境下,是绝对不能有 console.log() 的,所以要为 babel 配置 Remove console transform

    这里需要说明的是因为开发环境下启用了 hot module replacement,为了让样式源文件的修改也同样能被热替换,不能使用 ExtractTextPlugin,而转为随 JS Bundle 一起输出。

你需要三份配置文件

  1. webpack.base.config.js
    在 base 文件里,你需要将开发环境和生产环境中通用的配置集中放在这里:

    const CleanWebpackPlugin = require("clean-webpack-plugin");
    const path = require("path");
    const webpack = require("webpack");

    // 配置常量
    // 源代码的根目录(本地物理文件路径)
    const SRC_PATH = path.resolve("./src");
    // 打包后的资源根目录(本地物理文件路径)
    const ASSETS_BUILD_PATH = path.resolve("./build");
    // 资源根目录(可以是 CDN 上的绝对路径,或相对路径)
    const ASSETS_PUBLIC_PATH = "/assets/";

    module.exports = {
    context: SRC_PATH, // 设置源代码的默认根路径
    resolve: {
        extensions: [".js", ".jsx"]  // 同时支持 js 和 jsx
    },
    entry: {
        // 注意 entry 中的路径都是相对于 SRC_PATH 的路径
        vendor: "./vendor",
        a: ["./entry-a"],
        b: ["./entry-b"],
        c: ["./entry-c"]
    },
    output: {
        path: ASSETS_BUILD_PATH,
        publicPath: ASSETS_PUBLIC_PATH,
        filename: "./[name].js"
    },
    module: {
        rules: [
        {
            enforce: "pre",  // ESLint 优先级高于其他 JS 相关的 loader
            test: /.jsx?$/,
            exclude: /node_modules/,
            loader: "eslint-loader"
        },
        {
            test: /.jsx?$/,
            exclude: /node_modules/,
            // 建议把 babel 的运行时配置放在 .babelrc 里,从而与 eslint-loader 等共享配置
            loader: "babel-loader"
        },
        {
            test: /.(png|jpg|gif)$/,
            use:
            [
            {
                loader: "url-loader",
                options:
                {
                limit: 8192,
                name: "images/[name].[ext]"
                }
            }
            ]
        },
        {
            test: /.woff(2)?(?v=[0-9].[0-9].[0-9])?$/,
            use:
            [
            {
                loader: "url-loader",
                options:
                {
                limit: 8192,
                mimetype: "application/font-woff",
                name: "fonts/[name].[ext]"
                }
            }
            ]
        },
        {
            test: /.(ttf|eot|svg)(?v=[0-9].[0-9].[0-9])?$/,
            use:
            [
            {
                loader: "file-loader",
                options:
                {
                limit: 8192,
                mimetype: "application/font-woff",
                name: "fonts/[name].[ext]"
                }
            }
            ]
        }
        ]
    },
    plugins: [
        // 每次打包前,先清空原来目录中的内容
        new CleanWebpackPlugin([ASSETS_BUILD_PATH], { verbose: false }),
        // 启用 CommonChunkPlugin
        new webpack.optimize.CommonsChunkPlugin({
        names: "vendor",
        minChunks: Infinity
        })
    ]
    };
  1. webpack.dev.config.js

    这是用于开发环境的 Webpack 配置,继承自 base:

    const webpack = require("webpack");
    
    // 读取同一目录下的 base config
    const config = require("./webpack.base.config");
    
    // 添加 webpack-dev-server 相关的配置项
    config.devServer = {
    contentBase: "./",
    hot: true,
    publicPath: "/assets/"
    };
    // 有关 Webpack 的 API 本地代理,另请参考 https://webpack.github.io/docs/webpack-dev-server.html#proxy
    
    config.module.rules.push(
    {
        test: /.less$/,
        use: [
        "style-loader",
        "css-loader",
        "less-loader"
        ],
        exclude: /node_modules/
    }
    );
    
    // 真实场景中,React、jQuery 等优先走全站的 CDN,所以要放在 externals 中
    config.externals = {
    react: "React",
    "react-dom": "ReactDOM"
    };
    
    // 添加 Sourcemap 支持
    config.plugins.push(
    new webpack.SourceMapDevToolPlugin({
        filename: "[file].map",
        exclude: ["vendor.js"] // vendor 通常不需要 sourcemap
    })
    );
    
    // Hot module replacement
    Object.keys(config.entry).forEach((key) => {
    // 这里有一个私有的约定,如果 entry 是一个数组,则证明它需要被 hot module replace
    if (Array.isArray(config.entry[key])) {
        config.entry[key].unshift(
        "webpack-dev-server/client?http://0.0.0.0:8080",
        "webpack/hot/only-dev-server"
        );
    }
    });
    config.plugins.push(
    new webpack.HotModuleReplacementPlugin()
    );
    
    module.exports = config;
  1. webpack.config.js

    这是用于生产环境的 webpack 配置,同样继承自 base:

    const webpack = require("webpack");
    const ExtractTextPlugin = require("extract-text-webpack-plugin");
    
    // 读取同一目录下的 base config
    const config = require("./webpack.base.config");
    
    config.module.rules.push(
    {
        test: /.less$/,
        use: ExtractTextPlugin.extract(
        {
            use: [
            "css-loader",
            "less-loader"
            ],
            fallback: "style-loader"
        }
        ),
        exclude: /node_modules/
    }
    );
    
    config.plugins.push(
    // 官方文档推荐使用下面的插件确保 NODE_ENV
    new webpack.DefinePlugin({
        "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV || "production")
    }),
    // 启动 minify
    new webpack.LoaderOptionsPlugin({ minimize: true }),
    // 抽取 CSS 文件
    new ExtractTextPlugin({
        filename: "[name].css",
        allChunks: true,
        ignoreOrder: true
    })
    );
    
    module.exports = config;

现在在你的工程文件夹里应该已经有三个 Webpack 配置文件,它们分别是:

webpack.base.config.js
webpack.dev.config.js
webpack.config.js

最后,你还需要在 package.json 里添加相应的配置:


    {
    ...
    "scripts": {
        "build": "webpack --optimize-minimize",
        "dev": "webpack-dev-server --config webpack.dev.config.js",
        "start": "npm run dev" // 或添加你自己的 start 逻辑
    },
    ...
    }

和很多项目一样,在开发环境下的时候,你需要使用 npm run dev 来启动,而在生产环境中,则用 npm run build 来发布。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/2255.html

相关文章

  • 20170917 前端开发周报:JavaScript函数式编程、作用域和闭包

    摘要:用函数式编程对进行断舍离当从业的老司机学会函数式编程时,他扔掉了的特性,也不用面向对象了,最后发现了真爱啊作用域和闭包作用域和闭包在里非常重要。旨在帮助非函数式编程的同学,能快速切入到函数式编程的理念。 1、用函数式编程对JavaScript进行断舍离 当从业20的JavaScript老司机学会函数式编程时,他扔掉了90%的特性,也不用面向对象了,最后发现了真爱啊!!! https:/...

    tomener 评论0 收藏0
  • 20170917 前端开发周报:JavaScript函数式编程、作用域和闭包

    摘要:用函数式编程对进行断舍离当从业的老司机学会函数式编程时,他扔掉了的特性,也不用面向对象了,最后发现了真爱啊作用域和闭包作用域和闭包在里非常重要。旨在帮助非函数式编程的同学,能快速切入到函数式编程的理念。 1、用函数式编程对JavaScript进行断舍离 当从业20的JavaScript老司机学会函数式编程时,他扔掉了90%的特性,也不用面向对象了,最后发现了真爱啊!!! https:/...

    cyixlq 评论0 收藏0
  • 20170917 前端开发周报:JavaScript函数式编程、作用域和闭包

    摘要:用函数式编程对进行断舍离当从业的老司机学会函数式编程时,他扔掉了的特性,也不用面向对象了,最后发现了真爱啊作用域和闭包作用域和闭包在里非常重要。旨在帮助非函数式编程的同学,能快速切入到函数式编程的理念。 1、用函数式编程对JavaScript进行断舍离 当从业20的JavaScript老司机学会函数式编程时,他扔掉了90%的特性,也不用面向对象了,最后发现了真爱啊!!! https:/...

    lentoo 评论0 收藏0
  • 摒弃gulp.js+require.js,使用webpack配置多页面web项目

    摘要:主要做了两套方案,一套是基于的单页应用,还有一套就是多页应用。麻烦的是原先使用这个方案。这太麻烦了,还不如使用一步到位。后记暂时只做了这些,这套工具还没有正式投入使用估计会有挺多问题的。 不想看下面废话的,可以直接看这里。 前言 这两周负责公司前端基础工程的建设。主要做了两套方案,一套是基于vue的单页应用,还有一套就是多页应用。对于vue单页这块是没什么问题的,直接用vue-cli生...

    lingdududu 评论0 收藏0
  • 深入浅出webpack学习(11)--多种类型配置

    摘要:如果采用导出一个来描述所需配置的方法需要写两个文件。再在启动时通过指定使用哪个配置文件。例如启动命令是时,则的值是。采用描述的一份配置采用函数描述的一份配置采用异步函数描述的一份配置以上配置会导致针对这三份配置执行三次不同的构建。 除了通过导出一个Object来描述webpack所需要的配置,还有其他更灵活的方式,以简化不同场景的配置。下面来一一介绍它们。 1. 导出一个Functio...

    idisfkj 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<