玩转webpack(03):webpack进阶使用

news2024/11/18 17:48:43

一、自动清理构建目录

避免构建前每次都要手动删除dist

使用 clean-webpack-plugin(默认删除output指定的输出目录)

(1)依赖安装 

npm i clean-webpack-plugin -D

(2)使用 --- webpack.prod.js

const CleanWebpackPlugin = require('clean-webpack-plugin'); // 3.0版本以前的写法
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); // 3.0版本以后的写法


// ....

    plugins: [
        // ...
        new CleanWebpackPlugin()
    ],

二、自动补齐CSS3前缀

Trident(-ms)        Geko(-moz)        Webkit(-webkit)         Presto(-o)
.box {
    -moz-border-radius: 10px;
    -webkit-border-radius: 10px;
    -o-border-radius: 10px;
    border-radius: 10px;
}

使用 autoprefixer 添加厂商前缀。 

(1)依赖安装

npm install --save-dev postcss-loader postcss
npm install --save-dev autoprefixer

(2)使用(webpack.prod.js)

            {
                test: /\.css$/,
                // use数组中是从后往前调用的
                use: [
                    MiniCssExtractPlugin.loader,
                    {
                        loader: "css-loader",
                        options: {
                            importLoaders: 1,
                            // 0 => no loaders (default);
                            // 1 => postcss-loader;
                            // 2 => postcss-loader, sass-loader
                        },
                    },
                    {
                        loader: 'postcss-loader',
                        options: {
                            postcssOptions: {
                                plugins: [
                                    [
                                        // 'postcss-preset-env',
                                        'autoprefixer',
                                        {
                                            // 其他选项
                                            overrideBrowserslist: [
                                                'Android 4.1',
                                                'iOS 7.1',
                                                'Chrome > 31',
                                                'ff > 31',
                                                'ie >= 8',
                                                '>0.01%'
                                                //'last 10 versions', // 所有主流浏览器最近2个版本
                                            ]
                                        },
                                    ],
                                ],
                            },
                        },
                    }
                ]
            },

三、移动端px自动转换为rem

px2rem-loader

lib-flexible库,页面渲染时动态计算根元素的font-size值

(1)依赖安装

npm i px2rem-loader -D

npm i lib-flexible -S

(2)使用

修改webpack配置

{
    test: /\.css$/,
    // use数组中是从后往前调用的
    use: [
        MiniCssExtractPlugin.loader,
        {
            loader: "css-loader",
            options: {
                importLoaders: 1,
                // 0 => no loaders (default);
                // 1 => postcss-loader;
                // 2 => postcss-loader, sass-loader
            },
        },
        {
            loader: 'postcss-loader',
            options: {
                postcssOptions: {
                    plugins: [
                        [
                            // 'postcss-preset-env',
                            'autoprefixer',
                            {
                                // 其他选项
                                overrideBrowserslist: [
                                    'Android 4.1',
                                    'iOS 7.1',
                                    'Chrome > 31',
                                    'ff > 31',
                                    'ie >= 8',
                                    '>0.01%'
                                    //'last 10 versions', // 所有主流浏览器最近2个版本
                                ]
                            },
                        ],
                    ],
                },
            },
        },
        {
            loader: 'px2rem-loader',
            options: {
                remUnit: 75,
                remPrecesion: 8
            }
        }
    ]
},

修改reactTest.html(暂未实现静态资源内联,先copy  lib-flexible包的内容使用)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script type="text/javascript">

        ; (function (win, lib) {
            var doc = win.document;
            var docEl = doc.documentElement;
            var metaEl = doc.querySelector('meta[name="viewport"]');
            var flexibleEl = doc.querySelector('meta[name="flexible"]');
            var dpr = 0;
            var scale = 0;
            var tid;
            var flexible = lib.flexible || (lib.flexible = {});

            if (metaEl) {
                console.warn('将根据已有的meta标签来设置缩放比例');
                var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
                if (match) {
                    scale = parseFloat(match[1]);
                    dpr = parseInt(1 / scale);
                }
            } else if (flexibleEl) {
                var content = flexibleEl.getAttribute('content');
                if (content) {
                    var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
                    var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
                    if (initialDpr) {
                        dpr = parseFloat(initialDpr[1]);
                        scale = parseFloat((1 / dpr).toFixed(2));
                    }
                    if (maximumDpr) {
                        dpr = parseFloat(maximumDpr[1]);
                        scale = parseFloat((1 / dpr).toFixed(2));
                    }
                }
            }

            if (!dpr && !scale) {
                var isAndroid = win.navigator.appVersion.match(/android/gi);
                var isIPhone = win.navigator.appVersion.match(/iphone/gi);
                var devicePixelRatio = win.devicePixelRatio;
                if (isIPhone) {
                    // iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
                    if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
                        dpr = 3;
                    } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)) {
                        dpr = 2;
                    } else {
                        dpr = 1;
                    }
                } else {
                    // 其他设备下,仍旧使用1倍的方案
                    dpr = 1;
                }
                scale = 1 / dpr;
            }

            docEl.setAttribute('data-dpr', dpr);
            if (!metaEl) {
                metaEl = doc.createElement('meta');
                metaEl.setAttribute('name', 'viewport');
                metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
                if (docEl.firstElementChild) {
                    docEl.firstElementChild.appendChild(metaEl);
                } else {
                    var wrap = doc.createElement('div');
                    wrap.appendChild(metaEl);
                    doc.write(wrap.innerHTML);
                }
            }

            function refreshRem() {
                var width = docEl.getBoundingClientRect().width;
                if (width / dpr > 540) {
                    width = 540 * dpr;
                }
                var rem = width / 10;
                docEl.style.fontSize = rem + 'px';
                flexible.rem = win.rem = rem;
            }

            win.addEventListener('resize', function () {
                clearTimeout(tid);
                tid = setTimeout(refreshRem, 300);
            }, false);
            win.addEventListener('pageshow', function (e) {
                if (e.persisted) {
                    clearTimeout(tid);
                    tid = setTimeout(refreshRem, 300);
                }
            }, false);

            if (doc.readyState === 'complete') {
                doc.body.style.fontSize = 12 * dpr + 'px';
            } else {
                doc.addEventListener('DOMContentLoaded', function (e) {
                    doc.body.style.fontSize = 12 * dpr + 'px';
                }, false);
            }


            refreshRem();

            flexible.dpr = win.dpr = dpr;
            flexible.refreshRem = refreshRem;
            flexible.rem2px = function (d) {
                var val = parseFloat(d) * this.rem;
                if (typeof d === 'string' && d.match(/rem$/)) {
                    val += 'px';
                }
                return val;
            }
            flexible.px2rem = function (d) {
                var val = parseFloat(d) / this.rem;
                if (typeof d === 'string' && d.match(/px$/)) {
                    val += 'rem';
                }
                return val;
            }

        })(window, window['lib'] || (window['lib'] = {}));

    </script>
</head>

<body>
    <div id="root"></div>
</body>

</html>

四、静态资源内联

意义:

(1)代码层面

  • 页面框架的初始化脚本
  • 上报相关打点
  • css内联避免页面闪动

(2)请求层面:减少HTTP网络请求数

  • 小图片或者字体内联(url-loader)

1、HTML和JS内联

webpack5之前:raw-loader内联html 和js

webpack5:asset/source 替换 raw-loader

(1)新建meta.html

<meta charset="UTF-8">
<meta name="viewport" content="viewport-fit=cover,width=device-width,initial-scale=1,user-scalable=no">
<meta name="format-detection" content="telephone=no">
<meta name="keywords" content="now,now直播,直播,腾讯直播,QQ直播,美女直播,附近直播,才艺直播,小视频,个人直播,美女视频,在线直播,手机直播">
<meta name="name" itemprop="name" content="NOW直播—腾讯旗下全民视频社交直播平台"><meta name="description" itemprop="description" content="NOW直播,腾讯旗下全民高清视频直播平台,汇集中外大咖,最in网红,草根偶像,明星艺人,校花,小鲜肉,逗逼段子手,各类美食、音乐、旅游、时尚、健身达人与你24小时不间断互动直播,各种奇葩刺激的直播玩法,让你跃跃欲试,你会发现,原来人人都可以当主播赚钱!">
<meta name="image" itemprop="image" content="https://pub.idqqimg.com/pc/misc/files/20170831/60b60446e34b40b98fa26afcc62a5f74.jpg"><meta name="baidu-site-verification" content="G4ovcyX25V">
<meta name="apple-mobile-web-app-capable" content="no">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<link rel="dns-prefetch" href="//11.url.cn/">
<link rel="dns-prefetch" href="//open.mobile.qq.com/">

(2)webpack配置修改

        rules: [
            {
                resourceQuery: /raw/,
                type: 'asset/source',
            },
        ]

(3)修改reactTest.html

<!DOCTYPE html>
<html lang="en">
<head>
    <%= require('./meta.html?raw') %>
    <title>Document</title>
    <script type="text/javascript">
        <%= require('../node_modules/lib-flexible/flexible.js?raw') %>
    </script>
</head>
<body>
    <div id="root"></div>
</body>
</html>

2、CSS内联

方案一:借助style-loader

方案二:html-inline-css-webpack-plugin(推荐)

(1)方案一:

rules: [
      {
        test: /\.scss$/,
        use: [
          {
            loader: 'style-loader',
            options: {
              insertAt: 'top', // 样式插入到<head>
              singleton: true, //将所有的style标签合并成一个
            }
          },
          "css-loader",
          "sass-loader"
        ],
      },
    ]

 (2)方案二

npm i html-inline-css-webpack-plugin -D
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HTMLInlineCSSWebpackPlugin = require("html-inline-css-webpack-plugin").default;

module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      filename: "[name].css",
      chunkFilename: "[id].css"
    }),
    new HtmlWebpackPlugin(),
    new HTMLInlineCSSWebpackPlugin(),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader"
        ]
      }
    ]
  }
}

五、多页面应用打包通用方案 

(1)多页面应用(MPA)概念

每一次页面跳转的时候,后台服务器都会返回一个新的html文档,这种类型的网站也就是多页网站,也叫做多页应用

(2)多页没看打包基本思路

每个页面对应一个entry,一个html-webpack-plugin

缺点:每次新增或删除页面都要改webpack配置

(3)多页面打包通用方案

 动态获取entry 和设置 html-webpack-plugin数量

利用glob.sync

(4)实现

npm i glob -D
'use strict'

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HTMLInlineCSSWebpackPlugin = require("html-inline-css-webpack-plugin").default;
const glob = require('glob');

const setMPA = () => {
    const entry = {};
    const htmlWebpackPlugins = [];

    const entryFiles = glob.sync(path.join(__dirname, './src/*/index.js'));
    console.log('entryFiles', entryFiles);
    Object.keys(entryFiles).map((index) => {
        const entryFile = entryFiles[index];
        // console.log(entryFile);
        const match = entryFile.match(/src\/(.*)\/index\.js/);
        const pageName = match && match[1];
        entry[pageName] = entryFile;
        htmlWebpackPlugins.push(
            new HtmlWebpackPlugin({
                template: path.join(__dirname, `src/${pageName}/index.html`),
                filename: `${pageName}.html`,
                chunks: [pageName],
                inject: true,
                minify: {
                    html5: true,
                    collapseWhitespace: true,
                    preserveLineBreaks: false,
                    minifyCSS: true,
                    minifyJS: true,
                    removeComments: false
                }
            }),
        );
    })
    return {
        entry,
        htmlWebpackPlugins
    }
}
const { entry, htmlWebpackPlugins } = setMPA();

module.exports = {
    entry,
    output: {
        path: path.join(__dirname, 'dist'),
        filename: '[name]_[chunkhash:8].js'
    },
    mode: 'production',
    module: {
        rules: [
            {
                resourceQuery: /raw/,
                type: 'asset/source',
            },
            {
                test: /\.js$/,
                use: 'babel-loader'
            },
            {
                test: /\.css$/,
                // use数组中是从后往前调用的
                use: [
                    MiniCssExtractPlugin.loader,
                    {
                        loader: "css-loader",
                        options: {
                            importLoaders: 1,
                            // 0 => no loaders (default);
                            // 1 => postcss-loader;
                            // 2 => postcss-loader, sass-loader
                        },
                    },
                    {
                        loader: 'postcss-loader',
                        options: {
                            postcssOptions: {
                                plugins: [
                                    [
                                        // 'postcss-preset-env',
                                        'autoprefixer',
                                        {
                                            // 其他选项
                                            overrideBrowserslist: [
                                                'Android 4.1',
                                                'iOS 7.1',
                                                'Chrome > 31',
                                                'ff > 31',
                                                'ie >= 8',
                                                '>0.01%'
                                                //'last 10 versions', // 所有主流浏览器最近2个版本
                                            ]
                                        },
                                    ],
                                ],
                            },
                        },
                    },
                    {
                        loader: 'px2rem-loader',
                        options: {
                            remUnit: 75,
                            remPrecesion: 8
                        }
                    }
                ]
            },
            {
                test: /\.less$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'less-loader'
                ]
            },
            {
                test: /\.(png|jpg|gif|jpeg)$/,
                type: 'asset/resource'
            },
            {
                test: /\.(woff|woff2|eot|ttf|otf)/,
                type: 'asset/resource'
            },
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name]_[contenthash:8].css',
        }),
        new CleanWebpackPlugin(),
        new HTMLInlineCSSWebpackPlugin()
    ].concat(htmlWebpackPlugins),
    optimization: {
        minimize: true,
        minimizer: [
            new CssMinimizerPlugin(),
        ],
    },
}

六、使用source map

开发环境开启,线上环境关闭
通过source map 定位到源码

source map关键字

  • eval:使用eval报过模块代码
  • source map:产生的.map文件
  • cheap:不包含列信息
  • inline:将,map作为dataUrl嵌入,不单独生成.map文件
  • moudle:包含loader的sourcemap

详细选项参考:Devtool | webpack 中文文档 

devtool: 'eval-cheap-module-source-map',//开发环境配置 定位错误根源

七、基础库分离(externals)

防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)

例如,从 CDN 引入 jQuery,而不是把它打包:

(1)index.html

<script crossorigin src="https://unpkg.com/react@18.2.0/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18.2.0/umd/react-dom.production.min.js"></script>

(2)webpack配置

module.exports = {
  //...
  externals: {
        'react': 'React', // 页面inport时也需要是React
        'react-dom': 'ReactDOM',  // 页面inport时也需要是ReactDOM
    }
};

参考:外部扩展(Externals) | webpack 中文文档

八、提取页面公共资源 

1、SplitChunksPlugin

chunks 参数:

  • 有效值为 allasync 和 initial。设置为 all 可能特别强大,因为这意味着 chunk 可以在异步和非异步 chunk 之间共享
  • async异步银土的库进行分离(默认)
  • initial同步引入的库进行分离
  • all 所有引入的库进行分离(推荐)
    optimization: {
        splitChunks: {
            chunks: 'async',
            minSize: 20000, // 生成 chunk 的最小体积(以 bytes 为单位)。
            minRemainingSize: 0, // 通过确保拆分后剩余的最小 chunk 体积超过限制来避免大小为零的模块
            minChunks: 1, // 设置某一个方法最小的使用次数
            maxAsyncRequests: 30, // 同时请求异步资源的数量
            maxInitialRequests: 30, //入口点的最大并行请求数。
            enforceSizeThreshold: 50000, // 强制执行拆分的体积阈值和其他限制(minRemainingSize,maxAsyncRequests,maxInitialRequests)将被忽略。
            cacheGroups: {
                defaultVendors: {
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10,
                    reuseExistingChunk: true,
                },
                default: {
                    minChunks: 2,
                    priority: -20,
                    reuseExistingChunk: true,
                },
            },
            // commons此配置会把 react或react-dom 分离出来,分离出来的文件是 vendors.js
            commons: {
                test: /(react|react-dom)/,  // 匹配出需要分离的包
                // cacheGroupKey here is `commons` as the key of the cacheGroup
                // name(module, chunks, cacheGroupKey) {
                //     const moduleFileName = module
                //         .identifier()
                //         .split('/')
                //         .reduceRight((item) => item);
                //     const allChunksNames = chunks.map((item) => item.name).join('~');
                //     return `${cacheGroupKey}-${allChunksNames}-${moduleFileName}`;
                // },
                name: 'vendors', 
                chunks: 'all',
                minChunks: 2, // 设置最小引用次数为2次,如果只有一个页面用到,是不会提取出来的
            },
        },
    },

九、Tree Shaking的原理和使用

tree shaking 是一个术语,通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块语法的 静态结构 特性,例如 import 和 export。这个术语和概念实际上是由 ES2015 模块打包工具 rollup 普及起来的。 

webpack 2 正式版本内置支持 ES2015 模块(也叫做 harmony modules)和未使用模块检测能力。新的 webpack 4 正式版本扩展了此检测能力,通过 package.json 的 "sideEffects" 属性作为标记,向 compiler 提供提示,表明项目中的哪些文件是 "pure(纯正 ES2015 模块)",由此可以安全地删除文件中未使用的部分。

1、DCE

  • 代码不会被执行,不可达到
  • 代码执行的结果不会被用到
  • 代码只会影响死变量(只写不读)

2、原理

利用ES6模块的特点

  • 只能作为模块顶层的语句出现
  • import 的模块名只能是字符串常量
  • import binding 是 immutable的

代码擦除:uglify阶段删除无用代码

3、webpack默认开启tree-shaking

4、实践

(1)编写文件tree-shaking.js

export function add(a, b) {
    return a + b;
}


export function sub(a, b) {
    return a - b;
}

(2)在reactTest下的indexjs引入

import { add, sub } from './tree-shaking';

(3)修改webpack.prod.js中mode为none查看打包效果

(4)修改webpack.prod.js中mode为production查看打包效果

十、Scope Hoisting的原理和使用

1、webpack模块机制 

现象:构建后的代码存在大量必报代码

会导致的问题:

大量函数闭包包裹代码,导致体积增大(模块越多越明显)

运行代码时创建的函数作用域变多,内存开销变大


模块转换:

被webpack转换后的模块会带上一层包裹

import会被转换成__webpack_require

模块机制:

打包出来的是一个IIFE(匿名闭包)

moudle是一个数组,每一项是一个模块初始化函数

__webpack_require用来加载模块,返回moudle.exports

通过WEBPACK_REQUIRE_METHOD(0)启动程序

2、Scope Hoisting的原理 

原理:将所有模块的代码按照引用顺序放在一个函数作用域里,然后适当的重命名一些变量,防止命名冲突

对比:通过 scope hoisting 可以减少函数声明代码和内存开销

3、 Scope Hoisting的使用

webpack mode 为 production 默认开启

必须是ES6语法,CJS不支持

十一、代码分割和动态import 

1、代码分割的意义

        对于大的web应用来讲,将所有的代码都放在一个文件中显然是不够高效的,特别是当你的某些代码块是在某些特殊的时候才会使用到。webpack有一个功能就是将你的代码库分割成chunks(语块),当代码运行到它们的时候再进行加载。

适用的场景:

        抽离相同代码到一个共享块

        脚本懒加载,使得初始下载的代码更小

2、懒加载js的方式 

CommonJS:require.ensure

ES6:动态import(需要babel转换)

3、如何使用动态import?

(1) 安装babel插件

npm install --save-dev @babel/plugin-syntax-dynamic-import

(2)ES6:动态import(.babelrc)

{
    "presets": [
        "@babel/preset-env",
        "@babel/preset-react"
    ],
    "plugins": [
        "@babel/plugin-syntax-dynamic-import"
    ]
}

(3)添加test.js

import React from "react";

export default() =>  <div>动态import</div>;

(4)修改index.js

'use strict';
import React from "react";
import ReactDOM from "react-dom";
import './reactTest.css';
import back from './images/back.png';
import { add, sub } from './tree-shaking';

class ReactComponents extends React.Component {

    constructor() {
        super(...arguments);
        this.state = {
            Text: null
        };
    }

    testImport() {
        import('./test').then((Text) => {
            this.setState({
                Text: Text.default
            })
        })
    }
    render() {
        const { Text } = this.state;
            
        return <div className="react-test">
            <div onClick={this.testImport.bind(this)}>Hello React!!!!!!!!----</div>
            {
                Text ? <Text /> : null
            }
            <img src={back} />
        </div>;
    }
}

ReactDOM.render(
    <ReactComponents />,
    document.getElementById('root')
)

十二、在webpack中使用ESLint

1、行业中优秀的ESLint规范实践

Airbnb:esline-config-airbnb、esline-config-airbnb-base

腾讯:

        alloyteam团队:eslint-config-alloy(https://github.com/AlloyTeam/eslint-config-alloy)

        ivweb团队:eslint-config-ivweb(https://github.com/feflow/eslint-config-ivweb)

2、制定eslint规范

  • 不重复造轮子,基于eslint.recommend进行改造
  • 能够帮助发现代码错误的规则全部开启
  • 帮助保持团队的代码风格统一,而不是限制开发体验

3、ESLint落地

方案一:webpack与CI/CD集成

(1)安装 husky 

npm install husky --save-dev

(2)增加 npm script,通过 lint-staged 增量检查修改的⽂件

  "scripts": {
    "precommit": "lint-staged"
  },
  "lint-staged": {
    "linters": {
      "*.{js,scss}": [
        "eslint --fix",
        "git add"
      ]
    }
  },

方案二:webpack与eslint集成(eslint-config-airbnb为例)

(1)依赖安装

npm i eslint eslint-plugin-import eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-jsx-a11y -D
npm i eslint-loader -D

 注:eslint安装8版本时,安装eslint-loader(4版本)会报错,需要eslint版本6或7

npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! 
npm ERR! While resolving: webpackExercise@1.0.0
npm ERR! Found: eslint@8.29.0
npm ERR! node_modules/eslint
npm ERR!   dev eslint@"^8.29.0" from the root project
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer eslint@"^6.0.0 || ^7.0.0" from eslint-loader@4.0.2
npm ERR! node_modules/eslint-loader
npm ERR!   dev eslint-loader@"4.0.2" from the root project
npm ERR! 
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR! 
npm ERR! See /Users/liangchenyang/.npm/eresolve-report.txt for a full report.

npm ERR! A complete log of this run can be found in:

(2) 修改webpack配置

// webpack.prod.js
{
    test: /\.js$/,
    use: [
        'babel-loader',
        'eslint-loader'
    ]
},

(3)新建.eslintrc.js

npm i eslint-config-airbnb -D
module.exports = {
    "parser": "esprima",
    "extends": "airbnb",
    "env": {
        "browser": true,
        "node": true
    },
    "rules": {
        "indent": ["error", 4]
    },
}

十三、webpack打包库和组件

1、实现一个大整数加法库的打包

  • 未压缩版的库名称:large-number.js
  • 压缩版的库的名称:large-number.min.js

2、支持的使用方式

  • 支持ES module
import * as largeNumber from 'large-number';
  • 支持CJS
const largeNumbers = require('large-number');
  • 支持AMD
require(['large-number'],function(large-number){
    // ...
});
  • 直接通过script方式引入
<script src="....." />

3、如何将库暴露出去?

  • library:指定库的全局变量
  • libraryTarget:指定库引入的方式

4、实操

(1)新建 large-number 文件夹,并且初始化

npm init -y
npm i webpack webpack-cli -D

(2)新建src/index.js

// src/index.js
export default function add(a, b) {
    let i = a.length - 1;
    let j = b.length - 1;


    let carry = 0;
    let ret = '';
    while (i>=0 || j>=0) {
        let x = 0;
        let y = 0;
        let sum;
        if (i>= 0) {
            x = a[i] - '0';
            i--;
        }

        if (j>= 0) {
            y = b[j] - '0';
            j--;
        }

        sum = x + y + carry;

        if (sum >= 10) {
            carry = 1;
            sum -= 10;
        } else {
            carry = 0;
        }

        ret = sum + ret;
    }
    if (carry) {
        ret = carry + ret;
    }

    return ret;
}

(3)新建webpack.config.js

module.exports = {
    entry: {
        'large-number': './src/index.js',
        'large-number.min': './src/index.js',
    },
    output: {
        filename: '[name].js',
        library: 'large-number', // 打包出来的库的名字
        libraryTarget: 'umd', // 支持上边说的三种使用方式
        libraryExport: 'default'
    }
}

(4)修改package.json

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack"
  },

(5)此时打包出来的全部都是压缩后的代码,需要通过include设置只对.min.js结尾的文件进行压缩

npm i terser-webpack-plugin -D
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
    mode: 'none',
    entry: {
        'large-number': './src/index.js',
        'large-number.min': './src/index.js',
    },
    output: {
        filename: '[name].js',
        library: 'large-number', // 打包出来的库的名字
        libraryTarget: 'umd',
        libraryExport: 'default'
    },
    optimization: {
        minimize: true,
        minimizer: [
            new TerserPlugin({
                include: /\.min\.js$/,
            })
        ]
    }
}

(6)设置入口文件

{
  "name": "large-number",
  "version": "1.0.0",
  "description": "大整数加法",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "prepublish": "webpack"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "terser-webpack-plugin": "^5.3.6",
    "webpack": "^5.75.0",
    "webpack-cli": "^5.0.1"
  }
}

(7)在package.json的同级目录新建 index.js


// 如果将webpack.config.js中的mode设置为 production,
// 打包时 process.env.NODE_ENV 就是 production,否则就等于 development
if (process.env.NODE_ENV === 'production') {
    module.exports = require('./dist/large-number.min.js');
} else {
    module.exports = require('./dist/large-number.js');
}

(8)发布到npm 

npm publish

执行 npm publish  时,会运行 package.json 中 script 下的 prepublish

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/77670.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于java学生签到考勤系统

开发工具eclipse,jdk1.8 技术&#xff1a;java swing 数据库&#xff1a;mysql5.7 学生选课系统功能&#xff1a;管理员、教师、学生三个角色 一、管理员功能&#xff1a; 1.登录、修改密码、退出系统 2.学生管理&#xff1a;添加、修改、删除、查询 3.班级管理&#x…

YOLOV1算法学习记录

前言 R-CNN系列算法&#xff08;R-CNN、SPPNet、Fast R-CNN、Faster R-CNN&#xff09;均是采用two-stage的方法&#xff08;1.提取region proposal 2.分类边框回归&#xff09;&#xff0c;主要是对region proposal进行识别定位。虽然这类方法检测精度很高&#xff0c;但由于…

Leetcode番外篇——滑动窗口的应用

各位好&#xff0c;博主新建了个公众号《自学编程村》&#xff0c;拉到底部即可看到&#xff0c;有情趣可以关注看看哈哈&#xff0c;关注后还可以加博主wx呦~~~&#xff08;公众号拉到底部就能看到呦&#xff09; 我们刚刚在上一节讲述了TCP的滑动窗口。殊不知&#xff0c;它…

基于RISC-V的Copy-On-Write

为什么需要写时拷贝呢&#xff1f; 当 shell执行指令的时候会 fork()&#xff0c;而这个 fork()出来的进程首先会调用的就是 exec来执行对应的命令&#xff0c;如果我们将 fork()创建的进程对地址空间进行了完整的拷贝,那将是一个巨大的消耗 因为在实际应用中&#xff0c;for…

微信小程序框架-全面详解(学习总结---从入门到深化)

小程序与普通网页开发的区别 小程序的主要开发语言是 JavaScript &#xff0c;小程序的开发同普通的网页 开发相比有很大的相似性。对于前端开发者而言&#xff0c;从网页开发迁移 到小程序的开发成本并不高&#xff0c;但是二者还是多少有些许区别的&#xff0c;例如&#xff…

HCIP实验 4-1:路由引入与路由控制

实验 4-1 路由引入与路由控制 学习目的 掌握OSPF与ISIS相互路由引入的配置方法掌握通过地址前缀列表过滤路由信息的配置方法掌握通过Route-policy过滤路由信息的配置方法 拓扑图 场景 你是你们公司的网络管理员。公司网络中有两部分路由区域&#xff0c;一部分运行OSPF,另外…

【Proteus仿真】【51单片机】厨房天然气泄漏检测报警系统

文章目录一、功能简介二、软件设计三、实验现象联系作者一、功能简介 本项目使用Proteus8仿真51单片机控制器&#xff0c;使用LCD1602、按键、天然气、烟雾传感器、ADC&#xff0c;报警模块等。 系统运行后&#xff0c;LCD1602显示传感器检测的天然气浓度和烟雾浓度值。 可通…

中国土地交易数据库:300w数据中国土地高频交易数据2000-2022

土地交易是土地在流通过程中多方发生的经济关系&#xff0c;土地交易的行为主要是交换的土地所有权、使用权、租赁权、抵押权等。在我国&#xff0c;土地作为一种重要资源&#xff0c;其收购储备和交易行为都由国家进行统一管理。经过改革开放几十年的探索和实践&#xff0c;土…

手机投影到电脑显示 此设备不支持miracast,因此不能以无线投影到它

在家里使用手机的体感游戏,发现手机屏幕比较小,想要将其投影到自己的笔记本电脑上,这样看得就比较大了。然后我就打开笔记本电脑,操作如下: 如下图: 原文地址:手机投影到电脑显示 此设备不支持miracast&#xff0c;因此不能以无线投影到它 - 廖强的博客 但是结果我们就看到了…

Mysql安装配置和Mysql使用六千字详解!!

目录 课前导读 一、Mysql的安装和配置 二、数据库简介&#xff1a; 1、数据库中典型代表&#xff1a; 2、数据库类型&#xff1a; 3、Mysql简介&#xff1a; 4、客户端和服务器简介&#xff1a; 三、初始MySQL 四、数据库操作 五、表的基本操作 六、表的基础增删查改…

虚拟主机、WordPress 主机和云主机之间的区别

&#x1f482; 个人网站:【海拥】【摸鱼游戏】【神级源码资源网站】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 想寻找共同学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】&#x1f4ac; 免费且…

[附源码]JAVA毕业设计校园失物招领管理系统(系统+LW)

[附源码]JAVA毕业设计校园失物招领管理系统&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目…

港科夜闻|罗康锦教授获委任为香港科大工学院院长

关注并星标每周阅读港科夜闻建立新视野 开启新思维1、罗康锦教授获委任为香港科大工学院院长。该委任任期由2023年1月1日开始。罗康锦教授服务香港科大多年&#xff0c;是智慧交通系统、智慧城市和可持续发展的杰出学者&#xff0c;在学术研究方面屡获殊荣。罗教授拥有丰富的学…

阿里巴巴内部最新发布SpringCloud ALiBaBa全彩版

就在昨天&#xff0c;阿里巴巴发布了最新的SpringCloud ALiBaBa全解第三版同时也是全彩版&#xff0c;话不多说我们直接来看干货&#xff01; 由于文章的篇幅有限&#xff0c;下面只能为大家展示目录内容&#xff0c;需要领取完整版可以文末免费获取章节目录 微服务介绍 微服务…

Go 实现插入排序算法及优化

Go 实现插入排序算法及优化插入排序算法实现算法优化小结耐心和持久胜过激烈和狂热。 哈喽大家好&#xff0c;我是陈明勇&#xff0c;今天分享的内容是使用 Go 实现插入排序算法。如果本文对你有帮助&#xff0c;不妨点个赞&#xff0c;如果你是 Go 语言初学者&#xff0c;不妨…

python基于SVM的疫情评论情感数据分析

1、构建SVM情感分析模型 读取数据 使用pandas的库读取微薄数据读取并使进行数据打乱操作 import pandas as pd test pd.read_csv(".\\weibo.csv") test_data pd.DataFrame(test)[:1000] test_data 打乱数据 re_test_data test_data.sample(frac1).reset_index(…

代码随想录训练营第44天|完全背包、LeetCode 518. 零钱兑换 II、 377. 组合总和 Ⅳ

完全背包 有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品都有无限个&#xff08;也就是可以放入背包多次&#xff09;&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 完全背包和01背包问题唯一不同…

前端工程化实践——快速入门treeshaking

treeshaking treeshaking本质是去除多余代码算法。在前端的性能优化中&#xff0c;es6 推出了tree shaking机制&#xff0c;tree shaking就是当我们在项目中引入其他模块时&#xff0c;他会自动将我们用不到的代码&#xff0c;或者永远不会执行的代码摇掉&#xff0c;在…

【Java设计模式】用盖房子案例讲解建造者模式(生成器模式)

一、前言 今天学习了Java设计模式中的建造者模式&#xff0c;细心整理了学习笔记以及对这个模式的自我理解&#xff0c;希望大家喜欢&#xff01; 二、需求介绍 现在我们需要建房子&#xff0c;过程为打桩、砌墙、封顶。房子有各种各样的&#xff0c;比如普通房&#xff0c;…

【Java开发】 Spring 10 :Spring Boot 自动配置(Auto Configuration)原理及手动实现

用了这么久的 SpringBoot &#xff0c;我们再来回顾一下它&#xff0c;本文介绍 Spring Boot 的自动配置&#xff0c;这是它区别于 Spring 的最大的点&#xff0c;本文的自动配置项目包含三个项目&#xff0c;建议拉取仓库里的代码进行实践&#xff1a;尹煜 / AutoConfigDemo …