Decorative image frame

判断滚动条方向

之前遇到了这样一个问题,下滑滚动,到底加载数据,可是判断到底好判断,但是有时候在底向上滚动还是会触发条件,这时候就需要判断滚动的方向了,只有向下滚动才会触发数据请求。

这个代码的精髓就是利用了宏任务和微任务的特点而实现的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function scrollDirect() {
var last = 0;
document.getElementById("textarea").addEventListener("scroll", function (e) {
setTimeout(function () {
last = e.target.scrollTop;
}, 0);
console.log(last);
if (last > e.target.scrollTop) {
console.log("向上");
} else {
console.log("向下");
}
console.log(e.target.scrollTop);
console.log(e.target.scrollHeight);
});
}
scrollDirect();

webpack 模块异步加载机制

原文 https://segmentfault.com/a/1190000020233387?utm_source=tag-newest

精简:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
((modules) => {
// 3.module传入的为所有打包后的结果
var installedModules = {};
function __webpack_require__(moduleId) {
if (installedModules[moduleId]) {
// 做缓存的可以先不理
return installedModules[moduleId].exports;
}
var module = (installedModules[moduleId] = {
// 5.创建模块,每个模块都有一个exports对象
i: moduleId,
l: false,
exports: {},
});
modules[moduleId](module, module.exports, __webpack_require__); // 6.调用对应的模块函数,将模块exports传入
module.l = true;
// 8.用户会将结果放到module.exports对象上
return module.exports;
}
function startup() {
// 通过入口开始加载
return __webpack_require__("./src/index.js"); // 默认返回的是 module.exports结果;
}
return startup(); // 4.启动加载
})({
// 1.列出打包后的模块
"./src/a.js": (module) => {
eval(
"module.exports = 'webyouxuan'\n\n//# sourceURL=webpack:///./src/a.js?"
);
},
"./src/index.js": (__unusedmodule, __unusedexports, __webpack_require__) => {
// 7.加载其他模块,拿到其他模块的module.exports结果
eval(
'let webyouxuan = __webpack_require__(/*! ./a */ "./src/a.js");\nconsole.log(webyouxuan)\n\n//# sourceURL=webpack:///./src/index.js?'
);
},
});

懒加载

我们在 index 入口文件中,采用 import语法动态导入文件

1
2
3
4
5
6
7
8
const button = document.createElement('button');
button.innerHTML = '关注 webyouxuan';
document.body.appendChild(button);
document.addEventListener('click',()=>{
import('./a').then(data=>{
console.log(data.default);
})
});

再回头看编译的结果,貌似好像打包出来的结果有些复杂啦,没关系!其实核心很简单就是个 jsonp 加载文件。
打包出来的结果多了个src_a_js.main.js

1
2
3
4
5
6
7
8
9
10
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([
["src_a_js"],
{
"./src/a.js": module => {
eval(
"module.exports = 'webyouxuan'\n\n//# sourceURL=webpack:///./src/a.js?"
);
}
}
]);

最终会通过 script 标签加载这个文件,加载后默认会调用 window 下的 > webpackJsonp的 push 方法
我们来看看 main.js 发生了哪些变化,话说有代码些小复杂,分段来看
先来看index 模块做了那些事,为了看着方便我来把代码整理一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
"./src/index.js":
((__unusedmodule, __unusedexports, __webpack_require__) => {
eval(`
const button = document.createElement('button');
button.innerHTML = '关注 webyouxuan';
document.body.appendChild(button);
document.addEventListener('click',()=>{
__webpack_require__.e("src_a_js").then(
__webpack_require__.t.bind(__webpack_require__, "./src/a.js", 7)).then(data=>{
console.log(data.default);
})
})
`);
})

这里出现了两个方法 __webpack_require__.e__webpack_require__.t
__webpack_require__.e方法看似是用来加载文件的,咱们来找一找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
__webpack_require__.f = {};
__webpack_require__.e = (chunkId) => { // chunkId => src_a_js动态加载的模块
return Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key) => {
__webpack_require__.f[key](chunkId, promises); // 调用j方法 将数组传入
return promises;
}, []));
};
var installedChunks = {
"main": 0 // 默认main已经加载完成
};
// f方法上有个j属性
__webpack_require__.f.j = (chunkId, promises) => {
var installedChunkData = Object.prototype.hasOwnProperty.call(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;
if(installedChunkData !== 0) { // 默认installedChunks肯定没有要加载的模块
if(installedChunkData) {
promises.push(installedChunkData[2]);
} else {
if(true) { // 创建一个promise 把当前的promise 成功失败保存到 installedChunks
var promise = new Promise((resolve, reject) => {
installedChunkData = installedChunks[chunkId] = [resolve, reject];
});
// installedChunks[src_a_js] = [resolve,reject,promise]
promises.push(installedChunkData[2] = promise);
// 这句的意思是看是否配置publicPath,配置了就加个前缀
var url = __webpack_require__.p + __webpack_require__.u(chunkId);
// 1)创建script标签
var script = document.createElement('script');
var onScriptComplete;
script.charset = 'utf-8';
script.timeout = 120;
script.src = url; // 2)开始加载这个文件
var error = new Error();
onScriptComplete = function (event) { // 完成工作
script.onerror = script.onload = null;
clearTimeout(timeout);
var reportError = loadingEnded();
if(reportError) {
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;
reportError(error);
}
};
var timeout = setTimeout(function(){ // 超时工作
onScriptComplete({ type: 'timeout', target: script });
}, 120000);
script.onerror = script.onload = onScriptComplete;
document.head.appendChild(script); // 3)将标签插入到页面
} else installedChunks[chunkId] = 0;
}
}
};

虽然代码量比较多,其实核心就干了一件事 :> 创建 script 标签,文件加载回来那肯定就会调用 push 方法咯!
先跳过这段看下面的

1
2
3
4
5
6
7
8
9
10
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([
["src_a_js"],
{
"./src/a.js": module => {
eval(
"module.exports = 'webyouxuan'\n\n//# sourceURL=webpack:///./src/a.js?"
);
}
}
]);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function webpackJsonpCallback(data) { // 3) 文件加载后会调用此方法
var chunkIds = data[0]; // data是什么来着,你看看src_a_js怎么写的你就知道了 看上面! ["src_a_js"]
var moreModules = data[1]; // 获取新增的模块
var moduleId, chunkId, i = 0, resolves = [];
for(;i < chunkIds.length; i++) {
chunkId = chunkIds[i];
if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
// installedChunks[src_a_js] = [resolve,reject,promise] 这个是上面做的
// 很好理解 其实就是取到刚才放入的promise的resolve方法
resolves.push(installedChunks[chunkId][0]);
}
installedChunks[chunkId] = 0; // 模块加载完成
}
for(moduleId in moreModules) { // 将新增模块与默认的模块进行合并 也是就是modules模块,这样modules中就多了动态加载的模块
if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
__webpack_require__.m[moduleId] = moreModules[moduleId];
}
}
if(runtime) runtime(__webpack_require__);
if(parentJsonpFunction) parentJsonpFunction(data);
while(resolves.length) { // 调用promise的resolve方法,这样e方法就调用完成了
resolves.shift()();
}
};
var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || []; // 1) window["webpackJsonp"]等于一个数组
var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
jsonpArray.push = webpackJsonpCallback; // 2) 重写了数组的push方法
var parentJsonpFunction = oldJsonpFunction;

加载的文件会调用 webpackJsonpCallback方法,内部就是将新增的模块合并到modules上,并且让__webpack_require__.e完成

1
2
3
__webpack_require__.t = function(value, mode) { // t方法其实很简单就是
if(mode & 1) value = this(value); // 就是调用__webpack_require__加载最新的模块
};

这样用户就可以拿到新增的模块结果啦,源码虽难,但是多看几遍总会有收获!
到此我们就将 webpack5 的懒加载功能整个过了一遍,其实思路和 webpack4 的懒加载一样呢
,不过不得不说 webpack5 打包出来的代码更加简洁啦! 期待 webpack5 正式发版!!

我们来到这个世界,是为了创造本来没有的东西,而不是为了学写代码

我们到底崇拜什么

世界上根本不存在纯正的全栈工程师,为什么就是有那么一拨人被我们奉为全栈工程师而努力向他们学习?其实是因为如下的特点:

  1. 超强学习能力
  2. 超强的精力支配能力,对于“学多少可以产生最大回报”和“做多少可以产生最大回报”有准确预判
  3. 善于利用工具和发明工具,从而提高效率
  4. 善于整合资源
  5. 创新能力

你会发现这些特质你在专家工程师身上也可以找得到,只是少了一个“全面”。因为全面是以上特点导出的结果,而不是原因。具有以上特点的人不可能不全面。你认为一个专家不全面,是因为这个专家某一方面特别牛,但是并不代表别的方面就什么都不会。

我们该怎么办

写到这里我们才发现我们崇拜的不是全栈工程师,我们(可能)不看好的专家身上具有一模一样的品质。我们崇拜的原来是那些聪明的人。对应到上面的 5 个特点:

  1. 训练我们的大脑,多读书
  2. 开始计算自己的投入和价值产出,杜绝学一堆用不着的技术造出没有价值的东西,也要有决心为了更优的价值产出而努力学习更多有用的技术(变成全的过程)
  3. 人的生命有限,学会用现成的工具提高我们的效率
  4. 如果公司需要懂 linux 的人,聪明的人不会第一时间开始学 linux,而是看能不能找一个懂 linux 的人(或者机构),如果实在是找不到,才考虑自己学 linux
  5. 使用工具、整合资源,省下来的时间是为了解决之前这些办法解决不了的问题,这才是价值所在,需要自己真正花时间的部分,哪怕把省下来的时间花在女朋友身上,这完全满足“之前这些办法解决不了的问题”

最后

我们来到这个世界,是为了创造本来没有的东西,而不是为了学写代码。

开发环境快速搭建常见命令

choco:

1
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

nvm:

1
choco install nvm

yarn:

1
choco install yarn

react use Hook

react-use

npm i react-use

Usage

你需要安装 React 16.8.0或更高版本才能使用 Hooks API。你可以分别导入每个钩子
import useToggle from ‘react-use/lib/useToggle’
或使用 ES6 命名导入
import {useToggle} from ‘react-use’
根据绑定器的不同,你可能会在 ES6 命名导入语句中遇到缺少依赖项的错误。有些钩子要求安装对等依赖项,因此我们建议单独导入。如果你希望同时使用这两种方法,你可以通过将以下配置添加到.babelrc文件中,将命名的导入语句转换为使用babel-plugin-import的单个导入语句。
[
“import”, {
“libraryName”: “react-use”,
“libraryDirectory”: “lib”,
“camel2DashComponentName”: false
}
]

起点模块化(2)前世今生

Javascript 程序本来很小——在早期,它们大多被用来执行独立的脚本任务,在你的 web 页面需要的地方提供一定交互,所以一般不需要多大的脚本。过了几年,我们现在有了运行大量 Javascript 脚本的复杂程序,还有一些被用在其他环境(例如 Node.js)。
因此,近年来,有必要开始考虑提供一种将 JavaScript 程序拆分为可按需导入的单独模块的机制。Node.js 已经提供这个能力很长时间了,还有很多的 Javascript 库和框架 已经开始了模块的使用(例如, CommonJS 和基于 AMD 的其他模块系统 如 RequireJS, 以及最新的 WebpackBabel)。
好消息是,最新的浏览器开始原生支持模块功能了,这是本文要重点讲述的。这会是一个好事情 — 浏览器能够最优化加载模块,使它比使用库更有效率:使用库通常需要做额外的客户端处理。

在 es6 以前,还没有提出一套官方的规范,从社区和框架推广程度而言,目前通行的 javascript 模块规范有两种:CommonJS 和 AMD

而常说的 CMD 和 AMD 其实并不准确,CMD 就是Common Module Definition的缩写,而 AMD 是 Asynchronous Module Definition 的缩写。

最有意思的是 CMD 和 AMD 其实都是异步加载的。。。。。。。。。他们都是为了客户端服务的

只有 CommonJS 不是异步加载的。。。。。。。。。他是为了服务器端服务的。