webpack源码分析1
Posted zhuanglog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了webpack源码分析1相关的知识,希望对你有一定的参考价值。
1、打包后文件分析
(function (modules) { // webpackBootstrap
// The module cache
//缓存加载的模块
var installedModules = {};
// The require function
// 下面的这个方法就是 webpack 当中自定义的,它的核心作用就是返回模块的 exports
function __webpack_require__(moduleId) {
// Check if module is in cache
if (installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// Create a new module (and put it into the cache)
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
// Execute the module function,执行的是传进来的对应键值函数,执行之后会把结果挂载到module.export身上
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// Flag the module as loaded
module.l = true;
// Return the exports of the module
return module.exports;
}
// expose the modules object (__webpack_modules__)
__webpack_require__.m = modules;
// expose the module cache
__webpack_require__.c = installedModules;
// define getter function for harmony exports
__webpack_require__.d = function (exports, name, getter) {
if (!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, { enumerable: true, get: getter });
}
};
// define __esModule on exports
__webpack_require__.r = function (exports) {
if (typeof Symbol !== \'undefined\' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: \'Module\' });
}
Object.defineProperty(exports, \'__esModule\', { value: true });
};
// create a fake namespace object
// mode & 1: value is a module id, require it
// mode & 2: merge all properties of value into the ns
// mode & 4: return value when already ns object
// mode & 8|1: behave like require
__webpack_require__.t = function (value, mode) {
if (mode & 1) value = __webpack_require__(value);
if (mode & 8) return value;
if ((mode & 4) && typeof value === \'object\' && value && value.__esModule) return value;
var ns = Object.create(null);
__webpack_require__.r(ns);
Object.defineProperty(ns, \'default\', { enumerable: true, value: value });
if (mode & 2 && typeof value != \'string\') for (var key in value) __webpack_require__.d(ns, key, function (key) { return value[key]; }.bind(null, key));
return ns;
};
// getDefaultExport function for compatibility with non-harmony modules
__webpack_require__.n = function (module) {
var getter = module && module.__esModule ?
function getDefault() { return module[\'default\']; } :
function getModuleExports() { return module; };
__webpack_require__.d(getter, \'a\', getter);
return getter;
};
// Object.prototype.hasOwnProperty.call
__webpack_require__.o = function (object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
// __webpack_public_path__
__webpack_require__.p = "";
// Load entry module and return exports
return __webpack_require__(__webpack_require__.s = "./src/index.js");
})
/************************************************************************/
({
"./src/index.js":
/*! no static exports found */
(function (module, exports) {
console.log(\'index.js内容\')
module.exports = \'入口文件导出内容\'
})
});
/**
- 01 打包后的文件就是一个函数自调用,当前函数调用时传入一个对象
- 02 这个对象我们为了方便将之称为是模块定义,它就是一个键值对
- 03 这个键名就是当前被加载模块的文件名与某个目录的拼接()
- 04 这个键值就是一个函数,和 node.js 里的模块加载有一些类似,会将被加载模块中的内容包裹于一个函数中
- 05 这个函数在将来某个时间点上会被调用,同时会接收到一定的参数,利用这些参数就可以实现模块的加载操作
* - 06 针对于上述的代码就相当于是将 {}(模块定义) 传递给了 modules
*/
初始化了一个环境,匿名函数自调用__webpack_require__,模块ID表明所选模块,最终返回一个解析了所有依赖关系的模块
2、功能函数分析
(function (modules) { // webpackBootstrap
// The module cache
// 定义对象用于缓存已加载过的模块
var installedModules = {};
// The require function
// webpack 自定义的一个加载方法,核心功能就是返回被加载模块中导出的内容(具体内部是如何实现的,后续再分析)
function __webpack_require__(moduleId) {
// Check if module is in cache
if (installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// Create a new module (and put it into the cache)
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
// Execute the module function
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// Flag the module as loaded
module.l = true;
// Return the exports of the module
return module.exports;
}
// expose the modules object (__webpack_modules__)
// 将模块定义保存一份,通过 m 属性挂载到自定义的方法身上
__webpack_require__.m = modules;
// expose the module cache
__webpack_require__.c = installedModules;
// Object.prototype.hasOwnProperty.call
// 判断被传入的对象 obj 身上是否具有指定的属性 **** ,如果有则返回 true
__webpack_require__.o = function (object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
// define getter function for harmony exports
__webpack_require__.d = function (exports, name, getter) {
// 如果当前 exports 身上不具备 name 属性,则条件成立
if (!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, { enumerable: true, get: getter });
}
};
// define __esModule on exports
__webpack_require__.r = function (exports) {
// 下面的条件如果成立就说明是一个 esModule
if (typeof Symbol !== \'undefined\' && Symbol.toStringTag) {
// Object.prototype.toString.call(exports)
Object.defineProperty(exports, Symbol.toStringTag, { value: \'Module\' });
}
// 如果条件不成立,我们也直接在 exports 对象的身上添加一个 __esModule 属性,它的值就是true
Object.defineProperty(exports, \'__esModule\', { value: true });
};
// create a fake namespace object
// mode & 1: value is a module id, require it
// mode & 2: merge all properties of value into the ns
// mode & 4: return value when already ns object
// mode & 8|1: behave like require
__webpack_require__.t = function (value, mode) {
// 01 调用 t 方法之后,我们会拿到被加载模块中的内容 value
// 02 对于 value 来说我们可能会直接返回,也可能会处理之后再返回
if (mode & 1) value = __webpack_require__(value);
if (mode & 8) return value;
if ((mode & 4) && typeof value === \'object\' && value && value.__esModule) return value;
var ns = Object.create(null);
__webpack_require__.r(ns);
Object.defineProperty(ns, \'default\', { enumerable: true, value: value });
if (mode & 2 && typeof value != \'string\') for (var key in value) __webpack_require__.d(ns, key, function (key) { return value[key]; }.bind(null, key));
return ns;
};
// getDefaultExport function for compatibility with non-harmony modules
__webpack_require__.n = function (module) {
var getter = module && module.__esModule ?
function getDefault() { return module[\'default\']; } :
function getModuleExports() { return module; };
__webpack_require__.d(getter, \'a\', getter);
return getter;
};
// __webpack_public_path__
__webpack_require__.p = "";
// Load entry module and return exports
return __webpack_require__(__webpack_require__.s = "./src/index.js");
})
/************************************************************************/
({
"./src/index.js":
/*! no static exports found */
(function (module, exports, __webpack_require__) {
let name = __webpack_require__(/*! ./login.js */ "./src/login.js")
console.log(\'index.js内容执行了\')
console.log(name)
}),
"./src/login.js":
/*! no static exports found */
(function (module, exports) {
module.exports = \'拉勾教育\'
})
});
3、CommonJs打包
({
"./src/index.js":
(function (module, exports, __webpack_require__) {
let obj = __webpack_require__(/*! ./login.js */ "./src/login.js")
console.log(\'index.js内容执行了\')
console.log(obj.default, \'---->\', obj.age)
}),
"./src/login.js":
(function (module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);//给这个模块加标记
__webpack_require__.d(__webpack_exports__, "age", function () { return age; });//给这个模块添加age属性,后面的是getter方法
__webpack_exports__["default"] = (\'zcegg\');//默认值的添加
const age = 18
})
});
采用esm导出内容引入时采用require方法
4、esModule模块打包
({
"./src/index.js":
(function (module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
var _login_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./login.js */ "./src/login.js");
console.log(\'index.js内容加载了\')
console.log(_login_js__WEBPACK_IMPORTED_MODULE_0__["default"], \'---->\', _login_js__WEBPACK_IMPORTED_MODULE_0__["age"])
}),
"./src/login.js":
(function (module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, "age", function () { return age; });
__webpack_exports__["default"] = (\'拉勾教育\');
const age = 100
})
});
5、功能函数手写实现
(function (modules) {
// 01 定义对象用于将来缓存被加载过的模块
let installedModules = {}
// 02 定义一个 __webpack_require__ 方法来替换 import require 加载操作
function __webpack_require__(moduleId) {
// 2-1 判断当前缓存中是否存在要被加载的模块内容,如果存在则直接返回
if (installedModules[moduleId]) {
return installedModules[moduleId].exports
}
// 2-2 如果当前缓存中不存在则需要我们自己定义{} 执行被导入的模块内容加载
let module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
}
// 2-3 调用当前 moduleId 对应的函数,然后完成内容的加载
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__)
// 2-4 当上述的方法调用完成之后,我们就可以修改 l 的值用于表示当前模块内容已经加载完成了
module.l = true
// 2-5 加载工作完成之后,要将拿回来的内容返回至调用的位置
return module.exports
}
// 03 定义 m 属性用于保存 modules
__webpack_require__.m = modules
// 04 定义 c 属性用于保存 cache
__webpack_require__.c = installedModules
// 05 定义 o 方法用于判断对象的身上是否存在指定的属性
__webpack_require__.o = function (object, property) {
return Object.prototype.hasOwnProperty(object, property)
}
// 06 定义 d 方法用于在对象的身上添加指定的属性,同时给该属性提供一个 getter
__webpack_require__.d = function (exports, name, getter) {
if (!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, { enumerable: true, get: getter })
}
}
// 07 定义 r 方法用于标识当前模块是 es6 类型
__webpack_require__.r = function (exports) {
if (typeof Symbol !== \'undefined\' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" })
}
Object.defineProperty(exports, \'__esModule\', { value: true })
}
// 08 定义 n 方法,用于设置具体的 getter
__webpack_require__.n = function (module) {
let getter = module && module.__esModule ?
function getDefault() { return module[\'default\'] } :
function getModuleExports() { return module }
__webpack_require__.d(getter, \'a\', getter)
return getter
}
// 09 定义 P 属性,用于保存资源访问路径
__webpack_require__.p = ""
// 10 调用 __webpack_require__ 方法执行模块导入与加载操作
return __webpack_require__(__webpack_require__.s = \'./src/index.js\')
})
({
"./src/index.js":
(function (module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
var _login_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./login.js */ "./src/login.js");
console.log(\'index.js 执行了\')
console.log(_login_js__WEBPACK_IMPORTED_MODULE_0__["default"], \'<------\')
console.log(_login_js__WEBPACK_IMPORTED_MODULE_0__["age"], \'<------\')
}),
"./src/login.js":
(function (module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, "age", function () { return age; });
__webpack_exports__["default"] = (\'zce是一个帅哥\');
const age = 40
})
})
11、懒加载实现
let oBtn = document.getElementById(\'btn\')
oBtn.addEventListener(\'click\', function () {
import(/*webpackChunkName: "login"*/\'./login.js\').then((login) => {
console.log(login)
})
})
console.log(\'index.js执行了\')
({
"./src/index.js":
(function (module, exports, __webpack_require__) {
let oBtn = document.getElementById(\'btn\')
oBtn.addEventListener(\'click\', function () {
__webpack_require__.e(/*! import() | login */ "login").then(__webpack_require__.t.bind(null, /*! ./login.js */ "./src/login.js", 7)).then((login) => {
console.log(login)
})
})
console.log(\'index.js执行了\')
})
});
新的属性e,t方法
12、t方法
__webpack_require__.t = function (value, mode) {
/**
* 01 接收二个参数,一个是 value 一般用于表示被加载的模块id ,第二个值 mode 是一个二进制的数值
* 02 t 方法内部做的第一件事情就是调用自定义的 require 方法加载value 对应的模块导出,重新赋值给 value
* 03 当获取到了这个 value 值之后余下的 8 4 ns 2 都是对当前的内容进行加工处理,然后返回使用
* 04 当mode & 8 成立是直接将 value 返回 ( commonJS )
* 05 当 mode & 4 成立时直接将 value 返回(esModule)
* 06 如果上述条件都不成立,还是要继续处理 value ,定义一个 ns {}
* 6-1 如果拿到的 value 是一个可以直接使用的内容,例如是一个字符串,将它挂载到 ns 的 default 属性上
* 6-2 如果不是,就把这个对象的键值对一个一个赋值到ns上
*/
if (mode & 1) value = __webpack_require__(value);
if (mode & 8) return value;
if ((mode & 4) && typeof value === \'object\' && value && value.__esModule) return value;
var ns = Object.create(null);
__webpack_require__.r(ns);
Object.defineProperty(ns, \'default\', { enumerable: true, value: value });
if (mode & 2 && typeof value != \'string\') for (var key in value) __webpack_require__.d(ns, key, function (key) { return value[key]; }.bind(null, key));
return ns;
};
// 11 定义 t 方法,用于加载指定 value 的模块内容,之后对内容进行处理再返回
__webpack_require__.t = function (value, mode) {
// 01 加载 value 对应的模块内容( value 一般就是模块 id )
// 加载之后的内容又重新赋值给 value 变量
if (mode & 1) {
value = __webpack_require__(value)
}
if (mode & 8) { // 加载了可以直接返回使用的内容
return value
}
if ((mode & 4) && typeof value === \'object\' && value && value.__esModule) {
return value
}
// 如果 8 和 4 都没有成立则需要自定义 ns 来通过 default 属性返回内容
let ns = Object.create(null)
__webpack_require__.r(ns)
Object.defineProperty(ns, \'default\', { enumerable: true, value: value })
if (mode & 2 && typeof value !== \'string\') {
for (var key in value) {
__webpack_require__.d(ns, key, function (key) {
return value[key]
}.bind(null, key))
}
}
return ns
}
13、懒加载
(function (modules) { // webpackBootstrap
// install a JSONP callback for chunk loading
function webpackJsonpCallback(data) {
var chunkIds = data[0];
var moreModules = data[1];
// add "moreModules" to the modules object,
// then flag all "chunkIds" as loaded and fire callback
var moduleId, chunkId, i = 0, resolves = [];
for (; i < chunkIds.length; i++) {
chunkId = chunkIds[i];
if (Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
resolves.push(installedChunks[chunkId][0]);
}
installedChunks[chunkId] = 0;
}
for (moduleId in moreModules) {
if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
modules[moduleId] = moreModules[moduleId];
}
}
if (parentJsonpFunction) parentJsonpFunction(data);
while (resolves.length) {
resolves.shift()();
}
};
// The module cache
var installedModules = {};
// object to store loaded and loading chunks
// undefined = chunk not loaded, null = chunk preloaded/prefetched
// Promise = chunk loading, 0 = chunk loaded
var installedChunks = {
"main": 0
};
// script path function
function jsonpScriptSrc(chunkId) {
return __webpack_require__.p + "" + chunkId + ".built.js"
}
// The require function
function __webpack_require__(moduleId) {
// Check if module is in cache
if (installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// Create a new module (and put it into the cache)
var module = installedModules[moduleId] = {
i: moduleId,
l: false,
exports: {}
};
// Execute the module function
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// Flag the module as loaded
module.l = true;
// Return the exports of the module
return module.exports;
}
// This file contains only the entry chunk.
// The chunk loading function for additional chunks
__webpack_require__.e = function requireEnsure(chunkId) {
var promises = [];
// JSONP chunk loading for javascript
var installedChunkData = installedChunks[chunkId];
if (installedChunkData !== 0) { // 0 means "already installed".
// a Promise means "currently loading".
if (installedChunkData) {
promises.push(installedChunkData[2]);
} else {
// setup Promise in chunk cache
var promise = new Promise(function (resolve, reject) {
installedChunkData = installedChunks[chunkId] = [resolve, reject];
});
promises.push(installedChunkData[2] = promise);
// start chunk loading
var script = document.createElement(\'script\');
var onScriptComplete;
script.charset = \'utf-8\';
script.timeout = 120;
if (__webpack_require__.nc) {
script.setAttribute("nonce", __webpack_require__.nc);
}
script.src = jsonpScriptSrc(chunkId);
// create error before stack unwound to get useful stacktrace later
var error = new Error();
onScriptComplete = function (event) {
// avoid mem leaks in IE.
script.onerror = script.onload = null;
clearTimeout(timeout);
var chunk = installedChunks[chunkId];
if (chunk !== 0) {
if (chunk) {
var errorType = event && (event.type === \'load\' ? \'missing\' : event.type);
var realSrc = event && event.target && event.target.src;
error.message = \'Loading chunk \' + chunkId + \' failed.\\n(\' + errorType + \': \' + realSrc + \')\';
error.name = \'ChunkLoadError\';
error.type = errorType;
error.request = realSrc;
chunk[1](error);
}
installedChunks[chunkId] = undefined;
}
};
var timeout = setTimeout(function () {
onScriptComplete({ type: \'timeout\', target: script });
}, 120000);
script.onerror = script.onload = onScriptComplete;
document.head.appendChild(script);
}
}
return Promise.all(promises);
};
// expose the modules object (__webpack_modules__)
__webpack_require__.m = modules;
// expose the module cache
__webpack_require__.c = installedModules;
// define getter function for harmony exports
__webpack_require__.d = function (exports, name, getter) {
if (!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, { enumerable: true, get: getter });
}
};
// define __esModule on exports
__webpack_require__.r = function (exports) {
if (typeof Symbol !== \'undefined\' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: \'Module\' });
}
Object.defineProperty(exports, \'__esModule\', { value: true });
};
// create a fake namespace object
// mode & 1: value is a module id, require it
// mode & 2: merge all properties of value into the ns
// mode & 4: return value when already ns object
// mode & 8|1: behave like require
__webpack_require__.t = function (value, mode) {
if (mode & 1) value = __webpack_require__(value);
if (mode & 8) return value;
if ((mode & 4) && typeof value === \'object\' && value && value.__esModule) return value;
var ns = Object.create(null);
__webpack_require__.r(ns);
Object.defineProperty(ns, \'default\', { enumerable: true, value: value });
if (mode & 2 && typeof value != \'string\') for (var key in value) __webpack_require__.d(ns, key, function (key) { return value[key]; }.bind(null, key));
return ns;
};
// getDefaultExport function for compatibility with non-harmony modules
__webpack_require__.n = function (module) {
var getter = module && module.__esModule ?
function getDefault() { return module[\'default\']; } :
function getModuleExports() { return module; };
__webpack_require__.d(getter, \'a\', getter);
return getter;
};
// Object.prototype.hasOwnProperty.call
__webpack_require__.o = function (object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
// __webpack_public_path__
__webpack_require__.p = "";
// on error function for async loading
__webpack_require__.oe = function (err) { console.error(err); throw err; };
var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);//重写push方法
jsonpArray.push = webpackJsonpCallback;
jsonpArray = jsonpArray.slice();
for (var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
var parentJsonpFunction = oldJsonpFunction;
// Load entry module and return exports
return __webpack_require__(__webpack_require__.s = "./src/index.js");
})
/************************************************************************/
({
"./src/index.js":
(function (module, exports, __webpack_require__) {
let oBtn = document.getElementById(\'btn\')
oBtn.addEventListener(\'click\', function () {
__webpack_require__.e(/*! import() | login */ "login").then(__webpack_require__.t.bind(null, /*! ./login.js */ "./src/login.js", 7)).then((login) => {
console.log(login)
})
})
console.log(\'index.js执行了\')
})
});
14、懒加载手写实现
function webpackJsonpCallback(data) {//合并模块定义,改变promise状态执行后续行为
//获取需要动态加载的模块id
var chunkIds = data[0];
//获取需要被动态加载的模块依赖关系
var moreModules = data[1];
// add "moreModules" to the modules object,
// then flag all "chunkIds" as loaded and fire callback
var moduleId, chunkId, i = 0, resolves = [];
for (; i < chunkIds.length; i++) {
chunkId = chunkIds[i];
if (Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
resolves.push(installedChunks[chunkId][0]);
}
installedChunks[chunkId] = 0;
}
for (moduleId in moreModules) {
if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
modules[moduleId] = moreModules[moduleId];
}
}
if (parentJsonpFunction) parentJsonpFunction(data);
while (resolves.length) {
resolves.shift()();
}
};
//实现json加载内容,利用promise来实现异步加载操作
__webpack_require__.e = function requireEnsure(chunkId) {
var promises = [];
// JSONP chunk loading for javascript
// 获取chunkid对应的chunk,是否已经完成加载
var installedChunkData = installedChunks[chunkId];
if (installedChunkData !== 0) { // 0 means "already installed".
// a Promise means "currently loading".
if (installedChunkData) {
promises.push(installedChunkData[2]);
} else {
// setup Promise in chunk cache
var promise = new Promise(function (resolve, reject) {
installedChunkData = installedChunks[chunkId] = [resolve, reject];
});
promises.push(installedChunkData[2] = promise);//下标为2是参考的源码
// start chunk loading
var script = document.createElement(\'script\');
var onScriptComplete;
script.charset = \'utf-8\';
script.timeout = 120;
if (__webpack_require__.nc) {
script.setAttribute("nonce", __webpack_require__.nc);
}
script.src = jsonpScriptSrc(chunkId);
// create error before stack unwound to get useful stacktrace later
var error = new Error();
onScriptComplete = function (event) {
// avoid mem leaks in IE.
script.onerror = script.onload = null;
clearTimeout(timeout);
var chunk = installedChunks[chunkId];
if (chunk !== 0) {
if (chunk) {
var errorType = event && (event.type === \'load\' ? \'missing\' : event.type);
var realSrc = event && event.target && event.target.src;
error.message = \'Loading chunk \' + chunkId + \' failed.\\n(\' + errorType + \': \' + realSrc + \')\';
error.name = \'ChunkLoadError\';
error.type = errorType;
error.request = realSrc;
chunk[1](error);
}
installedChunks[chunkId] = undefined;
}
};
var timeout = setTimeout(function () {
onScriptComplete({ type: \'timeout\', target: script });
}, 120000);
script.onerror = script.onload = onScriptComplete;
document.head.appendChild(script);
}
}
return Promise.all(promises);
};
以上是关于webpack源码分析1的主要内容,如果未能解决你的问题,请参考以下文章
前端工程化9:Webpack构建流程分析,Webpack5源码解读
Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段
Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段