yinzhuoei_seho 发布的文章

首先, 我们作为程序员想要学习一个新东西是非常简单的, 尤其是我们熟悉的互联网领域, 我们通常接触的知识面是其他的互联网同僚们要很久才能接触到的, 是因为“信息差”这个有趣的东西; 那我的女朋友想0基础学习ui设计的话, 那我肯定到了最拿手的事情了, 作为一名老前端开发, 我和设计师的沟通(battle)是在日常工作中最多的, 所以这篇文章我会自己梳理学习ui设计的思路以及ui概念, 再到互联网企业中的ui设计到底需要会什么, 还有大家非常关心的薪资情况, 希望各位设计师读者多多指教哈~

认识一下这个职位是什么

很多人对ui,ux这些名词太陌生了, 所以通常都会造成一些误解,会认为ui/ux设计师就和装潢设计师一样需要下工地去做工程图, 就像外界对于程序员的看法都是“黑客”, “盗号”, “格子衫”一样. 所以无论是我们设计新手如何理解ui/ux概念还是如何向外行人解释这个职位, 我们都需要彻底了解职位本质, 我觉得这非常重要, 至少是比学习软件更重要.

UI

ui指的是(user interface), 程序员表示看到interface就不困了, 意思是用户+界面, 你可以简单理解为使用者(人)和界面(产品)之间用来沟通的, 其实在很早以前也有类似的知识(即广义上的ui), 只不过它们都不是电子产品/屏幕, 它们被称之为“人体工学”, 人体工学的核心理念就是使用者(人)和机器/环境的交互/沟通学科; 所以细细想想它们还是蛮相近的; 二战时期中战斗机的驾驶舱的操控设计, 通常都是把所有的按钮直接怼到你面前,有时因为飞行员的误操作造成了很多的空难意外, 人们也慢慢意识到用户和机器是需要更流畅更便利的交流, 所以才诞生了人体工学的学科概念, 那发展到现代, 我们往往接触的都是电子设备, 使用到的网站/app/系统应用也和人体工学一样需要有更好的交流, 这就是ui设计师的职责.

UX

ux指的是(user experience)用户经验/体验, 我个人会认为“体验”更贴切一点; 在大多数你能看到的企业的招聘需求中会把ui和ux放在一起讨论, ui&ux设计代表了设计师需要把流程和外观都要调整到最符合特定用户, 从前端工程师的角度出发, ux可以单纯的指动画/手势/字体/布局/跳转等等, 是需要研究特定人群才能得出的设计论, 而不是埋头苦干地做图. 在一些大型企业中, 你可能会看到专门招聘ux的岗位, 在这种岗位通常都会需要你具备: 研究用户心理, 分析产品易用性等等, 所以对于外观设计(ui)的要求不是那么高.

如何选择

如果你更偏向设计出nice的界面, 而不是专门研究用户心理, 分析产品等等, 你就可以选择ui这个方向了, 但是一个优秀的ui设计师是需要同时具备基础的ux能力的, 那么反之你不想去关心界面喜欢研究用户行为而且不愁就业问题, 那么你可以专心研究ux方向;

互联网团队的基础结构构成

如果你是一个还没入互联网圈子的朋友, 那么你大概率还不了解设计师在团队主要担任什么角色, 那么这个部分我就简要地概括一下团队构成, 让你有一个大概的认识吧. 首先一个互联网产品, 我们就以一个app来举例, 是需要分为需求方和产出方的, 需求方(甲方)一般是我们的老板和上级, 他们会规定app的大需求方向以及业务场景;

比如沈老板说: “我们下一个季度要开发一个应用, 名字就叫[小绿书], 照着小红书抄就可以了”

这个时候团队中会有一个产品经理(PM), 来整理老板的需求和方向, 并且产出文字性/图形化的东西, 用来给设计和开发表达需求; 产品经理这一步非常关键, 他所产出的东西必须要和老板的需求足够贴合, 并且得到老板的认可, 因为如果不这样, 开发和设计都会按照产品经理的错误产出进行研发, 会导致团队项目停滞崩溃;

产品经理小刘说: “老板, 收到, 我的需求已经和你确认过了, 那我就去和设计和开发同学开会了”

我们的设计师角色会根据产品的产出, 设计app的ui界面, 并且关联每个页面(比如a页面要跳转到b页面), 当然设计师并不是直接上手设计的, 通常他们都会调研背景, 适用人群以及竞品的方案, 会设计多个版本的ui和产品/老板进行交流协调, 最终拍板决定之后, 这个设计稿才会到我们的开发同学这里了.

设计师小蕾说: “昊哥, 我在figma (一个ui设计协作工具) 已经把ui确定了, 你们现在可以开发了”

这个时候前端同学昊哥拿到设计师提供的ui设计稿之后, 就吭哧吭哧进行开发啦, 这个时候会有一个角色叫做后端, 那么前端和后端是开发环节中最核心的部分, 前端负责把设计稿还原成代码而且负责和后端进行交互, 让app能够被使用 (被使用的意思就是可以登陆, 点赞, 评论), 那么登陆, 点赞, 评论的服务实现代码就是由后端同学完成的.

我们文章标题所说的“全栈工程师”广义上指的就是同时掌握多种技术的开发工程师 (技能不限于前端后端), 开发同学写完代码之后, 会由测试工程师进行质检, 在质检期间会发生很多很多“bug”, 所以测试工程师往往要和前后端进行协调改进产品, 最后一个被检验合格的app小绿书就正式上线了!

我们来梳理一下, 一个最基础的互联网团队的闭环:

老板(甲方) -> 产品经理 -> 设计师 -> 前端同学 -> 后端同学 -> 测试同学

学习什么设计工具

在这个部分, 仅仅代表我个人的看法, 学什么工具并没有什么鄙视链, 你需要结合你想工作的城市用人需求以及你的电脑系统. 我们简单梳理一下几款流行的设计软件:

  1. ps (懂得都懂, 如果你是windows用户, 那么ps将会成为你的设计必修课)
  2. sketch (音译: 死开吃) (如果你是mac用户, sketch就是你的必修课)
  3. figma (音译: 飞格马) (它是一个独立的设计工具, 也是目前世界最新潮的设计平台, 被国内外很多大厂投入使用, 最重要的是它不会在意你的电脑是windows/macos)
  4. mastergo (音译: 马死特儿狗) (它是一个类似figma的国产工具, 也是今年刚刚上线不久的, 在国外技术垄断的情况下, 如果你喜欢figma但是因为种种原因不能使用, 那么它也是一个非常好的选择)
  5. ae (选修且难度颇高)
  6. c4d (选修且难度颇高)

个人建议: 无脑选择figma, 因为figma和sketch很像, 你可以做到学一个通二个, ps你可以作为选修

要使用什么电脑

在知乎和各大论坛上, 程序员群体常常会因为是使用macos还是windows电脑而有很多讨论, 那么设计师群体我是真的没见到过.
请无脑选择macos (优先m1芯片-macbook pro), 不要选择屏幕大的mac笔记本, 因为公司肯定有外接显示器/公司土豪会给你准备一个imac, 那玩意屏幕大
如果你学习figma, 在学习期间, 你没有必要买一个mac电脑, 但是为了以后你的职业发展, 你以后的主力设计机器一定要是mac (让公司花钱, 自己花那钱干嘛)

想学设计的女同学千万不要误解了, 这个是mac苹果电脑, 可不是口红品牌啊

如何自学

如果你看我这篇文章之前, 有想过自己报班学习, 那么请你看完以后不要有这样的想法了; 报个班2/3w, 都够你买3个mac笔记本了, 你就把你手机上的游戏抖音卸载了, 好好自学个一年半载, 懂了吧, 这个板块就给你讲如何自学的, 我自身是程序员, 但是道理都是一样的, 我不指望和我一样6点起床学一天, 你就每天花2,3小时每天学一下, 完全就够了.

习惯

你需要保持几个好习惯, 在最初学习设计时, 我们首先要定期浏览设计咨询和文章, 来源的地方有很多, 除了我们熟知的知乎, 站酷, 优设, bilibili, (别以为我会提花瓣和千图, 这几个平台少看)等国内平台, 我们仍然需要关注几个国内外的平台 (有的打开会很慢, 有条件的同学可以翻墙上外网, 外网很有必要, 可以获得第一手资料):

  1. https://dribbble.com/ 看完灵感爆棚
  2. https://www.pinterest.com/ 配图绝了
  3. https://www.behance.net/ 超级有设计感的图集网站
  4. https://www.feeeedin.com 国内的设计资讯聚合, 一站式预览
  5. http://www.webdesignfile.com/ 一些优秀的网站设计
  6. https://muuuuu.org/ 同上
  7. https://siiimple.com/ 同上
  8. https://ui8.net/ 同上
  9. https://miro.com/signup/ 同上

设计同学要和前端同学打交道, 所以我们也有必要了解一下开发同学的组件库, 即阿里, 腾讯等大厂的设计规范(主要面向程序员), ui同学过个眼熟就行

  1. https://ant.design/components/button-cn/ 阿里的antdesign组件库
  2. https://youzan.github.io/vant/#/zh-CN/button 有赞的vant组件库, 移动端顶呱呱
  3. https://semi.design/zh-CN 抖音的semi组件库, 够新够酷

作为前端, 我筛选了3个你需要了解的组件库, 那么同样的, 你还需要了解一些图标组件/规范, 这一类的设计规范有很多, 我就列举一个国内最好的:

  1. https://www.iconfont.cn/

同样的, 还有字体规范, 我们在设计中需要很多特殊的字体, 就可以到这里去下载, 也是国内最好用的字体平台:

  1. https://www.hellofont.cn/

我们提供了这么多的链接, 就是想要教会你如何去获取资源, 在有外网的条件下, 你完全可以去更牛逼的平台获得最新的潮流资讯, 拿到第一手信息, 获得信息差, 提高行业竞争力. 我们只需要每天花半个小时, 把这些平台逛一逛, 文章读一读, 如果你有多余的精力, 完全可以把新东西用在你的作品中, 并且进行总结在社区发表文章, 这可以提高你的社区影响力 (和程序员一样)

视频资源

ohhh, 这就很值得考究了, 通常我们学习ui设计, 我们要多半学习软件的操作, 这也是培训班会教你的东西, 他们不会培养你的审美感, 大多数都只会教给你软件的使用方法. 那么在看这部分内容之前, 我相信你已经心中有数, 要学哪个软件了, 我就以figma为例, 给大家简单推荐几个教程作为入门.

  1. https://space.bilibili.com/15741969/channel/collectiondetail?sid=255058 b站搜像素范figma
  2. https://space.bilibili.com/108104104 b站挺火的figma宝藏up主

除了软件的教学, 我也希望你能观看一些著名设计师的演讲/公开课, 那么这些资源你可以在极客时间/掘金/知乎/优设中找到, 尤其在优设中你可以看到很多大厂的设计师分享的各种习惯, 尽管我觉得没多大用对于新手, 但是也能让你多少学一点东西.

书籍推荐

和程序员一样, ui设计师同样需要阅读一些著作, 这些著作不会短时间的提高你的设计水平, 而是会陪伴你的职业生涯, 是你之后迈向更高层级的资本

  1. ui设计黄金法则
  2. 大话设计师
  3. 腾讯传

如何高效率的自学

这个部分和ui设计没有关系, 我会推荐我在学习编程时, 把我觉得好用的工具链和习惯分享给你

  1. notion (笔记软件, 超高颜值且免费, 如果你是外网环境请优先使用它作为笔记软件)
  2. 印象笔记/wolai/语雀/腾讯文档 (wolai是notion的国内替代品, 如果你没有外网就可以考虑使用wolai)
  3. 所有的视频资源请第一时间去youtube上查询, 因为youtube的视频质量往往会比较高
  4. 避免使用百度, 请使用谷歌, 并且关键字检索时, 尽量避免使用中文 (你需要学到最国际化的设计理念, 而不是二手的国内知识)
  5. 你需要一个严格的作息时间, 即每天几点到几点是学习时间, 这个时候谁也不能打扰你, 也不要做转移注意力的事情, 比如刷抖音/小红书
  6. 尽量早睡早起, 早上5 6点起床是大脑非常清醒的, 只要你睡的够早, 就可以把晚上低效率的时间挪到早上
  7. 你需要分类你的浏览器标签, 把你每天所看到的有趣的, 添加到不同领域中
  8. 你要善于产出, 不管是视频还是文章, 无论你的好坏, 你都可以发布到自己的博客/朋友圈/其他设计平台, 便于纠错, 营造成就感, 让别人给你查漏补缺
  9. 作为设计师新手不像程序员, 要做很多很多项目, 其实不然, 你要做的就是接触更多领域的设计规范, 你可以去参加比赛, 或者接外包快速提高自己的水平, 不一定非要一个完整的项目
  10. 尝试加入一些小圈子, 比如互联网程序员圈子, 设计师圈子, 乱七八糟的群多加一点, 这样你有各方面的问题, 都有渠道去解决, 同理你也可以把你的疑惑发布到社区中

推荐时间表

  1. 19:00 - 22:00 学习figma软件, 并且产出笔记
  2. 6:00 - 7:00 你可以阅读一些书籍
  3. 12:00 - 13:00 浏览一下最新的技术资讯
  4. 15:00 - 18:00 将昨天晚上学习的笔记和阅读的书籍所结合, 多做几个demo, 给自己几个主题 (也可以做比赛的主题, 比赛优设一大堆, 随便参加)

除此之外, 如果你有空余精力的话, 请在地铁, 公交, 空闲时刻去刷资讯, 比较方便的话还是优设, 里面的文章质量还真的不错

关于卷

别听TM任何一个人看你学习, 就说你卷, 我特别不喜欢这个词, 你就学你自己的, 都是为了以后未来, 咋没人说考研人, 考公人卷, 都是一些老傻逼双标人了, 不要理他们. 更何况你是0基础学习ui设计, 你需要一个干净轻松的自学环境, 不要被周围的环境影响了, 如果你坚持不下去, 就可以去报班去学习ui设计; 我的很多朋友都是从编程培训班出来的, 我也或多或少听说过一些, 有些培训班的环境很差很差, 可能不仅不会帮助你建立氛围, 还会影响你的心智 (抱着娃来学ui设计你敢信, 销售小姐姐也在学, 所以竞争蛮大的).

就业情况

互联网行业的各个岗位都是处于半饱和状态, 而且今年的形势不容乐观, 在这个部分我会简单介绍西安的就业情况, 如果你想着重了解一线城市的薪资, 那就在这个西安的基础上加5k-8k, 可以供你参考. 设计行业不像程序员看中学历和学校, 相反会看中你的工作年限和技术能力, 所以我觉得对于学历不是那么好的同学是一个很大的好处, 除了大厂的ui设计师是有严格的学历要求(统招本科/硕士等), 在普遍的企业中基本没有这样的要求; 而且个人觉得大厂的员工并没有比其他非大厂的员工高人一等, 甚至大厂的某些部门做的业务是很差很差的, 所以希望刚刚入门学习设计的同学不要好高骛远, 也不要过度的追求大厂岗位, 提高自己的硬实力才是最重要的.

哪所城市

个人推荐几个还不错的城市(排名不分先后):

  1. 苏州
  2. 杭州
  3. 南京
  4. 成都
  5. 重庆
  6. 西安

薪资问题

以西安举例子, 也结合了一些设计师朋友给的建议, 给出了一个可参考的薪资范围, 在给定一个学历(大专-硕士)硬性条件之下, 有了下面这个表格:

  1. 在校实习生 (2-3k)
  2. 一年工作经验 (4-6k)
  3. 二年工作经验 (7-8k)
  4. 三年工作经验 (10-15k)

在西安的设计行业, 15k+是很难达到的, 除非你进大厂或者干到设计总监.
但是不可否认的是如果你能力特别突出, 是肯定可以突破这个薪资范围滴, 而且我们需要在工作期间尽可能的保持每天学习, 仅仅会一个技能是不够的, 你需要同时提高你的审美水平, 其他软件的技能, 以及硬实力和软实力.

招聘jd中的技能树

  1. figma, ai, ps
  2. 有些公司会让你有可视化大屏的设计经验 (这玩意西安老火了)
  3. 设计思路良好, 基本功扎实
  4. 有些傻逼公司会让你掌握html,css,js(前端知识), 要小心让你去写代码, 慎重
  5. 3dmax加分项
  6. c4d加分项
  7. ae加分项

如何挑选好的实习岗位

如果你是在西安的一本二本普通大学, 请不要相信学校承诺的包就业, 2022年了就别信这一套了, 咱们就自己多学点提前找, 不要等着快毕业才开始学开始找, 那就完蛋了, 晓得吧. 你走学校的校招也是个人比较推荐的, 但是通常我会建议同学们在校招之前自己去外面公司面试, 因为有3个好处:

  1. 有更多的面试经验
  2. 有更好的工作机会
  3. 可以提前知道自己值多少价

那么如何挑选公司呢, 你就记住以下的公式就可以了:

大厂 > 自主创业公司 > 大外包公司 (西安中软等这一类的) -> 小外包公司

  1. 去大厂之前先要询问做什么产品, 什么部门, 一个大厂履历对你以后职业生涯很有帮助
  2. 自主创业公司指的就是自己独立的产品, 无论公司大小, 只要你认为是不错的产品, 就值得一去
  3. 大外包公司, 项目很多, 加班基本都很多, 通常一个做完就下一个继续做, 和电子厂没区别, 有些公司福利也不好
  4. 小外包公司, 通常不仅有大外包公司的所有毛病, 还比不上大外包的福利, 去个锤子?

作为一个新人, 只要不倒贴钱, 建议不要太在意薪资, 只要不是太夸张, 我毕业第一份工作就是1.2k(非实习), 干几个月就跳槽不就得了.

面试须知

在面试的时候, 你不仅需要提前做好表达和简历, 还要准备一份精美的作品集, 用于展示你的能力. 此时你就需要挑选你之前做的demo/项目/比赛作品, 关于作品集, 我这里就不推荐了, 因为我也没做过, 但是网上有很多范本, 集中在优设/站酷等网站, 它们会告诉你怎样设计作品集

  1. 请不要编造简历, 这是不诚信的行为, 尤其是编造年龄/工作经验, 要不然试用期3个月你就每天活在胆战心惊中
  2. 面试要自信, 你和面试官是平等的, 不要唯唯诺诺恳求
  3. 记得真诚的要面试反馈, 如果好的面试官, 会给你下一步的方向, 还是很不错的
  4. 不要透露自己的理想薪资(对面试官和hr), 砍价都砍过吧? hr就是这样砍价的, 所以你就不要说你的薪资需求, 简历写“薪资面议”
  5. 一般来说, 设计师的简历是要自己设计的, 如果你懒得设计就去超级简历, 不要写word了, 求求你们了, 写word的简历直接丢垃圾桶
  6. 问好五险一金, 是否大小周, 加班费, 节假日福利, 这些都问好, 作为参考价值, 是属于工资之外的附加条件

结束

暂时想这么多, 如果有想知道其他方面, 就给我评论发消息吧, 设计的文章我以后基本也不会出了, 干编程老本行吧~~~

在这篇文章之后,我会经常发布一些关于框架设计/架构的一些文章,因为这将作为我的读书笔记,我最近在看一些书比如《vue.js设计与实现》和《前端架构入门和微前端》;我简单介绍一下这两本书,希望对你们有所帮助,首先前端架构这本书一直是我的床头书但是目前对我的工作帮助并不大,因为它比较偏理论个人认为,如果你有耐心并且非常愿意入门前端架构,这本书是一个非常不错的入门书籍;其次就是vue.js设计这本书是最近前端圈的网红书,如果你已经使用过了vue3一段时间了,想精通/深入了解vue3,那么这本书将会带你从设计到实现理清楚vue的所有脉络!

前言

我最近在写我人生中的第一款框架,尽管没有任何含金量,而且这种低级的作品居然是出自一个有着3年开发经验的程序员之手,我还蛮不好意思的;在写这款框架我犯了很多错误和技术债,由于前期没有很好的规划功能以及模块,导致走了不少弯路,而且没有设计框架的经验,我经常会把一个功能放到编译时还是运行时而苦恼,同样我会时常考虑用户的习惯,去联想其他后端框架,导致在框架API设计上有点四不像的感觉。无论如何这款框架再丑也是自己生的,相信不久之后就会和大家见面了,所以我这篇文章将结合我设计的sword.js和vue.js给大家好好聊一聊框架中如何权衡某些事情。

什么是权衡

我们在讲比如vue.js这一类框架时,其中的每一个模块并非独立的,而是互相依赖和制约,框架作者需要有着全局的把控才能更好的扣细节做优化,拆分...那么想象一下当我们要设计一款前端视图层框架的时候,我们需要首先考虑范式,它是声明式的还是命令式的呢,再比如说如果在框架中做hmr底层实现,甚至是构建工具,webpack/rollup/esbuild?可见我们要遇到的选择都太多太多了,那么这就是“权衡”的艺术,框架中的每一个地方,或者说我们在平时写业务的时候,我们都需要去考虑更多东西,这就是权衡。

声明式和命令式

我们从原生js开始说起,如果我想要给一个dom绑定一个点击事件(我全部用伪代码写):

const e = document.querySelector("#app");
e.innerText = "foo";
e.addEventListenner('click', () => {
    alert("hello foo")
})

这就是典型的命令式代码,代码的执行方式是可预期的,因为都是由开发人员自己编写的每一步操作,但是这就遇到一个很难的问题了,当程序越来越大,我们有多个dom需要绑定点击事件,就要获取n次dom并且一一绑定,这无疑是一种痛苦。那么声明式呢,它可以解决命令式的一些什么问题呢?

<div @click="() => {}" id="app">
</div>

如果你使用过vue.js,那么你肯定写过n个这样的代码,我们只给click提供了一个函数,我们并不关心vue是如何获取dom并且绑定点击事件的,我们只需要关注结果就可以了,但是不可否定的是,在vue内部的实现中一定是命令式的,而暴露给用户却是声明式的。那么关乎性能它们谁更好,答案当然是可以预想到的,命令式的代码有着不可替代的性能:

e.innerText = 'update text';

在命令式代码中只需要写这一句就可以了,但是如果是声明式代码,我们需要找出新dom和旧dom之间的差异,然后再动态修改text(调用上面这个代码),所以由此得知,尽管声明式代码的性能不如命令式,但是为了更好的维护,我们需要做的就是权衡(既然性能有差距,我们就往可维护性上靠,并且尽可能的优化diff算法,让性能无限接近命令式代码)。

虚拟dom的性能

刚刚我们讨论了声明式和命令式的区别,那么虚拟dom如果你使用了vue.js就一定不陌生,而且它是每个面试官都喜欢问的(我也不知道为什么喜欢问,感觉没啥技术含量)。那么虚拟dom就是为了能够更好的给vue进行diff而出现的,我们要比对如下2行代码:

<div @click="() => {}" id="app">hello foo</div> // old
<div @click="() => {}" id="app">hello bar</div> // new

如何用最小的性能消耗找出它们的差异呢?就是虚拟dom,我们在之前说过声明式和命令式代码天然的差距(虚拟dom更新不会比js dom api性能更好),但是事实上99%场景都很难写出绝对优化的命令式代码,但是声明式代码我们可以很轻松的写出来相对还不错的代码。我们为了了解虚拟dom,需要知道我们上述提到的js dom api是什么,要么是createElemnt或者innerHTML,所以我们就用虚拟dom对比一下这两个api的差异。

innerHTML vs 虚拟dom

innerHTML是我写jquery/jsp时的噩梦,因为在新手时期为了构建一个html字符串,我每天半夜调试屎山项目的html字符串,这个过程非常痛苦:

const html = `
<div>
    <span>innerHTML</span>
</div>
`
dom.innerHTML = html;

js新手小白都知道,dom操作的效率和js层面的计算是不能比较的,差距非常大,为了页面的展示,需要把html字符串转成dom树,然后再执行innerHTML; 反观虚拟dom创建页面需要2步:

  1. 把我们的模板代码转换成js对象
  2. 无限递归对象创建真实dom

这么一看,好像innerHTML更直接,而且html字符串转成dom树是dom层一次性且“高效”的运算,所以说虚拟dom创建页面的性能是不如innerHTML的,但是更新页面,虚拟dom的优势就显示出来了,首先innerHTML不仅会对html字符串进行运算,还会把之前的旧dom销毁,然后创建一个新的dom(恨人啊);虚拟dom只需要创建一个新的js对象再与旧的虚拟dom进行比对,哪里有变化就变更哪里!虽然说虚拟dom多了一个diff的操作,但是终究是js层面的运算是很快速的;当页面越来越大,而innerhtml必定都是全量更新,性能也会随着内容变多,和虚拟dom差距越来越大。

粗略比较三个方式的创建&更新技术

  • 性能:原生JS > 虚拟dom > innerhtml
  • 综合可维护性和性能以及心智负担权衡之下,虚拟dom是一个不错的选择。

运行时和编译时

我们作为框架的作者,希望程序是如何运行的,我们还是用vue.js举例子,刚刚我们讲了虚拟dom,但是却不知道虚拟dom这个js对象是什么样子,我们可以通过这个部分把虚拟dom重新梳理一下:

const obj = {
    tag: "div",
    children: [{
        tag: "p",
        children: "hello bar"
    }]
}

这就是一个虚拟dom对象,描述了每个node的信息以及每个子node的信息,我们如果要实现render方法,就需要对虚拟dom对象进行递归,我们简单实现一下:

const obj = {
  tag: "div",
  children: [
    {
      tag: "p",
      children: "hello bar"
    }
  ]
};

const render = (obj, root) => {
  // 创建一个父节点
  const element = document.createElement(obj.tag);
  if (typeof obj.children === "string") {
    // text节点
    element.appendChild(document.createTextNode(obj.children));
  } else if (obj.children) {
    obj.children.forEach((e) => {
      // 如果有多个子节点,就递归创建
      render(e, element);
    });
  }
  root.appendChild(element);
};

render(obj, document.body);

这样我们就完成了一个在运行时环境可以完美运行的render,用户可以使用render对页面进行创建元素,但是没有用户愿意每天写这种破数据结构的,所以就肯定要用到编译的东西帮助我们把模板语法转换成数据结构,这个时候就是编译时+运行时,所以vue大多数情况也是这样做的,通过vite/vue-cli对单组件文件进行编译。那么同理既然可以有纯运行时,那么就有纯编译时的东西,可以把我们的模板语法编译成命令式的代码,比如这样:

<div @click="() => {}" id="app">hello foo</div> // old

转换成

const e = document.querySelector("#app");
e.innerText = "foo";
e.addEventListenner('click', () => {
    alert("hello foo")
})

没有虚拟dom,没有diff,only compile!! 这也是svelte.js在做的很酷的事情。所以作为框架设计者关于运行时和编译时我们需要有自己的权衡,虽然vue.js是运行时+编译时,但是在编译时会提取内容,看看哪些内容是永远不可变哪些又是可变的,然后这部分会在运行时再次做优化。所以关于运行时和编译时,没有绝对的好也没有绝对的坏,还是看框架定位和作者自己的权衡了(佛系不引战)。

关于sword.js所做的权衡

如果还不清楚sword.js是做什么的,你可以看看以前的文章,简单的就是说一个nodejs框架,框架中自然就是拥有运行时和编译时,一个framework-core,一个cli。在sword.js中有一个蛮好玩的功能就是,ts运行时检测,这个技术的大概的原理就是,分析ts的类型生成一份schema,然后会有一个函数去比对对象和schema是否吻合,如果匹配成功,那么就算校验通过,这个技术用到参数校验特别好,比如这样:

export interface ReqParams{
    title: string;
    name: "小红" | "小蓝"
}

const obj = {
    title: "test',
    name: "小红"
}
validate(obj, schema); // 这里的schema就是interface转的json对象

那么我在实现这个功能的时候,分2步走,第一个就是生成schema,第二个就是校验;我把生成这部分放到了cli的编译层这里,程序会自动读取每一个API下的类型,然后转成一个proto.json,在这个json中,运行时可以去校验这部分的对象是否符合要求。权衡好了运行时该做什么,编译时该做什么,就可以把2个工具的大小大大压缩。

再比如说日志模块,在开发nodejs应用的时候,我们需要core的日志,也需要cli的日志,那么如何在终端表现也是需要权衡的。

结语

今年实在是很少时间写文章,就趁着看书和写框架做一个随心记录,希望你们能看得懂(内容偏水,应该都有看得懂)

现在支持私有部署同时也支持serverless各个平台的nodejs框架多么?答案是否定的,在2022年越来越多的企业会选择serverless来部署自己的应用,因为它足够轻巧省去了运维的成本,编写一个API可能只需要几行代码,越来越多的平台也推出了自己的云数据库以及云消息队列,我们前端开发编写后端api不再是一个头疼的事情。所以serverless是未来,我将在4月份之前完成一款具有插件机制的框架,它会在web服务器中运行也可以在serverless环境运行,在serverless中我会优先支持unicloud,我将使用这款框架参加2022年的dcloud插件大赛。

简单聊聊这款框架,我为什么要创建一款nodejs框架?

为什么要造轮子

在sword团队中我们使用unicloud构建应用程序,采用了CQRS,我们所有的写操作都由unicloud的云函数完成,但是你会发现在unicloud社区优秀的框架有很多,它们提供了url处理以及逻辑的分发,还有一些特色的框架也提供了诸如上传,和unicloud的部分特性封装,在我看来,这样的框架没有真正解决开发者的问题,比如:

  • 我想现在不想用unicloud,我想用传统服务器运行函数
  • 我想和云平台解耦,我希望我的云函数的特性和功能实现和某一平台无关
  • 我想使用ts开发
  • 我想有IDE强力支持
  • 我想使用一些开箱即用的方案,比如说hook,又比如HMR
  • 我想使用ES开发nodejs程序,使用先进的技术对程序进行捆绑(treeshaking...)

如果要满足上面的特性,那么只有midway.js了,midway.js很酷,但是它并没有unicloud的faas插件,而且我希望框架能够让sword团队更好的构建程序,简单就是说,我们想把控这个事情,而不是阿里;所以我们造一个轮子,来解决这些事情。

框架开发进度

我会在3月底前完成第一步的计划,即web服务器可以部署我们框架捆绑的应用,框架简单包含3部分

  • framework runtime
  • framework cli
  • framework type

我po一下我们的仓库:

作为一个没有学过C/C++的开发者(比如说我),在初步学习Rust中需要了解一些JavaScript中不存在的东西,那么所有权就是Rust中的核心功能之一。我需要一篇文章记录这一次的学习,在真正内容开始之前我需要描述一些基础知识,在最后也会简单看看”引用和借用“,”slice类型“这些与之相关的Rust概念。因为是Rust初学者,请大家阅读本篇文章带着自己的思考,因为每个人的思考方式和理解都不一样,所以可能会导致某些错误...。

参考资料:

预热

程序员编写的所有程序都必须管理其使用计算机内存的方式,比如说JavaScript这一类的语言具有垃圾回收机制(GC)它可以不断寻找不再使用的内存,因此不需要开发者手动干预;在另外一些语言中,我们需要亲自分配和释放内存;但是在Rust中就利用了所有权的概念管理内存,编译器在编译阶段会根据规则进行检查,同样的也不需要程序员手动干预内存,这些有关内存的工作都交给了所有权和编译器。所以我们学习Rust中的所有权的时候,准确说应该是学习编译器是通过什么“规则”来进行检查的,这个规则对我们更重要。

堆栈

我们在写JavaScript的时候,通常业务开发我们不需要考虑堆栈,但是在Rust中我们需要考虑一个值是在堆上还是在栈上,这和所有权息息相关,所以我们先简单回顾一下堆和栈的基础知识。堆和栈都是代码运行时可使用的的内存,它们是不相同的。大家都知道栈是先进后出,像服务生手中一个一个托盘一样,最先放入的托盘一般都会最晚从上方取出;栈中所有数据都占用固定的大小,程序在编译时都会看值大小是否可能会变化,如果是则需要存储到堆中,因为堆中所有的数据都是凌乱的,需要分配器去在堆中区域开辟一块空间,标记为已使用并且返回指针,这个过程也叫做在堆上分配内存

数据直接入栈当然也比在堆上分配内存更快,入栈直接放到栈顶即可,入堆的话分配器不仅要查看内存,分配内存还要记录以便为下一次分配做准备;访问堆上的数据也比访问栈中的数据要慢(因为指针),而因为处理器缓存的原因,跳转越少就越快,当访问的数据彼此都是栈中那自然快,但是彼此数据一个在栈中一个在堆中那自然会稍慢一点。那么所有权将会帮助我们处理堆中的重复数据,无用的数据以及跟踪哪些代码在使用堆中数据,总的来说所有权的存在就是为了管理堆数据。

所有权规则

  • 每一个值都有一个被称为其所有者的变量
  • 值在任一时刻只有一个所有者
  • 当所有者(变量)离开作用域的时候就会被丢弃

感觉目前理解这几个规则还比较晦涩,但是我们可以随着笔记深入慢慢地理解

作用域

所有权规则中既然提到了作用域,那我们简单看看作用域,其实和JavaScript相差不大,我们非常容易理解

fn main(){ // a在这里无效
    let a = "hello seho" // a有效
} // 作用域结束了,a无效

看起来很简单不是么?和JS一样?

我们此时会发现hello seho这个值是通过字面量硬编码创建了一个字符串并且绑定到a中,这种情况下a将会在栈中,因为它有着固定的大小,那么当作用域结束之后,a将会直接出栈。

内存和分配

对于以上的hello seho这种值,在我们的实际业务中并不常见,我们大多数会存在一个大小不定的内容,而且会在程序的运行时增大或者减小,那么为了支持这种需求只能在堆中开辟出一块内存,意味着程序需要在运行时去创建这块的区域,而且当我们处理完内容,需要把内存还给内存分配器。

第一步很好说,我们调用String::from这种方法就可以在堆中创建一块区域,那么第二步怎么还呢?

在C中是需要手动的释放内存的,但是程序终究是人写的,有时候忘记还了,有时候过早还了,有时候重复还了,都会造成系统bug。在有GC的语言中,GC会记录并且清除不使用的内容从而释放内存,所以这也就是我为啥喜欢JS的原因了,真的太爽了....

我们把上面的代码改造一下

fn main(){ // a在这里无效
    let a = String::from(""hello seho"") // a有效
} // 作用域结束了,a无效

在作用域结束之后,rust会调用一个特殊函数drop从而释放内存,在stirng内部会实现这个drop函数...

移动

我们在JS中写过很多这样的代码

let a = 1;
let b = a;

很显然,它在栈中创建了2个变量, a和b,他们的值都是1

在rust中也一样,但是如果我使用String::from创建变量呢,在堆中和栈中会有不同么?

let a = String::from("hello seho");
let b = a;

很显然,a的值是hello seho,b“复制”了a的指针,它们都存在栈中,指针指向了堆中的hello seho。

诶,难道说,rust的表现和js一样?在我们前面提到了所有权的规则之一,当变量离开了作用域将会自动释放,但是此时a和b都指向同一堆中内容,此时不是造成了多次释放的问题?答案是肯定的,在我们之前就说到过,没有释放,过早释放,多次释放都会对程序造成影响,所以rust在针对我们上面这种代码的时候,作出了一个处理即当a被“复制”到了b身上,此时a不再生效,程序会在编译期间报错。

如此看来,就避免了多次释放的问题,而“复制”也不是真正的复制,而是移动

那么如果真的想克隆一个一摸一样的值,可以调用clone方法,而我们在上面写了一个整型的例子,为什么没有调用clone也可以被克隆,是因为它们本身就是栈中的数据,在栈中的拷贝是快速的,不需要通过移动这种机制来实现拷贝

到此为止,我们应该能理解所有权到底是什么了,我们在本篇笔记中学习了所有权的概念和规则,以及复习了堆栈基本知识,还有rust的内存分配。我们在本篇笔记中大量的使用了js这门语言作为参照对象,如果你不熟悉js的话,下次笔记我会将语言的比较去掉,这样应该会更加容易理解,有问题发评论区,吴彦祖和迪丽热巴发的评论我肯定都会回复

store-persistedstate-killer

EN / 中文

杀手级别的持久化状态管理库


  • 可以为多个库提供持久化服务 (vuex, pinia)
  • 支持 TypeScript
  • 支持 预定义存储驱动 (localstorage, sessionstorage) 以及自定义驱动
  • 支持相对安全的存储环境(非明文)
  • ‍灵活的配置且没有副作用
  • 对开发友好的状态变更 Log
  • 持久化加强功能 (重命名...)

安装

npm i store-persistedstate-killer

快速使用

// main.ts
// pinia平台
import { plugins as killer, config } from 'store-persistedstate-killer'

createApp(App)
  .use(
    createPinia().use((context) => {
      killer.pinia.init(context)
      killer.pinia.use(context)
    })
  )
  .mount('#app')

Demo

Edit objective-sun-1wmt7

目标

  1. 用状态管理接管你的 storage,从此无需担心类型,像操作 store 一样操作 storage 即可
  2. 前端存储不再明文

killer 做的事情

设计

每一个平台的插件你可以单独引入它们,比如你是 pinia 平台,那你仅仅这样引入就可以了

createApp(App)
  .use(
    createPinia().use((context) => {
      killer.pinia.init(context)
      killer.pinia.use(context)
    })
  )
  .mount('#app')

killer 中每一个插件都包含2个部分, 一个就是 init,一个是 use

init

在应用初始化时,把我们 storage 内容同步到 store 中; 如若发现 store 有,但是 storage 没有的 state,也会执行一次同步。这个过程是双向的。在文档上方就有一个 killer的概要图,我们如果站在状态管理的视角下,可以理解 storage 为远端,双方的交流就可以当作pushpull

use

use 是 killer 的核心功能,它可以监听 state 的变更以及 patch 操作,它可以实时地把 state 同步给 storage


如你所见,如果你的业务中,仅仅需要监听 state 然后同步到 storage 这个需求,你也可以仅使用 use 这个插件

如果想看到更多有关平台插件的文档,你可以移步具体的文档中(就在下方)

支持的平台/库

PlatformLibDoc
pinia2
vuex4/5

核心

killer 为各个平台的插件提供了多个核心,使它们能够正常运转,每一个核心主要负责一个业务,比如说配置,加密,存储

配置

killer 本身自带一个开箱即用的配置,你如果有特殊的需要,可以去自定义它们。在此之前你需要了解各个插件的工作原理,我们以 pinia 举例子。pinia 由一个一个 store 组成,store 由 state,getters,action 组成,所以 killer 仅仅是在useStore()之后才运行的插件,killer 接管了 store 的 state,使之能够持久化到本地存储中;那么在持久化的过程中,我们可能需要做一些重命名, 加密数据等工作...

配置名含义类型默认建议
exclude排除指定的仓库名string[ ][ ]
include包含指定的仓库名string[ ][ ]
prefix缓存的key前缀stringpersistedstate-killer-建议传入有效的字符串
iv加密需要用的iv变量string''可以为空
isDev是否是开发环境booleanprocess.env.NODE_ENV === 'development'如果为false将自动加密
storageDriver插件预定义的存储驱动defineStorageDriverdefineStorageDriver('localStorage')支持传入localStorage和sessionStorage
store对仓库进行详细配置Partial<Record<K, StoreConfig>>没有默认配置
defineStorage自定义存储驱动setItem, getItem, removeItem, iteration没有默认配置如果预定义存储驱动defineStorageDriver没有满足你的需求,可以使用这个方法定义新的驱动

你的工程中的自定义配置可能就像这样:

import { plugins as killer, config } from 'store-persistedstate-killer'

createApp(App)
  .use(Router)
  .use(
    createPinia().use((context) => {
      config.defineConfig<'main'>({
        exclude: ['zhangsan'],
        include: ['main', 'test'],
        isDev: true,
        storageKey: 'seho',
        store: {
          main: {
            state: {
              hello: {
                rename: 'wuyu',
              }
            }
          }
        }
      })
      killer.pinia.init(context)
      killer.pinia.use(context)
    })
  )
  .mount('#app')

你可以看到, killer 提倡使用 ts 来构建插件,我们可以给 defineConfig 传入一个联合类型,声明需要对哪几个 store 进行操作,此时如果你在编写 include 和 store 配置时,将会有非常棒的类型提示。

ApiDescType
defineConfig注入配置doc

加密

前端的加密难道没有必要么?确实有人这么说,但是当我们把状态管理的数据明文暴露到 localstorage 中确实不是很好,尽管我们现在都这么做 。我们需要一款易用的加密,不仅可以给 killer 中内部使用,而且还可以暴露给用户,让用户可以加密 api,交换特殊信息?killer 内部使用了crypto-js,默认使用了浏览器ua -> base64, 同时你也可以根
据业务需要指定 key 和 iv。

import { crypto } from 'store-persistedstate-killer'

const _crypto = new crypto()
const message = 'hello, messagehello, messagehello, messagehello, messagehello, messagehello, messagehello, message'
const encryptData = _crypto.encrypt(message)
if (encryptData) {
  const decrypt = _crypto.decrypt(encryptData)
  console.log('解密结果', decrypt)
} else {
  throw Error('加密错误')
}

我们可以给构造函数传递一个 ctx

const _crypto = new crypto({
  iv: 'asdasdasdasdasdasdasdasd',
  key: 'sssaasdasdasdas234234s'
})

查看加密模块的类型声明

ApiDescType
encrypt加密` (data: string) => string \null`
decrypt解密` (data: string) => string \null`