After doing some debugging by putting breakpoints in the dependency modules I finally understood how it’s working.
First of all, app.use([path], callbackFn[, callbackFn]) will consume the prefix if the request url matches. That is, app.use('/public', callbackFn), will simply consume the /public if the GET(in this case) request is /public/style.css. The value of req.url becomes /style.css.
Secondly, express. static(path, [option]) use the provided path parameter as the base path for the static resources and appends to it the req.url value to serve the static assets.
Putting these together, when I use the app.use(express.static('./public')) and the GET request is /public/style.css, the file path becomes ../proj_dir/public/public/style.css, which doesn’t work because the style.css exists in the ../proj_dir/public/.
app.use('/public', express.static('./public')) works because the prefix of the url is now removed(thanks to the way the .use method works in nodejs), and the file path becomes ../proj_dir/public/style.css