SegmentFault 前端读书会最新的文章
2024-01-22T15:36:44+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
2023 end. 2024 start------------>
https://segmentfault.com/a/1190000044577853
2024-01-22T15:36:44+08:00
2024-01-22T15:36:44+08:00
wlove
https://segmentfault.com/u/dxgl
0
<h2>~</h2><p>随着清晨的一缕阳光悄然来袭,我怀着一颗激动的心<del>(有可能跟我喝咖啡太多的原因)</del>,想与你们分享我一年的所见所闻。<del>(发出来应该是下午了。)</del></p><p>在这篇年终总结中,<strong>我将分享在技术舞台上的点滴收获,通过回顾我的不容易/阳光来探寻成长的足迹,也为未来的征程描绘蓝图。愿我的分享如一湾清泉,在你心中激起涟漪,唤醒你内心对未来的热爱与探索。</strong></p><p>希望你们会喜欢这份总结,也期待在评论区听到你们的看法和交流。</p><p><img src="/img/remote/1460000044577855" alt="image.png" title="image.png"></p><h3>那些"技术点滴"</h3><blockquote>跟大家分享下我在技术的"投入",我这边日常写代码更多是算法/工具库/还有产品开始前期的技术架构。所以关于业务项目的一笔带过吧。先对2023年做个简单回顾,想到哪写到哪,无前后时间顺序。</blockquote><ol><li><strong>WebGL</strong>/<strong>Canvas</strong>/<strong>Shader</strong> + <strong>Javascript</strong>/<strong>TypeScript</strong> 主程&维护2D渲染库</li><li><strong>C++</strong> + <strong>Ligra</strong> 图算法引擎 编译各种输出 以及简单写了点图算法。</li><li><strong>Python</strong> + <strong>LLM</strong> (<strong>ChatGLM</strong> & <strong>text2vec</strong> & ...) 部署了私有大模型+知识库 算是是一个使用者吧(毕竟我算一个<strong>prompt</strong>工程师)。2024 我要稍微深入点 训练/微调。</li><li><strong>Nodejs</strong> 写了蛮多工具吧 比如DataGather.OCR.PDF-PNG....</li><li><strong>Vue3</strong> + <strong>Vite</strong> + <strong>Electron</strong> + <strong>Rust</strong> + <strong>A.</strong> 桌面版分析软件(集中时间好像就花了1-2周好像...不太完善)</li><li><strong>Java</strong> + <strong>C++</strong> 写了个某某软件的机器人 商业秘密 hh.</li><li><strong>C#</strong> <strong>JavaScript</strong> <strong>Java</strong> <strong>C++</strong>...项目维护了一丢丢内容吧 我属于哪里需要哪里搬的角色 。</li><li>我不太喜欢刷题. 但是偶尔也刷一下 今年估计200-300的样子。</li></ol><p>以上是2023年 我的一个参与列表/技术方向。其实围绕我的编程生涯的编程语言更多是<strong>JavaScript C++</strong>; 很荣幸在开始工作没多久真正做了一个前端。最开始的日子写了h5/hybrid app,小游戏,当然还有数不清的后台管理。对游戏感兴趣 又把<strong>C++</strong> 拾起来 做了稍大型的一个游戏,最后没真正意义上线还是蛮遗憾的。然后就跳槽做大数据可视化架构师。技术方向也因为业务/兴趣在逐步完善。一些不可控因素我变成了管理,最终成了部门负责人。。还年轻 路还长。我觉得我的技术还能更厉害。最差也能够更得心应手吧。</p><h3>那些 “不容易”</h3><blockquote>技术/生活/心态...都会遇到一些坎坷/挑战。还不错(趁机表扬一下自己吧) 都挺过来啦。</blockquote><ol><li>家人生病(现在已经身体健康,生龙活虎了) 无憾的是 今年一起陪着去医院了, 当然也不算陪床。晚上直接带着爸妈跑去酒店睡觉。</li><li>工作 2023疫情结束的一年 很多公司问题就暴露出来了 比如业务,资金/现金链,核心管理层纠纷,人员变动。我也不例外 但是我没有将简历投入"大海" 我选择一个朋友推荐的公司 (谈了谈条件 差不多就来了 关键是对我这个朋友/老板的信任吧)。</li><li>一年一度的个人迷茫期。 而且这个周期还不短。想逃避 想考研 想辞职出去浪一浪。想看看世界。回顾往昔 所做的都是 ”垃圾“ 怀疑自己。不想继续 又不想停下来。就是纠结。</li><li>管理角色的迷茫/摸索学习。我其实是管理岗好几年了(毕业没多久就是小组长了呢 哈哈)。但是其实更多是为下面的人分分活。争取点福利。当好一个技术咨询/顾问... 不算是真正的管理 最起码我心里不认为自己合格。我在逐步的完善。不限于能力/沟通/平衡/利益/凝聚/思考/共赢...</li><li>想买车/买房来着..但是在北京上班 北京内的话一个车牌一个交通状况...买房的话 北京估计在郊区能买个小一点,且又得考虑交通问题 而且跟租房真比不了 舒服度/空间...不了了之。</li></ol><p><strong>其他"不容易"好像想不起来啦。或许真的没了。</strong></p><h3>那些 “温暖阳光”</h3><blockquote>我的团队 我身边的人/事/物 都是洒在身体的阳光。</blockquote><ol><li><strong>团队的离别 并没有真的离别。</strong>我的前同事,与你们共事非常荣幸且开心。无论是偶尔的聚餐 工作的关照(不需要找工作很大原因也是因为前同事/老板)...</li><li><strong>新团队(20-30成员)融入的顺利。急剧的空降,没想到着陆地如此的平坦</strong>。感谢🙏。或许因为我的能力/交流/脸皮厚... 我能做的就是努力做到更好,成为一个真的管.</li><li>非常感谢发生任何小事情。身边的人都会投来关心。<strong>感谢遇到的每一个他吧 当然还有她!</strong></li><li><strong>去了一些地方,公差多 南北都有吧。</strong> 比如:广州来说 飞机在雨中降落 雨中升起。因为公差 只有落地那晚跟老板出去参加饭局。然后俩个逛了逛 然后吃个夜宵, 在这边也互相吹了一波牛。 这样我吹牛的地方又+1.</li><li><strong>当然还有一些小插曲</strong> 比如 老家新房子的缴费/装修/... 。家里亲戚的询问/帮忙/寻求帮助。 兄弟们结婚。 ... <strong>从过程/收获/认知 都是阳光。</strong></li><li>看本书 看个电影 看个老片 打个游戏都是静心修行。<strong>哦对 打游戏我也是在训练大脑哦!别误会。</strong></li></ol><p><strong>"温暖阳光"还是蛮多。这个真是因为太多 想不起来了。</strong> <em>哦对 当然还有那个曾经的我。</em></p><h3>心中 “未来蓝图”</h3><blockquote>简单对未来做一个展望和规划 就是随便聊聊我的想法。</blockquote><ol><li><strong>技术能力是我的优质资产</strong> , 我必定会加大对优质资产的投入/投资。</li><li><strong>管理能力/业务能力/思维认知能力 作为我的附加资产</strong> , 这个决定我上层的高度。 毋庸置疑, 需要逐步开始/继续跟进投资。很关键一点 我需要对信任我的人有责任心。</li><li><strong>我的一种理想就是 睡眠/规划/饮食/生活/学习 方方面面最起码都能做到随心。</strong></li><li><strong>身边的他/她/我 我需要认真考虑/思考/平衡一下彼此的微妙关系。</strong></li><li><strong>读书 实践 身体健康(运动,饮食,睡眠)</strong> 这种需要列详细计划 并且严格执行的 此处就略过了。我对我的内心以及执行有足够信心。</li></ol><p><strong>还是蛮多的...但是我想不起来啦。不想讲太多。。</strong></p><h5>最后🙏感谢你的阅读</h5>
一篇关于热点交流话题的总结和续集。
https://segmentfault.com/a/1190000044222426
2023-09-15T16:42:18+08:00
2023-09-15T16:42:18+08:00
wlove
https://segmentfault.com/u/dxgl
0
<h2>前言</h2><p>起因是我发布了一个<a href="https://link.segmentfault.com/?enc=EKjOs6ThWjduNYibu8uOeA%3D%3D.NmVQIKkuxcpWMaTHE7R2sxRreC9mOcVM%2BvOL9Ld%2FYpMIJ3EYqnuxnATljuiIQG2n%2BW7MesrnSEmZoG%2FAO65DFg%3D%3D" rel="nofollow">关于职场-技术的交流话题</a>,看看社区内的一些朋友/前辈什么想法交流学习下,谁曾想聊的过程非常热火且愉快。帮助到了一些"<strong>曾经的我</strong>"。</p><p>所以决定将一些问题做收录。<strong>同时将一些答应大家的问题做一个详细介绍。</strong></p><ol><li>我是具体怎么"爬起来"的。。</li><li>比如职场焦虑到底怎么克服。</li><li>遇到技术问题我是怎么解决的。</li><li>我的日常是怎么度过的。</li><li><p><del>有什么好的职场/个人发展建议。。这个需要结合个人情况 不打算讲笼统的讲</del></p><blockquote>本篇文章对于个人发展部分/技术发展部分/职场部分/进行了收录。<strong>注意本文不涉及具体细节技术讨论。</strong> 友情提示完毕,那么"<strong>故事</strong>"开始。</blockquote></li></ol><h2>正文[上述问题介绍]。</h2><p>其实可以换一个表达,<code>个人历史的回顾与反思</code>。当然有些过往其实我是太不愿意提起,可能永远也不会提起。更多是谈谈"光鲜"的部分吧。</p><blockquote>接下来先围绕遗留话题展开阐述下:</blockquote><h3><strong>1. 我是具体怎么"爬起来"的/我的职业经历?怎么抓住机会?</strong></h3><ul><li><p>努力肯定有,但是真的很少。我工作以来<strong>加班的次数/下班学习的次数/</strong> 可以掰手指头数的过来。选择适合自己的学习法方式,劳逸结合吧。</p><ul><li><strong>我喜欢看书的感觉</strong>,真不开玩笑,但是我懒得读书。他们不冲突...;读书甚至忘记时间(比如小说,哈哈)。读书是需要实践/练习的 <strong>当学习到新技术,我就喜欢跟会这个的人装/吵架/炫耀/ 让他们来反驳我</strong>。非常感谢他们面红耳赤的样子。</li><li><strong>喜欢一个平台/喜欢一个产品-业务方向/喜欢一个技术 非常重要。学习效率会高</strong>。没兴趣怎么办,不断寻去找兴趣。实在找不到,果断换个业务领域/发展方向/环境。</li></ul></li><li><p>机会来的时候,我敢上;当所有人说不行的时候,我说我试试,大不了被说菜呗。万一我行就<strong>站起来了</strong>嘛</p><ul><li>老板/领导说:"这xxx的团队行不行,搞半个月一个月了。这点问题都解决不了! " 恰好我在旁边。我直接说<strong>我可以试试吗。</strong> 有时候真的需要逼自己一把。</li></ul></li><li><p>在A公司用时2个多月,从研发-小组长。原因分析如下:</p><ul><li>领导信任度高,眼光好。安排我的工作都属于核心部分/很重要。<strong>40%</strong></li><li>团队成员压力大离开了。最后只剩我一个/安抚我的一个手段吧。 <strong>20%</strong></li><li>我将团队想法/团队后续工作的开展做了一个计划方案 发给领导并提了一嘴。<strong>20%</strong></li><li>我负责的模块稍微复杂点(还是得🙏领导)。而且跟其他团队协作时间不少,所以公司人缘还不错。领导认可。 <strong>20%</strong></li></ul><p><strong>后面就一路绿灯/逐步负责了整个h5/web/小游戏部门的技术管理和审核工作。(也是因为小游戏,我开始接触了游戏) 用时1年+</strong></p></li><li><p>在B公司用时3个月,从架构-部门经理。原因分析如下:</p><ul><li>解决了几个团队和公司存在的技术问题。 <strong>30%</strong></li><li>老板很nice,眼光好,而且我俩非常聊得来(因为我做的工作跟他博士修的方向很近)。<strong>50%</strong></li><li>部门属于战略发展的新成立技术部门,团队成员非常nice/很喜欢跟这些人共事。所以我非常努力去争取过这个岗位 <strong>20%</strong></li></ul><p><strong>后面也开绿灯了。变成技术总监(注意是 副的哦,哈哈)负 责软件研发/管理/外部对接的一些工作。用时不到2年</strong></p></li></ul><h3><strong>2.职场焦虑到底怎么克服</strong></h3><p><strong>非专业,想法仅供参考。</strong></p><ol><li>降低预期。关注眼下。</li></ol><p>学会目标拆分/任务拆分/计划拆分/执行。不要把目标建立的太远太大。</p><ol start="2"><li>寻求帮助。</li></ol><p>找前辈/家人/朋友寻求帮助。分享下现状 接纳下别人的意见。</p><ol start="3"><li>身体锻炼。</li></ol><p>生理的健康 很大程度会影响你的心理。比如你健身 虽然过程很难。但是走路都很自信。😄</p><ol start="4"><li>学会停下来。</li></ol><p>停下来(休假/辞职)去看看身边的美好,自然的美好。短期的失去是为了更好的将来。</p><h3><strong>3.遇到技术问题我是怎么解决的</strong></h3><ol><li><p>查资料</p><ul><li>语言/框架/库的问题 查资料且遵循一些原则。官方的一手资料为主,大神分析的“二手资料”为辅。doc和code为主,blog为辅。</li><li>软件架构设计问题 查相关竞品方案/实践使用调研。</li><li>业界难题/个人能力无法评估的难题。 尝试从学术界寻找方案,查找/翻阅相关文献。 (个人觉得理论可以说通的大部分内容是可以落地实现的!我只是思路的堵塞而已)</li></ul></li><li><p>外援</p><ul><li>如果是时间成本很高,技术成本很低的问题。我会去寻求别人的帮助,然后去做其他稍微难点的问题。怕磨灭掉我的兴趣点</li><li>技术难题 向该技术领域专家/团队请教。如果说自己没资源,及时向领导/老板去反馈,寻求帮助。而不是自己”修仙“。</li></ul></li><li><p>如果很难解决<del>不想解决</del>,那么从产品/用户交互重新考虑设计 提供方案给产品经理。</p><ul><li>这个问题很难,时间成本高。且之前有过更成熟的产品交互体系,那么大胆的提出的你的意见/想法。有效沟通寻求解决方案。</li></ul></li></ol><h3><strong>4.我的日常是怎么度过的。</strong></h3><p>就拿今天来讲吧,可能时间节点不是那么固定,其他还是非常真实的。</p><ul><li>7.30半起床。</li><li>8.00洗漱完成/收拾完成/出门。</li><li>9.00前到公司楼下 搞杯咖啡喝。</li><li>9.00-10.00 邮件-通讯软件-社交账号(查看并回复)。状态不好就看看书/文档【更多是工作相关的】。列一下checklist。即时通讯软件的回复。</li><li>10.00-11.00 看书。除工作软件其他消息不会回复。想想本周做的工作和例会提出的预期差距 适度调整。</li><li>11.00-11.20 休息-下楼走走。点外卖。</li><li>11.20-12.00 收尾上午的工作。下午事情列详细解决思路。</li><li>12.00-13.00 不确定。</li><li>13.00-13.30 下楼走走。 尽可能营造一个不被打扰的环境。</li><li>13.30-17.30 根据checklist完成今日内容(代码/协调/问题支持/文档/准备资料/其他)。即时通讯软件(有时候聊蛮久无意义的东西 要反思。)。关注调研其他产品/市面的新技术/。</li><li>17.30-18.00 收尾工作/总结今日/想想自己比昨天强在哪。</li><li>18.01-18.30 下班下班。</li><li>19.00------ 吃饭/玩/觉得脑力还够就学习(够呛 我超级懒)。睡觉。<strong>属于预期,大概率没什么别的事情</strong></li></ul><h2>交流话题回顾:</h2><h4>----1----</h4><h5>Q:</h5><p>想请教你分析下,我现在是个小组长,在公司呆了7年,以前的老员工都走的差不多了(公司原本是某个领域的top1,因为无法变现没落了,走了一个创始人和很多8-10年+的老员工),我现在的工作就是迭代,解决线上bug。因为我学历不高,初中毕业( 有自考专科和本科)。你觉得我有机会出去重新找个吗?我自己的内心想法是学历太低了出去找的话可能很容易被刷掉,或者碰到裁员第一个裁的是我(在现在的公司我基本不会被裁)。裁掉后可能很难有面试机会</p><h5>A:</h5><ol><li>能力强/学历弱。应该培养一定的圈子。让别人主动找你换工作。扩大你的个人影响力。2. 非坐班的工作也蛮多的。可以尝试下。3. 既然你觉得你的问题就是学历 那就提升它 攻克它。而不是想。那样永远是你的弱势/ 难听点 就是你的借口</li></ol><h4>----2----</h4><h5>Q:</h5><p>你好想请问下,一般这种介绍的外包项目,工期、交付如何界定呢,之前也做过熟人介绍的,因为费用和交付扯来扯去,最后熟人都变仇人了。</p><h5>A:</h5><ol><li>所有固化的问题点项目开始前要明确。最好落到文字并达成共识。 比如工期,费用,交付。。2. 一些特殊情况 延期/推进不下去/需求调整。如何解决,责任划分,费用预期。(最简单就是公时计算 * 单价)3. 技术无价 人有价。当你觉得对方不太合适合作的时候。尽早结束项目,尽早远离。</li></ol><h4>----3----</h4><h5>Q:</h5><p>想请教你分析下,我现在是个小组长,在公司呆了7年,以前的老员工都走的差不多了(公司原本是某个领域的top1,因为无法变现没落了,走了一个创始人和很多8-10年+的老员工),我现在的工作就是迭代,解决线上bug。因为我学历不高,初中毕业( 有自考专科和本科)。你觉得我有机会出去重新找个吗?我自己的内心想法是学历太低了出去找的话可能很容易被刷掉,或者碰到裁员第一个裁的是我(在现在的公司我基本不会被裁)。裁掉后可能很难有面试机会</p><h5>A:</h5><p>没有那么容易被裁员的吧</p><h4>----4----</h4><h5>Q:</h5><p>怎么控制自己去做现阶段“该做的事”?我没有像您一样幸运遇到贵人相助,目前也刚刚工作三年,中间甚至走了一截弯路(指做了别的岗位)。薪资也差不多就是平均水平。对技术的热忱也一般般,唯一驱使我赚钱的动力就是攒一点钱到三十或者三十五岁可以不用上班,躺平等死。但是现阶段肯定没法躺平。我看想要实现这个的途径都是靠着实现更大的价值,薪酬只是这个过程中的附加值。太难了。我也没有对编码的热爱。也没有很高的物欲鞭挞我。虽然表面上我还是有点卷,每天工作八小时之外保持2-4个小时的学习。但我深知我只是为了钱,而没有发自内心的对技术热爱。我也知道这样应该走不了多远。这种混沌的状态令我痛苦。总有厌学厌工到无法克制的一天。 思绪有点乱,还请您谅解。 相信您即便是有贵人相助,从底层开发干到技术总监,中间必然也是经历了非常多学习的过程,解决了一个又一个的技术难题,才能到这个位置吧。</p><h5>A:</h5><p>我感觉你的欲望不太强烈。你喜欢钱吗?你喜欢多少钱?你拿到钱要干点什么?现阶段去干需要干的事情 最重要就是心态 要学会延迟满足。为了将来的需求/享受 去让现阶段的自己更努力。其实这也说明 你目前生活质量还是不错的。 没有压力 没有”压迫“ 举个例子 最快的入睡方式前提就是不能焦虑 睡不着的后果。同理 不要太迷茫将来 做好眼下的事情。任务拆分, 降低预期。及时奖励。</p><h4>----5----</h4><h5>Q:</h5><p>大佬,我是社恐,就像开会都无法提出问题与同事交流. 你觉得我应该怎么做才能得到领导的赏识?</p><h5>A:</h5><p>简单分析下 然后你自己来决定怎么去做。1. 技术岗位对沟通能力包容性很强了, 能力越强沟通要求相对越低。2. 社恐 还是要改变的。毕竟人是社交动物。想谋求好的发展还是需要好的沟通能力。智商决定起点 情商绝对上限。3. 想得到赏识是有价值产出。不一定非的在沟通上。只需要体现出你的价值。比如领导交代的任务你线上及时给予反馈。难题攻克 梳理复盘总结 沉淀为团队知识。。 不一定非的线下。最后 希望你能勇敢点。 多听听梁静茹的勇气吧。</p><h4>----6----</h4><h5>Q:</h5><p>大佬是总监,想问问,对于技术总监和非技术总监,提拔一个人在哪几点(这个应该大同小异),最大的侧重点分别是...</p><h5>A:</h5><p>在公司项目环境下,代码能力肯定和主程差距没那么大。更多的是眼光 全局观 前瞻性 队伍协调性 项目把控 协调资源 把控全局吧类似。毕竟是领导岗位。</p><h4>----7----</h4><h5>Q:</h5><p>本人技术栈Vue,Android! 想往canvas,webgl 渲染方向去学习!请问需要哪些知识储备?</p><h5>A:</h5><p>www.bilibili.com看看我之前录的直播吧 思路我大概讲过。推荐倍速1.5(直播照顾一些人。)</p><h4>----8----</h4><h5>Q:</h5><p>请教大佬平时是怎么学习的?</p><h5>A:</h5><p>看书--实践-- 吹牛 哈哈哈我近几年思考能力逐步在减退。我也在反思。感觉没有之前对新事物的强烈好奇心和好胜心了。</p><h4>----9----</h4><h5>Q:</h5><p>批量下载文件,是前端一个个文件去请求打成zip包好,还是后端先对文件进行打包压缩处理,然后前端只需要下载一个压缩文件好?从服务器压力和接口性能等方面考虑,不为偷懒,只为讨论最优实现\~</p><h5>A:</h5><p>这是个好问题,围观一下。前端处理的好处是文件只用执行一次下载,如果后端打包压缩,那么相当于服务端要下一次,前端再下一次。劣势就是多网络请求的消耗了。我觉得如果文件不大,并且接口的并发不高可以服务端打包。</p><h4>----10----</h4><h5>Q:</h5><p>成功的秘诀是啥,年龄不太大就当副总</p><h5>A:</h5><p>机会来的时候能把握住吧。 我成名是因为解决了公司内部其他一个团队(7,8个研发)一个月没解决的问题 我半天搞定了。</p><h4>----11----</h4><h5>Q:</h5><p>能说说你最有成就感的事情吗,想听听。</p><h5>A:</h5><p>没什么特别有成就的。我的旅途可能太顺利了。 被好几个姑娘喜欢算吗?一个月减肥30+斤算吗? 解决一个team1个月没解决的bug算吗?....但是背后付出代价其实也不容忽视。我清晰的感知到自己没有以前快乐了。</p><h4>----12----</h4><h5>Q:</h5><p>对一个大数据量(单页1000条以上)的列表批量编辑,允许翻页和查询,但是要记录修改过的数据,有哪些好的性能优化建议呢</p><h5>A:</h5><p>前端 无非就是虚拟渲染(虚拟滚动条/可是区域的动态渲染)。换更适合的渲染协议去做。不是只考虑dom。后端 数据表是否合理 数据是否压缩和分片 传输方式是否合理 数据处理/查询是否合理。。。。关键要定位问题解决问题。学会性能监控和性能分析定位。</p><h4>----13----</h4><h5>Q:</h5><p>请问18届软工毕业,现在也五年了,刚开始做一些后台管理系统 crm erp,也做过RN,技术栈就是vue2年 react3年,也搞过webgis(leaflet,openlayers),webrtc(jssip,janus) 相关的,被动主动跳槽也有4次了,现在在一家快上市的北京小公司,迷茫中,前端没有团队就我一个,工资也就20;该如何破局,今年市场行情也很糟糕;本人对游戏也是有浓厚兴趣的 课余看了看cocos感觉蛮easy有望去游戏行业吗?</p><h5>A:</h5><p>答案是有的。没有做不成的事情 看你想不想而已。cocos都是小游戏/网页游戏类的居多 还有api封装是比较完善的。所以简单是正常的。希望越大失望越大 平常心。</p><h4>----14----</h4><h5>Q:</h5><p>如果用vue写页面,然后想打包成移动端,还能热更新,有什么方案吗。目前是flutter起一个webview套壳,效果不太好,因为本身webview启动就慢。还是说用uniapp打包推送强更更好呢?</p><h5>A:</h5><p>1. 为啥不用UniApp2. vue+Cordova3. 找三方插件/不光你有这个需求。</p><h4>----15----</h4><h5>Q:</h5><p>前端大专毕业在北京一家中小公司待三年了,薪资也给涨过几次,以后估计没法涨了,一直都后台管理那一套偶尔有小程序,近一年就只是一些线上bug的维护,空闲时间特别多,也是毫无目标的摸鱼,也没职业发展的目标,想请教大佬该如何破局,未来该往哪方面发展,感谢</p><h5>A:</h5><p>手机敲的。可能无前后顺序。凑乎看下 希望帮到你。1. 看看市场开发的各类技术岗JD 管理岗JD 哪个吸引你 让你有动力。2. 尝试在公司建立属于自己的团队/不一定非的职级。干事情/赚钱 一个人的力量终究薄弱。3. 技术发展 职业发展 归根结底 还是自己核心竞争力。所以选择一个行业 一个细分技术领域。深入一点。(怎么选 还是可以看看市场需求分析下,然后排除法。)4. 搞清楚你到底想要什么。你的优势到底在哪。</p><h4>----16----</h4><h5>Q:</h5><p>大佬有HC拍拍俺,有领导能这样解惑很少了</p><h5>A:</h5><p>给予也是所得,解惑也是学习。</p><h4>----17----</h4><h5>Q:</h5><p>公司 一直在招同岗位的 人员,但是现在人员是满足开发需求的, 我要准备一下被裁吗 ?</p><h5>A:</h5><p>有可能有别的业务需求。也不用太紧张。 但是被裁的问题也要注意 还是需要时刻保证自己的竞争力。有句梗 那就是写出别人改不了的代码 才有价值。</p><h4>----18----</h4><h5>Q:</h5><p>老哥年龄多大,你周围同事的年龄多大(开发同事),想知道下,我们这个行业年龄的大概范围</p><h5>A:</h5><p>我年龄不具备参考价值。我稍微有点年轻。 同事的话 20+ 30+ 40+都有 学历背景清华北大的也有。大厂 微软 meta也有。能力跟年龄挂钩/不挂钩也都有。 不行就卷国外。外资厂。。年龄真不是问题的。 收收心 干点喜欢的 干点有意义的。</p><h4>----19----</h4><h5>Q:</h5><p>我们部门的人从我进来开始陆续离职, (该部门的老员工全部离职包括组长) 我是不是要考虑离职了(主要偶尔还会欠薪), 但是我技术又很一般, 所以就在纠结</p><h5>A:</h5><p>需要考虑的问题点。1. 公司生态圈不稳定/不健康。 发薪不保证。是否还有能帮助你成长的地方/价值。没有就需要走。 不能恶性循环。你该想怎么走。何时走的问题 而不是待不待的问题。2. 技术一般是你的弱项 是否有其他优势。能否放大。 技术还能不能再精进一点, 代价是否能接受。 预期价值是否足够。</p><h4>----20----</h4><h5>Q:</h5><p>sqlite怎么去规划和使用比较合理。预期是有两张大表每月增速3w+记录,所有数据都是软删,要应对未来增减字段,数据迁移等</p><h5>A:</h5><p>我用sqlite少。提点通用的考虑方向吧。1. 表设计。字段是否合理/分表是否合理/主键字符是否合理/。。2. 索引优化/ 这块主要还是查询。3.定期清理软删除数据。/ 事务操作的合理性 比如分批啊这些。4. 建立好监控机制。日志机制。 定位问题 解决问题。</p><h4>----21----</h4><h5>Q:</h5><p>喜欢爸爸还是喜欢妈妈(假的),详细讲下linux 内核网络协议栈处理网络报文的流程</p><h5>A:</h5><p>都喜欢。我每次母亲节/父亲节 都是双份红包。他们对我只是表法爱的方式不同而已。-详细就不详细了。说下内核网络协议栈处理网络报文的流程,我的理解吧接收 --- 解析 ---- ip --- 协议解析/处理【tcp/udp】----- 业务/应用。当然还有一些硬件/软件的异常机制 会导致中断传输。。</p><h4>----22----</h4><h5>Q:</h5><p>佬儿,可以给应届生点建议吗?(前端最好</p><h5>A:</h5><p>1. 选对领导/团队 其次是平台。2. 学习的脚步不能停。3. 与校园的区别尽早了解。4. 要有自主性 要具有自我管理/团队管理的意识(考虑问题出发点不能仅仅是个人)。5. 越早介入越好。能创业就创业。不打工。等你经验有了 人有了。但你斗志没了。6.。。。蛮多所谓鸡汤 其实是受用的 自己权衡吧</p><h4>----23----</h4><h5>Q:</h5><p>公司开始优化人员,希望我给下面人打最低绩效赶人,如何解?(公司不想赔偿,无话语权,可能下一刀就是我)</p><h5>A:</h5><p>尽人事听天命。做力所能及的事情吧。当然寻找一些法律的援助 也是有意义的。发生在谁的身上都如上。</p><h4>----24----</h4><h5>Q:</h5><p>大佬,有两个线上问题,希望能得到点灵感1.在k8s的单个pod中,如果cpu节流率飙高,是不是大概率是cpu使用率一定彪到很高了?2.有个事务问题,在测试环境中和生产环境事务没有生效,但在本地环境通过junit测试事务是生效的;方法大概是 查询1->新开一个线程进行修改->查询2,查询2查询到了新开线程修改的结果,已经排除了mybatis缓存的可能,因为两次查询sql是不一样的,那么还有什么其他的可能?</p><h5>A:</h5><p>1. 节流机制 就是cpu使用超过阈值。 大部分情况是cpu使用率。 但也不排除其他原因 比如 调度 分配资源限制。。2. 这个可能从你描述来看。太多啦。归根到底 你需要排查数据库不同环境下的不一致性。。数据库配置/数据库驱动/事务网络通信/。。。</p><h2>最后</h2><p><img src="/img/remote/1460000044222429" alt="image.png" title="image.png"></p><p><strong>聚焦-做好当下,人一生很长 但是能做成一件事就很厉害了。</strong></p><p><strong>保持思考。保持热爱。保持...</strong></p><p><strong>希望我们一切都好。</strong></p><p><strong>最后再推荐往期文章,一款数据分析<a href="https://segmentfault.com/a/1190000044165480">FastVG产品</a>的介绍文章,逐步优化中。希望大家提出宝贵意见。</strong></p><p><strong>非常感谢你的阅读!</strong></p>
d3-force怎么使用?该算法是怎么实现的?
https://segmentfault.com/a/1190000044213054
2023-09-13T13:55:27+08:00
2023-09-13T13:55:27+08:00
wlove
https://segmentfault.com/u/dxgl
0
<h2>前言|force布局</h2><blockquote>笔者在fastVG产品图可视化布局中force布局采用D3-force-layout,因此介绍下该布局的一些算法逻辑和基础使用规则。</blockquote><p>本文预期收获:</p><ol><li>对于布局算法有更深入的了解。</li><li>在使用d3 & d3-force的时候 有调参规则的经验。</li><li>可结合其他渲染库进行独立使用。</li></ol><h2>算法逻辑简介</h2><h3>算法说明</h3><p><code>D3-force-layout (力布局)</code><em>模块利用<code>velocity Verlet</code>算法 实现了一个用于模拟粒子上物理力的数值积分器。当然内部的模拟做了简化, 假设每个step(的时间单位步长\_Δt</em> = 1 ,所有粒子质量 <em>m</em> = 1。因此,作用在粒子上的力 <em>F</em> 等效于在时间间隔 Δ <em>t上的恒定加速度</em> a,可以通过简单的方式将其与粒子的速度相加来模拟,然后将其添加到粒子的位置。</p><p>通俗简单来说<code>D3-force-layout</code>基于一定的物理规则来定位可视化元素(<strong>nodes and edges</strong>)。</p><h3>算法过程</h3><p>D3 的力布局使用基于<strong>物理的模拟器</strong>来定位视觉元素。</p><p>可以在元素之间设置<code>force(力)</code>,例如:</p><ul><li><strong>elements(所有元素)</strong> 都可以配置为与其他元素相互排斥</li><li><strong>elements(所有元素)</strong>可以被吸引到<strong>center(物理中也称为重心,可理解为中心)</strong>, 通俗来说就是所有节点的平均位置靠近。</li><li><strong>linked elements (链接元素)</strong> 可以设置为<strong>fixed distance(固定距离)</strong></li><li>利用<strong>collision detection(碰撞检测)</strong>, <strong>elements(元素)可以配置为避免相互交叉</strong>.</li></ul><p>通过配置, <code>force-layout</code>从而帮助我们以特定方式来进行定位元素。</p><p>本文主要讲如何使用<code>D3-force-layout</code>以及如何使用它来创建**网络可视化(<em>network visualisations</em>),集群(clusters)**展示。</p><p>请看下面这个<code>force-layout</code>的例子:假设我们有许多<code>circle</code>, 且这些<code>circles</code>分为3类(通过<code>category</code>字段区分) ,然后我们添加<code>forces</code>:</p><ul><li><strong>circles</strong>之间相互吸引(将<strong>circles</strong>聚集在一起)</li><li>碰撞检测(避免<strong>circles</strong>重叠)</li><li><strong>circles</strong>被三个重心之一吸引(<strong>category</strong>字段 :<code>A</code>,<code>B</code>或<code>C</code>)</li></ul><p><img src="/img/remote/1460000044213056" alt="image.png" title="image.png"></p><blockquote><a href="https://codepen.io/wantnocode/pen/jOzjXqB?editors=1111">在codepen中尝试编辑上面示例</a></blockquote><p><code>force-layout</code>比其他布局算法需要更多的计算量,因为算法内部的实现是迭代式的。逐步达到最优效果。</p><h2>算法结论/效果</h2><h3>force simulation</h3><p>一般来说,设置力模拟有 4 个步骤:</p><ul><li>创建<strong>对象数组</strong>(<code>nodes and edges</code>)</li><li>调用<code>forceSimulation</code>,传入对象数组 (<code>nodes</code>)</li><li>添加一个或多个<code>force functions(力函数)</code>(例如<code>forceManyBody</code>, <code>forceCenter</code>)</li><li>设置回调函数, <code>each tick (每次迭代)</code>后更新元素的位置。</li></ul><p>看个简单的例子:</p><pre><code>let width = 300, height = 300
let nodes = \[{}, {}, {}, {}, {}\]
let simulation = d3.forceSimulation(nodes)
.force('charge', d3.forceManyBody())
.force('center', d3.forceCenter(width / 2, height / 2))
.on('tick', ticked);
</code></pre><p>我们在这里创建了一个由 5 个对象组成的简单数组,并添加了两个力函数<code>forceManyBody</code>和<code>forceCenter</code>。(其中第一个使元素相互排斥,而第二个将元素吸引到中心点。)</p><p>每次模拟迭代时,<code>ticked</code>都会调用该函数。此函数将<code>nodes</code>数组连接到<code>circle</code>元素并更新它们的位置:</p><pre><code>function ticked() {
var u = d3.select('svg')
.selectAll('circle')
.data(nodes)
.join('circle')
.attr('r', 5)
.attr('cx', function(d) {
return d.x
})
.attr('cy', function(d) {
return d.y
});
}
</code></pre><p><img src="/img/remote/1460000044213057" alt="image.png" title="image.png"></p><blockquote><a href="https://codepen.io/wantnocode/pen/qBozLjv">在codepen中尝试编辑上面示例</a></blockquote><p><strong>force simulations(力模拟)</strong> 的强大和灵活集中在 <strong>force functions(力函数)</strong> 上,这些函数可以调整元素的位置和速度,以实现吸引、排斥和碰撞检测等多种效果。</p><p>D3 内置了很多有用的函数:</p><ul><li><code>forceCenter</code>(用于设置系统的重心)</li><li><code>forceManyBody</code>(用于使元素相互吸引或排斥)</li><li><code>forceCollide</code>(用于防止元素重叠)</li><li><code>forceX</code>和<code>forceY</code>(用于将元素吸引到给定点)</li><li><code>forceLink</code>(用于在连接元素之间创建固定距离)</li></ul><p>通过<code>.force()</code>将**force functions (力函数)**添加到模拟中,第一个参数是定义的 id,第二个参数是<code>force functions(力函数)</code>:</p><pre><code>simulation.force('charge', d3.forceManyBody())
</code></pre><p>下面我们展开看一下内置的<strong>force functions(力函数)</strong>。</p><h3>forceCenter</h3><p><code>forceCenter</code>对于将元素作为一个整体围绕<code>centering</code>居中是有用的。如果不设置默认坐标是 [0, 0]。</p><p>可以直接设置位置<code>[x,y]</code>初始化:</p><pre><code>d3.forceCenter(100, 100)
</code></pre><p>或使用配置功能<code>.x()</code>和<code>.y()</code>:</p><pre><code>d3.forceCenter().x(100).y(100)
</code></pre><p>然后使用以下方法将其添加到模拟中:</p><pre><code>simulation.force('center', d3.forceCenter(100, 100))
</code></pre><h3>forceManyBody</h3><p><code>forceManyBody</code>使所有元素相互吸引或排斥。可以设置吸引或排斥的强度,<code>.strength()</code>其中正值导致元素相互吸引,而负值将导致元素相互排斥。默认值为<code>-30</code>。</p><pre><code>simulation.force('charge', d3.forceManyBody().strength(-20))
</code></pre><p><img src="/img/remote/1460000044213058" alt="image.png" title="image.png"></p><blockquote><p>在创建网络图时,通常配置元素相互排斥。但对于元素聚集在一起的需求,则需要配置元素的吸引(引力)。</p><p><a href="https://codepen.io/wantnocode/pen/qBozLjv">在codepen中尝试编辑上面示例</a></p></blockquote><h3>forceCollide</h3><p><code>forceCollide</code>用于避免元素(此处是<code>circle</code>)重叠,并且可以将<code>circle</code>“聚集”在一起。</p><p>元素的<code>半径r</code>是通过将访问器函数<code>.radius</code>方法来传递给<code>forceCollide</code>'的,。此函数的第一个参数<code>d</code>是用来<code>data join</code>,可以从中得到<code>半径r</code>。</p><p>例如:</p><pre><code>let numNodes = 100
let nodes = d3.range(numNodes).map(function(d) {
return {radius: Math.random() \* 25}
})
let simulation = d3.forceSimulation(nodes)
.force('charge', d3.forceManyBody().strength(5))
.force('center', d3.forceCenter(width / 2, height / 2))
.force('collision', d3.forceCollide().radius(function(d) {
return d.radius
}))
</code></pre><p><img src="/img/remote/1460000044213059" alt="image.png" title="image.png"></p><blockquote><a href="https://codepen.io/wantnocode/pen/GRxbzMM">在codepen中尝试编辑上面示例</a></blockquote><p><code>forceManyBody</code>将所有节点聚集到一起,并将节点保持在容器的中心 ,<code>forceCollide</code>避免节点重叠。</p><h3>forceX 和 forceY</h3><p>forceX和forceY设置元素<strong>吸引到</strong>指定的位置。我们可以对所有元素使用一个中心,也可以为每个元素的基础上添加。同时使用 <code>.strength()</code> 配置引力,进行配合。</p><p>例如,假设您有许多元素,每个元素都有一个<code>category</code>具有 value<code>0</code>或<code>1</code>的属性<code>2</code>。您可以添加一个<code>forceX</code>力函数基于元素的<code>category</code>分别将元素吸引到 x 坐标100,300或500的地方:</p><pre><code>let xCenter = \[100, 300, 500\];
let simulation = d3.forceSimulation(nodes)
.force('charge', d3.forceManyBody().strength(5))
.force('x', d3.forceX().x(function(d) {
return xCenter\[d.category\];
}))
.force('collision', d3.forceCollide().radius(function(d) {
return d.radius;
}));
</code></pre><p><img src="/img/remote/1460000044213060" alt="image.png" title="image.png"></p><blockquote><p><a href="https://codepen.io/wantnocode/pen/jOzjXqB">在codepen中尝试编辑上面示例</a></p><p><code>forceManyBody</code>将所有节点聚集到一起,然后<code>forceX</code>将节点吸引到特定的 x 坐标。<code>forceCollide</code>避免(组织)节点相交。</p></blockquote><p>如果我们的数据具有相关坐标信息,当然也可以同时使用<code>forceX</code>或<code>forceY</code>去定位元素。</p><pre><code>...
.force('x', d3.forceX().x(function(d) {
return d.x;
}))
.force('y', d3.forceY().y(function(d) {
return d.y;
}))
...
</code></pre><h3>forceLink</h3><p><code>forceLink</code>将链接的元素移动到一个<strong>固定的距离(distance)</strong>。它需要<strong>links(一组链接)</strong>来指定将哪些元素链接在一起。每个链接对象指定一个<code>source</code>(源)元素和<code>target</code>(目标)元素,其中值是元素的<strong>标识id</strong> (<code>如果没有id可以用数组的索引</code>):</p><pre><code>let links = d3.range(nodes.length - 1).map(function(i) {
return {
source: Math.floor(Math.sqrt(i)),
target: i + 1,
};
});
let links = \[
{source: 0, target: 1},
...
]
</code></pre><p>然后,使用<code>.links()</code>方法将<code>links(链接数组)</code>传递给<code>forceLink</code>函数:</p><pre><code>let simulation = d3.forceSimulation(nodes)
.force('charge', d3.forceManyBody().strength(-100))
.force('center', d3.forceCenter(width / 2, height / 2))
.force('link', d3.forceLink().links(links));
</code></pre><p><img src="/img/remote/1460000044213061" alt="image.png" title="image.png"></p><blockquote><p><a href="https://codepen.io/wantnocode/pen/QWmXYqZ?editors=1111">在codepen中尝试编辑上面示例</a></p><p><code>forceManyBody</code>将节点分开,<code>forceCenter</code>使节点与画布容器保持居中,<code>forceLink</code>保持链接节点之间的固定距离。</p></blockquote><h3>算法聚类group webgl渲染效果</h3><pre><code>d3.forceSimulation(nodes)
.force("charge", d3.forceManyBody())
// defaults strength: Math.min(count(link.source), count(link.target));
// default distance 30
.force("link", d3.forceLink(layout_links))
.force('x', d3.forceX().x(function(d) { // 给定坐标进行节点聚类 group分组
return groups.indexOf(d.group) * 1200;
}))
.force("y", d3.forceY().y(function(d){
return Math.floor(groups.indexOf(d.group) / 3) * 100;
}))
.stop();
</code></pre><p><img src="/img/remote/1460000044213062" alt="image.png" title="image.png"></p><h2>最后</h2><p>本文只是针对一个库的使用介绍,无合适时机引申物理模型相关知识体系。下篇打算针对于d3-force源码:<strong>力模型(Force Model), 多种力类型的实现, 多体系统求解[Barnes-Hut 算法] 迭代/约束 事件处理</strong>等方面进行深入探讨/交流。</p><p>感谢您的阅读,有问题随时请联系沟通。</p>
数据可视化分析怎么搞定?图分析?chart图分析?算法分析?
https://segmentfault.com/a/1190000044165480
2023-08-30T15:44:05+08:00
2023-08-30T15:44:05+08:00
wlove
https://segmentfault.com/u/dxgl
1
<h2>前言</h2><p>如果你有数据分析的场景需求; 如果你有数据分析-图分析的场景需求; 如果你需要汇报PPT,有形成数据图表的需求,如果你有数据分析挖掘的需求; <strong>如果你有日常研发图可视化/chart图可视化的需求</strong>....</p><p>如果有需求欢迎您接着往下看(没有需求的话 还是非常希望大家了解一下的!)。</p><blockquote>本文会介绍主要围绕产品设计展开,当然也会涉及部分技术架构设计。</blockquote><p><img src="/img/remote/1460000044165482" alt="image.png" title="image.png"></p><h3>功能介绍</h3><p>接下来由我这边给大家安利一款fastVG产品,包含<strong>数据采集</strong>,<strong>数据分析</strong>,<strong>分析结论</strong>。下面展开介绍下各模块研发计划和已有功能(支持功能会注明)。</p><h4>数据采集模块</h4><ul><li>支持csv/xslx/...传统数据表格(RDB类型)+ schema + data-mapping的方式采集。<strong>【暂不支持】</strong></li><li>支持图JSON格式,一键进入分析的采集。<strong>【已支持】</strong></li></ul><h4>数据分析模块</h4><ul><li>支持图分析模式,可视化支持3种布局以上。支持分析过程的增删改查。支持数据过滤。可视化样式定制。<strong>【已支持】</strong></li><li>支持图分析模式所需要的图算法,不限于最短路径/全部路径/社区算法/邻居查找。<strong>【部分支持】</strong></li><li>支持chart图分析模式 包含数据各个维度统计汇总分析。<strong>【部分支持】</strong></li><li>支持表格分析,完备的表格过滤筛选功能。图表联动分析。<strong>【暂不支持】</strong></li><li>支持图算法分析数据量级说明 <strong>【试用版仅支持万级数据分析。专业版支持百亿规模数据分析】</strong></li><li>支持可视化分析数据量级说明 <strong>【试用版仅支持数k级数据分析 仅弹性布局开放支持十万级规模分析。专业版支持十万百万级规模数据分析】</strong></li></ul><h4>数据结论模块</h4><ul><li>支持导出分析结论 比如图片/文件(方便下次导入分析)。 <strong>【部分支持】</strong></li><li>支持分析过程帧化(将分析过程的特定步骤存为帧图/可整合ppt/gif等)。<strong>【暂不支持】</strong></li><li>支持数据分析一键式报告生成(ppt/word/) <strong>【暂不支持】</strong></li></ul><h3>目前公开的产品几点现状说明一下。</h3><ol><li>试用版 实则为mvp版本 存在功能缺失问题 需要修复请联系专业人员。</li><li>试用版本 存在性能问题。请联系专业人员支持。</li><li>试用版本 测试数据请获取方式可以查看下面说明。 也可以自己构造。</li><li>试用版本 未经本人允许禁止盈利使用。任何疑问联系专业人员支持。</li><li><strong>试用部署目前在外服 所以涉及网络问题 请多刷新几次就可以了。</strong></li><li>均为编译混淆代码。 无参考意义。如果需要技术对接请联系。</li></ol><h3>目前产品技术说明</h3><ol><li>可视化渲染性能版B/S架构下 采用WebGL+Canvas多屏渲染模式 所以渲染速度/交互速度是领先的。(可以导入数据使用弹性布局使用验证观点)</li><li>图算法分析模块 试用版本采用图算法引擎 Javascript的实现(矩阵/array结构) 存在性能问题。专业版方案是有图数据库的技术支持。提供百亿规模数据的方案。</li><li>产品目前只是作为一个功能介绍平台。所以数据资源建立都在本地。如果想获得更好的体验,请添加硬件的内存/GPU。</li><li>可视化部分支持作为技术工具库引入 超简单的微服务模式 。只需要数据/指令即可出图。</li></ol><h2><a href="https://link.segmentfault.com/?enc=BRxKzfxo5R5P2T4BpNgK7g%3D%3D.rYxAlLSjjkLLY6jiZ95ZWZ4pM8fGNLtzEcJ02De8GsNojEbPl4zLy2wA5IaZrv8j" rel="nofollow" title="试用可视化组件">讲到这里想试用FastVG的同学请点击,有疑问请看下面操作说明</a></h2><h3>试用步骤说明</h3><h4>1. 点击左侧数据--->点击导入。</h4><p><img src="/img/remote/1460000044165483" alt="image.png" title="image.png"></p><blockquote><a href="https://link.segmentfault.com/?enc=43OpSi%2Fm8jobN9ov47m2bw%3D%3D.fmflFVL0sn5jTG5SZL1S4VHs3dvvLxF6sK%2BQLtviWFynVYisHZbeNsJ5IRqF4MYt%2B4v9GE6lbD7EJGJqocSmtw%3D%3D" rel="nofollow" title="获取示例数据">获取示例数据</a></blockquote><p><img src="/img/remote/1460000044165484" alt="image.png" title="image.png"></p><h4>2. 按提示完成数据导入。</h4><p><img src="/img/remote/1460000044165485" alt="image.png" title="image.png"></p><h4>3. 可视化分析</h4><p><img src="/img/remote/1460000044165486" alt="image.png" title="image.png"></p><h4>4. 分析功能介绍</h4><p><img src="/img/remote/1460000044165487" alt="截屏2023-08-30 13.51.30.png" title="截屏2023-08-30 13.51.30.png"></p><ol><li>关系图分析工具列表,包含 弹性布局/树形布局/流向布局 显示/隐藏 合并/拆分 锁定/取消锁定 /下载图片</li><li><p>左导航菜单分为</p><ul><li>数据采集</li><li>数据分析(将数据分析的二级菜单 设置和算法也暂时放在一级了。)</li><li>分析结论(待建设)</li></ul></li><li>chat图表分析 目前支持通过选择字段类型 进行数量统计 进行常用图表展示。</li><li>图搜索 支持图中节点搜索 例如搜索节点text 会直接定位到节点位置 并选中。</li><li>图元过滤器 设计初衷是一个过滤器集合。目前这里只是显示了类型过滤。</li></ol><h2>最后</h2><p><a href="https://link.segmentfault.com/?enc=kEMN2WWIHilYPzKwRpUvhw%3D%3D.s0HQkEzxxQZ5BKKJZ%2B%2FlQb5iGx6sZIkJPq1cJnF5o8a6j97v%2FpDMkCRvnkGT5bJVUhyf0RUCFe2N3SxM76mq4A%3D%3D" rel="nofollow">提供一个意见反馈渠道</a>,任何对于试用版本的优化意见都可以留言。</p><p>非常感谢大家的阅读!</p>
记录一次内部分享会【G6js,图论和可视化的内容】
https://segmentfault.com/a/1190000043389936
2023-02-06T14:36:29+08:00
2023-02-06T14:36:29+08:00
wlove
https://segmentfault.com/u/dxgl
0
<p><img src="/img/bVc6dRz" alt="0979c41874ebdf3701d73d85833088d.png" title="0979c41874ebdf3701d73d85833088d.png"></p><blockquote><p>欢迎各位伙伴,能够观看这次live(前端数据可视化方向)。在过去的一年的分享加起来可能只有7 8次的样子,上传到B站的只有一个(总是忘记录屏...,这次也忘记了= =;) 收到了一些私信希望我多多继续,其中也有一些很好的意见,在逐步转换到我的分享中。今年会加大分享力度 内容呢就是可视化,算法,架构等等方向。</p><p>上面是我的个人微信,如果有需要可以添加一下。</p></blockquote><p><img src="/img/bVc6dRD" alt="2efb2c36903df3d7640460eca58a26c.png" title="2efb2c36903df3d7640460eca58a26c.png"></p><blockquote>今日分享主题围绕入门入行。已什么是G6?为引线展开。</blockquote><p><img src="/img/bVc6dRE" alt="a7de9080c9efe99fd1129d41352074f.png" title="a7de9080c9efe99fd1129d41352074f.png"></p><blockquote>G6是一个专注与图可视化分析的引擎,内部提供图的绘制、布局、分析、交互等基础能力。引申出 graph 和 visualization关键术语,需要分析探索一下。</blockquote><p><img src="/img/bVc6dRF" alt="c4610befd1c36567fe8f5abf2dd19d9.png" title="c4610befd1c36567fe8f5abf2dd19d9.png"></p><blockquote><p><code>Graph</code>并非我们传统认知的图画,图表,也不是指在计算机中图的数据结构。而是<code>图论</code> 一种图的理论。通俗来说它包含俩个元素 一个节点<code>vertex</code> 一个边 <code>edge</code>(或者称为实体与关系)。通过边来链接节点。(如果图中没有节点一定没边。)</p><p>图包含一些专业术语以及图的算法。有兴趣的小伙伴可以了解一下。</p></blockquote><p><img src="/img/bVc6dRG" alt="283d22bb9f43da5d4a0b62df07ea798.png" title="283d22bb9f43da5d4a0b62df07ea798.png"></p><blockquote><code>visualization</code> 可视化也并非我们传统认识 只是chart,graph等数据的可视化,还有有图像识别 地理信息展示等等。。。它存在于各大领域,科学, 工程, 新闻等等。数据可视化只是一种方向,描述。(非行业领域)。</blockquote><p><img src="/img/bVc6dRH" alt="71389e210bbbb029ed1f9e31cfec539.png" title="71389e210bbbb029ed1f9e31cfec539.png"></p><blockquote><p>进入今天的开头话题 G6有设计体系 通过一些场景分析,提取出不限于表现层,交互层等相关的设计。还有一些值得发现并深入学习研究的细节。(感谢背后团队的付出)</p><p>但是G6的软件工程设计还是可以展开谈谈的。在语雀里有内部他们的分享以及架构图。我这边通过学习贴的个人理解。</p><p>最新的4.+版本 layout模块 graph algorithms模块做了分离。(基于webWorker算法部分内部含外网链接,内网同学注意)</p></blockquote><p><img src="/img/bVc6dRP" alt="904ccbb47f34b25fc84645dde38d638.png" title="904ccbb47f34b25fc84645dde38d638.png"></p><blockquote><p>内部源码模块分析 也算是核心部分的模块。 核心主要包含:</p><p>1.基础渲染图元的封装<code>element</code>(基于<code>G</code>的封装)以及<code>combo</code>的特色实现。</p><ol start="2"><li>交互类的事件<code>events</code>,<code>animates</code>动画等模块。</li><li><code>plugins</code>插件化的工具类库实现以及如何接入使用。</li></ol></blockquote>
一个关于D3js学习的仓库建立了!
https://segmentfault.com/a/1190000042737004
2022-11-03T13:38:58+08:00
2022-11-03T13:38:58+08:00
wlove
https://segmentfault.com/u/dxgl
2
<h2>Learn-D3</h2><blockquote>因为中文教程比较少, 特定新建了一个d3相关学习(含demo)的中文<a href="https://link.segmentfault.com/?enc=P3QLquDJIMJFLTWjDwymzQ%3D%3D.ln40hp1akFCC3wl1q4u2MSToDjIt3cCnx4PMgJyMUbBEtLX07z3BspDFrN4KsTui" rel="nofollow">仓库</a></blockquote><p><strong>分享内容:</strong></p><ol><li>D3内部模块的深入讲解 有一个系统整体认知</li><li>Analysis- examples 分析场景的例子</li><li>Observable D3团队分享示例的环境介绍</li></ol><blockquote>第一点作为重点, 本系列会完整涵盖 D3 概念, 比如:选择、连接、数据请求、缩放函数、事件处理和转换。</blockquote><h3>D3-Introduction</h3><p><img src="/img/remote/1460000042737006" alt="image" title="image"><br><a href="https://link.segmentfault.com/?enc=2FV%2BM22aWLFrsCSrQzZ1tA%3D%3D.XJSWKVjTg2gQWaRpWl2Noi685rNHC16S%2BuBdocSH8GI%3D" rel="nofollow">D3.js</a>是一个 JavaScript 库,用于在 Web 上创建<strong>定制</strong>的<strong>交互式</strong>图表。</p><p><em>D3全称 Data-Driven Documents 3个D开头的单词也是它D3简写的由来。</em></p><p>大多数图表库(例如:<a href="https://link.segmentfault.com/?enc=NFuHA5122GrJIsz7FvVRog%3D%3D.oc%2B5JODIQZ4fYfTgbxHvy87lA1Hi8VEB%2FIFLiMjvHViDw%2B0RgHZs4p2DXpLLRoUR" rel="nofollow">Echarts</a>)提供的都是现成的图表,而 D3 由很多<strong>基础构建块</strong>组成,可以使用这些构建块构建自定义图表或地图。<br><img src="/img/remote/1460000042737007" alt="image" title="image"></p><blockquote><a href="https://codepen.io/wantnocode/pen/MWVNJwW?editors=1111">在codepen中尝试编辑上面示例</a></blockquote><p>使用 <code>echarts.js</code> 创建上面的条形图只需<a href="https://codepen.io/createwithdata/pen/axgoaQ">几行代码</a>,</p><p>但是使用<strong>D3</strong> 创建上面的图表就会复杂一些,因为它提供的方法更底层 (粒度更细一些)。并且需要有一些<strong>JavaScript</strong>, <strong>HTML</strong>, <strong>SVG</strong>和 <strong>CSS</strong>.的经验。</p><p>如果我们的需求只是标准条形图、折线图或饼图,应该考虑使用<code>Echarts</code>等库。但是,如果需要定制图表或有非常精确的需求,则应考虑 <code>D3js</code>。</p><h3>D3 的优势 功能到底有哪些?</h3><ul><li>非常受欢迎(上亿次的下载和上10万的star),社区活跃 有大量开发的资源( D3团队发布为主)。</li><li>超级灵活, 专注于图表组合的基础元素,例如<code>scales</code>,<code>shapes</code>。</li><li>提供数据驱动修改HTML 和 SVG 元素 。</li><li>各种标准数据加载 数据处理(例如 CSV 数据)。</li><li>生成复杂图表的助手,例如树形图、网络图。</li><li>在不同图表状态之间制作动画的强大转换效果。(非常多的内置函数)</li><li>强大的用户交互支持,包括平移、缩放和拖动。</li></ul><h3>D3 内部到底有哪些模块?</h3><p><img src="/img/remote/1460000042737008" alt="image" title="image"></p><p>上面就是<code>D3</code>所有的<code>repositories</code>(仓库), 大概分为几类:</p><ul><li>经常使用的, 基础的(带五角星的,本系列也会讲解)。例如: <code>shapes</code> <code>selection</code></li><li>工具类的 例如: <code>time format timer</code></li><li>废弃的 很长时间不更新的。<code>bundler request</code></li></ul><blockquote>注意本系列不会涉及到源码的讲解。 后续如果有需要会补充。</blockquote><h3>本系列contents(内容)</h3><p><img src="/img/remote/1460000042737009" alt="image" title="image"></p><p>上面就是本系列内容的大纲, 简单拿几个展开说说:</p><p><strong>Selection & data joins</strong></p><p><code>Selection</code>支持以<strong>数据驱动的</strong>方式添加、删除和修改 <code>HTML</code> 和 <code>SVG </code>元素。。包含了非常多函数对元素的处理,例如:<code>selecting Elements</code> , <code>modifying Elements</code> ...</p><p><code>data joins</code>支持将数据与元素进行绑定(也就是数据连接)。 </p><p>都是D3的基础模块。</p><p><strong>data requests</strong></p><p>可以帮助从给定的 URL 请求文件并将文件数据转换为 JavaScript 数组 例如(CSV)。使得后面处理真实数据变得非常容易。</p><p>支持CSV JSON TXT非常多的格式。</p><p><strong>force layout</strong></p><p>通过特定物理规则模拟,帮助我们特定方式展示元素信息。提供现成的内置力函数,并且支持拓展。</p><p><img src="/img/remote/1460000042737010" alt="image" title="image"></p><p><strong>transitions</strong></p><p><code>transitions</code>可以在不同图表状态平滑转换,制作动画。例如,有一些<code>circle</code>元素, 当用户点击<code>update data</code>时 平滑过渡到新的坐标位置。为图表增加了视觉吸引力。</p><p><img src="/img/remote/1460000042737011" alt="6c98e31c0e24358988942c787c79282f535e03788fa4482978f3c2d8a41f100ea740763315cc3c526724204a30ddea7cca6d5db8e2076b03c97ad2886d1279962f77d976d4f6f39750d512c331276b3ea9a7428848d7758b5978d8b1baf007488f76600a386bcc5c71816352c24e8e1" title="6c98e31c0e24358988942c787c79282f535e03788fa4482978f3c2d8a41f100ea740763315cc3c526724204a30ddea7cca6d5db8e2076b03c97ad2886d1279962f77d976d4f6f39750d512c331276b3ea9a7428848d7758b5978d8b1baf007488f76600a386bcc5c71816352c24e8e1"></p><h2>最后</h2><p>D3一直以来都是JavaScript最重要的数据可视化库之一,在创建者<code>Mike Bostock</code>的维护下,前途无量,至少现在没有<strong>能打</strong>的。换句话说 学习数据可视化过程中, 即便出发点不同 无论是渲染库,算法库, 工具类库 甚至工程架构。 D3这座大山是必须攀登的。</p><p>然后当您读到这里说明对上面的内容很感兴趣 那么让我们开始<a href="https://link.segmentfault.com/?enc=htVo3eLnAwCWh%2FGEmsvgew%3D%3D.MoI3LZLOT3CowrsDsXeED98ob6ZYNYnLkqcWPAWOgkHqnTaTLznu2xf3uRta7Tqp" rel="nofollow">具体模块</a>的学习吧。</p>
[live streaming] CG&WebGL&Threejs知识以及学习思维分享。
https://segmentfault.com/a/1190000041543599
2022-03-14T10:59:18+08:00
2022-03-14T10:59:18+08:00
wlove
https://segmentfault.com/u/dxgl
3
<p><strong>对于一次直播分享的文字版记录 (<del>建议看视频</del>), <a href="https://link.segmentfault.com/?enc=AtitrZlYut7QJW789ISZ6A%3D%3D.sqzi6OrNIoW%2Bbh47GE1u1B3DcZarisVe2tqD%2B9smfkoR1eCeGnd%2BUBQVQpQGPDel" rel="nofollow">视频云盘链接</a> 全篇主要分为几个方向进行了介绍:</strong></p><ol><li>Computer Graphics相关</li><li>WebGL相关</li><li>Threejs相关</li><li>Q&A 暂不记录</li></ol><blockquote>注意下述内容与直播内容可能稍微存在差异。</blockquote><hr><h2>Computer Graphics - 计算机图形学相关</h2><h3>什么是图形学? 如何学习计算机图形学?</h3><blockquote>计算机图形学就是研究如何在计算机中表示图形、以及利用计算机进行图形的计算、处理和显示的一门学科。</blockquote><p><img src="/img/remote/1460000041543601" alt="image.png" title="image.png"></p><p>上图包含101的免费分享内容,视频学习推荐某站闫大的Game101讲解(~~找不到的小伙伴, 需要链接获取可以评论)</p><h3>Rasterization 光栅化</h3><p><img src="/img/remote/1460000041543602" alt="image.png" title="image.png"></p><h3>Ray Tracing & Ray Casting</h3><p><img src="/img/remote/1460000041543603" alt="image.png" title="image.png"></p><h2>WebGL</h2><h3>WebGL内容概述</h3><p><strong>WebGL is just a rasterization engine. It draws points,lines,and triangles based on code you supply. Getting WebGL to do anything slse is up to you to provide code to use points,lines and Triangles to accomlish your task.</strong></p><p><img src="/img/remote/1460000041543604" alt="image.png" title="image.png"></p><h3>GLSL - 着色器语言</h3><p><img src="/img/remote/1460000041543605" alt="image.png" title="image.png"></p><h2>ThreeJs</h2><h3>Threejs Overview - Threejs概述</h3><p><img src="/img/remote/1460000041543606" alt="image.png" title="image.png"></p><h3>How to learn Threejs - 如何学习threejs</h3><p><img src="/img/remote/1460000041543607" alt="image.png" title="image.png"></p><h3>Threejs EXample - Threejs示例</h3><p><strong>带着思路回头再去看示例代码 顿时会感觉很简单.</strong><br><img src="/img/remote/1460000041543608" alt="image.png" title="image.png"></p><h2>最后</h2><p>可视化相关的架构设计,源码学习,日常开发。我会逐步进行深入分享。如果对你有帮助请关注我后续的内容。有需要的同学可以加一下我的联系方式(在我的主页,拉你进群聊)。</p>
【Threejs系列】-如何快速入门前置介绍
https://segmentfault.com/a/1190000041523959
2022-03-10T10:31:16+08:00
2022-03-10T10:31:16+08:00
wlove
https://segmentfault.com/u/dxgl
2
<p><img src="/img/remote/1460000041471702" alt="image.png" title="image.png"></p><ol><li>Threejs到底是什么?</li><li>学习Threejs需要提前了解什么?</li><li>3D方面知识以及术语解释</li><li>看段示例代码</li></ol><blockquote>注意下述内容是作为笔者的视角下很短时间内所学到知识点的总结,如有遗漏(不解处)及时指出 笔者做修复。(Threejs使用经验是接过外边的项目.. - -,)</blockquote><hr><h2>threejs 到底是什么?</h2><blockquote>The aim of the project is to create an easy to use, lightweight, cross-browser, general purpose 3D library. The current builds only include a WebGL renderer but WebGPU (experimental), SVG and CSS3D renderers are also available in the examples.</blockquote><p>通俗介绍来说就是包含WebGL 渲染器的3D库。简单进行一个我的认知评价(我看了部分源代码和示例):</p><p><strong>优点:</strong></p><ol><li><strong>面向对象</strong>编程实现,这样来说对于入门来说非常简单 以及后续拓展工作的简化,便捷化(OOP面向对象编程主要功能就是继承,这样想拓展直接基于基础类进行一个继承然后拓展就好了。)</li><li><strong>完整三维特性</strong>:PBR、风格化渲染、动画、粒子... 基本上上三维程序常用的功能特性都有支持。</li><li><strong>底层(源码)功能拓展非常简单</strong>, 上面提到了OOP的实现, 以至于底层的拓展不需要去了解它细节部分的实现逻辑。只需要继承一个基础类然后拓展就好啦。</li><li>生态全,行业认可度高 WebGL封装完善.... 挺多的</li></ol><p><strong>缺点:</strong></p><ol><li><strong>性能</strong> 大部分原因还是WebGL性能方面问题,其次还有就是封装问题 threejs目的是作为渲染器 那么在你特殊场景的无用模块的占用资源性能开销...</li><li>没啥大的缺点(我现在的场景没遇到..有的可以评论), 当然你不能说加载非常大的模型, 或者说要求和游戏引擎去比资源管理啊 这些完备功能。</li></ol><h2>学习Threejs需要提前了解什么?</h2><blockquote>如果你是一个前端er, 好像没什么需要准备的。因为three本身上手就蛮简单的。况且你可是一个前端工程师~! <br>不抖机灵了。进入正题:</blockquote><ol><li><strong>Javascript/typescript</strong></li><li><strong>WebGL</strong>相关概念/简单使用(在three编程中这方面封装特别好,以至于感知太差 不过最好还是了解学习一下)</li><li><strong>3D相关理论知识学习</strong></li><li><strong>数学</strong> 简单线代/矩阵的知识 不需要特别深奥回顾一下</li><li>至于WebGPU SVG什么的作为兴趣去学。(在three中还属于实践)</li></ol><h2>3D方面知识以及术语解释</h2><p>在threejs编程中含有的内容:</p><ol><li><strong>场景(Scene)</strong>:是物体、光源等元素的容器,可以配合 chrome 插件使用,抛出 window.scene即可实时调整 obj 的信息和材质信息。</li><li><strong>相机(Camera</strong>):场景中的相机,代替人眼去观察,场景中只能添加一个,一般常用的是透视相机PerspectiveCamera(透视相当于人眼模式) 其他还包含正交,阵列等等。。</li><li><strong>物体对象(Mesh)</strong>:包括二维物体(点、线、面)、三维物体,模型等等</li><li>光源(Light):场景中的光照,如果不添加光照场景将会是一片漆黑,包括全局光、平行光、点光源等</li><li><strong>渲染器(Renderer)</strong>:场景的渲染方式,如webGL\canvas2D\Css3D。</li><li><strong>控制器(Control)</strong>: 可通过键盘、鼠标控制相机的移动</li><li>其他模块:</li></ol><p><strong>加载器 灯光 材质 数学库....</strong></p><h2>看段示例代码</h2><blockquote>了解了上面的概念,学习threejs官网的示例代码就很简单了。按着3D模块去看。场景======渲染======相机===== 物体对象 | 控制器 | 灯光 | 加载器====== 效果图。 so easy~</blockquote><p><img src="/img/remote/1460000041523961" alt="image.png" title="image.png"></p><h2>最后</h2><p>可视化相关的架构设计,源码学习,日常开发。我会逐步进行深入分享。如果对你有帮助请关注我后续的内容。有需要的同学可以加一下我的联系方式(在我的主页,拉你进群聊)。</p>
JS引擎->V8, 2021最新执行流程分析
https://segmentfault.com/a/1190000041511338
2022-03-08T10:31:30+08:00
2022-03-08T10:31:30+08:00
wlove
https://segmentfault.com/u/dxgl
4
<p><img src="/img/remote/1460000041471702" alt="image.png" title="image.png"></p><ol><li>什么是V8?</li><li>V8整体执行流程</li><li>V8流程细节分析</li></ol><h2>什么是V8?</h2><blockquote>V8 是 Google 的开源高性能 JavaScript 和 WebAssembly 引擎,用 C++ 编写。它用于 Chrome 和 Node.js 等。它实现了<a href="https://link.segmentfault.com/?enc=6dGlMdXGcx9p2bCRGqCoHw%3D%3D.KOHoaKc3TrinruPjuuxNpZfwHdXcgaZYJVMTceryuq8%3D" rel="nofollow">ECMAScript</a>和<a href="https://link.segmentfault.com/?enc=qtTVeGROSrEcmLe4iaCP4A%3D%3D.r%2Fm7uSnNkJ5uA7Jk%2B2rPgnBjkD4a%2FSsTiBQ5noINGvmcP2TxbH8LMIsG%2Fi1yfxvU" rel="nofollow">WebAssembly</a>,并在 Windows 7 或更高版本、macOS 10.12+ 以及使用 x64、IA-32、ARM 或 MIPS 处理器的 Linux 系统上运行。V8 可以独立运行,也可以嵌入到任何 C++ 应用程序中。</blockquote><h2>V8整体执行流程</h2><p><img src="/img/remote/1460000041511340" alt="image.png" title="image.png"></p><h4>名词解释:</h4><ol><li><strong>source code</strong>: Javascript</li><li><strong>Parser</strong> 处理为AST的Parser(解析器)</li><li><strong>AST</strong> 抽象语法树</li><li><strong>Ignition</strong> 解释器 将AST转换为byteCode</li><li><strong>Sparkplug</strong> 无优化编译器 可以理解为将byteCode预编译(非常的快速)</li><li><strong>byteCode execute</strong> 字节码 可以理解为跨平台的一种编码(非平台机器码)</li><li><strong>Turbofan</strong> 优化编译器 对于byteCode进行编译并对于代码进行优化</li><li><strong>Machine Code</strong> 机器码 各平台执行的代码</li></ol><h2>V8 流程细节分析</h2><h3>Source Code</h3><blockquote>就是指Javascript源代码 至于你开发中使用的是TypeScript,在进入浏览器之前也是需要编译成Js的 可以理解为V8只识别JS。<br>下边拿一个函数进行分析:</blockquote><pre><code>function add(x,y){ // <- top level code 解析环节。
return x + y; // <- non top level
}</code></pre><h3>parser</h3><blockquote>parser部分,了解过编译的同学都知道有词法分析,语法分析...</blockquote><p><img src="/img/remote/1460000041511341" alt="image.png" title="image.png"></p><p><img src="/img/remote/1460000041511342" alt="image.png" title="image.png"></p><h3>AST</h3><p><strong>AST是指源代码的抽象语法结构的树状表现形式.</strong><br><img src="/img/remote/1460000041511343" alt="image.png" title="image.png"></p><h3>Ignition ==> byteCode</h3><blockquote>Parser过后就是生成字节码 这个是V8内部的类<code>BytecodeGenerator</code>进行生成的。<br><img src="/img/remote/1460000041511344" alt="image.png" title="image.png"></blockquote><h3>Turbofan</h3><p><img src="/img/remote/1460000041511345" alt="image.png" title="image.png"><br>Turbofan是根据字节码和热点函数反馈类型生成优化后的机器码,Turbofan很多优化过程,基本和编译原理的后端优化差不多,采用的sea-of-node。</p><h3>Sparkplug</h3><blockquote>Sparkplug 旨在快速编译。 非常快。 速度如此之快,几乎可以随时编译,所以能够比 TurboFan 代码更积极地对 Sparkplug 代码进行分层。<br><img src="/img/remote/1460000041511346" alt="image.png" title="image.png"></blockquote><ol><li>Sparkplug所编译的是byteCode</li><li>Sparkplug编译不做优化</li><li>Sparkplug只需要与解析器Ignition行为镜像 不需要某种状态的映射</li><li>...</li></ol><h2>最后</h2><p><strong>俩件事:</strong></p><ol><li>可视化相关的架构设计,源码学习,日常开发。我会逐步进行深入分享。如果对你有帮助请关注我后续的内容。有需要的同学可以加一下我的联系方式(在我的主页,拉你进群聊)。</li><li><p>javascript相关内容进阶联系我主页微信,嗯就这样。 bye~</p><blockquote>哦对, 还有一件事 祝各位女神,女神节快乐。 BYE~</blockquote></li></ol>
聊聊Canvas事件机制相关 (非API层,偏框架设计方面)
https://segmentfault.com/a/1190000041506890
2022-03-07T14:42:17+08:00
2022-03-07T14:42:17+08:00
wlove
https://segmentfault.com/u/dxgl
2
<p><img src="/img/remote/1460000041471702" alt="image.png" title="image.png"></p><blockquote>以下分析均采用 <a href="https://link.segmentfault.com/?enc=ka%2FJelOzfcYkrDiD5FbdOw%3D%3D.VOhj9hW5WifbPouvIqpV7tBO48RtmbKia3AcssQJ8u8UrgAMP8lFD%2FAwzKrs4Bjt" rel="nofollow">Sigmajs</a> 框架源码进行分析,有兴趣的同学可以去查看一下。 本文主要介绍下Canvas的事件机制,和一些设计思路。</blockquote><hr><h2>图形事件,设计思路及实现介绍。</h2><p>图形事件需要支持以下的内容:</p><ul><li>支持各类事件类型</li><li>事件触发机制</li><li>事件冲突问题</li></ul><p><img src="/img/remote/1460000041506892" alt="image.png" title="image.png"></p><h3>事件类型</h3><p><img src="/img/remote/1460000041506893" alt="image.png" title="image.png"></p><ul><li><p>mouse</p><ul><li>mousedown</li><li>mousemove</li><li>mouseup</li><li>mouseenter</li><li>mouseleave</li><li>dblclick</li><li>contextmenu</li><li>click</li><li>wheel</li><li><ul><li>drag <em>基于mousedown move leave做二次开发.</em></li></ul></li><li><ul><li>dragstart</li></ul></li><li><ul><li>dragend</li></ul></li></ul><p><img src="/img/remote/1460000041506894" alt="image.png" title="image.png"></p></li><li><p>touch</p><ul><li>touchstart</li><li>touchend</li><li>touchcancel</li><li>touchmove</li></ul></li></ul><h3>事件触发机制</h3><p>根据事件的触发源不同可以分为两种:</p><ul><li>图形上触发</li><li>Canvas上触发</li></ul><blockquote>所有事件均在Canvas的DOM事件触发基础上实现。</blockquote><p>以click为例:<br>鼠标在画布上点击 触发Canvas DOM事件,然后与图形进行碰撞 如果有就是图形点击 没有就是画布点击。伪代码示例:</p><pre><code>Canvas.addEventListener("click", function(){
// MouseCoords == CanvasCoords
// getShapeAtPoint(Shapes,CanvasCoords)
if(true){
// 如果碰撞到图形
this.emit("clickGraph",false);
return
}
this.emit("clickCanvas",false);
//
}, false);
</code></pre><h3>事件冲突问题</h3><h4>drag 和 click 的冲突</h4><p>由于 canvas 是一个DOM节点, 不是每个图元作为DOM 对象可以识别,所以无法通过 dragable 设置图形是否可以拖拽,这时候使用触摸板时的点击和拖拽会冲突。</p><p>在使用 mousedown,mouseup 过程中进行模拟, 对于在两者之间判定移动的距离和时间差进行一个判断。<br>具体是Click还是drag事件。</p><h4>click 和 dbclick的冲突</h4><p>其实不算是冲突问题, 是对于dbclick的实现基于click那么就需要对click事件有一个处理,方便识别。请看代码示例:</p><pre><code>handleClick(e: MouseEvent): void {
if (!this.enabled) return;
this.clicks++;
if (this.clicks === 2) {
this.clicks = 0;
if (typeof this.doubleClickTimeout === "number") {
clearTimeout(this.doubleClickTimeout);
this.doubleClickTimeout = null;
}
return this.handleDoubleClick(e);
}
setTimeout(() => {
this.clicks = 0;
this.doubleClickTimeout = null;
}, DOUBLE_CLICK_TIMEOUT);
if (this.draggedEvents < DRAGGED_EVENTS_TOLERANCE) this.emit("click", getMouseCoords(e, this.container));
}
</code></pre><h3>其他</h3><blockquote>关于事件的冒泡, 在Sigma中存在图形和画布,以及画布做了分层。彼此存在事件先后(冒泡)顺序。</blockquote><h2>最后</h2><p>可视化相关的架构设计,源码学习,日常开发。我会逐步进行深入分享。如果对你有帮助请关注我后续的内容。有需要的同学可以加一下我的联系方式(在我的主页,拉你进群聊)。</p>
聊聊Canvas渲染相关 (非API层,偏框架设计方面)
https://segmentfault.com/a/1190000041495060
2022-03-04T14:36:00+08:00
2022-03-04T14:36:00+08:00
wlove
https://segmentfault.com/u/dxgl
1
<p><img src="/img/remote/1460000041471702" alt="image.png" title="image.png"></p><ol><li>渲染机制</li><li>渲染性能分析</li><li>非即时渲染 (<del>即时渲染</del>)</li><li>分片渲染</li><li>分画布图元拆分渲染</li></ol><hr><h2>渲染机制</h2><ul><li>Canvas绘制机制:整个画布是一个画板,在上面进行绘制各种各样的图形,一旦绘制错误需要改正,就需要重新绘制。</li></ul><pre><code> ...
ctx.fillRect(0,0,10,10);
ctx.clearRect(100, 100, 100, 100);
ctx.fillRect(0,0,100,100);
</code></pre><h2>渲染性能分析</h2><blockquote>注意关于图形计算,例如复杂图形推导 不算在渲染性能进行分析。</blockquote><p>渲染性能主要分析以下几点:</p><ul><li>渲染图元数量 (视锥体内容[当前视口内容])</li><li>渲染图元的频率 (例如交互式分析,就需要实时刷新)</li></ul><p>对应优化手段</p><ul><li>控制可视区域数据。</li><li>减少刷新频率 或者将操作行为进行切片。</li><li>即时渲染和异步渲染。</li><li>分片/离屏渲染/全局渲染局部渲染。</li><li>...</li></ul><h2>非即时渲染</h2><p><strong>即时渲染</strong>指用户交互行为一旦触发立刻执行callback。比如下面的场景:</p><p><em>一个用户控制实体颜色16ms内触发了N次。我们有俩种解决办法:</em></p><p><img src="/img/remote/1460000041495062" alt="image.png" title="image.png"></p><ol><li>每次触发都进行draw()的操作重新绘制需要绘制N次。</li><li>每隔16ms执行draw()的操作绘制最终呈现的结果。</li></ol><h2>分片渲染</h2><p><strong>分片渲染</strong>本质上是将一个列表分片,将一次渲染分割成多次渲染。减少绘制图元,能够提高帧率。</p><p><img src="/img/remote/1460000041495063" alt="" title=""></p><p>通过上面的图我们可以看到分片渲染会导致整体的渲染时间变长,所以这种方案并非用于渲染的性能优化方案,而是作为提升帧率和响应效果的优化方案。</p><h2>分画布拆分图元渲染 (也算是分片)</h2><p><em>试想一个场景 画布内有非常多的图元 图元分为很多类别,比如文字,基础几何图形... 而文字内容很多 更新频率非常低 几乎不更新 怎么设计合理?</em><br>我提供一个思路,我将用俩个画布分别绘制这俩部份内容分别是text还有shape。进而减少更新的内容 提高帧率(如下图)。</p><p><img src="/img/remote/1460000041495064" alt="image.png" title="image.png"></p><h2>最后</h2><p>可视化相关的架构设计,源码学习,日常开发。我会逐步进行深入分享。如果对你有帮助请关注我后续的内容。有需要的同学可以加一下我的联系方式(在我的主页,拉你进群聊)。</p>
[live streaming] 一文看懂D3js的设计,学会如何阅读源码。
https://segmentfault.com/a/1190000041471700
2022-02-28T15:00:06+08:00
2022-02-28T15:00:06+08:00
wlove
https://segmentfault.com/u/dxgl
5
<h2>前言</h2><p>每周群内直播记录.欢迎大家加入前端可视化学习群.(2022/02/25)</p><p><img src="/img/remote/1460000041471702" alt="image.png" title="image.png"></p><ol><li>what is D3js</li><li>D3js Design</li><li>D3js Source code</li><li>Q&A</li></ol><h2>---------</h2><h3>what is D3js? (什么是D3js)</h3><p><strong>D3js</strong> is a JavaScript library for visualizing data using web standards. D3 helps you bring data to life using SVG, Canvas and HTML. D3 combines powerful visualization and interaction techniques with a data-driven approach to DOM manipulation, giving you the full capabilities of modern browsers and the freedom to design the right visual interface for your data.</p><blockquote><strong>D3js</strong>是一个JavaScript库,用于使用web标准可视化数据。D3帮助您使用SVG、画布和HTML将数据带入生活。D3将强大的可视化和交互技术与数据驱动的DOM操作方法相结合,为您提供现代浏览器的全部功能,并为您的数据设计正确的可视化界面。</blockquote><p><strong>下文D3js简称D3</strong></p><p><img src="/img/remote/1460000041471703" alt="image.png" title="image.png"></p><h3>D3js Design ? (D3js的设计)</h3><p><img src="/img/remote/1460000041471704" alt="image.png" title="image.png"><br>D3的威名在业内可算是top级别的,很多库也是学习借鉴了D3的某一个模块(甚至直接就拿过去用)。这个也是得益于D3设计的优势。D3设计的原则就是最大程度的进行解耦。</p><ol><li><strong>canvas,svg</strong> 作为该库的渲染API。</li><li><p>各个模块进行解耦,包含shape,selection等等...</p><ul><li>shape 离散的图形形状实现,例如符号、圆弧、直线和区域</li><li>selection 选择允许对文档对象模型 (DOM) 进行强大的数据驱动转换:设置属性、样式、属性、HTML或文本内容等</li><li>time 一个计算人类特殊时间约定的计算器</li><li>path 将画布路径命令序列化到SVG</li><li>scale 将抽象数据映射到视觉表示的模块。</li><li>zoom 使用鼠标或触摸输入平移和缩放SVG,HTML或canvas.</li><li>force <strong>弹性布局算法 采用的四叉树。(可参考我的其他文章)</strong></li><li>sankey <strong>流向布局算法</strong></li><li>...</li></ul></li></ol><ol start="3"><li>基于各个模块的融合使用可以构建丰富的图表。</li></ol><h3>D3js Source code (D3js 源码部分)</h3><h4>selection</h4><p>对于<code>append.js</code><a href="https://link.segmentfault.com/?enc=hvnAjAWUhdkqc68WOh4skg%3D%3D.5a30rdB4e64gnhdtyQxPMWmI9U2htO%2Bbugi3z%2FoW%2BrZQ4dYPJ%2FVRzOI9MRjo%2BvvD0xPLSkVJnjBCMynBBZGc0mn7HuRXl7g%2BnAwC3MLeAbU%3D" rel="nofollow">源码</a>进行了解其实就是dom元素appendChild的操作。</p><p><img src="/img/remote/1460000041471705" alt="image.png" title="image.png"></p><p>对于<code>classed.js</code><a href="https://link.segmentfault.com/?enc=hWgGHAPvyVuEc%2FuaPkglRw%3D%3D.dNbvyvEwpRvXUPuw2Jg%2B5AaExouGTaUL0r8oZkXlbTc0JXJDW2TtxgHnh%2BuXuMuHQLbwdd4dhfIPt%2B6Vv9dqQIfBnnPw5lf5bjCarn3X%2BoU%3D" rel="nofollow">源码</a>进行了解其实就是每个元素class的集合管理</p><p><img src="/img/remote/1460000041471706" alt="image.png" title="image.png"></p><h4>path</h4><p>对于<code>path.js</code><a href="https://link.segmentfault.com/?enc=VSuoZL16GMBW%2FYA%2Bz9Slkw%3D%3D.Bsu%2BBFnHlVr%2BR9Vz3zYw9ijTYc0TW2ScodRpeetpOH9AocXoT%2FR3uDpAxYonnBhZT0LMzjfrbPCoCmx%2FHL6SfQ%3D%3D" rel="nofollow">源码</a>进行了解,其实是对canvas2d的API进行了封装。这个也是D3对于canvas2d支持的一个兼容模块。<br><img src="/img/remote/1460000041471707" alt="image.png" title="image.png"></p><h3>Q&A</h3><h4>A同学: 如何学习框架源码 ?</h4><blockquote>框架源码学习,一定是先有宏观认知,再去学习具体某一个模块代码。什么是宏观认知,了解下框架的设计,背后的故事. 以及它是为了解决什么问题出现。最直接有效的例子 先去吧源码的目录结构搞明白 画一个思维导图进行分析。</blockquote><h4>可视化学习一定要学习数学,图形学嘛?</h4><blockquote>当你只是为了满足日常需求开发,就不一定需要学。 但是如果想学好 后面想更深入的学习。那么是的,需要学习。比如线性代数,矩阵等等。我后面会给大家推荐一些完整的学习书籍。</blockquote><h2>最后</h2><p>可视化相关的架构设计,源码学习,日常开发。我会逐步进行深入分享。如果对你有帮助请关注我后续的内容。有需要的同学可以加一下我的联系方式(在我的主页,拉你进群聊)。</p>
【可视化-源码阅读】antvis / g-base解读 - 1
https://segmentfault.com/a/1190000041442624
2022-02-22T14:57:56+08:00
2022-02-22T14:57:56+08:00
wlove
https://segmentfault.com/u/dxgl
5
<h2>前言</h2><p>分析大佬所写的代码有助于个人成长。今天来分析学习一下<code>G</code>的内部实现 版本为 <a href="https://link.segmentfault.com/?enc=86F7NG5vD04iAm4MeyfI7Q%3D%3D.hKj6ZUUy2CfK0Kg67eym4751e7n%2B8YsD1sxVWTfDBqUYk0YVSzDSPEoJpF0PWrfBgYZWvxjpYm4TOUSc01Ha1Q%3D%3D" rel="nofollow" title="0.5.1">0.5.1</a>。(其实我就是想偷偷卷 不为别的)。</p><blockquote>代码是其次 背后的设计 思路 落地方案才是重中之重; 开始源码分析之前先看看g的技术方案(<strong>以及截取一张内部开发人员的自我评价</strong>..) [各个渲染模式不同的入口,不同版本的 G 都有 Canvas、Group 和 Shape 的实现,通过统一的 interface 对外提供支持。】</blockquote><p><img src="/img/remote/1460000041442626" alt="image.png" title="image.png"></p><p><img src="/img/remote/1460000041442627" alt="image.png" title="image.png"></p><h2>开始我们的源码征途</h2><h3>1. 选择对应tags所在分支源码[0.5.1]的源码(我看到G6是用的这个..)</h3><p><img src="/img/remote/1460000041442628" alt="image.png" title="image.png"></p><h3>2. 查看一些工程方面的东西 看看怎么下手</h3><ul><li>package.json 查看了一些相关指令 用到lerna这个包管理 嗯 - -, 不是重点 接着往下。</li></ul><p><img src="/img/remote/1460000041442629" alt="image.png" title="image.png"></p><ul><li>lerna.json下面查看一下packages,可以理解源码大概包含了几个模块。分别是g-base g-canvas g-svg g-math。</li></ul><p><img src="/img/remote/1460000041442630" alt="image.png" title="image.png"></p><h3>3. 一起去看看内部的源码</h3><h4>进入packages目录下,看到了这几个文件。这里按我所了解的描述下每个的模块职责。</h4><p><img src="/img/remote/1460000041442631" alt="image.png" title="image.png"></p><ul><li>g-base <br>绘图所需内容进行了接口定义并实现。</li><li>g-canvas<br>2D渲染图元封装以及交互实现 canvas</li><li>g-math<br>对于几何图形的一些运算进行封装实现</li><li>g-svg<br>2D渲染图元封装以及交互实现 svg</li><li>g-webgl</li><li>g-mobile<br>webgl和mobile这个版本没有内容,待完善不介绍。</li></ul><h4>深入分析下g-base</h4><blockquote>绘图所需内容进行了接口定义并实现。</blockquote><h5>内部结构</h5><p><img src="/img/remote/1460000041442632" alt="image.png" title="image.png"></p><ul><li>abstract<br>内部概念的抽象/定义, 包含 element group,shape这些。</li><li>animate<br>动画.... 关于动画注册 销毁等等... 用到了D3-timer 和 d3-ease... 关于动画数学那块。</li><li>bbox<br>关于各类shape的坐标处理,大小处理 集合方面的。 circle,ellipse,line....</li><li>event<br>画布事件相关创建,销毁 'mousedown','mouseup','dblclick'.....</li><li>util <br>工具类库...说实话这块好全(- -,) createbox,color,matrix...</li><li>index.ts <br>整体入口</li><li>interfaces.ts <br><strong>所有接口的定义,预览这个文件 可以看到内部支持和所暴露的API</strong></li><li>types.ts<br>内部"结构体"的类型定义 比如BBox,SimpleBBox ,Ponit这些。</li></ul><h5>拿几段代码看看...</h5><blockquote>目录: g/packages/g-base/src/event/event-contoller.ts / 看下实体的拾取 这个优化空间很大。但是我没看完。他这个概念好像也没什么优势。。有懂得可以交流下。</blockquote><p><img src="/img/remote/1460000041442633" alt="image.png" title="image.png"></p><p><img src="/img/remote/1460000041442634" alt="image.png" title="image.png"></p><ol><li>_getShape只是为了抽离出getShape。getShape才是拾取代码的逻辑</li><li>_getPointInfo 获取点信息 然后去碰撞<br>看了下实体的拾取,因为G内部每个图元有box的概念 所以其实就是边界碰撞。 嗯 好像也没什么。 要看内部的bbox,我这边可能没太多时间去看。有想讨论可以加我可视化群聊。</li></ol><h4>深入分析下g-canvas</h4><h5>内部结构</h5><p><img src="/img/remote/1460000041442635" alt="image.png" title="image.png"></p><blockquote>g-canvas 2D渲染图元canvas封装以及交互实现</blockquote><ul><li>shape <br>各类渲染形状 圆/椭圆等等..</li><li>util <br>各类工具 关于实体捕获的 是否在路径in-path 是否在圆内... 等等</li><li>canvas.ts <br>canvas基本操作 get,draw/drawAll..,clear...等等</li><li>group.ts <br>group-shape的实现 (一个渲染图元概念)</li><li>index.ts<br>整体入口</li><li>interfaces.ts <br><strong>所有接口的定义,预览这个文件 可以看到内部支持和所暴露的API</strong></li><li>types.ts<br>内部"结构体"的类型定义 比如BBox,SimpleBBox ,Ponit这些。</li></ul><h5>找代码看看</h5><blockquote>局部刷新绘制</blockquote><p><img src="/img/remote/1460000041442636" alt="image.png" title="image.png"></p><p>局部刷新绘制。 清除指定区域 然后save; 然后进行clip(路径剪切) 将子元素绘制在剪切路径内。 嗯 就是局部绘制。性能考虑吧。 <del>不过个人觉得分层离屏会好点。。</del></p><h3>G的其他部分就先不分析了.. 比如g-svg和Canvas设计实现出于一个目的,只是API实现方面的细节问题.大家可以仔细阅读。</h3><h2>最后</h2><p><strong>2022/02/22 记录一下。</strong></p><p>最后一个好消息:2022/02/25晚上在群内直播<code>D3</code>相关源码阅读分析分享。</p>
【必须知道的JavaScript库】 - 解决95%问题的工具插件/库 - 1
https://segmentfault.com/a/1190000041405707
2022-02-15T10:44:58+08:00
2022-02-15T10:44:58+08:00
wlove
https://segmentfault.com/u/dxgl
42
<h2>前言</h2><p>在研发周期紧张的背景下,切记要避免时间开销在<code>无用</code>工作上 (比如造轮子) 如何有效确认是否已有轮子,本篇文章做一个list来帮助大家。只需要记住下面的库就可以解决工作中95%日常问题。</p><blockquote>以下所有链接均为源代码仓库 。</blockquote><h2>- ---</h2><h3>编辑器</h3><ul><li><a href="https://link.segmentfault.com/?enc=FX5lVvFAh3oPU3gHJPINrA%3D%3D.2z3KTba9OkClYsxIr%2BQj7XNksgGAXGrabw2m%2F%2FeZBLw%3D" rel="nofollow">ace</a>:Ace ,即 Ajax.org Cloud9 Editor。</li><li><a href="https://link.segmentfault.com/?enc=nxxkhRtZKoZjTMCqwIAYew%3D%3D.Iy0FXVVudUbB4Hdw8OgF2kVtONdq%2FCydpwGYT%2BV63yi07aW4dvEXKAltd7%2FhtSyk" rel="nofollow">CodeMirror</a>:一个浏览器端的代码编辑器,用 JS 实现。</li><li><a href="https://link.segmentfault.com/?enc=22JAMPXgRW01INZFd7x2BQ%3D%3D.qrAa9tysUbhfAfXHUkPCSyD2C8129N0gqYj52h2K1fhSb4hwVDiCN8pZFQ84iROn" rel="nofollow">esprima</a>:用于多用途分析的 ECMAScript 解析器。</li><li><a href="https://link.segmentfault.com/?enc=3kXWK58wOIPdPhxdsQ5UuQ%3D%3D.8682oCuDvgJHAM5qB2bc2eyWaPq5GTGsjgYyTJXo5oIByd7kwSluWpM88yK4DCOO" rel="nofollow">quill</a>:一个带有 API 的跨浏览器富文本编辑器。</li><li><a href="https://link.segmentfault.com/?enc=va7YMxGgRf1a8CDZvpqB4g%3D%3D.9RQqEQty7zeLMyzBf7f1iAFYDKNav60jBfkuhwCB0p9Q1cllacLpdYd5VPlf3phq" rel="nofollow">medium-editor</a>:Medium.com 使用的所见即所得编辑器的克隆版。</li><li><a href="https://link.segmentfault.com/?enc=q1GAXXTuDZ0dFkwpbxQo2g%3D%3D.eXBp75R26ckKMAuUF0FIanMsdu27W4TnDdkgtUiuhCU%3D" rel="nofollow">pen</a>:享受在线编辑(支持 markdown)。</li><li><a href="https://link.segmentfault.com/?enc=UowsqdBtogo1zBlTEkq%2F%2Bw%3D%3D.5t51tXZLr5GX8ffxUDWWOHK16fQmVJF%2FLbxoDfF6enY%2BthyBDtIHhBCsHD9yvni4GHCzxbwARZm5e2ffWh3lKw%3D%3D" rel="nofollow">jquery-notebook</a>:一个易用的、简洁优雅的文本编辑器。灵感来源于 Medium。</li><li><a href="https://link.segmentfault.com/?enc=vTCrDlyQ51xpoGbFnSEQ8g%3D%3D.JMhf4AfeNcy8N5IJ2rcmAjgHqOuODsUJSceN0WIci7FKo%2BWuhE8mpbNz0HqaDlvU" rel="nofollow">bootstrap-wysiwyg</a>:小巧的、兼容 bootstrap 的所见即所得的富文本编辑器。</li><li><a href="https://link.segmentfault.com/?enc=8IfeNrwcpGrkqKYpCojFVw%3D%3D.KhVHuv6kAehi4u2Y33rYEaI5FX%2Fn1MHgQkzqypm5toRZyQYy3DMu4y7EIoJ7RfAM" rel="nofollow">ckeditor-releases</a>:适用于每个人的 web 文本编辑器。</li><li><a href="https://link.segmentfault.com/?enc=ifgV4lExpcoeSySLjUFVNA%3D%3D.w6IHjnS0cTL5noYY5hXvNcENM1G1lAJXzuskl1Lnx6srCXrjNzeDnmgDj9aUPaJa" rel="nofollow">editor</a>:一个 markdown 编辑器,但仍在开发中。</li><li><a href="https://link.segmentfault.com/?enc=vA7fNOIxf%2B5kbx9iz9hCiQ%3D%3D.hdc5YrTwYHZbO512O0D0eP0WyU9rTclZ2u1%2BaWep26GhjJkvo54xGBenEGEuCJSu" rel="nofollow">EpicEditor</a>:一个可嵌入的 JavaScript Markdown 的编辑器,拥有分屏编辑、即时预览、自动保存草稿和离线支持等功能。</li><li><a href="https://link.segmentfault.com/?enc=5WTZC5bdTWDT3%2B4ThiX4Cg%3D%3D.P9sc983N9TCKqP9fVFtPcYI6bvVaDjWp%2B2Rs6wcPKx%2Flx%2FuOr9h%2Bdj1tmwEyQZ2c" rel="nofollow">jsoneditor</a>:查看、编辑和格式化 JSON 的 web 工具。</li><li><a href="https://link.segmentfault.com/?enc=thc8GQkKkFOAPJ9%2F65NZyw%3D%3D.u8j7l8rDNaoE4GCavwDIwGu2OZYAo%2BBmzGmBacOgKPRM%2Fer8OGdtX4k8%2FSGZ6yql" rel="nofollow">vim.js</a>:拥有持久化 ~/.vimrc 特性,支持 Vim 的 JavaScript 接口。</li><li><a href="https://link.segmentfault.com/?enc=CieDt6ywyDGIAfMAQ7kXxA%3D%3D.pR6iAljnj3bAdtbKPjTMk4aAYICo1nOWvuNv06hgaQc%3D" rel="nofollow">Squire</a>:一个 HTML5 富文本编辑器。</li><li><a href="https://link.segmentfault.com/?enc=YW7f%2FSJAKHDUnYxyAoL24Q%3D%3D.Jc7AnDU6PvGeGR8Ib8vnWAAAHVCydFyZ%2BRxR4uuOhwgxQy8NgRS37X9ktbDR7ZHg" rel="nofollow">TinyMCE</a>:一个 JavaScript 富文本编辑器。</li><li><a href="https://link.segmentfault.com/?enc=fDIbOSlyA0lF0JukuPW%2Fxg%3D%3D.Vgz1fg0tXdGhkoFjvZ2sPq7ZKpZGb7hWbgLvl6h%2FJM%2BY7e9MkvZG%2FrAeJAprzofH" rel="nofollow">trix</a>:由 Basecamp 制作,适用于每天写作的富文本编辑器。</li><li><a href="https://link.segmentfault.com/?enc=yMkzQpVjlAmw4Dv5yvZTpA%3D%3D.cTSFCAMjzPOGCUS7x4iWLJrLLx59hc%2BHyydTZaygkRpmb%2BQ63qeufuXj%2F3CGEijb" rel="nofollow">Trumbowyg</a>:一款轻量且惊人的所见即所得 JavaScript 编辑器</li><li><a href="https://link.segmentfault.com/?enc=Jgpt8R5M4seookRi82EtGQ%3D%3D.Bod1jH5ZH73I%2ByrJed75%2B5%2BLgtrgGDPLiwld8%2FxTMla493Co29Xufr%2FXti3FCEVS" rel="nofollow">Draft.js</a>:用于构建文本编辑器的 React 框架。</li><li><a href="https://link.segmentfault.com/?enc=xfICZpPuUKsB9ZFUx%2BHDvw%3D%3D.7TqKiE9xKlfXqvkbdCUAWvKxP6h5Ac14%2BBk%2BTuEZb8FVYCeQ7JhLDz1srQOfU93%2Fjn9QyuQo8reDGPoAq6Xd%2BQ%3D%3D" rel="nofollow">bootstrap-wysihtml5</a>:一款简单漂亮的所见即所得编辑器。</li><li><a href="https://link.segmentfault.com/?enc=Zr%2B0EZjgyrVvgWhzgi%2FYOA%3D%3D.lMBeDzIcDxszwljSEQhSMHubcuwQuLE9xEuY6WMrzc3%2BDAYg8FOkBYLZEYIR%2B3ET" rel="nofollow">wysihtml5</a>:基于 HTML5 ,渐进增强的开源富文本编辑器,利用非常严苛的规则旨在生成符合 HTML5 规范的标签,避免生成非规范标签和同行样式。</li><li><a href="https://link.segmentfault.com/?enc=G9z9I0hiSh5xbLH8vxSWNQ%3D%3D.Q%2BZc0jassgPuEjvjqGvWpQSwEmgZsXEf3AnVS0gMKCDiwRYdPjh0YvtbcjHu%2FzVc" rel="nofollow">raptor-editor</a>:Raptor,一款 HTML5 所见即所得内容编辑器!</li><li><a href="https://link.segmentfault.com/?enc=Qh4cySj%2FKDmmRSEy0FM9BQ%3D%3D.JxjSeu5frRLo2wf4mB8wEoGb06e6G9zEuRRmr8UANx7aVnovx9DPJ9sxMa2teMsb" rel="nofollow">popline</a>:Popline 是一款 HTML5 富文本工具栏。</li><li><a href="https://link.segmentfault.com/?enc=%2B%2F8brDQZAQh4fbGiqIku0g%3D%3D.PIA7DjogfXv1ygpQEYSWGEEGqYHK8Mt1tE17vzulbCS1mlRC8%2BtYow70c9UF%2Bl7C" rel="nofollow">Summernote</a>:一个极简的所见即所得编辑器。</li></ul><h3>文档</h3><ul><li><a href="https://link.segmentfault.com/?enc=0SRBNf8qRx2HoHusqLUcag%3D%3D.SxVkhFh46MJvpCItIfdMgQufBOO3kWvrhVicfjBkXQY%3D" rel="nofollow">DevDocs</a>:一款多合一接口文档阅读器,界面统一、高效、排版精良。</li><li><a href="https://link.segmentfault.com/?enc=K0H6szyru3oijQa7ISXpfw%3D%3D.mCYWLjyeVzJ5ayC9QWmOSrXnw59VsgOZOyNFGF9Py8w%3D" rel="nofollow">dexy</a>:一款格式自由且文艺的文档工具,可用于编写任何包含代码的技术文档。</li><li><a href="https://link.segmentfault.com/?enc=%2FyuMqKYSWPkAXlRx3P3EOQ%3D%3D.DCDPm6JP3jic9oWrbq4MLEvtEykVcGA6K2Hv9r9RN%2BIpm6rWyY79JQ2uPpWD1ew%2F" rel="nofollow">docco</a>:一款快且脏、百来行、文艺范的文档生成器,用 Literate CoffeeScript 实现。</li><li><a href="https://link.segmentfault.com/?enc=yPFOoeJe99vN5D5NpibUmA%3D%3D.e5pLtNwT0TOwtPCJ94dvpTNIrlq3w5ZONiuPunMDGBy%2FwNw8UwB1WKUpg9zUHJry" rel="nofollow">styledocco</a>:根据样式表生成风格规范文档。</li><li><a href="https://link.segmentfault.com/?enc=AVobNvhlBjSKIufOpJSmDQ%3D%3D.FicXX4KS93%2F9qRLYUer%2B56fZjtlVOj5OLAYySl6AV13GX%2B1f54QWDz6Txm2f%2FiqS" rel="nofollow">Ronn</a>:构建手册。把对人类阅读友好的简单文本文件转换成 roff 格式文件,便于终端显示,也可以转换为 HTML ,便于 Web 端显示。</li><li><a href="https://link.segmentfault.com/?enc=Qb%2FYsEI3jbaZX%2B9DaixbyA%3D%3D.gr83xgiMz8%2Bya8YEmySfuMErEe%2Bza%2F3i%2B6gHekQfqBY%3D" rel="nofollow">dox</a>:一款 Node.js 开发的 JavaScript 文档生成器。Dox 不会生成结构样式严苛的文档,而是采用 JSON 表示法,以实现 markdown 和 JSDoc 风格标签。</li><li><a href="https://link.segmentfault.com/?enc=ogy3p8WMy6I16YaZVSpE7g%3D%3D.IzcDoLulQUyS1TWlrsNyXKj21uF7rxtc4h09DoHs6DB3jTuWGo6Crv7VYifv5qI6" rel="nofollow">jsdox</a>:一款将 JSDoc3 转换成 markdown 的文档生成器。</li><li><a href="https://link.segmentfault.com/?enc=md9BvvfJYUgQCu20R1ItMA%3D%3D.rO9oDT3HpY0bhqwynjD%2FvPas7CnX8aLl5HEtLPshQBI%3D" rel="nofollow">ESDoc</a>:一款为 JavaScript 设计的优秀文档生成器。</li><li><a href="https://link.segmentfault.com/?enc=Nks4oywa5HiqGOP6Tkhtkw%3D%3D.nGYpmYXSLUbUh0TCIGCourEuhUTExG4wkFvxh2XW5is%3D" rel="nofollow">YUIDoc</a>:一款提取源码注释生成接口文档的 Node.js 应用,功能类似 Javadoc and Doxygen 。</li><li><a href="https://link.segmentfault.com/?enc=dBli079qXTzOAf6hrjklsA%3D%3D.U%2FdSjAxwAJcRo9vZKsfCjiou27Zg6AWER53%2B1LgWRZGXcZNNN1MnPlZxgtbZBwPf" rel="nofollow">coddoc</a>:一款 jsdoc 解析库。Coddoc 与其他工具相比)的不同之处在于,它很容易扩展,通过 coddoc.addTagHandler 和 coddoc.addCodeHandler 来添加标签和代码解析器。 Coddoc 还可以解析源码生成接口文档。</li><li><a href="https://link.segmentfault.com/?enc=8feeA74Ki%2BXthgeyeZ3BWA%3D%3D.KaE25cxh86cQvHBqiJayaV2s1Oa1pTMoy4lFBcP42oQ%3D" rel="nofollow">sphinx</a>:一款让创建智能且漂亮文档更加简单的工具。</li><li><a href="https://link.segmentfault.com/?enc=KWG62stN38AYd9cjXeNTYA%3D%3D.U%2FB0BNe5%2BaT5KOx%2F30J%2BBelKO6s34xg8o4Pj9R%2BmL1I%3D" rel="nofollow">Using JSDoc</a></li><li><a href="https://link.segmentfault.com/?enc=gfuban3HAt%2BK5fBCvAw5Ww%3D%3D.ePd%2FSU9NLu3aLl8yWn%2F8%2B3Mk9nGMKEE8xf7aRLctF0g%3D" rel="nofollow">Beautiful docs</a>:一款 markdown 格式文档阅读器。</li><li><a href="https://link.segmentfault.com/?enc=cL5fy6m7h9y0VVjTNl2oDw%3D%3D.P09cwLT8fV4lQPEEt9S0gOeROqSniJUyXTvA8mGPv2k%3D" rel="nofollow">documentation.js</a>:支持 ES2015+ 和流注释的接口文档生成器。</li><li><a href="https://link.segmentfault.com/?enc=2jXwjbBVJ1T5viMKsTr2mw%3D%3D.Gw2UH1n1sR0LKR8w4CkLmvubkVslV6PtTJxHTvciUsC05BPTQ016gWXiQelWS3wJ" rel="nofollow">jsduck</a>:为 Sencha JavaScript 框架开发的接口文档生成器,当然其他框架也能用。</li><li><a href="https://link.segmentfault.com/?enc=OXIGyMNZWlVrPrnBL1DtOQ%3D%3D.hIlXhqH7atrQ%2BRkIbrE39DHrpN%2BC46ylQKVQkJ0AcG3vzbGOT4J2rVskXkcRFGjd" rel="nofollow">codecrumbs</a>:一款用于学习和记录代码库的可视化工具,通过在源码中插入面包屑来实现功能。</li></ul><h3>文件</h3><ul><li><a href="https://link.segmentfault.com/?enc=2UKxASHmr5J%2BoqNtdoezVQ%3D%3D.Emw41ujt8qhKnyYkDEe1zpr3DCyo7UG7tGe4rS%2F887KLShkc3%2Fv%2BMQa4avtE8dQM" rel="nofollow">Papa Parse</a>:一款强大的 CSV 库,支持解析 CSV 文件/字符串,也能导出 CSV。</li><li><a href="https://link.segmentfault.com/?enc=wmkKFP%2BfRlg6H1FHBDf3CQ%3D%3D.KjkZ%2B5FT0148hmdOob11zPYxCUG0MqZyAlv8sedvemeXhdjWMMe7JzYCww5LQ7rT" rel="nofollow">jBinary</a>:对用声明式语法描述文件类型和数据结构的二进制文件,进行高级 I/O(加载、解析、操作、序列化、存储)操作。</li><li><a href="https://link.segmentfault.com/?enc=iWRDSqmg%2BMJqNK4JOGBKWQ%3D%3D.d7poq%2BIdndfat6ClPY9YufnEgXfY3UPLhOwGCry8pOBvJMrkGReiUKJstdDkjZ%2F4" rel="nofollow">diff2html</a>:git 差异输出解析器,也是 HTML 美化器。</li><li><a href="https://link.segmentfault.com/?enc=VMdw%2FZNQO1iIaJW%2FAzqo9A%3D%3D.z3sJFbuKNuummsA7o8cleucfAfaxIMY5l8bvJjsUd0I%3D" rel="nofollow">jsPDF</a>:一款 JavaScript PDF 文件生成器。</li><li><a href="https://link.segmentfault.com/?enc=S93NK2CaQG62XlK68vS%2F5g%3D%3D.E6OzKx739F1%2B7H6QP5gmHss%2Bl%2BTau0FynAyUYYJArFx1PIjNKDxmzBgDgAlbKOZK" rel="nofollow">PDF.js</a>:一个 JavaScript PDF 阅读器,社区驱动,Mozilla 支持。</li></ul><h3>函数式编程</h3><ul><li><a href="https://link.segmentfault.com/?enc=jMLP5FXuo4JhYM%2BtaH9PCA%3D%3D.LVxvV%2BWR%2BQiAwsGe7EIWFQ0kl24AuMAfG4LrG5ksuMpoAK2%2BHdHNiGfxsoCfsCnH" rel="nofollow">underscore</a>:JavaScript 的实用工具。</li><li><a href="https://link.segmentfault.com/?enc=SWSutymjD6YfACo1TvVkSw%3D%3D.HEpN285gBtqvfEtkqQ7Yby8d4LNQYIJnXrtGEGja1fVvG%2BtiCkT6udjSoXJoMMso" rel="nofollow">lodash</a>:提供一致性、可定制、高性能和额外功能的实用库。</li><li><a href="https://link.segmentfault.com/?enc=qTGrg4x9SO%2Fr7%2BXXOHULfA%3D%3D.2aUxwlEE9vLQM0HZuKSHw9mXvS5s45dAqEQ5XIORVT0e6T94gPW%2FB2sWGI7dblxH" rel="nofollow">Sugar</a>:一个扩展了原生对象功能的 JavaScript 库。</li><li><a href="https://link.segmentfault.com/?enc=4ivEgmVkkSJvABCUAfQ8gQ%3D%3D.3UI%2FUiS3ZDhAkD39bdz9FnxzbliKPDN071e27C3nA0k%3D" rel="nofollow">lazy.js</a>:类似 <code>Underscore</code>,但性能更优越</li><li><a href="https://link.segmentfault.com/?enc=tYKGzFOKSXrbJuf6OpQGfQ%3D%3D.LHlT%2Fuo4cUDtLNSjaNoRMm5NybLLJS2IdFCGTKUtAKKMlbvpzGxSB3KFFmyWFz7w" rel="nofollow">ramda</a>:一个针对 JavaScript 程序员的实用函数库。</li><li><a href="https://link.segmentfault.com/?enc=F6wbGTWf8LM70HXtodW16A%3D%3D.8cBpJzDF0gSxfn1zu1YpIk2p0%2BhDYvzlsh1Ej7rttvo%3D" rel="nofollow">mout</a>:模块化的 JavaScript 工具库。</li><li><a href="https://link.segmentfault.com/?enc=VtmUZ5s%2F%2BNhBpUrY936MLQ%3D%3D.2ha%2BK8Y4USvYjEnEPbszUAlsMIocuQ%2Bln272eDMG4Jw%3D" rel="nofollow">mesh</a>:流数据同步工具。</li><li><a href="https://link.segmentfault.com/?enc=e0wp4tSG7ngkexJUIhBQFQ%3D%3D.%2Fs4Ji3p5rABq%2F5PZmj7p6UPnJtNviw8arJBIcBCmkXULnW%2B4%2BAJO99ocOrr2IMF6" rel="nofollow">preludejs</a>:JavaScript 硬核函数式编程。</li><li><a href="https://link.segmentfault.com/?enc=XOru7it2iy8FDzBvEarA5Q%3D%3D.lfKsO%2F8c7%2BtACC0X%2FVmQq4QwuKdiDPAoKtNuHrT1h6JE2dchakYnN3T4Wp9byr2f" rel="nofollow">rambda</a>:<em>Ramda</em> 快速小巧的替代品。</li></ul><h3>响应式编程</h3><ul><li><a href="https://link.segmentfault.com/?enc=oPvgKjZBEnzRtrf9u3hZFQ%3D%3D.k61XcoUPIo2TYgVOuAn9qC7Q7rI8cepp2LQSEoYr8rXhZhpxaw03MBzRbGu8JWuU" rel="nofollow">RxJs</a>:对 JavaScript 进行响应式扩展。</li><li><a href="https://link.segmentfault.com/?enc=RVcIq7X35u1az%2B%2FUGMp3RA%3D%3D.yJftIQrPImHUmvb5Rqiyy4lgWWgAgroic65wb1gcJw%2F4lMiCsW8O6dJmv94Dwwt%2B" rel="nofollow">Bacon</a>:JavaScript 的 FPR(函数式响应式编程)库。</li><li><a href="https://link.segmentfault.com/?enc=PNf90bgh21kmYatggkdx3w%3D%3D.0d6ew6eHugkJesgECZ7sCzCP8tWA4LzpjAK5SGExN3Q%3D" rel="nofollow">Kefir</a>:受 Bacon.js 和 RxJS 启发的 FRP 库,专注于高性能和低内存消耗。</li><li><a href="https://link.segmentfault.com/?enc=9OboqzBrTbJvuq1nG14GPQ%3D%3D.bB4cohdc6ybZUqSIvlP%2FGAWo9WPLXT77XVDhsBpwwYo%3D" rel="nofollow">Highland</a>:对 JavaScript 实用工具的重新思考,Highland 能轻易地管理同步和异步信息,而且仅使用标准 JavaScript 和类 Node 流。</li><li><a href="https://link.segmentfault.com/?enc=DSO%2BOSfDv9GshJonePJroQ%3D%3D.C%2FBptW%2FeVJUBv0ziAB5e8EPH6sEPN%2BiwNaqKni59lyU%3D" rel="nofollow">Most.js</a>:高性能 FRP 库。</li><li><a href="https://link.segmentfault.com/?enc=BuQcJikftDdfse%2B0x8tIAw%3D%3D.bnbhwLALHBZSI963pTUu4VeEa20kuydHJva1c4BnNRQ%3D" rel="nofollow">Cycle.js</a>:一款用于可预测代码的函数式和响应式 JavaScript 框架。</li><li><a href="https://link.segmentfault.com/?enc=4Oewrn1cra8NlLzfcZEZ8A%3D%3D.OnAf6iIGSP%2FwgCf8iIdamqqxKqku84l2MJmt%2BTzZXJnsqwyhwB43ifQbL1JcZRMa" rel="nofollow">concent</a>:绝对是 ❤️ 最简单却 ⚡️ 最强大的 react 状态管理开发框架,可预测、渐进式、高性能。</li></ul><h3>数据结构</h3><p><em>数据结构库用于构建一个更复杂的应用。</em></p><ul><li><a href="https://link.segmentfault.com/?enc=OYtkJ8euR%2FSIsPaO000ohg%3D%3D.hPdtoO0CMHk69PfkPRbfkVm8AQmVy%2BWJ%2B6HvNW%2Bk2kD6qL2MMFgSEWJuKLbNTUtZ" rel="nofollow">immutable-js</a>:不可变的数据集合,包括 Sequence、Range、Repeat、Map、OrderedMap、Set 和 sparse Vector。</li><li><a href="https://link.segmentfault.com/?enc=gfydRSQ3Vo9EVyhsWxi6Sg%3D%3D.srUxHVN2vmcmpbT63P8paNJ%2Bw5X3axjs8MwnSAAKkfoDkmQVAtxi%2BwRf6%2B%2Bx%2BByh" rel="nofollow">mori</a>:使用 ClojureScript 持久化数据结构和支持原生 JavaScript API 的库。</li><li><a href="https://link.segmentfault.com/?enc=7N0csWC65I9Zhv9J0Cx%2FKA%3D%3D.K5j5jpXEs66BMJrLJim07SXyMT2OmkJYbVwVPwI4Ztzq9fLeU3vjpQGLPP03eNek" rel="nofollow">buckets</a>:完整的、经过充分测试和文档完备的数据结构的 JavaScript 库。</li><li><a href="https://link.segmentfault.com/?enc=CQbBSjx1fMRzIiS1T%2FfpJg%3D%3D.mov7WZdxRvBSbM5bFDC6SffsZQE%2BvYzrU2bICDVB4qwVnTXSgOs3gU%2BXgSetK4CE" rel="nofollow">hashmap</a>:简单的哈希映射实现,支持任何类型的键值。</li></ul><h3>日期</h3><p><em>日期库。</em></p><ul><li><a href="https://link.segmentfault.com/?enc=qK3RV55yIrGOjD6poNKnDQ%3D%3D.KbmpmmZHwNIFlYaC9twXs5V4LGvjhsE72I6AfydawPhg8btE1U6uaLntVxREAmLS" rel="nofollow">moment</a>:解析、验证、操作和显示日期。</li><li><a href="https://link.segmentfault.com/?enc=MJNlwu0gFWNQ7kD5gjIOpA%3D%3D.%2FpXZIz9uDYXNZT%2FUhYa4fxWx%2FrCvNaoonYMCjS%2FwG0FIOCVYliuTt4Mu%2B4wx40Py" rel="nofollow">moment-timezone</a>:基于 moment.js 的时区库。</li><li><a href="https://link.segmentfault.com/?enc=r6PblYCxxHS9Ho%2B7pcN%2Bew%3D%3D.Y%2BvXAFCIhjlUYPRA80Gti459WhVPRyYseXEuTewm4Fed4KOglESd2Fmocmn8YFLE" rel="nofollow">jquery-timeago</a>:一款支持自动更新模糊时间戳的 jQuery 插件(如:"4 分钟之前")。</li><li><a href="https://link.segmentfault.com/?enc=cvrAtz2dkEtVfqaSb7ZO4g%3D%3D.%2Bm42soJdGcRhvEton6GL24Zs1jQJpd0K%2FAVqSGAQhYRcESvnv1m%2B3NWOVQ0oq3kW" rel="nofollow">timezone-js</a>:让 JavaScript Date 对象拥有时区功能。使用 Olson zoneinfo 文件记录时区数据。</li><li><a href="https://link.segmentfault.com/?enc=Z8f8edphWbKj8ayCzu5mZA%3D%3D.dGAwFALhkBUa1Zj%2FR%2F9U21uBXsEreTKufhXODh92mo8k8vGFOgX8ycS4EZ5chGOy" rel="nofollow">date</a>:对人类友好的 Date()。</li><li><a href="https://link.segmentfault.com/?enc=HXI%2FCYCxOirJ%2FBmZer%2B3uA%3D%3D.5Xs0RgxOdTg0NvtRPNb7H7ocH2BLwcc%2FrMZM8dfiznc%3D" rel="nofollow">ms.js</a>:小巧的毫秒转换工具。</li><li><a href="https://link.segmentfault.com/?enc=4oW1hsJ0lAPUA4NMLnVbxw%3D%3D.qX30RMigRIEWj6dxqGm5vg1DRUUW1anmZJN3IhDQP4djGD8zVAN5HqdHYOA67EHv" rel="nofollow">countdown.js</a>:超小倒计时。</li><li><a href="https://link.segmentfault.com/?enc=32V5wLXG0ps9Q11gkjinLg%3D%3D.PWQwczY%2B2YNlYI3Ux3V6zJfGwzYvsBPU1%2FKil5j%2FnHw%3D" rel="nofollow">timeago.js</a>:一个非常轻量级(~1.7 Kb)的用于将时间转化成 <code>xxx时间前</code> 格式的库。</li><li><a href="https://link.segmentfault.com/?enc=EbeDOzIxB92pLtzNbZezNw%3D%3D.PSYe5GAkZmTasSWs0lNjo21XttMLeyAJ6p96GqSbkPZLXjhckhx0LUuRwyb2Y8CF" rel="nofollow">fecha</a>:轻量级日期格式化和解析库(约 2kb)。可以用来替换 moment.js 格式化和解析日期功能(moment.js 体积比较大——译者注)。</li><li><a href="https://link.segmentfault.com/?enc=z6e5ZBh%2BjJaZyYLO6sSW9Q%3D%3D.VVQ%2BV3xvb9WyPbezDdLyb4dsJLb1c%2F2qo4SPcw2p4n%2B183Ka1ZW6sWry4FdsF2dy" rel="nofollow">date-fns</a>:现代 JavaScript 日期功能库。</li><li><a href="https://link.segmentfault.com/?enc=1gpwFEAHuYrDw4LiMlChBQ%3D%3D.UDisH4xP1B56GzxLkkKxBhOs7O%2FrX7bgn87LQcGZaH0GsrqqTfrlhubb3HL2Iajd" rel="nofollow">map-countdown</a>:构建在 Google 地图上的浏览器倒计时。</li><li><a href="https://link.segmentfault.com/?enc=KmS2daNPSk3mYqTh9JuEoA%3D%3D.1qRmkEnBZI6lCLqmfMginpUxAjBo%2Fmm55IH0vJ5UFuk%3D" rel="nofollow">dayjs</a>:Day.js 是一款拥有和 Moment.js 一样的现代化接口的日期库,但它仅仅有 2kb 大小,可以用来替换 Moment.js。</li></ul><h3>字符串</h3><ul><li><a href="https://link.segmentfault.com/?enc=ZLiRd5GstBZ4bfFnmPB1%2FQ%3D%3D.zd81SpkWpzvBzU7Yn2p8sgGWDjcL3TN3uXCNJzkZO1zqXuI48m0PUU7HMonO5goz" rel="nofollow">voca</a>:一款超级好用的 JavaScript 字符串库。</li><li><a href="https://link.segmentfault.com/?enc=5Cy1x%2FxkpIKduIJhPt%2FUYA%3D%3D.N7zRSkkeAYlDO1olNDzzCgifDAPg2gR2zvNDDEiai6xjFeRyU9lQEw7UT9mCEJyS" rel="nofollow">selecting</a>:一个允许你获取用户选定文本的库。</li><li><a href="https://link.segmentfault.com/?enc=hGVNm6%2B9nLIGv4CWetWKGg%3D%3D.Q%2F3aHLoTOlomPip2SHniPFMPN%2FTNSoo2CeFqtDXsM%2Fww%2FBu5qS0gQNidkKis23UH" rel="nofollow">underscore.string</a>:Underscore.js 的字符串操作扩展。</li><li><a href="https://link.segmentfault.com/?enc=4L722rGzZohFmIffv76q5g%3D%3D.b8ovbC4A0UwJdYjT7mfAdTMab9aghbxKnS9l%2FHSmRyCFBGZ%2BG%2BGJp5tVXJxLwK2F" rel="nofollow">string.js</a>:额外的 JavaScript 字符串方法。</li><li><a href="https://link.segmentfault.com/?enc=sCeguGNxMSqOkddrRzrmGg%3D%3D.cacFyD3YgJiIikD%2FszGs%2F%2BbYHX9RYw3MIJXl3Cgt8CECdK4Thk9u2NuJx52AUg01" rel="nofollow">he</a>:健壮的 HTML 实体编码/解码器。</li><li><a href="https://link.segmentfault.com/?enc=dzC1lHGEfC1dAs8a6BMyJg%3D%3D.tI0H1aM5f1qWwYcfGJMXoCKMLuJ7h07QmPMtFbmVswnPf3E9Bp%2Fx7Mb7wtn1CDla" rel="nofollow">multiline</a>:多行字符串。</li><li><a href="https://link.segmentfault.com/?enc=WWP9aJm6Czclzb%2FSoLXuEw%3D%3D.wCDawBKFGYE1k3rj0fhRwSazLBneNu8JBYAmupophNxPl89WQQBvywGeIJg424DB" rel="nofollow">query-string</a>:解析和字符串化 URL 查询字符串。</li><li><a href="https://link.segmentfault.com/?enc=3sw6dsOiM6IR%2FTm2gqCLjQ%3D%3D.adujBfR8zF9FyidJh8MFo2Bnkx7%2FX4gMkfLK1RJ8MXtgcN0vx9ITN9ueI%2B%2BL88P6" rel="nofollow">URI.js</a>:URL 操作库。</li><li><a href="https://link.segmentfault.com/?enc=WywvfEuVNGDzuhc%2FG1JfRA%3D%3D.fXhEaEVnHrSiRJ80UlY27EIVPG8nTh2k79o6bbNeciA%3D" rel="nofollow">jsurl</a>:轻量的 URL 操作库。</li><li><a href="https://link.segmentfault.com/?enc=AEB2EaeqAF5LoU7BVhWSgA%3D%3D.Z8OXMMIGz0xej3aslLHt9qBhCgL6gkmZ0K6LloY%2BW3GrCstq9aBpU4SpGIb5qlHf" rel="nofollow">sprintf.js</a>:实现字符串格式化。</li><li><a href="https://link.segmentfault.com/?enc=fHwzCfTikM4ssmjjteR7ew%3D%3D.xl8f1PEx0Up9xBBJU7esUf2CSSIAy8DeOlLGGAPpzr%2BX4%2Bx%2BhNTD9CLIU48n2%2FGS" rel="nofollow">url-pattern</a>:比正则表达式匹配 url 或其它字符串更简单,字符串和数据可相互转化。</li><li><a href="https://link.segmentfault.com/?enc=r24ByAKP%2FIeoi4pkOA1A%2BA%3D%3D.a9eZblVtBMXjYSLJzNNft4IeZT9kaifUPZHBgXNXt7XrF6HNMIR41G1cU%2FSN1glw" rel="nofollow">plexis</a>:低保真、强大、社区驱动的字符串操作库。</li></ul><h3>数字</h3><ul><li><a href="https://link.segmentfault.com/?enc=dm%2FBB%2BQOJN6xjErHlyhTUg%3D%3D.RSGrV2Z4j0mStNu3XqMhHEPIRIvUelnndENXtT0a%2FqvOF0MW2VTAXYaenssdOR3L" rel="nofollow">Numeral-js</a>:对数字进行格式化和操作的库。</li><li><a href="https://link.segmentfault.com/?enc=6THgzhhnYtbpgqKkUCB9dg%3D%3D.z0tPrZB%2FSQEZnF%2FZT16xMgECiw6E0teCl6OibbYdNNQxEalIqR2d6JAgpfqH4Ci4" rel="nofollow">chance.js</a>:JavaScript 随机生成器,可以生成数字、字符串等。</li><li><a href="https://link.segmentfault.com/?enc=tu5qVKmkqimEXkVfCokzjw%3D%3D.oNWfkFJf98%2F7E%2Fhnm4PirSxpVXjojExqnuDtO%2F%2FqfdbMBKT1Itxyf%2Fck0Vww0yub" rel="nofollow">odometer</a>:流畅的数字过渡效果。</li><li><a href="https://link.segmentfault.com/?enc=BdEG2js2CWJg2F7AHgZA5g%3D%3D.UjxFLlGfjQ8bQ2wrEJ76n%2BYAQSu%2BuGMx82r2uv1I2JlL2GcDuzS8bKhlTKEBTlZy" rel="nofollow">accounting.js</a>:对数字、金钱、货币进行格式化的轻量库,完全本地化和无依赖。</li><li><a href="https://link.segmentfault.com/?enc=xMMVmkLfQvvjXUrtWtkeug%3D%3D.BP%2Barb8H8v%2FXlCC7K8Qcv1fFCyXa9O09SR0lpaFPuf7SznWECd%2BmpIvk7UWLVos5" rel="nofollow">money.js</a>:一个小巧(1kb)的货币转换库,适用于 web 和 nodeJS。</li><li><a href="https://link.segmentfault.com/?enc=Xy9StoJ01kcX3FaYGtPbRw%3D%3D.wmGqDV4WCc2dOIXV7vf%2FXiOXjQT7U%2Bn07e%2FEM4WGb095QhiPwO3pzfiJGdKGkjan" rel="nofollow">Fraction.js</a>:一个有理数库。</li><li><a href="https://link.segmentfault.com/?enc=1MQJP31Vo9fIabTeH6krsg%3D%3D.MgAghjKY%2BnvxEgyEgmS9zgtTFydEmrC7bJbip3VEuS%2B0pcjNplxilaleHJb7eHzV" rel="nofollow">Complex.js</a>:一个复数库。</li><li><a href="https://link.segmentfault.com/?enc=EZrighK7W2LGnarHdD0B%2FQ%3D%3D.iWN1I5vj6VRhcyXGcH2SQFVI1%2BhGtBgBub81dwKwdTuv5Xqi2pVmQfUybuVPCo0R" rel="nofollow">Polynomial.js</a>:一个多项式库。</li><li><a href="https://link.segmentfault.com/?enc=LtMFVKgZzNVD7XWLFAqV6w%3D%3D.tqh7I8z2r5cf%2BF6cj2fbrhlCIyXztsP55zqYY5pX6FtFGc8QtTY7bE%2F2i98EnRqV" rel="nofollow">Quaternion.js</a>:一款适用于 JavaScript 的四元数操作库。</li></ul><h3>存储</h3><ul><li><a href="https://link.segmentfault.com/?enc=XTVE1ffKZ%2BKwnDmxhWbdnA%3D%3D.JqF%2FHTRpAnfIrOb7ZTuR5JBtEg%2FMTkKAHLncv9rP0JikHxPekfSo%2BMwN7gPgQD7Y" rel="nofollow">store.js</a>:为所有浏览器封装了 LocalStorage,而没有使用 cookies 和 flash。隐秘地使用 localStorage、globalStorage 和用户数据。</li><li><a href="https://link.segmentfault.com/?enc=%2FQC7Ux4j7Q8DvG8ShCFR7A%3D%3D.LlWLyoK3WmMNHxGdohbXR2poHCTaOwCGxTut%2Fopzkjc8PjZv%2FOLduURTQmi1%2Fmes" rel="nofollow">localForage</a>:改善后的离线存储。其封装了 IndexedDB、WebSQL 和 localStorage,拥有操作简单和强大的 API。</li><li><a href="https://link.segmentfault.com/?enc=kcFyqYzfQZ0bPUBMBt%2F%2BpQ%3D%3D.izL6MOduHun4VxeeXodMGdej8iAVQNgfnsTu4s8cTQDJAEV7vs6FISjcSXYPoQDC" rel="nofollow">jStorage</a>:jStorage 是一个简单的键值对数据库,用于在浏览器端存储数据。</li><li><a href="https://link.segmentfault.com/?enc=rKdQop2CVZYyQ7vTkfjiNQ%3D%3D.vRAFUoasEo7vqSKpQL8AtmnipiZT%2BgTBJ7aNyJSpwqYKNWkffOpCDW42H9BVPaq5" rel="nofollow">cross-storage</a>:获得权限后,能跨域名本地存储。</li><li><a href="https://link.segmentfault.com/?enc=%2Bd9Jk9y4qH1kpTeQPznbpg%3D%3D.RTEG3FJrrVQX4Jb%2FT553adFz%2FvlkavuWTG54plQLKXYX9yDbqQjm7WfI%2FECcU2Xb" rel="nofollow">basket.js</a>:用 localStorage 加载和缓存脚本的资源加载器。</li><li><a href="https://link.segmentfault.com/?enc=GjW9WJtIBPe5EXmtZeSBCw%3D%3D.VxLtAzT6Ox2tOpc%2BKGy3H4ddF6Ul03z5NkxO%2B1Jzos9DPLt6IAY9fSp8Rldg76x%2B" rel="nofollow">bag.js</a>:可以缓存脚本和加载资源,与 basket.js 相似,但增加了键值对接口和对 localStorage / websql / indexedDB 的支持。</li><li><a href="https://link.segmentfault.com/?enc=XrZ0FjpdQVDVucJK5O8LeQ%3D%3D.HvQAISeJFuGTo9Kq3Qh4luvyOnaGmIp4GIm7Ff5PgMFNbU0m1aryGGv7EqhKPXXt" rel="nofollow">basil.js</a>:智能的 JavaScript 数据持久层库。</li><li><a href="https://link.segmentfault.com/?enc=wm5djnhvsWYadtS7OQlW0A%3D%3D.4PC%2BlglqrVIT0Np5tNG9pzhGx14XntMy6WdyExtoku%2BmawPpXhmX5gPTFHASW7py" rel="nofollow">jquery-cookie</a>:轻量简单的、用于读取、编辑和删除 cookie 的 jQuery 插件。</li><li><a href="https://link.segmentfault.com/?enc=U0kLNuNV6o0lkh7krgjptQ%3D%3D.y1Vg5vf6V1jB3R6WFBDe6lzO9iv77ag%2FfaleqDMBHox%2BOiEvcvtLk8gS9ErSxDGU" rel="nofollow">js-cookie</a>:一款简单、轻量的 cookies 读写删 jQuery 插件。</li><li><a href="https://link.segmentfault.com/?enc=UFCpaob9tSqv5q%2FNS0l4ag%3D%3D.r1m4Pyhht8S9As7oGnrleJhVosDQtKS3p%2BiZONgVn2556Cny712nOxXTCEB9VB0Q" rel="nofollow">Cookies</a>:一个客户端 Cookie 操作库。</li><li><a href="https://link.segmentfault.com/?enc=FU7Ql0N8lkwRxHXvnJkp0Q%3D%3D.OYclkSe4T94CTrrrn3V7N1%2FPzz653vR4TlAcWc7DdLnpBLFttu7ZVv1X7t0NsrKW" rel="nofollow">DB.js</a>:基于 Promise 的、封装了 IndexedDB 的库。</li><li><a href="https://link.segmentfault.com/?enc=Z8DMsbnrrB3JSeV8G94tww%3D%3D.M9g%2Bh8SViklGA3SYAwHyC2lfRUQebK6MF39idemZwL2ESpZCXyrWPfon4TFAPeBc" rel="nofollow">lawnchair.js</a>:简单的客户端 JSON 存储。</li><li><a href="https://link.segmentfault.com/?enc=WA9RMvCensLtcmJRS7jdxg%3D%3D.r0dOdYg4WgHVO%2BoaJzuBYaL2eqAtx8O1GCwdbgdEM3v4TvNEQXQn7nUFWevsB%2BRK" rel="nofollow">sql.js</a>:基于 Emscripten 将 SQLite 编译成 JavaScript。</li><li><a href="https://link.segmentfault.com/?enc=0YV8qer%2BkpePrpOsJ8Hp2g%3D%3D.uhyuoaCqEzE9JTrG84%2FKgL3fGxLxV%2BjKWweuWHbawKL0yNysLzTFBLrYYlxb4H8m" rel="nofollow">crumbsjs</a>:一款轻量级原生 ES6 cookies 和本地存储 JavaScript 库。</li><li><a href="https://link.segmentfault.com/?enc=2Cc28dZ4TaeoGDC36untcA%3D%3D.CMhR4rHK6A6OYYW0IAy5TWA1WdVLN%2FSA9vy7aZFzVsM8i6d6LYiEukifNFJoInBB" rel="nofollow">awesome-web-storage</a>:你必须知道的所有客户端存储知识。</li></ul><h3>颜色</h3><ul><li><a href="https://link.segmentfault.com/?enc=s4e8ujAL2rFRj0GsBIhiFw%3D%3D.FTZ9UVWtk8ssVQUNhdRqW14B0WkKNKjkYdxkZC4jtT2FehUlU%2FfsFnmnrZKkjDkT" rel="nofollow">randomColor</a>:JavaScript 颜色生成器。</li><li><a href="https://link.segmentfault.com/?enc=8uSWVaXKO%2BrwPX%2B4%2F4a60Q%3D%3D.J2VnpxQpVzkf3LULuDOfxvvkj9GipPk4npBxkVr9kkGjLLsWYeZ307wZNRWZvv7U" rel="nofollow">chroma.js</a>:拥有各种各样颜色操作的 JavaScript 库。</li><li><a href="https://link.segmentfault.com/?enc=jzsbCN%2BofnyHOXkkcyOEcw%3D%3D.a9zK%2FsS%2BCUdG3QV2jCSoZ3XLkaJjk0FkXlaxX4oyGSA%3D" rel="nofollow">color</a>:JavaScript 颜色转换和操作库。</li><li><a href="https://link.segmentfault.com/?enc=lVJBGtCZS6XgWfIN5tojlQ%3D%3D.jFp2MIyhu%2BesjwvGCrShZw5qAPSDRw1gpNm2gqH90Tg%3D" rel="nofollow">colors</a>:更智能的默认 web 颜色。</li><li><a href="https://link.segmentfault.com/?enc=RedoSxTcvez%2FinvvRluREA%3D%3D.sksI8q3zo62xIODfFTMtr5GNt8SzTkD67lFUbbxPEezAGwqxgKxLxTmPQpiZs7qc" rel="nofollow">PleaseJS</a>:随机创建出赏心悦目的颜色和配色方案的 JavaScript 库。</li><li><a href="https://link.segmentfault.com/?enc=sTqyJivEpLRSsthCK84TrA%3D%3D.FMjSnk78sgVixwHmxmpXFzoBFQMxiimMu6%2FtqcJuQUCg9ej1BZJqW0eFwQqmK2ow" rel="nofollow">TinyColor</a>:快速、轻巧的颜色操作和转换库。</li><li><a href="https://link.segmentfault.com/?enc=Ykn8iTHl1GiQ%2FxjRNrDsRA%3D%3D.AtGglQSZt%2Bwp27ubnC3mB%2F326mf349ev8WCaOoUbuYa9O%2FUi8ixAHxXn1bHIZm85" rel="nofollow">Vibrant.js</a>:从图像提取主要颜色。</li></ul><h3>国际化和本地化</h3><ul><li><a href="https://link.segmentfault.com/?enc=aDrcR2Tjx5Dq%2BDCKGcrCbg%3D%3D.LdWgyDFOcAqx8KX836xosglLGsJg1uD2F0%2Bq1rfu8q9Sr8JWL5G80JrgDe1HUuy1" rel="nofollow">i18next</a>:用 JavaScript 实现国际化(i18n)简单的方法。</li><li><a href="https://link.segmentfault.com/?enc=vfxFHO%2FJmr%2BFIzHlMSxAZg%3D%3D.g2tWBoExMoO5m7ilFxxnWMb1yvY6IZTqHVDl1%2FGUdOBWerG0jNoyP2wxhXeRhyB1" rel="nofollow">polyglot</a>:小巧的国际化助手库。</li><li><a href="https://link.segmentfault.com/?enc=s7sVS1rwGPk26ewE4C53vw%3D%3D.hMOI1lHsTCddRF2w8dEiOjY5yp5wLoAim4DMAuUZ6sWl4UxtY5%2B%2FZdzkmUJ9kdgj" rel="nofollow">babelfish</a>:提供友好易懂 API 的 i18n 库,并且内置多种支持。</li><li><a href="https://link.segmentfault.com/?enc=kS0FDWUioNiDhuQsH7EONw%3D%3D.AlpwWtvousJ6VBF%2FxhiytIb2gsLGAuFp2p6uFlAAeujUwvXuTPoak292FI%2FJi8nJ" rel="nofollow">ttag</a>:基于 ES6 标签模板和优秀的旧 GNU gettext ,新潮的 JavaScript 国际化、本地化库。</li></ul><h3>控制流</h3><ul><li><a href="https://link.segmentfault.com/?enc=Aerv7GEgiI%2FSlYvo1V1d%2Bw%3D%3D.Jf4EwUWE%2Fquxh1rtZZTXjesETYqdo0X6wDqop%2BLE%2BZ4%3D" rel="nofollow">async</a>:适用于 node.js 和浏览器的异步工具库。</li><li><a href="https://link.segmentfault.com/?enc=HsNEqkuC533sC9LtRJVblw%3D%3D.nlbxiLzrlPIg7%2Ftr3UIchaUZl6lFVsMSrnwRIJPiuok%3D" rel="nofollow">q</a>:实现异步 promise 的 JavaScript 工具。</li><li><a href="https://link.segmentfault.com/?enc=Ii1%2BPz0bxD7fFLkmMgdyDA%3D%3D.nPQGR4MuxRncgBxP4wMQzxcdE%2BEwb7K6SlrUtM6HnwPFK9JZTIhQUvFz6bO20cYI" rel="nofollow">step</a>:一款可以使逻辑执行更简单的异步控制流库。</li><li><a href="https://link.segmentfault.com/?enc=oZAshOq61kAncxFuvNgzTw%3D%3D.h8YHHnrR%2BbSerQMN578vAJhdMC5%2B5ND00wy8O2%2FQ2Y9tLcCTIBZLNi4LupOQlXt9" rel="nofollow">contra</a>:函数式风格实现的异步流控制。</li><li><a href="https://link.segmentfault.com/?enc=ErAGawEYLqpNsKIiFSfbyA%3D%3D.66sCBZtzq%2F9lVABcqlG95OgRacvFDgVgY7ixVkwUde58QNUhiIbcXdgPFwBj0qg6" rel="nofollow">Bluebird</a>:功能齐全的 promoise 库,专注于功能革新和性能提升。</li><li><a href="https://link.segmentfault.com/?enc=DZQzlsmAMretSP4iFkNERQ%3D%3D.3jiXxUwOK%2FC0%2BrbHMHwo2ROYPcMT0oMExybt%2BcAa5kY%3D" rel="nofollow">when</a>:快速可靠的、Promises/A+ 规范的 when() 实现,而且拥有其它异步操作的优秀特性。</li><li><a href="https://link.segmentfault.com/?enc=t8jjG7ahMn6Q5MVO4nbRVg%3D%3D.6AidoPk6DRxjHJ5Xy3wqVu05CBS50xFIP51mFOHi0AMhcvGBy2ogsHjfTeovBc1Y" rel="nofollow">ObjectEventTarget</a>:为普通对象添加事件监听原型(就如浏览器 DOMElement 的 EventTarget 一样)。</li><li><a href="https://link.segmentfault.com/?enc=KQdSDTOrYl3UOoAyj4b%2BwQ%3D%3D.PEDQHS6yyirRhuqlhRlrvGKotzt%2F6v8FssIQPzAxrOVjomhxUAruQ8w537z3JFmZ" rel="nofollow">sporadic</a>:位于 promise 顶部的组合并发抽象(就像流、协程和类似 Go channels 一样),支持 Node.js 和浏览器引擎。</li></ul><h3>路由</h3><ul><li><a href="https://link.segmentfault.com/?enc=SxUNuFofcHXFobEBkzjRPw%3D%3D.UUxTa%2FYlsUwy9drcccn%2B9TalKONioXpx7qKyWyePUjO2o8hZp3B%2FbGJbFvWVirXh" rel="nofollow">director</a>:一个小巧的、与 URL 同构的路由。</li><li><a href="https://link.segmentfault.com/?enc=Sp4UsLCktiFnORwja4EtJw%3D%3D.POiKvSQjWrurPbFYuK3e5sIRkeRBSkGzVBkYCzAfUokCLg7WF1gGKG6s9X6ErImh" rel="nofollow">page.js</a>:受 Express router 启发的小型客户端路由器(约为1200字节)。</li><li><a href="https://link.segmentfault.com/?enc=c2qoOChrmNGcHHbDoBbcGQ%3D%3D.XrL%2BdSJKC%2F2kn5tPWCYImmeKozXLfEDODu9gYhrnV%2FTaBfjJrZRyLStsp4enOARr" rel="nofollow">pathjs</a>:简单、轻量的 web 路由。</li><li><a href="https://link.segmentfault.com/?enc=dklWwKwq9hNgM9PKmDygvg%3D%3D.7TWM98abktJF5LpRZ3gVeNVrPpRn8DzCiOMc9tVPesZFFTUvfwl9D0uEepIELZTv" rel="nofollow">crossroads</a>:JavaScript 路由。</li><li><a href="https://link.segmentfault.com/?enc=RbsKhw3JmapZgt%2Fce0ZS4Q%3D%3D.PJJAzuU0t%2Fj0L4t302vaao4AMWq8f8Ii0TGIr3jqItBpDzMtCyen%2Bk9wiwXXMxOA" rel="nofollow">davis.js</a>:基于 pushState 可降级 RESTful 风格的 JavaScript 路由。</li><li><a href="https://link.segmentfault.com/?enc=DjupHH%2BIn3KZGdsTaxAeJw%3D%3D.cADBz0VzoTsL%2Fnpa3vw4pzwxKFvItwQ0hU8DL83mTsGNHGDQ38w0X2i0TlPr3yMd" rel="nofollow">navaid</a>:一款浏览器端的导航辅助系统(也可以成为路由),体积才 850 字节!</li></ul><h3>安全性</h3><ul><li><a href="https://link.segmentfault.com/?enc=oRjx7NcQi4p4CtPPgU2rQw%3D%3D.WhllSHbbScl0i9NggIlC9tk71WIXR0%2Fki%2BQpOZb9KisLuKlYrdCXYFEsNuNq0rJG" rel="nofollow">DOMPurify</a>:针对 HTML、MathML 和 SVG 的仅支持 DOM 的超快速、高容错的 XSS 过滤器。</li><li><a href="https://link.segmentfault.com/?enc=nJ1gsKapI0PEbaB3wfEiCw%3D%3D.iE2eod0SZbSB48HbnIC75sd6anmKZDzx0%2F1sP%2BQ7RdDI4bQC1ZXPhBQbLfT%2BFwVr" rel="nofollow">js-xss</a>:通过白名单配置,即可过滤不信任的 HTML(防止 XSS 攻击)。</li><li><a href="https://link.segmentfault.com/?enc=jfO3YUTRTCrRaBGX2lKr6A%3D%3D.euzZUtx2IR2Z%2B7uy0kwYw8c2vvREDgcZXQnnvE4ANhXFRHzZ0b5zNRmHxFlnW%2BJ8" rel="nofollow">xss-filters</a>:Yahoo 出品的安全 XSS 过滤器。</li></ul><h3>日志</h3><ul><li><a href="https://link.segmentfault.com/?enc=PW%2FshGb4BjuyQyuxTxLwSA%3D%3D.2NatfHletmA0DuV6ULpBzGvE2ZOSOUAVMhhHr3gOIO4fltEryj4qLUGZwVA34jBm" rel="nofollow">log</a>:带样式的 Console.log。</li><li><a href="https://link.segmentfault.com/?enc=w5eYC%2Fp4W07toUyd%2BofoZg%3D%3D.j58v%2FQbz1ey23D26t%2FJMs2ekbVymbdwKsfCabqpYylYmg3ma%2FR16W79ar%2Fkr1VhO" rel="nofollow">Conzole</a>:对 JavaScript 原生 console 对象方法和功能进行封装的 debug 面板,并将面板显示在页面内。</li><li><a href="https://link.segmentfault.com/?enc=0Tujdhoo674hBrcws2%2Fo2Q%3D%3D.Q11JeoYloB%2Bk61vXMAVkhzIYGo9dId6xUbiY84Kyy6xzWavw20Ab9t9UQU3S6OV%2B" rel="nofollow">console.log-wrapper</a>:将日志清晰地记录到 console,兼容所有浏览器。</li><li><a href="https://link.segmentfault.com/?enc=5elNR3yQ709cZKmJzsvD%2BQ%3D%3D.ybQNuLjHgXH1VqgEhVI%2Bf4NjCZKniaOPjrCZfukG184cttzV2OuJXHyeVusuO%2F3z" rel="nofollow">loglevel</a>:最轻量的 JavaScript 日志记录工具库,向封装后的 console.log 方法增加可靠的日志等级。</li><li><a href="https://link.segmentfault.com/?enc=VFgJD8yoj1m7WE14SeVcLA%3D%3D.INyju4eGzYAVQIkMXv5ZeyZqWFvjkdgcXnnrDH%2BAjDU%3D" rel="nofollow">minilog</a>:轻量的、流式 API 显示的、可用于客户端和服务器端的日志记录库。</li><li><a href="https://link.segmentfault.com/?enc=e7Oq4Gk%2BNqF4OCQ6ncD%2FSw%3D%3D.58nso2oP0XPz5jLWXcnc0Z2kGpx8T8S5wDl2SJp6mPu5nfCYRBDoD4Kx%2B8cDT89%2B" rel="nofollow">storyboard</a>:通用日志库 + Chrome 扩展。提供一个单一入口查看包含客户端和服务端的任务触发日志。</li></ul><h3>正则表达式</h3><ul><li><a href="https://link.segmentfault.com/?enc=8qjasHoWaDmbzzFpxZVP2g%3D%3D.u1DRQD0cFAyRAUCBBJJ8eMTsa1%2B4r5wMsP%2BAThpodSt9u4BunUpBojv8h7v44UIR" rel="nofollow">RegEx101</a>:在线的 JavaScript 正则表达式测试器和调试器。同时也支持 Python、PHP 和 PCRE。</li><li><a href="https://link.segmentfault.com/?enc=DJ7WKN0oRPPI0V6Xl9a0nQ%3D%3D.MbMLRPh9EWXftE2K6Ap5%2Bd2WWyE%2F78dA3If9g3o0UcE%3D" rel="nofollow">RegExr</a>:用于创建、测试和学习正则表达式的 HTML/JS 工具。</li></ul><h3>语音命令</h3><ul><li><a href="https://link.segmentfault.com/?enc=AzowarA5xi6k9IRuV8zQhg%3D%3D.DzS3%2FewyqRzZzPPvB783gPY8hnku%2B2Ro5KdnGmWwTIww%2BSHnnrrBzVxx0THR0Kfp" rel="nofollow">annyang</a>:向网站添加语音命令的语音识别库。</li><li><a href="https://link.segmentfault.com/?enc=bbwsyL%2B3yj1UG%2B4LkCKHIA%3D%3D.OKxO9LIxx5f2zDrshnh0WQy8IVU2FW%2F7HLwAXbwyRAE8%2FW7gTLKyH%2FKnK%2FMgIxkC" rel="nofollow">voix.js</a>:向网站、app 或游戏添加语音命令的 JavaScript 库。</li></ul><h3>API</h3><ul><li><a href="https://link.segmentfault.com/?enc=XE9%2FZPLl92UdWyEtCS1KRg%3D%3D.1jkGV7Jp3daXg7nNSzoUMEW5NCfgU3AR0A6gkXWEB6I%3D" rel="nofollow">axios</a>:基于 Promise 的 HTTP 客户端,适用于 Node.js 和 浏览器。</li><li><a href="https://link.segmentfault.com/?enc=DxWPiDx5pNa65EjnOp2Slg%3D%3D.lijDdtXeTIiU%2Faz9dFkRftwJKTv77xrMagzrWIOoJ%2FXra8QymVUKMFrytev5DMOs" rel="nofollow">bottleneck</a>:强大的速度限制器,使调节流量变得更容易。</li><li><a href="https://link.segmentfault.com/?enc=slc3SxqJaEpp3o7Xiy6piA%3D%3D.ga4l3%2ByXloTd1u%2BlXc6fWIYEp3WL%2F0UvwCBBc%2FNT%2BZ8mHsBtjy3f9SZwYrRwg99Z" rel="nofollow">oauth-signature-js</a>:适用于 node 和 浏览器的 OAuth 1.0a 签名生成器。</li><li><a href="https://link.segmentfault.com/?enc=uG4BnjnlI95WfgO%2Fje1Q5A%3D%3D.ewjtGEx3JxRRoYrc9eU6TdoQlK8Tvck3oPSrI%2F2HhVLaMbE7PUeX6VE91KzbZ3g4" rel="nofollow">amygdala</a>:为 Web 应用提供 RESTful HTTP 客户端解决方案。</li><li><a href="https://link.segmentfault.com/?enc=3jyRhtN58uEWNwNhSn%2Bgpg%3D%3D.EJQBJOpzb9KNIjOXnWmdE3TSfZaAWBWAgQoiX2g4YyXOTWRrdDuEzf0Kpa3p%2FIjk" rel="nofollow">jquery.rest</a>:一个让 RESTful API 更易用的 jQuery 插件。</li><li><a href="https://link.segmentfault.com/?enc=okMi0TUAqn%2BWBFT8Y52a%2Fw%3D%3D.G71t3o6A9f3zembhXavWCRaWcnw%2B4hCBbHg0iJ%2Bb%2FJdLS3yLrcJlFw8KVmDt8IBl" rel="nofollow">Rails Ranger</a>:为 Ruby Tails 接口设计的严格的 REST 客户端。</li><li><a href="https://link.segmentfault.com/?enc=NF4HZNVL4Z1d9Eij48VNIw%3D%3D.vUEGZE91TSVo9suqTSuD38D%2B69iumx3cTOYtM35%2Fp79GMXrinR0V5EL%2BA2Qe2MRu" rel="nofollow">wretch</a>:一款小巧的直觉语法系 fetch 功能封装包。</li><li><a href="https://link.segmentfault.com/?enc=FGdULuLMDr4cjgMVVQuWVA%3D%3D.CmY7vZZ6DX6VrFx0eff%2BukJDOwOhJv1mp5tfR3o6RAYqK3%2Bm%2F8l5PUmJMMI%2BhuvO" rel="nofollow">FarFetch</a>:简单易用的现代 Fetch 接口封装,简化了文件上传。</li><li><a href="https://link.segmentfault.com/?enc=cZKzWCbHWxi493HKh1Gghw%3D%3D.tCivanrB9fCKvWXi0cgJxRCVsf3Lh6xbXLPrT7WqnqgKuRGtDmRK03eufLrtWFdA" rel="nofollow">Optic</a>:Optic 用于对 API 自动测试和文档生成。</li><li><a href="https://link.segmentfault.com/?enc=F8yPz3UcpILFaHhWz3nclg%3D%3D.gtNNfqZgVqGdBR4VoXsouGwUh6Y0C2JJ%2F18gfjOqJQs%3D" rel="nofollow">SWR</a>:用于远端数据拉取的 React Hooks 库。</li></ul><h3>流媒体</h3><ul><li><a href="https://link.segmentfault.com/?enc=Z8eX6uLqp52cW5mL9Yv6Xw%3D%3D.Nd1IP0gLd3f52xTrlqj5eMQeAhUeq6h2tiwUTQdnxPEpbJY0UutNe9ztBF8IQ47L" rel="nofollow">Tailor</a>:适用于前端微服务的流媒体布局服务,灵感来自 Facebook BigPipe。</li></ul><h3>视觉检测</h3><ul><li><a href="https://link.segmentfault.com/?enc=MKVymiGvpuHg9cuMDInvjA%3D%3D.cbEMH12%2BUzAxr0lpNU6qV82EdBG%2B6BUDo7facSCHAbapqn%2FHmURh3iUyQSqza53B" rel="nofollow">tracking.js</a>:在 web 上实现计算视觉的一种现代方法。</li><li><a href="https://link.segmentfault.com/?enc=FFrqjEkZKLGqOWzuP17X8Q%3D%3D.kZBLiOTWpGrrSDLB0YoA3zu9tiJwQ4tVSJ6FvgjbyObERwMHLxkLAVuUFBHN%2FLSg" rel="nofollow">ocrad.js</a>:基于 Emscripten 的 JavaScript OCR 实现。</li></ul><h3>机器学习</h3><ul><li><a href="https://link.segmentfault.com/?enc=cyZnebDEpEie6QgyaSPHTg%3D%3D.F91nnaRgS8EZ1R8Hi1k1nhC6HKvoWU9rRc%2Fl5q9q2%2Fa%2BsBgDocO22PkXWvdGJyW5" rel="nofollow">ConvNetJS</a>:JavaScript 深度学习。在浏览器环境训练卷积神经网络(或者普通神经网络)。</li><li><a href="https://link.segmentfault.com/?enc=YrBc5AvazLsZYJcjKLJFgA%3D%3D.12pFX63fFlXVP1pskFWCcklV8jCF6TKctVt90uVEb%2FHo23fMqUTaFJbKWDKBXQbT" rel="nofollow">DN2A</a>:数字神经网络架构。</li><li><a href="https://link.segmentfault.com/?enc=D07bwHtD8zU7IE84fUooeg%3D%3D.4JZo4Ar50UgRFX3dqpyW6LJE7b7wSO3d%2B8mjmouVQnHRz3XVkLg1%2BC9bypMoStEG" rel="nofollow">Brain.js</a>:JavaScript 神经网络。</li><li><a href="https://link.segmentfault.com/?enc=dbfkmj7Ei8%2F%2F6ezTtHuMNg%3D%3D.xMAOkl38Yl6xm%2BiARErrRV9cra6mof9Db6IMOcZaETvzbDfVTJ%2FwGJ9I1aOhNJg3" rel="nofollow">Mind.js</a>:一款灵活的神经网络库。</li><li><a href="https://link.segmentfault.com/?enc=C4BdHl5f7i0Sb33mvuu8QQ%3D%3D.ZVM9ZHaynb3QlqN94a%2BjbXMS143RUGDXAdkXsCiRrY%2B3xGg2JhBZLG50ET6UxgP3" rel="nofollow">Synaptic.js</a>:适用于 Node.js 和浏览器的无架构神经网络库。</li><li><a href="https://link.segmentfault.com/?enc=aeOYfFWCt%2BqoPVX1BllGCQ%3D%3D.%2FAbDZ3uour446Xad3h8X8mcJeStxRb2oeDcARnsgRhQ%3D" rel="nofollow">TensorFlow.js</a>:一款用于在 浏览器和 Node.js 中训练和部署 ML 模型的 JavaScript 库。</li><li><a href="https://link.segmentfault.com/?enc=XV5YySez9lHu7DY%2BAeOXOA%3D%3D.JsUkt2Sy6Ew9JdmwKA5vH45jnEFatRAl2A1YK2DRm2c%3D" rel="nofollow">ml5.js</a>:友好的 Web 端机器学习库。</li><li><a href="https://link.segmentfault.com/?enc=0Ys1QTk%2BKeAZoGQWDrzUkg%3D%3D.JDdKH3p7MktE0xR7P8YvGZIZug0yXolF8sqyUgi%2FdhprGGLy3k8LQRhlCznDdhWe" rel="nofollow">Synapses</a>:轻量级跨平台神经网络库。</li></ul><h3>浏览器检测</h3><ul><li><a href="https://link.segmentfault.com/?enc=tVvQz5nejo8hKK%2BKDbqE7w%3D%3D.IaCy0vqZ12D%2B4DtA8kW9sKmO0FGAbBfeYcbuh6EmNQc%3D" rel="nofollow">bowser</a>:一个浏览器检测器,特点是小巧快速且 API 丰富。</li></ul><h3>基准测试</h3><ul><li><a href="https://link.segmentfault.com/?enc=QKF%2Fj%2FsGmp3%2BX9HRDyuTPw%3D%3D.vZ4cVwnIvyvbzOW10HASBkZiye7a6xvGW%2Bu%2FWf01l8TjVoRHI8P6M%2FC2jywDCzZC" rel="nofollow">benchmark.js</a>:jsPerf.com 使用的基准测试库。</li><li><a href="https://link.segmentfault.com/?enc=cUv6QowDnoBQr6qrgisAoQ%3D%3D.0XTqgBKSgp7EoK4N81WsoRMFcejK885%2F1xg9BKRXwuh0OioHGHPH3FR5Xq74oRYK" rel="nofollow">matcha</a>:一款咖啡因驱动的基准测试简单实现。</li></ul><h3>动画</h3><ul><li><a href="https://link.segmentfault.com/?enc=VDITDEyevpphZWCp1UJt9w%3D%3D.5woxHwXZYxUHFrzcXQ2WywUuGu9939t%2BZYa3SPq9iNs%2BeONzQfSmoxCZAxlKO%2BAH" rel="nofollow">velocity</a>:加速 JavaScript 动画。</li><li><a href="https://link.segmentfault.com/?enc=j8PHAQCbPWFjtFMw81E50A%3D%3D.LCcFHYRUlDft8WML2PqTpUG3m%2FJEmmNhZ3XN9E4MT8PApfaEESfJOiGwZO3w46wz" rel="nofollow">jquery.transit</a>:拥有超级流畅的 CSS3 变换和过渡效果的 jQuery 插件。</li><li><a href="https://link.segmentfault.com/?enc=GYJCo23pknl7wvkTNjz2Pw%3D%3D.WhpoxgUosvDv3oKeAnF%2FVJIGo007O6Ded%2BQnJ9nhrJWqHpGX5dcAQRdKCz1sMepp" rel="nofollow">impess.js</a>:在 HTML 文档里,运用 CSS3 变换和过渡制作类似 Prezi 的展现效果。</li><li><a href="https://link.segmentfault.com/?enc=GSj8lptHaif5aerJeashGQ%3D%3D.Ho8eRK2PyH5kWTWpHDCbgD1%2BRK6BCzZVxT56wIEtXZWYIS2p2fYvV9O9TZ98nZTb" rel="nofollow">bounce.js</a>:可以立刻创建有趣的 CSS3 动画。</li><li><a href="https://link.segmentfault.com/?enc=HAEvIXEPpRhoMWyrPfKf4w%3D%3D.Z6LLUhFzecBBwyG4nsd1Ds7TiWuNoFS6XzQPLQTtanvqFWWiGQCpjkVUwRG3CB41" rel="nofollow">GreenSock-JS</a>:适用于所有主流浏览器的高性能 HTML5 动画。</li><li><a href="https://link.segmentfault.com/?enc=oRqBwtkF04XqxFWWlrdGQA%3D%3D.rzvO08W6kBDjc7mB76qomJ94WxfvU8yNN3hlXHdqOEQlJuv2Ewr%2FdGWff8yl6ld7" rel="nofollow">TransitionEnd</a>:TransitionEnd 是一个运用 transitonend 事件的、跨浏览器的库。</li><li><a href="https://link.segmentfault.com/?enc=a8mCKAgtIxoQqa9Fd1kWug%3D%3D.f3GNLoYfBrC2gi7fQCvPInjwfsFSg68sQYjF4rPvYQWnVgRZD6TP2d0r%2BUl94ynB" rel="nofollow">Dynamics.js</a>:用于创建符合物理运动规律的 CSS 动画库。</li><li><a href="https://link.segmentfault.com/?enc=U8LUkPym0tTjPpFveCqlAA%3D%3D.gh7wcIyN4ohYJds1ObGYpsRhRDGl7h1TxzavFM6XqqzBdf6cwkIIZVK51qQJTCYN" rel="nofollow">the-cube</a>:The Cube 是一个 CSS3 过渡效果实验。</li><li><a href="https://link.segmentfault.com/?enc=%2BSxH3wLUi97LkAsate6kUA%3D%3D.S9j96iToZCknhBbEkhaS9bguIiahivGMStklvAH6%2BtpBXCsKGoyfVs5OtbkQtH11" rel="nofollow">Effeckt.css</a>:一款高性能过渡动画库。</li><li><a href="https://link.segmentfault.com/?enc=jYu0AORL4OekzqF2yxB5YA%3D%3D.dXWVInOx1Jz2HZHFwnmpQSUsB52W%2Bd8QHL5f2CxMIHEE%2FPXs9AIplsWZNhZIcjn1" rel="nofollow">animate.css</a>:要多易用有多易用的跨浏览器 CSS 动画库。</li><li><a href="https://link.segmentfault.com/?enc=s5onS794WZ6sYxKfEYgtmA%3D%3D.9PXR%2FblA5G3GJk3adTbdOznEhIzIMHp75xcuEhfP7HIkDq%2B71BX8VuVzfyB54dDe" rel="nofollow">textillate</a>:适用于 CSS3 文本动画的简单插件。</li><li><a href="https://link.segmentfault.com/?enc=Yy2PdsP4qs65bUHi0xIEpw%3D%3D.Y73OD89Lp%2FVCufu%2BcpPs6LzsSpHC9rON8HwXnujwagJDUxyviwmayzoEbVr2myjx" rel="nofollow">move.js</a>:基于 CSS3 的 JavaScript 动画框架。</li><li><a href="https://link.segmentfault.com/?enc=GCq5TAqIjFaDDYEQEpbFEQ%3D%3D.TEFfotylqHQOG1UdBVS8SzNN%2BQ1OfraPyR2FxBT6nS6NNQnACCkLi5Epurg2Razx" rel="nofollow">animatable</a>:一个属性,两个值,无穷个可能性。</li><li><a href="https://link.segmentfault.com/?enc=apj98Z1kQvxOSCX4ZkR0sA%3D%3D.aGT0efsuB8eGwKvLXnQJgFsD46OwCOrvRic5e7Md60lQ3DTlaKw%2BmgL5XVmtaF65" rel="nofollow">shuffle-images</a>:简单有创意地打乱图片。<a href="https://link.segmentfault.com/?enc=8nzT6eASe1SFXgPjE1yByg%3D%3D.gBfHoXySu5Dg8R3qd7Bo9FKEmwe6w2M65bDDWbRLU3I%2FUE7%2BMhBwPxqkO%2BQliC6FkHERfjmA98TfImrSLZyKmw%3D%3D" rel="nofollow">http://www.thepetedesign.com/demos/shuffle-images_demo.html</a></li><li><a href="https://link.segmentfault.com/?enc=k%2FrRoOxicAfZlq5MouMKaA%3D%3D.yXcKCxZ1Kk6%2FZOv%2BX8xN%2BjRYT%2FuiyVTyREEBM%2BwB2YbmqpMhhlq2ILs9eBADXQrV" rel="nofollow">smoothState.js</a>:免打扰式页面过渡 jQuery 库。<a href="https://link.segmentfault.com/?enc=eT7FV7jMDsdoS30KTBzIaw%3D%3D.rA5qpBc7h6aC0eR0s%2BwPdSYI958pnEBIcybAbLt13Fw%3D" rel="nofollow">http://smoothstate.com/</a></li><li><a href="https://link.segmentfault.com/?enc=wWwiQN8ewzYDrrdpVHeHcA%3D%3D.kHpl1zvlx3%2BuTA2ANsU7iUtb%2FuRwb8sDgLM%2FKy%2F9oAo%3D" rel="nofollow">Anime.js</a>:一款 JavaScript 动画引擎。<a href="https://link.segmentfault.com/?enc=0o8X7ba5MI9aKnB4vrbKDQ%3D%3D.LDtIFogN0yRpwLGK%2FZLRcZdUZnehlZVvLnXUASo9sg0%3D" rel="nofollow">http://animejs.com</a></li><li><a href="https://link.segmentfault.com/?enc=scjDN6ZrfP43fwQFfiudBA%3D%3D.HVP19hrhsccnUTI4JQNvG5yKcV7zic3apin5NqJ0tzBZYxTtmcNfKI9OoFf8HcKf" rel="nofollow">particles.js</a>:用于创建粒子的轻量 JavaScript 库。</li><li><a href="https://link.segmentfault.com/?enc=Ck3XhooJZBIqqwvgNnVVVg%3D%3D.K1MaSJqJMfrNxrChq4rp7oONBoGYjvuUhrKyoVY3xVvfFosKPUMakm8Is1NT7ILW" rel="nofollow">tsParticles</a>:particles.js 全新升级版本,修复了 bug ,增加许多新功能。</li><li><a href="https://link.segmentfault.com/?enc=H0a0n1ni3gY1Rv48kidhNg%3D%3D.S5ZsXFkGeq4%2FYZ5q%2FeWvRFVCDnE0g33ooZ5A9L0ZEGJFv4IO2Xh9PqKugJo5ZfUD" rel="nofollow">particles-bg</a>:一款轻量的粒子运动动画背景 React 组件。</li></ul><h3>图片处理</h3><ul><li><a href="https://link.segmentfault.com/?enc=DGMq2m2gCMxVgnEHeS9kJA%3D%3D.Jd4zq%2BLyX%2FUD8e24az7Y9rK%2BQ3Y2JCL01Lieyk61ZGhtOxOcVqhNvurMowjw6cvm" rel="nofollow">lena.js</a>:具有滤镜和实用功能的图像处理库。</li><li><a href="https://link.segmentfault.com/?enc=big7lQZcQM2P62Du4TdT6A%3D%3D.7SHMrbnbWj3vLjmN%2FKOKuyrxHV%2FL6USgbEqXkXUhHhA%3D" rel="nofollow">pica</a>:高质量地调整图片大小(使用快速、纯 JS 实现的 Lanczos 滤镜算法)。</li><li><a href="https://link.segmentfault.com/?enc=gFn0Lo90i6mHHvERlpghag%3D%3D.seGamVbXlARyu9fSme8EYvGWzzzZp%2B%2BM%2FAc5%2BkLZ7srAuUOo16bG2ujUkQiCypWT" rel="nofollow">cropper</a>:一个简单的图像裁剪 jQuery 插件。</li></ul><h2>最后</h2><h3>历史文章</h3><ul><li><a href="https://link.segmentfault.com/?enc=SKwhFSoSsD2TgwtFxlJIDA%3D%3D.FqSETZg5bBUZc58dgcYl2fQ7qu9WMj8bw10wpcNQaw06aDb9w6G%2Fqa0H805Ukasb" rel="nofollow">必须知道的JavaScript库 - 可视化库</a></li></ul>
必须知道的JavaScript库 - 可视化库
https://segmentfault.com/a/1190000041390654
2022-02-11T15:18:44+08:00
2022-02-11T15:18:44+08:00
wlove
https://segmentfault.com/u/dxgl
8
<h2>Data Visualization 数据可视化工具推荐</h2><p>在研发团队内从0打造一个公司级可视化库/工具可行性非常低(无论是成本还是是否具备能力等等...),综上种种,今天推荐一些web方向现有的工具(包含实现概要,技术方向)来供大家参考使用。</p><blockquote>以下所有链接均为源代码仓库 。</blockquote><p><strong>点赞 收藏 再也不要担心有数据可视化找不到库的烦恼了</strong>。</p><ul><li><a href="https://link.segmentfault.com/?enc=ehoDSDSWDwVKQdMbjCB9uA%3D%3D.YhB6NHVi5hpb0zq%2BZF7jlJK%2FowUfQcTOQY7B1xnZH%2BU%3D" rel="nofollow">d3</a> - 用于HTML和SVG的JavaScript可视化库.</li></ul><p><img src="/img/remote/1460000041390656" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=iANIrmS8o%2B7mB3qkwCVMxw%3D%3D.FTUxdqJEkFdlCR5M%2BC0XnLNEeXr7D8qdyAimiQh1qQYS%2B8dV6wRUF4xqVE7ZTub%2B" rel="nofollow">metrics-graphics</a> - 是一个构建在D3之上的库,针对时间序列数据的可视化和布局进行了优化。<br><img src="/img/remote/1460000041390657" alt="image.png" title="image.png"></li><li><a href="https://link.segmentfault.com/?enc=ADNXZh3tgxL2BUnIUG%2Fg5Q%3D%3D.sFsI02Fn%2F9A23uTFkB7l153BUKglsc0%2BPLHTUBZ2951bviXWV2QduxPjiQhqB4Fp" rel="nofollow">three.js</a> - JavaScript 3D库.<br><img src="/img/remote/1460000041390658" alt="image.png" title="image.png"></li><li><a href="https://link.segmentfault.com/?enc=HsmvdyltxT5jcOOkr7RvfA%3D%3D.u%2FpYXaA0UOVxjxEs2Acla5xQTLR84YAuotdkA47sOJjJ8dlkg6bYbr2MzS3mWcjK" rel="nofollow">Chart.js</a> - 使用Canvas的简单HTML5图表<br><img src="/img/remote/1460000041390659" alt="image.png" title="image.png"></li><li><a href="https://link.segmentfault.com/?enc=R85DsyE1LM5RstZg0gXNaw%3D%3D.%2FIOINccb4EX%2Fs%2B9iqxx6mwAZvH8cTGEnlr6YO%2Bzp6PPMyQq8QQFU1Cqu0sUcX06r" rel="nofollow">paper.js</a> - 矢量图形– Scriptographer使用HTML5画布移植到JavaScript和浏览器<br><img src="/img/remote/1460000041390660" alt="image.png" title="image.png"></li><li><a href="https://link.segmentfault.com/?enc=diO1QYUcS28PH4pIVu2X5A%3D%3D.jjWytLD1MBjBE004V1TKBhhoMuRWxN982a0V9QFWaQw0eccT0d%2FGHqCpBvOFS67e" rel="nofollow">fabric.js</a> - JavaScript Canvas库 同时提供SVG到Cnanvas(Canvas到SVG)解析器.<br><img src="/img/remote/1460000041390661" alt="image.png" title="image.png"></li></ul><ul><li><a href="https://link.segmentfault.com/?enc=EFaYisZJr%2FsiDO98m5Dvuw%3D%3D.3SrAGUPQKEHWXn6fFqD2Bdbgn13ityKZq0XUQ2IlMbK4u3FPlI5EvGxFmZnZIodo" rel="nofollow">raphael</a> - JavaScript 矢量图形库.</li></ul><p><img src="/img/remote/1460000041390662" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=mIFphZdw%2BKwIqjvYE2%2FA7w%3D%3D.NZc8GIPOm6BKy7TW%2FKbVrfVyLtkcxptmg6qDMntC5AwaobfTnVF18sHVFGm4rexo" rel="nofollow">echarts</a> - javascript 提供丰富的图表以及可视化库.</li></ul><p><img src="/img/remote/1460000041390663" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=bGdRV%2BUbrysprKOamjKRIg%3D%3D.KT2wCuY%2FSEaXIWczzDiNtq58xfFrtWlvn4hX001gAIKONfXpz0Uf6KIadR4hZqbb" rel="nofollow">sigma.js</a> -<code> 专用于图形绘制的JavaScript库 Version1:canvas+svg Version2 : Webgl+canvas</code></li></ul><p><img src="/img/remote/1460000041390664" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=xy%2BqnUOK3v3NGj8LNN1TYg%3D%3D.C%2FYdQ2aaJ5JUQFtcxUG6m7z%2BLYSEbRvCwK3jomljsNA%3D" rel="nofollow">visjs</a> - 多个库用于动态、基于浏览器的数据可视化。(下面截图为network)</li></ul><p><img src="/img/remote/1460000041390665" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=Cnm2P3HO9HeMfBrpiowk8g%3D%3D.3iygA3IkBpq9JpIJwLDbLDtYkP%2BV8rOL0%2BNpdJLmWRI7PnV7pih8phcyRpTSa4ve" rel="nofollow">two.js</a> - 一个与渲染器无关的web二维绘图api。</li></ul><p><img src="/img/remote/1460000041390666" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=%2BdllNhjuNt3pz9Hc5xf4wg%3D%3D.aatFmPde39vbbhPemVNvTFLX7LuWl12PyD2lwLD86fI%3D" rel="nofollow">dc.js</a> - 多维图表是为使用d3渲染的交叉过滤器进行本地工作而构建的js</li></ul><p><img src="/img/remote/1460000041390667" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=NCRCgzC0wlRzCWXHufFSTQ%3D%3D.%2BUsEu4nxEkSoG0d8wWDb6321E8UPrCSe6VplIOUZ7%2Fw%3D" rel="nofollow">flot</a> - 基于jQuery的JavaScript图表.</li></ul><p><img src="/img/remote/1460000041390668" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=Ta4zErk7eeQPRcKSuNd%2F5A%3D%3D.2pG0pjhUUQi%2Blo%2BYeuhxUUFK6XngoE2Xx2AmBbfHQ4Q%3D" rel="nofollow">nvd3</a> - 为d3构建可重用的图表和图表组件js.</li></ul><p><img src="/img/remote/1460000041390669" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=QKVQDYIMsPY0qxU0Exavsg%3D%3D.zC7%2F%2B7jh%2BFTypE0%2BaXbwXZHGs8nsx9jm7qebEaoMsRI%3D" rel="nofollow">svg.js</a> - 用于操纵SVG并为其设置动画的轻量级库。</li></ul><p><img src="/img/remote/1460000041390670" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=Fi%2Fmh6J6klnC7HxkLBXZeQ%3D%3D.rGRpoj8gYiB1LFiRon%2Flizd5HLBIs%2FA1dk5PTofHX6Y%3D" rel="nofollow">dimple.js</a> - d3支持的简单商业分析图表.</li></ul><p><img src="/img/remote/1460000041390671" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=VkO7nSzP%2FZObv6%2FEHLQO2w%3D%3D.LazNpDLMF%2F0U%2FUpvbSkz%2FTS%2BEeqBnRiRC2cSt0hmf94%2FYhznlieC0qd%2FyVLouZyW" rel="nofollow">chartist-js</a> - 简单的响应图表。</li></ul><p><img src="/img/remote/1460000041390672" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=%2FGxXazGjQoyzr%2F7IA4%2FyhA%3D%3D.K%2FSvilCTmHDUBxPXrHbleViwSiHxpWWjWkwc76ZKvBWtTnv2Ez5L%2BPGvzdF30Q37" rel="nofollow">epoch</a> - 通用实时图表库.</li></ul><p><img src="/img/remote/1460000041390673" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=GEBryy6AFs%2B%2FAWGajz2L3w%3D%3D.Z1v%2FYPXPjgL%2BWi7we0SJeOwwwxawgt0Vw36mVXf2cmo%3D" rel="nofollow">c3</a> - 基于D3的可重用图表库。</li></ul><p><img src="/img/remote/1460000041390674" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=F6Lk0F74lE%2FfdrNAK8vnFw%3D%3D.HHuMHcmZ5Zc8%2FkFTZs211De0GoI9BsusCXfCzZSaGDibfnp9SZItjTZ4Qo3VZtau" rel="nofollow">BabylonJS</a> - 一个用HTML5和WebGL构建3D游戏的框架。</li></ul><p><img src="/img/remote/1460000041390675" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=UBAsVWZW8uwuB3E%2Fxj2xmw%3D%3D.hls30Dc%2FxRRToBVTO09K5xU3r0s9K5XlL8WZhRj7qkWX4XV33yA9TqDYbC3gp264" rel="nofollow">recharts</a> - 使用React和D3重新定义图表库。</li></ul><p><img src="/img/remote/1460000041390676" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=uAuwPxi%2FfIE8VM41Y7w8cg%3D%3D.c9T56Xh9Tq6P%2BtvNbwrLPEtHzRUG6hfJ%2FdnFeuTtOZM35uPRTAm8tiWATfhLk7Fq" rel="nofollow">GraphicsJS</a> - 一个基于SVG/VML技术的轻量级JavaScript图形库,具有直观的API。</li></ul><p><img src="/img/remote/1460000041390677" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=p%2BG9eG8Ba0gWxDAe8%2FXdVg%3D%3D.58tqKzD4VR3wUaaqDJAH3jvgEUsZ7sUi5gTjiSwqiGE%3D" rel="nofollow">G2</a> - 是一套基于图形语法理论的可视化底层引擎,面向常规统计类的图表,可视化图形语法。</li></ul><p><img src="/img/remote/1460000041390678" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=blNqO11uzDAPw25rK5PZfw%3D%3D.BqRUIUg%2FlgXzOdMrFfDUUhEfyTz7Ut7TkPxJworH%2BhDUJ%2FtJ6Q1V0t4Ngc4gqfLp" rel="nofollow">G2Plot</a> - 一套简单、易用、并具备一定扩展能力和组合能力的统计图表库</li></ul><p><img src="/img/remote/1460000041390679" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=3e7EUkhPKjvXwckMZnjNDg%3D%3D.KoII7ZK1FUg35nbpntwFgRLFttjRABfG3UcBxpIVGWFOCl0nqQaM709vQLBym7yS" rel="nofollow">Cytoscape.js</a> -一个功能齐全的图论库.</li></ul><p><img src="/img/remote/1460000041390680" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=lpamwPDo%2F6sFbFZA6RLngg%3D%3D.UaDSAIFz24LKCn1Nsia7LF0Pl8cHP3vHJK4DkdJFvylg6wks4V%2BM7e4jtuu1YISU" rel="nofollow">cola.js</a> - library for arranging your HTML5 documents and diagrams using constraint-based optimization techniques</li></ul><p><img src="/img/remote/1460000041390681" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=8XGeTQWanYppRv0Dw%2BqlMg%3D%3D.UaQ9vUZYGamD3VnXm1MhQjuOemz2reQJlQHxdMYSg3f54bzgdFgsVAg5AVnlTziB" rel="nofollow">jointjs</a> - 图表库,用于创建静态图表或完全交互式的图表工具。(包含免费以及/付费版本)</li></ul><p><img src="/img/remote/1460000041390682" alt="image.png" title="image.png"></p><ul><li><a href="https://link.segmentfault.com/?enc=XmvmGh8y4875UOyfJiC9HQ%3D%3D.JuTFajxW8CtNxs9yxSNe9oLDqQF4ALWFmtUwrhBfQPm70yfjePiZv8OKIzJl%2FYYJ" rel="nofollow">vizzu</a> - 用于动画数据可视化和数据故事的库。</li></ul><p><img src="/img/remote/1460000041390683" alt="image.png" title="image.png"></p><p>------------------------------------网络问题未截图部分--------------------------------------------</p><ul><li><a href="https://link.segmentfault.com/?enc=3l8ck29TTa%2BZU2Jja%2FN2%2Fg%3D%3D.jMu0OJfZoV2LWdMK1uE2ANheE6njvrYE1edk5A0y62fRse847QLlw%2B%2BhWlBRUzD%2F" rel="nofollow">mxGraph</a> - Diagramming library that enables interactive graph and charting applications to be quickly created that run natively in any major browser that is supported by its vendor.</li><li><a href="https://link.segmentfault.com/?enc=gUbIoNiBm267pG9JLPfd0g%3D%3D.v20V0sSbqeSkBktRGx4AMwOCmfyQQPfnw9%2FkCzr2iLQVULyJjzRl%2FCwdE3uL6KHL" rel="nofollow">Frappe Charts</a> - GitHub-inspired simple and modern SVG charts for the web with zero dependencies.</li><li><a href="https://link.segmentfault.com/?enc=W1DVsq%2F89p2gKRlLfxQXmQ%3D%3D.dM7Mvfaa9Ec8pimU9OQRnkjpNf9aLPoJjTZXubQFZ1w%3D" rel="nofollow">Frappe Gantt</a> - A simple, interactive, modern gantt chart library for the web.</li><li><a href="https://link.segmentfault.com/?enc=ff53o9VAPsDyaJCbkw1lPA%3D%3D.gfwLXtq9FZpvT9UzgGQlPWp8ZISKLrxyqQgWnbVQyfnVCfzx2SomaWsMRXLckpBf" rel="nofollow">heatmap.js</a> - 基于HTML5画布的热图JavaScript库。</li><li><a href="https://link.segmentfault.com/?enc=hQCsCVgpeyDWe9sxjDa4WQ%3D%3D.Ic%2FOdzcjir7XNo4KTV1yu3y5carcKsXbVD91yOPUsHsPGRtsC%2FdT7peKzrb%2BrcPF" rel="nofollow">jquery.sparkline</a> - jQuery JavaScript库的一个插件,用于直接在浏览器中生成小的迷你图表.</li><li><a href="https://link.segmentfault.com/?enc=7U7MXv9MdhuXsfZbxKlIgg%3D%3D.G7RwlSOQLSbrfLY2GDWXsL0zl4Hy7do5uMhWd%2FDgmvH2Ea64dJ8GNk56RlufAttu" rel="nofollow">d3-cloud</a> - 在JavaScript中创建单词云.</li></ul><h2>其他一些付费产品</h2><p><a href="https://link.segmentfault.com/?enc=dviShdZ0rMYU8Pz%2FX2YXJg%3D%3D.kJGi74lBtpAzgAfebiiPiTVQj%2BCXU%2Bgkj%2FI3x4fSfy8%3D" rel="nofollow">amchart</a>, <a href="https://link.segmentfault.com/?enc=1ks8xQDr%2FqMqx8DFQ2pKvA%3D%3D.ARQxLDnoTXER7p7uP3Ih%2BNufC8QMRzjJ%2ByairRnLWuQ%3D" rel="nofollow">anychart</a>, <a href="https://link.segmentfault.com/?enc=sIO6EPMlMrNKS%2B6JIuSj6w%3D%3D.1YydJivcUWbrz7Pi3c9Lt3T%2Flt2csji2bIMHU28GwhQ%3D" rel="nofollow">plotly</a>, <a href="https://link.segmentfault.com/?enc=5sQnorgvsbaHUMRcHKxy%2Bg%3D%3D.uTdH3ov2Eg2PmMRwrtNscfZf5vXaLIy9fdj9eTFgEpI%3D" rel="nofollow">highchart</a>, <a href="https://link.segmentfault.com/?enc=CM3p6JcT%2BO6E0c7Tu1t8CQ%3D%3D.axwgwVxrDqKvdzQ9ZLXe5IjSKj3grZEPaU%2B9lfirCkDFa7mmIn7PBu%2FGMKp%2FZ2%2BC" rel="nofollow">lightning chart</a></p>
回顾下渲染引擎-G一年的历程
https://segmentfault.com/a/1190000041338111
2022-01-25T14:44:37+08:00
2022-01-25T14:44:37+08:00
wlove
https://segmentfault.com/u/dxgl
2
<h2>前言</h2><p><strong>对于社区来说(关注我的人,还有将要关注我的人)有俩件事提前说一下下:</strong></p><ol><li>建立的所有专栏我都会去维系。但是写作周期就不做承诺了。确实懒..也不打算靠写文能够给我带来一些收益。(嗯,我对钱没有兴趣)</li><li><p>伴随春节的到来,也应该对今年主导推进的技术架构设计,落地,未来计划做一个回顾。涉及到内部隐私,这里将部分内容分享出来。</p></li></ol><h3>简介</h3><p>2021年个人技术方向算是横向发展,有一定收获。本文贴合专栏对于其中一个方向:渲染引擎<code>(FastV)</code>的回顾。暂不开源。</p><blockquote>Fast是快速的意思,也是渲染引擎设计初衷(大数据下高性能体验);V visual(data visual)可视化;FastV 快速数据可视化,打造一个高性能渲染引擎。<del>至于功能方面可参考Antv的G2 G6,但是设计初衷不一样,彼此优势不一样。</del><br>下面主要介绍其中的一个基础渲染引擎研发的渲染库FastV-G</blockquote><h3>架构介绍</h3><h4>背景</h4><p>图可视化从几个方面来考虑:布局(algorithms-layout) 渲染(render) 交互(behaviors) 。</p><p><img src="/img/remote/1460000041338113" alt="image.png" title="image.png"></p><h5>请看上面的部分架构截图 (没有体验完整的设计)</h5><ul><li><p>右边(蓝色)说明引擎的整体思路:</p><blockquote>controller作为总体控制器 下面分为render(渲染) events(事件/交互) algorithms(算法/逻辑)</blockquote></li><li>左边为核心模块,举几个例子说明我们的优势。</li></ul><blockquote>1.behaviors 高性能交互。其实交互的性能体现是离不开渲染和算法的。其实优化也是定位,分解,各个击破。比如渲染,我针对交互式操作做了切分。拿BS架构下的渲染来说。</blockquote><p><img src="/img/remote/1460000041338114" alt="image.png" title="image.png"><br><strong><del>图片示意 切分可能比上图更复杂,主要是思路。</del></strong></p><p>将所有基础图元(shapes)使用WebGL渲染(硬件加速),label利用canvas-2d进行渲染。然后将交互部分做切分 每次update只做交互部分的update,它怎么能够不快。(当然这里的交互可能存在全部内容的更新,也需要对于交互进行分类 分不同的case)</p><blockquote>2.algorithms</blockquote><p>Graph 算法之图算法,图的数据结构重新设计,利用逻辑结构的优势。例如数组查询快。将矩阵(数组)作为图结构(可以学一下C++的一个库ligra,有兴趣后续可以分享一下) 图相关路径算法,社区算法作出非常大的提升。快就完事了。</p><p>layout 算法之布局算法,例如ForceDirectLyaout(力引导布局),点击交互 所需要的2D碰撞算法 底层数据结构均采取quadTree结构进行比对数据集减少。</p><blockquote><ol start="3"><li>...太多了</li></ol></blockquote><p>每个细节点交流都是精华。不能说在座的都不是对手, 但是基本信心在性能方面kill大部分的库还是有信心的。</p><h3>技术介绍</h3><h4>BS 技术选型说明</h4><ul><li>Webpack 工程构建</li><li>Node & Npm 服务 包管理</li><li>Mocha 单元测试</li><li>Typescript 主程序逻辑</li><li>Canvas WebGL 渲染API</li><li>Babel-loader 语言编译</li></ul><blockquote>P:算法-布局(layout)方向,因为存在复杂度高的算法 更多是C++编写 (一方面语言问题,一方面考虑到可扩展问题.通信问题目前不是瓶颈点[后面也可以做切分,有做思考])。通过restful方式进行通信。</blockquote><h3>应用场景</h3><p>从可视化(交互)角度来说, 分为展示, 分析,编辑三个方向。然后三个方向继而还可以衍生各类行业。比如图分析:</p><h4>图分析</h4><p>FastV-G 可支持的业务领域。</p><ul><li>知识图谱;</li><li>图平台:图数据库、图计算;</li><li>安全风控;</li><li>...</li></ul><h3>看个效果</h3><p><img src="/img/remote/1460000041338115" alt="e1246945d506de345dda1963ed1f398.jpg" title="e1246945d506de345dda1963ed1f398.jpg"></p><p>只是分享下技术成果, 视觉我就先不在意了。大家体谅哈哈哈.</p><ol><li>硬件是i5 CPU 集显 16G的内存</li><li>算法部分,大概10w图元(5w点 5w边) 上图为10s内的迭代效果。</li><li>渲染部分 秒级. zoom select drag相关操作均在30fps左右。</li></ol><h2>最后</h2><p>先介绍到这里.over</p><h4>招人!!!招人!!!招人!!!重要的事情说3遍。领导nice。环境nice。薪资nice。call me call me!!!! 想搞事情的速度联系,想从事图形学方向的想交流也可以加我 主页有联系方式。其他勿扰勿扰!!!</h4>
图形学之纹理后续/WebGL多纹理处理
https://segmentfault.com/a/1190000040809960
2021-10-14T10:55:32+08:00
2021-10-14T10:55:32+08:00
wlove
https://segmentfault.com/u/dxgl
1
<h2>背景</h2><p>本篇收录于<a href="https://link.segmentfault.com/?enc=llKd7IenzKE4zvrrTFIXgg%3D%3D.kA%2Fs89znGEXp0V7YjNIbNMW1HnoWqSbC93M8GLmv3%2F90SsM5v2eQq5Ib1BXS%2F67j" rel="nofollow">《数据可视化和图形学》</a>专栏</p><blockquote>之前介绍纹理相关的理论及简单使用 <a href="https://link.segmentfault.com/?enc=8uNmDufuBs0kQDZgw7hbBg%3D%3D.TlDTB9Ub3f8XigjzuR0BOXdJvKZWgj%2Fuka4bC1BE405%2BAk3wA8TfgjJSxRRmvCDf" rel="nofollow">有需要可以参考上文</a> , 在上文基础进行多纹理实践(更多是帮助群内小伙伴提前脱坑!!!!)</blockquote><h3>本篇大纲</h3><ol><li>多纹理渲染实现思路</li><li>多纹理渲染coding(几种场景)</li></ol><h4>1. 多纹理渲染实现思路</h4><blockquote>多纹理渲染更多是指 gl_FragColor采取混合纹理 texture2D * texture2D的关系。本篇初衷为了帮助群里小伙伴的进阶坎坷路~会提到多vertex(纹理)渲染 然后坐标重叠的需求。</blockquote><ol><li>定义vertexArray(本文示例为2point)</li><li>定义textureArray</li><li>创建缓冲区(此处注意多渲染节点公用缓冲区情况)</li><li>按部就班依此注册shader.绑定数据,渲染。</li></ol><h4>多纹理渲染coding(几种场景)</h4><blockquote>第一种 gl_FragColor采取混合纹理 texture2D * texture2D<br><img src="/img/remote/1460000040809962" alt="image.png" title="image.png"></blockquote><h5>shader部分没什么难度...</h5><pre><code>// vertex
attribute vec2 a_position; //坐标
attribute vec2 a_texCoord; //纹理
uniform vec2 u_resolution;
varying vec2 v_texCoord;
void main() {
// 坐标转换像素->1.0,0.0...
vec2 zeroToOne = a_position / u_resolution;
vec2 zeroToTwo = zeroToOne * 2.0;
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
// 纹理 给fragment使用
v_texCoord = a_texCoord;
}
// fragment
uniform sampler2D u_image0; // 纹理
uniform sampler2D u_image1;
// 来自vertex shader
varying vec2 v_texCoord;
void main() {
vec4 color0 = texture2D(u_image0, v_texCoord);
vec4 color1 = texture2D(u_image1, v_texCoord);
gl_FragColor = color0 * color1; // 可以理解为混合纹理
}
</code></pre><h5>JavaScript 也很简单 创建节点坐标/纹理/shader&数据连接/渲染 搞定!</h5><pre><code>// 此处示意代码 完整代码上传github
var texcoordBuffer = gl.createBuffer(); //纹理
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
// bufferData
...
for(...images length){
// 循环遍历
gl.bindTexture(gl.TEXTURE_2D, texture);
}
var positionBuffer = gl.createBuffer(); //节点坐标 此处绘制TRIANGLES 三角形
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// bufferData
// 缓冲区分配给attribute变量
gl.vertexAttribPointer(
texcoordLocation, size, type, normalize, stride, offset);
gl.vertexAttribPointer(
positionLocation, size, type, normalize, stride, offset);
// 开启attribute变量
gl.enableVertexAttribArray(positionLocation);
gl.enableVertexAttribArray(texcoordLocation);
// 纹理!!!!激活纹理单元
gl.activeTexture(gl.TEXTURE0);
//给定的纹理绑定到目标(vertex)
gl.bindTexture(gl.TEXTURE_2D, textures[0]);
// 绘制!!! 大功告成
gl.drawArrays(gl.TRIANGLES, 0, 6);
</code></pre><blockquote>第二种 多个节点纹理(其实就是坐标重叠 后者覆盖前者...)<br><img src="/img/remote/1460000040809963" alt="image.png" title="image.png"></blockquote><h5>shader部分也没什么难度(没什么改变)...</h5><pre><code>// vertex
attribute vec2 a_position;
attribute vec2 a_texCoord;
attribute lowp float textureIndex;
uniform vec2 u_resolution;
varying vec2 v_texCoord;
varying lowp float indexPicker;
void main() {
vec2 zeroToOne = a_position / u_resolution;
vec2 zeroToTwo = zeroToOne * 2.0;
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
v_texCoord = a_texCoord;
indexPicker = textureIndex; // 控制fragment shader选哪一个纹理
}
// fragment
precision mediump float;
// 纹理
uniform sampler2D u_image[2];
// the texCoords passed in from the vertex shader.
varying vec2 v_texCoord;
varying lowp float indexPicker;
void main() {
if (indexPicker < 0.5) {
gl_FragColor = texture2D(u_image[0], v_texCoord);
} else {
gl_FragColor = texture2D(u_image[1], v_texCoord);
}
}
</code></pre><h5>JavaScript 也很简单 创建节点坐标/纹理/shader&数据连接/渲染 搞定!</h5><pre><code>// 此处示意代码 完整代码上传github
var texcoordBuffer = gl.createBuffer(); //纹理
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
// bufferData
...
for(...images length){
// 循环遍历
gl.bindTexture(gl.TEXTURE_2D, texture);
}
// 注意vertex!!!!! 此处与上文不同
var positionBuffer = gl.createBuffer(); //节点坐标 此处绘制TRIANGLES 三角形
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// bufferData
// 缓冲区分配给attribute变量
gl.vertexAttribPointer(
texcoordLocation, size, type, normalize, stride, offset);
gl.vertexAttribPointer(
positionLocation, size, type, normalize, stride, offset);
// 开启attribute变量
gl.enableVertexAttribArray(positionLocation);
gl.enableVertexAttribArray(texcoordLocation);
// 纹理!!!!激活纹理单元
gl.activeTexture(gl.TEXTURE0);
//给定的纹理绑定到目标(vertex)
gl.bindTexture(gl.TEXTURE_2D, textures[0]);
// 绘制!!! 大功告成
gl.drawArrays(gl.TRIANGLES, 0, vertextArray.length/2); // 绘制多个(三角形组合)正方形
</code></pre><h5>!!!!注意 群里小伙伴留意代码中注释 (解决你的疑惑)</h5><p><img src="/img/remote/1460000040809964" alt="image.png" title="image.png"></p><blockquote><p>1.<code>webgl-utils.js</code> webgl相关函数封装工具库</p><p>完整代码示例 [请点击git仓库查看代码示例]<br><a href="https://link.segmentfault.com/?enc=KLvgOWR6L0ODyBISdD78ow%3D%3D.e0c2IT6eakNvzJMaC3lQnL3HeNrC90NtMyq0VcCRYtFNz1wlYdFAKFor9Jp0bNtzHKsqxboPkbCeZnCuRSHVJTQwL6XSNttKqgjNRFIVLGk%3D" rel="nofollow" title="texture.html">texture.html</a></p></blockquote><p><a href="https://link.segmentfault.com/?enc=6kN0kFw1ubqaxeUDZLFQqw%3D%3D.2G1rq%2BI3BwnDCtq9rOqdOt4wbkxEo4JwG4x%2FXuWvq4u3eT7q9mdW2V6Z4147vIlpp3vuCbxSwM3sEoXTxRjYmfQzdbfp2yavB1MyVseo%2Fz0%3D" rel="nofollow" title="texture1.html">texture1.html</a></p><h3>2D渲染方面你可能需要了解的有</h3><ol><li>纹理缓存</li><li>纹理压缩</li><li>2D/2D纹理优化</li><li>渲染优化...</li></ol><h2>最后</h2><blockquote>最后强烈希望大家学习相关理论知识;理论可能日常用到的地方很少,但是它能决定你走多远。(有的人问难怎么办,勤于练习吧) 写作速度我感觉我又行了,哈哈哈... 最近会持续更新(因为在自研自己的渲染引擎。唉 一言难尽。。。抱歉啦)</blockquote>
2021 Web Worker 需要了解的现状
https://segmentfault.com/a/1190000040521208
2021-08-16T13:32:16+08:00
2021-08-16T13:32:16+08:00
wlove
https://segmentfault.com/u/dxgl
6
<h2>前言</h2><ul><li>本文为转载</li><li>翻译原文标题:2021 Web Worker 现状</li><li>翻译原文作者:Tapir</li><li>翻译原文地址:<a href="https://link.segmentfault.com/?enc=Cq%2B5p3lUYsaL1Q%2BbE3DImw%3D%3D.Xxwa2tO1ZWd8SK0W6FaUf1h6uT8WOw%2BYGPkqWXLf3bklJfGfTZE%2BjPZu%2BlibuPot" rel="nofollow">知乎</a></li><li>原文地址: <a href="https://link.segmentfault.com/?enc=J8kBTiJln7ojZfgssSWEMw%3D%3D.yzL0VGRrUPcOjkHURSYJXPjWXMpFT1qqxu6l%2BP%2FBfK4De3zUuYPHnLQ3uC0FSzau9rfuoKa974e5fDe6tYuorA%3D%3D" rel="nofollow">The State Of Web Workers In 2021</a></li></ul><blockquote>导读: Web 是单线程的。这让编写流畅又灵敏的应用程序变得越来越困难。Web Worker 的名声很臭,但对 Web 开发者来说,它是解决流畅度问题的 一个非常重要的工具。让我们来了解一下 Web Worker 吧。</blockquote><p>我们总是把 Web 和 所谓的 “Native” 平台(比如 Android 和 iOS)放在一起比较。Web 是流式的,当你第一次打开一个应用程序时,本地是不存在任何可用资源的。这是一个根本的区别,这使得很多在 Native 上可用的架构 无法简单应用到 Web 上。</p><p>不过,不管你在关注在什么领域,都一定使用过或了解过 多线程技术。iOS 允许开发者 使用 <a href="https://link.segmentfault.com/?enc=1ZovWOA%2Fo09UaP5PSexcOw%3D%3D.KiHtlEPUrAMjk%2B%2FPIfuoxdzBcLNT5%2BqC0XIMgcb5DyIMU6p7OZizcz7CebwRlrRaS95lLNXxtvFqCemVuPEajHtG2jeA%2FYvBDqKVIeNPoumh1lwB8JscjEj8HtHkm8e1" rel="nofollow">Grand Central Dispatch</a> 简单的并行化代码,而 Android 通过 新的统一任务调度器 <a href="https://link.segmentfault.com/?enc=4LPSkG8MqxTb7fnQ8HA4Ag%3D%3D.iUPO122d3qG8g4V%2B8iDChGNFU%2B98JxsG2FdV85m9UGGqYFxPNLmonWwDa6LE4tSvPodYR%2BEaurzAYMlgSzqF3MrfcQZLk1sIqTDR77Ja5GaQvkCXpkJWPgU3Y1vgOm6O8hxUBX6rLc%2F%2FAkyA73EzHQ%3D%3D" rel="nofollow">WorkManager</a> 实现同样的事情,游戏引擎 Unity 则会使用 <a href="https://link.segmentfault.com/?enc=591UzKNpO6oTFZB9DHCUSQ%3D%3D.FwtVA%2BZ3odCultNkwng8upCNPlqYxrUJj%2B3in2TzbOHkv6h9G8GKZoB18rqc%2FFGzpK6aqwPjvDw4yaHDCNNRsfkqSelNsoY5%2B9brtRzJS%2F%2F82LMLfdesJU4lUOtMClpa" rel="nofollow">job systems</a> 。我上面列举的这些平台不仅支持了多线程,还让多线程编程变得尽可能简单。</p><p>在这篇文章,我将概述为什么我认为多线程在 Web 领域很重要,然后介绍作为开发者的我们能够使用的多线程原语。除此之外,我还会谈论一些有关架构的话题,以此帮助你更轻松的实现多线程编程(甚至可以渐进实现)。</p><h3>无法预测的性能问题</h3><p>我们的目标是保持应用程序的 流畅(smooth) 和 灵敏(responsive)。流畅 意味着 稳定 且 足够高 的 帧率。灵敏 意味着 UI 以最低的延迟 响应 用户交互。两者是保持应用程序 优雅 和 高质量 的 关键因素。</p><p>按照 <a href="https://link.segmentfault.com/?enc=sgMmT3s5ps2gDH68UETy1Q%3D%3D.wrjBNbUU%2F%2BpFxQ%2B09%2BBiLtlv6JlklSHGJ0ruyizLV2tClvvX%2FY77YhULzzbOKB03nYohPF1DnT3b%2B2d2AzGQQA%3D%3D" rel="nofollow">RAIL 模型</a>,灵敏 意味着响应用户行为的时间控制在 100ms 内,而 流畅 意味着屏幕上任何元素移动时 稳定在 60 fps。所以,我们作为开发者 拥有 1000ms/60 = 16.6ms 的时间 来生成每一帧,这也被称作 “帧预算”(frame budget)。</p><p>我刚刚提到了 “我们”,但实际上是 “浏览器” 需要 16.6ms 的时间 去完成 渲染一帧背后的所有工作。我们开发者仅仅直接负责 浏览器实际工作 的一部分。浏览器的工作包括(但不限于):</p><ul><li>检测 用户操作的 元素(element)</li><li>发出 对应的事件</li><li>运行相关的 JavaScript 时间处理程序</li><li>计算 样式</li><li>进行 布局(layout)</li><li>绘制(paint)图层</li><li>将这些图层合并成一张 最终用户在屏幕上看到的 图片</li><li>(以及更多…)</li></ul><p>好大的工作量啊。</p><p><img src="/img/remote/1460000040521210" alt="" title=""></p><p>另一方面,<a href="https://link.segmentfault.com/?enc=KaUjd5scFUF91fpMtzEvaQ%3D%3D.acccMwrfB2CBIxhbUa%2FyYVDkUbe84ssD9xMRSGoC9L47xHorcaGoKxZ2tsFsadfII4xZwYkz1EWHiJm3xwA4Xut6y4zfI6EmX%2BPBTBQfFeKcYWJss4jWpLfQ2%2BA1VWQxFRO7buyM%2BybmeSKXt4I4hA%3D%3D" rel="nofollow">“性能差距” 在不断扩大</a>。旗舰手机的性能随着 手机产品的更新换代 变得越来越高。而低端机型正在变得越来越便宜,这使得之前买不起手机的人能够接触到移动互联网了。就性能而言,这些低端手机的性能相当于 2012 年的 iPhone。</p><p>为 Web 构建的应用程序会广泛运行在性能差异很大的不同设备上。JavaScript 执行完成的时间取决于 运行代码设备有多快。不光是 JavaScript,浏览器执行的其他任务(如 layout 和 paint)也受制于设备的性能。在一台现代的 iPhone 上运行只需要 0.5ms 的任务 可能 到了 Nokia 2 上需要 10ms。用户设备的性能是完全无法预测的。</p><blockquote>注:RAIL 作为一个指导框架至今已经 6 年了。你需要注意一点,实际上 60fps 只是一个占位值,它表示的是用户的显示设备原生刷新率。例如,新的 Pixel 手机 有 90Hz 的屏幕 而 iPad Pro 的屏幕是 120Hz 的,这会让 帧预算 分别减少到 11.1ms 和 8.3ms。</blockquote><p>更复杂的是,除了测算 <code>requestAnimationFrame()</code> 回调之间的时间,<strong>没有更好的方法来确定运行 app 设备的刷新率</strong> 。</p><h3>JavaScript</h3><p>JavaScript 被设计成 与浏览器的主渲染循环同步运行。几乎所有的 Web 应用程序都会遵循这种模式。这种设计的缺点是:执行缓慢的 JavaScript 代码会阻塞浏览器渲染循环。JavaScript 与浏览器的主渲染循环 同步运行可以理解为:如果其中一个没有完成,另一个就不能继续。为了让长时间的任务能在 JavaScript中 协调运行,一种基于 回调 以及 后来的 Promise 的 <a href="https://link.segmentfault.com/?enc=gZ2%2BG2un3Vee%2FJttZegnOw%3D%3D.8xiMjcjm8j8RF65qSB6BG9pyBdcI6cne%2FYLpoFZ6xPWa9hLhYRrv6IeYwvH9YVfnFcskBnFsIcpeLIyyOvI00nADtyrVA5Xv0dgqXtSDqyw%3D" rel="nofollow">异步模型被建立起来</a>。</p><p>为了保持应用程序的 流畅,你需要保证你的 JavaScript 代码运行 连同 浏览器做的其他任务(样式、布局、绘制…)的时间加起来不超出设备的帧预算。为了保持应用程序的 灵敏,你需要确保任何给定的事件处理程序不会花费超过 100ms 的时间,这样才能及时在设备屏幕上展示变化。在开发中,即使用自己的设备实现上面这些已经很困难了,想要在所有的设备都上实现这些几乎是不可能的。</p><p>通常的建议是 “做代码分割(chunk your code)”,这种方式也可以被称作 “出让控制权(yield)给浏览器”。其根本的原理是一样的:为了给浏览器一个时机来进入下一帧,你需要将代码分割成大小相似的块(chunk),这样一来,在代码块间切换时 就能将控制权交还给 浏览器 来做渲染。</p><p>有很多种“出让控制权(yield)给浏览器” 的方法,但是没有那种特别优雅的。最近提出的 <a href="https://link.segmentfault.com/?enc=FCeyMtFSwGEXK0VbnzKXVA%3D%3D.5ByJFMuDDpBoEAPYwHtBmpbRUn2g%2FRLBud3rMAexzCgo60aea9wPM%2FNa3b20K8YdeXvuYF%2F%2BbwvKu8TfoTQ%2BjceBEB43S7TaHqgUkziqexo%3D" rel="nofollow">任务调度 API</a> 旨在直接暴露这种能力。然而,就算我们能够使用 <code>await yieldToBrowser()</code> (或者类似的其他东西) 这样的 API 来 出让控制权,这种技术本身还是会存在缺陷:为了保证不超出帧预算,你需要在足够小的块(chunk)中完成业务,而且,你的代码每一帧至少要 出让一次控制权。</p><p>过于频繁的出让控制权 的 代码 会导致 调度任务的开销过重,以至于对应用程序整体性能产生负面影响。再综合一下我之前提到的 “无法预测的设备性能”,我们就能得出结论 — 没有适合所有设备的块(chunk)大小。当尝试对 UI 业务进行 “代码分割” 时,你就会发现这种方式很成问题,因为通过出让控制权给浏览器来分步渲染完整的 UI 会增加 布局 和 绘制 的总成本。</p><h3>Web Workers</h3><p>有一种方法可以打破 与浏览器渲染线程同步的 代码执行。我们可以将一些代码挪到另一个不同的线程。一旦进入不同的线程,我们就可以任由 持续运行的 JavaScript 代码 阻塞,而不需要接受 <em>代码分割</em> 和 <em>出让控制权</em> 所带来的 复杂度 和 成本。使用这种方法,渲染进程甚至都不会注意到另一个线程在执行阻塞任务。在 Web 上实现这一点的 API就是 <a href="https://link.segmentfault.com/?enc=bNV3iaAeUdVLmUKzZ56Rrw%3D%3D.UYnWp4vrXqp6FT0VmFm98gzh16eJ1MygRUrC689O86sEwneyH7iTpZmp9Q9p5RkifOfpaiAY4KaPA3AAMrsnN%2FKkhQ%2BGEm0b61ooEaiHsvu9A536g%2B5yHqBicv%2BHSYvW" rel="nofollow">Web Worker</a>。通过传入一个独立的 JavaScript 文件路径 就可以 创建一个 Web Worker,而这个文件将在新创建的线程里加载和运行。</p><pre><code>const worker = new Worker("./worker.js");</code></pre><p>在我们深入讨论之前,有一点很重要,虽然 Web Workers, Service Worker 和 Worklet 很相似,但是它们完全不是一回事,它们的目的是不同的:</p><ul><li>在这篇文章中,我只讨论 <a href="https://link.segmentfault.com/?enc=Ulf3fLH%2F%2BAODBiRsOyW5AA%3D%3D.Qns42gaAz%2Fx1W9OZ7EnwGKczODVebBWY81CgTc1bNY4Hk%2FiTYzJw3gqKc1cSQWEm0VUk%2B4CxqEhLtaSIDME9JceAuooo5ZgcTJwjMNh67MgOdrLKQ%2FrHiCteUPwDyGLya9iXQGSRj0FQn5Ln2HAJ0w%3D%3D" rel="nofollow">Web Workers</a> (经常简称为 “Worker”)。Worker 就是一个运行在 独立线程里的 JavaScript 作用域。Worker 由一个页面生成(并所有)。</li><li><a href="https://link.segmentfault.com/?enc=B9ciRFFAWP6lpFAsmT2hTQ%3D%3D.x4duv7m7gcX9nWwwb85%2BTYZzkD3FJ6EkqGlizTKmbCJrnUE1AXbTFlBMsL9Gu4hZpD18pCvfDG79tJkBgWuw1HLVKwQLAHkemermmwHSmiWd71nEnHnVP0G6H5xxdGohmq3DFH4Mg7sW3PoBY7zoRQ%3D%3D" rel="nofollow">ServiceWorker</a> 是一个 <em>短期的</em> ,运行在 独立线程里的 JavaScript 作用域,作为一个 代理(proxy)处理 同源页面中发出的所有网络请求。最重要的一点,你能通过使用 Service Worker 来实现任意的复杂缓存逻辑。除此之外,你也可以利用 Service Worker 进一步实现 后台长请求,消息推送 和 其他那些无需关联特定页面的功能。它挺像 Web Worker 的,但是不同点在于 Service Worker 有一个特定的目的 和 额外的约束。</li><li>Worklet 是一个 API 收到严格限制的 独立 JavaScript 作用域,它可以选择是否运行在独立的线程上。Worklet 的重点在于,浏览器可以在线程间移动 Worklet。<a href="https://link.segmentfault.com/?enc=ruVCCgDcoIqGutr8TCd58w%3D%3D.w95jUmMuiX1WCWWpcNOsZAEq1szmfWbHI3LT64%2FviYS88pO%2FsDGZbweaor0NMzpe%2BWqaBRvTpZBd6X%2B1iirssLvYk1OkOpmVvIGU%2FItabtaLxk5pQMGQo6OjD9vKiVoN" rel="nofollow">AudioWorklet</a>,<a href="https://link.segmentfault.com/?enc=z2OHziiPkGlrWPAFg%2B6uqg%3D%3D.OdGmDEV56ZIoyl7NnvmrvsfYHhk9eY7x0rbTUaW3M7Y2kD33f4OEUnRC80btLSoyIQyZTyEKqDljPnHs1RU%2FRAUpNhC7%2Bknbxd6pVEyhUPBSSoxzERs8JGX5I%2BGeExn3GJSX8OzS0tnk7mFKXmq02w%3D%3D" rel="nofollow">CSS Painting API</a> 和 <a href="https://link.segmentfault.com/?enc=nM%2FFN1q71TDcJlcQ8KBzgw%3D%3D.0yu1GcsRcKcCDXMxs%2BJbGrxiyhSUYgOeMsRzFte82sEmF90rSHdDIMG4u%2BG9HDgjPAn3xbsh8u6q%2FKaFORyh50ZgrPzX3NYLKmd7GyfyoAabGJfNB%2Fn2oAPpU2BkiGj9PfBUKsuom3m8RU5lZHS%2Bmg%3D%3D" rel="nofollow">Animation Worklet</a> 都是 Worklet 应用的例子。</li><li><a href="https://link.segmentfault.com/?enc=FVG%2FR4VNz3viqmuNZmeuBQ%3D%3D.OxERmfjDzAHmD8JNYM3U9mGrwO88eTSqU7K2aSi%2F6uZT6kpyp9rzGHJzHzs8fC3Wd47AlWBbUUlOnjvEN0p%2BTDfoK19xPQmTpSXhJpU3tWG04YS73HPn4wKXTAxShUKBAeBUOYN7hw8GBIxp%2FlXk4A%3D%3D" rel="nofollow">SharedWorker</a> 是特殊的 Web Worker,同源的多个 Tab 和 窗口可以引用同一个 SharedWorker。这个 API 几乎不可能通过 polyfill 的方式使用,而且目前只有 Blink 实现过。所以,我不会在本文中深入介绍。</li></ul><p>JavaScript 被设计为和浏览器同步运行,也就是说没有并发需要处理,这导致很多暴露给 JavaScript 的 API 都不是 线程安全 的。对于一个数据结构来说,线程安全意味着它可以被多个线程并行访问和操作,而它的 状态(state)不会 被破坏(corrupted)。</p><p>这一般通过 <strong>互斥锁(mutexes)</strong> 实现。当一个线程执行操作时,互斥锁会锁定其他线程。浏览器 和 JavaScript 引擎 因为不处理锁定相关的逻辑,所以能够做更多优化来让代码执行更快。另一方面,没有锁机制 导致 Worker 需要运行在一个完全隔离的 JavaScript 作用域,因为任何形式的数据共享都会 因缺乏线程安全 而产生问题。</p><p>虽然 Worker 是 Web 的 <strong>“线程”原语</strong> ,但这里的 “线程” 和在 C++,Java 及其他语言中的非常不同。最大的区别在于,依赖于隔离环境 意味着 Worker 没有权限 访问其创建页面中其他变量和代码,反之,后者也无法访问 Worker 中的变量。数据通信的唯一方式就是调用 API <a href="https://link.segmentfault.com/?enc=nCMGJROcI57smzp1lT8z0Q%3D%3D.OP0gnEIr1t8QBSHfTLZ5wq4Fvj9yQWMHgXb5Z2XtePbwfbr19q1yr0rT3hMVNZHSh1boB8mgOtf2VHqd4aV%2Bt1xco%2BD%2FBtsRpYHjsBxKVieCUQuoiPBto0qpo5eKWEQugoXnzO%2Fvxuk5t%2FDMil1dHg%3D%3D" rel="nofollow">postMessage</a>,它会将传递信息复制一份,并在接收端 触发 <code>message</code> 事件。隔离环境也意味着 Worker 无法访问 DOM,在Worker 中也就无法更新 UI — 至少在没有付出巨大努力的情况下(比如 AMP 的 <a href="https://link.segmentfault.com/?enc=bYzIG5X95wj%2FSbp03tuEew%3D%3D.GKFQyIAJnAQpUwXJWcdGtuMUtjip5K4UmoG24l24f5%2BzjVqfMcL8o8ydcXkVujLAnqJi1BjaX0MMek6lTu%2BXbria7WfTjZaG2nINRqu3WYU%3D" rel="nofollow">worker-dom</a>)。</p><p><img src="/img/remote/1460000040521211" alt="" title=""></p><p>浏览器对 Web Worker 的支持可以说是普遍的,即使是 IE10 也支持。但是,Web Worker 的使用率依旧偏低,我认为这很大程度上是由于 Worker API 特殊的设计。</p><h3>JavaScript 的并发模型</h3><p>想要应用 Worker ,那么就需要对应用程序的架构进行调整。JavaScript 实际上支持两种不同的并发模型,这两种模型通常被归类为 “Off-Main-Thread 架构”(脱离主线程架构)。这两种模型都会使用 Worker,但是有非常不同的使用方式,每种方式都有自己的权衡策略。这两种模型了代表解决问题的两个方向,而任何应用程序都能在两者之间找到一个更合适的。</p><h4>并发模型 #1:Actor</h4><p>我个人倾向于将 Worker 理解为 <a href="https://link.segmentfault.com/?enc=VU6NF6LMMp8cszTB7Ltc2A%3D%3D.tcp6DpFLgXZT1%2FcXDxr1NysbUbjsgYjZwgfe6p1SX722FGtH0MCcsCZ4fw9Bu01EHq4vVy45rcwFIgcfb3HXSVTh%2BzEc3Jc9%2F1BSV5AXf8Q%3D" rel="nofollow">Actor 模型</a> 中的 Actor。编程语言 Erlang 中对于 Actor 模型 的实现可以说是最受欢迎的版本。每个 Actor 都可以选择是否运行在独立的线程上,而且完全保有自己操作的数据。没有其他的线程可以访问它,这使得像 互斥锁 这样的渲染同步机制就变得没有必要了。Actor 只会将信息传播给其他 Actor 并 响应它们接收到的信息。</p><p>例如,我会把 主线程 想象成 拥有并管理 DOM 或者说是 全部 UI 的 Actor。它负责更新 UI 和 捕获外界输入的事件。还会有一个 Actor 负责管理应用程序的状态。DOM Actor 将低级的输入事件 转换成 应用级的语义化的事件,并将这些事件传递给 <em>状态 Actor</em> 。状态 Actor 按照接收到的事件 修改 状态对象,可能会使用一个状态机 甚至涉及其他 Actor。一旦状态对象被更新,状态 Actor 就会发送一个 更新后状态对象的拷贝 到 DOM Actor。DOM Actor 就会按照新的状态对象更新 DOM 了。<a href="https://link.segmentfault.com/?enc=ujl4%2FJzFMYRnrdBfEjarJg%3D%3D.3vL6sxUoMpVOXjmGgaZji7DlxRE7zO%2B%2B30sq14C09hm3TRJEHEolfN2g1d0FIbMORj1PO8%2FZc74rm8Y1MYg0CTiFVy%2FZ%2BYh6hRPtnFn8%2Fu8%3D" rel="nofollow">Paul Lewis 和 我 曾经在 2018 年的 Chrome 开发峰会上探索过以 Actor 为中心的应用架构</a> 。</p><p>当然,这种模式也不是没有问题的。例如,你发送的每一条消息都需要被拷贝。拷贝所花的时间不仅取决于 消息的大小,还取决于当前应用程序的运行情况。根据我的经验,<a href="https://link.segmentfault.com/?enc=nOrhmS7skSafJaaspk%2BN2A%3D%3D.L6WZ1EsejOUytO%2BR42dqCBvtfGl%2BRFYuZ6pITdPGGv8A%2B8UYkMDA3M7rVrkFqpUApVvTpO6ayYnl5Jjs5pYYsnzSLU714MExp2Vg1PuBZ2E%3D" rel="nofollow">postMessage 通常 “足够快”</a>,但在某些场景确实不太行。另一个问题是,将代码迁移到 Worker 中可以解放 主线程,但同时不得不支付通信的开销,而且 Worker 可能会在响应你的消息之前忙于执行其他代码,我们需要考虑这些问题来做一个平衡。一不小心,Worker 可能会给 UI 响应带来负面影响。</p><p>通过 postMessage 可以传递非常复杂的消息。其底层算法(叫做 “结构化克隆”)可以处理 内部带有循环的数据结构 甚至是 <code>Map</code> 和 <code>Set</code> 。然而,他不能处理 函数 或者 类,因为这些代码在 JavaScript 中无法跨作用域共享。有点恼人的是,通过 postMessage 传一个 函数 会抛出一个 错误,然而一个类被传递的话,只会被静默的转换为一个普通的 JavaScript 对象,并在此过程中丢失所有方法(这背后的细节是有意义的,但是超出了本文讨论的范围)。</p><p>另外,postMessage 是一种 “Fire-and-Forget” 的消息传递机制,没有请求 和 响应 的概念。如果你想使用 请求/响应 机制(根据我的经验,大多数应用程序架构都会最终让你不得不这么做),你必须自己搞定。这就是我写了 <a href="https://link.segmentfault.com/?enc=oWSXZpMJWBfwhzIzwaekug%3D%3D.kiLmvzIodspSY6AFGjpNbiZ47MwwnFI4ugai%2Fj8Tri8SFVm6FsSdooOn9pxNajYZbYDoohp8EEalu2I4q%2FVNi7xeC3V3hbce1AAwOtPEWmY%3D" rel="nofollow">Comlink</a> 的原因,这是一个底层使用 RPC 协议的库,它能帮助实现 主线程 和 Worker 互相访问彼此对象。使用 Comlink 的时候,你完全不用管 postMessage。唯一需要注意的一点是,由于 postMessage 的异步性,函数并不会返回结果,而是会返回一个 promise。在我看来,Comlink 提炼了 Actor 模式 和 共享内存 两种并发模型中优秀的部分 并 提供给用户。</p><p><img src="/img/remote/1460000040521212" alt="" title=""></p><p>Comlink 并不是魔法,为了使用 RPC 协议 还是需要使用 postMessage。如果你的应用程序最终罕见的由于 postMessage 而产生瓶颈,那么你可以尝试利用 ArrayBuffers 可 <em>被转移(transferred)</em> 的特性。转移 ArrayBuffer 几乎是即时的,并同时完成所有权的转移:在这个过程中 发送方的 JavaScript 作用域会失去对数据的访问权。当我<a href="https://link.segmentfault.com/?enc=HICFo7fFK5IMQjOn8gzNGw%3D%3D.u356z4ao8jk4gnMFszjBpg2JgfxCDsnFPeFhfduwNq5eDgLTNi4KYXhmv%2Fe25xfjonJWC0ez9D4Os0fhxT02TBl4CK4P0arixzxcRkYXbJ5BHKbXzo%2B0B6Tj%2BWZHv37q" rel="nofollow">实验在主线程之外运行 WebVR 应用程序的物理模拟时</a>,用到了这个小技巧。</p><h4>并发模型 #2:共享内存</h4><p>就像我之前提到的,传统的线程处理方式是基于 共享内存 的。这种方式在 JavaScript 中是不可行的,因为几乎所有的 JavaScript API 都是假定没有并发访问对象 来设计的。现在要改变这一点要么会破坏 Web,要么会由于目前同步的必要性导致重大的性能损耗。相反,共享内存 这个概念目前被限制在一个专有类型:<a href="https://link.segmentfault.com/?enc=ocWtQBtsjEgsKMqQJorHog%3D%3D.whuPG%2B9eUOFixnqbo%2By0p%2BbqPQg2%2BgiFqvXmfd4QdoZKqIl8ht9e1ftFWJAlxFuj9jGlXP6AkJqRmfHlwwwlnH0aY404O0IXcsQMkIWqGTLeR3dJQi%2FnLlbAZN%2FH8PwRKRnvzeZzDTTrz%2F0LS4nsQkLs2vchy6d3UaaFfZbXEQ6alsEg3fUDt7qJ54BqMVYi" rel="nofollow">SharedArrayBuffer</a> (或简称 SAB)。</p><p>SAB 就像 ArrayBuffer,是线性的内存块,可以通过 <a href="https://link.segmentfault.com/?enc=0YpSgceAMvSot4lSXgfj1g%3D%3D.XchKs5ed%2Fmiw91VGhDs9SQVRj60i47EmwrWgHBKJk%2BkMzc5HzNIjjNlfMVb4A6%2BRzXl8o9ExAq9ZYwcuOBV9lJokW10bV2eN1Hq6yeTuALbQfyiFJ2HzEULfZaYQ%2BLMkUPHlwEcWnG16alWfLZph9QGvOYat2oK5CU8O7sMRZKQ%3D" rel="nofollow">Typed Array</a> 或 <a href="https://link.segmentfault.com/?enc=mVXp5Yj%2BYRuQRq6aWRQLUg%3D%3D.BXG2GH2hvyBQhkDRxePdAt9TrjF2I6wKoRg2gaOubHRllko13COLmOtmYBQ0aZWUXLDrbcb1zGIZcDi%2F%2BD5VmsC9GQlmS%2FDgAOEEWUyjcfyYEeHNUD27H1PLxNZfwdcOuqOKV%2BsBbffWoOHK%2BIPZhDg9IGSRPti09QMztGn%2F5t8%3D" rel="nofollow">DataView</a> 来操作。如果 SAB 通过 postMessage 发送,那么另一端不会接收到数据的拷贝,而是收到完全相同的内存块的句柄。在一个线程上的任何修改 在其他所有线程上都是可见的。为了让你创建自己的 互斥锁 和 其他的并发数据结构,<a href="https://link.segmentfault.com/?enc=ixHDlmqzuDRKhA3r4bynDw%3D%3D.Ai0pwcpTJfGwU4U7%2BsfczDoUtDBQdNmJPxal2%2FdXFZ61IGKF%2F9yv98xaydy3yX4iQOj8fVOkR7fvl0D4lmJsmjRM0bXJx9EeM8sKHHCtd0k7GAnFDK%2BUSXZm87byuL743ODLUBkAHwjAuk4c%2Fp3FqxZ61HeTmtYkon%2B8KtqJSjs%3D" rel="nofollow">Atomics</a> 提供了各种类型的工具 来实现 一些原子操作 和 线程安全的等待机制。</p><p>SAB 的 缺点是多方面的。首先,也是最重要的一点,SAB 只是一块内存。SAB 是一个非常低级的原语,以增加 工程复杂度 和 维护复杂度 作为成本,它提供了高灵活度 和 很多能力。而且,你无法按照你熟悉的方式去处理 JavaScript 对象 和 数组。它只是一串字节。</p><p>为了提升这方面的工作效率,我实验性的写了一个库 <a href="https://link.segmentfault.com/?enc=3eMYMr6ccegRzCRBhHnkfw%3D%3D.C%2FuVHkw3PgGnTnhGmczjuqM2GcnlcU0PiBp%2BrpS1PAYjgh0PdB7540kWSWkSEi%2Bt4ewEK3m8efv31B0SPfbhkg8Z9w652v1cfuzU%2B4eAjCH9x5oeWdrn1YFIrZa5HNuP" rel="nofollow">buffer-backed-object</a>。它可以合成 JavaScript 对象,将对象的值持久化到一个底层缓冲区中。另外,WebAssembly 利用 Worker 和 SharedArrayBuffer 来支持 C++ 或 其他语言 的线程模型。WebAssembly 目前提供了实现 共享内存并发 最好的方案,但也需要你放弃 JavaScript 的很多好处(和舒适度)转而使用另一种语言,而且通常这都会产出更多的二进制数据。</p><h3>案例研究: PROXX</h3><p><a href="https://link.segmentfault.com/?enc=JBNubTbeelWrqG9mMes2BA%3D%3D.d%2BMmGqFO9R5S5EeB0n8BXYjYMLCXZGf1%2F5%2BYDJR5v5ru%2BszKLDsS7YZe8RmPmcj4sfKQr%2FJw6thjii4pKbl69BR82Rfi2YOTPLe2vDV1jWE%3D" rel="nofollow">在 2019 年</a>,我和我的团队发布了 <a href="https://link.segmentfault.com/?enc=Jk4uS%2BheOWzOKWk%2FLgVT1g%3D%3D.AKMBbuIHvkeKxqOIP5b9J7reKH8XnmPtChKYv0AxToRNW6G0xghQvQMuM1ZUjBxuSqFxZFQaWsDt9W7ROw6O4w%3D%3D" rel="nofollow">PROXX</a>,这是一个基于 Web 的 扫雷游戏,专门针对功能机。功能机的分辨率很低,通常没有触摸界面,CPU 性能差劲,也没有凑乎的 GPU。尽管有这么多限制,这些功能机还是很受欢迎,因为他们的售价低的离谱 而且 有一个功能完备的 Web 浏览器。因为功能机的流行,移动互联网得以向那些之前负担不起的人开放。</p><p><img src="/img/remote/1460000040521213" alt="" title=""></p><p>为了确保这款游戏在这些功能机上灵敏流畅运行,我们使用了一种 类 Actor 的架构。主线程负责渲染 DOM(通过 preact,如果可用的话,还会使用 WebGL)和 捕捉 UI 事件。整个应用程序的状态 和 游戏逻辑 运行在一个 Worker 中,它会确认你是否踩到雷上了,如果没有踩上,在游戏界面上应该如何显示。游戏逻辑甚至会发送中间结果到 UI 线程 来持续为用户提供视觉更新。</p><p><img src="/img/remote/1460000040521214" alt="" title=""></p><h3>其他好处</h3><p>我谈论了 流畅度 和 灵敏度 的重要性,以及如何通过 Worker 来更轻松的实现这些目标。另外一个外在的好处就是 Web Worker 能帮助你的应用程序消耗更少的设备电量。通过并行使用更多的 CPU 核心,CPU 会更少的使用 “高性能” 模式,总体来说会让功耗降低。来自微软的 <a href="https://link.segmentfault.com/?enc=ZNGxjLYA2NvsOQW%2BbCFx6A%3D%3D.OOUYzdFxGJfxcSyxlR%2B5xHOPDuUZf6%2F3Vj1Gzopjg8SdG8Q14qLWpyaeqDgnYWM55dxeOwiSz4hmypbImBrAJw%3D%3D" rel="nofollow">David Rousset</a> 对 Web 应用程序的功耗进行了<a href="https://link.segmentfault.com/?enc=shzXk29aHW9wIlzz1UiWug%3D%3D.dkJ6hd2GEPA4oUM6qRicv749UD3im4jHUihvaV%2F80%2F3BCT7zeF7%2F05zVEX7QvQTlswYBqVWX%2FXbSGs%2BhhY43i9bYW4MeS8v%2Ffpn7n5tLbd0KfeAFvpcNq9ec3tSP%2F6qnqnFn6PLBlALu4vwJJ4irPB%2F%2FwHkcwLUlJ3JOy7JPojS%2BwBReQ1XQO7UbN5aZVIy6" rel="nofollow">探索</a>。</p><h3>采用 Web Worker</h3><p>如果你读到了这里,希望你已经更好的理解了 为什么 Worker 如此有用。那么现在下一个显而易见的问题就是:怎么使用。</p><p>目前 Worker 还没有被大规模使用,所以围绕 Worker 也没有太多的实践和架构。提前判断代码的哪些部分值得被迁移到 Worker 中是很困难的。我并不提倡使用某种特定的架构 而抛弃其他的,但我想跟你分享我的做法,我通过这种方式渐进的使用 Worker,并获得了不错的体验:</p><p>大多数人都使用过 模块 构建应用程序,因为大多数 打包器 都会依赖 模块 执行 打包 和 代码分割。使用 Web Worker 构建应用程序最主要的技巧就是将 UI 相关 和 纯计算逻辑 的代码 严格分离。这样一来,必须存在于主线程的模块(比如调用了 DOM API 的)数量就能减少,你可以转而在 Worker 中完成这些任务。</p><p>此外,尽量少的依靠同步,以便后续采用诸如 回调 和 async/await 等异步模式。如果实现了这一点,你就可以尝试使用 Comlink 来将模块从主线程迁移到 Worker 中,并测算这么做是否能够提升性能。</p><p>现有的项目想要使用 Worker 的话,可能会有点棘手。花点时间仔细分析代码中那些部分依赖 DOM 操作 或者 只能在主线程调用的 API。如果可能的话,通过重构删除这些依赖关系,并渐近的使用上面我提出的模型。</p><p>无论是哪种情况,一个关键点是,确保 <em>Off-Main-Thread 架构</em> 带来的影响是可测量的。不要假设(或者估算)使用 Worker 会更快还是更慢。浏览器有时会以一种莫名其妙的方式工作,以至于很多优化会导致反效果。测算出具体的数字很重要,这能帮你做出一个明智的决定!</p><h3>Web Worker 和 打包器(Bundler)</h3><p>大多数 Web 现代开发环境都会使用打包器来显著的提升加载性能。打包器能够将多个 JavaScript 模块打包到一个文件中。然而,对于 Worker,由于它构造函数的要求,我们需要让文件保持独立。我发现很多人都会将 Worker 的代码分离并编码成 Data URL 或 Blob URL,而不是选择在 打包器 上下功夫来实现需求。Data URL 和 Blob URL 这两种方式都会带来大问题:Data URL 在 Safari 中完全无法工作,Blob URL 虽说可以,但是没有 源(origin) 和 路径 的概念,这意味路径的解析和获取无法正常使用。这是使用 Worker 的另一个障碍,但是最近主流的打包器在处理 Worker 方面都已经加强了不少:</p><ul><li><strong>Webpack</strong> :对于 Webpack v4,<a href="https://link.segmentfault.com/?enc=dVDnGNKYmqNou%2BViZQz1SQ%3D%3D.yiujKPuLwLb2hm7aD0qa11XUt%2B94Vu3YIjoT%2Bez%2FIivz0Yb8ioyEmyDTumGhLuWvz8NBRcJzMrmGSDTZN9OtYfI6BQX5nIKQtydcFsaHAm4pELZObykCg%2BJ47XZmcDbg" rel="nofollow">worker-loader</a> 插件让 Webpack 能够理解 Worker。而从 Webpack v5 开始,Webpack 可以自动理解 Worker 的构造函数,甚至可以在 主线程 和 Worker 之间共享模块 而 避免重复加载。</li><li><strong>Rollup</strong> : 对于 Rollup,我写过 <a href="https://link.segmentfault.com/?enc=vnp9cnCnQbz04T%2Fu2Zya1Q%3D%3D.pbMIg1HvlLlHQyxSgP3o0HuUVPerHdXWDcJgojd6PaNYcVg8n%2Fc9pnRwQQFosgwW1TDEwGn8vS6nmm21JOwPVdhQCrR2R%2FoJwttKfsh7qRRwbZhjSeYN8H7RTY0Vt%2Ffh" rel="nofollow">rollup-plugin-off-main-thread</a> ,这个插件能让 Worker 变得开箱即用</li><li><strong>Parcel</strong> : Parcel 值得特别提一下,它的 v1 和 v2 都支持 Worker 的开箱即用,无需额外配置。</li></ul><p>在使用这些打包器开发应用程序时,使用 ES Module 是很常见的。然而,这又会带来新问题。</p><h3>Web Worker 和 ES Module</h3><p>所有的现代浏览器都支持通过 <code><script type="module" src="file.js"></code> 来运行 JavaScript 模块。Firefox 之外的所有现代浏览器现在也都支持对应 Worker 的一种写法:<code>new Worker("./worker.js", {type: "module"})</code> 。Safari 最近刚开始支持,所以考虑如何支持稍老一些的浏览器是很重要的。幸运的是,所有的打包器(配合上面提到的插件)都会确保你模块的代码运行在 Worker 中,即使浏览器不支持 Module Worker。从这个意义上来说,使用打包器可以被看作是对 Module Worker 的 polyfill。</p><p><img src="/img/remote/1460000040521215" alt="" title=""></p><h3>未来</h3><p>我喜欢 Actor 模式。但在 JavaScript 中的并发 设计的并不是很好。我们构建了很多的 工具 和 库 来弥补,但终究这是 JavaScript 应该在语言层面上去完成的。一些 TC39 的工程师对这个话题很感兴趣,他们正尝试找到让 JavaScript 更好的支持这两种模式的方式。目前多个相关的提案都在评估中,比如 允许代码被 postMessage 传输,比如 能够使用 高阶的,类似调度器的 API (这在 Native 上很常见) 来在线程间共享对象。</p><p>这些提案目前没还有在 标准化流程中 取得非常重大的进展,所以我不会在这里花时间深入讨论。如果你很好奇,你可以关注 <a href="https://link.segmentfault.com/?enc=noUz4Qj0poX95lwKNTQk5w%3D%3D.1BgLNATQNn99EUU35Qmj4JOKbQQZ4kbCutYCjWFJv2FX6Pg7Y544PTK1LZ2bmKozOVqmXDSvJfd%2FgJPlzUEUiU4ehQ%2Bqen6bj0%2BcdgJC9kY%3D" rel="nofollow">TC39 提案</a> ,看看下一代的 JavaScript 会包含哪些内容。</p><h3>总结</h3><p>Worker 是保证主线程 灵敏 和 流畅 的关键工具,它通过防止长时间运行代码阻塞浏览器渲染来保证这一点。由于和 Worker 通信 存在 内在的异步性,所以采用 Worker 需要对应用程序的架构进行一些调整,但作为回报,你能更轻松的支持各种性能差距巨大的设备来访问。</p><p>你应该确保使用一种 方便迁移代码的架构,这样你就能 测算 非主线程架构 带来的性能影响。Web Worker 的设计会导致一定的学习曲线,但是最复杂的部分可以被 Comlink 这样的库抽象出来。</p><hr><h3>FAQ</h3><p>总会有人提出一些常见的问题和想法,所以我想先发制人,将我的答案记录在这里。</p><h4><code>postMessage</code> 不慢吗?</h4><p>我针对所有性能问题的核心建议是:先测算!在你测算之前,没有快慢一说。但根据我的<a href="https://link.segmentfault.com/?enc=WASxn5GMzfj1WJhpsCix7g%3D%3D.WFQs8zswAm4tCaaVaRb9EQLuJ2DZ20ltQxe3ozDFX4HZ%2FiKeKIVM6ydKdcU9E1XRvT%2B6TsK6Dik%2FKLPfu%2Bsu%2B10wT3Bh26blg6aaMvapaFk%3D" rel="nofollow">经验</a>,postMessage 通常已经 “足够快” 了。这是我的一个经验法则:如果 <code>JSON.stringify(messagePayload)</code> 的参数小于 10kb,即使在速度最慢的手机上,你也不用担心会导致卡帧。如果 postMessage 真的成为了你应用程序中的瓶颈,你可以考虑下面的技巧:</p><ul><li>将你的任务拆分,这样你就可以发送更小的信息</li><li>如果消息是一个状态对象,其中只有很小一部分发生改变,那就只发送变更的部分而不是整个对象</li><li>如果你发送了很多消息,你可以尝试将多条消息整合成一条</li><li>最终手段,你可以尝试将你的信息转化为 数字表示,并转移ArrayBuffers 而不是 基于对象的消息</li></ul><h4>我想从 Worker 中访问 DOM</h4><p>我收到了很多类似这样的反馈。然而,在大多数情况下,这只是把问题转移了。你有也许能有效地创建第二个主线程,但你还会遇到相同的问题,区别在于这是在不同的线程中。为了让 DOM 在多线程中安全访问,就需要增加锁,这将导致 DOM 操作的速度降低,还可能会损害很多现有的 Web 应用。</p><p>另外,同步模型其实也是有优点的。它给了浏览器一个清晰的信号 — 什么时候 DOM 处于可用状态,能够被渲染到屏幕上。在一个多线程的 DOM 世界,这个信号会丢失,我们就不得不手动处理 部分渲染的逻辑 或是 什么其他的逻辑。</p><h4>我真的不喜欢为了使用 Worker 把我的代码拆分成独立的文件</h4><p>我同意。TC39 中有一些提案正在被评议,为了能够将一个模块内联到另一个模块中,而不会像 Data URL 和 Blob URL 一样有那么多小问题。虽然目前还没有一个令人满意的解决方案,但是未来 JavaScript 肯定会有一次迭代解决这个问题。</p><h3>补充总结说明</h3><h5>列举一些目前笔者使用Worker的场景:</h5><ol><li>当你的算法程序逻辑的时间相对长(超出了"帧预算"),且阻碍了渲染引擎。</li><li>当你想要尝试并发的设计模式</li><li>任务调度架构设计的调整(JS种种实现的调度机制可能并不是最优)</li><li>... 渲染和计算的完全解耦,计算要合理的拆分到Worker中</li></ol>
数据结构-使用JS实现链表-双向链表
https://segmentfault.com/a/1190000040409804
2021-07-27T14:37:52+08:00
2021-07-27T14:37:52+08:00
wlove
https://segmentfault.com/u/dxgl
1
<h2>前言</h2><p>上文中简单介绍了<a href="https://segmentfault.com/a/1190000040387082">数据结构-使用JS实现链表-单链表</a>;本篇作为一个续集出现.通过实现双向链表进一步加深对于链表的一些概念与实现。</p><blockquote>coding部分采取javascript进行实现。<strong>完整代码在末尾链接</strong></blockquote><h2>链表</h2><p><img src="/img/remote/1460000040409806" alt="image.png" title="image.png"></p><blockquote>双向链表也叫双链表,是链表的一种,它的每个数据<a href="https://link.segmentfault.com/?enc=RTq4k8CAcFmnW5osDsBQAg%3D%3D.1LaoT%2F%2B9V5FB%2FJs4Aw9NdsVXT3RUGl6y1wdKFeKE7IauqVI%2BSweNLRmkuVK7cFf8Eb399kbD%2FIcB2PAc7ykJDw%3D%3D" rel="nofollow">结点</a>中都有两个<a href="https://link.segmentfault.com/?enc=fp3HIHf%2Biycza4qqSZYlww%3D%3D.VBRaAKObkk2rCo72hYaW5QXwJi4rDvpGB09Rs%2BNQaxX0cF%2FsGEP14B5be5vjPYA7ORKhDtLRBH1DhdMSokDv6g%3D%3D" rel="nofollow">指针</a>,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向<a href="https://link.segmentfault.com/?enc=MqdHW4uH3zJ2zbJEaZQJsQ%3D%3D.6UWop390salccI%2F9D0NT2BuFm%2FN%2FLOD%2FfJmN5VZjCve2NecfU3N08GpYvHB33Up7CJZ05GJUCW2AnU01kS5FJuFu8G2K7UyBUG4qNijPyds%3D" rel="nofollow">循环链表</a>。【百度百科】</blockquote><h3>链表特点</h3><ul><li>用一组任意的内存空间去存储数据元素(这里的内存空间可以是连续的,也可以是不连续的)</li><li>每个节点(node)都由数据本身,<strong>一个指向上一个节点</strong>,一个指向后续节点的指针组成</li><li>整个链表的存取必须从头指针开始,头指针指向第一个节点。<strong>尾指针结束,尾指针指向最后一个节点。</strong></li></ul><blockquote>JS中并没有指针,上述节点的指针只是借用的C语言中的概念。</blockquote><h3>链表复杂度</h3><h4>时间复杂度</h4><table><thead><tr><th align="center">Access</th><th align="center">Search</th><th align="center">Insertion</th><th align="center">Deletion</th></tr></thead><tbody><tr><td align="center">O(n)</td><td align="center">O(n)</td><td align="center">O(1)</td><td align="center">O(1)</td></tr></tbody></table><h4>空间复杂度</h4><p><strong>O(n)</strong></p><h3>基础操作实现</h3><h4>节点结构</h4><pre><code>// 与单链表区别在于多了之前节点的指向
class DoublyLinkedListNode {
constructor(value, next = null, previous = null) {
this.value = value;
this.next = next;
this.previous = previous;
}
}</code></pre><h4>链表初始化</h4><pre><code>class DoublyLinkedList {
constructor() {
this.head = null;
this.tail = null; //多了尾部
}
}</code></pre><h4>Add 插入 (头部和尾部)</h4><pre><code>// 添加致头部
prepend(value) {
// 节点添加至头部
const newNode = new DoublyLinkedListNode(value, this.head);
// 如果有头部节点,那么设置为头部节点先前(previous)节点的引用
// 将头部节点设置为新节点
if (this.head) {
this.head.previous = newNode;
}
this.head = newNode;
// 如果没有尾部节点,把新节点设置为尾部节点.
if (!this.tail) {
this.tail = newNode;
}
return this;
}
// 添加致尾部
append(value) {
const newNode = new DoublyLinkedListNode(value);
// 同样判断 如果没有头部节点,那么将新节点设置为头部节点。
// 同时也是尾部节点
if (!this.head) {
this.head = newNode;
this.tail = newNode;
return this;
}
// 如果存在头部节点(当然也存在尾部节点)
// 将头部节点添加至链表的尾部
this.tail.next = newNode;
// 转换思考一下,当前新节点上一个个节点(previous)也就是当前this指向的尾部
newNode.previous = this.tail;
// 将新节点设置为链表的尾部
this.tail = newNode;
return this;
}</code></pre><h4>删除</h4><blockquote>为了阅读体验,此处部分非关键代码省略,可以去仓库进行查看。后面会贴链接。</blockquote><pre><code>delete(value) {
if (!this.head) {
return null;
}
let deletedNode = null;
let currentNode = this.head;
while (currentNode) {
if (currentNode.value === value) { // 仓库引入了一个比对js.逻辑很简单
deletedNode = currentNode;
// 想想其实就是处理3种情况
//1. 如果删除的节点是头部节点 需要做什么操作
//2. 如果删除的节点是尾部节点 需要坐什么操作
//3. 如果不是头部和尾部而是中间节点 需要做什么操作
if (deletedNode === this.head) {
// 如果删除的节点是头部节点
// 那么将头设置到第二个节点,该节点将成为新头
// 设置心头的previous为null
this.head = deletedNode.next;
if (this.head) {
this.head.previous = null;
}
if (deletedNode === this.tail) {
this.tail = null;
}
} else if (deletedNode === this.tail) {
// 如果删除是尾部节点,将tail设置为倒数第二个节点,这将成为新的tail
this.tail = deletedNode.previous;
this.tail.next = null;
} else {
// 如果要删除中间节点
const previousNode = deletedNode.previous;
const nextNode = deletedNode.next;
previousNode.next = nextNode;
nextNode.previous = previousNode;
}
}
currentNode = currentNode.next;
}
return deletedNode;
}</code></pre><h4>遍历链表</h4><blockquote>coding实现部分在删除中有体现,可自行提取。</blockquote><pre><code> // 思路简单来说就是value值比对
while(){
// 正向遍历,如果当前没有依次找next 直到尾部
// 反之 如果没有依次previous 直到头部。
}</code></pre><h2>结语</h2><p>做个简单总结,双向链表结构简单理解就是一个节点里面 包含当前value,上个previous, 下个next。 然后初始化就是一头一尾 head,tail。然后在这个基础结构进行增删改查。<br>慢慢理解之后,会感觉很简单。</p><blockquote>最后不要忘了要在实际场景中多加联系。</blockquote><h2>其他链接</h2><p><a href="https://link.segmentfault.com/?enc=7WjaLcn8iFHYNoTZCcrpNA%3D%3D.%2FORaW8kmiutZ1CXvMrKIfpq5rs81ZterF9%2BcXfAh9APLkw%2B8hEeUuEpO%2BgTAvkJLOVvOC6r0CUFUwH02P1SVX72zuYpQzNRKZ2DSgq1v6l7Y8S2rC%2BTBbvLW21RLOwdd2cglv98Xu8rWpH9Q8J1eUQ%3D%3D" rel="nofollow">上述示例代码仓库</a></p>
基于四叉树2D碰撞检测以及D3简单分析
https://segmentfault.com/a/1190000040406511
2021-07-26T23:00:09+08:00
2021-07-26T23:00:09+08:00
wlove
https://segmentfault.com/u/dxgl
2
<h2>前言</h2><p><a href="https://link.segmentfault.com/?enc=xrgiF6gDb63uS%2Bj827yOaA%3D%3D.vYkBN4MQBu565E%2FoG9kBzwWDJtpmcjBhV7oLoz2EPj4Gu%2F%2F08RUzktu2HMU2Or3O" rel="nofollow">《数据结构-使用JS实现四叉树》</a> 上文中简单介绍了四叉树的一些实现和应用场景 本篇文章应评论区各位小伙伴的留言 基于四叉树实现一下2D的碰撞检测。</p><p>话不多说开始今天的内容。</p><h3>首先看下quadtree测试效果图</h3><p><img src="/img/remote/1460000040406513" alt="quadtree.gif" title="quadtree.gif"></p><h3>正文coding部分</h3><h4>实现思路</h4><ul><li>数据结构采取四叉树</li><li><p>碰撞节点比对进行四叉树查找</p><blockquote>sample采取canvas2d进行绘制,JavaScript进行实现。</blockquote></li></ul><h4>实现过程</h4><h5>定义quadtree数据结构</h5><pre><code>/**
*四叉树构造函数
*@四叉树 2D类碰撞 需要四叉树边界/节点层数/
*@param{Rect}节点的边界({x,y,width,height})
*@param{number}[max\u objects=10]在拆分为4个子节点之前,节点可以容纳的最大对象数 默认:4
*@param{number}[max\u levels=4]根四叉树内的总最大级别 默认为 4
*@param{number}[level=0] 深度级别,子节点需要 默认:0 初始深度
*/
function Quadtree(bounds, max_objects, max_levels, level) {
this.max_objects = max_objects || 4;
this.max_levels = max_levels || 4;
this.level = level || 0;
this.bounds = bounds;
this.objects = [];
this.nodes = [];
};</code></pre><h4>添加objects/如果超出max_objects那么就拆分子节点</h4><pre><code>/*
*将对象插入节点。如果节点
*超过容量时,将拆分并添加所有
*对象到其相应的子节点。
*@param{Rect}要添加的对象的pRect边界({x,y,width,height})
*@memberof四叉树
*/
Quadtree.prototype.insert = function(pRect) {
var i = 0,
indexes;
//如果存在子节点,那么找到子节点像子节点添加数据(子节点就是子树)
if(this.nodes.length) {
indexes = this.getIndex(pRect);
for(i=0; i<indexes.length; i++) {
this.nodes[indexes[i]].insert(pRect);
}
return;
}
//如果没找到不存在nodes 向objects添加
this.objects.push(pRect);
// 如果objects超出最大max_objects 那么就拆分
// 开始
if(this.objects.length > this.max_objects && this.level < this.max_levels) {
//拆分子节点 (split方法可以移步仓库看代码,很简单。后续不贴了)
if(!this.nodes.length) {
this.split();
}
//将所有objects添加到对应的nodes
for(i=0; i<this.objects.length; i++) {
indexes = this.getIndex(this.objects[i]); //找到对应的nodes
for(var k=0; k<indexes.length; k++) {
this.nodes[indexes[k]].insert(this.objects[i]);
}
}
//清空这个objects
this.objects = [];
// 超出max_objects逻辑结束
}
};</code></pre><h4>获取</h4><pre><code>/**
*确定对象属于哪个节点
*@param{Rect}要检查的区域的pRect边界({x,y,width,height})
*@return{number[]}相交子节点的索引数组(0-3=右上、左上、左下、右下)
*@memberof四叉树
*/
Quadtree.prototype.getIndex = function(pRect) {
var indexes = [],
verticalMidpoint = this.bounds.x + (this.bounds.width/2), //x中心
horizontalMidpoint = this.bounds.y + (this.bounds.height/2); //y中心
// 判断属于哪个区域
var startIsNorth = pRect.y < horizontalMidpoint,
startIsWest = pRect.x < verticalMidpoint,
endIsEast = pRect.x + pRect.width > verticalMidpoint,
endIsSouth = pRect.y + pRect.height > horizontalMidpoint;
//右上 quad
if(startIsNorth && endIsEast) {
indexes.push(0);
}
//左上 quad
if(startIsWest && startIsNorth) {
indexes.push(1);
}
//左下 quad
if(startIsWest && endIsSouth) {
indexes.push(2);
}
//右下 quad
if(endIsEast && endIsSouth) {
indexes.push(3);
}
return indexes;
};</code></pre><h4>给定一个rect结构进行碰撞</h4><pre><code>/**
*返回所有可能与给定对象碰撞的对象
*@param{Rect}要检查的对象的pRect边界({x,y,width,height})
*@return{Rect[]}数组,包含所有检测到的对象
*@memberof四叉树
*/
Quadtree.prototype.retrieve = function(pRect) {
var indexes = this.getIndex(pRect),
returnObjects = this.objects;
//if we have subnodes, retrieve their objects
if(this.nodes.length) {
for(var i=0; i<indexes.length; i++) {
returnObjects = returnObjects.concat(this.nodes[indexes[i]].retrieve(pRect));
}
}
//objects去重
returnObjects = returnObjects.filter(function(item, index) {
return returnObjects.indexOf(item) >= index;
});
return returnObjects;
};</code></pre><h4>清除四叉树</h4><pre><code>/**
* 清除四叉树
* @memberof Quadtree
*/
Quadtree.prototype.clear = function() {
this.objects = [];
for(var i=0; i < this.nodes.length; i++) {
if(this.nodes.length) {
this.nodes[i].clear();
}
}
this.nodes = [];
};</code></pre><h3>quadtree其他相关仓库 <code>D3 Quadtree</code></h3><h4>拿D3来做一下学习,首先实现更为全面(可以看下面截图里具体的一个结构模块)有add cover,data,find.... 而且细节做的很棒,值得学习。下面简单举个例子</h4><p><img src="/img/remote/1460000040406514" alt="image.png" title="image.png"></p><blockquote><p>就拿一个模块来说: <code>find.js</code> 也是寻找相应的nodes 然后追加 同样的实现,区别在于可以理解为访问做了记录/不访问重复点,而且有就近访问原则(这个可以做一些其他工作)<code>i = (y >= ym) << 1 | (x >= xm)</code>,有兴趣的可以去学习一下。<br><img src="/img/remote/1460000040406515" alt="image.png" title="image.png"></p><p>总的来说D3内部存在一些能够帮助到你思维转变提升的东西,学习还是很要必要的。对了, 实践过程中可以尝试和d3-force模块结合使用,效果更佳。</p></blockquote><h2>结语</h2><p>四叉树作为树的一种结构,上面的例子很好的体现了四叉树在2D碰撞的实践,有任何疑问请随时留言。</p><p>最后~ 托更一天本人感到非常抱歉!!!</p><h2>其他链接</h2><p><a href="https://link.segmentfault.com/?enc=EdnKuqdKY0VExwx73PLtKg%3D%3D.M5%2BAnmInofALLgS8c4aezq3oYKJU6iah77jJyT86R1nKKr8RiMw9xO1iEKLUIVlJBHf28mZfGt6kMsyAqyqFJey183MRMJ8%2F0tUJkixWQ%2BQ%3D" rel="nofollow">上述示例代码仓库</a></p><p><a href="https://link.segmentfault.com/?enc=ig1VFLU2i7GYfyD4v7tVZQ%3D%3D.TpMEqb2s7aFZCnhGch8BwpkNZ%2Ba3jZdyzobX7fxB5fl6N6IdDarM99RvHXfyw6nZ" rel="nofollow">D3 Quadtree</a></p>
数据可视化之D3JS 不完全使用指南
https://segmentfault.com/a/1190000040387103
2021-07-22T14:27:06+08:00
2021-07-22T14:27:06+08:00
wlove
https://segmentfault.com/u/dxgl
2
<h2>背景</h2><p>本篇收录于<a href="https://link.segmentfault.com/?enc=lTzcEcPT2xYJfmXOGg8PHg%3D%3D.HMS5e4T0oVnwca98k1AX0jTj%2FHq9a3ScrbqMyOFI2mPb%2FU7XNwEtLr3fY5QJJz3G" rel="nofollow">《数据可视化和图形学》</a>专栏</p><p><a href="https://link.segmentfault.com/?enc=59Iteku7CrPaOxhG6jzXBg%3D%3D.kZHeFWd%2BAwEuu5%2FE0x9EY9QjHEG3yWGyjgKwPq896NW%2FHyvkz%2Bfjd%2FFJNJ4ETkqL" rel="nofollow">上文介绍了纹理相关的内容,并使用WebGL进行了3d的实现</a>,emm.....<br>本篇文章呢算是一个新方向,也算探索写作的一个思路。(应着群内小伙伴的需求来讲讲框架的日常使用) 当然理论知识肯定是依旧会穿插进来。</p><p>写作方式会从学习(调研),使用,深入进行混讲。 其实就是我日常使用某一个(框架)库的基础流程分享出来希望帮助到大家。</p><p>在开始前简单介绍下今天的主角<code>D3JS</code>(下面简称D3)</p><ul><li>D3全称 Data-Driven Documents 3个D开头的单词也是它D3简写的由来(不要有的人用了半天不知道这名字怎么来的 不要当假粉 哼~)</li><li><p>D3的优势 :</p><ol><li>明星级别库毋庸置疑 github的star 将近98k。那么可想而知用户 社区必然是完备的。</li><li>D3是灵活的 各模块可拆分可组合。</li><li>渲染方面设计优秀,关注的是shape,scale基础图元。不是特定图表的配置化。这样灵活性更高</li><li>animation(动画),interaction(交互)效果很优秀。</li><li>支持多种渲染模式,svg,canvas 很多...</li></ol></li><li>图表种类,图表示例很全很全,所以日常开发可能都不需要基础模块的组合 示例教程在<a href="https://link.segmentfault.com/?enc=g4n%2Bj7cuvQpnF3C6chPguA%3D%3D.f%2FnDd5PoU3W38m8IwYy54xQM2FRplNouoq%2B8qggDPyfLEqo2N7A93ZP8j2wEtzrW" rel="nofollow">Observable</a>。</li></ul><blockquote>今天对于如何学习D3,使用D3,深入D3做一次浅显的探讨。 follow me!</blockquote><h2>本篇大纲</h2><ol><li>学习库阶段(调研阶段)</li><li>使用库阶段(日常开发阶段)</li><li>深入库阶段(学习框架,或者新需求在框架源码上进行定制化。)</li></ol><h3>学习库(调研库)</h3><h4>从几个方面进行调研考察</h4><ol><li>首先看库是否满足业务开发和业务拓展</li><li>看下该库更新的一个频率,稳定性问题。( 当然有实力团队也可以clone一份当前使用版本,方便后续的维护。)</li><li>该库社区是否活跃 方便后续问题探讨跟进 (如果team有足够示例 可以不care这点)</li><li><del>对于是否与现有主框架(主要技术栈)兼容问题,这个如果一个库满足以上几点。应该问题不大(也可以列入考察中)</del></li></ol><h4>按上述步骤看看<code>D3</code>怎么说</h4><blockquote>1.首先看库是否满足业务开发和业务拓展</blockquote><p><strong>D3支持SVG(HTML)/Canvas的渲染模式,简单来说一些适合的抽象业务场景</strong></p><ul><li>日常图表开发种类丰富,有关系类,统计类,简单地图类...</li><li>数据量大但是属于静态类分析(只加载一次)可选择SVG渲染</li><li>数据量大而且属于高频交互 可以选择Canvas渲染(如果数据量海量。从交互,渲染等方面优化 甚至需要换渲染方式)</li><li>图表渲染前,中,后需要酷炫的动画效果。</li><li>...</li></ul><blockquote>第2,3点问题呢 贴几个图吧<br><strong>commit的一个情况和issues反馈情况</strong></blockquote><p><img src="/img/remote/1460000040387106" alt="image.png" title="image.png"></p><p><img src="/img/remote/1460000040387108" alt="image.png" title="image.png"></p><hr><p><strong>从成员以及提交曲线来看(截图不全 可以去github查看 还有这个是近一年的,可能不太准 bug太少了哈哈)</strong></p><p><img src="/img/remote/1460000040387111" alt="image.png" title="image.png"></p><h3>使用库阶段(日常开发阶段)</h3><h4>存在几种情况</h4><ol><li>有现成的例子且完全满足开发需求</li><li>无现成的例子但是可以通过几个例子混合使用达到效果</li><li>自己动手利用API使用 并深入研究,在库不满足需求情况下进行定制化开发。</li></ol><blockquote>首先很多人都处于第1种和第2种情况一直徘徊.这个原因说直白点还是代码搬运工的水平. 当然这个更多属于深入模块 只有知道它是什么才能发挥更大的优势。这方面的分享可能目前来说较少</blockquote><h5>拿一个图表来做示例。</h5><p><img src="/img/remote/1460000040387113" alt="image.png" title="image.png"><br>实现上图只需要下面几个步骤</p><ul><li>需要限制画布的一些属性,宽高</li><li>需要绘制2个刻度尺, 通过数据计算min,max</li><li>需要绘制柱状图形 ,数据绑定</li></ul><blockquote>需要限制画布的一些属性,宽高</blockquote><pre><code>
var width = 1000;
var height = 500;
var margin = ({
top:20,
right:0,
bottom:30,
left:40
})
const svg = d3.select("body").append("svg");
</code></pre><blockquote>需要绘制2个刻度尺。</blockquote><pre><code>var x =
d3.scaleBand()
.domain(data.map(d=>d.name))
.range([margin.left,width-margin.right])
.padding(0.2)
var y =
d3.scaleLinear()
.domain([0,d3.max(data,d=>d.value)]).nice()
.range([height - margin.bottom,margin.top])
svg.append("g")
.attr("class","x-axis")
.attr("transform",`translate(0,${height- margin.bottom})`)
.call(d3.axisBottom(x).tickSizeOuter(0))
// 设置x轴刻度
svg.append("g")
.attr("class","y-axis")
.attr("transform",`translate(${margin.left},0)`)
.call(d3.axisLeft(y)) //y轴坐标刻度
</code></pre><blockquote>需要绘制柱状图形</blockquote><pre><code>svg.append("g")
.attr("class","bars")
.attr("fill","skyblue")
.selectAll("rect")
.data(data)
.join("rect")
.attr("x",d=>x(d.name))
.attr("y",d=>y(d.value))
.attr("width",x.bandwidth())
.attr("height",d=>y(0) - y(d.value))
</code></pre><blockquote>上面这个简单的例子可以反映出来你使用D3的基本流程 首先你要明白你的画布是如何限制的。然后呢你要想你画什么。 其次就是把数据对应的加上去。<br><strong> 当然很重要的一点要学习它的语法 转变你编码的思维。 非结构化的编程过程。</strong></blockquote><h3>深入库阶段(学习框架,或者新需求在框架源码上进行定制化。)</h3><blockquote>首先先不考虑进行定制化开发,本篇首先去了解下它内部的实现。</blockquote><p><img src="/img/remote/1460000040387114" alt="image.png" title="image.png"></p><p>打开<a href="https://link.segmentfault.com/?enc=PuL2K74UdmjbIEHDbDvmSQ%3D%3D.n3ciI70ovhwCdE4aiJJmqdgYIKP8DmaGt2KKdpNEkhI753S3%2BldrdCyLMyDRPKVo" rel="nofollow" title="D3的仓库">D3的仓库</a>,阅读源码你会发现这个情况</p><ol><li>d3-array 数组</li><li>d3-axis 坐标轴</li><li>d3-brush</li><li>d3-color 颜色</li><li>...</li></ol><p>其实这个跟它的设计是有关系的。是把能拆的部分尽可能的拆出来,这样的优势在于你如果想与目前使用的库进行一些整合,非常简单。这也是D3火的原因。本篇不打算拆开具体模块进行展开。基本我目前用到的可能 drag(拖拽计算), quadtree(四叉树) 还有一些layout算法... 其实也并不是很多。有兴趣可以留言 专门拿出几篇来写这个《讨论如何在底层进行定制化》。</p><h4>友情链接</h4><ul><li><a href="https://link.segmentfault.com/?enc=1Dx7Llh%2FHZi%2F6bZTiYlOIQ%3D%3D.GN5vZBZ2Jq%2Fh953jy1vCJr6tGwDuTPRCozNLat7ktLU%3D" rel="nofollow" title="D3地址 https://github.com/d3/">D3仓库地址</a></li><li><a href="https://link.segmentfault.com/?enc=2G3oGX4NqZdqVKgPMPpRig%3D%3D.8kXGhWD4I0WKoZpaWgEfjfze60KNZExX93gkVeoOBd3cKeAdTsGtFopKUYH9loxiETnFPq%2FyxjBhy9qz%2BXd5Xw%3D%3D" rel="nofollow">mdn文档</a></li><li><a href="https://link.segmentfault.com/?enc=R79uqNUpzFoeEYMj%2BPWzqw%3D%3D.nycjgmdQXnU%2FXIhOnYWVhCmH6XD9aFKVHzUF87Rqm9BONhPs8Kdafd5qFguhByWl" rel="nofollow">示例代码仓库</a></li></ul><h2>最后</h2><p>关于图形学/数据可视化,怎么学习的方向问题呢,我也属于摸索。或许等我写完这个系列能够更好的解答大家。</p><blockquote>请大家多提提意见,不吝赐教指正。 有任何希望分享的地方只要我了解或者我可以学来分享。 (当然学不会肯定不会丢人现眼的呢 哈哈)</blockquote>
数据结构-使用JS实现链表-单链表
https://segmentfault.com/a/1190000040387082
2021-07-22T14:26:03+08:00
2021-07-22T14:26:03+08:00
wlove
https://segmentfault.com/u/dxgl
3
<h2>链表</h2><p><img src="/img/remote/1460000040387084" alt="link_list.svg" title="link_list.svg"></p><blockquote>链表是一种物理<a href="https://link.segmentfault.com/?enc=D9zzglqyYjFvJ187NpYezA%3D%3D.qMgnrzY%2F1VHMy8S4QI5qqofyg9HP%2FxGxoRUB6cHj4IMBZphqvLh%2FJlAJFbBJFshdQdGuPF8ztenc451FFm2Ca9TapgpYCP64h12UiIizlpc%3D" rel="nofollow">存储单元</a>上非连续、非顺序的<a href="https://link.segmentfault.com/?enc=58jgJ7So%2BGMSEmQmSgGQdQ%3D%3D.NkecJvQaROVkyJO%2BU9xFPfOrrDhiblENSD1nA7mLKKneUrol4sCngYrrklGoQjGAyfa%2FvAOxB%2BjC8mFV5DS9bfEeB%2FqnFz8qI5a61jcBS%2F0%3D" rel="nofollow">存储结构</a>,<a href="https://link.segmentfault.com/?enc=N9IOqE65SELkhFF3R2UTIg%3D%3D.9G2mM2TRoknvo%2FgM%2B1vJsnN4IUhHVxOeAXb%2B6OwaO9%2BNHXIqjs%2FGgZrd8Dj6Sp5wMODUpxwzEIgWs24CWxJpw2TP2y6aKHJIfDbYCOBb4Ts%3D" rel="nofollow">数据元素</a>的逻辑顺序是通过链表中的<a href="https://link.segmentfault.com/?enc=dih46ieBboNecee4z6ywRg%3D%3D.lQnICIYuw4BmdBHOofbtFzT3LlO2T1WDYkhVij%2Fv13r%2FXacUgde45LSyfHDQHkoFadwK13pOLxY4KxvSo7Hlmg%3D%3D" rel="nofollow">指针</a>链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储<a href="https://link.segmentfault.com/?enc=xH73Fyy9Jzt84UsftoiPqA%3D%3D.7tw5qyjzpEZHFvOQhGDdC5Q%2FGNo%2FQdxFx4YnwiAkt6D53t6F9XgPhqujhYIaAgGSdrRUoQ3MMCOi2zN%2BizWXg2RgU0gmZ1Zo2SCUHn%2B%2FGXs%3D" rel="nofollow">数据元素</a>的数据域,另一个是存储下一个结点地址的<a href="https://link.segmentfault.com/?enc=oY9NEZ5i3QUydSPWZPowfw%3D%3D.NmE2ZfSgQgMNMZcoDitvM0v6kBqE2G6h3H3Ejz0iIuiA8uwe013KIUEPkk8T85Um8%2FlEGxeZiQ85XMs8GLHT1w%3D%3D" rel="nofollow">指针</a>域。【来源百度百科】</blockquote><h3>链表特点</h3><ul><li>用一组任意的内存空间去存储数据元素(这里的内存空间可以是连续的,也可以是不连续的)</li><li>每个节点(node)都由数据本身和一个指向后续节点的指针组成</li><li>整个链表的存取必须从头指针开始,头指针指向第一个节点</li><li>最后一个节点的指针指向空(NULL)</li></ul><blockquote>JS中并没有指针,上述节点的指针只是借用的C语言中的概念。</blockquote><h3>链表复杂度</h3><h4>时间复杂度</h4><table><thead><tr><th align="center">Access</th><th align="center">Search</th><th align="center">Insertion</th><th align="center">Deletion</th><th> </th></tr></thead><tbody><tr><td align="center">O(n)</td><td align="center">O(n)</td><td align="center">O(1)</td><td align="center">O(1)</td></tr></tbody></table><h4>空间复杂度</h4><p><strong>O(n)</strong></p><h3>单向链表的实现</h3><h5>链表中的几个主要操作</h5><ul><li>初始化链表/节点</li><li>头部插入节点</li><li>搜索/遍历节点</li><li>删除节点</li><li>...</li></ul><p><strong>初始化链表中的节点</strong></p><pre><code>class LinkedListNode {
constructor(value, next = null) {
this.value = value;
this.next = next; // 初始化为null
}
}</code></pre><p><strong>初始化单向链表</strong></p><pre><code>class LinkedList {
constructor() {
// 初始空指针
this.head = null;
}
}</code></pre><p><strong>头部追加节点(append)</strong></p><pre><code>append(value) {
// 新建节点
const newNode = new LinkedListNode(value, this.head);
// 将值赋予head 为了下次新建
this.head = newNode;
}</code></pre><p><strong>删除节点</strong></p><pre><code>delete(value) {
// 如果不存在节点 return null
if (!this.head) {
return null;
}
// 如果删除的是第一个节点
let deletedNode = null;
// 如果删除的第一个元素
while (this.head && this.head.value === value) {
deletedNode = this.head;
this.head = this.head.next;
}
// 非第一个节点,遍历查找
let currentNode = this.head;
if (currentNode !== null) {
while (currentNode.next) {
if (currentNode.next.value === value) {
deletedNode = currentNode.next;
currentNode.next = currentNode.next.next;
} else {
currentNode = currentNode.next;
}
}
}
return deletedNode;
}</code></pre><h2>结语</h2><p>做个简单总结,链表结构简单理解就是一个节点里面 包含当前value,以及下个next。 然后初始化就是一个head。同样的结构依次去追加。理解的话会感觉很简单,当然链表远不止这些,还有双向链表,操作还有链表反转(reverse),删除(头,尾),转换数据结构等等。</p><p>下期见。</p><h2>其他链接</h2><p><a href="https://link.segmentfault.com/?enc=geVO4eoQLcGk1cSVEdhYdw%3D%3D.NRRV8HE5QPIzW4C20Xc1B4fKBtzAoJKKeW3xf7qLWVOhkkA8A7dNdW6ljQmILMRYHVozPZQ7HJeULWHHguyt9LF6Yl0rgWpmFi6TBMnEdnfKJUSp%2B%2BGlAK5anOHBsTAj3dmMM8WMmislbxDqgCJlWg%3D%3D" rel="nofollow">上述示例代码仓库</a></p>
来看看 新出的前端之《进阶不完全指南》专栏是干嘛的
https://segmentfault.com/a/1190000040369622
2021-07-19T17:47:53+08:00
2021-07-19T17:47:53+08:00
wlove
https://segmentfault.com/u/dxgl
1
<h2>背景</h2><p>本篇收录于<a href="https://link.segmentfault.com/?enc=jrzrnWzKqFn%2BLqN0rCvLOA%3D%3D.6QZyFqM70fBrMRTp%2FEfQm9ZoK4QtesrXJEp0jjWS%2FmtWRtn5%2F6s%2BLxHOhnX%2Bd59K" rel="nofollow">《进阶不完全指南》</a>专栏</p><p>按照以往的系列文章的惯例, 第一篇一般是一个背景介绍和大纲提炼。本系列照旧(写作能力在这 没办法。)</p><p>首先 表达下我的写作目的:</p><ol><li>分享给我的组员和朋友,把答应人的事情完成,无愧无悔 足矣!</li><li>想把进阶路线系统化。当然写作过程中无法避免存在碎片化内容 但是后续如果写的内容足够多 我也会进行凝练汇总(后话,后话 到那一步再说)。愿景: 能够帮助后来者少走几步弯路 足矣!</li><li>希望在面临一些职业决策的话题上能够给到你一定的建议,写作中难免唠叨,瞎扯 愿只言片语的碎话能够引起丝丝共鸣, 足矣!</li><li>提升自己的分享能力,写作能力,共情能力(与时间为伴,与读者为伴) 足矣!</li><li><strong>主要还是把之前备忘录零碎写的 摘抄的 总结的 全部梳理一次。</strong></li></ol><blockquote>根基不稳(内乱不平), 何筑万地高楼(何以开疆拓土).</blockquote><h2>专栏介绍</h2><p>本来呢,这篇专栏是只写数据结构与算法的,但是今天清晨 突然有个神秘的声音告诉我 你写数据结构与算法是不可行的,(我呢属于听话型) 答道: 好的,我抓紧去改一下。(<del>细想下也不无道理 毕竟创建那么多专栏万一发错了多尴尬。</del> )<strong>经过良久思想斗争, 就叫它 <a href="https://link.segmentfault.com/?enc=nnWYolh8f0TyctuYbLY1Jw%3D%3D.p9NksEKM2BwMFynKwfmAF%2FXAUelRm5ZwrF86Kp483p2Or1tDSQbq8SkTPT8PQzHb" rel="nofollow">《进阶不完全指南》</a>吧</strong></p><h3>内容模块</h3><p>语言差异再大,落在底层无非也就是系统磁盘读写、内存分配、cpu分配、垃圾回收,硬件指令...这套东西,实现千差万别,思想大同小异。</p><p>清楚操作系统/运行原理,你就知道原来语言某些目的上是不谋而合的。(例如javascript的容器v8=>浏览器)</p><p>搞清楚数据结构,你就知道为什么所有语言都会有数组、链表、哈希,什么时候该怎么用。</p><p>搞清楚算法,你会发现你写代码时 如有神主.思考问题的角度都会不一样。</p><p>搞清楚设计模式,你去看绝大部分框架原理的时候效率都会成倍增长。</p><blockquote>本系列都会真针对数据结构/算法/设计模式进行介绍。</blockquote><h4>数据结构</h4><p>首先基础的数结构包含哪些这边简单列举介绍一下,后面会逐步展开进行讨论。</p><ul><li><strong>数据结构(Data Structures)</strong></li><li><ol><li><strong>数组(array)</strong>: 数组是可以再内存中连续存储多个元素的结构,在内存中的分配也是连续的,数组中的元素通过数组下标进行访问。</li></ol></li><li><ol start="2"><li><strong>链表(linked list)</strong>: 链表是物理存储单元上非连续的、非顺序的存储结构,数据元素的逻辑顺序是通过链表的指针地址实现,每个元素包含两个结点,一个是存储元素的数据域 (内存空间),另一个是指向下一个结点地址的指针域。根据指针的指向,链表能形成不同的结构,例如单链表,双向链表,循环链表等。</li></ol></li><li><ol start="3"><li><strong>栈(stack)</strong>:栈是一种特殊的线性表,仅能在线性表的一端操作,栈顶允许操作,栈底不允许操作。 栈的特点是:先进后出,或者说是后进先出,从栈顶放入元素的操作叫入栈,取出元素叫出栈。也就是具有两种主要操作:<strong>push</strong>, 添加元素到栈的顶端(末尾);<strong>pop</strong>, 移除栈最顶端(末尾)的元素。</li></ol></li><li><ol start="4"><li><strong>队列</strong> 队列与栈一样,也是一种线性表,不同的是,队列可以在一端添加元素,在另一端取出元素,也就是:先进先出。从一端放入元素的操作称为入队,取出元素为出队。</li></ol></li><li><ol start="5"><li><strong>堆</strong> 堆是一种比较特殊的数据结构,可以被看做一棵树的数组对象。</li></ol></li><li><ol start="6"><li><strong>树</strong> 树是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合。</li></ol></li><li><ol start="7"><li><strong>散列表(hash table)</strong> 散列表,也叫哈希表,是根据关键码和值 (key和value) 直接进行访问的数据结构,通过key和value来映射到集合中的一个位置,快速找到集合中的对应元素。</li></ol></li><li><ol start="8"><li><strong>图(graph)</strong> 图是由结点的有穷集合V和边的集合E组成。实现方式有矩阵,邻接表等等</li></ol></li></ul><blockquote>以上只是内容概览,后续都会逐步展开介绍数据结构。 data<del>~~ Structures</del>~~</blockquote><h4>算法(algorithms)</h4><ul><li><li><strong>搜索查询类(Searches)</strong></li><li><ol><li>BFS 广度遍历</li></ol></li><li><ol start="2"><li>DFS 深度遍历</li></ol></li><li><ol start="3"><li>...</li></ol></li><li><strong>排序类(Sorting)</strong></li><li><ol><li>BubbleSort 冒泡排序</li></ol></li><li><ol start="2"><li>Quicksort 快速排序</li></ol></li><li><ol start="3"><li>...</li></ol></li><li>其他类...</li></ul><blockquote>以上只是内容概览,后续都会逐步展开 会利用上面的一些数据结构 algorithms~~~</blockquote><h4>设计模式(design_patterns)</h4><p>设计模式来源于众专家智慧的结晶(不用是损失), 设计模式兼顾了系统的可重用性和可扩展性。设计模式提供了一套通用的设计词汇和一种通用的形式更有效提高内部沟通。</p><p>面向对象设计模式</p><ul><li>创建型模式</li><li>结构型模式</li><li>行为型模式</li><li>...</li></ul><p>函数式设计模式</p><ul><li>改进版OOP观察者模式</li><li>...</li></ul><p>JS中的设计模式</p><ul><li>单例模式</li><li>策略模式</li><li>...</li></ul><blockquote>以上内容会在后续展开,所有提到的内容都会在本专栏体现。目的就是要进阶.就是神来了也挡不住我。我说的!design~~~~</blockquote><h3>注意模块 (请看一下导读内容/达成一定共识)</h3><ol><li>本系列内容coding部分采取<strong>JavaScript。</strong>;</li><li>本篇所有coding部分会在优化后上传github; 文中示意代码仅供逻辑参考。</li><li>注意因coding部分采取高级语言进行编写.存在编译时间等客观因素.在复杂度上带有不准确性,<strong>领会思路为主要目的。</strong></li><li>注意因coding部分采用javascript进行编写,基础数据结构实现上并不具有"真实"性(例如js中数组的实现存在快数组和慢数组(hashtable)多重形式)。</li><li>相关代码,测试代码可参考后续的git仓库说明。</li><li>系列文章内容会对于github现有仓库进行参考,后续每篇都会贴出参考链接。</li></ol><h2>最后</h2><p>请不要质疑我专栏更新的速度,因为要彼此相信(我相信你不相信。哈哈哈)。</p><p>总之我保证各专栏每周1篇以上的产出 。每一篇的背后我需要做一些工作: coding & coding test & 文章内容的完善。 请求来一个赞。</p><p><strong>还有一些希望:</strong></p><p>希望在写的过程中能为我带来不一样的思考方式。 共情能力。</p><p>希望在写的过程能为大家带来一些什么。 共进原则。</p><p>希望明天是晴天!</p>
图形学之WebGL实现3D效果/ 纹理的使用
https://segmentfault.com/a/1190000040364334
2021-07-18T17:45:29+08:00
2021-07-18T17:45:29+08:00
wlove
https://segmentfault.com/u/dxgl
1
<h2>背景</h2><p>本篇收录于<a href="https://link.segmentfault.com/?enc=%2FrWWlCWELkP9COAwI9dRuw%3D%3D.TvwwLxofzLsFshjCashzL5pfNFmdnluHhZMbtXD5FY7FTEDERFVxahlIs24Aahgr" rel="nofollow">《前端读书会》</a>专栏</p><p>上文介绍了纹理相关的内容,并使用WebGL进行了2d的实现,今天延续上节课的内容对于纹理的3D实现 也算是吸取了MDN-Tutorial的模式,感觉这样的学习轨道 也算是初衷(<del>每次长篇大论的讲图形学的理论我怕我和你都坚持不下去</del>)目前来说最好的方式了,对于WebGL的学习还是图形学的学习都能有一个认识。话不多说开始今天的内容(大部分是code的讲解 尽可能快速过完一些基础的API...第一步成为一个API工程师)</p><blockquote><a href="https://segmentfault.com/a/1190000040335519">上文介绍了纹理相关的内容,有需要可以参考上文</a></blockquote><h3>本篇大纲</h3><ol><li>2D与3D的区别?绘制思路?</li><li>coding (WebGL中简单使用纹理)</li></ol><blockquote>coding部分引入webgl-utils(方面一些useprogram,linkProgram的简写 无太大魔力)&martix(矩阵的一些生成/计算..)工具类库</blockquote><h3>1.2d与3d的区别</h3><p>首先我们都知道2D只是一个平面 3D可以理解为是6个平面,那么本篇文章最主要解决如何给添加5个平面就好(当然添加会伴随着一些问题,没关系 一步步的来)</p><p>本篇会对于上文的一些反馈进行改正 例如code 注释太少。。ok 我尽量多写点</p><h4>WebGl coding 首先回顾一下步骤</h4><ol><li>shader</li><li>创建program</li><li>处理坐标还有纹理相关</li><li>获取并绑定坐标信息</li><li>绘制draw</li></ol><blockquote>看着是如此简单 那么接下来我们一起来写一下</blockquote><h3>coding</h3><p>coding开始之前先看看效果</p><blockquote>缺一个gif图 明天找个有电脑的地方补充一下。</blockquote><p><img src="/img/remote/1460000040364336" alt="image.png" title="image.png"></p><p><img src="/img/remote/1460000040364337" alt="image.png" title="image.png"></p><h5>1. shader</h5><blockquote>有朋友反馈shader部分要照顾新手, 那么我逐行加下注释介绍下。当然更多细节信息还是得去看看相关资料呢。 我讲的不好(-- 主要是懒癌患者)</blockquote><pre><code>// vertex shader
// attribute变量 只能作用于顶点着色器 vec4 是矢量 a_position 一个变量名
attribute vec4 a_position;
// attribute变量 只能作用于顶点着色器 vec4 是矢量 a_position 一个变量名
attribute vec2 a_texcoord;
// uniform变量(全局) 可以在顶点和片元使用(如果俩个着色命名同名 可进行共享) mat4 是矩阵 u_matrix变量名
uniform mat4 u_matrix;
// varying变量(全局) 目的就是为了给片元传输数据 vec2 矢量 v_texcoord变量名
varying vec2 v_texcoord;
// main类似主执行函数 但是是必须的 C/C++用法类似
void main() {
// gl_Position 顶点坐标
gl_Position = u_matrix * a_position;
// 将纹理坐标传给片断着色器
v_texcoord = a_texcoord;
}
// fragment shader
// 精度限定 mediump中度精度 float数据类型 对了SL语言中是区分整型和浮点型的哦~
precision mediump float;
// varying变量(全局) 接收传输过来的数据 vec2 矢量 v_texcoord变量名
varying vec2 v_texcoord;
// uniform变量(只读 全局) sampler2D纹理 u_texture变量名
uniform sampler2D u_texture;
// 主执行函数
void main() {
// gl_FragColor 颜色信息 此处是纹理 texture2D是取样器纹理 v_texcoord纹理坐标 看js代码穿插理解
gl_FragColor = texture2D(u_texture, v_texcoord);
}</code></pre><p><strong>我这个注释写的 我都佩服。哈哈 不懂记得留言。我都让你们问,你们也不问。要知道不会就问没什么不好意思的 学会了那是自己的 强大自己升职加薪 加油</strong></p><h5>2. 创建program</h5><pre><code>// 用到了webgl-Utils 其实就是useVertex 和useFragment 然后linkProgram...
var program = webglUtils.createProgramFromScripts(gl, ["vertex-shader-3d", "fragment-shader-3d"]);</code></pre><h5>3. 处理坐标还有纹理相关</h5><pre><code>// 顶点坐标 发生了变换 6个面
var positions = new Float32Array(
[
-0.5, -0.5, -0.5,
-0.5, 0.5, -0.5,
0.5, -0.5, -0.5,
-0.5, 0.5, -0.5,
0.5, 0.5, -0.5,
0.5, -0.5, -0.5,
-0.5, -0.5, 0.5,
0.5, -0.5, 0.5,
-0.5, 0.5, 0.5,
-0.5, 0.5, 0.5,
0.5, -0.5, 0.5,
0.5, 0.5, 0.5,
-0.5, 0.5, -0.5,
-0.5, 0.5, 0.5,
0.5, 0.5, -0.5,
-0.5, 0.5, 0.5,
0.5, 0.5, 0.5,
0.5, 0.5, -0.5,
-0.5, -0.5, -0.5,
0.5, -0.5, -0.5,
-0.5, -0.5, 0.5,
-0.5, -0.5, 0.5,
0.5, -0.5, -0.5,
0.5, -0.5, 0.5,
-0.5, -0.5, -0.5,
-0.5, -0.5, 0.5,
-0.5, 0.5, -0.5,
-0.5, -0.5, 0.5,
-0.5, 0.5, 0.5,
-0.5, 0.5, -0.5,
0.5, -0.5, -0.5,
0.5, 0.5, -0.5,
0.5, -0.5, 0.5,
0.5, -0.5, 0.5,
0.5, 0.5, -0.5,
0.5, 0.5, 0.5,
]);
// 解决图片跨域问题 当然方式很多你也可以绘制canvas2d 然后创建纹理 canvas在标准上支持了图片跨域 有兴趣可以查查
function requestCORSIfNotSameOrigin(img, url) {
if ((new URL(url, window.location.href)).origin !== window.location.origin) {
img.crossOrigin = "";
}
}
// 加载图片 并在load完成绑定纹理
function loadImageAndCreateTextureInfo(url) {
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([0, 0, 255, 255]));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
var textureInfo = {
width: 1,
height: 1,
texture: tex,
};
var img = new Image();
img.addEventListener('load', function() {
textureInfo.width = img.width;
textureInfo.height = img.height;
gl.bindTexture(gl.TEXTURE_2D, textureInfo.texture);
// 调用 texImage2D() 把已经加载的图片图形数据写到纹理
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
});
requestCORSIfNotSameOrigin(img, url);
img.src = url;
return textureInfo;
}
var texInfo = loadImageAndCreateTextureInfo('https://webglfundamentals.org/webgl/resources/leaves.jpg');
</code></pre><h5>4. 获取并绑定坐标信息</h5><pre><code> var positionLocation = gl.getAttribLocation(program, "a_position");
var texcoordLocation = gl.getAttribLocation(program, "a_texcoord");
var matrixLocation = gl.getUniformLocation(program, "u_matrix");
var textureLocation = gl.getUniformLocation(program, "u_texture");
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
setGeometry(gl);
var texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
setTexcoords(gl);
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([0, 0, 255, 255]));</code></pre><h5>5. 使用program并提供buffer取数据 然后绘制</h5><pre><code>gl.vertexAttribPointer(
positionLocation, size, type, normalize, stride, offset);
// 启用texcoord属性
gl.enableVertexAttribArray(texcoordLocation);
// 绑定texcoord buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
....
gl.drawArrays(gl.TRIANGLES, 0, 6 * 6);
</code></pre><blockquote><p>ok, 效果就这样实现了.为了大家的练习, 说明一下需要注意的地方是上面的代码 纹理是采用了一个纹理 如果要改多个图片可以采取多个纹理。也可以合并成一个纹理 通过坐标信息进行调节。</p><p><a>全部代码请去github下载并使用</a></p></blockquote><h2>最后</h2><blockquote><p>最后强烈希望大家学习相关理论知识;理论可能日常用到的地方很少,但是它能决定你走多远。(有的人问难怎么办,勤于练习吧),写作速度呢 该专栏我加速(一周1-2篇) 其他专栏(1篇) 计算机图形学相关的基础知识都会带一遍。然后后续主要写数据可视化方向。</p><p>下一篇写点什么呢? 个人准备把<a href="https://link.segmentfault.com/?enc=9z5nkm7LWJTs%2FipZsmQdgg%3D%3D.Zj81QcsDGaLScHufJfVO%2F667n0jJfsc%2BItyKsaRX5M9Wd7%2Bn%2BOcEZsqO1teTzQS02v5w0eRtON4DGz%2F7aClwDUpcEGzaEuJVS9zbndM3GtzcqeKp5GEJUChJjxd%2FeLZO" rel="nofollow">MDN-Tutorial</a>部分的内容结个尾。然后开始正式的可视化环节(可以介绍几个大家常用的库 2D 3D的都可以,大家看看有什么好用的库需要我一起过一下的呢)。或者大家提提意见!</p></blockquote>
分享几个细节希望能够帮助你提升coding level
https://segmentfault.com/a/1190000040342887
2021-07-14T10:00:00+08:00
2021-07-14T10:00:00+08:00
wlove
https://segmentfault.com/u/dxgl
2
<h2>背景</h2><p>随着业务/人员日益增加,需要在团队内把coding review环节重新拾起来。所以在新老项目都做出了择优抽样分析审查(选择方面从可塑性,后期迭代多方面进行考虑)。确确实实暴露出了一些问题。本篇文章就是把这些问题分享出来,语言参考方面不仅仅局限于JavaScript(存在共同性都可以进行参考)。</p><p>这边大概列举一些大方向</p><ol><li>工程架构/设计思想</li><li>编码规范 各有特色</li><li>技术栈问题混乱</li></ol><h3>工程设计思想</h3><p>在产品架构设计/细节评审完成后,也就是在项目工程开始coding前,大部分都存在架构设计阶段。而且往往设计阶段是会投入很长一段时间去做这部分工作的(架构师,亦或者小组长对于整体把控)。</p><p>换句话说为这个工程注入一个设计灵魂,让大家共同向着一个目标去发展。方面后续人员拓展,功能拓展,产品落地拓展...</p><p>但是现实情况往往不尽人意,一个项目开展过程中总会出现一些意外情况:</p><ol><li>阶段性中断,断断续续的开发(半死不活的项目)</li><li>彼此任务太过清晰(彼此的模块互相没有认知)</li><li>team内设计思想的片面认知/没有共识</li><li>非coding外的工作(例如CI/CD)</li><li>人员更替(人员组织架构调整问题)</li><li>。。。</li></ol><p>个人的一些意见,也是目前在做的一些工作:</p><ul><li>文档输出,不仅仅是必要文档输出 涉及到阶段性结束 阶段性设计都要做到有迹可循 (有的人会说没时间写,没必要写... <strong>请相信我 文档会帮助你们减少很多后续工作</strong>)</li><li>项目规模,人员规模导致模块化分工开发 彼此的工作至少做到了解。要尽可能定期分享(请记住你们是一个team 不要每次扯那是某某人写的 我不太清楚...请大家"卷"起来)</li><li>设计思想的落实到开发人员,这部分更多是team leader的责任。请辛苦一下 分享给每个参与者 (我一直认可的话就是领导强不强不是看个人而是看你的team中everyone <em>嘻嘻我也在改进中 此处主要警醒我</em>)</li><li>非coding工作 这部分有点困难 怎么说呢 如果技术团队相对成熟 那么会涉及到部门彼此的工作划分。还是落实到每一个部署细节吧</li><li>人员更替问题 如果能参考以上几点..相信这个不会那么痛苦</li></ul><blockquote>如果大家有希望补充的请留言 我完善一下</blockquote><h3>2. 编码规范</h3><p>编码规范问题呢,团队相对大一点的 都会有这个环境 个人觉得很有很有必要。</p><blockquote>请记住代码是给人看的。请给后来人一片绿荫吧!</blockquote><h4>拿JavaScript简单举几个例子说明一下:</h4><h5>1. 变量/常量声明避免简写,请使用有意义的命名。(太头疼了)</h5><p><img src="/img/remote/1460000040342889" alt="image.png" title="image.png"></p><h5>2. 作用域问题 不要随便就污染全局域 let可以多用用呀 现在有现成的babel你怕啥 (况且还ie时代呢)?</h5><blockquote>不要改全局 不要去改! 意义在哪呢? 你函数内也会释放啊(别抬杠内存什么的 还不够那个资格呢..) 参考下面示意</blockquote><p><img src="/img/remote/1460000040342890" alt="image.png" title="image.png"></p><h5>3. 一个function只做一个事情 避免effect 什么意思呢。 函数要有纯度,也没说你写的多么可推敲。但是不要一个函数跟吧一段逻辑硬生生套起来式的 这不闲的吗.. 请保证它的单一</h5><p><img src="/img/remote/1460000040342891" alt="image.png" title="image.png"></p><h5>4. 限制函数传入参数(argument)个数 我就好奇 那么多参数是真的有必要吗。可读性很差,如果少选 漏传怎么办呢? 如果实在需要可以转换数据类型啊。object啊array啊不都可以吗</h5><p><img src="/img/remote/1460000040342892" alt="image.png" title="image.png"></p><h5>5. 多用用现成的API 拿JavaScript来举例子。</h5><p><img src="/img/remote/1460000040342893" alt="image.png" title="image.png"></p><blockquote>需要补充请留言。</blockquote><h3>3. 技术栈问题混乱</h3><p>这个问题最直接有效的办法,coding review定期执行 说什么大家充分信任问题这个不太靠谱。</p><p>简单说几点吧:</p><ol><li>不能叫review 应该是shared(每个人自己主动shared coding 哈哈 多损啊)</li><li>拿最简单的来说 在小的库/框架/工具组件 也需要拿出来理由 为什么要用 该有的流程要有。不然每个人引入一堆东西进来多烦。</li><li>有团队技术leader 公认都信服的老大来主导推进这个问题(很重要 很重要)</li></ol><blockquote>需要补充请留言。</blockquote><h2>最后</h2><p>本篇属于日常所得所写,如果喜欢这个类型的文章请关注我的专栏<a href="https://link.segmentfault.com/?enc=3kw7N%2Fx%2F%2F96%2BoIzwOIo0%2FQ%3D%3D.g47J%2BXHUIyXlvcEm1p5TwjuHwrQjTdo8W96T6fx8H9kdihmhV7ifmQbS7mFll7cb" rel="nofollow">coding日常</a></p><blockquote>最好由衷的希望大家共同呵护小家庭(team) 公共打造一个神话!</blockquote>
图形学之纹理简单介绍/WebGL进行coding
https://segmentfault.com/a/1190000040335519
2021-07-12T19:35:27+08:00
2021-07-12T19:35:27+08:00
wlove
https://segmentfault.com/u/dxgl
2
<h2>背景</h2><p>本篇收录于<a href="https://link.segmentfault.com/?enc=Pr8NpjIyggM9otlLNJsDBQ%3D%3D.54LsglUEI3fdSiVgkIgyOCLZAvrCRV3IMKuKhqRQGRpsvOoHqDkRUFTc%2BkTkWGkR" rel="nofollow">《数据可视化和图形学》</a>专栏</p><p>首先Canvas 2D 和 WebGL上篇文章做了简单的对比,可以了解到WebGL的API相对来说比较难懂。所以我觉得后续的coding更多是采取WebGL来实现我们的效果(欸,就是玩~) 当然知识点还是从简单到复杂。。。如果一上来就介绍视觉物理,光照/全局光照,抗锯齿,延迟渲染,实时渲染....或许我创建专栏的初衷就丢掉了;当然如果有想深入讨论的可以私下交流。</p><blockquote>上文实现了简单的2d图形 <a href="https://link.segmentfault.com/?enc=VCtOPVn4hSxQuR%2B%2B98D6Eg%3D%3D.ZVXFjEhzP%2BkIhQxYwy6ZvMFnZbNIsWNiWrn1fxp2R%2FSTndMYyHd89ACzdoB%2FTOYS" rel="nofollow">有需要可以参考上文</a></blockquote><h3>本篇大纲</h3><ol><li>什么是纹理?2d与3d纹理贴图区别?</li><li>纹理管线 The Texturing Pipeline</li><li>coding (WebGL中简单使用纹理)</li></ol><h4>1. 什么是纹理?2d与3d纹理贴图区别?</h4><p>在计算机图形学中,纹理贴图(Texturing)是使用图像、函数或其他数据源来改变物体表面外观的技术。</p><h5>2d纹理</h5><p>二维纹理(2D texture)是一张简单的位图图片,用于为三维模型提供表面点的颜色值</p><h5>3d纹理</h5><p>三维纹理(3D texture),可以被认为由很多张 2D 纹理组成,用于描述三维空间数据的图片。</p><h4>2. 通用纹理管线 The Texturing Pipeline</h4><blockquote>渲染管线对于日常使用可能是个黑盒 但是理解这个对你的编程是有莫大的提高...(不懂也没关系本来这块介绍的也很浅 大部分从别的地方copy的. 哈哈)<br>简单来说,纹理(Texturing)是一种针对物体表面属性进行“建模”的高效技术。图像纹理中的像素通常被称为纹素(Texels),区别于屏幕上的像素。</blockquote><h5>请看下图---单个纹理应用纹理贴图的详细过程</h5><p><img src="/img/remote/1460000040335521" alt="image.png" title="image.png"></p><blockquote><ol><li>投射函数(projector function) 就是将空间中的三维点转化为纹理坐标,也就是获取表面的位置并将其投影到参数空间中。例如有球形、圆柱、以及平面投影相关的函数</li><li>映射函数(The Corresponder Function)的作用是将参数空间坐标(parameter-space coordinates)转换为纹理空间位置(texture space locations)。</li></ol></blockquote><ul><li>第一步。通过将 <strong>投影方程(projector function)</strong> 运用于空间中的点 ,从而得到一组称为<strong>参数空间值(parameter-space values)</strong> 的关于纹理的数值。</li><li>第二步。在使用这些新值访问纹理之前,可以使用一个或者多个<strong>映射函数(corresponder function)</strong> 将<strong>参数空间值(parameter-space values)</strong> 转换到纹理空间。</li><li>第三步。使用这些<strong>纹理空间值(texture-space locations)</strong> 从纹理中获取<strong>相应的值(obtain value)</strong> 。例如,可以使用图像纹理的数组索引来检索像素值。</li><li>第四步。再使用<strong>值变换函数(value transform function)</strong> 对检索结果进行值变换,最后使用得到的新来改变表面属性,如材质或者着色法线等等。</li></ul><p><em>法线/法向量相关知识需要自行补充 在图形学中非常重要(经常会见到)</em></p><h4>coding (WebGL中简单使用纹理贴图2d)</h4><blockquote>简单用WebGL实现一个纹理(贴图),并通过一个矩阵进行动画(旋转) 插入的gif有点卡 大概示意...</blockquote><p><img src="/img/remote/1460000040335522" alt="webgl2d.gif" title="webgl2d.gif"></p><h5>1. shader编写 增加了纹理坐标 修改了顶点坐标(采用矩阵/旋转)</h5><pre><code>//vertex shader
attribute vec4 a_position;
attribute vec2 a_texcoord;
uniform mat4 u_matrix;
varying vec2 v_texcoord;
void main() {
gl_Position = u_matrix * a_position;
// 纹理坐标传递
v_texcoord = a_texcoord;
}
// fragment shader
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D u_texture;
void main() {
gl_FragColor = texture2D(u_texture, v_texcoord);
}</code></pre><h5>2. 初始化context(渲染上下文) 以及 着色器程序</h5><pre><code>var canvas = document.querySelector("#canvas");
var gl = canvas.getContext("webgl");
if (!gl) {
return;
}</code></pre><h5>3. 初始设置GLSL程序并新增缓冲区(纹理)</h5><pre><code>// 设置GLSL程序
var program = webglUtils.createProgramFromScripts(gl, ["vertex-shader", "fragment-shader"]);
// 获取顶点坐标需要绑定的地方
var positionLocation = gl.getAttribLocation(program, "a_position");
var texcoordLocation = gl.getAttribLocation(program, "a_texcoord");
// 获取 uniforms
var matrixLocation = gl.getUniformLocation(program, "u_matrix");
var textureLocation = gl.getUniformLocation(program, "u_texture");
// 创建缓冲区
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
var positions = [
-1, -1,
-1, 1,
1, -1,
1, -1,
-1, 1,
1, 1,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
// 为纹理坐标创建缓冲区
var texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
var texcoords = [
0, 0,
0, 1,
1, 0,
1, 0,
0, 1,
1, 1,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texcoords), gl.STATIC_DRAW);</code></pre><h5>4. 创建并加载纹理</h5><pre><code> //解决图片跨域问题
function requestCORSIfNotSameOrigin(img, url) {
if ((new URL(url, window.location.href)).origin !== window.location.origin) {
img.crossOrigin = "";
}
}
// 创建一个纹理 { width: w, height: h, texture: tex }
// 初始化1x1 px像素 图片加载完更新
function loadImageAndCreateTextureInfo(url) {
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
// Fill the texture with a 1x1 blue pixel.
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([0, 0, 255, 255]));
// let's assume all images are not a power of 2
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
var textureInfo = {
width: 1,
height: 1,
texture: tex,
};
var img = new Image();
img.addEventListener('load', function() {
textureInfo.width = img.width;
textureInfo.height = img.height;
gl.bindTexture(gl.TEXTURE_2D, textureInfo.texture);
// 调用 texImage2D() 把已经加载的图片图形数据写到纹理
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
render()
});
requestCORSIfNotSameOrigin(img, url);
img.src = url;
return textureInfo;
}
// 加载纹理
var texInfo = loadImageAndCreateTextureInfo('https://webglfundamentals.org/webgl/resources/leaves.jpg');
</code></pre><h5>5. 绘制相关设置 纹理坐标并赋值矩阵坐标。 使用着色程序 绘制。</h5><pre><code>function render(time) {
time *= 0.001; // time 加速度 旋转图片
// 重新设置canvas大小
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
// 裁剪像素区
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.bindTexture(gl.TEXTURE_2D, texInfo.texture);
// 使用着色器程序
gl.useProgram(program);
// 设置参数,让我们可以绘制任何尺寸的图像
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.enableVertexAttribArray(texcoordLocation);
gl.vertexAttribPointer(texcoordLocation, 2, gl.FLOAT, false, 0, 0);
var aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
var matrix = m4.scaling(1, aspect, 1); //可是试试将aspect 改为0.1看看效果 - -
matrix = m4.zRotate(matrix, time);
matrix = m4.scale(matrix, 0.5, 0.5, 1);
// 设置矩阵
gl.uniformMatrix4fv(matrixLocation, false, matrix);
// 从第0个unit开始获取纹理
gl.uniform1i(textureLocation, 0);
// 绘制 (2 triangles, 6 vertices)
gl.drawArrays(gl.TRIANGLES, 0, 6);
requestAnimationFrame(render);
}</code></pre><blockquote><ol><li><code>webgl-utils.js</code> webgl相关函数封装工具库</li><li><code>m4.js</code> 矩阵相关数学函数库<br>完整代码示例 <a href="https://link.segmentfault.com/?enc=HN8lH1EvoCWuIwboqfs%2FOg%3D%3D.3ZmNnwpGYQdtsibTNtL7EfGDG21uVydvqRBAZcsJBFxkLT4fkC4xtSg6f9eG3SEstKzR59%2FYxrHfwH1pOIQRZMDHu5hDGybrkdlyWk0f%2Bjo%3D" rel="nofollow">请点击git仓库查看代码示例</a></li></ol></blockquote><h3>2D渲染方面你可能需要了解的有</h3><ol><li>纹理缓存</li><li>纹理压缩</li><li>2D/2D纹理优化</li><li>渲染优化...</li></ol><h2>最后</h2><blockquote>最后强烈希望大家学习相关理论知识;理论可能日常用到的地方很少,但是它能决定你走多远。(有的人问难怎么办,勤于练习吧),写作速度呢 该专栏我加速(一周1-2篇) 其他专栏(1篇) 计算机图形学相关的基础知识都会带一遍。然后后续主要写数据可视化方向。</blockquote>
我想与你讨论一下Canvas2D 与WebGL
https://segmentfault.com/a/1190000040294810
2021-07-05T22:14:50+08:00
2021-07-05T22:14:50+08:00
wlove
https://segmentfault.com/u/dxgl
2
<h2>背景</h2><p>本篇收录于<a href="https://link.segmentfault.com/?enc=rkCcMXOGQ7VB7r23A6M8NA%3D%3D.LdnpOne%2Ft4TwZXgPogYau7sNeGHJcKRjuUK2VUn1uAkLOTqo7r7e2RsrpCH0VDtQ" rel="nofollow">《数据可视化和图形学》</a>专栏</p><p>上文谈到在 纠结 中砥砺前行,写下了第一篇专栏文章对于图形学和可视化的认知篇 实现了一个简单的程序。本来打算后续序列已算法和渲染方向为主.但是综合微信/QQ圈内同学反馈晦涩问题 后续文章更多已结论(小节产生效果)为导向进行展开学习。</p><blockquote>本篇不会讨论canvas与webGL的区别 相信大家都懂...</blockquote><h4>本篇大纲</h4><ol><li>Canvas与WebGL简介</li><li>会canvas不会WebGL?俩者如何下手</li><li>canvas的API如何用WebGL实现(比较区别)</li><li>大道志远,潜心修行(后续大纲)</li></ol><h3>1. Canvas 2D与WebGL简介</h3><h4>Canvas 2D</h4><p><strong>基本概念:</strong></p><p>Canvas API 提供了一个通过JavaScript 和 HTML的<canvas>元素来绘制图形的方式。它可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面。</p><p><strong>渲染(原理)流程:</strong></p><p><img src="/img/remote/1460000040294812" alt="image.png" title="image.png"></p><p>WebKit为例</p><ol><li>浏览器(JavaScript) canvas API</li><li>底层图形库 Skia(支持CPU/GPU绘制)</li><li>根据平台是否支持(策略) 选取绘制方式。</li></ol><p>可以参考一下WebKit中用来支持Canvas的类(本文不做展开) <br><img src="/img/remote/1460000040294813" alt="image.png" title="image.png"></p><h4>WebGL</h4><p><strong>基本概念:</strong><br>WebGL(Web图形库)是一个JavaScript API,可在任何兼容的Web浏览器中渲染高性能的交互式3D和2D图形,而无需使用插件。WebGL通过引入一个与OpenGL ES 2.0非常一致的API来做到这一点,该API可以在HTML5 <canvas>元素中使用。 这种一致性使API可以利用用户设备提供的<strong>硬件图形加速</strong>(与canvas 2D区别)。</p><p><strong>渲染(原理)流程:</strong></p><p><img src="/img/remote/1460000040294814" alt="image.png" title="image.png"></p><p>WebKit为例</p><ol><li>Shader/ res/texture/WebGL API</li><li>drawingBuffer (绘制缓冲区)</li><li>GL库/GPU绘制</li></ol><p>可以参考一下WebKit中用来支持Web的类(本文不做展开) <br><img src="/img/remote/1460000040294815" alt="image.png" title="image.png"></p><h3>2. 会canvas不会WebGL?俩者如何下手</h3><h4>回答问题为导向:</h4><ul><li>Canvas 2D API简单 WebGL API看不懂是为何</li><li>学习不知道如何入手?(API工程师?啃书?源码?)</li><li>...</li></ul><p>依此的简单回答:</p><ol><li>首先明白一点API越简单肯定越上层(上层直白来说发挥空间小,局限性大),为什么canvas 2D的API简单呢? 封装的完善 而且大部分工作隐藏在内核的实现上。 WebGL需要考虑shader,buffer,texture,program(程序),link(这块好像透明点)...总之就是难。多看看就好了。</li><li>学习这个话题太多人问了,拿我举例说也得看文档 coding 深入的话还得学习一些别人的产出。(直接看文献也是看别人的产出,没什么较真的) 没得办法。如果说那条路更简单的话,看别人的分享(需要是系统的分享 碎片化知识对于学习的路来说并没有多大帮助)。 看框架(库)实现源码什么的,那个也只是API的封装。看的反而怀疑人生 因为那个又牵扯一些别的知识,整体架构设计,数据模型,渲染,事件等等...</li><li>...留言讨论吧 有需要加微信也可以</li></ol><h3>3.Canvas 2D的API如何用WebGL实现(比较区别)</h3><p>已一个简单的来学习一下,绘制一个矩形:</p><p><img src="/img/remote/1460000040294816" alt="image.png" title="image.png"></p><blockquote>canvas</blockquote><pre><code>// html
<!-- 画布 -->
<canvas id="myDiagram" width="200" height="200"></canvas>
//js
// 获取canvas元素
var canvas = document.getElementById("canvas");
// 获取渲染上下文
var ctx = canvas.getContext("2d");
// 绘制样式 红色
ctx.fillStyle = "red";
// API 绘制rect
ctx.fillRect(10,10,100,100);
</code></pre><blockquote>WebGL</blockquote><pre><code>// html
<!-- 画布 -->
<canvas id="myDiagram" width="200" height="200"></canvas>
</code></pre><p><!-- webgl utils --></p><pre><code><script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
// js
// 获取渲染上下文
const gl = document.querySelector('#myDiagram').getContext('webgl');
// 顶点 vertex shader
const vs = `
// vertex shader
void main() {
gl_Position = vec4(0, 0, 0, 1); // 坐标 gl_开头都为webgl内置变量
gl_PointSize = 100.0; // 大小
}
`;
//片元 fragment shader
const fs = `
// fragment shader
void main() {
gl_FragColor = vec4(0, 0, 0, 1); // 颜色 红色
}
`;
// shader程序
const program = webglUtils.createProgramFromSources(gl, [vs, fs]);
// 使用 shader程序
gl.useProgram(program);
// offset 偏移
const offset = 0;
// count 个数
const count = 1;
// gl.POINTS 内置绘制方式
// 绘制函数 drawArrays
gl.drawArrays(gl.POINTS, offset, count);
</code></pre><h3>4. 大道志远,潜心修行(后续大纲)</h3><blockquote>此处应该为填空题(给点意见吧 好难~), 简单说下我的想法吧 顺序待定....</blockquote><ol><li>WebGL渲染2D篇</li><li>WebGL渲染3D篇</li><li>渲染优化手段 quadTree(2d渲染) 3d渲染...</li><li>光照和阴影 RayCasting / RayTracing...</li><li>其他 (算法类,框架解读类...)</li></ol><h2>最后</h2><blockquote>需要加微信群的请留言...我看到就会回复的 贴一下上文的一些链接。</blockquote><ol><li><a href="https://link.segmentfault.com/?enc=JCvIrOm6ZY17mkuJ5Lm6lQ%3D%3D.9XyIiT7X%2BxuEToRgeos4%2BbABgSG9dPwK8W%2Bxh6G0avlPo4tHNRd%2Bka5fh9zabo0svHG%2BxxnyHzzYxH%2Bz6O1PLin%2FZWZ2e4Rz3teDZHFtXV0%3D" rel="nofollow"> Canvas 2D 教程 </a></li><li><a href="https://link.segmentfault.com/?enc=e54%2F6qOkNNdsAA%2FtGWq4pw%3D%3D.OhG4xvccK4s2xDr79Yl6BB9I273UqKmSGYUPeABRq2S0OAPi1SS1Uwq7sy1h4Cyd%2BDbENILAWnL%2BjW7ZwwGzeBHiyuz%2FdE6UnDtlGAAuF%2FjpvwsrWy1qIsM1%2B%2FTD%2Bx8t" rel="nofollow"> WebGL教程 </a></li><li><a href="https://link.segmentfault.com/?enc=7eTc4mZVkCDb8vlGAdWEQw%3D%3D.YvyNUM%2FevjIS40pZFw8ySuIAMDoFlsrdGN%2FsnlvTrbi5FA4dSiCZfJy4%2FqOc4Z9s1iSWSAPpSseJDCV0etf9Bg%3D%3D" rel="nofollow">WebKit之WebGL原理====写的蛮细的</a></li></ol>
follow me ~ 一起去了解下-数据可视化/图形学/WebGL/Shader
https://segmentfault.com/a/1190000040269596
2021-07-01T09:21:27+08:00
2021-07-01T09:21:27+08:00
wlove
https://segmentfault.com/u/dxgl
5
<h2>背景</h2><p>《数据可视化和图形学》专栏创建已然日久,想了很久怎么去开展这个系列(专栏输出比碎片化的输出难多了~):</p><ol><li>如何精简凝练相关知识体系。</li><li>如何将系列知识,文章碎片化。</li><li>每章节如何承上启下,与整体可拆可合。</li><li>最重要一点第一篇咋个搞合适...</li></ol><p>总之在纠结中一直迟迟难以下笔。一天,一天.... 草稿箱里十几篇总有的,后面想通了。我尽力 在途中得到大家的意见然后加速改进。那么就开始我们的《数据可视化和图形学》</p><h4>本篇大纲</h4><blockquote><ol><li>介绍图形学和可视化</li><li>engineer/前端er如何入手</li><li>GPU渲染管线</li><li>webgl介绍 & coding</li></ol></blockquote><h3>1. 什么是图形学? 什么是数据可视化?</h3><h4>图形学</h4><p>图形学:利用数学公式/函数基础理论来以图形形式表现<br>简单介绍几点:</p><ol><li>说起图形学不要只想到游戏,还有很多应用工业,医学,影视...领域很广</li><li>图形学不单单指渲染,还有数学(计算),美术,物理...等等</li><li>想学好图形学并不是会点API就可以,更要学习其<strong>背后的基础知识(数学/物理)</strong></li><li>图形学的人才/知识积累/文献资料/先进技术..太稀缺了,<strong>大佬萌新们抓紧进来一起搞</strong></li></ol><blockquote>讨论图形学牵扯一点图像处理,简单说一下。图像处理就是图形学逻辑相反 输入图形输出数学公式/函数。</blockquote><h4>数据可视化</h4><p>数据可视化:将数据转化成为交互的图形或图像等。 <strong>数据可视化主要旨在借助于图形化⼿段,清晰有效地传达与沟通信息</strong></p><blockquote>在之前谈起可视化更多说是归属于计算机图形学CG(computer graphics)一类的,到后来发展势头越来越强,逐步脱离出来。知识图谱/医疗等等方向都有数据可视化的应用。</blockquote><h3>2. software engineer/前端er如何入手</h3><h4>从一些现实问题简单回答一下:</h4><ul><li>业务需求需要用到部分可视化</li><li>想深入图形学/可视化,奈何技法缺失(语言,数学...)</li><li>不知道如何入手(各个领域的engineer)</li><li>...</li></ul><p>依此的简单回答:</p><ol><li>业务需求主导可视化 框架肯定是利器,但是你会缺失发现它内部美的东西。有人就会说了 读源码可以嘛? 可以。但是这个工作量可能更大。并不一定是最有效途径。</li><li>深入图形学/可视化 方向迷茫。这点我也是在碰壁中,只能说把我踩过的坑分享出来。读文献 学数学 推导公式..这个是个漫长的过程 需要凝练 需要分类(分层) 二次打包推到更多人的视野。(不幸的是相关资料很少,因为图形学确实难...)</li><li>engineer不知道如何入手?首先在工程师角色加持下。最起码语言不用过分担心。拿前端er举例来说 入手当然是webGL(当然我的观点依旧是API仅仅是API.. 使用API并不是图形学的最佳途径) 但是拿问题驱动你的学习这个是可行的。</li></ol><h3>3. GPU渲染管线</h3><blockquote><p>明确一点 在不同的系列的GPU渲染管线存在差异</p><p>GPU的编码同CPU区别 debug不存在的... 无memory交集... 总之你会喜欢上它的~ <br>请看下图:</p></blockquote><p><img src="/img/remote/1460000040269598" alt="image.png" title="image.png"></p><ul><li>图片来源网络 侵删</li></ul><ol><li>CPU 计算节点基础信息(数量)</li><li>GPU Vertex Shader 进行顶点绘制</li><li>primitives Generation 负责结构生成(link/三角形)</li><li>Rasterization 光栅化</li><li>Fragment Shader 片元 (着色)</li><li>Testing Blending 混合测试 (Alpha透明度)</li><li>final render 渲染(图像呈现)</li></ol><blockquote><p>光栅化:把顶点数据转换为片元的过程(简单理解就是找到图形并转换所覆盖的像素)</p><p>着色过程中一般是线性补充(比如 0-1区间会进行线性补充 0.1 0.2....)</p></blockquote><h3>webgl介绍 coding</h3><blockquote><p>WebGL(Web图形库)是一个JavaScript API,可在任何兼容的Web浏览器中渲染高性能的交互式3D和2D图形,而无需使用插件。WebGL通过引入一个与OpenGL ES 2.0非常一致的API来做到这一点,该API可以在HTML5 <canvas>元素中使用。 这种一致性使API可以利用用户设备提供的硬件图形加速。</p><p>补充说明OpenGL也是渲染API ES是嵌入式的意思(可Google/baidu) 俩者API非常相近可以去了解一下。连接统一后面贴</p></blockquote><h4>coding</h4><blockquote>首先<a href="https://link.segmentfault.com/?enc=8QsHXKAKyCc0b0PSYrvc5A%3D%3D.W6IAMDTWzwlFzRA9fzjqU12ObUryCWYGBi0HZhCehs3BA%2F6tPrKtoRH6giqdOOTCta4%2FR6ycX6U%2Fc8XOTj2Viw%3D%3D" rel="nofollow">MDN</a>webGL相关介绍更全一些 以及<a href="https://link.segmentfault.com/?enc=fXUqWeM4KyBbu0dV%2BMImZQ%3D%3D.keK5fnZzM0zF%2FyRiE6md5%2F7IzQui0%2F6sNpKarZ%2FXfrX8CN1YB3F5awIoYHdkwloqlvVUVcc52ELxMCT2sJdFInChJTsAcikN56Kq6GSo5mpWpagR3djYiHJLsFd%2BY0nB" rel="nofollow">Tutorial</a> (教程) 非常适合入门.大家可以去学习一下。但是深入还是需要参考一些学习资料(可以自己动手实现一些光线追踪,折射...图形学欢迎你)</blockquote><h6>编码过程总结</h6><ol><li>init shader(vertex,fragment) <del>compile</del></li><li>create attrbute buffer <del>(coding no buffer)</del></li><li>draw</li></ol><blockquote>来吧展示 最小的程序 效果就是绘制一个rect</blockquote><p><img src="/img/remote/1460000040269599" alt="image.png" title="image.png"></p><h6>html</h6><pre><code><!-- 画布 -->
<canvas id="myDiagram" width="200" height="200"></canvas>
<!-- webgl utils -->
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
</code></pre><h6>JavaScript</h6><pre><code>// init shader(vertex,fragment)
const vs = `
// vertex shader
void main() {
gl_Position = vec4(0, 0, 0, 1); // 居中
gl_PointSize = 120.0; //大小
}
`;
const fs = `
// fragment shader
void main() {
gl_FragColor = vec4(0, 0, 0, 1); // 黑色
}
`;
// setup
//create program & use program
const program = webglUtils.createProgramFromSources(gl, [vs, fs]);
gl.useProgram(program);
const offset = 0;
const count = 1;
// 绘制函数 drawArrays
// gl.POINTS 内置绘制方式
// offset 偏移
// count 个数
gl.drawArrays(gl.POINTS, offset, count);
</code></pre><blockquote>so easy webgl就学会了... 哈哈 没那么简单的啦。我们后续慢慢搞</blockquote><h2>最后</h2><p>系列文章的下笔是如此的困难...系列的视频可如何是好! 大家多提提意见~</p><p>后续可视化方向会多一点,数学会多一点,物理会多一点.... 我们加油!</p><p>数学(算法)很美的:<code>quadtree</code>,<code>cluster-kmeans....</code> 哎呦都是好东西... 我都迫不及待写了(脑补坏笑的表情~)</p><p>如果有需要加微信群的联系我 ~</p>
我相信这是你需要知道的JS运行机制和JS引擎(V8)内部
https://segmentfault.com/a/1190000040089204
2021-05-30T19:25:06+08:00
2021-05-30T19:25:06+08:00
wlove
https://segmentfault.com/u/dxgl
6
<h2>前言</h2><p>前几天,我开始想写前端生态周边之浏览器幕后的文章。其实也是是对于零碎知识进行整合。希望能给大家带来从0-1,而非模块 的认识。另外一个初衷也是作为前端的视角到底应该了解浏览器的什么内容。(很多人想深入要看下WebKit源码,V8源码个人感觉稍微有点离谱 并不适合每个前端同学去实践 会让你感觉到绝望)</p><blockquote>希望大家在认真阅读的过程中能够纠正我的问题。快来打我脸!</blockquote><p>今天来到了大家日常接触最多的模块 JS引擎相关的内容;本篇文章的内容包含:</p><ol><li>JavaScript运行机制</li><li>简单了解一下引擎内部</li></ol><h3>术语概念 (熟悉的跳过,眼熟的回忆,不懂的认真看完后续有用到)</h3><ol><li>进程 cpu资源分配的最小单位 (<del>进程可包含多个进程 </del> <del>这个可以暂时不需要理解</del>)</li><li>线程 线程是cpu调度的最小单位</li><li>ECS:执行环境栈,Execution Context Stack</li></ol><hr><h2>JavaScript运行机制</h2><h3>概述</h3><ul><li>JS是单线程的脚本语言</li><li>JS为什么设计为单线程语言 主要的原因是因为与它的用途有关系,作为浏览器脚本语言,JS的主要用途是操作DOM。如果设计多线程,势必会带来操作冲突引发复杂的同步问题;</li><li>为了解决单线程排队问题;出现了让你头疼的运行机制 包含主线程,任务队列,event-loop。</li></ul><blockquote>为了更好地理解Event Loop,请看下图</blockquote><p><img src="/img/remote/1460000040089206" alt="" title=""></p><p>上图中,主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在"任务队列"中加入各种事件(onClick,onLoad)。只要栈中的代码执行完毕,主线程就会去读取"任务队列",依次执行那些事件所对应的回调函数。<br><strong>执行栈中的代码,总是在读取"任务队列"之前执行。</strong></p><h3>主线程 (执行栈)</h3><blockquote><code>执行栈(execution context stack)</code>是JavaScript执行事件任务的线程。</blockquote><p>请看下面这个gif图:</p><p><img src="/img/remote/1460000040089207" alt="" title=""></p><ol><li>当在全局代码中调用一个函数,程序将进入被调用的函数内部,并创建一个新的执行上下文,并将新创建的上下文压入执行栈的顶部。</li><li>调用函数内部调用了其他函数 会重复第一个步骤(程序进入调用函数内部,创建一个新的执行上下文并把它压入执行栈的顶部)</li><li>依次执行从栈顶开始: 执行位于栈顶的执行上下文,一旦当前上下文函数执行结束,它将被从栈顶弹出,并将上下文控制权交给当前的栈;继续执行直到栈空。</li></ol><h3>任务队列</h3><blockquote><code>任务队列(task queue)</code>是进行存放异步任务运行结果事件 一种是同步任务(synchronous),另一种是异步任务(asynchronous):</blockquote><ol><li>同步任务指的是在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。</li><li>异步任务指的是直接进入任务队列,然后等待满足条件(发出通知)然后最终进入主线程执行的任务。</li></ol><blockquote>请看下面这个gif图:</blockquote><p><img src="/img/remote/1460000040089208" alt="" title=""><br><del>(学过C++语言都知道main作为主入口。不了解的没关系此处main函数可做忽略)</del>~~</p><ol><li>进入全局执行上下文</li><li>console.log("start");进入主线程 (压入栈的顶部)</li><li>Timer1 执行中进入主线程发现是WEBapi 待timeout时间完成进入队列</li><li>Timer2 执行中进入主线程发现是WEBapi 待timeout时间完成进入队列</li><li><p>console.log("end);进入主线程执行</p><blockquote>执行顺序为 2-5-4-3</blockquote></li></ol><h3>Event Loop</h3><blockquote><code>event-loop</code>可以理解为一个处理机制。<br>主线程任务执行==主线程从"任务队列"中读取事件==执行...,这是个循环的过程。这种运行机制称为Event Loop(事件循环)</blockquote><p><strong>对照上面的动画 简单理解为主线程为空时就从任务队列读取待执行的事件(timer1,timer2)进入主线程进行执行。</strong></p><h2>JS引擎(V8)概述</h2><p>JavaScript 引擎 是一个程序或者执行 JavaScript 代码的解释器。一个JavaScript 引擎可以作为一个单独的解释器实现或者通过某种方式将 JavaScript 编译为字节码的即时编译器。</p><p>常说的JavaScript 引擎:</p><ol><li>JavaScriptCore 代表浏览器Safari</li><li>Rhino 代表浏览器Mozilla Firefox</li><li>Chakra 代表浏览器Internet Explorer(IE)</li><li><strong>V8 代表浏览器 Chrome 开源,用 C++ 实现的</strong></li></ol><blockquote>下面介绍一下V8的一些内部实现,优势。</blockquote><h3>JS执行内部过程</h3><p><img src="/img/remote/1460000040089209" alt="" title=""></p><ol><li>JS代码转换为AST语法树表示。</li></ol><pre><code>// 函数
function greet() {
console.log("wlove");
}
// AST树 json
{"type":"Program","start":0,"end":47,"body":[{"type":"FunctionDeclaration","start":0,"end":46,"id":{"type":"Identifier","start":9,"end":14,"name":"greet"},"expression":false,"generator":false,"async":false,"params":[],"body":{"type":"BlockStatement","start":17,"end":46,"body":[{"type":"ExpressionStatement","start":23,"end":44,"expression":{"type":"CallExpression","start":23,"end":43,"callee":{"type":"MemberExpression","start":23,"end":34,"object":{"type":"Identifier","start":23,"end":30,"name":"console"},"property":{"type":"Identifier","start":31,"end":34,"name":"log"},"computed":false,"optional":false},"arguments":[{"type":"Literal","start":35,"end":42,"value":"wlove","raw":"\"wlove\""}],"optional":false}}]}}],"sourceType":"module"}</code></pre><p><img src="/img/remote/1460000040089210" alt="" title=""></p><ol start="2"><li>AST转换为字节码 <br> 有了解的同学应该知道之前的V8直接是转换机器码。但是机器码占空间很大,如果v8 缓存机制将 所有 js 代码编译成机器码缓存下来,这样会导致缓存占用的内存、磁盘空间很大。而且退出 Chrome 再打开时序列化、反序列化缓存时间成本也很高。<br> 在时间,空间成本都很高的情况下 引入了字节码。</li><li><p>字节码解释器<code>TurboFan</code> 内部也存在很多工作内容简单列取几点:</p><ol><li>字节码处理程序生成</li><li>字节码生成</li><li>解释器寄存器分配</li><li>Context链</li><li>异常处理</li><li>JS代码解释执行</li><li>....</li></ol></li><li>JIT (Just In Time) 混合使用编译器和解释器的技术。编译器启动速度慢,执行速度快。解释器的启动速度快,执行速度慢。而JIT技术就是取俩者之长 (<code>Ignition</code>(字节码解释器) + <code>TurboFan</code> (JIT编译器) 的组合;后面的应用也是越来越广 )</li><li><p>虚拟机(垃圾回收,内存管理等)<br> <img src="/img/remote/1460000040089211" alt="" title=""><br> V8 使用了分代和大数据的内存分配,在回收内存时使用精简整理的算法标记未引用的对象,然后消除没有标记的对象,最后整理和压缩那些还未保存的对象,即可完成垃圾回收。<br> <strong>内存分配:</strong></p><ul><li>年轻分代:为新创建的对象分配内存空间,经常需要进行垃圾回收。为方便年轻分代中的内容回收,可再将年轻分代分为两半,一半用来分配,另一半在回收时负责将之前还需要保留的对象复制过来。</li></ul></li></ol><ul><li>年老分代:根据需要将年老的对象、指针、代码等数据保存起来,较少地进行垃圾回收。</li><li><p>大对象:为那些需要使用较多内存对象分配内存,当然同样可能包含数据和代码等分配的内存,一个页面只分配一个对象。</p><p><strong>内存(垃圾)回收:</strong></p><ol><li>年轻分代中的对象垃圾回收主要通过Scavenge算法进行垃圾回收。</li><li>因考虑在年老分代中存活对象居多,所以主要采用了Mark-Sweep(标记清除)标记清除和Mark-Compact(标记整理)相结合的方式进行垃圾回收。</li></ol></li></ul><ol start="6"><li>代码执行</li></ol><h3>工作过程编译和运行</h3><h4>V8引擎编译阶段:</h4><p>主要类如下所示:</p><ol><li><code>Script</code>:Script类 包含JS代码,和编译后的本地代码。(此处为编译入口)</li><li><code>Compiler</code>:编译器类:Script类调用编译生成代码 包括生成AST,本地代码等。</li><li><code>AstNode</code>:抽象语法树node类(作为节点基类,包含很多子类为后续生成代码做辅助)</li><li><code>AstVisitor</code>:抽象语法树访问类,主要用来遍历异构的抽象语法树;</li><li><code>FullCodeGenerator</code>:访问类可调用来遍历AST来为JS生成本地可执行代码。<br><img src="/img/remote/1460000040089212" alt="" title=""></li></ol><p>编译过程大概为:</p><ol><li>Script类调用Compiler类为其生成AST和本地代码。</li><li>Compile函数先使用Parser类生成AST,再使用FullCodeGenerator类来生成本地代码。</li><li>FullCodeGenerator使用多个后端来生成与平台相匹配的本地汇编代码。</li></ol><h4>V8引擎运行阶段:</h4><p>主要类如下所示:</p><ol><li><code>Script</code>此处为运行入口 编译之后生成的本地代码;</li><li><code>Execution</code>:JS运行过程中的辅组类;包含一些函数 如call函数;</li><li><code>JSFunction</code>:需要执行的JavaScript函数表示类;</li><li><code>Runtime</code>:运行这些本地代码的辅组类,主要提供运行时所需的辅组函数,如:属性访问、类型转换、编译、算术、位操作、比较、正则表达式等;</li><li><code>Heap</code>:运行本地代码需要使用的内存堆类;</li><li><code>MarkCompactCollector</code>:垃圾回收机制的主要实现类,用来标记、清除和整理等基本的垃圾回收过程;</li><li><code>SweeperThread</code>:负责垃圾回收的线程。</li></ol><p><img src="/img/remote/1460000040089213" alt="" title=""><br>执行过程大概为:</p><ol><li>V8 当函数被调用会检测是否有本地代码如果有进行调用。</li><li>V8 当函数被调用会检测是否又本地代码如果没有将会生成本地代码。</li><li>执行编译后代码构建JS对象需要Runtime类来辅组创建对象,并需要从Heap类分配内存。</li><li>最后将不用的空间进行标记清除和垃圾回收。</li></ol><blockquote>当然V8内部还有很多例如隐藏类,扩展机制等等。有兴趣可以看一下<code>《WebKit技术内幕》</code> 如果想要读源码推荐从<a href="https://link.segmentfault.com/?enc=tV%2BgcieullLr7eLjNKd8eA%3D%3D.4OeElj4oQlTXwBDoKl8ZQcMoH21kFwYnmlO3pELhS2R5fWVTqHe%2F7Akm61WHlI6dmPlsNmxMmXbliSiL%2BpXVmRv%2BcTQkI6b5WQjBG%2BufcfY%3D" rel="nofollow">代码量还少的V8</a> 开始阅读。加油少年。</blockquote><h2>最后</h2><blockquote>分享一下最近: 工作很忙很忙(对接外部资源,整合内部,做技术探索研发...);生活很忙很忙(有一点就是运动一定要坚持下来..) 然后呢写作也会坚持下来(每周1-3篇)。大家有任何想了解的内容随时留言 只要我知道的就会立刻分享出来帮助大家。如果不知道我可以去学。嘿嘿</blockquote><p><strong>下一篇待定吧 加油 前程似锦 靓仔靓女!!!</strong></p><blockquote>上文图片部分来源网络 侵权请联系删除 谢谢。</blockquote>
一个小故事引起的讨论:JavaScript中使用函数式编程
https://segmentfault.com/a/1190000040057574
2021-05-25T10:00:00+08:00
2021-05-25T10:00:00+08:00
wlove
https://segmentfault.com/u/dxgl
3
<h2>前言</h2><p>昨天的文章中提到了一个小插曲, 故事的由来是我微信中的一个"小老板"突然来了个不可言喻的表情(<code>Little (small) boss</code> "SB..."??? 哈哈 还是简称小吧)。然后开始了下面的对话:</p><ul><li>小: 最近TM的好烦</li><li>我: 怎么了 boss</li><li>小: 我写的代码最近新增了业务 改动起来 牵一发而动全身。心态有点小炸裂。</li><li>我: 具体怎么个情况?让小老弟帮你排排忧。</li><li>小: 之前不是刚独立负责项目吗,从0到1实现起来感觉也没啥太大的困难。但是呢 最近在原来的基础上新增了点新业务需求需要改动之前的代码。就总遇到一些问题 一个函数内的全局变量改动然后功能很多都失效了 导致需要一直debug追踪到底在哪里使用了。。。</li><li>我: 我一般拿到项目/产品需求之后在写代码前会花大量事件思考设计 可能比例7:3甚至8:2;经验是一方面。思考还是很重要的。回到你的问题 就说你前期没做好一些工作例如:<code>编码设计</code>;<code>整体模块划分</code>;<code>工程类框架</code>(做一些<code>单元测试,覆盖率测试</code>..)等等措施,今天给你聊一下具体如何解决这些<code>小问题</code>!!</li></ul><p>我展开了一系列装逼和舔的操作....</p><p>回到今天文章的主题介绍一下编程范式,以及如何在<code>JavaScript</code>中使用起来。</p><h2>什么是编程范式 ?</h2><p>编程范式(<code>Programming paradigm</code>)是指计算机中编程的典范模式或方法。</p><p>编程范式主要包含:命令式编程(<code>Imperative</code>)、声明式编程(<code>Declarative</code>)和函数式编程(<code>Functional</code>)...</p><h3>命令式编程:</h3><p>命令式编程的主要思想是关注计算机执行的步骤,即一步一步告诉计算机先做什么再做什么。</p><p>比如:如果你想进行一个变量与变量组合的输出,你需要这样告诉计算机:</p><ol><li>第一步: 创建一个变量(名字) name;</li><li>第二步: 创建一个变量(问候语) greeting;</li><li>第三步: console.log输出</li></ol><pre><code> var name = "wlove"; //声明
var greeting = "hello, I'm ";//声明
console.log(greeting + name);//输出 hello, I'm wolove</code></pre><h3>声明式编程:</h3><p>声明式编程是以<code>数据结构</code>的形式来表达程序执行的逻辑。它的主要思想是告诉计算机应该做什么,但不指定具体要怎么做。日程最接近的就说网页编程中用到的 HTML 和 CSS 都属于声明式编程。(解释一下:HTML/CSS它们的编写其实就是结构化的 不会具体告诉计算机做什么细节内容。所以属于声明式编程<code>Declarative programming</code>;其实和很多配置文件很相似。)</p><p>最直接举例就是<code>SQL</code>:</p><blockquote><code>SELECT * FROM table WHERE num < 5</code></blockquote><p>通过观察声明式编程的代码我们可以发现它有一个特点是它不需要创建变量用来存储数据。<br>另一个特点是它不包含循环控制的代码如 for, while。</p><h3>函数式编程:</h3><p>函数式编程和声明式编程是有所关联的,因为他们思想是一致的:即只关注做什么而不是怎么做。但函数式编程不仅仅局限于声明式编程。</p><p>函数式编程最重要的特点是”函数第一位",即函数可以出现在任何地方,比如你可以把函数作为参数传递给另一个函数,不仅如此你还可以将函数作为返回值。很重要一点;如果你使用得当这种编程只需要考虑输入输出的一个流。什么对象(原型)以及其的副作用(相互作用影响)都不需要考虑。</p><p>说起函数式编程 可能接触别的语言的朋友 一下就会回忆起 <code>Haskell</code>,<code>Clojure</code>这些语言。其实大部分常见的编程语言都已经提供了对这种编程方式的支持;比如今天的主角<code>JavaScript</code>。</p><blockquote>相比于以前的命令式编程;声明式编程,我更偏向于使用函数式编程。首先我实验过面向对象(原型)的方式来进行编程。很困惑或者说很痛苦;每一次的<code>debug</code>相当的提神醒脑;其次函数式编程可以让代码的逻辑更清晰更优雅;更安全。(<code>总之个人用起来就说很爽</code>)</blockquote><h2>JavaScript的函数式编程几个介绍:</h2><h5>说起函数式编程最基本需要考虑<code>side effect</code>(无副作用)和<code>pure</code>(纯);如果按这样去实现就能合理的解决小老板的一个很重要的问题 <code>debug</code>一个变量一直跟踪哪里修改了...;</h5><pre><code>//例子1 纯函数
function greet(name){
return "hello I'm " + name;
}
greet("wlove") // hello I'm wlove
//例子2 非纯函数 用到了外部的变量
var name = "wlove"
function _fn(name){
return pre + str
}
greet(name)// hello I'm wlove </code></pre><h5>函数式编程还有一个特点高阶函数(也是提升的一个重要知识点);函数可以作为一个函数的返回。</h5><pre><code>//高阶函数:
function greet(greeting){
return function(name){
return greeting + " " + name
};
}
var GREETING = greet("hello I' m");
GREETING("wlove"); // "hello I' m wlove"
</code></pre><h5>函数式编程还有一个需要思考的问题;如果说每次都重新生成一个Copy对象(举个例子 如果输入的参数是<code>array</code>);每次改变重新生成一个。那样会增加空间的使用。如何解决这个问题? 大家可以思考一下或者查阅一些资料。留言讨论一下。</h5><h2>最后</h2><blockquote>距离上一篇文章的生活日记: 20210524 日常生活的重复。开会;学习;写代码;跑步;写文章;录视频;逛社区...开心就完事了。</blockquote><p><strong>本篇文章主要是因为我特别想帮助(tian)小老板 哈哈;也算是随想随写;欢迎大家留言讨论。后面评论会补充介绍如何有效解决空间问题。</strong></p><p><del>聊完天之后我的小老板就是一顿夸,但是也不知道实践没实践。我得多主动跟人家聊聊天 嘿嘿</del></p><p><strong>下一篇JS引擎,作为前端到底需要认识到什么地步,以及如何学习。</strong></p>
浏览器之渲染引擎(WebKit)
https://segmentfault.com/a/1190000040050288
2021-05-23T19:53:09+08:00
2021-05-23T19:53:09+08:00
wlove
https://segmentfault.com/u/dxgl
4
<h2>前言</h2><p><a href="https://link.segmentfault.com/?enc=zcvjr6jL8l1nAHAvo6QvDw%3D%3D.RAFQt6YOh1QqynJrVaIEc12jFjy2X3qPXn2Ht%2F4sSK8sDKktvhtkq0uJ470n%2BRw5" rel="nofollow">一探浏览器幕后的《三俩事》</a>上一篇的介绍让大家对浏览器的组成有了个模糊的认识:</p><ol><li>浏览器是什么?</li><li>浏览器(chromium)基本架构</li><li>浏览器主要组件</li><li>浏览器内核是什么?</li><li>JavaScript(js)引擎</li><li>渲染引擎</li></ol><p>今天呢做渲染模块(WebKit)展开学习讨论。</p><p>首先作为顺序介绍来说。我应该具体介绍实现一个浏览器应该包含哪些内容(介绍一下<code>chromium</code>实现包含了哪些内容)。但是考虑在<code>前端</code>话题的介绍。只做简单的列举不做具体展开讨论;有兴趣可以自行下去学习。</p><p>其次按大家耳熟能详的程度来说的话我应该着重讨论一下<code>JS引擎(V8 event-loop相关)</code>;但是本节内容还是想先介绍讨论一下<code>渲染引擎(WebKit)</code>。不为别的我就是喜欢玩~ 因为我写这个系列的受众还是偏向前端,所以涉及到需要代码介入讲解的部分这边采取<code>JavaScript</code>来实现。</p><h2>渲染引擎</h2><p><img src="/img/remote/1460000040050290" alt="1621765871(1).jpg" title="1621765871(1).jpg"></p><blockquote>画架构图画习惯了,不要在意丑陋的细节 - -. <br>由上图可以得到渲染引擎内部解析执行大概过程的信息:</blockquote><ol><li>当访问页面的时候会进行网络请求(network)去获取页面的内容;当如果命中缓存(cache)就会从存储(memory)上直接读取。</li><li>HTMLParser 进行html解析通过特定标识进行分块。可解析为CSS DOM JS几个模块。</li><li>CSSParser 进行css也是解析。</li><li>DOMParser 进行DOM结构解析。(构建过程中发现是JS部分执行5,如果是资源进行异步请求.不会阻碍解析)。</li><li>JavaScript 部分丢给JS引擎进行解析。(如果有操作DOM/CSS部分会影响之前的解析,同时会阻碍解析)。</li><li>通过上文解析内容构建RenderTree。</li><li>解析完成通过renderTree进行布局和绘制。</li><li>将最终图像显示在屏幕上。</li></ol><p>下面对于上文所提部分进行展开讨论一下 flow me~ gogogo!</p><hr><h3>Parser 解析</h3><p>上面Parser的字眼是不是太多了。我们得到一个信息解析是渲染引擎中非常重要的一个环节,所以首先需要介绍一下解析.</p><p>解析文档是指将文档转化成为有意义的结构,也就是可让代码理解和使用的结构。解析得到的结果通常是代表了文档结构的节点树,它称作解析树或者语法树。</p><p>解析是以文档所遵循的语法规则(编写文档所用的语言或格式)为基础的。比方说HTML解析那么是在HTML4/5的规范上进行的。所有可以解析的格式都必须对应确定的语法(由词汇和语法规则构成)。</p><p><img src="/img/remote/1460000040050291" alt="image011.png" title="image011.png"></p><p>解析过程通常是分成两个子过程:<em>词法分析和语法分析</em>。<br><strong>词法分析</strong>是将输入内容分割成大量标记的过程。(进行切分可识别大量标记的词段)<strong>语法分析</strong>是应用语言的语法规则的过程(到底这段语言是要做什么工作)。</p><h3>HTMLParser</h3><p><strong>HTML 解析器的任务是将 HTML 标记解析成解析树。</strong></p><p><strong>HTML 语法定义:</strong><br> HTML 的词汇和语法在 W3C 组织创建的规范中进行了定义。</p><p><strong>DOM</strong><br>解析器的输出“解析树”是由 DOM 元素和属性节点构成的树结构。DOM 是文档对象模型 (Document Object Model) 的缩写。它是 <code>HTML</code> 文档的对象表示,同时也是外部内容(例如 JavaScript)与 HTML 元素之间的接口。<br>解析树的根节点是<code>Document</code>对象。</p><p><img src="/img/remote/1460000040050292" alt="image.png" title="image.png"></p><p>DOM解析code示意(不能运行的) 首先要了解解析过程一定是迭代过程:</p><pre><code>// 分析标记<>和属性的正则表达式
var startTag = /^<([-A-Za-z0-9_]+)((?:\s+[\w-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/,
endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/,
attr = /([-A-Za-z0-9_]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;
function ParserHTML(html){
// 接收参数html
while(html){
// 匹配注释内容
if(html.indexOf("<!--") == 0){
//
}
// 匹配开始标签
if (html.indexOf("<") == 0) {
match = html.match(startTag);
if (match) {
html = html.substring(match[0].length); //匹配截取
//继续迭代
}
}
if(html.indexOf("</") == 0){
//匹配结束标签操作
}
}
}
// 输出示意结构
{
element:"",
parentNode:{},
childrenNode:{},
content:"",
....
}
// 我之前写的找不到了--, 大家可以试着去实现一下。有问题随时沟通 本示例正则可用。
</code></pre><h3>CSSParser</h3><p>CSS解析同HTML不同地方是上下文无关的语法;可通过很多解析器做解析。<br>词法语法(词汇)是针对各个标记用正则表达式定义的:</p><ul><li><code>comment \/\*[^*]*\*+([^/*][^*]*\*+)*\/</code></li><li><code>num [0-9]+|[0-9]*"."[0-9]+</code></li><li><code>nonascii [\200-\377]</code></li><li><code>nmstart [_a-z]|{nonascii}|{escape}</code></li><li><code>nmchar [_a-z0-9-]|{nonascii}|{escape}</code></li><li><code>name {nmchar}+</code></li><li><code>ident {nmstart}{nmchar}*</code></li><li>通过 CSS 语法文件自动创建解析器, 会创建自下而上的移位归约解析器。会将 CSS 文件解析成 StyleSheet 对象,且每个对象都包含 CSS 规则。CSS 规则对象则包含选择器和声明对象,以及其他与 CSS 语法对应的对象。如下:</li></ul><p><img src="/img/remote/1460000040050293" alt="image023.png" title="image023.png"></p><h3>RenderTree</h3><p>在 DOM 树构建的同时,浏览器还会构建另一个树结构:渲染树。这是由可视化元素按照其显示顺序而组成的树,也是文档的可视化表示。它的作用是让您按照正确的顺序绘制内容。</p><p>WebKits RenderObject 类是所有渲染树的基类,其定义如下:</p><pre><code>class RenderObject{
virtual void layout(); //布局
virtual void paint(PaintInfo); //绘制
virtual void rect repaintRect(); //重新绘制Rect
Node* node; // DOM节点
RenderStyle* style; // 计算render style
RenderLayer* containgLayer; //render layer
}</code></pre><h6>渲染树和 DOM 树的关系</h6><p>渲染树是和 DOM 元素相对应的,但并非一一对应。非可视化的 DOM 元素不会插入渲染树中,例如“head”元素。如果元素的 display 属性值为“none”,那么也不会显示在渲染树中(但是 visibility 属性值为“hidden”的元素仍会显示)。</p><p>关于多渲染树的例子是格式无效的 HTML。根据 CSS 规范,inline 元素只能包含 block 元素或 inline 元素中的一种。如果出现了混合内容,则应创建匿名的 block 渲染树,以包裹 inline 元素。</p><p>有一些渲染对象对应于 DOM 节点,但在树中所在的位置与 DOM 节点不同。浮动定位和绝对定位的元素就是这样,它们处于正常的流程之外,放置在树中的其他地方,并映射到真正的框架,而放在原位的是占位框架。</p><h3>布局</h3><p>创建完成渲染树时,并不包含位置和大小信息。计算这些值的过程称为布局或重排。</p><p>HTML 采用基于流的布局模型,这意味着大多数情况下只要一次遍历就能计算出几何信息。处于流中靠后位置元素通常不会影响靠前位置元素的几何特征,因此布局可以按从左至右、从上至下的顺序遍历文档。但是也有例外情况,比如 HTML 表格的计算就需要不止一次的遍历 (3.5)。</p><p>坐标系是相对于根框架而建立的,使用的是上坐标和左坐标。</p><p>布局是一个递归的过程。它从根渲染树(对应于 HTML 文档的 <html> 元素)开始,然后递归遍历部分或所有的框架层次结构,为每一个需要计算的渲染树计算几何信息。</p><p>根渲染树的位置左上角 是<code> 0,0</code>,(跟canvas2D坐标规则一致)其尺寸为视口(也就是浏览器窗口的可见区域)。<br>所有的渲染树都有一个<code>layout</code>或者<code>reflow</code>方法,每一个渲染树将会调用其需要进行布局的子代的<code> layout</code> 方法。</p><h3>绘制</h3><p>在绘制阶段,系统会遍历渲染树,并调用渲染树的<code>paint</code>方法,将渲染树的内容显示在屏幕上。绘制工作是使用用户界面基础组件完成的。</p><p>绘制的顺序其实就是元素进入堆栈样式上下文的顺序;从后向前。块渲染树的堆栈顺序如下:</p><ol><li>背景颜色</li><li>背景图片</li><li>边框</li><li>子代</li><li>轮廓</li></ol><p>绘制实现可参考<code>canvas</code>(<code>canvas一定要学的啊 时刻关注一些前沿的动作</code>),了解基础图元绘制方法与过程。例如绘制<code>line</code>(需要坐标 基础图元结构等等):</p><p><img src="/img/remote/1460000040050294" alt="image.png" title="image.png"></p><p><a href="https://ke.segmentfault.com/course/1650000038925941">图片来源</a></p><h2>最后</h2><blockquote>距离上一篇文章的生活日记: 20210521那天呢照样开心(朋友圈依旧不活跃)。20210522呢不开心。20210523也就是今天总的来说过得去 并没有学习。(劳逸结合 哈哈)</blockquote><p><strong>然后呢因为最近有人总微信我编程范式到底选择哪种好一点呢? 我现在用的更多是什么编程范式呢? 自己写代码维护起来好困难怎么办呢?... (做了很久的解答,应该记录下来提供给大家)下期JS引擎之前 我会把编程范式做一个简单介绍供大家参考 小小插曲(预计明天)。</strong></p><p><strong>加油!有问题请大家随时留言,看到一定会回复的。对于上篇补架构图...好吧是我鸽了 我看也没人催..懒得画了</strong></p><h2>相关参考</h2><ol><li><a href="https://link.segmentfault.com/?enc=KyTB8NkImvjz0WpvxRqESQ%3D%3D.DlYEuuwGNG9TNPzavXKhYXJHbPC0YBFeGweh0S74eYCyYzF3LiioqqBb0jq0bb%2B4" rel="nofollow">CSS 的词法和语法</a></li><li><a href="https://link.segmentfault.com/?enc=b0Eah2%2Bfw7ah%2FcHCNzXdxQ%3D%3D.x%2BNQmXpXVLYvf%2B8%2Fcjb0t%2FJ8fgraF9dYXY4ASYzmXlMVk5g2t%2BIOv4kS60E%2BaVbExZCe36zBQ%2FIBUDe42DDOFw%3D%3D" rel="nofollow">处理模型</a></li><li><a href="https://link.segmentfault.com/?enc=SYNDSelV982DI90JD1RTVw%3D%3D.2FJ26GnOBinYkqjMo3Ofh5T7CIfqC6AwRb%2FI%2BG9WxJs%3D" rel="nofollow">WebCore Rendering</a></li><li><a href="https://link.segmentfault.com/?enc=fY7EsDyq7BsthLhxzouCLg%3D%3D.icfbQzOZ7UkznCeeDCxrtDpCrJ9XOs69Z6fjPfbnDSk%3D" rel="nofollow">WebKit官网</a></li><li><a href="https://link.segmentfault.com/?enc=pZ4DnY1x84NbwpGVGzFSTg%3D%3D.5GKM%2BAkT70VQaW3xkdK2NQnNVMvjmtIhk3XTNwWliboH%2B785WS9CbGdi9pwHkiVe" rel="nofollow">WebKit介绍</a></li><li><a href="https://ke.segmentfault.com/course/1650000038925941">canvas相关</a></li></ol>
走进浏览器的幕后瞧瞧!!!
https://segmentfault.com/a/1190000040038340
2021-05-20T23:03:44+08:00
2021-05-20T23:03:44+08:00
wlove
https://segmentfault.com/u/dxgl
5
<h2>前言</h2><p>相信大家接触前端开发一段时间后,会发现越学习越迷茫,亦或者疑问点越来越多。(摸鱼摸的累了不如摸摸石头过过河 --)</p><p>举几个例子:<br>大家都知道js是单线程的,但是它为什么是单线程的? 浏览器是不是也是单线程?</p><p>相信大家看到过不少相关的内容比如:《从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理》《深入浅出JS引擎》等等这些优秀的博客讲解。但是多少对一些细节还是存有迷惑。甚至是一些非科班的同学可能看过之后就慢慢遗忘了。。。<br>其次还有一点就是个人认为碎片化的知识并不一定能陪伴你走的长久(520快乐!!)更希望的是能够全面的了解。这也是本系列博客的一个出发点,让我们一起探索浏览器幕后的三俩事。</p><blockquote>本系列尽可能的将术语进行大白话的方式解释。关于一些术语定义概念问题也会贴出参考链接提供给需要的人。</blockquote><h2>认知内容</h2><blockquote>随我梳理一下浏览器到底包含那些东西,以及一直耳熟能详的模块里面到底存在什么秘密。</blockquote><h3>浏览器是什么?</h3><p>浏览器是用来检索、展示以及传递<code>Web</code>信息资源的应用程序。注意和<code>SEO</code>(搜索引擎)进行区分。</p><h3>浏览器架构,实现包含哪些模块</h3><blockquote>按chrome(准确来说chromium)进行介绍说明 想找图来贴 但是怕太复杂不太好理解。完事画个简单的贴上来。</blockquote><ol><li>主进程 主要包含主线程和I/O线程(input/output输入输出)。以及一些公共API,资源;负责合成。</li><li>子进程 主要包含主线程和其他线程 比如渲染线程Render。</li><li>内核 主要包含WebCore V8 WebKit等等。</li><li>第三方库 主要包含图形库(2D,3D) 存储 音频视频等等。</li></ol><p>注意: 浏览器是多进程多线程架构,后续会对内部实现做一个简单认知(不会太脱离前端)。</p><h3>浏览器主要组件</h3><p><img src="/img/bVk7AU" alt="layers.png" title="layers.png"><br>ps:图来源网络 侵删请留言。</p><ol><li><code>UserInterface</code> 用户界面 - 包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗口显示的您请求的页面外,其他显示的各个部分都属于用户界面。</li><li><code>Browser Engine</code> 浏览器引擎 - 在用户界面和呈现引擎之间传送指令。</li><li><code>Rendering Engine</code> 渲染引擎 - 负责显示请求的内容。如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。</li><li><code>Networking</code> 网络 - 用于网络调用,比如 HTTP 请求。其接口与平台无关,并为所有平台提供底层实现。</li><li><code>UI Backend</code> 用户界面后端 - 用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。</li><li><code>JavaScript Interpreter</code> JavaScript 解释器。用于解析和执行 JavaScript 代码。</li><li><code>Data Persistence</code> 数据持久层。这是存储。浏览器需要在硬盘上保存各种数据,例如 Cookie。</li></ol><h3>浏览器内核是什么?</h3><p>在浏览器中,有一个最重要的模块,它主要的作用是将页面转变成可视化(准确讲还要加上可听化)的图像结果,这就是浏览器内核。通常,它也被称为渲染引擎。常见的内核包括:</p><ol><li>Trident内核 代表浏览器Internet Explorer(IE)</li><li>Gecko内核 代表浏览器Mozilla Firefox</li><li>WebKit内核 代表浏览器Safari</li><li>Blink内核 代表浏览器Chrome,备注说明该内核是基于WebKit内核拉取出的分支进行二次开发的(谷歌团队觉得自己走的步伐更快)</li><li>其他内核</li></ol><h3>JavaScript(js)引擎</h3><blockquote>我们常说的谷歌浏览器/nodejs的v8引擎,注意它不是渲染引擎,V8是js引擎,是用来编译和执行js代码。</blockquote><p>JavaScript引擎是一个专门处理JavaScript脚本的虚拟机,一般会附带在网页浏览器之中。通常被称为js引擎,举几个引擎(<del>好几次问面试者 你知道除了v8之外还有什么JS引擎吗?几乎是全军覆没。。</del>):</p><ol><li>JavaScriptCore 代表浏览器Safari</li><li>Rhino 代表浏览器Mozilla Firefox</li><li>Chakra 代表浏览器Internet Explorer(IE)</li><li>V8 代表浏览器 Chrome</li></ol><p><em>后续会展开介绍V8</em></p><h3>渲染引擎</h3><p><img src="/img/bV2q81" alt="渲染引擎过程.png" title="渲染引擎过程.png"><br>ps:图来源网络 侵删请留言。</p><p>简化理解起来就是:</p><ol><li>解析HTML构建DOM树</li><li>构造渲染树</li><li>对于渲染树进行布局</li><li>绘制渲染树<br> 值得注意的是在解析过程中:js代码可能会修改DOM树结构。如果解析过程中依托外部资源会利用加载器进行加载(异步加载)但也会进行等待加载完毕在进行构建DOM树。</li></ol><h2>最后</h2><p>预知后事如何请听下次分解。</p><p>首先呢回顾一下今天 总体来说挺开心,朋友圈社交圈比往年安静祥和许多。所以决定写写文章回报广大单身贵族和奋战在一线的你们。</p><p>还有答应的系列内容一定会有后续。JS引擎内部 渲染引擎 网络等等这些必须写。</p><p>最后再次祝大家5,20快乐。希望明天5,21继续祥和~ </p><p>"下课!"</p>
聊一下前端工程化
https://segmentfault.com/a/1190000039729806
2021-03-29T09:00:00+08:00
2021-03-29T09:00:00+08:00
wlove
https://segmentfault.com/u/dxgl
3
<h2>前言</h2><pre><code>最近呢筛选了很多候选人的简历,看到很多关于前端工程化的描述:
> "个人一直致力于前端工程化..."
> "在公司前端工程化规范,自动化..方面的建设做出了巨大改革..."
...
首先我抱有的希望很大(人才嘛谁能不爱..)
然后呢经过交流探讨给我带来更多的反馈是失望。当然这个领域是`非主流前端领域`。
但是个人觉得还是很有趣味而且有必要进行学习的;所以今天笔者简单的聊一下前端工程化,希望能给大家带来帮助。
</code></pre><h3>回顾一下前端发展史</h3><p>笔者为了照顾到不同时间段融入前端大家庭的读者,这边对于前端研发"方式"发展进行一个简单的回顾;回顾经历过几个阶段,从而了解"工程化"提出的必要性。</p><ol><li><code>前后端不分离的时代</code>:那时候所谓的前端担任的工作更多是写页面。 然后还有就是通过一些技术(ASP,JSP...)将数据进行页面填充;涉及到这部分技术更偏向于后端,所以前端可能也不并会涉及到这部分工作。(当然也会有扯皮的时候,谁都不愿意干。。那就是看谁没话语权咯)</li><li><code>前后端半分离时代</code>: 在一部分工作俩边扯来扯去的背景下,就研究着如何进行前后端分离,方便更好的实现团队之间的协作分工。出来很多技术方案 比如在node发展的背景下出现 SSR(server side render) + Node的技术方案;就是服务端将处理好的html模板直接给到前端,前端负责展示。</li><li><code>前后端逐步完全分离</code>: 在各种技术的推进下慢慢出现了逐步前后端完全分离的各类架构方案例如RESTful架构;也就是双方通过协议(http/https/socket)进行调用进行通信数据传输。这样的话彼此的工作可能就不会有太多的扯皮。但是伴随还有一些问题,比如API的设计谁来定的问题,前端定义接口的话可能会更理想化(服务端那边呢有内部的一个大的技术架构服务比如跨集群这些,在这个背景下可能实现确实会比较困难 [也要帮后端兄弟说句话 哈哈.]);所以个人认为更多的是双方合理的讨论设计来进行推动。</li><li>....</li></ol><h3>什么是前端工程化?</h3><p>在研发技术方案的日益更新背景下。前端已经从"page开发工程师"逐步变成了"app研发工程师";前端的工作随着业务的复杂,用户的体验提升已经不是一个简单的工作了,面临着合作问题,性能问题等等...所以在模式转变下伴随着迎来很多问题:</p><ol><li>如何提供团队开发效率?</li><li>如何提高项目研发质量?</li><li>如何加强项目可维护性?</li><li>如何引进更多的人才?</li><li>...</li></ol><p>为了解决出现的一系列问题就需要选取好的技术架构方案,推进前端工程化的落地。工程化大概包含几个方面呢?</p><ol><li><p>整体规范化</p><ul><li>编码规范:项目结构规范;html,js,css编码规范;命名规范;接口规范等等。</li><li>协同规范:文档规范;资源管理规范;视觉|交互规范等等</li></ul></li><li><p>开发模式组件化</p><ul><li>模块组件化:公共css提取;公共js提取;公共资源提取等等</li><li>技术组件化:base函数库组件化;公共page组件化等等</li><li>业务型组件化:同类业务重叠功能组件化等等</li></ul></li><li><p>工具化&自动化</p><ul><li>研发工具化:资源加载整合</li><li>自动化构建研发生成环境</li><li>自动化部署,单元测试</li><li>持续化集成...</li></ul></li><li><p>技术优化,性能优化,方案优化...</p><ul><li>技术优化 大前端的背景下 一定要技术革新进步 别还是冷兵器时代 <code>Jquery</code>一把梭; MVC MVVM的设计模式的优势相信大家都是不言而喻,那么为什么不选择进步呢。</li><li>性能优化 2C的业务下这个是必然会经历的一个阶段,用户的体验越来越重要。考虑的不光是程序的实现还有用户的体验简单来说一个首屏加载的时间可能就会导致用户的流失。</li><li>方案优化 这方面比如团队来说并不一定有机会推行工程化每一个细节的落地,强行推这个整体方向可能会适得其反;没有最好的只有最合适的。</li></ul></li></ol><h3>工程能够解决什么?</h3><h6>下面通过一个简单的日常现象看一下能够帮我们解决什么问题.</h6><p>首先在非工具化(webpack构建生成)的背景下很多研发人员都会遇到一个问题。每次项目部署的时候需要使用者(测试,产品,用户)去手动清理浏览器的缓存;这就是一个很头疼的问题;每次解释也挺累的。"静态资源要清下缓存..."那么如何从工程方面解决这个问题呢?</p><pre><code>
...
<link herf ="XXXX.css">
...
<script src="XXX.js">
....
</code></pre><p>如上代码看起来是没有什么问题 为什么程序需要每次去清理浏览器缓存呢。原因是现代浏览器为了性能,开支方面的考虑。解决资源加载问题做出了缓存机制 相同静态资源会做出本地的缓存。那么如何解决这个问题呢?很简单请看下面伪代码:</p><pre><code>...
<link herf ="XXXX?+'new Date()'.css">
...
<script src="XXX?+'new Date()'+.js">
// 不能这样写哈 示意
....</code></pre><p>很多细心的同学在构建工程生产打包的时候已经看到了比如webpack打包它的文件生成一般是<code>"chunk" + hash + .js/css</code> 就是和上面一个解决思路。但是又有问题了 如何决定每个文件到底需不需要进行缓存的问题来做性能优化呢? 比如一些图片啊这些就不需要每次重新载入多浪费带宽资源啊?这个问题留给大家思考一下。或者看一下webpack这些构建工具是如何解决的。</p><h3>结束啦</h3><p>前端工程化的必要性以及对于个人的成长是起到了很大的作用;相信大家都有了些许了解。</p><blockquote>最后笔者认为大家有必要去学习一下。也能在简历上面显得不那么单调。很多人就只会写"精通各种语言,熟悉各种技术..."。哎呀这样只会让你的面试官更加的刁难你。每个语言设计者可能都不敢说自己对于每天在变新的语言每个细节都熟悉。比如就<code>JavaScript</code>来说在日益发展背景下光标准就几百页;谁能记得过来。要学会培养自己语言外的工程能力,架构能力。。加油~ 有任何需要交流的随时留言。</blockquote>
关于一个问题 为什么外层套一个立即执行函数
https://segmentfault.com/a/1190000020269175
2019-09-03T11:02:48+08:00
2019-09-03T11:02:48+08:00
wlove
https://segmentfault.com/u/dxgl
5
<h2>前言</h2>
<p>个人输出的动力就是问题的深入 并不是看表皮功夫 然后呢今天就碰到了这个我认为不是问题的问题 偏偏就是问题 然后呢我就梳理一下我的答案说明一下 有任何疑问都可以评论</p>
<h2>正题</h2>
<p>这是题注的问题:</p>
<pre><code> 这个是es6标准入门里面的一段代码:
var pipe = (function () {
return function (value) {
var stack = [];
var proxy = new Proxy({}, {
get(arrObj, fnName) {
if (fnName === 'get') {
stack.reduce((val, fn) => {
return fn(val);
}, value);
}
stack.push(window[fnName]);
return proxy;
}
});
return proxy;
}
})()
var double = n => n * 2;
var pow = n => n * n;
var reverseInt = n => n.toString().split('').reverse().join('') | 0;
pipe(3).double.pow.reverseInt.get;
在这里为什么要用到一个自执行的匿名函数呢, 完全可以直接写
var pipe = function(value) {
.............
}
</code></pre>
<blockquote>以上是他的原问题,然后我的回答关键字是<code>回收机制</code>还有<code>闭包</code>还有<code>写法区别</code> 然后另外一个答主说是<code>独立作用域 写法习惯</code>然后我被pass了 另外一个被采纳了......</blockquote>
<ol>
<li>首先分析某答主的答案 是独立作用域 当然每一个函数声明都有独立作用域 我不否定但是和问题有关系吗? 就被采纳了...写法习惯不敢苟同</li>
<li>接下来具体说一下我的答案 咀嚼一下 好让大家明白我为什么这么回答</li>
</ol>
<h3>主要从回收机制来说这个问题:</h3>
<p>回收机制规则大概如下:</p>
<pre><code> 1.全局变量不会被回收。
2.局部变量会被回收,也就是函数一旦运行完以后,函数内部的东西都会被销毁。
3.只要被另外一个作用域所引用就不会被回收</code></pre>
<blockquote>第三点 显然题目中是符合的呢 也就是闭包的概念, <code>proxy</code> 第二参数行为<code>函数</code> 使用了var stack = []该变量,那么就不会进行回收 也就是<code>闭包</code>.</blockquote>
<p>立即执行函数(immediately-inovked-function expression):<br>简称 IIFE。立即执行函数就是在定义的时候就立即执行,执行完以后就释放,包括函数内部的<code>所有</code>变量。</p>
<blockquote>然后对应他的问题 我想知道这个问题是我答错了?<br>然后说我抬杠....</blockquote>
<h2>贴个链接<a href="https://segmentfault.com/q/1010000020267312?_ea=18234830">问题地址</a>
</h2>
<h3>更新疑问 2019/9/3 16.55</h3>
<blockquote>这个问题问的是为什么外层套一个立即执行函数(IIFE) 我的出发点难道不是说这个IIFE起到什么作用? 不包IIFE有什么缺点?<p>难道回答一个独立作用域就解决了? 莫非不包这个IIFE <code>function (value) {} </code>它没有独立作用域?</p>
</blockquote>
随我来基于webpack构建一个简易的vue脚手架 (webpack系列二)
https://segmentfault.com/a/1190000016986938
2018-11-12T17:48:46+08:00
2018-11-12T17:48:46+08:00
wlove
https://segmentfault.com/u/dxgl
23
<h2>前言</h2>
<blockquote>之前有写了一篇webpack的文章(认识篇) <a href="https://segmentfault.com/a/1190000016927436">猛戳</a>,大家对于<code>webpack</code>基本概念<code>(entry,output,loader,plugin,mode...)</code>应该是有了较模糊的认识.今天希望在通过(对于<code>vue-cli</code>的效仿)搭建一个自己的脚手架的途中对于概念会有更<code>深刻</code>的认识.</blockquote>
<h2>目录</h2>
<p>1:搭建自己的项目模板<code>(template)</code> (基于vue的模板)</p>
<p>2:生成对应的<code>init</code>命令,也就是脚手架构建命令以及上传<code>NPM</code>包 , 方便之后模板的使用 (分开俩篇来讲,方便你我 下一篇见)</p>
<hr>
<hr>
<h3>一: 模板构建</h3>
<p><em>先来个鸡汤 (这是个什么玩意啊怎么这么简单,我没问题分分钟掌握它)</em> <code>摆正心态 </code> 那么<code>follow me !!!</code></p>
<p><img src="/img/bVbjreD?w=900&h=500" alt="clipboard.png" title="clipboard.png"></p>
<h4>初步构建</h4>
<pre><code>mkdir my-vue-cli && cd my-vue-cli // 新建一个文件 并进入更目录 `mkdir 是linux命令`
npm init -y // 初始一个packjage.json文件 -y 表示跳过询问步骤...
</code></pre>
<blockquote>完善项目结构</blockquote>
<pre><code>//生成如下目录
├── src //源目录(输入目录)
│ ├── index.js
│ ├── app.vue
+ |── index.html
├── package.json
|── webpack.config.js //webpack配置文件
</code></pre>
<blockquote><code>下载所需要的依赖(不太清楚的依次会介绍一下)</code></blockquote>
<ol>
<li>
<code> npm install --save-dev vue</code> <br> 基于vue的那么vue必不可少...不多介绍</li>
<li>
<code> npm install --save-dev webpack</code> <br> 基于webpack的那么webpack也必不可少...不多介绍</li>
<li>
<code> npm install --save-dev webpack-cli</code> <br><code>webpack version 4+</code> 需要下载<code>webpack-cli</code>(一些指令下文可能涉及到)</li>
<li>
<code> npm install --save-dev path</code> <br><code>path</code> 模块提供了一些工具函数,用于处理文件与目录的路径。</li>
<li>
<code> npm install --save-dev html-webpack-plugin</code> <br> 简化了HTML文件的创建 <code>Plugin that simplifies creation of HTML files to serve your bundles</code>
</li>
<li>` npm install --save-dev clean-webpack-plugin <br> 用于构建时清理构建文件夹下的内容 <code>A webpack plugin to remove/clean your build folder(s) before building</code>
</li>
<li>
<code> npm install --save-dev vue-loader</code> <br> Vue.js组件加载器(插件)</li>
<li>
<code> npm install --save-dev vue-template-compiler</code> <br> 对于模板的函数编译 与vue-loader 配合使用</li>
<li>
<code> npm install --save-dev webpack-dev-server</code><br> 热更新需要搭建服务模块</li>
<li>
<code> npm install --save-dev style-loader css-loader</code> <br> css样式处理器</li>
</ol>
<blockquote><code>项目代码构建</code></blockquote>
<p><strong><code>src/index.js</code></strong></p>
<pre><code>import Vue from 'vue' // 引入vue模块
import App from './app.vue' //引入文件(组件) app
new Vue({ //vue写法 新建一个实例
el:"#app", //元素
template:'<App/>', // 模板使用标签<app/>
components:{App} // 组件app
})
</code></pre>
<p><strong><code>src/app.vue</code></strong></p>
<pre><code><template>
<div id="app">
<p class="test">vue-cli-test vue-cli-test vue-cli-test </p>
<p class="test">{{msg}}</p>
</div>
</template>
<script>
import Vue from 'vue'
export default {
name:'app',
data(){
return {
msg:"hello vue !!"
}
},
}
</script>
<style >
.test{
color:#020202;
font-size:18px;
}
</style>
</code></pre>
<p><strong><code>webpack.config.js</code></strong></p>
<pre><code>const path = require('path'); //path 模块提供了一些工具函数,用于处理文件与目录的路径。
const HtmlWebpackPlugin = require('html-webpack-plugin'); //构建html文件
const CleanWebpackPlugin = require('clean-webpack-plugin'); // 清理构建目录下的文件
const webpack = require('webpack'); //webpack打包工具
const VueLoaderPlugin = require('vue-loader/lib/plugin'); // vue-loader 编译vue文件
const compiler = require('vue-template-compiler') // 模板函数编译 与vue-loader配合使用
module.exports = {
entry: { //入口
"app":"./src/index.js"
},
module:{ //处理项目中的不同类型的模块。
rules:[ // rules 各种规则(数组类型) 每个规则可以分为三部分 - 条件(condition),结果(result)和嵌套规则(nested rule)
{
test:/\.css/,
use: ['style-loader', 'css-loader'] // style-loader 和css-loader 编译css处理
},
{
test: /\.vue$/,
loader: 'vue-loader' //vue-loader 编译vue模块
}
]
},
devtool: 'inline-source-map', //生曾map 映射对应代码 方便错误查询
devServer:{
contentBase: './dist', // 告诉服务从哪提供代码内容(静态文件这么使用)
hot:true //hot模式开启
},
plugins:[
new CleanWebpackPlugin(['dist']), // 告诉清理插件的目录
new HtmlWebpackPlugin({ // 构建html
filename:'index.html', //文件名
title:'my-vue-cli', //title
template:'./index.html', //参照模板样式
}),
new webpack.HotModuleReplacementPlugin(), //热模块替换开启
new VueLoaderPlugin() //vue-loader插件开启
],
output: { //出口
filename: 'index.js', //文件名
path: path.resolve(__dirname, 'dist'), //路径
publicPath:"" //srcript 引入路径
},
resolve:{
//引入路径是不用写对应的后缀名
extensions: ['.js', '.vue', '.json'],
alias:{
//正在使用的是vue的运行时版本,而此版本中的编译器时不可用的,我们需要把它切换成运行时 + 编译的版本
'vue$':'vue/dist/vue.esm.js',
//用@直接指引到src目录下
'@': path.resolve(__dirname,'./src'),
}
},
};
</code></pre>
<p><strong><code>package.json</code>添加<code>script</code>命令</strong></p>
<pre><code>"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"watch": "webpack --watch",
"dev": "webpack-dev-server --open --hot",
"build": "webpack"
},
</code></pre>
<blockquote>
<code>npm run dev</code> 运行于8080/可看到预期效果.<br><code>npm run build </code> 打包编译同样可以看到效果 <code> skr~~~~~~~~~</code>
</blockquote>
<p><img src="/img/bVbjrfX?w=583&h=176" alt="clipboard.png" title="clipboard.png"></p>
<p><a href="https://link.segmentfault.com/?enc=dJkiwQrNaUevtIeyL74cpw%3D%3D.pnPEg6q2NnRl0B6dW4uL%2FUds8U5fdvgC9711X9kd06wlr31%2BIt3CxsfZOMT58Dbj" rel="nofollow">github代码仓库,猛戳</a></p>
<h2>结尾</h2>
<blockquote>本篇只是介绍基于vue,webpack的vue-cli简易搭建(因为init构建命令这些说好讲是好讲,但是讲太粗怕大家不太明白,那不如单独拿一篇出来让大家看),根据本文大家可以根据需求进行完善搞一个自己的脚手架.之后用自己的开发..是不是想想挺美~~ 快去行动吧.</blockquote>
<h3>想提前看看构建命令效果的同学 ></h3>
<h3><a href="https://link.segmentfault.com/?enc=%2FYQyBsVGq0cDwi2oTflLOA%3D%3D.11i%2Fh%2B4iJLGdKjilhzxezwRScTtlN4cUe3ScUPImSXdYp%2BIVsnmECVV7kc%2BqVGeD" rel="nofollow">git仓库</a></h3>
<p>ps:(有我讲的不明白的地方,评论区见.我来完善)</p>
还在写冗长的 if else判断在你的代码中吗?
https://segmentfault.com/a/1190000016941158
2018-11-07T18:40:12+08:00
2018-11-07T18:40:12+08:00
wlove
https://segmentfault.com/u/dxgl
54
<h2>前言</h2>
<blockquote>今天无意间看到一篇文章(- -。忘记哪了..我大概说一下吧,本来可以直接分享的...),对于平时冗长的<code>if else</code>的<code>优化</code>. 平时也是这么处理的 通过<code>object</code>对象的数据结构来实现优雅的判断条件书写! 但是看到通过<code>map</code>数据结构的利用 感觉适用更广,局限更低了 !一起来看看</blockquote>
<h4>首先对于<code>Map</code>数据结构来做个简单介绍:</h4>
<blockquote>
<p>定义:</p>
<blockquote>
<code>Map</code> 对象保存键值对。<code>任何值(对象或者原始值)</code> 都可以作为一个键或一个值。</blockquote>
<p>语法:</p>
<blockquote>
<code>new Map([iterable])</code> <code>Iterable</code> 可以是一个数组或者其他 <code>iterable</code> 对象,其元素或为键值对,或为两个元素的数组。 每个键值对都会添加到新的 <code>Map</code>。<code>null</code> 会被当做 <code>undefined</code>。</blockquote>
<p>方法:</p>
<blockquote>
<code>Map.prototype.get(key)</code> 返回键对应的值,如果不存在,则返回<code>undefined</code>。<br><code>Map.prototype.has(key)</code> 返回一个布尔值,表示Map实例是否包含键对应的值。<br><code>Map.prototype.set(key, value)</code> 设置Map对象中键的值。返回该Map对象。<br><code>对于Map数据结构来说,不支持 = 号的赋值~~~~~~~</code>
</blockquote>
<p><a href="https://link.segmentfault.com/?enc=dSNA44G4ewZJ5G6%2FiBCDog%3D%3D.OzMArq13cV8UntcWVtypKjIXuPr82FZN0dn9Cm9aXbmLPBqiW3UpDUAWSlqlHLXvOZxio%2BobcQFdsrBE79TMmZA6GRUwX8yZXQZD6nbVjiP6CO9doyXxLpOjq7LH%2Fyij" rel="nofollow">关于Map其他介绍API,**不多介绍了</a></p>
</blockquote>
<h2>正文</h2>
<blockquote>对于判断条件的单一</blockquote>
<pre><code> var status = 8;
// 常用的if else 进行 条件判断来do somethings
if(status == 1){
console.log(111111)
}else if(status == 2){
console.log(222222)
}else if(status == 3){
console.log(333333)
}else if(status == 4){
console.log(444444)
}else if(status == 5){
console.log(555555)
}else{
console.log(status)
} // 8
// switch case的写法 相比if else 是有一些优化了!
switch (status){
case 1:
console.log(status)
break
case 2:
console.log(status)
break
case 3:
console.log(status)
break
case 4:
console.log(status)
break
case 5:
console.log(status)
break
default:
console.log(status)
break;
} // 8
// 对象object 数据结构的写法 简洁了
var obj = {
"1":"11111",
"2":"22222",
"3":"33333",
"4":"44444",
"5":"55555",
}
console.log(obj[status] || status) // 8
// Map数据结构的写法 和object差不多
var mMap = new Map([
["1","11111"],
["2","22222"],
["3","33333"],
["4","44444"],
["5","55555"]
])
console.log(mMap.get(status) || status) // 8
</code></pre>
<blockquote>结果都可以达到预期的效果! 判断进行的顺利 ! 然而条件是个<code>多个条件呢? 范围呢? 条件是个运算呢?</code> <code>怎么实现? 接着看</code>
</blockquote>
<pre><code> var name = "lisi" , status = 1;
//if else 写法
if(name == "lisi"){
if(status == 1){
console.log("lisi1")
}else if(status == 2){
console.log("lisi2")
}else if(status == 3){
console.log("lisi3")
}else if(status == 4){
console.log("lisi4")
}else if(status == 5){
console.log("lisi5")
}else{
console.log(status)
}
}else if(name == "zhangsan"){
if(status == 1){
console.log("zhangsan1")
}else if(status == 2){
console.log("zhangsan2")
}else if(status == 3){
console.log("zhangsan3")
}else if(status == 4){
console.log("zhangsan4")
}else if(status == 5){
console.log("zhangsan5")
}else{
console.log(status)
}
} //lisi1
//swtich case 写法
switch (status && name){
case 1 && "lisi":
console.log(name + status)
break
...
default:
console.log(status)
break;
} //lisi1
// 对象数据结构的写法 //简洁
var obj = {
"lisi_1":"lisi1",
"lisi_2":"lisi2",
...
"zhangsan_5":"zhangsan5",
}
console.log(obj[name + "_" + status] || status) // lisi1
// Map数据结构的写法 和object差不多
var mMap = new Map([
["lisi_1","lisi1"],
["lisi_2","lisi2"],
...
["zhangsan_5","zhangsan5"]
])
console.log(mMap.get(name + "_" +status) || status) //lisi1
</code></pre>
<blockquote>多个条件也进行了对比,都可以完美实现,书写上相对于来说更为简洁 当然可读性较低一点.. 性能差异肯定也存在. 不过对于平日的基础业务可以忽略不计.接下来对于<code>运算,范围</code>用<code>Map</code>来实现一下 来了解一下~</blockquote>
<pre><code> var mMap = new Map([
[162,function(h,a){console.log("he height is" + h + " ,he age is" + a)}],
[174,function(h,a){console.log("he height is" + h + " ,he age is" + a)}],
[198,function(h,a){console.log("he height is" + h + " ,he age is" + a)}],
])
var height = 150, age = 12;
mMap.get(height + age)(height,age) //he height is150 ,he age is12
//正则
var mMap = new Map([
[/^\d{2,5}$/,function(h,a){console.log("位数大于2且小于5")}],
[/^\d{5,10}$/,function(h,a){console.log("位数大于5且小于10")}],
])
var arr = [...mMap].filter(([k,v])=>(k.test(`123`)))
arr.forEach(([k,v])=>v.call(this)) //位数大于2且小于5
</code></pre>
<blockquote>
<code>Map结构是支持任何对象任何原始值作为key|value的,所以你们可以开动大脑再试试其它,我就不介绍了.明白这样写就好</code>, 当然可以适当封装,但是这个业务代码耦合性略高,封装意义不大,此处就不做说明了!</blockquote>
<h2>最后</h2>
<pre><code>有小伙伴看过那篇的可以评论区贴一下,(那篇文章篇幅比我长...比我肯定全一些..)我只是简单的介绍了一下.平日都这么用.分享给大家.</code></pre>
<blockquote>关于<code>webpack后续</code>的文章 周一见 !</blockquote>
还不打算去认识一下webpack?
https://segmentfault.com/a/1190000016927436
2018-11-06T19:38:10+08:00
2018-11-06T19:38:10+08:00
wlove
https://segmentfault.com/u/dxgl
50
<h2>前言</h2>
<blockquote>随我来,去看看webpack!(<code>为时未晚</code>)<em>============》第一版(较浅显的知识,懂得可忽略本文)</em>
</blockquote>
<hr>
<h2>方向</h2>
<ol>
<li>安装,起步搭建运行. <code>(粗略代过)</code>
</li>
<li>对于资源的管理,对于输出的管理. <code>(举例介绍)</code>
</li>
<li>本地开发 <code>(基础服务)</code>
</li>
<li>热更新=[模块热替换] <code>(初步认识)</code>
</li>
</ol>
<hr>
<h3>1.初步构建</h3>
<pre><code>mkdir webpack_demo && cd webpack_demo // 新建一个文件 并进入更目录 `mkdir 是linux命令`
npm init -y // 初始一个packjage.json文件 -y 表示跳过询问步骤...
//安装webpack
npm install webpack --save-dev // 添加webpack-cli依赖到"devDependencies"
//webpack4.0+ 需要安装webpack-cli
npm install webpack-cli --save-dev // 添加webpack-cli依赖到"devDependencies"
</code></pre>
<pre><code>//生成如下目录
├── package.json
├── src //源目录(输入目录)
│ ├── index.js
├── dist // 输出目录
│ ├── index.html
</code></pre>
<pre><code>// 修改 `dist/index.html`
< !DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webpack_demo</title>
</head>
<body>
<script src="main.js"></script> //为什么是main.js下面会解释
</body>
</html>
//修改`src/index.js `
function component() {
var element = document.createElement('div');
element.innerHTML = "整一个盒子"
return element;
}
document.body.appendChild(component());</code></pre>
<blockquote>
<code> npx webpack</code> (Node 8.2+ 版本提供的 npx 命令) <br><code>node node_modules/.bin/webpack</code> (8.2-版本)</blockquote>
<h5>会将我们的脚本作为<code>入口起点</code>,然后 输出 为 <code>main.js</code>.</h5>
<blockquote>
<code>打开dist/index.html 你将会看到 </code>整一个盒子<code> 几个字样~</code>
</blockquote>
<h3>2.资源管理,输出管理.基本开发起步</h3>
<pre><code>//生成如下目录
├── package.json
+ |── webpack.config.js //webpack配置文件
├── src //源目录(输入目录)
│ ├── index.js
├── dist // 输出目录
│ ├── index.html
</code></pre>
<blockquote>先介绍一个<code>Lodash</code>库 它是一个<code>一致性、模块化、高性能</code>的 JavaScript 实用工具库 模块化处理非常适合<code>值操作和检测</code>(<code>说白了就是webpack用了我也试试...</code>) <br><a href="https://link.segmentfault.com/?enc=08UG7KQW4utgW8HvLFl0Ow%3D%3D.AQInmos1Dc%2FGxcU2ayNOOo7u1dgwRyBBuWK%2F4m%2FYeNFOQX5wE%2BZ79l21MruHlTU6" rel="nofollow">lodash相关文档</a><p><code>npm install lodash --save //非仅在开发的时候使用的依赖 就是需要打包到生产环境的包 不加-dev</code></p>
</blockquote>
<pre><code>
// src/index.js
import _ from 'lodash';
function component() {
var element = document.createElement('div');
element.innerHTML = _.join(['lodash','webpack'],''); //join将 array 中的所有元素转换为由''分隔的字符串 其它函数可以自己实践
return element;
}</code></pre>
<blockquote><code>打开index页面输出 loadshwebpack</code></blockquote>
<pre><code>//webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js', //入口
output: { //出口
filename: 'main.js', //打包之后脚本文件名称
path: path.resolve(__dirname, 'dist') //路径指向执行 js 文件的绝对路径 此处为/dist
}
};</code></pre>
<blockquote>执行<code>npx webpack --config webpack.config.js (把之前dist目录下main.js删除) </code> 新的脚本生成(其实没多大变化..)</blockquote>
<pre><code>// 配置一下package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack" //添加此行命令 下次执行打包就是 npm run build 相当于上面的npx webpack --config webpack.config.js
},
// 资源的配置 css 图片 js等等.. 举例 css 图片
</code></pre>
<blockquote>
<code>npm install --save-dev style-loader css-loader</code> css的loader<br><code>npm install --save-dev file-loader</code> file(图片)对象的 loader</blockquote>
<pre><code> //生成如下目录
├── package.json
+ |── webpack.config.js //webpack配置文件
├── src //源目录(输入目录)
│ ├── index.js
+ │ ├── index.css
+ │ ├── icon.jpg
├── dist // 输出目录
│ ├── index.html
</code></pre>
<pre><code>//修改webpack.config.js
const path = require('path'); //path路径模块
module.exports = {
entry: './src/index.js', //入口
output: { //出口
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/, //检测正则匹配.css结尾的文件
use: [ //使用俩个loader
'style-loader',
'css-loader'
]
},
{
test: /\.(png|svg|jpg|gif)$/, //正则匹配.png svg jpg gif结尾的文件
use: [ //使用file-loader
'file-loader'
]
}
]
}
};
//修改src/index.css
div{
color:red;
}
//修改src/index.js
import _ from 'lodash';
import "./index.css";
import Icon from './icon.jpg';
function component() {
var element = document.createElement('div');
element.innerHTML = _.join(['loadsh', 'webpack'], ' ');
var myIcon = new Image();
myIcon.src = Icon;
element.appendChild(myIcon);
return element;
}
document.body.appendChild(component());
</code></pre>
<blockquote>
<code>npm run build(删除之前的dist目录下main.js) 你会看红字和图片</code> 以上就是资源管理的简短介绍</blockquote>
<blockquote>
<code>npm install --save-dev html-webpack-plugin 安装html-webpack-plugin模块</code> 模块用到功能:<br> 1: 动态添加每次compile后 js css 的hash<br> 2: 可配置多页面 单页面 这些 <br> 3: 其它没涉及到<br><code>npm install clean-webpack-plugin --save-dev 清除dist文件夹(每次删除麻烦了..)</code>配置一下</blockquote>
<pre><code>//修改目录
├── package.json
|── webpack.config.js //webpack配置文件
├── src //源目录(输入目录)
+ │ ├── app.js
+ │ ├── print.js
│ ├── index.css
│ ├── icon.jpg
├── dist // 输出目录
│ ├── index.html
</code></pre>
<pre><code>//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',
print: './src/print.js'
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: 'webpack_demo'
})
],
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
]
}
};
//修改src/index.js ===================================================
import _ from 'lodash'; //引入lodash模块
import "./index.css"; // index.css
import Icon from './icon.jpg'; // 图片
import printMe from "./print.js" // printJS
function component() {
var element = document.createElement('div'); //创建一个元素
element.innerHTML = _.join(['loadsh', 'webpack'], ' '); // lodash中_.join方法
var myIcon = new Image(); //创建一个图片
myIcon.src = Icon; //src赋值
element.appendChild(myIcon); //追加图片
var btn = document.createElement('button'); //创建按钮
btn.innerHTML = 'Click me and check the console!'; //内容赋值
btn.onclick = printMe; //添加事件
element.appendChild(btn); //追加元素
return element;
}
document.body.appendChild(component()); //追加元素到body中
//修改src/print.js ==========================================
export default function printMe() {
console.log('from print.js');
}
</code></pre>
<blockquote>
<code>npm run build 会发现基本webpack的配置之后 ,有点模样(意思)了</code> 打开页面index.html正常访问</blockquote>
<h3>3.本地开发</h3>
<blockquote>
<code>npm install --save-dev webpack-dev-server </code> <code> "webpack-dev-server" 为你提供了一个简单的 web 服务器,并且能够实时重新加载</code>
</blockquote>
<pre><code> //修改webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: {
app: './src/index.js'
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
devServer: {
contentBase: './dist'
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: 'webpack_demo'
})
],
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
]
}
};
//修改package.json
...
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack-dev-server --open", //start命令
"build": "webpack"
},
...
</code></pre>
<blockquote><code>npm run start 本地起了8080端口的服务,你也可以看到自己的页面</code></blockquote>
<h3>4.热更新</h3>
<pre><code>//修改webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: {
app: './src/index.js'
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
devServer: {
contentBase: './dist',
hot: true
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: 'webpack_demo'
}),
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
],
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
]
}
};
</code></pre>
<blockquote>
<code>npm run start 运行http://localhost:8080/ 然后你去修改print js的console(或者添加其他代码) 会发现命令行输出updated. Recompiling... 字样</code> 这就是简单的实现了热更新</blockquote>
<h2>最后</h2>
<p>本文只是大概从几个demo来对于webpack的基础概念 <code>入口entry 出口 output</code> <code>loader</code> <code>plugins mode(没有直面涉及)</code>几大模块的梳理于实践,让大家对于webpack不在那么陌生!</p>
<blockquote>
<p>后续文章会从更深入的角度去学习webpack! 暂定下周1 发表文章(内容 详细介绍<code>hot</code> 实现一个<code>简易的vue-cli</code>等等)</p>
<blockquote>demo的代码我会同步<a href="https://link.segmentfault.com/?enc=HfMdXHN2wN8T34VdzVCdWw%3D%3D.3u%2B7COEvrhdM2cMdk97Tlv4bl6%2Fj0S7V4oI6GC7o5%2FXkWq8CSwEXEo%2FsSghleVbzpZntVl8j9W5AlVJDcc722jCwQYQeuMw%2FLJGhDMvVlZU%3D" rel="nofollow">github</a>
</blockquote>
</blockquote>
<h3>ps:</h3>
<blockquote>第二版 : <a href="https://segmentfault.com/a/1190000016986938">webpack构建一个简易的my-vue-cli</a>
</blockquote>
VUE使用中踩过的坑
https://segmentfault.com/a/1190000013008420
2018-01-26T17:46:23+08:00
2018-01-26T17:46:23+08:00
wlove
https://segmentfault.com/u/dxgl
61
<h3>前言</h3><p>vue如今可谓是一匹<strong>黑马</strong>,github star数已居第一位!前端开发对于vue的使用已经越来越多,它的优点就不做介绍了,<br>本篇是我对vue使用过程中以及对一些社区朋友提问我的问题中做的一些总结,帮助大家踩坑。如果喜欢的话可以点波赞,或者关注一下,希望本文可以帮到大家!!!</p><p>----------我是分割线</p><h3>本篇介绍的问题大概如下:</h3><ol><li>路由变化页面数据不刷新问题</li><li>setTimeout/setInterval(泛指异步回掉函数的this指向)this指向改变,无法用this访问VUe实例</li><li>setInterval路由跳转继续运行并没有及时进行销毁</li><li>vue 滚动行为用法,进入路由需要滚动到浏览器底部 头部等等</li><li>实现vue路由拦截浏览器的需求,进行 一系列操作 草稿保存等等</li><li>v-once 只渲染元素和组件一次,优化更新渲染性能</li><li>vue本地代理配置 解决跨域问题,仅限于开发环境</li><li>本地开发 没有任何问题 部署服务器 就404啊这些问题</li></ol><h3>1. 路由变化页面数据不刷新问题</h3><p>出现这种情况是因为依赖路由的params参数获取写在created生命周期里面,因为相同路由二次甚至多次加载的关系 没有达到监听,退出页面再进入另一个文章页面并不会运行created组件生命周期,导致文章数据还是第一次进入的数据</p><p>解决方法:watch监听路由是否变化</p><pre><code> watch: {
// 方法1
'$route' (to, from) { //监听路由是否变化
if(this.$route.params.articleId){// 判断条件1 判断传递值的变化
//获取文章数据
}
}
//方法2
'$route'(to, from) {
if (to.path == "/page") { /// 判断条件2 监听路由名 监听你从什么路由跳转过来的
this.message = this.$route.query.msg
}
}
}
</code></pre><h3>2. 异步回调函数中使用this无法指向vue实例对象</h3><pre><code>//setTimeout/setInterval ajax Promise等等
data(){
return{
...
}
},
methods (){
setTimeout(function () { //其它几种情况相同
console.log(this);//此时this指向并不是vue实例 导致操作的一些ma'f
},1000);
}</code></pre><p><strong>解决方案 变量赋值和箭头函数</strong> </p><p><a href="https://link.segmentfault.com/?enc=uCR4NRc92sVRGkr%2FQufY4w%3D%3D.ObnMcZTN%2FjTIAoznRRbtO80RtdfwDxV3OK5OsI0EL94hObOPFlQoQvl4lSizD2acyXtsrf1hOdlZ4jALEWKW4g%3D%3D" rel="nofollow">var和let的区别</a></p><pre><code> //使用变量访问this实例
let self=this;
setTimeout(function () {
console.log(self);//使用self变量访问this实例
},1000);
//箭头函数访问this实例 因为箭头函数本身没有绑定this
setTimeout(() => {
console.log(this);
}, 500);
</code></pre><h3>3. setInterval路由跳转继续运行并没有及时进行销毁</h3><p>比如一些弹幕,走马灯文字,这类需要定时调用的,路由跳转之后,因为组件已经销毁了,但是setInterval还没有销毁,还在继续后台调用,控制台会不断报错,如果运算量大的话,无法及时清除,会导致严重的页面卡顿。</p><p><strong>解决办法:在组件生命周期beforeDestroy停止setInterval</strong></p><pre><code>//组件销毁前执行的钩子函数,跟其他生命周期钩子函数的用法相同。
beforeDestroy(){
//我通常是把setInterval()定时器赋值给this实例,然后就可以像下面这么停止。
clearInterval(this.intervalId);
},
</code></pre><h3>4. vue 滚动行为用法,进入路由需要滚动到浏览器底部 头部等等</h3><pre><code>使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。
vue-router 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。
</code></pre><p><strong>注意: 这个功能只在支持 history.pushState 的浏览器中可用。</strong><br>路由设置如下: <a href="https://link.segmentfault.com/?enc=TuyenBYiRKXx0iZfkizkmw%3D%3D.ME7eNFekCxIAnbR7tvVrCpNRyiKgYLffF0JgQb2rBdP9VZIQal35NqnciX9etGl0iCFa5m1V2mXSIZBoFjUh6g%3D%3D" rel="nofollow">详情猛戳</a></p><pre><code> const router = new VueRouter({
mode: 'history',
scrollBehavior (to, from, savedPosition) {
if (savedPosition) { //如果savedPosition存在,滚动条会自动跳到记录的值的地方
return savedPosition
} else {
return { x: 0, y: 0 }//savedPosition也是一个记录x轴和y轴位置的对象
}
},
routes: [...]
})
</code></pre><h3>5. 实现vue路由拦截浏览器的需求,进行一系列操作 草稿保存等等</h3><p>场景:<br>为了防止用户失误点错关闭按钮等等,导致没有保存已输入的信息(关键信息)。<br>用法:<br>//在路由组件中:</p><pre><code>...
beforeRouteLeave (to, from, next) {
if(用户已经输入信息){
//出现弹窗提醒保存草稿,或者自动后台为其保存
}else{
next(true);//用户离开
}
}</code></pre><p>还有beforeEach、beforeRouteUpdate这些生命周期函数 <a href="https://link.segmentfault.com/?enc=I2dwwWUGqdtiqCvqHDoeqg%3D%3D.kCsOLijk6oDAqD0dXtCO38thFDhFYrZ2EjMjQYZvGbcnKR2LqFaH1Ts0D6L9CkLh4rehwyXp08Su8LbaTzEFRg%3D%3D" rel="nofollow">详情猛戳</a></p><h3>6. v-once 只渲染元素和组件一次,优化更新渲染性能</h3><pre><code>v-once这个指令相信大家用的很少,不过个人感觉还是挺实用的!</code></pre><p><strong>只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。</strong><br> 这个就不举例子了 直接猛戳这 <a href="https://link.segmentfault.com/?enc=XbOmFkQ7BgU9k8yhD%2F%2BS5w%3D%3D.Dy6ud1vJSkxUiQrTif159agvMmsa6NNYc%2F3KYhORsvEFPulA1CJuAwUnUfGzHmP1" rel="nofollow">v-once</a></p><h3>7. vue本地代理配置 解决跨域问题,仅限于开发环境</h3><p>这个本地代理用来解决开发环境下的跨域问题,跨域可谓老生常谈的问题了,proxy 在vue中配置代理非常简单</p><pre><code>//比方说你要访问 http://192.168.1.xxx:8888/backEnd/paper这个接口
//配置 config.js下面proxyTable对象
proxyTable: {
'/backEnd':{
target:'http://192.168.3.200:8888', //目标接口域名有端口可以把端口也写上
//或者prot本地起服务端口与服务端统一
changeOrigin:true,
}
},
// 发送request请求
axios.get('/backEnd/page') //按代理配置 匹配到/backEnd就代理到目标target地址
.then((res) => {
console.log(res) // 数据完全拿得到 配置成功
this.newsList = res.data
}, (err) => {
console.log(err)
})
或者:
proxyTable: {
'/api':{
target:'http://192.168.3.200:8888', //目标接口域名有端口可以把端口也写上,或者prot本地起服务端口与服端统一
changeOrigin:true,
pathRewrite: { //该属性重写接口把`/api`去掉
'^/api': ''
}
}
},
// 发送request请求
axios.get('/api/backEnd/page') //按代理配置 匹配到/backEnd就代理到目标target地址
.then((res) => {
console.log(res) // 数据完全拿得到 配置成功
this.newsList = res.data
}, (err) => {
console.log(err)
}) </code></pre><h3>8. 本地开发 没有任何问题 部署服务器 就404啊这些问题</h3><p><strong>由于前端路由缘故,单页面应用应该放到nginx或者apache、tomcat等web代理服务器中,千万不要直接访问index.html,同时要根据自己服务器的项目路径更改react或vue的路由地址</strong></p><p>注意点</p><pre><code>1: vue-router的 history 模式
2: 服务nginx配置
</code></pre><p><strong>到最后我想说声抱歉,之前答应你们的node由于个人原因没有及时更新出来,node 方面的文章篇幅一定短不了,因为要把它讲清楚不是一句俩句话的事,好在我发现了一篇文章 对初学者晋升高级初学者有很大帮助,请点击<a href="https://link.segmentfault.com/?enc=JN%2B5UNAJ8xBx%2FbzFOwFv5g%3D%3D.u426RP5j7zGtOXTv885ISdnaJ%2BUHNkJiBEppnjdG83Gmqzjk2%2FalDeoxhhBmvHZe" rel="nofollow">猛戳这里</a>,希望你认真的看完这篇文章(书把,篇幅不短),它会对你有很大的帮助,我一定在年前给你补一遍开发完整应用的node 方向的文章!</strong></p><h3>结语</h3><p><strong>这篇文章只是开始,它的待续还会很长很长,希望你我坚持下去!也希望我能帮助到更多的人,当然也会让我真正沉淀一下,为了以后为了将来 一起努力如果大家有什么问题,或者需要补充的 欢迎留言!我会进行补全的 ! 详细版本我稍后也会上传到gitHub!</strong></p><p><strong>如果觉得文章对大家有帮助,希望大家能点赞一下或者关注一下,得到肯定的我会更加努力!~~</strong></p><h2>需要内推的,优化简历的请联系主页微信。免费!!!!</h2>
几分钟的阅读让你明白node.JS的强大 走上web后端开发的道路 (一版)
https://segmentfault.com/a/1190000012588575
2017-12-26T14:52:42+08:00
2017-12-26T14:52:42+08:00
wlove
https://segmentfault.com/u/dxgl
12
<h2><strong>前言</strong></h2>
<p><strong>本文章主要写给那些想了解node语言的开发,我的目标希望大家通过阅读本篇文章能够简单使用node进行开发,以及了解一些事件驱动的异步编程风格,主要分<code>node的背景</code>,<code>安装配置</code>,<code>模块创建引用</code>等几个方面描述</strong></p>
<p><strong>建议大家在阅读本篇文章途中 可以<code>亲自</code>尝试一下我所带来的小例子,这样才能<code>更好</code>的掌握!</strong></p>
<h2><strong>背景</strong></h2>
<p><code>Node.js</code>,或者Node,是一个可以让<code>JavaScript 运行在服务器端的平台</code>。它可以让JavaScript 脱离浏览器的束缚运行在一般的服务器环境下,就像运行Python、Perl、PHP、Ruby程序一样。你可以用Node.js 轻松地进行服务器端应用开发,Python、Perl、PHP、Ruby 能做的事情Node.js 几乎都能做,而可以做得更好。</p>
<p>Node.js 是一个为<code>实时Web (Real-timeWeb) </code>应用开发而诞生的平台,它从诞生之初就充分考虑在<code>实时响应</code>、<code>超大规模数据要球下架构的可扩展性</code>。这使得它摒弃了传统平台依靠<code>多线程来实现高并发的设计思路</code>,而采用了<code>线程</code>、<code>异式I/O</code>、<code>事件驱动式</code>的程序设计模型。这些特性不仅带来了大的<code>性能提升</code>,还减少多线程程序设计的复杂性,进而提高了开发效率。Node.js 最初是由Ryan Dahl 发起的开源项目,后来被Joyent 公司注意到。Joyent 公司将Ryan Dahl 招人旗下,因此现在的Node.js 由Joyent 公司管理并维护。尽管它诞生的时间( 2009年) 还不长,但它的周围配经形成了一个庞大的生态系统。Nodejs 有着强大而灵活的包管理器<code>(nodepackagemanager,npm)</code>,目前已经有上万个第三方模块,其中有网站开发框架,有MySQL、PostgreSQL、MongoDB 数据库接口,有模板语言解析、CSS 生成T 具邮件、加密、图形、调试支持,甚至还有图形用户界面和操作系统API 具。由VMware 公司建立的云计算平台CloudFoundry 率先支持了Node.js。2011年6月,微软宣布与Joyent 公司合作,将Node.js 移植到Windows,同时Windows Azure 云计算平台也支持Node.js。Node.js 目前还处在迅速发展阶段,相信在不久的未来它一定会成为流行的Web应用开发平台。</p>
<p><strong>让我们从现在开始,一同探索Node.js 的美妙世界吧!</strong></p>
<h2>安装配置</h2>
<p>在<strong>window</strong>上面很容易安装node 只需要访问<a href="https://link.segmentfault.com/?enc=kvxJ4%2FwAs%2FZLXm9G3tqisw%3D%3D.VVQ64yq3pdtghVU8l%2BurZHwavFrF7wtQpBCZjAdNajM%3D" rel="nofollow">node官网</a>,点击<strong>DownLoad链接</strong>,然后选择<strong>window Installer</strong>,下载安装包,下载完成后打开 点击<strong>Next</strong> 如图,就可以安装完成</p>
<p><img src="/img/bV0YhA?w=417&h=325" alt="clipboard.png" title="clipboard.png"></p>
<p>为了检测安装是否完成,<strong>打开命令行CMD</strong>,然后输入node 如果下载成功 便进入node交互模式 如图:</p>
<p><img src="/img/bV0Yh8?w=516&h=302" alt="clipboard.png" title="clipboard.png"></p>
<p>通过这种方式安装还会自带<strong>NPM</strong> 我们可以直接在使用它!</p>
<p><strong>Mac OS X上安装Node.js</strong><br>只需要访问<a href="https://link.segmentfault.com/?enc=3gb9KDD6M5NPZRkLvCn2ng%3D%3D.%2BL5ojYz9mLhGBPIOAbH9toHhBlx1KHXETV15a%2FqUVzE%3D" rel="nofollow">node官网</a>,点击<strong>DownLoad链接</strong>,然后选择<strong>Macintosh Installer</strong>,下载安装包,下载完成后运行安装包,根据提示完成安装:</p>
<p><img src="/img/bV0YFm?w=447&h=325" alt="clipboard.png" title="clipboard.png"></p>
<p><strong>node和npm都会安装到/usr/local/bin目录下,安装过程需要管理员权限,安装完后在终端输入node回车进入node交互模式,如果提示-bash:node :command not found 那就是没有已正确的方式安装完成。需要重新安装</strong></p>
<h2>安装完成之后 练习一下</h2>
<p>进入node交互环境</p>
<ol>
<li>consoloe.log('hello,node.js') <br> 输出 // hello,node.js</li>
<li>1 + 2 <br> 输出 // 3</li>
</ol>
<p>可以进行<code>加减乘除</code> 运算 </p>
<p><em>接下来创建一下第一个应用 也可以叫模块 !</em><br>打开你所常用的文本编辑器输入</p>
<pre><code> var http = require('http'); //通过require对象(指令)来引用http模块
//参数request代表是发送请求对象 response代表是响应请求的对象
http.createServer(function(req,res){ //通过http对象的方法createServer创建一个服务器
res.writeHead(200, {'Content-Type': 'text/html'}); //规定数据的content-type文本类型为text/html
res.write('<h1>Node.js</h1>'); //响应请求然后write 字体大小为h1标准的node.js字样
res.end('<p>Hello World</p>'); //发送响应数据为 hello world;
}).listen(8888); //该服务监听本地host 端口8888
console.log('Server running at http://127.0.0.1:8888/'); //在终端告诉用户该服务运行与8888端口</code></pre>
<p><strong>保存为hello.js</strong></p>
<p>然后你打开当前目录,打开命令行输入 node hello.js 你就会在命令行处看到Server running at <a href="https://link.segmentfault.com/?enc=aOdt%2FWQwMYYZOiQYOVZCgw%3D%3D.LQS%2FfC4gyiyiuqV0cGXtlYkTBrlvM7JkbD7o%2BMrwPZg%3D" rel="nofollow">http://127.0.0.1</a>:8888/ ,然后就可以打开浏览器输入这个<a href="https://link.segmentfault.com/?enc=rGaQXdQwAf%2Bd6kv6%2BzK8Dg%3D%3D.3NiaXdbo18%2FuVXrs5eghIRbbHotO4g7Su42SyflpNgU%3D" rel="nofollow">http://127.0.0.1</a>:8888/ 就可以看到刚才写的响应数据</p>
<p><strong>**是不是特别简单!!!!!</strong></p>
<h2><strong>模块的创建与引用</strong></h2>
<p>学习node.JS必须懂的一些地方</p>
<ol>
<li><strong>什么是模块?</strong></li>
<li><strong>如何创建并加载模块?</strong></li>
<li><strong>如何创建一个包?</strong></li>
<li><strong>如何使用包管理?</strong></li>
</ol>
<p>这次先讲前俩点,和稍微渗透3 4 点</p>
<p><strong>第一点!</strong><br>模块是Node.js应用程序的基本组成部分,文件和模块是一一对应的。换句话说,一个Node.js文件就是一个模块,这个文件可能是JavaScript代码,JSON或者编译过的C/C++扩展。</p>
<p>var http= require('http'),其中http是Node.js的一个核心模块,其内部是用C++实现的,外部用Javascript封装。我们通过require函数获取这个模块 ,从而才能使用其中的对象。</p>
<p><strong>第二点!</strong><br>在Node.js,创建一个模块非常简单,因为一个文件就是一个模块,我们要关注的问题仅仅只是如何在其他文件获取这个模块,Node.js提供了exports和require俩个对象,其中exports是模块公开的接口,require用于从外部获取一个模块的接口,即获取模块的exports对象。</p>
<pre><code>// 创建模块
//module.js
var name;
exports.setName = function(thyName){
name = thyName;
}
exports.sayHello = function() {
console.log('hello' + name)
}
</code></pre>
<p>同级目录下创建getmodule.js</p>
<pre><code>//引入模块
// getmodule.js
var myModule = require('./module');
myModule.setName('zhangsan');
myModule.sayHello();
在命令行运行node getmodule.js 输出// hello zhangsan
</code></pre>
<p>以上该例子中,module.js通过exports对象吧setName 和 sayHello作为模块的访问接口,在getmodule.js中通过<br>require('./module')加载这个模块,然后就可以之间访问module.js中exports对象的成员函数了 </p>
<p><strong>require 不会重复加载模块,无论调用多少次require,获得的模块都是同一个,修改一下上面的getmodule.js</strong></p>
<pre><code>//修改getmodule.js
var hello1 = require('./module');
hello1.setName('zhangsan');
var hello2 = require('./module');
hello2.setName('zhangsan2');
hello1.sayHello(); // 输出结果为zhangsan2!
</code></pre>
<p>因为变量hello1和变量hello2执行都是同一个实例,因此hello1.setName的结果被hello2.setName覆盖!结果由后者决定</p>
<p>把一个对象封装到模块中 例如:</p>
<pre><code>
//hello.js
function hello (){
var name;
this.setName = function(thyName) {
name = thyName;
}
this.sayHello = function() {
console.log('hello' + name);
}
};
</code></pre>
<p>module.exports = Hello; //导出该模块接口; 如果按导出方法哪有 exports.Hello = Hello 引入require('./hello').Hello;</p>
<pre><code> 按上面module.exports = Hello导出 引入模块;
//getHello.js
var Hello = require('./hello');
hello = new Hello();
hello.setName('zhangsan');
hello.sayHello(); //输出hello zhangsan
</code></pre>
<p><strong>第三点 /创建包</strong></p>
<pre><code> 包是在模块基础上更深一步的抽象,Node.js的包类型与C/C++的函数库或者Java/.Net的类库.将某个独立的功能封装起来,用于发布</code></pre>
<p>更新,依赖管理和版本控制.Node.js根据CommonJS规范实现了包机制,开发了npm来解决包发布和获取;</p>
<pre><code> node.js的包就是一个目录,包含一个JSON格式的包说明文件package.json. package.json必须在包的顶层目录下,其他遵循CommonJS的规范不是特别严格
</code></pre>
<p><strong>第四点 /包管理</strong></p>
<p>node.js包管理,即npm是Node.js官方提供的包管理工具,它已经成为了Node.js包的标准发布平台,npm提供了命令行工具,你可以很方便去下载 安装 升级 删除包! 也可以发布 维护你自己的包!</p>
<pre><code> npm install(i) pagejson_name //下载的格式 本地模式 末尾加-g (--global) 就是全局模式
npm uninstall pagejson_name //删除的格式 末尾加-g (--global) 就是全局模式
//等等很多命令 可以去搜索 就不一一列举了
</code></pre>
<h2>待续!!</h2>
<p><strong>本来应该把node一些核心模块一块说了的 比如全局对象 process console 事件驱动events 事件发射器 error事件 继承EventEmitter 等等</strong> <br>但是今天确实身体不适,还请谅解,可能会休息几天 所以提前把这版发出来,暂时把他定位为第一版 对于node的探索,我还会更新 第二版的,下一章节除了会补全这次没有讲到了还会带大家利用node来进行web开发!初步定稿为1月2号!</p>
<ul>
<li>http 与express 安装与搭建</li>
<li>路由的控制 如工作原理 创建路由规则等等</li>
<li>模板引擎 什么是?怎么使用?如何利用布局?等等</li>
</ul>
<h2>结语</h2>
<p>希望大家可以通过看这篇文章能有收获,最简单的证明就是把项目用到node的配置文件再去拿出来看看,最起码知道它是怎么个流程,不一定知道它干什么,但是知道它从哪来 是导出还说引用!这就是进步!</p>
<p>//进一步学习参考这篇书 学习<br><a href="https://link.segmentfault.com/?enc=A62CyCRnnNpuz8u4zqkEvg%3D%3D.9ycl2W37FY5QPjM7RfcocDAJUpgpZTB9K0Qah9NWnYQ%3D" rel="nofollow">https://www.nodebeginner.org/</a></p>
<h2>不要做幻想的乞丐,要做幸福的创造者!!!</h2>
数组方法的封装/代码的复用加强
https://segmentfault.com/a/1190000012427137
2017-12-14T15:58:04+08:00
2017-12-14T15:58:04+08:00
wlove
https://segmentfault.com/u/dxgl
4
<h2>前言</h2>
<p>在编写JavaScript代码的时候存在一些对于数组的方法,可能涉及的页面会很多,然后每次去写一堆代码。长期下去代码会特别的<code>繁多</code>,是时候进行一波封装了,话不多说开始书写<code>优美</code>的代码</p>
<p>代码已上传github,需要的欢迎star(<a href="https://link.segmentfault.com/?enc=dT4SEDmfjq85GbwWZv%2FUqw%3D%3D.rXjgshf4uxkFs5Oy1swooxiff0iIC8hYOwBFBSTMYc417e31WHsuBwVA%2F9pWcJ3k" rel="nofollow">https://github.com/Xieguoiang...</a>)。</p>
<hr>
<h2>关于数组一些方法的封装</h2>
<p><strong>1.</strong><strong>数组<code>去重</code></strong></p>
<pre><code>`上文提到的Set的封装`
//ES6新增的Set数据结构,类似于数组,但是里面的元素都是唯一的 ,其构造函数可以接受一个数组作为参数
//ES6中Array新增了一个静态方法from,可以把类似数组的对象转换为数组
//方法二 new
function removeRepeatArray(arr){
return Array.from(new Set(arr))
}
</code></pre>
<p><strong>2. 数组<code>顺序打乱</code></strong></p>
<pre><code>function upsetArr(arr){
return arr.sort(function(){ return Math.random() - 0.5});
}
</code></pre>
<p><strong>3. 数组最<code>大</code>值最<code>小</code>值</strong></p>
<p>//这一块的封装,主要是针对数字类型的数组</p>
<pre><code>function maxArr(arr){
return Math.max.apply(null,arr);
}
function minArr(arr){
return Math.min.apply(null,arr);
}
</code></pre>
<p><strong>4 数组<code>求和</code>,<code>平均值</code></strong><br>这一块的封装,主要是针对数字类型的数组 求商品总价了 求总数了 很常用<br><strong>求和</strong></p>
<pre><code>function sumArr(arr){
var sumText=0;
for(var i=0,len=arr.length;i<len;i++){
sumText+=arr[i];
}
return sumText
}
</code></pre>
<p><strong>平均值,小数点可能会有很多位,这里不做处理 可能你需要<code>保留多少位</code> 自己处理一下吧 ~~</strong></p>
<pre><code>function covArr(arr){
var sumText=sumArr(arr);
var covText=sumText/length;
return covText
}
</code></pre>
<p><strong>5从数组中<code>随机</code>获取元素</strong></p>
<pre><code>//类似抽奖了什么的 适应场合很多
function randomOne(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
//randomOne([1,2,3,6,8,5,4,2,6])
//2
//randomOne([1,2,3,6,8,5,4,2,6])
//1
</code></pre>
<p><strong>6返回数组(字符串)一个元素出现的<code>次数</code></strong></p>
<pre><code>function getEleCount (obj, ele) {
var num = 0;
for (var i = 0, len = obj.length; i < len; i++) {
if (ele == obj[i]) {
num++;
}
}
return num;
}
//getEleCount('asd56+asdasdwqe','a')
//3
//getEleCount([1,2,3,4,5,66,77,22,55,22],22)
//2</code></pre>
<p>... 就不<code>一一列举</code> 如有需要详情 请<code>移步</code>我的github~~</p>
<h2>字符串以及date日期的封装</h2>
<p>列举3-4个//<br><strong>1 去除字符串<code>空格</code> 四种情况</strong><br><strong>去除空格 type 1-所有空格 2-前后空格 3-前空格 4-后空格</strong></p>
<pre><code>function trim(str,type){
switch (type){
case 1:return str.replace(/\s+/g,"");
case 2:return str.replace(/(^\s*)|(\s*$)/g, "");
case 3:return str.replace(/(^\s*)/g, "");
case 4:return str.replace(/(\s*$)/g, "");
default:return str;
}
}
</code></pre>
<p><strong>2 查找字符串 <code>字段所出现的次数 </code>~</strong></p>
<pre><code>function countStr (str,strSplit){
return str.split(strSplit).length-1
}
</code></pre>
<p><strong>3. 日期的<code>5-7</code>日期日期时间部分到某一个时间的<code>倒计时</code></strong></p>
<pre><code> function getEndTime(endTime){
var startDate=new Date(); //开始时间,当前时间
var endDate=new Date(endTime); //结束时间,需传入时间参数
var t=endDate.getTime()-startDate.getTime(); //时间差的毫秒数
var d=0,h=0,m=0,s=0;
if(t>=0){
d=Math.floor(t/1000/3600/24);
h=Math.floor(t/1000/60/60%24);
m=Math.floor(t/1000/60%60);
s=Math.floor(t/1000%60);
}
return "剩余时间"+d+"天 "+h+"小时 "+m+" 分钟"+s+" 秒";
}
如需更多觉得`用的多的功能的封装` 欢迎留言/ 大家一起`进步`
</code></pre>
<h2>结语</h2>
<p><strong>本文列举了一些常用JS方法的封装,代码拢杂是前端一大弊端,希望大家也可以学习思路,一起封装,一起进步</strong></p>
<h2>goTo--</h2>
<p><strong>欢迎加群Q 614569041 前端方向</strong></p>
ES6/JavaScript一些‘巧用’
https://segmentfault.com/a/1190000012408287
2017-12-13T15:01:43+08:00
2017-12-13T15:01:43+08:00
wlove
https://segmentfault.com/u/dxgl
6
<h2>前言</h2>
<p>第一次发表文章,如有不好的地方请<strong>见谅</strong>/</p>
<p>在编写JavaScript代码的时候存在的一些方法和技巧,虽然有时候条条大路都通向罗马,但是也许总会有那么一条<em>最短的路径</em>可走。本文将一些都知道却不怎么用的小技巧分享给大家/</p>
<hr>
<h2>一些小技巧</h2>
<p><strong>1.</strong><strong>new Set()</strong><br> 数组的<code>去重</code>,在'潜意识'里面感觉就应该去<code>循环</code>然后对比去重,其实在ES6中新增提供了新的数据结构<code>Set</code>,用他可以<code>轻松</code>去重数组,比如:</p>
<pre><code>let arr = [1,1, 2, 2, 3, 3];
let set = new Set(arr); //
let newArr = Array.from(set); // Array.from方法可以将 Set 结构转为数组。
console.log(newArr); // [1, 2, 3]
</code></pre>
<p><strong>2.Object.assign()</strong><br>也是ES6中提供的对象的扩展方法,其可以用于对象的合并拷贝,像之前对象合并也是很<code>繁琐</code>,但是现在很<code>easy</code>麽,比如:</p>
<pre><code>let obj1 = {a: 1};
let obj2 = {b: 2};
let obj3 = Object.assign({}, obj1, obj2);
console.log(obj3); // {a: 1, b: 2}
</code></pre>
<p><strong>3.map()</strong><br>map方法用于遍历数组,有返回值,可以对数组的每一项进行操作并生成一个新的数组,有些时候可以代替<code>for</code>和<code>forEach</code>循环,简化代码,比如:</p>
<pre><code>let arr3 = [1, 2, 3, 4, 5];
let newArr3 = arr3.map((e, i) => e * 10); // 给数组每一项乘以10
console.log(newArr3); // [10, 20, 30, 40, 50]
</code></pre>
<p><strong>4.filter()</strong><br>filter方法同样用于<code>遍历</code>数组,顾名思义,就是<code>过滤</code>数组,在每一项元素后面触发一个回调函数,通过判断,保留或移除当前项,最后返回一个<code>新</code>的数组,比如:</p>
<pre><code>let arr4 = [1, 2, 3, 4, 5];
let newArr4 = arr4.filter((e, i) => e % 2 === 0); // 取模,过滤余数不为0的数
console.log(newArr4); // [2,4]
</code></pre>
<p><strong>5.some()</strong><br><code>some</code>方法用于遍历数组,在每一项元素后面触发一个回调函数,只要一个满足条件就返回<code>true</code>,否则返回<code>false</code>,类似于 <code>||</code> 比较,比如:</p>
<pre><code>let arr5 = [{result: true}, {result: false}];
let newArr5 = arr5.some((e, i) => e.result); // 只要一个为true,即为true
console.log(newArr5); // true
</code></pre>
<p><strong>6.every()</strong> //与5相反的<br>every方法用于遍历数组,在每一项元素后面触发一个回调函数,只要一个不满足条件就返回<code>false</code>,否则返回<code>true</code>,类似于 <code>&&</code>比较,比如:</p>
<pre><code>let arr6 = [{result: true}, {result: false}];
let newArr6 = arr6.every((e, i) => e.result); // 只要一个为false,即为false
console.log(newArr6); // false
</code></pre>
<p><strong>7.三元运算符</strong><br>该运算符应该大家都比较熟悉,在默写情况下可以简化if else的写法,比如:</p>
<pre><code>let e = true,
f = '';
if (e) {
f = 'aaa';
} else {
f = 'bbb';
}
// 等同于
f = e ? 'aaa' : 'bbb';
</code></pre>
<p><strong>8.~~运算符</strong><br>~符号用在JavaScript中有按位取反的作用,~~即是取反两次,而位运算的操作值要求是整数,其结果也是整数,所以经过位运算的都会自动变成整数,可以巧妙的去掉小数部分,类似于parseInt,比如:</p>
<pre><code>let a = 1.23;
let b = -1.23;
console.log(~~a); // 1
console.log(~~b); // -1
</code></pre>
<h2>结语</h2>
<p>本文只列出了JavaScript语法中比较常见的几点能够提升速度的方法,希望大家在<strong>巧学过程中达到巧用知识的效果</strong>。</p>