豪翔天下

Change My World by Program

0%

看完《架构之美》过后就开始看《安全之美》,感觉大量的章节读起来的感觉都差不多,并没有深入的体会也没有感觉对我有什么启发。我想,主要的原因可能是我大学本来就是学习的计算机大类里面的信息安全专业,书里面的大部分内容以及相关推理和讨论,学校的教材里面就讲过。当然我推测,学校的教材可能也是借鉴了外国的著作,也有可能是我们学校所使用的教材本来就是高手编写的。

当然,绝对不能说这本书写得差,只能说此书在我现在的阶段所能给我的帮助太少了,如果我刚刚接触信息安全这方面,可能会特别感兴趣,也会学习到很多。不过,确实有一点,安全确实很美,但是这本书却一直没有像预期的那样透过哲学或是其他什么方式突出安全的”美“。后来,我去看了豆瓣对此书的评价,评分只有6.7,果然比《架构之美》的7.1要少。而且评价里面几乎都是差评,即使有好评,也仅仅是读者对安全的评价,并没有对此书做过多的评价。只希望最后一部《数据之美》能不再让我失望了。

我从小就是一个机械迷,喜欢木匠,喜欢硬件开发,喜欢各种DIY,有了自己的房子,我就能搭建自己的小小工作间了。

电器

电机

  • 舵机: 里面有一个控制板和一个普通电机以及减速齿轮。可以精准控制旋转角度,但是只能180度旋转。常用于机器人的手臂旋转等。
  • 减速电机: 和直流电机差不多,但是因为能减速所以扭矩非常大。
  • 直流电机: 普通的一直转的电机,扭矩比较小。
  • 步进电机: 能够控制旋转角度,扭矩属于中等。淘宝链接: 混合式57步进电机,扭矩2.3Nm,高75.5mm

  • 锂电钻: 相比于其他的电钻,主要就是携带方便,小巧玲珑,缺点是功率小,要想用来钻墙基本是不可能的。手上有一个12V不能变速的锂电钻,小幅度提升幸福感,电器的螺丝基本上可以,但是遇到需要强有力的螺丝的时候是凝不紧的,还得用手。试过用钻头钻木材,比较慢;而且夹持范围太小,1cm的钻头都夹不了。
  • 冲击钻: 和电锤原理一样,都是在旋转的时候做小幅度活塞运动。
  • 电锤: 冲击钻打不动的就用电锤了。
  • 石工钻头: 通体银白色,主要用于砖墙。
  • 木工钻头: 头上非常尖锐,主要用于木材。
  • 金工钻头(麻花钻头): 通体黑色,主要用于金属。

切磨

切磨当然是用切磨机,但是有时候也可以用电钻代替,有连接杆可以连接,但是用电钻的话并不方便实用。

  • 羊毛轮(羊毛抛光轮): 专用与抛光或修补擦伤的材料,如玻璃、陶瓷、石材、金属、塑料等
  • 切割片
  • 百叶轮:用来磨的

线

电流只与导线截面积有关。0.2平方的线是1A,0.3平方的线是1.8A,0.5平方的线可以通过2.5A/220V/550W,0.75平方的线是3.75A。

  • 杜邦线: 用于实验板的引脚扩展,可以非常牢靠地和插针连接,无需焊接,可以快速进行电路试验。
  • OK线:比普通导线细,一般用于电路板飞线,PCB跳线等,一般是30号规格,最大承受0.5A电流。价格都非常便宜。

继电器

一种电子控制器件,用较小的电流去控制较大电流的一种“自动开关”,在电路中起着自动调节、安全保护、转换电路等作用。当然,其实也能用弱电控制强电。继电器连线示意图:

继电器端口示意图

注意事项:

  • 继电器的VCC(接直流正极)与GND(接直流负极)是用来接模块的供电,必须是直流电。
  • IN端是模块触发端,当有触发信号时,继电器的输出端开关会闭合,就会通电。
  • 高电平触发(PNP触发): 正极触发,即在触发端与电源负极之间电压达到触发条件的电压时,继电器闭合。(例如,当IN端有3-12V时,继电器闭合,当IN端有0-0.5V时,继电器断开)
  • 低电平触发(NPN触发): 负极触发,即在触发端与电源负极之间电压为0V或接近0V时,继电器闭合。(例如,当IN端由0-4.5V时,继电器闭合,当IN端有大于5.5V时,继电器断开)

非电器

木料

铁料

钉子

  • 水泥钉

  • 膨胀螺丝(膨胀螺栓): 固定式灯具,如无特殊要求,墙壁开孔一般均为6毫米。

    螺栓规格(毫米) 钻孔尺寸 (毫米) 受力性能 (公斤)
    直径 深度 允许拉力 允许剪力
    M6 10.5 40 240 180
    M8 12.5 50 440 330
    M10 14.5 60 700 520
    M12 19 75 1030 740
    M16 23 100 1940 1440

2016总结

2017年是我的本命年,果然,在2017一开始的时候就遇到几件大事,和女朋友吵了一场很大的架,差点错过彼此。可以说,整个一月份都是在吵架、和好的过程中度过的,最后还是和好了,并且我也确定了她,过年的时候居然还订了婚,果然,吵架是情侣关系的一个坎,过去了,就一生一世,过不去,就只是路人。这里又一次,站在农历的过年前,写下了我的总结,再一次感叹,那些每次在新年一开始就写总结的人在最后的一个月里是多么的空闲。

今年,是我和她在一起的第二年,今年吵架的次数肯定是无数次的,吵到要分手的程度也有四五次吧,不过,基本上是我转正过后,准确地说是在我拿到现在这份期待已久的工资的时候。有了这份工资,我终于可以买到自己想要的东西了,也因为有了这份工资,无形中,自己被欲望所奴役,导致忘记了曾经那个理性的自己,忘记了曾经那个善良的自己,少了对她的疼爱,多了对房、车、以及所有希望买的东西的欲望。一直以来我都在忙着做自己的事情,在乎自己在乎的东西,总是觉得自己认为的就是对的,但是我并没有真正去了解她,并没有真正走进过她,被欲望遮住了双眼的我,甚至多次讽刺她,瞧不起她。即使这样,她都还是原谅了我,最终还是和好如初,这样的女人,我怎么可能不把她留在身边呢,所以,我们决定今年请双方家长一起过来团年,明年春节订婚。具体结婚的日期,我希望是2018年10月1日,因为那一天我生日,我在大概10岁的时候就算好了那一天是我的生日,从小学开始我就告诉身边的人,我要在那一天结婚,没想到,这个愿望还真有可能实现。而且晚婚假15天,那天我刚好25岁,真巧。

今年,我们买房了。一套走几分钟才能看到江的”江景房“,两室一厅,不算大,但是住我们倆,再加父母偶尔来一下,完全够了,而且阳台和厨房超大,以后可以更多的享受做饭和休闲的乐趣。旁边有两个公园,距离嘉陵江很近,非常适合养生,感觉住在这里可以多活好几年呢。贷款加上利息接近百万,要还款30年,其实没什么,刚需房,想买的时候就买,毕竟回头看去,只过了两个月,那套房子的价格又涨了我一年多的工资总数。另外,对于爸爸妈妈出钱付首付和花精力来帮我装修房子,还是只能说此生报不完的恩情。

今年,毕业了,正式工作了,虽然工作经验已经有了一年半,但是今年算是真正的开始工作了。毕业答辩有惊无险的过了,对于同班同学,由于大三才呆在一起,所以感情并不大,反而对以前那个班的同学感情比较深一点,当然,最深的还是室友,经常会想起他们,经常想起我在寝室飙歌,可惜,一切都回不去了。现在大家每天都有各自的工作,晚上回到家,大家也都很累,根本没时间大家联系了。不过还是希望大家,前程似锦。工作了三个月顺利转正,由于已经工作一年,对这些并无感,而且上家公司福利待遇超级好,哈哈。

今年,学日语的目标没有达成,连五十音都还不会呀。因为买房,所以给父母做全身检查的目标也没达成,结交IT界好友的目标没达成。。。。唯一达成的是博客日访问量10+盒SegmentFault声望1k+。当然,这些是我在今年年中定下的目标。每次,我的生命中有大事的时候,我就会每天想着它而不能干其他的事情,比如要毕业了,我心里一直想着毕业的事情,要买房了,一直想着买房的事情,要装修就一直想着装修的事情,这样一件接着一件,我觉得我每天都在浪费时间,突然觉得每个人的生命中都会遇到这样或者那样的大事,但可能只有我,需要花特别多的精力在上面,甚至不去想它就无法用心做其他的事情。无论怎样,希望明年的自己能够更强大,能够战胜拖延症。。。

2016,感谢你,让我变成今天的自己。

2017计划

豪翔天下

  • 简书: 1000+喜欢

  • 微信: 100+订阅

  • 微博: 粉丝+1000

  • 博客: 日均20+

  • 专栏: 1个

  • 阅读: 20+本

  • GitHub: 100+ star

  • SegmentFault: 3k+声望

  • 玩玩儿硬件开发

  • 搭建家庭NAS

  • 多做几个side project

  • 日语二级(这是我2014年定下的2015年的目标,2016年并没有完成。。。)

资产

  • 研究理财产品: 10+

  • 和女朋友存款1w+(婚戒、买车),(2017年1月26日,发年终奖了,婚戒已买)

  • 坚持记账

  • 把家装修成想要的样子

健康

  • 平均每天1w+步
  • 每周一次,运动量大一点的运动
  • 参加一次集体运动,如马拉松等

情感

  • 和女朋友至少一次出省旅游
  • 至少一次带家人旅游
  • 和女朋友多次省内旅游
  • 多去和老朋友聚聚会
  • 家人生日尽量回家
  • 带双方父母做健康检查

Aria2简介

Aria2是一个轻量级的多源多线程的跨平台的命令行下载工具,支持HTTP/HTTPs、FTP、SFTP、BitTorrent和Metalink等下载方式。当然,目测,国内更多用于百度云的下载,不过,自从我家里用了linux的nas,并且迅雷已经不能用的情况下,我也更倾向于使用aria2。

相比于you-get以及其他的下载工具,aria2最大的优点是其内部的连接数控制缓存控制能够明显提高下载速度,并且不会轻易失败

Aria2的安装

For Mac: brew install aria2

For Debian: apt-get install aria2

Aria2的使用

1
2
3
4
5
6
7
# 参数说明
--all-proxy=127.0.0.1:8181 # 设置http代理,包括了http/https/ftp
-m 100: 设置重试次数,默认是5
-c: 断点续传,如果有只下载了部分的文件那么继续下载
-k20M: 多大的缓存,默认是20M,建议修改为-k1M,因为网络环境不好
-s5: 使用多少个连接数,默认为5,建议为10
-x1: 每个服务器的最大连接数,默认为1,建议为16
  1. (可选)安装ria2GUI,这是Aria2的桌面GUI程序,集成了aria2c,支持多线程下载,为完成任务退出自动保存,支持PT/BT,可以显示整体下载速度。方便管理,从github进行下载,下载完成后直接解压打开就是个dmg应用了。
  1. 安装百度网盘的导出工具,谷歌浏览器插件下载地址,由于谷歌把它禁用了,得自己下载来使用(貌似是违反了谷歌插件的不能改变网页内容的条例)。

  2. 现在可以打开百度网盘,直接选择导出到rpc即可开始下载了。

重要概念

生产者(Producer)

消费者(Consumer)

消费消息。每个consumer属于一个特定的consumer group。使用consumer high level API时,同一个topic的一条消息只能被同一个consumer group内的一个consumer消费,但多个consumer group可同时消费这一消息。每个partition只会由一个consumer消费。

集群(Cluster)

宏观来看,Kafka主体包含的就是三部分: 生产者、消费者和集群,一个集群就是多个Broker的集合。

Broker

已经发布的消息就会保存在集群中的某个Broker中去。

Topic

用来区别message的种类,比如很多时候,与A相关的日志统一的topic定义为A,B相关的日志统一的topic定义为B,这样就不用一个一个单独地订阅了。物理上不通topic的消息分开存储,逻辑上一个topic的消息虽然保存于一个或多个broker上,但是用户只需指定消息的topic即可生产或消费数据而不必关心数据在哪里。

Partition

Kafka中每个Topic都会有一个或多个Partition,他是Kafaka数据存储的基本单元,每个Partition对应一个文件夹,文件夹下存储这个Partition的所有消息和索引。Kafka内部会根据算法得出一个值,根据这个值放入对应的partition目录中。所以读取时间复杂度为O(1)。分区的每一个消息都有一个连续的序列号叫做offset,用来在分区中唯一标识这个消息。一个topic可以保存在多个partition。Kafka会保证每个partition内部的顺序,但是不能保证跨partition的全局顺序,如果要保证全局有序,那么topic就只能有一个partition。如果一个group内部的consumer数量小于partition数量,那么至少有一个consumer会消费多个partition。当consumer数量和partition数量相等时效率最高。consumer数量不要大于partition数量,否则会有consumer空闲。consumer会自动负载到不同的partition。

  • 对于某一个topic,增加partition可以增加吞吐能力,但无法保证topic级别的有序性。

Segment

一个partition由多个Segment组成,一个Partition代表一个文件夹,一个Segment则代表该文件夹下的文件。Segment有大小限制,由log.segment.bytes 定义。

安装

docker方式安装

docker-compose.yml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
version: '2'
services:
zookeeper:
image: wurstmeister/zookeeper
environment:
JMX: 9000
ports:
- "2181:2181"
kafka:
image: wurstmeister/kafka # 这个镜像使用文档见https://github.com/wurstmeister/kafka-docker
ports:
- "9092"
environment:
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://:9092 # 这是重点,否则,在容器内部启动生产者消费者都会失败的
KAFKA_LISTENERS: PLAINTEXT://:9092
KAFKA_CREATE_TOPICS: "test:1:1" # 自动创建一个默认的topic
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_AUTO_CREATE_TOPICS_ENABLE: "false" # 禁用掉自动创建topic的功能,使用上面的镜像,kafka的参数设置都可以以这样的方式进行设置
volumes:
- /var/run/docker.sock:/var/run/docker.sock
kafka-manager:
image: sheepkiller/kafka-manager # 如果要安装web管理工具可以同时安装这个,最后通过宿主机IP的9000端口进行访问,例如172.31.148.174:9000
links:
- kafka
- zookeeper
environment:
ZK_HOSTS: zookeeper:2181
APPLICATION_SECRET: "letmein"
ports:
- "9000:9000"
expose:
- "9000"

安装命令:

1
2
3
4
5
docker-compose up -d			# 默认只会有一个kafka实例
docker-compose scale kafka=n # 将kafka实例增加到n个,什么都不用修改,就能直接建立一个集群
docker-compose stop # 暂停所有容器
docker-compose start # 开启所有容器
docker-compose rm -f # 删除所有容器

kafka命令

kafka-console-consumer.sh

1
kafka-console-consumer.sh --bootstrap-server localhost:9092 --from-beginning --topic test	# 启动一个消费者,监听test这个topic

kafka-console-producer.sh

1
2
kafka-console-producer.sh --broker-list localhost:9092 --topic test	# 启动一个生产者,直接输入消息回车即可发送消息了
kafka-console-producer.sh --broker-list localhost:9092 --topic test < access.log # 直接将文件内容传入kafka

kafka-consumer-groups.sh

1
2
kafka-consumer-groups.sh --new-consumer --bootstrap-server localhost:9092 --list	# 查看新消费者列表
kafka-consumer-groups.sh --new-consumer --bootstrap-server localhost:9092 --describe --group kafka-python-default-group # 查看某消费者的消费详情,这里的消费者名称就是kafka-python-default-group

kafka-producer-perf-test.sh自带的压测工具

1
kafka-producer-perf-test.sh --topic test --num-records 10000 --record-size 1 --throughput 100  --producer-props bootstrap.servers=localhost:9092	# 总共100条数据,每条大小是1

kafka-topics.sh

1
2
kafka-topics.sh --list --zookeeper zookeeper:2181		# 列出所有的topic
kafka-topics.sh --describe --zookeeper zookeeper:2181 # 查看集群描述

安全认证

Kafka可以配合SSL+ACL来进行安全认证: http://orchome.com/185

TroubleShooting

  • 容器内部启动生产者出现错误:[2016-12-26 03:03:39,983] WARN Error while fetching metadata with correlation id 0 : {test=UNKNOWN_TOPIC_OR_PARTITION} (org.apache.kafka.clients.NetworkClient)

    是因为docker-compose文件里面的宿主讥IP设置出错,如果是动态IP的话就没办法了,只能删除重新创建了

  • 启动生产者或者消费者出现LEADER_NOT_AVAILABLE:原因是没有执行docker-compose scale kafka=n

拓展阅读

之所以要叫超简短书评,是因为这本书我根本没看完,只看了前面几篇文章,我就毫无继续看下去的动力了。说来也怪,我看过那么多的书,还从来没有这种感觉。可能是最近身边的杂事太多,也可能是这本书确实不如期望中的那样好。给人的感觉就是作者在写身边的小故事,但是故事平平静静,并且平静中并没有什么惊喜,甚至我连他到底在讲什么都不知道。最后,和大多数散文集一样,此书书名“一生里的某一刻”同样取自其中一篇。然后我就直接跳到那一篇。果然,依然是云里雾里的。超简短书评,就这样吧,相信大家也能看出我对此书的态度了。

基本概念及程序框架

主要目录文件

AppDelegate.swift: 所有应用开始都有一个AppDelegate,是整个应用程序的一个代理。在应用启动的时候,最先被调用的就是这个AppDelegate中的applicationDidFinishLaunching方法,可以在这里做全局初始化,但一般为了保持代码的整洁,具体逻辑并不放在这里。

Assets.xcasserts:

Main.storyboard: 项目主要的UI文件

Info.plist: 项目的基础配置

*.h: 头文件。包含类,类型,函数和常量的声明

*.m: 源代码文件。可以有Objective-CCSwift代码

*.mm: 源代码文件。可以有Objective-C/C/C++/Swift

*.cpp: C++代码

组件IB(Interface Builder)

Labels and Text Field

Combo Boxes

Text Views

Sliders

Date Pickers

Buttons

Radio Buttons

Check Buttons

Image Views

定位

居中定位

1
# 直接选择该Object右下角的Align的Horizontally in Container和Vertically in Container全部设置为0,然后选择Update Frame修改为Items of New Constraints,再点击Add 2 Constraints button.

权限

info.plist权限列表,如果要用到相应的功能却没有添加相应的权限,很可能APP会直接闪退。以下列表来自于[槑头脑]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!-- 相册 --> 
<key>NSPhotoLibraryUsageDescription</key>
<string>App需要您的同意,才能访问相册</string>
<!-- 相机 -->
<key>NSCameraUsageDescription</key>
<string>App需要您的同意,才能访问相机</string>
<!-- 麦克风 -->
<key>NSMicrophoneUsageDescription</key>
<string>App需要您的同意,才能访问麦克风</string>
<!-- 位置 -->
<key>NSLocationUsageDescription</key>
<string>App需要您的同意,才能访问位置</string>
<!-- 在使用期间访问位置 -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>App需要您的同意,才能在使用期间访问位置</string>
<!-- 始终访问位置 -->
<key>NSLocationAlwaysUsageDescription</key>
<string>App需要您的同意,才能始终访问位置</string>
<!-- 日历 -->
<key>NSCalendarsUsageDescription</key>
<string>App需要您的同意,才能访问日历</string>
<!-- 提醒事项 -->
<key>NSRemindersUsageDescription</key>
<string>App需要您的同意,才能访问提醒事项</string>
<!-- 运动与健身 -->
<key>NSMotionUsageDescription</key> <string>App需要您的同意,才能访问运动与健身</string>
<!-- 健康更新 -->
<key>NSHealthUpdateUsageDescription</key>
<string>App需要您的同意,才能访问健康更新 </string>
<!-- 健康分享 -->
<key>NSHealthShareUsageDescription</key>
<string>App需要您的同意,才能访问健康分享</string>
<!-- 蓝牙 -->
<key>NSBluetoothPeripheralUsageDescription</key>
<string>App需要您的同意,才能访问蓝牙</string>
<!-- 媒体资料库 -->
<key>NSAppleMusicUsageDescription</key>
<string>App需要您的同意,才能访问媒体资料库</string>

widget也都可以通过代码来设置他们的属性,例如

在applicationDidFinishLaunching函数内部写上

label1.stringValue = “wanghao”
button1.title = “Change Text”
Toolbar 菜单栏
view controller,也就是最开始的那种storyboard
container view,这里应该是可以放其他的view,相当于网页里面的iframe
custom view
vertical Split view/horizontal split view :水平或者垂直分割的view
Collection view:就是将一些列的数据以表格的形式展(包含了Collecotion Item),datasource可以直接outlets到view controller
object:就是一个对象,它可以与类帮顶起来(右上角custom class进行定义),定义了过后,又可以将它与下面的view相联系(control+拖曳,选择outlets),
table view:表格

Array Controller:好像是管理一组controller

menu bar:

要连接两个view,也是control拖曳

这个地方可以添加绝对定位或者想对定位 绝对定位想对定位

右边那个按钮则是像html里面的margin

上面是Day 2:auto layout

创建新的类的时候也可以同时创建xib文件,上面的collection controller,如果要有一个复杂的item那么久需要自己创建一个类

menu bar

直接在appdelegate里面:要设置application is agent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(NSVariableStatusItemLength)
//let menu = NSMenu()
let popover = NSPopover()

func applicationDidFinishLaunching(...){
if let button = statusItem.button {
button.image = NSImage(named: "图片名称")// 在asets里面添加了的图片
//button.action = Selector("showWeatheraaa")

button.actoin = Selector("toggleWeather:")
}

//menu.addItem(NSMenuItem(title: "直接加", action: Selector("showWeather:"), keyEquivalent:"S"))
//menu.addItem(NSMEnuItem.separatorItem())
//menu.addItem(NSMenuItem(title:"quit", action: Selector("terminate:"), keyEquivalent:"q"))

// statusItem.menu = menu
popover.contentViewController =
}

func showWeather(sender: NSStatusBarButton){
print("我靠")
}

func toogleWeather(sender: NSStatusBarButton){
if popover.shown{
popover.performClose(sender)
}else{
if let button = statusItem.button {
popover.shownRelativeToRect(button.bounds, ofView: button, perferredEdge: .MinY)
}
}
}

新建了NSOBbject过后,要让它MainMenu .xib加载的时候久被加载,需要把它以object的形式添加到XIB中去

而这个object的Outlets就是相对应的view,比如Status Menu

同理如果创建的是一个NSview,那么一个view泽可以与之关联

安装与配置

  • angular不同的版本对typescript的版本要求是不同的,可以参考这里
  • angular升级是非常简单的,只要参考官方升级文档一步一步升即可
1
2
3
4
5
6
7
8
9
10
11
ng serve --host 0.0.0.0 --port 3000	# 启动,指定host,指定port

ng build --aot --optimization --build-optimizer # 编译项目
--aot # 默认为false,是否用提前编译进行构建
--optimization # 默认为false,使用构建输出优化
--build-optimizer # 默认为false,使用aot进行优化,推荐加上这个参数
--extract-css # 默认为false,从全局样式中提取css到css文件而不是放在js文件
--source-map # 默认为true,输出source-map文件
--vendor-chunk # 默认为true,将第三方包单独放到一个vendor文件中

ng build --deploy-url /app/ --deploy-url /app/ # 如果想要app运行在一个子路由路径下可以这样做

Module

1
2
3
4
5
6
7
8
9
10
11
@NgModule({
declarations: [
UserComponent
],
imports: [

],
entryComponents: [
DialogComponent, // 对于动态调用的组件,没有在html中调用,而是用js来调用的组件需要在这里声明,例如一些弹框,不声明的话,在动态编译的时候可能发现模板没有引用就不去加载了,但是我发现在lazy loading的时候,如果在child module中声明entryComponents不起作用,只能在app.module中声明才行
]
})

Lazy loading延迟加载

  • 延迟加载是基于页面路由的,每个路由都可以单独作为一个延迟加载,在进入页面的时候加载该页面所需要的组件
  • 如果实现了延迟加载我们在进入对应的页面后会发现新请求一个1.xxxx.js的文件,开头是一个数字。这就是当前页面的一些组件,同时我们会发现当前页面的组件在main.js中没有了
  • 如果我们的页面都是单纯的component而不是module的话需要做这些改造
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 在页面组件新建路由文件,例如dashboard.route.ts
export const routes: Routes = [
{path: '', component: DashboardComponent}
]

// 在页面组件新建module文件,例如dashboard.module.ts
import {routes} from './dashboard.route';
@NgModule({
imports: [
CommonModule,
RouterModule.forChild(routes) // 注意这里是forChild不是forRoot
],
declarations: [
DashboardComponent,
]
})
export class DashboardComponent { }

// app-routing.module.ts修改路由方式
const routes: :Routes = [
{
path: 'dashboard',
loadChildren: () => import('./dashhboard/dashboard.module').then(m => m.DashboardModule) // 注意这里是Module不是Component
}
]

// 最后在app.module.ts中,移除DashboardCompoent依赖即可

模板语法

数据绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 变量字符串连接
<img src="https://haofly.net/{{ image.url }}" />

// 动态绑定类
[ngClass]="{'myClass': selected}"
[ngClass]="type='xxx' ? 'mt-1' : 'mt-2'"

// 动态绑定样式
[ngStyle]="{'pointer-events': ok ? 'none' : 'auto'}"

<div [innerHTML]="string"></div> // 直接渲染html变量,默认会去掉元素内部的inline styling等属性
<div [innerHTML]="var"></div>
constructor(protected _sanitizer: DomSanitizer) {
this.var = this._sanitizer.bypassSecurityTrustHTML('string') // 直接渲染html变量,和上面的innerHTML不同的是,这样做,元素内部的inline styling等不会被去掉
}

控制语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// for 循环
<ul>
<li *ngFor="let item of items; let i = index">
{{i}}:{{item}}
</li>
<li *ngFor="let item of map | mapToIterable"> <!--对于key value的map进行for循环遍历-->
{{item.key}}:{{item.value}}
</li>
</ul>

// switch语句
<div [ngSwitch]="myvalue">
<div *ngSwitchCase="'aaa'">
...
</div>
<div *ngSwitchCase="'bbb'">
...
</div>
<div *ngSwitchDefault>
...
</div>
</div>

// ngShow和ngHide在angular 2+已经不支持了,可以直接这样做
[hidden]="myVar"

get方法/computed方法

  • 类似于vuejs中的computed
1
2
3
get 字段名() {
return this.firstname + ' ' + this.lastname;
}

表单Form

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// js/ts文件
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';

export class MyComponent implements OnInit {
myForm: FormGroup;

constructor(private formBuilder: FormBuilder);

ngOnInit(): void {
this.myForm = this.formBuilder.group({
formFieldName: ['初始值', [Validators.required, this.checkName()]], // 第一个参数设置初始值,第二个参数是验证方法列表,注意如果有多个validator,后面的一定要用中括号包起来,否则会报错Expected validator to return Promise or Observable
字段2: ['', []],
字段3: new FormControl('', {
validators: [
this.aaaaaa.bind(this) // 自定义验证方法
],
updateOn: 'blur' // 失去焦点的时候进行验证
}),
字段4: [{value: '初始值', disabled: true}] // 如果要让某个字段disabled需要在这里做,直接在html上面disable可能不生效
}, {
validator: this.checkAll // 如果不是针对某个字段,而是针对整个表单,比如同时验证多个字段,那么可以在这里做
})
}

this.checkName(): any {
return (control: AbstractControl): { [key: string]: boolean } | null => {
return control.value >= 0 && control.value <= 2 ? null : {nameValueError: true}; // 如果出错可以返回一个key-value
};
}

this.checkAll(formGroup: FormGroup): any {
return (formGroup.value.formName !== 'new') ? null : {typeEmpty: true};
}

onSubmit(): void {
this.submitting = true;
this.myForm.get('field1').setValue(value); // 手动设置form表单字段的额值
if (this.myForm.valid) {
console.log('its ok');
}
}
}

// html中这样使用
<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
<div class="form-group">
<label>Name</label>
<input type="text" class="form-control" (input)="inputChange" formControlName="formFieldName" [(ngModel)]="user.name">
<p class="form-warning" *ngIf="submitting && createForm.get('formName').errors">
<span *ngIf="createForm.get('formName').errors.nameValueError"> // 这是上面自定义的错误
Name Should be 1 or 2.
</span>
</p>
</div>
<div class="mat-form-field"> // 注意表单级别的校验error,不能写在field下面,后者不会显示出来,mmp
<mat-error class="form-errors" *ngIf="formGroup.hasError('wrongDate')" i18n
>The end date should be after the start date.
</mat-error>
</div>
<button type=submit">Submit</button>
</form>

filter过滤器

1
{{ timestamp * 1000 | date: 'yyyy-MM-dd'}} // 时间格式化

样式

1
2
3
4
// 如果要覆盖第三方组件的样式,可以用::ng-deep,并且为了防止把其他组件也覆盖了,可以加:host前缀将样式覆盖限制在当前的宿主元素上面去
:host ::ng-deep .xxx {

}

组件通信

父组件至子组件通信

  • 直接用@Input
1
2
3
4
5
<app-child [field]="value"></app-child>

export class ChildComponent {
@Input() field: any; // 根据我的测试,子组件可能无法在contructor或者onInit中获取到这个值,因为这个值可能是动态的,所以最好在子组件创建一个get XXX()方法来获取变化后的值
}

子组件至父组件通信

  • @Output EventEmitter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<app-child (field)="onChildClick($event)"></app-child>

export class ParentComponent {
onChildClick(field) {
console.log(field);
}
}

export class ChildComponent {
@Output() field = new EventEmitter<String>();

onClick() {
this.field.emit('click');
}
}
  • @ViewChild不仅能获取子组件的字段,还能直接使用子组件的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<app-child></app-child>

export class ParentComponent {
@ViewChild(ChildComponent)
private childComponent: ChildComponent

onTest () {
this.childComponent.onTest1();
}
}

export class ChildComponent {
onTest1 () {}
}

不相关的组件通信

  • 创建service来通信,复杂的应用场景这个还是用得比较多
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 需要先找个地方新建一个service
@Injectable()
export class MyFieldService {
private myField: Subject<string> = new Subject<string>();

setMessage(value: string) {
this.myField.next(value)
}

getMessage() {
return this.myField.asObservable()
}
}

export class Component1 {
constructor(private myFieldService: MyFieldService)

onFieldChange() {
this.myFieldService.setMessage('new value');
}
}

export class Component2 {
constructor(private myFieldService: MyFieldService) {
// 需要特别注意的是,如果回调函数报错了,之后就不会监听了,造成了只能监听一次的假象
this.myFieldService.getMessage().subscribe((value) => {
...
}
}
}

生命周期

依次是

  • ngOnChanges(需implements OnChanges): 当设置或重新设置数据绑定的输入属性时响应,但是当组件没有输入,或者使用它时没有提供任何输入,那么框架就不会调用ngOnChanges()
  • ngOnInit(需implements OnInit)
  • ngDoCheck
  • ngAfterContentInit()
  • ngAfterContentChecked()
  • ngAfterViewInit(需implements AfterViewInit): 当初始化完组件视图以及子视图或包含该指令的视图之后调用,只会调用一次
  • ngAfterViewChecked
  • ngOnDestroy

扩展

  • @angular/flex-layout: angular的flex布局组件,能够很方便地实现flex响应式布局

    1
    <div fxLayout="row" fxLayoutAlign="space-between"></div>

事件

Angular1里元素绑定点击事件用ng-click,但是Angular2里元素绑定点击事件用(click),例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// click事件
<button (click)="toggleImage()">

// input事件是指输入的时候
// change事件是指内容改变以后(离开焦点)
<input (input)="onInput()" (change)="onChange()"
(keyup)="onKeyUp(event)" // 键盘输入事件event.target.value可以获取input的value
>

<!-- select元素点击获取选择的值 -->
<select (change)="onChange($event.target.value)">
<option *ngFor="let item of devices | keyvalue" value="{{ item.key }}">{{ item.value }}</option> <!--keyvalue过滤器将字典转换为key value对象的形式-->
</select>

// keydown事件指定键,例如按下回车
<input (keydown.enter)="" />

网络请求

  • angularjs的网络操作由HttpClient服务提供,在4.3.x开始使用HttpClient代替Http
  • angular的http请求返回的是一个Observable(可观察对象),在被消费者subscribe(订阅)之前,不会被执行。subscribe函数返回一个subscription对象,里面有一个unsubscribe函数,可以随时拒绝消息的接收
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
constructor(private http: HttpClient) {}
ngOnInit(): void {
// 必须使用subscribe才会真的去发送请求。每次调用subscribe可以发送一次请求,也就算是说要发送多个请求,直接在最后那subscribe就可以了。

this.http.get('/').subscribe(
data => {},
error => {
error.json // 获取json格式的错误相应
} // catch error
);
this.http.post('', body, {}, {params: new HttpParams().set('id', 3')}); // 添加url参数
this.http.post('', body).subscribe(...); // post请求
this.http.post('', body, {headers: new HttpHeaders().set('Authorization', 'my-auth-token')}); // 设置请求头
this.http.get('').subscribe(
data => {}
err => {'错误处理'}
);
this.http.get('').retry(3).subscribe(...); // 设置重试次数
this.http.get(''). {responseType: 'text'}.subscribe(...); // 请求非json数据

// 设置自定义的超时时间
import { timeout, catchError } from 'rxjs/operators';
import { of } from 'rxjs/observable/of';

this.http.get('').pipe(timeout(2000), catchError(e => {
return of(null); // 需要注意的是,这里的of的参数会传递给subscribe的res作为返回值
})).subscribe((res) => {});

await this.http.get('').toPromise(); // 将网络请求转换为promise就可以用promise的await语法了

// 如果一个函数需要返回一个Observable对象,但是又根据条件来进行http请求,条件满足直接返回结果可以用of来封装一下
if ([condition]) {
return of('result');
} else {
return this.http.get('');
}
}

httpclient全局error handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 新建一个http-interceptor.ts文件,或者其他名字都可
import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse, HTTP_INTERCEPTORS } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { _throw } from 'rxjs/observable/throw';
import 'rxjs/add/operator/catch';

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req)
.catch(errorResponse => {
if (errorResponse.error && errorResponse.error.msg) {
...
}

throw errorResponse;
});
}
}

export const ErrorInterceptorProvider = {
provide: HTTP_INTERCEPTORS,
useClass: ErrorInterceptor,
multi: true,
};

// 然后在app.module.ts中声明这个provider即可
@NgModule({
providers: [
ErrorInterceptor
]
})

文件上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<input #photoUpload type="file" accept="image/*" (change)="onInput($event)">
<button class="primaryButton" (click)="uploadImage()">Upload Image</button>

export class MyComponent {
@ViewChild('photoUpload') adminPhotoUpload: ElementRef;

uploadImage(): {
const files = this.photoUpload.nativeElement.files;
const formData: FormData = new FormData();
formData.append('file', file, file.name);

const headers = new HttpHeaders();
headers.append('Content-Type', 'multipart/form-data');
headers.append('Accept', 'application/json');

return this.http.post(`${apiURL}/api/storage`, formData, {
headers
});
}

// 或者这样做
onInput(event): {
this.file = event.target.files[0];
}
}

单元测试

所有的单元测试文件均以.spec.ts结尾,该文件具体语法规则如下:

1
2
3
4
describe('test haofly"s function', () =>{
it('true is true', () => expect(true).toEqual(true));
it('null is true', () => exect(null).not.toEqual(true));
});

推荐扩展包

ngx-dropzone

  • 拖拽上传文件组件

ngx-socket-io

  • Socket-io扩展
  • 有一个问题是该第三方包现在是支持extraHeaders的(支持自定义header传入后端),但是却没有发布到npm仓库,参考这个issue,下面有人提出解决办法,参考这里,但是登录的时候还没有token,所以最好是在组件的init里面自己new一个Socket对象吧

TroubleShooting

  • Cannot read property ‘stringify’ of undefined: 在模板中无法直接使用JSON等原生对象,可以在constructor()中传入:

    1
    2
    3
    public constructor() {
    this.JSON = JSON;
    }
  • can’t bind to ‘ngSwitchWhen’ since it isn’t a known property of ‘template’: ngSwitchWhen已经被ngSwitchCase替代了

  • **can’t bind to ‘ngModel’ since it isn’t a known property of ‘input’: ** 尝试将FormsModule添加到@NgModuleimports

  • ng: command not found: npm install -g @angular/cli@latest

  • URLSearchParams is not a constructor: 通常是因为引用URLSearchParams是通过import { URLSearchParams } from "url"引入的,但其实它早就内置于nodejs中了,可以不用写import语句直接用就可以了

  • 相同路由改变query params页面不跳转: 这是和很多单页框架一样的特性,这个时候可以用window.location.search进行页面刷新或者通过监听请求参数的变化来重新获取数据,例如:

    1
    2
    3
    4
    5
    ngOnInit() {
    this.route.params.subscribe(params => {
    this.service.get(params).then(...);
    }
    }
  • **ExpressionChangedAfterItHasBeenCheckedErrord: Expression has changed after it was checked.**:这是因为在子组件里面直接改变了父组件的值,通常是在ngAfterViewInit或者ngOnChanges中,因为这种改变可能会导致无限循环,所以是禁止的,但是如果确保不会发生无限循环,可以将改变的语句写到setTimeout中去

  • 给用代码生成的元素绑定事件/addEventListener需要使用.bind方法才能在回调函数内部使用this:

    1
    2
    3
    4
    5
    6
    7
    ngAfterViewInit() {
    document.querySelector('my-element').addEventListener('click', this.onClick.bind(data, this));
    }

    onClick(data, event) {

    }

扩展

现在是2016年11月22日,毕业大约5个月,外面飘着小雨,突然想起以前好像每次都喜欢在这样的场景下看着书思考人生呢。细想毕业的这几个月,感觉我的人生是不是走得太快了。
原以为,毕业以后,至少能每周都找个地方玩儿吧,周末约上三五个好友,踏踏青,赏赏水,坐坐长江的邮轮,顺便回南山看看母校的樱花。原以为,毕业以后,至少能挥金如土吧,刚签约的时候感觉自己的工资和同学比起来虽然不高,但是相比于大学每个月的生活费,那可不止三四倍呀,是不是很快就可以过上想买就买的日子了呢。原以为,毕业以后,至少能经常给家人朋友买点礼物吧,每个人生日的时候我都能当面送上一份特别的礼物,给他们来个惊喜。
但是,生活在这短短的时间里给我泼了好几大盆的冷水。
虽然每天朝九晚六,可以睡到八点半再起床,但是每天还是不够睡。即使是运气好不用加班,六点就能下班,也实在不想拖着疲惫的身体去找好朋友逛吃逛吃。看似每天工作只有7.5小时,实际上,剩下的时间都只能用于恢复精力。只想在家躺着,哪儿也不去,能够躺在舒适的床上才是我最大的希冀。周末?哪里来的周末。周末得坐长途车去看女朋友,女朋友比我更累吧,周末都得上班,所以周末必须好好陪陪她,陪伴的形式就是陪她上班。没有踏青,没有赏水,只有偶尔互相之间温柔的眼神。那是我每周长途跋涉唯一的安慰。
是谁发明的挥金如土这个词,难道不应该是挥金然后吃土吗。白领的意思就是每个月发了工资还了信用卡然后惊讶的发现,我靠,这个月工资又白领了。总有那么多花钱的地方,房租、水、电、气、长途车费、吃,基本上每个月会有1/3的钱会花在能让我活下去的这些需求上。接下来的1/3,偶尔吃个大餐,偶尔买个礼物,偶尔送个红包,基本上也花出去了,再剩下的1/3可以存下来,然后隔一两个月,心血来潮想买一个大件,然后,就没有然后了。买不起礼物,记住了每个朋友的生日,但是朋友生日的时候顶多说一声生日快乐,朋友结婚或者生孩子,有时候连“赞”都不敢点。
有位室友遇到公司裁员,丢了工作。对,就是那个我认为他是我大学见过最聪明的一位室友,居然被公司开除了,我完全不敢相信。听到这个消息的那天,我情绪很激动,想立马给他找到一份工作,但是问了好几家,基本上对工龄的要求都至少是两年。这可是一位刚毕业的本科大学生啊,哪儿来两年的工作经验呢。还好,写这篇文章的时候,他应该已经过了某个公司的的面试了,不过,那个地方,真的好偏远。
室友被离职的同一天,我另一个朋友也打电话来了,说他准备报一个培训班,从零开始学开发,问我要一些建议。这位朋友是我从小玩儿到大的,碰巧连所学的专业也相近,都跟计算机有关,不过我偏软件,他便硬件。毕业后他去了格力,结果发现里面有好多不可描述的内部危机,所以干脆直接回家乡发展,但是,同样的,也是一个工作经验连半年都没有的人,甚至他连基本的技能也差好大一截,所以不得不去报培训班。我一直觉得他比我努力好多倍,但总感觉他的运气总是不好,总是会在人生面对重大决策的时候或者人生将要迎来更加光明的时候生活给他泼一盆冷水。
更甚的,在前面的文章中有提到,我的妹妹去世了,一个二十出头的女孩子,刚当上妈妈就离开了这个原本美丽的世界。
相比于他们,或许,我算是很幸运的了。毕业以后找了一份“相对”稳定的工作。只能说相对,这两年是IT业的寒冬,谁也说不准明天会怎样,唯一知道的是如果要裁员,多半是拿新人开刀。另外,我还在重庆买了房子,走在了百分之九十几的同学前面了。贷款50万,分30年还清,利息大概30万。为什么那么急着买房子?说简单点,一是怕房价继续上涨(从去年的趋势看,基本上我要是提前一年买房,我可以少奋斗一年,这一年还不吃不喝才能补上那个差价),二是害怕突如其来的婚姻,不是不想结婚,只是还没准备好,不过,由于某些原因,我还是选择提前准备一下,我不想我的孩子一生下来,就呆在别人的房子里。最近,公司开始从PHP转Java了,动作挺大的,而我,确是一个PHP程序员,看样子我不得不去学曾经最讨厌,黑得最多的一门垃圾语言了。
我知道,当我们进入社会后,会面临各种各样的压力,各种各样的困难,各种各样的挫折。这些我真的都知道,只是,真的没想到,这一切会来得那么快,以迅雷不及掩耳之势一棒子打在刚脱离“青春”的我们头上,把我们打得晕头转向,不知所措。长夜漫漫,夜晚越来越睡不着了,想好多事,憧憬一下未来的样子,反正,就是不敢想现在,害怕发现现在的自己正在远离曾经的梦想。

先有Mac还是先有钱?我不知道有多少人是因为池老师的一句话而买的苹果电脑,反正,我买苹果电脑的很大一部分原因是因为池老师的影响,说池老师是苹果亚太区总布道师不足为过。

之前看过池老师的《人生元编程》(读后感),没想到半年不到我就拜读了池老师的第二本书了。看上一本书的时候我的电脑还没满一年,看这本书时,已经换了一个电源插座了(500多,心里十分痛)。不过,让我有点小小失望的是,池老师自诩为一个作家,但是读完他的第二部作品,我发现,他的作品并不能称之为一部经典的著作。可能是我看的书越来越多了,像这集合各种网文的书籍有了一点反感,不过还好,池老师的作品并没有那么“鸡汤”性质,干货还是满满的,只是这个价钱,稍贵。

池老师看起来已经老了,这本书读起来有点像他的归山之作,看样子,他可能要把他最后的青春献给锤子了。虽然我一直不看好锤子,但是他们认真做事的态度着实让我佩服,真的找不到第三家像他们那样在自己的产品上投入那么多感情的手机厂商了。

这本书讲述了池老师在五个方面的见解:写给走在编程路上的人、文艺中年、自省、跨越、人物,写出了他这么多年经历中的各种感悟,读来让人回味无穷。原来我们确实只是普通人,原来成功的人确实做了很多我们常人做不到的事情。我也有运营者一个公众号,也有自己业余的小项目,也有很多很多的idea,但总是因为下班后的各种琐事而推迟,渐渐地被遗忘,最后变成了岁月中的一抹尘埃,随着年华被侵蚀。此书更多的是纠正了我在编程领域的一些错误的思想,甚至是最近经常在自己周围发生的。

池老师,加油,希望有一天我也能站在你那样的高度。

语录

全世界都放弃,至少还有我在聆听。——《他居然是这种人》

以最小的代价解决问题!知行合一,方可无敌于天下。——《程序员真正的价值》

我不是懂得多,我只是学得快而已。——《程序员如何选择技术方向》

年轻的时候谁能不犯错?重要的是犯错之后你做了什么。强大了,还是沉沦了。
无论犯什么错,永远不要执行:sudo rm -rf /
无论如何,最好不要犯全天下男人都会犯的错。
——《程序员犯过的错误》

只有疯狂到自以为能够改变世界的人,才能真正地改变世界。——《把时间“浪费”在美好的事物上》

老树终归会老去,朝花有一天会盛开,每个人都有自己的时代,自己的路自己走,走不动了,就是终点。——《朝花与老树》

你本布衣,只要躬耕于南阳,终能指点江山争天下。——《旅途中的思考》

无论世界多么浮躁,总会有人潜于浮华之下,在深水河中静静地打磨那些精美的鹅卵石和珍珠,追逐自己的梦想。无论在哪个时代,这样的人才是推动世界向前的力量。——《年龄的故事》

王小波用他短暂的生命给时间留下了丰厚的遗产。有人欣赏他杂文的讥诮反讽,有人享受他小说的天马行空,有人赞扬他激情浪漫,有人仰慕他特立独行。在这些表象的背后,他一生最珍贵的东西,是对自由的追求。——《我的阅读之路》

Write the code, change the world.——《专访:谈技术、成长及锤子》

不管做什么,从年轻的时候,你就要对你做的事情有深入的了解,不肤浅,不浮躁,坚持去做一件事情,同时有意识地去提升自己的能力。——《专访:谈技术、成长及锤子》

世界上优秀的人本来就是少数,认识到这一点,你会更容易理解这个世界。——《专访:谈技术、成长及锤子》

有的鸟儿注定是无法被困住的,因为它们的予以是如此的流光溢彩。——《希望可能意味着一切》

人的一辈子活满了,最多也就3万多天,我们不可能从开始就知道结局,也不可能像朝阳升起和夕阳西下那样精准地规划自己的每一天,未知才是最美好的,所以,最初的选择可能没有想象的重要,在路上,才是最重要的。——《你是牛儿我是渣》

人类所能想象到的所有美好和黑暗,都能从书里找到。——《我在大学里学到的几件事》

A reader lives a thousand lives before he dies. The man who never reads lives only one. ——《我在大学里学到的几件事》

我对生命的意义有种理论。我们可以在第一章里对读者解释生命的意义何在。这样可以吸引住他们。一旦他们被吸引住,并且付钱买了书,剩下的章节里我们就可以胡扯了。——《你需要多久才能变成一个“傻瓜”》

如果你工作时,经常在“能不能行呢”的不安感和“啊,成功了”的成就感之间往复行走,那么恭喜你,离成功已经没有几公里了。——《你需要多久才能变成一个“傻瓜”》

既然已经知道了无论怎么努力都很难提前完成,还不如踏踏实实等待截止日期的降临,具体的做法大致如此。
1.确定截止日期和最晚的开始时间。如11月30日要提交演讲稿,那最好11月25日开始动手准备。
2.25日之前不要开始真正的准备工作,踏下心来忙其他事情,但是要留出点时间来做相关的思考和阅读,有好的创意或想法,记下来。
3.不要焦虑,但是告诉自己的大脑,月底还有这么一件事情在等你,别忘了。
4.一旦进入预定的启动时间,立刻开足马力工作,截止日期带来的恐惧和焦虑会帮助你迅速进入“深度优先处理”的状态,之前的阅读、思考和想法会让你专注、果断、创意十足,也许你真的能在这个时间段提前完成既定目标。
——《如何克服焦虑——深度优先处理》

年轻的时候常常会产生一种错觉,无论看长河落日,望滚滚常见,还是在史书中阅尽人世沧桑,人事渺小,你还是感到,日子会这样一天天长长久久地过下去。——《40岁了,还有没有路走》

如果你忙的不可开交,除了提醒自己要时不时地抬头看路之外,真的没什么可抱怨的,这是好事,生活的本质就是让自己忙碌起来,然后把一件事情做成,再把另一件事情做成。真正该抱怨的是那些处于舒适区的、闲散的人们。我清楚地记得自己每一次处于恐慌和焦虑的时候,都是因为在某个领域处于舒适区太久,缺乏挑战,生活闲适造成的,每一次改变也是由此开始。——《闲适有毒》

1.基于工具和平台提供服务。企业愿意要什么软件,就用这些工具和平台做什么软件,费用按人月结算,算是实现了真正的人月神话。遇到外包项目或工具做不了的项目,直接拉黑。
2.做垂直小软件,解决某行业80%中小企业客户的80%的需求,价格低,走量。
3.为企业软件公司提供互联网和数据服务,如云存储、项目管理、信息流协作、数据服务等,在免费和收费之间兜兜转转,也有生存之道。
——《淤出来的聪明之企业软件》