本文主要说明Webpack懒加载构建和加载的原理,对构建后的源码进行分析。

一 说明

本文以一个简单的示例,通过对构建好的bundle.js源码进行分析,说明Webpack懒加载构建原理。

本文使用的Webpack版本是4.32.2版本。

注意:之前也分析过Webpack3.10.0版本构建出来的bundle.js,通过和这次的Webpack 4.32.2版本对比,核心的构建原理基本一致,只是将模块索引id改为文件路径和名字、模块代码改为了eval(moduleString)执行的方式等一些优化改造。

二 示例

1)Webpack.config.js文件内容:

 const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = {
entry: {
app: './src/index.js'
},
output: {
filename: '[name].bundle.js',
chunkFilename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: 'Output Management'
})
],
mode: 'development' // 'production' 用于配置开发还是发布模式
};

2)创建src文件夹,添加入口文件index.js:

 function component() {
var element = document.createElement('div');
var button = document.createElement('button');
var br = document.createElement('br'); button.innerHTML = 'Click me and look at the console!';
element.innerHTML = 'Hello webpack'; // _.join(['Hello', 'webpack'], ' ');
element.appendChild(br);
element.appendChild(button); button.onclick = (
e => {
// 注意:下边的注释不写的话,打包出来的print文件包名就不是print.bundle.js,而是0.bundle.js
import(/* webpackChunkName: "print" */'./print').then(
module => {
var print = module.default;
print();
}
)
}
); return element;
} document.body.appendChild(component());

3)在src目录下创建print.js文件:

 export default () => {
console.log('Button Clicked: Here\'s "some text"!');
}

4)package.json文件内容:

{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"webpack": "webpack",
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"clean-webpack-plugin": "^0.1.18",
"html-webpack-plugin": "^3.2.0",
"webpack": "^4.32.2",
"webpack-cli": "^3.3.2"
},
"dependencies": {
"lodash": "^4.17.4"
}
}

三 执行构建

执行构建命令:npm run webpack

在dist目录下生成了两个文件:app.bundle.js和print.bundle.js。

app.bundle.js源码如下(下边代码是将注释去掉、压缩的代码还原后的代码):

 (function (modules) {
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 (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 = {
"app": 0
}; // script path function
function jsonpScriptSrc(chunkId) {
return __webpack_require__.p + "" + ({"print": "print"}[chunkId] || chunkId) + ".bundle.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.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方法,这个方法的this是jsonpArray
jsonpArray.push = webpackJsonpCallback; // TODO: 为什么要复写push,而不是直接增加一个新方法名?
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__) {
function component() {
var element = document.createElement('div');
var button = document.createElement('button');
var br = document.createElement('br'); button.innerHTML = 'Click me and look at the console!';
element.innerHTML = 'Hello webpack'; // _.join(['Hello', 'webpack'], ' ');
element.appendChild(br);
element.appendChild(button); button.onclick = (
e => {
__webpack_require__.e("print")
.then(__webpack_require__.bind(null, "./src/print.js"))
.then(
module => {
var print = module.default;
print();
}
)
}
); return element;
} document.body.appendChild(component());
})
});

print.bundle.js的源码如下:

 (window["webpackJsonp"] = window["webpackJsonp"] || []).push([ // 注意:这个push实际是webpackJsonpCallback方法
["print"],
{
"./src/print.js": (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
__webpack_exports__["default"] = (() => {
console.log('Button Clicked: Here\'s "some text"!');
});
})
}
]);

四 源码解读

说明:懒加载构建和和上一篇的基础构建原理中有很多相同的代码,这里不再重复说明,本文主要详细说明其中增加的懒加载方面的内容。

app.bundle.js是构建好的入口文件,里边就是一个自执行函数,基本结构和上一篇基础构建源码中一致,这里不再详细说明。下边是使用懒加载模块构建后,增加的内容,这里详细说明这些内容:

 (function (modules) {
function webpackJsonpCallback(data) {...}; // 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 = {
"app": 0
}; // script path function
function jsonpScriptSrc(chunkId) {...} // The require function
function __webpack_require__(moduleId) {...} // This file contains only the entry chunk.
// The chunk loading function for additional chunks
__webpack_require__.e = function requireEnsure(chunkId) {...}; // .... // 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方法,这个方法的this是jsonpArray
jsonpArray.push = webpackJsonpCallback; // TODO: 为什么要复写push,而不是直接增加一个新方法名?
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__) {...})
});

我们详细分析下新增的这些代码。

4.1 installedChunks缓存变量

根据注释,该对象变量主要缓存各个独立的js文件模块的加载状态。

该对象的key就是chunkId,而chunkId实际就是文件名去掉.bundle.js后剩余的内容,例如:print.bundle.js的chunkId就是print。

根据值的不同标志着key对应的文件加载状态主要有以下几种:

undefined:key对应的文件未加载;

null:key对应的文件延迟加载;

数组:正在加载(注意,这里的注释有点不准确,这个数组实际存储的是一个promise的实例,以及对应的reject和resolve);

0:已经加载过了。

这个变量的核心作用:当一个懒加载模块被多个文件依赖时,如果该模块已经被加载过了,就不会被其它模块加载了。判断方法就是通过该缓存变量判断的。具体源码可以在__webpack_require__.e函数中看到:

 __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 {
// ...
// 创建一个<script>标签,将路径设置为懒加载文件路径,并插入HTML,实现该懒加载文件的加载。
}
}
return Promise.all(promises);
};

4.2 __webpack_require__.e函数

该函数主要作用就是创建一个<script>标签,然后将chunkId对应的文件通过该标签加载。

源代码如下:

 1 __webpack_require__.e = function requireEnsure(chunkId) {
2 var promises = [];
3
4 // JSONP chunk loading for javascript
5
6 var installedChunkData = installedChunks[chunkId];
7 if (installedChunkData !== 0) { // 0 means "already installed".
8
9 // a Promise means "currently loading".
10 if (installedChunkData) {
11 promises.push(installedChunkData[2]);
12 } else {
13 // setup Promise in chunk cache
14 var promise = new Promise(function (resolve, reject) {
15 installedChunkData = installedChunks[chunkId] = [resolve, reject];
16 });
17 promises.push(installedChunkData[2] = promise);
18
19 // start chunk loading
20 var script = document.createElement('script');
21 var onScriptComplete;
22
23 script.charset = 'utf-8';
24 script.timeout = 120;
25 if (__webpack_require__.nc) {
26 script.setAttribute("nonce", __webpack_require__.nc);
27 }
28 script.src = jsonpScriptSrc(chunkId);
29
30 // create error before stack unwound to get useful stacktrace later
31 var error = new Error();
32 onScriptComplete = function (event) {
33 // avoid mem leaks in IE.
34 script.onerror = script.onload = null;
35 clearTimeout(timeout);
36 var chunk = installedChunks[chunkId];
37 if (chunk !== 0) {
38 if (chunk) {
39 var errorType = event && (event.type === 'load' ? 'missing' : event.type);
40 var realSrc = event && event.target && event.target.src;
41 error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')';
42 error.type = errorType;
43 error.request = realSrc;
44 chunk[1](error);
45 }
46 installedChunks[chunkId] = undefined;
47 }
48 };
49 var timeout = setTimeout(function () {
50 onScriptComplete({type: 'timeout', target: script});
51 }, 120000);
52 script.onerror = script.onload = onScriptComplete;
53 document.head.appendChild(script);
54 }
55 }
56 return Promise.all(promises);
57 };

主要做了如下几个事情:

1)判断chunkId对应的模块是否已经加载了,如果已经加载了,就不再重新加载;

2)如果模块没有被加载过,但模块处于正在被加载的过程,不再重复加载,直接将加载模块的promise返回。

为什么会出现这种情况?

例如:我们将index.js中加载print.js文件的地方改造为下边多次通过ES6的import加载print.js文件:

 1 button.onclick = (
2 e => {
3
4 import('./print').then(
5 module => {
6 var print = module.default;
7 print();
8 }
9 );
10
11 import('./print').then(
12 module => {
13 var print = module.default;
14 print();
15 }
16 )
17 }
18 );

从上边代码可以看出,当第一import加载print.js文件时,还没有resolve,就又执行第二个import文件了,而为了避免重复加载该文件,就通过将这里的判断,避免了重复加载。

3)如果模块没有被加载过,也不处于加载过程,就创建一个promise,并将resolve、reject、promise构成的数组存储在上边说过的installedChunks缓存对象属性中。然后创建一个script标签加载对应的文件,加载超时时间是2分钟。如果script文件加载失败,触发reject(对应源码中:chunk[1](error),chunk[1]就是上边缓存的数组的第二个元素reject),并将installedChunks缓存对象中对应key的值设置为undefined,标识其没有被加载。

4)最后返回promise

注意:源码中,这里返回的是Promise.all(promises),分析代码发现promises好像只可能有一个元素。可能还没遇到多个promises的场景吧。留待后续研究。

4.3 自执行函数体代码分析

整个app.bundle.js文件是一个自执行函数,该函数中执行的代码如下:

1     var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
2 var oldJsonpFunction = jsonpArray.push.bind(jsonpArray); // 复制一个数组的push方法,这个方法的this是jsonpArray
3 jsonpArray.push = webpackJsonpCallback; // TODO: 为什么要复写push,而不是直接增加一个新方法名?
4 jsonpArray = jsonpArray.slice(); // 拷贝一个新数组
5 for (var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
6 var parentJsonpFunction = oldJsonpFunction;

这段代码主要做了如下几个事情:

1)定义了一个全局变量webpackJsonp,改变量是一个数组,该数组变量的原生push方法被复写为webpackJsonpCallback方法,该方法是懒加载实现的一个核心方法,具体代码会在下边分析。

该全局变量在懒加载文件中被用到。在print.bundle.js中:

1 (window["webpackJsonp"] = window["webpackJsonp"] || []).push([ // 注意:这个push实际是webpackJsonpCallback方法
2 ["print"],
3 {
4 "./src/print.js": (function(module, __webpack_exports__, __webpack_require__) {...})
5 }
6 ]);

2)将数组的原生push方法备份,赋值给parentJsonpFunction变量保存。

注意:该方法的this是全局变量webpackJsonp,也就是说parentJsonpFunction('111')后,全局数组变量webpackJsonp就增加了一个'111'元素。

该方法在webpackJsonpCallback中会用到,是将懒加载文件的内容保存到全局变量webpackJsonp中。

3)上边第一步中复写push的原因?

可能是因为在懒加载文件中,调用了复写后的push,执行了原生push的功能,因此,为了更形象的表达该意思,因此直接复写了push。

但个人认为这个不太好,不易读。直接新增一个_push或者extendPush,这样是不是读起来就很简单了。

4.4 webpackJsonpCallback函数分析

该函数是懒加载的一个比较核心代码。其代码如下:

 1     function webpackJsonpCallback(data) {
2 var chunkIds = data[0];
3 var moreModules = data[1];
4
5 // add "moreModules" to the modules object,
6 // then flag all "chunkIds" as loaded and fire callback
7 var moduleId, chunkId, i = 0, resolves = [];
8 for (; i < chunkIds.length; i++) {
9 chunkId = chunkIds[i];
10 if (installedChunks[chunkId]) {
11 resolves.push(installedChunks[chunkId][0]);
12 }
13 installedChunks[chunkId] = 0;
14 }
15 for (moduleId in moreModules) {
16 if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
17 modules[moduleId] = moreModules[moduleId];
18 }
19 }
20 if (parentJsonpFunction) parentJsonpFunction(data);
21
22 while (resolves.length) {
23 resolves.shift()();
24 }
25 };

参数说明:

参数是一个数组。有两个元素:第一个元素是要懒加载文件中所有模块的chunkId组成的数组;第二个参数是一个对象,对象的属性和值分别是要加载模块的moduleId和模块代码函数。

该函数主要做的事情如下:

1)遍历参数中的chunkId:

判断installedChunks缓存变量中对应chunkId的属性值:如果是真,说明模块正在加载,因为从上边分析中可以知道,installedChunks[chunkId]只有一种情况是真,那就是在对应的模块正在加载时,会将加载模块创建的promise的三个信息搞成一个数组[resolve, reject, proimise]赋值给installedChunks[chunkId]。将resolve存入resolves变量中。

将installedChunks中对应的chunkId置为0,标识该模块已经被加载过了。

2)遍历参数中模块对象所有属性:

将模块代码函数存储到modules中,该modules是入口文件app.bundle.js中自执行函数的参数。

这一步非常关键,因为执行模块加载函数__webpack_require__时,获取模块代码时,就是通过moduleId从modules中查找对应模块代码。

3)调用parentJsonpFunction(原生push方法)将整个懒加载文件的数据存入全局数组变量window.webpackJsonp。

4)遍历resolves,执行所有promise的resolve:

当执行了promise的resolve后,才会走到promise.then的成功回调中,查看源码可以看到:

 1             button.onclick = (
2 e => {
3 __webpack_require__.e("print")
4 .then(__webpack_require__.bind(null, "./src/print.js"))
5 .then(
6 module => {
7 var print = module.default;
8 print();
9 }
10 )
11 }
12 );

resolve后,执行了两个then回调:

第一个回调是调用__webpack_require__函数,传入的参数是懒加载文件中的一个模块的moduleId,而这个moduleId就是上边存入到modules变量其中一个。这样就通过__webpack_require__执行了模块的代码。并将模块的返回值,传递给第二个then的回调函数;

第二个回调函数是真正的onclick回调函数的业务代码。

5)重要思考:

从这个函数可以看出:

调用__webpack_require__.e('print')方法,实际只是将对应的print.bundle.js文件加载和创建了一个异步的promise(因为并不知道什么时候这个文件才能执行完,因此需要一个异步promise,而promise的resolve会在对应的文件加载时执行,这样就能实现异步文件加载了),并没有将懒加载文件中保存的模块代码执行。

在加载对应print.bundle.js文件代码时,通过调用webpackJsonpCallback函数,实现触发加载文件时创建的promise的resolve。

resolve触发后,会执行promise的then回调,这个回调通过__webpack_require__函数执行了真正需要模块的代码(注意:如果print.bundle.js中有很多模块,只会执行用到的模块代码,而不是执行所有模块的代码),执行完后将模块的exports返回给promise的下一个then函数,该函数也就是真正的业务代码了。

综上,可以看出,webpack实际是通过promise,巧妙的实现了模块的懒加载功能。

5 懒加载构建原理图

Webpack探索【16】--- 懒加载构建原理详解(模块如何被组建&如何加载)&源码解读的更多相关文章

  1. Webpack探索【15】--- 基础构建原理详解(模块如何被组建&如何加载)&源码解读

    本文主要说明Webpack模块构建和加载的原理,对构建后的源码进行分析. 一 说明 本文以一个简单的示例,通过对构建好的bundle.js源码进行分析,说明Webpack的基础构建原理. 本文使用的W ...

  2. Webpack探索【4】--- entry和output详解

    本文主要讲entry和output相关内容.

  3. 【动画消消乐】HTML+CSS 自定义加载动画:清新折叠方块效果 063(附源码及原理详解)

    前言 Hello!小伙伴! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出- 自我介绍ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简介:因C语言结识编程,随后转入计算机专 ...

  4. Tomcat启动过程原理详解 -- 非常的报错:涉及了2个web.xml等文件的加载流程

    Tomcat启动过程原理详解 发表于: Tomcat, Web Server, 旧文存档 | 作者: 谋万世全局者 标签: Tomcat,原理,启动过程,详解 基于Java的Web 应用程序是 ser ...

  5. 转: javascript模块加载框架seajs详解

    javascript模块加载框架seajs详解 SeaJS是一个遵循commonJS规范的javascript模块加载框架,可以实现javascript的模块化开发和模块化加载(模块可按需加载或全部加 ...

  6. seo网页加速技术,预加载 DNS Prefetching 详解

    seo网页加速技术,预加载 DNS Prefetching 详解 DNS Prefetching 是什么 : DNS 是什么-- Domain Name System,域名系统,作为域名和IP地址相互 ...

  7. Prism 源码解读3-Modules加载

    介绍 在软件开发过程中,总想组件式的开发方式,各个组件之间最好互不影响,独立测试.Prism的Modules很好的满足了这一点. 这个架构图很好了讲解了Prism的Modules的概念 Prism支持 ...

  8. 通过 JFR 与日志深入探索 JVM - TLAB 原理详解

    全系列目录:通过 JFR 与日志深入探索 JVM - 总览篇 什么是 TLAB? TLAB(Thread Local Allocation Buffer)线程本地分配缓存区,这是一个线程专用的内存分配 ...

  9. CesiumJS 2022^ 源码解读[7] - 3DTiles 的请求、加载处理流程解析

    目录 1. 3DTiles 数据集的类型 2. 创建瓦片树 2.1. 请求入口文件 2.2. 创建树结构 2.3. 瓦片缓存机制带来的能力 3. 瓦片树的遍历更新 3.1. 三个大步骤 3.2. 遍历 ...

随机推荐

  1. 洛谷——P2781 传教

    P2781 传教 题目背景 写完暑假作业后,bx2k去找pear玩.pear表示他要去汉中传教,于是bx2k准备跟着去围观. 题目描述 pear把即将接受传教的人排成一行,每个人从左到右的编号为1-n ...

  2. Cat Snuke and a Voyage --AtCoder

    题目描述 In Takahashi Kingdom, there is an archipelago of N islands, called Takahashi Islands. For conve ...

  3. 2016北京集训测试赛(十)Problem A: azelso

    Solution 我们把遇到一个旗子或者是遇到一个敌人称为一个事件. 这一题思路的巧妙之处在于我们要用\(f[i]\)表示从\(i\)这个事件一直走到终点这段路程中, \(i\)到\(i + 1\)这 ...

  4. MFC中 CString转换为char

    网上好多方法,比如强制转换: CString strTest = _T(“abcd”); char *buf = (LPSTR)(LPCTSTR)strTest; 可是都只得到了第一个字符. 后来,找 ...

  5. getchar()和getch()的区别

    1.getchar();从键盘读取一个字符并输出,该函数的返回值是输入第一个字符的ASCII码:若用户输入的是一连串字符,函数直到用户输入回车时结束,输入的字符连同回车一起存入键盘缓冲区.若程序中有后 ...

  6. 【前端阅读】——《活用PHP、MySQL建构Web世界》摘记之设计技巧

    二.设计技巧 Programming的习惯因人而异,这里只提供一些经验,可以参考. 1.利用Include模块化你的程序代码 Include函数基本上说:就像是把另一个文件(HTML或者PHP程序)读 ...

  7. Linux内核——内存管理

    内存管理 页 内核把物理页作为内存管理的基本单位.内存管理单元(MMU,管理内存并把虚拟地址转换为物理地址)通常以页为单位进行处理.MMU以页大小为单位来管理系统中的页表. 从虚拟内存的角度看,页就是 ...

  8. LeetCode Recover Binary Search Tree——二查搜索树中两个节点错误

    Two elements of a binary search tree (BST) are swapped by mistake.Recover the tree without changing ...

  9. mybatis技术文章

    http://legend2011.blog.51cto.com/3018495/1600478

  10. 在Centos 7上安装配置 Apche Kafka 分布式消息系统集群

    Apache Kafka是一种颇受欢迎的分布式消息代理系统,旨在有效地处理大量的实时数据.Kafka集群不仅具有高度可扩展性和容错性,而且与其他消息代理(如ActiveMQ和RabbitMQ)相比,还 ...