5.核心概念-Pligin—下-灵析社区

懒人学前端

五、MiniCssExtractPlugin

MiniCssExtractPlugin

MiniCssExtractPlugin 插件会将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件。这个插件需要在 webpack 5 中才能正常工作,webpack 4 中使用 extract-text-webpack-plugin 插件做 CSS 分离。之前的文章中我们介绍了 style-loader 的使用,style-loader 是将 JS 中包含的 CSS 内容以 <style> 标签的形式插入到 DOM 中,而 MiniCssExtractPlugin 插件则是将 JS 中包含的 CSS 创建一个新的文件,并且以 <link> 标签的形式插入链接到加载依赖的文件中。MiniCssExtractPlugin 作用与 style-loader 类似,但是实现方式却完全不同。MiniCssExtractPlugin 一般与 css-loader 同时使用。

注意:下面【使用】模块的案例,index.css 分离是 MiniCssExtractPlugin 做的,而通过 `<link>` 标签插入index.html 是 HtmlWebpackPlugin 插件做的,MiniCssExtractPlugin 插件可以将非入口文件通过 `<link>` 标签插入到加载依赖的文件中,即 MiniCssExtractPlugin 插入 `<link>` 仅适用于非初始(异步)块。

使用

index.js

import styles from "./index.css";
import main from "./main.css";

const divElement = document.createElement("div");
divElement.className = "demo";
divElement.innerHTML = 'mini-css-extract-plugin';
document.body.appendChild(divElement);

index.css

.demo {
    color: red;
}

main.css

.demo {
    font-size: 20px;
}

安装 mini-css-extract-plugin

npm install mini-css-extract-plugin -D

安装成功后,将 mini-css-extract-plugin 配置到 webpack.config.js 的 plugins 和 module 中。

webpack.config.js

...
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
    ...
    module: {
        rules: [
          {
            test: /\.css$/i,
            use: [MiniCssExtractPlugin.loader, "css-loader"],
          },
        ],
    },
    plugins: [
        ...
        new MiniCssExtractPlugin()
    ]
}

我们在入口文件 index.js 中导入了 main.css 和 index.css 两个样式文件。一个改变字体,一个改变字体颜色。执行打包命令,可以看到在 dist 文件夹下除了 index.js 和 index.html 文件之外还生成了一个 index.css 文件。index.css 文件中包含 main.css 和 index.css 的内容。在 index.html 中通过 <link> 标签的形式引入了 index.css 文件。在浏览器中打开 dist/index.html 可以看到样式被正常展示。

上面的例子中在 index.js 中导入了两个样式文件,接下来我们试一下在入口文件 index.js 中导入 main.js 和 index.css,其中 main.js 中导入 main.css。其他配置保持不变。

index.js

import styles from "./index.css";
import { default as mainElement } from './main';

const divElement = document.createElement("div");
divElement.className = "demo";
divElement.innerHTML = 'mini-css-extract-plugin';
divElement.appendChild(mainElement);
document.body.appendChild(divElement);

main.js

import main from "./main.css";

const divElement = document.createElement("div");
divElement.className = "demo-main";
divElement.innerHTML = 'demo-main';

export default divElement;

main.css

.demo-main {
    font-size: 20px;
}

执行打包命令后,dist 文件夹下依然只有 index.html,index.js,index.css 文件。查看后发现虽然在 main.js 中导入的 main.css,但由于 main.js 被 index.js 依赖。所以 css 文件内容依然都打包到了 index.css 中。

配置

MiniCssExtractPlugin 包含两部分配置,plugin 配置和 loader 配置。

Plugin Options

filename

类型:string | ((pathData: PathData, assetInfo?: AssetInfo) => string);

默认值:[name].css

作用:输出的 css 文件的名称,默认为 chunkName 值。

webpack.config.js

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      ...
       filename: '[name].css' // index.css
      //  filename: (pathData) => {
      //     return pathData.hash + '-index.css';
      //  }, // 8de455759e47e7f4d53b-index.css
    }),
  ]
};

chunkFilename

类型:string | ((pathData: PathData, assetInfo?: AssetInfo) => string);

默认值:基于 filename

作用:修改非入口文件的打包后的名称。

index.js

import styles from "./index.css";
// 将 main.js 打包到单独文件
const mainElement = import(/* webpackChunkName: "main" */ './main');
...

webpack.config.js

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      chunkFilename: 'chunk-[id].css', // chunk-main.css
    }),
  ]
};

打开 dist 我们可以看到 main.js 文件已经单独打包,并且生成了一个 chunk-name.css 文件。

ignoreOrder

类型:boolean

默认值:false

作用:模块中加载 css 文件顺序不一致是否发出警告,例如:1.js 中加载 css 的顺序是 a.css、b.css,2.js 中加载顺序是 b.css、a.css,就会发出警告,详细可看下官网 issue

webpack.config.js


module.exports = {
  ...
  plugins: [
    new MiniCssExtractPlugin({
      ignoreOrder: false,
    })
  ]
};

insert

类型:string | ((linkTag: HTMLLinkElement) => void)

默认值:document.head.appendChild(linkTag),即默认插入到 DOM 文件的 head 中

作用:打包的 CSS 文件,通过 <link> 标签插入的位置。

index.js

import styles from "./index.css";
const divElement = document.createElement("div");
// import 方法导入的模块为异步加载
import(/* webpackChunkName: "main" */ './main').then(res => {
  divElement.appendChild(res.default)
});

divElement.className = "demo";
divElement.innerHTML = 'mini-css-extract-plugin';
document.body.appendChild(divElement);

index.html

...
<body>
  <div id="div-element"></div>
</body>

webpack.config.js

module.exports = {
  ...
  plugins: [
    new MiniCssExtractPlugin({
      insert: '#div-element'
    })
  ]
};

执行打包命令后,在 dist/index.html 文件中,只包含了 index.css 的 <link> 标签,在浏览器中运行 index.html,在控制台可以看到 chunk-main.css 的 <link> 标签被插入到选择器为 #div-element 元素的后面。在生成的 index.js 文件中可以看到创建 link 标签的源码。

attributes

类型:object

默认值:{}

作用:在 <link> 标签中添加属性

webpack.config.js

module.exports = {
  ...
  plugins: [
    ...
    new MiniCssExtractPlugin({
        attributes: {
            "data-target": "data" // <link data-target="data" rel="stylesheet" type="text/css" href=".../dist/main.css">
        },
    })
  ]
};

linkType

类型:string | boolean

默认值:text/css

作用:修改 <link> 标签中 type 属性,type 的默认值为 text/css。

webpack.config.js

module.exports = {
  ...
  plugins: [
    ...
    new MiniCssExtractPlugin({
        linkType: "text/css" // <link type="text/css" ...>
        // linkType: false // <link rel="stylesheet" href=".../dist/main.css">
    })
  ]
};

runtime

类型:boolean

默认值:true

作用:允许启用/禁用运行时生成。

webpack.config.js

module.exports = {
  ...
  plugins: [
    ...
    new MiniCssExtractPlugin({
        runtime: false // 运行 index.html 时,main.css 不会被插入到 DOM 中。
    })
  ]
};

Loader Options

publicPath

类型:string | ((resourcePath: string, rootContext: string) => string)

默认值:webpackOptions.output 选项的值

作用:输出的 CSS 文件中,为图像、文件等外部资源指定自定义公共路径。

main.css

.demo-main {
    font-size: 20px;
    background: url(./img/1.png);
}

webpack.config.js

module.exports = {
  ...
  module: {
        rules: [
          {
            test: /\.css$/i,
            use: [
                {
                    loader: MiniCssExtractPlugin.loader,
                    options: {
                      publicPath: "/public/path/to/", // main.css background: url(/public/path/to/e30cb9c395cbb5ae00e9.png);
                    },
                }, "css-loader"],
          },
        ],
    },
};

打包成功后,查看生成的 main.css 文件,background 地址已经被替换成传入的 publicPath,通过传入 publicPath 可以引用外部资源。

emit

类型:boolean

默认值:true

作用:提取的 CSS 是否生成对应文件。当 emit 值为 true 会生成 CSS 文件,值为 false 不生成 CSS 文件。

webpack.config.js

module.exports = {
  ...
  module: {
        rules: [
          {
            test: /\.css$/i,
            use: [
                {
                    loader: MiniCssExtractPlugin.loader,
                    options: {
                      emit: false, // 打包后的 dist 中没有生成 index.css 和 main.css,运行 index.html 样式不生效
                    },
                }, "css-loader"],
          },
        ],
    },
};

esModule

类型:boolean

默认值:true

作用:生成的文件是否使用 ES 模块语法,开启 ES 语法对 tree-shaking 将非常有用。

webpack.config.js

module.exports = {
  ...
  module: {
        rules: [
          {
            test: /\.css$/i,
            use: [
                {
                    loader: MiniCssExtractPlugin.loader,
                    options: {
                      esModule: false, // 启用CommonJS 语法
                    },
                }, "css-loader"],
          },
        ],
    },
};

总结

此章节演示了 MiniCssExtractPlugin 的用法和包含的配置参数。MiniCssExtractPlugin 还有一些推荐的例子,感兴趣的同学可查看官方。

六、WebpackManifestPlugin

WebpackManifestPlugin

WebpackManifestPlugin 是一个 webpack 插件,此插件的作用是生成资产清单即 manifest.json 文件,在使用此插件前,我们先看下什么是 manifest 以及 manifest 的作用,以下引自 webpack 官网。

一旦你的应用在浏览器中以 index.html 文件的形式被打开,一些 bundle 和应用需要的各种资源都需要用某种方式被加载与链接起来。在经过打包、压缩、>为延迟加载而拆分为细小的 chunk 这些 webpack 优化 之后,你精心安排的 /src 目录的文件结构都已经不再存在。所以 webpack 如何管理所有所需模 >块之间的交互呢?这就是 manifest 数据用途的由来……

总结来说,manifest.json 就是记录项目中生成的 bundle 之间的映射关系。有了这份清单,我们可以通过特定的方法加载资源,如服务端渲染或通过遍历 manifest.json 将记录输出到页面上等。在当前流行的微前端框架中,通过引入不同子项目的 manifest.json 文件,并遍历文件内容动态输出到 DOM 中,从而实现加载不同子项目工程,这会比手动获取子项目资源清单减少出错概率和省事的多。

使用

index.js

import styles from "./index.css";
const divElement = document.createElement("div");
divElement.className = "demo";
divElement.innerHTML = 'webpack-manifest-plugin';
document.body.appendChild(divElement);

index.css

.demo {
    color: red;
    background: url(./img/1.png);
}

安装 webpack-manifest-plugin

npm install webpack-manifest-plugin -D

安装成功后,将 webpack-manifest-plugin 配置到 webpack.config.js 的 plugins 中。

webpack.config.js

...
const { WebpackManifestPlugin } = require('webpack-manifest-plugin');

module.exports = {
    ...
    plugins: [
        ...
        new WebpackManifestPlugin({
            publicPath: './'
        })
    ]
}

我们在 index.js 文件中创建了一个 div 标签,在 index.css 中设置了 div 标签的字体颜色和背景图片,此时执行打包命令,打包成功后在 dist 中可以看到除了项目资源文件外还有一个 manifest.json 文件。打开 manifest.json 文件看下内容。

manifest.json 包含了资源的映射关系,我们需要遍历 manifest.json 对象的内容,将需要的如 js 资源通过 createElement 等方式挂载到 index.html 中项目即可正常使用。

配置

以下为包含部分常用配置,全部配置可查看官网

basePath

类型:String

默认值:''

作用:生成的 manifest.json 文件中,basePath 参数传入的值将添加到对象的 key 值前面。

webpack.config.js

module.exports = {
  plugins: [
    new WebpackManifestPlugin({
      ...
      basePath: 'src' // "src/index.js": ".../index.js",
      basePath: '' // "index.js": ".../index.js",
    }),
  ]
};

fileName

类型:String

默认值:manifest.json

作用:fileName 传入的值作为输出的文件清单的名称。

webpack.config.js

module.exports = {
  plugins: [
    new WebpackManifestPlugin({
        fileName: 'manifest-filename.json' // manifest.json -> manifest-filename.json
    })
  ]
};

filter

类型:(file: FileDescriptor) => Boolean

默认值:undefined

作用:执行 filter 回调函数,返回 true 则 manifest.json 中包含 bundle 的映射关系,返回 false 则不包含此 bundle 映射关系。

webpack.config.js

module.exports = {
  ...
  plugins: [
    new WebpackManifestPlugin({
      filter: (file) => {
          return file?.name?.endsWith('.js') // manifest.json 中只包含 js 拓展名的文件
      }
    })
  ]
};

generate

类型:(seed: Object, files: FileDescriptor[], entries: string[]) => Object

默认值:undefined

作用:自定义修改生成的 manifest.json 中的键值对内容,generate 传入的函数返回值需要为对象。

webpack.config.js

module.exports = {
  ...
  plugins: [
    new WebpackManifestPlugin({
      generate: (seed, files, entries) => {
          console.log(seed, 'seed')
          console.log(files, 'files')
          console.log(entries, 'entries')
          return { 'main-index': 'index.js' }
      }
    }) // manifest.json 中内容 { "main-index": "index.js" }
  ]
};

map

类型:(file: FileDescriptor) => FileDescriptor

默认值:undefined

作用:自定义修改生成的 manifest.json 中的键值对内容。

webpack.config.js

module.exports = {
  ...
  plugins: [
    ...
    new WebpackManifestPlugin({
        map: (file) => {
            const fileName = file?.name;
            if (file?.name?.endsWith('.js')) {
                file.name = 'assets.' + fileName
            }
            return file
        }
    }) 
    // { "assets.main.js": "auto/main.js" ... }
  ]
};

publicPath

类型:String

默认值:webpack.config.js 中的 output.publicPath 值

作用:publicPath 传入的内容将添加到 manifest.json 对象的值前面。

webpack.config.js

module.exports = {
  ...
  plugins: [
    ...
     new WebpackManifestPlugin({
        // {
        //   "index.js": "./dist-public/index.js",
        //   "1.png": "./dist-public/2b2001bb98465dd14a87.png",
        //   "index.html": "./dist-public/index.html"
        // }
        publicPath: './dist-public'  
      })
  ]
};

serialize

类型:(Object) => string

默认值:undefined

作用:格式化 manifest.json 中的内容。

webpack.config.js

module.exports = {
  ...
  plugins: [
    ...
    new WebpackManifestPlugin({
        serialize: (obj) => {
            console.log(obj)
            return JSON.stringify(obj) // {"index.js":"auto/index.js","1.png":"auto/2b2001bb98465dd14a87.png","index.html":"auto/index.html"}
        }
    })
  ]
};

总结

此章节演示了 WebpackManifestPlugin 的用法和包含的部分常用配置参数,通过 WebpackManifestPlugin 生成的资源清单可以让我们在项目中快速找到引用的依赖文件路径,这对于服务端渲染或微前端等将非常有用。

阅读量:1135

点赞量:0

收藏量:0