Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 500|回复: 0

【Webpack的使用指南 02】Webpack的常用解决方案

[复制链接]
  • TA的每日心情
    奋斗
    4 天前
  • 签到天数: 802 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726006
    发表于 2021-6-12 14:16:39 | 显示全部楼层 |阅读模式

    前言

    说是解决方案实际上更像是webpack的插件索引。
    写这一篇的目的是为了形成一个索引,将来要用时直接来查找即可。

    索引目录

    1. 自动构建HTML,可压缩空格,可给引用的js加版本号或随机数:html-webpack-plugin
    2. 处理CSS:css-loader与style-loader
    3. 处理LESS:less-loade与less
    4. 提取css代码到css文件中: extract-text-webpack-plugin
    5. 开发环境下的服务器搭建:webpack-dev-server
    6. 解析ES6代码:babel-core babel-preset-env babel-loader
    7. 解析ES6新增的对象函数:babel-polyfill
    8. 解析react的jsx语法:babel-preset-react
    9. 转换相对路径到绝度路径:nodejs的path模块
    10. 给文件加上hash值:[chunkhash],[hash]
    11. 清空输出文件夹之前的输出文件:clean-webpack-plugin
    12. 模块热替换:NamedModulesPlugin和HotModuleReplacementPlugin
    13. 环境变量
    14. 跨平台使用环境变量: cross-env
    15. 处理图片路径: file-loader和html-loader
    16. 图片压缩:image-webpack-loader
    17. 定位源文件代码:source-map
    18. 分离生产环境和开发环境的配置文件
    19. webpack输出文件体积与交互关系的可视化:webpack-bundle-analyzer

    1.自动构建HTML,可压缩空格,可给引用的js加版本号或随机数:html-webpack-plugin

    解决方案:使用插件 html-webpack-plugin
    webpack.config.js如下:

    module.exports = {
      entry: './src/app.js',
      output: {
        path: __dirname + '/dist',
        filename: 'app.bundle.js'
      },
      plugins: [new HtmlWebpackPlugin({
        template: './src/模板文件.html',
        filename: '构建的.html',
        minify: {
          collapseWhitespace: true,
        },
        hash: true,
      })]
    };
    

    注意要有path,因为这个输出的html需要知道输出目录

    2.处理CSS:css-loader与style-loader

    loader用于对模块的源代码进行预处理转换。

    解决方案:使用css-loaderstyle-loader

    看一下项目结构:
    引用了css的js

    此时运行webpack命令会抛出错误:
    webpack不能打包css

    接下来安装 css-loader 和 style-loader

    npm install --save-dev css-loader style-loader
    

    再修改webpack.config.js为:
    红框中为新加的配置

    这其中rules数组就是loader用来的匹配和转换资源的规则数组。
    test代表匹配需转换文件的正则表达式,而图中表示匹配所有以css结尾的文件。
    而use数组代表用哪些loader去处理这些匹配到的文件。

    此时再运行webpack,打包后的文件bundle.js就包含了css代码。
    其中css-loader负责加载css,打包css到js中。
    而style-loader负责生成:在js运行时,将css代码通过style标签注入到dom中。

    3.处理LESS:less-loade与less

    解决方案:使用less-loader
    但是用less-loader只是将LESS代码转换为css代码。如果要打包文件到js中,还是需要用到上面提到的css-loader和style-loader。

    看一下项目结构:
    less项目结构

    然后app.js的代码为:

    import styles from './app.less';
    
    console.info('我是一个js文件123')
    

    为了解决这种情况,首先要安装 less-loader,而less-loader是基于less的,所以也要安装less。

    npm i --save-dev less less-loader
    

    修改webpack.config.js为:

    module: {
      rules: [
        {
          test: /\.less$/,
          use: [ 'style-loader', 'css-loader', 'less-loader' ]
        }
      ]
    }
    

    4.提取css代码到css文件中: extract-text-webpack-plugin

    很多时候我们想要的效果并不是想要把几个LESS或者CSS处理好后,打包到一个js中,而是想要把它打包到一个css文件中。
    此时就有了插件 extract-text-webpack-plugin
    首先进行安装

    npm i --save-dev extract-text-webpack-plugin
    

    然后修改webpack.config.js为:
    红框中为新加或修改的配置

    与原配置对比可以发现,比html-webpack-plugin这个插件多做了一步,就是在匹配和转换规则里面的use中使用了ExtractTextPlugin.extract。
    注意这里的fallback表示,在提取文件失败后,将继续使用style-loader去打包到js中。
    此时运行webpack
    可以发现输出目录build下生成了一个style.css文件,也就是我们在webpack.config.js中期望生成的文件,并且在生成的demo.html中被引用了。

    5.开发环境下的服务器搭建:webpack-dev-server

    webpack-dev-server可以在本地搭建一个简单的开发环境用的服务器,自动打开浏览器,而且还可以达到webpack -watch的效果。
    首先安装一下:

    npm i -g  webpack-dev-server
    npm i --save-dev webpack-dev-server
    

    这里不需要改动webpack.config.js,直接运行命令

    webpack-dev-server
    

    查看控制台输出:
    控制台输出

    显示项目运行在http://localhost:8080/
    webpack的输出目录的路径在/下面
    并且这个服务器会自动识别输出目录下名为index的HTML文件,而我们之前输出的文件名为demo.html。
    所以还需要将之前html-webpack-plugin中配置的filename改为index.html,或者直接用http://localhost:8080/demo.html也行。
    当我们修改了源代码后,打开的网页还会自动更新。

    为了更灵活的应用开发环境的服务器,也可以在webpack.config.js中加入如下代码:
    image.png

    devServer配置 功能
    port 修改端口为8787,而不是默认的8080。
    open 为true表示会自动打开浏览器,而不是需要我们再手动打开浏览器并在里面输入http://localhost:8080。
    compress 对本地server返回的文件提供gzip压缩
    index 指定网站首页映射的文件,默认为index.html

    6.解析ES6代码:babel-core babel-preset-env babel-loader

    这里说是ES6,实际上可以认为是ECMAScript的高版本代码,只是代指而已。
    babel的作用是将浏览器还未支持的这些高版本js代码转换成可以被指定浏览器支持的js代码。

    这里列出可以转换的大致语法:
    babel-preset-env支持的转换

    那么首先就需要安装babel

    npm install babel-core babel-preset-env --save-dev
    

    然后,为了和webpack结合起来,要用到babel-loader

    npm install babel-loader --save-dev
    

    然后在webpack.config.js的rules数组中增加以下代码:

    {
      test: /\.js$/,
      exclude: /(node_modules)/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['env']
        }
      }
    }
    

    这行代码的意思是用babel-loader解析除了node_modules文件下的所有js文件。
    而babel-loader就是用babel去解析js代码。
    options的内容类似于.babelrc文件的配置,有了这个就不需要.babelrc文件了。
    presets表示预处理器,现在的babel不像以前需要很多预处理器了,只需要env这一个就够了。

    修改之前的app.js中的代码为:

    console.info('我是一个js文件123')
    const doSomething=() => {
      console.info('do do do')
    }
    

    使用webpack命令后,可以看到我们最后的打包js文件中代码变成了这样:
    image.png

    7.解析ES6新增的对象函数:babel-polyfill

    以下为这些新增函数:
    babel-polyfill支持的转换

    安装:

    npm install --save-dev babel-polyfill
    

    为了确保babel-polyfill被最先加载和解析,所以一般都是讲babel-polyfill在最开始的脚本中引入。
    而在webpack中,就是在放到entry中,所以需要修改webpack.config.js中的配置为:

    红框中为修改的部分

    8.解析react的jsx语法:babel-preset-react

    安装

    npm install --save-dev babel-preset-react
    

    配置:
    修改后的配置

    这里是匹配所有以js或者jsx结尾的文件,并用 babel-preset-env和babel-preset-react进行解析

    9.转换相对路径到绝度路径:nodejs的path模块

    这里首先介绍一下nodejs的path模块的一个功能:resolve。
    将相对路径转换为绝对路径。
    在最开始引用path模块

    var path = require('path');
    

    然后可以在输出设置那里修改代码为:

      output: {
        path: path.resolve(__dirname, 'build'),
        filename: 'bundle.js'
      },
    

    和我们原来的代码没有任何区别。

    10.给文件加上hash值:[chunkhash],[hash]

    hash和chunkhash有区别,hash的话输出的文件用的都是同一个hash值,而chunkhash的话是根据模块来计算的,每个输出文件的hash值都不一样。
    直接将输出文件改为

    output: {
      path: path.resolve(__dirname, 'build'),
      filename: 'bundle.[chunkhash].js'
    },
    

    [chunkhash]就代表一串随机的hash值

    11.清空输出文件夹之前的输出文件:clean-webpack-plugin

    当我们像上面一样不断改变输出文件时,之前的输出文件并没有去掉。
    为了解决这个问题就需要clean-webpack-plugin。
    首先安装

    npm i clean-webpack-plugin --save-dev
    

    然后引用插件,并声明每次生成输出需要清空的文件夹

    var CleanWebpackPlugin = require('clean-webpack-plugin');
    var pathsToClean = [
      'build',
    ]
    

    再在插件配置中加入:

    new CleanWebpackPlugin(pathsToClean)
    

    12.模块热替换:NamedModulesPlugin和HotModuleReplacementPlugin

    之前的webpack-dev-server提供了监听功能,只要代码改变,浏览器就会刷新。
    但是模块热替换是不会刷新浏览器,只刷新修改到的那部分模块。
    模块热替换无需安装。
    首先需要引入模块

    var webpack = require('webpack')
    

    其实插件中加入:

    new webpack.NamedModulesPlugin(),
    new webpack.HotModuleReplacementPlugin()
    

    此时运行webpack可能会报错,我们需要把之前在输出环境中写的[chunkhash]改为[hash]

    13.环境变量

    可以在脚本中这么写:

    "scripts": {
    "dev": "webpack-dev-server",
    "prod": "set NODE_ENV=production && webpack -p"
    },

    这样在webpack.config.js中这样修改上面的东西:

    if (isProduction) {
        config.output.filename = 'bundle.[chunkhash].js'
    } else {
        config.plugins.push(new webpack.NamedModulesPlugin())
        config.plugins.push(new webpack.HotModuleReplacementPlugin())
    }
    

    这样就可以根据环境的不同来运行不同的配置

    14.跨平台使用环境变量: cross-env

    上述设置环境变量的脚本中只有在window下才有效,在linux和mac上需要使用

    "prod": "NODE_ENV=production webpack -p"
    

    为了解决这个问题,使得不同平台的人能公用一套代码,我们可以使用cross-env。
    首先进行安装:

    npm i --save-dev cross-env
    

    然后命令直接使用类似于mac上的用法即可

    "prod": "cross-env NODE_ENV=production webpack -p"
    

    15.处理图片路径: file-loader和html-loader

    file-loader可以用来处理图片和字体文件在css文件中的路径问题,输出的css文件中会引用输出的文件地址。
    html-loader可以用来处理html中,比如img元素的图片路径问题。
    首先安装

    npm i --save-dev file-loader html-loader
    

    配置:

            {
                test: /\.(gif|png|jpe?g|svg)$/i,
                use: {
                    loader: 'file-loader',
                    options: {
                        name: '[name].[ext]',
                        outputPath: 'src/images/'
                    }
                }
            },
            {
                test: /\.html$/,
                use: [{
                    loader: 'html-loader',
                    options: {
                        minimize: true
                    }
                }],
            }
    

    16.图片压缩:image-webpack-loader

    安装:

    npm i --save-dev image-webpack-loader
    

    配置:

        {
                test: /\.(gif|png|jpe?g|svg)$/i,
                use: [{
                        loader: 'file-loader',
                        options: {
                            name: '[name].[ext]',
                            outputPath: 'images/'
                        }
                    },
                    {
                        loader: 'image-webpack-loader',
                        options: {
                            bypassOnDebug: true,
                        }
                    }
                ]
            },
    

    这里的options中也可以具体配置各个图片类型的压缩质量

    17.定位源文件代码:source-map

    如果我们用web-dev-server运行我们的输出文件,发现其中有些BUG,然后打开开发者工具取定位文件的时候,只会定位到我们的输出文件。
    而这些输出文件是经过处理的,我们只有找到我们的源文件代码,然后进行相应的修改才能解决问题。
    于是这里我们需要用到source-map。
    很简单,在webpack.config.js中加入如下配置即可:

    devtool: 'source-map',
    

    就这么简单,还不需要安装什么插件。
    但是这只对js有效,如果我们的css出现错误了呢,答案就是如下配置:
    在这些loader后面加上?sourceMap即可

    18.分离生产环境和开发环境的配置文件

    之前我们通过在命令中设置环境变量,并且通过环境变量来判断环境来进行不同的配置。
    现在我们用官方推荐的方法来分离生产环境和开发环境的配置文件。
    我们将webpack.config.js分为三个文件

    • webpack.common.js
    • webpack.dev.js
    • webpack.prod.js

    其中webpack.common.config.js为生产环境和开发环境共有的配置,dev为开发环境独有的配置,prod为生成环境独有的配置。
    而想要合成真正的配置文件,还需要一个工具:webpack-merge。

      npm install --save-dev webpack-merge
    

    以下是我们之前的webpack.config.js代码:

    var ExtractTextPlugin = require('extract-text-webpack-plugin')
    var HtmlWebpackPlugin = require('html-webpack-plugin')
    var CleanWebpackPlugin = require('clean-webpack-plugin')
    var path = require('path')
    var webpack = require('webpack')
    
    var pathsToClean = [
        'build',
    ]
    
    var isProduction = process.env.NODE_ENV === 'production'
    
    var config = {
        entry: ['babel-polyfill', './src/app.js'],
        output: {
            path: path.resolve(__dirname, 'build'),
            filename: '[name].[hash].js'
        },
        devtool: 'source-map',
        devServer: {
            port: 8787,
            open: true,
            compress: true,
            index: 'demo.html'
        },
        plugins: [
            new HtmlWebpackPlugin({
                template: './template/index.html',
                filename: 'demo.html',
                minify: {
                    collapseWhitespace: true,
                },
                hash: true
            }),
            new ExtractTextPlugin({ filename: 'style.css', allChunks: false }),
            new CleanWebpackPlugin(pathsToClean)
        ],
        module: {
            rules: [{
                    test: /\.css$/,
                    use: ExtractTextPlugin.extract({
                        fallback: 'style-loader',
                        use: ['css-loader?sourceMap']
                    })
                },
                {
                    test: /\.less$/,
                    use: ExtractTextPlugin.extract({
                        fallback: 'style-loader',
                        use: ['css-loader?sourceMap', 'less-loader?sourceMap']
                    })
                },
                {
                    test: /\.jsx?$/,
                    exclude: /(node_modules)/,
                    use: {
                        loader: 'babel-loader',
                        options: {
                            presets: ['env', 'react']
                        }
                    }
                },
                {
                    test: /\.(gif|png|jpe?g|svg)$/i,
                    use: [{
                            loader: 'file-loader',
                            options: {
                                name: '[name].[ext]',
                                outputPath: 'images/'
                            }
                        },
                        {
                            loader: 'image-webpack-loader',
                            options: {
                                bypassOnDebug: true,
                            }
                        }
                    ]
                },
                {
                    test: /\.html$/,
                    use: [{
                        loader: 'html-loader',
                        options: {
                            minimize: true
                        }
                    }],
                }
            ]
        }
    };
    
    if (isProduction) {
        config.output.filename = '[name].[chunkhash].js'
    } else {
        config.plugins.push(new webpack.NamedModulesPlugin())
        config.plugins.push(new webpack.HotModuleReplacementPlugin())
    }
    
    module.exports = config
    

    接下来分为三个文件,webpack.common.js:
    var ExtractTextPlugin = require('extract-text-webpack-plugin')
    var HtmlWebpackPlugin = require('html-webpack-plugin')
    var CleanWebpackPlugin = require('clean-webpack-plugin')
    var path = require('path')
    var webpack = require('webpack')

    var pathsToClean = [
        'build',
    ]
    
    var isProduction = process.env.NODE_ENV === 'production'
    
    module.exports = {
        entry: ['babel-polyfill', './src/app.js'],
        output: {
            path: path.resolve(__dirname, 'build'),
            filename: '[name].[chunkhash].js'
        },
        plugins: [
            new HtmlWebpackPlugin({
                template: './template/index.html',
                filename: 'demo.html',
                minify: {
                    collapseWhitespace: true,
                },
                hash: isProduction
            }),
            new ExtractTextPlugin({ filename: '[name].[contenthash].css', allChunks: false }),
            new CleanWebpackPlugin(pathsToClean)
        ],
        module: {
            rules: [{
                    test: /\.jsx?$/,
                    exclude: /(node_modules)/,
                    use: {
                        loader: 'babel-loader',
                        options: {
                            presets: ['env', 'react']
                        }
                    }
                },
                {
                    test: /\.(gif|png|jpe?g|svg)$/i,
                    use: [{
                            loader: 'file-loader',
                            options: {
                                name: '[name].[ext]',
                                outputPath: 'images/'
                            }
                        },
                        {
                            loader: 'image-webpack-loader',
                            options: {
                                bypassOnDebug: true,
                            }
                        }
                    ]
                },
                {
                    test: /\.html$/,
                    use: [{
                        loader: 'html-loader',
                        options: {
                            minimize: true
                        }
                    }],
                }
            ]
        }
    };
    

    然后是webpack.dev.js:

    const merge = require('webpack-merge');
    const common = require('./webpack.common.js');
    const webpack = require('webpack');
    const ExtractTextPlugin = require('extract-text-webpack-plugin')
    
    module.exports = merge(common, {
        output: {
            filename: '[name].[hash].js'
        },
        devtool: 'source-map',
        devServer: {
            port: 8787,
            open: true,
            compress: true,
            index: 'demo.html'
        },
        plugins: [
            new webpack.NamedModulesPlugin(),
            new webpack.HotModuleReplacementPlugin()
        ],
        module: {
            rules: [{
                    test: /\.css$/,
                    use: ExtractTextPlugin.extract({
                        fallback: 'style-loader',
                        use: ['css-loader?sourceMap']
                    })
                },
                {
                    test: /\.less$/,
                    use: ExtractTextPlugin.extract({
                        fallback: 'style-loader',
                        use: ['css-loader?sourceMap', 'less-loader?sourceMap']
                    })
                }
            ]
        }
    });
    

    最后是webpack.prod.js:

    const merge = require('webpack-merge');
    const common = require('./webpack.common.js');
    const ExtractTextPlugin = require('extract-text-webpack-plugin')
    
    module.exports = merge(common, {
        module: {
            rules: [{
                    test: /\.css$/,
                    use: ExtractTextPlugin.extract({
                        fallback: 'style-loader',
                        use: ['css-loader']
                    })
                },
                {
                    test: /\.less$/,
                    use: ExtractTextPlugin.extract({
                        fallback: 'style-loader',
                        use: ['css-loader', 'less-loader']
                    })
                }
            ]
        }
    });
    

    然后修改一下package.json中的脚本即可

      "scripts": {
        "dev": "webpack-dev-server --config webpack.dev.js",
        "prod": "cross-env NODE_ENV=production webpack -p --config webpack.prod.js"
    },
    

    19. webpack输出文件体积与交互关系的可视化:webpack-bundle-analyzer

    安装:

    npm install --save-dev webpack-bundle-analyzer
    

    然后修改webpack.config.js:

    const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
    
    module.exports = merge(common, {
      // ...
      plugins: [
        new BundleAnalyzerPlugin({ analyzerPort: 8919 })
      ],
    });
    

    这里的analyzerPort为打包后的本地展示网页的端口,默认是8888。

    然后webpack即可。

    效果图如下:

    总结

    各个插件以及loader的玩法还有很多,这里不具体介绍。

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-11-15 07:51 , Processed in 0.058415 second(s), 28 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表