共计 4630 个字符,预计需要花费 12 分钟才能阅读完成。
第六期: 前端九条启发分享
本期
这个系列是本来叫 ’ 九条 bug’, 然而始终感觉名字不是很贴切, 想了很久决定从这次起改成九条启发(尽管名字还是很烂), 这次要分享的内容大部分是我的项目构造相干的, 让咱们一起看看吧。
一: soucemap 反解析打包后的文件
咱们正式环境安排的都是压缩后的文件, 如果正式环境报错那么咱们要如何精确晓得谬误产生在代码的什么地位? 兴许某些状况下你须要对代码进行 soucemap 反解析
, 这就须要mozila 官网 sourcemap 反解库
, 让咱们新建一个文件尝试一下:
npx create-react-app my_inspire
咱们成心做一个 error 进去, 比方在 App.js
里增加如下的代码:
function cc(str) {str.replace("-", ",");
}
cc();
进入到 node_module/react-scripts/config/webpack.config.js
做如下替换:
// const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
const shouldUseSourceMap = true;
运行yarn build
(重点)咱们把 .map
文件都独自拿进去放在一个 map 文件夹里, 如果这个文件在 build
文件夹内会主动帮咱们做了 soucemap 的解析
性能。
咱们能够看到报出的 error
正式咱们的 ’replace’ 谬误, 点击下放的 main
压缩文件。
记住这两个参数 line = 1;
和 column = 241;
。
当初咱们要新建一个我的项目:
mikdir my_map
cd ./my_map
npm init -y
yarn add source-map
mikdir src
cd src
touch index.js
在 index.js
外面增加解析代码:
const sourceMap = require("source-map");
const fs = require("fs");
let data = fs
.readFileSync(process.cwd() + "/map/static/js/main.761f3095.chunk.js.map")
.toString();
const consumer = new sourceMap.SourceMapConsumer(data);
consumer.then((code) => {
const line = 1;
const column = 241;
const res = code.originalPositionFor({line, column});
console.log(
code
.sourceContentFor(res.source)
.split("\n")
.slice(Math.max(res.line - 5, 0), res.line + 5)
.join("\n")
);
});
一起看看这个代码.slice(Math.max(res.line - 5, 0), res.line + 5)
, 这里是同时打印出错代码的高低 5 行代码。
用 node 命令执行这个文件就能够失去如下后果:
二: sendBeacon 发送数据
比方咱们要上报用户的敞开页面操作, 间接应用 axios
上报就会呈现申请没有收回去或被浏览器 cancel 掉, 比方上面这种形式会使卸载变慢:
window.addEventListener('unload', logData, false);
function logData() {var client = new XMLHttpRequest();
client.open("POST", "/log", false);
client.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
client.send(analyticsData);
}
看了以后应用的埋点和监控库的代码, 发现他们会应用 sendBeacon api
进行数据的上报, 它能够保证数据无效送达,且不会阻塞页面的卸载或加载, 值得注意的是这个办法最好只传递大量数据不应设计的简单, 用法如下:
window.addEventListener('unload', logData, false);
function logData() {navigator.sendBeacon("你的上报地址", "上报的数据");
}
应用 sendBeacon() 办法会使用户代理在有机会时异步地向服务器发送数据,同时不会提早页面的卸载或影响下一导航的载入性能。这就解决了提交剖析数据时的所有的问题:数据牢靠,传输异步并且不会影响下一页面的加载。
三: 如何监测到页面卡死无响应
该如何监控到用户的页面解体了? 因为 js
属于单线程执行, 如果页面里有一个 死循环
, 那么咱们前面的代码都无奈被执行, 当然谬误的监控代码也就无奈被执行🤔。
思路就是要跳出 单线程
, Worker 线程
降下来了正义之光, Worker 线程是能够独立于 JS 主线程执行的子线程,不受 JS 主线程
影响, 而咱们能够通过 Worker 线程
每 3 秒给 JS 主线程
发送一个音讯, 如果超过 6 秒没有收到主线程的回复, 那就能够认定是主线程卡死了。
上面是一个简易版的监控:
index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var worker = new Worker('./worker.js');
worker.onmessage = function (event) {console.log(` 收到了子线程的问候: ${event.data}`)
worker.postMessage('我很好!');
}
</script>
</body>
</html>
worker.js
:
let come = 0;
const timer = setInterval(() => {
waiting = true;
come -= 1;
self.postMessage("主线程你还好么?");
if (come < -2) {navigator.sendBeacon("你的上报地址", "上报页面解体");
}
}, 3000);
self.addEventListener(
"message",
function (e) {
waiting = false;
console.log(` 收到了主线程的回答: ${e.data}`);
come += 1;
},
false
);
四: 深层依赖无奈更新
比方咱们的 主我的项目
依赖 a 包
& b 包
, a 包
也依赖 b 包
, 当 a 包
降级了须要依赖 b 包
最新的版本时, 间接执行 yarn
命令可能会导致咱们咱们 主我的项目
外面的 b 包
更新了, 然而 a 包
的package.json
文件尽管写了 "b":"*"
然而可能也无奈更新到最新版的 b
, 删掉node_modules
文件夹也无用, 因为这个 bug 是 yarn.lock
文件导致的, 须要咱们 yarn remove a
而后从新 yarn add a
就能够解决问题。
五: yarn why
yarn why
命令次要是用于显示须要包的起因, 比方我的我的项目里 yarn add antd
, 然而我没有装置dayjs
库, 这时我执行命令:
yarn why dayjs
“antd#rc-picker” depends on it
antd
外面这个日期组件用到了它, 并且还会列出这个包的大小。
六: vscode 工作空间设置
就是图里的这两个设置项:
工作空间是指应用 VS Code 关上的某个文件夹,在该文件夹下会创立一个名为.vscode 的暗藏文件夹,外面蕴含着仅实用于当前目录的 VS Code 的设置,工作空间的设置会笼罩用户的设置, 咱们来把字体调大。
变得好大:
咱们改回 12
利用这个性能能够做 格式化的对立
以及某个插件针对特定我的项目的针对性配置。
七: react 异步引入组件 @loadable/component
如果咱们引入文件的地址是动静的, 他的引入门路是 /home/home1
或者是/home/home2
, 如下的写法会报错:
const Home = () => import(`./home/home1`);
function App() {
return (
<div className="App">
<p>app 页面 </p>
<Home></Home>
</div>
);
}
export default App;
借助插件实现动静批改引入门路:
yarn add import @loadable/component
import "./App.css";
import loadable from "@loadable/component";
const n = 1;
const Home = loadable(() => import(`./home/home${n}`));
function App() {
return (
<div className="App">
<p>app 页面 </p>
<Home></Home>
</div>
);
}
export default App;
这样至多不必引入两个组件。
八: package.json
引入本地包作为依赖
一提到引入本地包第一工夫想到的就是 npm link
& npm unlink
这对兄弟命令, 然而当你要引入的 本地包
变多的时候就会很麻烦, 比方公布代码的时候还要 npm unlink
, 所以当初好多我的项目都采纳lerna
来做我的项目的包治理, 当前有机会具体聊聊lerna
。
在这里我想介绍另一种形式, 如下图:
咱们有个包名为 @lulu/bao
, 新建bao
文件夹, 放在我的项目里并且须要 npm init
一下把信息填残缺:
包文件 index.js
内容为
export default () => {console.log("我是本地的包");
};
在 package.json
中增加配置, file:
指向本地
要留神, 以后须要 npm install
一次才能够失效, 就能够如下的形式应用了。
import bao from "@lulu/bao";
bao();
九: ts 在替换 ui 库时的重要性
ts
某些时候真的太重要了, 最近我参加了一个老工程整体替换 ui
组件库的我的项目, 本认为都是款式上的不同, 后果踩坑满满。
比如说旧版的 日期组件
的onchange
事件第二个参数返回的是 dayjs
对象格局的, 然而新版组件返回的是string
, 导致之后的所有解决办法全不对了, 这种谬误 ts 就能够很明确的报进去。
属性的更替也是同理, 比方新版的某个组件身上不接管 style
属性了, 那么之前传入 style
属性的中央都飘红, 我就须要想别的方法把一堆 style
以其余形式作用在组件上。
还有属性值的范畴, button
组件的 size 之前有 4 个属性可选, 然而新版的只有两个, 真是很让人头秃。
end
第九条说了替换 ui 组件的问题, 下一篇我会专门聊一聊我为一个大我的项目替换 ui 组件遇到的 20 几种问题类型, 真的是十分开眼界, 这次就是这样, 心愿与你一起提高。