3.核心概念—出口-灵析社区

懒人学前端

一、配置参数详解

output 输出配置详解

output 属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。主要输出文件的默认值是 ./dist/main.js,其他生成文件默认放置在 ./dist 文件夹中。

我们可以通过在配置中指定一个 output 对象,来配置这些处理过程:

const path = require('path');
module.exports = {
  mode: 'development',
  entry: {
    index: './src/index.js'
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
    publicPath: '/assets/',
    library: 'DemoLibrary', // 导出名称
    libraryTarget: 'window' // 挂载目标
  }
};

output 对象中可以包含数十个配置项,其中大部分开发中使用频率不高,我们在本章内容中只介绍几个常用配置,对其他配置感兴趣的同学可以查看官网 output配置

filename

filename 决定了每个输出 bundle 的名称。这些 bundle 将写入到 output.path 选项指定的目录下。

对于单个入口起点,filename 会是一个静态名称。 filename 支持以字符串和函数的形式定义参数。

// 字符串形式
module.exports = {
  ...
  output: {
    filename: 'bundle.js',
  }
};
// 函数形式
module.exports = {
  ...
  output: {
    filename: (pathData) => {
      console.log(pathData)
      return '[name].js';
    }
  }
};

字符串形式的 filename,会在输出文件中生成 bundle.js,函数形式的 filename 会在输出文件中生成 index.js (以 chunk name 命名),在控制台输出下 pathData,我们可以看到返回了一个包含 chunk 内容等信息的对象。

filename 可以不仅仅是 bundle 的名字,还可以使用像 'js/[name]/bundle.js' 这样的文件路径,即便路径中的目录不存在也没关系,webpack 会在输出资源时创建该目录。例子如下:

module.exports = {
  ...
  output: {
    filename: 'js/[name]/bundle.js'
  }
};

当通过多个入口起点(entry point)、代码拆分(code splitting)或各种插件(plugin)创建多个 bundle,应该使用以下一种替换方式,来赋予每个 bundle 一个唯一的名称

上面的配置除了可以对不同的 bundle 进行名称区分,还能起到一个控制客户端缓存的作用,表中的[chunkhash] 和 [contenthash] 都与文件内容直接相关,在 filename 中使用了这些变量后,当对文件内容做了修改,可以引起 bundle 文件名的修改,从而用户在下一次请求文件资源时会重新加载文件,而不会直接命中缓存资源。

在实际的工程中,我们一般使用较多的是[name],一般与定义的 chunk 一一对应,可读性较高,为了控制客户端缓存,我们一般还加上 [contenthash],如:

module.exports = {
  ...
  output: {
    filename: '[name]-[contenthash].js'
  }
};

打包结果如下

path

path 可以指定资源输出位置,要求必须使用绝对路径,如

const path = require('path');
module.exports = {
  mode: 'development',
  entry: {
    index: './src/index.js',
  },
  output: {
    path: path.resolve(__dirname, 'dist')
  }
};

上述配置将工程的dist目录设置为资源的输出目录,在 webpack 4 之后,output.path 已经默认为 dist 目录,除非我们需要修改他,否则可以不用单独配置。

publicPath

publicPath 从功能上来说,用于指定资源的请求位置。页面中的资源分为两种,一种是由 HTML 页面直接请求的,比如通过 script 标签加载 js,通过 link 标签加载 css。另一种是由 js 或 css 请求的,如加载图片字体文件等。 publicPath 就用来指定第二种间接资源的请求位置。如果指定了一个错误的值,则在加载这些资源时会收到 404 错误。

publicPath 有以下三种形式

  1. 相对于 HTML
  2. 相对于 HOST
  3. 相对于 CDN

相对于 HTML

在请求资源时,会以当前 html 页面所在路径加上 publicPath 的相对路径来构成实际请求的 URL,如

// 假设当前 html 页面地址为 http://demo.com/webpack/index.html
// 需要请求文件名为 demo.png
module.exports = {
  ...
  output: {
    publicPath: '' // 实际请求路径 http://demo.com/webpack/demo.png
    publicPath: './css' // 实际请求路径 http://demo.com/webpack/css/demo.png
    publicPath: '../assets/' // 实际请求路径 http://demo.com/assets/demo.png
  }
};

相对于 HOST

若 publicPath 的值以 “/” 开始,则代表此时 publicPath 是以当前页面的域名加上 publicPath 的相对路径来构成实际请求的 URL,如


// 假设当前 html 页面地址为 http://demo.com/webpack/index.html
// 需要请求文件名为 demo.png
module.exports = {
  ...
  output: {
    publicPath: '/' // 实际请求路径 http://demo.com/demo.png
    publicPath: '/css' // 实际请求路径 http://demo.com/css/demo.png
    publicPath: '../assets/' // 实际请求路径 http://demo.com/assets/demo.png
  }
};

相对于 CDN

上面两种配置都是相对路径,我们也可以使用绝对路径的形式配置 publicPath,这种情况一般发生在将静态资源放在 CDN 上面,如

// 假设当前 html 页面地址为 http://demo.com/webpack/index.html
// 需要请求文件名为 demo.png
module.exports = {
  ...
  output: {
    publicPath: 'http://cdn.example.com/assets/' // 实际请求路径 http://cdn.example.com/assets/demo.png
    publicPath: 'https://cdn.example.com/assets/' // 实际请求路径 https://cdn.example.com/assets/demo.png
    publicPath: '//cdn.example.com/assets/' // 实际请求路径 //cdn.example.com/assets/demo.png
  }
};

webpack-dev-server 也会默认从 publicPath 为基准,使用它来决定在哪个目录下启用服务,来访问 webpack 输出的文件。

library

library 的作用是将打包的内容生成一个库,可以供其他工程加载使用。这一点在目前流行的微前端架构实战上面很有用,如子应用通过输出类库的形式将内容输出到一个对象上,这样主应用就可以通过加载 js 的方式去引入子应用,并且可以通过子应用输出的对象名称来加载子应用的内容。library 具体的使用方法,我们来看下面的例子:

webpack.config.js

module.exports = {
  ...
  entry: './src/index.js',
  output: {
    library: 'DemoLibrary'
  }
};

src/index.js 的入口中导出了如下函数

export function hello(webpack) {
  console.log(`hello ${webpack}`);
}

此时,变量 DemoLibrary 将与入口文件所导出的文件进行绑定,下面是如何使用打包生成的index.js文件:

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <title>测试DemoLibrary库</title>
</head>
<body>
    <script src='./dist/index.js'></script>
    <script>
    DemoLibrary.hello('webpack');
    </script>
</body>
</html>

在浏览器中可以看到成功输出 hello webpack。

library 的类型可以为字符串、数组、和对象,字符串的参数类型则直接指代库的名称,与对象中设置 name 属性作用相同。如果 entry 入口设置为 object,所有入口都可以通过 library 的 array 语法暴露:

module.exports = {
  // …
  entry: {
    a: './src/a.js',
    b: './src/b.js',
  },
  output: {
    filename: '[name].js',
    library: ['DemoLibrary', '[name]'], // [name] 为 chunk name
  },
};

假设 a.js 与 b.js 导出名为 hello 的函数,下面就是如何使用这些库的方法:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>测试DemoLibrary库</title>
</head>
<body>
    <script src='./dist/a.js'></script>
    <script src='./dist/b.js'></script>
    <script>
    DemoLibrary.a.hello('webpack');
    DemoLibrary.b.hello('webpack');
    </script>
</body>
</html>

请注意,如果你打算在每个入口点配置 library 配置项的话,以上配置将不能按照预期执行。这里是如何 在每个入口点下 做的方法:

module.exports = {
  // …
  entry: {
    main: {
      import: './src/index.js',
      library: {
        // `output.library` 下的所有配置项可以在这里使用
        name: 'MyLibrary',
        type: 'umd',
        umdNamedDefine: true,
      },
    },
    another: {
      import: './src/another.js',
      library: {
        name: 'AnotherLibrary',
        type: 'commonjs2',
      },
    },
  },
};

library 包含以下可配置参数

这里我们说下 type 类型,在实际的使用中,我们可能根据工程运行环境的需要,而需要将类库暴露为不同的类型,如 支持 esModule、amd、cmd、umd 等,type 配置就可以帮我们完成不同输出方式。

type 类型默认包括 'var'、'module'、'assign'、'assign-properties'、'this'、'window'、'self'、'global'、'commonjs'、'commonjs2'、'commonjs-module'、'commonjs-static'、'amd'、'amd-require'、'umd'、'umd2'、'jsonp' 以及 'system',除此之外也可以通过插件添加。官方文档对每种类型给了详细说明和事例,具体我们可查看官方文档,output.target.type 配置

总结

以上为我们在实际开发中使用的 output 配置,包含 path、filename、publicPath、library,日常使用中可能还会用到 libraryTarget ,不过 webpack 未来会放弃对 output.libraryTarget 的支持,所以可以使用 output.library.type 替代 output.libraryTarget。

二、输出配置实例

output 输出配置实例

到目前为止,我们都是在 index.html 文件中手动引入打包生成的资源,然而随着应用程序增长,并且一旦开始在文件名中使用 hash 并输出 多个 bundle,如果继续手动管理 index.html 文件,就会变得困难起来。然而,通过一些插件可以使这个过程更容易管控。HtmlWebpackPlugin 可以帮我们解决这个问题。

设置 HtmlWebpackPlugin

继续使用之前的工程文件,目录结构为:

首先安装插件,并且调整 webpack.config.js 文件:

安装 html-webpack-plugin 插件

npm install --save-dev html-webpack-plugin

webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
  mode: 'development',
  entry: {
    index: './src/index.js',
    hello: './src/hello.js'
  },
  output: {
    filename: '[name].js'
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: '管理输出',
    }),
  ],
};

执行构建命令 npm run build,我们看下打包后的结果,我们可以看到,打包文件包含两个入口文件对应的 js 文件,还包含一个 index.html 文件

在 dist 目录下我们看下打包的 index.html 文件,我们可以看到 HtmlWebpackPlugin 创建了一个全新的 index.html 文件,所有的 bundle 会自动添加到 html 中。

清理 /dist 文件夹

你可能已经注意到,由于遗留了之前指南中的代码示例,我们的 /dist 文件夹显得相当杂乱。webpack 将生成文件并放置在 /dist 文件夹中,但是它不会追踪哪些文件是实际在项目中用到的。

通常比较推荐的做法是,在每次构建前清理 /dist 文件夹,这样 /dist 文件夹中只有最近一次生成的文件。让我们使用 output.clean( webpack 5.20.0 及以上版本支持)配置项实现这个需求。

webpack.config.js

module.exports = {
  ...
  output: {
    clean: true
  }
};

现在,执行 npm run build,检查 /dist 文件夹。如果一切顺利,现在只会看到构建后生成的文件,而没有旧文件!

总结

本章我们介绍了一个优化开发效率的插件和一个配置项,使用 HtmlWebpackPlugin 插件可以动态的生成 index.html 文件,以及动态的向 index.html 文件插入 bundle。了解了如何在编译时清空 dist 文件内容。

阅读量:2013

点赞量:0

收藏量:0