webpack-dev-server
更新日期:
一. 源码分析
1.1 依赖
1.2 源码分析
1.2.1 使用入口
const compiler = webpack(wpConfig); // webpack 编译器;wpConfig是webpack的配置文件 const server = new WebpackDevServer(compiler, { contentBase: wpConfig.context, publicPath: '/agent-asset/', hot: true, quiet: false, noInfo: false, watchOption: { aggregateTimeout: 300, // 延迟rebuild poll: true }, headers, // 设置res的头,一般可以不设置 stats: { // 一些console提示的配置,可忽略 // chunks: false, colors: true }, historyApiFallback: { // spa的historyback,可忽略 index: 'index.html' }, proxy: [ { path: /^\/MOD/g, target: "http://192.168.1.224:8080" }, { path: /^\/ADD/g, target: "http://192.168.1.224:8080" }, { path: /^\/GET/g, target: "http://192.168.1.224:8080" } ] // 代理配置, 也可以是个object }); // 监听8080端口,这里,很多文档里写成 server.listen(8080, 'localhost', function () { .... // 这样导致的问题是,localhost换成本机ip时,无法访问。 server.listen(8080, function () { console.log('Webpack-Dev-Server: started on port 8080'); }); |
1.2.2 webpack-dev-server 主代码分析
问题一:webpack-dev-server 本质是用express启的一个server
.......
// 在lib/Server.js的48行左右
// 使用express 启动一个service
var app = this.app = new express();
// 这行很重要!!use了webpack-dev-middleware这个中间件!!!
// compiler是webpack的编译器,是我们在1.2.1中传入的:compiler = webpack(wpConfig);
app.use(this.middleware = webpackDevMiddleware(compiler, options));
........
问题二:webpack-dev-server 怎么实现代理的
// 在112行左右,
if (options.proxy) {
if (!Array.isArray(options.proxy)) { // 如果proxy是object
options.proxy = Object.keys(options.proxy).map(function (path) {
var proxyOptions;
if (typeof options.proxy[path] === 'string') {
proxyOptions = {path: path, target: options.proxy[path]};
} else {
proxyOptions = options.proxy[path];
proxyOptions.path = path;
}
return proxyOptions;
});
}
options.proxy.forEach(function (proxyOptions) {
proxyOptions.ws = proxyOptions.hasOwnProperty('ws') ? proxyOptions.ws : true;
// 下面代码的含义是:app拦截能匹配proxyOptions.path的请求,然后,去代理服务器上获取数据等
app.all(proxyOptions.path, function (req, res) {
if(typeof proxyOptions.rewrite === 'function') proxyOptions.rewrite(req, proxyOptions);
if (proxyOptions.host) {
req.headers.host = proxyOptions.host;
}
// proxy 是一个代理服务器:var proxy = new httpProxy.createProxyServer();
proxy.web(req, res, proxyOptions, function(err){
var msg = "cannot proxy to " + proxyOptions.target + " (" + err.message + ")";
this.io.sockets.emit("proxy-error", [msg]);
res.statusCode = 502;
res.end();
}.bind(this));
if (proxyOptions.configure) {
proxyOptions.configure(proxy);
}
}.bind(this));
}.bind(this));
}
问题三:webpack-dev-server 怎么获取entry.js|entry.css|common.bundle.js的?
请看webpack-dev-middleware中的middleware.js
a.启动webpack的编译
// 大约在30行
// store our files in memory
// 这行很重要!生成的js文件,是存在内存中的!!
// outputFileSystem 其实就是个object,
// eg:{data:{'entry.js': 2进制流}}
var files = {};
var fs = compiler.outputFileSystem = new MemoryFileSystem();
// start watching 大约在100行
// 启动编译和监听
if(!options.lazy) {
// compiler 是一个webpack的编译器
// compiler.watch 开始启动监听,以及开始第一次编译
var watching = compiler.watch(options.watchOptions, function(err) {
if(err) throw err;
});
} else {
state = true;
}
b.如何获取获取entry.js|entry.css|common.bundle.js的
// 大约在143行
function webpackDevMiddleware(req, res, next) {
var filename = getFilenameFromUrl(req.url);
if (filename === false) return next();
// in lazy mode, rebuild on bundle request
if(options.lazy && (!options.filename || options.filename.test(filename)))
rebuild();
// delay the request until we have a vaild bundle
ready(function() {
try {
var stat = fs.statSync(filename);
if(!stat.isFile()) {
if (stat.isDirectory()) {
filename = path.join(filename, "index.html");
stat = fs.statSync(filename);
if(!stat.isFile()) throw "next";
} else {
throw "next";
}
}
} catch(e) {
return next();
}
// server content
// 从内存中,拿到相应文件的内容,返回,这里的fs其实是a问题中的compiler.outputFileSystem
var content = fs.readFileSync(filename);
res.setHeader("Access-Control-Allow-Origin", "*"); // To support XHR, etc.
res.setHeader("Content-Type", mime.lookup(filename));
res.setHeader("Content-Length", content.length);
if(options.headers) {
for(var name in options.headers) {
res.setHeader(name, options.headers[name]);
}
}
res.end(content);
}, req);
}
问题四:如何获得index.html的?
// 在webpack-dev-server/lib/Server 的190行
// express.static(contentBase) 如果你没有设置contentBase,express会在你项目的根目录,读取index.html
app.get("*", this.setContentHeaders.bind(this), this.serveMagicHtml.bind(this), express.static(contentBase), serveIndex(contentBase));