文章目錄
  1. 1. 一. 源码分析
    1. 1.1. 1.1 依赖
    2. 1.2. 1.2 源码分析
      1. 1.2.1. 1.2.1 使用入口
      2. 1.2.2. 1.2.2 webpack-dev-server 主代码分析

一. 源码分析

1.1 依赖

Alt text

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));
文章目錄
  1. 1. 一. 源码分析
    1. 1.1. 1.1 依赖
    2. 1.2. 1.2 源码分析
      1. 1.2.1. 1.2.1 使用入口
      2. 1.2.2. 1.2.2 webpack-dev-server 主代码分析