-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsearch.xml
More file actions
419 lines (419 loc) · 106 KB
/
search.xml
File metadata and controls
419 lines (419 loc) · 106 KB
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[apache中的代理]]></title>
<url>%2F2018%2F12%2F02%2Fapache-1%2F</url>
<content type="text"><![CDATA[sudo vim /etc/apache2/httpd.conf将 Include /private/etc/apache2/extra/httpd-vhosts.conf 这行的注释 # 去掉。 sudo vim /etc/apache2/extra/httpd-vhosts.conf添加以下内容并保存(具体域名信息和代理端口可自由配置): 123456789101112<VirtualHost *:80> ServerName "local.waptest.taobao.com" ServerAlias "local.wapa.taobao.com" <Proxy *> Order Deny,Allow Deny from all Allow from 127.0.0.1 </Proxy> ProxyRequests Off ProxyPass / http://127.0.0.1:3333/ ProxyPassReverse / http://127.0.0.1:3333/</VirtualHost> 重启 Apache:sudo apachectl restart 添加绑定(具体域名信息和上面的一致):sudo vim /etc/hosts127.0.0.1 local.waptest.taobao.com local.wapa.taobao.com]]></content>
<tags>
<tag>apache</tag>
<tag>代理</tag>
</tags>
</entry>
<entry>
<title><![CDATA[redis 笔记]]></title>
<url>%2F2018%2F12%2F02%2Fredis%2F</url>
<content type="text"><![CDATA[一次讲座的笔记,当然,前面的基础是自己补的。。 Install12brew install redisbrew services start redis Start12redis-serverredis-cli BaseString123SET name 'yxp'GET nameDEL name Hash123HMSET person name 'yxp' age 27HGET person nameHGETALL person List123lpush names yxplpush names shawnlrange names 0 1 Set123sadd player kobesadd player currysmembers player zset12zadd [key] [score] [member] # score可理解为权重,将按score降序排序ZRANGEBYSCORE [key] [start_score] [end_score] 使用场景 RDB 持久化 手动触发:save(阻塞)、bgsave(fork子进程) 自动触发:redis配置、主从同步、debug reload、shutdown AOF以独立日志写命令,重启时执行AOF文件中的命令恢复数据 AOF写入 appednonly=yes appendfsync=(always|evetyset|no) 重写 主从同步积压缓存区主从断裂的判断时间 集群拓扑结构键分配模式HASH_SLOT cacheCloud应用MQ 发布订阅 stream场景 cache 排行榜 计数器 HyperLogLog 社交网络(微博) 消息队列(聊天室)建议 专业精细化与简易通用的取舍 结合业务场景 TIPS 分布式锁:setex 设置过期时间,避免死锁 pipeline:一次发送多条命令 慢查询:showlog-log-slower-than、showlog-max-len、slowlog get Bigkey 缓存穿透 热点数据 info]]></content>
<tags>
<tag>redis</tag>
</tags>
</entry>
<entry>
<title><![CDATA[记一次微信小程序的项目实践]]></title>
<url>%2F2018%2F12%2F02%2Fmp%2F</url>
<content type="text"><![CDATA[很早之前的项目,一直到现在才总结。。。 小程序的几个相关概念 App Page Component 视图层和逻辑层 视图层->逻辑层 事件 逻辑层->视图层 setData 小程序的生命周期 项目结构构建工具gulp + typescript 项目结构1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253.├── 3rd│ ├── upyun-wxapp-sdk.d.ts│ ├── upyun-wxapp-sdk.js│ ├── weapp-qrcode.d.ts│ └── weapp-qrcode.js├── app.json├── app.ts├── app.wxss├── assets│ ├── avatar_female.png│ ├── avatar_male.png│ ├── ...├── behaviors│ └── district.ts├── components│ ├── cell│ ├── ...├── filters│ ├── date.wxs├── mixins│ ├── login.ts├── pages│ ├── 404│ ├── login│ ├── certificate_list│ ├── discovery├── project.config.json├── styles│ ├── button.wxss│ ├── checkbox.wxss│ ├── fonts.wxss│ ├── input.wxss│ ├── layout.wxss│ ├── picker.wxss│ ├── radio.wxss│ └── txt.wxss├── utils│ ├── cache.ts│ ├── constants.ts│ ├── cookie.ts│ ├── date.ts│ ├── district.ts│ ├── meeting.d.ts│ ├── meeting.ts│ ├── resource.ts│ ├── response.d.ts│ ├── upyun.ts│ └── utils.ts├── wx.d.ts└── wxp.d.ts68 directories, 54 files 要点cookie需要手动保存下来,并在下次请求带上 逻辑复用 可以用behavior实现组件间的代码复用,如自定义的表单组件 123456789101112131415161718export default Behavior({ properties: { value: { type: Object, value: null, observer: '_valueChange', }, placeholder: String, }, data: { }, methods: { _valueChange() { }, },}); 组件与页面间的代码复用只能用类extend或assign的方式实现 filter要用wxs1234567891011// date.wxsmodule.exports = function dateStr(str, formatter) { if (!str) return ''; var time = getDate(str); return formatter.replace('YYYY', time.getFullYear()) .replace('MM', (time.getMonth() < 9 ? '0' : '') + (time.getMonth() + 1)) .replace('DD', (time.getDate() < 10 ? '0' : '') + time.getDate()) .replace('HH', (time.getHours() < 10 ? '0' : '') + time.getHours()) .replace('mm', (time.getMinutes() < 10 ? '0' : '') + time.getMinutes()) .replace('ss', (time.getSeconds() < 10 ? '0' : '') + time.getSeconds());} 1234// xxx.wxml<wxs src="../../filters/date.wxs" module="date"></wxs><text>{{date(meeting.open_start, 'YYYY-MM-DD')}}至{{date(meeting.open_end, 'YYYY-MM-DD')}}</text> 组件的样式是独立的可通过externalClasses向组件内传递样式类。 组件间值的传递 父传子:插值 子传父:事件 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071// image_uploader.jsimport upyunSvc from '../../utils/upyun';Component({ /** * 组件的属性列表 */ behaviors: ['wx://form-field'], properties: { tips: { type: String, value: '请选择图片', }, value: { type: String, }, type: String, }, /** * 组件的初始数据 */ data: { }, /** * 组件的方法列表 */ methods: { chooseImage(event: TapEvent) { const that = this; wx.chooseImage({ count: 1, // 默认9 sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有 sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有 success (res) { // 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片 const filePaths: string[] = res.tempFilePaths; // /{filemd5}{.suffix} wx.showLoading({ title: '正在上传', }); upyunSvc.upload({ localPath: filePaths[0], remotePath: '/{filemd5}{.suffix}', success: function (res: string2AnyMap) { wx.hideLoading(); if (res.statusCode !== 200) { wx.showToast({ title: res.errMsg, }); } else { const data = JSON.parse(res.data); const url = `https://zoneke-img.b0.upaiyun.com/${data.url}`; that.setData({ value: url }); that.triggerEvent('change', { value: url }); // triggerEvent } }, fail: function ({ errMsg }: { errMsg: string }) { wx.hideLoading(); wx.showToast({ title: errMsg, }); }, }) }, }); }, }}); 123456789// Demo.wxml<image-uploader value="http://xxx.avatar.com" name="avatar" bind:change="onImageChange" />// Demo.jsPage({ onImageChange(event) { console.log(event.detail.value); },}) 事件的绑定及其值的传递12// example.wxml<view bindtap="jump" data-url="/page/example"></view> 12345678// example.jsPage({ jump(event: TapEvent) { wx.navigateTo({ url: event.currentTarget.dataset.url, }); },}); Resource + formatter面向RESTFul规范的接口]]></content>
<tags>
<tag>小程序</tag>
<tag>微信小程序</tag>
</tags>
</entry>
<entry>
<title><![CDATA[用栈模拟队列]]></title>
<url>%2F2018%2F08%2F09%2Fqueue-using-stack%2F</url>
<content type="text"><![CDATA[又是一道被面试题,虽然据说是基础题,但是方法还是很巧妙,值得马克。 123456789101112131415161718192021222324252627282930313233class Stack { constructor() { this.stack = []; } pop () { return this.stack.pop(); } push (...args) { this.stack.push(...args); }}class Queue { constructor() { this.inStack = new Stack; this.outStack = new Stack; } enqueue(...args) { this.inStack.push(...args); } dequeue() { let res = this.outStack.pop(); if (typeof res === 'undefined') { let temp = this.inStack.pop(); while (typeof temp !== 'undefined') { this.outStack.push(temp); temp = this.inStack.pop(); } res = this.outStack.pop(); } return res; }}]]></content>
<tags>
<tag>算法</tag>
<tag>面试</tag>
<tag>栈</tag>
<tag>队列</tag>
</tags>
</entry>
<entry>
<title><![CDATA[大数相乘]]></title>
<url>%2F2018%2F08%2F09%2Flarge-number%2F</url>
<content type="text"><![CDATA[面试的时候被问了大数相加的问题,马克一下吧~ 顺便试下大数相乘。 大数相加12345678910111213function add(n1, n2) { const a1 = n1.split('').reverse(); const a2 = n2.split('').reverse(); const result = []; for (let i = 0, l = Math.max(a1.length, a2.length); i < l; i++) { result[i] = (result[i] || 0) + parseInt(a1[i] || 0) + parseInt(a2[i] || 0); while (result[i] >= 10) { result[i] -= 10; result[i + 1] = (result[i + 1] || 0) + 1; } } return result.reverse().join('');} 大数相乘123456789101112131415function multiply(n1, n2) { const a1 = n1.split('').reverse(); const a2 = n2.split('').reverse(); const result = []; for (let i = 0, l1 = a1.length; i < l1; i++) { for (let j = 0, l2 = a2.length; j < l2; j++) { result[i + j] = (result[i + j] || 0) + a1[i] * a2[j]; while (result[i + j] >= 10) { result[i + j] -= 10; result[i + j + 1] = (result[i + j + 1] || 0) + 1; } } } return result.reverse().join('');}]]></content>
<tags>
<tag>算法</tag>
<tag>面试</tag>
</tags>
</entry>
<entry>
<title><![CDATA[几个概念:虚拟主机、ECS和VPS]]></title>
<url>%2F2018%2F07%2F21%2Fconcepts%2F</url>
<content type="text"><![CDATA[虚拟主机: 利用虚拟化的技术,将一台服务器划分出一定大小的空间,每个空间都给予单独的 FTP 权限和 Web 访问权限,多个用户共同平均使用这台服务器的硬件资源。 ECS: Elastic Compute Service, 一种弹性可伸缩的计算服务 VPS: Virtual Private Server, 虚拟专用服务器技术,将一台服务器分割成多个虚拟专享服务器的优质服务。 DIFF: VPS 是在物理服务器上运行的,一旦这台物理服务器出现故障,上面所有的VPS都将受影响无法正常使用。而云服务器是在集群服务器中运行的,集群中的每台机器都会有云服务器的镜像备份,即使其中一台机器或者几台机器出现故障,依然不影响云服务器的正常访问。]]></content>
<tags>
<tag>虚拟主机</tag>
<tag>ECS</tag>
<tag>VPS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Ubuntu 16.04下折腾记]]></title>
<url>%2F2018%2F06%2F29%2Fubuntu%2F</url>
<content type="text"><![CDATA[安装zsh和oh-my-zsh见Ubuntu 16.04下安装zsh和oh-my-zsh 安装nginx1sudo apt-get install nginx 安装dockerSET UP THE REPOSITORY1234567891011121314sudo apt-get update # Update the apt package indexsudo apt-get install \ apt-transport-https \ ca-certificates \ curl \ software-properties-common # Install packages to allow apt to use a repository over HTTPScurl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - # Add Docker’s official GPG keysudo add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable" INSTALL DOCKER CE12sudo apt-get update # Update the apt package index.sudo apt-get install docker-ce # Install the latest version of Docker CE 使用daocloud 创建项目 => 构建镜像(可以通过git hook 自动构建) 创建应用 => 部署镜像(可以自动构建镜像完成之后自动发布更新应用)]]></content>
<tags>
<tag>笔记</tag>
<tag>shell</tag>
<tag>docker</tag>
<tag>ubuntu</tag>
<tag>linux</tag>
<tag>daocloud</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Ubuntu 16.04下安装zsh和oh-my-zsh]]></title>
<url>%2F2018%2F06%2F29%2Fzsh%2F</url>
<content type="text"><![CDATA[文章来源 最近接连整了几台服务器的环境,觉得有必要记下来。 安装zsh123sudo apt-get install zsh # 安装chsh -s /bin/zsh # 更改默认shellsudo vim /etc/passwd # 把root用户和ubuntu用户的默认shell改成zsh 安装oh-my-zsh(via git)12sudo apt-get install git # 安装gitsh -c "$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)" # 安装oh-my-zsh 安装插件将对应的插件安装到$ZSH_CUSTOM/plugins目录中,并在~/.zshrc文件的plugins中添加引用,最后source ~/.zshrc即可。 语法高亮1git clone https://github.com/zsh-users/zsh-syntax-highlighting.git $ZSH_CUSTOM/plugins/zsh-syntax-highlighting 自动补全1git clone git://github.com/zsh-users/zsh-autosuggestions $ZSH_CUSTOM/plugins/zsh-autosuggestions 配置主题与安装插件类似,将主题安装到$ZSH_CUSTOM/themes目录中,并在~/.zshrc文件的ZSH_THEME中修改当前主题,最后source ~/.zshrc即可。 卸载zsh(暂无必要)12sudo sh -c "$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/uninstall.sh)"sudo vim /etc/passwd # 把root用户和ubuntu用户的默认shell改回bash]]></content>
<tags>
<tag>笔记</tag>
<tag>shell</tag>
<tag>ubuntu</tag>
<tag>linux</tag>
<tag>zsh</tag>
</tags>
</entry>
<entry>
<title><![CDATA[利用pre-commit对js代码进行eslint检查]]></title>
<url>%2F2018%2F06%2F28%2Fpre-commit%2F</url>
<content type="text"><![CDATA[背景:项目中有两个子前端项目,分别是Gulp + Angular.js 和 Vue + Webpack。两个项目有各自的eslint插件和规则。 需求:要在项目中添加pre-commit钩子,对每次提交中有改动的js或vue文件进行eslint检查。由于历史原因,不要对项目中的所有js文件进行检查。 调研:钩子其实是git提供的,只要在项目的根目录的.git/hooks中添加pre-commit脚本即可。至于怎么添加脚本,以及添加什么解释器的脚本,就八仙过海各显神通了。 方案:在项目根目录下用npm安装pre-commit,该命令会在当前目录的.git/hooks中添加pre-commit脚本。我们需要在根目录的package.json中指明pre-commit阶段具体执行的脚本。代码如下: 12345678910111213{ "scripts": { "lintAdmin": "git status -s | grep -E '^[MARC][^D].+fe_build/apps/web_admin/src/.*\\.js$' | rev | cut -d ' ' -f 1 | rev | xargs fe_build/apps/web_admin/node_modules/eslint/bin/eslint.js", "lintMobile": "git status -s | grep -E '^[MARC][^D].+fe_build/apps/mobile_v2/src/.*\\.(js|vue)$' | rev | cut -d ' ' -f 1 | rev | xargs fe_build/apps/mobile_v2/node_modules/eslint/bin/eslint.js" }, "pre-commit": [ "lintMobile", "lintAdmin" ], "devDependencies": { "pre-commit": "^1.2.2" }} 关键点git status反应了当前工作区和暂存区的文件状态,使用git status -s可以输出XY PATH或XY ORIG_PATH -> PATH(仅对重命名和复制的文件有用)格式的多行内容。其中,XY表示的含义在处理冲突的时候分别表示的是两个分支的文件状态,在非处理冲突的时候分别表示暂存区和工作区的文件状态。 下面是从官网摘下来的状态详细说明:非处理冲突时 X Y Meaning [AMD] not updated M [ MD] updated in index A [ MD] added to index D deleted from index R [ MD] renamed in index C [ MD] copied in index [MARC] index and work tree matches [ MARC] M work tree changed since index [ MARC] D deleted in work tree [ D] R renamed in work tree [ D] C copied in work tree 处理冲突时 X Y Meaning D D unmerged, both deleted A U unmerged, added by us U D unmerged, deleted by them U A unmerged, added by them D U unmerged, deleted by us A A unmerged, both added U U unmerged, both modified 其他情况 X Y Meaning ? ? untracked ! ! ignored 其中, M A D R C U unmodified modified added deleted renamed copied updated but unmerged grepgrep -E后面可以加正则表达式,对输入的行进行正则匹配。 revrev 可以使内容翻转,由此可以通过cut命令读到剪切完的最后一个field。因为git status -s输出的内容格式有两种(XY PATH和XY ORIG_PATH -> PATH),从而使得cut -d ' ' -f 1的裁剪结果的分组数是不一样的,但是明显最后一个PATH才是我们需要匹配的。 [MARC][^D]由于对已删除的文件进行eslint检查是会报错的,所以我们需要过滤掉已删除的文件。 对于非merge的情况,X代表暂存区的文件状态,Y代表工作区的文件状态,因而[MARC][^D]表示在暂存区中修改、添加、重命名或复制过,且在工作区中没有被删除的文件。 PS: cut和xargs真是好东西啊~ 后记后来发现git diff --cached --name-only --diff-filter ACMR可以直接输出暂存区中ACMR状态的文件。这个跟我手写的唯一区别应该在于加入暂存区的文件在工作区被删除的情况了,这个版本还是会去对已删除的文件进行eslint检查从而报错,我的版本会过滤掉这样的文件。 由此,package.json可被修正成:12345678910111213{ "scripts": { "lintAdmin": "git diff --cached --name-only --diff-filter ACMR | grep -E 'fe_build/apps/web_admin/src/.*\\.js$' | xargs fe_build/apps/web_admin/node_modules/eslint/bin/eslint.js", "lintMobile": "git diff --cached --name-only --diff-filter ACMR | grep -E 'fe_build/apps/mobile_v2/src/.*\\.(js|vue)$' | xargs fe_build/apps/mobile_v2/node_modules/eslint/bin/eslint.js" }, "pre-commit": [ "lintMobile", "lintAdmin" ], "devDependencies": { "pre-commit": "^1.2.2" }} 噗~]]></content>
<tags>
<tag>shell</tag>
<tag>git</tag>
<tag>pre-commit</tag>
<tag>eslint</tag>
</tags>
</entry>
<entry>
<title><![CDATA[一键清理没用的docker容器]]></title>
<url>%2F2018%2F06%2F19%2Fclear-docker%2F</url>
<content type="text"><![CDATA[1docker ps -a | grep Exited | cut -d ' ' -f 1 | xargs docker rm 管道是个好东西 cut 是个好东西 xargs是个好东西 docker是个好东西 同样的方法可以清掉没用的git 分支1git br -vv | grep gone | cut -d ' ' -f 3 | xargs git br -d]]></content>
<tags>
<tag>shell</tag>
<tag>docker</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Weex初体验]]></title>
<url>%2F2018%2F05%2F27%2Fweex%2F</url>
<content type="text"><![CDATA[是时候写点东西了。 话说这段经验已经跟团队内的人分享过两次了,直接把之前整理的大纲挪上来就好,但是跟别人做的总结跟自己做的总结总是有点不一样的,还是重新整理下吧。 What is Weex 一个使用 Web 开发体验来开发高性能原生应用的框架。 这是Weex的自我介绍,高不高性能可以再议,但是Web开发体验却是真的。 Weex俗称Vue Native,类似于React的React Native,都试图用WEB技术来写NATIVE。与此类似的技术还有NativeScript、小程序和快应用等等。现在只比较Weex和RN,两者最大的区别从他们的宣传理念便可见端倪:前者是Write once, run everywhere,后者是Learn once, write everywhere。RN可以看做是一种语法,既可以用来写Web,又可以写Native,甚至还能写VR(见React 360),但是两者是互相隔离开的,两者用的都是不同的组件库。而Weex则可看做是一种兼容Web、Android、iOS三端的解决方案,一份代码可同时在三端运行,极大的提高了代码的复用性。 How Weex Works 在移动应用客户端里,Weex SDK 会准备好一个 JavaScript 执行环境,并且在用户打开一个 Weex 页面时在这个执行环境中执行相应的 JS bundle,并将执行过程中产生的各种命令发送到 native 端进行界面渲染、数据存储、网络通信、调用设备功能及用户交互响应等功能。 上面的原理图和描述源于其官网,简单的说,Weex就是在Native环境中运行一份JS代码,并通过与Native的交互达到页面渲染、数据存储等功能。换句话说,不管我们在开发环境怎么折腾,只要最后把一份JS代码交给Native就OK。从这个角度来说,Weex对开发者还是蛮友好的。 How to Use其实很简单。 安装weex-toolkitnpm install -g weex-toolkit 创建项目weex create awesome-project 安装依赖cd awesome-project && npm install 开发 在浏览器预览npm run dev & npm run serve 在App内预览(启一个静态server,然后用App扫一扫即可)anywhere # 一个很有用的npm包,可以在任意目录下启动一个静态服务。 打包npm run build Project in PraticeWhy Weex 每一个前端工程师都有一个全栈的梦想 用Web写Native,方便人力资源的流动和调用 编写适用三端的组件,提高代码的可复用性 Main Packages weex vue + vue-router + vuex # 三套马车,终于齐全了 weex-ui weex-axios # 可在weex使用的类axios库 weex-bindingx # 用表达式来调用原生动画的库 Main Output在项目实践的时候,除了正常的业务逻辑的编写,一个主要产出就是各种拓展的模块和组件了,这也正体现了weex代码高复用性的特点。 Diff in Module and Component在跟客户端同学交流的过程中,发现他们最容易搞混的概念就是模块和组件了,觉得有必要区分下: 模块:功能层面的封装、在JS中使用、编程式调用 组件:UI层面的封装、在模板中使用、声明式调用 Customer Modules qcModal # 自定义样式的modal qcShare # 用于分享 qcNavigator # 用于跳转原生页面 qcTrack # 用于埋点 qcTel # 用于拨打电话 Customer Compents qc-rich-text # 富文本 qc-qr-code # 二维码 Tips: 由于weex追求的是三端均可正常使用,因此对于上面的自定义模块和组件,Web, Android和iOS都需要有各自的实现,同时保持接口一致。 Main Problems在开发过程中,不踩坑是不可能的,其中最大的包括以下几个。 Pack由于历史原因,核心项目没有前后端分离,所以最后不论怎么着,我都要把weex项目引用到核心项目中去。同时我需要处理怎么把打包好的JS文件给到客户端,并实现热更新(这点也是促使我们使用weex的很重要的一点)。 最后的实现方式是,修改了weex项目的打包配置,在生成js文件之后,一方面将其中给Native使用的js文件生成一份基于MD5的版本号映射文件version.json或version_test.json,并将该文件和之前打包好的文件上传到upyun,然后App可以在每次启动时获取这个版本文件,并更新其静态资源,从而实现热更新。另一方面,我们会将整个项目打包上传自己的npm服务器上,并在核心项目中安装引用这个npm包。这样,客户端和Web端都能同时使用整个weex项目的业务代码,达到一份代码三端使用的目的。 Navigator其实weex提供了一个navigator的模块,用于实现页面间的跳转。但由于实际项目中多个页面共享了vuex中的状态(如购物车的状态需要多个页面共享),而vuex状态的持久化比较麻烦,同时web端页面的跳转只能用vue-router来实现,因此最后舍弃了navigator,直接用vue-router的abstract模式去管理页面的跳转。 Animationweex提供的animation只有一个animation.transition(el, options, callback)的API,当我需要实现复杂动画的时候,如实现一个抛物线动画的时候,我只能多次调用这个API,并在每次的callback中指定下一次调用。但这样的调用涉及到多次上下文的切换,造成的后果就是在安卓上会卡顿。在尝试过自定义的animation模块之后,我们发现了weex-bindingX这个好东西,可以直接用动画表达式的方式一次性将需要执行的动画通知到客户端,从而减少了上下文切换的成本。123456789101112BindingX.bind({ eventType: 'timing', props: [{ element: el, property:'transform.translateX', expression: `linear(t, ${from.left - to.left}, ${to.left - from.left}, ${duration})`, }, { element: el, property:'transform.translateY', expression: `easeInSine(t, ${from.top - to.top}, ${to.top - from.top}, ${duration})`, }]}); Style Compatibility样式兼容其实才是整个开发过程中最让人蛋疼、懵逼的问题,一份代码需要匹配三端,意味着我需要确保这一份CSS在三端的显示是正常的。然而这实际开发过程中经常有跟预期不一样的表现,其中的主要原因包括以下两点: 安卓上的盒子模型是overflow: hidden的,所有超出盒子的内容就直接不展示。这个特点让人很难受,特别是在写组件的时候,经常会有浮层等一些超过父级元素大小的子元素,这个在web上可以正常显示的,但在安卓上就不行了。 没有了文档流的概念。在web中,默认情况下所有的的元素都是会按照顺序自动计算宽高从上往下排列的,超过窗口的会自动滚动,超过盒子的也会显示,但在Native中,这些滚动必须要用scroll来实现。而且一旦一个盒子没有声明宽度或高度,它的渲染结果就会很有可能表现的很匪夷所思,如我曾遇到过的iOS上的黑线问题。因此,建议盒子最好都要显式的声明宽度和高度,不要依赖自动计算。 Degradation Programs由于weex是一门新技术,为了保留后路,我们也做好了在App内直接使用web页面的准备,version.json中的weex-enabled就是控制是用weex页面还是用web页面的开关。但由于项目中有些页面需要跳转到APP原来的Native页面,因此同样的页面跳转逻辑,我们需要保证web到Native和weex到Native都是OK,故而调试成本又上去了。 Render由于Native的限制,weex中的vue只能使用v-if,不能使用v-show,也就是说每当v-if对应的布尔值发生变化,其控制的元素就要重新渲染。当然这对一般的元素可能还不觉得有什么,但是要是有v-if要是控制了长列表的渲染,那状态切换时页面可就是卡的不要不要的了。当然后来weex提供了recycle-list的组件,不过这都是后话了。 After说心里话,这个项目之后,我的心里还是蛮受伤的。想起今年年初的年初计划,当时还是兴致勃勃的要尝试Weex,但尝试过后,我的第一感觉居然是不想再用了,其主要原因在于兼容多端的困难,尤其是Web和Native的样式表现差的太远,经常在Web上好好的样式到安卓上就挂了,好不容易安卓好了,iOS上又有问题。再加上没有一个好用的调试工具,因而在调试样式的时候还是蛮痛苦的。 不过话说回来,我们为什么一定要执着于用web来写Native应用,web应用和Native应用到底有什么区别。 经调研,不外乎渲染能力、离线能力、复杂动画、硬件接口这几点。但其实随着硬件的升级,渲染能力的差距其实已经很小了;而且对于简单的页面来说,两者几乎的差别真的是微乎其微。要知道,在定位问题的时候其他人问的关于APP的最多一个问题就是“这个页面是APP还是web”了。当前发展火热的pwa技术也填补了web离线能力的欠缺。不过对于复杂动画和调用硬件接口这两点,web应用还真是比不过Native应用了。 所以说,在决定到底是web还是Native的时候,还是得多考虑下具体的业务需求。 PS: 虽然在weex上受了点伤,但是最近还是被小程序(当然不是那种嵌着webview的假小程序)吸引了。看着自己写的demo在微信中流畅的切换,感觉还是挺好的。嘻嘻~]]></content>
<tags>
<tag>总结</tag>
<tag>Weex</tag>
</tags>
</entry>
<entry>
<title><![CDATA[【一日一题】无重复字符的最长子串]]></title>
<url>%2F2018%2F05%2F24%2Fleetcode-4%2F</url>
<content type="text"><![CDATA[题目描述题目来源给定一个字符串,找出不含有重复字符的最长子串的长度。 示例: 给定 “abcabcbb” ,没有重复字符的最长子串是 “abc” ,那么长度就是3。 给定 “bbbbb” ,最长的子串就是 “b” ,长度是1。 给定 “pwwkew” ,最长子串是 “wke” ,长度是3。请注意答案必须是一个子串,”pwke” 是 子序列 而不是子串。 代码123456789101112131415161718class Solution(object): def lengthOfLongestSubstring(self, s): """ :type s: str :rtype: int """ l = [] matched = '' temp = '' for i in s: index = temp.find(i) if index == -1: temp += i else: if (len(temp) > len(matched)): matched = temp temp = temp[index + 1:] + i return max(len(matched), len(temp))]]></content>
<tags>
<tag>python</tag>
<tag>算法</tag>
<tag>一日一题</tag>
<tag>leetcode</tag>
<tag>字符串</tag>
</tags>
</entry>
<entry>
<title><![CDATA[【一日一题】删除二叉搜索树中的节点]]></title>
<url>%2F2018%2F05%2F24%2Fleetcode-3%2F</url>
<content type="text"><![CDATA[题目描述题目来源给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。 一般来说,删除节点可分为两个步骤: 首先找到需要删除的节点;如果找到了,删除它。说明: 要求算法时间复杂度为 O(h),h 为树的高度。 代码123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051# Definition for a binary tree node.# class TreeNode:# def __init__(self, x):# self.val = x# self.left = None# self.right = Noneclass Solution: def deleteNode(self, root, key): """ :type root: TreeNode :type key: int :rtype: TreeNode """ parent = None selected = None current = root while current: if current.val == key: selected = current break elif current.val > key: if not current.left: break parent = current current = current.left else: if not current.right: break parent = current current = current.right if not selected: return root newSelected = self.rebuildTree(selected.left, selected.right) if parent: if parent.val > selected.val: parent.left = newSelected else: parent.right = newSelected return root if parent else newSelected def rebuildTree(self, left, right): if not left: return right if not right: return left current = right while current.left: current = current.left current.left = left return right]]></content>
<tags>
<tag>python</tag>
<tag>算法</tag>
<tag>一日一题</tag>
<tag>leetcode</tag>
<tag>树</tag>
</tags>
</entry>
<entry>
<title><![CDATA[【一日一题】不用额外空间的数组去重]]></title>
<url>%2F2018%2F05%2F24%2Fleetcode-2%2F</url>
<content type="text"><![CDATA[题目描述:题目来源给定一个整数数组 a,其中1 ≤ a[i] ≤ n (n为数组长度), 其中有些元素出现两次而其他元素出现一次。 找到所有出现两次的元素。 你可以不用到任何额外空间并在O(n)时间复杂度内解决这个问题吗? 代码1234567891011121314class Solution: def findDuplicates(self, nums): """ :type nums: List[int] :rtype: List[int] """ result = [] for i in nums: index = abs(i) - 1 if nums[index] < 0: result.append(abs(i)) else: nums[index] = -nums[index] return result]]></content>
<tags>
<tag>python</tag>
<tag>算法</tag>
<tag>一日一题</tag>
<tag>leetcode</tag>
<tag>数组</tag>
</tags>
</entry>
<entry>
<title><![CDATA[【一日一题】单词拆分]]></title>
<url>%2F2018%2F05%2F24%2Fleetcode-1%2F</url>
<content type="text"><![CDATA[背景:前段时间公司内部拉了一个一日一道算法题的群,虽然坚持不满一个月,但还是蛮有收获的,有些题目还是蛮有意思的,值得马克。 过程过尝试着用Python来写,也算是一种修行吧~ 题目描述:题目来源 给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在字符串中增加空格来构建一个句子,使得句子中所有的单词都在词典中。返回所有这些可能的句子。 说明: 分隔时可以重复使用字典中的单词。 你可以假设字典中没有重复的单词。 代码:1234567891011121314151617181920212223class Solution: def wordBreak(self, s, wordDict, wordMap = None): """ :type s: str :type wordDict: List[str] :rtype: List[str] """ if wordMap is None: wordMap = {} wordDict.sort(key = lambda x: -len(x)) if s in wordMap: return wordMap[s] result = [] for word in wordDict: if s == word: result.append(s) elif s.startswith(word): subS = s[len(word):] subResult = self.wordBreak(subS, wordDict, wordMap) if len(subResult) > 0: result += [word + ' ' + w for w in subResult] wordMap[s] = result return result]]></content>
<tags>
<tag>python</tag>
<tag>算法</tag>
<tag>一日一题</tag>
<tag>leetcode</tag>
<tag>动态规划</tag>
</tags>
</entry>
<entry>
<title><![CDATA[2017]]></title>
<url>%2F2018%2F01%2F07%2F2017%2F</url>
<content type="text"><![CDATA[又是新的一年,正式成为码农的第三年了,虽然内心有点抗拒,但是还得对过去的一年做个小结吧。总的来说,过去的一年很平淡,似乎已经进入一个成长的平缓期了,感觉过去的一年除了常规业务,基本没有什么成长。哭~但是要是强行找进步的话,还是能找出一二的。 angular-clover-uiAngular.js 1.3.16版本下的UI框架,应该是这一年最显著的成果了。当然这件事的起源还是来自于正常业务,来自设计师小光同学对当时Sass系统UI风格不一、缺乏规范的不满,整理了一套全新的、风格统一的、组件化的UI规范。为了支持设计师的工作以及自己造轮子的想法,于是动工了,还搭上了业务的车上线了。整个过程中,主要参考了饿了么的element和阿里的Ant Design,虽然人家是分别基于Vue和React写的,由于一些历史原因,我们只能出一个Angular.js 1.3.16下的版本,但是还是借鉴了它们的组件的调用方法,保持了类似的接口。同时,为了能让其他人更好的使用这套规范,还对每个组件写了文档,并挂在GitHub Page上。另外,为了更方便的部署文档,还利用drone进行自动化部署。总之,这个项目还是花费了好大一番功夫的,从组件到文档再到自动化部署,基本上都是一个人的战斗。所幸从目前来看,这套规范还是用起来了,还是蛮值得欣慰的。传送门 多平台的尝试全栈算是一个梦想吧,我还是想让自己变得更全面一些的。这一年还是进行了一番尝试的,虽然现在还没有什么实践成果。 React Native。当然这建立在React的基础上,虽然JSX看起来有点恶心,但写起来还是能接受的。整体的语法也比较简单易懂。本来年中的时候就想要在项目中实战了,但是由于种种原因还是搁浅了。 Weex,别名Vue Native。符合我们当前的技术栈,目前正在尝试,打算用在当前的项目中。 后端语言,包括Python和Django,Node.js和Express或Sail。一直在尝试,不过基本都是浅尝辄止,惭愧惭愧。 运维? 当然现在基本不懂,试着买了个阿里云的服务器,但是只整了一半,就挂起在那里了。 两个愿望许两个愿望吧,当然今年不一定能实现。 独立整一个项目,从前端到后端,从Web到Native,从H5到小程序,从搭建服务到自动化部署。 整一个MVVM框架,类似Vue React Angular。 注:第一个优先级高于第二个,第二个的先决条件包括阅读足够多的框架源码。]]></content>
<tags>
<tag>个人成长</tag>
<tag>总结</tag>
</tags>
</entry>
<entry>
<title><![CDATA[重构AngularJS项目有感一]]></title>
<url>%2F2017%2F12%2F10%2Fangular%2F</url>
<content type="text"><![CDATA[最近在重构一个AngularJS的项目,这应该是公司年纪最长的项目了,用的是当时最前卫的MVC框架AngularJS。是的,是AngularJS, 1.3.16的版本,不是后来重头来过的Angular。这种上了年纪的项目想要重构,除非推到重来,否则都是一件令人头疼的事,我们能做的无非是把这些年积累下来的技术债转化成某些最佳实践,使得代码的可读性、复用性和可拓展性稍微提升一点罢了。 资源获取的几个问题 接口对RESTFul风格的支持并不友好。项目用了angular-resource, 使得前端的请求能够以RESTFul的形式去跟后端交互,然而因为某些残忍的现实原因,我们的接口并没有保持良好的RESTFul风格,这一点也只能之后跟后端同志沟通时尽量达成一致了。 后端返回的数据结构是经过封装的,(在前后端约定中,所有的请求后端都会返回200的响应,而真正的请求状态和数据藏在响应的body中。)每次请求我们都需要响应主题进行简单判断后才能得到想要的数据。本来这些可以放在$httpProvider.interceptors中对所有的请求进行统一处理,但处理后请求下一层的数据都会改变,之前所有的判断都要修改,不得不暂且放弃。 请求之前和请求回来之后需要对数据进行特殊处理(formatter)。这包括get、put请求回来之后的formatter,和post请求之前的formatter。因为一些历史原因,当前项目的formatter很多都是在controller中进行的。而比较后期的实践已经把这个放在factory中了。 获取同样资源的多次请求问题。现在都在推崇组件化,但组件化的一个问题是有可能多个组件都需要获取同一个资源,如何多个组件中单独请求的话,容易产生冗余请求和资源浪费。 ModelFactory为了解决上述的几个问题,我们抽象出一个通用的factory – ModelFactory, 它主要做下面几件事情。 添加构造函数ModelFactory,其衍生的实例提供get、save、update、delete四种资源获取方法,分别代理了$resource对应的四种请求方法。 在代理的过程中处理了响应主体的数据,将真正的数据传到下一层。 支持在上述每个方法的代理过程中添加对应的formatter和preFormatter,只需在具体的实例中添加对应方法即可。 添加资源获取时的Promise对象缓存,缓存时间内的同一种请求返回同一个Promise对象,从而实现在多次资源获取中只有一次请求。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071(function (angular) { angular.module('setting.services') .factory('reqSuccessCallback', function($q) { return function reqSuccessCallback(event) { if (event.status == 200) { return $q.when(event.data); } else { return $q.reject(event.msg); } } }) .factory('ModelFactory', function (reqSuccessCallback, $q) { function ModelFactory (resource) { this.resource = resource; } ModelFactory.prototype.CACHE_DURATION = 5000; ModelFactory.prototype._getCacheValidity = function (params) { return _.isEqual(this.paramsCache, params) && this.promiseCache && (Date.now() - this.cacheTime <= this.CACHE_DURATION); }; ModelFactory.prototype.get = function (params) { var _self = this; if (this._getCacheValidity(params)) return this.promiseCache; this.paramsCache = params; this.cacheTime = Date.now(); this.promiseCache = this.resource.get(params || {}).$promise .then(reqSuccessCallback) .then(function (data) { var _formatter = _self.formatterOnGet || _self.formatter || _self._valueFn; return $q.when(_formatter(data)); }); return this.promiseCache; }; ModelFactory.prototype.save = function (params, data) { var _self = this; var _preFormatter = _self.preFormatterOnSave || _self.preFormatter || _self._valueFn; return _save = this.resource.save(params || {}, _preFormatter(data)).$promise .then(reqSuccessCallback) .then(function (data) { var _formatter = _self.formatterOnSaved || _self.formatter || _self._valueFn; return $q.when(_formatter(data)); }); }; ModelFactory.prototype.update = function (params, data) { var _self = this; var _preFormatter = _self.preFormatterOnUpdate || _self.preFormatter || _self._valueFn; return this.resource.update(params || {}, _preFormatter(data)).$promise .then(reqSuccessCallback) .then(function (data) { var _formatter = _self.formatterOnUpdated || _self.formatter || _self._valueFn; return $q.when(_formatter(data)); }); }; ModelFactory.prototype.delete = function (params) { return this.resource.delete(params || {}).$promise .then(reqSuccessCallback); }; ModelFactory.prototype._valueFn = function (value) { return value; }; return ModelFactory; })})(angular);]]></content>
<tags>
<tag>总结</tag>
<tag>AngularJS</tag>
</tags>
</entry>
<entry>
<title><![CDATA[记一次使用drone持续集成的经历]]></title>
<url>%2F2017%2F09%2F09%2Fdrone%2F</url>
<content type="text"><![CDATA[前段时间一直在整一个angular 1.xx版本的UI项目,从项目建立,支持上传CDN,开发过程持续热更新,可持续生成文档,到利用drone实现自动化上传CDN、生成文档、并部署到GitHub的gh-page上,这一路也算是折腾艰辛,不过也感觉一下子收获了很多经验值。 整个构建过程还是主要用gulp写的,期间用了很多的各种插件,包括但不限于: browser-sync 绝对的持续热更新的利器,而且还可以多个端同步。多个端同时打开一个页面,任何交互都会同步。 marked markdown转HTML rainbow.js 让代码在HTML中语法高亮起来 gulp-template 模板替换,在生成demo的模板文件的时候用到。 gulp-tap 在pipeline中做一些其他事情,如另外读取文件、去掉注释、markdown转成HTML等 gulp-header 在文件头部加上header,使得生成出来的文件有逼格 gulp-upyun、gulp-eslint、gulp-rename、gulp-cssnano、gulp-uglify等看到名字就知道能干嘛的插件 当然这次项目的挑战性还是在持续集成上,虽然之前折腾过用Travis CI自动化部署个人博客,但这次还是蛋疼了一段时间,主要点在要将生成的文档这一堆静态文件部署到gh-page上。因为这次的项目是放在公司自己搭建的git服务器上的,但是公司的git服务不支持gh-page, 想要像https://qingchengfed.github.io/angular-clover-ui/这样轻松的访问该项目的文档不太现实。而且这个项目的文档是有脚本的,是要用到angular的,所以gitbook也不能满足要求。 在尝试过多种方法后,最终还是采用了在项目下建个临时文件夹,拉下指定的仓库后,生成commit后强制push到远程的方法。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859# .drone.ymlworkspace: base: /data/gitbook path: uedpipeline: restore-cache: image: drillster/drone-volume-cache restore: true mount: - node_modules volumes: - /data/gitbook/ued/:/cache/ build: image: node:7 secrets: [ UPYUN_USERNAME, UPYUN_PASSWORD ] commands: - npm install -g cnpm --registry=https://registry.npm.taobao.org - cnpm install - npm run build - npm run docs rebuild-cache: image: drillster/drone-volume-cache rebuild: true mount: - docs - node_modules volumes: - /data/gitbook/ued/:/cache/ restore-cache: image: drillster/drone-volume-cache restore: true mount: - docs volumes: - /data/gitbook/ued/:/cache/ docs: image: plugins/git secrets: [ GITHUB_USERNAME, GITHUB_EMAIL, GITHUB_PASSWORD, GITHUB_REPO ] commands: - git config --global user.name $GITHUB_USERNAME - git config --global user.email $GITHUB_EMAIL - git clone $GITHUB_REPO $$DEPLOY_DIR - cp -rf docs $$DEPLOY_DIR - cd $$DEPLOY_DIR - git add docs - updated_at=$(date "+Docs updated:%Y-%m-%d %H:%M:%S") - git commit -m "$updated_at" - git push origin master -f - cd .. - rm -rf $$DEPLOY_DIR environment: - DEPLOY_DIR=.deploy_gitbranches: master 上面的.drone.yml文件中有几点值得注意 pipeline中的每一步都是一个独立的docker环境,数据是隔离的。但是volume的数据是可以共享的,drillster/drone-volume-cache这个镜像能帮我们做到生成cache和取出cache。 drone可以在后台配置全局变量,使用的时候在对应pipeline中的secrets里面声明即可。 每个pipeline中可以用environment独立配置环境变量,但是因为drone有一个预编译过程,使用变量的时候需要$$DEPLOY_DIR这么使用。 git clone支持https,可以通过https://[username]:[password]@github.com/[username]/[project_name].git的方式直接获取,这些值可以放在全局变量中。之前思维一直被限制在ssh上,总想着如何配置ssh的key,最后发现在当前情景下还是https的方法来的简单。]]></content>
<tags>
<tag>CI</tag>
<tag>drone</tag>
<tag>持续集成</tag>
<tag>持续交付</tag>
</tags>
</entry>
<entry>
<title><![CDATA[有阴影的三角形]]></title>
<url>%2F2017%2F09%2F03%2Farrow%2F</url>
<content type="text"><![CDATA[普通的三角形在CSS中可通过指定上下左右四个方向的border来实现。如下面的代码可以声明出一个宽12px、高6px的倒立的等腰三角形。123border-top: solid 6px #fff;border-left: solid 6px transparent;border-right: solid 6px transparent; 但想要这个三角形有阴影,直接声明box-shadow是没用的,因为这个声明是对整个盒子模型生效的,这样声明出来的三角形会在四周有一个“很方”的阴影。 这个问题可以通过再声明一个伪元素,让这个伪元素高度或宽度足够小,所有的阴影效果声明在这个伪元素上。然后让三角形覆盖在上面,这样就可以trick出一个有阴影的三角形了。123456789101112131415161718192021222324252627282930// less中声明一个带三角和阴影的消息对话框.message-box { width: 300px; height: 200px; box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.2); box-sizing: border-box; &.top { &::after { content: ''; position: absolute; bottom: -6px; left: 50%; margin-left: -10px; border-top: solid 6px #fff; border-left: solid 6px transparent; border-right: solid 6px transparent; } &::before { content: ''; width: 12px; height: 3px; background-color: transparent; position: absolute; bottom: 0; left: 50%; margin-left: -10px; box-shadow: 0 6px 10px 0 rgba(0, 0, 0, .2); } }} 然而这个方法的问题在于,在那个为三角形贡献阴影的伪元素的box-shaow的偏移量和模糊半径如何取值,只能根据视觉效果慢慢调了。只要有点耐心,这个box-shaow会跟外面的box-shaow和谐相处的。]]></content>
<tags>
<tag>css</tag>
<tag>trick</tag>
<tag>三角形</tag>
</tags>
</entry>
<entry>
<title><![CDATA[一个时区的坑]]></title>
<url>%2F2017%2F08%2F27%2Fdate%2F</url>
<content type="text"><![CDATA[目测自己应该很久才会有一次比较大的产出了,但是如果每次都等着憋大招,那什么也不用做了。那还是从平时踩到的坑开始吧,即便只是解决一个比较小的问题,还是做下笔记比较好,谨防以后再次碰到。 后端传的时间均为2017-08-25T10:00:00的格式,这个在比较两个均没有offset的时间的时候没什么问题,但是一旦将这个时间与当前时间比较就会有问题,因为new Date()出来的Date对象是带着offset信息的, 如Fri Aug 25 2017 14:43:03 GMT+0800 (CST)。因此,在与当前时间比较时,一定要手动加上offset。123var time = '2017-08-25T10:00:00';new Date(time) // Safari Fri Aug 25 2017 18:00:00 GMT+0800 (CST)new Date(time) // Chrome Fri Aug 25 2017 10:00:00 GMT+0800 (CST) angular 中添加一个filter,方便复用。1234567891011app.filter('addTimeZoneOffset', [function () { var _offset = parseInt(new Date().getTimezoneOffset() / 60); // 获取当前时区的offset,东八区值为-8,表示格林时间落后东八区8个小时。 _offset = (_offset < 0 ? '+' : '-') + (Math.abs(_offset) < 10 ? '0' : '') + Math.abs(_offset) + ':00'; // 东八区为+08:00 return function (time) { if(!time || time.length !== 19) return time; // '2017-08-20T18:00:00' 长度为19的时候才加offset return time + _offset; }}]) Tips: 下次再遇到不同浏览器表现不一致、跟时间判断相关的问题,可以考虑下是不是时区offset的问题。]]></content>
<tags>
<tag>坑</tag>
<tag>JavaScript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[最初的梦想]]></title>
<url>%2F2017%2F05%2F21%2Fdream%2F</url>
<content type="text"><![CDATA[又要到月底了,一个月至少一篇文章的愿望眼看就要破灭,好不容易来的一次双休机会,还是抓紧时间完成指标吧~然而最近在技术上并没有多少进展,没有看多少新的内容,也没有多少已有内容的总结,只能整点“类鸡汤”给自己补补身子了。不知道什么原因,最近状态不怎么好,仿佛一下子对生活和工作失去了热情,很多事情都不愿去想,去搭理。也许是最近做的项目比较磨人吧,感觉事情做的不够好,但又没有耐心和性子去把他做的更好。又也许是因为又到了一个瓶颈期吧,突然不知道该干什么了,不知该何去何从了。虽说自己一直在追求效率,但是目前看来并没有实现这个目标,顾虑的太多,动手的时候畏手畏脚的,现在反而适得其反了。 长叹一口气吧~ 话说人在不得意的时候总是爱回忆一些往事,最近的我也开始了。我现在26岁,研究生毕业一年,在一家创业公司当前端工程师,准两年代码经验。两年之前没有方向,然后确定方向,一年半之前明确方向,一年前坚定方向,现在又有点迷失,因而又回头看了。那在两年之前的再之前呢? 其实故事应该从小学开始,犹记得当时上英语课时老师问我们的梦想是什么,为了回答这个问题,我特意翻阅了词典,找到了scientist 这个单词,用蹩脚的发音和不足的底气把它说了出来,当时觉得这个单词好难读啊,还是teacher、worker什么的比较好读,估计老师都听不懂,搞不好最后还得用中文说出来。所幸我们的英语老师“听”多识广,听懂了我说的单词,把它翻译了出来。毕竟还是小男生啊,喜欢自然课本胜过社会课本,还是有着一个科学家之梦的。这个梦一直伴随着我成长,初中时候累计的《科学之谜》、高中时候的《新发现》、《科学世界》等等各种各样的科普杂志堆了一两个箱子,现在还在家里躺着呢~ 就连高考报志愿的时候,也要冒着一定的风险填报了那个除了可望不可及的清华北大之外、听起来就像是科学家的摇篮的学校——中国科学技术大学。 大学之后,一切就都不一样了。即便当时被调剂分配到了听起来不怎么感冒的传播学专业,大一的时候还是积极的跟物理系的学生一块上课,计划着等到大一结束分流的时候再转到物理系。第一个学期教力学的老师我还是蛮喜欢的,他第一节课跟我们说的“证伪”和“自洽”让我至今印象深刻,而这个学期的节奏还是能在掌控范围之内的,然而到了第二个学期上电磁学的时候就没能这么幸运了,期中考试居然不及格——人生中第一次考试不及格啊,还是我最喜欢的物理。从那时开始,我突然对物理没了什么自信心,觉得自己根本不适合学物理。再说,学物理的牛人那么多,到最后能有几个成科学家啊,又有几个能成为爱因斯坦那样的人物啊,我这么渺小,居然连考个试都能不及格,呵呵~ 就这样,我放弃了转系,放弃了成为科学家的梦想。想想也是可笑啊,坚持了这么久的梦想,最后就这么败给了一次考试不及格。但是即便当时我能坚持下来,我也不确定我真的能学好做好啊,科研之路这么苦,说不准到什么时候又当逃兵了呢~ 再后来嘛,放弃了科学家之梦,破罐子破摔,本科生活就那样浑浑噩噩中结束了,快毕业时由于迷茫、无所适从决定参加支教,为自己争取点思考时间,希望大西北的风能把自己吹醒,再读个研究生缓个两三年。经过产品经理的试水失败之后,我终于找到了一条比较适合自己的路——安安静静的写代码。不管现在怎样,状态不好、瓶颈期、没有激情或什么的,起码在开始的时候我是能从中体会到快乐的。 其实到现在,我对物理依然保留着一股热忱和激情的,或者说这段时间我开始重新找回了一些往日的激情。前段时间看了那本相对论史话《时间的形状》,感觉突然间以往的那些困惑和不解又有了答案,对这个世界又有了几分新的理解。原来这个世界真的是一片空啊~ 突然想起,小学或初中的时候,我曾在父亲的手机里留过一段话,具体内容记不太清了,大意是:我想要成为一名科学家,希望爸爸妈妈支持和鼓励。也许当时写的应该会更煽情点,毕竟想让他看到,感动,然后支持,哈哈。然而这么多年过去了,他可能没有看到吧,但是现在的我看到了,看到了那个纯真的少年。少年,再见。少年,你好。 (看到这篇文章的人,特别是同事,不要瞎传播。我想暴露自我,但不想完全暴露自我。就像我喜欢晒太阳,但不喜欢在烈日下暴晒。谢谢,再见。)]]></content>
<tags>
<tag>个人成长</tag>
<tag>随笔</tag>
</tags>
</entry>
<entry>
<title><![CDATA[mac中apache的基本配置]]></title>
<url>%2F2017%2F04%2F05%2Fapache%2F</url>
<content type="text"><![CDATA[主要相关的配置文件123sudo vim /etc/apache2/httpd.conf # apache的主要配置文件sudo vim /etc/apache2/extra/httpd-vhosts.conf # 配置虚拟主机sudo vim /etc/hosts # host配置文件 三个主要的配置文件,/etc/apache2/httpd.conf为主要配置文件,模块开启和关闭什么的都在里面,/etc/apache2/extra/httpd-vhosts.conf 就是用来配置apache的虚拟主机的,包括80端口的默认http服务和403端口的默认https服务,/etc/hosts 目前只知道用127.0.0.1 shawn.me来配置虚拟主机。 语法检查与重启12sudo apachectl configtest # 检查配置文件sudo apachectl restart # 重启 每次修改配置文件后都要对配置文件进行语法检查,然后重启服务 添加虚拟主机123456<VirtualHost *:80> DocumentRoot "/Library/WebServer/Documents" ServerName localhost ErrorLog "/private/var/log/apache2/localhost-error_log" CustomLog "/private/var/log/apache2/localhost-access_log" common</VirtualHost> 开启ssl生成主机密码1234mkdir /private/etc/apache2/ssl# 在/etc目录下也行,/etc 其实是个symlink, 指向/private/etccd /private/etc/apache2/sslsudo ssh-keygen -f server.key 生成证书请求文件1sudo openssl req -new -key server.key -out request.csr # 需要填一连串的验证信息,验证信息不通过将被标记为不安全的私密链接 生成ssl证书1sudo openssl x509 -req -days 365 -in request.csr -signkey server.key -out server.crt apache 配置 /private/etc/apache2/httpd.conf去掉下面代码原来的注释 123LoadModule ssl_module libexec/apache2/mod_ssl.soInclude /private/etc/apache2/extra/httpd-ssl.confInclude/private/etc/apache2/extra/httpd-vhosts.conf /private/etc/apache2/extra/httpd-ssl.conf去掉下面代码原来的注释 12SSLCertificateFile "/private/etc/apache2/ssl/server.crt"SSLCertificateKeyFile "/private/etc/apache2/ssl/server.key" /private/etc/apache2/extra/httpd-vhosts.conf添加403的虚拟主机 12345678<VirtualHost *:443> SSLEngine on SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL SSLCertificateFile /private/etc/apache2/ssl/server.crt SSLCertificateKeyFile /private/etc/apache2/ssl/server.key ServerName localhost DocumentRoot "/some/website/directory/"</VirtualHost> 可能的问题 403 没有权限查看 1234<Directory /> AllowOverride none# Require all denied</Directory> 注释掉/etc/apache2/httpd.conf文件中directory标签内的Require all denied,不然访问虚拟主机里配置的DocumentRoot会直接报没有权限。 为什么会写这个?其实我只想配置下HTTPS而已。。。]]></content>
<tags>
<tag>apache</tag>
<tag>mac</tag>
</tags>
</entry>
<entry>
<title><![CDATA[总结下那些常用的伪类和伪元素]]></title>
<url>%2F2017%2F03%2F28%2Fpseudo-class%2F</url>
<content type="text"><![CDATA[伪类和伪元素应该是CSS中常见的两个概念了,两者区别在于前者等效于一个类,后者等效于一个元素,伪类的效果可以通过添加一个类来实现,伪元素的效果则需要通过添加一个元素才能实现。伪类使用时用:,伪元素使用时用::。本来这两个概念在日常工作中都是很平安的,直到发现自己写过的input[type="checkbox"][checked="checked"]对checked的input元素失效,才发现自己需要的是input[type="checkbox"]:checked。虽然前者作为属性选择器,对于在属性中声明checked状态的input是管用的,但对于不显式声明checked状态的input就无能为力了,只能靠后者了。由此,决心重新整理下那些常用和 将来可能会常用 的伪类和伪元素,顺便归类和标记一下~ 伪类状态相关几乎所有元素通用,不过常用于a元素,:focus还比较常用于表单元素,:hover也很常用 :link :visited :hover :active :focus 结构相关父子元素间的元素位置 :first-child :last-child :nth-child 根据元素的位置匹配一个或者多个元素,它接受一个an+b形式的参数 如:nth-child(2n) :nth-last-child 与:nth-child类似,但是从最后一个子元素开始计数 :only-child 父子元素间的元素类型 :first-of-type 匹配当前元素是其父元素的第一个该类型元素的元素 :last-of-type :nth-of-type :nth-last-of-type :only-of-type 其他 :not 匹配不符合参数选择器的元素 :target 匹配URL中的锚指向的元素 :empty 匹配没有子元素的元素 表单相关顺便翻了下表单元素,其包括 form input select option button textarea label fieldset legend optgroup然而,我居然没怎么用过后面三种。。 表单通用 :disabled 禁用的 :enabled 可用的 :required 必选的 :optional 可选的 :read-only 只能读 :read-write 能读能写 :default 默认样式,对button input option 有用,目测平时用不着 :valid 合法的 :invalid 非法的 :focus // 通用伪类,对于表单元素来说更常见 对于上面成双成对的,日常使用的话应该只会用其中一个,如:disabled,:required,:read-only,毕竟他们的另一半都是默认的。。 文本类 :placeholder-shown // 草稿中 可以指定显示placeholder的时候input元素的样式,注意这不能修改placeholder的样式,如果想要修改,请使用::placeholder 选择类 :checked 包括input[type="checkbox"]:checked, input[type="radio"]:checked, option:checked(实际上并没有什么卵用,因为option的样式并不能通过CSS来修改) :indeterminate 未定 数字类 :in-range :input[type="number"]:in-range 在指定区间内 :out-of-range :input[type="number"]:out-of-range 不在指定区间 伪元素单双冒号皆可 ::after ::before ::first-letter ::first-line 即便如此,还是都用::吧,与伪类区分开来。 仅双冒号 ::selection 文档中被用户高亮的部分,只支持color, background-color, cursor, outline, text-decoration, text-emphasis-color和text-shadow ::placeholder input元素中placeholder文本的样式]]></content>
<tags>
<tag>总结</tag>
<tag>css</tag>
<tag>伪类</tag>
<tag>伪元素</tag>
</tags>
</entry>
<entry>
<title><![CDATA[line-height]]></title>
<url>%2F2017%2F03%2F26%2Fline-height%2F</url>
<content type="text"><![CDATA[Box 普通流(文档流):只要不是float和绝对定位方式布局的,都在普通流里面。 浮动:浮动的框可以向左或向右移动,直到它的外边缘碰到包含框或另一个浮动框的边框为止。 定位:相对定位在文档流之中,绝对定位则脱离文档流。 Block Box宽度的计算块级元素框的水平部分 = 其父元素的width = margin-left+margin-right + padding-left + padding-right + border-left + border-right + 自身width BFC(Block Formatting Context)特点: 内部盒子会在垂直方向排列 同一个BFC中的元素可能会发生margin叠加; BFC就是页面上的一个隔离的独立容器,里外互相不影响 计算BFC的高度时,考虑BFC所包含的所有子元素,连浮动元素也参与计算; 当元素不是BFC的子元素的时候,浮动元素高度不参与BFC计算(既是常见的盒子塌陷问题) 触发条件: 根元素 float属性不为none position为absolute或fixed display为inline-block, table-cell, table-caption, flex, inline-flex overflow不为visible 其作用 清除浮动 阻止边距折叠 用于布局,什么两栏自适应高度之类的 Line Box 匿名文本:未包含在行内元素的字符串 行内框:非替换元素,行内框高度=line-height;替换元素,行内框高度=内容区宽度(行间距不应用到替换元素) baseline: 字母x的下边缘线(居然还有个单位叫x-height,即字母x的高度,vertical-align: middle并不是真正的垂直居中对齐,而是与基线往上1/2 x-height的距离) line-height: 两条baseline之间的距离 可替换元素:展现不是由CSS来控制的、外观渲染独立于CSS的外部对象,如img、input、textarea、select等非替换元素:与可替换元素相反,大部分元素皆属于该类]]></content>
<tags>
<tag>css</tag>
<tag>布局</tag>
<tag>line-height</tag>
</tags>
</entry>
<entry>
<title><![CDATA[左侧定宽、右侧自适应、垂直居中的布局]]></title>
<url>%2F2017%2F02%2F21%2Fvertical-align-layout%2F</url>
<content type="text"><![CDATA[具体业务需求:左侧是一个icon,右侧是一大段文字(不定高),要求在各种屏幕宽度下垂直居中 1234<div class="wrap"> <i class="icn"></i> <p class="txt"></p></div> 1234567891011121314151617.wrap { font-size: 0; //去掉换行引起的多余空间}.icn { display: inline-block; vertical-align: middle; width: 40px; height: 40px;}.txt { display: inline-block; vertical-align: middle; width: 100%; box-sizing: border-box; margin-left: -40px; //margin 负值,使得两个inline-block元素在同行显示 padding-left: 40px; //与左侧的元素拉开距离,可适当增加距离} 关键:margin负值]]></content>
<tags>
<tag>css</tag>
<tag>布局</tag>
<tag>垂直居中</tag>
</tags>
</entry>
<entry>
<title><![CDATA[2016]]></title>
<url>%2F2017%2F01%2F27%2Fsummary%2F</url>
<content type="text"><![CDATA[怎么说也是2016年的第一天,冒个泡马克一记,一般来说新旧交接之际往往适合总结过去展望未来,然而我现在并没有这样的欲望,一来日子过的平淡,淡到时间忘了我,我忘了时间,二来步上一条新路,急得往前走,顾不得往回看,不晓得当前的选择是否正确,怕一回首心态又不够坚决了,只能一条路走到黑了。 这是2016年的第一天我发的微博,没有总结,只有匆匆忙忙的一记标记。到了2017年,也是拖沓了将近一个月,终于有时间来好好的对过去的2016年总结一番。总的来说,过去的一年还是比较圆满的,用同事调侃的话来说就是“学业、事业、爱情三丰收”,虽然有点言过其实,但是大体上还是比较契合的。 学业上,终于结束19年的学生生活,获得硕士学位毕业了。回顾这次写毕业论文的过程,真的是不想再经历第二次,四月底要提交的论文,居然4月中旬才开始写(不过公司组织的四月初的马代之旅还是相当棒的)。不过即便如此,我还是投机取巧的一个星期之内写完了,然后之后答辩的过程也算是比较顺利,匆匆忙忙办完离校手续,我就又跑回北京继续实习了。这一切似乎很快,就像一个镜头快速切过,来不及反应过来,我就已经告别了传播学,告别了学校,告别了合肥,在北京798艺术区的某个木屋子里面对着电脑写代码了。而且才短短半年,我就已经感觉那种校园生活离我很远了,看着还在学校的同学发布的朋友圈,我都觉得很陌生,也只会草草的瞥一眼过去了。 工作上,我正式开启了我的前端工程师之旅,这正是微博中提到的一条新的道路,当初上路上的比较晚,走的比较急,生怕一回头就看到起点,但一转眼距离开始的地方已经一年半多了,我也不再是一个菜鸟了。回首这一年,感觉自己的变化还是挺大的,现在已经基本能比较独立的开展前端项目了,遇到问题的时候也基本能通过各种方法有效的解决问题了。 还行的回顾这一年做过的大大小小的项目,大体上比较满意的有: 官网首页。这应该是做过的最大的纯静态页面的项目了,切图切到我心碎。【JQuery、JQuery-fullpage、video.js、slick(用于图片轮播)】 会员端的年终报告:页面动画最酷炫、构建工具用的最多、跟字体接触最多、优化考虑的最多的一次,然而在安卓微信端X5内核里渲染效果不佳。 zepto 替代 jQuery zepto-fullpage 替代 jQuery-fullpage countUp.js animate.css echarts.js(后来发现这个略大,移动端用这个亏了) postcss-pxtorem postcss-sprites gulp-base64 (已舍弃) gulp-svg2ttf(由字体svg生成ttf字体) gulp-fontmin(压缩字体包) gulp-iconfont (可根据少量svg生成字体svg,改掉相应的Unicode可生成字体svg) gulp-imagemin(压缩图片) 博客搭建:过程有点曲折,但是结果是好的。【hexo、travis(自动化部署)】 图片压缩:最有内涵和逼格的一件。虽然是按照教程一步一步来的,但是也是蛮有收获的。【canvas、promise】 请求优化:利用定时器和标记去掉冗余的请求 登录注册模块:虽然不记得具体写了什么,但却是写的逻辑最清晰、复用性也不错的一个模块。 有意思的通过这些项目,我也总算发现了自己在工作中比较喜欢做的事情: 组件的抽象 模块的封装 自动化构建 动画和过渡 其中前三者都是追求效率的表现,最后一个是追求浮夸酷炫的缘故。 可尝试的当然在未来,我觉得有意思值得尝试的事情有: canvas动画(封装和抽象) 图表 表单验证的抽象 Python & Django Node & Express 机器学习 前三个跟我当前岗位的相关性还是蛮高的,后三个就有点跨界的意思了,但是这有什么关系呢,兴趣广泛点不挺好的嘛~ 需要提升的 信心。可能是因为自己非专业出身吧,跟其他同行在讨论问题的时候缺乏自信。一旦别人说了一些不了解的概念,便开始一脸懵逼了,跟别人讨论的时候也没有什么底气了。说到底,我的缺乏自信是由自己的无知造成的,为了提升这点,我需要了解更多。所幸现在的我对行业的了解已经没有之前的那么无知了,希望以后能有更大的进步。 耐心。现在对于英文检索结果的阅读总是浅尝则止,思考问题时遇到阻碍时也总是容易放弃,缺乏足够的耐心。 其他 2016年去过的城市:马尔代夫、北京、合肥、上海、武汉、乌兰布统 关于爱情上的丰收:很幸运、很知足、很开心,但是也要低调。]]></content>
<tags>
<tag>个人成长</tag>
<tag>总结</tag>
</tags>
</entry>
<entry>
<title><![CDATA[元素hover时渐隐渐现弹窗(纯CSS)]]></title>
<url>%2F2016%2F12%2F11%2Fhover%2F</url>
<content type="text"><![CDATA[若直接用display:none和display:block则没有渐隐渐进效果,而如果直接用opacity:0和opacity:1则占据多余的空间,鼠标没有放在图标上也会出现layer。而用visibility:hidden和visibility:visible则可避免这个问题。 12345678910111213141516.icn { margin: 0 30px; cursor: pointer; position: relative; .m-layer { opacity: 0; visibility: hidden; transition: all .3s ease; } &:hover { .m-layer { visibility: visible; opacity: 1; } }}]]></content>
<tags>
<tag>css</tag>
<tag>hover</tag>
</tags>
</entry>
<entry>
<title><![CDATA[图片和文字垂直居中对齐]]></title>
<url>%2F2016%2F12%2F11%2Fvm%2F</url>
<content type="text"><![CDATA[块级元素内的行内元素(包括inline和inline-block)的垂直居中 inline-block && 伪元素 12345678910# html<div class="parent"> <img src="" alt="" class="icn" /> <i class="txt">文字</i></div># css.parent { height: 200px;}.icn {display: inline-block; vertical-align: middle;}.txt {vertical-align: middle;}.parent:before {content: ''; display: inline-block; height: 100%; vertical-align: middle;} .parent 设置box-sizing: border-box可以避免设置border的影响 table && table-cell; 12345<!-- HTML --><div class="parent"> <img src="" alt="" class="img" /> <div class="txt">文字</div></div> 123456//display:table方法.parent { display: table;}.img, .txt { display: table-cell; vertical-align: middle;} 父元素不要设置height,不然img元素可能会不居中,可通过设置img的padding来调节.父元素的高度。 **inline-block元素和inline元素的vertical-align方法只能确保元素之间对齐,并不能保证元素在父元素内对齐,常用的设置line-height和height相等的方法并不能真正做到真正的垂直居中。** **vertical-align仅对inline元素和inline-block元素(还有table-cell)有效,决定的是该行内元素的基线相对于该元素所在行的基线的垂直对齐。** **line-height决定的是当前元素的行高,作用于其行内子元素。**]]></content>
<tags>
<tag>css</tag>
<tag>垂直居中</tag>
</tags>
</entry>
<entry>
<title><![CDATA[git merge与git rebase]]></title>
<url>%2F2016%2F09%2F26%2Fgit-2%2F</url>
<content type="text"><![CDATA[别人的小结 共同目的:合并分支 git merge: 被merge的分支内容以新的commit合并到当前分支,git merge <commit1> [<commit2>] commit2默认为当前分支。 git rebase: 先把当前分支较两个分支的最新共同commit的修改保存起来,然后重新确定根commit(被rebase的最新commit即新的跟commit),再之前保存起的修改内容以备份的形式合并到当前分支。 git merge-base <commit1> <commit2>: 找两个分支/commit 间最新的共同commit]]></content>
<tags>
<tag>git</tag>
</tags>
</entry>
<entry>
<title><![CDATA[npm包的打包和发布]]></title>
<url>%2F2016%2F09%2F26%2Fnpm%2F</url>
<content type="text"><![CDATA[本地打包npm pack [<directory || current_directory>] 本地打包,生成package npm install <package_name> 安装本地包 在线打包npm addUser //绑定账号 Username: your name Password: your password Email: yourmail@gmail.com npm whoami //查看当前账号 npm publish //发布]]></content>
<tags>
<tag>npm</tag>
</tags>
</entry>
<entry>
<title><![CDATA[mac中python环境的配置]]></title>
<url>%2F2016%2F09%2F26%2Fenv%2F</url>
<content type="text"><![CDATA[安装Homebrew ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 安装Homebrew Cask brew install caskroom/cask/brew-cask 用brew安装mysql/python/node/git/iTtem2 安装Oh My Zsh sh -c "$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)" python 环境安装 brew install python(mac自带的Python有问题,需要重新安装) brew install pip(Python的包管理工具) virtualenv 安装 [sudo] pip install virtualenv cd .virtualenvs && vim postactivate 1234567#!/bin/zsh# This hook is run after every virtualenv is activated.PS1="$_OLD_VIRTUAL_PS1"_OLD_RPROMPT="$RPROMPT"RPROMPT="%{${fg_bold[white]}%}(env: %{${fg[green]}%}`basename \"$VIRTUAL_ENV\"`%{${fg_bold[white]}%})%{${reset_color}%} $RPROMPT"proj_name=$(echo $VIRTUAL_ENV|awk -F'/' '{print $NF}')cd ~/projects/$proj_name 上述最后一行为项目的所在目录 克隆git项目 cd ~/projects git clone [project_address] mkvirtualenv source /usr/local/bin/virtualenvwrapper.sh (可将上述命令添加到~/.zshrc中,每次启用zsh都会自动执行该命令) mkvirtualenv [project_name] workon [project_name] 启动mysql mysql -u root -p新建database mysql -u root -p [database_name] < [mysql file] 配置项目的env 12345678910111213141516# -*- coding: utf-8 -*-# __author__ = chenchiyuanfrom __future__ import division, unicode_literals, print_functionDeployEnv = "gravity"ConfigOverride = [("django", "debug", "True"),("django", "template_dubug", "True"),("db", "host", "localhost"),("db", "username", "root"),("db", "password", ""),("db", "db_name", "[database_name]"),("db", "port", "3306"),] 启动服务 pip install -r requirements.txt ./manage.py runserver [port] ./manage.py runserver 0.0.0.0:[port]可通过局域网ip访问]]></content>
<tags>
<tag>mac</tag>
<tag>环境配置</tag>
<tag>python</tag>
</tags>
</entry>
<entry>
<title><![CDATA[git 常用命令整理]]></title>
<url>%2F2016%2F09%2F26%2Fgit-1%2F</url>
<content type="text"><![CDATA[阮一峰整理的常用git命令 一张关于git原理的示意图 几个基本概念Workspace:工作区 Index / Stage:暂存区 Repository:仓库区(或本地仓库) Remote:远程仓库 个人常用的命令[]为可选项,<>为必需项 初始化12$ git init [project_name]$ git clone <url> 配置配置文件.gitconfig 123456789# 显示当前的Git配置$ git config --list# 编辑Git配置文件$ git config -e [--global]# 设置提交代码时的用户信息$ git config [--global] user.name "<name>"$ git config [--global] user.email "<email address>" 代码提交12345678# 提交工作区自上次commit之后的变化,直接到仓库区# 等效于 git add . && git commit$ git commit -a# 使用一次新的commit,替代上一次提交# 如果代码没有任何新变化,则用来改写上一次commit的提交信息# 可以指定file$ git commit --amend -m [message] [file] 分支123456789101112# 列出所有远程分支$ git branch -r# 列出所有本地分支和远程分支$ git branch -a# 选择一个commit,合并进当前分支$ git cherry-pick [commit]# 删除远程分支$ git push origin --delete <branch-name>$ git branch -dr <remote> <branch-name> 标签12345678# 新建一个tag在指定commit$ git tag <tag> [commit]# 提交指定tag$ git push [remote] [tag]# 提交所有tag$ git push [remote] --tags 查看信息123456789101112131415# 显示某个文件的版本历史,包括文件改名$ git log --follow [file]$ git whatchanged [file]# 显示指定文件相关的每一次diff$ git log -p [file]# 显示指定文件是什么人在什么时间修改过$ git blame [file]# 显示某次提交发生变化的文件$ git show --name-only [commit]# 显示某次提交时,某个文件的内容$ git show [commit]:[filename] 远程同步12345678# 下载远程仓库的所有变动$ git fetch [remote]# 显示某个远程仓库的信息$ git remote show [remote]# 增加一个新的远程仓库,并命名$ git remote add [shortname] [url] 撤销123456789# 恢复上一个commit的所有文件到工作区$ git checkout .# 重置当前HEAD为指定commit,但保持暂存区和工作区不变$ git reset --keep [commit]# 新建一个commit,用来撤销指定commit# 后者的所有变化都将被前者抵消,并且应用到当前分支$ git revert [commit]]]></content>
<tags>
<tag>git</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Android机型 translate-3d的点击穿透问题]]></title>
<url>%2F2016%2F09%2F26%2Ftranslate-3d%2F</url>
<content type="text"><![CDATA[某元素1tranform: translate3d(0px, 0px, 36px); 点击浮层会有点击穿透问题 解决方法: 父元素: 12transform-style: preserve-3d; //使被转换的子元素保留其 3D 转换:-webkit-transform-style: preserve-3d; 子元素: 12transform: translateZ(100px);-webkit-transform: translateZ(100px); 只要子元素的translateZ值大于某元素translate3d中z轴的值,则点击子元素,不会发生点击穿透;]]></content>
<tags>
<tag>css</tag>
<tag>坑</tag>
<tag>translate-3d</tag>
<tag>点击穿透</tag>
</tags>
</entry>
<entry>
<title><![CDATA[正则表达式的基础整理]]></title>
<url>%2F2016%2F09%2F20%2Fregexp-base%2F</url>
<content type="text"><![CDATA[正则元字符基本型 ^: 集合中表排除,集合外表开始,多行模式也表\n或\r之后的位置 $: 表结束,多行模式也表\n或\r之前的位置 {}: 表数量范围 (): 表捕获 []: 表集合 -: 表区间 .: 表\n外的所有字符 数量型 *: 0次或多次 <=> {0,} +: 1次或多次 <=> {1,} ?: 0次或1次 <=> {0,1} {n}: n次 {n,}: n次(含)以上 {n,m}: n次(含)以上,m次(含)以下 转义型基本字符类 \d: 表数字 <=> [0-9] \D: <=> [^0-9] \w: 表包括下划线的任何单词字符 <=> [A-Za-z0-9_] \W: <=> [^A-Za-z0-9_] 空白类 \b: 表单词边界 \B: 表非单词边界 \s:表任意空白字符<=>[\f\n\r\t\v] \S:表任意非空白字符<=>[^\f\n\r\t\v] \f:表换页符 \n:表换行符 \r:表回车符 \t:表制表符 \v:表垂直制表符 其他类 \NUM: 向后引用,NUM为正整数,如(.)\1表两个连续的相同字符 \uNUM: 表Unicode字符,NUM为四个十六进制数字表示的Unicode字符,[\u4e00-\u9fa5]*表任意数量的汉字 优先级 优先级 符号 最高 \ 高 ()、(?:)、(?=)、[] 中 *、+、?、{n}、{n,}、{m,n} 低 ^、$、- 最低 分割线(markdown 表格打不出来) 正则表达式的捕获和预查捕获(pattern) 匹配并捕获。 123var reg = /Java(6|7)/;reg.exec('Java6')// ["Java6", "6"] (?:pattern) 匹配不捕获。 123var reg = /Java(?:6|7)/;reg.exec('Java6')// ["Java6"] 正向预查(?=pattern) 正向匹配,不消耗字符 123var reg = /Java(?=6|7)/;reg.exec('Java6')// ["Java"] (?!pattern) 正向不匹配,不消耗字符 123var reg = /Java(?!6|7)/;reg.exec('Java8')// ["Java"] 2、反向预查JavaScript不支持 (?<=pattern) 反向匹配,不消耗字符 (?<!pattern) 反向不匹配,不消耗字符—]]></content>
<tags>
<tag>JavaScript</tag>
<tag>正则表达式</tag>
</tags>
</entry>
<entry>
<title><![CDATA[正则表达式相关的JS方法]]></title>
<url>%2F2016%2F09%2F19%2Fregexp%2F</url>
<content type="text"><![CDATA[String.prototypeString.prototype.split语法:str.split([separator[, limit]]) 参数:separator支持正则 String.prototype.search语法:str.search(regexp) 参数:一个正则对象 返回值:所给定字符串的第一个正则匹配项的index,没有匹配项则返回-1;直接忽略正则的g; 类似于 String.prototype.indexOf方法(也支持正则查询) String.prototype.match语法:str.match(regexp) 参数:一个正则对象,如果传的是其他类型,该类型会用new RegExp()转化成正则对象 返回值:有匹配项时返回数组,否则返回null; 数组内容: 全局匹配时没有捕获项,只有匹配项; 非全局匹配数组第一项为匹配项,其他依次为捕获项; 如果有嵌套捕获,先捕获外层再捕获内层。 String.prototype.replace语法:str.replace(regexp|substr, newSubStr|function) 参数一:正则或字符串 参数二: 新的字符串支持$的字符串($$、$&、$n) 替换函数(指定参数包括match, p1, p2, p3, offset, string) match为匹配项,p1,p2,p3依次为捕获项,offset为匹配项在原字符串的index,string为原字符串; RegExp.prototypeRegExp.prototype.exec语法:regexObj.exec(str) 返回值:与String.prototype.match的返回值类似,有匹配项时返回数组,否则返回null; 数组内容: 不管是不是全局匹配,数组第一项为最后一个匹配项,其他依次为该匹配项的捕获项; 全局匹配时从lastIndex(默认值为0)开始匹配,每次返回的值可能都不一样(循环); 该数组还有两个属性index(匹配项在str中的index)和input(原字符串str); 调用该方法后的regexObj多个属性值有更新:lastIndex(上次匹配项最后一个字符的index,全局匹配时下次调用将从lastIndex开始匹配)、ignoreCase(是否忽略大小写)、global(是否全局匹配)、multiline(是否多行匹配)、source(原字符串); RegExp.prototype.test语法:regexObj.test(str) 返回值:布尔值,是否有匹配项]]></content>
<tags>
<tag>JavaScript</tag>
<tag>正则表达式</tag>
</tags>
</entry>
<entry>
<title><![CDATA[突如其来的危机感]]></title>
<url>%2F2016%2F09%2F18%2Fcrisis%2F</url>
<content type="text"><![CDATA[已经好久没有更新我的博客了,虽然其实我都没有怎么好好的经营过我的博客,目前上面的大部分内容也只是从其他地方搬运过来的,即便美其名曰是大自然的搬运工,然而自身的身份仍未能从内容消费者转化成内容生产者。想起去年曾经在《简书》上写过一篇信誓旦旦的文章,决意从一个内容消费者转型成一个内容生产者,还意气风发的列出了几点理由。然而一年多时间过去了,我的生产内容屈指可数。也许是当时受《简书》平台的影响吧,毕竟是一个不错的生产工具,于是“许下难以承受的诺言”。一年时间过去了,我的职业身份发生了相当大的转变,从一个产品方向的学习者变成了一个前端开发工程师,期间的艰辛和不易不用多说,每一个经历过蜕变的人应该都懂的脱胎换骨的过程,只是身份转变后,我的生产内容也得随之发生变化了。于是在历尽艰辛成功搭建一个属于自己的博客之后,上面的内容也自然的都是一些技术博客,即便大部分是搬运过来的。但是现在我不想只在写一些技术内容,自己的博客自己说了算,我想在上面分享自己的一些想法或心路历程也是我的自由吧。(毕竟没多少人会去看你的博客~) 过去的一个多星期时间一直在外面出差,很难想象一个开发人员还要出差吧?!然而这样的事情就这么发生了,毕竟创业公司人力紧缺。先是在上海参加FIBO展会,然后去武汉参加公司的校招。是的,才刚毕业3个月的我就要去参加校招了。整个校招过程基本还算顺利,也有一个比较皆大欢喜的Happy ending,然而校招结束后我最大的感触还是危机感。在武大的宣讲会结束之后,我们收到了一大波的前端简历,为了分散人流,公司此次前行四人组中唯一的前端工程师也不得已跟那些准备毕业的前端工作意向的学生一并聊天,虽然当时表面很镇定,其实我的内心是十分惶恐的,毕竟我只是一个半道出家、只学了一年多时间前端的小菜鸡啊~所幸当晚跟那些求职者的聊天还是蛮顺利的,相对于作为面试官的我,那些前来求职的面试者的内心才是真正的诚惶诚恐啊,只要我故作镇定,保持住气场,一切都会很顺利的。 然后我们开始连夜筛简历,看着这些五花八门的简历,我的心情十分复杂,感觉这些专业出身的求职者的经历真的是精彩了,直接完虐去年只学了三个月前端的我啊。但是转神又想,简历上的经历可信度不高,即便他列出了这么多的项目、框架和工具,但是他在实际项目中真正参与的内容、对这些框架的理解、对这些工具的使用和熟悉程度都是不得而知的,还需面试聊过才能判定。据此安慰自己,从而度过一个平安的夜晚。 面试的主要工作并不是交给我这个小菜鸡,但是我还是领到了一个面试任务。然而第一次面试别人之前并没有准备充分,只是简单的看了下一些前端面试题,就直接打电话面试了。整个电面的过程很快,每一个问的问题对方都很快的回答上来了,但是我并没有追问,只是简单的嗯嗯就过去了。因为当我想追问的时候,我开始对这个问题的答案开始模糊了,我觉得自己都不能清晰的将该问题回答出来。即便是有那么一两次我追问了,得到的结果跟我想象中的不一样,我却也没有勇气去纠正对方的错误,因为我突然不知道自己理解的是不是正确的了。于是乎我的第一次电话面试就在我的各种怀疑和不确定中结束了,也许对方觉得面试很顺利,问的都是一些很基础的问题,而且基本都能回答上来,反而是我自己开始怀疑自己了,不知道自己理解的是不是正确的,自信心也开始部分瓦解了。这应该还是跟这次面试前的准备不足有关系,我应该自己先把每个问题的答案明确之后再去面试别人,应该确定好每个问题可能会有的追问和相应答案,而不是很草率的就直接上阵了,如果对方冷不防的来一个追问,我这个面试官可能就直接挂了。 说到底,应该还是自己的基础比较薄弱吧,即便之前看过,可能理解的并不是很深入,更有可能的是看过后就忘记了,这种基础性的东西似乎只在面试的时候管用,项目实践中用处并不明显,但是从长远来看,对基础越扎实,对一些基本概念的理解越透彻,之后的开发之路会越稳健。所以,我真是得好好的再把一些基础知识回顾一遍. 是的,危机感来了,仅仅转正3个月后马上就要有一大批新鲜的前端小鲜肉要上架了,专业的或半路出家的,几个月经验的或几年的,比我小一两岁的或比我小三四岁的,统统都已经箭在弦上不得不发了。我现在的能力并不能让自己养尊处优,必须push自己快步前行了。 为未来几个月的自己定下几个目标或任务吧~ 1.巩固基础:重新回到求职时的状态,加深HTML、CSS、JS中基础概念的理解。 2.查看优秀框架(包括Angular和Vue)的源码,并随时做备注、记笔记。(当前已在进行中) 3.执行项目之前做好规划,并严格按照计划和排期执行。 任务一主要还是来源自这次的面试经历,在别人过自己这关之前总得让自己过了自己这关吧。基础真的很重要,特别是对我这种半路出家的人来说。 任务二是这段时间一直在做的事情,现在提上议程有助于自己能有更多的动力坚持下去。毕竟阅读源码这种事情是比较痛苦的,如果没有足够的动力的话,很快的就会消逝在风中了。 任务三是近期对公司项目执行的一个反思,总感觉最近公司开发的效率不高,连续几个项目都是一再延期,当然这也跟近来各种项目穿插进行有关,但是一旦有了排期,一切还是尽量按着排期来。所以排期需要慎决定,对项目的工作量要有一个合理的评估。于我而言,在项目执行之前必须要合理评估,慎重规划,并严格执行,这样才能充分的利用时间,在项目实践和个人提升之间保持平衡。]]></content>
<tags>
<tag>个人成长</tag>
<tag>随笔</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Promise对象]]></title>
<url>%2F2016%2F08%2F23%2Fpromise%2F</url>
<content type="text"><![CDATA[最近调研Promise,在小组内部分享,懒得再展开整理了,先把提纲什么的放在这么晾晾~ promise的由来解决异步编程的各种困难: 1.异常处理; 2.函数嵌套过深,回调地狱; 3.异步转同步 其主要两种解决方式: 1.事件发布与订阅模式; 2.Promise/Deferred 模式 CommonJs的promise模型Promise/APromise对象有三种状态: 未完成(pending)、完成(fulfilled) 和 失败(rejected).Promise对象的状态转化只有两种: > pending => fulfilled; > pending => rejected;Promise对象的状态一旦发生改变,将不能再更改. Promise对象的then方法,可接受三个回调函数 onFulFilled, onRejected, onProgress(返回进度消息时) Promise与Deferred的区别Deferred主要用于内部,负责维护模型的状态,Promise则用于外部,暴露接口给外部添加业务逻辑. 链式调用then()方法调用后返回的是一个新的Promise对象,该对象同样也有then()方法,因此可实现链式调用(promise chain).基本原则: 一个promise对象必须是fulfilled状态或rejected状态才能调用then方法中的callback.1.内部链式调用1234567891011return getUsername().then(function (username) { return getUser(username) .then(function (user) { // if we get here without an error, // the value returned here // or the exception thrown here // resolves the promise returned // by the first line })}); 2.外部链式调用1234567891011return getUsername().then(function (username) { return getUser(username);}).then(function (user) { // if we get here without an error, // the value returned here // or the exception thrown here // resolves the promise returned // by the first line}); 第一种方式看起来跟普通的回调函数没什么区别,也很容易掉到回调地狱中, 第二种逻辑清晰, 但是具体业务中可根据业务逻辑内外部混合链式调用.1234567891011121314return getUsername().then(function (username) { return getUser(username);})// chained because we will not need the user name in the next event.then(function (user) { return getPassword() // nested because we need both user and password next .then(function (password) { if (user.passwordHash !== hash(password)) { throw new Error("Can't authenticate"); } });}); Promise/B增加了API,如when方法 Promise/D如何判断一个对象是Promise对象 Promise/A+组织对then方法进行了补充将then方法作为promise对象的甄别方法 Promise实现1.Q模块 主要基于Promise/A2.ES6 Promise 基于Promise/A+ 主要差别:1.notify callback2.可通过操作Deferred对象更改Promise对象的状态3.值的传递: > 前者必须返回 promise对象才能将promise对象resolve的值传递给那个then方法里的回调函数 > 后者then方法中返回的值(非promise对象)可直接传递给下个then方法 Angular中的Promise - $q,依赖于$rootScope两种主要返回promise方式:类Q和类ES6 1.类Q创建Deferred对象: var deferred = $q.defer()Deferred对象的APIdeferred.resolve(value)deferred.reject(reason)deferred.notify(value) 返回Promise对象: return deferred.promise Promise 的方法promise.then(successCallback, errorCallback, notifyCallback)promise.catch(errorCallback)promise.finally(callback, notifyCallback) 2.类ES612345678910$q(function(resolve, reject){ //Do somthing to resolve setTimeout(function(){ if (Math.random() > 0.5) { resolve('resolved'); } else { reject('rejected'); } }, 1000)}) $q.resolve(value)直接返回一个fulfilled状态的promise对象, 并向之后then方法中的回调函数传入值value$q.reject(reason)直接返回一个rejected状态的promise对象, 并向之后then方法中的回调函数传入值reason $q.all(promises)$q.race(promises) ES6 Promise创建promise对象12345678910var promise= new Promise(function(resolve, reject){ //Do somthing to resolve setTimeout(function(){ if (Math.random() > 0.5) { resolve('resolved'); } else { reject('rejected'); } }, 1000)}); 1.异步调用2.[[PromiseStatus]], [[PromiseValue]],每次调用then方法后都返回一个新的promise对象3.promise对象的APIthen(onFulfilled, onRejected), catch(onRejected)4.Promise的APIall(promises), race(promises)resolve(value), reject(reason)]]></content>
<tags>
<tag>JavaScript</tag>
<tag>Promise</tag>
<tag>ES6</tag>
</tags>
</entry>
<entry>
<title><![CDATA[自适应的正方形]]></title>
<url>%2F2016%2F08%2F04%2Fsquare%2F</url>
<content type="text"><![CDATA[想要让元素的宽度随着屏幕宽度自适应,一般使用百分比就好了,但是想要让元素的高度随着屏幕自适应,那就有难度了.今天终于得知了这个奇淫技巧.这是要实现的效果图. 主要要求 图片为正方形图片大小能随着屏幕宽度自适应 最终实现的HTML代码12345678<ul class="list"> <li class="item"> <div class="imgWrap"> <img src="..." alt="" /> </div> </li> <!-- li * N --></ul> CSS.list { padding: 0 15px; font-size: 0; /* inline-block布局需要font-size:0 防止子元素宽度溢出 */ margin: 0 -5px; /* .imgWrap设了margin: 0 5px, 防止.list在水平位置上偏移 */ } .item { display: inline-block; width: 33.33333%; /* 百分比,每行3张图片 */ margin: 10px 0; .imgWrap { margin: 0 5px; height: 0; padding-bottom: 100%; /* 关键: 将图片的外围元素高度设为0, 并将其padding-bottom 设为100% */ overflow: hidden; /* 隐藏正方形之外的区域 */ } } 关键点: 元素上padding的百分比值是相对于父元素的宽度的.height: 0;padding-bottom: 100%; 要想实现正伸缩的正方形区域,其关键在与将元素的高度设为0, 并将其padding-bottom或padding-top值设为100%. 其原因是元素上padding的百分比值是相对于父元素的宽度的, padding-bottom: 100%可强制使得计算后的padding-bottom值等于其宽度值. 由于padding是内边距, 是在盒子内部的, 盒子内部的元素是能在该区域出现的, 所以图片是可以显示的. 再加上overflow: hidden, 整个元素就是一个可随屏幕宽度变化的正方形了. 另外, 如果想要在该正方形区域中怎么展示该图片, 那就是另外的问题了.]]></content>
<tags>
<tag>css</tag>
<tag>布局</tag>
<tag>trick</tag>
</tags>
</entry>
<entry>
<title><![CDATA[圣杯布局和双飞翼布局]]></title>
<url>%2F2016%2F08%2F04%2Flayout%2F</url>
<content type="text"><![CDATA[再整理下传说中的圣杯布局和双飞翼布局~两种方式主要想实现的布局如图所示:而且其关键要求是: #main必须是优先于#left和#right加载的. 圣杯布局据说圣杯布局源自In Search of the Holy Grail, 这里也有中文翻译版. 主要思想 给包裹着三个元素的 container 加一个 padding, 让 padding-left 和 padding-right 的数值是 left 和 right 的宽度,然后利用相对定位把他们再移动在两旁。 圣杯布局的HTML结构如下:1234567<div id="header">header</div><div id="container"> <div id="main" class="column">main</div> <div id="left" class="column">left</div> <div id="right" class="column">right</div></div><div id="footer">footer</div> CSS 样式如下:1234567891011121314151617181920212223242526272829303132body { min-width: 550px; /* 2x LC width + RC width */}#container { padding-left: 200px; /* LC width */ padding-right: 150px; /* RC width */}#container .column { position: relative; float: left;}#main { width: 100%;}#left { width: 200px; /* LC width */ right: 200px; /* LC width */ margin-left: -100%;}#right { width: 150px; /* RC width */ margin-right: -150px; /* RC width */ /* 另一种写法 */ ** /* margin-left: -150px; right: -150px; */ **}#footer { clear: both;}/*** IE6 Fix ***/* html #left { left: 150px; /* RC width */} 主要步骤 需要三栏布局的#container容器利用padding-left: 200px和padding-right: 150px为左右栏腾出空间. 对#main、#left和#right指定宽度,尤其是#main指定了宽度100%,根据父级块级元素和子级块级元素之间的宽度关系(子级块级元素将占据父级块级元素除margin、padding和border之外的所有宽度),子元素将占据剩下的所有空间. 利用margin负值将#left设置margin-left: -100%,这时#left将被顶到#main之前左侧的位置,由于之前设置了position: relative,因此可通过right: 200px让#right向左偏移其宽度的距离,从而到达之前#contain腾出的左侧的位置. 通过margin-left: -150px将#right往左拉回其宽度的距离,从而使其到达#contain的右侧位置. 关键点在于: margin负值: margin-left: -100%和margin-left: -150px 相对定位后的位置调整: position: relative; right: 200px 如果要等高需要对margin-bottom和padding-bottom进行调整1234567#container { overflow: hidden;}#container .column { padding-bottom: 20010px; /* X + padding-bottom */ margin-bottom: -20000px; /* X */} 双飞翼布局主要思想 在 container 里面再添加一个 div, 然后对这个 div 进行 margin-left 和 margin-right. HTML 较圣杯布局的调整主要是在#main中添加了#wrap, 并将主要内容写在#wrap中.123456789<div id="header">head</div> <div id="container cleanfix"> <div id="main"> <div id="wrap">main</div> </div> <div id="left">left</div> <div id="right">right</div> </div><div id="foot">foot</div> 其CSS 样式如下:123456789101112131415161718192021#left, #right, #main { float: left;}#left { width: 40px; height: 60px; margin-left: -100%;}#right { width: 60px; height: 80px; margin-left: -60px;}#main { width: 100%;}#wrap { margin-left: 40px; margin-right: 60px;} 圣杯布局和双飞翼布局的异同点1.#container内部的三个元素全部左浮动,然后清除浮动防止影响2.给#main 100% 的宽度让他占满一行3.给 #left -100% 的margin-left 让他移动到最左边,给 right 和他宽度一样的负 margin 让他移动到最右边4.针对移动后 #main 的两边会被 #left 和 #right 重合覆盖掉做出不同的改变,这儿也就是两个布局的本质区别 圣杯布局会给 #container 内边距,左右分别为 #left 和 #right的宽度,然后再利用相对定位移动 left 和 right双飞翼布局会在 #container 里面再加一层 wrap ,然后把内容都写在 wrap 里面,正对 wrap 设置他的 margin, 左右外边距和 left 与 right 一样 主要来源In Search of the Holy Grail关于圣杯布局那些年,奇妙的圣杯与双飞翼,还有负边距 我是大自然的搬运工~]]></content>
<tags>
<tag>css</tag>
<tag>笔记</tag>
<tag>布局</tag>
</tags>
</entry>
<entry>
<title><![CDATA[两列自适应布局和三列自适应布局]]></title>
<url>%2F2016%2F08%2F04%2F2collumns-layout%2F</url>
<content type="text"><![CDATA[整理下之前的两列自适应布局和三列自适应布局的实现方式 两列自适应布局绝对定位法HTML:12<div class="left"></div><div class="main"></div> CSS:1234567891011.main { margin-left: 200px; height: 300px;}.left { position: absolute; left: 0; top: 0; width: 200px; height: 300px;} 左侧浮动法HTML:12<div class="left"></div><div class="main"></div> CSS:123456789.main { margin-left: 200px; height: 300px;}.left { float: left; width: 200px; height: 300px;} 主体嵌套法(主体优先)HTML:1234<div class="main"> <div class="content"></div></div><div class="left"></div> CSS:1234567891011121314.main { float: left; width: 100%;}.content { margin-left: 200px; height: 300px;}.left { float: left; margin-left: -100%; width: 200px; height: 300px;} 个人更偏爱左侧浮动法,简单实用.主体优先的前提下选择主体嵌套法. 三列自适应布局(左右两列定宽,主体自适应)绝对定位法HTML:123div class="left"></div><div class="main"></div><div class="right"></div> CSS:12345678910111213141516171819.left { position: absolute; left: 0; top: 0; width: 200px; height: 300px;}.right { position: absolute; right: 0; top: 0; width: 200px; height: 300px;}.main { margin-left: 200px; margin-right: 200px; height: 300px;} 自身浮动法HTML:123<div class="left"></div><div class="right"></div><div class="main"></div> CSS:123456789101112131415.left { float: left; width: 200px; height: 300px;}.right { float: right; width: 200px; height: 300px;}.main { margin-left: 200px; margin-right: 200px; height: 300px;} 主体嵌套法(主体优先, margin负值)即双飞翼布局的核心部分HTML:12345<div class="main"> <div class="content"></div></div><div class="left"></div><div class="right"></div> CSS:123456789101112131415161718192021.main { float: left; width: 100%;}.content { margin-left: 200px; margin-right: 200px; height: 300px;}.left { float: left; margin-left: -100%; width: 200px; height: 300px;}.right { float: left; width: 200px; margin-left: -200px; height: 300px;}]]></content>
<tags>
<tag>css</tag>
<tag>笔记</tag>
<tag>布局</tag>
<tag>两列自适应布局</tag>
<tag>三列自适应布局</tag>
</tags>
</entry>
<entry>
<title><![CDATA[几个git命令]]></title>
<url>%2F2016%2F08%2F01%2Fgit%2F</url>
<content type="text"><![CDATA[学到几个git的新命令 删除远程分支$ git push origin :[branch_name] 分支名称前的冒号表示在远程删除该分支 修改远程关联分支$ git remote set-url origin [new_repository_address] ([old_repository_address])]]></content>
<tags>
<tag>git</tag>
</tags>
</entry>
<entry>
<title><![CDATA[使用HEXO和Github搭建博客有感]]></title>
<url>%2F2016%2F07%2F31%2Fabout-hexo-github%2F</url>
<content type="text"><![CDATA[总算用HEXO和Github成功的构建了博客,并用Travis CI实现了自动部署。 参考文章: HEXO+Github,搭建属于自己的博客 史上最新版GitHub+Hexo配置系列教程 用 Travis CI 自动部署 hexo 重新整理下流程: 生成HEXO项目12345npm install hexo-cli -g hexo init [project_name]cd [project_name]npm installhexo server 配置主题参考NexT.Mist 部署到GitHub建立Github项目项目名称应为[user_name.github.io] 安装hexo-deployer-git1npm install hexo-deployer-git --save 配置_config.yml123456# Deployment## Docs: https://hexo.io/docs/deployment.htmldeploy: type: git repo: https://github.com/[user_name]/[user_name].github.io.git branch: master 利用Travis CI实现自动化部署开启Travis CI,并将对应的项目状态开启生成公钥1ssh-keygen -t rsa -C "youremail@example.com" # 生成id_rsa.pub和id_rsa 将id_rsa.pub添加到GitHub项目的Deploy key中,并将Allow write access打开加密私钥1234mkdir .travisgem install travis # 安装 clitravis login --auto # 登录travis encrypt-file id_rsa --add # id_rsa为刚生成的密钥文件,这会在当前目录生成加密后的私钥文件id_rsa.enc,同时会输入encrypted_key 和 encrypted_iv 添加ssh_config12345Host github.com User git StrictHostKeyChecking no IdentityFile ~/.ssh/id_rsa IdentitiesOnly yes 放在.travis目录中 配置_travis.yml12345678910111213141516171819202122232425262728293031323334353637# 声明语言和版本language: node_jsnode_js: - "7" # 指定分支branches: only: - pages-origin# 添加缓存cache: directories: - node_modulesbefore_install:# 解密私钥文件,并放到~/.ssh目录中- openssl aes-256-cbc -K $encrypted_xxxxxx_key -iv $encrypted_xxxx2548ca7ff03a_iv -in .travis/id_rsa.enc -out ~/.ssh/id_rsa -d# 改变文件权限- chmod 600 ~/.ssh/id_rsa# 配置ssh- eval $(ssh-agent)- ssh-add ~/.ssh/id_rsa- cp .travis/ssh_config ~/.ssh/config# 配置git账号- git config --global user.name xxx- git config --global user.email xxxxinstall:- npm install# 生成博客script:- ./node_modules/hexo/bin/hexo clean- ./node_modules/hexo/bin/hexo g# 部署after_success:- ./node_modules/hexo/bin/hexo d]]></content>
<tags>
<tag>总结</tag>
<tag>HEXO</tag>
<tag>Github</tag>
<tag>Travis CI</tag>
<tag>CI</tag>
</tags>
</entry>
</search>