• 02 【开发服务器 资源模块】


    4.开发服务器&自动化

    webpack-dev-server 可用于快速开发应用程序。请查阅 开发指南 开始使用。

    当前页面记录了影响 webpack-dev-server (简写: dev-server) version >= 4.0.0 配置的选项。

    Warning

    webpack-dev-server v4.0.0+ 要求 node >= v12.13.0webpack >= v4.37.0(但是我们推荐使用 webpack >= v5.0.0)和 webpack-cli >= v4.7.0

    devServer:开发服务器,用来自动化(自动编译,自动打开浏览器,自动刷新浏览器)

    特点:只会在内存中编译打包,不会有任何输出(不会在项目中新建一个文件夹)

    4.1 下载包

    pnpm i webpack-dev-server -D
    
    • 1

    4.2 配置

    基础配置

    webpack.config.js 文件中进行配置

    module.exports = {
      ...
      // 热更新
      devServer: {
        static: path.resolve(__dirname, './dist'), // 默认是把/dist目录当作web服务的根目录
        host: "localhost", // 启动服务器域名
        port: "3000", // 启动服务器端口号
        open: true, // 是否自动打开浏览器
        compress: true, //可选择开启gzips压缩功能,对应静态资源请求的响应头里的Content-Encoding: gzip
        hot:true,  // 启用热模块功能
        liveReload: true,  // 启用热加载功能
      },
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    为了方便,我们配置一下工程的脚本命令,在package.json的scripts里。

    {
        //...
        "scripts": {
        //...
        "dev": "webpack serve --mode development"
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意!如果您需要指定配置文件的路径,请在命令的后面添加 --config [path], 比如:

    webpack serve --mode development --config webpack.config.js
    
    • 1

    上述是一个基本的示例,我们可以根据自己的需求定制化devServer的参数对象,比 如添加响应头,开启代理来解决跨域问题, http2, https等功能。

    添加响应头

    有些场景需求下,我们需要为所有响应添加headers,来对资源的请求和响应打入 标志,以便做一些安全防范,或者方便发生异常后做请求的链路追踪。比如:

    // webpack-config
    module.exports = {
    //...
        devServer: {
                // 在所有响应中添加首部内容
            headers: {
            	'X-Fast-Id': 'p3fdg42njghm34gi9ukj',
            },
        },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这时,在浏览器中右键检查(或者使用f12快捷键),在Network一栏查看任意资源访 问,我们发现响应头里成功打入了一个FastId。

    开启代理

    我们打包出的 js bundle 里有时会含有一些对特定接口的网络请求(ajax/fetch). 要注意,此时客户端地址是在 http://localhost:3000/ 下,假设我们的接口来自 http://localhost:4001/ ,那么毫无疑问,此时控制台里会报错并提示你跨域。 如何解决这个问题? 在开发环境下,我们可以使用devServer自带的proxy功 能:

    module.exports = {
    //...
        devServer: {
            proxy: {
            '/api': 'http://localhost:4001',
            },
        },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    现在,对 /api/users 的请求会将请求代理到 http://localhost:4001/api/users 。 如 果不希望传递/api,则需要重写路径:

    module.exports = {
    //...
        devServer: {
                proxy: {
                    '/api': {
                    target: 'http://localhost:4001',
                    pathRewrite: { '^/api': '' },
                },
            },
        },
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    默认情况下,将不接受在 HTTPS 上运行且证书无效的后端服务器。 如果需要,可以 这样修改配置:

    module.exports = {
    //...
        devServer: {
            proxy: {
                '/api': {
                    target: 'https://other-server.example.com',
                    secure: false,
                },
            },
        },
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    http

    如果想让我们的本地http服务变成https服务,我们只需要这样配置:

    module.exports = {
    //...
        devServer: {
        	https: true, // https//localhost...
        },
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注意,此时我们访问http://localhost:port 是无法访问我们的服务的,我们需要在地 址栏里加前缀:https: 注意:由于默认配置使用的是自签名证书,所以有得浏览器会 告诉你是不安全的,但我们依然可以继续访问它。 当然我们也可以提供自己的证书 ——如果有的话:

    module.exports = {
        devServer: {
            https: {
                cacert: './server.pem',
                pfx: './server.pfx',
                key: './server.key',
                cert: './server.crt',
                passphrase: 'webpack-dev-server',
                requestCert: true,
            },
        },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    http2

    如果想要配置http2,那么直接设置:

    devServer: {
    	http2: true, // https//localhost...
    },
    
    • 1
    • 2
    • 3

    即可,http2默认自带https自签名证书,当然我们仍然可以通过https配置项来使用 自己的证书。

    historyApiFallback

    如果我们的应用是个SPA(单页面应用),当路由到/some时(可以直接在地址栏里 输入),会发现此时刷新页面后,控制台会报错。

    GET http://localhost:3000/some 404 (Not Found)
    
    • 1

    此时打开network,刷新并查看,就会发现问题所在———浏览器把这个路由当作了 静态资源地址去请求,然而我们并没有打包出/some这样的资源,所以这个访问无疑 是404的。 如何解决它? 这种时候,我们可以通过配置来提供页面代替任何404的静 态资源响应:

    module.exports = {
    //...
        devServer: {
            // 任意的 404 响应都被替代为 index.html
        // 基于node connect-history-api-fallback包实现,主要应用于history路由刷新页面404的场景
        	historyApiFallback: true,
        },
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    此时重启服务刷新后发现请求变成了index.html。 当然,在多数业务场景下,我们 需要根据不同的访问路径定制替代的页面,这种情况下,我们可以使用rewrites这个 配置项。 类似这样:

    module.exports = {
    //...
        devServer: {
            historyApiFallback: {
                rewrites: [
                    { from: /^\/$/, to: '/views/landing.html' },
                    { from: /^\/subpage/, to: '/views/subpage.html' },
                    { from: /./, to: '/views/404.html' },
                ],
            },
        },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    开发服务器主机

    如果你在开发环境中起了一个devserve服务,并期望你的同事能访问到它,你只 需要配置:

    module.exports = {
    //...
        devServer: {
        	host: '0.0.0.0',
        },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这时候,如果你的同事跟你处在同一局域网下,就可以通过局域网ip来访问你的服务啦。

    启动服务的时候有个ipv4的网络地址,可以通过那个访问

    4.4 模块热替换与热加载

    模块热替换(HMR - hot module replacement)功能会在应用程序运行过程中,替换、添加或删除 模块,而无需重新加载整个页面。主要是通过以下几种方式,来显著加快开发速度:

    • 保留在完全重新加载页面期间丢失的应用程序状态。
    • 只更新变更内容,以节省宝贵的开发时间。
    • 在源代码中 CSS/JS 产生修改时,会立刻在浏览器中进行更新,这几乎相当于在浏览器 devtools 直接更改样式。

    启用 webpack 的 热模块替换 特性,需要配置devServer.hot参数:

    module.exports = {
    //...
        devServer: {
        	hot: true,
        },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    此时我们实现了基本的模块热替换功能。

    HMR 加载样式 如果你配置了style-loader,那么现在已经同样支持样式文件的 热替换功能了。

    module.exports={
     module: {
         rules: [
             {
                 test: /\.css$/,
                 use: ['style-loader', 'css-loader'],
             },
         ],
     },
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    如果已经通过 HotModuleReplacementPlugin 启用了模块热替换(Hot Module Replacement),则它的接口将被暴露在 module.hot 属性下面。通常,用户先要检查这个接口是否可访问,然后再开始使用它。举个例子,你可以这样 accept 一个更新的模块:

    if (module.hot) {
      // 需要使用热替换的js模块
      module.hot.accept('./input.js', function() {
        // 使用更新过的 library 模块执行某些操作...
      })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    热加载(文件更新时,自动刷新我们的服务和页面)

    新版的webpack-dev-server 默认已经开启了热加载的功能。 它对应的参数是devServer.liveReload,默认为 true。 注意,如果想要关掉它,要将liveReload设置为false的同时,也要关掉 hot

    module.exports = {
    //...
        devServer: {
        	liveReload: false, //默认为true,即开启热更新功能。
        },
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    4.3 启动

    • npx webpack serve(webpack-cli: 4.x);
    • npx webpack-dev-server(webpack-cli 3.x);

    5.资源模块Asset Modules

    官方说明:https://webpack.docschina.org/guides/asset-modules

    该方法需将资源在 JS 中通过 import 进行导入或css中进行导入

    // js 文件导入
    import 命名 from '资源路径'
    
    // css 文件引用
    .box {
      background-image: url('资源路径');
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    资源模块类型

    • asset/resource:发送一个单独的文件并导出 URL
    • asset/inline:导出一个资源的 Data URI ( base64 )
    • asset/source:导出资源的源代码
    • asset:在导出一个资源的 Data URI 和发送一个单独的文件之间自动进行选择

    5.1 resource

    module.exports = {
      output: {
        path: path.resolve(__dirname, "dist"),
        filename: "js/bundle.js", 
        assetModuleFilename: '[contenthash][ext][query]'
      },
      ...
      module: {
        rules: [
          {
            // 监听资源文件
            test: /\.png$/i,
            // 设置资源类型
            type: 'asset/resource',
            generator: {
              // 将图片文件输出到 static/imgs 目录中
              // 将图片文件命名 [hash:8][ext][query]
              // contenthash: 根据内容生成hash值 或者 [hash:8]: hash值取8位
              // [ext]: 使用之前的文件扩展名
              // [query]: 添加之前的query参数
              filename: "[hash:8][ext][query]",
            }
          }
        ]
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    generator中自定义的输出文件名优先级大于output中的assetModuleFilename。

    运行指令

    npx webpack
    
    • 1

    此时输出文件目录:

    image-20220802183005730

    展示在页面

    index.js

    import imgsrc from './assets/img-1.png'
    
    const img = document.createElement('img')
    img.src = imgsrc
    document.body.appendChild(img)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    打开浏览器

    image-20220802182942299

    5.2 inline

    module.exports = {
      ...
      module: {
        rules: [
          {
            // 监听资源文件
            test: /\.svg$/i,
            // 设置资源类型
            type: 'asset/inline'
          }
        ]
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    展示在页面

    index.js

    import logoSvg from './assets/webpack-logo.svg'
    
    const img2 = document.createElement('img')
    img2.style.cssText = 'width: 600px; height: 200px'
    img2.src = logoSvg
    document.body.appendChild(img2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    打开浏览器:

    image-20220802183156821

    可见, .svg 文件都将作为 data URI 注入到 bundle 中。

    5.3 source

    module.exports = {
      ...
      module: {
        rules: [
          {
            // 监听资源文件
            test: /\.txt$/i,
            // 设置资源类型
            type: 'source'
          }
        ]
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    展示在页面

    index.js

    import exampleTxt from './assets/example.txt'
    
    const block = document.createElement('div')
    block.style.cssText = 'width: 200px; height: 200px; background: aliceblue'
    block.textContent = exampleTxt
    document.body.appendChild(block)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    打开浏览器:

    image-20220802183416499

    所有 .txt 文件将原样注入到 bundle.js 中。

    5.4 asset

    module.exports = {
      ...
      module: {
        rules: [
          {
            // 监听资源文件
            test: /\.jpg$/i,
            // 设置资源类型
            type: 'asset',
            // 默认为4kb 
            // 小于设置的大小则转为 64 位图,否则转 URL
            parser: {
              dataUrlCondition: {
                maxSize: 4 * 1024, // 4kb
                maxSize: 4 * 1024 * 1024// 4mb
              }
            },
            generator: {
              // 生成资源名称
              filename: 'assets/images/[name][ext]'
            }
          }
        ]
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    展示在页面

    index.js

    import jpgMap from './assets/qianfeng-sem.jpg'
    
    const img3 = document.createElement('img')
    img3.style.cssText = 'width: 600px; height: 240px; display: block'
    img3.src = jpgMap
    document.body.appendChild(img3)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    打开浏览器:

    image-20220802185641061

    image-20220802185651259

    发现当前的.jpg文件被打包成了单独的文件,因为此文件大小超过了 4kb

  • 相关阅读:
    ActiveMQ Artemis与SpringBoot整合
    15 | 流
    spring引入外部属性文件
    Linux--进程--进程-父进程退出
    i.MX rt 系列微控制器的学习记录
    为什么要学习 Linux?
    Sentinel
    kubernetes之标签label
    谷歌SEO是什么,它对外贸企业有什么好处?
    【Java实验五】继承与多态
  • 原文地址:https://blog.csdn.net/DSelegent/article/details/126166767