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;
相关推荐
祈澈菇凉5 小时前
webpack和grunt以及gulp有什么不同?
前端·webpack·gulp
AsBefore麦小兜10 小时前
Vite vs Webpack
前端·webpack
垣宇1 天前
Vite 和 Webpack 的区别和选择
前端·webpack·node.js
小纯洁w2 天前
Webpack 的 require.context 和 Vite 的 import.meta.glob 的详细介绍和使用
前端·webpack·node.js
海盗强2 天前
Webpack打包优化
前端·webpack·node.js
祈澈菇凉2 天前
如何优化 Webpack 的构建速度?
前端·webpack·node.js
懒羊羊我小弟2 天前
常用 Webpack Plugin 汇总
前端·webpack·npm·node.js·yarn
祈澈菇凉3 天前
Webpack的持久化缓存机制具体是如何实现的?
前端·webpack·gulp
懒羊羊我小弟4 天前
Webpack 基础入门
前端·webpack·rust·node.js·es6
刽子手发艺4 天前
Selenium+OpenCV处理滑块验证问题
opencv·selenium·webpack