一、初始化项目
二、devServer常用的配置
1、host: '0.0.0.0'
devServer.host配置项用于配置DevServer服务监听的地址,只能通 过命令行参数传入。host的默认值是 127.0.0.1,即只有本地可以访问DevServer的HTTP服务。若想让局域网中的其他设备访问自己的本地 服务,则可以在启动DevServer时带上--host 0.0.0.0。
2、open
devServer.open用于在DevServer启动且第一次构建完时,自动用我 们的系统的默认浏览器去打开要开发的网页。还提供了 devServer.openPage 配置项来打开指定 URL的网页。
3、inline
默认为true。它用来控制是否向 Chunk中注入代理客户端,默认会注入。事实上,在开启inline时, DevServer会向每个输出的Chunk中注入代理客户端的代码,当我们的项 目需要输出很多Chunk时,就会导致构建缓慢。其实要完成自动刷新, 一个页面只需要一个代理客户端,DevServer之所以粗暴地为每个Chunk都注入,是因为它不知道某个网页依赖哪几个 Chunk,索性全部都注入 一个代理客户端。网页只要依赖了其中任何一个Chunk,代理客户端就 被注入网页中。这里的优化思路是关闭还不够优雅的inline模式,只注入一个代理 客户端。
设置为false,需要访问网址:http://localhost:8080/webpack-dev-server/
要开发的网页被放进了一个iframe中,编辑源码后,iframe会被自 动刷新。要输出的Chunk数量越多,构建性能提升的效果越明显。
4、historyApiFallback
路由重定向,找不到路径的情况下,不会出现找不到页面的情况,会停留在当前页面。
没有配置前访问一个不存在的页面:
当使用 HTML5 History API 时,任意的 404
响应都可能需要被替代为 index.html
。通过传入以下启用:
historyApiFallback: true
配置后访问一个不存在的页面,会依然停留在当前页面:
配置也支持传递一个对象:
historyApiFallback: {
rewrites: [
{ from: /^\/$/, to: '/views/landing.html' },
{ from: /^\/subpage/, to: '/views/subpage.html' },
{ from: /./, to: '/views/404.html' }
]
}
to也可以是一个函数:
historyApiFallback: {
rewites: [
{
from: /^\/([-~]+)/,
to: function(context) {
return './'+context.match[1]+'html'
}
}
]
},
5、lazy
优化打包速度,多入口情况下需要开启,只有被访问的入口文会进行编译,不开启的话会把所有入口都编译。
6、overlay
把错误显示在页面上,遮罩显示。
7、hot
hot:true, 修改css文件页面不刷新,只更新。修改js文件会发生reload更新,也就是页面刷新。
8、hotOnly
设置为true,禁用掉所有的页面刷新,只使用模块热更新。
9、port
指定要监听请求的端口号,默认8080
port: 8081
webpack-dev-server --port 8081
10、proxy
proxy: {
"/api": "http://localhost:3000"
}
请求到 /api/users
现在会被代理到请求 http://localhost:3000/api/users
。
如果你不想始终传递 /api
,则需要重写路径:
proxy: {
"/api": {
target: "http://localhost:3000",
pathRewrite: {"^/api" : ""}
}
}
请求到 /api/users
现在会被代理到请求 http://localhost:3000/users
。
对于浏览器请求,你想要提供一个 HTML 页面,但是对于 API 请求则保持代理。你可以这样做:
proxy: {
"/api": {
target: "http://localhost:3000",
bypass: function(req, res, proxyOptions) {
if (req.headers.accept.indexOf("html") !== -1) {
console.log("Skipping proxy for browser request.");
return "/index.html";
}
}
}
}
支持匹配多个
proxy: {
'/': { // 匹配所有的请求
target:'https://mooc.study.163.com', // /smartSpec/detail/1202816603.html
changeOrigin: true,
},
'/smartSpec': { // 匹配 /smartSpec开头的
target:'https://mooc.study.163.com', // /smartSpec/detail/1202816603.html
changeOrigin: true,
pathRewrite: {
'^/smartSpec/qd':"smartSpec/detail/1202816603.html" // 请求这个/smartSpec/qd 就代表请求 /smartSpec/detail/1202816603.html
}
},
}
三、webpack-dev-server的原理
- 当运行 webpack-dev-server的时候会执行webpack-dev-server.js文件,这个是webpack-dev-server/bin中的文件。它调用webpack的api,传递config,生成compiler,compiler = webpack(config);再传递给server = new Server(compiler, options, log);Server在 webpack-dev-server/lib中的Server.js中定义,这个文件主要负责开启本地服务,设置静态文件路径;但是在执行webpack后,并没有看到有打包后的文件输出。打开浏览器是怎么访问到需要的资源的呢?是因为webpack-dev-middleware
- webpack-dev-middleware中开启了webpack的监听模式:监听资源的变更,然后自动打包。资源文件变化会重新打包,然后把文件放置在内存当中不输出文件,输入输入内存比较快。
context.watching = compiler.watch(options.watchOptions, (err) => {
if (err) {
context.log.error(err.stack || err);
if (err.details) {
context.log.error(err.details);
}
}
});
webpack-dev-middleware的主入口文件,返回的是一个中间件:
module.exports = function wdm(compiler, opts) {
const options = Object.assign({}, defaults, opts);
// defining custom MIME type
if (options.mimeTypes) {
const typeMap = options.mimeTypes.typeMap || options.mimeTypes;
const force = !!options.mimeTypes.force;
mime.define(typeMap, force);
}
const context = createContext(compiler, options);
// start watching
if (!options.lazy) {
context.watching = compiler.watch(options.watchOptions, (err) => {
if (err) {
context.log.error(err.stack || err);
if (err.details) {
context.log.error(err.details);
}
}
});
} else {
if (typeof options.filename === 'string') {
const filename = options.filename
.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&') // eslint-disable-line no-useless-escape
.replace(/\\\[[a-z]+\\\]/gi, '.+');
options.filename = new RegExp(`^[/]{0,1}${filename}$`);
}
context.state = true;
}
if (options.writeToDisk) {
toDisk(context);
}
setFs(context, compiler);
return Object.assign(middleware(context), {
close(callback) {
// eslint-disable-next-line no-param-reassign
callback = callback || noop;
if (context.watching) {
context.watching.close(callback);
} else {
callback();
}
},
context,
fileSystem: context.fs,
getFilenameFromUrl: getFilenameFromUrl.bind(
this,
context.options.publicPath,
context.compiler
),
invalidate(callback) {
// eslint-disable-next-line no-param-reassign
callback = callback || noop;
if (context.watching) {
ready(context, callback, {});
context.watching.invalidate();
} else {
callback();
}
},
waitUntilValid(callback) {
// eslint-disable-next-line no-param-reassign
callback = callback || noop;
ready(context, callback, {});
},
});
};
简化一下webpack-dev-server如下:
const express = require('express')
const webpackDevMid = require('webpack-dev-middleware');
const webpackHotMid = require('webpack-hot-middleware')
const webpack = require('webpack');
const app = express()
const config = require('./webpack.common')
Object.keys(config.entry).forEach(function(name) {
config.entry[name] = ['webpack-hot-middleware/client?noinfo=true&reload=true'].concat(config.entry[name])
})
const compiler = webpack(config)
app.use(webpackDevMid(compiler, {}))
app.use(webpackHotMid(compiler, {
overlayStyles: true
}))
app.listen(2000)