The full text is 3000 words, welcome to like and forward
The thing is like this, a asked me one day: 16164f338c51c5 Why can't see the page I wrote after running my webpack, but:
Um? File list page? Well, I don’t seem to have encountered this situation before, and I can’t give an answer at once, I can only come to the key code:
Focus on the configuration of devServer + HMR
webpack.config.js
is used, among which:
- Webpack version is 5.37.0
- webpack-dev-server version is 3.11.2
After watching for a long time, there is no problem. I gave a few papery suggestions and still couldn't solve the problem, so I just put it down for the time being at the meeting. After a while, the little friend rushed over and told me that after some blind guessing, the problem was solved:
output.publicPath = '/'
is ok at 06164f338c529boutput.publicPath = './'
, return to the file list page
Ah? This thing will also affect the effect of devServer
, and my instinct tells me it shouldn't.
emmm, I have successfully aroused my curiosity. Although I have written some articles about Webpack source code analysis, webpack-dev-server
is really out of my knowledge. Fortunately, I have a book "16164f338c5308 How to read the source code-taking Vetur as an example ", yes Time to show the real technology!
Step 1: Define the problem
First review the process of the problem:
webpack.config.js
is configured with bothouput.publicPath
anddevServer
- Run
npx webpack serve
start the development server - Browser access
http://localhost:9000
did not return the user code as expected, but returned to the file list page; but ifoutput.publicPath
is restored, everything is as usual
ouput.publicPath
be reasonable, 06164f338c53e9 should only affect the path referenced by the final product. Try the command line tool to run curl
check the content returned on the homepage:
Tips: Sometimes you can try to bypass the complex logic of the browser and use the simplest tool to verify the content returned by the http request.
As you can see, requesting the http://localhost:9000
returns a large string of html codes, and the title of the page is listing directory
-which is the file list page we see:
Although I don't know at which layer it was generated, I am definitely not writing it, and it happened at the HTTP level.
So the core issue is: why Webpack of output.publicPath
affect webpack-dev-server
operating results of ?
Step 2: Review the background
With questions, I reviewed the official Webpack documentation.
publicPath
configuration
First of all, output.publicPath
is described as follows:
This is an important option when using on-demand-loading or loading external resources like images, files, etc. If an incorrect value is specified you'll receive 404 errors while loading these resources.
To the effect, this is an option to control on-demand loading or resource file loading. If the corresponding path resource fails to load, it will return 404.
Hey, in fact, this description is very unclear. A simple understanding of output.publicPath
will change the path of the product resource in the html file. For example, after Webpack is compiled, the bundle.js
file is generated. By default, the path to html is:
<script src="bundle.js" />
If the output.publicPath
set, the prefix will be added before the path:
<script src="${output.publicPath}/bundle.js" />
It seems very simple.
devServer
configuration item
Let's take a look at the devServer
configuration:
This set of options is picked up by webpack-dev-server and can be used to change its behavior in various ways.
devServer
is that the 06164f338c5697 configuration will eventually be by 16164f338c569a webpack-dev-server , and webpack-dev-server provides web services including HMR-module hot update.
Feel that the bottom layer of scaffolding tools such as vue-cli and create-react-app depend on webpack-dev-server . You can imagine its role and importance.
Step 3: Analyze the problem
According to the existing intelligence and my understanding of the HTTP protocol, it can be basically inferred that the problem must 16164f338c56f0 webpack-dev-server framework that processes the homepage request. There is a high probability that the output.publicPath
attribute affects the judgment logic of the homepage resource. , Resulting in webpack-dev-server can not find the corresponding resource file, return to the bottom file list page.
Well, I think it's reliable, so dig the source code along this line of thought to find out the specific reason.
Step 4: Analyze the code
Structural analysis
The book should be very shallow. You need to look at the source code for debugging. Don’t say anything, first open the 16164f338c5738 webpack-dev-server package to see the content:
Tips: Readers can also try clone webpack-dev-server warehouse code, there are surprises\~\~
The project structure is not complicated. According to the habits of Webpack, it can be inferred that the main code is in the lib
directory:
cloc
is a very easy to use code statistics tool, official website: https://www.npmjs.com/package/cloc
The amount of code is in the early 2000s, which is fine.
Next, open the package.json
file again to see which dependency
. After one by one, the dependencies that are strongly related to our problem are:
express
: No need to introduce the applicationwebpack-dev-middleware
: This should not have been noticed by most people. Judging from the official website document, this is a middleware that bridges the Webpack compilation process and express.serve-index
: for the file list page in a specific directory! ! !
According to this description, this pot must be serve-index
, which feels very close to the answer.
Partial analysis
Entry point: verify the role of the serve-index
After the above analysis, although I don't know where the problem is, I can roughly determine that it is serve-index
package. First search for where the webpack-dev-server references this package:
Fortunately, it is only used in the lib/Server.js
file. It is much simpler. The static analysis of the call statement can roughly deduce:
serveIndex
call is wrapped inthis.app.use
. It is speculated thatthis.app
points to the express example. Theuse
function is used to register the middleware, so the entireserveIndex
is a middleware- In addition to
setupStaticServeIndexFeature
, theServer
type also contains othersetupXXXFeature
, which are basically used to add express middleware. The combination of these middleware assembles the HMR, proxy, ssl and other functions provided by webpack-dev-server.
I don’t see anything else. Let’s do a control experiment first, and run dynamically analyze code to verify if there is an error in this place. First insert the debugger
serveIndex
function, and then:
- First execute
ndb npx webpack serve
according to the normal situation, that is,output.publicPath = '/'
, the result is that the page is opened as usual, no interruption point is hit, no interruption - Then execute
ndb npx webpack serve
according toouput.publicPath = './'
enter the breakpoint:
Tips: ndb is an out-of-the-box node debugger tool. It is very convenient to debug node applications without any configuration.
OK, the answer is revealed. In the ouput.publicPath = './'
scenario, this middleware will be hit, and the serveIndex
function will be executed to return the file directory list, which is very sense.
However, as a pursuing programmer, how can you stop here? Let's continue digging down: What piece of code determines whether the process will enter the serveIndex
middleware?
Entry point: determine the upstream middleware of serveIndex
Think about it, the characteristic of the express architecture is-based on the onion model of middleware, and the next middleware is next
function between the middleware.
Well, there is an idea. We follow the middleware queue of webpack-dev-server and find serveIndex
what middleware is available before 06164f338c5b54. Analysis of the code of these middleware should be able to answer:
Which piece of code determines whether the process will enter the serveIndex
middleware?
However, under the express middleware architecture, there next
call to the actual middleware function. It is difficult to judge the upper-level middleware through the call stack of the breakpoint, and the higher-level middleware is in where:
At this time, you can't be rigid, you have to change a technique-find the code to create the express example, and use magic to wrap the use
function:
Tips: This technique is particularly useful in some complex scenarios. For example, when I was learning the Webpack source code, I often used the Proxy
class to implant debugger statements on the hook to track who the hook was monitored and where it was triggered.
Through this method of rewriting functions and implanting breakpoints, we can easily trace back which middleware is used by webpack-dev-server and the order of middleware registration:
setupCompressFeature => 注册资源压缩中间件
setupMiddleware => 注册 webpack-dev-middleware 中间件
setupStaticFeature => 注册静态资源服务中间件
setupServeIndexFeature => 注册 serveIndex 中间件
You can see a total of four registered middleware functions in the current configuration Webpack, four middleware will be executed according to the registration order from top to bottom in accordance with the execution logic express, so serveIndex
immediately upstream function is setupStaticFeature
registered static Resource service middleware.
Continue to look at the code of the setupStaticFeature
Here is just calling the standardized [express.static](https://expressjs.com/en/starter/static-files.html)
function to inject the static resource service function. If the corresponding file resource cannot be found according to the path when this middleware is running, the next middleware will be called to continue processing the request, which seems to have nothing to do with our problem. .
Go on and look at the setupMiddleware
function:
Registered webpack-dev-middleware
, you can see from the name that this middleware webpack-dev-server
to do with 06164f338c5cbe, then continue to open webpack-dev-middleware
see the code inside:
I go. . . It's not too much, it seems too hard, I just want to find the cause of this bug, there is no need to read it all! Then try to search for the keyword publicPath
Fortunately, publicPath
keyword is still relatively small:
webpack-dev-middleware/lib/middleware.js
is used 1 time in the filewebpack-dev-middleware/lib/util.js
is used 23 times in the file
Then, first pick the soft persimmon and see how it is used in the middleware.js
const { getFilenameFromUrl } = require('./util');
module.exports = function wrapper(context) {
return function middleware(req, res, next) {
function goNext() {
// ...
resolve(next());
}
// ...
let filename = getFilenameFromUrl(
context.options.publicPath,
context.compiler,
req.url
);
if (filename === false) {
return goNext();
}
return new Promise((resolve) => {
handleRequest(context, filename, processRequest, req);
// ...
});
};
};
Note that the code in a logical, is to call util
file getFilenameFromUrl
function, and determine the return filename
whether the value of false
, is then invoked next
function, it looks like that story!
Then go in and take a look at the code of getFilenameFromUrl
Analyze it line by line, and pay attention to the sentence in the red box:
if(xxx && url.indexOf(publicPath) !== 0){
return false;
}
Reason, from the literal sense of the url
should be sent, the client requests a connection, publicPath
should be that we webpack.config.js
-configured output.publicPath
value of the item, right? Let's run it and see:
Sure enough, after entering the breakpoint, you can see that these two values do indeed conform to the previous conjecture. The problem lies here. At this time:
url = '/`'
publicPath = output.publicPath = '/helloworld'
- So
url.indexOf(publicPath) === false
real hammer
getFilenameFromUrl
function execution result is false
, so webpack-dev-middleware
will directly call the next
method to enter the next middleware.
If you manually add the content of output.publicPath
after the path opened by default:
Sure enough, it worked again.
Step 5: Summary
Hey, you see, this is the process of source code analysis. It is tedious but not complicated. Everyone can become a great technologist. Review the flow of the code:
webpack-dev-server
started, it will call to automatically open the browser to access the default pathhttp://localhost:9000
- At this time,
webpack-dev-server
receives the default path request, and gradually walks along the express logic to thewebpack-dev-middleware
middleware webpack-dev-middleware
middleware, it continues to call thegetFilenameFromUrl
method of thewebpack-dev-middleware/lib/util.js
getFilenameFromUrl
Internal judgmenturl.indexOf(publicPath)
- If
getFilenameFromUrl
returnsfalse
thenwebpack-dev-middleware
directly callsnext
, and the process enters the next middlewareexpress.static
express.static
tried to readhttp://localhost:9000
, and found that the file does not exist, the process continues to the last middlewareserveIndex
serveIndex
returns to the product directory structure interface, which does not meet the developer's expectations
In the final analysis, the problem here:
- The introduction on the Webpack official website about
output.publicPath
only said that it would affect the bundle product path, but did not say that it would affect the index path of the main page. The developer said it was very confuse. webpack-dev-server
started, when the page is automatically opened, theoutput.publicPath
value is not automatically appended to the link. The default opened path is inconsistent with the real index homepage, and it has not returned to 404 and other general error prompts, instead of an unknown file On the list page , it is difficult for developers to quickly get to where the problem is
At this point, I have dug up the problem from the appearance, to the principle, to the most fundamental problem, and I can tell other students in the future:
During the development phase, try to avoid configuring output.publicPath
, otherwise there will be surprises\~\~
Truly·Summary
The entire debug process took about half an hour, and the document was written in the middle of the night. . . I thought I would die 1000 words, but I wrote 3000+ in the end. . .
However, the process is indeed used the " how to read the source code - for example to Vetur " processes and techniques mentioned:
- Clearly define the goal first
- Review the background and understand the key knowledge points
- Then define the entry point
- Analyze the code structure again and guess where the problem may be
- Then we will analyze it in depth and decipher it layer by layer until the root of the problem.
It can be regarded How to read the source code-taking Vetur as an example ". I hope that readers can think about it, and everyone can do source code analysis.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。