共计 7308 个字符,预计需要花费 19 分钟才能阅读完成。
在 webpack
中构建本地服务,最重要的一个插件 webpack-dev-server
, 咱们俗称WDS
, 它承当起了在开发环境 模块热加载
、 本地服务
、 接口代理
等十分重要的性能。
本文是笔者对 wds
的一些了解和意识,心愿在我的项目中有所帮忙。
注释开始 …
在浏览本文之前,本文会大略从下几个方面去理解wds
1、理解 wds
是什么
2、wds
在 webpack 中如何应用
3、我的项目中应用 wds
是怎么样的
4、对于配置 devServer
的一些罕用配置,代理等
5、wds
如何实现模块热加载原理
理解webpack-dev-server
顾名思义,这是一个在 开发环境
下的应用的 本地服务
,它承当了一个提供前端动态服务的作用
首先咱们疾速搭建一个我的项目, 创立一个我的项目 webpack-07-wds
执行npm init -y
, 而后装置根底反对的插件
npm i webpack webpack-cli html-webpack-plugin webpack-dev-server -D
创立一个webpack.config.js
const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].js'
},
plugins: [new htmlWebpackPlugin({template: './public/index.html'})]
}
在根目录下创立 public
, 新建html
文件
<!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>webpack-for-dev-server</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
咱们在入口文件写入一段简略的代码
// index
(() => {const appDom = document.getElementById('app');
appDom.innerHTML = 'hello webpack for wds'
})()
咱们曾经筹备好了内容,当初须要启动 wds
,因而咱们须要在在package.json
中启动服务
"scripts": {
"test": "echo \"Error: no test specified\"&& exit 1",
"start": "webpack server"
},
执行npm run start
高枕无忧,原来就是一行命令就能够了
然而这行命令的背地实际上有 webpack-cli
帮咱们做了一下事件,实际上在 .bin
目录下,当你执行该命令时,webpack
就会启用告知 webpack-dev-server
开启服务,通过 webpack
依据 webpack.config.js
的配置信息进行 compiler
, 而后再交给webpack-dev-server
解决
参考官网文档 webpack-dev-server
根目录新建server.js
// server.js
const webpack = require('webpack');
const webpackDevServer = require('webpack-dev-server');
const webpackConfig = require('./webpack.config.js');
// webpack 解决入口配置相干文件
const compiler = webpack(webpackConfig);
// devServer 的相干配置
const devServerOption = {
port: 8081,
static: {directory: path.join(__dirname, 'public')
},
compress: true // 开启 gizps 压缩 public 中的 html
};
const server = new webpackDevServer(devServerOption, compiler);
const startServer = async () => {console.log('server is start');
await server.start();}
startServer();
终端执行 node server.js
或者在 package.json
中配置, 执行npm run server
"scripts": {
"test": "echo \"Error: no test specified\"&& exit 1",
"start": "webpack server",
"server": "node ./server.js"
}
关上页面 http://localhost:8081
地址,发现是 ok
的
咱们留神到能够应用 webpack server
启动服务,这个次要是 webpack-cli
的命令 server
对于在命令行中设置对应的环境,在以前我的项目中可能你会看到,process.env.NODE_ENV
这样的设置
你能够在 cli
命令中配置, 留神只能在最后面设置,不能像以下这种形式设置webpack server NODE_ENV=test NODE_API=api,不然会有效
"scripts": {
"test": "echo \"Error: no test specified\"&& exit 1",
"start": "NODE_ENV=test NODE_API=api webpack server",
"server": "node ./server.js"
},
在 webpack.config.js
中就能够看到设置的参数
// webpack.config.js
const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin')
console.log(process.env.NODE_ENV, process.env.NODE_API) // test api
module.exports = {
entry: './src/index.js',
mode: 'development',
output: {path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].js'
},
plugins: [new htmlWebpackPlugin({template: './public/index.html'})]
}
你能够设置 --node-env xxx
环境参数来指定环境变量
"start:test": "webpack server --node-env test",
更多参数设置参考官网 cli
wds 在 webpack 中的应用
咱们上述是用一个 server.js
,通过命令行形式,调用webpack-dev-server
API 形式去启动一个本地的动态服务,然而实际上,在webpack
中间接在配置 devServer 接口中配置就行。
理解几个罕用的配置
port
指定端口关上页面-
client
- overlay 当程序谬误时,浏览器页面全屏正告
- webSocketURL 容许指定 websocket 服务器
progress
启动开发环境 gizp 压缩动态 htmlhistoryApiFallback
当应用路由模式为 history 时,必须设置这个,要不然前端刷新间接 404 页面hot
模块热加载,须要联合module.hot.accept('xxx/xxx')
指定某个模块热加载 module.hot.accept-
open 当咱们启动本地服务时,主动关上指定配置端口的浏览器
module.exports = { ... devServer: { port: '9000', client: { progress: true, // 启用 gizp overlay: { errors: true, // 如果有谬误会有蒙层 warnings: false, }, webSocketURL: { hostname: '0.0.0.0', pathname: '/ws', port: 8080, protocol: 'ws', } }, historyApiFallback: true, // 应用路由模式为 history 时,必须设置这个,要不然前端刷新会间接 404 页面 hot: true, // 模块热加载,对应模块须配合 module.hot.accept('xxx/xxx.js')指定模块热加载 open: true, // 当服务启动时默认主动间接关上浏览器,能够指定关上哪个页面 } }
proxy
proxy
这是我的项目中接触最多一点,也是初学者配置代理时常最令人头疼的事件,实际上proxy
实质就是将本地的接口路由前缀
代理到指标服务器环境, 能够同时代理多个不同环境, 具体参考以下... module.exports = { ... devServer: { ... proxy: { '/j': { target: 'https://movie.douban.com', // 代理豆瓣 changeOrigin: true }, '/cmc': { target: 'https://apps.game.qq.com', // 代理王者光荣官网 changeOrigin: true, // 必须要加,否则代理接口间接返回 html pathRewrite: {'^/cmc': '/cmc'}, } } } }
咱们批改
index.js
(() => {const $ = id => document.getElementById(id); const appDomMovie = $('movie'); const gameDom = $('wang'); // appDom.innerHTML = 'hello webpack for wds,'; // https://movie.douban.com/j/new_search_subjects?sort=U&range=0,10&tags=%E7%94%B5%E5%BD%B1&start=0 // 豆瓣电影 const featchMovie = async () => {const { data = [] } = await (await fetch('/j/new_search_subjects?sort=U&range=0,10&tags=%E7%94%B5%E5%BD%B1&start=0')).json() // console.log(data) const divDom = document.createElement('div'); let str = ''; data.forEach(item => {const { title, rate} = item; str += ` <span>${title},${rate}</span>` }) divDom.innerHTML = str; appDomMovie.appendChild(divDom); } featchMovie(); const wangzherongyao = async () => {const divDom = document.createElement('div'); // https://apps.game.qq.com/cmc/cross?serviceId=18&filter=tag&sortby=sIdxTime&source=web_pc&limit=20&logic=or&typeids=1%2C2&exclusiveChannel=4&exclusiveChannelSign=8a28b7e82d30142c1a986bb7acdcc068&time=1655732988&tagids=931 // 王者光荣官网 const {data: { items = [] } } = await (await fetch('/cmc/cross?serviceId=18&filter=tag&sortby=sIdxTime&source=web_pc&limit=20&logic=or&typeids=1%2C2&exclusiveChannel=4&exclusiveChannelSign=8a28b7e82d30142c1a986bb7acdcc068&time=1655732988&tagids=931')).json() let str = ''; console.log(items) items.forEach(item => {const { sTitle, sIMG} = item; str += `<div> <img src=${sIMG} /> <div>${sTitle}</div> </div>` }); divDom.innerHTML = str; gameDom.appendChild(divDom); } wangzherongyao()})()
对应的两个接口数据就曾经在页面上渲染进去了
对于代理咱们会经常容易会犯以下 几个误区
-
第一种, 多个接口代理,第一个间接以
/
代理,这会造成第二个代理有效,接口间接 404,优先级会先匹配第一个{ devServer: { proxy: { '/': { target: 'https://movie.douban.com', // 代理豆瓣 changeOrigin: true, }, '/cmc': { target: 'https://apps.game.qq.com', // 代理王者光荣官网 changeOrigin: true, // 必须要加,否则代理接口间接返回 html pathRewrite: {'^/cmc': '/cmc'}, } } } }
- 第二种,
pathRewrite
要不要加,什么时候该加,不晓得你发现没有我第一个接口拦挡并没有加pathRewrite
,然而和第二个加了成果是一样的。
当初有一个场景,就是你本地测试服务接口与线上接口是有区别的,个别你在本地开发是联调环境,后端的接口不依照常理出牌,假如联调环境后端就是死活不批准对立接口门路怎么办?
当初假如后端接口
联调环境:/dev/api/cmc/cross
线上环境是/api/cmc/cross
于是你想到有以下两种计划:
1、在 axios 申请拦挡依据 环境变量
手动增加前缀, 然而这不是一种很好的计划,相当于把不确定性的逻辑代码打包到线下来了,有肯定危险
2、不论开发环境还是本地联调环境都是对立的门路,仅仅只是在 proxy
的pathRewrite
做解决,这样危险很小,不容易造成线上接口 404 危险
于是这时候 pathRewrite
的作用就来了, 重写门路,留神是pathRewrite: {'^/cmc': '/dev/cmc'}
咱们仅仅是在开发环境从新了 /cmc
接口门路,实际上代码环境的代码并不会打包到线上
{
devServer: {
proxy: {
'/j': {
target: 'https://movie.douban.com', // 代理豆瓣
changeOrigin: true,
},
'/cmc': {
target: 'https://apps.game.qq.com', // 代理王者光荣官网
changeOrigin: true, // 必须要加,否则代理接口间接返回 html
pathRewrite: {'^/cmc': '/dev/cmc'},
}
}
}
}
- 第三种,短少
changeOrigin:true
,像上面这种失落了changeOrigin
是不行的
devServer: {
proxy: {
'/j': {
target: 'https://movie.douban.com', // 代理豆瓣
// changeOrigin: true,
pathRewrite: {'^/j': '/j'},
},
'/cmc': {
target: 'https://apps.game.qq.com', // 代理王者光荣官网
//changeOrigin: true,
pathRewrite: {'^/cmc': '/dev/cmc'},
}
}
}
}
如果遇到有多个路由指向的是同一个服务器怎么办,别急,官网有计划,你能够这么做
{
devServer: {
proxy: [
{context: ['/j', '/cmc'],
target: 'https://movie.douban.com'
}
]
}
}
我的项目罕用的就是以上这些了,另外拓展的,比方能够反对本地 https
,因为默认本地是http
, 还有反对以后能够开启一个websocket
服务,更多配置参考官网,或者有更多特地的需要,及时翻阅官网
WDS 模块热加载原理(HMR)
只更新页面模块变动的内容,无需全站刷新
实质上就是 webpack-dev-server
中的两个服务,一个 express
提供的动态服务,通过 webpack
去compiler
入口的依赖文件,加载打包内存中的bundle.js
第二个模块热加载是一个 websocket
服务,通过 socketio
, 当源码动态文件发生变化时,此时会生成一个manifest
文件,这个文件会记录一个 hash
以及对应文件批改的 chunk.js
, 当文件批改时websocket
会独自向浏览器发送一个 ws
服务,从而更新页面局部模块,更多能够参考官网 hot-module-replacement
总结
- 理解
webpack-dev-server
是什么,它是一个开发环境的动态服务 webpack-dev-server
在 webpack 中的应用- 对于
WDS
一些罕用的配置,比方如何配置接口代理等 - 浅识
HMR
模块热加载,原生webpack
尽管也提供了模块热加载,然而webpack-dev-server
能够实现模块热加载,罕用框架,比方vue
,外部热加载是用vue-loader
实现的,在应用WDS
时,默认是开启了热加载的。
欢送关注公众号:Web 技术学苑
好好学习,天天向上!