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;
相关推荐
王解12 小时前
【Webpack配置全解析】打造你的专属构建流程️(4)
前端·webpack·node.js
几何心凉2 天前
Webpack 中无法解析别名路径的原因及解决方案
运维·前端·webpack
friend_ship3 天前
Vue Cli的配置中configureWebpack和chainWebpack的主要作用及区别是什么?
vue.js·webpack·chainwebpack
friend_ship3 天前
Vite与Vue Cli的区别与详解
vue.js·webpack·rollup·vite·vue脚手架·vue cli
Man4 天前
webpack分包的几种方式和优缺点
前端·webpack
熊的猫4 天前
webpack 核心模块 — loader & plugins
前端·javascript·chrome·webpack·前端框架·node.js·ecmascript
理想不理想v4 天前
vue种ref跟reactive的区别?
前端·javascript·vue.js·webpack·前端框架·node.js·ecmascript
~甲壳虫5 天前
说说webpack中常见的Plugin?解决了什么问题?
前端·webpack·node.js
Beamon__5 天前
element-plus按需引入报错AutoImport is not a function
webpack·element-plus
CodeToGym5 天前
Webpack性能优化指南:从构建到部署的全方位策略
前端·webpack·性能优化