引言

最近在学习webpack,发现好多知识点,之前一点都没有接触过,如babel、core-js、browserslist等等。以前习惯了使用cli构建项目,很多东西不用考虑,拿来就用,这样的码农是不会有能力提升的,必须了解更多的知识点,才能成为一位出色的前端工程师。

我大致梳理了一下我的学习历程,将它简单的归类,以解决问题的路径娓娓道来。当然我的学习也不算完整,还在继续努力,也请各位大佬多多指导。

学习webpack首先必须拥有的基础是:

1.了解node.js是什么;

2.了解npm包管理器是什么以及package.json的常用属性的意义;

在此基础上,我们来一步步的学习webpack,以及如何使用webpack进行打包。


webpack 简介

webapck是一款前端资源打包工具,其最核心的功能是解决模块之间的依赖问题。听起来是不是很耳熟?对,没错,如果你学习过AMD规范、CommonJS规范,你会发现webpack是在这些规范的基础上发展出来的开源工具,解决前端开发人员模块化到工程化的问题。(PS:没有了解过AMDCommonJS的小伙伴建议去简单了解一下,因为这是js模块化的基础,了解该基础才能理解前端模块化,而进一步理解前端工程化,才能明白webpack到底在干什么。)

这里简单说一下js模块化。很多小伙伴从切图仔进阶到前后端分离的前端工程模式时,都有些犯怵,一下子不理解前端工程到底是个什么概念,其实前端工程化说白了,就是在开发中大型web应用时,页面的交互非常频繁,很多计算、数据处理、业务代码都放在客户端(浏览器)进行处理了。那这么多的代码,怎么进行管理和维护呢,难道还是按照不同的页面<script>标签的先后顺序么?就不能一个html,只引用一个<script>,其他的都在这个<script>里面去解决么?这时,就发展出了js模块化,也就出现了AMDCommonJS等这些规范(Node.js就是根据CommonJS规范处理模块的,新版的jQuery是根据UMD规范来进行编写的)。有了这些规范,我们就可以很从容的管理js与js之间的依赖关系了,而webpack正是将多个模块打包合并成一个js(或多个)的工具,html里面也可以只引入一个<script>标签。

webpack不仅可以打包js,还可以将前端各种各样的资源整合起来进行打包,如:css、image、video等等...并且在打包的过程中对资源进行处理,如:代码压缩、代码合并等等...并利用各式各样的loader(预处理器)、plugins(插件),让你的代码自动完成ES6 => ES5、SCSS => css转化等功能,。总的来说,webpack现在已然是一款前端构建工具,可以构建你的前端开发环境,并在配置好各项设置后,可以集中精力聚焦于开发业务,其他的事就交给webpack帮你来处理。


安装

在安装webpack之前,我们需要确保在本机上已经安装了Node.js。果没有安装,请去Node.js官网下载。(https://nodejs.org/

我们先初始化我们的项目文件夹。

    npm init -y

初始化完毕后,我们打开webpack的官网:https://webpack.js.org/,阅读英文有困难的小伙伴,可以选择右上方的语言切换按钮,选择中文阅览。切换好语言后,我们再次点击导航栏中的“文档”栏目,并点击二级导航的“指南”页面。这时,左侧的菜单中,就已经出现了webpack的基础指南。

57c717e75066b659cfe19dc2a643153c.svg

首先我们选择菜单-安装

出于学习的目的,大家可以选择全局安装。而如果出于项目制作的考虑,以及可能会用到Git进行版本控制和分享,我推荐大家进行本地安装。使用本地安装,webpack会存于node_modules文件夹内与devDependencies属性内,更方便项目文件迁移以及协同开发等情况。

使用webpack需要安装3个包,分别是

1.webpack(核心包)
2.webpack-cli(脚手架)
3.webpack-dev-server(开发服务器)

webpack核心包,顾名思义,是基础,必须使用的包。

webpack-cli脚手架,是可以帮助我们在使用webpack的时候,减少一些需要手动配置的选项,更方便我们使用自定义配置的工具,从wepback v4.0开始必须安装

webpack-dev-server是我们在开发环境时,不可能每一次调试都重新构建一次。所以一个热重载的服务器就很有必要。

使用npm命令:

    npm install --save-dev webpack webpack-cli webpack-dev-server

安装成功

好的,安装成功。


配置文件

webpack 默认提供了一套基础配置,以供新手使用者快速上手使用。webpack 会假定项目的入口起点为 src/index,然后会在 dist/main.js 输出结果,并且在生产环境开启压缩和优化。

但是通常情况下,我们在制作项目时,还需要 webpack 提供给我们各种各样的复杂功能。而此时我们可以在项目根目录下创建一个 webpack.config.js 文件,webpack 会自动使用它。

const path = require('path');

module.exports = {
    mode: "production", // "production" | "development" | "none"  // Chosen mode tells webpack to use its built-in optimizations accordingly.
    entry: "./app/entry", // string | object | array  // 默认为 './src'
    // 这里应用程序开始执行
    // webpack 开始打包
    output: {
        // webpack 如何输出结果的相关选项
        path: path.resolve(__dirname, "dist"), // string
        // 所有输出文件的目标路径
        // 必须是绝对路径(使用 Node.js 的 path 模块)
        filename: "bundle.js", // string    // 「入口分块(entry chunk)」的文件名模板
        publicPath: "/assets/", // string    // 输出解析文件的目录,url 相对于 HTML 页面
        library: "MyLibrary", // string,
        // 导出库(exported library)的名称
        libraryTarget: "umd", // 通用模块定义    // 导出库(exported library)的类型
        /* 高级输出配置(点击显示) */
    },
    module: {
        // 关于模块配置
        rules: [
            // 模块规则(配置 loader、解析器等选项)
            {
                test: /\.jsx?$/,
                include: [
                    path.resolve(__dirname, "app")
                ],
                exclude: [
                    path.resolve(__dirname, "app/demo-files")
                ],
                // 这里是匹配条件,每个选项都接收一个正则表达式或字符串
                // test 和 include 具有相同的作用,都是必须匹配选项
                // exclude 是必不匹配选项(优先于 test 和 include)
                // 最佳实践:
                // - 只在 test 和 文件名匹配 中使用正则表达式
                // - 在 include 和 exclude 中使用绝对路径数组
                // - 尽量避免 exclude,更倾向于使用 include
                issuer: {
                    test,
                    include,
                    exclude
                },
                // issuer 条件(导入源)
                enforce: "pre",
                enforce: "post",
                // 标识应用这些规则,即使规则覆盖(高级选项)
                loader: "babel-loader",
                // 应该应用的 loader,它相对上下文解析
                // 为了更清晰,`-loader` 后缀在 webpack 2 中不再是可选的
                // 查看 webpack 1 升级指南。
                options: {
                    presets: ["es2015"]
                },
                // loader 的可选项
            },
            {
                test: /\.html$/,
                use: [
                    // 应用多个 loader 和选项
                    "htmllint-loader",
                    {
                        loader: "html-loader",
                        options: {
                            /* ... */
                        }
                    }
                ]
            },
            {
                oneOf: [ /* rules */ ]
            },
            // 只使用这些嵌套规则之一
            {
                rules: [ /* rules */ ]
            },
            // 使用所有这些嵌套规则(合并可用条件)
            {
                resource: {
                    and: [ /* 条件 */ ]
                }
            },
            // 仅当所有条件都匹配时才匹配
            {
                resource: {
                    or: [ /* 条件 */ ]
                }
            },
            {
                resource: [ /* 条件 */ ]
            },
            // 任意条件匹配时匹配(默认为数组)
            {
                resource: {
                    not: /* 条件 */
                }
            }
            // 条件不匹配时匹配
        ],
        /* 高级模块配置(点击展示) */
    },
    resolve: {
        // 解析模块请求的选项
        // (不适用于对 loader 解析)
        modules: [
            "node_modules",
            path.resolve(__dirname, "app")
        ],
        // 用于查找模块的目录
        extensions: [".js", ".json", ".jsx", ".css"],
        // 使用的扩展名
        alias: {
            // 模块别名列表
            "module": "new-module",
            // 起别名:"module" -> "new-module" 和 "module/path/file" -> "new-module/path/file"
            "only-module$": "new-module",
            // 起别名 "only-module" -> "new-module",但不匹配 "only-module/path/file" -> "new-module/path/file"
            "module": path.resolve(__dirname, "app/third/module.js"),
            // 起别名 "module" -> "./app/third/module.js" 和 "module/file" 会导致错误
            // 模块别名相对于当前上下文导入
        },
        /* 可供选择的别名语法(点击展示) */
        /* 高级解析选项(点击展示) */
    },
    performance: {
        hints: "warning", // 枚举    maxAssetSize: 200000, // 整数类型(以字节为单位)
        maxEntrypointSize: 400000, // 整数类型(以字节为单位)
        assetFilter: function (assetFilename) {
            // 提供资源文件名的断言函数
            return assetFilename.endsWith('.css') || assetFilename.endsWith('.js');
        }
    },
    devtool: "source-map", // enum  // 通过在浏览器调试工具(browser devtools)中添加元信息(meta info)增强调试
    // 牺牲了构建速度的 `source-map' 是最详细的。
    context: __dirname, // string(绝对路径!)
    // webpack 的主目录
    // entry 和 module.rules.loader 选项
    // 相对于此目录解析
    target: "web", // 枚举  // bundle 应该运行的环境
    // 更改 块加载行为(chunk loading behavior) 和 可用模块(available module)
    externals: ["react", /^@angular\//], // 不要遵循/打包这些模块,而是在运行时从环境中请求他们
    serve: { //object
        port: 1337,
        content: './dist',
        // ...
    },
    // 为 webpack-serve 提供选项
    stats: "errors-only", // 精确控制要显示的 bundle 信息
    devServer: {
        proxy: { // proxy URLs to backend development server
            '/api': 'http://localhost:3000'
        },
        contentBase: path.join(__dirname, 'public'), // boolean | string | array, static file location
        compress: true, // enable gzip compression
        historyApiFallback: true, // true for index.html upon 404, object for multiple paths
        hot: true, // hot module replacement. Depends on HotModuleReplacementPlugin
        https: false, // true for self-signed, object for cert authority
        noInfo: true, // only errors & warns on hot reload
        // ...
    },
    plugins: [
        // ...
    ],
    // 附加插件列表
    /* 高级配置(点击展示) */
}

以上文件是引用的 webpack 官网的“配置”一篇中的一个样例,通过上面的样例我们可以看出, webpack 的配置项真的是多如繁星,这也正是我们学习 webpack 的难点之一。我们会在后面的学习中,逐步弄明白每一个配置项具体有什么意义,能为我们干什么。


多配制文件

有时,我们会因为项目的不用需求,建立不同的配置文件,以供我们应对多种情况。这时,可以新建多个配置文件,如:build.conf.js、test.conf.js。并在package.json中进行这样的设置:

配置文件

这样,我们就可以通过使用不同的npm命令,来使用不同的配置文件,进行相应打包操作了。


入口与输出

了解npm的小伙伴,肯定知道,package.json里有一个main属性,代表整个包的输出口,通过main指向的js文件,得到一个对象(或类或函数)。

我们通过CommonJS规范语法:

const XXX = require('XXX')'

来引入该模块,并使用它。


而webpack的入口与输出,与这个非常的类似。当我们使用webpack进行打包时,需要指定至少一个入口,并设置其对应的输出配置。比如:

    const path = require('path')

    module.exports = {
        entry: "./src/main", // string | object | array  // 默认为 './src'
        // 这里应用程序开始执行
        // webpack 开始打包
        output: {
            // webpack 如何输出结果的相关选项
            path: path.resolve(__dirname, "dist/assets"), // string
            // 所有输出文件的目标路径
            // 必须是绝对路径(使用 Node.js 的 path 模块)
            filename: "/[name].js", // string    // 「入口分块(entry chunk)」的文件名模板
        },
    }

当我们配置好这些后,只需要命令webpack开始打包即可。

npx webpack
注意,此处因为我们并没有全局安装 webpack 和 webpack-cli ,所以我们无法直接使用 webpack 命令来进行打包,而使用了 npx 命令再转而使用 webpack 命令,此处只是为了向大家展示,是可以使用 webpack 命令直接进行打包的,但往往在实际项目开发的过程中,我们并不会这样使用。所以在后面的学习中,我们将只使用,package.json 中的 scripts 脚本命令,来进行打包的命令操作。而在 package.json 中的脚本命令,是不需要全局安装 webpack 的,在本地安装即可。也就是下面的配置:

package.json

{
    "scripts": {
        "build":"webpack --config webpack.config.js"
    }
}

然后使用npm run 脚本名称即可运行

npm run build

build结果


未完待续...


liyan
1 声望0 粉丝