跳至主要內容

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

约 2549 字大约 9 分钟...

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

1.开发服务器&自动化

webpack-dev-serveropen in new window 可用于快速开发应用程序。请查阅 开发指南open in new window 开始使用。

当前页面记录了影响 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:开发服务器,用来自动化(自动编译,自动打开浏览器,自动刷新浏览器)

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

1.1 下载包

pnpm i webpack-dev-server -D

1.2 配置

1.2.1 基础配置

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,  // 启用热加载功能
  },
}

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

{
    //...
    "scripts": {
    //...
    "dev": "webpack serve --mode development"
    }
}

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

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

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

1.2.2 添加响应头

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

// webpack-config
module.exports = {
//...
    devServer: {
            // 在所有响应中添加首部内容
        headers: {
        	'X-Fast-Id': 'p3fdg42njghm34gi9ukj',
        },
    },
};

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

1.2.3 开启代理

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

module.exports = {
//...
    devServer: {
        proxy: {
        '/api': 'http://localhost:4001',
        },
    },
};

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

module.exports = {
//...
    devServer: {
            proxy: {
                '/api': {
                target: 'http://localhost:4001',
                pathRewrite: { '^/api': '' },
            },
        },
    },
};

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

module.exports = {
//...
    devServer: {
        proxy: {
            '/api': {
                target: 'https://other-server.example.com',
                secure: false,
            },
        },
    },
}

1.2.4 http

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

module.exports = {
//...
    devServer: {
    	https: true, // https//localhost...
    },
}

注意,此时我们访问http://localhostopen in new window: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.5 http2

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

devServer: {
	http2: true, // https//localhost...
},

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

1.2.6 historyApiFallback

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

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

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

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

此时重启服务刷新后发现请求变成了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.7 开发服务器主机

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

module.exports = {
//...
    devServer: {
    	host: '0.0.0.0',
    },
};

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

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

1.2.8 progress

在浏览器中以百分比显示编译进度。

module.exports = {
  //...
  devServer: {
    client: {
      progress: true,
    },
  },
};

1.2.9 overlay

boolean = true` `object: { errors boolean = true, warnings boolean = true }

当出现编译错误或警告时,在浏览器中显示全屏覆盖。

module.exports = {
  //...
  devServer: {
    client: {
      overlay: true,
    },
  },
};

如果你只想显示错误信息:

module.exports = {
  //...
  devServer: {
    client: {
      overlay: {
        errors: true,
        warnings: false,
      },
    },
  },
};

1.3 模块热替与热加载

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

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

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

module.exports = {
//...
    devServer: {
    	hot: true,
    },
};

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

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

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

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

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

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

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

module.exports = {
//...
    devServer: {
    	liveReload: false, //默认为true,即开启热更新功能。
    },
};

1.4 启动

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

2.资源模块Asset Modules

官方说明:https://webpack.docschina.org/guides/asset-modulesopen in new window

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

// js 文件导入
import 命名 from '资源路径'

// css 文件引用
.box {
  background-image: url('资源路径');
}

资源模块类型

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

2.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]",
        }
      }
    ]
  }
}

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

运行指令

npx webpack

此时输出文件目录:

image-20220802183005730
image-20220802183005730

展示在页面

index.js

import imgsrc from './assets/img-1.png'

const img = document.createElement('img')
img.src = imgsrc
document.body.appendChild(img)

打开浏览器

image-20220802182942299
image-20220802182942299

2.2 inline

module.exports = {
  ...
  module: {
    rules: [
      {
        // 监听资源文件
        test: /\.svg$/i,
        // 设置资源类型
        type: 'asset/inline'
      }
    ]
  }
}

展示在页面

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)

打开浏览器:

image-20220802183156821
image-20220802183156821

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

2.3 source

module.exports = {
  ...
  module: {
    rules: [
      {
        // 监听资源文件
        test: /\.txt$/i,
        // 设置资源类型
        type: 'source'
      }
    ]
  }
}

展示在页面

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)

打开浏览器:

image-20220802183416499
image-20220802183416499

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

2.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]'
        }
      }
    ]
  }
}

展示在页面

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)

打开浏览器:

image-20220802185641061
image-20220802185641061
image-20220802185651259
image-20220802185651259

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

已到达文章底部,欢迎留言、表情互动~
  • 赞一个
    0
    赞一个
  • 支持下
    0
    支持下
  • 有点酷
    0
    有点酷
  • 啥玩意
    0
    啥玩意
  • 看不懂
    0
    看不懂
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.8