webpack与多页面( MPA)开发基础(二)

发布于:2024-04-26 ⋅ 阅读:(22) ⋅ 点赞:(0)

前言

还没看之前文章的同学,建议看下我写的前一篇文章:

遇到的问题

我在这篇文章中主要解决下面三个问题:

  • css自动注入
  • 移除打包过程中生成的空白JavaScript
  • 图片处理

简单了解webpack如何处理文件

webpack的入口文件主要是JavaScript(之后会称js)文件,入口文件作为构建其内部依赖图的起点,这些入口文件通常包含了应用程序的主要逻辑或者是一个引导应用程序启动的脚本。对于图片、css/less/scss、html、字体等文件需要使用对应的loader去处理。

css自动注入

根据上面的知识,我们知道webpack是以js为入口,那么我们就可以在js中引入相关css/scss/less文件,再使用对应的loader去处理。

在开始书写代码之前,您有必要知道目前的项目结构:

├── package-lock.json
├── package.json
├── src
│   ├── css         // css文件夹
│   ├── images      // 图片文件夹
│   ├── index.html  // 静态页面
│   ├── js          // js文件夹
└── webpack.config.js

这里以scss文件为例,来书写相关的代码,如果可以的话,烦请您按照下面的步骤亲自做一下:

  1. 在css文件夹中新建一个scss文件,比如test.scss
  2. 在js文件夹中新建一个js文件,比如test.js,并在test.js中引入刚刚建立的test.scss
import '../css/test.scss'
  1. 在src目录建立一个静态文件,比如test.html,您需要在html中填写基本的结构,这里仅给一个简单示例:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>测试</title>
</head>
<body>
    <div>我是测试页面</div>
</body>
</html>
  1. 在webapck.config.js配置以下代码:
module.exprots = {
   // ... 
   entry: {
     'test': './src/js/test.js', // 新增的代码
   },

   plugins: [
     // ...
     new htmlWebpackPlugin({ // 新增的代码
       inject: 'body',
       template: './src/test.html',
       filename: 'test.html',
       chunks: ['test'],
     }),
     
     new MiniCssExtractPlugin({ // 新增代码,提取css为独立的文件
      // 定义输出文件名和路径
      filename: 'css/[name].[contenthash].css',
     }),
   ],
   
   module: {
     rules: [
       // ...
       {
         test: /\.scss$/,
         use: [
           MiniCssExtractPlugin.loader,
           'css-loader',
           {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [
                  require('autoprefixer')()
                ]
              }
            }
          },
          'sass-loader'
         ]
       },
     ]
   },
   // ...
}
  1. 执行npm run build,看是否可以成功打包。如果成功,dist文件夹中的test.html的head以及body中应该分别插入了css以及js。

移除打包过程中生成的空白JavaScript

查看下dist/js文件下的test.js的内容,可以看到内容是空的。这是因为我们这个js仅仅引入了scss文件,没有写其他js的代码,在scss被loader处理完成之后,js的内容就变成了空的。

那么是否有插件处理这种类型的文件呢?我估计开源社区是有的,但是本人见识有限,未能找到对应处理这种文件的plugin,所以自己写了个plugin来处理,大家可以参考下:

module.exports = class RemoveEmptyScriptsPlugin {
    apply(compiler) {
      compiler.hooks.compilation.tap('RemoveEmptyScriptsPlugin', (compilation) => {
        // HtmlWebpackPlugin版本4和5的兼容处理
        let hook = compilation.hooks.htmlWebpackPluginAlterAssetTags;
        console.log('* hook', hook);
        if (!hook) {
          const [HtmlWebpackPlugin] = compiler.options.plugins.filter((plugin) => plugin.constructor.name === 'HtmlWebpackPlugin');
          if (HtmlWebpackPlugin) {
            hook = HtmlWebpackPlugin.constructor.getHooks(compilation).alterAssetTagGroups;
          }
        }
  
        if (hook) {
          hook.tapAsync('RemoveEmptyScriptsPlugin', (data, cb) => {
            // 过滤掉空的JS文件
            data.headTags = data.headTags.filter(tag => !this.isEmptyScript(tag, compilation));
            data.bodyTags = data.bodyTags.filter(tag => !this.isEmptyScript(tag, compilation));
            console.log('* data.bodyTags', data.bodyTags);
            cb(null, data);
          });
        }
      });
  
      compiler.hooks.emit.tapAsync('RemoveEmptyScriptsPlugin', (compilation, callback) => {
        Object.keys(compilation.assets).forEach((asset) => {
          if (this.isEmptyAsset(compilation.assets[asset])) {
            // 删除相应的资源
            delete compilation.assets[asset];
          }
        });
        callback();
      });
    }
  
    isEmptyScript(tag, compilation) {
      if (tag.tagName !== 'script' || !tag.attributes || !tag.attributes.src) {
        // 如果不是script标签或者没有src属性,直接返回false
        return false;
      }
      
      const assetName = tag.attributes.src.split('/').pop(); // 获取资源名称
      let asset = null;
      console.log('* compilation:', compilation.assets);
      for(let itm in compilation.assets) {
        if (compilation.assets.hasOwnProperty(itm) && itm.endsWith(assetName)) {
          asset = compilation.assets[itm];
          break;
        }
      }
      console.log('* asset', asset);
      return asset && this.isEmptyAsset(asset); // 确保资源存在并调用isEmptyAsset检查
    }
  
    isEmptyAsset(asset) {
      // 首先检查资源是否存在以及资源是否有source方法
      return asset && asset.source && asset.source().length === 0;
    }
  }

在项目根目录新建plugins文件夹,并在其中新建RemoveEmptyScriptsPlugin.js,将上述代码拷入其中。 在webpack.config.js中引入新建的插件文件,并将下面代码拷入plugins中:

module.exports = {
  // ...
  plugins: [
    // ...
    new RemoveEmptyScriptsPlugin(),// 放入htmlWebpackPlugin之后
  ]
}

如果上述代码使用有问题,欢迎大家留言交流。

图片处理

不像vue、react这种js框架/库,我们可以直接引入图片资源,后续交给webpack打包即可。在静态页面开发中,我们图片一般在html文件中直接引入。在我的实践中,总结出了两种方法可以解决这个问题,下面逐个介绍。

使用CopyWebpackPlugin插件

这里不过多介绍该插件,大家可以搜索该插件的详细用法,在webpack.config.js的plugins中加入该插件即可:

module.exports = {
  // ...
  plugins: [
    // ...
    new CopyWebpackPlugin({
        patterns: [
          {
            from: './src/images/', // 从哪里拷贝
           to: 'images2', // 拷贝到哪里去
          },
        ],
    }),
  ]
}

以上面CopyWebpackPlugin的为例,我们在html中使用相对路径引入图片。这里举个简单的示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>测试</title>
</head>
<body>
    <div>我是测试页面</div>
    <div>
        <!--注意,这里的图片路径是被拷贝之后的路径-->
        <img src="images2/about-1.png" />
    </div>
</body>
</html>

html-loader

html-loader 在 webpack 构建过程中的作用是处理 HTML 文件。它将 HTML 文件作为字符串导入到你的 JavaScript 文件中。此外,html-loader 还会处理 HTML 文件中的图片和链接,并将它们转换为需要的模块依赖,这样就可以配合其他 loader(如 file-loaderurl-loaderasset/resource)一起使用,处理这些资源。这里就简单介绍下与asset/resource配合处理图片。

在webpack.config.js的module中增加下面代码:

module.exprots = {
  //...
  module: {
    rules: [
      // ...
      {
        test: /\.html$/,
        use: [
          {
            loader: 'html-loader',
            options: {
              minimize: false, // 不压缩
              esModule: false, // 不使用esModule
            }
          }
        ]
      },
      /** 处理图片 **/
      {
        test: /\.(png|jpg|jpeg|gif|svg)$/,
        exclude: /node_modules/,
        type: 'asset/resource',
        generator: {
          filename: 'images/[name][ext]', // 生成的文件名
        },
      },
    ]
  }
}

以上面CopyWebpackPlugin的为例,我们在html中使用相对路径引入原始路径的图片。这里举个简单的示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>测试</title>
</head>
<body>
    <div>我是测试页面</div>
    <div>
        <!--注意:这里是原始的图片路径-->
        <img src="./images/about-1.png" />
    </div>
</body>
</html>

总结

这篇文章简单介绍并解决了多页面开发中遇到的三个问题:

  • css自动注入
  • 移除打包过程中生成的空白JavaScript
  • 图片处理

由于本人能力、知识有限,难免不了有疏漏的地方,欢迎在评论区留言讨论。