CSSOM View Module 中的尺寸与位置属性
CSSOM
指 _CSS Object Model_,即 _CSS对象模型_。CSSOM 是 JavaScript 操纵 CSS 的一系列 API 集合,它属是 DOM 和 HTML API 的附属。
其中视图模型(View Model)中定义了一系列接口,包括多个关于窗体、文档和元素的位置尺寸信息,特别容易混淆。
CSSOM
指 _CSS Object Model_,即 _CSS对象模型_。CSSOM 是 JavaScript 操纵 CSS 的一系列 API 集合,它属是 DOM 和 HTML API 的附属。
其中视图模型(View Model)中定义了一系列接口,包括多个关于窗体、文档和元素的位置尺寸信息,特别容易混淆。
虽然 redux 的模型非常简单,但如何对其理解不深,在实际的业务研发中很容易迷失,比如会纠结该如何定义枚举的 Action Type
。
Webcomponents
草案包含四个特性:
具体每个特性的意义不再冗述,网上到处都有,但对四个特性之间的联系及应用普遍缺少更深入的解释。
Webcomponents
的名字立即会让人想起组件化,有观点认为 Custom Elements
与 HTML Imports
是主要部分,Templates
与 Shadow DOM
是次要部分,毕竟,传统的组件系统就是由组件依赖以及组件内容构成的。甚至有人经常把 Custom Elements
与 HTML Imports
绑定在一起,认为 Custom Elements
都是 import 进来的。
以上观点肯定是错误的,可能由于 Webcomponents
还在草案之中,文档不全,造成误解也难怪。
先来看 Custom Elements
,如何注册一个自定义元素?是这样么:
1 | <!DOCTYPE html> |
不是,如果是,就证明了 Custom Elements
都是 import 进来的观点了。
自定义元素需要通过 JavaScript 脚本来注册:
1 | document.registerElement('x-rank', options) |
使用没有注册过的自定义元素通常也不会有问题,不过它只能代表其后代元素的意义,本身并没有语义。一般地,我们会通过定义 options 参数来扩展 DOM 元素的方法:
1 |
|
在这种场景下,registerElement
就是必须的了。因此,在不支持 Custom Elements
的浏览器上,polyfill 是不可能完全实现的。
好了,现在我们实现的 Custom Elements
已经有了自定义方法,但还可能需要在内部添加一些固定的后代元素,比如,对于一个 Article 来讲,Header,Footer 就是固定的后代元素,而 Summary 则非。这时候,我们一般使用模板引擎,渲染后直接将 HTML 片段插入到 DOM 中进行解析和展现。Webcomponents
提供了原生的 template 元素,预先解析了这部分 DOM,但并不展现,需要时,取出其后代元素的集合(DocumentFragment):
1 | <wc-rank id="rank"></wc-rank> |
如果自定义元素在创建时就已经拥有了固定的后代元素呢,该如何实现?
1 | <wc-rank id="rank"> |
可见,template 数量增多后会使操作十分麻烦,Shadow DOM
可以解决这个问题:
1 | <wc-rank id="rank"> |
这样,便以优雅的方式达到了定义自定义元素以及高效重用的目的。现在,将 Custom Elements
的定义分离出去,维护在单独的文档中,这就是 HTML Imports
的用处之一。
1 | <!-- wc-rank.html --> |
1 | <!-- index.html --> |
如此,四个 Webcomponents
的特性全部有了用武之地:
(语义)->
Custom Elements
(内容)->
Templates
(效率)->
Shadow DOM
(重用)->
HTML Imports
它们都可以独立使用,但相互组合,更能实现优雅和高效的组件化。
由于目前仅 Chrome(Opera)实现了全部特性,因此 Polyfill 仍有存在一定的价值。下面几个项目都依赖了 webcomponentsjs,但在 API 上有所不同。
谷歌发起的项目。Polymer 使用 < dom-module > 标签来定义 Custom Elements
:
1 |
|
内部同时声明了 Templates
,并使用 shady DOM 来针对不支持 shadow DOM 的浏览器。HTML Imports
也被支持。
值得一提的是,Polymer 支持更复杂的 template,比如 mustache 语法及 dom-if_、_dom-repeat 指令,有点类似于 Angular 的 ng-if 和 _ng-repeat_。
X-tag 是微软支持的项目。X-tag 以纯 JavaScript 脚本声明 Custom Elements
:
1 | xtag.register('wc-rank', { |
X-tag 实现了 Custom elements
的生命周期回调,对 HTML Imports
、Templates
和 Shadow DOM
没有明显的支持。
Bosonic 旨在构建一套低级的 UI 元素:即拿即用。
1 | <element name="wc-rank"> |
Rosetta 是百度的一套 Webcomponents
解决方案,与上面三个项目最大的区别是在线下利用构建进行 polyfill,以提高运行时效率。
1 | <element name="r-slider"> |
API 与 Polymer 如出一辙。
换个角度,Webcomponents
目前在前端生产环境中使用还为时尚早,但其组织方式可以被服务端借鉴。试想,每个组件或者自定义元素都组织在私有的目录下:
将 Custom Elements
作为 组件名 和 __组件引用指令__,如:
1 | <!--wc-rank.html--> |
那么最终输出可以是:
1 | <div is="wc-rank"> |
该过程完全可以在服务端完成,已经成为了一种简单的模板引擎。同时,如果需要执行 document.createElement(‘wc-rank’) ,可以将 wc-rank.html 和 wc-rank-content.html 带到前端进行动态解析。
接着,css 和 js 可以按照传统的方式进行依赖搜索和 combo。这样便将 Webcomponents
应用于服务端,并沿用组件化的思想和 Webcomponents
的草案 API,不失为前端工程化的一种解决方案。
Incremental DOM 是谷歌公司在 2015年4月起发起的一种支持模板引擎的高效 DOM 操作技术。相比于 React.js
的 DOM Diff
算法和 Ember.js
的 Glimmer
引擎,Incremental DOM
没有使用 Virtual DOM
的概念,转而直接去操作 DOM,因此其特点就是节约内存消耗,这对于移动端来说可能存在积极的意义。
先来看 Incremental DOM
的 API 的样子:
1 | elementOpen('div', '', ['title', 'tip']); |
可以说 Incremental DOM
的 API 十分地原始和简陋。Google Developers 也提到,Incremental DOM
并非为开发者直接使用,而是用于模板引擎的底层实现。
现在来简单分析下 Incremental DOM
的实现原理。
由于操作 DOM 的代价相当高,因此 React.js
和 Glimmer
都有一套算法来计算最小的 DOM 操作量。在 Incremental DOM
内部,通过__遍历__原始 DOM 进行脏检查来计算这个值。
首先了解 Incremental DOM
的几个概念:
在 Incremental DOM
内部,维护了多个节点(HTMLElement)指针:
主要操作有 enterNode
、exitNode
、nextNode
。
假设当前指针指向为:
1 | <div id="content"> <!--currentParent--> <!--prevCurrentParent--> |
enterNode()
操作,即进入 .item3
内部,各指针变为:
1 | <div id="content"> <!--prevCurrentParent--> |
exitNode()
操作,离开 .item3
,各指针变为:
1 | <div id="content"> <!--currentParent--> |
nextNode()
操作,遍历至下一个节点,各指针变为:
1 | <div id="content"> <!--currentParent--> <!--prevCurrentParent--> |
明白了 Incremental DOM
的内部指针状态后,我们来看一个例子。
1 | <div id="content"> |
我们要修改 .child
元素的 title 值:
1 | patch(document.querySelector('#content'), function () { |
实际的 DOM 遍历和操作为:
setAttribute('title', 'Jim')
由于 JS 代码与 HTML 在结构上是一致的,因此当遍历到 .child
元素时,直接修改其元素,而其它元素由于结构属性都没有改变,因而没有额外的 DOM 操作。
如果我们要进一步修改 DOM 为:
1 | <div id="content"> |
Incremental DOM
的 API 操作为:
1 | patch(document.querySelector('#content'), function () { |
实际的 DOM 遍历和操作为:
removeAttribute('title')
createElement()
createText()
Incremental DOM
正是通过这种简单粗暴的方式来实现最小量的 DOM 操作。
由于直接在原始 DOM 上做脏检查,Incremental DOM
在性能上有所下降。
上图是各个框架在布局和绘画上的性能比较,可见 Incremental DOM
是垫底的。
Incremental DOM
用性能来换取内存优化:
上图是各个框架在 GC 上的性能,Incremental DOM
表现十分优越。
Incremental DOM
的特点使得它适用于内存敏感型而非性能敏感型的应用。同时,前面也提到,Incremental DOM
为模板引擎的底层所设计,不适合直接调用其 API。
已经应用了 Incremental DOM
的模板引擎有:
Incremental DOM
仍在发展中,相比 react.js
与 ember.js
而言并没有受到太多的关注,期待其对模板引擎在性能上的促进和发展。
npm3 于2015年6月发布,它与 npm2 很大的一点不同是__依赖管理方案__的升级。
为了管理同一个模块的不同版本,npm2 采用严格树形嵌套的形式组织依赖模块的目录,而 npm3 则尽量_扁平化_,将依赖模块提升至顶层目录:
对于 B 模块的两个版本,v1 版本会被放置于顶层,而 v2 版本则因为冲突关系仍放置于 C 模块下面。
这样的布置有什么好处?共用。加入再有一个模块 D 依赖与 Bv1,则不必再安装,直接使用顶层的 Bv1即可。
那如果 A 和 D 模块都依赖 Bv2 呢?npm3 不会将 Bv2 移至顶层,而是将 Bv2 仍挂在 C 和 D 下面:
似乎 npm3 并没有那么优化到最好,Bv2 模块没有被复用。要想实现这种最优的组织,需要手动执行命令:
$npm dedupe
那么,npm3 如何决定 B 模块的哪一个版本在顶层呢?答案是按照自然顺序的先后,最先安装的版本会放置于顶层。
理论上,自然顺序在任何情况下都是一定的,因此依赖模块的最终组织形式也是一定的。但这个前提是安装依赖之前 node_modules 是空的。一旦 node_modules 内已经有依赖模块,最终的组织形式就会受到影响。
假如我们现在有这样的一个应用:
修改 A 模块,使之依赖 Bv2:
现在,发布应用,在另一台机器上重新部署应用:
可见这两种环境下,依赖的目录组织形式不是一致的。
因此,npm3 的这种新的依赖管理方式可能造成依赖模块的目录不一致,除非所有依赖模块都删除并重新安装。但是,这种不一致理论上是无害的,每个模块都能找到所有符合要求的依赖。
开发过移动端页面的同学一定听过 dpr
、scale
、rem
三个概念。最起码,也会用过 scale
,如
因为如果你不设置这一行,几乎所有的移动端浏览器都会把宽度设置为 980px
,页面上的文字变得太小而难以阅读。
那么,这三者究竟有着怎样的关系呢?
首先从需求讲起。
移动端设备的屏幕尺寸千差万别,即便设计师能够提供每一种尺寸下的 UE 图,工程师也无法做到针对每种场景的适配。一般地,作为近似,在技术上可以使用媒体查询(media query)的方式将屏幕尺寸划分为几个等级,不同等级下使用不用的CSS样式。
但这显然不够精确,在不同设备上很难做到体验一致,不但代码难以维护,同时存在着被设计师吐槽的风险。
如何在不同的屏幕上完美还原设计图,同时兼顾有限的人力与时间?
由此我们可以提出需求:
这有两种方案:
我们分开来讲讲这两种方案。
我们经常看到业界的大致方案是:
将
scale
设置为1 / dpr
,<html>
的font-size
计算为screen.width * dpr / >10
,然后在以less
将UE图上得到的尺寸透明转换为对应的rem
值。
由于 rem
是比例值,因此能做到最终每个元素的尺寸相对于 UE 图的比例都是一致的。
那么,问题是,上面的公式是怎么得到的?
我们来用最基本的数据算式推导一下。
设基准 UE 的图宽为 ue_w
,<html>
的 font-size
值为 ue_fs px
。
在 PSD 上量得一个元素的宽度为 psd_w px
,等于 psd_rem
,即:
psd_rem * ue_fs = psd_w -----------(1)
在一个宽度为 foo_w
的设备上,该元素应该给定的宽度为 x_w px
。
根据 rem
单位的意义可知:
foo_rem * foo_fs / foo_w = psd_rem * ue_fs / ue_w -----------(2)
即:
foo_rem = psd_rem * (ue_fs / foo_fs) * (foo_w / ue_w) -----------(3)
其中 psd_rem
、ue_fs
、foo_w
、ue_w
皆为已知,而 foo_fs
可给定一个具体值,相当于已知。
1 | .px2rem(@px){ |
例如,以iPhone6的尺寸为基准,即:
ue_fs = 75px(任取值)
ue_w = 375px
一个宽度为屏幕宽度一般的元素,即:
psd_rem = 375px / 2 / 75px = 2.5rem
在一台 iPhone6 plus 上,则:
foo_w = 414px
foo_fs = 69px(任取值)
代入(3),得
foo_rem = 2.5 * (75 / 69) * (414 / 375) = 3rem = 3 * 69px = 414px / 2
刚好也为屏幕的一半。因此,上面的 LESS
实际内容是:
1 | .px2rem(@px){ |
可见,实现与 UE 图等比例的效果,只要定一个基准的 ue_w
和一个基准的 ue_fs
,并任取一个当前设备的 foo_fs
就可以了,跟什么 dpr
、scale
根本没有关系。
那么如何根据屏幕宽度取一个合适的 foo_fs
呢?
再来看上面的(3)式,为了更精确的还原UE图,我们一定希望 foo_rem
和 psd_rem
都是有限小数,那么:
(ue_fs / foo_fs) * (foo_w / ue_w)
就也一定是有限小数。分解:
ue_fs / ue_w
和:
foo_fs / foo_w
最好都是有限小数。因此,只要取屏幕宽度的___约数___做 foo_fs
就可以了,如 iPhone6 上的 75px,iPhone6 plus 上的 69px 等等。
我们知道,在 dpr
大于1的设备上,是画不出来真正 1px 的,除非将 scale
设置成 1 / dpr
。这样,foo_w
也会成倍增加:
foo_w = screen.width * dpr
因此为了支持1物理像素,scale
必须设置成 1 / dpr
,foo_fs
取 screen.width * dpr
的约数。
CSS3
中新增了 vw
和 vh
两个单位,分别代表可见区域宽高的百分之一,目前浏览器支持程度还不好:
为了向后兼容,我们取 foo_w
的十分之一(百分之一会出小数)作为 foo_fs
:
foo_fs = foo_w / 10 = screen.width * dpr / 10 -----------(4)
这样,dpr
为2时,一个宽度为屏幕一半的元素尺寸为 5rem
,或 50vw
,仅数量级不同。
这就是为什么业界以(4)式计算 foo_fs
的缘由了。
rem
的方案就讲到这里,已经用数学算式推导出了 foo_fs
的计算公式(4)。
核心代码参考:
1 | var dpr = window.devicePixelRatio; |
相比于第一种,第二种方案显得简单粗暴:
设置一个基准的尺寸,页面上所有元素都按照此基准布局。然后将页面缩放到设备的真实尺寸上去。
比如设定基准为 400px,而真实设备尺寸为 500px,则 scale
必须为 500 / 400 = 1.25
。核心代码参考:
1 | var baseW = 400 |
同时还必须要设置HTML的宽度:
1 | html { |
不必再去计算 foo_rem
、foo_fs
等参数。由于 scale
并非等于 1 / dpr
,因此1物理像素也就没法实现了。同时,vw
、vh
的兼容也没有体现。好在它不需要转换 px 为 rem。
比较上述两种方案:
方案 | 等比布局 | 1物理像素 | 兼容vw/vh | 绝对定位 | UE尺寸 | 高清图 |
---|---|---|---|---|---|---|
第一种 | ✔ | ✔ | ✔ | ✔ | LESS | ✔ |
第二种 | ✔ | ✘ | ✘ | ✔ | ✔ | 有误差 |
因此两种方案的使用场景是:
vw/vh
向后兼容,则使用方案一,代表有淘宝,,缺点是需要单位换算,也存在一定的误差;上述两个方案比较让人不爽的是都使用了 JavaScript 脚本来动态设置 scale
的大小。在苹果公司的原始设计中, viewport
是这样使用的么?
参见 Safari HTML Reference,viewport
允许开发者设置 width 来调整适配的目标设备宽度,但最终document.documentElement.clientWidth
的值为
document.documentElement.clientWidth === Math.max(screen.width / scale, width)
在前面两个方案中,width 等于 screen.width
,scale
小于1,因此
document.documentElement.clientWidth === screen.width / scale === screen.width * dpr
如果 scale
写死为1,自定义的 width 不能小于 screen.width
。但一旦 width 大于 screen.width
,就会出现滚动条,这时,JavaScript 动态计算的 scale
上场了。
因此,按照苹果公司的设计初衷,没办法不使用 JavaScript 实现完美的 UE 还原。使用一份 UE 图,无法做到多个屏幕尺寸上呈现一致的效果。
移动端开发经常缺失的一个环节是,设计师很少提供横屏版的 UE。当手机屏幕横过来怎么办?
可以监听 resize
、pageshow
等事件,事件触发后,重新计算 scale
、foo_fs
等值。这样能保证页面元素的比例仍与 UE 相当。
有没有问题?
水平方向上好像没什么问题。垂直方向呢?
当整体页面按比例放大后,页面高度必然也会等比放大,而在横屏模式下,屏幕垂直高度又很小,从而导致大部分内容都被推出了首屏,体验和视觉上效果都不好。
因此,元素的高度一般不是用 rem 而使用 px,除非元素尺寸与屏幕尺寸强相关。
这是不是意味着所谓的完美还原是不切实际的?对于那种划页 H5, rem 的高度仍然试用,但对于普通的文本内容则不合适了。
依据具体需求采取不同的方案。
对于普通的需求,width=device-width
和 scale=1
就够了,虽然不能在不同的设备上展示同样的效果,但是够用。
对展现要求稍高,则使用 JavaScript 动态计算 scale
和 foo_fs
。
当小组决定让我写这个主题时,我是拒绝的,因为以我的资历,还远远不能积累到足够的素材、故事和教训来拼凑一篇像样的回忆录。因此,这里算是我的这个主题的个人
认识吧。
经常听到这样的言语:
现在业界对前端工程师这个角色的看法是有误解的,事实上前端的工作并没有那样简单。
我的个人观点是:__业界没有误解,前端的工作就是那样简单__。
最近在特别关注UI设计的社会培训课程,几乎每家培训机构在UI课程中都会加入一项”Web前端开发”,期望能在几天的培训课程后,能够让UI设计学员掌握一定的编码技巧,有能力承担简单网页的制作、JS特效等开发。可见前端入门水平之低。
让我们看拉勾网上一个招聘职位的 Job Description(JD):
1. 3年以上前端开发经验;
2. 熟练使用html和css制作符合W3C标准的页面,注重页面性能、语义化,能从整个产品的角度去考虑代码结构;
3. 熟悉JS,使用过JQ等前端框架,了解基本的语法
4. 熟悉AngularJS或同等框架,能做基于API接口的前端产品
5. 喜欢前端技术并且善于钻研,喜欢与他人分享自己的成果,乐于跟同行讨论问题
这是一个对有着三年工作经验的前端工程师的期望,
熟练使用html和css制作符合W3C标准的页面
,这就是前端工程师的基本工作内容;”注重页面性能、语义化,能从整个产品的角度去考虑代码结构”,ok,这有一点提升的要求了,不仅仅要求你制作的页面能用,还要求一定的优化。不过这一点属于extra内容,对于一家创业团队来说,现在重点是怎么迅速将投资人的钱转换为看得见的产品,起码有个交代,至于优化这些工作,你加班来做吧,反正老板暂时是不怎么care的;熟悉JS,使用过JQ等前端框架,了解基本的语法
,开什么玩笑,前端工程师如果不熟悉JS还能叫前端工程师吗,什么叫了解基本的语法,以现在这个年代的页面复杂性,恐怕需要了解全部语法而不是仅仅基本语法。不够了解全部语法也不是什么难事,相信99%的工程师都能做到,剩下那1%可以刨出工程师范畴之外了。JQ更不必说了,即使不是前端工程师,都有一大批人听说并且有能力使用;熟悉AngularJS或同等框架
,这要求又高了,AngularJS可不是所有前端工程师都听说过的,更不用说熟悉了,况且它那么复杂。这里有一个很有意思的点,”熟悉JS”和”熟悉AngularJS”,我理解这是一种包含的关系 ,熟悉AngularJS”必然”熟悉JS”,反之则不一定,因此,JD中的”熟悉JS”可以认为是废话;喜欢前端技术并且善于钻研
,这是套话了,暂且不表看来三年的前端工作经验也无非如此,只需要掌握基本的职业技能,再花点时间稍微扩展一下知识就能拿到14k~18k的月薪了,前端就是这么一个low的职业,不需要你拥有高学历,也不需要你有能力解决XXX算法之类的烧脑问题。因此,无数非计算机专业的、计算机专业里常挂科的、野鸡大学的、培训机构的都来分前端这块面包,造就了大量的前端工程师。好在近两年互联网移动化转型,产生了大量移动web前端职位,前景似乎一片光明。
随着业务的发展,我们贴吧这样的前端团队也需要及时补充血液,在过去几个月里,面试了的很大一部分候选人。其实对于百度这样公司的人才甄选标准,能够进入面试环节,简历必然也是相当可以的。而且对于有工作经验的社招候选人,其编码能力也不会差到哪去,支持团队的日常业务开发任务问题不会很大。为什么大部分候选人会被毙掉呢?因为我们要的是工程师
,而他们更像是网页制作
。
这是一个很大的差异,你可以理解为战略和战术的区别:网页制作能够解决单个Case(一般是一个HTML网页)的开发就ok,而工程师则要负责一套包含HTML页面的整体解决方案。咦?前端的工作有这么复杂么?有的。什么时候有的?我不太清楚,最近几年吧。
我们看一些前端的工作都复杂在哪里。首先,当我们设想一个前端工程的时候,会考虑:
上面每一个主题都可以拆成N多个小主题,涉及到的问题成百上千。其实前端工程中的真正难点还不是这些,而是备选方案五花八门,没有统一标准
。相对于其它领域,前端的技术发展太快,没有一种技术能够流行到成为主流。比如前些年的JS模块化规范 Require.js
现在已基本不被推荐,反而基于 Babel
的 ES6 Modules
越来越流行。能够利用自己的实践经验来有效规划合适的技术架构的人,已经成为了一种新的职业:前端架构师。
既然前端领域也如此善变和复杂,那么一个前端工程师应该如何学习和成长才能成为一名前端架构师呢?
以上几点是我对一名前端工程师的能力的基本认识,这与几年前相比几乎是截然不同的。之前很重要的技能在今天看来不值一提,今天必备的技能在之前看来也是遥不可及,可见随着时代变迁、技术的发展,对工程师的定义也会发生翻天覆地的变化。
那么成为一名合格的前端工程师之后,如何变为一名前端架构师呢?这里我认为有两点特征就够了,这两点特征也是大多数人的瓶颈,阻挡着大部分候选人进入百度,也阻碍着大部分”网页制作”变成”前端工程师”。这两点是:
在任何行业任何工种下,学习都是一个永恒的话题,特别对于web前端这样高速发展的领域。只有通过不断学习,才能有机会扩展视界,沉淀出足够的技术方案。同时,也只有总结,才能归纳出最合适的方案。这两点缺一不可,前端提到的几点要求只是现状,如何不满足,但拥有足够的学习和总结能力,这些就都会很快解决。因此,学习能力和总结能力才是工程师成长中最需要掌握的技能。
现在我们回到前面,为什么我说”前端的工作就是这么简单”,因为绝大多数业务的需求就是比较简单,只要开发一个可以看得见的网页就好了,这是普通网页制作人员就能Hold住的工作内容。这也说明了市场上绝大多数所谓”前端工程师”都是”网页制作”。一旦业务升级、团队规模扩大,”网页制作”的工作就会立即将整个团队的业务节奏推向万劫不复的深渊。一般地,在这种情况下至少会有一个人发现了问题,并开始解决,从此走向了”前端工程师”之路。
因此,在你的成长过程中,遇到困难和问题并非一定都是坏事,往往越糟糕的环境就越能激发你的创造天赋。在这一点上,一个有一定发展但又不足够成熟的公司往往更适合你的初期发展,它会培养你的学习和总结能力。我个人认为在中国,BAT这类公司并不合适,反而像豆瓣、知乎这类中小型公司,效率相对更高,会提供给你更大的发展空间。当你发现自己在这类公司中已经解决了很多问题,积累了足够的经验后,再去大型公司,比较一下他们的工作,会发现你之前的工作方式是多么低级和不规范,从而产生敬畏之心。当你怀着一份敬畏之心来对待这份工作,你会有着不一样的看法:前端领域深似海。
当我本人看穿这一切后,忽然觉得,前端如此,哪个领域又何尝不是?一味地追求极致只会让自己陷入我执当中。技术界多有高人精通,却有几人真正明白这些都是对于不完美的妥协?生活亦如此,每个人都称热爱生活,谁又会在意其背后的心酸、苦楚与无奈?
一切技术都是浮云,美好生活才是真谛。当你做这份工作时,失败和妥协只是你成长过程中不断被踢开的绊脚石,热爱和坚持才是那永不枯竭的推动力。不要过份追求成长,当你的经验教训积累足够,成长就会像那初春解冻的冰川,势如破竹。
我叫 yinyong,2013年硕士研究生毕业开始在宇宙中心五道口的搜狐网络大厦工作直到去年,目前在百度。2008年南方大雪的时候 刚好
选择了也留在西安过年,一起的还有许多不能回家的小伙伴。因为无聊,拿起了别人从图书馆借来的却没有阅读的《Java程序设计》,从此一发不可收拾踏上了编程之路。不过,毕业时做的WEB前端工程师的角色,目前扔在坚持但主要做前端技术架构方向的研究。
我是工程师,不是程序员。
上学时喜爱手机,曾经手握7部各种操作系统的手机(iOS除外,因为买不起)。不过后来兴趣越来越淡了,在折腾了几年的Android后,年纪大了心也累了,目前手握一部iPhone6,娱乐工作都能应付,而且充电速度很满意。家里的iPad Air基本上很少碰了,之前还用它看看美剧。
工作上我一直使用自己购买的Mac Air 2012,后来公司发了台Mac Pro 2014,发现除了续航外,体验等方面都不如自己的Air。在公司还放着一台神舟K480N-i7D3,买来主要看上了其CPU、显卡并且价格不高,在更换了SSD并加了内存后,主要用做玩游戏。不过现在也很少玩了,电脑放在那基本算是备用机。
此外,我基本没有其它硬件设备,特别对机械键盘我是嗤之以鼻的,因为它会让我觉得自己是手指体力劳动者而非脑力劳动者。
在文本编辑上,我一直使用 Sublime Text,最爱它的多行编辑功能,同时其插件使用Python编写也比较容易,自己也写过一个。
其它软件:
总之很少有特别的软件能反应出我的实际工作内容,甚至是便签我也一直使用mac自带的。
喜欢工位的一侧靠着窗户并且有很宽广的视野,同时希望后面不要有人可以随时窥视我的显示器。
因为现在很少再专注于一个具体技术问题,而更多的是大方案上的思考,因此更多的是 Google 各种国外 blog来攒取灵感。但偶尔也会去Github上搜寻有意思的项目。
一款iOS应用Days Matter,我用它来统计我自出生以来的天数。
本文参与了「利器社群计划」,发现更多创造者和他们的工具:http://liqi.io/community/
序号 | 时间 | 位置 |
---|---|---|
1 | 2014.12.15 | Call |
2 | 2015.2.14 | HuaiRou |
3 | 2015.3.14 | HuaiRou |
4 | 2015.3.16 | WangJing |
5 | 2015.3.22 | HuaiRou |
6 | 2015.3.25 | WangJing |
7 | 2015.3.26 | WangJing |
8 | 2015.3.29 | WangJingxi |
9 | 2015.4.2 | WangJing |
10 | 2015.4.4 | WangJingxi |
11 | 2015.4.6 | WangJingxi |
12 | 2015.4.10 | XiErqi |
13 | 2015.4.11 | WangJingxi |
14 | 2015.4.18 | WuDaokou |
15 | 2015.4.19 | HuaiRou |
16 | 2015.4.22 | WangJingxi |
17 | 2015.4.23 | WangJingxi |
18 | 2015.4.25 | HuiLongguan |
19 | 2015.4.30~5.1 | HuiLongguan/LongZe |
20 | 2015.5.2~5.3 | YanQing/LongZe |
21 | 2015.5.6 | WangJingxi |
22 | 2015.5.8 | WangJingxi |
23 | 2015.5.9 | ShangDi |
24 | 2015.5.13 | WangJingxi |
25 | 2015.5.15~5.17 | WangJingxi/HuiLongguan/LongZe |
26 | 2015.5.21 | WangJingxi |
27 | 2015.5.22~5.23 | HuiLongguan/LongZe |
28 | 2015.5.25 | WangJingxi |
29 | 2015.5.27 | WangJing |
30 | 2015.5.30 | WangJingxi/LongZe |
31 | 2015.6.2 | WangJingxi |
32 | 2015.6.5~6.7 | HuiLongguan/LongZe/HuaiRou |
33 | 2015.6.13~6.14 | YanQing/LongZe |
34 | 2015.6.16 | WangJingxi |
35 | 2015.6.17 | LongZe |
36 | 2015.6.23~2016.2.2 | LongZe/WangJing |
37 | 2016.2.11~2016.3.22 | WangJing |
38 | 2016.2.25~ | WangJing |