实战Webpack插件和加载器

280
2024-05-31 14:36
6 个月前

Webpack-plugin和Webpack-loader的重要性

这两个东西是Webpack构建过程的核心扩展点,他们在构建项目具有重要的作用,下面我来列举一些例子来说明一下

插件(plugin)的重要性

构建优化和代码处理:插件能够优化构建过程,包括代码压缩、文件合并、分离公共代码等,从而提高项目的性能和加载速度。

资源管理和版本控制:插件可以管理和控制项目中的资源,例如生成文件的哈希值作为版本号,有效地解决浏览器缓存问题。

自动化任务和流程:插件可以执行自动化任务,例如自动部署、清理构建目录、生成统计信息等,简化开发流程,提高开发效率。

环境变量注入:插件可以根据不同的构建环境注入不同的环境变量,帮助项目实现不同环境下的配置切换

开发体验的提升:插件支持模块热替换(HMR),在开发过程中实现实时更新,提高开发体验,减少开发者等待时间

加载器(loader)的重要性

文件类型的处理:加载器允许 Webpack 处理项目中各种类型的文件,例如处理 CSS、处理图片、处理数据文件等,使得这些文件能够被项目正确引用和使用

代码预处理:加载器可以对代码进行预处理,例如将新语法转为浏览器可识别的语法(Babel Loader)、将 TypeScript 转为 JavaScript(TypeScript Loader),帮助项目更好地适应各种技术栈。

模块化的处理:加载器将项目中的各种资源文件视为模块,使得这些资源文件能够被正确加载和引用,实现模块化开发。

样式处理:加载器能够处理样式文件,包括将 CSS 转为模块化可用的格式、将样式注入到 DOM 中等,实现样式的模块化和动态加载。

项目中的资源处理:加载器使得开发者能够更自由地处理和使用项目中的各类资源文件,例如在构建时对图片进行压缩、对数据文件进行处理等。

为什么要有自定义的需求

我们在平常的开发中,在项目立项到项目开发之前我,我们就要根据公司现有的环境(后端环境,部署环境)根据实际情况来实现项目打包。这个时候在我们项目团队在业务开发之前应该把项目的上线流程整理完毕,例如静态资源上传到哪里,是否需要dll包,外部的js怎么引入,这个时候我们一般会使用webpack或者vite去构建。下面会根据一下实际例子说明如何利用webpack实现一下常用的功能

引入外部js

//添加新的文件
import fs from 'fs';
import path from 'path';
import { Compiler, Compilation } from 'webpack';
const readFile = (path: string) => {
    return fs.readFileSync(path);
};
// 读取要引入的文件
const data = readFile(path.join(__dirname, '../libs/libs.js')).toString();
type IMyPluginOptions = Record<string, any>;
class MyPlugin {
    constructor(options: IMyPluginOptions) {
        console.log(options);
    }
    apply(compiler: Compiler) {
        compiler.hooks.emit.tap('fileList', (compilation: Compilation) => {
            const assets = compilation.assets;

            Object.keys(assets)
                .filter(item => {
                    return item.indexOf('.html') > -1;
                })
                .forEach(item => {
                    const file = item.split('/')[0];
                    // 输出到静态资源
                    // @ts-ignore
                    assets[path.join(file, '../public/libs/libs.js')] = {
                        source() {
                            return data;
                        },
                        size() {
                            return data.length;
                        }
                    };
                });
        });
    }
}
export default MyPlugin;

sad上面的这个插件就是把要添加一个静态文件到打包的目录里面去

往HTML添加自己的内容

// 往生成的html文件的head标签内插入自己需要的内容
import { Compiler, Compilation } from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
class ReplaceHeadPlugin {
    context: string;
    constructor(options: { context: string }) {
        this.context = options.context;
        // 获取输入内容
        console.log(options);
    }
    apply(compiler: Compiler) {
        const context = this.context;

        compiler.hooks.compilation.tap(
            'replaceHeadPlugin',
            (compilation: Compilation) => {
                HtmlWebpackPlugin.getHooks(
                    compilation
                ).afterTemplateExecution.tapAsync(
                    'replaceHeadPlugin',
                    (htmlData, cb) => {
                        let html = htmlData.html;
                        const append = this.context || '';
                        html = html.replace(
                            /<head>([\w\W]*)<\/head>/,
                            `<head>$1${append}</head>`
                        );
                        htmlData.html = html.replace(
                            /(\r\n|\r|\n)|(\t)|(\t\W+)/g,
                            ''
                        );
                        cb(null, htmlData);
                    }
                );
            }
        );
    }
}
export default ReplaceHeadPlugin;

使用例子

//添加一个静态文件,这个文件可能是自己拷贝或者外部依赖,或者自己写的dll
new ReplaceHeadPlugin({
     context: '<script src="/public/libs/libs.js"></script>'
})
//往头部添加百度统计代码,下面的id可以用process.env.BAIDU_ANALYTICSID来根据环境变量打包
new ReplaceHeadPlugin({
     context: '<script>
var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?id";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script>
'
})
//添加公共样式
new ReplaceHeadPlugin({
     context: '<style>
body{
  background:red;
}
</style>
'
})

使用Loader突破google浏览器六个并发请求的限制

// 自定义loader实现变量替换,使用异步loader的方式
//不能使用箭头函数
const customCssLoader = function (this: any, content: any) {
    const callback = this.async();
    asyncLoader(content).then(res => {
        callback(null, res);
    });
};
const baseUrl = [
    'https://test.image.com',
    'https://test2.image.com',
    'https://test3.image.com'
];
// 获取0-2的随机数
const random = () => {
    return Math.floor(Math.random() * 3);
};
//myCustomStatic是自己设置的规则
const asyncLoader = (content: any) => {
    return new Promise(resolve => {
        content = content.replaceAll('myCustomStatic', baseUrl[random()]);
        resolve(content);
    });
};
export default customCssLoader;

使用示例

{
                        test: /\.s[ac]ss$/,
                        use: [
                            isDev
                                ? 'style-loader'
                                : MiniCssExtractPlugin.loader,
                            {
                                loader: 'css-loader'
                            },
                            'postcss-loader',
                            {
                                loader: 'sass-loader',
                                options: {
                                    implementation: require('sass')
                                }
                            },
                            {
                                loader: path.join(
                                    __dirname,
                                    './CustomCssLoader.ts'
                                )
                            }
                        ]
                    }

我的css文件就像这么写

.div-container .div-btn {
    width: 100px;
    height: 30px;
    border-radius: 4px;
    text-align: center;
    line-height: 30px;
    font-size: 14px;
    border: 1px solid #108EE9;
    transition: all;
    background-image: url("myCustomStatic/image/123");
}
#app{
    width:100%;
    color:#333;
    font-size:14px;
    line-height: 22px;
    font-weight: 500;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height:100vh;
    background-image: url("myCustomStatic/image/456");
}
.site-layout{
    transition: all 0.5s;
    background-image: url("myCustomStatic/image/7809");
}

打包后查看一下打包的文件