提示:在继续阅读之前,请注意此文章最后更新于 1792 天前,其中的部分内容可能已经无效或过时。

q使用webpack chain来设置webpack,使用方法如下:

import Config = requirer('webpack-chain')

// 声明一个Config实例,之后在实例上设置相关内容
const _config=new Config();

设置通用功能

// _setGeneral
_config.devtool('source-map')
    .performance.hints(false)
    .end() //返回最近的状态,这里返回config,即后面继续在config上进行设置
    .set('fs', 'empty') // 设置一些暂不知道是什么作用的变量
    .set('net', 'empty')
    .set('tls', 'empty')
    .set('child_process', 'empty');

对应的webpack配置

module.exports={
    devtool: 'source-map',
    performance: {
        hints: 'error' // 打包文件超过25KB报错
    },
    fs: 'empty',
    net: 'empty',
    tls: 'empty',
    child_process: 'empty',
}

设置入口

// _setEntry
_config.entry('index').add('src/index.js');
// webpack
module.export={
    // 上面的配置...
    entry: {
        index: ['src/index.js']
    }
}

设置出口

// _setOutput
_config.output.path(getProject('dist')).when(
    isSite(),
    (config)=>{
        config.chunkFilename('static/js/[name].[fullhash:8].js');
        config.publicPath('./');
        config.filename('static/js/[name].[fullhash:8].js');
        return config
    },
    (config)=>{
        config.filename('index.js');
        config.libraryTarget('commonjs');
        return config
    }
)

when接受三个参数,第一个是一个布尔值,后面是两个回调。true执行第一个回调,false执行第二个回调
对应webpack

// 对应第一个回调
module.exports={
    // ...
    output: {
        chunkFilename: 'static/js/[name].[fullhash:8].chunk.js',
        filename: 'static/js/[name].[fullhash:8].js',
        publicPath: './',
        pathinfo: true
    }
}

设置resolve

_config.resolve.modules
    .add(getProjectPath('node_modules'))
    .add('node_modules')
    .end() // 回到resolve
    .extensions.merge(['.js', '.ts', '.tsx', '.json'])
    .end()
    .alias.set('src', getProjectPath('src'))
    .set('theme', getProjectPath('src/ui/theme'))

设置了resolve的modulesextensionsalias。对应的webpack配置:

module.exports = {
    // ...
    resolve: {
        modules: ['src', path.join(cwd, 'node_modules'), 'node_modules'],
        extensions: ['.js', '.ts', '.tsx', '.json'],
        alias: {
            src: path.join(cwd, 'src'),
            theme: path.join(cwd, 'src/ui/theme')
        }
    }
}

resolve.modules

设置modules会改变模块解析路径,会根据给定的数组来解析。比如这里的解析顺序为src>>当前目录下的node_modules>>node_modules。具体就是,比如src下有个文件

// test-module.js
module.exports={}

在另一个文件

const tm=require('test-module');

这样打包的时候就会直接解析到这个模块

resolve.extensions

按顺序解析这些后缀名,能引入文件的时候不带扩展。node.js默认的解析是js > json > node。此项会覆盖默认数组,如果要访问默认的,在数组里添加...

resolve.alias

为常用路径添加别名,比如在src/utils有很多常用模块,就可以添加一个配置

module.exports={
    resolve:{
        alias:{
            utils: path.resolve(__dirname,'src/utils');
        }
    }
}

这样在使用的时候,就可以使用别名:

import { getProjectPath } from 'utils/getProjectPath'

参考: resolve

plugins

yarn add copy-webpack-plugin \
add-asset-html-webpack-plugin \
case-sensitive-paths-webpack-plugin \
clean-webpack-plugin \
duplicate-package-checker-webpack-plugin \
extract-text-webpack-plugin \
fork-ts-checker-webpack-plugin \
html-webpack-plugin \
mini-css-extract-plugin \
progress-bar-webpack-plugin \
script-ext-html-webpack-plugin \
terser-webpack-plugin \
tslint-plugin-prettier \
uglifyjs-webpack-plugin \
webpack-manifest-plugin \
webpack-filter-warnings-plugin \
react-dev-utils \
webpackbar \
progress-bar-webpack-plugin
 -D

copy-webpack-plugin

看说明这应该是一个拷贝一些文件的插件,具体的后续在研究。

const paths = {
    publicPath: path.join(__dirname, '../../../public/'),
    appHtml: path.join(__dirname, '../../../public/index.html'),
};
_config.plugin('CopyWebpackPlugin').use(
    new CopyWebpackPlugin([
        `${paths.publicPath}/style.css`,
        `${paths.publicPath}/favicon.ico`,
        `${paths.publicPath}/loading.svg`,
        `${paths.publicPath}/loading.gif`,
    ])
)
module.exports = {
    plugin: [
        new CopyWebpackPlugin({
            patterns: [
                {
                    from: '/root/ENVIRONEMNT/node/GM_PRO/q/packages/q-scripts/public/style.css',
                },
                '/root/ENVIRONEMNT/node/GM_PRO/q/packages/q-scripts/public/favicon.ico',
                '/root/ENVIRONEMNT/node/GM_PRO/q/packages/q-scripts/public/loading.svg',
                '/root/ENVIRONEMNT/node/GM_PRO/q/packages/q-scripts/public/loading.gif'
            ]
        }),
    ]
}

会转化成一个数组,数组里是一系列对象,对象包含fromto等参数。如果直接使用字符串,默认为from

html-webpack-plugin

根据模板生成html的插件

const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path')

_config.plugin('HtmlWebpackPlugin').use(
    new HtmlWebpackPlugin({
        inject: true, // 默认就是true,可以不填。会决定插入webpack打包的js的位置。可选值: true|'head'|'body'|false
        template: path.resolve(__dirname, '../index.html'),
        minify: {
            removeRedundantAttributes: true,    // 如果匹配HTML4.01中的默认值,则优化掉
            collapseWhitespace: true,   // 去除多余空格换行以优化文档树,SCRIPT, STYLE, PRE or TEXTAREA不会优化
            useShortDoctype: true,  // 将非HTML 5 Doc(如hmlt 4.01)换成更短的HTML5 Doctype,这会对文档造成影响
            removeEmptyAttributes: true, // 移除标签中为空的属性
            removeStyleLinkTypeAttributes: true,// Remove type="text/css" from style and link tags. Other type attribute values are left intact(完好无损)
            keepClosingSlash: true, // 保持单元素尾部斜杠,如 <br />不会被优化为<br>
            removeComments: true,   // 去除注释
            minifyCSS: true,
            minifyJS: true,
            minifyURLs: true, // 压缩CSS,JSS,URLs 
        },
        title: 'xzdryのtitle' // 需要在index.html里写占位符
    })
)

对应的webpack配置格式一样,这里就不贴了。相关的参考文档:

效果:

<!-- removeRedundantAttributes的效果 -->

<input type="text"> <!-- 优化为<input> -->
<input type="password"><!-- 不会优化 -->

<!-- removeRedundantAttributes的效果 -->

<div> x x x </div>
<textarea name="" id="" cols="30" rows="10">

 a a a
    dsad
</textarea>

<!-- useShortDoctype效果 -->

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<!-- useShortDoctype效果 -->

<!-- removeEmptyAttributes效果 -->

<p id="" STYLE="" title=""></p>

<!-- removeEmptyAttributes效果 -->

<!-- removeStyleLinkTypeAttributes -->

<link rel="stylesheet" href="" type="text/css">

<!-- removeStyleLinkTypeAttributes -->

<!-- keepClosingSlash -->

<br />

<!-- keepClosingSlash -->

<!-- title的效果 -->

<title><%= htmlWebpackPlugin.options.title %></title>

<!-- title的效果 -->

优化后

<input> <input type="password"><div>x x x</div><textarea name="" cols="30" rows="10">

 a a a
    dsad
</textarea><!doctype html><p></p><link rel="stylesheet" href=""><br/><title>xzdry的HtmlWbpackPlugin</title>

mini-css-extract-plugin

将css提取到单独的文件中,为每个包含css的js文件创建一个css文件,需要配合loader一起使用

_config.plugin('MiniCssExtractPlugin').use(new MiniCssExtractPlugin({
    filename: 'static/css/[name].[contenthash:8].css'
}))

对应webpack配置

module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/i,
                use: [MiniCssExtractPlugin.loader, 'css-loader']
            }
        ]
    },
    plugin: [
        new MiniCssExtractPlugin({
            filename: 'static/css/[name].[contenthash:8].css'
        })
    ]
}

webpack-manifest-plugin

此插件不在官网插件列表里。参考webpack-manifest-plugin
会生成一个清单,比如index.js对应的打包后的带有hash的js。虽然暂时不知道这个清单的作用

_config.plugin('WebpackManifestPlugin').use(new WebpackManifestPlugin({
    filename: 'asset-manifest.json'
}))

ignore-plugin

webpack内置插件,忽略第三方包的插件,比如moment只用到了中文,打包的时候排除掉非中文。参考webpack IgnorePlugin。未验证

_config.plugin('IgnorePlugin').use(new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/))

watch-ignore-plugin

watch mode下忽略监视指定的文件

_config.plugin('WatchIgnorePlugin').use(new webpack.WatchIgnorePlugin([/\.js$/, /\.d\.ts$/]))

对应webpack

module.exports={
    plugin: [
        new webpack.WatchIgnorePlugin({
            paths: [/\.js$/, /\.d\.ts$/]
        })
    ]
}

webpack-filter-waring-plugin

忽略指定的webpack waring,可以传正则或正则数组。官网还有字符串和函数的示例,实测不行,原因未知。参考:webpack-filter-warnings-plugin

_config.plugin('export').use(
    new FilterWarningsPlugin({
        exclude: /export .* was not found in/,
    }),
);
_config.plugin('export2').use(
    new FilterWarningsPlugin({
        exclude: /.* is not exported from/,
    }),
);
_config.plugin('multiple versions').use(
    new FilterWarningsPlugin({
        exclude: /ansi-regex|strip-ansi/,
    }),
);
// 配合performance.hints开启警告使用,把开启的警告在过滤掉
_config.plugin('my test').use(
    new FilterWarningsPlugin({
        exclude: /limit/,
    }), 
)

webpack

module.exports = {
    plugin: [
        // webpack-chain会生成4个插件,合成一个正则数组传过去也可行
        new FilterWarningsPlugin({
            exclude: /limit/
        }),
        new FilterWarningsPlugin({
            exclude: /export .* was not found in/
        }),
        new FilterWarningsPlugin({
            exclude: /.* is not exported from/
        }),
        new FilterWarningsPlugin({
            exclude: /ansi-regex|strip-ansi/
        })
    ]
}

webpackbar

win下用progress-bar-webpack-plugin,linux下用webpackbar
这里贴一下WebpackBar的配置

if (!isCIEnvironment()) {
    if (process.platform === 'win32') {
        _config.plugin('progress').use(require('progress-bar-webpack-plugin'));
    } else {
        _config.plugin('progress').use(require('webpackbar'), [
            {
                color: 'green',
                reporters: ['fancy'],
            },
        ]);
    }
}
// webpack
module.exports={
    plugin:[
        // 此plugin还提供了reporter生命周期钩子函数
        new WebpackBar({
            color: '#9900ff',
        }),
        new MyPlugin() 
    ]
}

这里使用了一个自定义的Plugin来使打包时间长一些看到进度条的细节,plugin实现如下:

class MyPlugin {
    apply(compiler) {
        compiler.hooks.emit.tapAsync('My Plugin', async (stats) => {
            await new Promise(res => setTimeout(res, 20000))
            console.log('this is my plugin');
        });
    }

}
module.exports = MyPlugin;