webpack笔记(4)-进阶用法三

99 阅读1分钟

打包库和组件

题目:实现一个大整数加法库的打包,要求:1.需要打包压缩版和非压缩版;2.支持AMD/CJS/EMS模块引入,也可以通过script标签形式引入

支持模式引入语法
ES moduleimport * as largeNumber from 'large-number';
largeNumber.add('999','1');
支持CJSconst largeNumbers = require('large-number');
largeNumber.add('999','1');
支持AMDrequire(['larger-bunber'],function(larger-bunber){
 largeNumber.add('999','1');
});
支持script<script src="https://xxx/large-number"></script>
<script
largeNumber.add('999','1');//global variable
window.largeNumber.add('999','1');//挂在window上

实现步骤
1.webpack文件配置,其中包含库打包的入口,出口,对外暴露的名字等

const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
    entry: {
        'large-number': './src/index.js',
        'large-number.min': './src/index.js'
    },
    output: {
        filename: '[name].js',
        // library为库暴露出去的名称
        library: 'largeNumber',
        // 导出结果的形式
        libraryTarget: 'umd',
        //这里需要声明,不然调用地方需要 再.default才能访问
        libraryExport: 'default'
    },
    mode: 'none',
    optimization: {
        minimize: true,
        minimizer: [
            new TerserPlugin({
                // UglifyJS 在 ES6 代码压缩上做的不够好,代码也不再维护,terser是UglifyJS的分支,一直有维护。
                include: /\.min\.js$/
            })
        ]
    }
}

2.配置不同环境的入口-跟目录下的index

// package.json的main字段为index.js
if (process.env.NODE_ENV === 'production') {
    module.exports = require('./dist/large-number.min.js');
} else {
    module.exports = require('./dist/large-number.js');
}

3.各种libraryTarget配置详细解读

juejin.cn/post/700434…
webpack5用法:tehub.com/a/9KvNfaKdt…

实现ssr打包

实现步骤
1.创建服务端的编译文件webpack.ssr.js

// 1.output需要增加libraryTarget
output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name]-server.js',
        // SSR的时候Node.js需要去require我们生成的这个js文件,而Node.js的包管理遵从commonjs规范,因此构建生成的这个js文件需要兼容commonjs。
        // 否则无法再 Node.js 里面进行 require。
        libraryTarget: 'umd'
    },
// 2.HtmlWebpackPlugin入口文件需要修改一下

2.package.json中增加启动命令

"build:ssr": "webpack --config webpack.ssr.js"

3.创建server目录,目录下创建index.js,作为服务端的编译文件

// 增加一个hack
if(typeof window === 'undefined') {
    global.window = {};
}

const fs = require('fs');
const path = require('path');
const express = require('express');
// react组件转字符串
const {renderToString} = require('react-dom/server');
// 需要渲染的组件
const SSR = require('../dist/search-server');
const template = fs.readFileSync(path.join(__dirname, '../dist/search.html'), 'utf-8');
const data = require('./data.json');

const server = port => {
    const app = express();

    app.use(express.static('dist'));
    app.get('/search', (req, res) => {
        const html = renderMarkup(renderToString(SSR));
        res.status(200).send(html);
    });

    app.listen(port, () => {
        console.log('running at' + port)
    });
}

server(process.env.PORT || 3000);

const renderMarkup = str => {
    const dataStr = JSON.stringify(data)
    return template.replace('<!--HTML_PLACEHOLDER-->', str)
        .replace('<!--INITIAL_DATA_PLACEHOLDER-->', `<script>window.__initial_data=${dataStr}</script>`)
}

// 该当时解决样式问题比较曲折
// // 返回一个html页面,用一个模版字符串包装一下
// const renderMarkup = str => {
//     return `<!DOCTYPE html>
//     <html lang="en">
//     <head>
//         <meta charset="UTF-8">
//         <meta name="viewport" content="width=device-width, initial-scale=1.0">
//         <meta http-equiv="X-UA-Compatible" content="ie=edge">
//         <title>Document</title>
//     </head>
//     <body>
//         <div id="root">${str}</div>
//     </body>
//     </html>`;
// }

4.增加服务端入口文件search/index-server.js

'use strict';

// 因为是服务端,所以需要commonjs
const React = require('react');
const logo = require('./images/logo.png');
require('./search.less');

class Search extends React.Component {
    constructor() {
        super(...arguments);
        this.state = {
            Text: null
        }
    }
    render() {
        const {Text} = this.state;
        return <div className="search-text">
            搜索文字的a<img src={ logo } onClick={this.loadComponent.bind(this)}/>
        </div>;
    }
}

// server不能使用reactDOM.render进行渲染
module.exports = <Search />;

5.html中加入占位符用于替换:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="root"><!--HTML_PLACEHOLDER--></div>
    <!--INITIAL_DATA_PLACEHOLDER-->
</body>
</html>

优化构建日志

// 1.配置stats,生产环境配置直接配置在module中,dev环境配置在devServer中
stats: 'errors-only'
// 2.安装插件:friendly-errors-webpack-plugin,放在plugins中
plugins: [
    new FriendlyErrorsWebpackPlugin()
]

构建异常和中断处理

plugins: [
    function() {
        this.hooks.done.tap('done', stats => {
            if (stats.compilation.errors 
                && stats.compilation.errors.length
                && process.argv.indexOf('--watch') == -1
            ) {
                // 添加错误日志
                console.log('build error')
                // 更改错误码
                process.exit(1);
            }
        })
    }
]

实际未生效。。。待排查 image.png