-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.xml
425 lines (425 loc) · 304 KB
/
search.xml
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
420
421
422
423
424
425
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[CSSDOM简介]]></title>
<url>%2F2018%2F12%2F13%2Fcss%2FCSSDOM%E7%AE%80%E4%BB%8B%2F</url>
<content type="text"><![CDATA[什么是CSSOM CSS对象模型是一组允许从JavaScript处理CSS的API。它很像DOM,但对于CSS而不是HTML。它允许用户动态地读取和修改CSS样式。 通过element.style获取和设置内联样式使用JavaScript操作或访问CSS属性和值的最基本方法是通过元素的style属性进行操作: 123document.body.style.background = 'lightblue';document.body.style.width = '888px';document.body.style.cssFloat = 'left'; 这是使用JavaScript操作或获取CSS属性和值的简单方法。但是style以这种方式使用属性需要注意:这只适用于元素的内联样式。 如果元素没有定义内联样式,即时有一个外部样式文件,获取不存在的样式属性时将得不到任何东西: 1console.log(document.body.cssFloat) // "" 这显然有一些显着的局限性,所以让我们看一些使用JavaScript阅读和操作样式的更有用的技术。 获取计算样式可以使用以下window.getComputedStyle()方法读取元素上任何CSS属性的计算CSS值: 1234widnow.getComputedStyle(document.body).background// "rgba(0, 0, 0, 0) none repeat scroll 0% 0% / auto padding-box border-box"getComputedStyle(document.body).width// "134px" 计算属性将会得到最终的样式结果,包括浏览器默认的样式,这可能会得到太多东西了,(⊙o⊙)…! 有几种不同的方法可以使用window.getComputedStyle(): 12345678// dot notation, same as abovewindow.getComputedStyle(el).backgroundColor;// square bracket notationwindow.getComputedStyle(el)['background-color'];// using getPropertyValue()window.getComputedStyle(el).getPropertyValue('background-color'); 获取伪元素的计算样式一个鲜为人知的小问题window.getComputedStyle()是,它允许您检索伪元素的样式信息。你会经常看到这样的window.getComputedStyle()声明: 1window.getComputedStyle(document.body, ":before").width; 第二个可选参数允许我指定我正在访问伪元素的计算CSS。 CSSStyleDeclaration不管是通过style还是window.getComputedStyle,它返回的都是CSSStyleDeclaration对象,后者是只读的。 setProperty(), getPropertyValue() 和 item()考虑如下代码: 123456let box = document.querySelector('.box');box.style.setProperty('color', 'orange');box.style.setProperty('font-family', 'Georgia, serif');op.innerHTML = box.style.getPropertyValue('color');op2.innerHTML = `${box.style.item(0)}, ${box.style.item(1)}`; setProperty(css属性名,css属性值) getPropertyValue(css属性名) item(index):上图item(0)为color、item(1)为font-family,item(2)如果没有的话返回”” removeProperty()12345box.style.setProperty('font-size', '1.5em');box.style.item(0) // "font-size"document.body.style.removeProperty('font-size');document.body.style.item(0); // "" 获取和设置属性的优先级12345box.style.setProperty('font-family', 'Georgia, serif', 'important');box.style.setProperty('font-size', '1.5em');box.style.getPropertyPriority('font-family'); // importantop2.innerHTML = box.style.getPropertyPriority('font-size'); // "" 当setProperty增加第三个参数important时,font-family将会有!important权重。 我要强调的是,这些方法可以与已经直接放在HTML元素style属性上的任何内联样式一起使用。所以,如果我有以下HTML: 1<div class="box" style="border: solid 1px red !important;"> 其他子属性也将会有!important权重。 123456// These all return "important"box.style.getPropertyPriority('border'));box.style.getPropertyPriority('border-top-width'));box.style.getPropertyPriority('border-bottom-width'));box.style.getPropertyPriority('border-color'));box.style.getPropertyPriority('border-style')); CSSStyleSheet接口从样式表访问信息的最简单方法是使用document开放的styleSheets属性,例如,下面的行使用该length属性来查看当前页面具有多少样式表: 1document.styleSheets.length; // 1 我可以使用从零开始的索引来引用任何文档的样式表: 1document.styleSheets[0]; 它将返回CSSStyleSheet对象,里面最有用的是cssRules属性,此属性提供该样式表中包含的所有CSS规则(包括声明块,规则,媒体规则等)的列表。在下面的部分,我们将详细介绍该API的操作。 cssRules(CSSRuleList) -> (CSSStyleRule) -> type, cssText, selectorText, style(CSSStyleDeclaration),… 使用样式表对象下面使用样式表对象遍历选择器: 12345678let myRules = document.styleSheets[0].cssRules, p = document.querySelector('p');for (i of myRules) { if (i.type === 1) { p.innerHTML += `<code>${i.selectorText}</code><br>`; }} 注意:cssRules是对象。 type类型: 1: STYLE_RULE 3: IMPORT_RULE 4: MEDIA_RULE 5: KEYFRAMES_RULE 完整类型请参考:https://developer.mozilla.org/en-US/docs/Web/API/CSSRule#Type_constants 其中selectorText是可写属性,所以我们可以动态修改: 1234567for (i of myRules) { if (i.type === 1) { if (i.selectorText === 'a:hover') { i.selectorText = 'a:hover, a:active'; } } } 使用CSSOM访问@media规则假如有如下属性: 12345678910@media (max-width: 800px) { body { line-height: 1.2; } .component { float: none; margin: 0; }} 可以通过conditionText获取: 123456789let myRules = document.styleSheets[0].cssRules, p = document.querySelector('.output');for (i of myRules) { if (i.type === 4) { p.innerHTML += `<code>${i.conditionText}</code><br>`; // (max-width: 800px) }} 使用CSSOM访问@keyframes规则假如有如下属性: 12345678910111213@keyframes exampleAnimation { from { color: blue; } 20% { color: orange; } to { color: green; }} 可以通过keyText获取: 123456789for (i of myRules) { if (i.type === 7) { for (j of i.cssRules) { p.innerHTML += `<code>${j.keyText}</code><br>`; } }}// 0% 20% 100% 您会注意到我的原始CSS使用from并to作为第一个和最后一个关键帧,但keyText属性计算这些0%和100%。keyText也可以设置值。在我的示例样式表中,我可以像这样硬编码: 12345678// Read the current value (0%)document.styleSheets[0].cssRules[6].cssRules[0].keyText;// Change the value to 10%document.styleSheets[0].cssRules[6].cssRules[0].keyText = '10%'// Read the new value (10%)document.styleSheets[0].cssRules[6].cssRules[0].keyText; 访问@keyframes规则时可用的另一个属性是name: 12345678910let myRules = document.styleSheets[0].cssRules, p = document.querySelector('.output');for (i of myRules) { if (i.type === 7) { p.innerHTML += `<code>${i.name}</code><br>`; }}// exampleAnimation 我在这里要提到的最后一件事是能够获取单个关键帧内的特定样式。这是一些带有演示的示例代码: 12345678910let myRules = document.styleSheets[0].cssRules, p = document.querySelector('.output');for (i of myRules) { if (i.type === 7) { for (j of i.cssRules) { p.innerHTML += `<code>${j.style.color}</code><br>`; } }} 增加或删除CSSDeclarations规则通过insertRule()增加规则: 12345let myStylesheet = document.styleSheets[0];console.log(myStylesheet.cssRules.length); // 8document.styleSheets[0].insertRule('article { line-height: 1.5; font-size: 1.5em; }', myStylesheet.cssRules.length);console.log(document.styleSheets[0].cssRules.length); // 9 insertRule()第一个参数是样式字符串,第二个参数是表示您希望插入规则的位置或索引。如果未包括此项,则默认为0(意味着规则将插入规则集合的开头)。如果索引恰好大于rules对象的长度,则会抛出错误。 通过deleteRule()删除规则: 12345let myStylesheet = document.styleSheets[0];console.log(myStylesheet.cssRules.length); // 8myStylesheet.deleteRule(3);console.log(myStylesheet.cssRules.length); // 7 该方法接受一个参数,该参数表示我要删除的规则的索引,参数传入的索引值必须小于cssRules对象的长度,否则将引发错误。 重新访问CSSStyleDeclaration API记住,CSSRule下的style属性返回CSSStyleDeclaration属性: 123456789101112131415161718192021222324252627// Grab the style rules for the body and main elementslet myBodyRule = document.styleSheets[0].cssRules[1].style, myMainRule = document.styleSheets[0].cssRules[2].style;// Set the bg color on the bodymyBodyRule.setProperty('background-color', 'peachpuff');// Get the font size of the bodymyBodyRule.getPropertyValue('font-size');// Get the 5th item in the body's style rulemyBodyRule.item(5);// Log the current length of the body style rule (8)myBodyRule.length;// Remove the line heightmyBodyRule.removeProperty('line-height');// log the length again (7)myBodyRule.length;// Check priority of font-family (empty string)myBodyRule.getPropertyPriority('font-family');// Check priority of margin in the "main" style rule (!important)myMainRule.getPropertyPriority('margin'); CSSDOM的未来?在我在本文中考虑过的所有内容之后,我不得不打破这样的消息,即有一天我们所知道的CSSOM可能已经过时了。 这是因为称为CSS Typed OM的东西,它是Houdini项目的一部分。虽然有些人已经注意到新的Typed OM与目前的CSSOM相比更加冗长,但Eric Bidelman在本文中概述的好处包括: 更少的错误 算术运算和单位转换 更好的性能 错误处理 CSS属性名称始终是字符串 有关这些功能的完整详细信息和语法的一瞥,请务必查看完整的文章。 在撰写本文时,CSS Typed OM仅在Chrome中受支持。]]></content>
<tags>
<tag>css</tag>
</tags>
</entry>
<entry>
<title><![CDATA[2018年学习计划(js原理篇)]]></title>
<url>%2F2018%2F11%2F01%2Fjavascript%2F2018%E5%B9%B4%E5%AD%A6%E4%B9%A0%E8%AE%A1%E5%88%92%EF%BC%88js%E5%8E%9F%E7%90%86%E7%AF%87%EF%BC%89%2F</url>
<content type="text"><![CDATA[Javascript是单线程单一并发语言,这意味着它可以一次处理一个任务或一次处理一段代码。它有一个单独的调用堆栈,它与堆之类的其他部分一起构成了Javascript并发模型(在V8内部实现)。我们首先来看看这些术语: 下面是我总结的一张图片: 这里我展开讲,如果需要详细了解请参阅下面参考网址。 下面以一道面试题展开: 12345678910111213function Foo() { getName = function () { alert (1); }; return this;}Foo.getName = function () { alert (2);};Foo.prototype.getName = function () { alert (3);};var getName = function () { alert (4);};function getName() { alert (5);}//请写出以下输出结果:getName();Foo().getName();getName(); 执行上下文代码开始,引擎创建全局执行上下文: 创建阶段 ThisBinding: 这边的this为window对象 词法环境: 环境记录: Foo: getName: 外部环境引用:null 变量环境: 环境记录: getName: undefined 外部环境应用: 正是由于javascript引擎会在执行上下文之前的创建阶段(即变量提升),所以代码实际上是变成如下: 12345678910function Foo() { getName = function () { alert (1); }; return this;}var getName;//只提升变量声明function getName() { alert (5);}//提升函数声明,覆盖var的声明 Foo.getName = function () { alert (2);};Foo.prototype.getName = function () { alert (3);};getName = function () { alert (4);};//最终的赋值再次覆盖function getName声明 所以第一问的答案输出为4。 执行阶段 调用堆栈假设当开始执行到Foo().getName()时,开始进入Foo函数,把Foo函数压入调用堆栈顶部。创建Foo局部执行上下文创建阶段: ThisBinding: 由于这边没有对象调用,故为this为window对象 词法环境: 环境记录: 外部环境引用: 变量环境: 环境记录: getName: function(){ alert(4); } 外部环境应用: return this返回时,Foo函数弹出栈顶,Foo局部执行上下文销毁。 故执行时第二问答案输出1;第三问因为getName已被第二问中修改,故输出1。 事件循环事件循环是指主线程重复从消息队列中取消息、执行的过程。如果遇到同步函数,直接压入调用栈,如果异步代码,则委托给事件队列,然后继续运行代码,当异步处理返回时压入事件队列,主线程在执行完当前循环中的所有代码后,就会到消息队列取出这条消息,并执行它: 参考网址 https://tylermcginnis.com/javascript-visualizer/?code=function%20Foo%28%29%20%7B%0A%20%20%20%20getName%20%3D%20function%20%28%29%20%7B%20alert%20%281%29%3B%20%7D%3B%0A%20%20%20%20return%20this%3B%0A%7D%0AFoo.getName%20%3D%20function%20%28%29%20%7B%20alert%20%282%29%3B%7D%3B%0AFoo.prototype.getName%20%3D%20function%20%28%29%20%7B%20alert%20%283%29%3B%7D%3B%0Avar%20getName%20%3D%20function%20%28%29%20%7B%20alert%20%284%29%3B%7D%3B%0Afunction%20getName%28%29%20%7B%20alert%20%285%29%3B%7D%0A%0A%2F%2F%E8%AF%B7%E5%86%99%E5%87%BA%E4%BB%A5%E4%B8%8B%E8%BE%93%E5%87%BA%E7%BB%93%E6%9E%9C%EF%BC%9A%0AgetName%28%29%3B%0AFoo%28%29.getName%28%29%3B%0AgetName%28%29%3B https://mp.weixin.qq.com/s/QljMmVPJ8k7eYB2ynV03LQ https://medium.com/@gaurav.pandvia/understanding-javascript-function-executions-tasks-event-loop-call-stack-more-part-1-5683dea1f5ec https://www.valentinog.com/blog/js-execution-context-call-stack/ https://blog.bitsrc.io/understanding-execution-context-and-execution-stack-in-javascript-1c9ea8642dd0]]></content>
<tags>
<tag>javascript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[lerna在项目中的运用]]></title>
<url>%2F2018%2F10%2F26%2Fnodejs%2Flerna%E5%9C%A8%E9%A1%B9%E7%9B%AE%E4%B8%AD%E7%9A%84%E8%BF%90%E7%94%A8%2F</url>
<content type="text"><![CDATA[背景公司自己编写的库越来越多,而且之间的关联性比较强,如果某个包升级了版本,其他包也得相对应升级,这样非常的繁琐。 什么是 monorepo 和 multirepo在讲lerna之前,我们要先了解一下两个不同的代码管理组织方式 monorepo 和 multirepo: monorepo:把所有的相关项目放在同一个仓库下,有点类似yarn workspaces multirepo:每个子项目拥有自己的 repo 什么是lernalerna是一个管理多个javascript包的工具。它属于monorepo类型,当你的项目有相关联时最好使用monorepo方式进行管理。 以一个简单的示例进行说明新建一个testLerna项目: 1$ npx lerna init 初始化一个lerna命令,生成以下目录结构: 1234testLerna/ packages/ lerna.json package.json pcakges目录主要放置包,lerna.json为配置文件。 两种模式 Fixed/Locked 模式 (默认) 通过在lerna.json中指定version,当你运行lerna publish时,如果其中某一个包至上次发布以来有更新,则将会更新version。 这个模式是babel目前使用的模式,当你想把所有包关联在一起的时候可以考虑使用这种模式,需要注意的是,当其中某个包升级了大版本之后,其他的每个包都会自动升级大版本。 Independent mode 1lerna init --independent 独立模式允许您更具体地更新每个包的版本,并对一组组件有意义。跟semantic-release等工具配合使用会更好。 已经有结合使用的示例 发布假设以下项目: 1234packages shareui shareui-html shareui-font shareui依赖shareui-html,shareui-html依赖shareui-font,可以命令添加: 12$ lerna add shareui-html --scope=shareui$ lerna add shareui-font --scope=shareui-html 前提是要关联git,修改registry到公司私库 git提交到暂存区域,之后通过lerna publish发布: 1$ lerna publish 以下情况需要考虑: shareui-font升级了,相对应的shareui-html和shareui也要升级? 运行lerna bootstrap升级对应依赖 参考网址 https://github.com/lerna/lerna https://zhuanlan.zhihu.com/p/33858131 https://zhuanlan.zhihu.com/p/35237759]]></content>
<tags>
<tag>npm</tag>
<tag>lerna</tag>
</tags>
</entry>
<entry>
<title><![CDATA[2018年学习计划(eslint篇)]]></title>
<url>%2F2018%2F10%2F11%2Fjavascript%2F2018%E5%B9%B4%E5%AD%A6%E4%B9%A0%E8%AE%A1%E5%88%92%EF%BC%88eslint%E7%AF%87%EF%BC%89%2F</url>
<content type="text"><![CDATA[概述ESLint 是在 ECMAScript/JavaScript 代码中识别和报告模式匹配的工具,它的目标是保证代码的一致性和避免错误。在许多方面,它和 JSLint、JSHint 相似,除了少数的例外: ESLint 使用 Espree 解析 JavaScript。 ESLint 使用 AST 去分析代码中的模式 ESLint 是完全插件化的。每一个规则都是一个插件并且你可以在运行时添加更多的规则。 使用12345678// 安装yarn add eslint --dev// 生成配置文件,或者:npx eslint --init./node_modules/.bin/eslint --init // 运行./node_modules/.bin/eslint yourfile.js 第二个值为规则的配置对象。 extendsextend 提供的是 eslint 现有规则的一系列预设。 它可以继承多个。如安装了 eslint-config-airbnb,就可以在 extends 这里引用 airbnb/base, 这样就相当于预设了 airbnb/base 的规则,常用的预设:”eslint:recommended” “airbnb/base” “stanard”(需先安装 eslint-config-standard)。当然,除了 eslint-config-xxx 提供了一系列预设,插件(eslint-plugin-xxx)也能提供预设用于继承,例如,当你安装了 eslint-plugin-react 时,就可以在 extends 这里指定 “plugin:react/recommended”,当然,也可以指定一个具体的 eslint 配置文件 path/to/file 继承。 123456{ extends: [ "airbnb/base", "plugin:react/recommended" ]} 假设以aribnb风格(一般以eslint-config-开头)为例: 1yarn add eslint-config-aribnb --dev 然后在.eslintrc.js文件添加: 123{ extends: ['aribnb']} aribnb的javascript风格:https://github.com/airbnb/javascript plugins这里指定插件,插件名一般为 eslint-plugin-xxx,这里可以缩写为 xxx,插件提供了除 eslint 规定之外额外的规则。 plugin 与 extend 的区别:extend 提供的是 eslint 现有规则的一系列预设而 plugin 则提供了除预设之外的自定义规则,当你在 eslint 的规则里找不到合适的的时候就可以借用插件来实现了 1234"plugins": [ // 这里安装了 eslint-plugin-import "import"] plugins只提供rules不会设置eslint配置,所以最好在extends中使用会更好点 rules在.eslintrc.js中会存在rules规则配置。第一个值是错误级别,可以使用以下值之一: off或0:关闭规则 warn或者1:将规则视为一个警告(不会影响退出码) error或者2:将规则视为一个错误 (退出码为1) 两种格式:”rule-name”: 0/1/2 “rule-name”: [0/1/2, configDetail],当然前面的0/1/2也可以使用英文表示。 parser默认使用 eslint 自己的 Espree(可支持 ES5,ES6,ES7)来进行解析,但是在一些实验性语法等eslint本身不支持时会报错,所以一般我们会使用eslint-babel转换器: 1yarn add babel-eslint --dev 123{ parser: 'babel-eslint`} 可以通过parserOptions来配置转换器,这里不展开说明,请查看相对应的转换器文档。 env指定环境,每个环境都有自己预定义的全局变量,可以同时指定多个环境,不矛盾,主流的库或构建系统都能支持,列表见官方文档:http://eslint.cn/docs/user-guide/configuring#specifying-environments browser - 浏览器环境中的全局变量。 node - Node.js 全局变量和 Node.js 作用域。 commonjs - CommonJS 全局变量和 CommonJS 作用域 (用于 Browserify/WebPack 打包的只在浏览器中运行的代码)。 shared-node-browser - Node.js 和 Browser 通用全局变量。 es6 - 启用除了 modules 以外的所有 ECMAScript 6 特性(该选项会自动设置 ecmaVersion 解析器选项为 6)。 worker - Web Workers 全局变量。 amd - 将 require() 和 define() 定义为像 amd 一样的全局变量。 mocha - 添加所有的 Mocha 测试全局变量。 jasmine - 添加所有的 Jasmine 版本 1.3 和 2.0 的测试全局变量。 jest - Jest 全局变量。 phantomjs - PhantomJS 全局变量。 protractor - Protractor 全局变量。 qunit - QUnit 全局变量。 jquery - jQuery 全局变量。 prototypejs - Prototype.js 全局变量。 shelljs - ShellJS 全局变量。 meteor - Meteor 全局变量。 mongo - MongoDB 全局变量。 applescript - AppleScript 全局变量。 nashorn - Java 8 Nashorn 全局变量。 serviceworker - Service Worker 全局变量。 atomtest - Atom 测试全局变量。 embertest - Ember 测试全局变量。 webextensions - WebExtensions 全局变量。 greasemonkey - GreaseMonkey 全局变量。 比如指定browser: true,则代码中的window、document等全局对象就不会报未定义。 global指定环境为我们提供了其预置的全局变量,对于那些我们自定义的全局变量,可以在这里指定,设为 true 表示不应该被重写,设为 false 表示可以被重写。 123456{ "global": { var1: true, var2: false }} settingsESLint 支持在配置文件添加共享设置。你可以添加 settings 对象到配置文件,它将提供给每一个将被执行的规则。如果你想添加的自定义规则而且使它们可以访问到相同的信息,这将会很有用,并且很容易配置。 参考:https://github.com/benmosher/eslint-plugin-import#settings overrides有时,你可能需要更精细的配置,比如,如果同一个目录下的文件需要有不同的配置。因此,你可以在配置中使用 overrides 键,它只适用于匹配特定的 glob 模式的文件,使用你在命令行上传递的格式 (e.g., app/*/.test.js)。 在webstrom中使用首先设置eslint修复的快捷键: 我设置的是ctrl + E,本来想设置ctrl + S保存代码时自动修改,但是好像会冲突。 在webstrom中,需要开启eslint配置项: 这里的Eslint Packge一定要选项目的中的eslint哦! 之后试一下ctrl + E看能不能自动修改代码,如果不行,请点击File -> Invalidate Caches / Restart重启webstrom即可。 禁用规则可以在你的文件中使用以下格式的块注释来临时禁止规则出现警告: 12345/* eslint-disable */alert('foo');/* eslint-enable */ 你也可以对指定的规则启用或禁用警告: 123456/* eslint-disable no-alert, no-console */alert('foo');console.log('bar');/* eslint-enable no-alert, no-console */ 如果在整个文件范围内禁止规则出现警告,将 /* eslint-disable */ 块注释放在文件顶部: 123/* eslint-disable */alert('foo'); 你也可以对整个文件启用或禁用警告: 1234/* eslint-disable no-alert */// Disables no-alert for the rest of the filealert('foo'); 可以在你的文件中使用以下格式的行注释或块注释在某一特定的行上禁用所有规则: 123456789alert('foo'); // eslint-disable-linealert('foo'); /* eslint-disable-line */// eslint-disable-next-linealert('foo');/* eslint-disable-next-line */alert('foo'); 在某一特定的行上禁用指定的规则: 123456789alert('foo'); // eslint-disable-line no-alert, quotes, semi// eslint-disable-next-line no-alertalert('foo');alert('foo'); /* eslint-disable-line no-alert *//* eslint-disable-next-line no-alert */alert('foo'); 你可以通过在项目根目录创建一个 .eslintignore 文件告诉 ESLint 去忽略特定的文件和目录。 命令行1eslint [options] [file|dir|glob]* 比如:eslint --ext .jsx,.js lib/将检测lib目录下以jsx或js结尾的文件。 eslint --ext .jsx,.js --fix lib/将检测并尽可能地修复lib目录下以jsx或js结尾的文件,剩下的未修复的问题才会输出。 配合Prettier使用^_^配合prettier使你的代码更简洁! eslint-plugin-prettier导出的recommanded配置打开了eslint-plugin-prettier和eslint-config-prettier,所以你只要把它加进去即可: 123{ "extends": ["plugin:prettier/recommended"]} 当然别忘了安装依赖: 1yarn add --dev eslint-plugin-prettier eslint-config-prettier 还有就是要配置成和eslint相符合的配置: 123456{ "prettier/prettier": ['error', { singleQuote: true, // 使用单引号 tabWidth: 4 //使用4个空格缩进 }]} 参考网址:https://prettier.io/docs/en/eslint.html 总结eslint对于规范项目代码有非常大的作用,以下是我自己项目中目前的配置: 123456789101112131415161718192021{ "extends": [ "airbnb-base", "plugin:react/recommended", "plugin:prettier/recommended" ], "parser": "babel-eslint", "rules": { "prettier/prettier": ['error', { singleQuote: true, tabWidth: 4 }], "class-methods-use-this": ['warn'], "import/no-extraneous-dependencies": ["error", {"devDependencies": true}], "indent": ['error', 4], "eol-last": ['off'], "import/no-named-as-default": ['off'], "comma-dangle": ['off'], "linebreak-style": ['off'] }} 参考网址 https://gist.github.com/yangfch3/bfc268ccacda29bb8cb3ece610cfc5ee https://medium.com/@sherryhsu/how-to-change-relative-paths-to-absolute-paths-for-imports-32ba6cce18a5]]></content>
<tags>
<tag>eslint</tag>
</tags>
</entry>
<entry>
<title><![CDATA[2018年学习计划(git篇)]]></title>
<url>%2F2018%2F09%2F28%2Fjavascript%2F2018%E5%B9%B4%E5%AD%A6%E4%B9%A0%E8%AE%A1%E5%88%92%EF%BC%88git%E7%AF%87%EF%BC%89%2F</url>
<content type="text"><![CDATA[直接快照,而非比较差异Git只关心文件数据的整体是否发生变化,而大多数其他系统则只关心文件内容的具体差异。Git更像是一个小型的文件系统,它保存每次更新时的文件快照。所以Git近乎所有的操作都可以在本地执行。 三种状态 已提交(committed):该文件已经被安全地保存在本地数据库中了 已修改(modified):修改了某个文件,但还没有提交保存 已暂存(staged):把已修改的文件放在下次提交时要保存的清单中 分支 创建分支 git checkout -b newBranch相当于: 12git branch newBranchgit checkout newBranch 删除分支 1git branch -d newBranch 基本合并 12git checkout mastergit merge newBranch 冲突的合并 任何包含未解决冲突的文件都会以未合并(unmerged)状态列出。可以手动或通过图形工具来解决冲突: 1git mergetool 冲突解决之后再add提交。 123git branch // 查看所有分支git branch -v // 查看所有分支最后一次commit的信息git branch --merged/--no-merged // 查看已合并/未合并分支 分支式的工作流程长期分支master分支中保留完整稳定的代码,develop或next分支专门用于后续的开发,仅用于稳定性测试,当然并不是说一定要绝对稳定,不过一旦进入某种稳定的状态,便可以把它合并到master里。 特性分支一个特性分支是指一个短期的,用来实现单一特性或与其相关工作的分支 远程分支当我们从远程仓库克隆时,git会自动将你仓库命名为origin,并下载其中所有数据,建立一个指向它的master分支,在本地命名为origin/master,你无法在本地更改其数据。 接着,git建立一个本地属于你的master分支,始于origin上master分支相同的位置。只要你不和服务器通讯,你的origin/master分支不会移动。 可以通过git fetch origin命令来进行同步。 推送如果你有个叫 serverfix 的分支需要和他人一起开发,可以运行 git push (远程仓库名) (分支名) : 1git push origin serverfix Git 自动把 serverfix 分支名扩展为 refs/heads/serverfix:refs/heads/serverfix ,意为“取出我的 serverfix 本地分支,推送它来更新远程仓库的 serverfix 分支” 也可以运行 git push origin serverfix:serferfix 来实现相同的效果,它的意思是“提取我的 serverfix 并更新到远程仓库的 serverfix” 值得注意的是,在 fetch 操作抓来新的远程分支之后,你仍然无法在本地编辑该远程仓库。换句话说,在本例中,你不会有一个新的 serverfix 分支,有的只是一个你无法移动的 origin/serverfix 指针。 如果要把该内容合并到当前分支,可以运行 git merge origin/serverfix 。如果想要一份自己的 serverfix 来开发,可以在远程分支的基础上分化出一个新的分支来: 1git checkout -b serverfix origin/serverfix 跟踪分支从远程分支检出的本地分支,称为跟踪分支(tracking branch)。跟踪分支是一种和远程分支有直接联系的本地分支。在跟踪分支里输入 git push ,Git 会自行推断应该向哪个服务器的哪个分支推送数据。反过来,在这些分支里运行 git pull 会获取所有远程索引,并把它们的数据都合并到本地分支中来。 在克隆仓库时,Git 通常会自动创建一个 master 分支来跟踪 origin/master 。这正是 git push 和 git pull 一开始就能正常工作的原因。当然,你可以随心所欲地设定为其它跟踪分支,比如 origin 上除了 master 之外的其它分支。刚才我们已经看到了这样的一个例子: git checkout -b [分支名] [远程名]/[分支名] 。如果你有 1.6.2 以上版本的 Git,还可以用 –track 选项简化: 123$ git checkout --track origin/serverfixBranch serverfix set up to track remote branch refs/remotes/origin/serverfix.Switched to a new branch "serverfix" 要为本地分支设定不同于远程分支的名字,只需在前个版本的命令里换个名字: 123$ git checkout -b sf origin/serverfixBranch sf set up to track remote branch refs/remotes/origin/serverfix.Switched to a new branch "sf" 删除远程分支123$ git push origin :serverfixTo git@github.com:schacon/simplegit.git- [deleted] serverfix 衍合把一个分支整合到另一个分支的办法有两种: merge(合并) 和 rebase(衍合) 衍合基础rebase命令可以把在一个分支里提交的改变在另一个分支里重放一遍: 12git checkout experimentgit rebase master 你可以经常使用衍合,确保在远程分支里的提交历史更清晰。比方说,某些项目自己不是维护者,但想帮点忙,就应该尽可能使用衍合:先在一个分支里进行开发,当准备向主项目提交补丁的时候,再把它衍合到origin/master 里面。这样,维护者就不需要做任何整合工作,只需根据你提供的仓库地址作一次快进,或者采纳你提交的补丁。 更多有趣的衍合1$ git rebase --onto master server client 检出 client 分支,找出 client 分支和 server 分支的共同祖先之后的变化,然后把它们在 master 上重演一遍 git rebase [主分支][特性分支]命令会先检出特性分支 server ,然后在主分支 master 上重演: 1git rebase master server 衍合的风险永远不要衍合那些已经推送到公共仓库的更新 如果把衍合当成一种在推送之前清理提交历史的手段,而且仅仅衍合那些永远不会公开的 commit,那就不会有任何问题。如果衍合那些已经公开的 commit,而与此同时其他人已经用这些 commit 进行了后续的开发工作,那你有得麻烦了。]]></content>
<tags>
<tag>git</tag>
</tags>
</entry>
<entry>
<title><![CDATA[koa2学习:路由]]></title>
<url>%2F2018%2F09%2F05%2Fnodejs%2Fkoa2%E5%AD%A6%E4%B9%A0%EF%BC%9A%E8%B7%AF%E7%94%B1%2F</url>
<content type="text"><![CDATA[参考网址:https://chenshenhai.github.io/koa2-note/ 原生koa2路由123456789101112131415161718192021222324252627282930313233343536373839404142434445const Koa = require('koa');const app = new Koa();const fs = require('fs');async function route(url) { let view = '404.html'; switch (url) { case '/': view = 'index.html'; break; case '/index': view = 'index.html'; break; case '/todo': view = 'todo.html'; break; case '/404': view = '404.html'; break; default: break; } return await render(view);}async function render(page) { return new Promise((resolve, reject) => { fs.readFile(`./views/${page}`, 'utf8', (err, data) => { if (err) { reject(err); }else{ resolve(data); } }); })}app.use(async (ctx) => { const {url} = ctx.request; ctx.body = await route(url);});app.listen(3000);console.log('[demo] start-quick is starting at port 3000'); 这里fs.readFile(..., 'utf8', ...)第二个参数是文件编码,如果不指定默认为raw buffer,刷新页面会变成下载。 koa-router中间件1234567891011121314151617181920212223242526272829const Router = require('koa-router');// 主页路由let home = new Router();home.get('/', async(ctx) => { ctx.body = ` <ul> <li><a href="/page/helloworld">/page/helloworld</a></li> <li><a href="/page/404">/page/404</a></li> </ul> `;});// 子页路由let page = new Router();page.get('/404', async(ctx) => { ctx.body = '404 page';}).get('/helloworld', async(ctx) => { ctx.body = 'helloworld page';});// 主路由let router = new Router();router.use('/', home.routes(), home.allowedMethods());router.use('/page', page.routes(), page.allowedMethods());app.use(router.routes()).use(router.allowedMethods()); 官方文档:https://github.com/alexmingoia/koa-router 获取请求参数GET请求参数获取在koa中,获取GET请求数据源头是koa中request对象中的query方法或querystring方法,query返回是格式化好的参数对象,querystring返回的是请求字符串,由于ctx对request的API有直接引用的方式,所以获取GET请求数据有两个途径。 POST请求参数获取对于POST请求的处理,koa2没有封装获取参数的方法,需要通过解析上下文context中的原生node.js请求对象req,将POST表单数据解析成query string(例如:a=1&b=2&c=3),再将query string 解析成JSON格式(例如:{“a”:”1”, “b”:”2”, “c”:”3”}) 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950function parsePostData(ctx) { return new Promise((resolve, reject) => { try { let postdata = ''; ctx.req.addListener('data', function (data) { postdata += data; }); ctx.req.addListener('end', function () { let parseData = parseQueryStr(postdata); resolve(parseData); }); }catch(err){ reject(err); } });}// 将POST请求参数字符串解析成JSONfunction parseQueryStr(queryStr) { let queryData = {} let queryStrList = queryStr.split('&') for (let [index, queryStr] of queryStrList.entries()) { let itemList = queryStr.split('=') queryData[itemList[0]] = decodeURIComponent(itemList[1]) } return queryData}app.use(async (ctx) => { if (ctx.url === '/' && ctx.method === 'GET') { ctx.body = ` <h1>koa2 request post demo</h1> <form method="POST" action="/"> <p>userName</p> <input name="userName" /><br/> <p>nickName</p> <input name="nickName" /><br/> <p>email</p> <input name="email" /><br/> <button type="submit">submit</button> </form> `; } else if (ctx.url === '/' && ctx.method === 'POST') { ctx.body = await parsePostData(ctx); } else { // 其他请求显示404 ctx.body = '<h1>404!!! o(╯□╰)o</h1>' }}); koa-bodyparser中间件1234567891011var Koa = require('koa');var bodyParser = require('koa-bodyparser');var app = new Koa();app.use(bodyParser());app.use(async ctx => { // the parsed body will store in ctx.request.body // if nothing was parsed, body will be an empty object {} ctx.body = ctx.request.body;}); 静态资源加载12345678910111213141516const serve = require('koa-static');const Koa = require('koa');const app = new Koa();// $ GET /package.jsonapp.use(serve('.'));// $ GET /hello.txtapp.use(serve('test/fixtures'));// or use absolute pathsapp.use(serve(__dirname + '/test/fixtures'));app.listen(3000);console.log('listening on port 3000'); session操作koa2原生功能只提供了cookie的操作,但是没有提供session操作。session就只用自己实现或者通过第三方中间件实现。在koa2中实现session的方案有一下几种 如果session数据量很小,可以直接存在内存中 如果session数据量很大,则需要存储介质存放session数据 数据库存储方案: 将session存放在MySQL数据库中 需要用到中间件: koa-session-minimal 适用于koa2 的session中间件,提供存储介质的读写接口 。 koa-mysql-session 为koa-session-minimal中间件提供MySQL数据库的session数据读写操作。 将sessionId和对于的数据存到数据库 将数据库的存储的sessionId存到页面的cookie中 根据cookie的sessionId去获取对于的session信息]]></content>
<tags>
<tag>koa2</tag>
</tags>
</entry>
<entry>
<title><![CDATA[2018年学习计划(babel篇)]]></title>
<url>%2F2018%2F09%2F04%2Fjavascript%2F2018%E5%B9%B4%E5%AD%A6%E4%B9%A0%E8%AE%A1%E5%88%92%EF%BC%88babel%E7%AF%87%EF%BC%89%2F</url>
<content type="text"><![CDATA[本篇基于最新版的babel@7。详细请查看官网:https://babeljs.io/docs/en 使用指南 安装必须的包: 12yarn add @babel/core @babel/cli @babel/preset-env --devyarn add @babel/polyfill 创建babel.config.js文件: 12345678910111213const presets = [ ["@babel/env", { targets: { edge: "17", firefox: "60", chrome: "67", safari: "11.1" }, useBuiltIns: "usage" }]];module.exports = { presets }; 运行编译命令: 1./node_modules/.bin/babel src --out-dir lib 当然也可以使用npx babel命令 polyfill@babel/polyfill最新的选项useBuiltIns包括三个选项: false:不为每个文件自动添加polyfill文件或者转换`import ‘@babel/polyfill’; entry:只会在入口引入@babel/polyfll将会自动转换成引用全部core-js模块; 12345678// Inimport "@babel/polyfill";// Outrequire("core-js/modules/es6.array.copy-within");require("core-js/modules/es6.array.fill");require("core-js/modules/es6.array.find");... usage:代码中不需要引入@babel/polyfll,它会自动引入需要的core-js模块: 123456789101112131415// Innew Promise(function (resolve) { setTimeout(function () { resolve(); }, 2000); }); // Outrequire("core-js/modules/es6.promise");new Promise(function (resolve) { setTimeout(function () { resolve(); }, 2000); }); 预设一下是比较常用的官方预设: @babel/preset-env @babel/preset-flow @babel/preset-react @babel/preset-typescript @babel/plugin-transform-runtime 当我们使用gernerator/async时自动引用@babel/runtime/regenerator 当需要使用垫片方法时,自动引用core-js中的相对应方法 自动移除babel所需的一些帮助方法,使用@babel/runtime/helpers替代,以防多次引用 Babel 转译后的代码要实现源代码同样的功能需要借助一些帮助函数,例如,{ [name]: ‘JavaScript’ } 转译后的代码如下所示: 123456789101112131415'use strict';function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj;}var obj = _defineProperty({}, 'name', 'JavaScript'); 类似上面的帮助函数 _defineProperty 可能会重复出现在一些模块里,导致编译后的代码体积变大。Babel 为了解决这个问题,提供了单独的包 babel-runtime 供编译模块复用工具函数。 启用插件 babel-plugin-transform-runtime 后,Babel 就会使用 babel-runtime 下的工具函数,转译代码如下: 123456'use strict';// 之前的 _defineProperty 函数已经作为公共模块 `babel-runtime/helpers/defineProperty` 使用var _defineProperty2 = require('babel-runtime/helpers/defineProperty');var _defineProperty3 = _interopRequireDefault(_defineProperty2);function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }var obj = (0, _defineProperty3.default)({}, 'name', 'JavaScript'); 除此之外,babel 还为源代码的非实例方法(Object.assign,实例方法是类似这样的 “foobar”.includes(“foo”))和 babel-runtime/helps 下的工具函数自动引用了 polyfill。这样可以避免污染全局命名空间,非常适合于 JavaScript 库和工具包的实现。例如 const obj = {}, Object.assign(obj, { age: 30 }); 转译后的代码如下所示: 123456789'use strict';// 使用了 core-js 提供的 assignvar _assign = require('babel-runtime/core-js/object/assign');var _assign2 = _interopRequireDefault(_assign);function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }var obj = {};(0, _assign2.default)(obj, { age: 30}); 思考:babel-runtime 为什么适合 JavaScript 库和工具包的实现? 避免 babel 编译的工具函数在每个模块里重复出现,减小库和工具包的体积; 在没有使用 babel-runtime 之前,库和工具包一般不会直接引入 polyfill。否则像 Promise 这样的全局对象会污染全局命名空间,这就要求库的使用者自己提供 polyfill。这些 polyfill 一般在库和工具的使用说明中会提到,比如很多库都会有要求提供 es5 的 polyfill。在使用 @babel/runtime 后,库和工具只要在 package.json 中增加依赖 babel-runtime,交给 babel-runtime 去引入 polyfill 就行了; 总结: 具体项目还是需要使用 babel-polyfill,只使用 babel-runtime 的话,实例方法不能正常工作(例如 “foobar”.includes(“foo”)); JavaScript 库和工具可以使用 babel-runtime,在实际项目中使用这些库和工具,需要该项目本身提供 polyfill; 参考网址 https://survivejs.com/webpack/optimizing/minifying/ http://2ality.com/2015/12/babel6-loose-mode.html]]></content>
<tags>
<tag>babel</tag>
</tags>
</entry>
<entry>
<title><![CDATA[《svg精髓》学习笔记(路径)]]></title>
<url>%2F2018%2F08%2F23%2Fcss%2F%E3%80%8Asvg%E7%B2%BE%E9%AB%93%E3%80%8B%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%EF%BC%88%E8%B7%AF%E5%BE%84%EF%BC%89%2F</url>
<content type="text"><![CDATA[之前所有的基本形状都是元素的简写形式,path接受d属性(data)表示一系列路径。 moveto、lineto和closepath如下: 1<path d="M 10 10 L 100 10 L 100 100 L 10 100 Z"/> 表示,先移动到点(10, 10),然后lineto到点(100, 10),lineto到点(100, 100), lineto到点(10, 100),大写的Z表示自动关闭路径。 M和L大小写是有区别的,大写的M和L表示绝对坐标,小写的话表示相对于当前画笔的坐标。 路径的快捷方式使用H和V可以简写水平和垂直方式的画线: 简写形式 等价的冗长形式 效果 H 20 L 20 current_y 绘制一条到绝对位置(20, current_y)的线 h 20 L 20 0 绘制一条到(current_x + 20, current_y)的线 V 20 L current_x 20 绘制一条到绝对位置(current_x, 20)的线 v 20 L 0 20 绘制一条到(current_x, current_y + 20)的线 路径快捷方式表示法 可以在L或l后面放多组坐标 1<path d="M 10 10 L 30 50 60 80 90 20 Z"/> 所有不必要的空格都可以消除,比如字母后面的空格 椭圆弧椭圆弧以字母A为命令,接受7个参数: 点所在的椭圆的x半径和y半径 椭圆的x轴旋转角度x-axis-rotation large-arc-flag,如果需要圆弧角度小于180度,其为0;否则为1 sweep-flag,如果坐标需要以负角度绘制则为0,已正角度绘制则为1 终点的x坐标和y坐标 1<path d="M 100 100 A100,50 0 0,0 255 255" />]]></content>
<tags>
<tag>svg</tag>
</tags>
</entry>
<entry>
<title><![CDATA[《svg精髓》学习笔记(常用形状绘制)]]></title>
<url>%2F2018%2F08%2F16%2Fcss%2F%E3%80%8Asvg%E7%B2%BE%E9%AB%93%E3%80%8B%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%EF%BC%88%E5%B8%B8%E7%94%A8%E5%BD%A2%E7%8A%B6%E7%BB%98%E5%88%B6%EF%BC%89%2F</url>
<content type="text"><![CDATA[基本形状线段1<line x1="start-x" y1="start-y" x2="end-x" y2="end-y" /> 笔画特性笔画的尺寸、颜色和风格都会影响线段的表现,这些都可以通过style属性来指定。 stroke-width画布中的坐标系统网格线是无线细的,网格线位于画笔的正中心: 1<line x1="0" y1="0" x2="100" y2="100" style="stroke-width: 10; stroke: black" /> 大部分svg阅读器都会默认开启反锯齿功能,可以通过指定CSS属性shape-rendering的值来控制反锯齿特性,取值crispEdges(在元素上,或者整个svg上)会关闭反锯齿特性。 笔画颜色你可以通过以下几种方式指定画笔的颜色: 基本颜色 16进制 rgb/rgba currentColor:当前元素应用的css属性的color值 1<line x1="0" y1="0" x2="100" y2="100" style="stroke-width: 10; stroke: black" /> stroke-opacity控制线条的透明度。 stroke-dasharray如果你需要电线或者虚线,则需要使用这个属性,它的值由一系列数字构成,代表线的长度和空隙的长度,数字之间用逗号或空格隔开。数字的个数应为偶数。 12// 9个像素的虚线,5个像素的间隙stroke-dasharray: 9, 5 矩形矩形只需要指定左上角坐标x和y,以及宽度width和高度height即可。如果不指定fill,则默认用黑色填充,需要指定stroke,不然它不会绘制: 1<rect x="10" y="10" width="100" height="100" /> 圆角矩形圆角矩形可以通过指定x方向和y方向的圆角半径(rx和ry),rx的最大值是矩形宽度的一半,ry同理,如果只指定其中一个,则另外一个也相等。 圆和椭圆圆使用circle元素,通过指定中心点(x,y)和半径(r),椭圆使用ellipse需要另外同时指定x方向的半径和y方向的半径(rx, ry): 多边形元素可以用来画任意封闭图形,使用时只需为points属性指定一系列的x/y坐标点,并用逗号或空格隔开,指定坐标时不需要在最后指定返回起始坐标,因为图形总是封闭的,会自动回到起始点。 1<polygon points="15, 10 55, 10 45, 20 5, 20" style="fill: red; stroke: black" /> 填充边线交叉的多边形可以使用fill-rule: [nonzero | evenodd] 参考网址:https://blog.csdn.net/wjnf012/article/details/72875739 折线使用polyline元素,通过points属性指定点,但是和polygon不同的是,它不会自动封闭。 最好将polyline的fill设置为none,不然svg阅读器可能会自动填充黑色。 线帽和线连接当使用line或polyline时,可以为stroke-linecap:butt、round、square指定。round和square在起始位置都超过了真实位置,默认值butt则精确和起止位置对齐。 你也可以通过stroke-linejoin:miter(尖的)、round(圆的)、bevel(平的)属性来指定线段在图形棱角处交叉时的效果。]]></content>
<tags>
<tag>svg</tag>
</tags>
</entry>
<entry>
<title><![CDATA[《svg精髓》学习笔记(坐标系统)]]></title>
<url>%2F2018%2F08%2F15%2Fcss%2F%E3%80%8Asvg%E7%B2%BE%E9%AB%93%E3%80%8B%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%EF%BC%88%E5%9D%90%E6%A0%87%E7%B3%BB%E7%BB%9F%EF%BC%89%2F</url>
<content type="text"><![CDATA[在网页中使用svg 将svg作为图像:使用img元素包含svg文件 将svg作为css背景:使用background-image 将svg作为对象 内联svg 坐标系统视口文档打算使用的画布区域被称为视口。我们可以使用svg元素上的width和height属性确定视口的大小。属性值如果没有单位默认是px。 默认用户坐标原点(x=0, y=0)在阅读器的左上角 为视口指定用户坐标没有单位的数值都被视为像素,但是有时候我们并不想这样。为了实现这一效果,我们可以在svg元素上设置viewBox属性,这个属性的值由4个数值组成,他们分别代表想要叠加在视口上的用户坐标系统的最小x坐标、最小y坐标、宽度、高度。 因此,要在4厘米*5厘米的图纸上设置一个每厘米16个单位的坐标系统,要使用这个开始标记: 1<svg width="4cm" height="5cm" viewBox="0 0 64 80"> 保留宽高比如果viewBox和视口宽高比不一样,svg可以做三件事: 按较小的尺寸等比例缩放 按较大的尺寸等比例缩放并裁剪掉超出视口的部分 拉伸或挤压绘图以恰好填充新的视口(不保留宽高比) svg元素的preserveAspectRatio="align [meet | slice:裁剪]""属性允许我们指定被缩放的图像相对视口的对齐方式,以及它适配边缘还是要裁剪。 默认值:xMidYMid meet,它会缩小图像以适配可用的空间,并且使它水平和垂直居中。 <?xml> Cat 如果preserveAspectRatio值为none,则不保留宽高比。 嵌套坐标系统可以在文档中嵌套svg元素。默认新坐标是0,0,当然也可以通过x和y属性指定新原点。 参考网址:https://segmentfault.com/a/1190000007143300]]></content>
<tags>
<tag>svg</tag>
</tags>
</entry>
<entry>
<title><![CDATA[2018年学习计划(yarn篇)]]></title>
<url>%2F2018%2F08%2F13%2Fjavascript%2F2018%E5%B9%B4%E5%AD%A6%E4%B9%A0%E8%AE%A1%E5%88%92%EF%BC%88yarn%E7%AF%87%EF%BC%89%2F</url>
<content type="text"><![CDATA[这里只记一些感觉比较重要的内容。 什么是yarnYarn 是 Facebook, Google, Exponent 和 Tilde 开发的一款新的 JavaScript 包管理工具。它并没有试图完全取代 npm。Yarn 同样是一个从 npm 注册源获取模块的新的 CLI 客户端。注册的方式不会有任何变化 —— 你同样可以正常获取与发布包。它存在的目的是解决团队在使用 npm 面临的少数问题。当然,在 Node 版本断更替中,Npm 本身也在积极更新。 npm 与 yarn 常用命令对比 Npm Yarn 功能描述 npm install(npm i) yarn install(yarn) 根据 package.json 安装所有依赖 npm i –save [package] yarn add [package] 添加依赖包 npm i –save-dev [package] yarn add [package] –dev 添加依赖包至 devDependencies npm i -g [package] yarn global add [package] 进行全局安装依赖包 npm update –save yarn upgrade [package] 升级依赖包 npm uninstall [package] yarn remove [package] 移除依赖包 如何探查 npm 包如果你想从cli打开模块的主页,你可以这样做: 1npm home axios 要检查未决的问题或公开的路线图(如果有的话),你可以试试这个: 1npm bugs axios 另外,如果你只是想检查模块的 git 仓库,请输入: 1npm repo axios 几个感觉有用的yarn命令 yarn add -D/–dev 安装到devDependencies依赖中 yarn add 安装到dependencies依赖中 yarn add -E/–exact 安装包的精确版本,默认是安装包的主要版本里的最新版本。 比如说, yarn add foo@1.2.3 会接受 1.9.1 版,但是 yarn add foo@1.2.3 –exact 只会接受 1.2.3 版。 yarn cache dir 显示yarn缓存的目录,我电脑默认:C:\Users\Administrator\AppData\Local\Yarn\Cache\v2 通过yarn config set cache-folder <path>改变缓存目录,不然放在C盘太占空间了。通过yarn cache clean清空掉。 检测包完整性 yarn check 验证当前项目 package.json 里的依赖版本和 yarn 的 lock 文件是否匹配。 yarn check –integrity 验证当前项目 package.json 里包内容的版本和 hash 值是否与 yarn 的 lock 文件一致。 这有助于验证包依赖没有更改。 yarn check –verify-tree 检查node_modules下的dependencies依赖包是否存在和版本是否正确。 假设一个项目:package.json中依赖bootstrap3.x版本,假设: 更改package.json中bootstrap版本改成4.x,分别运行以上三句命令: yarn check: 123error Lockfile does not contain pattern: "bootstrap@4.x"error "bootstrap" is wrong version: expected "4.1.3", got "3.3.7"error Found 2 errors. - `yarn check --integrity`: 12error Lockfile does not contain pattern: "bootstrap@4.x"error Found 1 errors. - `yarn check --verify-tree`: 12error "bootstrap" is wrong version: expected "4.x", got "3.3.7"error Found 1 errors. 如上结果,可以看出`--integrity`主要是判断`package.json`是否被手动改坏掉了,`--verify-tree`用于判断`node_modules`下是否已经根据`package.json`更新了版本。 > 个人感觉:应该可以在项目启动或打包前运行`yarn check --verify-tree`,因为这个速度比较快;在多人合作开发中可以告知其他开发者要手动运行`yarn`更新依赖版本了。 yarn create [] 从一些以create-*开头的脚手架生成项目。比如:yarn create react-app my-app相当于yarn global add create-react-app && create-react-app my-app。 yarn help 查看命令帮助 yarn info [] 查看某个包的描述。yarn info <package> version查看某个包的所有可用版本。 yarn install –flat 仅为某一个包安装一个确定版本,如果有不同版本依赖将会让你选择。它将会在在package.json文件增加resolutions字段。 yarn outdated [package…] 列出一个或多个程序包依赖项的版本信息。 yarn upgrade [package]… –latest|-L [–caret | –tilde | –exact] [–pattern] --latest标识升级到最新版本。通过--caret、或--tilde指定使用什么类型的版本。比如:yarn upgrade --latest --caret升级boostrap: '3.x'到bootstrap: ^4.1.3。 参考 https://yarn.bootcss.com/docs/]]></content>
<tags>
<tag>javascript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[2018年学习计划(npm篇)]]></title>
<url>%2F2018%2F08%2F02%2Fjavascript%2F2018%E5%B9%B4%E5%AD%A6%E4%B9%A0%E8%AE%A1%E5%88%92%EF%BC%88npm%E7%AF%87%EF%BC%89%2F</url>
<content type="text"><![CDATA[主要温习下npm知识,这篇不会很详细介绍npm的使用,而是记下认为有用的知识点。 package.json name: 全部小写 单个单词,不能有空格 允许使用连字符和下划线 bugs: issue地址 指定依赖类型安装到生成依赖: 1npm install <package_name> [--save-prod] 安装到开发依赖: 1npm install <package_name> --save-dev 更新和检查更新更新依赖: 1npm update 检查过期包: 1npm outdated 更新包版本1npm version <update_type> update_type见下图。 语义化版本当你的项目要分享给其他人时,你应该从1.0.0版本开始(尽管有些项目没有遵循这一点)。 针对开发者 初次发布:1.0.0 修复bug等其他修改:1.0.1 增加不影响旧版本使用的功能时:1.1.0 增加不向下兼容的功能时:2.0.0 当升级版本号,下级的版本号要置为0 针对使用者 Patch releases: 1.0 or 1.0.x or ~1.0.4 Minor releases: 1 or 1.x or ^1.0.4 Major releases: * or x 比如我们项目依赖jquery版本^1.0.4,当我们npm update时,将会升级次要版本(Minor)到最新。 标签(dist-tag)标签是对语义化版本的补充,使用它们来组织和标记不同版本的包。除了比semver编号更具人性可读性之外,标签还允许发布者更有效地分发他们的包。 打标签1npm dist-tag add <pkg>@<version> [<tag>] 发布标签默认地,当我们运行npm publish时将会自动打上latest标签,如果你要发布其他名称的标签,比如: 1npm publish --tag beta 安装标签1npm install somepkg@beta 因为dist-tag与semver共享相同的名称空间,所以请避免使用可能导致冲突的标记名称。最佳做法是避免使用以数字或字母“v”开头的标签。 理解包和模块之间的关系Node.js和npm具有非常具体的包和模块定义,很容易混淆。我们将在这里讨论这些定义,使它们区别开来,并解释为什么某些默认文件以它们的方式命名。 概要 一个包是指被package.json描述的文件或目录。 一个模块是指可以被Node.js通过require加载的一些文件或目录。 什么是包 a) 包含package.json文件描述的程序的文件夹。 b) A gzipped tarball containing (a). c) A url that resolves to (b). d) A @ that is published on the registry with (c). e) A @ that points to (d). f) A that has a latest tag satisfying (e). g) A git url that, when cloned, results in (a). 什么是模块 package.json包含main字段的文件的文件夹。 包含index.js文件的文件夹。 一个JavaScript文件。 大多数npm包都是模块通常,加载Node.js程序中使用的npm包require,使它们成为模块。但是,并不要求npm包是一个模块! 某些包(例如cli包)仅包含可执行的命令行界面,并且不提供main在Node.js程序中使用的字段。这些包不是模块。 几乎所有的npm软件包都包含很多模块(因为它们加载的每个文件 require()都是一个模块)。 例如,如果您创建了一个文件,node_modules/foo.js然后有一个程序var f = require(‘foo.js’),它将加载该模块。但是,foo.js在这种情况下,它不是“包”,因为它没有package.json。 npm auditnpm audit 是 npm 6 新增的一个命令,可以允许开发人员分析复杂的代码并查明特定的漏洞。在刚刚发布的 npm 6.1.0 版本中,开发团队对该命令进行了完善。现在可使用 npm audit fix 子命令自动修复检测到的漏洞,而不必再自己进行跟踪和修复。 新版本针对 npm audit 还包括以下改进: npm audit –json —— 新的子命令,用于以 JSON 格式打印报告 npm install 摘要输出中将包含审计软件包的数量 npm-audit-report@1.2.1 —— 对审计安装和输出格式进行了大幅调整,新格式更加紧凑,更符合 CLI 视觉风格,同时仍然提供所需的重要信息。 此外,npm 6.1.0 同样扩展了 npm init 命令的功能,新增对 git 包的支持,即 npm init 。 故障排除 更新node版本 更新npm版本 清除缓存:npm cache clean –force window上权限错误:Error: ENOENT, stat 'C:\Users\<user>\AppData\Roaming\npm': 解决方法是确保C:\Users\\AppData\Roaming\npm存在并且可以使用普通用户帐户写入。 参考网址:https://docs.npmjs.com/troubleshooting/common-errors npm配置npm从以下来源获取配置,按优先级顺序: 命令行--foo bar将会设置配置项foo值为bar,如果不传值,则设为true;--flag1 --flag2 -- bar中flag1和flag2将被设为true,bar将作为命令行参数。 环境变量以npm_config_/NPM_CONFIG_开头的环境变量将会设置为配置项,如:npm_config_foo=bar,将设置配置项foo的值为bar,没有給值的将设为true。 npmrc文件这四个相关文件是: 当前项目下的配置文件(/path/to/my/project/.npmrc) 当前用户下的配置文件(默认是$HOME/.npmrc) 全局下的配置文件(默认是$PREFIX/etc/npmrc) npm的内置配置文件(/path/to/npm/npmrc) 查看npm当前配置运行npm config ls -l以查看npm内部的一组配置参数,如果未指定其他任何配置参数,则为默认值: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135; cli configslong = truemetrics-registry = "http://192.168.0.62:7001/"scope = ""user-agent = "npm/5.5.1 node/v8.9.1 win32 x64"; userconfig C:\Users\Administrator\.npmrc//192.168.0.62:7001/:always-auth = false//192.168.0.62:7001/:email = "liaoyf@sunsharing.com.cn"//192.168.0.62:7001/:username = "liaoyf"prefix = "D:\\nodejs"registry = "http://192.168.0.62:7001/"strict-ssl = false; globalconfig D:\nodejs\etc\npmrcmsvs_version = "2015"python = "C:\\Users\\Administrator\\.windows-build-tools\\python27\\python.exe"; builtin config undefined; prefix = "C:\\Users\\Administrator\\AppData\\Roaming\\npm" (overridden); default valuesaccess = nullallow-same-version = falsealso = nullalways-auth = falseauth-type = "legacy"bin-links = truebrowser = nullca = nullcache = "C:\\Users\\Administrator\\AppData\\Roaming\\npm-cache"cache-lock-retries = 10cache-lock-stale = 60000cache-lock-wait = 10000cache-max = nullcache-min = 10cafile = undefinedcert = nullcidr = nullcolor = truecommit-hooks = truedepth = nulldescription = truedev = falsedry-run = falseeditor = "notepad.exe"engine-strict = falsefetch-retries = 2fetch-retry-factor = 10fetch-retry-maxtimeout = 60000fetch-retry-mintimeout = 10000force = falsegit = "git"git-tag-version = trueglobal = falseglobal-style = falseglobalconfig = "D:\\nodejs\\etc\\npmrc"globalignorefile = "D:\\nodejs\\etc\\npmignore"group = 0ham-it-up = falseheading = "npm"https-proxy = nullif-present = falseignore-prepublish = falseignore-scripts = falseinit-author-email = ""init-author-name = ""init-author-url = ""init-license = "ISC"init-module = "C:\\Users\\Administrator\\.npm-init.js"init-version = "1.0.0"json = falsekey = nulllegacy-bundling = falselink = falselocal-address = undefinedloglevel = "notice"logs-max = 10; long = false (overridden)maxsockets = 50message = "%s"; metrics-registry = null (overridden)node-version = "8.9.1"offline = falseonload-script = nullonly = nulloptional = trueotp = 0package-lock = trueparseable = falseprefer-offline = falseprefer-online = falseprefix = "D:\\nodejs"production = falseprogress = trueproxy = nullread-only = falserebuild-bundle = true; registry = "https://registry.npmjs.org/" (overridden)rollback = truesave = truesave-bundle = falsesave-dev = falsesave-exact = falsesave-optional = falsesave-prefix = "^"save-prod = falsescope = ""script-shell = nullscripts-prepend-node-path = "warn-only"searchexclude = nullsearchlimit = 20searchopts = ""searchstaleness = 900send-metrics = falseshell = "C:\\Windows\\system32\\cmd.exe"shrinkwrap = truesign-git-tag = falsesso-poll-frequency = 500sso-type = "oauth"; strict-ssl = true (overridden)tag = "latest"tag-version-prefix = "v"timing = falsetmp = "C:\\Users\\ADMINI~1\\AppData\\Local\\Temp"umask = 0unicode = falseunsafe-perm = trueusage = falseuser = 0; user-agent = "npm/{npm-version} node/{node-version} {platform} {arch}" (overridden)userconfig = "C:\\Users\\Administrator\\.npmrc"version = falseversions = falseviewer = "browser" 查询简短语句123456789101112131415161718192021222324-v: --version-h, -?, --help, -H: --usage-s, --silent: --loglevel silent-q, --quiet: --loglevel warn-d: --loglevel info-dd, --verbose: --loglevel verbose-ddd: --loglevel silly-g: --global-C: --prefix-l: --long-m: --message-p, --porcelain: --parseable-reg: --registry-f: --force-desc: --description-S: --save-P: --save-prod-D: --save-dev-O: --save-optional-B: --save-bundle-E: --save-exact-y: --yes-n: --yes falsell and la commands: ls --long 当前包的配置12345678910// package.json{ "name" : "foo", "config" : { "port" : "8080" }, "scripts" : { "start" : "node server.js" } }// server.jshttp.createServer(...).listen(process.env.npm_package_config_port)// cmdnpm config set foo:port 80 测试您的.npmignore或files配置是否有效如果要在发布时仔细检查包中是否只包含您想要的文件,可以在npm pack本地运行命令,这将在工作目录中生成tarball,就像发布时一样。 链接包npm link旨在安装开发包并实时查看更改,而无需继续重新安装。(当然,您需要重新链接或npm rebuild -g更新已编译的包。) 发布之前:确保您的包安装和工作1npm install . -g 那会告诉你它正在发挥作用。如果您只想创建指向工作目录的符号链接包,请执行以下操作: 1npm link 要测试本地安装,请进入其他文件夹,然后执行以下操作: 12cd ../some-other-foldernpm install ../my-package 将其本地安装到该位置的node_modules文件夹中。 创建用户帐户1npm adduser 然后根据提示进行。 Semver语义化包的版本安装: 1npm install --save semver 示例: 12345678910const semver = require('semver') semver.valid('1.2.3') // '1.2.3'semver.valid('a.b.c') // nullsemver.clean(' =v1.2.3 ') // '1.2.3'semver.satisfies('1.2.3', '1.x || >=2.5.0 || 5.0.0 - 7.2.3') // truesemver.gt('1.2.3', '9.8.7') // falsesemver.lt('1.2.3', '9.8.7') // truesemver.valid(semver.coerce('v2')) // '2.0.0'semver.valid(semver.coerce('42.6.7.9.3-alpha')) // '42.6.7' package-lock.jsonpackage-lock.json为npm修改node_modules树或的任何操作自动生成package.json。它描述了生成的确切树,以便后续安装能够生成相同的树,而不管中间依赖性更新。]]></content>
<tags>
<tag>npm</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Rollup(入门学习)]]></title>
<url>%2F2018%2F08%2F01%2Frollup%2FRollup%EF%BC%88%E5%85%A5%E9%97%A8%E5%AD%A6%E4%B9%A0%EF%BC%89%2F</url>
<content type="text"><![CDATA[概述Rollup 是一个 JavaScript 模块打包器,Rollup 对代码模块使用新的标准化格式,这些标准都包含在 JavaScript 的 ES6 版本中,而不是以前的特殊解决方案,如 CommonJS 和 AMD。ES6 模块可以使你自由、无缝地使用你最喜爱的 library 中那些最有用独立函数,而你的项目不必携带其他未使用的代码。ES6 模块最终还是要由浏览器原生实现,但当前 Rollup 可以使你提前体验。 安装可以使用全局安装rollup: 1npm install --global rollup 摇树优化(Tree-Shaking)在使用CommonJS时,必须导入完整的库对象: 1234var utils = require('utils');var query = 'Rollup';utils.ajax('...').then(...); 但在使用ES6模块时,无需导入整个utils对象,我们只要导入我们所需的ajax即可: 1234import { ajax } from 'utils';var query = 'Rollup';utils.ajax('...').then(...); 因为 Rollup 只引入最基本最精简代码,所以可以生成轻量、快速,以及低复杂度的 library 和应用程序。因为这种基于显式的 import 和 export 语句的方式,它远比「在编译后的输出代码中,简单地运行自动 minifier 检测未使用的变量」更有效。 js各种模块分类: CommonJS:一个单独的文件就是一个模块,每个模块都是一个单独的作用域,在该模块内部定义的变量,无法被其他模块读取,除非定义为global对象的属性。模块只有一个出口,exports对象。 CommonJS2:commonjs 规范只定义了exports,而 module.exports是nodejs对commonjs的实现,实现往往会在满足规范前提下作些扩展,我们这里把这种实现称为了commonjs2,所以CommonJS2只是添加了module.exports而已。 AMD:需要用到对应的库函数,比如RequireJS。多个js文件可能有依赖关系,通过define定义模块,require加载模块 CMD:推崇一个模块一个文件,使用define定义模块,一个文件一个模块,所以经常就用文件名作为模块id;推崇依赖就近,所以一般不在define的参数中写依赖,在factory中写 ES:跟CommonJS类似,通过export命令显式指定输出的代码,再通过import命令输入,es module输出的是值的引用,而commonJS模块输出的是值的拷贝,不存在动态更新 UMD:在执行UMD规范时,会优先判断是当前环境是否支持AMD环境,然后再检验是否支持CommonJS环境,否则认为当前环境为浏览器环境12345678910111213141516171819> (function (root, factory) {> if (typeof define === 'function' && define.amd) {> // AMD> define(['jquery'], factory);> } else if (typeof exports === 'object') {> // Node, CommonJS-like> module.exports = factory(require('jquery'));> } else {> // Browser globals (root is window)> root.returnExports = factory(root.jQuery);> }> }(this, function ($) {> // methods> function myFunc(){};> > // exposed public method> return myFunc;> }));> 参考网址: https://www.cnblogs.com/chengdabelief/p/6547847.html https://www.cnblogs.com/cag2050/p/7562550.html 兼容性导入CommJSRollup 可以通过rollup-plugin-commonjs导入已存在的 CommonJS 模块,它会将 CommonJS 转换成 ES2015 模块的。 发布 ES6 模块为了确保你的 ES6 模块可以直接与「运行在 CommonJS(例如 Node.js 和 webpack)中的工具(tool)」使用,你可以使用 Rollup 编译为 UMD 或 CommonJS 格式,然后在 package.json 文件的 main 属性中指向当前编译的版本。如果你的 package.json 也具有 module 字段,像 Rollup 和 webpack 2 这样的 ES6 感知工具(ES6-aware tools)将会直接导入 ES6 模块版本。 Rollup可以通过pkg.module字段感知ES模块: 1234567> {> "name": "my-package",> "version": "0.1.0",> "main": "dist/my-package.umd.js",> "module": "dist/my-package.esm.js"> }> 当打包工具遇到我们的模块时: 如果它已经支持 pkg.module 字段则会优先使用 ES6 模块规范的版本,这样可以启用 Tree Shaking 机制。 如果它还不识别 pkg.module 字段则会使用我们已经编译成 CommonJS 规范的版本,也不会阻碍打包流程。 参考网址:https://loveky.github.io/2018/02/26/tree-shaking-and-pkg.module/ 常见问题Rollup 是用来构建库还是应用程序?Rollup 已被许多主流的 JavaScript 库使用,也可用于构建绝大多数应用程序。但是 Rollup 还不支持一些特定的高级功能,尤其是用在构建一些应用程序的时候,特别是代码拆分和运行时态的动态导入 dynamic imports at runtime. 如果你的项目中更需要这些功能,那使用 Webpack可能更符合你的需求。 针对app级别的应该使用Webpack,针对js库级别的应用应该使用Rollup 给npm包作者的建议:请使用pkg.module!在很长一段时间,使用JavaScript库就意味着你要冒很大的风险,这是因为你和库作者使用的模块系统可能不一样,所以这就需要你和库作者在模块系统的选择上必须达成一致意见。举个例子,假设你使用的是Browserify打包工具,但是库作者更喜欢AMD模块系统,所以在你构建之前,你必须把库作者的模块系统替换成自己项目所使用的模块系统。虽说Universal Module Definition(UMD)模块系统可以稍稍解决上述问题,不过,由于UMD模块系统不强制要求你使用什么的模块系统,(这也就意味着),你永远不知道你下一步使用的模块系统是哪一种。 ES2015模块颠覆了这一切,这是因为import以及export命令已经变成JavaScript语言的一部分啦。在不久的将来,模块系统的选择将会变得更加明确,不再模棱两可啦,而且所有的JavaScript代码都能够无缝对接。不幸的是,由于浏览器(大多数)以及Node还不支持import以及export命令,所以我们仍然需要对js文件使用UMD模块系统(如果你构建的文件只是用于Node,或许可以考虑CommonJS)。 通过给你项目的package.json文件增加(针对模块)入口”module”: “dist/my-library.es.js”配置,让你的项目同时支持UMD以及ES2015模块系统(的想法)变成了可能。这一点很重要,这是因为,Webpack和Rollup都会使用pkg.module来尽可能构建出高效代码。在某些情况下,Webpack以及Rollup甚至都能利用tree-shake特性来剔除项目中未使用的代码。 创建第一个bundle12345678// src/main.jsimport foo from './foo.js';export default function () { console.log(foo);}// foo.jsexport default 'hello world!'; 现在可以创建bundle了: 1rollup src/main.js -f cjs -f 选项(–output.format 的缩写)指定了所创建 bundle 的类型——这里是 CommonJS(在 Node.js 中运行)。由于没有指定输出文件,所以会直接打印在 stdout 中: 123456789'use strict';var foo = 'hello world!';var main = function () { console.log(foo);};module.exports = main; 也可以像下面一样将 bundle 保存为文件: 1rollup src/main.js -o bundle.js -f cjs (你也可以用 rollup src/main.js -f cjs > bundle.js,但是我们之后会提到,这种方法在生成 sourcemap 时灵活性不高。) 试着运行下面的代码: 1234node> var myBundle = require('./bundle.js');> myBundle();'hello world!' 恭喜,你已经用 Rollup 完成了第一个 bundle。 使用配置文件类似webpack.config.js,rollup使用rollup.config.js: 12345678// rollup.config.jsexport default { input: 'src/main.js', output: { file: 'bundle.js', format: 'cjs' }}; 我们用 –config 或 -c 来使用配置文件: 12rm bundle.js # so we can check the command works!rollup -c 同样的命令行选项将会覆盖配置文件中的选项: 1rollup -c -o bundle-2.js # `-o` is short for `--output.file` (注意 Rollup 本身会处理配置文件,所以可以使用 export default 语法——代码不会经过 Babel 等类似工具编译,所以只能使用所用 Node.js 版本支持的 ES2015 语法。) 如果愿意的话,也可以指定与默认 rollup.config.js 文件不同的配置文件: 12rollup --config rollup.config.dev.jsrollup --config rollup.config.prod.js 配置项预览: 12345678910111213141516171819202122232425262728293031323334353637383940// rollup.config.jsexport default { // 核心选项 input, // 必须 external, plugins, // 额外选项 onwarn, // danger zone acorn, context, moduleContext, legacy output: { // 必须 (如果要输出多个,可以是一个数组) // 核心选项 file, // 必须 format, // 必须 name, globals, // 额外选项 paths, banner, footer, intro, outro, sourcemap, sourcemapFile, interop, // 高危选项 exports, amd, indent strict },}; 你必须使用配置文件才能执行以下操作: 把一个项目打包,然后输出多个文件 使用Rollup插件, 例如 rollup-plugin-node-resolve 和 rollup-plugin-commonjs 。这两个插件可以让你加载Node.js里面的CommonJS模块 如果你想使用Rollup的配置文件,记得在命令行里加上–config或者-c @@2 使用插件类似webpack插件,比如要支持读取JSON格式文件,增加插件支持: 1234567891011// rollup.config.jsimport json from 'rollup-plugin-json';export default { input: 'src/main.js', output: { file: 'bundle.js', format: 'cjs' }, plugins: [ json() ]}; npm run build 执行 Rollup。结果如下: 123456789'use strict';var version = "1.0.0";var main = function () { console.log('version ' + version);};module.exports = main; 使用命令行命令行的参数: 1234567891011121314151617-i, --input 要打包的文件(必须)-o, --output.file 输出的文件 (如果没有这个参数,则直接输出到控制台)-f, --output.format [es] 输出的文件类型 (amd, cjs, es, iife, umd)-e, --external 将模块ID的逗号分隔列表排除-g, --globals 以`module ID:Global` 键值对的形式,用逗号分隔开 任何定义在这里模块ID定义添加到外部依赖-n, --name 生成UMD模块的名字-m, --sourcemap 生成 sourcemap (`-m inline` for inline map)--amd.id AMD模块的ID,默认是个匿名函数--amd.define 使用Function来代替`define`--no-strict 在生成的包中省略`"use strict";`--no-conflict 对于UMD模块来说,给全局变量生成一个无冲突的方法--intro 在打包好的文件的块的内部(wrapper内部)的最顶部插入一段内容--outro 在打包好的文件的块的内部(wrapper内部)的最底部插入一段内容--banner 在打包好的文件的块的外部(wrapper外部)的最顶部插入一段内容--footer 在打包好的文件的块的外部(wrapper外部)的最底部插入一段内容--interop 包含公共的模块(这个选项是默认添加的) 此外,还可以使用以下参数:1-h/--help 打印帮助文档。1-v/--version 打印已安装的Rollup版本号。1-w/--watch 监听源文件是否有改动,如果有改动,重新打包1--silent 不要将警告打印到控制台。 Javascript APIRollup 提供 JavaScript 接口那样可以通过 Node.js 来使用。你可以很少使用,而且很可能使用命令行接口,除非你想扩展 Rollup 本身,或者用于一些难懂的任务,例如用代码把文件束生成出来。 rollup.rollupThe rollup.rollup 函数返回一个 Promise,它解析了一个 bundle 对象,此对象带有不同的属性及方法,如下: 12345678910111213141516171819202122const rollup = require('rollup');// see below for details on the optionsconst inputOptions = {...};const outputOptions = {...};async function build() { // create a bundle const bundle = await rollup.rollup(inputOptions); console.log(bundle.imports); // an array of external dependencies console.log(bundle.exports); // an array of names exported by the entry point console.log(bundle.modules); // an array of module objects // generate code and a sourcemap const { code, map } = await bundle.generate(outputOptions); // or write the bundle to disk await bundle.write(outputOptions);}build(); 输入参数inputOptions 对象包含下列属性 (查看big list of options 以获得这些参数更详细的资料): 12345678910111213141516const inputOptions = { // 核心参数 input, // 唯一必填参数 external, plugins, // 高级参数 onwarn, cache, // 危险参数 acorn, context, moduleContext, legacy}; 输出参数outputOptions 对象包括下列属性 (查看 big list of options 以获得这些参数更详细的资料): 1234567891011121314151617181920212223const outputOptions = { // 核心参数 file, // 若有bundle.write,必填 format, // 必填 name, globals, // 高级参数 paths, banner, footer, intro, outro, sourcemap, sourcemapFile, interop, // 危险区域 exports, amd, indent strict}; rollup.watchRollup 也提供了 rollup.watch 函数,当它检测到磁盘上单个模块已经改变,它会重新构建你的文件束。 当你通过命令行运行 Rollup,并带上 –watch 标记时,此函数会被内部使用。 1234567891011121314151617const rollup = require('rollup');const watchOptions = {...};const watcher = rollup.watch(watchOptions);watcher.on('event', event => { // event.code 会是下面其中一个: // START — 监听器正在启动(重启) // BUNDLE_START — 构建单个文件束 // BUNDLE_END — 完成文件束构建 // END — 完成所有文件束构建 // ERROR — 构建时遇到错误 // FATAL — 遇到无可修复的错误});// 停止监听watcher.close(); 监听参数watchOptions 参数是一个你会从一个配置文件中导出的配置 (或一个配置数据)。 123456789const watchOptions = { ...inputOptions, output: [outputOptions], watch: { chokidar, include, exclude }}; 查看以上文档知道更多 inputOptions 和 outputOptions 的细节, 或查询 big list of options 关 chokidar, include 和 exclude 的资料。 Rollup与其他工具的集成npm依赖包Rollup并不像webpack那样,它不知道如何打破常规去处理这些依赖,因此我们需要一些配置: 1npm install --save-dev rollup-plugin-node-resolve 1234567891011// rollup.config.jsimport resolve from 'rollup-plugin-node-resolve';export default { input: 'src/main.js', output: { file: 'bundle.js', format: 'cjs' }, plugins: [ resolve() ]}; Babel1npm i -D rollup-plugin-babel 1234567891011121314151617// rollup.config.jsimport resolve from 'rollup-plugin-node-resolve';import babel from 'rollup-plugin-babel';export default { input: 'src/main.js', output: { file: 'bundle.js', format: 'cjs' }, plugins: [ resolve(), babel({ exclude: 'node_modules/**' // 只编译我们的源代码 }) ]}; 在Babel实际编译代码之前,需要进行配置。 创建一个新文件src/.babelrc: 12345678910{ "presets": [ ["latest", { "es2015": { "modules": false } }] ], "plugins": ["external-helpers"]} 这个设置有一些不寻常的地方。首先,我们设置”modules”: false,否则 Babel 会在 Rollup 有机会做处理之前,将我们的模块转成 CommonJS,导致 Rollup 的一些处理失败。 第三,我们将.babelrc文件放在src中,而不是根目录下。 这允许我们对于不同的任务有不同的.babelrc配置,比如像测试,如果我们以后需要的话 - 通常为单独的任务单独配置会更好。 ES模块语法导入导入的值不能重新分配,尽管导入的对象和数组可以被修改(导出模块,以及任何其他的导入,都将受到该修改的影响)。在这种情况下,它们的行为与const声明类似。 命名导入1import { something } from './module.js'; 1import { something as somethingElse } from './module.js'; 命名空间导入1import * as module from './module.js' 默认导入1import something from './module.js'; 空导入1import './module.js'; 这对于polyfills是有用的,或者当导入的代码的主要目的是与原型有关的时候。 导出命名导出导出以前声明的值: 12var something = true;export { something }; 在导出时重命名: 1export { something as somethingElse }; 声明后立即导出: 12// 这可以与 `var`, `let`, `const`, `class`, and `function` 配合使用export var something = true; 默认导出1export default something; 仅当源模块只有一个导出时,才建议使用此做法。 将默认和命名导出组合在同一模块中是不好的做法,尽管它是规范允许的。 绑定是如何工作的ES模块导出实时绑定,而不是值,所以值可以在最初根据这个示例导入后更改: 123456// incrementer.jsexport let count = 0;export function increment() { count += 1;} 12345678// main.jsimport { count, increment } from './incrementer.js';console.log(count); // 0increment();console.log(count); // 1count += 1; // Error — 只有 incrementer.js 可以改变这个值。 大选项列表(配置项)核心功能输入(input -i/–input)String 这个包的入口点 (例如:你的 main.js 或者 app.js 或者 index.js) 文件(file -o/–output.file)String 要写入的文件。也可用于生成 sourcemaps,如果适用 格式(format -f/–output.format)String 生成包的格式。 下列之一: amd – 异步模块定义,用于像RequireJS这样的模块加载器 cjs – CommonJS,适用于 Node 和 Browserify/Webpack es – 将软件包保存为ES模块文件 iife – 一个自动执行的功能,适合作为标签。(如果要为应用程序创建一个捆绑包,您可能想要使用它,因为它会使文件大小变小。) umd – 通用模块定义,以amd,cjs 和 iife 为一体 生成包名称(name -n/–name)String 变量名,代表你的 iife/umd 包,同一页上的其他脚本可以访问它。 1234567891011// rollup.config.jsexport default { ..., output: { file: 'bundle.js', format: 'iife', name: 'MyBundle' }};// -> var MyBundle = (function () {... 插件(plugins)插件对象 数组 Array (或一个插件对象) – 有关详细信息请参阅 插件入门。记住要调用导入的插件函数(即 commonjs(), 而不是 commonjs). 1234567891011// rollup.config.jsimport resolve from 'rollup-plugin-node-resolve';import commonjs from 'rollup-plugin-commonjs';export default { entry: 'main.js', plugins: [ resolve(), commonjs() ]}; 外链(external -e/–external)两者任一 Function 需要一个 id 并返回 true(外部引用)或 false(不是外部的引用), 或者 Array 应该保留在bundle的外部引用的模块ID。ID应该是: 外部依赖的名称 一个已被找到路径的ID(像文件的绝对路径) 12345678910// rollup.config.jsimport path from 'path';export default { ..., external: [ 'some-externally-required-library', path.resolve( './src/some-local-file-that-should-not-be-bundled.js' ) ]}; 当作为命令行参数给出时,它应该是以逗号分隔的ID列表: 1rollup -i src/main.js ... -e foo,bar,baz 全局模块(globals -g/–globals)Object 形式的 id: name 键值对,用于umd/iife包。例如:在这样的情况下… 123456789101112131415// rollup.config.jsexport default { ..., format: 'iife', name: 'MyBundle', globals: { jquery: '$' }};/*var MyBundle = (function ($) { // 代码到这里}(window.jQuery));*/. 高级功能(Advanced functionality)路径(paths)Function,它获取一个ID并返回一个路径,或者id:path对的Object。在提供的位置,这些路径将被用于生成的包而不是模块ID,从而允许您(例如)从CDN加载依赖关系: 12345678910111213141516171819202122232425// app.jsimport { selectAll } from 'd3';selectAll('p').style('color', 'purple');// ...// rollup.config.jsexport default { input: 'app.js', external: ['d3'], output: { file: 'bundle.js', format: 'amd', paths: { d3: 'https://d3js.org/d3.v4.min' } }};// bundle.jsdefine(['https://d3js.org/d3.v4.min'], function (d3) { d3.selectAll('p').style('color', 'purple'); // ...}); banner/footerString 字符串以 前置/追加 到文件束(bundle)。(注意:“banner”和“footer”选项不会破坏sourcemaps) 123456// rollup.config.jsexport default { ..., banner: '/* my-library version ' + version + ' */', footer: '/* follow me on Twitter! @rich_harris */'}; intro/outro1234export default { ..., intro: 'var ENVIRONMENT = "production";'}; 缓存(cache)Object 以前生成的包。使用它来加速后续的构建——Rollup只会重新分析已经更改的模块。 onwarnFunction 将拦截警告信息。如果没有提供,警告将被复制并打印到控制台。 警告是至少有一个code 和 message属性的对象,这意味着您可以控制如何处理不同类型的警告: 12345678910onwarn (warning) { // 跳过某些警告 if (warning.code === 'UNUSED_EXTERNAL_IMPORT') return; // 抛出异常 if (warning.code === 'NON_EXISTENT_EXPORT') throw new Error(warning.message); // 控制台打印一切警告 console.warn(warning.message);} sourcemap -m/–sourcemap如果 true,将创建一个单独的sourcemap文件。如果 inline,sourcemap将作为数据URI附加到生成的output文件中。 sourcemapFileString生成的包的位置。如果这是一个绝对路径,sourcemap中的所有源代码路径都将相对于它。 map.file属性是sourcemapFile的基本名称(basename),因为sourcemap的位置被假定为与bundle相邻 如果指定 output,sourcemapFile 不是必需的,在这种情况下,将通过给bundle输出文件添加 “.map” 后缀来推断输出文件名。 interopBoolean 是否添加’interop块’。默认情况下(interop:true),为了安全起见,如果需要区分默认和命名导出,则Rollup会将任何外部依赖项“default”导出到一个单独的变量。这通常只适用于您的外部依赖关系(例如与Babel)(如果您确定不需要它),则可以使用“interop:false”来节省几个字节。 危险区域(Danger zone)你可能不需要使用这些选项,除非你知道你在做什么! treeshake是否应用tree-shaking。建议您省略此选项(默认为treeshake:true),除非您发现由tree-shaking算法引起的bug,在这种情况下,请使用“treeshake:false”,一旦您提交了问题! acorn任何应该传递给Acorn的选项,例如allowReserved:true。 context默认情况下,模块的上下文 - 即顶级的this的值为undefined。在极少数情况下,您可能需要将其更改为其他内容,如 ‘window’。 moduleContext和options.context一样,但是每个模块可以是id: context对的对象,也可以是id => context函数。 legacy为了增加对诸如IE8之类的旧版环境的支持,通过剥离更多可能无法正常工作的现代化的代码,其代价是偏离ES6模块环境所需的精确规范。 exportsString 使用什么导出模式。默认为auto,它根据entry模块导出的内容猜测你的意图: default – 如果你使用 export default … 仅仅导出一个东西,那适合用这个 named – 如果你导出多个东西,适合用这个 none – 如果你不导出任何内容 (例如,你正在构建应用程序,而不是库),则适合用这个 default 和 named之间的区别会影响其他人如何使用文件束(bundle)。如果您使用default,则CommonJS用户可以执行此操作,例如 var yourLib = require( ‘your-lib’ ); 使用 named,用户可以这样做: var yourMethod = require( ‘your-lib’ ).yourMethod; 有点波折就是如果你使用named导出,但是同时也有一个default导出,用户必须这样做才能使用默认的导出: var yourMethod = require( ‘your-lib’ ).yourMethod;var yourLib = require( ‘your-lib’ )[‘default’];amd –amd.id and –amd.define Object 可以包含以下属性: amd.id String 用于 AMD/UMD 软件包的ID: // rollup.config.jsexport default { …, format: ‘amd’, amd: { id: ‘my-bundle’ }}; // -> define(‘my-bundle’, [‘dependency’], …amd.define String 要使用的函数名称,而不是 define: // rollup.config.jsexport default { …, format: ‘amd’, amd: { define: ‘def’ }}; // -> def([‘dependency’],… indentString 是要使用的缩进字符串,对于需要缩进代码的格式(amd,iife,umd)。也可以是false(无缩进)或true(默认 - 自动缩进) // rollup.config.jsexport default { …, indent: false}; stricttrue或false(默认为true) - 是否在生成的非ES6软件包的顶部包含’use strict’pragma。严格来说(geddit?),ES6模块始终都是严格模式,所以你应该没有很好的理由来禁用它。 Watch options这些选项仅在运行 Rollup 时使用 –watch 标志或使用 rollup.watch 时生效。 watch.chokidar一个 Boolean 值表示应该使用 chokidar 而不是内置的 fs.watch,或者是一个传递给 chokidar 的选项对象。 如果你希望使用它,你必须单独安装chokidar。 watch.include限制文件监控至某些文件: // rollup.config.jsexport default { …, watch: { include: ‘src/**’ }}; watch.exclude防止文件被监控: // rollup.config.jsexport default { …, watch: { exclude: ‘node_modules/**’ }};]]></content>
<tags>
<tag>rollup</tag>
</tags>
</entry>
<entry>
<title><![CDATA[前端调试技巧]]></title>
<url>%2F2018%2F07%2F25%2Fjavascript%2F%E5%89%8D%E7%AB%AF%E8%B0%83%E8%AF%95%E6%8A%80%E5%B7%A7%2F</url>
<content type="text"><![CDATA[前端调试说简单也简单,说难也难,我们今天就以chrome浏览器为主,探究常用的调试技巧。 控制台这里主要以控制台API为主: console.log —— 参考考虑如下代码: 123456var fruits = [{one: 1}, {two: 2}, {three: 3}];console.log('fruits before modification: ', fruits);console.log('fruits before modification - stringed: ', JSON.stringify(fruits));fruits.splice(1);console.log('fruits after modification: ', fruits);console.log('fruits after modification - stringed : ', JSON.stringify(fruits)) 调试对象或数组时,需要小心。我们看到fruits 修改之前包含3个对象,但它们不再存在。要在此特定时刻查看结果,请使用JSON.stringify以保持信息可见。当然,对于较大的对象,这可能不方便。别担心; 稍后,我们会找到更好的解决方案。 console.log —— 对象属性排序javascript中的object类型是一个无序属性的集合,你无法确定你的对象属性是否有序,浏览器以各种方式实现,很高兴在chrome如下打印是按正常顺序排序的: 1234567var letters = { z: 1, t: 2, k: 6};console.log('fruits', letters);console.log('fruits - stringify', JSON.stringify(letters)); console.assert(expression, message)当表达式结果为false时会抛出错误信息message,但是assert不会中断代码的执行,它可能有助于调试冗长而棘手的代码,或者发现在多次迭代后显示自身的错误。 console.count(label)运行多少次: 1234567891011121314for(var i =0; i <=3; i++){ console.count(i + ' Can I go with you?'); console.count('No, no this time');}// out0 Can I go with you?: 1No, no this time: 11 Can I go with you?: 1No, no this time: 22 Can I go with you?: 1No, no this time: 33 Can I go with you?: 1No, no this time: 4 console.table()以表格形式展示对象或数组。虽然好看,但是懒得用…… console.group() / console.groupEnd()分组显示: 123456789101112console.log('iteration');for(var firstLevel = 0; firstLevel<2; firstLevel++){ console.group('First level: ', firstLevel); for(var secondLevel = 0; secondLevel<2; secondLevel++){ console.group('Second level: ', secondLevel); for(var thirdLevel = 0; thirdLevel<2; thirdLevel++){ console.log('This is third level number: ', thirdLevel); } console.groupEnd(); } console.groupEnd();} console.trace()将堆栈跟踪打印到Console中: 12345678910function func1() { func2();}function func2() { func3();}function func3() { console.trace();}func1(); $_, $0 - $4$_返回最近的表达式的值。 $0-$4作为最近5个审查过的HTML元素的引用。 getEventListeners(object)返回在特定DOM元素上注册的事件侦听器。还有一种更方便的方法来设置侦听器,但我将在下一个教程中介绍它。 monitorEvents(DOMElement, [events]) / unmonitorEvents(DOMElement)如果触发了这些设置事件中的任何一个,我们将在控制台中获取信息。直到你取消监控事件。 复制数据在对象上单击鼠标右键并按复制,或将其存储为全局元素。然后,您可以在控制台中操作存储的元素。 控制台中的任何内容也可以使用复制copy(‘object-name’)。 控制台样式化1console.log('%c Truly hackers code!', 'background: #222; color: #bada55'); %d or %i — 整数 %f — 浮点型 %o — 可展开的DOM元素 %O — 可展开的js对象 %c — 样式化输出 HTML/CSS进入Elements选项卡的两种基本方法: 鼠标右键单击任意元素 > 审查元素 使用ctrl + shift + i打开DevTools并选择Elements选项卡 选项卡的左侧 首先,可以检查DOM树元素。要展开,请单击左侧的三角形。 通过单击鼠标右键,我们打开一些其他选项: Add attribute - 向所选元素添加新属性 Edit attribute - 编辑属性,仅在单击属性时可用 Edit as HTML - 通过选择此属性,您可以编辑整个元素; 也可用于复制要在其他地方使用的元素的一部分 复制: Copy outerHTML - 复制标记,包括标记本身和子元素 Copy selector - CSS选择器的副本(div> span> #id) Copy XPath - XPath的副本// * [@ id =“answer11208745-20”] / div / div [3 ] /时间,更多进一步阅读 Cut element - 剪切元素 Copy element - 复制元素和子元素 Hide element - 通过添加display:none;(cmd + H / ctrl + H) Delete element - 删除元素和子元素暂时隐藏元素,可以通过cmd + z反转 Expand all - 展开所有节点 Collapse all - 折叠所有节点 :active - 将元素设置为活动状态* :hover - 设置元素处于悬停状态* :focus - 设置元素处于焦点状态* :visited - 设置元素处于访问状态* Scroll into view - 使您立即转到网页上的选定元素 断点: subtree modification - 在子树修改上设置断点** (这个在一些需要点击时间才能显示的情况下,如果:react-select的下拉框,是非常有用的) attribute modification - 在属性修改时设置断点** node removal** 捕获节点截图: 在审查元素中,单击选中节点,按ctrl + shift + p打开命令菜单,输入node screenshot并选中Captrue node screenshot,即可对节点截图 参考网址 https://blog.pragmatists.com/how-to-debug-front-end-elements-d97da4cbc3ea https://developers.google.com/web/tools/chrome-devtools/]]></content>
<tags>
<tag>javascript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[css-transforms-3d]]></title>
<url>%2F2018%2F06%2F21%2Fcss%2Fcss-transforms-3d%2F</url>
<content type="text"><![CDATA[浏览器支持情况2D TransformsChrome Safari Firefox Opera IE Android iOSAny 3.1+ 3.5+ 10.5+ 9+ 4.1+ At least 4 3D TransformsChrome Safari Firefox Opera IE Android iOS10+ 4+ 12+ none 10+ 4.1+ 5+ 2D Transforms scale(): 缩放元素,包括:font-size,padding,height,width。它还提供scaleX和scaleY速记函数。 skewX() and skewY(): 元素向左或者向右倾斜,这个没有skew函数。 translate(): 位移元素 rotate(): 顺时针翻转元素 matrix(): 矩阵,这个函数可能不是专门用手写的,但将所有转换合并为一个。 perspective(): 不会影响元素本身,但会影响后代元素3D变换的变换,从而使它们都具有一致的深度透视图。 3D TransformsPerspective要激活3D空间,元素必须要有透视。可以有两种方式使用:使用transform: perspective(600px)或者perspective: 600px。 perspective决定3D效果的强度的值。把它看作从观察者到物体的距离。值越大,距离越远,视觉效果越不强烈。perspective: 2000px;产生微妙的3D效果,就像我们通过双筒望远镜从远处观看物体一样。perspective: 100px;产生巨大的3D效果,就像一只看到巨大物体的小昆虫。 可以在子元素上使用perspective或者父级元素,但是,当用于多个元素时,转换后的元素不会按预期排列。如果跨不同位置的元素使用相同的变换,则每个元素都有自己的视点。为了解决这个问题,请使用perspective父元素的属性,以便每个孩子可以共享相同的3D空间。参考https://desandro.github.io/3dtransforms/examples/perspective-02-children.html See the Pen css-3d-transforms-perspective by liaoyf (@liaoyf) on CodePen. 默认情况下,3D空间的视点位于中心。你可以用perspective-origin属性来改变视点的位置。 1perspective-origin: 25% 75%; 3D Transform functions rotateX( angle ) rotateY( angle ) rotateZ( angle ) translateZ( tz ) scaleZ( sz ) 还有几个速记变换函数需要所有三个维度的值: translate3d( tx, ty, tz ) scale3d( sx, sy, sz ) rotate3d( rx, ry, rz, angle ) 这些foo3d()转换函数还具有在Safari中触发硬件加速的好处,如果您正在编写适用于iOS或Safari的生产准备CSS,请务必使用这些foo3d()函数以获得最佳的渲染性能。]]></content>
<tags>
<tag>css</tag>
</tags>
</entry>
<entry>
<title><![CDATA[react-native版本0.55搭建]]></title>
<url>%2F2018%2F06%2F20%2Freact-native%2Freact-native%E7%89%88%E6%9C%AC0.55%E6%90%AD%E5%BB%BA%2F</url>
<content type="text"><![CDATA[react-native开发流程create-react-native-app方式使用此方式无需安装编译器、xcode或者Android Studio等。 1234npm install -g create-react-nateive-appcreate-react-native-app AwesomeProjectcd AwesomeProjectnpm start 启动后,控制台会输出一个二维码,接下来,你需要使用手机安装Expo应用程序,然后使用二维码登录即可在访问,如果你修改代码,应该也会热更新。 用native code构建在IOS上,需要安装一些必须软件: 123456brew install nodebrew install watchmannpm install -g react-native-clireact-native init AwesomeProjectcd AwesomeProjectreact-native run-ios 使用Command + R刷新。 注意:Xcode版本必须要>=8 react-native run-ios报错报错如下: 1234567891011An error was encountered processing the command (domain=NSPOSIXErrorDomain, code=2):Failed to install the requested applicationAn application bundle was not found at the provided path.Provide a valid path to the desired application bundle.Print: Entry, ":CFBundleIdentifier", Does Not Exist/development/misc/react/AwesomeProject/node_modules/promise/lib/done.js:10 throw err; ^Error: Command failed: /usr/libexec/PlistBuddy -c Print:CFBundleIdentifier build/Build/Products/Debug-iphonesimulator/AwesomeProject.app/Info.plistPrint: Entry, ":CFBundleIdentifier", Does Not Exist 泪崩。。。试用遍了网上大多数的解决方法均不行,浪费了好多宝贵时间,后来发现是xcode的版本问题,0.55版本要求xcode的版本要>= 8。 所以要更新xcode版本即可解决,记得更新完后重新react-native init项目。 结合antd-mobile使用安装antd-mobile-rn: 1yarn add antd-mobile-rn 使用: 1234567891011import React, { Component } from 'react';import { AppRegistry } from 'react-native';import Button from 'antd-mobile-rn/lib/button';class HelloWorldApp extends Component { render() { return <Button>Start</Button>; }}AppRegistry.registerComponent('HelloWorldApp', () => HelloWorldApp); 更多坑请点击查看 调用原生模块123import { NativeModules } from 'react-native';var CalendarManager = NativeModules.CalendarManager;CalendarManager.addEvent('Birthday Party', '4 Privet Drive, Surrey'); 详细介绍请点击查看 开发后安装问题在真机上测试iOS应用需要一台Mac电脑,同时还需要注册一个Apple ID。如果你需要把应用发布到App Store,那么你还需要去苹果开发者网站购买一个开发者账户(在自己手机上测试则不用) 在真机上访问开发服务器(packager)你可以在真机上访问开发服务器以快速测试和迭代。首先需要确保设备已使用usb连接至电脑,同时和电脑处在同一wifi网络内,然后在Xcode中选择你的设备作为编译目标(左上角运行按钮的右边),然后点击运行按钮即可。如果你需要在真机上启用调试功能,则需要打开RCTWebSocketExecutor.m文件,然后将其中的”localhost”改为你的电脑的IP地址,最后启用开发者菜单中的”Debug JS Remotely”选项。 开发步骤: 使用xcode打开项目 使用USB链接手机,并和电脑处于统一wifi信号下 选择右上角为你的手机,然后点击三角形运行 输入电脑登录密码,之后软件会安装在你的手机上 由于软件不被信任,所以要在通用->设备管理->点击信任 提示:摇晃设备就可以打开开发者菜单。 发布应用当你使用React Native做好一个漂亮的应用之后,一定跃跃欲试想要在App Store上发布了吧。发布的流程和其他iOS原生应用完全一样,除了以下一些注意事项。在App Store上发布应用首先需要编译一个“发布版本”(release)的应用。具体的做法是在Xcode中选择Product -> Scheme -> Edit Scheme (cmd + <),然后选择Run选项卡,将Build Configuration设置为release。 Release版本的应用会自动禁用开发者菜单,同时也会将js文件和静态图片打包压缩后内置到包中,这样应用可以在本地读取而无需访问开发服务器(同时这样一来你也无法再调试,需要调试请将Buiid Configuration再改为debug)。由于发布版本已经内置了js文件,因而也无法再通过开发服务器来实时更新。面向用户的热更新,请使用专门的热更新服务。编译完成后,你就可以打包提交到TestFlight进行内测,或是提交到App Store进行发布。相关流程较为复杂,不熟悉原生应用发布流程的同学请自行搜索学习。 App Transport SecurityApp Transport Security(简称ATS)是iOS 9中新增的一项安全特性。在默认设置下,只允许HTTPS的请求,而所有HTTP的请求都会被拒绝。详情可参考这篇帖子。 使用react-native开发的可行性根据Airbnb提供,具体参考:https://zhuanlan.zhihu.com/p/38288285?utm_source=com.alibaba.android.rimet&utm_medium=social: 优点 跨平台 (只有 0.2% 的平台特定代码) 统一的设计语言,同时还能为不同平台提供不同设计 React 的 scale 很好,生命周期比原生简单,声明式很好 迭代速度快(主要是 hot reloading 很快) 大量基础设施的投入值得(网络、国际化、复杂动画、设备信息、用户信息等等都是通过一个桥把原生 api 暴露给 RN 的。) 同时他们在这里也指出:他们并不相信在一个已有 app 上集成 RN 是一件简单事儿,必须要大量且持续地投入基础设施才行(说好的「满意的地方」呢) 性能 (尽管大家都担心但是其实基本没有问题) 不过首次渲染比较慢,导致不适合用作启动屏、deeplink,也增加了可交互时间(TTI),另外掉帧不好 debug(说好的「满意的地方」呢) Redux(好用,虽然废话太多) 背后是原生,一些曾经不确定能不能做的功能(Shared element transitions、动画库 Lottie、网络层、核心基础设施)发现都能做 静态分析(eslint,prettier,一些性能检测) 动画 JS/React 的开源生态 Flexbox (via Yoga) 有时候可以加上 Web 跨三端 缺点 RN 太不成熟 需要 fork RN JS 不行 (JS 没有类型不 scale,flow 不好用,TS 不好集成到 babel 和 metro) 不好重构(JS 没有类型无法静态分析,重构引起的错误不能在编译时被捕捉到) 咳,用我 FB 的新编译到 JS 语言大 ReasonML 啊……静态强类型 + 类型推断 + 自带不可变数据结构 + JS 友好语法 + 官方 React 支持,绝对 scale(咳扯远了 JSCore 在 iOS / Android 上不一致 (Android 上是 RN 自己 bundle 的),很难 debug 这种坑 RN 的开源库质量不行(因为太少人能精通所有平台了) 做功能时要回去搞基础设施(因为有的基础设施可能还没暴露给 RN) 奔溃监控(业内没方案,只能自己搞) 原生桥太难写,另外 JS 的类型太难预料(和强类型语言 interop 时)-RN 运行时的初始化太慢 首次渲染时间慢(需要从 主线程 -> JS -> Yoga -> 主线程) 应用体积 64 位 (因为 RN 不兼容的 issue 导致他们至今没法在 android 发布 64 位应用) 手势(iOS 和 Android 的手势不好统一,虽然他们搞了 react-native-gesture-handler) 长列表 升级 RN 有的时候非常麻烦 Accessibility (RN 的有 bug,又要 fork) 稀奇古怪的奔溃 安卓上的应用实例序列化问题 个人观点如果不需要开发那种非常复杂的功能,react-native社区提供的第三方库基本能满足开发需求;]]></content>
<tags>
<tag>react-native</tag>
</tags>
</entry>
<entry>
<title><![CDATA[jest测试工具学习(入门篇)]]></title>
<url>%2F2018%2F06%2F19%2Fjavascript%2Fjest%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7%E5%AD%A6%E4%B9%A0%EF%BC%88%E5%85%A5%E9%97%A8%E7%AF%87%EF%BC%89%2F</url>
<content type="text"><![CDATA[使用匹配器普通匹配器 toBe:测试值的方法是看是否精确匹配,相反的匹配器是.not.toBe; toEqual: 递归检查对象或数组的每个字段 Truthiness toBeNull toBeUndefined toBeDefine toBeTruthy: 匹配任何 if 语句为真 toBeFalsy: 匹配任何 if 语句为假 数字大多数的比较数字有等价的匹配器。 1234567test('two plus two', () => { const value = 2 + 2; expect(value).toBeGreaterThan(3); expect(value).toBeGreaterOrEqual(3.5); expect(value).toBeLessThan(5); expect(value).toBeLessOrEqual(4.5);}); 对于比较浮点数相等,使用 toBeCloseTo 而不是 toEqual,因为你不希望测试取决于一个小小的舍入误差。 12345test('两个浮点数字相加', () => { const value = 0.1 + 0.2; //expect(value).toBe(0.3); 这句会报错,因为浮点数有舍入误差 expect(value).toBeCloseTo(0.3); // 这句可以运行}); 字符串您可以检查对具有toMatch正则表达式的字符串︰ 1234567test('there is no I in team', () => { expect('team').not.toMatch(/I/);});test('but there is a "stop" in Christoph', () => { expect('Christoph').toMatch(/stop/);}); 数组你可以检查数组是否包含特定子项使用 toContain︰ 1234567891011const shoppingList = [ 'diapers', 'kleenex', 'trash bags', 'paper towels', 'beer',];test('购物清单(shopping list)里面有啤酒(beer)', () => { expect(shoppingList).toContain('beer');}); 例外如果你想要测试的特定函数抛出一个错误,在它调用时,使用 toThrow。 123456789101112function compileAndroidCode() { throw new ConfigError('you are using the wrong JDK');}test('compiling android goes as expected', () => { expect(compileAndroidCode).toThrow(); expect(compileAndroidCode).toThrow(ConfigError); // You can also use the exact error message or a regexp expect(compileAndroidCode).toThrow('you are using the wrong JDK'); expect(compileAndroidCode).toThrow(/JDK/);}); 测试异步代码回调使用单个参数调用 done,而不是将测试放在一个空参数的函数。 Jest会等done回调函数执行结束后,结束测试。 12345678test('the data is peanut butter', done => { function callback(data) { expect(data).toBe('peanut butter'); done(); } fetchData(callback);}); 如果 done()永远不会调用,这个测试将失败,这也是你所希望发生的。 Promises123456test('the data is peanut butter', () => { expect.assertions(1); return fetchData().then(data => { expect(data).toBe('peanut butter'); });}); 一定要返回 Promise - 如果你省略 return 语句,您的测试将在 fetchData 完成之前完成。如果你想要 Promise 被拒绝,使用 .catch 方法。 请确保添加 expect.assertions 来验证一定数量的断言被调用。 否则一个fulfilled态的 Promise 不会让测试失败︰ 1234test('the fetch fails with an error', () => { expect.assertions(1); return fetchData().catch(e => expect(e).toMatch('error'));}); .resolves / .rejects您还可以使用 .resolves 匹配器在您期望的声明,Jest 会等待这一 Promise 来解决。如果 Promise 被拒绝,则测试将自动失败。 1234test('the data is peanut butter', () => { expect.assertions(1); return expect(fetchData()).resolves.toBe('peanut butter');}); 如果你想要 Promise 被拒绝,使用 .catch 方法。 它参照工程 .resolves 匹配器。 如果 Promise 被拒绝,则测试将自动失败。 1234test('the fetch fails with an error', () => { expect.assertions(1); return expect(fetchData()).rejects.toMatch('error');}); Async/Await123456789test('the data is peanut butter', async () => { expect.assertions(1); await expect(fetchData()).resolves.toBe('peanut butter');});test('the fetch fails with an error', async () => { expect.assertions(1); await expect(fetchData()).rejects.toMatch('error');}); 钩子函数为多次测试重复设置123456789101112131415beforeEach(() => { return initializeCityDatabase();});afterEach(() => { return clearCityDatabase();});test('city database has Vienna', () => { expect(isCity('Vienna')).toBeTruthy();});test('city database has San Juan', () => { expect(isCity('San Juan')).toBeTruthy();}); 一次性设置在某些情况下,你只需要在文件的开头做一次设置。 当这种设置是异步行为时,可能非常恼人,你不太可能一行就解决它。 Jest 提供 beforeAll 和 afterAll 处理这种情况。 123456789101112131415beforeAll(() => { return initializeCityDatabase();});afterAll(() => { return clearCityDatabase();});test('city database has Vienna', () => { expect(isCity('Vienna')).toBeTruthy();});test('city database has San Juan', () => { expect(isCity('San Juan')).toBeTruthy();}); 作用域默认情况下,before 和 after 的块可以应用到文件中的每个测试。 此外可以通过 describe 块来将测试分组。 当 before 和 after 的块在 describe 块内部时,则其只适用于该 describe 块内的测试。 比如说,我们不仅有一个城市的数据库,还有一个食品数据库。我们可以为不同的测试做不同的设置 123456789101112131415161718192021222324252627// Applies to all tests in this filebeforeEach(() => { return initializeCityDatabase();});test('city database has Vienna', () => { expect(isCity('Vienna')).toBeTruthy();});test('city database has San Juan', () => { expect(isCity('San Juan')).toBeTruthy();});describe('matching cities to foods', () => { // Applies only to tests in this describe block beforeEach(() => { return initializeFoodDatabase(); }); test('Vienna <3 sausage', () => { expect(isValidCityFoodPair('Vienna', 'Wiener Schnitzel')).toBe(true); }); test('San Juan <3 plantains', () => { expect(isValidCityFoodPair('San Juan', 'Mofongo')).toBe(true); });}); 12345678910111213141516171819202122232425beforeAll(() => console.log('1 - beforeAll'));afterAll(() => console.log('1 - afterAll'));beforeEach(() => console.log('1 - beforeEach'));afterEach(() => console.log('1 - afterEach'));test('', () => console.log('1 - test'));describe('Scoped / Nested block', () => { beforeAll(() => console.log('2 - beforeAll')); afterAll(() => console.log('2 - afterAll')); beforeEach(() => console.log('2 - beforeEach')); afterEach(() => console.log('2 - afterEach')); test('', () => console.log('2 - test'));});// 1 - beforeAll// 1 - beforeEach// 1 - test// 1 - afterEach// 2 - beforeAll// 1 - beforeEach// 2 - beforeEach// 2 - test// 2 - afterEach// 1 - afterEach// 2 - afterAll// 1 - afterAll desribe和test块的执行顺序Jest在执行任何实际测试之前执行所有描述处理程序的测试文件。这是在before*和after*的处理程序中进行设置和拆卸的另一个原因,而不是在描述块中,一旦描述块完成,默认情况下,Jest将按照它们在收集阶段遇到的顺序依次运行所有测试,等待每个测试完成并在继续之前进行整理。 12345678910111213141516171819202122232425262728293031323334353637describe('outer', () => { console.log('describe outer-a'); describe('describe inner 1', () => { console.log('describe inner 1'); test('test 1', () => { console.log('test for describe inner 1'); expect(true).toEqual(true); }); }); console.log('describe outer-b'); test('test 1', () => { console.log('test for describe outer'); expect(true).toEqual(true); }); describe('describe inner 2', () => { console.log('describe inner 2'); test('test for describe inner 2', () => { console.log('test for describe inner 2'); expect(false).toEqual(false); }); }); console.log('describe outer-c');});// describe outer-a// describe inner 1// describe outer-b// describe inner 2// describe outer-c// test for describe inner 1// test for describe outer// test for describe inner 2 通用建议如果测试失败,第一件要检查的事就是,当仅运行这条测试时,它是否仍然失败。 在 Jest 中很容易地只运行一个测试 — — 只需暂时将 test 命令更改为 test.only: 1234567test.only('this will be the only test that runs', () => { expect(true).toBe(false);});test('this test will not run', () => { expect('A').toBe('A');}); 如果你有一个测试,当它作为一个更大的用例中的一部分时,经常运行失败,但是当你单独运行它时,并不会失败,所以最好考虑其他测试对这个测试的影响。 通常可以通过修改 beforeEach 来清除一些共享的状态来修复这种问题。 如果不确定某些共享状态是否被修改,还可以尝试在 beforeEach 中 log 数据来 debug。 Mock函数1234567891011121314const mockCallback = jest.fn();forEach([0, 1], mockCallback);// The mock function is called twiceexpect(mockCallback.mock.calls.length).toBe(2);// The first argument of the first call to the function was 0expect(mockCallback.mock.calls[0][0]).toBe(0);// The first argument of the second call to the function was 1expect(mockCallback.mock.calls[1][0]).toBe(1);// The return value of the first call to the function was 42expect(mockCallback.mock.results[0].value).toBe(42); .mock 属性123456789const myMock = jest.fn();const a = new myMock();const b = {};const bound = myMock.bind(b);bound();console.log(myMock.mock.instances);// > [ <a>, <b> ] 123456789101112131415161718// The function was called exactly onceexpect(someMockFunction.mock.calls.length).toBe(1);// The first arg of the first call to the function was 'first arg'expect(someMockFunction.mock.calls[0][0]).toBe('first arg');// The second arg of the first call to the function was 'second arg'expect(someMockFunction.mock.calls[0][1]).toBe('second arg');// The return value of the first call to the function was 'return value'expect(someMockFunction.mock.results[0].value).toBe('return value');// This function was instantiated exactly twiceexpect(someMockFunction.mock.instances.length).toBe(2);// The object returned by the first instantiation of this function// had a `name` property whose value was set to 'test'expect(someMockFunction.mock.instances[0].name).toEqual('test'); Mock 的返回值1234567891011const myMock = jest.fn();console.log(myMock());// > undefinedmyMock .mockReturnValueOnce(10) .mockReturnValueOnce('x') .mockReturnValue(true);console.log(myMock(), myMock(), myMock(), myMock());// > 10, 'x', true, true 123456789101112const filterTestFn = jest.fn();// Make the mock return `true` for the first call,// and `false` for the second callfilterTestFn.mockReturnValueOnce(true).mockReturnValueOnce(false);const result = [11, 12].filter(filterTestFn);console.log(result);// > [11]console.log(filterTestFn.mock.calls);// > [ [11], [12] ] Mocking Modules123456789101112131415// users.test.jsimport axios from 'axios';import Users from './users';jest.mock('axios');test('should fetch users', () => { const resp = {data: [{name: 'Bob'}]}; axios.get.mockResolvedValue(resp); // or you could use the following depending on your use case: // axios.get.mockImplementation(() => Promise.resolve(resp)) return Users.all().then(users => expect(users).toEqual(resp.data));}); Mock 实现1234567const myMockFn = jest.fn(cb => cb(null, true));myMockFn((err, val) => console.log(val));// > truemyMockFn((err, val) => console.log(val));// > true 12345678910111213// foo.jsmodule.exports = function() { // some implementation;};// test.jsjest.mock('../foo'); // this happens automatically with automockingconst foo = require('../foo');// foo is a mock functionfoo.mockImplementation(() => 42);foo();// > 42 12345678910const myMockFn = jest .fn() .mockImplementationOnce(cb => cb(null, true)) .mockImplementationOnce(cb => cb(null, false));myMockFn((err, val) => console.log(val));// > truemyMockFn((err, val) => console.log(val));// > false 1234567onst myMockFn = jest .fn(() => 'default') .mockImplementationOnce(() => 'first call') .mockImplementationOnce(() => 'second call');console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn());// > 'first call', 'second call', 'default', 'default' 1234567891011const myObj = { myMethod: jest.fn().mockReturnThis(),};// is the same asconst otherObj = { myMethod: jest.fn(function() { return this; }),}; Mock 名称12345const myMockFn = jest .fn() .mockReturnValue('default') .mockImplementation(scalar => 42 + scalar) .mockName('add42'); 自定义匹配器1234567891011// 这个 mock 函数至少被调用一次expect(mockFunc).toBeCalled();// 这个 mock 函数至少被调用一次,而且传入了特定参数expect(mockFunc).toBeCalledWith(arg1, arg2);// 这个 mock 函数的最后一次调用传入了特定参数expect(mockFunc).lastCalledWith(arg1, arg2);// 所有的 mock 的调用和名称都被写入了快照expect(mockFunc).toMatchSnapshot(); 1234567891011121314151617181920// 这个 mock 函数至少被调用一次expect(mockFunc.mock.calls.length).toBeGreaterThan(0);// 这个 mock 函数至少被调用一次,而且传入了特定参数expect(mockFunc.mock.calls).toContain([arg1, arg2]);// 这个 mock 函数的最后一次调用传入了特定参数expect(mockFunc.mock.calls[mockFunc.mock.calls.length - 1]).toEqual([ arg1, arg2,]);// 这个 mock 函数的最后一次调用的第一个参数是`42`// (注意这个断言的规范是没有语法糖的)expect(mockFunc.mock.calls[mockFunc.mock.calls.length - 1][0]).toBe(42);// 快照会检查 mock 函数被调用了同样的次数,// 同样的顺序,和同样的参数 它还会在名称上断言。expect(mockFunc.mock.calls).toEqual([[arg1, arg2]]);expect(mockFunc.mock.getMockName()).toBe('a mock name'); Jest平台工具您可以挑选Jest的特定功能,并将它们作为独立软件包使用。以下是可用软件包的列表: jest-changed-files用于识别git / hg存储库中已修改文件的工具。出口两个功能: getChangedFilesForRoots 返回一个承诺,解析为更改后的文件和回购对象。findRepos 返回一个承诺,解析为包含在指定路径中的一组存储库。 123456const {getChangedFilesForRoots} = require('jest-changed-files');// 打印出当前目录最后修改过的一组文件getChangedFilesForRoots(['./'], { lastCommit: true,}).then(result => console.log(result.changedFiles)); jest-diff用于可视化数据更改的工具 123456789const diff = require('jest-diff');const a = {a: {b: {c: 5}}};const b = {a: {b: {c: 6}}};const result = diff(a, b);// print diffconsole.log(result); jest-docblock12345678910111213141516const {parseWithComments} = require('jest-docblock');const code = `/** * This is a sample * * @flow */ console.log('Hello World!');`;const parsed = parseWithComments(code);// prints an object with two attributes: comments and pragmas.console.log(parsed); jest-get-type123456789101112const getType = require('jest-get-type');const array = [1, 2, 3];const nullValue = null;const undefinedValue = undefined;// prints 'array'console.log(getType(array));// prints 'null'console.log(getType(nullValue));// prints 'undefined'console.log(getType(undefinedValue)); jest-validate用于验证用户提交的配置的工具 123456789101112const {validate} = require('jest-validate');const configByUser = { transform: '<rootDir>/node_modules/my-custom-transform',};const result = validate(configByUser, { comment: ' Documentation: http://custom-docs.com', exampleConfig: {transform: '<rootDir>/node_modules/babel-jest'},});console.log(result); jest-worker用于任务并行化的模块 1234567// heavy-task.jsmodule.exports = { myHeavyTask: args => { // long running CPU intensive task. },}; 123456789101112131415// main.jsasync function main() { const worker = new Worker(require.resolve('./heavy-task.js')); // run 2 tasks in parallel with different arguments const results = await Promise.all([ worker.myHeavyTask({foo: 'bar'}), worker.myHeavyTask({bar: 'foo'}), ]); console.log(results);}main(); pretty-format导出一个将任何JavaScript值转换为可读的字符串的函数。支持所有内置的JavaScript类型,并允许通过用户定义的插件扩展特定于应用程序的类型。 123456789const prettyFormat = require('pretty-format');const val = {object: {}};val.circularReference = val;val[Symbol('foo')] = 'foo';val.map = new Map([['prop', 'value']]);val.array = [-0, Infinity, NaN];console.log(prettyFormat(val));]]></content>
<tags>
<tag>jest</tag>
</tags>
</entry>
<entry>
<title><![CDATA[移动端下开发问题总结]]></title>
<url>%2F2018%2F06%2F14%2Fjavascript%2F%E7%A7%BB%E5%8A%A8%E7%AB%AF%E4%B8%8B%E5%BC%80%E5%8F%91%E9%97%AE%E9%A2%98%E6%80%BB%E7%BB%93%2F</url>
<content type="text"><![CDATA[ios端微信浏览器会给input框加上自带的样式,例如圆角和内阴影。是因为-webkit-appearance这个属性的影响解决:给input添加以下样式 12345input { appearance: none; -webkit-appearance: none; /* safari */ -moz-appearance: none;} ios微信端下z-index被transform属性影响去掉transform属性 iOS的弹性滑动属性-webkit-overflow-scrolling: touch会导致z-index属性失效去掉-webkit-overflow-scrolling???]]></content>
<tags>
<tag>javascript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[webpack学习——loose模式]]></title>
<url>%2F2018%2F05%2F29%2Fwebpack%2Fwebpack%E5%AD%A6%E4%B9%A0%E2%80%94%E2%80%94loose%E6%A8%A1%E5%BC%8F%2F</url>
<content type="text"><![CDATA[简要介绍Babel6的loose mode。 简介babel的松散模式将ES6代码转换为不遵循ES6语义的ES5代码。 两种模式babel中的许多插件有两种模式: 正常模式尽可能地遵循ECMAScript 6的语义。 松散模式产生更简单的ES5代码。 通常,建议不要使用松散模式。优点和缺点是: 优点:生成的代码可能更快,并且与旧引擎兼容。它也趋于更清洁,更“ES5式”。 缺点:当你从ES6转换到ES6时,你可能会遇到问题。这很少是值得冒险的。 打开松散模式es2015-loose预设是标准ES6预设的松散版本。它提供了一个概观如何打开某个插件的松散模式: 12345678module.exports = { plugins: [ ··· [require("babel-plugin-transform-es2015-classes"), {loose: true}], require("babel-plugin-transform-es2015-object-super"), ··· ]}; 示例:松散模式和正常模式输出区别让我们看看模式的区别如何影响到以下代码的输出: 123456789class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return `(${this.x}, ${this.y})`; }} 正常模式正常模式下,类的属性通过Object.defineProperty: 123456789101112131415161718192021222324252627282930313233343536373839404142"use strict";var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); // (A) } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; };})();function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); }}var Point = (function () { function Point(x, y) { _classCallCheck(this, Point); this.x = x; this.y = y; } _createClass(Point, [{ key: "toString", value: function toString() { return "(" + this.x + ", " + this.y + ")"; } }]); return Point;})(); 松散模式松散模式下,通过正常添加方法方式,更像es5写法: 123456789101112131415161718"use strict";function _classCallCheck(instance, Constructor) { ··· }var Point = (function () { function Point(x, y) { _classCallCheck(this, Point); this.x = x; this.y = y; } Point.prototype.toString = function toString() { // (A) return "(" + this.x + ", " + this.y + ")"; }; return Point;})();]]></content>
<tags>
<tag>webpack</tag>
<tag>babel</tag>
</tags>
</entry>
<entry>
<title><![CDATA[前端知识学习概要]]></title>
<url>%2F2018%2F05%2F02%2Fjavascript%2F%E5%89%8D%E7%AB%AF%E7%9F%A5%E8%AF%86%E5%AD%A6%E4%B9%A0%E6%A6%82%E8%A6%81%2F</url>
<content type="text"><![CDATA[了解web浏览器 浏览器如何渲染页面 浏览器的工作原理 What is a browser engine? What forces layout / reflow What Every Frontend Developer Should Know About Webpage Rendering]]></content>
<tags>
<tag>javascript</tag>
</tags>
</entry>
<entry>
<title><![CDATA[逃离async、await地狱]]></title>
<url>%2F2018%2F04%2F16%2Fjavascript%2F%E9%80%83%E7%A6%BBasync%E3%80%81await%E5%9C%B0%E7%8B%B1%2F</url>
<content type="text"><![CDATA[async/await已经帮助我们逃离了回调函数的地狱,但是我们又陷入了async/await地狱。 什么是async/await地狱当我们处理异步函数调用时,我们习惯在调用前添加一个await,然后一行接着一行,以同步的形式书写,但是这样就造成了我们的语句并不依赖前一个,你必须等待前一个语句的完成。 一个async/await地狱的示例考虑你要订购pizza和drink,代码如下: 123456789(async () => { const pizzaData = await getPizzaData() // async call const drinkData = await getDrinkData() // async call const chosenPizza = choosePizza() // sync call const chosenDrink = chooseDrink() // sync call await addPizzaToCart(chosenPizza) // async call await addDrinkToCart(chosenDrink) // async call orderItems() // async call})() 表面上看代码没有问题,但是这却不是有好的实践,因为它没有实现并发。 解释我们已经使用IIFE包装我们的代码,订购流程如下: 获取pizza列表. 获取drink列表. 从pizza列表中选择商品. 从drink中选择商品. 添加pizza到购物车. 添加drink到购物车. 下单. 那么,这样有什么问题吗?就像之前说的,每句都是依赖上一句,这里并没有并发:pizza和drink应该可以并发运行的,pizza相关的工作和drink相关的工作可以并行进行,但涉及相关工作的各个步骤需要按顺序(逐个)进行。 另一个糟糕的示例此JavaScript代码段将获取购物车中的商品并发出订购请求。 1234567async function orderItems() { const items = await getCartItems() // async call const noOfItems = items.length for(var i = 0; i < noOfItems; i++) { await sendRequest(items[i]) // async call }} 每次循环我们都必须等待sendRequest的完成,但是,我们并不需要等待。我们希望尽快发送所有请求,然后我们可以等待所有请求完成。 怎么逃离async/await地狱第一步:查找依赖于其他语句执行的语句在我们的第一个例子中,我们选择了一个披萨和一杯饮料。我们的结论是,在选择比萨饼之前,我们需要有比萨饼的名单。在将比萨加入购物车之前,我们需要选择比萨饼。所以我们可以说这三个步骤取决于对方。在完成前一件事之前我们不能做一件事。但如果我们更广泛地来看,我们发现选择比萨不依赖于选择饮料,所以我们可以并行选择它们。这是机器可以做得比我们更好的一件事。因此我们发现了一些依赖于其他语句执行的语句,有些则没有。 第二步:把相关的操作独立进行封装我们可以封装selectPizza()和selectDrink()。 第三步:并发运行相关操作函数然后我们利用事件循环同时运行这些异步非阻塞函数。这样做的两种常见模式是return Promise和Promise.all方法。 让我们来解决示例中的问题12345678910111213141516171819202122232425async function selectPizza() { const pizzaData = await getPizzaData() // async call const chosenPizza = choosePizza() // sync call await addPizzaToCart(chosenPizza) // async call}async function selectDrink() { const drinkData = await getDrinkData() // async call const chosenDrink = chooseDrink() // sync call await addDrinkToCart(chosenDrink) // async call}(async () => { const pizzaPromise = selectPizza() const drinkPromise = selectDrink() await pizzaPromise await drinkPromise orderItems() // async call})()// Although I prefer it this way (async () => { Promise.all([selectPizza(), selectDrink()]).then(orderItems) // async call})() 针对循环中sendRequest的等待问题,我们使用Promise.all来并发请求: 12345678910async function orderItems() { const items = await getCartItems() // async call const noOfItems = items.length const promises = [] for(var i = 0; i < noOfItems; i++) { const orderPromise = sendRequest(items[i]) // async call promises.push(orderPromise) // sync call } await Promise.all(promises) // async call}]]></content>
<tags>
<tag>es6</tag>
</tags>
</entry>
<entry>
<title><![CDATA[javascript设计模式-面向对象]]></title>
<url>%2F2018%2F04%2F06%2Fjavascript%2Fjavascript%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%2F</url>
<content type="text"><![CDATA[灵活的javascript假如有如下的校验表单方法: 1234567891011function checkName(){}function checkEmail(){}function checkPassword(){} 这样的声明可能会和别人写的冲突,我们可以把他们放在一个变量中,这样可以减少覆盖或者被覆盖的风险: 12345var CheckObject = { checkName: function(){...}, checkEmail: function(){...}, checkPassword: function(){...}}; 使用对象实现: 1234var CheckObject = function(){};CheckObject.prototype.checkName = function(){};CheckObject.prototype.checkEmail = function(){};CheckObject.prototype.checkPassword = function(){}; 这样别人就可以基于你的对象进行扩展。这样创建实例对象时,创建出来的对象所拥有的方法就只有一个。 1234var a = new CheckObject();a.checkName();a.checkEmail();a.checkPassword(); 这样有个问题,每次都要书写a对象,我们可以通过每个方法后面返回对象来支持链式调用: 12345678var CheckObject = function(){};CheckObject.prototype = { checkName: function(){ ... return this; }, ...}; 这样就可以通过a.checkName().checkEmail().checkPassword()来链式调用。 写的都是看到的创建一个类12345678var Book = function(id, bookname, price){ this.id = id; this.bookname = bookname; this.price = price;};Book.protype.display = function(){}; 使用类: 12var book = new Book(10, 'javascript begin', 120);book.display(); constructorconstructor是一个属性,当创建一个函数或者对象时都会为其创建一个原型对象prototype,在prototype对象中又会创建一个constructor属性,那么constructor属性指向的就是拥有整个原型对象的函数或对象。 私有属性、私有方法、特权方法等12345678910111213141516171819202122232425262728var Book = function(id, name, price){ // 私有属性 var num = 1; // 私有方法 function checkId(){ } // 特权方法 this.getName = function(){}; this.setName = function(){}; // 对象公有属性 this.id = id; this.name = name; this.price = price; // 对象公有方法 this.copy = function(){}; // 构造器 this.sertName(name); this.setPrice(price);};// 静态公有属性Book.isChinese = true;Book.prototype = { // 公有属性 isJSBook: true}; 有时候我们经常将类的静态变量通过闭包来实现: 1234567891011121314151617var Book = (function(){ var bookNum = 0; function checkBook(name){ } return function(newId, newName, newPrice){ var name, price; function checkID(id){} bookNum++; if(bookNum > 100){ throw new Error('Error'); } }})(); 闭包是有权访问另一个函数作用域中变量的函数,即在一个函数内部创建另一个函数。 继承12345678910111213141516function SuperClass(){ this.superValue = true;}SuperClass.prototype.getSuperValue = function(){ return this.superValue;};function SubClass(){ this.subValue = false;}// 继承父类SubClass.prototype = new SuperClass();SubClass.prototype.getSubValue = function(){ return this.subValue;}; 另一种方式: 123456789101112function SuperClass(){ this.superValue = true;}SuperClass.prototype.getSuperValue = function(){ return this.superValue;};function SubClass(){ // 继承父类 SuperClass.call(this); this.subValue = false;} 组合继承: 123456789101112131415function SuperClass(){ this.superValue = true;}SuperClass.prototype.getSuperValue = function(){ return this.superValue;};function SubClass(){ // 继承父类 SuperClass.call(this); this.subValue = false;}SubClass.prototype = new SuperClass(); 洁净的继承者-原型式继承: 123456function inheritObject(o){ function F(){} F.prototype = o; return new F();}]]></content>
<tags>
<tag>javascript</tag>
<tag>设计模式</tag>
</tags>
</entry>
<entry>
<title><![CDATA[react学习总结]]></title>
<url>%2F2018%2F04%2F02%2Freact%2Freact%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93%2F</url>
<content type="text"><![CDATA[协调(Reconciliation)当你使用React,在单一时间点你可以考虑render()函数作为创建React元素的树。在下一次状态或属性更新,render()函数将返回一个不同的React元素的树。React需要算出如何高效更新UI以匹配最新的树。 有一些解决将一棵树转换为另一棵树的最小操作数算法问题的通用方案。然而,树中元素个数为n,最先进的算法的时间复杂度为O(n 3 ) 。 若我们在React中使用,展示1000个元素则需要进行10亿次的比较。这操作太过昂贵,相反,React基于两点假设,实现了一个启发的O(n)算法: 两个不同类型的元素将产生不同的树。 通过渲染器附带key属性,开发者可以示意哪些子元素可能是稳定的。 实践中,上述假设适用于大部分应用场景。 对比算法当对比两棵树时,React首先比较两个根节点。根节点的type不同,其行为也不同。 不同类型的元素每当根元素有不同类型,React将卸载旧树并重新构建新树。 当树被卸载,旧的DOM节点将被销毁。组件实例会调用componentWillUnmount()。当构建一棵新树,新的DOM节点被插入到DOM中。组件实例将依次调用componentWillMount()和componentDidMount()。任何与旧树有关的状态都将丢弃。 这个根节点下所有的组件都将会被卸载,同时他们的状态将被销毁。例如,以下节点对比之后: 1234567<div> <Counter /></div><span> <Counter /></span> 这将会销毁旧的Counter并重装新的Counter。 相同类型的DOM元素当比较两个相同类型的React DOM元素时,React则会观察二者的属性,保持相同的底层DOM节点,并仅更新变化的属性。例如: 123<div className="before" title="stuff" /><div className="after" title="stuff" /> 通过比较两个元素,React知道仅更改底层DOM元素的className。 当更新style时,React同样知道仅更新变更的属性。例如: 123<div style={{color: 'red', fontWeight: 'bold'}} /><div style={{color: 'green', fontWeight: 'bold'}} /> 当在调整两个元素时,React知道仅改变color样式而不是fontWeight。 在处理完DOM元素后,React递归其子元素。 相同类型的组件元素当组件更新时,实例仍保持一致,以让状态能够在渲染之间保留。React通过更新底层组件实例的props来产生新元素,并在底层实例上依次调用componentWillReceiveProps()和componentWillUpdate()方法。 接下来,render()方法被调用,同时对比算法会递归处理之前的结果和新的结果。 权衡牢记协调算法的实现细节非常重要。React可能会在每次操作时渲染整个应用;而结果仍是相同的。为保证大多数场景效率能更快,我们通常提炼启发式的算法。 在目前实现中,可以表明一个事实,即子树在其兄弟节点中移动,但你无法告知其移动到哪。该算法会重渲整个子树。 由于React依赖于该启发式算法,若其背后的假设没得到满足,则其性能将会受到影响: 算法无法尝试匹配不同组件类型的子元素。若你发现两个输出非常相似的组件类型交替出现,你可能希望使其成为相同类型。实践中,我们并非发现这是一个问题。 Keys应该是稳定的,可预测的,且唯一的。不稳定的key(类似由Math.random()生成的)将使得大量组件实例和DOM节点进行不必要的重建,使得性能下降并丢失子组件的状态。 Context(React v16.3.0)创建生产者1React.createContext(/* some value */) 消费者123<Consumer> {value => /* render something based on the context value */}</Consumer> 示例123456789101112131415161718192021222324252627282930// Create a theme context, defaulting to light themeconst ThemeContext = React.createContext('light');function ThemedButton(props) { // The ThemedButton receives the theme from context return ( <ThemeContext.Consumer> {theme => <Button {...props} theme={theme} />} </ThemeContext.Consumer> );}// An intermediate componentfunction Toolbar(props) { return ( <div> <ThemedButton /> </div> );}class App extends React.Component { render() { return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> ); }} 在生命周期中,可以通过this.props访问: 12345678910111213141516171819202122232425class Button extends React.Component { componentDidMount() { // ThemeContext value is this.props.theme } componentDidUpdate(prevProps, prevState) { // Previous ThemeContext value is prevProps.theme // New ThemeContext value is this.props.theme } render() { const {theme, children} = this.props; return ( <button className={theme ? 'dark' : 'light'}> {children} </button> ); }}export default props => ( <ThemeContext.Consumer> {theme => <Button {...props} theme={theme} />} </ThemeContext.Consumer>); 创建消费HOC组件123456789101112131415const ThemeContext = React.createContext('light');// This function takes a component...export function withTheme(Component) { // ...and returns another component... return function ThemedComponent(props) { // ... and renders the wrapped component with the context theme! // Notice that we pass through any additional props as well return ( <ThemeContext.Consumer> {theme => <Component {...props} theme={theme} />} </ThemeContext.Consumer> ); };} 现在就可以简单用了: 12345function Button({theme, ...rest}) { return <button className={theme} {...rest} />;}const ThemedButton = withTheme(Button); ref引用到被包装组件(React v16.3.0)v16.3.0引入React.createRef和React.forwardRef新语法: 123456789101112131415161718// fancy-button.jsclass FancyButton extends React.Component { focus() { // ... } // ...}// Use context to pass the current "theme" to FancyButton.// Use forwardRef to pass refs to FancyButton as well.export default React.forwardRef((props, ref) => ( <ThemeContext.Consumer> {theme => ( <FancyButton {...props} theme={theme} ref={ref} /> )} </ThemeContext.Consumer>)); 1234567891011// app.jsimport FancyButton from './fancy-button';const ref = React.createRef();// Our ref will point to the FancyButton component,// And not the ThemeContext.Consumer that wraps it.// This means we can call FancyButton methods like ref.current.focus()<FancyButton ref={ref} onClick={handleClick}> Click me!</FancyButton>; FragmentsFragments 看起来像空的JSX 标签: 123456789render() { return ( <> <ChildA /> <ChildB /> <ChildC /> </> );} 另一种使用片段的方式是使用React.Fragment组件,React.Fragment组件可以在React对象上使用。这可能是必要的,如果你的工具还不支持JSX片段。注意在React中,<></>是<React.Fragment/>的语法糖。 12345678910class Columns extends React.Component { render() { return ( <React.Fragment> <td>Hello</td> <td>World</td> </React.Fragment> ); }} <></> 语法不能接受键值或属性。 如果你需要一个带key的片段,你可以直接使用<React.Fragment />。一个使用场景是映射一个集合为一个片段数组—例如:创建一个描述列表: 12345678910111213function Glossary(props) { return ( <dl> {props.items.map(item => ( // 没有`key`,将会触发一个key警告 <React.Fragment key={item.id}> <dt>{item.term}</dt> <dd>{item.description}</dd> </React.Fragment> ))} </dl> );} PortalsPortals 提供了一种很好的将子节点渲染到父组件以外的DOM 节点的方式: 1ReactDOM.createPortal(child, container) 第一个参数(child)是任何可渲染的React子元素,例如一个元素,字符串或碎片。第二个参数(container)则是一个DOM元素。 对于portal的一个典型用例是当父组件有overflow: hidden或z-index样式,但你需要子组件能够在视觉上“跳出(break out)”其容器。例如,对话框、hovercards以及提示框。 尽管portal可以被放置在DOM树的任何地方,但在其他方面其行为和普通的React子节点行为一致。如上下文特性依然能够如之前一样正确地工作,无论其子节点是否是portal,由于portal仍存在于React树中,而不用考虑其在DOM树中的位置。 这包含事件冒泡。一个从portal内部会触发的事件会一直冒泡至包含React树的祖先。假设如下HTML结构: 123456<html> <body> <div id="app-root"></div> <div id="modal-root"></div> </body></html> 在#app-root里的Parent组件能够捕获到未被捕获的从兄弟节点#modal-root冒泡上来的事件。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071// These two containers are siblings in the DOMconst appRoot = document.getElementById('app-root');const modalRoot = document.getElementById('modal-root');class Modal extends React.Component { constructor(props) { super(props); this.el = document.createElement('div'); } componentDidMount() { modalRoot.appendChild(this.el); } componentWillUnmount() { modalRoot.removeChild(this.el); } render() { return ReactDOM.createPortal( this.props.children, this.el, ); }}class Parent extends React.Component { constructor(props) { super(props); this.state = {clicks: 0}; this.handleClick = this.handleClick.bind(this); } handleClick() { // This will fire when the button in Child is clicked, // updating Parent's state, even though button // is not direct descendant in the DOM. this.setState(prevState => ({ clicks: prevState.clicks + 1 })); } render() { return ( <div onClick={this.handleClick}> <p>Number of clicks: {this.state.clicks}</p> <p> Open up the browser DevTools to observe that the button is not a child of the div with the onClick handler. </p> <Modal> <Child /> </Modal> </div> ); }}function Child() { // The click event on this button will bubble up to parent, // because there is no 'onClick' attribute defined return ( <div className="modal"> <button>Click</button> </div> );}ReactDOM.render(<Parent />, appRoot); 在父组件里捕获一个来自portal的事件冒泡能够在开发时具有不完全依赖于portal的更为灵活的抽象。例如,若你在渲染一个组件,父组件能够捕获其事件而无论其是否采用portal实现。 Error Boundaries错误边界是用于捕获其子组件树JavaScript异常,记录错误并展示一个回退的UI的React组件,而不是整个组件树的异常。错误组件在渲染期间,生命周期方法内,以及整个组件树构造函数内捕获错误。 如果一个类组件定义了一个名为componentDidCatch(error, info):的新方法,则其成为一个错误边界: 123456789101112131415161718192021class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } componentDidCatch(error, info) { // Display fallback UI this.setState({ hasError: true }); // You can also log the error to an error reporting service logErrorToMyService(error, info); } render() { if (this.state.hasError) { // You can render any custom fallback UI return <h1>Something went wrong.</h1>; } return this.props.children; }} 而后你可以像一个普通的组件一样使用: 123<ErrorBoundary> <MyWidget /></ErrorBoundary> componentDidCatch()方法机制类似于JavaScript catch {},但是针对组件。仅有类组件可以成为错误边界。实际上,大多数时间你仅想要定义一个错误边界组件并在你的整个应用中使用。 注意错误边界仅可以捕获其子组件的错误。错误边界无法捕获其自身的错误。如果一个错误边界无法渲染错误信息,则错误会向上冒泡至最接近的错误边界。这也类似于JavaScript中catch {}的工作机制。 componentDidCatch 参数error 是被抛出的错误。 info是一个含有componentStack属性的对象。这一属性包含了错误期间关于组件的堆栈信息。 12345678910111213//...componentDidCatch(error, info) { /* Example stack information: in ComponentThatThrows (created by App) in ErrorBoundary (created by App) in div (created by App) in App */ logComponentStackToMyService(info.componentStack);}//... Test Utilities未来计划 16.3:介绍别名为不安全的生命周期,UNSAFE_componentWillMount,UNSAFE_componentWillReceiveProps,和UNSAFE_componentWillUpdate。(旧的生命周期名称和新的别名都可以在此版本中使用。) 未来的16.x版本:启用弃用警告componentWillMount,componentWillReceiveProps和componentWillUpdate。(旧的生命周期名称和新的别名都可以在此版本中使用,但旧名称会记录DEV模式警告。) 17.0:删除componentWillMount,componentWillReceiveProps和componentWillUpdate。(从现在开始,只有新的“UNSAFE_”生命周期名称将起作用。)]]></content>
<tags>
<tag>react</tag>
</tags>
</entry>
<entry>
<title><![CDATA[create-react-app读后感]]></title>
<url>%2F2018%2F03%2F30%2Freact%2Fcreate-react-app%E8%AF%BB%E5%90%8E%E6%84%9F%2F</url>
<content type="text"><![CDATA[项目github地址https://github.com/facebook/create-react-app 一些概念npx npm 5.2+多出一个工具,npx 会帮你执行依赖包里的二进制文件。 旨在提高从npm注册表使用软件包的体验 ,npm使得它非常容易地安装和管理托管在注册表上的依赖项,npx使得使用CLI工具和其他托管在注册表。 举例来说,之前我们可能会写这样的命令: 12npm i -D webpack./node_modules/.bin/webpack -v 有了 npx,你只需要这样: 12npm i -D webpacknpx webpack -v 也就是说 npx 会自动查找当前依赖包中的可执行文件,如果找不到,就会去 PATH 里找。如果依然找不到,就会帮你安装! npx 甚至支持运行远程仓库的可执行文件,如: 12345678910$ npx github:piuccio/cowsay hellonpx: 1 安装成功,用时 1.663 秒 _______< hello > ------- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || 再比如 npx http-server 可以一句话帮你开启一个静态服务器!(第一次运行会稍微慢一些): 1234567$ npx http-servernpx: 23 安装成功,用时 48.633 秒Starting up http-server, serving ./Available on: http://127.0.0.1:8080 http://192.168.5.14:8080Hit CTRL-C to stop the server react-scripts bin目录下主要文件: 123456789101112131415161718192021222324252627282930313233343536373839404142#!/usr/bin/env node"use strict";const crypto = require('crypto');const path = require('path');// 跨平台调用系统命令const spawn = require('cross-spawn');const script = process.argv[2];const args = process.argv.slice(3);switch (script) { case 'build': case 'start': case 'upload': case 'test': { const result = spawn.sync( 'node', [require.resolve(path.join('../scripts', script))].concat(args), { stdio: 'inherit' } ); process.exit(result.status); break; } case 'pwhash': { let stdin = process.openStdin(); let data = ""; stdin.on('data', function(chunk) { data += chunk; }); stdin.on('end', function() { let hash = crypto.createHash('md5').update(data).digest('hex'); console.log(hash); }); break; } default: console.log(`Unknown script "${script}".`); break;} !/usr/bin/node是告诉操作系统执行这个脚本的时候,调用/usr/bin下的node解释器; !/usr/bin/env node这种用法是为了防止操作系统用户没有将node装在默认的/usr/bin路径里。当系统看到这一行的时候,首先会到env设置里查找node的安装路径,再调用对应路径下的解释器程序完成操作。 创建项目 node版本必须>=6 运行创建命令: 1npx create-react-app my-app 目录结构大致如下: 1234567891011121314151617my-app├── README.md├── node_modules├── package.json├── .gitignore├── public│ └── favicon.ico│ └── index.html│ └── manifest.json└── src └── App.css └── App.js └── App.test.js └── index.css └── index.js └── logo.svg └── registerServiceWorker.js 运行npm start或yarn start开启项目运行npm test或yarn test测试运行npm run build或者yarn run build打包项目 未完待续…]]></content>
<tags>
<tag>react</tag>
</tags>
</entry>
<entry>
<title><![CDATA[React遇到的坑.md]]></title>
<url>%2F2018%2F03%2F27%2Freact%2FReact%E9%81%87%E5%88%B0%E7%9A%84%E5%9D%91%2F</url>
<content type="text"><![CDATA[webpack编译后,代码中判断子组件名称功能失效。因为压缩后函数名称混淆,所以不能通过typeof child.type === 'function' && child.type.name === 'FormControl',而是应该通过child.type === FormControl 当input框输入值时输入框自动被失去了焦点,原因是在render函数里又用了render。 参考网址:https://stackoverflow.com/questions/25385578/nested-react-input-element-loses-focus-on-typing input值不会随着属性改变而改变123<Parent> <Child inputVal={this.state.inputVal}/></Parent> 当Child组件的value为null后input的值不会更改。所以一般要判断是否存在null值: 1<FormControl value={this.props.inputVal || ''}/> UglifyJs编辑后有些arguments无效升级uglifyjs-webpack-plugin和uglify-js: 12"uglify-js": "^3.1.6","uglifyjs-webpack-plugin": "^1.0.1", 上传文件不能上传同一张图片在上传完成后,设置$('input').val('')即可。 react中ref传递给页面组件时失效比如如下代码: 12let Component = require(`./${item.componentName}/index.js`).default;return <Component ref={c => this.a = c} key={i} {...props} updateCzsj={updateCzsj}/>; 这时,使用ref是无效的,这时因为包装了withRouter导致ref失效,应该使用wrappedComponentRef,获取时使用如下语句获取: 1this.a 在react组件中引入样式文件导致echarts宽度计算失败的bug比如下面: 12345678<div className="box"> <div className="left"> <Echart ...> </div> <div className="right"> <Echart ...> </div></div> box中的left、right各占50%(样式写在样式文件内),这时候渲染echarts的时候,因为样式文件还未生效,所以Echarts读取的left和right宽度是100%的。 这个是为什么呢? 修改react子组件123456789101112131415161718192021222324return React.Children.map(this.props.children, child => { if (!child) return child; if (typeof child.type === 'function' && child.type.name === 'Tr') { // 这边要修改children属性而不是直接返回它的children return React.cloneElement(child, { children: ( React.Children.map(child.props.children, subChild => { if (typeof subChild.type === 'function' && subChild.type.name === 'Label') { return React.cloneElement(subChild, { required: true }) } else if (typeof subChild.type === 'function' && subChild.type.name === 'Content') { return React.cloneElement(subChild, { children: [...subChild.props.children, textTip], validationState: validationState }) } }) ) }) } else { return child; }}); 使用react-css-modules后的问题使用react-css-modules后会导致antd的Picker不能滑动选择(setState会一直触发componentWillReceiveProps)。所以改成babel-plugin-react-css-modules,配置如下: 123456789101112131415plugins: [ [ 'react-css-modules', { exclude: 'node_modules', filetypes: { '.scss': { syntax: 'postcss-scss' } }, handleMissingStyleName: 'ignore', // 这个必须和css-loader配置的名称一致,不然会导致生成的类名不相对应 generateScopedName: '[local]___[hash:base64:5]' } ]]]]></content>
<tags>
<tag>react</tag>
</tags>
</entry>
<entry>
<title><![CDATA[git笔记]]></title>
<url>%2F2018%2F02%2F08%2Fgit%2Fgit%E7%AC%94%E8%AE%B0%2F</url>
<content type="text"><![CDATA[版本回退1git log [--pretty=oneline] 先查看下日志,查看各个版本的mode id 1git reset --hard (HEAD^ | modeid) HEAD表示当前版本,HEAD^表示上一个版本,HEAD~100表示之前的100个版本,也可以通过modeid来回退版本 1git reflog 记录你的每一次命令,在我们回退后又后悔的时候可以查看各个版本的mode id 工作区和暂存区 工作区:文件目录 暂存区:.git文件里的,git还为我们创建了第一个分支master,以及一个指向master的指针叫HEAD 前面讲了我们把文件往Git版本库里添加的时候,是分两步执行的: 第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区; 第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。 因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支,所以,现在,git commit就是往master分支上提交更改。 你可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。 管理修改git add . :他会监控工作区的状态树,使用它会把工作时的所有变化提交到暂存区,包括文件内容修改(modified)以及新文件(new),但不包括被删除的文件。 git add -u :他仅监控已经被add的文件(即tracked file),他会将被修改的文件提交到暂存区。add -u 不会提交新文件(untracked file)。(git add –update的缩写) git add -A :是上面两个功能的合集(git add –all的缩写) 撤销修改1git checkout -- file 丢弃工作区的修改,让这个文件回到最近一次git commit或git add时的状态;命令中的--很重要,没有--,就变成了“切换到另一个分支”的命令 1git reset HEAD file 丢弃暂存区的修改(unstage)重新放回工作区 当然如果已经提交到版本库或远程仓库,你就要回退版本了。 删除文件1git rm file 从版本库中删除文件,然后git commit就好了 1git checkout -- file 从版本库中获取误删的文件 远程仓库创建github远程仓库 创建SSH Key: 在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsa和id_rsa.pub这两个文件,如果已经有了,可直接跳到下一步。如果没有,打开Shell(Windows下打开Git Bash),创建SSH Key: 1$ ssh-keygen -t rsa -C "youremail@example.com" 生成两个文件:id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人 登陆GitHub,打开“Account settings”,“SSH Keys”页面: 然后,点“Add SSH Key”,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容使用命令git push -u origin master第一次推送master分支的所有内容;此后,每次本地提交后,只要有必要,就可以使用命令git push origin master推送最新修改; 分支 创建一个名为dev的分支: 1234git checkout -b dev//相当于以下两句git branch devgit checkout dev git checkout命令加上-b参数表示创建并切换,然后用git branch查看当前分支: 1234$ git branch* dev master *号表示当前分支。 合并dev分支到master: 12345$ git merge devUpdating d17efd8..fec145aFast-forward readme.txt | 1 + 1 file changed, 1 insertion(+) git merge命令用于合并指定分支到当前分支,注意到上面的Fast-forward信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。 当然,也不是每次合并都能Fast-forward,我们后面会讲其他方式的合并。 合并完成后,就可以放心地删除dev分支了: 12$ git branch -d devDeleted branch dev (was fec145a). 如果有冲突,需要手动解决。可以用git log命令查看分支的合并情况: 12345678$ git log --graph --pretty=oneline --abbrev-commit* 59bc1cb conflict fixed|\| * 75a857c AND simple* | 400b400 & simple|/* fec145a branch test... 分支管理策略使用Fast Forward模式下,删除分支后,分支信息也会没掉: 1$ git merge --no-ff -m "merge with no-ff" dev 禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息,–no-ff参数,表示禁用Fast forward 在实际开发中,我们应该按照几个基本原则进行分支管理: 首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活; 那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本; 你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。 Bug分支 stash功能: 可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作: 123$ git stashSaved working directory and index state WIP on dev: 6224937 add mergeHEAD is now at 6224937 add merge 现在,用git status查看工作区,就是干净的(除非有没有被Git管理的文件),因此可以放心地创建分支来修复bug。 首先确定要在哪个分支上修复bug,假定需要在master分支上修复,就从master创建临时分支: 12345$ git checkout masterSwitched to branch 'master'Your branch is ahead of 'origin/master' by 6 commits.$ git checkout -b issue-101Switched to a new branch 'issue-101' 修复完成后,切换到master分支,并完成合并,最后删除issue-101分支: 123456789$ git checkout masterSwitched to branch 'master'Your branch is ahead of 'origin/master' by 2 commits.$ git merge --no-ff -m "merged bug fix 101" issue-101Merge made by the 'recursive' strategy. readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)$ git branch -d issue-101Deleted branch issue-101 (was cc17032). 现在,是时候接着回到dev分支干活了! 12345$ git checkout devSwitched to branch 'dev'$ git status# On branch devnothing to commit (working directory clean) 工作区是干净的,刚才的工作现场存到哪去了?用git stash list命令看看: 12$ git stash liststash@{0}: WIP on dev: 6224937 add merge 工作现场还在,Git把stash内容存在某个地方了,但是需要恢复一下,有两个办法: 一是用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除; 另一种方式是用git stash pop,恢复的同时把stash内容也删了: 1$ git stash pop 再用git stash list查看,就看不到任何stash内容了,可以多次stash,恢复的时候,先用git stash list查看,然后恢复指定的stash,用命令: 1$ git stash apply stash@{0} Feature分支删除分支: 1git branch -d feature-vulcan 强制删除分支: 1git branch -D feature-vulcan 多人协作远程仓库的默认名称为origin; 抓取远程分支: 1git checkout -b dev origin/dev 设置本地dev分支和远程origin/dev分支的链接: 1git branch --set-upstream dev origin/dev 提交到远程分支: 1git push origin dev 如果远程分支和你冲突了,要先git pull把最新的提交从origin/dev中拿下来,本地解决冲突后,再推送 因此,多人协作的工作模式通常是这样: 首先,可以试图用git push origin branch-name推送自己的修改; 如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并; 如果合并有冲突,则解决冲突,并在本地提交; 没有冲突或者解决掉冲突后,再用git push origin branch-name推送就能成功! 如果git pull提示“no tracking information”,则说明本地分支和远程分支的链接关系没有创建,用命令git branch –set-upstream branch-name origin/branch-name。 这就是多人协作的工作模式,一旦熟悉了,就非常简单。 标签管理标签是版本库的一个快照。 创建标签打标签:git tag <name>; 查看所有标签:git tag; 在某个提交版本打标签:git tag <name> <commitId>; 查看标签:git show v0.9; 创建带有说明的标签,用-a指定标签名,-m指定说明文字:git tag -a v0.1 -m "version 0.1 released" 3432525; 通过-s用私钥签名一个标签:git tag -s v0.2 -m "signed version 0.2 released" fec145a; 操作标签命令git push origin <tagname>可以推送一个本地标签; 命令git push origin --tags可以推送全部未推送过的本地标签; 命令git tag -d <tagname>可以删除一个本地标签; 命令git push origin :refs/tags/<tagname>可以删除一个远程标签。 移除文件的版本控制如果你想把一个文件从版本控制中移除,并且保留本地的文件,首先需要把这个文件加入到gitignore文件中。然后执行以下命令就可以了。 1git rm file_path --cached 以上命令将file_path所代表的文件从版本控制中删除,并保留本地文件,此外还要进行commit操作才能将服务器端的文件删掉。如果想把一个文件夹从版本控制中删除并保留本地的文件,只需在上述命令的基础上加上-r参数,即 1git rm -r folder_path --cached 如果想把所有gitignore中的文件从版本控制中删除的话,需要执行以下两个命令,即先移除所有文件,再执行添加所有文件(这次会忽略gitignore中的文件)。 12git rm -r . --cachedgit add .]]></content>
<tags>
<tag>git</tag>
</tags>
</entry>
<entry>
<title><![CDATA[react-bootstrap读后感]]></title>
<url>%2F2018%2F02%2F02%2Freact%2Freact-bootstrap%E8%AF%BB%E5%90%8E%E6%84%9F%2F</url>
<content type="text"><![CDATA[基于react-bootstrap的0.32.1版本。 eslintextendseslint-config-airbnb使用了基于airbnb的js代码风格,详细了解: https://github.com/airbnb/javascript 中文版:https://github.com/yuche/javascript prettier格式化代码,使代码风格统一,详细了解: https://prettier.io/ plugins eslint-plugin-import:帮助校验是否正确导入模块 eslint-plugin-jsx-a11y:帮助校验React属性是否支持 eslint-plugin-prettier:prettier的eslint插件 eslint-plugin-react:React校验规则插件 babel插件 babel-plugin-transform-class-properties:转换class属性,如实例属性、类静态属性 babel-plugin-add-module-exports:省去require('module').default中的default babel-plugin-transform-export-extensions:支持export * as ns from 'mod'和export v from 'mod';语法 babel-plugin-transform-object-rest-spread:支持...运算符 babel-plugin-transform-runtime:使用babel-runtime替换,免去引用babel-polyfill造成的全局变量污染 参考文章: https://zhuanlan.zhihu.com/p/27777995 https://segmentfault.com/a/1190000009065987 preset babel-preset-env babel-preset-react 感觉挺有用的第三方模块 chai:Node.js的断言库 codecov:集成测试覆盖率工具 参考网址: https://www.jianshu.com/p/146c4769d4b1 colors:很容易设置颜色 cross-env:解决跨平台命令问题 enzyme:测试React fs-extra:为原生的fs模块添加promise支持 husky:Git hooks made easy sinon:Standalone test spies, stubs and mocks for JavaScript. Works with any unit testing framework. invariant:在开发环境下提示错误的快速写法 warning:facebook的warning,快速写警告 keycode:在键盘键码和键名之间进行转换,反之亦然 用到的测试框架 mocha:Node.js的测试框架 karama:JS测试框架 React好像推荐的是Jest框架,这个有待了解 编译提供的选择 dist:使用webpack提供的webpack方法进行编译; lib:使用babel的transform方法编译; es:使用babel的transform方法编译,和lib方法不同的是少了一个babel-plugin-add-module-exports插件;(这个文件应该没什么用吧,现在浏览器支持率这么低) bower:复制dist文件夹 用到的一些JS语法 柯里化函数: http://www.zhangxinxu.com/wordpress/2013/02/js-currying/ https://www.jianshu.com/p/f5033cec605e async/await: http://es6.ruanyifeng.com/#docs/generator React获取子组件或父组件属性: https://liaoyongfu.github.io/2017/08/02/react/react%E8%8E%B7%E5%8F%96%E7%88%B6%E7%BB%84%E4%BB%B6%E6%88%96%E5%AD%90%E7%BB%84%E4%BB%B6%E5%B1%9E%E6%80%A7/ 使用yarn替换npm运行yarn命令 忽略一些文件当别人安装时通过制定package.json的files字段,或者也可以制定.npmignore。以下文件一定会包含: package.json README CHANGES / CHANGELOG / HISTORY LICENSE / LICENCE NOTICE main字段指定的文件 相反,以下文件总会被忽略: .git CVS .svn .hg .lock-wscript .wafpickle-N .*.swp .DS_Store ._* npm-debug.log .npmrc node_modules config.gypi *.orig package-lock.json (use shrinkwrap instead) 在webstrom中使用eslint校验先安装eslint插件,配置Eslint Plugin: 然后打开file/settings/Keymap,搜索ESlint找到Fix ESLint Problems,右击选择Add Keyboard Shortcut, 键入ctrl + e(这里看个人习惯) 详细:https://github.com/idok/eslint-plugin gatsby报错:RootQueryType.allSitePage field type must be Output Type but got: SitePageConnection.运行yarn list gatsby发现会有多个依赖: 12npm remove graphqlnpm install gatsby 要统一一个版本的gatsby 报错"jsonName" of undefined目录中不能包含中文目录 Error: Schema must contain unique named types but contains multiple types named “JSON”暂时不知道为什么 有时候要删除.cache文件并重启才能生效,不知道为什么???hexo d -g报错[Windows] bash: /dev/tty: No such a device or addressTry adding this into your git config12[credential] helper = wincred or via console 1git config --global credential.helper wincred shareui文档构建首先在根目录下运行脚手架: 12gatsby new doc cd doc 然后修改gatsby的配置: 12345678910111213141516171819202122232425const path = require("path");module.exports = { siteMetadata: { title: "Shareui文档" }, plugins: [ "gatsby-plugin-react-helmet", { resolve: "gatsby-source-filesystem", options: { path: path.resolve(__dirname, "../src"), name: "source" } }, "gatsby-transformer-react-docgen", { resolve: "gatsby-transformer-remark", options: { plugins: ["gatsby-remark-prismjs"] } }, "gatsby-plugin-catch-links" ]}; 使用gatsby-source-filesystem可以方便查询本地文件的相关信息;使用gatsby-transformer-react-docgen方便查询组件信息。 在src/pages目录下新建alert.js(会自动生成alert页面): 123456789101112131415161718192021import React from "react";import Link from "gatsby-link";import PropTable from "../components/PropTable";const Page = ({data}) => ( <div> <h1>{data.componentMetadata && data.componentMetadata.description}</h1> <Link to="/">返回首页</Link> <PropTable metadata={data} /> </div>);export const query = graphql` query AlertQuery { componentMetadata(displayName: {eq: "Alert"}) { ...PropTable_metadata } }`;export default Page; 其中的片段PropTable_metadata如下: 12345678910111213141516171819export const metadataFragment = graphql` fragment PropTable_metadata on ComponentMetadata { displayName description props { name type { name value raw } defaultValue { value computed } required } }`; 为了列出所有的组件,我们使用gatsby-source-filesystem查询出在src目录下的所有组件,并添加到首页(doc/src/index.js): 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647import React from "react";import Link from "gatsby-link";const IndexPage = ({data}) => ( <div> <h3>基础组件</h3> <ol> {data.allFile.edges //先排序 .sort((a, b) => { if (a.node.name > b.node.name) { return 1; } else if (a.node.name < b.node.name) { return -1; } return 0; }) //排除index.js和utils下的文件 .map( (edge, index) => edge.node.relativeDirectory !== "utils" && edge.node.name !== "index" && ( <li key={index}> <Link to={`/${edge.node.name}/`}> {edge.node.name} </Link> </li> ) )} </ol> </div>);export const query = graphql` query allComponent { allFile { edges { node { name relativeDirectory } } } }`;export default IndexPage; 读取组件的注释: 12345/** * 提示框(Alert) */class Alert extends React.Component {} 之后通过data.componentMetadata.description字段即可获取。 属性注释会报错Error: Schema must contain unique named types but contains multiple types named “JSON”,暂时不知道为什么]]></content>
<tags>
<tag>react</tag>
</tags>
</entry>
<entry>
<title><![CDATA[react-native中遇到的坑]]></title>
<url>%2F2017%2F12%2F21%2Freact-native%2Freact-native%E4%B8%AD%E9%81%87%E5%88%B0%E7%9A%84%E5%9D%91%2F</url>
<content type="text"><![CDATA[Android中TextInput有下边框添加transparent即可解决 1<TextInput underlineColorAndroid="transparent"/> Android中borderRadius和borderWidth一起使用时效果会出错看issue好像还没有解决: https://github.com/facebook/react-native/issues/11042]]></content>
<tags>
<tag>react-native</tag>
</tags>
</entry>
<entry>
<title><![CDATA[使用node.js发送邮件]]></title>
<url>%2F2017%2F12%2F13%2Fnodejs%2F%E4%BD%BF%E7%94%A8node.js%E5%8F%91%E9%80%81%E9%82%AE%E4%BB%B6%2F</url>
<content type="text"><![CDATA[使用nodemailler发送邮件这里我们以使用qq邮箱发送邮件为例。 安装nodemailer 1npm install --save nodemailer 使用 12345678910111213141516171819var nodemailer = require('nodemailer');var mailTransport = nodemailer.createTransport({ host: 'smtp.qq.com', //服务器地址 port: 465, //端口 auth: { user: 'xxx@qq.com', pass: '授权码' //注:这里需要是授权码(在设置->账户->POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务下“生成授权码”),而不是登录密码 } }); mailTransport.sendMail({ from: '"your name" <xxx@qq.com>', to: 'xxx@gmail.com', //收件人地址 subject: '测试邮件111', text: '测试内容哦11111!!!!!!!' }, function(err){ if(err) console.error('发送失败:', err); console.info('发送成功!'); })]]></content>
<tags>
<tag>express</tag>
<tag>nodejs</tag>
</tags>
</entry>
<entry>
<title><![CDATA[React项目问题总结]]></title>
<url>%2F2017%2F11%2F01%2Freact%2FReact%E9%A1%B9%E7%9B%AE%E9%97%AE%E9%A2%98%E6%80%BB%E7%BB%93%2F</url>
<content type="text"><![CDATA[目前出现的问题 需要定义很多初始化state变量? 表单编辑后点取消,重新从后台拿数据如果是null的时候它不会自动更新state? 组件复用:未编写PropTypes,如果写了有时候返回的字段是null的,怎么写PropTypes? 组件复用:如果组件自己处理内部逻辑,那外面的组件如何拿到组件里面的数据? 页面需要记住类似查询状态,如何简单有效的实现?使用高阶组件实现?如何做到不冲突? 提示框太难用了? 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798/** * * Created by liaoyf on 2017/11/1 0001. */import React from 'react';import 'bootstrap';import { render } from 'react-dom';import v4 from 'uuid';class ModalTool{ constructor(options){ this.options = { okText: '确定', cancelText: '取消', title: '提示框', okAutoClose: true, cancelAutoClose: true, bsStyle: null, bsSize: '', subContent: '', customContent: '', closeBtn: true, backdrop: true, ...options }; this.init(); } bindEvent(){ let { onOk, onCancel } = this.options; $('#' + this.modalId + ' .btn-ok').unbind('click').on('click', function(){ onOk && onOk(); }); $('#' + this.modalId + ' .btn-cancel').unbind('click').on('click', function(){ onCancel && onCancel(); }); } closeModal(){ $('#' + this.modalId ).modal('hide'); } init(){ // $('.modalBox').remove(); $('.modal-backdrop').remove(); let { title, content, okText, closeBtn, cancelText, okAutoClose, cancelAutoClose, bsStyle, bsSize, subContent, customContent, backdrop } = this.options; if(bsStyle){ content = ( <dl className={`modal-state-show ${bsStyle === 'warning' ? 'state-error' : bsStyle === 'success' ? 'state-success' : ''}`}> <dt className="state-ico"><i className="fa"></i></dt> <dd className="state-text">{content}</dd> {subContent && <dd className="state-info">{subContent}</dd>} {customContent && <dd><p>{customContent}</p></dd>} </dl> ); }else if(!React.isValidElement(content)){ content = ( <div className="text-vertical-wrap"> <p className="text-center text-vertical">{content}</p> </div> ); } let modalId = `modal_${v4.v4()}`; let html = `<div class="modal fade modalBox" id="${modalId}" tabIndex="-1" role="dialog" aria-labelledby="myModalLabel"> <div class="${'modal-dialog ' + (bsSize ? 'modal-' + bsSize : (bsStyle ? 'modal-sm' : ''))}" role="document"> <div class="modal-content"> <div class="modal-header"> ${closeBtn ? `<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>` : ''} <h4 class="modal-title" id="myModalLabel">${title}</h4> </div> <div class="body-inset modal-body" id="modalBoxBody"> </div> <div class="modal-footer"> ${okText ? `<button type="button" class="btn btn-primary btn-ok" ${okAutoClose && `data-dismiss="modal"`}>${okText}</button>` : ''} ${cancelText ? `<button type="button" class="btn btn-default btn-cancel" ${cancelAutoClose && `data-dismiss="modal"`}>${cancelText}</button>` : ''} </div> </div> </div></div> `; $('body').append(html); this.modalId = modalId; render( content, $(`#${modalId} #modalBoxBody`)[0], () => { $(`#${modalId}`).modal({ backdrop: backdrop }); this.bindEvent(); } ); return $(`#${modalId}`); }}export default ModalTool; 需要封装常用的对象或数组操作工具方法 表单使用体验太差:需要编写一大推标签;需要手动编写value和onChange;表单校验需要改善 npm 包依赖如何做到同步更新?npm安装依赖包经常报错?npm打包太慢,启动太慢?打包后的文件不会自动加入svn版本控制? npm安装依赖包报: npm ERR! unlink的错误,解决方法: 12345rm (-rf) node_modulesrm package-lock.json yarn.locknpm cache clear --forcenpm i -g npm # 5.4.1, as for nownpm install --no-optional 最好打包时自动运行npm update操作: 12345//package.json"script": { "updater": "npm update", "build": "npm run updater&&webpack --env=prod --progress --profile --colors"} 如何做到公共thunk的合并? front编译如何做到自动化? process.env.NODE_ENV在编译后变成I.env.NODE_ENV? 如何require或import远程地址的文件? 脚手架运行时最好能判断当前的版本是不是最新的,如果不是就报错,这样能确保安装最新的脚手架? 热加载更新? 1234567891011121314151617import Container from './router';const render = (Component) => { ReactDOM.render( <Component/>, document.getElementById('root') ) };render(Container);if (module.hot) { module.hot.accept('./router.js', () => { const NextRootContainer = require('./router').default; render(NextRootContainer); });} js生成sourcemap方便调试? 组件间的样式在打包后会被相互影响? 使用css module 如何解决ulynlist问题:basePath问题(貌似可以自己引)? 打包时如果output.publicPath为空字符串时会找不到字体图标的bug? 配置: 12345678910111213141516171819{ test: /\.scss$/, use: ExtractTextPlugin.extract({ //主要是加这一句 publicPath: '../', fallback: 'style-loader', use: [ { loader: 'css-loader?sourceMap' }, { loader: 'resolve-url-loader' }, { loader: 'sass-loader?sourceMap' } ] })} 样式想要抽成公用的,有两种方法: 如果只想在入口引用一个,则必须写到entry中;如果在多个文件中引用?]]></content>
<tags>
<tag>react</tag>
</tags>
</entry>
<entry>
<title><![CDATA[React入门——简介]]></title>
<url>%2F2017%2F10%2F31%2Freact%2FReact%E5%85%A5%E9%97%A8%E2%80%94%E2%80%94%E7%AE%80%E4%BB%8B%2F</url>
<content type="text"><![CDATA[一、什么是React? React是Facebook开源的一个用于构建用户界面的Javascript库,他不是一个框架,而是专注于MVC架构中的V,即视图。这使得React很容易和开发者已有的开发栈进行融合。React顺应了Web开发组件化的趋势。应用React时,你总是应该从UI出发抽象出不同的组件,然后像搭积木一样把它们拼装起来。 二、React能做什么? 补充:桌面应用程序(NW.js和Electron.js) 二、React全家桶 自身库: react.js、react-dom.js、add-ons 语法:ES5/ES6、JSX 构建工具:Fis3、webpack、babel 状态管理:Redux、Flux、react-redux、Immutable 路由:react-router、react-redux-router 日志:redux-logger 中间件:redux-thunk 主题:react-bootstrap 表单:redux-form]]></content>
<tags>
<tag>react</tag>
</tags>
</entry>
<entry>
<title><![CDATA[创建一个React项目]]></title>
<url>%2F2017%2F10%2F31%2Freact%2F%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AAReact%E9%A1%B9%E7%9B%AE%2F</url>
<content type="text"><![CDATA[React由Facebook所写,由虚拟Dom、组件化获得广大前端开发者的青睐,下面我们通过一个示例来演示创建React项目的步骤: 为什么用ReactReact解决了创建大型项目性能以及复用性问题,React可以有两种写法: 使用React.createClass语法 var HelloComponent = React.createClass({ render: function() { return ( <div className="hello"> Hello, world! </div> ); } }); 使用ES6语法 class HelloComponent extends React.Component { render() { return ( <div className="hello"> Hello, world! </div> ); } } 创建React项目 安装Node.js和NPM,然后运行npm init创建一个package.json文件. 在控制台中运行:npm install react react-dom babel-core babel-loader babel-preset-es2015 babel-preset-react webpack webpack-dev-server --save 创建webpack.config.js配置文件,这个的作用是帮我们打包资源,转换JSX为JS文件、合并、压缩、编译等等等。。。 一个简单的webpack.config.js大致如下: var debug = process.env.NODE_ENV !== "production"; var webpack = require('webpack'); var path = require('path'); module.exports = { context: path.join(__dirname, "src"), devtool: debug ? "inline-sourcemap" : null, entry: "./js/App.js", devServer: { inline: true, port: 3333 }, module: { loaders: [ { test: /\.jsx?$/, exclude: /(node_modules|bower_components)/, loader: 'babel-loader', query: { presets: ['react', 'es2015'], plugins: ['react-html-attrs', 'transform-class-properties', 'transform-decorators-legacy'], } } ] }, output: { path: __dirname + "/src/", filename: "bundle.min.js" }, plugins: debug ? [] : [ new webpack.optimize.DedupePlugin(), new webpack.optimize.OccurenceOrderPlugin(), new webpack.optimize.UglifyJsPlugin({ mangle: false, sourcemap: false }), ], }; 程序的入口通过entry设置,即页面第一次加载运行的文件,Webpack将把所有的JS和JSX文件到文件的输出对象,通过devServer设置webpack开发服务器为内联,并设置端口为3333,在module配置中,我们配置babel转换规则:使用react和es2015,plugins增加了类的属性和装饰器的功能。 热加载首先安装热加载模块: npm install --save-dev babel-preset-react-hmre 然后加到配置中: .... query: { presets: ['react', 'es2015', 'react-hmre'], plugins: ['react-html-attrs', 'transform-class-properties', 'transform-decorators-legacy'], } 另一个选择是安装react-hot-loader然后添加react-hot到webpack.config.js配置中: ... loader: ['babel-loader', 'react-hot'] ... 为了运行项目更简单,我们一般会使用package.json的命令: { "scripts": { "start": "node_modules/.bin/webpack-dev-server --progress --inline --hot", } } 注意:我们命令中添加了--hot,这个启动了热加载模式. 路由路由是一个应用非常重要的一部分,在React中比较受欢迎的莫属React Router了,事实上,很多开发者认为它就是React官方版的路由,当然,你得先安装它: npm install --save react-router 一个简单的示例看起来是这样子的: import React from 'react'; import { render } from 'react-dom'; import { browserHistory, Router, Route, IndexRoute } from 'react-router' import App from '../components/App' import Home from '../components/Home' import About from '../components/About' import Features from '../components/Features' render( <Router history={browserHistory}> <Route path='/' component={App}> <IndexRoute component={Home} /> <Route path='about' component={About} /> <Route path='features' component={Features} /> </Route> </Router>, document.getElementById('app') ) 国际化(I18N)通过react-intl你可以很轻松地实现国际化,它支持超过150中不同语言,默认是英文,呃~ 权限认证Authentication is an important part of any application. The best way to do user authentication for single page apps is via JSON Web Tokens (JWT). A typical authentication flow is this: A user signs up/logs in, generate JWT token and return it to the client Store the JWT token on the client and send it via headers/query parameters for future requests A comprehensive example of adding authentication to a ReactJS app is here. Using Redux? Here is a good example of setting up authentication in your ReactJS application. 数据持久化Without a backend, you can persist data in your Single Page App by using Firebase. In a Reactjs app, all you simply need is ReactFire. It is a ReactJS mixin for easy Firebase integration. With ReactFire, it only takes a few lines of JavaScript to integrate Firebase data into React apps via the ReactFireMixin npm install --save reactfire react firebase TodoList Example import React from 'react'; class TodoList extends React.Component { render() { var _this = this; var createItem = (item, index) => { return ( <li key={ index }> { item.text } <span onClick={ _this.props.removeItem.bind(null, item['.key']) } style=> X </span> </li> ); }; return <ul>{ this.props.items.map(createItem) }</ul>; } } class TodoApp extends React.Component { getInitialState() { return { items: [], text: '' }; } componentWillMount() { this.firebaseRef = firebase.database().ref('todoApp/items'); this.firebaseRef.limitToLast(25).on('value', function(dataSnapshot) { var items = []; dataSnapshot.forEach(childSnapshot => { const item = childSnapshot.val(); item['.key'] = childSnapshot.key; items.push(item); }); this.setState({ items: items }); }.bind(this)); } componentWillUnmount() { this.firebaseRef.off(); } onChange(e) { this.setState({text: e.target.value}); } removeItem(key) { var firebaseRef = firebase.database().ref('todoApp/items');; firebaseRef.child(key).remove(); } handleSubmit(e) { e.preventDefault(); if (this.state.text && this.state.text.trim().length !== 0) { this.firebaseRef.push({ text: this.state.text }); this.setState({ text: '' }); } } render() { return ( <div> <TodoList items={ this.state.items } removeItem={ this.removeItem } /> <form onSubmit={ this.handleSubmit }> <input onChange={ this.onChange } value={ this.state.text } /> <button>{ 'Add #' + (this.state.items.length + 1) }</button> </form> </div> ); } } ReactDOM.render(<TodoApp />, document.getElementById('todoApp')); More information about persisting your data using ReactFire here. 测试Most projects become a mountain of spaghetti code at some point during development due to lack of solid tests or no tests at all. ReactJS apps are no different, but this can be avoided if you know some core principles. When writing tests for ReactJS code, it is helpful to pull out any functionality that doesn’t have to do with any UI components into separate modules, so that they can be tested separately. Tools for unit testing those functionalities are mocha, expect, chai, jasmine. Testing becomes tricky in a ReactJS application when you have to deal with components. How do you test stateless components? How do you test components with state? Now, ReactJS provides a nice set of test utilities that allow us to inspect and examine the components we build. A particular concept worthy of mention is Shallow Rendering. Instead of rendering into a DOM the idea of shallow rendering is to instantiate a component and get the result of its render method. You can also check its props and children and verify they work as expected. More information here. Facebook uses Jest to test React applications. AirBnB uses Enzyme. When bootstrapping your ReactJS application, you can set up any of these awesome tools to implement testing. 脚手架和模板A lot of tools have been mentioned in this post in relation to setting up different parts of a ReactJS app. If you don’t intend writing your app from scratch, there are lots of generators and boilerplates that tie all these tools together to give you a great starting point for your app. One fantastic example is React Starter Kit. It has a Yeoman generator. It’s an isomorphic web app boilerplate that contains almost everything you need to build a ReactJS app. Another boilerplate is React Static boilerplate, it helps you build a web app that can be hosted directly from CDNs like Firebase and Github Pages. Other alternatives are React redux starter kit and React webpack generator. Recently, a nice and effective tool called create-react-app was released by the guys at Facebook. It’s a CLI tool that helps create React apps with no build configuration! 结语There are several tools that will help bootstrap your React app, we looked at a couple that we consider quite good and will have your application up and running in no time. But feel free to search for your own tools, and if you think that we are missing something, let us know in the comments. Setting up a React project should be painless! “Setting up a React project should be painless!”]]></content>
<tags>
<tag>react</tag>
</tags>
</entry>
<entry>
<title><![CDATA[css样式遇到的坑]]></title>
<url>%2F2017%2F10%2F31%2Fcss%2Fcss%E6%A0%B7%E5%BC%8F%E9%81%87%E5%88%B0%E7%9A%84%E5%9D%91%2F</url>
<content type="text"><![CDATA[bootstrap-datetime-picker在模态框中会随着页面滚动而错位解决方法:在options中增加container: '#modalId .modal-dialog',默认的container是body,所以要改成.modal-dialog。]]></content>
<tags>
<tag>css</tag>
</tags>
</entry>
<entry>
<title><![CDATA[express入门]]></title>
<url>%2F2017%2F10%2F12%2Fnodejs%2Fexpress%E5%85%A5%E9%97%A8%2F</url>
<content type="text"><![CDATA[配置handlebars12345678910111213//须安装express-handlebarsvar exhbs = require('express-handlebars');var index = require('./routes/index');var users = require('./routes/users');var app = express();// view engine setup,这边名称如果为.hbs,则文件命名结尾也是.hbs(extname无效?)app.engine('.hbs', exhbs({ extname: '.hbs'}));app.set('views', path.join(__dirname, 'views'));app.set('view engine', '.hbs'); 使用supervisor进行热部署安装: 1npm install -g supervisor 如果你使用的是 Linux 或 Mac,直接键入上面的命令很可能会有权限错误。原因是 npm需要把 supervisor 安装到系统目录,需要管理员授权,可以使用 sudo npm install -g supervisor 命令来安装。 接下来,更改pcakge.json中的start字段: 1"start": "supervisor bin/www" 之后运行npm start既可,命令行窗口会显示启动成功信息,即开启了代码监听。 开放API123456789101112131415161718192021222324252627282930313233343536var express = require('express');var router = express.Router();var mysql = require('mysql');var conncection = mysql.createConnection({ host: 'localhost', user: 'root', password: '*****', database: 'test', port: 3306});conncection.connect();router.get('/getFamilyList', function(req, res, next){ conncection.query('select * from family', function(err, results, fields){ if(err) throw err; res.json(results); });});router.get('/getFamilyById', function (req, res, next) { conncection.query('select * from family where id=?', [req.query.id], function(err, results, fields){ if(err) throw err; res.json(results[0]); });});router.post('/updateFamilyById', function (req, res, next) { conncection.query('update family set name=?,age=? where id=?', [req.body.name, req.body.age, req.body.id], function(err, results, fields){ if(err) throw err; res.json({ status: true }); });});module.exports = router;]]></content>
<tags>
<tag>express</tag>
<tag>nodejs</tag>
</tags>
</entry>
<entry>
<title><![CDATA[React 16新增功能]]></title>
<url>%2F2017%2F09%2F29%2Freact%2FReact%2016%E6%96%B0%E5%A2%9E%E5%8A%9F%E8%83%BD%2F</url>
<content type="text"><![CDATA[官网介绍https://facebook.github.io/react/blog/2017/09/26/react-v16.0.html render函数不必在包一层元素123456789render() { // No need to wrap list items in an extra element! return [ // Don't forget the keys :) <li key="A">First item</li>, <li key="B">Second item</li>, <li key="C">Third item</li>, ];} 更好的错误处理以前的版本,如果子组件有报错整个组件本身直接不渲染,而现在可以通过componentDidCatch捕获错误信息: 123456789101112131415161718192021222324252627282930313233class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { error: null, errorInfo: null }; } componentDidCatch(error, errorInfo) { // Catch errors in any components below and re-render with error message this.setState({ error: error, errorInfo: errorInfo }) // You can also log error messages to an error reporting service here } render() { if (this.state.errorInfo) { // Error path return ( <div> <h2>Something went wrong.</h2> <details style={{ whiteSpace: 'pre-wrap' }}> {this.state.error && this.state.error.toString()} <br /> {this.state.errorInfo.componentStack} </details> </div> ); } // Normally, just render children return this.props.children; }} Portals现在可以通过React.createPortal(<SomeComponent/>, document.getElementById('root'))的形式在react应用容器之外修改或增加DOM: 12345678render() { // React does *not* create a new div. It renders the children into `domNode`. // `domNode` is any valid DOM node, regardless of its location in the DOM. return ReactDOM.createPortal( this.props.children, domNode, );} 这样对于像要创建模态框就很容易了: 123456789101112131415161718192021222324252627282930313233343536373839404142434445class Overlay extends React.Component{ constructor(){ super(); this.overlayContainer = document.createElement('div'); document.body.appendChild(this.overlayContainer); } componentWillUnmount(){ document.body.removeChild(this.overlayContainer); } render(){ return ReactDOM.createPortal( <div className="overlay" style={{position: 'absolute', top: 0, right: 0, bottom: 0, left: 0, background: 'rgba(0,0,0, 0.5)', color: '#fff'}}> {this.props.children} </div>, this.overlayContainer ) }}class Demo extends React.Component{ constructor(){ super(); this.state = { overlayActive: true }; } render(){ return ( <div> <button type="button" onClick={() => this.setState({ user: null })}>Update</button> {this.state.overlayActive && ( <Overlay onClose={() => this.setState({overlayActive: false})}> hello modal!!! <button type="button" onClick={() => this.setState({overlayActive: false})}>Close</button> </Overlay> )} </div> ); }} 更好的服务端渲染省略。。。 支持自定义DOM属性以前是直接忽视无法识别的属性,而现在是直接渲染给DOM,这也直接节省了很多代码。 具体参考:https://facebook.github.io/react/blog/2017/09/08/dom-attributes-in-react-16.html 文件减少 react is 5.3 kb (2.2 kb gzipped), down from 20.7 kb (6.9 kb gzipped). react-dom is 103.7 kb (32.6 kb gzipped), down from 141 kb (42.9 kb gzipped). react + react-dom is 109 kb (34.8 kb gzipped), down from 161.7 kb (49.8 kb gzipped). MIT协议妥协了… 新的核心架构Fiber]]></content>
<tags>
<tag>react</tag>
</tags>
</entry>
<entry>
<title><![CDATA[react-native中FlatList的使用]]></title>
<url>%2F2017%2F09%2F29%2Freact-native%2Freact-native%E4%B8%ADFlatList%E7%9A%84%E4%BD%BF%E7%94%A8%2F</url>
<content type="text"><![CDATA[原文地址:https://medium.com/react-native-development/how-to-use-the-flatlist-component-react-native-basics-92c482816fe6 简介自从0.43版本以来,react-native新增了两个新的列表视图组件:FlatList以及SectionList,现在我们来看看FlatList组件的使用。 简单示例在FlatList中有个比较重要的属性:data和renderItem属性,data为object数组,renderItem可以控制每个item的渲染规则。 这边的示例我们将会从Random User Generator API中拿,UI渲染将会使用react-native-elements的组件。 点击查看完整源代码 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051//FlatListDemo.jsimport React, { Component } from "react";import { View, Text, FlatList } from "react-native";class FlatListDemo extends Component { constructor(props) { super(props); this.state = { loading: false, data: [], page: 1, seed: 1, error: null, refreshing: false, }; } componentDidMount() { this.makeRemoteRequest(); } makeRemoteRequest = () => { const { page, seed } = this.state; const url = `https://randomuser.me/api/?seed=${seed}&page=${page}&results=20`; this.setState({ loading: true }); fetch(url) .then(res => res.json()) .then(res => { this.setState({ data: page === 1 ? res.results : [...this.state.data, ...res.results], error: res.error || null, loading: false, refreshing: false }); }) .catch(error => { this.setState({ error, loading: false }); }); }; render() { return ( <View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}> <Text>Coming soon...</Text> </View> ); }}export default FlatListDemo; 然后外边就可以使用: 12345678910111213141516171819202122import FlatList from './FlatListDemo.js';import React from 'react';import { List, ListItem } from 'react-native-elements';class AwesomeProject extends React.Component{ render(){ <List> <FlatList data={this.state.data} renderItem={({ item }) => ( <ListItem roundAvatar title={`${item.name.first} ${item.name.last}`} subtitle={item.email} avatar={{ uri: item.picture.thumbnail }} keyExtractor={item => item.email} /> )} /> </List> }} 效果图如下:]]></content>
<tags>
<tag>react-native</tag>
</tags>
</entry>
<entry>
<title><![CDATA[js浅拷贝和深拷贝]]></title>
<url>%2F2017%2F08%2F02%2Fjavascript%2Fjs%E6%B5%85%E6%8B%B7%E8%B4%9D%E5%92%8C%E6%B7%B1%E6%8B%B7%E8%B4%9D%2F</url>
<content type="text"><![CDATA[javaScript的变量类型(1)基本类型: 5种基本数据类型Undefined、Null、Boolean、Number 和 String,变量是直接按值存放的,存放在栈内存中的简单数据段,可以直接访问。 (2)引用类型: 存放在堆内存中的对象,变量保存的是一个指针,这个指针指向另一个位置。当需要访问引用类型(如对象,数组等)的值时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。 JavaScript存储对象都是存地址的,所以浅拷贝会导致 obj1 和obj2 指向同一块内存地址。改变了其中一方的内容,都是在原来的内存上做修改会导致拷贝对象和源对象都发生改变,而深拷贝是开辟一块新的内存地址,将原对象的各个属性逐个复制进去。对拷贝对象和源对象各自的操作互不影响。 例如:数组拷贝 123456//浅拷贝,双向改变,指向同一片内存空间var arr1 = [1, 2, 3];var arr2 = arr1;arr1[0] = 'change';console.log('shallow copy: ' + arr1 + " ); //shallow copy: change,2,3console.log('shallow copy: ' + arr2 + " ); //shallow copy: change,2,3 浅拷贝的实现 简单的引用复制1234567891011121314function shallowClone(copyObj) { var obj = {}; for ( var i in copyObj) { obj[i] = copyObj[i]; } return obj;}var x = { a: 1, b: { f: { g: 1 } }, c: [ 1, 2, 3 ]};var y = shallowClone(x);console.log(y.b.f === x.b.f); // true Object.assign()Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。 1234567var x = { a: 1, b: { f: { g: 1 } }, c: [ 1, 2, 3 ]};var y = Object.assign({}, x);console.log(y.b.f === x.b.f); // true 深拷贝的实现Array的slice和concat方法Array的slice和concat方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。之所以把它放在深拷贝里,是因为它看起来像是深拷贝。而实际上它是浅拷贝。原数组的元素会按照下述规则拷贝: 如果该元素是个对象引用 (不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。 对于字符串、数字及布尔值来说(不是 String、Number 或者 Boolean 对象),slice 会拷贝这些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。 如果向两个数组任一中添加了新元素,则另一个不会受到影响。例子如下: 1234567var array = [1,2,3]; var array_shallow = array; var array_concat = array.concat(); var array_slice = array.slice(0); console.log(array === array_shallow); //true console.log(array === array_slice); //false,“看起来”像深拷贝console.log(array === array_concat); //false,“看起来”像深拷贝 可以看出,concat和slice返回的不同的数组实例,这与直接的引用复制是不同的。而从另一个例子可以看出Array的concat和slice并不是真正的深复制,数组中的对象元素(Object,Array等)只是复制了引用。如下: 123456789var array = [1, [1,2,3], {name:"array"}]; var array_concat = array.concat();var array_slice = array.slice(0);array_concat[1][0] = 5; //改变array_concat中数组元素的值 console.log(array[1]); //[5,2,3] console.log(array_slice[1]); //[5,2,3] array_slice[2].name = "array_slice"; //改变array_slice中对象元素的值 console.log(array[2].name); //array_sliceconsole.log(array_concat[2].name); //array_slice JSON对象的parse和stringifyJSON对象是ES5中引入的新的类型(支持的浏览器为IE8+),JSON对象parse方法可以将JSON字符串反序列化成JS对象,stringify方法可以将JS对象序列化成JSON字符串,借助这两个方法,也可以实现对象的深拷贝。 123456789101112131415161718//例1var source = { name:"source", child:{ name:"child" } } var target = JSON.parse(JSON.stringify(source));target.name = "target"; //改变target的name属性console.log(source.name); //source console.log(target.name); //targettarget.child.name = "target child"; //改变target的child console.log(source.child.name); //child console.log(target.child.name); //target child//例2var source = { name:function(){console.log(1);}, child:{ name:"child" } } var target = JSON.parse(JSON.stringify(source));console.log(target.name); //undefined//例3var source = { name:function(){console.log(1);}, child:new RegExp("e") }var target = JSON.parse(JSON.stringify(source));console.log(target.name); //undefinedconsole.log(target.child); //Object {} 这种方法使用较为简单,可以满足基本的深拷贝需求,而且能够处理JSON格式能表示的所有数据类型,但是对于正则表达式类型、函数类型等无法进行深拷贝(而且会直接丢失相应的值)。还有一点不好的地方是它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。同时如果对象中存在循环引用的情况也无法正确处理。 jQuery.extend()方法源码实现123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354jQuery.extend = jQuery.fn.extend = function() { //给jQuery对象和jQuery原型对象都添加了extend扩展方法 var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; //以上其中的变量:options是一个缓存变量,用来缓存arguments[i],name是用来接收将要被扩展对象的key,src改变之前target对象上每个key对应的value。 //copy传入对象上每个key对应的value,copyIsArray判定copy是否为一个数组,clone深拷贝中用来临时存对象或数组的src。 // 处理深拷贝的情况 if (typeof target === "boolean") { deep = target; target = arguments[1] || {}; //跳过布尔值和目标 i++; } // 控制当target不是object或者function的情况 if (typeof target !== "object" && !jQuery.isFunction(target)) { target = {}; } // 当参数列表长度等于i的时候,扩展jQuery对象自身。 if (length === i) { target = this; --i; } for (; i < length; i++) { if ((options = arguments[i]) != null) { // 扩展基础对象 for (name in options) { src = target[name]; copy = options[name]; // 防止永无止境的循环,这里举个例子,如var i = {};i.a = i;$.extend(true,{},i);如果没有这个判断变成死循环了 if (target === copy) { continue; } if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))) { if (copyIsArray) { copyIsArray = false; clone = src && jQuery.isArray(src) ? src: []; // 如果src存在且是数组的话就让clone副本等于src否则等于空数组。 } else { clone = src && jQuery.isPlainObject(src) ? src: {}; // 如果src存在且是对象的话就让clone副本等于src否则等于空数组。 } // 递归拷贝 target[name] = jQuery.extend(deep, clone, copy); } else if (copy !== undefined) { target[name] = copy; // 若原对象存在name属性,则直接覆盖掉;若不存在,则创建新的属性。 } } } } // 返回修改的对象 return target;}; jQuery的extend方法使用基本的递归思路实现了浅拷贝和深拷贝,但是这个方法也无法处理源对象内部循环引用,例如: 12345var a = {"name":"aaa"};var b = {"name":"bbb"};a.child = b;b.parent = a;$.extend(true,{},a);//直接报了栈溢出。Uncaught RangeError: Maximum call stack size exceeded 自己动手实现一个拷贝方法12345678910111213141516171819202122232425262728293031323334353637var $ = (function(){ var types = 'Array Object String Date RegExp Function Boolean Number Null Undefined'.split(' '); function type() { return Object.prototype.toString.call(this).slice(8, -1); } for (var i = types.length; i--;) { $['is' + types[i]] = (function (self) { return function (elem) { return type.call(elem) === self; }; })(types[i]); } return $;})();//类型判断 function copy(obj,deep){ if(obj === null || (!$.isObject(obj) && !$.isFunction(obj))){ return obj; } if($.isFunction(obj)){ return new Function("return " + obj.toString())(); }else{ var name, target = $.isArray(obj) ? [] : {}, value; for(name in obj){ value = obj[name]; if(value === obj) { continue; } if(deep && ($.isArray(value) || $.isObject(value))){ target[name] = copy(value,deep); }else{ target[name] = value; } } return target; } }]]></content>
<tags>
<tag>react</tag>
</tags>
</entry>
<entry>
<title><![CDATA[React高阶组件]]></title>
<url>%2F2017%2F08%2F02%2Freact%2FReact%E9%AB%98%E9%98%B6%E7%BB%84%E4%BB%B6%2F</url>
<content type="text"><![CDATA[链接:https://zhuanlan.zhihu.com/p/24776678?group_id=802649040843051008 摘要这篇文章主要面向想要使用 HOC(Higher Order Component,高阶组件) 模式的进阶开发者。如果你是 React 新手,建议你从阅读 React 的文档开始。 高阶组件是一种很好的模式,很多 React 库已经证明了其价值。这篇文章中我们将会详细的讲解什么是 HOC,你能用它做什么,它有哪些局限,如何实现它。 在附录中有一些相关的话题,可能不是 HOC 的核心,但是我认为应该提到。 这篇文章尽量做到详尽无遗,如果你发现任何遗漏的,请提出来,我会做出必要的改动。 这篇文章假设你已经了解 ES6。 让我们开始吧! 什么是高阶组件? 高阶组件就是一个 React 组件包裹着另外一个 React 组件 这种模式通常使用函数来实现,基本上是一个类工厂(是的,一个类工厂!),它的函数签名可以用类似 haskell 的伪代码表示 1hocFactory:: W: React.Component => E: React.Component 其中 W (WrappedComponent) 指被包裹的 React.Component,E (EnhancedComponent) 指返回类型为 React.Component 的新的 HOC。 我们有意模糊了定义中“包裹”的概念,因为它可能会有以下两种不同的含义之一: Props Proxy: HOC 对传给 WrappedComponent W 的 porps 进行操作; Inheritance Inversion: HOC 继承 WrappedComponent W。 (译注:原作者在评论中提到希望对 Props Proxy 和 Inheritance Inversion 不做翻译,故保留原文) 我们会深入地探究这两种模式。 HOC 工厂的实现方法这一节我们将会研究 React 中两种 HOC 的实现方法:Props Proxy (PP) and Inheritance Inversion (II)。两种方法都可以操作 WrappedComponent。 Props ProxyProps Proxy (PP) 的最简实现: 1234567function ppHOC(WrappedComponent) { return class PP extends React.Component { render() { return <WrappedComponent {...this.props}/> } } } 这里主要是 HOC 在 render 方法中 返回 了一个 WrappedComponent 类型的 React Element。我们还传入了 HOC 接收到的 props,这就是名字 Props Proxy 的由来。 123<WrappedComponent {...this.props}/>// 等价于React.createElement(WrappedComponent, this.props, null) 在 React 内部的一致化处理(reconciliation process)中,两者都创建了一个 React Element 用于渲染。如果你想了解关于 React Elements vs Components ,请看 Dan Abramov 的这篇文章,想了解一致化处理请参考文档。 (译注:一致化处理(reconciliation process)可理解为 React 内部将虚拟 DOM 同步更新到真实 DOM 的过程,包括新旧虚拟 DOM 的比较及计算最小 DOM 操作) 使用 Props Proxy 可以做什么? 操作 props 通过 Refs 访问到组件实例 提取 state 用其他元素包裹 WrappedComponent 操作 props你可以读取、添加、编辑、删除传给 WrappedComponent 的 props。 当删除或者编辑重要的 props 时要小心,你可能应该通过命名空间(命名空间是什么鬼?)确保高阶组件的 props 不会破坏 WrappedComponent。 例子:添加新的 props。在这个应用中,当前登录的用户可以在 WrappedComponent 中通过 this.props.user 访问到。 12345678910function ppHOC(WrappedComponent) { return class PP extends React.Component { render() { const newProps = { user: currentLoggedInUser } return <WrappedComponent {...this.props} {...newProps}/> } }} 通过 Refs 访问到组件实例你可以通过引用(ref)访问到 this (WrappedComponent 的实例),但为了得到引用,WrappedComponent 还需要一个初始渲染,意味着你需要在 HOC 的 render 方法中返回 WrappedComponent 元素,让 React 开始它的一致化处理,你就可以得到 WrappedComponent 的实例的引用。 例子:如何通过 refs 访问到实例的方法和实例本身: 123456789101112function refsHOC(WrappedComponent) { return class RefsHOC extends React.Component { proc(wrappedComponentInstance) { wrappedComponentInstance.method() } render() { const props = Object.assign({}, this.props, {ref: this.proc.bind(this)}) return <WrappedComponent {...props}/> } }} Ref 的回调函数会在 WrappedComponent 渲染时执行,你就可以得到 WrappedComponent 的引用。这可以用来读取/添加实例的 props ,调用实例的方法。 提取 state你可以通过传入 props 和回调函数把 state 提取出来,类似于 smart component 与 dumb component。更多关于 dumb and smart component。 提取 state 的例子:提取了 input 的 value 和 onChange 方法。这个简单的例子不是很常规,但足够说明问题。 1234567891011121314151617181920212223242526function ppHOC(WrappedComponent) { return class PP extends React.Component { constructor(props) { super(props) this.state = { name: '' } this.onNameChange = this.onNameChange.bind(this) } onNameChange(event) { this.setState({ name: event.target.value }) } render() { const newProps = { name: { value: this.state.name, onChange: this.onNameChange } } return <WrappedComponent {...this.props} {...newProps}/> } }} 你可以这样用: 123456@ppHOCclass Example extends React.Component { render() { return <input name="name" {...this.props.name}/> }} 这个 input 会自动成为受控input。 更多关于常规的双向绑定 HOC 请点击 链接 用其他元素包裹 WrappedComponent为了封装样式、布局或别的目的,你可以用其它组件和元素包裹 WrappedComponent。基本方法是使用父组件(附录 B)实现,但通过 HOC 你可以得到更多灵活性。 例子:包裹样式 1234567891011function ppHOC(WrappedComponent) { return class PP extends React.Component { render() { return ( <div style={{display: 'block'}}> <WrappedComponent {...this.props}/> </div> ) } }} Inheritance InversionInheritance Inversion (II) 的最简实现: 1234567function iiHOC(WrappedComponent) { return class Enhancer extends WrappedComponent { render() { return super.render() } }} 你可以看到,返回的 HOC 类(Enhancer)继承了 WrappedComponent。之所以被称为 Inheritance Inversion 是因为 WrappedComponent 被 Enhancer 继承了,而不是 WrappedComponent 继承了 Enhancer。在这种方式中,它们的关系看上去被反转(inverse)了。 Inheritance Inversion 允许 HOC 通过 this 访问到 WrappedComponent,意味着它可以访问到 state、props、组件生命周期方法和 render 方法。 关于生命周期方法可以用来做什么,我不想细说,因为它是 React 的特性而不是 HOC 的特性。但请注意通过 II 你可以创建新的生命周期方法。为了不破坏 WrappedComponent,记得调用 super.[lifecycleHook]。 一致化处理(Reconciliation process)开始之前我们先理清一些概念。 React 元素决定描述了在 React 执行一致化处理时它要渲染什么。 React 元素有两种类型:字符串和函数。字符串类型的 React 元素代表 DOM 节点,函数类型的 React 元素代表继承 React.Component 的组件。更多关于元素(Element)和组件(Component)请看这篇文章。 函数类型的 React 元素会在一致化处理中被解析成一个完全由字符串类型 React 组件组成的树(而最后的结果永远是 DOM 元素)。 这很重要,意味着 Inheritance Inversion 的高阶组件不一定会解析完整子树 Inheritance Inversion 的高阶组件不一定会解析完整子树 这在学习渲染劫持(Render Highjacking)时非常重要。 你可以用 Inheritance Inversion 做什么? 渲染劫持(Render Highjacking) 操作 state 渲染劫持之所以被称为渲染劫持是因为 HOC 控制着 WrappedComponent 的渲染输出,可以用它做各种各样的事。 通过渲染劫持你可以: 在由 render输出的任何 React 元素中读取、添加、编辑、删除 props 读取和修改由 render 输出的 React 元素树 有条件地渲染元素树 把样式包裹进元素树(就像在 Props Proxy 中的那样) *render 指 WrappedComponent.render 方法 你不能编辑或添加 WrappedComponent 实例的 props,因为 React 组件不能编辑它接收到的 props,但你可以修改由 render 方法返回的组件的 props。 就像我们刚才学到的,II 类型的 HOC 不一定会解析完整子树,意味着渲染劫持有一些限制。根据经验,使用渲染劫持你可以完全操作 WrappedComponent 的 render 方法返回的元素树。但是如果元素树包括一个函数类型的 React 组件,你就不能操作它的子组件了。(被 React 的一致化处理推迟到了真正渲染到屏幕时) 例1:条件渲染。当 this.props.loggedIn 为 true 时,这个 HOC 会完全渲染 WrappedComponent 的渲染结果。(假设 HOC 接收到了 loggedIn 这个 prop) 1234567891011function iiHOC(WrappedComponent) { return class Enhancer extends WrappedComponent { render() { if (this.props.loggedIn) { return super.render() } else { return null } } }} 例2:修改由 render 方法输出的 React 组件树。 1234567891011121314function iiHOC(WrappedComponent) { return class Enhancer extends WrappedComponent { render() { const elementsTree = super.render() let newProps = {}; if (elementsTree && elementsTree.type === 'input') { newProps = {value: 'may the force be with you'} } const props = Object.assign({}, elementsTree.props, newProps) const newElementsTree = React.cloneElement(elementsTree, props, elementsTree.props.children) return newElementsTree } }} 在这个例子中,如果 WrappedComponent 的输出在最顶层有一个 input,那么就把它的 value 设为 “may the force be with you”。 你可以在这里做各种各样的事,你可以遍历整个元素树,然后修改元素树中任何元素的 props。这也正是样式处理库 Radium 所用的方法(案例分析一节中有更多关于 Radium 的信息)。 注:在 Props Proxy 中不能做到渲染劫持。 虽然通过 WrappedComponent.prototype.render 你可以访问到 render 方法,不过还需要模拟 WrappedComponent 的实例和它的 props,还可能亲自处理组件的生命周期,而不是交给 React。根据我的实验,这么做不值,你要是想做到渲染劫持你应该用 Inheritance Inversion 而不是 Props Proxy。记住,React 在内部处理了组件实例,你处理实例的唯一方法是通过 this 或者 refs。 操作 stateHOC 可以读取、编辑和删除 WrappedComponent 实例的 state,如果你需要,你也可以给它添加更多的 state。记住,这会搞乱 WrappedComponent 的 state,导致你可能会破坏某些东西。要限制 HOC 读取或添加 state,添加 state 时应该放在单独的命名空间里,而不是和 WrappedComponent 的 state 混在一起。 例子:通过访问 WrappedComponent 的 props 和 state 来做调试。 1234567891011121314export function IIHOCDEBUGGER(WrappedComponent) { return class II extends WrappedComponent { render() { return ( <div> <h2>HOC Debugger Component</h2> <p>Props</p> <pre>{JSON.stringify(this.props, null, 2)}</pre> <p>State</p><pre>{JSON.stringify(this.state, null, 2)}</pre> {super.render()} </div> ) } }} 这里 HOC 用其他元素包裹着 WrappedComponent,还输出了 WrappedComponent 实例的 props 和 state。JSON.stringify 的小技巧是由 Ryan Florence 和 Michael Jackson 教我的。这个调试器完整的实现在这里。 命名用 HOC 包裹了一个组件会使它失去原本 WrappedComponent 的名字,可能会影响开发和调试。 通常会用 WrappedComponent 的名字加上一些 前缀作为 HOC 的名字。下面的代码来自 React-Redux: 12345678HOC.displayName = `HOC(${getDisplayName(WrappedComponent)})`//或class HOC extends ... { static displayName = `HOC(${getDisplayName(WrappedComponent)})` ...} getDisplayName 函数: 12345function getDisplayName(WrappedComponent) { return WrappedComponent.displayName || WrappedComponent.name || ‘Component’} 实际上你不用自己写,recompose 提供了这个函数。]]></content>
<tags>
<tag>react</tag>
</tags>
</entry>
<entry>
<title><![CDATA[react文档-css动画]]></title>
<url>%2F2017%2F08%2F02%2Freact%2Freact%E6%96%87%E6%A1%A3-css%E5%8A%A8%E7%94%BB%2F</url>
<content type="text"><![CDATA[TransitionGroup和CSSTransitionGroup已移动到react-transition-group有社区维护。它的1.x分支与现有插件的API是完全兼容的。 TransitionGroup是一个具有低级API的动画组件,而CSSTransitionGroup是一个基于css的animation和transition更容易被使用的组件。 高级API:CSSTransitionGroup123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051import { CSSTransitionGroup } from 'react-transition-group' // ES6var CSSTransitionGroup = require('react-transition-group/CSSTransitionGroup') // ES5 with npmclass TodoList extends React.Component{ constructor(){ super(); this.state = { items: [ 'hello', 'world', 'click', 'me' ] }; this.handleAdd = this.handleAdd.bind(this); } handleAdd() { const newItems = this.state.items.concat([ prompt('Enter some text') ]); this.setState({items: newItems}); } handleRemove(i) { let newItems = this.state.items.slice(); newItems.splice(i, 1); this.setState({items: newItems}); } render(){ const items = this.state.items.map((item, i) => ( <div key={i} onClick={() => this.handleRemove(i)}> {item} </div> )); return ( <div> <button type="button" onClick={this.handleAdd}>Add Item</button> <CSSTransitionGroup transitionName="example" transitionEnterTimeout={500} transitionLeaveTimeout={300} > {items} </CSSTransitionGroup> </div> ); }} 在这个组件中,当一个新的Item被添加时,ReactCSSTransitionGroup将获得一个example-enter的类和example-enter-active的类,这些类名是基于trnasitionName的值。 您可以使用这些类来触发css动画或转换。例如:尝试添加如下样式: 1234567891011121314.example-enter{ opacity: 0.01;}.example-enter.example-enter-active{ opacity: 1; transition: opacity 500ms ease-in;}.example-leave{ opacity: 1;}.example-leavel.example-leavel-active{ opacity: 0.01; transition: opacity 300ms ease-in;} 初始化加载动画ReactCSSTransitionGroup提供一个可选的属性transitionAppear,以在组件的初始渲染时添加额外的过渡阶段。默认为false: 123456789101112render() { return ( <CSSTransitionGroup transitionName="example" transitionAppear={true} transitionAppearTimeout={500} transitionEnter={false} transitionLeave={false}> <h1>Fading at Initial Mount</h1> </CSSTransitionGroup> );} 在初始化渲染ReactCSSTransitionGroup过程中,example-appear类和example-appear-active类将被添加。 1234567.example-appear { opacity: 0.01;}.example-appear.example-appear-active { opacity: 1; transition: opacity .5s ease-in;} 在初始化渲染阶段,所有ReactCSSTransitionGroup的孩子节点将会appear而不是enter,然而,所有孩子稍后被渲染后将会触发enter而不是appear。 注意: 属性transitionAppear在0.13版本中才被添加进入ReactCSSTransitionGroup中,为了向后兼容,默认值为false。 然而,transitionEnter和transtionLeave默认为true,所以你默认只要指定transitionEnterTimeout和transitionLeaveTimeout。 自定义类名当然,我们也可以使用自定义类名代替每一步默认的类名。你可以通过传递一个包含enter或leave或appear等等的对象给transitionName而不是字符串。 如果未提供active时,默认会为添加-active后的类名: 12345678910111213141516171819202122<CSSTransitionGroup transitionName={ { enter: 'enter', enterActive: 'enterActive', leave: 'leave', leaveActive: 'leaveActive', appear: 'appear', appearActive: 'appearActive' } }>{item}</CSSTransitionGroup><CSSTransitionGroup transitionName={ { enter: 'enter', leave: 'leave', appear: 'appear' } }> {item2}</CSSTransitionGroup> 动画组必须已经被渲染到DOM为了时其子节点的动画能够生效,ReactCSSTransitionGroup必须已经被加载进DOM中,或者transitionAppear必须为true。 下面的例子将不会正常运行,因为ReactCSSTransitionGroup被和新的item一起渲染,而不是新的item渲染进其子节点: 123456789101112131415render() { const items = this.state.items.map((item, i) => ( <div key={item} onClick={() => this.handleRemove(i)}> <CSSTransitionGroup transitionName="example"> {item} </CSSTransitionGroup> </div> )); return ( <div> <button onClick={this.handleAdd}>Add Item</button> {items} </div> );} 渲染一个或零个子节点在上面的例子中,我们渲染了一个列表节点,当然,ReactCSSTransitionGroup也可以渲染一个或零个节点: 12345678910111213import ReactCSSTransitionGroup from 'react-addons-css-transition-group';function ImageCarousel(props) { return ( <div> <CSSTransitionGroup transitionName="carousel" transitionEnterTimeout={300} transitionLeaveTimeout={300}> <img src={props.imageSrc} key={props.imageSrc} /> </CSSTransitionGroup> </div> );} 禁用动画可以通过设置false来禁用指定阶段,如:transitionEnter为false禁用进入后的动画效果。 注意:使用ReactCSSTransitionGroup你没办法知道一个transition已经结束或其他更详细的细节,如果你需要,则得使用低级APIReactTransitionGroup,它提供更多的钩子让你能够监听。 低级API:TransitionGroup12import TransitionGroup from 'react-transition-group/TransitionGroup' // ES6var TransitionGroup = require('react-transition-group/TransitionGroup') // ES5 with npm ReactTransitionGroup是动画的基础。当子节点添加或删除,下面这些钩子函数将会被调用: componentWillAppear(callback) 这个方法将在渲染到节点时调用,它将阻塞其他当前正在发生的动画,知道callback被调用。这个方法仅仅在初始化渲染TransitionGroup时被调用。 componentDidAppear() 这个方法将在componentWillAppear的callback被调用后被调用。 componentWillEnter(callback) 当有新元素被添加到TransitionGroup时调用,它将会阻塞其他动画知道callback调用。 componentDidEnter() 在componentWillEnter的callback调用后触发。 componentWillLeave(callback) 当有元素被移除时触发,尽管元素已经被删除,但它还是还将保留在DOM中,直到callback被调用。 componentDidLeave() 在componentWillLeave的callback被调用时触发(和componentWillUnmount()同时触发)。 渲染其他组件TransitionGroup默认渲染为一个span,你通过增加一个compoennt属性改变默认行为: 123<TransitionGroup component="ul"> {/*...*/}</TranstionGroup> 另外,用户自定义的属性也会对应增加到组件的属性,如下例子,ul也将会得到className属性: 123<TransitionGroup component="ul" className="animated-list"> {/* ... */}</TransitionGroup> 所有react可以渲染的组件都可以成为component,它不是必须为一个DOM节点,如:component={CustomList},那么,TransitionGroup的子节点传递给CustomList组件的属性this.props.children。 渲染单独的子节点我们经常会用TransitionGroup来作为单独子节点的渲染和移除时的效果,诸如手风琴效果的面板。 正常情况下,TransitionGroup把其子节点包裹在一个span(或者自定义的component中),这是因为react的render函数必须返回单独子节点,但是TransitionGroup并不需要这样的规则。 然而,如果你需要只渲染一个单独子节点,你可以通过定义一个只渲染第一个子节点的组件: 1234function FirstChild(props) { const childrenArray = React.Children.toArray(props.children); return childrenArray[0] || null;} 现在你可以指定TransitionGroup的component属性为FirstChild来避免包裹外层组件: 123<TransitionGroup component={FirstChild}> {someCondition ? <MyComponent /> : null}</TransitionGroup> 这仅仅在你只想要渲染一个子节点时有用,在需要渲染多个节点时没有效果。]]></content>
<tags>
<tag>react</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Flexbox布局兼容性总结]]></title>
<url>%2F2017%2F08%2F02%2Fcss%2FFlexbox%E5%B8%83%E5%B1%80%E5%85%BC%E5%AE%B9%E6%80%A7%E6%80%BB%E7%BB%93%2F</url>
<content type="text"><![CDATA[写在前面IE9不支持…试了一下flexibility库,发现支持还好,就是嵌套的时候会有问题,因为它是通过定位的方式,暂时无解…… 入门教程传送门 W3C各个版本的flex 2009 version标志:display: box; or a property that is box-{*} (eg. box-pack) 2011 version标志:display: flexbox; or the flex() function or flex-pack property 2012 version标志:display: flex/inline-flex; and flex-{*} properties 2014 version新增了对flex项z-index的规定 2015 W3C Editor’s Draft没有大的改动 P.S.注意2015的是W3C Editor’s Draft,只是个草案,还处于修修改改的阶段,还没有征集浏览器供应商的意见。 浏览器兼容性关于flex的W3C规范: http://dev.w3.org/csswg/css-flexbox-1/ 浏览器兼容性可以参考CanIUse: http://caniuse.com/#feat=flexbox 根据CanIUse的数据可以总结如下: IE10部分支持2012,需要-ms-前缀 Android4.1/4.2-4.3部分支持2009 ,需要-webkit-前缀 Safari7/7.1/8部分支持2012, 需要-webkit-前缀 IOS Safari7.0-7.1/8.1-8.3部分支持2012,需要-webkit-前缀 所以需要考虑新版本2012: http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/ 而Android需要考虑旧版本2009: http://www.w3.org/TR/2009/WD-css3-flexbox-20090723/ 浏览器兼容的flex语法上面分析得很清楚,针对需要兼容的目标使用对应版本的语法就好了,下面给出常用的布局代码: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119/* 子元素-平均分栏 */.flex1 { -webkit-box-flex: 1; /* OLD - iOS 6-, Safari 3.1-6 */ -moz-box-flex: 1; /* OLD - Firefox 19- */ width: 20%; /* For old syntax, otherwise collapses. */ -webkit-flex: 1; /* Chrome */ -ms-flex: 1; /* IE 10 */ flex: 1; /* NEW, Spec - Opera 12.1, Firefox 20+ */}/* 父元素-横向排列(主轴) */.flex-h { display: box; /* OLD - Android 4.4- */ display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */ display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */ display: -ms-flexbox; /* TWEENER - IE 10 */ display: -webkit-flex; /* NEW - Chrome */ display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */ /* 09版 */ -webkit-box-orient: horizontal; /* 12版 */ -webkit-flex-direction: row; -moz-flex-direction: row; -ms-flex-direction: row; -o-flex-direction: row; flex-direction: row;}/* 父元素-横向换行 */.flex-hw { /* 09版 */ /*-webkit-box-lines: multiple;*/ /* 12版 */ -webkit-flex-wrap: wrap; -moz-flex-wrap: wrap; -ms-flex-wrap: wrap; -o-flex-wrap: wrap; flex-wrap: wrap;}/* 父元素-水平居中(主轴是横向才生效) */.flex-hc { /* 09版 */ -webkit-box-pack: center; /* 12版 */ -webkit-justify-content: center; -moz-justify-content: center; -ms-justify-content: center; -o-justify-content: center; justify-content: center; /* 其它取值如下: align-items 主轴原点方向对齐 flex-end 主轴延伸方向对齐 space-between 等间距排列,首尾不留白 space-around 等间距排列,首尾留白 */}/* 父元素-纵向排列(主轴) */.flex-v { display: box; /* OLD - Android 4.4- */ display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */ display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */ display: -ms-flexbox; /* TWEENER - IE 10 */ display: -webkit-flex; /* NEW - Chrome */ display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */ /* 09版 */ -webkit-box-orient: vertical; /* 12版 */ -webkit-flex-direction: column; -moz-flex-direction: column; -ms-flex-direction: column; -o-flex-direction: column; flex-direction: column;}/* 父元素-纵向换行 */.flex-vw { /* 09版 */ /*-webkit-box-lines: multiple;*/ /* 12版 */ -webkit-flex-wrap: wrap; -moz-flex-wrap: wrap; -ms-flex-wrap: wrap; -o-flex-wrap: wrap; flex-wrap: wrap;}/* 父元素-竖直居中(主轴是横向才生效) */.flex-vc { /* 09版 */ -webkit-box-align: center; /* 12版 */ -webkit-align-items: center; -moz-align-items: center; -ms-align-items: center; -o-align-items: center; align-items: center;}/* 子元素-显示在从左向右(从上向下)第1个位置,用于改变源文档顺序显示 */.flex-1 { -webkit-box-ordinal-group: 1; /* OLD - iOS 6-, Safari 3.1-6 */ -moz-box-ordinal-group: 1; /* OLD - Firefox 19- */ -ms-flex-order: 1; /* TWEENER - IE 10 */ -webkit-order: 1; /* NEW - Chrome */ order: 1; /* NEW, Spec - Opera 12.1, Firefox 20+ */}/* 子元素-显示在从左向右(从上向下)第2个位置,用于改变源文档顺序显示 */.flex-2 { -webkit-box-ordinal-group: 2; /* OLD - iOS 6-, Safari 3.1-6 */ -moz-box-ordinal-group: 2; /* OLD - Firefox 19- */ -ms-flex-order: 2; /* TWEENER - IE 10 */ -webkit-order: 2; /* NEW - Chrome */ order: 2; /* NEW, Spec - Opera 12.1, Firefox 20+ */}为了更好的兼容性,我们需要给容器声明flex-h/flex-v,而不是一般的flex:/* 父元素-flex容器 */.flex { display: box; /* OLD - Android 4.4- */ display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */ display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */ display: -ms-flexbox; /* TWEENER - IE 10 */ display: -webkit-flex; /* NEW - Chrome */ display: flex; /* NEW, Spec - Opera 12.1, Firefox 20+ */} 所以,建议在需要兼容Android时(2009版语法)采用flex-h/flex-v声明容器使用flex模式,在不需要兼容Android时(2012版语法)使用flex设置容器 注意:上面给的代码并不是完全兼容各个高端浏览器的,但要比任何其它现有代码兼容性好,具体兼容性测试结果请看Demo sass定义123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107@mixin flex{ display: box; /* old - Android 4.4- */ display: -webkit-box; /* old - IOS 6-, Safari 3.1-6 */ display: -moz-box; /* old - Firefox 19 - (buggy but most works) */ display: -ms-flexbox; /* IE 10 */ display: -webkit-flex; /* New - Chrome */ display: flex; -js-display: flex;}@mixin flexDirection($type: row){ /* 09版 */ @if $type == 'row'{ -webkit-box-orient: horizontal; }@else if $type == 'row-reverse'{ -webkit-box-orient: horizontal; }@else if $type == 'column'{ -webkit-box-orient: vertical; }@else if $type == 'column-reverse'{ -webkit-box-orient: vertical; } /* 12版 */ -webkit-flex-direction: $type; -moz-flex-direction: $type; -ms-flex-direction: $type; -o-flex-direction: $type; flex-direction: $type; -js-flex-direction: $type;}@mixin flexWrap($type: nowrap){ /* 09版 */ /*-webkit-box-lines: multiple;*/ /* 12版 */ -webkit-flex-wrap: $type; -moz-flex-wrap: $type; -ms-flex-wrap: $type; -o-flex-wrap: $type; flex-wrap: $type; -js-flex-wrap: $type;}@mixin justifyContent($type: flex-start){ /* 09版 */ //-webkit-box-pack: justify; /* 12版 */ -webkit-justify-content: $type; -moz-justify-content: $type; -ms-justify-content: $type; -o-justify-content: $type; justify-content: $type; /* 其它取值如下: align-items 主轴原点方向对齐 flex-end 主轴延伸方向对齐 space-between 等间距排列,首尾不留白 space-around 等间距排列,首尾留白 */ -js-justify-content: $type;}@mixin alignItems($type: stretch){ /* 09版 */ //-webkit-box-align: center; /* 12版 */ -webkit-align-items: $type; -moz-align-items: $type; -ms-align-items: $type; -o-align-items: $type; align-items: $type; -js-align-items: $type;}@mixin alignContent($type: stretch){ /* 09版 */ //-webkit-box-align: center; /* 12版 */ -webkit-align-content: $type; -moz-align-content: $type; -ms-align-content: $type; -o-align-content: $type; align-content: $type; -js-align-content: $type;}@mixin flexOrder($val: 0){ -webkit-box-ordinal-group: $val; /* OLD - iOS 6-, Safari 3.1-6 */ -moz-box-ordinal-group: $val; /* OLD - Firefox 19- */ -ms-flex-order: $val; /* TWEENER - IE 10 */ -webkit-order: $val; /* NEW - Chrome */ order: $val; /* NEW, Spec - Opera 12.1, Firefox 20+ */ -js-order: $val;}@mixin flexGrow($val: 0){ -webkit-box-flex-grow: $val; /* OLD - iOS 6-, Safari 3.1-6 */ -moz-box-flex-grow: $val; /* OLD - Firefox 19- */ -webkit-flex-grow: $val; /* Chrome */ -ms-flex-grow: $val; /* IE 10 */ flex-grow: $val; /* NEW, Spec - Opera 12.1, Firefox 20+ */ -js-flex-grow: $val;}@mixin flexShrink($val: 1){ -webkit-box-flex-shrink: $val; /* OLD - iOS 6-, Safari 3.1-6 */ -moz-box-flex-shrink: $val; /* OLD - Firefox 19- */ -webkit-flex-shrink: $val; /* Chrome */ -ms-flex-shrink: $val; /* IE 10 */ flex-shrink: $val; /* NEW, Spec - Opera 12.1, Firefox 20+ */ -js-flex-shrink: $val;}//横向.#{$namespace}flex-h{ @include flex(); @include flexDirection();}]]></content>
<tags>
<tag>css</tag>
<tag>flexbox</tag>
</tags>
</entry>
<entry>
<title><![CDATA[关于同一元素下dbClick触发两次click的问题]]></title>
<url>%2F2017%2F08%2F02%2Fjavascript%2F%E5%85%B3%E4%BA%8E%E5%90%8C%E4%B8%80%E5%85%83%E7%B4%A0%E4%B8%8BdbClick%E8%A7%A6%E5%8F%91%E4%B8%A4%E6%AC%A1click%E7%9A%84%E9%97%AE%E9%A2%98%2F</url>
<content type="text"><![CDATA[问题将处理程序绑定到相同元素的click和dblclick事件是不合适的。 触发的事件顺序因浏览器而异,有些在dblclick之前接收到两个点击事件,而其他事件只有一个。 双击灵敏度(双击检测到的点击之间的最大时间)可能因操作系统和浏览器而异,并且通常是用户可配置的。 所以最好不要在同一个元素下绑定click和dbclick事件。 解决方法方法一: 12345678910111213141516var v_Result; function OneClick(event) { console.log("detail",event.detail); //if (event.detail == 2) // return ; v_Result = false; window.setTimeout(check, 300); function check() { if (v_Result != false) return; console.log("单击"); } } function TwoClick() { v_Result = true; console.log("双击"); } 方法二: 123456789101112131415161718192021222324252627var clickTimer = null; function _click() { if (clickTimer) { console.log("clearTimeout", clickTimer); window.clearTimeout(clickTimer); clickTimer = null; } clickTimer = window.setTimeout(function() { // your click process code here console.log("你单击了我"); }, 300); console.log("setTimeout", clickTimer); } function _dblclick() { console.log("dblclick"); if (clickTimer) { console.log("=clearTimeout", clickTimer); window.clearTimeout(clickTimer); clickTimer = null; } // your click process code here console.log("你双击了我"); }]]></content>
<tags>
<tag>js</tag>
</tags>
</entry>
<entry>
<title><![CDATA[ES6的坑]]></title>
<url>%2F2017%2F08%2F02%2Fjavascript%2FES6%E7%9A%84%E5%9D%91%2F</url>
<content type="text"><![CDATA[IE8下用babel转换会报错: function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 解决方法: $ npm install --save-dev babel-plugin-transform-es2015-modules-simple-commonjs 配置: //webpack.config.js "plugins": ["transform-es2015-modules-simple-commonjs"] ES6 + angular1 + webpack,遇到controller文件里的constructor运行2次? 那是因为声明了2次controller,在配置中配了app.controller('MyController'),然后又在页面中使用了ng-controller,导致运行了2次,坑爹~ Babel转ES5后IE8下的兼容性解决方法。 1)webpack配置文件,增加插件transform-es3-property-literals和transform-es3-member-expression-literals const webpackdevConfig = { entry: entry, output: { path: path.join(__dirname, 'dist/js'), filename: '[name].js', publicPath: '/static/' }, plugins: [ new webpack.NoErrorsPlugin(), ], module: { loaders: [ { test: /\.js$/, loader: ['babel'], include: [path.join(new_dir, 'src')], query:{ "presets": ["es2015", "stage-0"], "plugins" : [ "transform-es3-property-literals", "transform-es3-member-expression-literals", ] } }, {test: /\.scss$/, loaders: ['style', 'css', 'sass'], include: path.join(new_dir, 'src/style')}, {test: /\.(jpg|png)$/, loader: 'url-loader?limit=8192', include: path.join(new_dir, 'src/img')} ] } } 2)模块导出不能使用 export default ,改为export { xxx } 3)模块引入使用 import { } from ‘xxx’ 4)引入es5-shim.min.js和es5-sham.min.js]]></content>
<tags>
<tag>js</tag>
</tags>
</entry>
<entry>
<title><![CDATA[React获取父组件或子组件属性]]></title>
<url>%2F2017%2F08%2F02%2Freact%2Freact%E8%8E%B7%E5%8F%96%E7%88%B6%E7%BB%84%E4%BB%B6%E6%88%96%E5%AD%90%E7%BB%84%E4%BB%B6%E5%B1%9E%E6%80%A7%2F</url>
<content type="text"><![CDATA[获取子组件的方法可以通过递归this.props.children中得到 获取父组件的方法方法一:可以通过react内部私有函数this._reactInternalInstance._currentElement._owner._instance获取 12345678910111213141516171819var Parent = React.createClass({ render() { return <Child v="test" />; }, doAThing() { console.log("I'm the parent, doing a thing.", this.props.testing); }});var Child = React.createClass({ render() { return <button onClick={this.onClick}>{this.props.v}</button> }, onClick() { var parent = this._reactInternalInstance._currentElement._owner._instance; console.log("parent:", parent); parent.doAThing(); }});ReactDOM.render(<Parent testing={true} />, container); 但是这种方法是不推荐的。 方法二:通过属性传递给子组件 12345678910111213class Parent extends React.Component { constructor(props) { super(props) this.fn = this.fn.bind(this) } fn() { console.log('parent') } render() { return <Child fn={this.fn} /> }}const Child = ({ fn }) => <button onClick={fn}>Click me!</button> 但是这种只在Child组件在Parent组件中时才可以用。 方法三:使用上下文(没有直接的父/子关系) 123456789101112131415161718192021222324class Parent extends React.Component { constructor(props) { super(props) this.fn = this.fn.bind(this) } getChildContext() { return { fn: this.fn, } } fn() { console.log('parent') } render() { return <Child fn={this.fn} /> }}Parent.childContextTypes = { fn: React.PropTypes.func,}const Child = (props, { fn }) => <button onClick={fn}>Click me!</button>Child.contextTypes = { fn: React.PropTypes.func,} 给子组件(没有直接父/子关系)添加属性1return React.cloneElement(this.props.children, {/*要添加的属性*/}) React关于子组件的APIReact.chlidren.map1React.Children.map(children, function[(thisArg)]) 对包含在 children 中的每个直接子元素调用一个函数,使用 this 设置 thisArg 。 如果 children 是一个键片段(keyed fragment)或数组,它将被遍历:该函数永远不会传递容器对象(container objects)。 如果 children 为 null 或 undefined ,返回 null 或 undefined,而不是一个数组。 12345678910111213141516171819202122232425262728import React from 'react';const Salmonize = ({ children }) => ( <div> {React.Children.map(children, child => ( React.cloneElement(child, { style: { backgroundColor: 'salmon', color: 'seagreen', } }) ))} </div>);const SalmonBlog = ({ title, posts }) => ( <div> <Salmonize> <NavBar title={title} /> </Salmonize> {posts.map(post => ( <Post key={post.id}> <Salmonize> <PostHeader title={post.title} /> </Salmonize> <PostBody text={post.text} /> </Post> ))} </div>); 在上面这个例子中,Salmonize组件并不需要在乎谁是它的子组件,它通过遍历克隆每个子组件,通过React.cloneElement给子组件增加属性。 在React中编写真正可重复使用的组件肯定有点棘手,如果你遇到这样的麻烦,那么这种map和clone方法可以帮助到你。 React.children.forEach1React.Children.forEach(children, function[(thisArg)]) 类似React.children.map,但是没有返回值。 React.Children.count1React.Children.count(children) 返回 children 中的组件总数,等于传递给 map 或 forEach 的回调将被调用的次数。 React.Children.only1React.Children.only(children) 返回 children 中的唯一子集。否则抛出异常。当您想要确保组件只有一个子级时,这可能会派上用场,如果不满足此条件,则会抛出错误。 12345678export default React.createClass({ // ... render: function() { const {name, selectedValue, onChange, children} = this.props; const renderedChildren = children(radio(name, selectedValue, onChange)); return renderedChildren && React.Children.only(renderedChildren); }}); React.Children.toArray1React.Children.toArray(children) 将 children 不透明数据结构作为一个平面数组返回,并且 key 分配给每个子集。 如果你想在渲染方法中操作children集合,特别是如果你想在传递它之前重新排序或切割 this.props.children ,这将非常有用。 该方法将children组件的支持转换为纯JavaScript数组,这可以使您比React.Children.map提供更多的灵活性。React.Children.toArray最近在我想要渲染一个分隔符的列表中时,它们之间散布着很方便。这导致我创建一个完成这个的组件。 123456789101112131415161718192021import React from 'react';const IntersperseDividers = ({ children }) => ( <div> {React.Children.toArray(children).reduce((elements, child, i, array) => { elements.push(child); if (i < array.length - 1) { elements.push(<hr key={`${i}--divider`} />); } return elements; }, [])} </div>);const List = ({ data }) => ( <IntersperseDividers> {data.map((item, i) => ( <div key={i}> {item.value} </div> ))} </IntersperseDividers>);]]></content>
<tags>
<tag>react</tag>
</tags>
</entry>
<entry>
<title><![CDATA[React要手动绑定方法的原因]]></title>
<url>%2F2017%2F08%2F02%2Freact%2FReact%E8%A6%81%E6%89%8B%E5%8A%A8%E7%BB%91%E5%AE%9A%E6%96%B9%E6%B3%95%E7%9A%84%E5%8E%9F%E5%9B%A0%2F</url>
<content type="text"><![CDATA[我们从javascript开始吧在js中,函数的上下文是指函数调用的时候,而不是定义的时候。 有以下四中调用函数的模式: 函数调用模式 方法调用模式 构造函数调用模式 应用调用模式 所有这些使用的模式都不同地定义函数上下文。接下来我们看看各种模式的区别。 函数调用模式定义:如果在调用时没有.操作,那么上下文可能为window。 调用函数最直接的方法就是直接调用它: 1234var func = function(){ //...};func(); 这时的上下文(this)将会设置成javascript操作环境的全局变量,在浏览器中,它是window变量。 我们再来看另一个例子: 12345var unicorns = { func: function() { // ... }};var fun = unicorns.func;fun(); 你认为在func中的上下文为uniconrns对象?那是错误的,由于上下文时通过调用此函数时确定的,所以这里的上下文还是window。 方法调用模式定义:如果函数调用中有点操作,则上下文将会是一序列点中最右边的那个变量。 如上面的例子中,如果我们直接调用unicorns.func(),上下文会是unicorns对象。 123456789var frog = { RUN_SOUND: "POP!!", run: function() { return this.RUN_SOUND; }};frog.run(); // returns "POP!!" since this points to the `frog` object.var runningFun = frog.run;runningFun(); // returns "undefined" since this points to the window 构造函数模式定义:每次看到一个new函数名后,你this将指向一个新创建的新对象。 123function Wizard() { this.castSpell = function() { return "KABOOM"; }} 直接调用它将会是window(因为它是一个函数调用),但是如果通过new来调用: 12345function Wizard() { this.castSpell = function() { return "KABOOM"; };}var merlin = new Wizard(); // this is set to an empty object {}. Returns `this` implicitly.merlin.castSpell() // returns "KABOOM"; 这将会发生两件事: 函数将会有一个指向当前对象的上下文this。 如果没有指定return或者这个函数返回一个非对象值,this将从这个函数返回。 应用调用模式当你对函数有引用的时候,你可以通过两种方法来手动提供上下文: call apply 1234567function addAndSetX(a, b) { this.x += a + b;}var obj1 = { x: 1, y: 2 };addAndSetX.call(obj1, 1, 1); // this = obj1, obj1 after call = { x: 3, y : 2 }// It is the same as:// addAndSetX.apply(obj1, [1, 1]); 如果您需要调用从某个其他地方传递的函数(例如,作为参数到函数中)与某个上下文对象,这是非常方便的。它不是非常可用于异步回调,因为绑定与一个函数调用相结合。 要使用回调设置正确的上下文,您可能需要另一种方便的技术 - 您可以从中创建有界函数。 绑定功能有界函数是一个限定给定上下文的函数,这意味着无论你怎么调用它,它的上下文都是不变的。唯一例外是总是返回一个新上下文的new运算符。 要是普通函数变成有界函数,应该使用bind方法,bind方法将您要将函数绑定到的上下文作为第一个参数。其余的参数是将始终传递给这样的函数的参数。 结果返回有界函数。我们来看一个例子: 12345678910function add(x, y) { this.result += x + y;}var computation1 = { result: 0 };var boundedAdd = add.bind(computation1);boundedAdd(1, 2); // `this` is set to `computation1`. // computation1 after call: { result: 3 }var boundedAddPlusTwo = add.bind(computation1, 2);boundedAddPlusTwo(4); // `this` is set to `computation1`. // computation1 after call: { result: 9 } 被绑定了的函数甚至不能在通过call或apply改变上下文: 123456789var obj = { boundedPlusTwo: boundedAddPlusTwo };obj.boundedPlusTwo(4); // `this` is set to `computation1`. // even though method is called on `obj`. // computation1 after call: { result: 15 }var computation2 = { result: 0 };boundedAdd.call(computation2, 1, 2); // `this` is set to `computation1`. // even though context passed to call is // `computation2` // computation1 after call: { result: 18 } 您现在已经掌握了关于JavaScript的知识,现在让我们来看react中的情况。 怎么绑定以及绑定什么ECMAScript 2015(ECMAScript 6)引入了一种新的类语法,可用于创建React组件类。实际上,这个类语法是面向对象JavaScript 的旧的原型系统的语法糖。 这意味着ES2015类中的函数上下文调用遵循与其余JavaScript相同的原则。 123456789101112class Foo { constructor() { this.x = 2; this.y = 4; } bar() { // ... } baz() { // ... }} 与以下大致相同: 123456function Foo() { this.x = 2; this.y = 4; this.bar = function() { // ... }; this.baz = function() { // ... };} 记住这只是一个简化。在确定函数上下文调用的情况下,这个更复杂的逻辑遵循与上面的代码片段相同的原理。 React.createClass在这个语法下,绑定问题是不存在的,在传递给对象的对象中定义的所有方法React.createClass将自动绑定到组件实例。这意味着你可以随时使用setState,访问props和state等等这些方法。 尽管在99%的情况下可能完全可以接受,但它限制了您对任意设置上下文的能力 - 这可能是更复杂的代码库中的一个大问题。 ECMAScript 2015 classes在ECMAScript 2015 classes写法中,你需要手动绑定方法。 以下是React库中是可以识别为方法调用模式执行调用: 组件生命周期方法。它仅仅通过component.componentDidUpdate(…)方式调用(因此,this已经正确绑定到组件实例本身)。 render方法。它也是被识别为方法调用模式执行调用。大多数的非事件处理函数在render方法中调用,它已经被自动绑定到组件实例,所以你可以放心使用。 但是,传递给事件处理属性的函数可能有许多来源,甚至可能通过顶级组件的属性从非React级别传递给他们。 在React.createClassReact假定它们来自您的组件并自动绑定它们。但是在ES2015 classes中你有自由。在引擎中,它们被以函数调用模式调用。 这意味这,在默认情况下,你无法在事件处理程序中读取组件属性、状态和组件的方法,为此,你需要明确地绑定他们。 绑定事件处理程序的最佳位置是构造函数: 1234567891011121314class InputExample extends React.Component { constructor(props) { super(props); this.state = { text: '' }; this.change = this.change.bind(this); } change(ev) { this.setState({ text: ev.target.value }); } render() { let { text } = this.state; return (<input type="text" value={text} onChange={this.change} />); }} 这样你的事件处理程序的上下文将会绑定到组件实例中。 类属性有一个实验功能,称为类属性,可以帮助您明确避免绑定方法。它是用于在构造函数中定义字段和函数的语法糖。看起来像这样: 1234class InputExample extends React.Component { state = { text: '' }; // ...} 并编译成以下: 1234567class InputExample extends React.Component { constructor(...arguments) { super(...arguments); this.state = { text: '' }; } // ...} 那么怎么定义一个方法呢? 1234567class InputExample extends React.Component { state = { text: '' }; change = function(ev) { this.setState({ text: ev.target.value }); }; // ...} 所以现在,你得到一个等同于以下类: 12345678910class InputExample extends React.Component { constructor(...arguments) { super(...arguments); this.state = { text: '' }; this.change = function(ev) { this.setState({ text: ev.target.value }); }; } // ...} 但是这样有一个问题,this.change函数上下文还是错误的,所以我们要结合箭头函数: 12345678class InputExample extends React.Component { state = { text: '' }; change = ev => this.setState({text: ev.target.value}); render() { let {text} = this.state; return (<input type="text" value={text} onChange={this.change} />); }} 该解决方案的缺点是类属性仍处于实验阶段。这意味着此功能可以在ECMAScript 2016(也称为ECMAScript 7或ES7)的后续迭代中被删除,而不会发出警告。 createClass以及class语法编译完的不同我们先来看类写法: 12345678910111213141516class Todo extends Component{ handleClick(){ console.info(this); } method(){ console.info(this); } render(){ this.method(); return ( <div> <p onClick={this.handleClick}>Hello</p> </div> ) }} 编译完: 1234567891011121314151617181920212223242526var Todo = function (_Component) { _inherits(Todo, _Component); function Todo() { _classCallCheck(this, Todo); return _possibleConstructorReturn(this, _Component.apply(this, arguments)); } Todo.prototype.handleClick = function handleClick() { console.info(this); }; Todo.prototype.method = function method() { console.info(this); }; Todo.prototype.render = function render() { this.method(); return __WEBPACK_IMPORTED_MODULE_0_react___default.a.createElement( 'div', null, __WEBPACK_IMPORTED_MODULE_0_react___default.a.createElement( 'p', { onClick: this.handleClick }, 'Hello' ) ); }; return Todo;}(__WEBPACK_IMPORTED_MODULE_0_react__["Component"]); this.handleClick被放在{onClick: this.handleClick}中,所以当被调用的时候会被识别为函数调用模式,所以这时的上下文是null(为什么不是window或其他的???)]]></content>
<tags>
<tag>react</tag>
</tags>
</entry>
<entry>
<title></title>
<url>%2F2017%2F08%2F02%2Fwebpack%2F%E8%BF%81%E7%A7%BB%E5%88%B0webpack2%2F</url>
<content type="text"><![CDATA[迁移到webpack2Webpack is on the verge of having its latest major version released, and it’s expected to drop very soon. However, the main thing holding the release back is documentation, and the code is mostly written. I recently took the time to update our work project from Webpack 1 to 2, and thought I’d document the steps taken for anyone else who wants to make the move. You can also check out the migration guide on the Webpack documentation. Install the webpack2 The first thing to do is install the latest version. Because it’s not a stable release, you have to specify the exact beta version you’d like. At the time of writing it’s 2.1.0-beta.25: npm install --save-dev webpack@2.1.0-beta.25 If you’re using any other Webpack plugins, be aware that they might need updating. For example, the Extract Text Plugin has a v2 in beta also: npm install --save-dev extract-text-webpack-plugin@2.0.0-beta.4 module.loaders => module.rules This is not a breaking change because module.loaders will continue to be supported, but in the future it will be deprecated in favour of module.rules. This is just an easy renaming step. // before modules: { loaders: {...} } // after modules: { rules: {...} } resolve.modulesDirectories => resolve.modules Another renaming step, the resolve options have been renamed: // before resolve: { modulesDirectories: [...], } // after resolve: { modules: [...], } No webpack.optimize.OccurenceOrderPlugin It’s now included by default, so there is no need to have this in our config. Configuring loaders At work we’re using postcss and postcss-loader to load our CSS through Webpack. The loader used to expect a top level postcss key in the Webpack config. As of Webpack 2 this is no longer allowed; we can instead define an options key when we configure the loader. This replaces the query option from Webpack 1. Any plugin that looked for top level configuration will have to be swapped to this style. // before, in Webpack top level postcss: { plugins: ... } // after module: { rules: [{ test: /\.scss$/, use: [ { loader: 'postcss-loader', options: { plugins: ... } }, 'sass-loader' ] }] } ExtractTextPlugin changes The above change to loader configuration also makes it way easier to configure multiple loaders; previously it would only be possible to pass an array of loaders in string form to some plugins, such as ExtractTextPlugin: // Webpack 1 ExtractTextPlugin.extract( 'style-loader', 'css-loader!postcss-loader!sass-loader' ); This quickly got very hard to work with if you had to pass options: // Webpack 1 ExtractTextPlugin.extract( 'style-loader', 'css-loader?modules-true!postcss-loader!sass-loader' ); But now Webpack 2 can deal with arrays of objects to configure loaders. We can replace the above with: // Webpack 2 var loaders = [ { loader: 'css-loader', options: { modules: true } }, { loader: 'postcss-loader' }, { loader: 'sass-loader' } ] Whereas in Webpack 1 we used the key query for configuring loaders, we now use options. ExtractTextPlugin can now take this array, rather than only allowing the string form: // Webpack 2 ExtractTextPlugin.extract({ fallbackLoader: 'style-loader', loader: loaders, }) Stop Babel from compiling ES2015 modules Webpack 1 wasn’t able to parse ES2015 modules, so Babel would convert them into CommonJS. Webpack 2 can parse ES2015 modules, and is able to eliminate dead code based on which modules are used, so it’s recommended that you tell Babel not to convert modules into CommonJS. You can do this by changing your .babelrc: // before "presets": ["es2015"] // after "presets": [ ["es2015", { "modules": false }] ] We’ve seen a good file size saving by doing this, and hopefully this will continue to improve in the future! Fin Webpack 2 offers better performance, improved bundling and a much nicer experience when configuring it. Given that the code is so stable, despite its beta status, I highly recommend giving it a go on your projects when you can.]]></content>
</entry>
<entry>
<title></title>
<url>%2F2017%2F08%2F02%2Fwebpack%2Fwebpack%E9%81%87%E5%88%B0%E7%9A%84%E5%9D%91%2F</url>
<content type="text"><![CDATA[webpack的坑 引用jQuery插件时会报”jQuery is not defined”,解决方法: (1). Prefer unminified CommonJS/AMD over dist Most modules link the dist version in the main field of their package.json. While this is useful for most developers, for webpack it is better to alias the src version because this way webpack is able to optimize dependencies better (e.g. when using the DedupePlugin). // webpack.config.js module.exports = { ... resolve: { alias: { jquery: "jquery/src/jquery" } } }; However, in most cases the dist version works just fine as well. (2). Use the ProvidePlugin to inject implicit globals Most legacy modules rely on the presence of specific globals, like jQuery plugins do on $ or jQuery. In this scenario you can configure webpack, to prepend var $ = require(“jquery”) everytime it encounters the global $ identifier. var webpack = require("webpack"); ... plugins: [ new webpack.ProvidePlugin({ $: "jquery", jQuery: "jquery" }) ] (3). Use the imports-loader to configure this Some legacy modules rely on this being the window object. This becomes a problem when the module is executed in a CommonJS context where this equals module.exports. In this case you can override this with the imports-loader. Run npm i imports-loader --save-dev and then module: { loaders: [ { test: /[\/\\]node_modules[\/\\]some-module[\/\\]index\.js$/, loader: "imports?this=>window" } ] } The imports-loader can also be used to manually inject variables of all kinds. But most of the time the ProvidePlugin is more useful when it comes to implicit globals. (4). Use the imports-loader to disable AMD There are modules that support different module styles, like AMD, CommonJS and legacy. However, most of the time they first check for define and then use some quirky code to export properties. In these cases, it could help to force the CommonJS path by setting define = false. module: { loaders: [ { test: /[\/\\]node_modules[\/\\]some-module[\/\\]index\.js$/, loader: "imports?define=>false" } ] } (5). Use the script-loader to globally import scripts If you don’t care about global variables and just want legacy scripts to work, you can also use the script-loader. It executes the module in a global context, just as if you had included them via the <script> tag. (6). Use noParse to include large dists When there is no AMD/CommonJS version of the module and you want to include the dist, you can flag this module as noParse. Then webpack will just include the module without parsing it, which can be used to improve the build time. This means that any feature requiring the AST, like the ProvidePlugin, will not work. module: { noParse: [ /[\/\\]node_modules[\/\\]angular[\/\\]angular\.js$/ ] } 常见的loader { test: /\.js/, loader: "babel-loader", query: { "presets": ["es2015", 'stage-0'], plugins: [] }, exclude: /(node_modules)/ }, { test: /\.css$/, //注意:此处不能有autoprefix-loader loader: ExtractText.extract('style-loader', 'css-loader') }, { test: /\.(png|gif|jpg|jpeg)$/, loader: "url?name=img/[hash:8].[ext]" }, { test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?name=font/[name].[ext]&limit=10000&minetype=application/font-woff' }, { test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?name=font/[name].[ext]&limit=10&minetype=application/font-woff' }, { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?name=font/[name].[ext]&limit=10&minetype=application/octet-stream' }, { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file' }, { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'url?limit=10&minetype=image/svg+xml' } 样式的loader (1)style-loader|css-loader is the way to do it just with css (2)style-loader|css-loader|postcss-loader is the way to post-process css (3)style-loader|css-loader|less-loader is the way to do it if you want to use less (4)style-loader|css-loader|postcss-loader|less-loader is the way to post-process the compiled less (css) ES6引用art-template,报错:Module not found: Error: Cannot resolve module 'fs',解决方法: //webpack.config.js module.exports={ node: { fs: "empty" } }; 参考链接 webpack开发时打包第三方库都比较大,可以通过配置alias指向压缩版本: resolve: { alias: { modernizr$: path.resolve(__dirname, "./.modernizrrc"), bootstrap: path.join(__dirname, "./node_modules/bootstrap/dist/js/bootstrap.min.js"), bootstrapCss: path.join(__dirname, "./node_modules/bootstrap/dist/css/bootstrap.min.css"), fontAwesomeCss: path.join(__dirname, "./node_modules/font-awesome/css/font-awesome.min.css") } } 引用第三方插件如:ulynlist,需要配置别名: alias: { 'ulynlist.table': path.join(__dirname, './src/sslib/ulynlist/ulynlist.table.js'), 'ulynlist.pager': path.join(__dirname, './src/sslib/ulynlist/ulynlist.pager.js'), artTemplate: path.join(__dirname, './node_modules/art-template') } import样式文件页面会有闪烁现象,这是可以通过extract-text-webpack-plugin抽取样式文件,就不会有这个问题了 使用ES6 + webpack + angular教程参考链接 合并jquery和第三方插件时,外面是读取不到$和jQuery的,所以我们可以通过expose-loader把jQuery对象导出到全局: You can either do this when you require it: require("expose?$!jquery"); or you can do this in your config: loaders: [ { test: require.resolve('jquery'), loader: 'expose?jQuery!expose?$' } ] 相同的道理,如果插件里有this,则我们可以通过imports-loader把this当成window处理: { test: require.resolve('respond.js'), loader: 'imports?this=>window' } 使用第三方插件,如果其没有判断commonjs这一层,则我们可以配合exports-loader和imports-loader使用,如eos3还有eos服务,eos3需要导出eos对象,eos服务的js需要导入eos这个对象: import 'exports?eos!./lib/eos3/eos3'; //这里define设为false,防止组件判断为AMD模块 import 'imports?define=>false,this=>window!./lib/auth/dmService'; webpack-dev-server默认是localhost访问,不能通过ip访问,我们可以配置如下: webpack-dev-server –host 0.0.0.0 大项目打包时经常内存溢出: 修改打包命令: 1node --max_old_space_size=4096 ./node_modules/webpack/bin/webpack.js --env=prod --progress --profile --colors]]></content>
</entry>
<entry>
<title></title>
<url>%2F2017%2F08%2F02%2Fwebpack%2Fwebpack%E5%AD%A6%E4%B9%A006%E2%80%94%E2%80%94%E6%80%8E%E4%B9%88%E5%86%99loader%2F</url>
<content type="text"><![CDATA[webpack学习06——怎么写loader一个loader是一个node模块,导出一个函数。这个函数当资源被转换时会执行,这个loader有一个入参:待转换资源名称。可以在loader中通过this访问上下文。 一个同步的loader可以仅仅返回一个值。在其他情况下,loader可以通过this.callback(err, values)返回多个值。 一个loader被期望返回一个或两个值,第一个值返回字符串或buffer类型的javascript代码,第二个返回sourceMap。 在复杂的情况下,当多个loader链接时,仅仅只要最后一个loader返回资源文件,仅仅第一个loader期望返回一个或两个值(javascript代码或buffer)。 Example: module.exports = function(source){ return source; }; // // Identity loader with SourceMap support module.exports = function(source, map) { this.callback(null, source, map); }; 指南(按优先顺序排序,第一个应该得到最高优先级) 只做一个单一的任务loader可以被链接,他们不应该转换为javascript代码,如果他们不需要的话。例如:从模板中通过查询参数渲染html,我会编写一个从源代码中编译的loader,执行他并返回一个包含包含html的字符串,这是不好的。而是我应该编写装载程序在这个用例中的每一个任务,并将它们全部应用(流水线): jade-loader: Convert template to a module that exports a function. apply-loader: Takes a function exporting module and returns raw result by applying query parameters. html-loader: Takes HTML and exports a string exporting module. generate modules that are modular加载程序生成模块应尊重相同的设计原则,如正常模块。例子:这是一个糟糕的设计:(不模块化,全局状态,…) require("any-template-language-loader!./xyz.atl"); var html = anyTemplateLanguage.render("xyz"); 标志本身缓存如果可能的话大多数装载机是可缓存的,所以他们应该标志本身作为缓存。只要在loader中调用cacheable。 //Cacheable identity loader module.exports = function(source){ this.cacheable(); return source(); }; not keep state between runs and modules一个加载程序应该独立于编译的其他模块(由装载程序发布的这些模块的期望)。一个程序应该独立于以前的编译的模块。 依赖如果loader需要依赖第三方资源(如从系统中读取文件),他们必须要写清楚,此信息用于无效的缓存装载机和编译在观看模式。 // Loader adding a header var path = require("path"); module.exports = function(source) { this.cacheable(); var callback = this.async(); var headerPath = path.resolve("header.js"); this.addDependency(headerPath); fs.readFile(headerPath, "utf-8", function(err, header) { if(err) return callback(err); callback(null, header + "\n" + source); }); }; 解决依赖关系一些语言有自己的解决依赖图式,例如css的@import和url(...)。这些必须被模块系统解决。 有两个方法可以做到: 把他们转换成require; 使用this.resolve解析路径; 例子1:css-loader把依赖转换成require其他样式文件。例子2:less-loader不转换为require,因为因为所有的less文件需要编译一次跟踪变量和混合,因此,less-loader扩展less编译器一个自定义的路径解决方法,该自定义逻辑使用this.resolve解决模块的系统配置文件(走样,自定义模块目录,等)。If the language only accept relative urls (like css: url(file) always means ./file), there is the ~-convention to specify references to modules: url(file) -> require("./file") url(~module) -> require("module") 抽离公共代码don’t generate much code that is common in every module processed by that loader. Create a (runtime) file in the loader and generate a require to that common code. 不应嵌入绝对路径don’t put absolute paths in to the module code. They break hashing when the root for the project is moved. There is a method stringifyRequest in loader-utils which converts an absolute path to an relative one. Example: var loaderUtils = require("loader-utils"); return "var runtime = require(" + loaderUtils.stringifyRequest(this, "!" + require.resolve("module/runtime")) + ");"; use a library as peerDependencies when they wrap itusing a peerDependency allows the application developer to specify the exact version in package.json if desired. The dependency should be relatively open to allow updating the library without needing to publish a new loader version. "peerDependencies": { "library": "^1.3.5" } programmable objects as query-optionthere are situations where your loader requires programmable objects with functions which cannot stringified as query-string. The less-loader, for example, provides the possibility to specify LESS-plugins. In these cases, a loader is allowed to extend webpack’s options-object to retrieve that specific option. In order to avoid name collisions, however, it is important that the option is namespaced under the loader’s camelCased npm-name.]]></content>
</entry>
<entry>
<title></title>
<url>%2F2017%2F08%2F02%2Fwebpack%2Fwebpack%E5%AD%A6%E4%B9%A005%E2%80%94%E2%80%94%E9%95%BF%E6%9C%9F%E7%BC%93%E5%AD%98%2F</url>
<content type="text"><![CDATA[webpack学习05——长期缓存有两种级别的添加hash方法: Compute a hash of all chunks and add it. Compute a hash per chunk and add it. 方法一:只有一个hash{ output: { path: path.join(__dirname, "assets", "[hash]"), publicPath: "assets/[hash]/", filename: "output.[hash].bundle.js", chunkFilename: "[id].[hash].bundle.js" } } 方法二:每个块都有一个hashoutput: { chunkFilename: "[chunkhash].bundle.js" } Note that you need to reference the entry chunk with its hash in your HTML. You may want to extract the hash or the filename from the stats. In combination with Hot Code Replacement you must use option 1, but not on the publicPath config option. 从文件名获取状态You probably want to access the final filename of the asset to embed it into your HTML. This information is available in the webpack stats. If you are using the CLI you can run it with –json to get the stats as JSON to stdout. You can add a plugin such as assets-webpack-plugin to your configuration which allows you to access the stats object. Here is an example how to write it into a file: plugins: [ function() { this.plugin("done", function(stats) { require("fs").writeFileSync( path.join(__dirname, "..", "stats.json"), JSON.stringify(stats.toJson())); }); } ] The stats JSON contains a useful property assetsByChunkName which is a object containing chunk name as key and filename(s) as value. Note: It’s an array if you are emitting multiple assets per chunk. I. e. a JavaScript file and a SourceMap. The first one is your JavaScript source.]]></content>
</entry>
<entry>
<title></title>
<url>%2F2017%2F08%2F02%2Fwebpack%2Fwebpack%E5%AD%A6%E4%B9%A004%E2%80%94%E2%80%94%E5%8E%8B%E7%BC%A9%E4%BC%98%E5%8C%96%2F</url>
<content type="text"><![CDATA[webpack学习04——压缩优化压缩为了压缩你的脚本(和你的样式,如果你用css-loader的话),webpack支持下面两个途径: --optimize-minimize 或者 new webpack.optimize.UglifyJsPlugin() webpack给你的模块和块赋予了id来区别他们,webpack可以为经常用到的id通过下面途径得到最小id长度: --optimize-occurrence-order resp. new webpack.optimize.OccurrenceOrderPlugin() 去重如果你使用第三方库有相同依赖时,会重复引用相同的文件,webpack可以找到并去重,默认是不开启的,可以使用一下方法开启: --optimize-dedupe resp. new webpack.optimize.DedupePlugin() 块优化限制快的最大大小 –optimize-max-chunks 15 new webpack.optimize.LimitChunkCountPlugin({maxChunks: 15})限制块的最小大小 –optimize-min-chunk-size 10000 new webpack.optimize.MinChunkSizePlugin({minChunkSize: 10000}) Webpack会照顾它通过合并块(它会合并块,有重复的模块)。不会有东西合并到入口块,所以不会影响初始页面加载时间。 单页应用A Single-Page-App is the type of web app webpack is designed and optimized for. You may have split the app into multiple chunks, which are loaded at your router. The entry chunk only contains the router and some libraries, but no content. This works great while your user is navigating through your app, but for initial page load you need 2 round trips: One for the router and one for the current content page. If you use the HTML5 History API to reflect the current content page in the URL, your server can know which content page will be requested by the client code. To save round trips to the server you can include the content chunk in the response: This is possible by just adding it as script tag. The browser will load both chunks parallel. <script src="entry-chunk.js" type="text/javascript" charset="utf-8"></script> <script src="3.chunk.js" type="text/javascript" charset="utf-8"></script> You can extract the chunk filename from the stats. (stats-webpack-plugin could be used for exports the build stats) 多页应用var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin"); module.exports = { entry: { p1: "./page1", p2: "./page2", p3: "./page3", ap1: "./admin/page1", ap2: "./admin/page2" }, output: { filename: "[name].js" }, plugins: [ new CommonsChunkPlugin("admin-commons.js", ["ap1", "ap2"]), new CommonsChunkPlugin("commons.js", ["p1", "p2", "admin-commons.js"]) ] }; // <script>s required: // page1.html: commons.js, p1.js // page2.html: commons.js, p2.js // page3.html: p3.js // admin-page1.html: commons.js, admin-commons.js, ap1.js // admin-page2.html: commons.js, admin-commons.js, ap2.js Advanced hint: You can run code inside the commons chunk: var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin"); module.exports = { entry: { p1: "./page1", p2: "./page2", commons: "./entry-for-the-commons-chunk" }, plugins: [ new CommonsChunkPlugin("commons", "commons.js") ] }; See also multiple-entry-points example and advanced multiple-commons-chunks example.]]></content>
</entry>
<entry>
<title></title>
<url>%2F2017%2F08%2F02%2Fwebpack%2Fwebpack%E5%AD%A6%E4%B9%A003%E2%80%94%E2%80%94%E6%A0%B7%E5%BC%8F%2F</url>
<content type="text"><![CDATA[webpack学习03——样式嵌入样式{ test: /\.css$/, loader: "style-loader!css-loader" } 这种情况下会在页面添加style标签式的样式 抽成样式文件可以使用extract-text-webpack-plugin抽成样式文件。 var ExtractTextPlugin = require("extract-text-webpack-plugin"); ... loaders: [ // Extract css files { test: /\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader") }, // Optionally extract less files // or any other compile-to-css language { test: /\.less$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader!less-loader") } // You could also use other loaders the same way. I. e. the autoprefixer-loader ], plugins: [ new ExtractTextPlugin("[name].css") ] 所有样式文件合并成一个样式文件plugins: [ new ExtractTextPlugin("style.css", { allChunks: true }) ] 公共样式和CommonsChunkPlugin一起使用,commons块就会生成commons.css样文件。 plugins: [ new webpack.optimize.CommonsChunkPlugin("commons", "commons.js"), new ExtractTextPlugin("[name].css") ]]]></content>
</entry>
<entry>
<title></title>
<url>%2F2017%2F08%2F02%2Fwebpack%2Fwebpack%E5%AD%A6%E4%B9%A002%E2%80%94%E2%80%94%E4%BB%A3%E7%A0%81%E5%88%86%E5%89%B2%2F</url>
<content type="text"><![CDATA[webpack学习02——代码分割定义一个代码分割点commonjs: require.ensurerequire.ensure(dependencies, callback) example: //require.ensure仅仅加载而不执行模块 require.ensure(["module-a", "module-b"], function(require) { var a = require("module-a"); // ... }); AMD: requirerequire(dependencies, callback) example: //AMD的require加载并会执行模块 require(["module-a", "module-b"], function(a, b) { // ... }); ES6模块webpack1是不支持原生es6模块的,可以通过babal转换 块的内容所有分隔的文件会成为一个块,这个块是由其依赖递归加进去的。 块压缩如果两个块包含同一个模块,他们会被合并成一个。这可能造成块有相同的父级。如果一个模块在一个块的所有父级中是可获取的,这个模块将会在这个块中删除。如果一个块包含另一个块的所有模块,则存储这个块,它实现多个块。 块加载根据配置target目标,将将块加载的运行时逻辑添加到包中。例如:web目标块通过jsonp加载。只有一个块被加载一次,并行请求被合并成一个。运行时检查加载的块是否完成多个块。 块的类型入口块一个入口块包含了请求时加载的模块。如果这个块包含模块0则加载并执行它,如果没有,它等待有请求模块0的块。 正常块一个正常的块不会在运行时加载,它仅仅包含一些模块,这个结构依赖于块加载算法,例如:如果目标是jsonp,则这些模块会包含一个jsonp回调函数,这个块当然还包含它负责的一些块id列表。 初始化块(非入口)一个初始化块是一个正常的块,不同的仅仅是压缩工具视它更重要,因为它计算向初始加载时间(像入口块),这个块类型可以结合CommonsChunkPlugin发生。 分隔app和vendor代码var webpack = require("webpack"); module.exports = { entry: { app: "./app.js", vendor: ["jquery", "underscore", ...], }, output: { filename: "bundle.js" }, plugins: [ new webpack.optimize.CommonsChunkPlugin(/* chunkName= */"vendor", /* filename= */"vendor.bundle.js") ] }; vendor块将会从移除所有在app块中的模块,使得bundle块仅仅包含你的代码而不包含其依赖。 <script src="vendor.bundle.js"></script> <script src="bundle.js"></script> 多入口块加载多入口通过CommonsChukPlugin运行时被移动到公共块,入口文件变成初始化块。只有当初始块可以被加载时,其他入口块才可以被加载。 var webpack = require("webpack"); module.exports = { entry: { a: "./a", b: "./b" }, output: { filename: "[name].js" }, plugins: [ new webpack.optimize.CommonsChunkPlugin("init.js") ] } <script src="init.js"></script> <script src="a.js"></script> <script src="b.js"></script> 公共块CommonsChunkPlugin可以移动发生在多个入口的模块到一个新的块(公共块),运行时也移动到公共块,这意味着旧的入口块已经变成初始化块。 压缩优化插件 LimitChunkCountPlugin MinChunkSizePlugin AggressiveMergingPlugin 命名块The require.ensure function accepts an additional 3rd parameter. This must be a string. If two split point pass the same string they use the same chunk. require.includerequire.include(request) require.include is a webpack specific function that adds a module to the current chunk, but doesn’t evaluate it (The statement is removed from the bundle). Example: require.ensure(["./file"], function(require) { require("./file2"); }); // is equal to require.ensure([], function(require) { require.include("./file"); require("./file2"); }); require.include can be useful if a module is in multiple child chunks. A require.include in the parent would include the module and the instances of the modules in the child chunks would disappear.]]></content>
</entry>
<entry>
<title></title>
<url>%2F2017%2F08%2F02%2Fwebpack%2Fwebpack%E5%AD%A6%E4%B9%A001%E2%80%94%E2%80%94%E7%AE%80%E4%BB%8B%2F</url>
<content type="text"><![CDATA[webpack学习01——简介 webpack是一个模块打包工具, webpack特性: 插件:webpack有大量的插件,大多数的功能是使用这个接口的内部插件,这使得webpack很很大的灵活性; 性能:webpack使用异步I/O和多级缓存机制,使得webpack有更快的速度,而且编辑速度超快; 加载器(loaders):webpack通过定义loaders预处理文件,这允许你打包任何资源而不仅仅是javascript,你也可以自己写适合自己的加载器; 模块化:webpack支持AMD和CommonJs模块化规范,它能够聪明地分析你的代码,甚至有一个评估引擎来评估简单的表达式,这个允许你支持大多数现存模块化规范 代码分割:webpack允许你把代码分割成不同的块thunk,块是按需加载的,这个可以减少初始加载所发费的时间。 代码压缩:webpack可以压缩你的代码,它当然也支持通过哈希缓存; 开发工具:WebPACK支持调试简单sourceurls和sourcemaps。还可以通过development middleware和development server实现自动刷新功能; 多个目标:webpack的首要目标是web,但是它也支持在WebWorkers、Node.js中使用; 加载器: 转换文件; 加载器可以通过管道被链接,最后一个加载器则返回javascript,其他的加载器则可以返回任意的格式代码; 加载器可以是同步或异步的; 加载器在Node.js环境中运行,所以你可以做任何你想要的事情; 加载器支持查询参数; 加载器可以在配置中绑定到扩展名或正则表达式的监听; 加载器可以被发布到npm上或从npm中安装下来; Normal modules can export a loader in addition to the normal main via package.json loader. 加载器可以访问配置; 插件可以向加载器提供更多的特性和功能; 加载器可以调用额外的任意文件; 。。。 加载器解决方案 加载器类似模块,一个加载器导出一个兼容于Node的javascript函数,一般情况下,你通过npm管理你的加载器,当然你也可以作为文件在你的项目中使用 引用加载器:加载器一般以xxx-loader命名,xxx是其名字,例如:json-loader; 你可以通过全名xxx-loader或者短名xxx来使用加载器; 加载器的命名惯例和优先顺序是由resolveLoader.moduleTemplates配置决定的; 加载器的命名惯例一些情况下是很有用的,特别是在require()里面引用加载器的时候; 加载器的使用情况: 在require()语句内; //使用文件loader.js来转换file.txt文件 require('./loader!./dir/file.txt'); //使用jade-loader,如果配置文件中已经有绑定到此文件的加载器,他们仍然会运行的 require('jade!./template.jade'); //由less-loader->css-loader->style-loader转换顺序,由于有前缀`!`,所以如果在配置文件中已经 //有绑定到此文件的加载器,他们将不会运行 require('!style!css!less!bootstrap/less/bootstrap.less'); 在配置文件中配置;(推荐这种方式) { module: { loaders: [ {test: /\.jade$/, loader: 'jade'}, {test: /\.css$/, loader: 'style!css'}, {test: /\.css$/, loaders: ['style', 'css']} ] } } 在CLI命令行中配置; webpack --module-bind jade --module-bind 'css=style!css' 加载器的查询参数 可以在加载器后面加上?添加多个查询参数,例如:url-loader?mimetype=image/png.大多数的加载器支持正常的格式?key=value&key2=value2和JSON对象格式?{key: "value", key2: "value2"} 在require()语句中: require('url-loader?mimetype=image/png!./file.png'); 在配置中: {test: /\.png$/, loader: 'url-loader?mimetype=image/png'} 或者 { test: /\.png$/, loader: 'url-loader', query: { mimetype: 'image/png' } } 在CLI中: webpack --module-bind "png=url-loader?mimetype=image/png"]]></content>
</entry>
<entry>
<title><![CDATA[使用css视口单位]]></title>
<url>%2F2016%2F08%2F02%2Fcss%2F%E4%BD%BF%E7%94%A8css%E8%A7%86%E5%8F%A3%E5%8D%95%E4%BD%8D%2F</url>
<content type="text"><![CDATA[css提供了许多单位用来规定元素,最熟悉的莫属px、%、pt、em以及最近比较火的rem,还有其他两个vw、vh,它们是相对单位,但是不同于em和rem那样相对于当前元素或相对于根元素,他们是相对于视口,一个视口单位等于1%的视口的宽度(vw)或高度(vh)。 这是很有用的。vw单位可以用于一些有大小的规则(如:font-size、或者一个div的高度),下面是一些使用案例: 使标题固定如果你想让一个标题占满横向屏幕且让他固定在一行,你可以使用vw单位,这实际上是用原生的方法实现了jquery插件FitText的功能,但是相对于使用vw,FitText需要手动管理大小,使用检查工具,是一个快速的方法以确定适当的值。 //html <h1>Always a great fit!</h1> //css h1{ font-size: 12vw; text-align: center; } Infinite LinesWhilst building the falcon633 WordPress theme (used on this site), I needed to create an angled background that would appear to continue indefinitely. This is achievable by 1) making sure that the angle in the centre stays the same regardless of window size and 2) setting the height to be relative to the viewport width. I used an SVG background for the overall cut-out then set the height based on the width using vw units. 简单的视频包装Let’s say you want to set the proportions of an element, an iframe, to stay at a fixed aspect ratio. You previously might have chosen to create a relative div filling the required space, then set carefully selected padding values with iframe inside absolutely positioned to cling to div on all sides (e.g. the approach demonstrated here). A better solution could be to use the vw and vh units. This way you can set your height and width directly on the element in question, whilst also keeping the ‘layers-for-layout’ number down. 全屏的hero Image你要做的只是在body和html上运用height: 100%,然后在元素上简单地使用width: 100vw; heihgt: 100vh就行了。 div居中一个常见的需求是要让一个div在页面中居中,这时候只要设置margin: 20vh 20vw; width: 60vw, height: 60vh; padding: 10vh 10vw即可。 浏览器支持情况IE9及以上]]></content>
<tags>
<tag>css</tag>
</tags>
</entry>
<entry>
<title><![CDATA[HTML不同空格的特性和表现研究]]></title>
<url>%2F2016%2F08%2F02%2Fcss%2FHTML%E4%B8%8D%E5%90%8C%E7%A9%BA%E6%A0%BC%E7%9A%84%E7%89%B9%E6%80%A7%E5%92%8C%E8%A1%A8%E7%8E%B0%E7%A0%94%E7%A9%B6%2F</url>
<content type="text"><![CDATA[Unicode编码空格 &nbsp;: 不换行空格,用于不会被浏览器判断为可以在中间打断,比如:There is&nbsp;Space,如果会换行,只会在There和is之间换行,而不会在is和Space之间换行。 跟随字体大小产生相应空白的空格:&ensp;(1/2em),&emsp;(1em),&thinsp;(1/6em),这类就很适合坐表单label的排版了,坑爹,终于找到好的解决方法了]]></content>
<tags>
<tag>css</tag>
</tags>
</entry>
<entry>
<title><![CDATA[angular2遇到的问题.md]]></title>
<url>%2F2016%2F08%2F02%2Fangular2%2F%E9%81%87%E5%88%B0%E7%9A%84%E9%97%AE%E9%A2%98%2F</url>
<content type="text"><![CDATA[在组件时moduleId: module.id时,报Cannot find name 'module'? 解决方法: You need to install node ambientDependencies. Typings.json "ambientDependencies": { "node": "github:DefinitelyTyped/DefinitelyTyped/node/node.d.ts#138ad74b9e8e6c08af7633964962835add4c91e2" } Another way can use typings manager to install node definition file globally: typings install dt~node --global --save Then your typings.json file will look like this: "globalDependencies": { "node": "registry:dt/node#6.0.0+20160608110640" } If you use Typescript 2^ just use the following command: npm install @types/node --global --save Or install it within the project: npm install @types/node --save and then add a reference to it from your bootstrap: ///<reference path="../../node_modules/@types/node/index.d.ts" /> 而后出现Url.match is not a function? 解决方法: 报错:Property 'find' does not exist on type 'Hero[]'.? 解决方法:在typings.json中配置如下代码 { "globalDependencies": { "core-js": "registry:dt/core-js#0.0.0+20160725163759", "jasmine": "registry:dt/jasmine#2.2.0+20160621224255", "node": "registry:dt/node#6.0.0+20160909174046" } }]]></content>
<tags>
<tag>angular</tag>
</tags>
</entry>
</search>