webpack源码分析——enhanced-resolve库之getType、normalize、join和cachedJoin函数

一、PathType 路径类型

js 复制代码
const PathType = Object.freeze({
	Empty: 0, // 空
	Normal: 1, // 默认值
	Relative: 2, // 相对路径
	AbsoluteWin: 3, // win 下的绝对路径
	AbsolutePosix: 4, // posix 下的绝对路径
	Internal: 5 // enhanced-resolve 内部自定义的一种类型,具体是用来 escaping,具体说明:https://www.npmjs.com/package/enhanced-resolve
});

注:什么是Posix?点击查看path

函数中用到的变量定义

js 复制代码
const path = require("path");

const CHAR_HASH = "#".charCodeAt(0);
const CHAR_SLASH = "/".charCodeAt(0);
const CHAR_BACKSLASH = "\\".charCodeAt(0);
const CHAR_A = "A".charCodeAt(0);
const CHAR_Z = "Z".charCodeAt(0);
const CHAR_LOWER_A = "a".charCodeAt(0);
const CHAR_LOWER_Z = "z".charCodeAt(0);
const CHAR_DOT = ".".charCodeAt(0);
const CHAR_COLON = ":".charCodeAt(0);

const posixNormalize = path.posix.normalize; 
const winNormalize = path.win32.normalize;

二、getType 函数

该函数通过传入的路径,判断并返回其类型值。返回的类型值为PathType中定义的值之一

  1. 根据传入的路径length判断
js 复制代码
switch (p.length) {
		case 0:
			return PathType.Empty; // 当length ==0 时, 返回 PathType.Empty
		case 1: {
			const c0 = p.charCodeAt(0);
			switch (c0) {
				case CHAR_DOT:
					return PathType.Relative; // 如果开头的第一个字符为'.'时,返回PathType.Relative
				case CHAR_SLASH:
					return PathType.AbsolutePosix; // 如果开头的第一个字符为'/'时,返回PathType.AbsolutePosix
				case CHAR_HASH:
					return PathType.Internal; // 如果开头的第一个字符为'#'时,返回PathType.Internal
			}
			return PathType.Normal; // 没有匹配到返回兜底值PathType.Normal
		}
		case 2: {
			const c0 = p.charCodeAt(0);
			switch (c0) {
				case CHAR_DOT: { // 当第一个字符为'.'
					const c1 = p.charCodeAt(1);
					switch (c1) {
						case CHAR_DOT:
						case CHAR_SLASH:
							return PathType.Relative; // 当第二个字符为'.'或'/'时。返回PathType.Relative
					}
					return PathType.Normal; // 没有匹配到返回兜底值PathType.Normal
				}
				case CHAR_SLASH:
					return PathType.AbsolutePosix; // 当第二个字符为'/'时。返回PathType.AbsolutePosix
				case CHAR_HASH:
					return PathType.Internal; // 当第二个字符为'#'时。返回PathType.Internal
			}
			const c1 = p.charCodeAt(1);
			if (c1 === CHAR_COLON) {  // 判断是否时win平台
				if (
					(c0 >= CHAR_A && c0 <= CHAR_Z) ||
					(c0 >= CHAR_LOWER_A && c0 <= CHAR_LOWER_Z)
				) {
					return PathType.AbsoluteWin; // 是 win 返回PathType.AbsoluteWin
				}
			}
			return PathType.Normal; // 没有匹配到返回兜底值PathType.Normal
		}
	}
  1. 当路径length大于2时
js 复制代码
	const c0 = p.charCodeAt(0); // 获取第一个字符
	switch (c0) {
		case CHAR_DOT: {
			const c1 = p.charCodeAt(1);
			switch (c1) {
				case CHAR_SLASH:
					return PathType.Relative; // 当第一个字符为'.'第二个字符为'/'时,返回PathType.Relative
				case CHAR_DOT: {
					const c2 = p.charCodeAt(2);
					if (c2 === CHAR_SLASH) return PathType.Relative; // 当第一个字符为'.'第二个字符为'.'和第三个字符为'/'时,返回PathType.Relative
					return PathType.Normal; // 没有匹配到返回兜底值PathType.Normal
				}
			}
			return PathType.Normal;// 没有匹配到返回兜底值PathType.Normal
		}
		case CHAR_SLASH:
			return PathType.AbsolutePosix; // 当第一个字符为'/'时,返回PathType.AbsolutePosix
		case CHAR_HASH:
			return PathType.Internal;// 当第一个字符为'#'时,返回PathType.Internal
	}
	const c1 = p.charCodeAt(1);
	if (c1 === CHAR_COLON) { // 判断是否在win下,并且为绝对路径
		const c2 = p.charCodeAt(2);
		if (
			(c2 === CHAR_BACKSLASH || c2 === CHAR_SLASH) &&
			((c0 >= CHAR_A && c0 <= CHAR_Z) ||
				(c0 >= CHAR_LOWER_A && c0 <= CHAR_LOWER_Z))
		) {
			return PathType.AbsoluteWin;
		}
	}
	return PathType.Normal;// 没有匹配到返回兜底值PathType.Normal

例1:win上的绝对路径

例2:posix上的绝对路径

三、normalize函数

该函数通过调用node的path.normalize方法规范化给定的 path

  1. 对传入的路径调用getType函数根据返回值,对Empty、AbsoluteWin和Relative三种类型进行处理

    js 复制代码
    switch (getType(p)) {
       case PathType.Empty:
       case PathType.AbsoluteWin:
       case PathType.Relative: 
    }
  2. 当为路径类型为PathType.Empty

    js 复制代码
    return p; // 直接返回
  3. 当为路径类型为PathType.AbsoluteWin

    js 复制代码
    return winNormalize(p); // path.win32.normalize
  4. 当为路径类型为PathType.Relative

    js 复制代码
    const r = posixNormalize(p); // path.posix.normalize
    return getType(r) === PathType.Relative ? r : `./${r}`; // 因为 './webpack' 这样的路径被 posixNormalize 函数处理后 变为 'webpack',所有需要这一行进行特殊处理

源码

js 复制代码
const normalize = p => {
	switch (getType(p)) {
		case PathType.Empty:
			return p;
		case PathType.AbsoluteWin:
			return winNormalize(p);
		case PathType.Relative: {
			const r = posixNormalize(p);
			return getType(r) === PathType.Relative ? r : `./${r}`;
		}
	}
	return posixNormalize(p);
};

四、join函数

该函数进行路径进行拼接

  1. 如果 request 变量没有传入

    js 复制代码
    if (!request) return normalize(rootPath); // 直接调用normalize 函数返回
  2. 根据rootPath和request类型判断

    js 复制代码
    const requestType = getType(request); // 首先判断 requestType 类型
    switch (requestType) { // 如果时绝对路径,就不需要拼接了,直接调用 posixNormalize/winNormalize  返回
    	case PathType.AbsolutePosix:
    		return posixNormalize(request);
    	case PathType.AbsoluteWin:
    		return winNormalize(request);
    }
    switch (getType(rootPath)) { // 判断 rootPath 类型,上面 request 类型已经排除了绝对路径的情况,所有判断 rootPath 类型后直接和request进行拼接
    	case PathType.Normal:
    	case PathType.Relative:
    	case PathType.AbsolutePosix:
    		return posixNormalize(`${rootPath}/${request}`);
    	case PathType.AbsoluteWin:
    		return winNormalize(`${rootPath}\\${request}`);
    }
    /**
      * request 类型不为 AbsolutePosix和AbsoluteWin
      * rootPath 类型不为 Normal、Relative、AbsolutePosix和AbsoluteWin时
      * 进入下面阶段
      */
    switch (requestType) {
    	case PathType.Empty: // request 为空时(这里不存在因为在函数顶部已经错了空的判断),直接返回 rootPath。但是 rootPath 也有可能为空
    		return rootPath;
    	case PathType.Relative: {
    		const r = posixNormalize(rootPath);
    		return getType(r) === PathType.Relative ? r : `./${r}`;
    	}
    }
  3. 兜底

    js 复制代码
    return posixNormalize(rootPath);

源码

js 复制代码
const join = (rootPath, request) => {
	if (!request) return normalize(rootPath);
	const requestType = getType(request);
	switch (requestType) {
		case PathType.AbsolutePosix:
			return posixNormalize(request);
		case PathType.AbsoluteWin:
			return winNormalize(request);
	}
	switch (getType(rootPath)) {
		case PathType.Normal:
		case PathType.Relative:
		case PathType.AbsolutePosix:
			return posixNormalize(`${rootPath}/${request}`);
		case PathType.AbsoluteWin:
			return winNormalize(`${rootPath}\\${request}`);
	}
	switch (requestType) {
		case PathType.Empty:
			return rootPath;
		case PathType.Relative: {
			const r = posixNormalize(rootPath);
			return getType(r) === PathType.Relative ? r : `./${r}`;
		}
	}
	return posixNormalize(rootPath);
};

五、cachedJoin函数

该函数在 join 函数的基础上加上缓存

  1. 判断 rootPath 是否在缓存中

    js 复制代码
    const joinCache = new Map();
    
    let cache = joinCache.get(rootPath); // 从 map 中获取 rootPath
    if (cache === undefined) { // rootPath 没有在缓存中
       joinCache.set(rootPath, (cache = new Map())); // 新增缓存
    } else {
       cacheEntry = cache.get(request); // 在缓存中,根据request获取rootPath对应缓存的值
       if (cacheEntry !== undefined) return cacheEntry;
    }
    cacheEntry = join(rootPath, request);
    cache.set(request, cacheEntry); // 没有在缓存中时,对当前路径进行缓存
    return cacheEntry;
相关推荐
liangshanbo12156 天前
面试题:Webpack 的 publicPath 有什么作用?
前端·webpack·node.js
liangshanbo12158 天前
Webpack vs Vite 底层原理高频追问的问题
前端·webpack·node.js
liangshanbo12158 天前
面试题:AI 对话上下文超限怎么办?
面试·webpack·devops
liangshanbo12159 天前
Webpack 文件指纹面试题整理
前端·webpack·node.js
liangshanbo12159 天前
Webpack 常见 Plugin 面试题
前端·webpack·node.js
liangshanbo12159 天前
Webpack Tree Shaking 面试题整理
前端·webpack·node.js
陪我一起学编程9 天前
vue3笔记
前端·webpack·vue3·vite·脚手架·vue3笔记·组件式
liangshanbo12159 天前
Webpack的分包策略面试题
前端·webpack·node.js
liangshanbo121510 天前
面试题:如何优化 Webpack 的打包速度?
前端·webpack·node.js
晚风醉蝶12 天前
webpack 模块提取
前端·webpack·node.js·ast·逆向分析