seho 发布的文章

最近学习rust的时候,了解到rust的浮点数实现是和js是一样的, 也就导致了我们在js上遇到的精度问题, 在rust同样也能遇到.

首先我们来理清, rust的默认浮点类型是f64, 而js由于和其他语言不同, 无论是整数和浮点数都是number类型, 也是64位固定长度, 也就是标准的双精度浮点数,

双精度浮点数(double)是计算机使用的一种数据类型,使用 64 位(8字节) 来存储一个浮点数。 它可以表示十进制的15或16位有效数字,其可以表示的数字的绝对值范围大约是:-1.79E+308 ~ +1.79E+308 [1] 。

既然2种语言底层的标准都是一样的, 都是使用了IEEE 754标准中的double精度, 那我们就直接使用大家熟悉的js来做demo.

为什么使用double精度标准

对比单精度标准来说, 虽然double精度占用比单精度高(8byte > 4byte), 这也就间接意味着cpu在处理上, 单精度会有优势, 但是单精度的致命缺陷就是有效数少而且范围也会更小, 总的来说适用性略低, 而且在现代cpu来说, 处理速度上基本是可以忽略不计的.所以在rust中默认的浮点数类型就是f64, 如果有需要就选择f32(单精度)

double精度如何存储

直接从wiki上抄一张图下来

618px-IEEE_754_Double_Floating_Point_Format.svg.png

  • 符号位:1 位 (+, -)
  • 指数:11 位 (次方)
  • 有效位数精度:52 位

运算过程

我们通常会用十进制来表达浮点数, 但是我们rust/js底层都是用二进制实现浮点数类型的, 比如说我们写一句这样的代码:

var a = 0.1;

在我们程序员眼中它可能就是绝对等于0.1的, 但是在内部实现中, 它需要转换为二进制, 但是在二进制中就是无限精度类型, 也就变成了下面这样:

0.1 -> 0.0001 1001 1001 1001...(1100循环)

但是由于我们底层的标准, 有效位数的精度是52位, 我们在做浮点运算的时候, 多余的数字都会被截断, 所以在js从二进制转换为十进制之后, 就不是我们预想的答案了(在一定精度结果是对的)
, 同理在rust/js中我们也不要使用浮点数做比较, 因为是一个危险不受信赖的计算结果, 也希望精度问题能够引起大家重视, 因为有很多危险的事件是由转换精度触发的:

对于Ariane 4火箭的工作代码在Ariane 5中被重新使用,但是Ariane 5更高速的运算引擎在火箭航天计算机中的算法程序中触发了一个bug。该错误存在于将64位浮点数转换为16位带符号整数的程序中。更快的运算引擎导致了Ariane 5中的64位数据要比Ariane 4中更长,直接诱发了溢出条件,最终导致了航天计算机的崩溃。首先501航天飞机的备份计算机崩溃,然后0.05秒之后,主计算机也崩溃了。这些计算机崩溃直接导致了火箭的主要处理器使火箭的运算引擎过载,同时导致火箭在发射40秒后解体破碎。

顺带提一句, rust中对于整型有溢出处理, 在release环境下, 会按照补码循环溢出的规则去解决, 但是这仍然会造成结果不一致的错误.

如何解决

rust: 我不知道咋解决, 我才学rust
js: 大把的精度库, 最流行的方案就是底层使用string了, bignumber.js, 就可以避免浮点数陷阱;

基础类型

rust是一门静态编程语言, 所以我们有必要知道它的类型, rust的类型可以分为基本类型和复合类型(也可以称之为复杂类型), 基本类型指的就是原子化最小的类型, 它不能转换为其他类型;

rust不像ts, ts是js的超集, ts可以更好的推断类型, 但是rust拥有一个很聪明的编译器, 你可以无需指定类型, 让编译器去自动推断, 但是某些情况下编译器无法推断类型, 比如这样

let guess = "42".parse().expect("Not a number!");

鬼知道guess是什么类型, 所以编译器会报错, 你只需要显式的指定类型即可;

数值类型

rust创建数值非常简单

let a = 1;

整数

我们继续来探讨整数类型, 我们之前了解过的i32类型, 表示有符号的32位整数

i表示integer, 与之相反的是u, 代表无符号

类型统一定义为 “有无符号” + “位数(bit)”, 无符号表示就是正数, 有符号就是正负; 简单的使用准则需要我们记住, rust整形默认使用i32, 可以首选i32, 而且性能也是最好的;

我们在处理整型的时候, 如果设置的值超过了范围, 那么rust会区分模式, 做不同的决策, 比如在debug模式中, 程序会报错退出; 如果是release环境, 会被补码为范围内的最小值, 因此这样的结果是不符合预期的, 会被认为是错误的.

浮点数

浮点数默认类型是f64, 但是rust有一个浮点数的陷阱, 我们通常会使用十进制表达浮点数, 但是rust底层是使用二进制实现浮点数类型的, 比如说0.5在十进制上存在精确表达, 但是在二进制中不存在, 所以就会造成歧义, 所以想要表达完整的真实浮点数字, 就必须使用无限精度的浮点数才行.

还有一个注意的点就是, 我们不要对浮点进行比较, 这和js一样, 由于rust的浮点类型底层是二进制, 而js同样在浮点数运算时也是二进制, 都会存在不准确的问题; 在rust中对浮点数进行比较的时候, float声明了PartialEq, 注意它不是Eq; 在map数据结构中的k类型, rust是需要你传入声明过Eq 的类型, 很显然float不满足要求; 而且当我们使用浮点做比较的时候, 会造成编译器错误.

NaN

和js一样, 我们可以在rust中做一个防御性编程:

fn main() {
    let x = (-42.0_f32).sqrt();
    if x.is_nan() {
        println!("未定义的数学行为")
    }
}

序列

rust提供了一种简洁的方式生成连续的数值

// 生成1-5 (包括5)
1..=5
// 生成1-5 (不包括5)
1..5

字符, 布尔, 单元类型

这一部分比较简单,

char

fn main() {
    let c = 'z';
    let z = 'ℤ';
    let g = '国';
    let heart_eyed_cat = 'hhh';
}

不管是ascii, 还是unicloud都算作rust字符char; 另外字符是用’’包裹的, “”是表达为字符串

bool

rust中的布尔类型和其他语言一样, 存在true/false

单元类型

0长度的元组, 单元类型使用()来表达, 我们可以用单元类型占位, 它不占用任何内存, rust函数中的main还有print函数都会返回一个单元类型. 我们不能说main函数没有返回值, 因为没有返回值在rust中是有单独的定义的(发散函数: diverge function) , 顾名思义, 无法收敛的函数.

语句和表达式

在rust的函数体中是一系列语句组成, 最后是由表达式返回, 比如这样

fn add(x: i32, y: i32) -> i32 {
    let x = x + 2; // 语句
    let y = y + 3; // 语句
    x + y // 表达式
}

语句

 let x = x + 2; // 语句
 let y = y + 3; // 语句

由于let是语句, 我们不能将let赋值给其他值:

let b = (let a = 8);

这样编译器会报错…

表达式

表达式会进行运算/求值, 比如1+2, 会返回3, 我们在语句上面写了这段代码:

 let x = x + 2; 

这里的x + 2确实是一个表达式, 只要记住只要能返回值, 它就是表达式 而且表达式不能包含分号, 比如下面一段代码

let y = {
    let x = 3;
    x + 1
};

如果x + 1包含了分号, 那么此时这个语句块不会返回任何值了, 也就不算作表达式了, 所以我们在rust中编写函数的时候, 如果要返回值, 就要注意这里不需要加分号;

函数

rust的函数很简单, 下面是一个例子, 有关键字, 函数名称, 形参名称和类型, 函数返回值类型, 以及一个代码块, 就构成了一个最简单的函数:

fn add(i: i32, j: i32) -> i32 {
   i + j
 }
  1. 首先rust是强类型的语言, 我们在编写函数的时候, 参数的类型是必须的, 如果不提供参数的类型是会报错的.
  2. 函数的返回值就是代码块的最后一行的表达式返回值, 当然我们也可以使用return关键字提前返回也可以
  3. 在单元类型中, 我们提到了无返回值, 我们可以使用单元类型作为返回, 而一些表达式你不幸加了分号变成了语句, 那么此时也会返回一个单元类型.
  4. 在上文提到了diverge function, 我们可以使用!作为函数返回类型, 这一般会作为程序崩溃的函数, 代表永不返回:
fn end() -> ! {
  panic!("it is over...");
}

首先, 我们作为程序员想要学习一个新东西是非常简单的, 尤其是我们熟悉的互联网领域, 我们通常接触的知识面是其他的互联网同僚们要很久才能接触到的, 是因为“信息差”这个有趣的东西; 那我的女朋友想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一下我们的仓库: