-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
389 lines (222 loc) · 801 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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>proxy和reflect实现数据拦截处理</title>
<link href="/2019/03/14/proxy/"/>
<content type="html"><![CDATA[<p>最近在面试时,遇到双向绑定的原理,我只了解脏检查和object.defineProperty进行数据挟持的方法。面试官问我还知道别的方法吗?(what?)不造啊</p><p>后来私下查了一下,es6的proxy可对数据进行一层“拦截”,同样能够实现对数据的一些操作:比如双向绑定。</p><h2 id="proxy"><a href="#proxy" class="headerlink" title="proxy"></a>proxy</h2><p>Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以是一种“元编程”,即对编程语言进行编程。</p><p>简单地理解,就是在目标对象之前假设一层“拦截”,外界对改对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤。基本就想字面意思:代理。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> proxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(target, handler);</span><br></pre></td></tr></table></figure></p><p>其中,target、handler 均为对象,target 指目标对象(需要拦截的),handler 中定义拦截行为。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = <span class="keyword">new</span> <span class="built_in">Proxy</span>({}, {</span><br><span class="line"> <span class="keyword">get</span>: function (target, key, receiver) {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`getting <span class="subst">${key}</span>!`</span>); <span class="comment">// es 6 新増的字符串拓展写法</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">Reflect</span>.get(target, key, receiver);</span><br><span class="line"> },</span><br><span class="line"> <span class="keyword">set</span>: function (target, key, value, receiver) {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`setting <span class="subst">${key}</span>!`</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">Reflect</span>.set(target, key, value, receiver);</span><br><span class="line"> }</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">obj.count = <span class="number">1</span></span><br><span class="line"><span class="comment">// setting count!</span></span><br><span class="line">++obj.count</span><br><span class="line"><span class="comment">// getting count!</span></span><br><span class="line"><span class="comment">// setting count!</span></span><br><span class="line"><span class="comment">// 2</span></span><br></pre></td></tr></table></figure><p>上面重新定义了属性的读取和设置,搭配Reflect一起使用。</p><p>注意,要使得Proxy起作用,必须针对Proxy实例(上例是proxy对象)进行操作,而不是针对目标对象(上例是空对象)进行操作。</p><p>Proxy 实例也可以作为其他对象的原型对象。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> proxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>({}, {</span><br><span class="line"> <span class="keyword">get</span>: function(target, property) {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">35</span>;</span><br><span class="line"> }</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> obj = <span class="built_in">Object</span>.create(proxy);</span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> Object.create()方法接受两个参数:Object.create(obj,propertiesObject) ;</span></span><br><span class="line"><span class="comment"> obj:一个对象,应该是新创建的对象的原型。</span></span><br><span class="line"><span class="comment"> propertiesObject:可选。该参数对象是一组属性与值,该对象的属性名称将是新创建的对象的属性名称,值是属性描述符(这些属性描述符的结构与Object.defineProperties()的第二个参数一样)。注意:该参数对象不能是 undefined,另外只有该对象中自身拥有的可枚举的属性才有效,也就是说该对象的原型链上属性是无效的。</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">obj.time <span class="comment">// 35</span></span><br></pre></td></tr></table></figure></p><p>proxy对象是obj对象的原型,obj对象本身并没有time属性,所以根据原型链,会在proxy对象上读取该属性,导致被拦截。</p><p>拦截器上可以设置多个操作,如果没有设置,直接落在目标对象上,按原先的方法产生结果。</p><h3 id="操作"><a href="#操作" class="headerlink" title="操作"></a>操作</h3><ul><li>get(target, propKey, receiver):拦截对象属性的读取,比如proxy.foo和proxy[‘foo’]。</li><li>set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = v或proxy[‘foo’] = v,返回一个布尔值。</li><li>has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。</li><li>deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。</li><li>ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for…in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。</li><li>getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。</li><li>defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。</li><li>preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。</li><li>getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。</li><li>isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。</li><li>setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。</li><li>apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(…args)、proxy.call(object, …args)、proxy.apply(…)。</li><li>construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(…args)。</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//get</span></span><br><span class="line"><span class="keyword">var</span> person = {</span><br><span class="line"> name: <span class="string">"张三"</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> proxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(person, {</span><br><span class="line"> <span class="keyword">get</span>: function(target, property) {</span><br><span class="line"> <span class="keyword">if</span> (property <span class="keyword">in</span> target) {</span><br><span class="line"> <span class="keyword">return</span> target[property];</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">ReferenceError</span>(<span class="string">"Property \""</span> + property + <span class="string">"\" does not exist."</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">proxy.name <span class="comment">// "张三"</span></span><br><span class="line">proxy.age <span class="comment">// 抛出一个错误</span></span><br></pre></td></tr></table></figure><h3 id="this问题"><a href="#this问题" class="headerlink" title="this问题"></a>this问题</h3><p>虽然 Proxy 可以代理针对目标对象的访问,但它不是目标对象的透明代理,即不做任何拦截的情况下,也无法保证与目标对象的行为一致。主要原因就是在 Proxy 代理的情况下,目标对象内部的this关键字会指向 Proxy 代理。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> target = {</span><br><span class="line"> m: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span> === proxy);</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"><span class="keyword">const</span> handler = {};</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> proxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(target, handler);</span><br><span class="line"></span><br><span class="line">target.m() <span class="comment">// false</span></span><br><span class="line">proxy.m() <span class="comment">// true</span></span><br></pre></td></tr></table></figure></p><h2 id="reflect"><a href="#reflect" class="headerlink" title="reflect"></a>reflect</h2><p>Reflect对象的设计目的有这样几个。</p><p>(1) 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。也就是说,从Reflect对象上可以拿到语言内部的方法。<br><img src="/images/proxy_01.jpg"><br>(2) 修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 老写法</span></span><br><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> <span class="built_in">Object</span>.defineProperty(target, property, attributes);</span><br><span class="line"> <span class="comment">// success</span></span><br><span class="line">} <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="comment">// failure</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 新写法</span></span><br><span class="line"><span class="keyword">if</span> (<span class="built_in">Reflect</span>.defineProperty(target, property, attributes)) {</span><br><span class="line"> <span class="comment">// success</span></span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// failure</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>(3) 让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 老写法</span></span><br><span class="line"><span class="string">'assign'</span> <span class="keyword">in</span> <span class="built_in">Object</span> <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 新写法</span></span><br><span class="line"><span class="built_in">Reflect</span>.has(<span class="built_in">Object</span>, <span class="string">'assign'</span>) <span class="comment">// true</span></span><br></pre></td></tr></table></figure></p><p>(4)Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。</p><p>这就是前面使用的功能,使用reflect完成默认行为。保证原生行为能够正常执行。</p>]]></content>
</entry>
<entry>
<title>浅谈发布/订阅模式</title>
<link href="/2019/02/26/observer/"/>
<content type="html"><![CDATA[<p>观察者模式时软件设计模式的一种。<br>在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实时事件处理系统。</p><a id="more"></a><p>我们要实现用邮件订阅一个数据库,然后数据库有更新时就向我们发布信息。因此要实现:</p><p>一个方法用于订阅————subscribe<br>一个方法用于发布————publish<br>一个方法用于取消订阅————unSubscribe<br>一个用来存储我们订阅信息的数据“注册表”对象————store</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> publisher = {</span><br><span class="line"> store: {},</span><br><span class="line"> subscribe: <span class="function"><span class="keyword">function</span> (<span class="params">type, fn</span>) </span>{},</span><br><span class="line"> unSubscribe: <span class="function"><span class="keyword">function</span> (<span class="params">type, fnName</span>) </span>{},</span><br><span class="line"> publish: <span class="function"><span class="keyword">function</span> (<span class="params">type, message</span>) </span>{}</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>store把它设计成一个对象是因为考虑到可能有更多类型的数据,比如 游戏,金融,投资理财等等,所以就把它设计成对象以key-value的形式存储订阅者, 比如:{‘game’:[],’monetary’:[]}该形式</li><li>subscribe 则是publisher提供给我们的对其进行订阅的方法,参数是type和 fn。type就是我们订阅的类型,fn就是我们提供给publisher用于通知我的渠道 (邮箱)。在JavaScript中更多的是回调函数。</li><li>unSubscribe 是publisher提供给我们的对其进行取消订阅的方法,参数是type和 fnName。type就不多说了,fnName则是我们提供给publisher用于取消订阅的标志,比如说邮箱,或者是回调函数的名字等等。</li><li>publish说到比较重要的方法,这就是publisher向所有订阅者发布消息的方法。</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> publisher = {</span><br><span class="line"> store: {},</span><br><span class="line"> subscribe: <span class="function"><span class="keyword">function</span> (<span class="params">type, fn</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">Object</span>.keys(<span class="keyword">this</span>.store).indexOf(type) >= <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">this</span>.store[type].push(fn);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">this</span>.store[type] = [];</span><br><span class="line"> <span class="keyword">this</span>.store[type].push(fn);</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> unSubscribe: <span class="function"><span class="keyword">function</span> (<span class="params">type, fnName</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">Object</span>.keys(<span class="keyword">this</span>.store).indexOf(type) >= <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">let</span> index = <span class="number">-1</span>;</span><br><span class="line"> <span class="keyword">this</span>.store[type].forEach(<span class="function"><span class="keyword">function</span> (<span class="params">func, idx</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (func.name === fnName) {</span><br><span class="line"> index = idx;</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> index > <span class="number">-1</span> ? <span class="keyword">this</span>.store[type].splice(index, <span class="number">1</span>) : <span class="literal">null</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> publish: <span class="function"><span class="keyword">function</span> (<span class="params">type, message</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">Object</span>.keys(<span class="keyword">this</span>.store).indexOf(type) >= <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> fn <span class="keyword">of</span> <span class="keyword">this</span>.store[type]) {</span><br><span class="line"> fn(message)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>进行调用:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> subscriberA = <span class="function"><span class="keyword">function</span> (<span class="params">message</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`A收到通知:<span class="subst">${message}</span>`</span>)</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> subscriberB = <span class="function"><span class="keyword">function</span> (<span class="params">message</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`B收到通知:<span class="subst">${message}</span>`</span>)</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> subscriberC = <span class="function"><span class="keyword">function</span> (<span class="params">message</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`C收到通知:<span class="subst">${message}</span>`</span>)</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">publisher.subscribe(<span class="string">'game'</span>, subscriberA);</span><br><span class="line">publisher.subscribe(<span class="string">'game'</span>, subscriberB);</span><br><span class="line">publisher.subscribe(<span class="string">'game'</span>, subscriberC);</span><br><span class="line"></span><br><span class="line">publisher.publish(<span class="string">'game'</span>, <span class="string">'we play'</span>)</span><br></pre></td></tr></table></figure></p><p>再取消订阅<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">publisher.unSubscribe(<span class="string">'game'</span>, subscriberB.name)</span><br></pre></td></tr></table></figure></p><p>B就接收不到数据了。</p>]]></content>
<tags>
<tag> js </tag>
</tags>
</entry>
<entry>
<title>常见排序算法的原理与实现</title>
<link href="/2019/01/17/%E5%B8%B8%E8%A7%81%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%9A%84%E5%8E%9F%E7%90%86%E4%B8%8E%E5%AE%9E%E7%8E%B0/"/>
<content type="html"><![CDATA[<h4 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h4><p>本文介绍了冒泡、选择、插入、归并、快速、堆排序等多种排序的原理、js实现和性能表现。希望能把排序算法发光发热!</p><ul><li>冒泡: 将数组相邻元素两两比较大小,一遍一遍把当前最大值(最小值)冒泡到最后。</li><li>选择: 通过数组循环,记录当前最小值(最大值)所在,再与第一个元素进行元素交换,依次类推。</li><li>插入: 对未排序数据中,从已排序序列中从后向前查询,找到相应的位置进行插入。</li><li>归并: 将数组拆分成n个一个元素的数组,再进行两两合并。从底层往上合并时,左右两边的数组都是排序好的,因此合并容易。</li><li>快排: 选择基准元素(一般是中间),所有小于基准的放在左边,大于基准的放在右边,重复直到结束。</li><li>堆排: 初始化堆后,将最后一个元素与堆顶元素交换后,最后元素移出堆排序序列中,再不断调整堆使满足堆的性质。</li></ul><a id="more"></a><h1 id="冒泡排序"><a href="#冒泡排序" class="headerlink" title="冒泡排序"></a>冒泡排序</h1><h2 id="算法简介"><a href="#算法简介" class="headerlink" title="算法简介"></a>算法简介</h2><p>它的基本思想是:</p><ul><li>比较相邻的元素。如果第一个比第二个大,就交换它们两个;</li><li>对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;</li><li>再对最后一位以外的数组,重复前面的过程,直至全部排序完成。</li></ul><p>举例子:</p><blockquote><p>数组为[3,1,4,2], 3和1交换为[1,3,4,2], 第二位的3和4比较不交换,第三位的4和2交换为[1,3,2,4]。第一遍结束后,最后一个值必然是最大的。再重复以上操作,继续排序[1,3,2]数组,依次循环。</p></blockquote><h2 id="算法实现"><a href="#算法实现" class="headerlink" title="算法实现"></a>算法实现</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bubbleSort</span>(<span class="params">arr</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> len = arr.length;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < len; i++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> j = <span class="number">0</span>; j < len - <span class="number">1</span> - i; j++) {</span><br><span class="line"> <span class="keyword">if</span> (arr[j] > arr[j+<span class="number">1</span>]) { <span class="comment">//相邻元素两两对比</span></span><br><span class="line"> <span class="keyword">var</span> temp = arr[j+<span class="number">1</span>]; <span class="comment">//元素交换</span></span><br><span class="line"> arr[j+<span class="number">1</span>] = arr[j];</span><br><span class="line"> arr[j] = temp;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> arr;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="算法分析"><a href="#算法分析" class="headerlink" title="算法分析"></a>算法分析</h2><ul><li>最佳情况:T(n) = O(n)</li><li>最差情况:T(n) = O(n2)</li><li>平均情况:T(n) = O(n2)</li></ul><h1 id="选择排序"><a href="#选择排序" class="headerlink" title="选择排序"></a>选择排序</h1><h2 id="算法简介-1"><a href="#算法简介-1" class="headerlink" title="算法简介"></a>算法简介</h2><p>选择排序(Selection Sort)与冒泡排序类似,也是依次对相邻的数进行两两比较。不同之处在于,它不是每比较一次就调换位置,而是一轮比较完毕,找到最大值(或最小值)之后,将其放在正确的位置,其他数的位置不变。</p><p>举例子:</p><blockquote><p>数组为[3,1,4,2], 先假设第一位3为最小值,当前的最小值为3,与第二位1比较,所以1是最小值。然后最小值与第三位4、第四位2比较,最小值不改变。<br>所以第一位3与最小值1进行交换,数组为[1,3,4,2]。<br>然后比较[3,4,2]数组。依次类推。</p></blockquote><h2 id="算法实现-1"><a href="#算法实现-1" class="headerlink" title="算法实现"></a>算法实现</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">selectionSort</span>(<span class="params">arr</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> len = arr.length;</span><br><span class="line"> <span class="keyword">var</span> minIndex, temp;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < len - <span class="number">1</span>; i++) {</span><br><span class="line"> minIndex = i;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> j = i + <span class="number">1</span>; j < len; j++) {</span><br><span class="line"> <span class="keyword">if</span> (arr[j] < arr[minIndex]) { <span class="comment">//寻找最小的数</span></span><br><span class="line"> minIndex = j; <span class="comment">//将最小数的索引保存</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> temp = arr[i];</span><br><span class="line"> arr[i] = arr[minIndex];</span><br><span class="line"> arr[minIndex] = temp;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> arr;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="算法分析-1"><a href="#算法分析-1" class="headerlink" title="算法分析"></a>算法分析</h2><p>表现最稳定的排序算法之一,最佳最差都是O(n²)的时间复杂度。</p><h1 id="插入排序"><a href="#插入排序" class="headerlink" title="插入排序"></a>插入排序</h1><h2 id="算法简介-2"><a href="#算法简介-2" class="headerlink" title="算法简介"></a>算法简介</h2><p>通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。</p><p>举例子:</p><blockquote><p>当前未排序数组为[3,1,4,2],第二位1从已排序好的[3]插入,变为[1,3]。第三位4在[1,3]中插入变成[1,3,4], 第四位2在已排序好的[1,3,4]中排序变成[1,2,3,4]。</p></blockquote><h2 id="算法实现-2"><a href="#算法实现-2" class="headerlink" title="算法实现"></a>算法实现</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">insertionSort</span>(<span class="params">arr</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> len = arr.length;</span><br><span class="line"> <span class="keyword">var</span> preIndex, current;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">1</span>; i < len; i++) {</span><br><span class="line"> preIndex = i - <span class="number">1</span>;</span><br><span class="line"> current = arr[i];</span><br><span class="line"> <span class="keyword">while</span>(preIndex >= <span class="number">0</span> && arr[preIndex] > current) {</span><br><span class="line"> arr[preIndex+<span class="number">1</span>] = arr[preIndex];</span><br><span class="line"> preIndex--;</span><br><span class="line"> }</span><br><span class="line"> arr[preIndex+<span class="number">1</span>] = current;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> arr;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="算法分析-2"><a href="#算法分析-2" class="headerlink" title="算法分析"></a>算法分析</h2><ul><li>最佳情况:输入数组按升序排列。T(n) = O(n)</li><li>最坏情况:输入数组按降序排列。T(n) = O(n2)</li><li>平均情况:T(n) = O(n2)</li></ul><h1 id="归并排序"><a href="#归并排序" class="headerlink" title="归并排序"></a>归并排序</h1><h2 id="算法简介-3"><a href="#算法简介-3" class="headerlink" title="算法简介"></a>算法简介</h2><p>前三种时间复杂度太高,效率低,不适合实际使用。<br>基本思想:将两个已经排序的数组合并,要比从头开始排序所有元素来得快。因此,可以将数组拆开,分成n个只有一个元素的数组,然后不断地两两合并,直到全部排序完成。</p><p>举个例子:</p><blockquote><p>[3,1,4,2]数组分成[3,1]和[4,2]两个部分,[3,1]分为[3]和[1]两部分,再合成[1,3]。同理右边合成了[2,4]。再将两者进行合并为[1,2,3,4]</p></blockquote><h2 id="算法实现-3"><a href="#算法实现-3" class="headerlink" title="算法实现"></a>算法实现</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">mergeSort</span>(<span class="params">arr</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> len = arr.length;</span><br><span class="line"> <span class="keyword">if</span>(len <= <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">return</span> arr;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">var</span> middle = <span class="built_in">Math</span>.floor(arr.length/<span class="number">2</span>),</span><br><span class="line"> left = arr.slice(<span class="number">0</span>, middle),</span><br><span class="line"> right = arr.slice(middle);</span><br><span class="line"> <span class="keyword">return</span> merge(mergeSort(left), mergeSort(right));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">merge</span>(<span class="params">left, right</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> result = [];</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span>(left.length && right.length) {</span><br><span class="line"> <span class="keyword">if</span> (left[<span class="number">0</span>] <= right[<span class="number">0</span>]) {</span><br><span class="line"> result.push(left.shift());</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> result.push(right.shift());</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(left.length) {</span><br><span class="line"> result = result.concat(left);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(right.length) {</span><br><span class="line"> result = result.concat(right);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="算法分析-3"><a href="#算法分析-3" class="headerlink" title="算法分析"></a>算法分析</h2><p>和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是O(n log n)的时间复杂度。代价是需要额外的内存空间。</p><ul><li>最佳情况:T(n) = O(n)</li><li>最差情况:T(n) = O(nlogn)</li><li>平均情况:T(n) = O(nlogn)</li></ul><h1 id="快速排序"><a href="#快速排序" class="headerlink" title="快速排序"></a>快速排序</h1><h2 id="算法简介-4"><a href="#算法简介-4" class="headerlink" title="算法简介"></a>算法简介</h2><p>速度快!效率高!是处理大数据最快的排序算法之一。<br>基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。</p><blockquote><p>(1)在数据集之中,选择一个元素作为”基准”(pivot)。<br>(2)所有小于”基准”的元素,都移到”基准”的左边;所有大于”基准”的元素,都移到”基准”的右边。<br>(3)对”基准”左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。</p></blockquote><h2 id="算法实现-4"><a href="#算法实现-4" class="headerlink" title="算法实现"></a>算法实现</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> quickSort = <span class="function"><span class="keyword">function</span>(<span class="params">arr</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (arr.length <= <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">return</span> arr;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">var</span> pivotIndex = <span class="built_in">Math</span>.floor(arr.length/<span class="number">2</span>);</span><br><span class="line"> <span class="keyword">var</span> pivot = arr.splice(pivotIndex, <span class="number">1</span>)[<span class="number">0</span>]; <span class="comment">//获取基准元素的值,同时原数组已经改变了</span></span><br><span class="line"> <span class="keyword">var</span> left = [];</span><br><span class="line"> <span class="keyword">var</span> right = [];</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">var</span> i = <span class="number">0</span>; i < arr.length; i++) {</span><br><span class="line"> <span class="keyword">if</span> (arr[i] < pivot) {</span><br><span class="line"> left.push(arr[i]);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> right.push(arr[i]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> quickSort(left).concat(pivot, quickSort(right));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="算法分析-4"><a href="#算法分析-4" class="headerlink" title="算法分析"></a>算法分析</h2><blockquote><p>快速排序的最坏运行情况是O(n²),比如说顺序数列的快排。但它的平摊期望时间是O(n log n) ,且O(n log n)记号中隐含的常数因子很小,比复杂度稳定等于O(n log n)的归并排序要小很多。所以,对绝大多数顺序性较弱的随机数列而言,快速排序总是优于归并排序。</p></blockquote><ul><li>最佳情况:T(n) = O(nlogn)</li><li>最差情况:T(n) = O(n2)</li><li>平均情况:T(n) = O(nlogn)</li></ul><h1 id="堆排序"><a href="#堆排序" class="headerlink" title="堆排序"></a>堆排序</h1><h2 id="算法简介-5"><a href="#算法简介-5" class="headerlink" title="算法简介"></a>算法简介</h2><p>利用堆的概念来排序的选择排序。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。</p><p>分为两种方法:</p><ol><li>大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列</li><li>小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列</li></ol><p>将待排序列构造成一个大顶堆(或小顶堆),整个序列的最大值(或最小值)就是堆顶的根结点,将根节点的值和堆数组的末尾元素交换,此时末尾元素就是最大值(或最小值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次大值(或次小值),如此反复执行,最终得到一个有序序列。</p><h2 id="算法实现-5"><a href="#算法实现-5" class="headerlink" title="算法实现"></a>算法实现</h2><ol><li>根据数组元素构建一个完成二叉树</li><li>初始化大顶推(使父节点值大于子节点的值,从父节点、左孩子节点、右孩子节点三者中选择最大者跟父节点进行交换, 从下往上进行比较交换)</li><li>将堆的根节点(初始化后为当前二叉树的最大元素)与最后一个元素交换,同时把当前这个最大元素去除堆元素排序之外,堆的数量减一。</li><li>因为交换了元素后,很大可能不满足堆的性质了。从堆顶往下进行调整(使父节点值大于子节点的值,从父节点、左孩子节点、右孩子节点三者中选择最大者跟父节点进行交换, 从上往下进行交换)使堆满足堆的性质。重复3、4步骤,直到结束。</li></ol><p>具体的流程图可以参考:<a href="http://www.cnblogs.com/0zcl/p/6737944.html" target="_blank" rel="noopener">http://www.cnblogs.com/0zcl/p/6737944.html</a></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">heapSort</span>(<span class="params">arr</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> heapSize = arr.length, temp;</span><br><span class="line"> <span class="comment">//建堆,初始化堆</span></span><br><span class="line"> <span class="comment">//length / 2 - 1是二叉树中最后一个非叶子结点的序号</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="built_in">Math</span>.floor(heapSize/<span class="number">2</span>) - <span class="number">1</span>; i >= <span class="number">0</span>; i--) {</span><br><span class="line"> heapify(arr, i, heapSize)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//堆进行排序</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> j = heapSize - <span class="number">1</span>; j >= <span class="number">1</span> ; j--) {</span><br><span class="line"> <span class="comment">//将最后一个元素与堆顶元素交换</span></span><br><span class="line"> temp = arr[j];</span><br><span class="line"> arr[j] = arr[<span class="number">0</span>];</span><br><span class="line"> arr[<span class="number">0</span>] = temp;</span><br><span class="line"> heapify(arr, <span class="number">0</span>, j);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> arr;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">heapify</span>(<span class="params">arr, i, len</span>) </span>{</span><br><span class="line"> <span class="comment">//l,r是当前节点的左右孩子节点</span></span><br><span class="line"> <span class="comment">//当前节点的下标保存在largest中</span></span><br><span class="line"> <span class="keyword">var</span> l = <span class="number">2</span>*i + <span class="number">1</span>, r = <span class="number">2</span>*i + <span class="number">2</span>, largest = i, temp;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(l < len && arr[l] > arr[largest]) {</span><br><span class="line"> <span class="comment">//左节点大于父节点</span></span><br><span class="line"> largest = l;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (r < len && arr[r] > arr[largest]) {</span><br><span class="line"> <span class="comment">//右节点在父节点、左孩子节点中更大</span></span><br><span class="line"> largest = r;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 若i处的值比其左右孩子结点的值小,就将其和最大值进行交换</span></span><br><span class="line"> <span class="keyword">if</span>(largest != i) {</span><br><span class="line"> temp = arr[i];</span><br><span class="line"> arr[i] = arr[largest];</span><br><span class="line"> arr[largest] = temp;</span><br><span class="line"> <span class="comment">// 交换后再查看剩下的堆是否满足堆的性质(以前满足)</span></span><br><span class="line"> heapify(arr, largest, len);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="算法分析-5"><a href="#算法分析-5" class="headerlink" title="算法分析"></a>算法分析</h2><p>参考:<a href="https://juejin.im/entry/5883501a128fe10065daaeb0" target="_blank" rel="noopener">https://juejin.im/entry/5883501a128fe10065daaeb0</a></p>]]></content>
<tags>
<tag> 算法 </tag>
</tags>
</entry>
<entry>
<title>求取两个排序数组的中位数</title>
<link href="/2019/01/09/medianTwoArray/"/>
<content type="html"><![CDATA[<p>##问题:</p><blockquote><p>给定两个大小为 m 和 n 的有序数组 nums1 和 nums2 。<br>请找出这两个有序数组的中位数。要求算法的时间复杂度为 O(log (m+n)) 。<br>你可以假设 nums1 和 nums2 不同时为空。<br>示例 1:<br>nums1 = [1, 3]<br>nums2 = [2]<br>中位数是 2.0<br>示例 2:<br>nums1 = [1, 2]<br>nums2 = [3, 4]<br>中位数是 (2 + 3)/2 = 2.5</p></blockquote><p>##思考:<br>总体思考:<br>最主要的难点是算法复杂度的限制, log级别的时间复杂度我们自然想到二分法,每次取中间值,自然得出的是log2(n)级别的。<br>因为nums1和nums2是有序数组,我们对这两个数组分别在某一位置进行划分。</p><p>比如nums1数组设为A, 划分位置为 i :<br>left_A | right_A<br>A [0],A [1],…,A [i-1] | A [i],A [i + 1],…,A [m-1]<br>这样左边有 i 个元素,右边有( m - i )个元素。</p><p>nums2数组设为B,划分位置为 j :<br>left_B | right_B<br>B [0],B [1],…,B [j-1] | B [j],B [j + 1],…,B [n-1]<br>这样左边有 j 个元素,右边有( n - j )个元素。</p><p>把两个数组的左右两边分别混合,也就是left_A和left_B放一起,right_A和right_B放一起,得到中位数需要满足下面两个条件:</p><ol><li>左右数量相等(偶数:i + j = m - i + n -j )或者左边比右边大一个(奇数:i + j = m - i + n -j + 1)</li><li>max(left_A) 小于 min(right_B),而且max(left_B) 小于 min(right_A),因为left_A肯定小于right_A, left_B肯定小于right_B</li></ol><p>那么对于总长度为奇数,中位数 = max(left_A, left_B);对于总长度为偶数,中位数 = ( max(left_A, left_B) ,min(right_A,right_B) ) / 2</p><p>把上面两个条件进行整理:</p><ol><li>i = 0〜m,j = parseInt( (m + n + 1) / 2) - i (奇偶通用,阴影划分的为左边,其他为右边)<br><img src="https://upload-images.jianshu.io/upload_images/11352552-79f3071104811c90.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="总长度为奇数"><br><img src="https://upload-images.jianshu.io/upload_images/11352552-966deac16c1abb40.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="总长度为偶数"><br>所以n要大于等于m(n>=m),这样确保 j 是合法索引。</li><li>shortArray[ i -1] <= longArray[ j ],longArray[ j - 1 ] <= shortArray [ i ]</li></ol><p>##实现细节:</p><ol><li>先对nums1和nums2的长度进行判断,选择长的作为longArray, 短的作为shortArray</li><li>i 在 [ 0, m ]中进行取值,并根据二分法取中值,j = parseInt( (m + n + 1) / 2) - i </li><li>如果( j > 0 && i < m && longArray[j-1] > shortArray[i] ),说明shortArray [i]太小,i 应该继续增大,取值范围变为 [ i + 1, m ]</li><li>如果( i > 0 && j < n && shortArray[i-1] > longArray[j] ),说明shortArray [i]太大, i 应该减小,取值范围变为[0, i - 1]</li><li>以此循环,直到上面条件都不满足,说明要么取到边界了(i =0 , j=0 等情况),要么shortArray和longArray左边都取了值,要选出shortArray和longArray左边最大的值。</li></ol><ul><li>(i =0 情况下,左侧确定,左边最大值为longArray[j - 1] )<br><img src="https://upload-images.jianshu.io/upload_images/11352552-269f3f76f8ca7f7f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png"></li><li>(j = 0情况下,左侧确定,左侧最大值为shortArray[i - 1])<br><img src="https://upload-images.jianshu.io/upload_images/11352552-eeb4360a82713bb5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png"></li><li>(其他情况,shortArray和longArray左边都取了值)<br><img src="https://upload-images.jianshu.io/upload_images/11352552-bc42f88fec4e1c07.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="image.png"></li></ul><ol start="6"><li>对于总长度为奇数, 中位数 = max(left_A, left_B), 也就是第5步得到的值。如果是奇数,要再得到 minRight.</li></ol><p>##代码:<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> findMedianSortedArrays = <span class="function"><span class="keyword">function</span>(<span class="params">nums1, nums2</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> shortArray = [],longArray = [], m = nums1.length, n = nums2.length,</span><br><span class="line"> temp = <span class="number">0</span>, maxLeft = <span class="number">0</span>, minRight = <span class="number">0</span>;</span><br><span class="line"> <span class="comment">//保证 n >= m </span></span><br><span class="line"> <span class="keyword">if</span>(m > n) {</span><br><span class="line"> shortArray = nums2;</span><br><span class="line"> longArray = nums1;</span><br><span class="line"> temp = m;</span><br><span class="line"> m = n;</span><br><span class="line"> n = temp;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> shortArray = nums1;</span><br><span class="line"> longArray = nums2;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//假设shortArray取i个,longArray取parseInt((m+n+1)/2)-i个</span></span><br><span class="line"> <span class="keyword">var</span> imin = <span class="number">0</span>, imax = m, i, j;</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> i = <span class="built_in">parseInt</span>((imin + imax)/<span class="number">2</span>);</span><br><span class="line"> j = <span class="built_in">parseInt</span>((m + n + <span class="number">1</span>)/<span class="number">2</span> - i);</span><br><span class="line"> <span class="keyword">if</span>(j > <span class="number">0</span> && i < m && longArray[j<span class="number">-1</span>] > shortArray[i]) {</span><br><span class="line"> <span class="comment">// shortArray和longArray左右都取了值,且i值取小了</span></span><br><span class="line"> imin = i + <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span>( i > <span class="number">0</span> && j < n && shortArray[i<span class="number">-1</span>] > longArray[j]) {</span><br><span class="line"> <span class="comment">// shortArray和longArray左右都取了值,且i值取大了</span></span><br><span class="line"> imax = i - <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 考虑边界情况</span></span><br><span class="line"> <span class="keyword">if</span>(i == <span class="number">0</span>) {</span><br><span class="line"> maxLeft = longArray[j - <span class="number">1</span>];</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (j == <span class="number">0</span> ) {</span><br><span class="line"> maxLeft = shortArray[i - <span class="number">1</span>];</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// i值不小不大, 也就是(shortArray[i-1] <= longArray[j] && longArray[j-1] <= shortArray[i])</span></span><br><span class="line"> maxLeft = <span class="built_in">Math</span>.max(shortArray[i - <span class="number">1</span>], longArray[j <span class="number">-1</span>]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">while</span> (imin <= imax);</span><br><span class="line"> <span class="comment">//两数组相加个数是奇数,只要取中间值</span></span><br><span class="line"> <span class="keyword">if</span>( (m + n)%<span class="number">2</span> == <span class="number">1</span> ) <span class="keyword">return</span> maxLeft;</span><br><span class="line"> <span class="comment">//个数是偶数,必须左右两边中位数相加除以二</span></span><br><span class="line"> <span class="keyword">if</span>(i == m) {</span><br><span class="line"> minRight = longArray[j];</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span>(j == n){</span><br><span class="line"> minRight = shortArray[i];</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> minRight = <span class="built_in">Math</span>.min(shortArray[i], longArray[j]);</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> (maxLeft + minRight)/<span class="number">2</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>]]></content>
<tags>
<tag> 算法 </tag>
</tags>
</entry>
<entry>
<title>flux/redux/mobx/vuex相关解析</title>
<link href="/2018/12/27/vuexAndRedux/"/>
<content type="html"><![CDATA[<h1 id="状态管理是什么"><a href="#状态管理是什么" class="headerlink" title="状态管理是什么?"></a>状态管理是什么?</h1><p>在组件化开发的新前端发展史上,组件的发展提供了更好的编码效率,更好的代码阅读性,维护性,补充HTML5语义化标签的不足。前端承担了越来越多的任务,特别是做一个spa项目。然而我们父子组件沟通可以通过props和回调,但是在两个组件我们并不知道它们的调用关系的时候,如何进行沟通呢?<br><a id="more"></a><br>所以:引入了状态管理的概念。比如在react中,通信解决方式有状态提升和发布、订阅,状态提升又分为container组件定义和使用context属性传递:</p><ol><li>container组件是说把两个组件需要共享的状态提升到一个共同的根组件上,通过 props 传递 state 以及 changeState 的方法。</li><li>context属性传递是说在使用React.createContext()声明了context对象,这个Context对象包含两个组件,<provider>和<consumer>。使用这个对象的provider包装需要共用这个context的父组件,把要传递的值通过value属性传递给子组件;使用这个对象的consumer包装要使用context属性的子组件。</consumer></provider></li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> ThemeContext = React.createContext({</span><br><span class="line"> background: <span class="string">'red'</span>,</span><br><span class="line"> color: <span class="string">'white'</span></span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">App</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</span><br><span class="line"> render () {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <ThemeContext.Provider value={{<span class="attr">background</span>: <span class="string">'green'</span>, <span class="attr">color</span>: <span class="string">'white'</span>}}></span><br><span class="line"> <Header /></span><br><span class="line"> <<span class="regexp">/ThemeContext.Provider></span></span><br><span class="line"><span class="regexp"> );</span></span><br><span class="line"><span class="regexp"> }</span></span><br><span class="line"><span class="regexp">}</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">class Header extends React.Component {</span></span><br><span class="line"><span class="regexp"> render () {</span></span><br><span class="line"><span class="regexp"> return (</span></span><br><span class="line"><span class="regexp"> <Title>Hello React Context API</</span>Title></span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"> </span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Title</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</span><br><span class="line"> render () {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <ThemeContext.Consumer></span><br><span class="line"> {context => (</span><br><span class="line"> <h1 style={{<span class="attr">background</span>: context.background, <span class="attr">color</span>: context.color}}></span><br><span class="line"> {<span class="keyword">this</span>.props.children}</span><br><span class="line"> <<span class="regexp">/h1></span></span><br><span class="line"><span class="regexp"> )}</span></span><br><span class="line"><span class="regexp"> </</span>ThemeContext.Consumer></span><br><span class="line"> );</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 也可以不使用consumer</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Title</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</span><br><span class="line"> <span class="keyword">static</span> contextType = ThemeContext;</span><br><span class="line"> render () {</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <h1 style={{<span class="attr">background</span>: <span class="keyword">this</span>.context.background, <span class="attr">color</span>: <span class="keyword">this</span>.context.color}}></span><br><span class="line"> {<span class="keyword">this</span>.props.children}</span><br><span class="line"> <<span class="regexp">/h1></span></span><br><span class="line"><span class="regexp"> );</span></span><br><span class="line"><span class="regexp"> }</span></span><br><span class="line"><span class="regexp">}</span></span><br></pre></td></tr></table></figure><p>它会使组件更新更加困难。这个provider有更新,它的子代所有组件会重新渲染。不受shouldComponentUpdate方法约束,更改是通过使用与Object.is相同的算法比较新值和旧值来确定的。<br>旧版本的Context的更新需要依赖setState(),是不可靠的,不过这个问题在新版的API中得以解决。<br>但如果开发组件过程中可以确保组件的内聚性,可控可维护,不破坏组件树的依赖关系,影响范围小,可以考虑使用Context解决一些问题。</p><blockquote><p>context运用于react-router: <a href="https://www.jianshu.com/p/eba2b76b290b" target="_blank" rel="noopener">https://www.jianshu.com/p/eba2b76b290b</a><br>如果组件的功能不能单靠组件自身来完成,还需要依赖额外的子组件,那么可以利用Context构建一个由多个子组件组合的组件。为了让相关的子组件一同发挥作用,react-router的实现方案是利用Context在<code><Router /></code>、<code><Link /></code>以及<code><Route /></code>这些相关的组件之间共享一个router,进而完成路由的统一操作和管理。<br><code><Router /></code>的核心就是为子组件提供一个带有router属性的Context,同时监听history,一旦history发生变化,便通过setState()触发组件重新渲染。<br><code><Link /></code>的核心就是渲染<code><a></code>标签,拦截<code><a></code>标签的点击事件,然后通过<code><Router /></code>共享的router对history进行路由操作,进而通知<code><Router /></code>重新渲染。<br><code><Route /></code>有一部分源码与<code><Router /></code>相似,可以实现路由的嵌套,但其核心是通过Context共享的router,判断是否匹配当前路由的路径,然后渲染组件。</p></blockquote><ol start="3"><li>发布订阅就是一个组件订阅,一个组件发布。</li></ol><p>因此,出现了独立管理状态的地方。可以实现数据的订阅、发布。如flux/redux/vuex/mobx。他们有什么区别呢?</p><p>一句话总结:<br>它们都是基于单向数据流的状态管理方法论。Flux最早提出,作为对传统前端MVC的一种改进(我不认为是颠覆)。Redux深受Flux的启发,又加入了函数式编程的思想,算是Flux的极大增强版本。Vuex可以说是基于Flux并且吸收了Redux的一些特点,但它与Vue是紧密捆绑的。Redux其实除了在React中广泛应用。</p><h2 id="flux"><a href="#flux" class="headerlink" title="flux"></a>flux</h2><img src="/images/vuexandredux_02.png"><p>View: 确定相应的Store以及监听其变化来更新视图。发起Action。<br>Action:每个Action都是一个对象,包含一个actionType属性(说明动作的类型)和一些其他属性(用来传递数据)<br>Dispatcher:全局唯一。逻辑简单,只用来派发action去相应的store。通过 AppDispatcher.register() 来登记各种Action的回调函数。<br>Store: 存放view中的数据。发送change事件,通过view中定义的handler捕捉变化。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line">//store</span><br><span class="line">var EventEmitter = require('events').EventEmitter;</span><br><span class="line">var ListStore = assign({}, EventEmitter.prototype, {</span><br><span class="line"> // data in View</span><br><span class="line"> items: [],</span><br><span class="line"> emitChange: function () {</span><br><span class="line"> this.emit('change');</span><br><span class="line"> },</span><br><span class="line"> addChangeListener: function(callback) {</span><br><span class="line"> this.on('change', callback);</span><br><span class="line"> }</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">// view</span><br><span class="line">ListStore.addChangeListener(this._onChange); //如果listStorage值变化change,调用回调this._onChange</span><br><span class="line"></span><br><span class="line">var ButtonActions = {</span><br><span class="line"> addNewItem: function (text) {</span><br><span class="line"> AppDispatcher.dispatch({</span><br><span class="line"> actionType: 'ADD_NEW_ITEM',</span><br><span class="line"> text: text</span><br><span class="line"> });</span><br><span class="line"> },</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">ButtonActions.addNewItem('new item');</span><br><span class="line"></span><br><span class="line">//这里是dispatcher</span><br><span class="line">// dispatcher/AppDispatcher.js 全局唯一</span><br><span class="line">var Dispatcher = require('flux').Dispatcher;</span><br><span class="line">module.exports = new Dispatcher();</span><br><span class="line"></span><br><span class="line">//注册action回调函数</span><br><span class="line">AppDispatcher.register(function (action) {</span><br><span class="line"> switch(action.actionType) {</span><br><span class="line"> case 'ADD_NEW_ITEM':</span><br><span class="line"> ListStore.addNewItemHandler(action.text);</span><br><span class="line"> ListStore.emitChange(); //通知listStorage触发change, 调用注册的回调</span><br><span class="line"> break;</span><br><span class="line"> default:</span><br><span class="line"> // no op</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>可以看出,flux更新逻辑在store。dispatcher只是将action进行处理,然后调用store里面的方法去更新数据。</p><h2 id="redux"><a href="#redux" class="headerlink" title="redux"></a>redux</h2><img src="/images/vuexandredux_03.png"><p>Redux = Reducer + Flux</p><ol><li>Redux将Flux中的Dispatcher并入了Store。也可以理解为Redux没Dispatcher。Redux的设想是用户永远不会变动数据,应该在reducer中返回新的对象来作为应用的新状态。</li><li>Redux增加了Reducer.<br>注:通过代码对比的直观感受就是Flux中的view需要知道具体对应哪个store。而在Redux中,store成为一个被所有view共享的公共对象,view只需要通过store.dispatch()来发送action,无需关心具体处理函数。</li></ol><p>View: 通过全局唯一的store dispatch action 以及获取最新state<br>Action: 与flux一致。<br>reducer: 是current state 和 action 为参数计算new state的纯函数。<br>Store: 全局唯一。Dispatcher功能已被整合进store:store.dispatch()。state 一旦有变化,store 就会调用通过store.subscribe()注册的回调函数(一般是render)。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line">const createStore = (reducer) => {</span><br><span class="line"> let state; // 定义存储的state</span><br><span class="line"> const listeners = [];</span><br><span class="line"></span><br><span class="line"> // getState的作用很简单就是返回当前是state</span><br><span class="line"> const getState = () => state;</span><br><span class="line"></span><br><span class="line"> //定义一个派发函数</span><br><span class="line"> //当在外界调用此函数的时候,会修改状态</span><br><span class="line"> const dispatch = (action) => {</span><br><span class="line"> //调用reducer函数修改状态,返回一新的状态并赋值给这个局部状态变量</span><br><span class="line"> state = reducer(state, action);</span><br><span class="line"> //依次调用监听函数,通知所有的监听函数</span><br><span class="line"> listeners.forEach(listener => listener());</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //订阅此状态的函数,当状态发生变化的时候记得调用此监听函数</span><br><span class="line"> const subscribe = function(listener) {</span><br><span class="line"> //先把此监听 加到数组中</span><br><span class="line"> listeners.push(listener);</span><br><span class="line"></span><br><span class="line"> //返回一个函数,当调用它的时候将此监听函数从监听数组移除</span><br><span class="line"> return function() {</span><br><span class="line"> listeners = listeners.filter(l => l !== listener);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //默认调用一次dispatch给state赋一个初始值</span><br><span class="line"> dispatch();</span><br><span class="line"></span><br><span class="line"> return {</span><br><span class="line"> getState,</span><br><span class="line"> dispatch,</span><br><span class="line"> subscribe</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// 订阅状态变化事件,当状态变化时用监听函数</span><br><span class="line">store.subscribe(render);</span><br><span class="line">store.dispatch({</span><br><span class="line"> type: 'add',</span><br><span class="line"> payload: '3'</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">createStore(reducer);</span><br></pre></td></tr></table></figure><p>[三大原则,store 唯一,state 只读, reducer 纯函数。]</p><p>所以实现我们大部分使用:redux + react-redux + redux-chunk + redux-immutable 。用来与react框架进行交互。redux的dispatch默认只能传输action, 引用redux-thunk中间件,可以让action创建函数先不返回一个action对象,而是返回一个函数,函数传递两个参数(dispatch,getState),在函数体内进行业务逻辑的封装。通过使用指定的 middleware,action 创建函数除了返回 action 对象外还可以返回函数。以此来让你 dispatch 一些除了 action 以外的其他内容,例如:函数或者 Promise。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">const mapStateToProps = state => {</span><br><span class="line"> return {</span><br><span class="line"> todos: state.get('todos')</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">const mapDispatchToProps = dispatch => {</span><br><span class="line"> return {</span><br><span class="line"> addTodo: info => {</span><br><span class="line"> dispatch(addTodo(info))</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">export default connect(mapStateToProps,mapDispatchToProps)(TodoList);</span><br><span class="line"></span><br><span class="line">//使用指定的 React Redux 组件 <Provider> 来 魔法般的 让所有容器组件都可以访问 store,而不必显示地传递它。</span><br><span class="line"><Provider store={store}></span><br><span class="line"> <App /></span><br><span class="line"></Provider></span><br></pre></td></tr></table></figure><p>React Redux 组件 <provider> 来魔法般的让所有容器组件都可以访问 store,而不必显示地传递它。</provider></p><p>容器组件就是使用 store.subscribe() 从 Redux state 树中读取部分数据,并通过 props 来把这些数据提供给要渲染的组件。<br>你可以手工来开发容器组件,但建议使用 React Redux 库的 connect() 方法来生成,这个方法做了性能优化来避免很多不必要的重复渲染。</p><p>connect 出来的 HOC, 通过 Provider 提供的 context 上的 store,在内部向 store subscribe 了 onStateChange 事件。只要派发了 action,就会触发一次 onStateChange 事件,HOC 就能感知 store 的更新再根据 onStateChange 的结果决定是否要 update。</p><h2 id="vuex"><a href="#vuex" class="headerlink" title="vuex"></a>vuex</h2><p><img src="https://raw.githubusercontent.com/vuejs/vuex/dev/docs/.vuepress/public/vuex.png" alt></p><p>只用来读取的状态集中放在store中;改变状态的方式是提交mutations,这是个同步的事物;异步逻辑应该封装在action中。</p><p>state<br>Vuex 使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据。<br>mutations<br>mutations定义的方法动态修改Vuex 的 store 中的状态或数据。mutation 必须是同步函数。<br>getters<br>类似vue的计算属性,主要用来过滤一些数据。<br>action<br>actions可以理解为通过将mutations里面处理数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。view 层通过 store.dispath 来分发 action。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> store = <span class="keyword">new</span> Vuex.Store(storeSettings)</span><br><span class="line"></span><br><span class="line"><span class="keyword">new</span> Vue({</span><br><span class="line"> router,</span><br><span class="line"> store,</span><br><span class="line"> render: <span class="function"><span class="params">h</span> =></span> h(App)</span><br><span class="line">}).$mount(<span class="string">'#app'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">//这个storeSettings</span></span><br><span class="line"><span class="keyword">const</span> storeSettings = {</span><br><span class="line"> state: {</span><br><span class="line"> selfInfo: {},</span><br><span class="line"> },</span><br><span class="line"> getters: {</span><br><span class="line"> <span class="comment">//会根据依赖缓存起来</span></span><br><span class="line"> getType: state = > {</span><br><span class="line"> <span class="keyword">return</span> state.type</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> mutations: {</span><br><span class="line"> updateSelfInfo(state, payload) {</span><br><span class="line"> state.selfInfo = payload;</span><br><span class="line"> },</span><br><span class="line"> },</span><br><span class="line"> actions: {</span><br><span class="line"> Login_Action({commit}, userInfo) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> { </span><br><span class="line"> (<span class="keyword">async</span> ()=>{</span><br><span class="line"> <span class="keyword">try</span>{</span><br><span class="line"> <span class="keyword">const</span> data = <span class="keyword">await</span> getMyBriefInfo();</span><br><span class="line"> commit(<span class="string">'updateSelfInfo'</span>, data);</span><br><span class="line"> resolve();</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="keyword">let</span> e_j = (e && <span class="built_in">JSON</span>.parse(e) && <span class="built_in">JSON</span>.parse(e).message) ? <span class="built_in">JSON</span>.parse(e).message : <span class="string">'登录失败,请稍后重试!'</span>;</span><br><span class="line"> commit(<span class="string">'clearToken'</span>);</span><br><span class="line"> reject(e_j);</span><br><span class="line"> }</span><br><span class="line"> })(); </span><br><span class="line"> })</span><br><span class="line"> },</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//组件中使用</span></span><br><span class="line"><span class="keyword">this</span>.$store.dispatch(<span class="string">'Login_Action'</span>, {</span><br><span class="line"> userName, password, checked, code</span><br><span class="line">}).then(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> </span><br><span class="line">}, (info) => {</span><br><span class="line"> </span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="keyword">this</span>.$store.state.selfInfo <span class="comment">//获取state</span></span><br><span class="line"><span class="keyword">this</span>.$store.getters.getType <span class="comment">//获取getters</span></span><br><span class="line"><span class="keyword">this</span>.$store.commit(<span class="string">'updateSelfInfo'</span>, data)</span><br></pre></td></tr></table></figure><p>由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块</p><p>局部模块:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> moduleA = {</span><br><span class="line"> state: { <span class="attr">count</span>: <span class="number">0</span> },</span><br><span class="line"> mutations: {</span><br><span class="line"> increment (state) {</span><br><span class="line"> <span class="comment">// 这里的 `state` 对象是模块的局部状态</span></span><br><span class="line"> state.count++</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> getters: {</span><br><span class="line"> <span class="comment">//根节点状态会作为第三个参数暴露出来</span></span><br><span class="line"> doubleCount (state, getters, rootState) {</span><br><span class="line"> <span class="keyword">return</span> state.count * <span class="number">2</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> actions: {</span><br><span class="line"> <span class="comment">//局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState</span></span><br><span class="line"> incrementIfOddOnRootSum ({ state, commit, rootState }) {</span><br><span class="line"> <span class="keyword">if</span> ((state.count + rootState.count) % <span class="number">2</span> === <span class="number">1</span>) {</span><br><span class="line"> commit(<span class="string">'increment'</span>)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> store = <span class="keyword">new</span> Vuex.Store({</span><br><span class="line"> modules: {</span><br><span class="line"> a: moduleA,</span><br><span class="line"> b: moduleB</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">store.state.a <span class="comment">// -> moduleA 的状态</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">this</span>.$store.dispatch(<span class="string">'a/incrementIfOddOnRootSum'</span>)</span><br></pre></td></tr></table></figure></p><p>使用mutation来替换redux中的reducer<br>Vuex有自动渲染的功能,所以无需要专门监听state。<br>Vuex中的action是一个函数集合对象,用于async/sync commit mutaions. 和Redux或者Flux中的action只是简单对象有本质不同,只是叫了一个相同名字。 </p><h2 id="mobx"><a href="#mobx" class="headerlink" title="mobx"></a>mobx</h2><p>通过observable观察某一个变量,当该变量产生变化时,对应的autorun内的回调函数就会发生变化。 Observable 、Computed 依赖state产生 obserable 、Autonrun、Action、Observer,</p><p><img src="http://blog.codingplayboy.com/wp-content/uploads/2018/03/mobx-flow.png" alt></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> {observable, autorun} <span class="keyword">from</span> <span class="string">'mobx'</span>;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">var</span> todoStore = observable({</span><br><span class="line"> <span class="comment">/* some observable state */</span></span><br><span class="line"> todos: [],</span><br><span class="line"> </span><br><span class="line"> <span class="comment">/* a derived value */</span></span><br><span class="line"> <span class="keyword">get</span> completedCount() {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.todos.filter(<span class="function"><span class="params">todo</span> =></span> todo.completed).length;</span><br><span class="line"> }</span><br><span class="line">});</span><br><span class="line"> </span><br><span class="line"><span class="comment">/* a function that observes the state */</span></span><br><span class="line">autorun(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"Completed %d of %d items"</span>,</span><br><span class="line"> todoStore.completedCount,</span><br><span class="line"> todoStore.todos.length</span><br><span class="line"> );</span><br><span class="line">});</span><br><span class="line"> </span><br><span class="line"><span class="comment">/* ..and some actions that modify the state */</span></span><br><span class="line">todoStore.todos[<span class="number">0</span>] = {</span><br><span class="line"> title: <span class="string">"Take a walk"</span>,</span><br><span class="line"> completed: <span class="literal">false</span></span><br><span class="line">};</span><br><span class="line"><span class="comment">// -> synchronously prints: 'Completed 0 of 1 items'</span></span><br><span class="line"> </span><br><span class="line">todoStore.todos[<span class="number">0</span>].completed = <span class="literal">true</span>;</span><br></pre></td></tr></table></figure><p>Action:定义改变状态的动作函数,包括如何变更状态;<br>Store:集中管理模块状态(State)和动作(action);<br>Derivation(衍生):从应用状态中派生而出,且没有任何其他影响的数据,我们称为derivation(衍生),衍生在以下情况下存在:<br> 用户界面;<br> 衍生数据;<br> 衍生主要有两种:<br> Computed Values(计算值):计算值总是可以使用纯函数(pure function)从当前可观察状态中获取;<br> Reactions(反应):反应指状态变更时需要自动发生的副作用,这种情况下,我们需要实现其读写操作;</p><p>react-mobx而言,同样需要两个步骤:</p><p>Provider:使用mobx-react提供的Provider将所有stores注入应用;<br>使用inject将特定store注入某组件,store可以传递状态或action;然后使用observer保证组件能响应store中的可观察对象(observable)变更,即store更新,组件视图响应式更新。</p><h2 id="mobx-和-redux-比较"><a href="#mobx-和-redux-比较" class="headerlink" title="mobx 和 redux 比较"></a>mobx 和 redux 比较</h2><ul><li>redux对象通常不可变(不能直接操作状态对象,而总是在原来状态对象基础上返回一个新的状态对象,这样就能很方便的返回应用上一状态)。而Mobx中可以直接使用新值更新状态对象。</li><li>redux单一store,mobx多个</li><li>redux函数式编程,mobx面对对象</li><li>Redux需要手动追踪所有状态对象的变更,Redux需要手动追踪所有状态对象的变更</li><li>运用到react上,mobx-react和react-redux</li></ul>]]></content>
<tags>
<tag> react </tag>
<tag> vue </tag>
</tags>
</entry>
<entry>
<title>node第一步!</title>
<link href="/2018/12/10/node/"/>
<content type="html"><![CDATA[<p>node在我们前端运用中频繁被提及,现在让我们来了解node是什么!</p><h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。<br>作为异步驱动的 JavaScript 运行时,Node 被设计成可升级的网络应用。<br>Node 的用户不必担心死锁过程, 因为没有锁。Node 中几乎没有函数直接执行 I/O 操作,因此进程从不阻塞。由于没有任何阻塞,可伸缩系统在 Node 中开发是非常合理的。<br><a id="more"></a></p><h2 id="模块化"><a href="#模块化" class="headerlink" title="模块化"></a>模块化</h2><p>模块化:一个.js文件就称为一个模块,模块的名字就是文件名(去掉.js后缀)<br>模块加载机制被称为CommonJS规范:向外暴露变量可以用module.exports = variable;,引用其他模块暴露的变量,用var ref = require(‘module_name’);,require函数是Node提供的,请求地址会依次在内置模块、全局模块和当前模块下查找。</p><p>module.exports实现:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 准备module对象:</span></span><br><span class="line"><span class="keyword">var</span> <span class="built_in">module</span> = {</span><br><span class="line"> id: <span class="string">'hello'</span>,</span><br><span class="line"> exports: {}</span><br><span class="line">};</span><br><span class="line"><span class="keyword">var</span> load = <span class="function"><span class="keyword">function</span> (<span class="params">exports, module</span>) </span>{</span><br><span class="line"> <span class="comment">// 读取的hello.js代码:</span></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">greet</span>(<span class="params">name</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'Hello, '</span> + name + <span class="string">'!'</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">module</span>.exports = greet;</span><br><span class="line"> <span class="comment">// hello.js代码结束</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">module</span>.exports;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> exported = load(<span class="built_in">module</span>.exports, <span class="built_in">module</span>);</span><br><span class="line"><span class="comment">// 保存module:</span></span><br><span class="line">save(<span class="built_in">module</span>, exported);</span><br></pre></td></tr></table></figure></p><p>node为每个模块先准备一个module对象,通过把参数module传递给load()函数,hello.js就顺利地把一个变量传递给了Node执行环境,Node会把module变量保存到某个地方。<br>最后一行由于Node保存了所有导入的module,当我们用require()获取module时,Node找到对应的module,把这个module的exports变量返回,这样,另一个模块就顺利拿到了模块的输出</p><p>Node中exports和module.exports的区别?<br>module.exports 初始值为一个空对象 {}, exports 是指向的 module.exports 的引用, require() 返回的是 module.exports 而不是 exports<br>所以当我们给exports赋值引用变量时,断开了跟module.exports的链接,指向了新的对象。<br>如果我们要输出的是一个函数或数组,那么,只能给module.exports赋值:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{ <span class="keyword">return</span> <span class="string">'foo'</span>; };</span><br></pre></td></tr></table></figure></p><p>给exports赋值是无效的,因为赋值后,module.exports仍然是空对象{}。</p><h3 id="如何寻址"><a href="#如何寻址" class="headerlink" title="如何寻址"></a>如何寻址</h3><h2 id="全局变量"><a href="#全局变量" class="headerlink" title="全局变量"></a>全局变量</h2><p>全局对象global和进程对象process(process是global的一个属性)<br><img src="/images/node_01.png"></p><p>Node.js不断执行响应事件的JavaScript函数,直到没有任何响应事件的函数可以执行时,Node.js就退出了。如果我们想要在下一次事件响应中执行代码,可以调用process.nextTick()。<br>node进程本身的事件由process对象来处理,如果我们响应exit事件,就可以在程序即将退出时执行某个回调函数<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 程序即将退出时的回调函数:</span></span><br><span class="line">process.on(<span class="string">'exit'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">code</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'about to exit with code: '</span> + code);</span><br><span class="line">});</span><br></pre></td></tr></table></figure></p><h2 id="基本模块"><a href="#基本模块" class="headerlink" title="基本模块"></a>基本模块</h2><p>fs / stream /url /path /http /crypto</p><h3 id="fs"><a href="#fs" class="headerlink" title="fs"></a>fs</h3><table><thead><tr><th>类型</th><th>读文件</th><th>写文件</th><th>获取文件大小、创建时间信息</th><th>读取文件流</th><th>写入文件流</th></tr></thead><tbody><tr><td>异步</td><td>fs.readFile()</td><td>fs.writeFile()</td><td>fs.stat()</td><td></td><td></td></tr><tr><td>同步</td><td>fs.readFileSync()</td><td>fs.writeFileSync()</td><td>fs.statSync()</td><td></td><td></td></tr><tr><td>事件驱动</td><td></td><td></td><td></td><td>fs.createReadStream()</td><td>fs.createWriteStream()</td></tr></tbody></table><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">'use strict'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>);</span><br><span class="line"></span><br><span class="line">fs.readFile(<span class="string">'rrr.txt'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">err, data</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (err) {</span><br><span class="line"> <span class="built_in">console</span>.log(err);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">console</span>.log(data);</span><br><span class="line"> <span class="built_in">console</span>.log(data.length + <span class="string">' bytes'</span>);</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'utf-8:'</span> + data.toString(<span class="string">'UTF-8'</span>)); </span><br><span class="line"> }</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">//写文件</span></span><br><span class="line"><span class="keyword">var</span> data = <span class="string">'Hello, Node.js'</span>;</span><br><span class="line">fs.writeFile(<span class="string">'output.txt'</span>, data, <span class="function"><span class="keyword">function</span> (<span class="params">err</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (err) {</span><br><span class="line"> <span class="built_in">console</span>.log(err);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'ok.'</span>);</span><br><span class="line"> }</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">//获取信息</span></span><br><span class="line">fs.stat(<span class="string">'rrr.txt'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">err, stat</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (err) {</span><br><span class="line"> <span class="built_in">console</span>.log(err);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 是否是文件:</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'isFile: '</span> + stat.isFile());</span><br><span class="line"> <span class="comment">// 是否是目录:</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'isDirectory: '</span> + stat.isDirectory());</span><br><span class="line"> <span class="keyword">if</span> (stat.isFile()) {</span><br><span class="line"> <span class="comment">// 文件大小:</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'size: '</span> + stat.size);</span><br><span class="line"> <span class="comment">// 创建时间, Date对象:</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'birth time: '</span> + stat.birthtime);</span><br><span class="line"> <span class="comment">// 修改时间, Date对象:</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'modified time: '</span> + stat.mtime);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>文件流:<br>流的特点是数据是有序的,而且必须依次读取,或者依次写入,不能像Array那样随机定位。<br>在Node.js中,流也是一个对象,我们只需要响应流的事件就可以了:data事件表示流的数据已经可以读取了,end事件表示这个流已经到末尾了,没有数据可以读取了,error事件表示出错了。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> rs = fs.createReadStream(<span class="string">'rr2.txt'</span>, <span class="string">'utf-8'</span>);</span><br><span class="line"></span><br><span class="line">rs.on(<span class="string">'data'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">chunk</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'DATA:'</span>)</span><br><span class="line"> <span class="built_in">console</span>.log(chunk);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">rs.on(<span class="string">'end'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'END'</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">rs.on(<span class="string">'error'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">err</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'ERROR: '</span> + err);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">// var ws1 = fs.createWriteStream('rr3.txt', 'utf-8');</span></span><br><span class="line"><span class="comment">// ws1.write('使用Stream写入文本数据...\n');</span></span><br><span class="line"><span class="comment">// ws1.end();</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> ws2 = fs.createWriteStream(<span class="string">'rr3.txt'</span>);</span><br><span class="line">ws2.write(<span class="keyword">new</span> Buffer(<span class="string">'使用Stream写入二进制数据data...\n'</span>, <span class="string">'utf-8'</span>));</span><br><span class="line">ws2.write(<span class="keyword">new</span> Buffer(<span class="string">'END.\n'</span>, <span class="string">'utf-8'</span>));</span><br><span class="line">ws2.end();</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> ws1 = fs.createWriteStream(<span class="string">'rr3.txt'</span>, <span class="string">'utf-8'</span>);</span><br><span class="line">ws1.write(<span class="string">'使用Stream写入文本数据...\n'</span>);</span><br><span class="line">rs.pipe(ws1); </span><br><span class="line">ws1.write(<span class="string">'END.'</span>);</span><br></pre></td></tr></table></figure></p><h3 id="url模块:解析url"><a href="#url模块:解析url" class="headerlink" title="url模块:解析url"></a>url模块:解析url</h3><img src="/images/node_02.png"><h3 id="path"><a href="#path" class="headerlink" title="path"></a>path</h3><p>可以方便的构造目录<br>使用path模块可以正确处理操作系统相关的文件路径。在Windows系统下,返回的路径类似于C:\Users\michael\static\index.html,这样,我们就不关心怎么拼接路径了。</p><h3 id="http"><a href="#http" class="headerlink" title="http"></a>http</h3><p>request对象,封装了HTTP请求,调用此对象属性和方法可以获取HTTP请求的信息<br>response对象,封装了HTTP响应,调用此对象的方法,可以把HTTP响应返回给浏览器</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> http = <span class="built_in">require</span>(<span class="string">'http'</span>);</span><br><span class="line"><span class="keyword">var</span> server = http.createServer(<span class="function"><span class="keyword">function</span>(<span class="params">request,response</span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(request.method+<span class="string">':'</span>+request.url);</span><br><span class="line"> response.writeHead(<span class="number">200</span>, {<span class="string">'Content-Type'</span>:<span class="string">'text/html'</span>});</span><br><span class="line"> response.end(<span class="string">'<h1>hello http!</h1>'</span>);</span><br><span class="line">});</span><br><span class="line">server.listen(<span class="number">8080</span>);</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'WAITING'</span>);</span><br></pre></td></tr></table></figure><p>一个简单的文件服务器:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">'use strict'</span>;</span><br><span class="line"><span class="keyword">var</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>);</span><br><span class="line"><span class="keyword">var</span> workDir = path.resolve(<span class="string">'.'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span></span><br><span class="line"> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>),</span><br><span class="line"> url = <span class="built_in">require</span>(<span class="string">'url'</span>),</span><br><span class="line"> path = <span class="built_in">require</span>(<span class="string">'path'</span>),</span><br><span class="line"> http = <span class="built_in">require</span>(<span class="string">'http'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 从命令行参数获取root目录,默认是当前目录:</span></span><br><span class="line"><span class="keyword">var</span> root = path.resolve(process.argv[<span class="number">2</span>] || <span class="string">'.'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建服务器:</span></span><br><span class="line"><span class="keyword">var</span> server = http.createServer(<span class="function"><span class="keyword">function</span> (<span class="params">request, response</span>) </span>{</span><br><span class="line"> <span class="comment">// 获得URL的path,类似 '/css/bootstrap.css':</span></span><br><span class="line"> <span class="keyword">var</span> pathname = url.parse(request.url).pathname;</span><br><span class="line"> <span class="comment">// 获得对应的本地文件路径,类似 '/srv/www/css/bootstrap.css':</span></span><br><span class="line"> <span class="keyword">var</span> filepath = path.join(root, pathname);</span><br><span class="line"> <span class="built_in">console</span>.log(filepath);</span><br><span class="line"> <span class="comment">// 获取文件状态:</span></span><br><span class="line"> fs.stat(filepath, <span class="function"><span class="keyword">function</span> (<span class="params">err, stats</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (!err) {</span><br><span class="line"> <span class="keyword">if</span>(stats.isFile()){</span><br><span class="line"> <span class="comment">// 没有出错并且文件存在:</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'200 '</span> + request.url);</span><br><span class="line"> <span class="comment">// 发送200响应:</span></span><br><span class="line"> response.writeHead(<span class="number">200</span>);</span><br><span class="line"> <span class="comment">// 将文件流导向response:</span></span><br><span class="line"> fs.createReadStream(filepath).pipe(response);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(stats.isDirectory){</span><br><span class="line"> <span class="keyword">var</span> filepath1 = path.join(filepath,<span class="string">'index.html'</span>);</span><br><span class="line"> <span class="keyword">var</span> filepath2 = path.join(filepath,<span class="string">'default.html'</span>);</span><br><span class="line"> <span class="keyword">if</span>(fs.existsSync(filepath1)){</span><br><span class="line"> <span class="comment">// 没有出错并且文件存在:</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'200 '</span> + filepath1);</span><br><span class="line"> <span class="comment">// 发送200响应:</span></span><br><span class="line"> response.writeHead(<span class="number">200</span>);</span><br><span class="line"> <span class="comment">// 将文件流导向response:</span></span><br><span class="line"> fs.createReadStream(filepath1).pipe(response);</span><br><span class="line"> }<span class="keyword">else</span> <span class="keyword">if</span>(fs.existsSync(filepath2)){</span><br><span class="line"> <span class="comment">// 没有出错并且文件存在:</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'200 '</span> + filepath2);</span><br><span class="line"> <span class="comment">// 发送200响应:</span></span><br><span class="line"> response.writeHead(<span class="number">200</span>);</span><br><span class="line"> <span class="comment">// 将文件流导向response:</span></span><br><span class="line"> fs.createReadStream(filepath2).pipe(response);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 出错了或者文件不存在:</span></span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'404 '</span> + request.url);</span><br><span class="line"> <span class="comment">// 发送404响应:</span></span><br><span class="line"> response.writeHead(<span class="number">404</span>);</span><br><span class="line"> response.end(<span class="string">'404 Not Found'</span>);</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">server.listen(<span class="number">8080</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'Server is running at http://127.0.0.1:8080/'</span>);</span><br></pre></td></tr></table></figure></p><h2 id="错误优先的回调函数"><a href="#错误优先的回调函数" class="headerlink" title="错误优先的回调函数"></a>错误优先的回调函数</h2><p>node的回调函数第一个参数始终是一个错误对象。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">fs.readFile(<span class="string">'rrr.txt'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">err, data</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (err) {</span><br><span class="line"> <span class="built_in">console</span>.log(err);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">console</span>.log(data);</span><br><span class="line"> <span class="built_in">console</span>.log(data.length + <span class="string">' bytes'</span>);</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'utf-8:'</span> + data.toString(<span class="string">'UTF-8'</span>)); </span><br><span class="line"> }</span><br><span class="line">});</span><br></pre></td></tr></table></figure></p><h2 id="node-的-event-loop"><a href="#node-的-event-loop" class="headerlink" title="node 的 event loop"></a>node 的 event loop</h2><p>Node.js也是单线程的Event Loop,但是它的运行机制不同于浏览器环境。<br><img src="http://www.ruanyifeng.com/blogimg/asset/2014/bg2014100803.png" alt><br>(1)V8引擎解析JavaScript脚本。<br>(2)解析后的代码,调用Node API。<br>(3)libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。<br>(4)V8引擎再将结果返回给用户。<br>除了setTimeout和setInterval这两个方法,Node.js还提供了另外两个与”任务队列”有关的方法:process.nextTick和setImmediate。</p><p>process.nextTick方法可以在当前”执行栈”的尾部—-下一次Event Loop(主线程读取”任务队列”)之前—-触发回调函数。也就是说,它指定的任务总是发生在所有异步任务之前。<br>setImmediate方法则是在当前”任务队列”的尾部添加事件,也就是说,它指定的任务总是在下一次Event Loop时执行,这与setTimeout(fn, 0)很像。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">process.nextTick(<span class="function"><span class="keyword">function</span> <span class="title">A</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="number">1</span>);</span><br><span class="line"> process.nextTick(<span class="function"><span class="keyword">function</span> <span class="title">B</span>(<span class="params"></span>)</span>{<span class="built_in">console</span>.log(<span class="number">2</span>);});</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">setTimeout(<span class="function"><span class="keyword">function</span> <span class="title">timeout</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'TIMEOUT FIRED'</span>);</span><br><span class="line">}, <span class="number">0</span>)</span><br><span class="line"><span class="comment">// 1</span></span><br><span class="line"><span class="comment">// 2</span></span><br><span class="line"><span class="comment">// TIMEOUT FIRED</span></span><br></pre></td></tr></table></figure><p>上面代码中,由于process.nextTick方法指定的回调函数,总是在当前”执行栈”的尾部触发,所以不仅函数A比setTimeout指定的回调函数timeout先执行,而且函数B也比timeout先执行。这说明,如果有多个process.nextTick语句(不管它们是否嵌套),将全部在当前”执行栈”执行。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">setImmediate(<span class="function"><span class="keyword">function</span> <span class="title">A</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="number">1</span>);</span><br><span class="line"> setImmediate(<span class="function"><span class="keyword">function</span> <span class="title">B</span>(<span class="params"></span>)</span>{<span class="built_in">console</span>.log(<span class="number">2</span>);});</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">setTimeout(<span class="function"><span class="keyword">function</span> <span class="title">timeout</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'TIMEOUT FIRED'</span>);</span><br><span class="line">}, <span class="number">0</span>);</span><br></pre></td></tr></table></figure><p>上面代码中,setImmediate与setTimeout(fn,0)各自添加了一个回调函数A和timeout,都是在下一次Event Loop触发。那么,哪个回调函数先执行呢?答案是不确定。运行结果可能是1–TIMEOUT FIRED–2,也可能是TIMEOUT FIRED–1–2。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">setImmediate(<span class="function"><span class="keyword">function</span> (<span class="params"></span>)</span>{</span><br><span class="line"> setImmediate(<span class="function"><span class="keyword">function</span> <span class="title">A</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="number">1</span>);</span><br><span class="line"> setImmediate(<span class="function"><span class="keyword">function</span> <span class="title">B</span>(<span class="params"></span>)</span>{<span class="built_in">console</span>.log(<span class="number">2</span>);});</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> setTimeout(<span class="function"><span class="keyword">function</span> <span class="title">timeout</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'TIMEOUT FIRED'</span>);</span><br><span class="line"> }, <span class="number">0</span>);</span><br><span class="line">});</span><br><span class="line"><span class="comment">// 1</span></span><br><span class="line"><span class="comment">// TIMEOUT FIRED</span></span><br><span class="line"><span class="comment">// 2</span></span><br></pre></td></tr></table></figure><p>setImmediate和setTimeout被封装在一个setImmediate里面,它的运行结果总是1–TIMEOUT FIRED–2,这时函数A一定在timeout前面触发。是因为setImmediate总是将事件注册到下一轮Event Loop,所以函数A和timeout是在同一轮Loop执行,而函数B在下一轮Loop执行。</p><p>so.个process.nextTick语句总是在当前”执行栈”一次执行完,多个setImmediate可能则需要多次loop才能执行完。事实上,这正是Node.js 10.0版添加setImmediate方法的原因,否则像下面这样的递归调用process.nextTick,将会没完没了,主线程根本不会去读取”事件队列”!<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">process.nextTick(<span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> process.nextTick(foo);</span><br><span class="line">});</span><br></pre></td></tr></table></figure></p><h3 id="Node-js是单线程,如何实现多个同时的文件IO?"><a href="#Node-js是单线程,如何实现多个同时的文件IO?" class="headerlink" title="Node.js是单线程,如何实现多个同时的文件IO?"></a>Node.js是单线程,如何实现多个同时的文件IO?</h3><p>Node.js的fs调用V8的libuv中的uv_fs_open,绑定JS的callback到一个c的函数指针上,然后推入事件列表队列(QueueUserWorkItem),再根据操作系统,Windows下使用IOCP来完成异步IO,* NIX上使用libev来实现。说明Node.js从上层到V8是单线程,从libuv到IOCP或者libev是多线程IO读写。</p><p>so, Node采用的是单线程的处理机制(所有的I/O请求都采用非阻塞的工作方式),至少从Node.js开发者的角度是这样的。 而在底层,Node.js借助libuv来作为抽象封装层, 从而屏蔽不同操作系统的差异,Node可以借助libuv来来实现多线程。</p><h2 id="node-优缺点"><a href="#node-优缺点" class="headerlink" title="node 优缺点"></a>node 优缺点</h2><p>单线程:服务器无法接受新的请求,即阻塞式 I/O。<br>多线程:服务器每创建一个线程,每个线程大概会占用 2M 的系统内存,而且线程之间的切换也会降低服务器的处理效率。但并发量高的时候,请求仍然需要等待。成本效率低,且要考虑死锁,数据不一致等问题。<br>事件驱动I/O:所有请求以及同时传入的回调函数均发送至同一线程,该线程通常叫做 Event loop 线程,该线程负责在 I/O 执行完毕后,将结果返回给回调函数。 I/O 操作本身并不在该线程内执行,所以不会阻塞后续请求。但是维护事件队列也需要成本,再由于NodeJS是单线程,事件队列越长,得到响应的时间就越长,并发量上去还是会力不从心。</p><p>优点:</p><ol><li>高并发(最重要的优点)</li><li>适合I/O密集型应用</li></ol><p>缺点:</p><ol><li>不适合CPU密集型应用;CPU密集型应用给Node带来的挑战主要是:由于JavaScript单线程的原因,如果有长时间运行的计算(比如大循环),将会导致CPU时间片不能释放,使得后续I/O无法发起;<br>解决方法:分解大型运算任务为多个小任务,使得运算能够适时释放,不阻塞I/O调用的发起;</li><li>只支持单核CPU,不能充分利用CPU</li><li>可靠性低,一旦代码某个环节崩溃,整个系统都崩溃(单进程,单线程)<br>解决方案:(1)Ngix反向代理,负载均衡,开多个进程,绑定多个端口;(2)开多个进程监听同一个端口,使用cluster模块;</li><li>开源组件库质量参差不齐,更新快,向下不兼容</li><li>Debug不方便,错误没有stack trace</li></ol><h2 id="多核处理器模块cluster"><a href="#多核处理器模块cluster" class="headerlink" title="多核处理器模块cluster"></a>多核处理器模块cluster</h2><p>nodejs是一个单进程单线程的服务器引擎,不管有多么的强大硬件,只能利用到单个CPU进行计算。所以,有人开发了第三方的cluster,让node可以利用多核CPU实现并行。在V0.6.0版本,Nodejs内置了cluster的特性。<br>cluster模块,可以帮助我们简化多进程并行化程序的开发难度,轻松构建一个用于负载均衡的集群。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> cluster = <span class="built_in">require</span>(<span class="string">'cluster'</span>);</span><br><span class="line"><span class="keyword">var</span> http = <span class="built_in">require</span>(<span class="string">'http'</span>);</span><br><span class="line"><span class="keyword">var</span> numCPUs = <span class="built_in">require</span>(<span class="string">'os'</span>).cpus().length;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (cluster.isMaster) {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"master start..."</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Fork workers.</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < numCPUs; i++) {</span><br><span class="line"> cluster.fork();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> cluster.on(<span class="string">'listening'</span>,<span class="function"><span class="keyword">function</span>(<span class="params">worker,address</span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'listening: worker '</span> + worker.process.pid +<span class="string">', Address: '</span>+address.address+<span class="string">":"</span>+address.port);</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> cluster.on(<span class="string">'exit'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">worker, code, signal</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'worker '</span> + worker.process.pid + <span class="string">' died'</span>);</span><br><span class="line"> });</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> http.createServer(<span class="function"><span class="keyword">function</span>(<span class="params">req, res</span>) </span>{</span><br><span class="line"> res.writeHead(<span class="number">200</span>);</span><br><span class="line"> res.end(<span class="string">"hello world\n"</span>);</span><br><span class="line"> }).listen(<span class="number">0</span>);</span><br><span class="line">}</span><br><span class="line"><span class="comment">//</span></span><br><span class="line">master start...</span><br><span class="line">listening: worker <span class="number">2368</span>, <span class="attr">Address</span>: <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span>:<span class="number">57132</span></span><br><span class="line">listening: worker <span class="number">1880</span>, <span class="attr">Address</span>: <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span>:<span class="number">57132</span></span><br><span class="line">listening: worker <span class="number">1384</span>, <span class="attr">Address</span>: <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span>:<span class="number">57132</span></span><br><span class="line">listening: worker <span class="number">1652</span>, <span class="attr">Address</span>: <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span>:<span class="number">57132</span></span><br><span class="line"><span class="comment">//master是总控节点,worker是运行节点。然后根据CPU的数量,启动worker。</span></span><br></pre></td></tr></table></figure></p><p>原理:<br>每个worker进程通过使用child_process.fork()函数,基于IPC(Inter-Process Communication,进程间通信),实现与master进程间通信。</p><p>当worker使用server.listen()函数时 ,将参数序列传递给master进程。如果master进程已经匹配workers,会将传递句柄给工人。如果master没有匹配好worker,那么会创建一个worker,再传递并句柄传递给worker。</p><p>在边界条件,有3个有趣的行为:<br>注:下面server.listen(),是对底层“http.Server–>net.Server”类的调用。</p><ol><li>server.listen({fd: 7}):在master和worker通信过程,通过传递文件,master会监听“文件描述为7”,而不是传递“文件描述为7”的引用。</li><li>server.listen(handle):master和worker通信过程,通过handle函数进行通信,而不用进程联系</li><li>server.listen(0):在master和worker通信过程,集群中的worker会打开一个随机端口共用,通过socket通信,像上例中的57132<br>当多个进程都在 accept() 同样的资源的时候,操作系统的负载均衡非常高效。Node.js没有路由逻辑,worker之间没有共享状态。所以,程序要设计得简单一些,比如基于内存的session。</li></ol><p>因为workers都是独力运行的,根据程序的需要,它们可以被独立删除或者重启,worker并不相互影响。只要还有workers存活,则master将继续接收连接。Node不会自动维护workers的数目。我们可以建立自己的连接池。</p><p>进行负载均衡:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> cluster = <span class="built_in">require</span>(<span class="string">'cluster'</span>);</span><br><span class="line"><span class="keyword">var</span> http = <span class="built_in">require</span>(<span class="string">'http'</span>);</span><br><span class="line"><span class="keyword">var</span> numCPUs = <span class="built_in">require</span>(<span class="string">'os'</span>).cpus().length;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (cluster.isMaster) {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'[master] '</span> + <span class="string">"start master..."</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < numCPUs; i++) {</span><br><span class="line"> cluster.fork();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> cluster.on(<span class="string">'listening'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">worker, address</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'[master] '</span> + <span class="string">'listening: worker'</span> + worker.id + <span class="string">',pid:'</span> + worker.process.pid + <span class="string">', Address:'</span> + address.address + <span class="string">":"</span> + address.port);</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line">} <span class="keyword">else</span> <span class="keyword">if</span> (cluster.isWorker) {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'[worker] '</span> + <span class="string">"start worker ..."</span> + cluster.worker.id);</span><br><span class="line"> http.createServer(<span class="function"><span class="keyword">function</span> (<span class="params">req, res</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'worker'</span>+cluster.worker.id);</span><br><span class="line"> res.end(<span class="string">'worker'</span>+cluster.worker.id+<span class="string">',PID:'</span>+process.pid);</span><br><span class="line"> }).listen(<span class="number">3000</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>]]></content>
<tags>
<tag> node </tag>
</tags>
</entry>
<entry>
<title>两个数正序和逆序相加</title>
<link href="/2018/11/29/AddTwoNumbers/"/>
<content type="html"><![CDATA[<h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>本章将介绍两个数正序和逆序相加两种方法。</p><a id="more"></a><p>##问题1:</p><blockquote><p>给定两个非空链表来表示两个非负整数。位数按照逆序方式存储,它们的每个节点只存储单个数字。将两数相加返回一个新的链表。<br>你可以假设除了数字 0 之外,这两个数字都不会以零开头。<br>示例:<br>输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)<br>输出:7 -> 0 -> 8<br>原因:342 + 465 = 807</p></blockquote><p>##思考:</p><ol><li>342 + 465 = 807 也就是位位相加,注意一下每一位相加要加上进位,最后一次两个链表都到了结束,也要注意一下有没有进位:比如 99 + 1 = 100;</li><li>注意链表怎么把每个节点连接起来:每次生成一个listnode元素,通过当前listnode的下一个元素(cur_node.next)连城链。</li><li>最后返回一个列表,所以要给个头(result)指示列表起始的位置,再通过cur_node链接起链表串</li></ol><p>##代码:<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* Definition for singly-linked list.</span></span><br><span class="line"><span class="comment">* function ListNode(val) {</span></span><br><span class="line"><span class="comment">* this.val = val;</span></span><br><span class="line"><span class="comment">* this.next = null;</span></span><br><span class="line"><span class="comment">* }</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> addTwoNumbers = <span class="function"><span class="keyword">function</span>(<span class="params">l1, l2</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> val1 = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">var</span> val2 = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">var</span> carry = <span class="number">0</span>;<span class="comment">//进位</span></span><br><span class="line"> <span class="keyword">var</span> result = <span class="literal">null</span>;<span class="comment">//返回值</span></span><br><span class="line"> <span class="keyword">var</span> cur_node = <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> sumWithCarry = <span class="function"><span class="keyword">function</span>(<span class="params">sum</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span>(sum >= <span class="number">10</span>) {</span><br><span class="line"> carry = <span class="number">1</span>;</span><br><span class="line"> sum = sum - <span class="number">10</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> carry = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> sum;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(l1 || l2) {</span><br><span class="line"> val1 = l1 ? l1.val : <span class="number">0</span>;</span><br><span class="line"> val2 = l2 ? l2.val : <span class="number">0</span>;</span><br><span class="line"> l1 = l1 ? l1.next : <span class="literal">null</span>;</span><br><span class="line"> l2 = l2 ? l2.next : <span class="literal">null</span>;</span><br><span class="line"> cur_node = <span class="keyword">new</span> ListNode(sumWithCarry(val1 + val2));</span><br><span class="line"> result = cur_node;</span><br><span class="line"> <span class="keyword">while</span>(l1 || l2) {</span><br><span class="line"> val1 = l1 ? l1.val : <span class="number">0</span>;</span><br><span class="line"> val2 = l2 ? l2.val : <span class="number">0</span>;</span><br><span class="line"> l1 = l1 ? l1.next : <span class="literal">null</span>;</span><br><span class="line"> l2 = l2 ? l2.next : <span class="literal">null</span>;</span><br><span class="line"> cur_node.next = <span class="keyword">new</span> ListNode(sumWithCarry(val1 + val2 + carry));</span><br><span class="line"> cur_node = cur_node.next;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(carry != <span class="number">0</span>) {</span><br><span class="line"> cur_node.next = <span class="keyword">new</span> ListNode(sumWithCarry(carry));</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">};</span><br></pre></td></tr></table></figure></p><p>##问题2:</p><blockquote><p>如果链表中的数字不是按逆序存储的呢?例如:<br>(3 -> 4 -> 2) + (4 -> 6 -> 5) = 8 -> 0 -> 7<br>符合人类观察的习惯: 342+465 = 807</p></blockquote><p>##思路:</p><ol><li>思路1也是位位相加, 将两个列表中的值存入数组中:result_array = [3+4, 4+6, 2+5] = [7,10, 7], 再考虑进位为题变成 [ 8, 0, 7 ], 最后生成listnode连接起来。但是这种方法在列表不等长的情况下是不可行了,可跳过去看最后。<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> addTwoNumbers = <span class="function"><span class="keyword">function</span>(<span class="params">l1, l2</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> result_array = [];</span><br><span class="line"> <span class="keyword">var</span> result = <span class="literal">null</span>;<span class="comment">//返回值</span></span><br><span class="line"> <span class="keyword">var</span> cur_node = <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">var</span> val1 = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">var</span> val2 = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span>(l1 || l2) {</span><br><span class="line"> val1 = l1 ? l1.val : <span class="number">0</span>;</span><br><span class="line"> val2 = l2 ? l2.val : <span class="number">0</span>;</span><br><span class="line"> l1 = l1 ? l1.next : <span class="literal">null</span>;</span><br><span class="line"> l2 = l2 ? l2.next : <span class="literal">null</span>;</span><br><span class="line"> result_array.push(val1 + val2)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = result_array.length - <span class="number">1</span>; i >= <span class="number">0</span>; i--) {</span><br><span class="line"> <span class="keyword">if</span>(result_array[i] >= <span class="number">10</span>) {</span><br><span class="line"> result_array[i] = result_array[i] - <span class="number">10</span>;</span><br><span class="line"> <span class="keyword">if</span>(i > <span class="number">0</span>) {</span><br><span class="line"> result_array[i <span class="number">-1</span>] = result_array[i<span class="number">-1</span>] + <span class="number">1</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> result_array.unshift(<span class="number">1</span>);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>( result_array.length ){</span><br><span class="line"> result = <span class="keyword">new</span> ListNode(result_array[<span class="number">0</span>]);</span><br><span class="line"> cur_node = result;</span><br><span class="line"> <span class="keyword">if</span>(result_array.length > <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">1</span>; i <= result_array.length - <span class="number">1</span>; i ++) {</span><br><span class="line"> cur_node.next = <span class="keyword">new</span> ListNode(result_array[i]);</span><br><span class="line"> cur_node = cur_node.next;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">};</span><br></pre></td></tr></table></figure></li></ol><p>乍看起来非常完美,但是遇到了一个极其重要的逻辑问题:在处理两列表等长时这个方法可行,但是不等长时相加错误,比如: [9,9] + [2] = [9, 11]=[1,0,1], 但上述办法算出来是[11,9]=[1,1,9],所以在两列表不等长时,必须将相加位位置对齐,在另一个列表元素数组前面加上很多0;但是我觉得这种方式时间复杂度太高了。</p><ol start="2"><li>思路2:然后我想为什么不能将它们变为数字再进行相加呢?[9,9] + [2] = 99 + 2 = 101 = [1,0,1]</li></ol><p>##代码:<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> addTwoNumbers = <span class="function"><span class="keyword">function</span>(<span class="params">l1, l2</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> result_array = [];</span><br><span class="line"> <span class="keyword">var</span> result_sum = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">var</span> result1 = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">var</span> result2 = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">var</span> result = <span class="literal">null</span>;<span class="comment">//返回值</span></span><br><span class="line"> <span class="keyword">var</span> cur_node = <span class="literal">null</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">var</span> val1 = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">var</span> val2 = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span>(l1 || l2) {</span><br><span class="line"> val1 = l1 ? l1.val : <span class="number">0</span>;</span><br><span class="line"> l1 = l1 ? l1.next : <span class="literal">null</span>;</span><br><span class="line"> result1 = val1 + result1*<span class="number">10</span>;</span><br><span class="line"> val2 = l2 ? l2.val : <span class="number">0</span>;</span><br><span class="line"> l2 = l2 ? l2.next : <span class="literal">null</span>;</span><br><span class="line"> result2 = val2 + result2*<span class="number">10</span>;</span><br><span class="line"> }</span><br><span class="line"> result_sum = result1 + result2;</span><br><span class="line"> <span class="keyword">while</span>(result_sum >= <span class="number">10</span>) {</span><br><span class="line"> result_array.push(result_sum % <span class="number">10</span>);</span><br><span class="line"> result_sum = ~~(result_sum / <span class="number">10</span>);</span><br><span class="line"> }</span><br><span class="line"> result_array.push(result_sum);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>( result_array.length ){</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = result_array.length - <span class="number">1</span>; i >= <span class="number">0</span>; i--) {</span><br><span class="line"> <span class="keyword">if</span>(i == result_array.length - <span class="number">1</span>) {</span><br><span class="line"> result = <span class="keyword">new</span> ListNode(result_array[i]);</span><br><span class="line"> cur_node = result;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> cur_node.next = <span class="keyword">new</span> ListNode(result_array[i]);</span><br><span class="line"> cur_node = cur_node.next;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line">};</span><br></pre></td></tr></table></figure></p>]]></content>
<tags>
<tag> 算法 </tag>
</tags>
</entry>
<entry>
<title>常见webpack的loaders和插件</title>
<link href="/2018/11/16/webpack-common/"/>
<content type="html"><![CDATA[<p>本文将介绍一些常见webpack的loaders和插件,以供参考学习~<br><a id="more"></a></p><h2 id="loaders"><a href="#loaders" class="headerlink" title="loaders"></a>loaders</h2><h3 id="css-loader"><a href="#css-loader" class="headerlink" title="css loader"></a>css loader</h3><p>loader的加载顺序是从右往左。</p><h4 id="css-loader-1"><a href="#css-loader-1" class="headerlink" title="css-loader"></a>css-loader</h4><p>css-loader 解释(interpret) @import 和 url() ,会 import/require() 后再解析(resolve)它们。<br>css-loader以字符串形式读取CSS文件。<br>查询参数 modules 会启用 CSS 模块规范, 这将启用局部作用域 CSS。你可以使用 :global(…) 或 :global 关闭选择器 and/or 规则。</p><h4 id="style-loader"><a href="#style-loader" class="headerlink" title="style-loader"></a>style-loader</h4><p>获取这些样式并创建 style 中的标记 head 包含这些样式的元素。<br>在使用局部作用域 CSS 时,模块导出生成的(局部)标识符(identifier)。</p><h4 id="sass-loader"><a href="#sass-loader" class="headerlink" title="sass-loader"></a>sass-loader</h4><p>将sass转换成css</p><h4 id="postcss-loader"><a href="#postcss-loader" class="headerlink" title="postcss-loader"></a>postcss-loader</h4><p>PostCSS是基于js插件去转换css的一个工具。这些插件支持变量,mixin,未来的css语法,在线图片甚至更多。</p><p>常用的插件有:autoprefixer,cssnano (压缩css代码)<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> loader: <span class="string">'postcss-loader'</span>,</span><br><span class="line"> options: {</span><br><span class="line"> plugins: <span class="function"><span class="params">()</span> =></span> [</span><br><span class="line"> <span class="built_in">require</span>(<span class="string">'cssnano'</span>)({</span><br><span class="line"> preset: <span class="string">'default'</span>,</span><br><span class="line"> }),</span><br><span class="line"> <span class="built_in">require</span>(<span class="string">'autoprefixer'</span>)({ <span class="attr">browsers</span>: [<span class="string">'last 10 Chrome versions'</span>, <span class="string">'last 5 Firefox versions'</span>, <span class="string">'Safari >= 6'</span>, <span class="string">'ie > 8'</span>] })</span><br><span class="line"> ]</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h3 id="文件和图片-loader"><a href="#文件和图片-loader" class="headerlink" title="文件和图片 loader"></a>文件和图片 loader</h3><h4 id="file-loader"><a href="#file-loader" class="headerlink" title="file-loader"></a>file-loader</h4><p>默认情况下,生成的文件的文件名就是文件内容的 MD5 哈希值并会保留所引用资源的原始扩展名。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">use: <span class="string">'file-loader?name=[hash].[ext]'</span></span><br></pre></td></tr></table></figure></p><h4 id="url-loader"><a href="#url-loader" class="headerlink" title="url-loader"></a>url-loader</h4><p>url-loader 功能类似于 file-loader,但是在文件大小(单位 byte)低于指定的限制时,可以返回一个 DataURL。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">use: <span class="string">'url-loader?limit=1024'</span></span><br></pre></td></tr></table></figure></p><h3 id="babel-loader"><a href="#babel-loader" class="headerlink" title="babel-loader"></a>babel-loader</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>: {</span><br><span class="line"> rules: [</span><br><span class="line"> {</span><br><span class="line"> test: <span class="regexp">/\.js$/</span>,</span><br><span class="line"> exclude: <span class="regexp">/(node_modules|bower_components)/</span>,</span><br><span class="line"> use: {</span><br><span class="line"> loader: <span class="string">'babel-loader?cacheDirectory=true'</span>,</span><br><span class="line"> options: {</span><br><span class="line"> presets: [<span class="string">'@babel/preset-env'</span>],</span><br><span class="line"> plugins: [<span class="built_in">require</span>(<span class="string">'@babel/plugin-transform-object-rest-spread'</span>)]</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>此 loader 也支持下面这些 loader 特定(loader-specific)的选项:</p><p>cacheDirectory:默认值为 false。当有设置时,指定的目录将用来缓存 loader 的执行结果。之后的 webpack 构建,将会尝试读取缓存,来避免在每次执行时,可能产生的、高性能消耗的 Babel 重新编译过程(recompilation process)。如果设置了一个空值 (loader: ‘babel-loader?cacheDirectory’) 或者 true (loader: babel-loader?cacheDirectory=true),loader 将使用默认的缓存目录 node_modules/.cache/babel-loader,如果在任何根目录下都没有找到 node_modules 目录,将会降级回退到操作系统默认的临时文件目录。</p><p>cacheIdentifier:默认是一个由 babel-core 版本号,babel-loader 版本号,.babelrc 文件内容(存在的情况下),环境变量 BABEL_ENV 的值(没有时降级到 NODE_ENV)组成的字符串。可以设置为一个自定义的值,在 identifier 改变后,强制缓存失效。</p><p>forceEnv:默认将解析 BABEL_ENV 然后是 NODE_ENV。允许你在 loader 级别上覆盖 BABEL_ENV/NODE_ENV。对有不同 babel 配置的,客户端和服务端同构应用非常有用。</p><h2 id="plugins"><a href="#plugins" class="headerlink" title="plugins"></a>plugins</h2><h3 id="CommonsChunkPlugin"><a href="#CommonsChunkPlugin" class="headerlink" title="CommonsChunkPlugin"></a>CommonsChunkPlugin</h3><p>通过将公共模块拆出来,最终合成的文件能够在最开始的时候加载一次,便存到缓存中供后续使用。这个带来速度上的提升,因为浏览器会迅速将公共的代码从缓存中取出来,而不是每次访问一个新页面时,再去加载一个更大的文件。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">entry: {</span><br><span class="line"> vendor: [<span class="string">"jquery"</span>, <span class="string">"other-lib"</span>],</span><br><span class="line"> app: <span class="string">"./entry"</span></span><br><span class="line">},</span><br><span class="line">plugins: [</span><br><span class="line"> <span class="keyword">new</span> webpack.optimize.CommonsChunkPlugin({</span><br><span class="line"> name: <span class="string">"vendor"</span>,</span><br><span class="line"> <span class="comment">// filename: "vendor.js"</span></span><br><span class="line"> <span class="comment">// (给 chunk 一个不同的名字)</span></span><br><span class="line"></span><br><span class="line"> minChunks: <span class="literal">Infinity</span>,</span><br><span class="line"> <span class="comment">// (随着 entry chunk 越来越多,</span></span><br><span class="line"> <span class="comment">// 这个配置保证没其它的模块会打包进 vendor chunk)</span></span><br><span class="line"> })</span><br><span class="line">]</span><br></pre></td></tr></table></figure></p><p>filename:可以指定提取出的公共代码的文件名称,可以使用output配置项中文件名的占位符。未定义时使用name作为文件名。</p><h3 id="ExtractTextWebpackPlugin"><a href="#ExtractTextWebpackPlugin" class="headerlink" title="ExtractTextWebpackPlugin"></a>ExtractTextWebpackPlugin</h3><p>它会将所有的入口 chunk(entry chunks)中引用的 * .css,移动到独立分离的 CSS 文件。因此,你的样式将不再内嵌到 JS bundle 中,而是会放到一个单独的 CSS 文件(即 styles.css)当中。 如果你的样式文件大小较大,这会做更快提前加载,因为 CSS bundle 会跟 JS bundle 并行加载。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> ExtractTextPlugin = <span class="built_in">require</span>(<span class="string">"extract-text-webpack-plugin"</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> <span class="built_in">module</span>: {</span><br><span class="line"> rules: [</span><br><span class="line"> {</span><br><span class="line"> test: <span class="regexp">/\.css$/</span>,</span><br><span class="line"> use: ExtractTextPlugin.extract({</span><br><span class="line"> fallback: <span class="string">"style-loader"</span>,</span><br><span class="line"> use: <span class="string">"css-loader"</span></span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line"> },</span><br><span class="line"> plugins: [</span><br><span class="line"> <span class="keyword">new</span> ExtractTextPlugin(<span class="string">"styles.css"</span>),</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>filename : 生成文件的文件名。可能包含 [name], [id] and [contenthash]<br>allChunks : 从所有额外的 chunk(additional chunk) 提取(默认情况下,它仅从初始chunk(initial chunk) 中提取)</p><p>当使用 CommonsChunkPlugin 并且在公共 chunk 中有提取的 chunk(来自ExtractTextPlugin.extract)时,allChunks 必须设置为 true</p><h3 id="HotModuleReplacementPlugin"><a href="#HotModuleReplacementPlugin" class="headerlink" title="HotModuleReplacementPlugin"></a>HotModuleReplacementPlugin</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> webpack.HotModuleReplacementPlugin({</span><br><span class="line"> <span class="comment">// Options...</span></span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>可以使用webpack-dev-server –hot 自动调用这个plugin</p><h3 id="UglifyjsWebpackPlugin"><a href="#UglifyjsWebpackPlugin" class="headerlink" title="UglifyjsWebpackPlugin"></a>UglifyjsWebpackPlugin</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> UglifyJsPlugin({</span><br><span class="line"> cache: <span class="literal">true</span>,</span><br><span class="line"> parallel: os.cpus().length</span><br><span class="line">}),</span><br></pre></td></tr></table></figure><p>parallel: 使用多进程并行运行来提高构建速度。并行化可以显著地加速构建,因此强烈推荐使用并行化。默认并发运行次数:os.cpus().length- 1。</p><h3 id="HtmlWebpackPlugin"><a href="#HtmlWebpackPlugin" class="headerlink" title="HtmlWebpackPlugin"></a>HtmlWebpackPlugin</h3><p>HtmlWebpackPlugin简化了HTML文件的创建,以便为你的webpack包提供服务。这对于在文件名中包含每次会随着编译而发生变化哈希的 webpack bundle 尤其有用。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> HtmlWebpackPlugin = <span class="built_in">require</span>(<span class="string">'html-webpack-plugin'</span>);</span><br><span class="line"><span class="keyword">new</span> HtmlwebpackPlugin({</span><br><span class="line"> filename: <span class="string">'../templates/index.html'</span>,</span><br><span class="line"> template: <span class="string">'src/index.ejs'</span>,</span><br><span class="line"> title: <span class="string">'name'</span>,</span><br><span class="line"> favicon: path.resolve(__dirname, <span class="string">"src/asserts/image/favicon.ico"</span>)</span><br><span class="line">}),</span><br></pre></td></tr></table></figure>]]></content>
</entry>
<entry>
<title>webpack原理呀!</title>
<link href="/2018/11/11/webpack-theory/"/>
<content type="html"><![CDATA[<p>本文将简单介绍webpack相关的原理~</p><h2 id="webpack构建流程"><a href="#webpack构建流程" class="headerlink" title="webpack构建流程"></a>webpack构建流程</h2><ol><li>初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;</li><li>开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;</li><li>确定入口:根据配置中的 entry 找出所有的入口文件;</li><li>编译模块:从入口文件出发,调用所有配置的 Loader </li><li>对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;</li><li>完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;</li><li>输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;</li><li>输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。</li></ol><p>在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。</p><a id="more"></a><h2 id="loader编译原理"><a href="#loader编译原理" class="headerlink" title="loader编译原理"></a>loader编译原理</h2><p>Loader像一个”翻译官”把读到的源文件内容转义成新的文件内容,并且每个Loader通过链式操作,将源文件一步步翻译成想要的样子。</p><p>编写Loader时要遵循单一原则,每个Loader只做一种”转义”工作。 每个Loader的拿到的是源文件内容(source),可以通过返回值的方式将处理后的内容输出,也可以调用this.callback()方法,将内容返回给webpack。 还可以通过 this.async()生成一个callback函数,再用这个callback将处理后的内容输出出去。 此外webpack还为开发者准备了开发loader的工具函数集——loader-utils。</p><h2 id="plugins原理"><a href="#plugins原理" class="headerlink" title="plugins原理"></a>plugins原理</h2><p> webpack在运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。</p><h2 id="用webpack进行优化"><a href="#用webpack进行优化" class="headerlink" title="用webpack进行优化"></a>用webpack进行优化</h2><p>如何利用webpack来优化前端性能?</p><ul><li>压缩代码。删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的UglifyJsPlugin和ParallelUglifyPlugin来压缩JS文件, 利用cssnano(css-loader?minimize)来压缩css</li><li>利用CDN加速。在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。可以利用webpack对于output参数和各loader的publicPath参数来修改资源路径</li><li>删除死代码(Tree Shaking)。将代码中永远不会走到的片段删除掉。可以通过在启动webpack时追加参数–optimize-minimize来实现或者直接配置uglifyjs + sideEffects</li><li>提取公共代码。</li></ul><h2 id="如何提高webpack的构建速度?"><a href="#如何提高webpack的构建速度?" class="headerlink" title="如何提高webpack的构建速度?"></a>如何提高webpack的构建速度?</h2><ul><li>多入口情况下,使用CommonsChunkPlugin来提取公共代码</li><li>通过externals配置来提取常用库</li><li>利用DllPlugin和DllReferencePlugin预编译资源模块 </li><li>通过DllPlugin来对那些我们引用但是绝对不会修改的npm包来进行预编译,再通过DllReferencePlugin将预编译的模块加载进来。</li><li>使用Happypack 实现多线程加速编译</li><li>使用webpack-uglify-parallel来提升uglifyPlugin的压缩速度。 原理上webpack-uglify-parallel采用了多核并行压缩来提升压缩速度</li><li>使用Tree-shaking和Scope Hoisting来剔除多余代码</li></ul><h2 id="如何在vue项目中实现按需加载?"><a href="#如何在vue项目中实现按需加载?" class="headerlink" title="如何在vue项目中实现按需加载?"></a>如何在vue项目中实现按需加载?</h2><p>Vue UI组件库的按需加载<br>为了快速开发前端项目,经常会引入现成的UI组件库如ElementUI、iView等,但是他们的体积和他们所提供的功能一样,是很庞大的。 而通常情况下, 我们仅仅需要少量的几个组件就足够了,但是我们却将庞大的组件库打包到我们的源码中,造成了不必要的开销。</p><p>不过很多组件库已经提供了现成的解决方案,如Element出品的babel-plugin-component和AntDesign出品的babel-plugin-import 安装以上插件后,在.babelrc配置中或babel-loader的参数中进行设置,即可实现组件按需加载了。</p><p>通过import()语句来控制加载时机,webpack内置了对于import()的解析,会将import()中引入的模块作为一个新的入口在生成一个chunk。 当代码执行到import()语句时,会去加载Chunk对应生成的文件。import()会返回一个Promise对象,所以为了让浏览器支持,需要事先注入Promise polyfill</p><h2 id="什么是长缓存?在webpack中如何做到长缓存优化?"><a href="#什么是长缓存?在webpack中如何做到长缓存优化?" class="headerlink" title="什么是长缓存?在webpack中如何做到长缓存优化?"></a>什么是长缓存?在webpack中如何做到长缓存优化?</h2><p>浏览器在用户访问页面的时候,为了加快加载速度,会对用户访问的静态资源进行存储,但是每一次代码升级或是更新,都需要浏览器去下载新的代码,最方便和简单的更新方式就是引入新的文件名称。在webpack中可以在output纵输出的文件指定chunkhash,并且分离经常更新的代码和框架代码。通过NameModulesPlugin或是HashedModuleIdsPlugin使再次打包文件名不变。</p><h2 id="热重启"><a href="#热重启" class="headerlink" title="热重启"></a>热重启</h2><p>热重启原理:eventsource sse,一旦服务器资源有更新,能够及时通知到客户端,从而实时的反馈到用户界面上。本质上是一个http,通过response流实时推送服务器信息到客户端。链接断开后会持续出发重连。_ webpack_hmr:每隔10s推送一条在消息到浏览器</p><p>实现:<br>client:创建new EventSource (“/message”),<br>Server:需要返回类型为text/event-stream的响应头,发送数据以data开头,\n\n结尾;<br>webpack-dev-server是一个机遇express的web server,监听8080,server内部调用webpack,这样的好处是提供了热加载和热替换的功能;<br>webpack-hot-middleware和webpack-dev-middleware</p><p>EventSource和websocket的区别:<br>eventSource本质仍然是http,仅提供服务器端到浏览器端的单向文本传输,不需要心跳链接,链接断开回持续重发链接;<br>websocket是基于TCP的协议,提供双向数据传输,支持二进制,需要心跳链接,断开链接不会重链;<br>EventSource更简洁轻量,WebSocket支持行更好(IE10+)。后者功能更强大一点。</p>]]></content>
</entry>
<entry>
<title>移动端高清适配方案</title>
<link href="/2018/10/07/one-pix/"/>
<content type="html"><![CDATA[<p>在移动端设置了边框为1px,为什么显示出来经常是模糊的呢?<br>这个涉及设备像素比的知识:<a href="https://www.zhangxinxu.com/wordpress/2012/08/window-devicepixelratio/" target="_blank" rel="noopener">https://www.zhangxinxu.com/wordpress/2012/08/window-devicepixelratio/</a><br>点击这个链接了解更多。</p><a id="more"></a><p>对于设计稿,我们一般采用iphone6的375×667,但是对于Retina屏幕是(如dpr = 2),物理像素是原来的四倍(750×1334)。这是什么意思呢?</p><h2 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h2><ul><li><p>物理像素(physical pixel)<br>一个物理像素是显示器(手机屏幕)上最小的物理显示单元,在操作系统的调度下,每一个设备像素都有自己的颜色值和亮度值。</p></li><li><p>设备独立像素(density-independent pixel)<br>设备独立像素(也叫密度无关像素),可以认为是计算机坐标系统中得一个点,这个点代表一个可以由程序使用的虚拟像素(比如: css像素),然后由相关系统转换为物理像素。</p></li></ul><p>所以说,物理像素和设备独立像素之间存在着一定的对应关系,这就是接下来要说的设备像素比。</p><ul><li>设备像素比(device pixel ratio )<br>设备像素比(简称dpr)定义了物理像素和设备独立像素的对应关系,它的值可以按如下的公式的得到:</li></ul><blockquote><p>设备像素比 = 物理像素 / 设备独立像素 (单方向)<br>在javascript中,可以通过window.devicePixelRatio获取到当前设备的dpr。</p></blockquote><p>通过下面的代码可以获取设备的逻辑像素:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> logicalHeight = screen.height;</span><br><span class="line"><span class="keyword">let</span> logicalWidth = screen.width;</span><br></pre></td></tr></table></figure></p><p>那么很多人就会认为:物理像素 = 逻辑像素 * dpr<br>但实际情况并不是这样,官方声称 iPhone6 plus 的 dpr = 3, 414px x 736px → 乘以 3 倍 dpr → 1242px x 2208px, 那么 iPhone6 plus 只有 1080px x 1920px,怎么去展示 1241px x 2208px 的分辨率呢?<br>原来 iPhone6 plus 对逻辑像素做了缩小处理,以适应物理像素,也就是<br>1241px x 2208px 除以 115% ,得到 1080px x 1920px。</p><p>so,在不同的屏幕上(普通屏幕 vs retina屏幕),css像素所呈现的大小(物理尺寸)是一致的,不同的是1个css像素所对应的物理像素个数是不一致的。</p><p>在普通屏幕下,1个css像素 对应 1个物理像素(1:1)。 在retina 屏幕下,1个css像素对应 4个物理像素(1:4)。</p><ul><li>位图像素<br>一个位图像素是栅格图像(如:png, jpg, gif等)最小的数据单元。每一个位图像素都包含着一些自身的显示信息(如:显示位置,颜色值,透明度等)。<br>对于dpr=2的retina屏幕而言,1个位图像素对应于4个物理像素,由于单个位图像素不可以再进一步分割,所以只能就近取色,从而导致图片模糊(注意上述的几个颜色值)。</li></ul><h2 id="图片高清问题"><a href="#图片高清问题" class="headerlink" title="图片高清问题"></a>图片高清问题</h2><p>比较好的方案就是两倍图片(@2x)。</p><p>200×300(css pixel)img标签,就需要提供400×600的图片。如此一来,位图像素点个数就是原来的4倍,在retina屏幕下,位图像素点个数就可以跟物理像素点个数形成 1 : 1的比例,图片自然就清晰了。<br>在普通屏幕下,200×300(css pixel)img标签,所对应的物理像素个数就是200×300个,而两倍图片的位图像素个数则是200×300x4,所以就出现一个物理像素点对应4个位图像素点,所以它的取色也只能通过一定的算法(显示结果就是一张只有原图像素总数四分之一,我们称这个过程叫做downsampling),肉眼看上去虽然图片不会模糊,但是会觉得图片缺少一些锐利度,或者是有点色差(但还是可以接受的)。</p><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">width</span>: 200<span class="selector-tag">px</span>;</span><br><span class="line"><span class="selector-tag">height</span>: 300<span class="selector-tag">px</span>;</span><br><span class="line"><span class="selector-tag">background-image</span>: <span class="selector-tag">url</span>(<span class="selector-tag">image</span>@<span class="keyword">2x</span>.<span class="keyword">jpg</span>);</span><br><span class="line">background-size: 200px 300px; // 或者: background-size: contain;</span><br></pre></td></tr></table></figure><h2 id="border-1px问题"><a href="#border-1px问题" class="headerlink" title="border:1px问题"></a>border:1px问题</h2><p>同样是1px的border,在高清屏下的线条粗。</p><p>设计师想要的retina下border: 1px;,其实就是1物理像素宽,对于css而言,可以认为是border: 0.5px;,这是retina下(dpr=2)下能显示的最小单位。<br>然而,无奈并不是所有手机浏览器都能识别border: 0.5px;ios7以下,android等其他系统里,0.5px会被当成为0px处理,那么如何实现这0.5px呢?</p><p>最简单的一个做法就是这样(元素scale):</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.scale</span>{</span><br><span class="line"> <span class="attribute">position</span>: relative;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.scale</span><span class="selector-pseudo">:after</span>{</span><br><span class="line"> <span class="attribute">content</span>:<span class="string">""</span>;</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">bottom</span>:<span class="number">0px</span>;</span><br><span class="line"> <span class="attribute">left</span>:<span class="number">0px</span>;</span><br><span class="line"> <span class="attribute">right</span>:<span class="number">0px</span>;</span><br><span class="line"> <span class="attribute">border-bottom</span>:<span class="number">1px</span> solid <span class="number">#ddd</span>;</span><br><span class="line"> <span class="attribute">-webkit-transform</span>:<span class="built_in">scaleY</span>(.5);</span><br><span class="line"> <span class="attribute">-webkit-transform-origin</span>:<span class="number">0</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>我们照常写border-bottom: 1px solid #ddd;,然后通过transform: scaleY(.5)缩小0.5倍来达到0.5px的效果.但是这样hack实在是不够通用(如:圆角等),写起来也麻烦。</p><p>最推荐的是:对于iphone5(dpr=2),添加如下的meta标签,设置viewport(scale 0.5):</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=640,initial-scale=0.5,maximum-scale=0.5, minimum-scale=0.5,user-scalable=no"</span>></span></span><br></pre></td></tr></table></figure><p>这样,页面中的所有的border: 1px都将缩小0.5,从而达到border: 0.5px;的效果。</p><h2 id="多屏适配问题"><a href="#多屏适配问题" class="headerlink" title="多屏适配问题"></a>多屏适配问题</h2><p>针对不同手机屏幕尺寸和dpr动态的改变根节点html的font-size大小(基准值)。</p><blockquote><p>rem = document.documentElement.clientWidth * dpr / 10</p></blockquote><p>乘以dpr,是因为页面有可能为了实现1px border页面会缩放(scale) 1/dpr 倍(如果没有,dpr=1),。<br>除以10,是为了取整,方便计算(理论上可以是任何值)</p><p>so,html的font-size可能会:</p><p>iphone3gs: 320px / 10 = 32px</p><p>iphone4/5: 320px * 2 / 10 = 64px</p><p>iphone6: 375px * 2 / 10 = 75px</p><p>如果有一个区块,在psd文件中量出:宽高750×300px的div,那么如何转换成rem单位呢?</p><blockquote><p>rem = px / 基准值;</p></blockquote><p>// 例如: .px2rem(height, 80);<br>.px2rem(@name, @px){<br> @{name}: @px / 75 * 1rem;<br>}<br>所以,对于宽高750×300px的div,我们用less就这样写</p><p>.px2rem(width, 750);<br>.px2rem(height, 300);</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">html</span>></span> </span><br><span class="line"> </span><br><span class="line"> <span class="tag"><<span class="name">head</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>1px question<span class="tag"></<span class="name">title</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"Content-Type"</span> <span class="attr">content</span>=<span class="string">"text/html;charset=UTF-8"</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">id</span>=<span class="string">"WebViewport"</span> <span class="attr">content</span>=<span class="string">"initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">style</span>></span><span class="undefined"> </span></span><br><span class="line"><span class="undefined"> html { </span></span><br><span class="line"><span class="undefined"> font-size: 1px; </span></span><br><span class="line"><span class="undefined"> } </span></span><br><span class="line"><span class="undefined"> * { </span></span><br><span class="line"><span class="undefined"> padding: 0; </span></span><br><span class="line"><span class="undefined"> margin: 0; </span></span><br><span class="line"><span class="undefined"> } </span></span><br><span class="line"><span class="undefined"> </span></span><br><span class="line"><span class="css"> <span class="selector-class">.bds_b</span> { </span></span><br><span class="line"><span class="css"> <span class="selector-tag">border-bottom</span>: 1<span class="selector-tag">px</span> <span class="selector-tag">solid</span> <span class="selector-id">#ccc</span>; </span></span><br><span class="line"><span class="undefined"> } </span></span><br><span class="line"><span class="undefined"> </span></span><br><span class="line"><span class="css"> <span class="selector-class">.a</span>, </span></span><br><span class="line"><span class="css"> <span class="selector-class">.b</span> { </span></span><br><span class="line"><span class="undefined"> margin-top: 1rem; </span></span><br><span class="line"><span class="undefined"> padding: 1rem; </span></span><br><span class="line"><span class="css"> <span class="selector-tag">font-size</span>: 1<span class="selector-class">.4rem</span>; </span></span><br><span class="line"><span class="undefined"> } </span></span><br><span class="line"><span class="undefined"> </span></span><br><span class="line"><span class="css"> <span class="selector-class">.a</span> { </span></span><br><span class="line"><span class="undefined"> width: 30rem; </span></span><br><span class="line"><span class="undefined"> } </span></span><br><span class="line"><span class="undefined"> </span></span><br><span class="line"><span class="css"> <span class="selector-class">.b</span> { </span></span><br><span class="line"><span class="css"> <span class="selector-tag">background</span>: <span class="selector-id">#f5f5f5</span>; </span></span><br><span class="line"><span class="undefined"> width: 20rem; </span></span><br><span class="line"><span class="undefined"> } </span></span><br><span class="line"><span class="undefined"> </span><span class="tag"></<span class="name">style</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">script</span>></span><span class="undefined"> </span></span><br><span class="line"><span class="undefined"> </span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> viewport = <span class="built_in">document</span>.querySelector(<span class="string">"meta[name=viewport]"</span>); </span></span><br><span class="line"><span class="javascript"> <span class="comment">//下面是根据设备像素设置viewport </span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span> (<span class="built_in">window</span>.devicePixelRatio == <span class="number">1</span>) { </span></span><br><span class="line"><span class="javascript"> viewport.setAttribute(<span class="string">'content'</span>, <span class="string">'width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no'</span>); </span></span><br><span class="line"><span class="undefined"> } </span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span> (<span class="built_in">window</span>.devicePixelRatio == <span class="number">2</span>) { </span></span><br><span class="line"><span class="javascript"> viewport.setAttribute(<span class="string">'content'</span>, <span class="string">'width=device-width,initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no'</span>); </span></span><br><span class="line"><span class="undefined"> } </span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span> (<span class="built_in">window</span>.devicePixelRatio == <span class="number">3</span>) { </span></span><br><span class="line"><span class="javascript"> viewport.setAttribute(<span class="string">'content'</span>, <span class="string">'width=device-width,initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no'</span>); </span></span><br><span class="line"><span class="undefined"> } </span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> docEl = <span class="built_in">document</span>.documentElement; </span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> fontsize = <span class="number">10</span> * (docEl.clientWidth / <span class="number">320</span>) + <span class="string">'px'</span>; </span></span><br><span class="line"><span class="undefined"> docEl.style.fontSize = fontsize; </span></span><br><span class="line"><span class="undefined"> </span></span><br><span class="line"><span class="undefined"> </span><span class="tag"></<span class="name">script</span>></span> </span><br><span class="line"> <span class="tag"></<span class="name">head</span>></span> </span><br><span class="line"> </span><br><span class="line"> <span class="tag"><<span class="name">body</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"bds_b a"</span>></span>下面的底边宽度是虚拟1像素的<span class="tag"></<span class="name">div</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"b"</span>></span>上面的边框宽度是虚拟1像素的<span class="tag"></<span class="name">div</span>></span> </span><br><span class="line"> <span class="tag"></<span class="name">body</span>></span> </span><br><span class="line"> </span><br><span class="line"><span class="tag"></<span class="name">html</span>></span><span class="tag"></<span class="name">span</span>></span></span><br></pre></td></tr></table></figure><h2 id="微信小程序的rpx"><a href="#微信小程序的rpx" class="headerlink" title="微信小程序的rpx"></a>微信小程序的rpx</h2><p>微信小程序所定义的一套 WXSS (WeiXin Style Sheets) 中有一个有趣的长度单位 rpx,官方描述:</p><blockquote><p>rpx(responsive pixel), 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。</p></blockquote><p>rpx与px的关系呢?</p><p>三款机器的逻辑像素:</p><p>iPhone5 : 320px x 568px<br>iPhone6 : 375px x 667px<br>iPhone6 plus : 414px x 736px</p><p>rpx 转换成 px 是需要乘以一个系数的(750 / 屏幕宽度):</p><p>px = rpx * n<br>其中系数 n,是跟着设备改变的:</p><p>iPhone5: n = 2.34<br>iPhone6: n = 2<br>iPhone6 plus: n = 1.81<br>所以 rpx 只是定义一个绝对值 750 宽度,然后简单的根据不同设备的逻辑像素来进行 rpx 到 px 的换算。</p><p>rpx 压根就不需要关心 DPR 和 PPI 的概念。</p>]]></content>
<tags>
<tag> css </tag>
</tags>
</entry>
<entry>
<title>微信小程序开发小记</title>
<link href="/2018/09/26/wxExe/"/>
<content type="html"><![CDATA[<h2 id="目录结构"><a href="#目录结构" class="headerlink" title="目录结构"></a>目录结构</h2><p>根目录生成:项目配置文件project.config.json。setting属性下包含:<br><img src="/images/wx_01.png"></p><h3 id="配置层"><a href="#配置层" class="headerlink" title="配置层"></a>配置层</h3><p>小程序包含一个描述整体程序的 app 和多个描述各自页面的 page。</p><ul><li>app有 app.js / app.json / app.wxss 文件。</li><li>小程序页面有 js/wxml/json/wxss 文件。</li></ul><blockquote><p>app.json:文件用来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。<br> (pages, window, tabBar, networkTimeout)<br> 普通json:页面的配置只能设置 app.json 中部分 window 配置项的内容,页面中配置项会覆盖 app.json 的 window 中相同的配置项。</p></blockquote><h3 id="逻辑层"><a href="#逻辑层" class="headerlink" title="逻辑层"></a>逻辑层</h3><p>小程序框架的逻辑层并非运行在浏览器中,因此 JavaScript 在 web 中一些能力都无法使用,如 window,document 等。<br>app.js<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">App({</span><br><span class="line"> app.js: App({onLaunch(options) {</span><br><span class="line"> <span class="comment">// Do something initial when launch.</span></span><br><span class="line"> },</span><br><span class="line"> onShow(options) {</span><br><span class="line"> <span class="comment">// Do something when show.</span></span><br><span class="line"> },</span><br><span class="line"> onHide() {</span><br><span class="line"> <span class="comment">// Do something when hide.</span></span><br><span class="line"> },</span><br><span class="line"> onError(msg) {</span><br><span class="line"> <span class="built_in">console</span>.log(msg)</span><br><span class="line"> },</span><br><span class="line"> globalData: <span class="string">'I am global data'</span></span><br><span class="line">})</span><br></pre></td></tr></table></figure></p><p>对于小程序,可以在 App 的 onLaunch 和 onShow,或wx.getLaunchOptionsSync 中获取场景值。<br>wx.getLaunchOptionsSync(path, scene, query, shareTicker, refererInfo) 获取小程序启动时的参数。</p><p>普通js<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// index.js</span></span><br><span class="line">Page({</span><br><span class="line"> data: {</span><br><span class="line"> text: <span class="string">'This is page data.'</span></span><br><span class="line"> },</span><br><span class="line"> onLoad(options) {</span><br><span class="line"> <span class="comment">// Do some initialize when page load.</span></span><br><span class="line"> },</span><br><span class="line"> onReady() {</span><br><span class="line"> <span class="comment">// Do something when page ready.</span></span><br><span class="line"> },</span><br><span class="line"> onShow() {</span><br><span class="line"> <span class="comment">// Do something when page show.</span></span><br><span class="line"> },</span><br><span class="line"> onHide() {</span><br><span class="line"> <span class="comment">// Do something when page hide.</span></span><br><span class="line"> },</span><br><span class="line"> onUnload() {</span><br><span class="line"> <span class="comment">// Do something when page close.</span></span><br><span class="line"> },</span><br><span class="line"> onPullDownRefresh() {</span><br><span class="line"> <span class="comment">// Do something when pull down.</span></span><br><span class="line"> },</span><br><span class="line"> onReachBottom() {</span><br><span class="line"> <span class="comment">// Do something when page reach bottom.</span></span><br><span class="line"> },</span><br><span class="line"> onShareAppMessage() {</span><br><span class="line"> <span class="comment">// return custom share data when user share.</span></span><br><span class="line"> },</span><br><span class="line"> onPageScroll() {</span><br><span class="line"> <span class="comment">// Do something when page scroll</span></span><br><span class="line"> },</span><br><span class="line"> onResize() {</span><br><span class="line"> <span class="comment">// Do something when page resize</span></span><br><span class="line"> },</span><br><span class="line"> onTabItemTap(item) {</span><br><span class="line"> <span class="built_in">console</span>.log(item.index)</span><br><span class="line"> <span class="built_in">console</span>.log(item.pagePath)</span><br><span class="line"> <span class="built_in">console</span>.log(item.text)</span><br><span class="line"> },</span><br><span class="line"> <span class="comment">// Event handler.</span></span><br><span class="line"> viewTap() {</span><br><span class="line"> <span class="keyword">this</span>.setData({</span><br><span class="line"> text: <span class="string">'Set some data for updating view.'</span></span><br><span class="line"> }, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="comment">// this is setData callback</span></span><br><span class="line"> })</span><br><span class="line"> },</span><br><span class="line"> customData: {</span><br><span class="line"> hi: <span class="string">'MINA'</span></span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure></p><p>生命周期:<br>onLoad() 页面加载时触发,只会调用一次,可获取当前页面路径中的参数。<br>onShow() 页面显示/切入前台时触发,一般用来发送数据请求;<br>onReady() 页面初次渲染完成时触发, 只会调用一次,代表页面已可和视图层进行交互。<br>onHide() 页面隐藏/切入后台时触发, 如底部 tab 切换到其他页面或小程序切入后台等。<br>onUnload() 页面卸载时触发,如redirectTo或navigateBack到其他页面时。</p><h3 id="页面路由"><a href="#页面路由" class="headerlink" title="页面路由"></a>页面路由</h3><p>Tab 切换:页面全部出栈,只留下新的 Tab 页面<br>初始化:新页面入栈<br>打开新页面:新页面入栈<br>页面重定向:当前页面出栈,新页面入栈<br>页面返回:页面不断出栈,直到目标返回页</p><p>getCurrentPages() 函数用于获取当前页面栈的实例,以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面。</p><blockquote><p>navigateTo, redirectTo 只能打开非 tabBar 页面。<br>switchTab 只能打开 tabBar 页面。<br>reLaunch 可以打开任意页面。<br>页面底部的 tabBar 由页面决定,即只要是定义为 tabBar 的页面,底部都有 tabBar。<br>调用页面路由带的参数可以在目标页面的onLoad中获取</p></blockquote><p>wx.navigateTo 或使用组件 <code><navigator open-type="navigateTo"/></code><br>url: 类型是String 跳转非 tabBar 的页面的路径 , 路径后可以带参数。参数与路径之间使用?分隔,参数键与参数值用=相连,不同参数用&分隔;如 ‘path?key=value&key2=value2’<br>success: 类型Function 接口调用成功的回调函数<br>fail: 类型Function 接口调用失败的回调函数<br>complete: 类型Function 接口调用结束的回调函数(调用成功、失败都会执行)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">wx.navigateTo({</span><br><span class="line"> url: 'test?id=1'</span><br><span class="line">})</span><br><span class="line">wx.switchTab({</span><br><span class="line"> url: '/index' //不能带参数</span><br><span class="line">})</span><br></pre></td></tr></table></figure><h3 id="模块化"><a href="#模块化" class="headerlink" title="模块化"></a>模块化</h3><p>使用 require(path) 将公共代码引入<br>module.exports 导出</p><p>且有模板:<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">template</span> <span class="attr">name</span>=<span class="string">"head"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">view</span> <span class="attr">class</span>=<span class="string">'head-container'</span>></span></span><br><span class="line"> {{ info }}</span><br><span class="line"> <span class="tag"><<span class="name">image</span> <span class="attr">src</span>=<span class="string">'../../images/icons/back.png'</span> <span class="attr">class</span>=<span class="string">'back-icon'</span> <span class="attr">wx:if</span>=<span class="string">"{{hasBack}}"</span> <span class="attr">bindtap</span>=<span class="string">'toBack'</span>></span><span class="tag"></<span class="name">image</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">view</span>></span></span><br><span class="line"><span class="tag"></<span class="name">template</span>></span></span><br></pre></td></tr></table></figure></p><p>使用模板:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><<span class="keyword">import</span> src=<span class="string">"../../components/head.wxml"</span>/></span><br><span class="line"></span><br><span class="line"><template is=<span class="string">"head"</span> data=<span class="string">"{{info:'灯光设置', hasBack: true}}"</span>/></span><br></pre></td></tr></table></figure></p><p>C import B,B import A,在C中可以使用B定义的template,在B中可以使用A定义的template,但是C不能使用A定义的template。<br>include 可以将目标文件除了 <code><template/></code> <code><wxs/></code> 外的整个代码引入,相当于是拷贝到 include 位置<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><!-- index.wxml --></span><br><span class="line"><include src=<span class="string">"header.wxml"</span> /></span><br><span class="line"><view>body<<span class="regexp">/view></span></span><br><span class="line"><span class="regexp"><include src="footer.wxml" /</span>></span><br></pre></td></tr></table></figure></p><p>wxs:(像computer方法)<br>是小程序的一套脚本语言,结合 WXML,可以构建出页面的结构。<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">wxs</span> <span class="attr">module</span>=<span class="string">"m1"</span>></span></span><br><span class="line"> var getMax = function(array) { </span><br><span class="line"> var max = undefined; </span><br><span class="line"> for (var i = 0; i <span class="tag">< <span class="attr">array.length</span>; ++<span class="attr">i</span>) { </span></span><br><span class="line"><span class="tag"> <span class="attr">max</span> = <span class="string">max</span> === <span class="string">undefined</span> ? <span class="attr">array</span>[<span class="attr">i</span>] <span class="attr">:</span> (<span class="attr">max</span> ></span>= array[i] ? max : array[i]); </span><br><span class="line"> } </span><br><span class="line"> return max; </span><br><span class="line"> } </span><br><span class="line"> module.exports.getMax = getMax;</span><br><span class="line"><span class="tag"></<span class="name">wxs</span>></span></span><br><span class="line"></span><br><span class="line"><span class="comment"><!-- 调用 wxs 里面的 getMax 函数,参数为 page.js 里面的 array --></span></span><br><span class="line"><span class="tag"><<span class="name">view</span>></span>{{m1.getMax(array)}}<span class="tag"></<span class="name">view</span>></span></span><br></pre></td></tr></table></figure></p><p>在.wxs模块中引用其他 wxs 文件模块,可以使用 require 函数。</p><ul><li>只能引用 .wxs 文件模块,且必须使用相对路径。</li><li>wxs 模块均为单例,wxs 模块在第一次被引用时,会自动初始化为单例对象。多个页面,多个地方,多次引用,使用的都是同一个 wxs 模块对象。</li><li>如果一个 wxs 模块在定义之后,一直没有被引用,则该模块不会被解析与运行。</li></ul><h3 id="存储与请求"><a href="#存储与请求" class="headerlink" title="存储与请求"></a>存储与请求</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">wx.setStorageSync(<span class="string">'key'</span>, <span class="string">'value'</span>) <span class="comment">//同步</span></span><br><span class="line">wx.getStorageSync(<span class="string">'key'</span>)</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line">wx.request({</span><br><span class="line"> url: apiRoot + options.url,</span><br><span class="line"> data: options.data,</span><br><span class="line"> method: options.method ? options.method : <span class="string">'POST'</span>,</span><br><span class="line"> header: {</span><br><span class="line"> <span class="string">'Cache-Control'</span>: <span class="string">'no-cache'</span>,</span><br><span class="line"> <span class="string">'Content-Type'</span>: <span class="string">'application/x-www-form-urlencoded'</span>,</span><br><span class="line"> <span class="string">'token'</span>: token,</span><br><span class="line"> <span class="string">'XX-Device-Type'</span>: <span class="string">'wxapp'</span>,</span><br><span class="line"> <span class="string">'XX-Api-Version'</span>: <span class="keyword">this</span>.API_VERSION</span><br><span class="line"> },</span><br><span class="line"> success: <span class="function"><span class="params">res</span> =></span> {</span><br><span class="line"></span><br><span class="line"> },</span><br><span class="line"> fail: <span class="function"><span class="keyword">function</span> (<span class="params">res</span>) </span>{</span><br><span class="line"></span><br><span class="line"> },</span><br><span class="line"> complete: options.complete ? options.complete : <span class="literal">null</span></span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">wx.login({</span><br><span class="line"> success: <span class="function"><span class="params">loginRes</span> =></span> {</span><br><span class="line"> <span class="keyword">if</span> (loginRes.code) { <span class="comment">//开发者需要在开发者服务器后台调用 code2Session,使用 code 换取 openid 和 session_key 等信息</span></span><br><span class="line"> wx.getUserInfo({</span><br><span class="line"> withCredentials: <span class="literal">true</span>,</span><br><span class="line"> success: <span class="function"><span class="params">res</span> =></span> {</span><br><span class="line"> <span class="keyword">const</span> userInfo = res.userInfo</span><br><span class="line"> <span class="keyword">const</span> nickName = userInfo.nickName</span><br><span class="line"> <span class="keyword">const</span> avatarUrl = userInfo.avatarUrl</span><br><span class="line"> <span class="keyword">const</span> gender = userInfo.gender <span class="comment">// 性别 0:未知、1:男、2:女</span></span><br><span class="line"> <span class="keyword">const</span> province = userInfo.province</span><br><span class="line"> <span class="keyword">const</span> city = userInfo.city</span><br><span class="line"> <span class="keyword">const</span> country = userInfo.country</span><br><span class="line"> },</span><br><span class="line"> fail: <span class="function">(<span class="params">res</span>) =></span> {</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> fail: <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">开发者可以使用 wx.getSetting 获取用户当前的授权状态。</span><br><span class="line">wx.authorize(<span class="built_in">Object</span> object) <span class="comment">//提前向用户发起授权请求。</span></span><br><span class="line">wx.getUserInfo使用前要获取授权。</span><br></pre></td></tr></table></figure><p>如何封装数据请求:<br>在根目录下创建utils目录及api.js文件和apiConfig.js文件;<br>在apiConfig.js 封装基础的get, post 和 put, upload等请求方法,设置请求体,带上token和异常处理等;<br>在api中引入apiConfig.js封装好的请求方法,根据页面数据请求的urls, 设置对应的方法并导出;<br>在具体的页面中导入。</p><p>小程序只可以跟指定的域名与进行网络通信。包括普通 HTTPS 请求(wx.request)、上传文件(wx.uploadFile)、下载文件(wx.downloadFile) 和 WebSocket 通信(wx.connectSocket)</p><ul><li>域名只支持 https</li></ul><h3 id="视图wxss"><a href="#视图wxss" class="headerlink" title="视图wxss"></a>视图wxss</h3><p>和css一样,都是用来描述页面的样子;<br>WXSS 具有 CSS 大部分的特性,也做了一些扩充和修改;<br>WXSS新增了尺寸单位,WXSS 在底层支持新的尺寸单位 rpx;<br>WXSS 仅支持部分 CSS 选择器;<br>WXSS 提供全局样式与局部样式。</p><h3 id="节点信息获取"><a href="#节点信息获取" class="headerlink" title="节点信息获取"></a>节点信息获取</h3><p>节点信息查询 API 可以用于获取节点属性、样式、在界面上的位置等信息。<br>最常见的用法是使用这个接口来查询某个节点的当前位置,以及界面的滚动位置。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> query = wx.createSelectorQuery()</span><br><span class="line">query.select(<span class="string">'#the-id'</span>).boundingClientRect(<span class="function"><span class="keyword">function</span> (<span class="params">res</span>) </span>{</span><br><span class="line"> res.top <span class="comment">// #the-id 节点的上边界坐标(相对于显示区域)</span></span><br><span class="line">})</span><br><span class="line">query.selectViewport().scrollOffset(<span class="function"><span class="keyword">function</span> (<span class="params">res</span>) </span>{</span><br><span class="line"> res.scrollTop <span class="comment">// 显示区域的竖直滚动位置</span></span><br><span class="line">})</span><br><span class="line">query.exec()</span><br></pre></td></tr></table></figure><h3 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h3><p>小程序本质就是一个单页面应用,所有的页面渲染和事件处理,都在一个页面内进行,但又可以通过微信客户端调用原生的各种接口;<br>它的架构,是数据驱动的架构模式,它的UI和数据是分离的,所有的页面更新,都需要通过对数据的更改来实现;<br>它从技术讲和现有的前端开发差不多,采用JavaScript、WXML、WXSS三种技术进行开发;<br>功能可分为webview和appService两个部分;<br>webview用来展现UI,appService有来处理业务逻辑、数据及接口调用;<br>两个部分在两个进程中运行,通过系统层JSBridge实现通信,实现UI的渲染、事件的处理等。</p><p>架构:<br>微信小程序的框架包含两部分View视图层、App Service逻辑层,View层用来渲染页面结构,AppService层用来逻辑处理、数据请求、接口调用,它们在两个进程(两个Webview)里运行。<br>小程序启动时会从CDN下载小程序的完整包。</p><p>实现:<br>UI视图和逻辑处理是用多个webview实现的,逻辑处理的JS代码全部加载到一个Webview里面,称之为AppService,整个小程序只有一个,并且整个生命周期常驻内存,而所有的视图(wxml和wxss)都是单独的Webview来承载,称之为AppView。所以一个小程序打开至少就会有2个webview进程,正式因为每个视图都是一个独立的webview进程。</p><p>AppService:<br>可以理解AppService即一个简单的页面,主要功能是负责逻辑处理部分的执行,底层提供一个WAService.js的文件来提供各种api接口,主要是以下几个部分:<br>消息通信封装为WeixinJSBridge(开发环境为window.postMessage, IOS下为WKWebview的window.webkit.messageHandlers.invokeHandler.postMessage,android下用WeixinJSCore.invokeHandler)<br>1、日志组件Reporter封装<br>2、wx对象下面的api方法<br>3、全局的App,Page,getApp,getCurrentPages等全局方法<br>4、还有就是对AMD模块规范的实现<br>然后整个页面就是加载一堆JS文件,包括小程序配置config,上面的WAService.js(调试模式下有asdebug.js),剩下就是我们自己写的全部的js文件,一次性都加载。</p><p>上线后是应用部分会打包为2个文件,名称app-config.json和app-service.js,然后微信会打开webview去加载。线上部分应该是微信自身提供了相应的模板文件</p><p>AppView:<br>可以理解为h5的页面,提供UI渲染,底层提供一个WAWebview.js来提供底层的功能:<br>消息通信封装为WeixinJSBridge(开发环境为window.postMessage, IOS下为WKWebview的window.webkit.messageHandlers.invokeHandler.postMessage,android下用WeixinJSCore.invokeHandler)<br>2、日志组件Reporter封装<br>3、wx对象下的api,这里的api跟WAService里的还不太一样,有几个跟那边功能差不多,但是大部分都是处理UI显示相关的方法<br>4、小程序组件实现和注册<br>5、VirtualDOM,Diff和Render UI实现<br>6、页面事件触发</p><p>通信:<br>使用消息publish和subscribe机制实现两个Webview之间的通信,实现方式就是统一封装一个WeixinJSBridge对象,而不同的环境封装的接口不一样。</p><h3 id="web-view页面分享"><a href="#web-view页面分享" class="headerlink" title="web-view页面分享"></a>web-view页面分享</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><web-view src="https://mp.weixin.qq.com/" bindmessage"bindGetMsg"></web-view></span><br><span class="line"></span><br><span class="line">//向小程序发送消息,会在特定时机(小程序后退、组件销毁、分享)触发组件的message事件</span><br><span class="line">wx.miniProgram.postMessage({</span><br><span class="line"> data:'foo'</span><br><span class="line">})</span><br><span class="line">wx.miniProgram.postMessage({ </span><br><span class="line"> data: {foo: 'bar'} </span><br><span class="line">})</span><br><span class="line">bindGetMsg(e) {</span><br><span class="line"> // e.detail = { data },data是多次 postMessage 的参数组成的数组</span><br><span class="line"> // e.detail.data[0]</span><br><span class="line"> let options = e.detail.data[0]</span><br><span class="line"> this.setData({</span><br><span class="line"> w_title: options.goods_name,</span><br><span class="line"> img: util.httpUrl + options.img,</span><br><span class="line"> share_url: '/pages/detailed/detailed?team_id=' + options.team_id + '&goods_id=' + options.goods_id</span><br><span class="line"> })</span><br><span class="line">}</span><br><span class="line">//分享的时候获取这些data</span><br><span class="line">onShareAppMessage: function () {</span><br><span class="line"> return {</span><br><span class="line"> title: this.data.w_title,</span><br><span class="line"> imageUrl: this.data.img,</span><br><span class="line"> path: this.data.share_url</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>]]></content>
<tags>
<tag> wx </tag>
</tags>
</entry>
<entry>
<title>无重复字符的最长子串问题</title>
<link href="/2018/09/12/WithoutRepeatingSubstr/"/>
<content type="html"><![CDATA[<p>##问题:</p><blockquote><p>给定一个字符串,找出不含有重复字符的最长子串的长度。<br>示例 1:<br>输入: “abcabcbb”<br>输出: 3<br>解释: 无重复字符的最长子串是 “abc”,其长度为 3。<br>示例 2:<br>输入: “bbbbb”<br>输出: 1<br>解释: 无重复字符的最长子串是 “b”,其长度为 1。<br>示例 3:<br>输入: “pwwkew”<br>输出: 3<br>解释: 无重复字符的最长子串是 “wke”,其长度为 3。<br> 请注意,答案必须是一个子串,”pwke” 是一个子序列 而不是子串。</p></blockquote><p>##思考:<br>采用动态的划窗来查找无重复子串,用noRepeatHead指示当前划窗的起始位置,noRepeatEnd指示当前划窗的下一个需要验证的元素,当前不重复的字符组成的划窗子串为initialString。<br>初始时:<br><img src="https://upload-images.jianshu.io/upload_images/11352552-b73a59e542807a13.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt></p><p>查找noRepeatEnd对应的元素是否在initialString中存在,若不存在,划窗自动加上这个元素,然后变成下面的状态:<br><img src="https://upload-images.jianshu.io/upload_images/11352552-a3c78ae4c4114d0d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt></p><p>若元素已经存在,直接截取从重复元素到当前元素的字符串,保持整个字符串中字符不重复。<br>(因为之后的字符串可能更长,比如‘abcbedg’, 原先的initialString为abc, 第四个字符‘b’在initialString中,直接截取‘cb’,再往后判断,因为往后可能有子串比目前的最大长度3还要大,例如‘cbedg’的长度5)<br>因为截取后长度肯定不大于之前noRepeatHead和noRepeatEnd之间的长度,所以直接不判断temp(当前initialString的长度)和返回值(nowMaxLength)的大小<br><img src="https://upload-images.jianshu.io/upload_images/11352552-2a3aed48d590a4a3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt></p><p>直到noRepeatHead到达字符串末尾结束(下图 noRepeatEnd == s.length 了):<br><img src="https://upload-images.jianshu.io/upload_images/11352552-74a8a3be69b40f14.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt></p><p>或者从noRepeatHead到字符串末尾的长度已经不能超过当前的最大长度(nowMaxLength)了,自然就不关心是否要继续判断:<br><img src="https://upload-images.jianshu.io/upload_images/11352552-d57aafdc0890d0cb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="noRepeatHead = 5, s.length = 8, nowMaxLength = 3, 所以最后三个就不用判断啦"></p><p>##代码:<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* @param {string} s</span></span><br><span class="line"><span class="comment">* @return {number}</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">var</span> lengthOfLongestSubstring = <span class="function"><span class="keyword">function</span>(<span class="params">s</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> result = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">if</span>(s.length <= <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">return</span> s.length;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">var</span> nowMaxLength = <span class="number">1</span>, noRepeatHead = <span class="number">0</span>, noRepeatEnd = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">var</span> initialString = s[<span class="number">0</span>]; <span class="keyword">var</span> temp = <span class="number">1</span>, index = <span class="number">-1</span>;</span><br><span class="line"> <span class="keyword">do</span> {</span><br><span class="line"> index = initialString.indexOf(s[noRepeatEnd]);</span><br><span class="line"> <span class="keyword">if</span>(index == <span class="number">-1</span>) {</span><br><span class="line"> temp++;</span><br><span class="line"> <span class="keyword">if</span>(temp>nowMaxLength){</span><br><span class="line"> nowMaxLength = temp;</span><br><span class="line"> }</span><br><span class="line"> initialString += s[noRepeatEnd];</span><br><span class="line"> noRepeatEnd ++;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> noRepeatHead = noRepeatHead+ index + <span class="number">1</span>;<span class="comment">//重复的话就把头移到重复元素的下一个元素上;</span></span><br><span class="line"> noRepeatEnd ++;</span><br><span class="line"> initialString = s.substring(noRepeatHead, noRepeatEnd);</span><br><span class="line"> temp = initialString.length;</span><br><span class="line"> }</span><br><span class="line"> }<span class="keyword">while</span>(noRepeatHead < (s.length - nowMaxLength) && (noRepeatEnd < s.length));</span><br><span class="line"> <span class="keyword">return</span> nowMaxLength;</span><br><span class="line">};</span><br></pre></td></tr></table></figure></p>]]></content>
<tags>
<tag> 算法 </tag>
</tags>
</entry>
<entry>
<title>来来来,走进双向绑定的大门</title>
<link href="/2018/07/22/doubleBind/"/>
<content type="html"><![CDATA[<p>之前了解过一点点angularjs的双向绑定,采用的是脏检查。而我们的vue采用的是数据挟持的方式。<br>这些双向绑定的方式究竟有什么不同呢?</p><a id="more"></a><p>双向绑定就是说:就是在数据和UI之间建立双向的通信通道,当用户通过Function改变了数据,那么这个改变也会立即反应到UI上;或者说用户通过UI的操作,那么这些操作也会随之引起对应的数据变动。</p><h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>双向绑定一般有三种方法:<br>Knockout / Backbone — 发布-订阅模式<br>Angular — ‘脏检查’<br>Vue — ‘Object.defineProperty’<br>Vue新 — es6 proxy</p><h2 id="vue实现方式"><a href="#vue实现方式" class="headerlink" title="vue实现方式"></a>vue实现方式</h2><p>Object.defineProperty 仅仅是实现了对数据的监控,后续实现对UI的重新渲染并不是它做的,所以这里还涉及到 发布-订阅模式。<br>过程是,当监控的数据对象被更改后,这个变更会被广播给所有订阅该数据的watcher,然后由该 watcher实现对页面的重新渲染。</p><ol><li>首先,Vue的Compile模块会对Vue的 template 代码进行编译解析并生成一系列的watcher,也可以称之为“更新函数”,它负责把变更后的相关数据重新渲染到指定的地方。</li></ol><p><code><input v-model="message"></code> Compile会解析出 v-model 这个指令并且生成 watcher 并连接数据中的 message 和当前这个Dom对象,一旦收到这个message被变更的通知,watcher就会根据变更对这个Dom进行重新渲染。</p><ol start="2"><li><p>observer模块通过object.defineProperty对数据进行监控。</p></li><li><p>当然一个页面或者一个项目中肯定有很多watcher,因此Vue使用了Dep这个对象来存储每一个watcher,当数据发生变更,Observer会调用Dep的notify方法以通知所有订阅了该数据的watcher</p></li></ol><img src="/images/db_01.png"><p>js实现简单的双向绑定<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"app"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">id</span>=<span class="string">"txt"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">id</span>=<span class="string">"show"</span>></span><span class="tag"></<span class="name">p</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span>></span><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> obj = {}</span></span><br><span class="line"><span class="javascript"> <span class="built_in">Object</span>.defineProperty(obj, <span class="string">'txt'</span>, {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">get</span>: function () {</span></span><br><span class="line"><span class="javascript"> <span class="keyword">return</span> obj</span></span><br><span class="line"><span class="undefined"> },</span></span><br><span class="line"><span class="javascript"> <span class="keyword">set</span>: function (newValue) {</span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.getElementById(<span class="string">'txt'</span>).value = newValue</span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.getElementById(<span class="string">'show'</span>).innerHTML = newValue</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> })</span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.addEventListener(<span class="string">'keyup'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>{</span></span><br><span class="line"><span class="undefined"> obj.txt = e.target.value</span></span><br><span class="line"><span class="undefined"> })</span></span><br><span class="line"><span class="undefined"></span><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure></p><p>下面进行实现完整过程:<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE html></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">"X-UA-Compatible"</span> <span class="attr">content</span>=<span class="string">"ie=edge"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>Object.defineProperty实现双向绑定<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h1</span> <span class="attr">id</span>=<span class="string">'h1'</span>></span><span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">id</span>=<span class="string">"inp"</span> <span class="attr">onkeyup</span>=<span class="string">"inputChange(event)"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"button"</span> <span class="attr">value</span>=<span class="string">"加"</span> <span class="attr">onclick</span>=<span class="string">"btnAdd()"</span> /></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span> <span class="attr">src</span>=<span class="string">"index.js"</span>></span><span class="undefined"></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//实现这个index.js</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//数据源</span></span><br><span class="line"><span class="keyword">let</span> vm = {</span><br><span class="line"> value: <span class="number">0</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//然后定义一个Dep,用于存储watcher</span></span><br><span class="line"><span class="keyword">let</span> Dep = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.list = [];</span><br><span class="line"> <span class="keyword">this</span>.add = <span class="function"><span class="keyword">function</span>(<span class="params">watcher</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.list.push(watcher);</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">this</span>.notify = <span class="function"><span class="keyword">function</span>(<span class="params">newValue</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.list.forEach(<span class="function"><span class="keyword">function</span> (<span class="params">fn</span>) </span>{</span><br><span class="line"> fn(newValue)</span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 模拟compile,生成watcher</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">renderIput</span>(<span class="params">newValue</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> el = <span class="built_in">document</span>.getElementById(<span class="string">'inp'</span>);</span><br><span class="line"> <span class="keyword">if</span> (el) {</span><br><span class="line"> el.value = newValue</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">renderTitle</span>(<span class="params">newValue</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> el = <span class="built_in">document</span>.getElementById(<span class="string">'h1'</span>);</span><br><span class="line"> <span class="keyword">if</span> (el) {</span><br><span class="line"> el.innerHTML = newValue</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//将解析出来的watcher存入Dep中待用</span></span><br><span class="line"><span class="keyword">let</span> dep = <span class="keyword">new</span> Dep();</span><br><span class="line">dep.add(renderInput);</span><br><span class="line">dep.add(renderTitle)</span><br><span class="line"></span><br><span class="line"><span class="comment">//使用Object.defineProperty 定义一个Observer</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">observer</span>(<span class="params">vm, key, value</span>) </span>{</span><br><span class="line"> <span class="built_in">Object</span>.defineProperty(vm, key, {</span><br><span class="line"> enumerable: <span class="literal">true</span>,</span><br><span class="line"> configurable: <span class="literal">true</span>,</span><br><span class="line"> <span class="keyword">get</span>: function () {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'Get'</span>);</span><br><span class="line"> <span class="keyword">return</span> value</span><br><span class="line"> },</span><br><span class="line"> <span class="keyword">set</span>: function (newValue) {</span><br><span class="line"> <span class="keyword">if</span> (value !== newValue) {</span><br><span class="line"> value = newValue</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'Update'</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment">//将变动通知给相关的订阅者</span></span><br><span class="line"> dep.notify(newValue)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//数据初始化方法</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">initMVVM</span>(<span class="params">vm</span>) </span>{</span><br><span class="line"> <span class="built_in">Object</span>.keys(vm).forEach(<span class="function"><span class="keyword">function</span> (<span class="params">key</span>) </span>{</span><br><span class="line"> observer(vm, key, vm[key])</span><br><span class="line"> })</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化数据源</span></span><br><span class="line">initMVVM(vm)</span><br><span class="line"></span><br><span class="line"><span class="comment">//初始化页面,将数据源渲染到UI</span></span><br><span class="line">dep.notify(vm.value);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//实现html调用的事件方法</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">inputChange</span>(<span class="params">ev</span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> value = <span class="built_in">Number</span>.parseInt(ev.target.value);</span><br><span class="line"> vm.value = (<span class="built_in">Number</span>.isNaN(value)) ? <span class="number">0</span> : value;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">btnAdd</span>(<span class="params"></span>) </span>{</span><br><span class="line"> vm.value = vm.value + <span class="number">1</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="object-defineProperty"><a href="#object-defineProperty" class="headerlink" title="object.defineProperty"></a>object.defineProperty</h3><p>当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。</p><ul><li>Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性</li><li>Object.defineProperty(obj, prop, desc), prop属性名,desc 属性描述符。</li></ul><p>javacript 有三种类型的属性:</p><ul><li>命名数据属性:拥有一个确定的值的属性。这也是最常见的属性</li><li>命名访问器属性:通过getter和setter进行读取和赋值的属性</li><li>内部属性:由JavaScript引擎内部使用的属性,不能通过JavaScript代码直接访问到,不过可以通过一些方法间接的读取和设置。比如,每个对象都有一个内部属性[[Prototype]],你不能直接访问这个属性,但可以通过Object.getPrototypeOf()方法间接的读取到它的值。虽然内部属性通常用一个双吕括号包围的名称来表示,但实际上这并不是它们的名字,它们是一种抽象操作,是不可0见的,根本没有上面两种属性有的那种字符串类型的属性</li></ul><p>描述符有:<br><img src="https://upload-images.jianshu.io/upload_images/5016475-9cd41a36735b667d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1000/format/webp" alt></p><p>数据描述符:value, writable<br>存取描述符:get(一个给属性提供getter的方法,如果没有getter则为undefined。该方法返回值被用作属性值。默认为undefined。)<br> set(一个给属性提供setter的方法,如果没有setter则为undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认值为undefined。)<br>都有这两个描述符:configrable 描述属性是否配置,以及可否删除;enumerable 描述属性是否会出现在for in 或者 Object.keys()的遍历中</p><p>(当configrable为true,而writable为false, 可以通过配置方式修改属性值)<br><img src="https://upload-images.jianshu.io/upload_images/5016475-6cfce802f175016b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/942/format/webp" alt></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">let</span> Person = {}</span><br><span class="line"><span class="keyword">let</span> temp = <span class="literal">null</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">Object</span>.defineProperty(Person, <span class="string">'name'</span>, {</span><br><span class="line"> <span class="keyword">get</span>: function () {</span><br><span class="line"> <span class="keyword">return</span> temp</span><br><span class="line"> },</span><br><span class="line"> <span class="keyword">set</span>: function (val) {</span><br><span class="line"> temp = val</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure><h3 id="es6-proxy"><a href="#es6-proxy" class="headerlink" title="es6 proxy"></a>es6 proxy</h3><p>3.0 将带来一个基于 Proxy 的 observer 实现,它可以提供覆盖语言 (JavaScript——译注) 全范围的响应式能力,消除了当前 Vue 2 系列中基于 Object.defineProperty 所存在的一些局限,如:</p><p>对属性的添加、删除动作的监测<br>对数组基于下标的修改、对于 .length 修改的监测<br>对 Map、Set、WeakMap 和 WeakSet 的支持</p><p>还有以下特性:<a href="https://juejin.im/post/5bb719b9f265da0ab915dbdd" target="_blank" rel="noopener">https://juejin.im/post/5bb719b9f265da0ab915dbdd</a></p><ol><li>公开的用于创建 observable (即响应式对象——译注) 的 API。就像new Vue({data: {…}})。</li><li>默认是惰性检测。2.x版本启动时必然被监测。新版本只有应用的初始可见部分所用的数据会被检测。</li><li>更精准的变动通知:在 2.x 系列中,通过 Vue.set 强制添加一个新的属性,将导致所有依赖于这个对象的 watch 函数都会被执行一次;而在 3.x 中,只有依赖于这个具体属性的 watch 函数会被通知到。</li><li>不可变监测对象(Immutable observable),可以创建一个对象的“不可变”版本</li><li>更良好的可调试能力。</li></ol><p>proxy与设计模式<br>Proxy实现前端中3种代理模式的使用场景,分别是:缓存代理、验证代理、实现私有属性。</p><h2 id="脏检查"><a href="#脏检查" class="headerlink" title="脏检查"></a>脏检查</h2><p>以典型的mvvm框架angularjs为代表,angular通过检查脏数据来进行UI层的操作更新。<br>关于angular的脏检测,有几点需要了解些: </p><ul><li>脏检测机制并不是使用定时检测。 只有当UI事件、ajax请求或者timeout延迟事件,才会触发脏检查。</li><li>脏检测的时机是在数据发生变化时进行。 </li><li>angular对常用的dom事件,xhr事件等做了封装,在里面触发进入angular的digest流程。 </li><li>在digest流程里面,会从rootscope开始遍历,检查所有的watcher。</li></ul><p>Angular每一个绑定到UI的数据,就会有一个$watch对象。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">watch = {</span><br><span class="line"> name:<span class="string">''</span>, <span class="comment">//当前的watch对象观测的数据名</span></span><br><span class="line"> getNewValue:<span class="function"><span class="keyword">function</span>(<span class="params">$scope</span>)</span>{ <span class="comment">//得到新值</span></span><br><span class="line"> ...</span><br><span class="line"> <span class="keyword">return</span> newValue;</span><br><span class="line"> },</span><br><span class="line"> listener:<span class="function"><span class="keyword">function</span>(<span class="params">newValue,oldValue</span>)</span>{ <span class="comment">//当数据发生改变时需要执行的操作</span></span><br><span class="line"> ...</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">getNewValue() 可以得到当前$scope上的最新值,listener函数得到新值和旧值并进行一些操作。</span><br></pre></td></tr></table></figure></p><p>一次脏检查就是调用一次$apply()或者$digest(),将数据中最新的值呈现在界面上。</p>]]></content>
<tags>
<tag> js </tag>
<tag> vue </tag>
</tags>
</entry>
<entry>
<title>vue的实用指南</title>
<link href="/2018/07/08/vue/"/>
<content type="html"><![CDATA[<p>本章将介绍vue的常用语法和常见问题,请多多指教~<br><a id="more"></a></p><h1 id="基本实用"><a href="#基本实用" class="headerlink" title="基本实用"></a>基本实用</h1><h2 id="常用表达"><a href="#常用表达" class="headerlink" title="常用表达"></a>常用表达</h2><table><thead><tr><th>表达方式</th><th style="text-align:center">作用</th><th>备注</th></tr></thead><tbody><tr><td>v-bind:title = ‘message’</td><td style="text-align:center">第二种绑定方式,可直接作用在html特性上。 将这个元素节点的 title 特性和 Vue 实例的 message 属性保持一致</td><td>简写 v-bind:href == :href</td></tr><tr><td>v-if = ‘seen’ v-else</td><td style="text-align:center">控制元素是否显示</td><td>Vue 也提供一个强大的过渡效果系统,可以在 Vue 插入/更新/移除元素时自动应用过渡效果</td></tr><tr><td>v-for = ‘todo in todos’</td><td style="text-align:center">循环数组进行显示</td><td>在控制台里,输入 app4.todos.push({ text: ‘新项目’ })可以往数组新增数据</td></tr><tr><td>v-on:click=’event’</td><td style="text-align:center">添加事件监听器</td><td>methods: { event: function () { this.message = this.message.split(‘’).reverse().join(‘’) } } 简写 v-on:click == @click</td></tr><tr><td>v-modal=’message’</td><td style="text-align:center">实现表单输入和应用状态之间的双向绑定</td><td><input v-model=”message”></td></tr><tr><td>v-once</td><td style="text-align:center">执行一次性插值,当数据改变时,插值处的内容不会更新。</td><td>span v-once >这个将不会改变: </span></td></tr><tr><td>v-html</td><td style="text-align:center">输出真正的 HTML</td><td><span v-html=”rawHtml”></span></td></tr></tbody></table><h2 id="组件化"><a href="#组件化" class="headerlink" title="组件化"></a>组件化</h2><p>注册组件:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">// 定义名为 todo-item 的新组件</span><br><span class="line">Vue.component('todo-item', {</span><br><span class="line"> props:['todo']</span><br><span class="line"> template: '<li>{{ todo.text }}</li>',</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">//调用组件</span><br><span class="line"><ol id='app'></span><br><span class="line"> <!-- 创建一个 todo-item 组件的实例 --></span><br><span class="line"> <todo-item v-for='item in grocyList' :todo='item'></span><br><span class="line"> </todo-item></span><br><span class="line"></ol></span><br><span class="line"></span><br><span class="line">var data = {</span><br><span class="line"> groceryList: [</span><br><span class="line"> { id: 0, text: '蔬菜' },</span><br><span class="line"> { id: 1, text: '奶酪' },</span><br><span class="line"> { id: 2, text: '随便其它什么人吃的东西' }</span><br><span class="line"> ]</span><br><span class="line"> };</span><br><span class="line">var app = new Vue({</span><br><span class="line"> el: '#app',</span><br><span class="line"> data: data</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">app.$data = data //true</span><br><span class="line"></span><br><span class="line">// $watch 是一个实例方法</span><br><span class="line">app.$watch('a', function (newValue, oldValue) {</span><br><span class="line">// 这个回调将在 `app.a` 改变后调用</span><br><span class="line">})</span><br></pre></td></tr></table></figure></p><p>props向子组件传递值,data是组件内部的值,并且需要设置初始值。<br>生命周期钩子有: created、mounted、updated 和 destroyed,<br>不要在选项属性或回调上使用箭头函数,比如 created: () => console.log(this.a) 或 vm.$watch(‘a’, newValue => this.myMethod())。因为箭头函数是和父级上下文绑定在一起的,this 不会是如你所预期的 Vue 实例,经常导致 Uncaught TypeError: Cannot read property of undefined 或 Uncaught TypeError: this.myMethod is not a function 之类的错误。</p><p>图示:<br><img src="https://cn.vuejs.org/images/lifecycle.png" alt></p><h2 id="计算属性和侦听器"><a href="#计算属性和侦听器" class="headerlink" title="计算属性和侦听器"></a>计算属性和侦听器</h2><p>计算属性:用来规定复杂逻辑,防止在插值中逻辑太多。<br>调用方法:也可以在表达式中调用方法来达到相同效果。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><div id="example"></span><br><span class="line"> <p>Original message: "{{ message }}"</p></span><br><span class="line"> <p>Computed reversed message: "{{ reversedMessage }}"</p></span><br><span class="line"> <p>Reversed message: "{{ reversedMessage() }}"</p></span><br><span class="line"></div></span><br><span class="line"></span><br><span class="line">var vm = new Vue({</span><br><span class="line"> el: '#example',</span><br><span class="line"> data: {</span><br><span class="line"> message: 'Hello'</span><br><span class="line"> },</span><br><span class="line"> computed: {</span><br><span class="line"> // 计算属性的 getter</span><br><span class="line"> reversedMessage: function () {</span><br><span class="line"> // `this` 指向 vm 实例</span><br><span class="line"> return this.message.split('').reverse().join('')</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> // 在组件中</span><br><span class="line"> methods: {</span><br><span class="line"> reversedMessage: function () {</span><br><span class="line"> return this.message.split('').reverse().join('')</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure></p><p>message属性更新,依赖的计算属性也更新了。 Vue 知道 vm.reversedMessage 依赖于 vm.message,因此当 vm.message 发生改变时,所有依赖 vm.reversedMessage 的绑定也会更新。</p><p>区别:</p><ul><li><p>计算属性是基于它们的依赖进行缓存的。只在相关依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。</p></li><li><p>每当触发重新渲染时,调用方法将总会再次执行函数。</p></li></ul><p>侦听器:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">watch: {</span><br><span class="line"> // 如果 `question` 发生改变,这个函数就会运行</span><br><span class="line"> question: function (newQuestion, oldQuestion) {</span><br><span class="line"> this.answer = 'Waiting for you to stop typing...'</span><br><span class="line"> this.debouncedGetAnswer()</span><br><span class="line"> }</span><br><span class="line">},</span><br></pre></td></tr></table></figure></p><h2 id="数组更新检测"><a href="#数组更新检测" class="headerlink" title="数组更新检测"></a>数组更新检测</h2><p>由于 JavaScript 的限制,Vue 不能检测以下变动的数组:</p><ol><li>当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue</li><li>当你修改数组的长度时,例如:vm.items.length = newLength</li></ol><p>【解决方法】<br> 第一类问题,不仅更改值,也能触发状态更新(两种方法):<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Vue.set</span></span><br><span class="line">Vue.set(vm.items, indexOfItem, newValue)</span><br><span class="line">或实例方法</span><br><span class="line">vm.$<span class="keyword">set</span>(vm.items, indexOfItem, newValue)</span><br><span class="line"></span><br><span class="line">或者</span><br><span class="line">// Array.prototype.splice</span><br><span class="line">vm.items.splice(indexOfItem, 1, newValue)</span><br></pre></td></tr></table></figure></p><p> 第二类:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vm.items.splice(newLength)</span><br></pre></td></tr></table></figure></p><h2 id="对象更新检测"><a href="#对象更新检测" class="headerlink" title="对象更新检测"></a>对象更新检测</h2><p>由于js的限制,不能检测对象属性的添加或删除。只有实例创建时data中的属性值才是响应式的。如果已经创建了实例,就不能动态创建根级别的响应式属性。但是,可以使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Vue.set(vm.userProfile, 'age', 27)</span><br><span class="line">vm.$set(vm.userProfile, 'age', 27)</span><br><span class="line"></span><br><span class="line">//赋予多个属性,比如使用 Object.assign() 或 _.extend()。创建一个新的对象,而不是加到原有的对象中</span><br><span class="line">vm.userProfile = Object.assign({}, vm.userProfile, {</span><br><span class="line"> age: 27,</span><br><span class="line"> favoriteColor: 'Vue Green'</span><br><span class="line">})</span><br></pre></td></tr></table></figure><h2 id="事件处理"><a href="#事件处理" class="headerlink" title="事件处理"></a>事件处理</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">warn: <span class="function"><span class="keyword">function</span> (<span class="params">message, event</span>) </span>{</span><br><span class="line"> <span class="comment">// 现在我们可以访问原生事件对象</span></span><br><span class="line"> <span class="keyword">if</span> (event) event.preventDefault()</span><br><span class="line"> alert(message)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>事件修饰符( 修饰符是由点开头的指令后缀来表示的。)</p><p>.stop<br>.prevent<br>.capture<br>.self<br>.once<br>.passive 滚动事件的默认行为 (即滚动行为) 将会立即触发,而不会等待 <code>onScroll</code> 完成,这其中包含 <code>event.preventDefault()</code> 的情况</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"><!-- 阻止单击事件继续传播 --></span></span><br><span class="line"><span class="tag"><<span class="name">a</span> <span class="attr">v-on:click.stop</span>=<span class="string">"doThis"</span>></span><span class="tag"></<span class="name">a</span>></span></span><br><span class="line"></span><br><span class="line"><span class="comment"><!-- 提交事件不再重载页面 --></span></span><br><span class="line"><span class="tag"><<span class="name">form</span> <span class="attr">v-on:submit.prevent</span>=<span class="string">"onSubmit"</span>></span><span class="tag"></<span class="name">form</span>></span></span><br><span class="line"></span><br><span class="line"><span class="comment"><!-- 修饰符可以串联 --></span></span><br><span class="line"><span class="tag"><<span class="name">a</span> <span class="attr">v-on:click.stop.prevent</span>=<span class="string">"doThat"</span>></span><span class="tag"></<span class="name">a</span>></span></span><br><span class="line"></span><br><span class="line"><span class="comment"><!-- 只有修饰符 --></span></span><br><span class="line"><span class="tag"><<span class="name">form</span> <span class="attr">v-on:submit.prevent</span>></span><span class="tag"></<span class="name">form</span>></span></span><br><span class="line"></span><br><span class="line"><span class="comment"><!-- 添加事件监听器时使用事件捕获模式 --></span></span><br><span class="line"><span class="comment"><!-- 即元素自身触发的事件先在此处理,然后才交由内部元素进行处理 --></span></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">v-on:click.capture</span>=<span class="string">"doThis"</span>></span>...<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"><span class="comment"><!-- 只当在 event.target 是当前元素自身时触发处理函数 --></span></span><br><span class="line"><span class="comment"><!-- 即事件不是从内部元素触发的 --></span></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">v-on:click.self</span>=<span class="string">"doThat"</span>></span>...<span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure><p>按键修饰符</p><p>.enter<br>.tab<br>.delete (捕获“删除”和“退格”键)<br>.esc<br>.space<br>.up<br>.down<br>.left (鼠标事件)<br>.right<br>.middle</p><h2 id="输入绑定"><a href="#输入绑定" class="headerlink" title="输入绑定"></a>输入绑定</h2><p>v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。</p><p>.lazy 在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy 修饰符,从而转变为使用 change 事件进行同步:(在“change”时而非“input”时更新)<br>.number 自动将用户的输入值转为数值类型. 这通常很有用,因为即使在 type=”number” 时,HTML 输入元素的值也总会返回字符串。<br>.trim 自动过滤用户输入的首尾空白字符</p><h2 id="组件基础"><a href="#组件基础" class="headerlink" title="组件基础"></a>组件基础</h2><p>组件必须只有一个根元素,组件的data必须为一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝。<br>内联:通过事件向父级组件发送消息($emit方法),用$event访问到值<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">button</span> <span class="attr">v-on:click</span>=<span class="string">"$emit('enlarge-text', 0.1)"</span>></span></span><br><span class="line">Enlarge text</span><br><span class="line"><span class="tag"></<span class="name">button</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">blog-post</span></span></span><br><span class="line"><span class="tag"><span class="attr">...</span></span></span><br><span class="line"><span class="tag"><span class="attr">v-on:enlarge-text</span>=<span class="string">"postFontSize += $event"</span></span></span><br><span class="line"><span class="tag">></span><span class="tag"></<span class="name">blog-post</span>></span></span><br></pre></td></tr></table></figure></p><p>若是方法:直接作为第一个参数传入方法<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><blog-post</span><br><span class="line">...</span><br><span class="line">v-on:enlarge-text="onEnlargeText"</span><br><span class="line">></blog-post></span><br><span class="line"></span><br><span class="line">methods: {</span><br><span class="line"> onEnlargeText: function (enlargeAmount) {</span><br><span class="line"> this.postFontSize += enlargeAmount</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">这个 0.1就直接传到enlargeAmount了</span><br></pre></td></tr></table></figure></p><p>用于组件上时,v-model 等价于<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">custom-input</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-bind:value</span>=<span class="string">"searchText"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">v-on:input</span>=<span class="string">"searchText = $event"</span></span></span><br><span class="line"><span class="tag">></span><span class="tag"></<span class="name">custom-input</span>></span></span><br></pre></td></tr></table></figure></p><p>全局注册:使用 Vue.component 来创建组件。 这些组件是全局注册的。也就是说它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中。全局注册的行为必须在根 Vue 实例 (通过 new Vue) 创建之前发生。<br>局部注册:通过一个普通的 JavaScript 对象来定义组件,然后在 components 选项中定义你想要使用的组件<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">var ComponentA = { /* ... */ }</span><br><span class="line">new Vue({</span><br><span class="line"> el: '#app'</span><br><span class="line"> components: {</span><br><span class="line"> 'component-a': ComponentA,</span><br><span class="line"> 'component-b': ComponentB</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure></p><p>比如什么输入框等相对通用的元素为基础组件,像element-ui里面一样,为了避免重复import,注册为全局组件。比如基础组件的自动化注册:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> Vue <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line"><span class="keyword">import</span> upperFirst <span class="keyword">from</span> <span class="string">'lodash/upperFirst'</span></span><br><span class="line"><span class="keyword">import</span> camelCase <span class="keyword">from</span> <span class="string">'lodash/camelCase'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> requireComponent = <span class="built_in">require</span>.context(</span><br><span class="line"> <span class="comment">// 其组件目录的相对路径</span></span><br><span class="line"> <span class="string">'./components'</span>,</span><br><span class="line"> <span class="comment">// 是否查询其子目录</span></span><br><span class="line"> <span class="literal">false</span>,</span><br><span class="line"> <span class="comment">// 匹配基础组件文件名的正则表达式</span></span><br><span class="line"> /Base[A-Z]\w+\.(vue|js)$/</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">requireComponent.keys().forEach(<span class="function"><span class="params">fileName</span> =></span> {</span><br><span class="line"> <span class="comment">// 获取组件配置</span></span><br><span class="line"> <span class="keyword">const</span> componentConfig = requireComponent(fileName)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 获取组件的 PascalCase 命名</span></span><br><span class="line"> <span class="keyword">const</span> componentName = upperFirst(</span><br><span class="line"> camelCase(</span><br><span class="line"> <span class="comment">// 剥去文件名开头的 `./` 和结尾的扩展名</span></span><br><span class="line"> fileName.replace(<span class="regexp">/^\.\/(.*)\.\w+$/</span>, <span class="string">'$1'</span>)</span><br><span class="line"> )</span><br><span class="line"> )</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 全局注册组件</span></span><br><span class="line"> Vue.component(</span><br><span class="line"> componentName,</span><br><span class="line"> <span class="comment">// 如果这个组件选项是通过 `export default` 导出的,</span></span><br><span class="line"> <span class="comment">// 那么就会优先使用 `.default`,</span></span><br><span class="line"> <span class="comment">// 否则回退到使用模块的根。</span></span><br><span class="line"> componentConfig.default || componentConfig</span><br><span class="line"> )</span><br><span class="line">})</span><br></pre></td></tr></table></figure></p><p>组件传递的属性值一般会替换掉,但是class 和 style 特性会稍微智能一些,即父传递的和子组件本身的值会被合并起来。<br>如果你不希望组件的根元素继承特性,你可以设置在组件的选项中设置 inheritAttrs: false。<br>可以使用$attrs传递组件的属性。</p><h2 id="自定义事件"><a href="#自定义事件" class="headerlink" title="自定义事件"></a>自定义事件</h2><p>注意:跟组件和 prop 不同,事件名不会被用作一个 JavaScript 变量名或属性名,所以就没有理由使用 camelCase 或 PascalCase 了。并且 v-on 事件监听器在 DOM 模板中会被自动转换为全小写 (因为 HTML 是大小写不敏感的),所以 v-on:myEvent 将会变成 v-on:myevent——导致 myEvent 不可能被监听到。因此,推荐始终使用 kebab-case 的事件名(使用全小写,然后用短横线连接)。</p><p>自定义v-model:</p><p>一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value 特性用于不同的目的。model 选项可以用来避免这样的冲突。<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">base-checkbox</span> <span class="attr">v-model</span>=<span class="string">"lovingVue"</span>></span><span class="tag"></<span class="name">base-checkbox</span>></span></span><br></pre></td></tr></table></figure></p><p>model: {<br> prop: ‘checked’, // 代表引用model时的值传入名叫checked的prop中<br> event: ‘change’ // 代表触发change时,lovingVue 值改变<br>}</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">Vue.component('base-checkbox', {</span><br><span class="line"> model: {</span><br><span class="line"> prop: 'checked',</span><br><span class="line"> event: 'change'</span><br><span class="line"> },</span><br><span class="line"> props: {</span><br><span class="line"> checked: Boolean</span><br><span class="line"> },</span><br><span class="line"> template: `</span><br><span class="line"> <input</span><br><span class="line"> type="checkbox"</span><br><span class="line"> v-bind:checked="checked"</span><br><span class="line"> v-on:change="$emit('change', $event.target.checked)"</span><br><span class="line"> ></span><br><span class="line"> `</span><br><span class="line">})</span><br></pre></td></tr></table></figure><h2 id="原生事件"><a href="#原生事件" class="headerlink" title="原生事件"></a>原生事件</h2><p>用 v-on 的 .native 修饰符,在一个组件的根元素上直接监听一个原生事件。<br>不过在你尝试监听一个类似 <code><input></code> 的非常特定的元素时,这并不是个好主意。<br>这时,父级的 .native 监听器将静默失败。它不会产生任何报错,但是 onFocus 处理函数不会如你预期地被调用。<br>为了解决这个问题,Vue 提供了一个 $listeners 属性,它是一个对象,里面包含了作用在这个组件上的所有监听器。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">Vue.component('base-input', {</span><br><span class="line"> inheritAttrs: false,</span><br><span class="line"> props: ['label', 'value'],</span><br><span class="line"> computed: {</span><br><span class="line"> inputListeners: function () {</span><br><span class="line"> var vm = this</span><br><span class="line"> // `Object.assign` 将所有的对象合并为一个新对象</span><br><span class="line"> return Object.assign({},</span><br><span class="line"> // 我们从父级添加所有的监听器</span><br><span class="line"> this.$listeners,</span><br><span class="line"> // 然后我们添加自定义监听器,</span><br><span class="line"> // 或覆写一些监听器的行为</span><br><span class="line"> {</span><br><span class="line"> // 这里确保组件配合 `v-model` 的工作</span><br><span class="line"> input: function (event) {</span><br><span class="line"> vm.$emit('input', event.target.value)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> template: `</span><br><span class="line"> <label></span><br><span class="line"> {{ label }}</span><br><span class="line"> <input</span><br><span class="line"> v-bind="$attrs"</span><br><span class="line"> v-bind:value="value"</span><br><span class="line"> v-on="inputListeners"</span><br><span class="line"> ></span><br><span class="line"> </label></span><br><span class="line"> `</span><br><span class="line">})</span><br></pre></td></tr></table></figure><h2 id="动态组件"><a href="#动态组件" class="headerlink" title="动态组件"></a>动态组件</h2><p>当在这些组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。<br>为了解决这个问题,我们可以用一个 <keep-alive> 元素将其动态组件包裹起来。<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"><!-- 失活的组件将会被缓存!--></span></span><br><span class="line"><span class="tag"><<span class="name">keep-alive</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">component</span> <span class="attr">v-bind:is</span>=<span class="string">"currentTabComponent"</span>></span><span class="tag"></<span class="name">component</span>></span></span><br><span class="line"><span class="tag"></<span class="name">keep-alive</span>></span></span><br></pre></td></tr></table></figure></keep-alive></p><h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><ol><li>对于MVVM的理解?<br>MVVM 是 Model-View-ViewModel 的缩写。</li></ol><ul><li>Model代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。</li><li>View 代表UI 组件,它负责将数据模型转化成UI 展现出来。</li><li>ViewModel 监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步View 和 Model的对象,连接Model和View。<br>在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。<br>ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。</li></ul><ol start="2"><li>vue的生命周期</li></ol><ul><li>beforeCreate(创建前) 在数据观测和初始化事件还未开始</li><li>created(创建后) 完成数据观测,属性和方法的运算,初始化事件,$el属性还没有显示出来</li><li>beforeMount(载入前) 在挂载开始之前被调用,相关的render函数首次被调用。实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上。</li><li>mounted(载入后) 在el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用。实例已完成以下的配置:用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html页面中。此过程中进行ajax交互。</li><li>beforeUpdate(更新前) 在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。</li><li>updated(更新后) 在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。</li><li>beforeDestroy(销毁前) 在实例销毁之前调用。实例仍然完全可用。</li><li>destroyed(销毁后) 在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。</li></ul><ol start="3"><li><p>vue实现双向绑定的原理 Object.defineProperty()<br>采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。</p></li><li><p>Vue组件间的参数传递</p></li></ol><ul><li>父组件与子组件传值<br>父组件传给子组件:子组件通过props方法接受数据;<br>子组件传给父组件:$emit方法传递参数</li><li>非父子组件间的数据传递,兄弟组件传值<br>eventBus,就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。项目比较小时,用这个比较合适。</li></ul><ol start="5"><li><p>路由实现<br>hash模式:在浏览器中符号“#”,#以及#后面的字符称之为hash,用window.location.hash读取;<br>特点:hash虽然在URL中,但不被包括在HTTP请求中;用来指导浏览器动作,对服务端安全无用,hash不会重加载页面。<br>hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 <a href="http://www.xxx.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回" target="_blank" rel="noopener">http://www.xxx.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回</a> 404 错误。<br>history模式:history采用HTML5的新特性;且提供了两个新方法:pushState(),replaceState()可以对浏览器历史记录栈进行修改,以及popState事件的监听到状态变更。<br>history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 <a href="http://www.xxx.com/items/id。后端如果缺少对" target="_blank" rel="noopener">http://www.xxx.com/items/id。后端如果缺少对</a> /items/id 的路由处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还需要后台配置支持……所以呢,你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”</p></li><li><p>vue and react and angular<br>与React的区别<br>相同点:<br>React采用特殊的JSX语法,Vue.js在组件开发中也推崇编写.vue特殊文件格式,对文件内容都有一些约定,两者都需要编译后使用;中心思想相同:一切都是组件,组件实例之间可以嵌套;都提供合理的钩子函数,可以让开发者定制化地去处理需求;都不内置AJAX,Route等功能到核心包,而是以插件的方式加载;在组件开发中都支持mixins的特性。<br>不同点:</p></li></ol><ul><li>React整体是函数式的思想,把组件设计成纯组件,状态和逻辑通过参数传入,所以在react中,是单向数据流,推崇结合immutable来实现数据不可变。而vue的思想是响应式的,也就是基于是数据可变的,通过对每一个属性建立Watcher来监听,当属性变化的时候,响应式的更新对应的虚拟dom。<br>react的性能优化需要手动去做,而vue的性能优化是自动的,但是vue的响应式机制也有问题,就是当state特别多的时候,Watcher也会很多,会导致卡顿,所以大型应用(状态特别多的)一般用react,更加可控。</li><li>react的思路是all in js,通过js来生成html,所以设计了jsx。vue是把html,css,js组合到一起,用各自的处理方式,vue有单文件组件,可以把html、css、js写到一个文件中,html提供了模板引擎来处理。</li><li>react功能一般交给社区,vue是内置。</li></ul><ol start="7"><li><p>vue的路由钩子<br>首页可以控制导航跳转,beforeEach,afterEach等,一般用于页面title的修改。一些需要登录才能调整页面的重定向功能。<br>beforeEach主要有3个参数to,from,next:<br>to:route即将进入的目标路由对象,<br>from:route当前导航正要离开的路由<br>next:function一定要调用该方法resolve这个钩子。执行效果依赖next方法的调用参数。可以控制网页的跳转。<br>比如我写的代码:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><span class="line">router.beforeEach(<span class="function">(<span class="params">to, <span class="keyword">from</span>, next</span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> isLogin = <span class="built_in">window</span>.sessionStorage.getItem(<span class="string">'cweb-access'</span>) || <span class="built_in">window</span>.localStorage.getItem(<span class="string">'cweb-access-lo'</span>);</span><br><span class="line"> <span class="keyword">const</span> params = {</span><br><span class="line"> searchData: {</span><br><span class="line"> regex: <span class="regexp">/achievement\/data-query/g</span>,</span><br><span class="line"> canEnter: isLogin,</span><br><span class="line"> message: <span class="string">'请先登录。'</span>,</span><br><span class="line"> toUrl: <span class="string">'/login'</span></span><br><span class="line"> },</span><br><span class="line"> account: {</span><br><span class="line"> regex: <span class="regexp">/account/g</span>,</span><br><span class="line"> canEnter: store.state.selfInfo && store.state.selfInfo.accountType == <span class="string">'ADMIN'</span>,</span><br><span class="line"> message: <span class="string">'请先登录,或您没有查看系统账号列表的权限。'</span>,</span><br><span class="line"> toUrl: <span class="string">'/'</span></span><br><span class="line"> },</span><br><span class="line"> service: {</span><br><span class="line"> regex: <span class="regexp">/service/g</span>,</span><br><span class="line"> canEnter: isLogin,</span><br><span class="line"> message: <span class="string">'请先登录。'</span>,</span><br><span class="line"> toUrl: <span class="string">'/login'</span></span><br><span class="line"> },</span><br><span class="line"> selfInfo: {</span><br><span class="line"> regex: <span class="regexp">/selfInfo/g</span>,</span><br><span class="line"> canEnter: isLogin,</span><br><span class="line"> message: <span class="string">'请先登录。'</span>,</span><br><span class="line"> toUrl: <span class="string">'/login'</span></span><br><span class="line"> },</span><br><span class="line"> modifyPassword: {</span><br><span class="line"> regex: <span class="regexp">/login\/modifyPassword/g</span>,</span><br><span class="line"> canEnter: isLogin,</span><br><span class="line"> message: <span class="string">'请先登录。'</span>,</span><br><span class="line"> toUrl: <span class="string">'/login'</span></span><br><span class="line"> },</span><br><span class="line"> };</span><br><span class="line"> <span class="comment">//从编辑或者创建页面退出时应该进行提示</span></span><br><span class="line"> <span class="keyword">if</span>( <span class="keyword">from</span>.path && <span class="keyword">from</span>.path.match(<span class="regexp">/\/(add)|(edit)/g</span>) && (!to.path.match(<span class="regexp">/\/(view)/g</span>))) {</span><br><span class="line"> ElementUI.MessageBox.confirm(<span class="string">'返回不能保存已填数据, 是否离开此页面?'</span>, <span class="string">'提示'</span>, {</span><br><span class="line"> type: <span class="string">'info'</span></span><br><span class="line"> }).then(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * 继续的话退出</span></span><br><span class="line"><span class="comment"> * 判断一些模块是否可以访问</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> i <span class="keyword">in</span> params) {</span><br><span class="line"> <span class="keyword">let</span> { regex, canEnter, message, toUrl } = params[i];</span><br><span class="line"> <span class="keyword">if</span>( to.path && to.path.match(regex) && !canEnter) {</span><br><span class="line"> ElementUI.Message(message);</span><br><span class="line"> next({</span><br><span class="line"> path: toUrl</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> next(); </span><br><span class="line"> }).catch(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> next(<span class="literal">false</span>); </span><br><span class="line"> });</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> * 判断一些模块是否可以访问</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> i <span class="keyword">in</span> params) {</span><br><span class="line"> <span class="keyword">let</span> { regex, canEnter, message, toUrl } = params[i];</span><br><span class="line"> <span class="keyword">if</span>( to.path && to.path.match(regex) && !canEnter) {</span><br><span class="line"> ElementUI.Message(message);</span><br><span class="line"> next({</span><br><span class="line"> path: toUrl</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> next();</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure></li><li><p>创建自定义指令<br>局部指令:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> app = <span class="keyword">new</span> Vue({</span><br><span class="line"> el: <span class="string">'#app'</span>,</span><br><span class="line"> data: { </span><br><span class="line"> },</span><br><span class="line"> <span class="comment">// 创建指令(可以多个)</span></span><br><span class="line"> directives: {</span><br><span class="line"> <span class="comment">// 指令名称</span></span><br><span class="line"> dir1: {</span><br><span class="line"> inserted(el) {</span><br><span class="line"> <span class="comment">// 指令中第一个参数是当前使用指令的DOM</span></span><br><span class="line"> <span class="built_in">console</span>.log(el);</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="built_in">arguments</span>);</span><br><span class="line"> <span class="comment">// 对DOM进行操作</span></span><br><span class="line"> el.style.width = <span class="string">'200px'</span>;</span><br><span class="line"> el.style.height = <span class="string">'200px'</span>;</span><br><span class="line"> el.style.background = <span class="string">'#000'</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure></li></ol><p>全局指令:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">Vue.directive(<span class="string">'dir2'</span>, {</span><br><span class="line"> <span class="comment">// 当被绑定的元素插入到 DOM 中时……</span></span><br><span class="line"> inserted(el) {</span><br><span class="line"> <span class="built_in">console</span>.log(el);</span><br><span class="line"> },</span><br><span class="line"> <span class="comment">// bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。</span></span><br><span class="line"> bind(el, binding, vnode, oldVnode) {</span><br><span class="line"> },</span><br><span class="line"> <span class="comment">//所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新</span></span><br><span class="line"> update() {</span><br><span class="line"></span><br><span class="line"> },</span><br><span class="line"> componentUpdated() {</span><br><span class="line"></span><br><span class="line"> },</span><br><span class="line"> unbind() {</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure></p><ol start="9"><li><p>自定义过滤器</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> vm=<span class="keyword">new</span> Vue({</span><br><span class="line"> el:<span class="string">"#app"</span>,</span><br><span class="line"> data:{</span><br><span class="line"> msg:<span class="string">''</span></span><br><span class="line"> },</span><br><span class="line"> filters: {</span><br><span class="line"> capitalize: <span class="function"><span class="keyword">function</span> (<span class="params">value</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (!value) <span class="keyword">return</span> <span class="string">''</span></span><br><span class="line"> value = value.toString()</span><br><span class="line"> <span class="keyword">return</span> value.charAt(<span class="number">0</span>).toUpperCase() + value.slice(<span class="number">1</span>)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">Vue.filter(<span class="string">'capitalize'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">value</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (!value) <span class="keyword">return</span> <span class="string">''</span></span><br><span class="line"> value = value.toString()</span><br><span class="line"> <span class="keyword">return</span> value.charAt(<span class="number">0</span>).toUpperCase() + value.slice(<span class="number">1</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure></li><li><p>keep-alive<br>keep-alive是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。<br>在vue 2.1.0 版本之后,keep-alive新加入了两个属性: include(包含的组件缓存) 与 exclude(排除的组件不缓存,优先级大于include) 。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">keep-alive</span> <span class="attr">include</span>=<span class="string">'include_components'</span> <span class="attr">exclude</span>=<span class="string">'exclude_components'</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">component</span>></span></span><br><span class="line"> <span class="comment"><!-- 该组件是否缓存取决于include和exclude属性 --></span></span><br><span class="line"> <span class="tag"></<span class="name">component</span>></span></span><br><span class="line"><span class="tag"></<span class="name">keep-alive</span>></span></span><br></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag> vue </tag>
</tags>
</entry>
<entry>
<title>webpack的相关概念与使用</title>
<link href="/2018/06/02/webpack/"/>
<content type="html"><![CDATA[<h1 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h1><p>本章介绍了webpack和相关技术的核心概念与使用,以及相关的扩展:懒加载、tree shaking、代码分离、缓存的方法~</p><h2 id="webpack是什么?"><a href="#webpack是什么?" class="headerlink" title="webpack是什么?"></a>webpack是什么?</h2><p>具体的介绍可见官网:<a href="https://www.webpackjs.com/concepts/" target="_blank" rel="noopener">https://www.webpackjs.com/concepts/</a></p><blockquote><p>本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。</p></blockquote><p>把所有依赖打包成一个或多个 bundle 文件,通过代码分割成单元片段并按需加载。</p><h2 id="与-gulp-和-grunt的区别"><a href="#与-gulp-和-grunt的区别" class="headerlink" title="与 gulp 和 grunt的区别"></a>与 gulp 和 grunt的区别</h2><p>三者都是前端构建工具,grunt和gulp在早期比较流行,现在webpack相对来说比较主流,不过一些轻量化的任务还是会用gulp来处理,比如单独打包CSS文件等。</p><p>grunt和gulp是基于任务和流(Task、Stream)的。类似jQuery,找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程。</p><p>webpack是基于入口的。webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能。</p><p>所以总结一下: </p><ul><li><p>从构建思路来说<br>gulp和grunt需要开发者将整个前端构建过程拆分成多个<code>Task</code>,并合理控制所有<code>Task</code>的调用关系<br>webpack需要开发者找到入口,并需要清楚对于不同的资源应该使用什么Loader做何种解析和加工</p></li><li><p>对于知识背景来说<br>gulp更像后端开发者的思路,需要对于整个流程了如指掌 webpack更倾向于前端开发者的思路</p></li></ul><h2 id="其他类似的工具"><a href="#其他类似的工具" class="headerlink" title="其他类似的工具"></a>其他类似的工具</h2><p>同样是基于入口的打包工具还有以下几个主流的:</p><ul><li>webpack</li><li>rollup</li><li>parcel</li></ul><p>webpack适用于大型复杂的前端站点构建;rollup适用于基础库的打包,如vue、react;parcel适用于简单的实验性项目,他可以满足低门槛的快速看到效果</p><h3 id="webpack-与-rollup"><a href="#webpack-与-rollup" class="headerlink" title="webpack 与 rollup"></a>webpack 与 rollup</h3><p>webpack 发起之初主要是为了解决以下两个问题:</p><ul><li>代码拆分(Code Splitting): 可以将应用程序分解成可管理的代码块,可以按需加载,这样用户便可快速与应用交互,而不必等到整个应用程序下载和解析完成才能使用,以此构建复杂的单页应用程序(SPA);</li><li>静态资源(Static Assets): 可以将所有的静态资源,如 js、css、图片、字体等,导入到应用程序中,然后由 webpack 使用 hash 重命名需要的资源文件,而无需为文件 URL 增添 hash 而使用 hack 脚本,并且一个资源还能依赖其他资源。</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/******/</span> (<span class="function"><span class="keyword">function</span>(<span class="params">modules</span>) </span>{ <span class="comment">// webpackBootstrap</span></span><br><span class="line"><span class="comment">/******/</span> <span class="comment">// The module cache</span></span><br><span class="line"><span class="comment">/******/</span> <span class="keyword">var</span> installedModules = {};</span><br><span class="line"><span class="comment">/******/</span></span><br><span class="line"><span class="comment">/******/</span> <span class="comment">// The require function</span></span><br><span class="line"><span class="comment">/******/</span> <span class="function"><span class="keyword">function</span> <span class="title">__webpack_require__</span>(<span class="params">moduleId</span>) </span>{</span><br><span class="line"><span class="comment">/******/</span></span><br><span class="line"><span class="comment">/******/</span> <span class="comment">// Check if module is in cache</span></span><br><span class="line"><span class="comment">/******/</span> <span class="keyword">if</span>(installedModules[moduleId]) {</span><br><span class="line"><span class="comment">/******/</span> <span class="keyword">return</span> installedModules[moduleId].exports;</span><br><span class="line"><span class="comment">/******/</span> }</span><br><span class="line"><span class="comment">/******/</span> <span class="comment">// Create a new module (and put it into the cache)</span></span><br><span class="line"><span class="comment">/******/</span> <span class="keyword">var</span> <span class="built_in">module</span> = installedModules[moduleId] = {</span><br><span class="line"><span class="comment">/******/</span> i: moduleId,</span><br><span class="line"><span class="comment">/******/</span> l: <span class="literal">false</span>,</span><br><span class="line"><span class="comment">/******/</span> exports: {}</span><br><span class="line"><span class="comment">/******/</span> };</span><br><span class="line"><span class="comment">/******/</span></span><br><span class="line"><span class="comment">/******/</span> <span class="comment">// Execute the module function</span></span><br><span class="line"><span class="comment">/******/</span> modules[moduleId].call(<span class="built_in">module</span>.exports, <span class="built_in">module</span>, <span class="built_in">module</span>.exports, __webpack_require__);</span><br><span class="line"><span class="comment">/******/</span></span><br><span class="line"><span class="comment">/******/</span> <span class="comment">// Flag the module as loaded</span></span><br><span class="line"><span class="comment">/******/</span> <span class="built_in">module</span>.l = <span class="literal">true</span>;</span><br><span class="line"><span class="comment">/******/</span></span><br><span class="line"><span class="comment">/******/</span> <span class="comment">// Return the exports of the module</span></span><br><span class="line"><span class="comment">/******/</span> <span class="keyword">return</span> <span class="built_in">module</span>.exports;</span><br><span class="line"><span class="comment">/******/</span> }</span><br><span class="line"><span class="comment">/******/</span></span><br><span class="line"><span class="comment">/******/</span></span><br><span class="line"><span class="comment">/******/</span> <span class="comment">// expose the modules object (__webpack_modules__)</span></span><br><span class="line"><span class="comment">/******/</span> __webpack_require__.m = modules;</span><br><span class="line"><span class="comment">/******/</span></span><br><span class="line"><span class="comment">/******/</span> <span class="comment">// expose the module cache</span></span><br><span class="line"><span class="comment">/******/</span> __webpack_require__.c = installedModules;</span><br><span class="line"><span class="comment">/******/</span></span><br><span class="line"><span class="comment">/******/</span> <span class="comment">// define getter function for harmony exports</span></span><br><span class="line"><span class="comment">/******/</span> __webpack_require__.d = <span class="function"><span class="keyword">function</span>(<span class="params">exports, name, getter</span>) </span>{</span><br><span class="line"><span class="comment">/******/</span> <span class="keyword">if</span>(!__webpack_require__.o(exports, name)) {</span><br><span class="line"><span class="comment">/******/</span> <span class="built_in">Object</span>.defineProperty(exports, name, {</span><br><span class="line"><span class="comment">/******/</span> configurable: <span class="literal">false</span>,</span><br><span class="line"><span class="comment">/******/</span> enumerable: <span class="literal">true</span>,</span><br><span class="line"><span class="comment">/******/</span> <span class="keyword">get</span>: getter</span><br><span class="line">/******/ });</span><br><span class="line">/******/ }</span><br><span class="line">/******/ };</span><br><span class="line">/******/</span><br><span class="line">/******/ // getDefaultExport function for compatibility with non-harmony modules</span><br><span class="line">/******/ __webpack_require__.n = function(module) {</span><br><span class="line"><span class="comment">/******/</span> <span class="keyword">var</span> getter = <span class="built_in">module</span> && <span class="built_in">module</span>.__esModule ?</span><br><span class="line"><span class="comment">/******/</span> <span class="function"><span class="keyword">function</span> <span class="title">getDefault</span>(<span class="params"></span>) </span>{ <span class="keyword">return</span> <span class="built_in">module</span>[<span class="string">'default'</span>]; } :</span><br><span class="line"><span class="comment">/******/</span> <span class="function"><span class="keyword">function</span> <span class="title">getModuleExports</span>(<span class="params"></span>) </span>{ <span class="keyword">return</span> <span class="built_in">module</span>; };</span><br><span class="line"><span class="comment">/******/</span> __webpack_require__.d(getter, <span class="string">'a'</span>, getter);</span><br><span class="line"><span class="comment">/******/</span> <span class="keyword">return</span> getter;</span><br><span class="line"><span class="comment">/******/</span> };</span><br><span class="line"><span class="comment">/******/</span></span><br><span class="line"><span class="comment">/******/</span> <span class="comment">// Object.prototype.hasOwnProperty.call</span></span><br><span class="line"><span class="comment">/******/</span> __webpack_require__.o = <span class="function"><span class="keyword">function</span>(<span class="params">object, property</span>) </span>{ <span class="keyword">return</span> <span class="built_in">Object</span>.prototype.hasOwnProperty.call(object, property); };</span><br><span class="line"><span class="comment">/******/</span></span><br><span class="line"><span class="comment">/******/</span> <span class="comment">// __webpack_public_path__</span></span><br><span class="line"><span class="comment">/******/</span> __webpack_require__.p = <span class="string">""</span>;</span><br><span class="line"><span class="comment">/******/</span></span><br><span class="line"><span class="comment">/******/</span> <span class="comment">// Load entry module and return exports</span></span><br><span class="line"><span class="comment">/******/</span> <span class="keyword">return</span> __webpack_require__(__webpack_require__.s = <span class="number">0</span>);</span><br><span class="line"><span class="comment">/******/</span> })</span><br><span class="line"><span class="comment">/************************************************************************/</span></span><br><span class="line"><span class="comment">/******/</span> ([</span><br><span class="line"><span class="comment">/* 0 */</span></span><br><span class="line"><span class="comment">/***/</span> (<span class="function"><span class="keyword">function</span>(<span class="params">module, __webpack_exports__, __webpack_require__</span>) </span>{</span><br><span class="line"></span><br><span class="line"><span class="meta">"use strict"</span>;</span><br><span class="line"><span class="built_in">Object</span>.defineProperty(__webpack_exports__, <span class="string">"__esModule"</span>, { <span class="attr">value</span>: <span class="literal">true</span> });</span><br><span class="line"></span><br><span class="line"><span class="comment">/* harmony default export */</span> __webpack_exports__[<span class="string">"default"</span>] = (<span class="function"><span class="params">str</span> =></span> str);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/***/</span> })</span><br><span class="line"><span class="comment">/******/</span> ]);</span><br></pre></td></tr></table></figure><p>把 export default str => str; 这段代码用 webpack 打包就会得到上面的结果。</p><p>这在以下的一些情境中就不太高效,需要寻求更好的解决方案:</p><p>需要 js 高效运行。因为 webpack 对子模块定义和运行时的依赖处理(<strong>webpack_require</strong>),不仅导致文件体积增大,还会大幅拉低性能;<br>项目(特别是类库)只有 js,而没有其他的静态资源文件,使用 webpack 就有点大才小用了,因为 webpack bundle 文件的体积略大,运行略慢,可读性略低。</p><p>rollup 相对 webpack 而言,要小巧、干净利落一些,但不具备 webpack 的一些强大的功能,如热更新,代码分割,公共依赖提取等。</p><h2 id="webpack-核心概念"><a href="#webpack-核心概念" class="headerlink" title="webpack 核心概念"></a>webpack 核心概念</h2><p>核心概念有:入口、输出、loader、plugins</p><ol><li>入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。</li><li>output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist。【基本上,整个应用程序结构,都会被编译到你指定的输出路径的文件夹中!!!!!!!】</li><li><p>loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。</p><pre><code>在 webpack 的配置中 loader 有两个目标:test 属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。use 属性,表示进行转换时,应该使用哪个 loader。</code></pre></li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>: {</span><br><span class="line"> rules: [{</span><br><span class="line"> test: <span class="regexp">/\.css$/</span>,</span><br><span class="line"> use: [<span class="string">"style-loader"</span>, <span class="string">'css-loader'</span>]</span><br><span class="line"> }]</span><br><span class="line">},</span><br></pre></td></tr></table></figure><ol start="4"><li><p>loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,【从打包优化和压缩,一直到重新定义环境中的变量】。插件接口功能极其强大,可以用来处理各种各样的任务。<br>想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建它的一个实例。</p></li><li><p>模式:通过选择 development 或 production 之中的一个,来设置 mode 参数,你可以启用相应模式下的 webpack 内置的优化</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> mode: <span class="string">'production'</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure></li></ol><h3 id="入口"><a href="#入口" class="headerlink" title="入口"></a>入口</h3><p>向 entry 属性传入「文件路径(file path)数组」将创建“多个主入口(multi-main entry)”。在你想要多个依赖文件一起注入,并且将它们的依赖导向(graph)到一个“chunk”时,传入数组的方式就很有用。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> config = {</span><br><span class="line"> entry: <span class="string">'./path/to/my/entry/file.js'</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = config;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 等同于</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> config = {</span><br><span class="line"> entry: {</span><br><span class="line"> main: <span class="string">'./path/to/my/entry/file.js'</span></span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure></p><p>或者使用对象语言:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> config = {</span><br><span class="line"> entry: {</span><br><span class="line"> app: <span class="string">'./src/app.js'</span>,</span><br><span class="line"> vendors: <span class="string">'./src/vendors.js'</span></span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>分离了应用程序app和第三方库vendor入口,这告诉我们 webpack 从 app.js 和 vendors.js 开始创建依赖图。这些依赖图是彼此完全分离、互相独立的。vendors选项中的引用可以使用 CommonsChunkPlugin 从整体bundle中提取 vendor bundle,并把引用 vendor 的部分替换为 <strong>webpack_require</strong>() 调用。</p><p>多页面:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> config = {</span><br><span class="line"> entry: {</span><br><span class="line"> pageOne: <span class="string">'./src/pageOne/index.js'</span>,</span><br><span class="line"> pageTwo: <span class="string">'./src/pageTwo/index.js'</span>,</span><br><span class="line"> pageThree: <span class="string">'./src/pageThree/index.js'</span></span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure></p><p>则每个键(key)会是 chunk 的名称,该值描述了 chunk 的入口起点。</p><h3 id="输出"><a href="#输出" class="headerlink" title="输出"></a>输出</h3><p>即使可以存在多个入口起点,但只指定一个输出配置。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 普通输出</span></span><br><span class="line"><span class="keyword">const</span> config = {</span><br><span class="line"> output: {</span><br><span class="line"> filename: <span class="string">'bundle.js'</span>,</span><br><span class="line"> path: <span class="string">'/home/proj/public/assets'</span></span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = config;</span><br><span class="line"></span><br><span class="line"><span class="comment">//多页面输出</span></span><br><span class="line">{</span><br><span class="line"> entry: {</span><br><span class="line"> app: <span class="string">'./src/app.js'</span>,</span><br><span class="line"> search: <span class="string">'./src/search.js'</span></span><br><span class="line"> },</span><br><span class="line"> output: {</span><br><span class="line"> filename: <span class="string">'[name].js'</span>,</span><br><span class="line"> path: __dirname + <span class="string">'/dist'</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//其他选项</span></span><br><span class="line">output: {</span><br><span class="line"> filename: <span class="string">'[name].js'</span>, <span class="comment">//对应于entry里面生成出来的文件名</span></span><br><span class="line"> chunkFilename: <span class="string">'lib/[name].js'</span><span class="comment">//就是未被列在entry中,但有些场景需要被打包出来的文件命名配置。</span></span><br><span class="line"> <span class="comment">// 比如按需加载(异步)模块的时候 require.ensure</span></span><br><span class="line"> <span class="comment">// 不是在ensure方法中引入的模块,此属性不会生效,只能用CommonsChunkPlugin插件来提取</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>filename: 决定了每个输出 bundle 的名称。这些 bundle 将写入到 output.path 选项指定的目录下。</p><h3 id="loaders"><a href="#loaders" class="headerlink" title="loaders"></a>loaders</h3><p>在你的应用程序中,有三种使用 loader 的方式:</p><p>配置(推荐):在 webpack.config.js 文件中指定 loader。<br>内联:在每个 import 语句中显式指定 loader。<br>CLI:在 shell 命令中指定它们。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 内联方式</span></span><br><span class="line"><span class="keyword">import</span> Styles <span class="keyword">from</span> <span class="string">'style-loader!css-loader?modules!./styles.css'</span>;</span><br></pre></td></tr></table></figure><p>使用 ! 将资源中的 loader 分开。分开的每个部分都相对于当前目录解析。</p><p>特性:</p><blockquote><p>loader 支持链式传递。能够对资源使用流水线(pipeline)。一组链式的 loader 将按照相反的顺序执行。loader 链中的第一个 loader 返回值给下一个 loader。在最后一个 loader,返回 webpack 所预期的 JavaScript。<br>loader 可以是同步的,也可以是异步的。<br>loader 运行在 Node.js 中,并且能够执行任何可能的操作。<br>loader 接收查询参数。用于对 loader 传递配置。<br>loader 也能够使用 options 对象进行配置。<br>除了使用 package.json 常见的 main 属性,还可以将普通的 npm 模块导出为 loader,做法是在 package.json 里定义一个 loader 字段。<br>插件(plugin)可以为 loader 带来更多特性。<br>loader 能够产生额外的任意文件。</p></blockquote><h3 id="插件"><a href="#插件" class="headerlink" title="插件"></a>插件</h3><p>插件目的在于解决 loader 无法实现的其他事。<br>在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。<br>webpack 插件是一个具有 apply 属性的 JavaScript 对象。apply 属性会被 webpack compiler 调用,并且 compiler 对象可在整个编译生命周期访问。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//ConsoleLogOnBuildWebpackPlugin.js</span></span><br><span class="line"><span class="keyword">const</span> pluginName = <span class="string">'ConsoleLogOnBuildWebpackPlugin'</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ConsoleLogOnBuildWebpackPlugin</span> </span>{</span><br><span class="line"> apply(compiler) {</span><br><span class="line"> compiler.hooks.run.tap(pluginName, compilation => {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"webpack 构建过程开始!"</span>);</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h2 id="webpack解析规则"><a href="#webpack解析规则" class="headerlink" title="webpack解析规则"></a>webpack解析规则</h2><p>resolver 是一个库(library),用于帮助找到模块的绝对路径。<br>resolver 帮助 webpack 找到 bundle 中需要引入的模块代码,这些代码在包含在每个 require/import 语句中。 当打包模块时,webpack 使用 enhanced-resolve 来解析文件路径。</p><p>使用 enhanced-resolve,webpack 能够解析三种文件路径:</p><ul><li>绝对路径:不用解析了</li><li>相对路径:使用 import 或 require 的资源文件(resource file)所在的目录被认为是上下文目录(context directory)。在 import/require 中给定的相对路径,会添加此上下文路径(context path),以产生模块的绝对路径(absolute path)。</li></ul><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">"../src/file1"</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">"./file2"</span>;</span><br></pre></td></tr></table></figure><ul><li>模块路径:模块将在 resolve.modules 中指定的所有目录内搜索。 你可以替换初始模块路径,此替换路径通过使用 resolve.alias 配置选项来创建一个别名。如果路径指向一个文件:如果路径具有文件扩展名,则被直接将文件打包。否则,将使用 [resolve.extensions] 选项作为文件扩展名来解析,此选项告诉解析器在解析中能够接受哪些扩展名(例如 .js, .jsx)。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">"module"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// webpack.config.js中配置</span></span><br><span class="line"> resolve: {</span><br><span class="line"> modules: [</span><br><span class="line"> <span class="string">"node_modules"</span>,</span><br><span class="line"> path.resolve(__dirname, <span class="string">"src"</span>)</span><br><span class="line"> ],</span><br><span class="line"> extensions: [<span class="string">".js"</span>, <span class="string">".jsx"</span>, <span class="string">".json"</span>, <span class="string">".css"</span>, <span class="string">".scss"</span>],</span><br><span class="line"> alias: {</span><br><span class="line"> xyz$: <span class="string">"./dir/file.js"</span></span><br><span class="line"> }<span class="comment">//创建 import 或 require 的别名,来确保模块引入变得更简单。</span></span><br><span class="line"> },</span><br></pre></td></tr></table></figure></li></ul><h2 id="生产环境构建和manifest"><a href="#生产环境构建和manifest" class="headerlink" title="生产环境构建和manifest"></a>生产环境构建和manifest</h2><p>source map:用来调试代码,生产环境下一般使用:<br>(none)(省略 devtool 选项) - 不生成 source map。这是一个不错的选择。<br>source-map - 整个 source map 作为一个单独的文件生成。它为 bundle 添加了一个引用注释,以便开发工具知道在哪里可以找到它。</p><p>runtime,以及伴随的 manifest 数据,主要是指:在浏览器运行时,webpack 用来连接模块化的应用程序的所有代码。<br>当编译器(compiler)开始执行、解析和映射应用程序时,它会保留所有模块的详细要点。这个数据集合称为 “Manifest”,当完成打包并发送到浏览器时,会在运行时通过 Manifest 来解析和加载模块。通过使用 manifest 中的数据,runtime 将能够查询模块标识符,检索出背后对应的模块。</p><h2 id="模块热替换"><a href="#模块热替换" class="headerlink" title="模块热替换"></a>模块热替换</h2><p>hmr————hot module replacement:会在应用程序运行过程中替换、添加或删除模块,而无需进行完全刷新。</p><hr><p>代码变化后自动编译代码,有三种实现方式:<br>webpack’s Watch Mode / webpack-dev-derver / webpack-dev-middleware,后两者都需要在配置里面加入devServer配置项</p><p>第一种:package.json文件的script指令:”watch”: “webpack –watch”,但是需要刷新浏览器<br>第二种:webpack.config.js的devServer(开发服务器)修改,运行命令:”dev”: “webpack-dev-server –progress –hot”<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">+ devServer: {</span><br><span class="line">+ contentBase: <span class="string">'./dist'</span>, <span class="comment">//在哪儿查找文件,把 dist 目录下的文件,作为可访问文件。</span></span><br><span class="line">+ historyApiFallback: <span class="literal">true</span>, <span class="comment">//当使用 HTML5 History API 时,任意的 404 响应都可能需要被替代为 index.html。</span></span><br><span class="line">+ host: <span class="string">'http://localhost:8055'</span>, <span class="comment">//指定端口</span></span><br><span class="line">+ hot: <span class="literal">true</span> <span class="comment">//启用 webpack 的模块热替换特性, 需要webpack或者webpack-dev-server 指令添加 --hot 属性,</span></span><br><span class="line"> <span class="comment">// 这样webpack.HotModuleReplacementPlugin 会自动添加</span></span><br><span class="line">+ proxy: {</span><br><span class="line">+ <span class="string">"/api"</span>: <span class="string">"http://localhost:3000"</span>, <span class="comment">// 请求到 /api/users 现在会被代理到请求 http://localhost:3000/api/users</span></span><br><span class="line">+ <span class="string">"/video"</span>: {</span><br><span class="line"> target: <span class="string">"http://localhost:3000"</span>, <span class="comment">//请求到 /videos/users 现在会被代理到请求 http://localhost:3000/users</span></span><br><span class="line"> pathRewrite: {<span class="string">"^/video"</span> : <span class="string">""</span>},</span><br><span class="line"> secure: <span class="literal">false</span> <span class="comment">//默认为true, false为接受运行在 HTTPS 上,且使用了无效证书的后端服务器。</span></span><br><span class="line"> }</span><br><span class="line">+ },</span><br><span class="line"> publicPath: <span class="string">"/assets/"</span> <span class="comment">//如果我们输出的文件名为bundle.js,可以在浏览器中通过 http://localhost:8080/assets/bundle.js 访问</span></span><br><span class="line">+ }</span><br></pre></td></tr></table></figure></p><p>第三种:是一个容器(wrapper),它可以把 webpack 处理后的文件传递给一个服务器(server)。 webpack-dev-server 在内部使用了它,同时,它也可以作为一个单独的包来使用,以便进行更多自定义设置来实现更多的需求。</p><hr><p>启动HMR:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"> devServer: {</span><br><span class="line"> contentBase: <span class="string">'./dist'</span>,</span><br><span class="line">+ hot: <span class="literal">true</span></span><br><span class="line"> },</span><br><span class="line"> plugins: [</span><br><span class="line"> <span class="keyword">new</span> CleanWebpackPlugin([<span class="string">'dist'</span>]),</span><br><span class="line"> <span class="keyword">new</span> HtmlWebpackPlugin({</span><br><span class="line"> title: <span class="string">'Hot Module Replacement'</span></span><br><span class="line"> }),</span><br><span class="line">+ <span class="keyword">new</span> webpack.NamedModulesPlugin(), <span class="comment">//以便更容易查看要修补(patch)的依赖。</span></span><br><span class="line">+ <span class="keyword">new</span> webpack.HotModuleReplacementPlugin()</span><br><span class="line"> ],</span><br></pre></td></tr></table></figure></p><p>原理:(参考:<a href="https://zhuanlan.zhihu.com/p/30669007)" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/30669007)</a><br>首先要知道server端和client端都做了处理工作</p><ol><li>第一步,在 webpack 的 watch 模式下,文件系统中某一个文件发生修改,webpack 监听到文件变化,根据配置文件对模块重新编译打包,并将打包后的代码通过简单的 JavaScript 对象保存在内存中。</li><li>第二步是 webpack-dev-server 和 webpack 之间的接口交互,而在这一步,主要是 dev-server 的中间件 webpack-dev-middleware 和 webpack 之间的交互,webpack-dev-middleware 调用 webpack 暴露的 API对代码变化进行监控,并且告诉 webpack,将代码打包到内存中。</li><li>第三步是 webpack-dev-server 对文件变化的一个监控,这一步不同于第一步,并不是监控代码变化重新打包。当我们在配置文件中配置了devServer.watchContentBase 为 true 的时候,Server 会监听这些配置文件夹中静态文件的变化,变化后会通知浏览器端对应用进行 live reload。注意,这儿是浏览器刷新,和 HMR 是两个概念。</li><li>第四步也是 webpack-dev-server 代码的工作,该步骤主要是通过 sockjs(webpack-dev-server 的依赖)在浏览器端和服务端之间建立一个 websocket 长连接,将 webpack 编译打包的各个阶段的状态信息告知浏览器端,同时也包括第三步中 Server 监听静态文件变化的信息。浏览器端根据这些 socket 消息进行不同的操作。当然服务端传递的最主要信息还是新模块的 hash 值,后面的步骤根据这一 hash 值来进行模块热替换。</li><li>webpack-dev-server/client 端并不能够请求更新的代码,也不会执行热更模块操作,而把这些工作又交回给了 webpack,webpack/hot/dev-server 的工作就是根据 webpack-dev-server/client 传给它的信息以及 dev-server 的配置决定是刷新浏览器呢还是进行模块热更新。当然如果仅仅是刷新浏览器,也就没有后面那些步骤了。</li><li>HotModuleReplacement.runtime 是客户端 HMR 的中枢,它接收到上一步传递给他的新模块的 hash 值,它通过 JsonpMainTemplate.runtime 向 server 端发送 Ajax 请求,服务端返回一个 json,该 json 包含了所有要更新的模块的 hash 值,获取到更新列表后,该模块再次通过 jsonp 请求,获取到最新的模块代码。这就是上图中 7、8、9 步骤。</li><li>而第 10 步是决定 HMR 成功与否的关键步骤,在该步骤中,HotModulePlugin 将会对新旧模块进行对比,决定是否更新模块,在决定更新模块后,检查模块之间的依赖关系,更新模块的同时更新模块间的依赖引用。</li><li>最后一步,当 HMR 失败后,回退到 live reload 操作,也就是进行浏览器刷新来获取最新打包代码。</li></ol><img src="https://ask.qcloudimg.com/http-save/yehe-1687375/9m5z9odoe6.jpeg?imageView2/2/w/1620"><h2 id="扩展"><a href="#扩展" class="headerlink" title="扩展"></a>扩展</h2><h3 id="webpack的-hash-和-缓存"><a href="#webpack的-hash-和-缓存" class="headerlink" title="webpack的 hash 和 缓存"></a>webpack的 hash 和 缓存</h3><p>可以通过命中缓存,以降低网络流量,使网站加载速度更快,然而,如果我们在部署新版本时不更改资源的文件名,浏览器可能会认为它没有被更新,就会使用它的缓存版本。由于缓存的存在,当你需要获取新的代码时,就会显得很棘手。</p><ol><li>使用输出文件的文件名 filename: ‘[name].[chunkhash].js’,但是我们不做修改,这个hash也会改变!!!!【因为webpack 在入口 chunk 中,包含了某些样板(boilerplate),特别是 runtime 和 manifest。】</li><li>CommonsChunkPlugin 可以用于将模块分离到单独的文件中。然而 CommonsChunkPlugin 有一个较少有人知道的功能是,能够在每次修改后的构建结果中,将 webpack 的样板(boilerplate)和 manifest 提取出来。将第三方库(library)(例如 lodash 或 react)提取到单独的 vendor chunk 文件中,是比较推荐的做法,这是因为,它们很少像本地的源代码那样频繁修改。因此通过实现以上步骤,利用客户端的长效缓存机制,可以通过命中缓存来消除请求,并减少向服务器获取资源,同时还能保证客户端代码和服务器端代码版本一致。</li><li>当改变时,vendor也会改变,。这是因为每个 module.id 会基于默认的解析顺序(resolve order)进行增量。也就是说,当解析顺序发生变化,ID 也会随之改变。可以用 NamedModulesPlugin,将使用模块的路径,而不是数字标识符;或者 HashedModuleIdsPlugin,推荐用于生产环境构建。</li></ol><p>webpack有各种hash值,包括每次项目构建hash,不同入口的chunkhash、文件的内容contenthash,这么多hash,它们有什么区别呢?</p><ul><li>hash是跟整个webpack构建项目相关的,每次项目构建hash对应的值都是不同的,即使项目文件没有做“任何修改”。</li><li>chunkhash,从字面上就能猜出它是跟webpack打包的chunk相关的。具体来说webpack是根据入口entry配置文件来分析其依赖项并由此来构建该entry的chunk,并生成对应的hash值。不同的chunk会有不同的hash值。一般在项目中把公共的依赖库和程序入口文件隔离并进行单独打包构建,用chunkhash来生成hash值,只要依赖公共库不变,那么其对应的chunkhash就不会变,从而达到缓存的目的。</li><li>contenthash表示由文件内容产生的hash值,内容不同产生的contenthash值也不一样。在项目中,通常做法是把项目中css都抽离出对应的css文件来加以引用。</li></ul><h3 id="tree-shaking"><a href="#tree-shaking" class="headerlink" title="tree shaking"></a>tree shaking</h3><p>通常用于描述移除 JavaScript 上下文中的未引用代码(dead-code)。它依赖于 ES2015 模块系统中的静态结构特性,例如 import 和 export。<br>新的 webpack 4 正式版本,扩展了这个检测能力,通过 package.json 的 “sideEffects” 属性作为标记,向 compiler 提供提示,表明项目中的哪些文件是 “pure(纯的 ES2015 模块)”,由此可以安全地删除文件中未使用的部分。</p><p>一个js文件两个方法:add()和square(),只有add()被使用了,square()就要被去除,</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//package.json</span></span><br><span class="line">{</span><br><span class="line"> <span class="string">"name"</span>: <span class="string">"your-project"</span>,</span><br><span class="line"> <span class="string">"sideEffects"</span>: <span class="literal">false</span> <span class="comment">//如果所有代码都不包含副作用,我们就可以简单地将该属性标记为 false,来告知 webpack,</span></span><br><span class="line"> <span class="comment">//它可以安全地删除未用到的 export 导出。</span></span><br><span class="line"> <span class="comment">//比如polyfill,影响全局作用域,不提供export,这就是副作用</span></span><br><span class="line">}</span><br><span class="line"><span class="comment">//或者</span></span><br><span class="line">{</span><br><span class="line"> <span class="string">"name"</span>: <span class="string">"your-project"</span>,</span><br><span class="line"> <span class="string">"sideEffects"</span>: [</span><br><span class="line"> <span class="string">"./src/some-side-effectful-file.js"</span></span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>通过如上方式,我们已经可以通过 import 和 export 语法,找出那些需要删除的“未使用代码”,然而,我们不只是要找出,还需要在 bundle 中删除它们。为此,我们将使用 -p(production) 这个 webpack 编译标记,来启用 uglifyjs 压缩插件。<br>从 webpack 4 开始,也可以通过 “mode” 配置选项轻松切换到压缩输出,只需设置为 “production”。</p><p>总结:</p><ul><li>使用 ES2015 模块语法(即 import 和 export)。</li><li>在项目 package.json 文件中,添加一个 “sideEffects” 入口。</li><li>引入一个能够删除未引用代码(dead code)的压缩工具(minifier)(例如 UglifyJSPlugin)。</li></ul><h3 id="代码分离"><a href="#代码分离" class="headerlink" title="代码分离"></a>代码分离</h3><ol><li>如果我们的项目有多个入口,入口之间包含重复的模块,那些重复模块都会被引入到各个 bundle 中。可以通过通过使用 CommonsChunkPlugin 来移除重复的模块。<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"> <span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>);</span><br><span class="line"> <span class="keyword">const</span> webpack = <span class="built_in">require</span>(<span class="string">'webpack'</span>);</span><br><span class="line"> <span class="keyword">const</span> HTMLWebpackPlugin = <span class="built_in">require</span>(<span class="string">'html-webpack-plugin'</span>);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">module</span>.exports = {</span><br><span class="line"> entry: {</span><br><span class="line"> index: <span class="string">'./src/index.js'</span>,</span><br><span class="line"> another: <span class="string">'./src/another-module.js'</span></span><br><span class="line"> },</span><br><span class="line"> plugins: [</span><br><span class="line"> <span class="keyword">new</span> HTMLWebpackPlugin({</span><br><span class="line"> title: <span class="string">'Code Splitting'</span></span><br><span class="line">+ }),</span><br><span class="line">+ <span class="keyword">new</span> webpack.optimize.CommonsChunkPlugin({</span><br><span class="line">+ name: <span class="string">'common'</span> <span class="comment">// 指定公共 bundle 的名称。</span></span><br><span class="line">+ })</span><br><span class="line"> ],</span><br><span class="line"> output: {</span><br><span class="line"> filename: <span class="string">'[name].bundle.js'</span>,</span><br><span class="line"> path: path.resolve(__dirname, <span class="string">'dist'</span>)</span><br><span class="line"> }</span><br><span class="line"> };</span><br></pre></td></tr></table></figure></li></ol><p>输出:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">Hash: 70a59f8d46ff12575481</span><br><span class="line">Version: webpack 2.6.1</span><br><span class="line">Time: 510ms</span><br><span class="line"> Asset Size Chunks Chunk Names</span><br><span class="line"> index.bundle.js 665 bytes 0 [emitted] index</span><br><span class="line">another.bundle.js 537 bytes 1 [emitted] another</span><br><span class="line"> common.bundle.js 547 kB 2 [emitted] [big] common</span><br><span class="line"> [0] ./~/lodash/lodash.js 540 kB {2} [built]</span><br><span class="line"> [1] (webpack)/buildin/global.js 509 bytes {2} [built]</span><br><span class="line"> [2] (webpack)/buildin/module.js 517 bytes {2} [built]</span><br><span class="line"> [3] ./src/another-module.js 87 bytes {1} [built]</span><br><span class="line"> [4] ./src/index.js 216 bytes {0} [built]</span><br></pre></td></tr></table></figure></p><ol start="2"><li><p>使用ExtractTextPlugin: 将css从主应用中分离</p></li><li><p>对于动态导入,第一种,也是优先选择的方式是,使用符合 ECMAScript 提案 的 import() 语法。<br>第二种,则是使用 webpack 特定的 require.ensure。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span>(<span class="string">'react-image-crop'</span>).then(<span class="function"><span class="params">ReactCrop</span> =></span> {</span><br><span class="line"></span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"><span class="comment">// 可以搭配await一起使用, 但需要 babel 和 plugin syntax-dynamic-import</span></span><br></pre></td></tr></table></figure></li></ol><h3 id="懒加载或者按需加载"><a href="#懒加载或者按需加载" class="headerlink" title="懒加载或者按需加载"></a>懒加载或者按需加载</h3><p>先把你的代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载。</p><p>上面的动态导入已经产生一个分离的代码块,不要没有交互使用懒加载,这样做并没有对我们有很多帮助,还会对性能产生负面影响。<br>最好是用户有操作交互后,再进行懒加载。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">+ button.onclick = <span class="function"><span class="params">e</span> =></span> <span class="keyword">import</span>(<span class="comment">/* webpackChunkName: "print" */</span> <span class="string">'./print'</span>).then(<span class="function"><span class="params">module</span> =></span> {</span><br><span class="line">+ <span class="keyword">var</span> print = <span class="built_in">module</span>.default;</span><br><span class="line">+</span><br><span class="line">+ print();</span><br><span class="line">+ });</span><br></pre></td></tr></table></figure><h3 id="externals"><a href="#externals" class="headerlink" title="externals"></a>externals</h3><p>如果我们想引用一个库,但是又不想让webpack打包,并且又不影响我们在程序中以CMD、AMD或者window/global全局等方式进行使用,那就可以通过配置externals。</p><p>使用React<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span><br></pre></td></tr></table></figure></p><p>配置externals<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">externals: {</span><br><span class="line"> <span class="string">"react"</span>: React</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>]]></content>
<tags>
<tag> webpack </tag>
</tags>
</entry>
<entry>
<title>文件下载一二事</title>
<link href="/2018/04/26/download/"/>
<content type="html"><![CDATA[<p>最近博主做的项目对文件下载的方式有需求,因此也整理了部分资料以供分享。</p><h2 id="a标签"><a href="#a标签" class="headerlink" title="a标签"></a>a标签</h2><p>这是我们最熟悉的方式。但是对于浏览器可以默认打开的格式:pdf、图像等,就不是默认下载了。<br>所以我们可以使用html5新增的download属性,默认下载。<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">a</span> <span class="attr">href</span>=<span class="string">'file.js'</span>></span>file.js<span class="tag"></<span class="name">a</span>></span></span><br></pre></td></tr></table></figure></p><p>这种方式有两个缺点:<br>1、兼容性不好<br>2、href必须是同源地址,不然download属性不生效,也是默认打开。也就是跨域无效。</p><p>决定是否能下载取决于:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//表明文件类型</span></span><br><span class="line">header( <span class="string">"Content-Type: video/mp4"</span> ); </span><br><span class="line"><span class="comment">//类型为下载,同时设置下载文件名</span></span><br><span class="line">header(<span class="string">"Content-Disposition: attachment;filename=qwe.mp4"</span>);</span><br></pre></td></tr></table></figure></p><h2 id="window-location-href"><a href="#window-location-href" class="headerlink" title="window.location.href"></a>window.location.href</h2><p>使用window.location.href = ‘’也可以获取资源。不能下载默认打开格式也是一样的,不过可以通过<br>window.open() 打开新页面</p><h2 id="createObjectURL"><a href="#createObjectURL" class="headerlink" title="createObjectURL"></a>createObjectURL</h2><p>Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1)引入了对DOM window.URL.createObjectURL() 和 window.URL.revokeObjectURL() 方法的支持。这使得你可以创建用于引用任何数据的简单URL字符串,也可以引用一个包括用户电脑上的本地文件的DOM File对象。</p><h3 id="createObjectURL和-revokeObjectURL"><a href="#createObjectURL和-revokeObjectURL" class="headerlink" title="createObjectURL和 revokeObjectURL"></a>createObjectURL和 revokeObjectURL</h3><p>这个对象URL是一个标识File对象的字符串。每次你调用window.URL.createObjectURL(),就会产生一个唯一的对象URL,即使是你对一个已创建了对象URL的文件再次创建一个对象URL。每个创建了的对象URL必须要释放。当文档关闭时,它们会自动被释放。如果你的网页要动态使用它们,你需要显式调用 window.URL.revokeObjectURL()来释放它们。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> blobObject = <span class="keyword">new</span> Blob([<span class="string">"I scream. You scream. We all scream for ice cream."</span>]);</span><br></pre></td></tr></table></figure><p>但是这种方式在IE存在问题,你们看ie的createObjectURL和chrome不一样,chrome带了域名,而ie不带域名。<br>blob:242CACD6-06D5-4145-A6DA-55DBE47409DB<br>blob:null/242CACD6-06D5-4145-A6DA-55DBE47409DB<br>所以要采用ie独有的方法window.navigator.msSaveOrOpenBlob 或者window.navigator.msSaveBlob。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//解决浏览器兼容性问题</span></span><br><span class="line"><span class="keyword">if</span>(<span class="string">'msSaveOrOpenBlob'</span> <span class="keyword">in</span> navigator){</span><br><span class="line"> <span class="comment">// Microsoft Edge and Microsoft Internet Explorer 10-11</span></span><br><span class="line"> <span class="built_in">window</span>.navigator.msSaveOrOpenBlob(fileBlob, name);</span><br><span class="line">}<span class="keyword">else</span>{</span><br><span class="line"> <span class="comment">// standard code for Google Chrome, Mozilla Firefox etc</span></span><br><span class="line"> <span class="keyword">let</span> dom = <span class="built_in">document</span>.createElement(<span class="string">'a'</span>);</span><br><span class="line"> dom.download = name;</span><br><span class="line"> dom.style.display = <span class="string">'none'</span>;</span><br><span class="line"> <span class="keyword">let</span> url = URL.createObjectURL(fileBlob);</span><br><span class="line"> dom.href = url;</span><br><span class="line"> <span class="built_in">document</span>.body.appendChild(dom);</span><br><span class="line"> dom.click();</span><br><span class="line"> URL.revokeObjectURL(url); </span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>我运用到这个需求是我请求资源必须在header的token中携带自己的身份信息,所以我不能使用a标签进行请求。必须使用请求获取二进制blob数据。</p>]]></content>
<tags>
<tag> js </tag>
</tags>
</entry>
<entry>
<title>大文件上传</title>
<link href="/2018/04/02/bigfile/"/>
<content type="html"><![CDATA[<p>使用React生成一个大文件上传功能,此功能我们调用了ant design 的ui。</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><FileUpload multiple chunked threadNum={<span class="number">3</span>}></span><br><span class="line"> <p className=<span class="string">"ant-upload-drag-icon"</span>></span><br><span class="line"> <Icon type=<span class="string">"inbox"</span> /></span><br><span class="line"> <<span class="regexp">/p></span></span><br><span class="line"><span class="regexp"> <p className="ant-upload-text">点击选择文件或将文件拖拽到这里</</span>p></span><br><span class="line"> <p className=<span class="string">"ant-upload-hint"</span>>系统支持在线播放.mp4,.flv,.ogv,.webm格式视频,其他格式视频需要下载后才可观看。<<span class="regexp">/p></span></span><br><span class="line"><span class="regexp"></</span>FileUpload></span><br></pre></td></tr></table></figure><p>这个组件的参数如下:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> multiple: PropTypes.bool, <span class="comment">//是否支持多选文件</span></span><br><span class="line"> chunkSize: PropTypes.number, <span class="comment">//文件分块大小</span></span><br><span class="line"> threadNum: PropTypes.number, <span class="comment">//并发分块数量</span></span><br><span class="line"> accept: PropTypes.string, <span class="comment">//接受上传的文件类型</span></span><br><span class="line"> onChange: PropTypes.func, <span class="comment">//上传完成后的回调</span></span><br><span class="line"> chunked: PropTypes.bool <span class="comment">//是否分片上传</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> FileUpload = <span class="function">(<span class="params">{ chunked , ...rest }</span>) =></span> (</span><br><span class="line"> chunked ? <LargeFileUpload {...rest} /> : <SmallFileUpload {...rest} /></span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>我们有两种上传方式,大文件上传,小文件上传</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> PropTypes <span class="keyword">from</span> <span class="string">'prop-types'</span>;</span><br><span class="line"><span class="keyword">import</span> {</span><br><span class="line"> Upload,</span><br><span class="line"> Button,</span><br><span class="line"> Icon,</span><br><span class="line"> Modal</span><br><span class="line">} <span class="keyword">from</span> <span class="string">'antd'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> {</span><br><span class="line"> uploadFile</span><br><span class="line">} <span class="keyword">from</span> <span class="string">'../../../unit/fetch'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> propTypes = {</span><br><span class="line"> multiple: PropTypes.bool,</span><br><span class="line"> accept: PropTypes.string,</span><br><span class="line"> onChange: PropTypes.func</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> Dragger = Upload.Dragger;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> FILE_STATE = {</span><br><span class="line"> WAITING: <span class="built_in">Symbol</span>(),</span><br><span class="line"> UPLOADING: <span class="built_in">Symbol</span>(),</span><br><span class="line"> FINISHED: <span class="built_in">Symbol</span>(),</span><br><span class="line"> ERRORED: <span class="built_in">Symbol</span>()</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SmallFileUpload</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>({</span><br><span class="line"> multiple = <span class="literal">false</span>,</span><br><span class="line"> accept = <span class="string">''</span></span><br><span class="line"> }) {</span><br><span class="line"> <span class="keyword">super</span>();</span><br><span class="line"> <span class="keyword">this</span>.state = {</span><br><span class="line"> multiple,</span><br><span class="line"> accept,</span><br><span class="line"> disabled: <span class="literal">false</span>,</span><br><span class="line"> fileList: []</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.toUpload = <span class="keyword">this</span>.toUpload.bind(<span class="keyword">this</span>);</span><br><span class="line"> <span class="keyword">this</span>.submit = <span class="keyword">this</span>.submit.bind(<span class="keyword">this</span>);</span><br><span class="line"> <span class="keyword">this</span>.onCompleted = <span class="keyword">this</span>.onCompleted.bind(<span class="keyword">this</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> onCompleted() {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="keyword">this</span>.props.onChange === <span class="string">'function'</span>) {</span><br><span class="line"> <span class="keyword">this</span>.props.onChange({</span><br><span class="line"> success: <span class="keyword">this</span>.state.fileList.filter(<span class="function"><span class="params">info</span> =></span> info.state === FILE_STATE[<span class="string">'FINISHED'</span>]).map(<span class="function"><span class="params">info</span> =></span> {<span class="keyword">return</span> {<span class="attr">token</span>: info.token, <span class="attr">name</span>: info.config.file.name}}),</span><br><span class="line"> total: <span class="keyword">this</span>.state.fileList.length</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> toUpload(fileInfo) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="keyword">async</span> (resolve) => { </span><br><span class="line"> <span class="keyword">try</span> { </span><br><span class="line"> <span class="keyword">const</span> { token } = <span class="keyword">await</span> uploadFile({</span><br><span class="line"> file: fileInfo.config.file</span><br><span class="line"> });</span><br><span class="line"> fileInfo.token = token;</span><br><span class="line"> fileInfo.state = FILE_STATE[<span class="string">'FINISHED'</span>];</span><br><span class="line"> fileInfo.config.onSuccess();</span><br><span class="line"> } <span class="keyword">catch</span>(e) {</span><br><span class="line"> fileInfo.state = FILE_STATE[<span class="string">'ERRORED'</span>];</span><br><span class="line"> fileInfo.config.onError();</span><br><span class="line"> }</span><br><span class="line"> resolve();</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">async</span> submit() {</span><br><span class="line"> <span class="keyword">if</span> (!<span class="keyword">this</span>.state.fileList.length) {</span><br><span class="line"> Modal.info({</span><br><span class="line"> title: <span class="string">'提示'</span>,</span><br><span class="line"> content: (</span><br><span class="line"> <div></span><br><span class="line"> <p>请选择文件后,再提交!<<span class="regexp">/p></span></span><br><span class="line"><span class="regexp"> </</span>div></span><br><span class="line"> )</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> fileInfo <span class="keyword">of</span> <span class="keyword">this</span>.state.fileList) {</span><br><span class="line"> <span class="keyword">if</span> (fileInfo.state === FILE_STATE[<span class="string">'WAITING'</span>]) {</span><br><span class="line"> fileInfo.state = FILE_STATE[<span class="string">'UPLOADING'</span>];</span><br><span class="line"> <span class="keyword">await</span> <span class="keyword">this</span>.toUpload(fileInfo);</span><br><span class="line"> <span class="keyword">this</span>.onCompleted();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> render() {</span><br><span class="line"> <span class="keyword">const</span> draggerProps = {</span><br><span class="line"> multiple: <span class="keyword">this</span>.state.multiple,</span><br><span class="line"> accept: <span class="keyword">this</span>.state.accept,</span><br><span class="line"> disabled: <span class="keyword">this</span>.state.disabled,</span><br><span class="line"> customRequest: <span class="function">(<span class="params">config</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> fileInfo = {</span><br><span class="line"> id: config.file.uid,</span><br><span class="line"> state: FILE_STATE[<span class="string">'WAITING'</span>],</span><br><span class="line"> token: <span class="literal">null</span>,</span><br><span class="line"> config</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">this</span>.setState({</span><br><span class="line"> fileList: [...this.state.fileList, fileInfo],</span><br><span class="line"> disabled: !<span class="keyword">this</span>.state.multiple</span><br><span class="line"> });</span><br><span class="line"> },</span><br><span class="line"> onRemove: <span class="function"><span class="params">file</span> =></span> {</span><br><span class="line"> <span class="keyword">const</span> fileList = [...this.state.fileList.filter(<span class="function"><span class="params">info</span> =></span> info.id !== file.uid)];</span><br><span class="line"> <span class="keyword">let</span> isCompleted = fileList.every(<span class="function"><span class="params">info</span> =></span> [FILE_STATE[<span class="string">'FINISHED'</span>], FILE_STATE[<span class="string">'ERRORED'</span>]].includes(info.state));</span><br><span class="line"> <span class="keyword">this</span>.setState({ </span><br><span class="line"> fileList,</span><br><span class="line"> disabled: fileList.length && !<span class="keyword">this</span>.state.multiple</span><br><span class="line"> }, () => {</span><br><span class="line"> isCompleted && <span class="keyword">this</span>.onCompleted();</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> (<span class="xml"><span class="tag"><<span class="name">div</span>></span></span></span><br><span class="line"> <Dragger {...draggerProps}></span><br><span class="line"> {this.props.children ? this.props.children :</span><br><span class="line"> <div></span><br><span class="line"> <p className="ant-upload-drag-icon"><Icon type="inbox" /></p></span><br><span class="line"> <p className="ant-upload-text">点击选择文件或将文件拖拽到这里</p></span><br><span class="line"> </div>}</span><br><span class="line"> </Dragger></span><br><span class="line"> <div style={{marginTop: '10px'}}></span><br><span class="line"> <Button ghost type="primary" size="small" onClick={this.submit}>上传</Button></span><br><span class="line"> </div></span><br><span class="line"><span class="xml"> <span class="tag"></<span class="name">div</span>></span>);</span></span><br><span class="line"><span class="xml"> }</span></span><br><span class="line"><span class="xml">}</span></span><br><span class="line"><span class="xml"></span></span><br><span class="line"><span class="xml">SmallFileUpload.propTypes = propTypes;</span></span><br><span class="line"><span class="xml"></span></span><br><span class="line"><span class="xml">export default SmallFileUpload;</span></span><br></pre></td></tr></table></figure><p>使用了ant design 的upload 组件,使用customRequest自己写上传逻辑。<br>获得的config对象:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> action: <span class="string">""</span></span><br><span class="line"> data: {}</span><br><span class="line"> file: File {<span class="attr">uid</span>: <span class="string">"rc-upload-1551879565599-4"</span>, <span class="attr">name</span>: <span class="string">"面试.txt"</span>, <span class="attr">lastModified</span>: <span class="number">1547794695512</span>, <span class="attr">lastModifiedDate</span>: Fri Jan <span class="number">18</span> <span class="number">2019</span> <span class="number">14</span>:<span class="number">58</span>:<span class="number">15</span> GMT+<span class="number">0800</span> (中国标准时间), <span class="attr">webkitRelativePath</span>: <span class="string">""</span>, …}</span><br><span class="line"> filename: <span class="string">"file"</span></span><br><span class="line"> headers: {}</span><br><span class="line"> onError: ƒ (r,o)</span><br><span class="line"> onProgress: ƒ (t)</span><br><span class="line"> onSuccess: ƒ (r,o)</span><br><span class="line"> withCredentials: <span class="literal">false</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>我们的文件对象:<br><img src="/images/bigfile_01.png"></p><p>我们通过fetch上传一个一个小文件, 如果文件太大呢?</p><p>我们需要将文件进行分片上传。为了保证文件上传的正确性,不被挟持。我们要传递给后台md5值。</p><p>MD5在论坛上、软件发布时经常用,是为了保证文件的正确性,防止一些人盗用程序,加些木马或者篡改版权,设计的一套验证系统。每个文件都可以用MD5验证程序算出一个固定的MD5码来。</p><p>我们使用了Spark-MD5 JS, 它的api我们使用如下:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> hexHash = SparkMD5.hash(<span class="string">'Hi there'</span>); <span class="comment">//二进制hash</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 增量相加</span></span><br><span class="line"><span class="keyword">var</span> spark = <span class="keyword">new</span> SparkMD5();</span><br><span class="line">spark.append(<span class="string">'Hi'</span>);</span><br><span class="line">spark.append(<span class="string">' there'</span>);</span><br><span class="line"><span class="keyword">var</span> hexHash = spark.end(); <span class="comment">// hex hash</span></span><br><span class="line"><span class="keyword">var</span> rawHash = spark.end(<span class="literal">true</span>); </span><br><span class="line"></span><br><span class="line"><span class="comment">// 传递二进制数据的话</span></span><br><span class="line"><span class="keyword">var</span> chunks = <span class="built_in">Math</span>.ceil(file.size / chunkSize),</span><br><span class="line"> spark = <span class="keyword">new</span> SparkMD5.ArrayBuffer(),</span><br><span class="line"> currentChunk = <span class="number">0</span>;</span><br><span class="line">fileReader = <span class="keyword">new</span> FileReader();</span><br><span class="line">fileReader.onload = <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>{</span><br><span class="line"> spark.append(e.target.result); <span class="comment">// Append array buffer</span></span><br><span class="line"> currentChunk++;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (currentChunk < chunks) {</span><br><span class="line"> loadNext();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'finished loading'</span>);</span><br><span class="line"> <span class="built_in">console</span>.info(<span class="string">'computed hash'</span>, spark.end()); <span class="comment">// Compute hash</span></span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">loadNext</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> start = currentChunk * chunkSize,</span><br><span class="line"> end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;</span><br><span class="line"></span><br><span class="line"> fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>上传的接口我们采用了三个接口,一个是用户根据MD5值,文件大小和名字创建上传Token。<br>二再根据上传Token,order向文件继续追加内容。<br>三是大文件合并,参数是Token和全部patch的数量</p><p>同时,因为大文件上传缓慢,我们要获取上传进度值。使用了progress event。</p><p>Progress Events定义了与客户端服务器通信有关的事件。这些事件最早其实只针对XHR操作,但目前也被其它API借鉴。</p><p>loadstart:在接收到相应数据的第一个字节时触发。<br>progress:在接收相应期间持续不断触发。<br>error:在请求发生错误时触发。<br>abort:在因为调用abort()方法而终止链接时触发。<br>load:在接收到完整的相应数据时触发。<br>loadend:在通信完成或者触发error、abort或load事件后触</p><p>通过xhr.upload.onprogress获取上传进度,lengthComputable是一个表示进度信息是否可用的布尔值</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br></pre></td><td class="code"><pre><span class="line">xhr.upload.onprogress = <span class="function"><span class="keyword">function</span>(<span class="params">evt</span>) </span>{</span><br><span class="line"> <span class="comment">// event.total是需要传输的总字节,event.loaded是已经传输的字节。如果event.lengthComputable不为真,则event.total等于0</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//这是单文件上传</span></span><br><span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">uploadFile</span>(<span class="params">options, progressBarFunc</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> xhr = <span class="keyword">new</span> XMLHttpRequest();</span><br><span class="line"> <span class="keyword">let</span> url = address.upload.substring(<span class="number">0</span>, address.upload.length - <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">let</span> formdata = <span class="keyword">new</span> FormData();</span><br><span class="line"> formdata.append(<span class="string">'file'</span>, options.file);</span><br><span class="line"> xhr.open(<span class="string">'POST'</span>, url);</span><br><span class="line"> xhr.setRequestHeader(<span class="string">'Auth-Token'</span>, getAccessToken());</span><br><span class="line"> xhr.upload.onprogress = <span class="function"><span class="keyword">function</span>(<span class="params">evt</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span>(evt.lengthComputable) {</span><br><span class="line"> progressBarFunc && progressBarFunc(evt.loaded, evt.total);</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> xhr.onreadystatechange = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">if</span>(xhr.readyState === <span class="number">4</span>) {</span><br><span class="line"> <span class="keyword">if</span> (xhr.status === <span class="number">200</span>) {</span><br><span class="line"> resolve(<span class="built_in">JSON</span>.parse(xhr.responseText));</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> reject(<span class="built_in">JSON</span>.parse(xhr.responseText))</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> xhr.send(formdata);</span><br><span class="line"> });</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 分块上传,用户根据MD5值,文件大小和名字创建上传Token。再根据上传Token,order向文件继续追加内容。</span></span><br><span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">getUploadTokenAndProgress</span>(<span class="params">option</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> executeFetch(address.upload, {</span><br><span class="line"> method: <span class="string">'POST'</span>,</span><br><span class="line"> headers: {</span><br><span class="line"> <span class="string">'Content-Type'</span>: <span class="string">'application/x-www-form-urlencoded'</span>,</span><br><span class="line"> <span class="string">'Auth-Token'</span>: getAccessToken()</span><br><span class="line"> },</span><br><span class="line"> body: objToQueryString(option).substring(<span class="number">1</span>)</span><br><span class="line"> });</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">uploadFileThunk</span>(<span class="params">options, progressBarFunc</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> xhr = <span class="keyword">new</span> XMLHttpRequest();</span><br><span class="line"> <span class="keyword">let</span> url = <span class="string">`<span class="subst">${address.upload}</span>/append`</span>;</span><br><span class="line"> <span class="keyword">let</span> formdata = <span class="keyword">new</span> FormData();</span><br><span class="line"> formdata.append(<span class="string">'uploadToken'</span>, options.uploadToken);</span><br><span class="line"> formdata.append(<span class="string">'order'</span>, options.order);</span><br><span class="line"> formdata.append(<span class="string">'file'</span>, options.file);</span><br><span class="line"> xhr.open(<span class="string">'POST'</span>, url);</span><br><span class="line"> xhr.setRequestHeader(<span class="string">'Auth-Token'</span>, getAccessToken());</span><br><span class="line"> xhr.upload.onprogress = <span class="function"><span class="keyword">function</span>(<span class="params">evt</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span>(evt.lengthComputable) {</span><br><span class="line"> progressBarFunc && progressBarFunc(evt.loaded, evt.total);</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> xhr.onreadystatechange = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">if</span>(xhr.readyState === <span class="number">4</span>) {</span><br><span class="line"> <span class="keyword">if</span> (xhr.status === <span class="number">200</span>) {</span><br><span class="line"> resolve();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> reject(<span class="built_in">JSON</span>.parse(xhr.responseText))</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> xhr.send(formdata);</span><br><span class="line"> });</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 大文件合并, 参数是Token和全部patch的数量</span></span><br><span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">finishFileUpload</span>(<span class="params">option</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> executeFetch(<span class="string">`<span class="subst">${address.upload}</span>/complete`</span>, {</span><br><span class="line"> method: <span class="string">'PUT'</span>,</span><br><span class="line"> headers: {</span><br><span class="line"> <span class="string">'Content-Type'</span>: <span class="string">'application/x-www-form-urlencoded'</span>,</span><br><span class="line"> <span class="string">'Auth-Token'</span>: getAccessToken()</span><br><span class="line"> },</span><br><span class="line"> body: objToQueryString(option).substring(<span class="number">1</span>)</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>完整的生成调用代码如下:</p><figure class="highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> React <span class="keyword">from</span> <span class="string">'react'</span>;</span><br><span class="line"><span class="keyword">import</span> PropTypes <span class="keyword">from</span> <span class="string">'prop-types'</span>;</span><br><span class="line"><span class="keyword">import</span> { Upload, Button, Icon, Modal } <span class="keyword">from</span> <span class="string">'antd'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> { getUploadTokenAndProgress, uploadFileThunk, finishFileUpload } <span class="keyword">from</span> <span class="string">'../../../unit/fetch'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> propTypes = {</span><br><span class="line"> multiple: PropTypes.bool,</span><br><span class="line"> chunkSize: PropTypes.number,</span><br><span class="line"> threadNum: PropTypes.number,</span><br><span class="line"> accept: PropTypes.string,</span><br><span class="line"> onChange: PropTypes.func</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> Dragger = Upload.Dragger;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> FILE_STATE = {</span><br><span class="line"> PREPARING: <span class="built_in">Symbol</span>(),</span><br><span class="line"> WAITING: <span class="built_in">Symbol</span>(),</span><br><span class="line"> UPLOADING: <span class="built_in">Symbol</span>(),</span><br><span class="line"> FINISHED: <span class="built_in">Symbol</span>(),</span><br><span class="line"> ERRORED: <span class="built_in">Symbol</span>()</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">LargeFileUpload</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>({</span><br><span class="line"> multiple = <span class="literal">false</span>,</span><br><span class="line"> chunkSize = <span class="number">5</span> * <span class="number">1024</span> * <span class="number">1024</span>,</span><br><span class="line"> threadNum = <span class="number">1</span>,</span><br><span class="line"> accept = <span class="string">''</span></span><br><span class="line"> }) {</span><br><span class="line"> <span class="keyword">super</span>();</span><br><span class="line"> <span class="keyword">this</span>.state = {</span><br><span class="line"> multiple,</span><br><span class="line"> chunkSize,</span><br><span class="line"> threadNum,</span><br><span class="line"> accept,</span><br><span class="line"> disabled: <span class="literal">false</span>,</span><br><span class="line"> fileList: [],</span><br><span class="line"> isPrepared: <span class="literal">false</span></span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.submit = <span class="keyword">this</span>.submit.bind(<span class="keyword">this</span>);</span><br><span class="line"> <span class="keyword">this</span>.sliceFile = <span class="keyword">this</span>.sliceFile.bind(<span class="keyword">this</span>);</span><br><span class="line"> <span class="keyword">this</span>.md5File = <span class="keyword">this</span>.md5File.bind(<span class="keyword">this</span>);</span><br><span class="line"> <span class="keyword">this</span>.toUpload = <span class="keyword">this</span>.toUpload.bind(<span class="keyword">this</span>);</span><br><span class="line"> <span class="keyword">this</span>.onCompleted = <span class="keyword">this</span>.onCompleted.bind(<span class="keyword">this</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">import</span>(<span class="string">'../../../unit/tool/MD5File'</span>).then(<span class="function"><span class="params">MD5WorkerUrl</span> =></span> {</span><br><span class="line"> <span class="keyword">this</span>.MD5Worker = <span class="keyword">new</span> Worker(MD5WorkerUrl);</span><br><span class="line"> <span class="keyword">this</span>.MD5Worker.onmessage = <span class="function">(<span class="params">{data}</span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> fileInfo = <span class="keyword">this</span>.state.fileList.find( <span class="function"><span class="params">info</span> =></span> info.id === data.id );</span><br><span class="line"> <span class="keyword">if</span>(!fileInfo) {</span><br><span class="line"> <span class="keyword">return</span> ;</span><br><span class="line"> }</span><br><span class="line"> fileInfo.MD5 = data.hash;</span><br><span class="line"> fileInfo.state = FILE_STATE[<span class="string">'WAITING'</span>];</span><br><span class="line"> <span class="keyword">let</span> isPrepared = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> info <span class="keyword">of</span> <span class="keyword">this</span>.state.fileList) {</span><br><span class="line"> <span class="keyword">if</span> (info.state === FILE_STATE[<span class="string">'PREPARING'</span>]) {</span><br><span class="line"> isPrepared = <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.setState({ isPrepared });</span><br><span class="line"> };</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> onCompleted() {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="keyword">this</span>.props.onChange === <span class="string">'function'</span>) {</span><br><span class="line"> <span class="keyword">this</span>.props.onChange({</span><br><span class="line"> success: <span class="keyword">this</span>.state.fileList.filter(<span class="function"><span class="params">info</span> =></span> info.state === FILE_STATE[<span class="string">'FINISHED'</span>]).map(<span class="function"><span class="params">info</span> =></span> {<span class="keyword">return</span> {<span class="attr">token</span>: info.token, <span class="attr">name</span>: info.config.file.name}}),</span><br><span class="line"> total: <span class="keyword">this</span>.state.fileList.length,</span><br><span class="line"> isCompleted: !<span class="keyword">this</span>.state.fileList.some(<span class="function"><span class="params">info</span> =></span> ![FILE_STATE[<span class="string">'FINISHED'</span>], FILE_STATE[<span class="string">'ERRORED'</span>]].includes(info.state))</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//文件切片</span></span><br><span class="line"> sliceFile(file, chunkCount) {</span><br><span class="line"> <span class="keyword">let</span> sliceRst = [];</span><br><span class="line"> <span class="keyword">let</span> blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>, start, end; i < chunkCount; i++) {</span><br><span class="line"> start = i * <span class="keyword">this</span>.state.chunkSize;</span><br><span class="line"> end = <span class="built_in">Math</span>.min(file.size, start + <span class="keyword">this</span>.state.chunkSize);</span><br><span class="line"> sliceRst.push(blobSlice.call(file, start, end));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> sliceRst;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//文件MD5</span></span><br><span class="line"> md5File(fileInfo) {</span><br><span class="line"> <span class="keyword">this</span>.MD5Worker.postMessage({</span><br><span class="line"> <span class="string">'fileChunk'</span>: <span class="keyword">this</span>.sliceFile(fileInfo.config.file, fileInfo.chunkCount),</span><br><span class="line"> <span class="string">'id'</span>: fileInfo.id</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> toUpload(fileInfo) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="keyword">async</span> (resolve, reject) => {</span><br><span class="line"> <span class="keyword">const</span> successThunkArr = <span class="keyword">new</span> <span class="built_in">Set</span>();</span><br><span class="line"> <span class="keyword">const</span> fileChunkArray = <span class="keyword">this</span>.sliceFile(fileInfo.config.file, fileInfo.chunkCount);</span><br><span class="line"> <span class="keyword">const</span> completeUploadingFile = <span class="keyword">async</span> () => {</span><br><span class="line"> fileInfo.config.onProgress({<span class="attr">percent</span>: <span class="number">100</span>});</span><br><span class="line"> <span class="keyword">try</span>{</span><br><span class="line"> <span class="keyword">const</span> { token } = <span class="keyword">await</span> finishFileUpload({</span><br><span class="line"> uploadToken: fileInfo.uploadToken,</span><br><span class="line"> patchSum: fileInfo.chunkCount</span><br><span class="line"> });</span><br><span class="line"> fileInfo.token = token;</span><br><span class="line"> fileInfo.state = FILE_STATE[<span class="string">'FINISHED'</span>];</span><br><span class="line"> fileInfo.config.onSuccess();</span><br><span class="line"> resolve();</span><br><span class="line"> } <span class="keyword">catch</span>(e) {</span><br><span class="line"> failureUploadThunk();</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">const</span> failureUploadThunk = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> fileInfo.state = FILE_STATE[<span class="string">'ERRORED'</span>];</span><br><span class="line"> fileInfo.config.onError();</span><br><span class="line"> resolve();</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">const</span> successUploadThunk = <span class="function"><span class="params">curChunkIndex</span> =></span> {</span><br><span class="line"> successThunkArr.add(+curChunkIndex);</span><br><span class="line"> <span class="keyword">if</span> (successThunkArr.size === fileInfo.chunkCount) {</span><br><span class="line"> completeUploadingFile();</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (successThunkArr.size < fileInfo.chunkCount) {</span><br><span class="line"> fileInfo.config.onProgress({<span class="attr">percent</span>: (successThunkArr.size / fileInfo.chunkCount).toFixed(<span class="number">2</span>) * <span class="number">100</span> });</span><br><span class="line"> curChunkIndex && uploadThunk(successThunkArr.size);</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> <span class="comment">//chunkIndex从0开始</span></span><br><span class="line"> <span class="keyword">const</span> uploadThunk = <span class="function">(<span class="params">currentSuccessThunkNum, chunkIndex</span>) =></span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="keyword">async</span> resolve => {</span><br><span class="line"> <span class="keyword">const</span> curChunkIndex = chunkIndex + <span class="number">1</span> ? chunkIndex : currentSuccessThunkNum + <span class="built_in">Math</span>.min(<span class="keyword">this</span>.state.threadNum, fileInfo.chunkCount) - <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">let</span> failureTimes = <span class="number">0</span>;</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">toUploadThunk</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="keyword">async</span> resolve => {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">await</span> uploadFileThunk({</span><br><span class="line"> uploadToken: fileInfo.uploadToken,</span><br><span class="line"> order: curChunkIndex,</span><br><span class="line"> file: fileChunkArray[curChunkIndex]</span><br><span class="line"> });</span><br><span class="line"> successUploadThunk(curChunkIndex);</span><br><span class="line"> resolve();</span><br><span class="line"> } <span class="keyword">catch</span>(e) {</span><br><span class="line"> failureTimes++;</span><br><span class="line"> <span class="keyword">if</span> (failureTimes < <span class="number">3</span>) {</span><br><span class="line"> toUploadThunk();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">//文件上传失败,结束上传</span></span><br><span class="line"> failureUploadThunk();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(curChunkIndex >= fileInfo.chunkCount) {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">const</span> fileSize = fileInfo.config.file.size;</span><br><span class="line"> <span class="keyword">const</span> { uploadToken, thunkArr } = <span class="keyword">await</span> getUploadTokenAndProgress({</span><br><span class="line"> md5: fileInfo.MD5,</span><br><span class="line"> fileSize,</span><br><span class="line"> fileName: <span class="built_in">encodeURIComponent</span>(fileInfo.config.file.name)</span><br><span class="line"> });</span><br><span class="line"> fileInfo.uploadToken = uploadToken;</span><br><span class="line"> thunkArr.forEach( <span class="function"><span class="params">index</span> =></span> successThunkArr.add(+index) );</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (successThunkArr.has(+curChunkIndex)) { <span class="comment">//当前要上传的分片已存在服务器中</span></span><br><span class="line"> successUploadThunk(curChunkIndex);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">await</span> toUploadThunk();</span><br><span class="line"> }</span><br><span class="line"> resolve()</span><br><span class="line"> } <span class="keyword">catch</span>(e) {</span><br><span class="line"> failureUploadThunk();</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="keyword">await</span> uploadThunk(<span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">1</span>, len = <span class="built_in">Math</span>.min(<span class="keyword">this</span>.state.threadNum + <span class="number">1</span>, fileInfo.chunkCount); i < len; i++) {</span><br><span class="line"> uploadThunk(successThunkArr.size, i);</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">async</span> submit() {</span><br><span class="line"> <span class="keyword">if</span>(!<span class="keyword">this</span>.state.fileList.length) {</span><br><span class="line"> Modal.info({</span><br><span class="line"> title: <span class="string">'提示'</span>,</span><br><span class="line"> content: (</span><br><span class="line"> <div></span><br><span class="line"> <p>请选择文件后,再提交!<<span class="regexp">/p></span></span><br><span class="line"><span class="regexp"> </</span>div></span><br><span class="line"> )</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> fileInfo <span class="keyword">of</span> <span class="keyword">this</span>.state.fileList) {</span><br><span class="line"> <span class="keyword">if</span> (fileInfo.state === FILE_STATE[<span class="string">'WAITING'</span>]) {</span><br><span class="line"> fileInfo.state = FILE_STATE[<span class="string">'UPLOADING'</span>];</span><br><span class="line"> <span class="keyword">await</span> <span class="keyword">this</span>.toUpload(fileInfo);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.onCompleted();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> render() {</span><br><span class="line"> <span class="keyword">const</span> draggerProps = {</span><br><span class="line"> multiple: <span class="keyword">this</span>.state.multiple,</span><br><span class="line"> accept: <span class="keyword">this</span>.state.accept,</span><br><span class="line"> disabled: <span class="keyword">this</span>.state.disabled,</span><br><span class="line"> customRequest: <span class="function"><span class="params">config</span> =></span> {</span><br><span class="line"> <span class="keyword">let</span> fileInfo = {</span><br><span class="line"> id : config.file.uid,</span><br><span class="line"> state: FILE_STATE[<span class="string">'PREPARING'</span>],</span><br><span class="line"> chunkCount: <span class="built_in">Math</span>.ceil(config.file.size / <span class="keyword">this</span>.state.chunkSize),</span><br><span class="line"> MD5: <span class="literal">null</span>,</span><br><span class="line"> uploadToken: <span class="literal">null</span>,</span><br><span class="line"> token: <span class="literal">null</span>,</span><br><span class="line"> config</span><br><span class="line"> };</span><br><span class="line"> <span class="built_in">console</span>.log(config);</span><br><span class="line"> <span class="keyword">this</span>.setState({</span><br><span class="line"> fileList: [...this.state.fileList, fileInfo],</span><br><span class="line"> disabled: !<span class="keyword">this</span>.state.multiple,</span><br><span class="line"> isPrepared: <span class="literal">true</span></span><br><span class="line"> }, <span class="keyword">this</span>.onCompleted);</span><br><span class="line"> <span class="keyword">this</span>.md5File(fileInfo);</span><br><span class="line"> },</span><br><span class="line"> onRemove: <span class="function"><span class="params">file</span> =></span> {</span><br><span class="line"> <span class="keyword">const</span> fileList = [...this.state.fileList.filter(<span class="function"><span class="params">info</span> =></span> info.id !== file.uid)];</span><br><span class="line"> <span class="keyword">let</span> isCompleted = !fileList.some(<span class="function"><span class="params">info</span> =></span> ![FILE_STATE[<span class="string">'FINISHED'</span>], FILE_STATE[<span class="string">'ERRORED'</span>]].includes(info.state));</span><br><span class="line"> <span class="keyword">this</span>.setState({ </span><br><span class="line"> fileList,</span><br><span class="line"> disabled: fileList.length && !<span class="keyword">this</span>.state.multiple</span><br><span class="line"> }, () => {</span><br><span class="line"> isCompleted && <span class="keyword">this</span>.onCompleted();</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> (<span class="xml"><span class="tag"><<span class="name">div</span>></span></span></span><br><span class="line"> <Dragger {...draggerProps}></span><br><span class="line"> {this.props.children ? this.props.children :</span><br><span class="line"> <div></span><br><span class="line"> <p className="ant-upload-drag-icon"><Icon type="inbox" /></p></span><br><span class="line"> <p className="ant-upload-text">点击选择文件或将文件拖拽到这里</p></span><br><span class="line"> </div>}</span><br><span class="line"> </Dragger></span><br><span class="line"> <div style={{marginTop: '10px'}}></span><br><span class="line"> <Button ghost type="primary" size="small" onClick={this.submit} loading={this.state.isPrepared}>上传</Button></span><br><span class="line"> { this.state.isPrepared && <span style={{marginLeft: '7px'}}>正在读取文件,请稍后!</span>}</span><br><span class="line"> </div></span><br><span class="line"><span class="xml"> <span class="tag"></<span class="name">div</span>></span>);</span></span><br><span class="line"><span class="xml"> }</span></span><br><span class="line"><span class="xml">}</span></span><br><span class="line"><span class="xml"></span></span><br><span class="line"><span class="xml">LargeFileUpload.propTypes = propTypes;</span></span><br><span class="line"><span class="xml"></span></span><br><span class="line"><span class="xml">export default LargeFileUpload;</span></span><br></pre></td></tr></table></figure><p>逻辑是:比如并发的线程是3,我们的文件分成9块传送,<br>第一次:向后台请求我们文件发送的token,以及已经上传成功的块的order假设是Set[0,1,3]。第0块已有跳过。<br>第二步:根据线程数对[1,2,3]order的块重新发送上传请求,第一个请求order:1已经存在服务器了,就把【当前已经存储的数目+并发线程-1】=2+3-1 = 4,order为4的进行传递; order为2的不存在,继续上传。<br>第三步:直到上传完成。</p>]]></content>
<tags>
<tag> js </tag>
<tag> react </tag>
</tags>
</entry>
<entry>
<title>web worker 使用指南</title>
<link href="/2018/03/23/web-worker/"/>
<content type="html"><![CDATA[<p>JavaScript 语言采用的是单线程模型,也就是说,所有任务只能在一个线程上完成,一次只能做一件事。前面的任务没做完,后面的任务只能等着。随着电脑计算能力的增强,尤其是多核 CPU 的出现,单线程带来很大的不便,无法充分发挥计算机的计算能力。</p><p>Web Worker 是HTML5标准的一部分,这一规范定义了一套 API,它允许一段JavaScript程序运行在主线程之外的另外一个线程中。<br>Web Worker 规范中定义了两类工作线程,分别是专用线程Dedicated Worker和共享线程 Shared Worker,其中,Dedicated Worker只能为一个页面所使用,而Shared Worker则可以被多个页面所共享</p><a id="more"></a><p>特点:<br>worker线程的创建的是异步的:主线程代码不会阻塞在这里等待worker线程去加载、执行指定的脚本文件,而是会立即向下继续执行后面代码。<br>postMessage消息交互由内核调度<br>worker线程数据通讯方式:通信是拷贝关系,即是传值而不是地址,子线程对通信内容的修改,不会影响到主线程。</p><p>局限:<br>同源限制。webworker不能跨域加载JS<br>DOM限制。worker内代码不能访问dom,原因是worker有自己独立的global worker环境,不是浏览器window,所以alert(),dom等操作无法进行<br>文件限制。子线程无法读取本地文件,即worker只能加载网络文件。<br>不是每个浏览器都支持这个新特性,且各个浏览器对Worker的实现不大一致,例如FF里允许worker中创建新的worker,而Chrome中就不行</p><h2 id="使用方法"><a href="#使用方法" class="headerlink" title="使用方法"></a>使用方法</h2><p>主线程文件:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> worker = <span class="keyword">new</span> Worker(<span class="string">'work.js'</span>); <span class="comment">//脚本文件即为要执行的任务,不能读取本地文件</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//主线程调用worker.postMessage()方法,向 Worker 发消息。</span></span><br><span class="line">worker.postMessage(<span class="string">'Hello World'</span>); <span class="comment">//数据可以是各种类型,包括二进制</span></span><br><span class="line">worker.postMessage({<span class="attr">method</span>: <span class="string">'echo'</span>, <span class="attr">args</span>: [<span class="string">'Work'</span>]});</span><br><span class="line"></span><br><span class="line"><span class="comment">//监听子线程发回来的消息。</span></span><br><span class="line">worker.onmessage = <span class="function"><span class="keyword">function</span> (<span class="params">event</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'Received message '</span> + event.data);</span><br><span class="line"> doSomething();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">worker.onerror(<span class="function"><span class="keyword">function</span> (<span class="params">event</span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log([</span><br><span class="line"> <span class="string">'ERROR: Line '</span>, e.lineno, <span class="string">' in '</span>, e.filename, <span class="string">': '</span>, e.message</span><br><span class="line"> ].join(<span class="string">''</span>));</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">doSomething</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="comment">// 执行任务</span></span><br><span class="line"> worker.postMessage(<span class="string">'Work done!'</span>);</span><br><span class="line"> worker.terminate(); <span class="comment">//结束</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>worker线程:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">self.addEventListener(<span class="string">'message'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> data = e.data;</span><br><span class="line"> <span class="keyword">switch</span> (data.cmd) {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'start'</span>:</span><br><span class="line"> self.postMessage(<span class="string">'WORKER STARTED: '</span> + data.msg);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'stop'</span>:</span><br><span class="line"> self.postMessage(<span class="string">'WORKER STOPPED: '</span> + data.msg);</span><br><span class="line"> self.close(); <span class="comment">//在 Worker 内部关闭自身。</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> self.postMessage(<span class="string">'Unknown command: '</span> + data.msg);</span><br><span class="line"> };</span><br><span class="line">}, <span class="literal">false</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//Worker 内部如果要加载其他脚本,有一个专门的方法importScripts()。</span></span><br><span class="line">importScripts(<span class="string">'script1.js'</span>, <span class="string">'script2.js'</span>);</span><br></pre></td></tr></table></figure></p><h2 id="注意点"><a href="#注意点" class="headerlink" title="注意点"></a>注意点</h2><p>主线程与 Worker 之间的通信内容,可以是文本,也可以是对象。需要注意的是,这种通信是拷贝关系,即是传值而不是传址,Worker 对通信内容的修改,不会影响到主线程。事实上,浏览器内部的运行机制是,先将通信内容串行化,然后把串行化后的字符串发给 Worker,后者再将它还原。</p><p>主线程与 Worker 之间也可以交换二进制数据,比如 File、Blob、ArrayBuffer 等类型,也可以在线程之间发送。</p><h2 id="同页面worker"><a href="#同页面worker" class="headerlink" title="同页面worker"></a>同页面worker</h2><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE html></span></span><br><span class="line"> <span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">script</span> <span class="attr">id</span>=<span class="string">"worker"</span> <span class="attr">type</span>=<span class="string">"app/worker"</span>></span><span class="undefined"></span></span><br><span class="line"><span class="javascript"> addEventListener(<span class="string">'message'</span>, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span></span><br><span class="line"><span class="javascript"> postMessage(<span class="string">'some message'</span>);</span></span><br><span class="line"><span class="javascript"> }, <span class="literal">false</span>);</span></span><br><span class="line"><span class="undefined"> </span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><p>上面是一段嵌入网页的脚本,注意必须指定script标签的type属性是一个浏览器不认识的值,上例是app/worker。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> blob = <span class="keyword">new</span> Blob([<span class="built_in">document</span>.querySelector(<span class="string">'#worker'</span>).textContent]);</span><br><span class="line"><span class="keyword">var</span> url = <span class="built_in">window</span>.URL.createObjectURL(blob);</span><br><span class="line"><span class="keyword">var</span> worker = <span class="keyword">new</span> Worker(url);</span><br><span class="line"></span><br><span class="line">worker.onmessage = <span class="function"><span class="keyword">function</span> (<span class="params">e</span>) </span>{</span><br><span class="line"> <span class="comment">// e.data === 'some message'</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>上面代码中,先将嵌入网页的脚本代码,转成一个二进制对象,然后为这个二进制对象生成 URL,再让 Worker 加载这个 URL。这样就做到了,主线程和 Worker 的代码都在同一个网页上面。</p>]]></content>
<tags>
<tag> html </tag>
</tags>
</entry>
<entry>
<title>页面布局的简单介绍</title>
<link href="/2018/03/14/layout/"/>
<content type="html"><![CDATA[<h1 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h1><p>我们的页面布局有多种方式,他们布局的具体原理是什么呢?<br><a id="more"></a></p><h2 id="定位(position)和包含块-(Containing-Block)"><a href="#定位(position)和包含块-(Containing-Block)" class="headerlink" title="定位(position)和包含块 (Containing Block)"></a>定位(position)和包含块 (Containing Block)</h2><p>元素设置属性 (width、height、padding、margin和border, top、right、bottom和left)时有个“相对参考系”,所以这些有“相对参考系”的祖先元素,其容纳区域叫包含块。</p><ul><li><p>static: 无法设置top、right、bottom和left这四个偏移属性,width、height、padding、margin相对参考系” 是其包含块(块级祖先元素(一般是父元素)的content box)(如果祖先元素是inline等,往上找直到找到块级元素)</p></li><li><p>relative: 尺寸属性(width、height等)“相对参考系” 是其包含块(块级祖先元素(一般是父元素)的content box)(如果祖先元素是inline等,往上找直到找到块级元素)偏移属性(top、right、bottom和left)的“相对坐标系”则是其在文档流原来的位置。</p></li></ul><ul><li>absolute:”相对参考系”是离这个元素最近的非static(relative、absolute和fixed)定位的祖先元素。若没有,为根元素<html>的包含块(ICB)——viewport(可视窗口)的大小,比如bottom为0时出现在窗口最下面,但是会随着滚动而改变位置。</html></li></ul><ul><li>fixed:”相对参考系”为浏览器窗口,不跟着滚动而改变位置</li></ul><h2 id="文档流"><a href="#文档流" class="headerlink" title="文档流"></a>文档流</h2><p>文档流内的元素,相互尊重:有序排列,彼此识别</p><p>脱离文档流:父元素无法识别,不参与父元素高度计算。会出现“高度坍塌”。</p><ul><li>将元素设置为浮动元素</li><li>将元素设置为absolute、fixed元素</li></ul><h2 id="浮动"><a href="#浮动" class="headerlink" title="浮动"></a>浮动</h2><ul><li><p>浮动起始位置<br>浮动元素(包括左右)的浮动起始位置,为最后一行最左侧的空白位置,而不管空白位置是否能够容纳当前浮动元素;</p></li><li><p>浮动方向<br>左浮动元素的浮动方向为从起始位置向左浮动;<br>右浮动元素的浮动方向为从起始位置向右浮动;</p></li><li><p>浮动结束位置<br>左浮动元素遇到第一个左浮动元素或包含块的最左侧padding时,结束浮动;<br>右浮动元素遇到第一个右浮动元素或包含块的最右侧padding时,结束浮动;</p></li></ul><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"fl"</span> <span class="attr">style</span>=<span class="string">"width: 30%; height: 100px;background: #A71F1F"</span>></span>左浮动元素-1(width: 30%; height: 100px;)<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"fl"</span> <span class="attr">style</span>=<span class="string">"width: 30%; height: 200px;background: #FF92CB"</span>></span>左浮动元素-2(width: 30%; height: 200px;)<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"fl"</span> <span class="attr">style</span>=<span class="string">"width: 30%; height: 100px;background: #8DB1E6"</span>></span>左浮动元素-3(width: 30%; height: 100px;)<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"fl"</span> <span class="attr">style</span>=<span class="string">"width: 30%; height: 100px;background: #B0ED8A"</span>></span>左浮动元素-4(width: 30%; height: 100px;)<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"width: 30%; height: 100px;background: #7242E0;float: right;"</span>></span>右浮动元素-4(width: 30%; height: 100px;)<span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure><p>我们调整各类浮动属性,表现如下:</p><img src="/images/layout_01.png"><img src="/images/layout_02.png"><h2 id="清除浮动"><a href="#清除浮动" class="headerlink" title="清除浮动"></a>清除浮动</h2><p>很多人都已经习惯说清除浮动,但是确切地来说是不准确的。<br>1)清除浮动:清除对应的单词是 clear,对应CSS中的属性是 clear:left | right | both | none;<br>2)闭合浮动:更确切的含义是使浮动元素闭合,从而减少浮动带来的影响。</p><p>清除浮动:<br>clear:取值 left、right和both, 跟当前元素的是否是浮动元素或浮动方向没有任何关系,而取决于其前面声明的浮动元素的浮动方向,若取值和前一个浮动元素的浮动方向不同向,则不会换行</p><p>闭合浮动:</p><ul><li>在想要清除浮动的元素后面加:<div style="clear:both;"></div></li><li>使用 br标签和其自身的 html属性:br 有 clear=“all | left | right | none” 属性</li><li>父元素设置为BFC元素</li><li>使用:after 伪元素<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.clearfix</span><span class="selector-pseudo">:after</span> {</span><br><span class="line"> <span class="attribute">content</span>: <span class="string">""</span>;</span><br><span class="line"> <span class="attribute">display</span>: block;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">clear</span>: both;</span><br><span class="line"> <span class="attribute">visibility</span>: hidden;</span><br><span class="line">}</span><br><span class="line"><span class="comment">/* 为兼容IE6,IE7,因为ie6,ie7不能用after伪类 */</span></span><br><span class="line"><span class="selector-class">.clearfix</span>{ </span><br><span class="line"> <span class="attribute">zoom</span>:<span class="number">1</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><p>清除浮动后margin合并问题:<br>两个浮动元素之间,其垂直方向上的margin不会发生合并<br>非浮动的块级元素和浮动元素之间,其垂直方向上的margin会发生合并</p><img src="/images/layout_03.png"><h2 id="行框"><a href="#行框" class="headerlink" title="行框"></a>行框</h2><p>包含IFC内部的所有子元素的虚拟矩形区域,形成的每一行,称为line box</p><p>行高计算:</p><ol><li>一个元素的行框高度,可由该元素的line-height属性设置;</li><li>一个元素的行框高度,可由不可置换(span、a、label等)子元素的line-height属性 和 内容高度(text-top到text-bottom的垂直距离)影响设置;</li><li>一个元素的行框高度,可由可置换行内元素(如img)或display属性为inline-block、inline-table的这一类inline-block-level子元素的margin box高度和vertical-align属性决定,当vertical-align为top或bottom时,行框的高度达到最小,刚好为子元素的margin box高度;</li></ol><img src="/images/layout_04.png"><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"background: #A71F1F;"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">style</span>=<span class="string">'background: #7242E0;line-height: 64px;'</span>></span>line height 64<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">style</span>=<span class="string">'background: #B0ED8A;line-height: 32p;'</span>></span>line height 32<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">"./timg2.jpg"</span> <span class="attr">style</span>=<span class="string">'height: 300px'</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure><img src="/images/layout_05.png"><p>vertical-align( 设置inline-level元素自身在“行框”内的垂直对齐方式,其控制范围在一行内。较常用的值有top、middle、baseline(默认值)和bottom)<br>vertical-align属性的另一个作用:就是table-cell元素用于控制其内部子元素在垂直方向上的对齐方式,而且这些子元素的类型不受限制,不仅可以是span,而且可以是div,图片也可以。</p>]]></content>
<tags>
<tag> css </tag>
</tags>
</entry>
<entry>
<title>BFC究竟是什么?为啥能解决外边距坍塌问题?</title>
<link href="/2018/03/05/BFC/"/>
<content type="html"><![CDATA[<h1 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h1><p>在做项目的过程中,我发现了浏览器渲染的一个特殊的点:外边距坍塌!<br>只有普通文档流中块框的垂直外边距才会发生外边距合并。行内框、浮动框或绝对定位之间的外边距不会合并。</p><img src="/images/BFC_01.png"><a id="more"></a><p>哭泣!!<br>在父子块状元素的如上图下面部分的样式,如果我们不想让浏览器执行这个默认操作,有如下的解决方法:</p><ol><li>父元素是BFC元素</li><li>父元素拥有border</li><li>父元素拥有padding</li><li>子元素是可置换元素或display为inline-block、inline-table、table-caption的元素</li></ol><p>BFC? 这是什么? 让我们细细道来。</p><p>在 普通流 中的盒子会参与一种格式上下文,这个盒子可能是块盒也可能是行内盒,但不可能同时是块盒又是行内盒。块级盒参与块级格式上下文( BFC ),行内级盒参与行级格式上下文( IFC )。BFC内部元素和外部元素不会互相影响。</p><p>除了BFC和IFC,我们的flex布局参加 FFC( flex formatting context ) , grid布局参加 GFC(grid formatting context)</p><h1 id="BFC"><a href="#BFC" class="headerlink" title="BFC"></a>BFC</h1><p>BFC(Block Formatting Context,属于普通流) ,独立渲染区域,区域内 垂直方向上 相邻的块级元素会发生margin合并</p><h2 id="触发BFC特性"><a href="#触发BFC特性" class="headerlink" title="触发BFC特性"></a>触发BFC特性</h2><ul><li>body 根元素</li><li>浮动元素:float 除 none 以外的值</li><li>position的值不为 static 或 relative (absolute、fixed)</li><li>display 为 inline-block、table-cells、flex、grid</li><li>overflow 除了 visible 以外的值 (hidden、auto、scroll)</li></ul><h2 id="BFC的特性"><a href="#BFC的特性" class="headerlink" title="BFC的特性"></a>BFC的特性</h2><ol><li>子元素的CSS样式不会影响BFC元素外部</li></ol><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">//bfc</span><br><span class="line"><span class="tag"><<span class="name">body</span> ></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin: 20px;overflow: hidden;background-color: #F6B7B7"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin: 20px;"</span>></span>我是块级元素,margin为20<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin: 20px;"</span>></span>我是块级元素,margin为20<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin: 20px;overflow: hidden;background-color: #F6B7B7"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin: 20px;"</span>></span>我是块级元素,margin为20<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin: 20px;"</span>></span>我是块级元素,margin为20<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"></span><br><span class="line">//普通流</span><br><span class="line"><span class="tag"><<span class="name">body</span> ></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"background-color: #F6B7B7"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin: 20px;"</span>></span>我是块级元素,margin为20<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin: 20px;"</span>></span>我是块级元素,margin为20<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"background-color: #F6B7B7"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin: 20px;"</span>></span>我是块级元素,margin为20<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin: 20px;"</span>></span>我是块级元素,margin为20<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br></pre></td></tr></table></figure><img src="/images/BFC_02.png"><p>说明:<br>普通块级元素,其子元素的margin-top,不会隔开自身与父元素(普通块级元素),但是会作用到父元素外部(将父元素和叔伯元素或祖父元素隔开);<br>BFC元素,作为一个独立、封闭的渲染区域,其子元素的margin-top,则会隔开自身与父元素(BFC元素),而不会影响到父元素外部;</p><ol start="2"><li>浮动子元素参与BFC父元素的高度计算,也就是BFC元素能够识别浮动元素(将元素声明为BFC元素,也是clearfix解决父元素塌陷问题的一种常用方法)<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"background-color: #F6B7B7"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin: 20px;float: left;"</span>></span>我是块级元素,margin为20<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin: 20px;overflow: hidden;background-color: #F6B7B7"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin: 20px;float: left;"</span>></span>我是块级元素,margin为20<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure></li></ol><img src="/images/BFC_03.png"><ol start="3"><li>占据文档流的BFC元素(可使用overflow: auto创建),能够识别浮动的兄弟元素;<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin: 20px;background-color: #F6B7B7"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin: 20px;float: left;background-color: #890909"</span>></span>我是浮动元素<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin: 20px;background-color: #FFE0E0"</span>></span>我是普通元素呀呀呀呀呀呀呀呀<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin: 20px;background-color: #F6B7B7"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin: 20px;float: left;background-color: #890909"</span>></span>我是浮动元素<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"margin: 20px;overflow: hidden;background-color: #FFE0E0"</span>></span>我是BFC块呀呀呀呀呀呀呀呀呀<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure></li></ol><img src="/images/BFC_04.png"><ol start="4"><li>占据文档流的BFC元素(可使用overflow: auto创建),width为auto时,会占满当前行的剩余宽度,但margin-left设置没有用;<img src="/images/BFC_05.png">如上图,BFC元素设置了margin-left,但是没有作用。</li></ol><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>BFC会阻止垂直外边距(margin-top、margin-bottom)折叠<br>BFC不会重叠浮动元素<br>BFC高度可以包含浮动</p><p>那么,IFC又是什么?</p><h1 id="IFC"><a href="#IFC" class="headerlink" title="IFC"></a>IFC</h1><p>在IFC中,盒子水平放置,一个接着一个,从包含块的顶部开始。水平margins,borders,和padding在这些盒子中被平分。这些盒子也许通过不同的方式进行对齐:他们的底部和顶部也许被对齐,或者通过文字的基线进行对齐。矩形区域包含着来自一行的盒子叫做line box。line box 的宽度由浮动情况和它的包含块决定。line box的高度由 line-height 的计算结果决定。</p><p>具体介绍可见下一篇~《页面布局的简单介绍》</p>]]></content>
<tags>
<tag> css </tag>
</tags>
</entry>
<entry>
<title>向推箱子进击!</title>
<link href="/2018/01/28/pushbox2/"/>
<content type="html"><![CDATA[<h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>自从,上次完成了一个推箱子的简易版本,自然想实现一个牛掰一点的可以实现多个关卡的推箱子小游戏:<br><img src="/images/pushbox_04.png" width="400"><br><a id="more"></a><br><img src="/images/pushbox_03.png" width="400"></p><h1 id="实现思路"><a href="#实现思路" class="headerlink" title="实现思路"></a>实现思路</h1><p>1、我们首先使用对象保存每个对象的信息:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//level表示关卡</span></span><br><span class="line"><span class="comment">//state为地图,其中0表示无,1表示隔离,2表示空地,3表示目标,4表示箱子,5表示当前位置</span></span><br><span class="line"><span class="comment">//person代表当前位置</span></span><br><span class="line"><span class="comment">//box代表目标位置</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> level = [</span><br><span class="line"> {</span><br><span class="line"> state:[</span><br><span class="line"> [<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">1</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">3</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">5</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">3</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">0</span>]</span><br><span class="line"> ],</span><br><span class="line"> person:{<span class="attr">x</span>:<span class="number">8</span>,<span class="attr">y</span>:<span class="number">3</span>},</span><br><span class="line"> box: [{<span class="attr">x</span>:<span class="number">1</span>,<span class="attr">y</span>:<span class="number">3</span>},{<span class="attr">x</span>:<span class="number">1</span>,<span class="attr">y</span>:<span class="number">4</span>},{<span class="attr">x</span>:<span class="number">2</span>,<span class="attr">y</span>:<span class="number">2</span>},{<span class="attr">x</span>:<span class="number">2</span>,<span class="attr">y</span>:<span class="number">3</span>},{<span class="attr">x</span>:<span class="number">2</span>,<span class="attr">y</span>:<span class="number">4</span>}]</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> state:[</span><br><span class="line"> [<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">0</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">0</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">0</span>,<span class="number">1</span>,<span class="number">5</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">3</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>]</span><br><span class="line"> ],</span><br><span class="line"> person: {<span class="attr">x</span> : <span class="number">2</span>, <span class="attr">y</span> : <span class="number">2</span>},</span><br><span class="line"> box: [{<span class="attr">x</span>:<span class="number">1</span>, <span class="attr">y</span> : <span class="number">4</span>}, {<span class="attr">x</span>:<span class="number">1</span>,<span class="attr">y</span>:<span class="number">5</span>} ,{<span class="attr">x</span>:<span class="number">1</span>, <span class="attr">y</span>:<span class="number">6</span>}]</span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure></p><p>2、对每个关卡新建一个sokoban对象, 这个sokoban对象如何实现推箱子的基本逻辑可以看我的上一篇。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">chapterFirst.onclick = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> levelNum = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">const</span> sokoban = <span class="keyword">new</span> Sokoban(levelNum,ctx,canvas.width,canvas.height);</span><br><span class="line"> sokoban.init();</span><br><span class="line"></span><br><span class="line"> reset.onclick = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> sokoban.reset(level,levelNum);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">chapterSecond.onclick = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> levelNum = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">const</span> sokoban = <span class="keyword">new</span> Sokoban(levelNum,ctx,canvas.width,canvas.height);</span><br><span class="line"> sokoban.init();</span><br><span class="line"></span><br><span class="line"> reset.onclick = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> sokoban.reset(level,levelNum);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>3、从关卡返回首页,可以重新选择关卡<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> backImg = <span class="keyword">new</span> Image();</span><br><span class="line">backImg.src = <span class="string">'images/back.png'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> canvas = <span class="built_in">document</span>.getElementById(<span class="string">'myCanvas'</span>);</span><br><span class="line"><span class="keyword">const</span> ctx = canvas.getContext(<span class="string">'2d'</span>);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">home</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">if</span> (backImg.complete) {</span><br><span class="line"> showHome()</span><br><span class="line"> }<span class="keyword">else</span> {</span><br><span class="line"> showHome()</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">showHome</span>(<span class="params"></span>) </span>{</span><br><span class="line"> ctx.clearRect(<span class="number">0</span>,<span class="number">0</span>,canvas.width,canvas.height);</span><br><span class="line"> ctx.drawImage(backImg,<span class="number">0</span>,<span class="number">0</span>,canvas.width,canvas.height);</span><br><span class="line"> ctx.font = <span class="string">"60px STCaiyun"</span>;</span><br><span class="line"> ctx.textAlign = <span class="string">'center'</span>; </span><br><span class="line"> ctx.strokeStyle = <span class="string">'#DD6C0A'</span>;</span><br><span class="line"> ctx.lineWidth = <span class="number">2</span>;</span><br><span class="line"> ctx.strokeText(<span class="string">'选择关卡'</span>,<span class="number">400</span>,<span class="number">70</span>);</span><br><span class="line"></span><br><span class="line"> chapter.style.display = <span class="string">'block'</span>;</span><br><span class="line"> reset.style.display = <span class="string">"none"</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h1 id="遇到的问题"><a href="#遇到的问题" class="headerlink" title="遇到的问题"></a>遇到的问题</h1><p>重置地图这里我遇到了一个新的问题!<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">reset(level,levelNumber) {</span><br><span class="line"> <span class="keyword">const</span> temp = level[levelNumber];</span><br><span class="line"> <span class="keyword">this</span>.myMap = temp.state;</span><br><span class="line"> <span class="keyword">this</span>.curLoc = temp.person;</span><br><span class="line"> <span class="keyword">this</span>.init();</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>这样是不能重置地图的!<br>因为js中我们的数组对象是引用类型,b = [1,2], a=b 在我们修改a中的元素后,b实际上已经改变了!</p><p>再举一个小小的例子:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a=[{<span class="attr">state</span>:<span class="number">1</span>}, {<span class="attr">state</span>:<span class="number">3</span>}];</span><br><span class="line"><span class="keyword">var</span> b=a.slice(<span class="number">0</span>);</span><br><span class="line">b[<span class="number">0</span>].state= <span class="number">3</span>;</span><br><span class="line">a[<span class="number">0</span>].state <span class="comment">//3</span></span><br></pre></td></tr></table></figure></p><p>使用slice是可以复制一个新的数组,但是我们的a内容里面都是对象(引用类型),所以仍然无用。</p><p>可以使用:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">JSON</span>.parse(<span class="built_in">JSON</span>.stringify(object))</span><br></pre></td></tr></table></figure></p><h1 id="完整实现"><a href="#完整实现" class="headerlink" title="完整实现"></a>完整实现</h1><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE html></span></span><br><span class="line"><span class="tag"><<span class="name">html</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">header</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"utf-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>新推箱子<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">type</span>=<span class="string">"text/css"</span> <span class="attr">href</span>=<span class="string">"push.css"</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">header</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"background"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"control"</span>></span></span><br><span class="line"> <span id="returnHome"><<返回</span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">id</span>=<span class="string">"reset"</span>></span>重置<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="comment"><!-- <span>下一关</span> --></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"chapterF"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">canvas</span> <span class="attr">id</span>=<span class="string">"myCanvas"</span> <span class="attr">width</span>=<span class="string">"800px"</span> <span class="attr">height</span>=<span class="string">"600px"</span>></span><span class="tag"></<span class="name">canvas</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"chapter"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">button</span> <span class="attr">class</span>=<span class="string">"chapterItem"</span> <span class="attr">data</span>=<span class="string">"0"</span>></span></span><br><span class="line"> 第一关</span><br><span class="line"> <span class="tag"></<span class="name">button</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">button</span> <span class="attr">class</span>=<span class="string">"chapterItem"</span> <span class="attr">data</span>=<span class="string">'1'</span>></span></span><br><span class="line"> 第二关</span><br><span class="line"> <span class="tag"></<span class="name">button</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> </span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">script</span> <span class="attr">src</span>=<span class="string">"save.js"</span>></span><span class="undefined"></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> canvas = <span class="built_in">document</span>.getElementById(<span class="string">'myCanvas'</span>);</span><br><span class="line"><span class="keyword">const</span> ctx = canvas.getContext(<span class="string">'2d'</span>);</span><br><span class="line"><span class="keyword">const</span> returnHome = <span class="built_in">document</span>.getElementById(<span class="string">'returnHome'</span>);</span><br><span class="line"><span class="keyword">const</span> reset = <span class="built_in">document</span>.getElementById(<span class="string">'reset'</span>);</span><br><span class="line"><span class="keyword">const</span> chapterFirst = <span class="built_in">document</span>.getElementsByTagName(<span class="string">'button'</span>)[<span class="number">0</span>];</span><br><span class="line"><span class="keyword">const</span> chapterSecond = <span class="built_in">document</span>.getElementsByTagName(<span class="string">'button'</span>)[<span class="number">1</span>];</span><br><span class="line"><span class="keyword">const</span> chapter = <span class="built_in">document</span>.getElementsByClassName(<span class="string">'chapter'</span>)[<span class="number">0</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> levelNum = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> backImg = <span class="keyword">new</span> Image();</span><br><span class="line">backImg.src = <span class="string">'images/back.png'</span>;</span><br><span class="line"></span><br><span class="line">chapterFirst.onclick = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> levelNum = <span class="number">0</span>;</span><br><span class="line"> reset.style.display = <span class="string">'block'</span>;</span><br><span class="line"> chapter.style.display = <span class="string">'none'</span>;</span><br><span class="line"> <span class="keyword">const</span> sokoban = <span class="keyword">new</span> Sokoban(levelNum,ctx,canvas.width,canvas.height);</span><br><span class="line"> sokoban.init();</span><br><span class="line"></span><br><span class="line"> reset.onclick = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> sokoban.reset(level,levelNum);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">chapterSecond.onclick = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> levelNum = <span class="number">1</span>;</span><br><span class="line"> reset.style.display = <span class="string">'block'</span>;</span><br><span class="line"> chapter.style.display = <span class="string">'none'</span>;</span><br><span class="line"> <span class="keyword">const</span> sokoban = <span class="keyword">new</span> Sokoban(levelNum,ctx,canvas.width,canvas.height);</span><br><span class="line"> sokoban.init();</span><br><span class="line"></span><br><span class="line"> reset.onclick = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> sokoban.reset(level,levelNum);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">//level表示关卡</span></span><br><span class="line"><span class="comment">//state为地图,其中0表示无,1表示隔离,2表示空地,3表示目标,4表示箱子,5表示当前位置</span></span><br><span class="line"><span class="comment">//person代表当前位置</span></span><br><span class="line"><span class="comment">//box代表目标位置</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> level = [</span><br><span class="line"> {</span><br><span class="line"> state:[</span><br><span class="line"> [<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">1</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">3</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">5</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">3</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">0</span>]</span><br><span class="line"> ],</span><br><span class="line"> person:{<span class="attr">x</span>:<span class="number">8</span>,<span class="attr">y</span>:<span class="number">3</span>},</span><br><span class="line"> box: [{<span class="attr">x</span>:<span class="number">1</span>,<span class="attr">y</span>:<span class="number">3</span>},{<span class="attr">x</span>:<span class="number">1</span>,<span class="attr">y</span>:<span class="number">4</span>},{<span class="attr">x</span>:<span class="number">2</span>,<span class="attr">y</span>:<span class="number">2</span>},{<span class="attr">x</span>:<span class="number">2</span>,<span class="attr">y</span>:<span class="number">3</span>},{<span class="attr">x</span>:<span class="number">2</span>,<span class="attr">y</span>:<span class="number">4</span>}]</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> state:[</span><br><span class="line"> [<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">0</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">0</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">0</span>,<span class="number">1</span>,<span class="number">5</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">3</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>]</span><br><span class="line"> ],</span><br><span class="line"> person: {<span class="attr">x</span> : <span class="number">2</span>, <span class="attr">y</span> : <span class="number">2</span>},</span><br><span class="line"> box: [{<span class="attr">x</span>:<span class="number">1</span>, <span class="attr">y</span> : <span class="number">4</span>}, {<span class="attr">x</span>:<span class="number">1</span>,<span class="attr">y</span>:<span class="number">5</span>} ,{<span class="attr">x</span>:<span class="number">1</span>, <span class="attr">y</span>:<span class="number">6</span>}]</span><br><span class="line"> }</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> Sokoban = <span class="function"><span class="keyword">function</span>(<span class="params">levelNumber,ctx,width,height</span>)</span>{</span><br><span class="line"> <span class="keyword">const</span> temp = <span class="built_in">JSON</span>.parse(<span class="built_in">JSON</span>.stringify(level[levelNumber]));</span><br><span class="line"> <span class="keyword">this</span>.myMap = temp.state;</span><br><span class="line"> <span class="keyword">this</span>.curLoc = temp.person;</span><br><span class="line"> <span class="keyword">this</span>.targetLoc = temp.box;</span><br><span class="line"> <span class="keyword">this</span>.height = height;</span><br><span class="line"> <span class="keyword">this</span>.width = width;</span><br><span class="line"> <span class="keyword">this</span>.ctx = ctx;</span><br><span class="line"> <span class="keyword">this</span>.imageArray = [<span class="keyword">new</span> Image(),<span class="keyword">new</span> Image(),<span class="keyword">new</span> Image(),<span class="keyword">new</span> Image(),<span class="keyword">new</span> Image(),<span class="keyword">new</span> Image()];</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">Sokoban.prototype = {</span><br><span class="line"> init() {</span><br><span class="line"> <span class="comment">//准备好照片后自动生成地图</span></span><br><span class="line"> <span class="keyword">this</span>.prepareImage();</span><br><span class="line"> <span class="built_in">window</span>.onkeydown = <span class="function">(<span class="params">e</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> keyNum = <span class="built_in">window</span>.event?e.keyCode:e.which;</span><br><span class="line"> e.preventDefault();</span><br><span class="line"> <span class="keyword">this</span>.action(keyNum);</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> prepareImage() {</span><br><span class="line"> <span class="keyword">const</span> imgSrcArray = [<span class="string">'images/1.png'</span>,<span class="string">'images/2.png'</span>,<span class="string">'images/3.png'</span>,<span class="string">'images/4.png'</span>,<span class="string">'images/8.png'</span>,<span class="string">'images/tree.png'</span>];</span><br><span class="line"> <span class="keyword">var</span> imgLength = <span class="keyword">this</span>.imageArray.length;</span><br><span class="line"> <span class="keyword">this</span>.imageArray.forEach(<span class="function">(<span class="params">current,index</span>) =></span> {</span><br><span class="line"> current.src = imgSrcArray[index];</span><br><span class="line"> <span class="comment">//判断图片是否加载完成</span></span><br><span class="line"> <span class="keyword">if</span> (current.complete) {</span><br><span class="line"> <span class="keyword">if</span>(index == imgLength<span class="number">-1</span>) </span><br><span class="line"> <span class="keyword">this</span>.fillCell();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> current.onload = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="keyword">if</span>(index == imgLength<span class="number">-1</span>) <span class="keyword">this</span>.fillCell();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }) </span><br><span class="line"> },</span><br><span class="line"> fillCell() {</span><br><span class="line"> <span class="keyword">const</span> cellWidth = ~~(<span class="keyword">this</span>.width/<span class="keyword">this</span>.myMap[<span class="number">0</span>].length +<span class="number">1</span>);</span><br><span class="line"> <span class="keyword">const</span> cellHeight = ~~(<span class="keyword">this</span>.height/<span class="keyword">this</span>.myMap.length +<span class="number">1</span>);</span><br><span class="line"> <span class="comment">// console.log('单个元素的宽为:' + cellWidth+',高为:'+cellHeight);</span></span><br><span class="line"> <span class="keyword">let</span> mapType = <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">let</span> ptn = <span class="literal">null</span>;</span><br><span class="line"> <span class="comment">//i为第几行,j为第几列</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span> ; i < <span class="keyword">this</span>.myMap.length ; i++){</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> j = <span class="number">0</span> ; j < <span class="keyword">this</span>.myMap[i].length ; j++){</span><br><span class="line"> mapType = <span class="keyword">this</span>.myMap[i][j];</span><br><span class="line"> <span class="comment">// console.log(mapType);</span></span><br><span class="line"> <span class="keyword">switch</span>(mapType){</span><br><span class="line"> <span class="keyword">case</span> <span class="number">0</span>: <span class="keyword">this</span>.ctx.save();</span><br><span class="line"> <span class="keyword">this</span>.ctx.clearRect(j*cellWidth,i*cellHeight,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.ctx.fillStyle = <span class="string">'#333'</span>;</span><br><span class="line"> <span class="keyword">this</span>.ctx.fillRect(j*cellWidth,i*cellHeight,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.ctx.drawImage(<span class="keyword">this</span>.imageArray[<span class="number">5</span>],j*cellWidth,i*cellHeight,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.ctx.restore();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">1</span>: <span class="keyword">this</span>.ctx.save();</span><br><span class="line"> <span class="keyword">this</span>.ctx.clearRect(j*cellWidth,i*cellHeight,cellWidth,cellHeight);</span><br><span class="line"> ptn = <span class="keyword">this</span>.ctx.createPattern(<span class="keyword">this</span>.imageArray[<span class="number">0</span>],<span class="string">'repeat'</span>);</span><br><span class="line"> <span class="keyword">this</span>.ctx.shadowBlur=<span class="number">5</span>;</span><br><span class="line"> <span class="keyword">this</span>.ctx.shadowColor=<span class="string">"black"</span>;</span><br><span class="line"> <span class="keyword">this</span>.ctx.fillStyle = ptn;</span><br><span class="line"> <span class="keyword">this</span>.ctx.fillRect(j*cellWidth,i*cellHeight,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.ctx.restore();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">2</span>: <span class="keyword">this</span>.ctx.save();</span><br><span class="line"> <span class="keyword">this</span>.ctx.clearRect(j*cellWidth,i*cellHeight,cellWidth,cellHeight);</span><br><span class="line"> ptn = <span class="keyword">this</span>.ctx.createPattern(<span class="keyword">this</span>.imageArray[<span class="number">1</span>],<span class="string">'repeat'</span>);</span><br><span class="line"> <span class="keyword">this</span>.ctx.fillStyle = ptn;</span><br><span class="line"> <span class="keyword">this</span>.ctx.fillRect(j*cellWidth,i*cellHeight,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.ctx.restore();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">3</span>: <span class="keyword">this</span>.ctx.save();</span><br><span class="line"> <span class="keyword">this</span>.ctx.clearRect(j*cellWidth,i*cellHeight,cellWidth,cellHeight);</span><br><span class="line"> ptn = <span class="keyword">this</span>.ctx.createPattern(<span class="keyword">this</span>.imageArray[<span class="number">1</span>],<span class="string">'repeat'</span>);</span><br><span class="line"> <span class="keyword">this</span>.ctx.fillStyle = ptn;</span><br><span class="line"> <span class="keyword">this</span>.ctx.fillRect(j*cellWidth,i*cellHeight,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.ctx.drawImage(<span class="keyword">this</span>.imageArray[<span class="number">2</span>],j*cellWidth,i*cellHeight,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.ctx.restore();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">4</span>: <span class="keyword">this</span>.ctx.save();</span><br><span class="line"> <span class="keyword">this</span>.ctx.clearRect(j*cellWidth,i*cellHeight,cellWidth,cellHeight);</span><br><span class="line"> ptn = <span class="keyword">this</span>.ctx.createPattern(<span class="keyword">this</span>.imageArray[<span class="number">1</span>],<span class="string">'repeat'</span>);</span><br><span class="line"> <span class="keyword">this</span>.ctx.fillStyle = ptn;</span><br><span class="line"> <span class="keyword">this</span>.ctx.fillRect(j*cellWidth,i*cellHeight,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.ctx.drawImage(<span class="keyword">this</span>.imageArray[<span class="number">3</span>],j*cellWidth,i*cellHeight,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.ctx.restore();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">5</span>: <span class="keyword">this</span>.ctx.save();</span><br><span class="line"> <span class="keyword">this</span>.ctx.clearRect(j*cellWidth,i*cellHeight,cellWidth,cellHeight);</span><br><span class="line"> ptn = <span class="keyword">this</span>.ctx.createPattern(<span class="keyword">this</span>.imageArray[<span class="number">1</span>],<span class="string">'repeat'</span>);</span><br><span class="line"> <span class="keyword">this</span>.ctx.fillStyle = ptn;</span><br><span class="line"> <span class="keyword">this</span>.ctx.fillRect(j*cellWidth,i*cellHeight,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.ctx.drawImage(<span class="keyword">this</span>.imageArray[<span class="number">4</span>],j*cellWidth,i*cellHeight,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.ctx.restore();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:<span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> action(keyNum) {</span><br><span class="line"> <span class="keyword">let</span> nextLoc = {};</span><br><span class="line"> <span class="keyword">let</span> nextTwoStepLoc = {};</span><br><span class="line"> <span class="keyword">switch</span> (keyNum) {</span><br><span class="line"> <span class="keyword">case</span> <span class="number">37</span>: nextLoc.x = <span class="keyword">this</span>.curLoc.x<span class="number">-1</span>;</span><br><span class="line"> nextLoc.y = <span class="keyword">this</span>.curLoc.y;</span><br><span class="line"> nextLoc.mapType = <span class="keyword">this</span>.myMap[nextLoc.y][nextLoc.x];</span><br><span class="line"> <span class="comment">//下下一步</span></span><br><span class="line"> nextTwoStepLoc.x = <span class="keyword">this</span>.curLoc.x<span class="number">-2</span>;</span><br><span class="line"> nextTwoStepLoc.y = <span class="keyword">this</span>.curLoc.y;</span><br><span class="line"> nextTwoStepLoc.mapType = <span class="keyword">this</span>.myMap[nextTwoStepLoc.y][nextTwoStepLoc.x];</span><br><span class="line"> <span class="comment">//空地和目的地都可以随便走</span></span><br><span class="line"> <span class="keyword">if</span> (nextLoc.mapType == <span class="number">2</span> || nextLoc.mapType == <span class="number">3</span>) {</span><br><span class="line"> <span class="keyword">this</span>.changeLoc(nextLoc);</span><br><span class="line"> <span class="keyword">this</span>.fillCell();</span><br><span class="line"> }<span class="keyword">else</span> <span class="keyword">if</span> (nextLoc.mapType == <span class="number">4</span> && (nextTwoStepLoc.mapType == <span class="number">2</span> || nextTwoStepLoc.mapType == <span class="number">3</span>)) {</span><br><span class="line"> <span class="comment">// console.log('有障碍');</span></span><br><span class="line"> <span class="keyword">this</span>.myMap[nextTwoStepLoc.y][nextTwoStepLoc.x] = <span class="number">4</span>;</span><br><span class="line"> <span class="keyword">this</span>.changeLoc(nextLoc);</span><br><span class="line"> <span class="keyword">this</span>.fillCell();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.check();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">38</span>: nextLoc.x = <span class="keyword">this</span>.curLoc.x;</span><br><span class="line"> nextLoc.y = <span class="keyword">this</span>.curLoc.y<span class="number">-1</span>;</span><br><span class="line"> nextLoc.mapType = <span class="keyword">this</span>.myMap[nextLoc.y][nextLoc.x];</span><br><span class="line"> <span class="comment">//下下一步</span></span><br><span class="line"> nextTwoStepLoc.x = <span class="keyword">this</span>.curLoc.x;</span><br><span class="line"> nextTwoStepLoc.y = <span class="keyword">this</span>.curLoc.y<span class="number">-2</span>;</span><br><span class="line"> nextTwoStepLoc.mapType = <span class="keyword">this</span>.myMap[nextTwoStepLoc.y][nextTwoStepLoc.x];</span><br><span class="line"> <span class="keyword">if</span> (nextLoc.mapType == <span class="number">2</span> || nextLoc.mapType == <span class="number">3</span>) {</span><br><span class="line"> <span class="keyword">this</span>.changeLoc(nextLoc);</span><br><span class="line"> <span class="keyword">this</span>.fillCell();</span><br><span class="line"> }<span class="keyword">else</span> <span class="keyword">if</span> (nextLoc.mapType == <span class="number">4</span> && (nextTwoStepLoc.mapType == <span class="number">2</span> || nextTwoStepLoc.mapType == <span class="number">3</span>)) {</span><br><span class="line"> <span class="comment">// console.log('有障碍');</span></span><br><span class="line"> <span class="keyword">this</span>.myMap[nextTwoStepLoc.y][nextTwoStepLoc.x] = <span class="number">4</span>;</span><br><span class="line"> <span class="keyword">this</span>.changeLoc(nextLoc);</span><br><span class="line"> <span class="keyword">this</span>.fillCell();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.check();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">39</span>: nextLoc.x = <span class="keyword">this</span>.curLoc.x+<span class="number">1</span>;</span><br><span class="line"> nextLoc.y = <span class="keyword">this</span>.curLoc.y;</span><br><span class="line"> nextLoc.mapType = <span class="keyword">this</span>.myMap[nextLoc.y][nextLoc.x];</span><br><span class="line"> <span class="comment">//下下一步</span></span><br><span class="line"> nextTwoStepLoc.x = <span class="keyword">this</span>.curLoc.x+<span class="number">2</span>;</span><br><span class="line"> nextTwoStepLoc.y = <span class="keyword">this</span>.curLoc.y;</span><br><span class="line"> nextTwoStepLoc.mapType = <span class="keyword">this</span>.myMap[nextTwoStepLoc.y][nextTwoStepLoc.x];</span><br><span class="line"> <span class="keyword">if</span> (nextLoc.mapType == <span class="number">2</span> || nextLoc.mapType == <span class="number">3</span>) {</span><br><span class="line"> <span class="keyword">this</span>.changeLoc(nextLoc);</span><br><span class="line"> <span class="keyword">this</span>.fillCell();</span><br><span class="line"> }<span class="keyword">else</span> <span class="keyword">if</span> (nextLoc.mapType == <span class="number">4</span> && (nextTwoStepLoc.mapType == <span class="number">2</span> || nextTwoStepLoc.mapType == <span class="number">3</span>)) {</span><br><span class="line"> <span class="comment">// console.log('有障碍');</span></span><br><span class="line"> <span class="keyword">this</span>.myMap[nextTwoStepLoc.y][nextTwoStepLoc.x] = <span class="number">4</span>;</span><br><span class="line"> <span class="keyword">this</span>.changeLoc(nextLoc);</span><br><span class="line"> <span class="keyword">this</span>.fillCell();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.check();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">40</span>: nextLoc.x = <span class="keyword">this</span>.curLoc.x;</span><br><span class="line"> nextLoc.y = <span class="keyword">this</span>.curLoc.y+<span class="number">1</span>;</span><br><span class="line"> nextLoc.mapType = <span class="keyword">this</span>.myMap[nextLoc.y][nextLoc.x];</span><br><span class="line"> <span class="comment">//下下一步</span></span><br><span class="line"> nextTwoStepLoc.x = <span class="keyword">this</span>.curLoc.x;</span><br><span class="line"> nextTwoStepLoc.y = <span class="keyword">this</span>.curLoc.y+<span class="number">2</span>;</span><br><span class="line"> nextTwoStepLoc.mapType = <span class="keyword">this</span>.myMap[nextTwoStepLoc.y][nextTwoStepLoc.x];</span><br><span class="line"> <span class="keyword">if</span> (nextLoc.mapType == <span class="number">2</span> || nextLoc.mapType == <span class="number">3</span>) {</span><br><span class="line"> <span class="keyword">this</span>.changeLoc(nextLoc);</span><br><span class="line"> <span class="keyword">this</span>.fillCell();</span><br><span class="line"> }<span class="keyword">else</span> <span class="keyword">if</span> (nextLoc.mapType == <span class="number">4</span> && (nextTwoStepLoc.mapType == <span class="number">2</span> || nextTwoStepLoc.mapType == <span class="number">3</span>)) {</span><br><span class="line"> <span class="comment">// console.log('有障碍');</span></span><br><span class="line"> <span class="keyword">this</span>.myMap[nextTwoStepLoc.y][nextTwoStepLoc.x] = <span class="number">4</span>;</span><br><span class="line"> <span class="keyword">this</span>.changeLoc(nextLoc);</span><br><span class="line"> <span class="keyword">this</span>.fillCell();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.check();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>: <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> changeLoc(nextLoc) {</span><br><span class="line"> <span class="keyword">this</span>.myMap[nextLoc.y][nextLoc.x] = <span class="number">5</span>;</span><br><span class="line"> <span class="keyword">this</span>.myMap[<span class="keyword">this</span>.curLoc.y][<span class="keyword">this</span>.curLoc.x] = <span class="number">2</span>;</span><br><span class="line"> <span class="comment">//判断是否踩在目标点上</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="keyword">this</span>.targetLoc.length<span class="number">-1</span> ; i >= <span class="number">0</span> ; i--){</span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">this</span>.curLoc.x == <span class="keyword">this</span>.targetLoc[i].x && <span class="keyword">this</span>.curLoc.y == <span class="keyword">this</span>.targetLoc[i].y) {</span><br><span class="line"> <span class="keyword">this</span>.myMap[<span class="keyword">this</span>.curLoc.y][<span class="keyword">this</span>.curLoc.x] = <span class="number">3</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.curLoc.x = nextLoc.x;</span><br><span class="line"> <span class="keyword">this</span>.curLoc.y = nextLoc.y;</span><br><span class="line"> },</span><br><span class="line"> check(){</span><br><span class="line"> <span class="keyword">let</span> count = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">let</span> tot = <span class="keyword">this</span>.targetLoc.length;</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="keyword">this</span>.targetLoc.length<span class="number">-1</span> ; i >= <span class="number">0</span> ; i--){</span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">this</span>.myMap[<span class="keyword">this</span>.targetLoc[i].y][<span class="keyword">this</span>.targetLoc[i].x] == <span class="number">4</span>) {</span><br><span class="line"> count++;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span>(count == tot) {</span><br><span class="line"> ctx.font = <span class="string">"60px STCaiyun"</span>;</span><br><span class="line"> ctx.textAlign = <span class="string">'center'</span>; </span><br><span class="line"> ctx.strokeStyle = <span class="string">'#FFFFFF'</span>;</span><br><span class="line"> ctx.lineWidth = <span class="number">2</span>;</span><br><span class="line"> ctx.strokeText(<span class="string">'闯关成功!'</span>,<span class="number">400</span>,<span class="number">70</span>);</span><br><span class="line"> };</span><br><span class="line"> },</span><br><span class="line"> reset(level,levelNumber) {</span><br><span class="line"> <span class="keyword">const</span> temp = <span class="built_in">JSON</span>.parse(<span class="built_in">JSON</span>.stringify(level[levelNumber]));</span><br><span class="line"> <span class="keyword">this</span>.myMap = temp.state;</span><br><span class="line"> <span class="keyword">this</span>.curLoc = temp.person;</span><br><span class="line"> <span class="keyword">this</span>.init();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">home</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">if</span> (backImg.complete) {</span><br><span class="line"> ctx.clearRect(<span class="number">0</span>,<span class="number">0</span>,canvas.width,canvas.height);</span><br><span class="line"> ctx.drawImage(backImg,<span class="number">0</span>,<span class="number">0</span>,canvas.width,canvas.height);</span><br><span class="line"> ctx.font = <span class="string">"60px STCaiyun"</span>;</span><br><span class="line"> ctx.textAlign = <span class="string">'center'</span>; </span><br><span class="line"> ctx.strokeStyle = <span class="string">'#DD6C0A'</span>;</span><br><span class="line"> ctx.lineWidth = <span class="number">2</span>;</span><br><span class="line"> ctx.strokeText(<span class="string">'选择关卡'</span>,<span class="number">400</span>,<span class="number">70</span>);</span><br><span class="line"></span><br><span class="line"> chapter.style.display = <span class="string">'block'</span>;</span><br><span class="line"> reset.style.display = <span class="string">"none"</span>;</span><br><span class="line"> }<span class="keyword">else</span> {</span><br><span class="line"> backImg.onload = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> ctx.clearRect(<span class="number">0</span>,<span class="number">0</span>,canvas.width,canvas.height);</span><br><span class="line"> ctx.drawImage(backImg,<span class="number">0</span>,<span class="number">0</span>,canvas.width,canvas.height);</span><br><span class="line"> ctx.font = <span class="string">"60px STCaiyun"</span>;</span><br><span class="line"> ctx.textAlign = <span class="string">'center'</span>; </span><br><span class="line"> ctx.strokeStyle = <span class="string">'#DD6C0A'</span>;</span><br><span class="line"> ctx.lineWidth = <span class="number">2</span>;</span><br><span class="line"> ctx.strokeText(<span class="string">'选择关卡'</span>,<span class="number">400</span>,<span class="number">70</span>);</span><br><span class="line"></span><br><span class="line"> chapter.style.display = <span class="string">'block'</span>;</span><br><span class="line"> reset.style.display = <span class="string">"none"</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">home();</span><br><span class="line"></span><br><span class="line">returnHome.onclick = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> home();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">canvas.onclick = <span class="function">(<span class="params">e</span>) =></span> {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'pageX:'</span>+e.pageX);</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'layerX:'</span>+e.layerX);</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'offsetX:'</span>+e.offsetX);</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'clientX:'</span>+e.clientX);</span><br><span class="line"> <span class="built_in">console</span>.log(e.target.offsetLeft);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*$width: 800px;</span></span><br><span class="line"><span class="comment">$height: 600px;*/</span> </span><br><span class="line"><span class="selector-tag">body</span>{</span><br><span class="line"> <span class="comment">/*text-align: center;*/</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.background</span>{</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">bottom</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">background</span>: <span class="built_in">radial-gradient</span>(#F4A5E8,#F0CCCC,#F9EDF7);</span><br><span class="line">}</span><br><span class="line"><span class="selector-tag">svg</span>{</span><br><span class="line"> <span class="attribute">display</span>: block;</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">590px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">200px</span>;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">80px</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">translate</span>(-50%,-50%);</span><br><span class="line"> <span class="attribute">overflow</span>: hidden;</span><br><span class="line"> <span class="attribute">background-size</span>: cover;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="selector-class">.filtered</span>{</span><br><span class="line"> <span class="attribute">filter</span>: <span class="built_in">url</span>(#filter);</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#0777C7</span>;</span><br><span class="line"> <span class="attribute">fill</span>: <span class="number">#073B40</span>;</span><br><span class="line"> <span class="attribute">font-family</span>: <span class="string">'Oleo Script'</span>, cursive;</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">120px</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.control</span> {</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">800px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">60px</span>;</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">160px</span> auto <span class="number">0</span> auto;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">background</span>: <span class="built_in">-webkit-linear-gradient</span>(left,red, blue); <span class="comment">/* Safari 5.1 - 6.0 */</span></span><br><span class="line"> <span class="attribute">background</span>: <span class="built_in">-o-linear-gradient</span>(right,red, blue); <span class="comment">/* Opera 11.1 - 12.0 */</span></span><br><span class="line"> <span class="attribute">background</span>: <span class="built_in">-moz-linear-gradient</span>(right,red, blue); <span class="comment">/* Firefox 3.6 - 15 */</span></span><br><span class="line"> <span class="attribute">background</span>: <span class="built_in">linear-gradient</span>(to right,#E18E0E,#F6E97E);</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">10px</span> <span class="number">10px</span> <span class="number">0</span> <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">display</span>: flex;</span><br><span class="line"> <span class="attribute">justify-content</span>: space-between;</span><br><span class="line"> </span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.control</span> <span class="selector-tag">span</span>{</span><br><span class="line"> <span class="attribute">font-size</span>:<span class="number">24px</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100px</span>;</span><br><span class="line"> <span class="attribute">color</span>: white;</span><br><span class="line"> <span class="attribute">align-items</span>: center;</span><br><span class="line"> <span class="attribute">text-align</span>: center;</span><br><span class="line"> <span class="attribute">margin</span>:<span class="number">10px</span>;</span><br><span class="line"> <span class="attribute">cursor</span>:pointer;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.control</span> <span class="selector-tag">span</span><span class="selector-pseudo">:hover</span>{</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#DED5D5</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-id">#reset</span>{</span><br><span class="line"> <span class="attribute">display</span>:none; </span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.chapterF</span> {</span><br><span class="line"> <span class="attribute">position</span>: relative;</span><br><span class="line"> <span class="attribute">margin</span>: auto;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">800px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-id">#myCanvas</span> {</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">0</span> auto;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.chapter</span> {</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">250px</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">270px</span>;</span><br><span class="line"> <span class="comment">/*display: none;*/</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.chapterItem</span>{</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">10px</span>;</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="number">#C97D07</span>; <span class="comment">/* Green */</span></span><br><span class="line"> <span class="attribute">border</span>: none;</span><br><span class="line"> <span class="attribute">border-radius</span>: <span class="number">4px</span>;</span><br><span class="line"> <span class="attribute">color</span>: white;</span><br><span class="line"> <span class="attribute">padding</span>: <span class="number">15px</span> <span class="number">32px</span>;</span><br><span class="line"> <span class="attribute">text-align</span>: center;</span><br><span class="line"> <span class="attribute">text-decoration</span>: none;</span><br><span class="line"> <span class="attribute">display</span>: inline-block;</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">16px</span>;</span><br><span class="line"> <span class="attribute">cursor</span>: pointer;</span><br><span class="line"> <span class="attribute">transition</span>: background-color <span class="number">0.5s</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.chapterItem</span><span class="selector-pseudo">:hover</span>{</span><br><span class="line"> <span class="attribute">background-color</span>: <span class="number">#E18E0E</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<tags>
<tag> js </tag>
</tags>
</entry>
<entry>
<title>如何实现canvas绘制推箱子功能?</title>
<link href="/2018/01/24/pushbox/"/>
<content type="html"><![CDATA[<h1 id="实现效果"><a href="#实现效果" class="headerlink" title="实现效果"></a>实现效果</h1><p>推箱子是一款风靡已久的益智小游戏,如何在网页实现推箱子功能?这就不得不说我们html5新增的canvas功能,让我们能对图片等资源可以更好的绘制。<br><img src="/images/pushbox_01.png"><br><a id="more"></a></p><h1 id="设计思路"><a href="#设计思路" class="headerlink" title="设计思路"></a>设计思路</h1><h2 id="素材"><a href="#素材" class="headerlink" title="素材"></a>素材</h2><p>首先我们应该拥有设计素材,博主木有嗷~所以在网上找了一些合适的素材。分别代表墙、草地、应该被推入的位置标志、箱子和我们的人物,如下图。<br><img src="/images/pushbox_02.png"></p><h2 id="实现思路"><a href="#实现思路" class="headerlink" title="实现思路"></a>实现思路</h2><ol><li>生成背景图</li></ol><p>对于页面的显示,我们使用像素化的表示方式。如上图,一个正方块为假设为一个单元,我们的实现图就是一个10 * 7 的地图,我们可以用二维数组来表示它:0表示无,1表示隔离,2表示空地,3表示目标,4表示箱子,5表示当前位置。<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 我们的初始地图可以表示为:</span></span><br><span class="line">[[<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">1</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">3</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">5</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">3</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">0</span>]];</span><br></pre></td></tr></table></figure></p><p>我们生成一个Sokoban对象,用来记录我们的推箱子一个地图的所有内容:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> Sokoban = <span class="function"><span class="keyword">function</span>(<span class="params">boxContext,width,height</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.context = boxContext; <span class="comment">//存储canvas的 ctx 对象 </span></span><br><span class="line"> <span class="keyword">this</span>.width = width;</span><br><span class="line"> <span class="keyword">this</span>.height = height;</span><br><span class="line"> <span class="keyword">this</span>.curLoc = {<span class="attr">x</span>:<span class="number">8</span>,<span class="attr">y</span>:<span class="number">3</span>}; <span class="comment">//是箱子初始的位置</span></span><br><span class="line"> <span class="keyword">this</span>.targetLoc = [{<span class="attr">x</span>:<span class="number">1</span>,<span class="attr">y</span>:<span class="number">3</span>},{<span class="attr">x</span>:<span class="number">1</span>,<span class="attr">y</span>:<span class="number">4</span>},{<span class="attr">x</span>:<span class="number">2</span>,<span class="attr">y</span>:<span class="number">2</span>},{<span class="attr">x</span>:<span class="number">2</span>,<span class="attr">y</span>:<span class="number">3</span>},{<span class="attr">x</span>:<span class="number">2</span>,<span class="attr">y</span>:<span class="number">4</span>}]; <span class="comment">//是应该把箱子推入的位置</span></span><br><span class="line"> <span class="keyword">this</span>.imgArray = [<span class="keyword">new</span> Image(),<span class="keyword">new</span> Image(),<span class="keyword">new</span> Image(),<span class="keyword">new</span> Image(),<span class="keyword">new</span> Image()]; <span class="comment">//我们填充地图的图片资源,现在没有给src</span></span><br><span class="line"> <span class="comment">//0表示无,1表示隔离,2表示空地,3表示目标,4表示箱子,5表示当前位置</span></span><br><span class="line"> <span class="keyword">this</span>.cellArray = [[<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">1</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">3</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">5</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">3</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">0</span>]];</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><ol start="2"><li>页面打开时,根据地图把二维数组用图片进行背景的填充:<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//给sokoban对象附加原型方法</span></span><br><span class="line">paintCells : <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="comment">//0表示无,1表示隔离,2表示空地,3表示目标,4表示箱子,5表示当前位置</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">var</span> i=<span class="number">0</span>,w=<span class="keyword">this</span>.cellArray[<span class="number">0</span>].length,h=<span class="keyword">this</span>.cellArray.length;i<h;i++){</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">var</span> j=<span class="number">0</span>;j<w;j++){</span><br><span class="line"> <span class="keyword">this</span>.fillCell(<span class="keyword">this</span>.cellArray[i][j],j,i);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">},</span><br><span class="line">fillCell : <span class="function"><span class="keyword">function</span>(<span class="params">type,x,y</span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> cellWidth = <span class="keyword">this</span>.width/<span class="keyword">this</span>.cellArray[<span class="number">0</span>].length; <span class="comment">//一个单元的宽度</span></span><br><span class="line"> <span class="keyword">var</span> cellHeight = <span class="keyword">this</span>.height/<span class="keyword">this</span>.cellArray.length; <span class="comment">//一个单元的高度</span></span><br><span class="line"> <span class="keyword">switch</span>(type){</span><br><span class="line"> <span class="keyword">case</span> <span class="number">0</span>: <span class="keyword">this</span>.context.save();</span><br><span class="line"> <span class="keyword">this</span>.context.clearRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.context.fillStyle = <span class="string">'#000'</span>;</span><br><span class="line"> <span class="keyword">this</span>.context.fillRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.context.restore();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">1</span>: </span><br><span class="line"> <span class="keyword">this</span>.context.save();</span><br><span class="line"> <span class="keyword">this</span>.context.clearRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">var</span> ptn = <span class="keyword">this</span>.context.createPattern(<span class="keyword">this</span>.imgArray[<span class="number">0</span>],<span class="string">"repeat"</span>);</span><br><span class="line"> <span class="keyword">this</span>.context.fillStyle = ptn; </span><br><span class="line"> <span class="keyword">this</span>.context.fillRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.context.restore();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">2</span>: <span class="keyword">this</span>.context.save();</span><br><span class="line"> <span class="keyword">this</span>.context.clearRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.context.fillStyle = <span class="keyword">this</span>.context.createPattern(<span class="keyword">this</span>.imgArray[<span class="number">1</span>],<span class="string">"repeat"</span>); </span><br><span class="line"> <span class="keyword">this</span>.context.fillRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.context.restore();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">3</span>: <span class="keyword">this</span>.context.save();</span><br><span class="line"> <span class="keyword">this</span>.context.clearRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.context.fillStyle = <span class="keyword">this</span>.context.createPattern(<span class="keyword">this</span>.imgArray[<span class="number">1</span>],<span class="string">"repeat"</span>); </span><br><span class="line"> <span class="keyword">this</span>.context.fillRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.context.drawImage(<span class="keyword">this</span>.imgArray[<span class="number">2</span>],cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.context.restore(); </span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">4</span>: <span class="keyword">this</span>.context.save();</span><br><span class="line"> <span class="keyword">this</span>.context.clearRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.context.fillStyle = <span class="keyword">this</span>.context.createPattern(<span class="keyword">this</span>.imgArray[<span class="number">1</span>],<span class="string">"repeat"</span>); </span><br><span class="line"> <span class="keyword">this</span>.context.fillRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.context.drawImage(<span class="keyword">this</span>.imgArray[<span class="number">3</span>],cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.context.restore(); </span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">5</span>: <span class="keyword">this</span>.context.save();</span><br><span class="line"> <span class="keyword">this</span>.context.clearRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.context.fillStyle = <span class="keyword">this</span>.context.createPattern(<span class="keyword">this</span>.imgArray[<span class="number">1</span>],<span class="string">"repeat"</span>); </span><br><span class="line"> <span class="keyword">this</span>.context.fillRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.context.drawImage(<span class="keyword">this</span>.imgArray[<span class="number">4</span>],cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span><br><span class="line"> <span class="keyword">this</span>.context.restore(); </span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line">},</span><br></pre></td></tr></table></figure></li></ol><p>3、监听按键事件window.onkeydown ,其中我们的重点是上下左右按键,分别对应的unicode编码为:<br>←:37<br>↑:38<br>→:39<br>↓:40<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.onkeydown = <span class="function"><span class="keyword">function</span>(<span class="params">e</span>)</span>{</span><br><span class="line"> e.preventDefault();</span><br><span class="line"> that.action(e.keyCode); <span class="comment">//处理按键值</span></span><br><span class="line">};</span><br><span class="line"><span class="comment">//处理上下左右的逻辑 </span></span><br><span class="line"> action : <span class="function"><span class="keyword">function</span>(<span class="params">direction</span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> nextLoc = {};</span><br><span class="line"> <span class="keyword">switch</span>(direction){</span><br><span class="line"> <span class="keyword">case</span> <span class="number">38</span>: </span><br><span class="line"> nextLoc.type = <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y<span class="number">-1</span>][<span class="keyword">this</span>.curLoc.x];</span><br><span class="line"> nextLoc.x = <span class="keyword">this</span>.curLoc.x;</span><br><span class="line"> nextLoc.y = <span class="keyword">this</span>.curLoc.y<span class="number">-1</span>;</span><br><span class="line"> <span class="keyword">if</span>( nextLoc.type == <span class="number">2</span> || nextLoc.type == <span class="number">3</span>){</span><br><span class="line"> <span class="keyword">this</span>.move(nextLoc); <span class="comment">//更新移动后的位置</span></span><br><span class="line"> <span class="keyword">this</span>.paintCells(); <span class="comment">//重新绘制地图</span></span><br><span class="line"> }<span class="keyword">else</span> <span class="keyword">if</span>( nextLoc.type == <span class="number">4</span> && <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y<span class="number">-2</span>][<span class="keyword">this</span>.curLoc.x] == <span class="number">2</span> || <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y<span class="number">-2</span>][<span class="keyword">this</span>.curLoc.x] == <span class="number">3</span>){</span><br><span class="line"> <span class="keyword">this</span>.pushBox(nextLoc.x,nextLoc.y<span class="number">-1</span>);</span><br><span class="line"> <span class="keyword">this</span>.move(nextLoc);</span><br><span class="line"> <span class="keyword">this</span>.paintCells();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.checkResult();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">40</span>: </span><br><span class="line"> nextLoc.type = <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y+<span class="number">1</span>][<span class="keyword">this</span>.curLoc.x];</span><br><span class="line"> nextLoc.x = <span class="keyword">this</span>.curLoc.x;</span><br><span class="line"> nextLoc.y = <span class="keyword">this</span>.curLoc.y+<span class="number">1</span>;</span><br><span class="line"> <span class="keyword">if</span>( nextLoc.type == <span class="number">2</span> || nextLoc.type == <span class="number">3</span>){</span><br><span class="line"> <span class="keyword">this</span>.move(nextLoc);</span><br><span class="line"> <span class="keyword">this</span>.paintCells();</span><br><span class="line"> }<span class="keyword">else</span> <span class="keyword">if</span>( nextLoc.type == <span class="number">4</span> && <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y+<span class="number">2</span>][<span class="keyword">this</span>.curLoc.x] == <span class="number">2</span> || <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y+<span class="number">2</span>][<span class="keyword">this</span>.curLoc.x] == <span class="number">3</span>){</span><br><span class="line"> <span class="keyword">this</span>.pushBox(nextLoc.x,nextLoc.y+<span class="number">1</span>);</span><br><span class="line"> <span class="keyword">this</span>.move(nextLoc);</span><br><span class="line"> <span class="keyword">this</span>.paintCells();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.checkResult();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">37</span>: </span><br><span class="line"> nextLoc.type = <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y][<span class="keyword">this</span>.curLoc.x<span class="number">-1</span>];</span><br><span class="line"> nextLoc.x = <span class="keyword">this</span>.curLoc.x<span class="number">-1</span>;</span><br><span class="line"> nextLoc.y = <span class="keyword">this</span>.curLoc.y;</span><br><span class="line"> <span class="keyword">if</span>( nextLoc.type == <span class="number">2</span> || nextLoc.type == <span class="number">3</span>){</span><br><span class="line"> <span class="keyword">this</span>.move(nextLoc);</span><br><span class="line"> <span class="keyword">this</span>.paintCells();</span><br><span class="line"> }<span class="keyword">else</span> <span class="keyword">if</span>( nextLoc.type == <span class="number">4</span> && <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y][<span class="keyword">this</span>.curLoc.x<span class="number">-2</span>] == <span class="number">2</span> || <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y][<span class="keyword">this</span>.curLoc.x<span class="number">-2</span>] == <span class="number">3</span>){</span><br><span class="line"> <span class="keyword">this</span>.pushBox(nextLoc.x<span class="number">-1</span>,nextLoc.y);</span><br><span class="line"> <span class="keyword">this</span>.move(nextLoc);</span><br><span class="line"> <span class="keyword">this</span>.paintCells();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.checkResult();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">39</span>: </span><br><span class="line"> nextLoc.type = <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y][<span class="keyword">this</span>.curLoc.x+<span class="number">1</span>];</span><br><span class="line"> nextLoc.x = <span class="keyword">this</span>.curLoc.x+<span class="number">1</span>;</span><br><span class="line"> nextLoc.y = <span class="keyword">this</span>.curLoc.y;</span><br><span class="line"> <span class="keyword">if</span>( nextLoc.type == <span class="number">2</span> || nextLoc.type == <span class="number">3</span>){</span><br><span class="line"> <span class="keyword">this</span>.move(nextLoc);</span><br><span class="line"> <span class="keyword">this</span>.paintCells();</span><br><span class="line"> }<span class="keyword">else</span> <span class="keyword">if</span>( nextLoc.type == <span class="number">4</span> && <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y][<span class="keyword">this</span>.curLoc.x+<span class="number">2</span>] == <span class="number">2</span> || <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y][<span class="keyword">this</span>.curLoc.x+<span class="number">2</span>] == <span class="number">3</span>){</span><br><span class="line"> <span class="keyword">this</span>.pushBox(nextLoc.x+<span class="number">1</span>,nextLoc.y);</span><br><span class="line"> <span class="keyword">this</span>.move(nextLoc);</span><br><span class="line"> <span class="keyword">this</span>.paintCells();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.checkResult();</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>: <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> pushBox : <span class="function"><span class="keyword">function</span>(<span class="params">boxToX,boxToY</span>)</span>{</span><br><span class="line"> <span class="keyword">this</span>.cellArray[boxToY][boxToX] = <span class="number">4</span>;</span><br><span class="line"> },</span><br><span class="line"> move : <span class="function"><span class="keyword">function</span>(<span class="params">nextLoc</span>)</span>{</span><br><span class="line"> <span class="keyword">this</span>.cellArray[nextLoc.y][nextLoc.x] = <span class="number">5</span>;</span><br><span class="line"> <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y][<span class="keyword">this</span>.curLoc.x] = <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">var</span> i = <span class="keyword">this</span>.targetLoc.length <span class="number">-1</span>; i>=<span class="number">0</span>; i--){</span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">this</span>.targetLoc[i].x == <span class="keyword">this</span>.curLoc.x && <span class="keyword">this</span>.targetLoc[i].y == <span class="keyword">this</span>.curLoc.y){</span><br><span class="line"> <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y][<span class="keyword">this</span>.curLoc.x] = <span class="number">3</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.curLoc.y = nextLoc.y;</span><br><span class="line"> <span class="keyword">this</span>.curLoc.x = nextLoc.x;</span><br><span class="line"> },</span><br></pre></td></tr></table></figure></p><ol start="4"><li><p>检测我们的通关条件,提示“通关”!<br>所有的应该被添位置,都有箱子了,就通关啦</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">checkResult : <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> done = <span class="number">0</span>, total = <span class="keyword">this</span>.targetLoc.length;</span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">var</span> i = total <span class="number">-1</span>; i>=<span class="number">0</span>; i--){</span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.targetLoc[i].y][<span class="keyword">this</span>.targetLoc[i].x] == <span class="number">4</span>){</span><br><span class="line"> done++;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>( done == total ){</span><br><span class="line"> <span class="built_in">window</span>.onkeydown = <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">this</span>.context.save();</span><br><span class="line"> <span class="keyword">this</span>.context.font=<span class="string">"40px Verdana"</span>;</span><br><span class="line"> <span class="keyword">this</span>.context.textAlign = <span class="string">"center"</span>;</span><br><span class="line"> <span class="comment">// 创建渐变</span></span><br><span class="line"> <span class="keyword">var</span> gradient=<span class="keyword">this</span>.context.createLinearGradient(<span class="number">0</span>,<span class="number">0</span>,<span class="keyword">this</span>.width,<span class="keyword">this</span>.height);</span><br><span class="line"> gradient.addColorStop(<span class="string">"0.3"</span>,<span class="string">"red"</span>);</span><br><span class="line"> gradient.addColorStop(<span class="string">"0.5"</span>,<span class="string">"blue"</span>);</span><br><span class="line"> gradient.addColorStop(<span class="string">"0.7"</span>,<span class="string">"yellow"</span>);</span><br><span class="line"> <span class="comment">// 用渐变填色</span></span><br><span class="line"> <span class="keyword">this</span>.context.fillStyle = gradient;</span><br><span class="line"> <span class="keyword">this</span>.context.fillText(<span class="string">"Congratulations!!"</span>,<span class="keyword">this</span>.width/<span class="number">2</span>,<span class="keyword">this</span>.height/<span class="number">2</span>);</span><br><span class="line"> <span class="keyword">this</span>.context.restore();</span><br><span class="line"> }</span><br><span class="line"> },</span><br></pre></td></tr></table></figure></li><li><p>重置地图<br>如果用户发现走到了死胡同,这时候需要重置地图。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">restart : <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">this</span>.curLoc = {<span class="attr">x</span>:<span class="number">8</span>,<span class="attr">y</span>:<span class="number">3</span>};</span><br><span class="line"> <span class="comment">//0表示无,1表示隔离,2表示空地,3表示目标,4表示箱子,5表示当前位置</span></span><br><span class="line"> <span class="keyword">this</span>.cellArray = [[<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">1</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">3</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">5</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">3</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">4</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">1</span>],</span><br><span class="line"> [<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">2</span>,<span class="number">1</span>,<span class="number">0</span>],</span><br><span class="line"> [<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">0</span>]];</span><br><span class="line"> sokoban.init();</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ol><h2 id="整体实现"><a href="#整体实现" class="headerlink" title="整体实现"></a>整体实现</h2><p>以下,献上完整代码:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE html></span></span><br><span class="line"><span class="tag"><<span class="name">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"utf-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>推箱子<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">canvas</span> <span class="attr">id</span>=<span class="string">"boxCanvas"</span> <span class="attr">width</span>=<span class="string">"500px"</span> <span class="attr">height</span>=<span class="string">"350px"</span>></span><span class="tag"></<span class="name">canvas</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"width:500px;text-align:center;margin-top:10px"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">button</span> <span class="attr">style</span>=<span class="string">"width:90px;height:32px;"</span> <span class="attr">onclick</span>=<span class="string">"restartGame();"</span>></span>Restart<span class="tag"></<span class="name">button</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span>></span><span class="undefined"></span></span><br><span class="line"><span class="javascript"><span class="function"><span class="keyword">function</span> <span class="title">restartGame</span>(<span class="params"></span>)</span>{</span></span><br><span class="line"><span class="undefined"> sokoban.restart();</span></span><br><span class="line"><span class="undefined">}</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"><span class="keyword">var</span> Sokoban = <span class="function"><span class="keyword">function</span>(<span class="params">boxContext,width,height</span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context = boxContext;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.width = width;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.height = height;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.curLoc = {<span class="attr">x</span>:<span class="number">8</span>,<span class="attr">y</span>:<span class="number">3</span>};</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.targetLoc = [{<span class="attr">x</span>:<span class="number">1</span>,<span class="attr">y</span>:<span class="number">3</span>},{<span class="attr">x</span>:<span class="number">1</span>,<span class="attr">y</span>:<span class="number">4</span>},{<span class="attr">x</span>:<span class="number">2</span>,<span class="attr">y</span>:<span class="number">2</span>},{<span class="attr">x</span>:<span class="number">2</span>,<span class="attr">y</span>:<span class="number">3</span>},{<span class="attr">x</span>:<span class="number">2</span>,<span class="attr">y</span>:<span class="number">4</span>}];</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.imgArray = [<span class="keyword">new</span> Image(),<span class="keyword">new</span> Image(),<span class="keyword">new</span> Image(),<span class="keyword">new</span> Image(),<span class="keyword">new</span> Image()];</span></span><br><span class="line"><span class="javascript"> <span class="comment">//0表示无,1表示隔离,2表示空地,3表示目标,4表示箱子,5表示当前位置</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.cellArray = [[<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">0</span>],</span></span><br><span class="line"><span class="undefined"> [0,1,1,1,2,2,2,2,1,0],</span></span><br><span class="line"><span class="undefined"> [1,1,3,2,4,1,1,2,1,1],</span></span><br><span class="line"><span class="undefined"> [1,3,3,4,2,4,2,2,5,1],</span></span><br><span class="line"><span class="undefined"> [1,3,3,2,4,2,4,2,1,1],</span></span><br><span class="line"><span class="undefined"> [1,1,1,1,1,1,2,2,1,0],</span></span><br><span class="line"><span class="undefined"> [0,0,0,0,0,1,1,1,1,0]];</span></span><br><span class="line"><span class="undefined">}</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="undefined">Sokoban.prototype = {</span></span><br><span class="line"><span class="javascript"> fillCell : <span class="function"><span class="keyword">function</span>(<span class="params">type,x,y</span>)</span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> cellWidth = <span class="keyword">this</span>.width/<span class="keyword">this</span>.cellArray[<span class="number">0</span>].length;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> cellHeight = <span class="keyword">this</span>.height/<span class="keyword">this</span>.cellArray.length;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">switch</span>(type){</span></span><br><span class="line"><span class="javascript"> <span class="keyword">case</span> <span class="number">0</span>: <span class="keyword">this</span>.context.save();</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.clearRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.fillStyle = <span class="string">'#000'</span>;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.fillRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.restore();</span></span><br><span class="line"><span class="javascript"> <span class="keyword">break</span>;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">case</span> <span class="number">1</span>: </span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.save();</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.clearRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> ptn = <span class="keyword">this</span>.context.createPattern(<span class="keyword">this</span>.imgArray[<span class="number">0</span>],<span class="string">"repeat"</span>);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.fillStyle = ptn; </span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.fillRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.restore();</span></span><br><span class="line"><span class="javascript"> <span class="keyword">break</span>;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">case</span> <span class="number">2</span>: <span class="keyword">this</span>.context.save();</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.clearRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.fillStyle = <span class="keyword">this</span>.context.createPattern(<span class="keyword">this</span>.imgArray[<span class="number">1</span>],<span class="string">"repeat"</span>); </span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.fillRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.restore();</span></span><br><span class="line"><span class="javascript"> <span class="keyword">break</span>;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">case</span> <span class="number">3</span>: <span class="keyword">this</span>.context.save();</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.clearRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.fillStyle = <span class="keyword">this</span>.context.createPattern(<span class="keyword">this</span>.imgArray[<span class="number">1</span>],<span class="string">"repeat"</span>); </span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.fillRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.drawImage(<span class="keyword">this</span>.imgArray[<span class="number">2</span>],cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.restore(); </span></span><br><span class="line"><span class="javascript"> <span class="keyword">break</span>;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">case</span> <span class="number">4</span>: <span class="keyword">this</span>.context.save();</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.clearRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.fillStyle = <span class="keyword">this</span>.context.createPattern(<span class="keyword">this</span>.imgArray[<span class="number">1</span>],<span class="string">"repeat"</span>); </span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.fillRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.drawImage(<span class="keyword">this</span>.imgArray[<span class="number">3</span>],cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.restore(); </span></span><br><span class="line"><span class="javascript"> <span class="keyword">break</span>;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">case</span> <span class="number">5</span>: <span class="keyword">this</span>.context.save();</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.clearRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.fillStyle = <span class="keyword">this</span>.context.createPattern(<span class="keyword">this</span>.imgArray[<span class="number">1</span>],<span class="string">"repeat"</span>); </span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.fillRect(cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.drawImage(<span class="keyword">this</span>.imgArray[<span class="number">4</span>],cellWidth*x,cellHeight*y,cellWidth,cellHeight);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.restore(); </span></span><br><span class="line"><span class="javascript"> <span class="keyword">break</span>;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> },</span></span><br><span class="line"><span class="javascript"> prepareImage : <span class="function"><span class="keyword">function</span>(<span class="params">callback</span>)</span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> imgSrcArray = [<span class="string">'images/1.png'</span>,<span class="string">'images/2.png'</span>,<span class="string">'images/3.png'</span>,<span class="string">'images/4.png'</span>,<span class="string">'images/5.png'</span>];</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> that = <span class="keyword">this</span>;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">for</span>(<span class="keyword">var</span> i=<span class="number">0</span>,len=<span class="keyword">this</span>.imgArray.length,loading=len<span class="number">-1</span>;i<<span class="number">5</span>;i++){</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.imgArray[i].src = imgSrcArray[i];</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.imgArray[i].onload = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span></span><br><span class="line"><span class="undefined"> len--;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span>(!len){</span></span><br><span class="line"><span class="undefined"> callback.call(that);</span></span><br><span class="line"><span class="javascript"> <span class="built_in">window</span>.onkeydown = <span class="function"><span class="keyword">function</span>(<span class="params">e</span>)</span>{</span></span><br><span class="line"><span class="undefined"> e.preventDefault();</span></span><br><span class="line"><span class="undefined"> that.action(e.keyCode);</span></span><br><span class="line"><span class="undefined"> };</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> };</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> },</span></span><br><span class="line"><span class="javascript"> paintCells : <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span></span><br><span class="line"><span class="javascript"> <span class="comment">//0表示无,1表示隔离,2表示空地,3表示目标,4表示箱子,5表示当前位置</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">for</span>(<span class="keyword">var</span> i=<span class="number">0</span>,w=<span class="keyword">this</span>.cellArray[<span class="number">0</span>].length,h=<span class="keyword">this</span>.cellArray.length;i<h;i++){</span></span><br><span class="line"><span class="javascript"> <span class="keyword">for</span>(<span class="keyword">var</span> j=<span class="number">0</span>;j<w;j++){</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.fillCell(<span class="keyword">this</span>.cellArray[i][j],j,i);</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> },</span></span><br><span class="line"><span class="javascript"> init : <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.prepareImage(<span class="keyword">this</span>.paintCells);</span></span><br><span class="line"><span class="undefined"> },</span></span><br><span class="line"><span class="javascript"> action : <span class="function"><span class="keyword">function</span>(<span class="params">direction</span>)</span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> nextLoc = {};</span></span><br><span class="line"><span class="javascript"> <span class="keyword">switch</span>(direction){</span></span><br><span class="line"><span class="javascript"> <span class="keyword">case</span> <span class="number">38</span>: </span></span><br><span class="line"><span class="javascript"> nextLoc.type = <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y<span class="number">-1</span>][<span class="keyword">this</span>.curLoc.x];</span></span><br><span class="line"><span class="javascript"> nextLoc.x = <span class="keyword">this</span>.curLoc.x;</span></span><br><span class="line"><span class="javascript"> nextLoc.y = <span class="keyword">this</span>.curLoc.y<span class="number">-1</span>;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span>( nextLoc.type == <span class="number">2</span> || nextLoc.type == <span class="number">3</span>){</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.move(nextLoc);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.paintCells();</span></span><br><span class="line"><span class="javascript"> }<span class="keyword">else</span> <span class="keyword">if</span>( nextLoc.type == <span class="number">4</span> && <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y<span class="number">-2</span>][<span class="keyword">this</span>.curLoc.x] == <span class="number">2</span> || <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y<span class="number">-2</span>][<span class="keyword">this</span>.curLoc.x] == <span class="number">3</span>){</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.pushBox(nextLoc.x,nextLoc.y<span class="number">-1</span>);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.move(nextLoc);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.paintCells();</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.checkResult();</span></span><br><span class="line"><span class="javascript"> <span class="keyword">break</span>;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">case</span> <span class="number">40</span>: </span></span><br><span class="line"><span class="javascript"> nextLoc.type = <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y+<span class="number">1</span>][<span class="keyword">this</span>.curLoc.x];</span></span><br><span class="line"><span class="javascript"> nextLoc.x = <span class="keyword">this</span>.curLoc.x;</span></span><br><span class="line"><span class="javascript"> nextLoc.y = <span class="keyword">this</span>.curLoc.y+<span class="number">1</span>;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span>( nextLoc.type == <span class="number">2</span> || nextLoc.type == <span class="number">3</span>){</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.move(nextLoc);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.paintCells();</span></span><br><span class="line"><span class="javascript"> }<span class="keyword">else</span> <span class="keyword">if</span>( nextLoc.type == <span class="number">4</span> && <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y+<span class="number">2</span>][<span class="keyword">this</span>.curLoc.x] == <span class="number">2</span> || <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y+<span class="number">2</span>][<span class="keyword">this</span>.curLoc.x] == <span class="number">3</span>){</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.pushBox(nextLoc.x,nextLoc.y+<span class="number">1</span>);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.move(nextLoc);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.paintCells();</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.checkResult();</span></span><br><span class="line"><span class="javascript"> <span class="keyword">break</span>;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">case</span> <span class="number">37</span>: </span></span><br><span class="line"><span class="javascript"> nextLoc.type = <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y][<span class="keyword">this</span>.curLoc.x<span class="number">-1</span>];</span></span><br><span class="line"><span class="javascript"> nextLoc.x = <span class="keyword">this</span>.curLoc.x<span class="number">-1</span>;</span></span><br><span class="line"><span class="javascript"> nextLoc.y = <span class="keyword">this</span>.curLoc.y;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span>( nextLoc.type == <span class="number">2</span> || nextLoc.type == <span class="number">3</span>){</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.move(nextLoc);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.paintCells();</span></span><br><span class="line"><span class="javascript"> }<span class="keyword">else</span> <span class="keyword">if</span>( nextLoc.type == <span class="number">4</span> && <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y][<span class="keyword">this</span>.curLoc.x<span class="number">-2</span>] == <span class="number">2</span> || <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y][<span class="keyword">this</span>.curLoc.x<span class="number">-2</span>] == <span class="number">3</span>){</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.pushBox(nextLoc.x<span class="number">-1</span>,nextLoc.y);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.move(nextLoc);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.paintCells();</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.checkResult();</span></span><br><span class="line"><span class="javascript"> <span class="keyword">break</span>;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">case</span> <span class="number">39</span>: </span></span><br><span class="line"><span class="javascript"> nextLoc.type = <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y][<span class="keyword">this</span>.curLoc.x+<span class="number">1</span>];</span></span><br><span class="line"><span class="javascript"> nextLoc.x = <span class="keyword">this</span>.curLoc.x+<span class="number">1</span>;</span></span><br><span class="line"><span class="javascript"> nextLoc.y = <span class="keyword">this</span>.curLoc.y;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span>( nextLoc.type == <span class="number">2</span> || nextLoc.type == <span class="number">3</span>){</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.move(nextLoc);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.paintCells();</span></span><br><span class="line"><span class="javascript"> }<span class="keyword">else</span> <span class="keyword">if</span>( nextLoc.type == <span class="number">4</span> && <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y][<span class="keyword">this</span>.curLoc.x+<span class="number">2</span>] == <span class="number">2</span> || <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y][<span class="keyword">this</span>.curLoc.x+<span class="number">2</span>] == <span class="number">3</span>){</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.pushBox(nextLoc.x+<span class="number">1</span>,nextLoc.y);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.move(nextLoc);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.paintCells();</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.checkResult();</span></span><br><span class="line"><span class="javascript"> <span class="keyword">break</span>;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">default</span>: <span class="keyword">break</span>;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> },</span></span><br><span class="line"><span class="javascript"> pushBox : <span class="function"><span class="keyword">function</span>(<span class="params">boxToX,boxToY</span>)</span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.cellArray[boxToY][boxToX] = <span class="number">4</span>;</span></span><br><span class="line"><span class="undefined"> },</span></span><br><span class="line"><span class="javascript"> move : <span class="function"><span class="keyword">function</span>(<span class="params">nextLoc</span>)</span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.cellArray[nextLoc.y][nextLoc.x] = <span class="number">5</span>;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y][<span class="keyword">this</span>.curLoc.x] = <span class="number">2</span>;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">for</span>(<span class="keyword">var</span> i = <span class="keyword">this</span>.targetLoc.length <span class="number">-1</span>; i>=<span class="number">0</span>; i--){</span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span>(<span class="keyword">this</span>.targetLoc[i].x == <span class="keyword">this</span>.curLoc.x && <span class="keyword">this</span>.targetLoc[i].y == <span class="keyword">this</span>.curLoc.y){</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.curLoc.y][<span class="keyword">this</span>.curLoc.x] = <span class="number">3</span>;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.curLoc.y = nextLoc.y;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.curLoc.x = nextLoc.x;</span></span><br><span class="line"><span class="undefined"> },</span></span><br><span class="line"><span class="javascript"> checkResult : <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> done = <span class="number">0</span>, total = <span class="keyword">this</span>.targetLoc.length;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">for</span>(<span class="keyword">var</span> i = total <span class="number">-1</span>; i>=<span class="number">0</span>; i--){</span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span>(<span class="keyword">this</span>.cellArray[<span class="keyword">this</span>.targetLoc[i].y][<span class="keyword">this</span>.targetLoc[i].x] == <span class="number">4</span>){</span></span><br><span class="line"><span class="undefined"> done++;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span>( done == total ){</span></span><br><span class="line"><span class="javascript"> <span class="built_in">window</span>.onkeydown = <span class="literal">null</span>;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.save();</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.font=<span class="string">"40px Verdana"</span>;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.textAlign = <span class="string">"center"</span>;</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 创建渐变</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> gradient=<span class="keyword">this</span>.context.createLinearGradient(<span class="number">0</span>,<span class="number">0</span>,<span class="keyword">this</span>.width,<span class="keyword">this</span>.height);</span></span><br><span class="line"><span class="javascript"> gradient.addColorStop(<span class="string">"0.3"</span>,<span class="string">"red"</span>);</span></span><br><span class="line"><span class="javascript"> gradient.addColorStop(<span class="string">"0.5"</span>,<span class="string">"blue"</span>);</span></span><br><span class="line"><span class="javascript"> gradient.addColorStop(<span class="string">"0.7"</span>,<span class="string">"yellow"</span>);</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 用渐变填色</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.fillStyle = gradient;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.fillText(<span class="string">"Congratulations!!"</span>,<span class="keyword">this</span>.width/<span class="number">2</span>,<span class="keyword">this</span>.height/<span class="number">2</span>);</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.context.restore();</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> },</span></span><br><span class="line"><span class="javascript"> restart : <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.curLoc = {<span class="attr">x</span>:<span class="number">8</span>,<span class="attr">y</span>:<span class="number">3</span>};</span></span><br><span class="line"><span class="javascript"> <span class="comment">//0表示无,1表示隔离,2表示空地,3表示目标,4表示箱子,5表示当前位置</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">this</span>.cellArray = [[<span class="number">0</span>,<span class="number">0</span>,<span class="number">0</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">1</span>,<span class="number">0</span>],</span></span><br><span class="line"><span class="undefined"> [0,1,1,1,2,2,2,2,1,0],</span></span><br><span class="line"><span class="undefined"> [1,1,3,2,4,1,1,2,1,1],</span></span><br><span class="line"><span class="undefined"> [1,3,3,4,2,4,2,2,5,1],</span></span><br><span class="line"><span class="undefined"> [1,3,3,2,4,2,4,2,1,1],</span></span><br><span class="line"><span class="undefined"> [1,1,1,1,1,1,2,2,1,0],</span></span><br><span class="line"><span class="undefined"> [0,0,0,0,0,1,1,1,1,0]];</span></span><br><span class="line"><span class="undefined"> sokoban.init();</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined">};</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"><span class="keyword">var</span> boxCanvas = <span class="built_in">document</span>.getElementById(<span class="string">"boxCanvas"</span>);</span></span><br><span class="line"><span class="javascript"><span class="keyword">var</span> sokoban = <span class="keyword">new</span> Sokoban(boxCanvas.getContext(<span class="string">'2d'</span>),boxCanvas.width,boxCanvas.height);</span></span><br><span class="line"><span class="undefined">sokoban.init();</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="undefined"></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><p>你学会了吗?</p>]]></content>
<tags>
<tag> js </tag>
</tags>
</entry>
<entry>
<title>用js获取浏览器位置属性详解(offsetLeft, pageX, clientX, layerX, scrollLeft等等)</title>
<link href="/2018/01/14/position-attr/"/>
<content type="html"><![CDATA[<p>在实际运用中,我们经常要获取鼠标点击事件的位置和元素的位置信息。针对页面、浏览器、屏幕,各大厂商也有自己设置的各类属性。所以他们究竟有什么不同呢?哪个属性才是我们想要的呢?</p><h1 id="event属性"><a href="#event属性" class="headerlink" title="event属性"></a>event属性</h1><img src="/images/position_attr_01.png"><p>观察上图,对于X坐标的值,我们一个鼠标事件有clientX,layerX,movementX,offsetX,pageX,screenX,x 多个属性值。它们分别代表什么呢?</p><a id="more"></a><ul><li>screenX 和 screenY<br>鼠标在屏幕上的坐标。screenX,screenY的最大值不会超过屏幕分辨率。作者有两个屏幕,分辨率为1920 * 1080,所以上图点击浏览器的body左上角时,x值为1920。</li><li>clientX 和 clientY<br>相对于浏览器窗口可视区域的X,Y坐标(窗口坐标),鼠标滚动后原来的起始点就不是起始点,而是以以浏览器滑动条此刻的滑动到的位置为参考点。<img src="/images/position_attr_03.png"></li><li>pageX 和 pageY<br>鼠标在页面上的位置。从页面左上角开始,即是以页面为参考点,不随滑动条移动而变化。IE中不支持,取而代之的是event.x 和 event.y<br>e.pageX = e.clientX + 水平滚动的距离。</li><li>offsetX 和 offsetY<br>offset意为偏移量,是被点击的元素距左上角为参考原点的长度(不包括border), 如果鼠标进入到border区域,为返回负值。</li><li>layerX 和 layerY<img src="/images/position_attr_05.jpg">上图计算公式有点不正确:<br>Chrome: 相对于参考点的长度(包括border)e.layerX = e.offsetX + border-left-width<br>IE: e.layerX = e.pageX</li><li>x 和 y<img src="/images/position_attr_06.jpg"></li></ul><p>浏览器的支持情况如下:<br><img src="/images/position_attr_04.png"></p><hr><h1 id="DOM元素"><a href="#DOM元素" class="headerlink" title="DOM元素"></a>DOM元素</h1><p>前面介绍的event对象的位置属性,而我们的dom元素也有相应的属性。<br><img src="/images/placeholder.png" data-src="/images/position_attr_07.png" class="lazyload"><br><img src="/images/placeholder.png" data-src="/images/position_attr_08.png" class="lazyload"><br><img src="/images/placeholder.png" data-src="/images/position_attr_09.png" class="lazyload"><br>我们绘制了一个正方体,宽高200px,border为5px。</p><ul><li>clientLeft / clientTop<br>元素边框border的大小</li><li>clientWidth / clientHeight<br>元素的可见宽度或高度。等于padding+width</li><li>offsetWidth / offsetHeight<br>元素相对父元素的偏移宽度或高度。等于border+padding+width</li><li>scrollWidth / scrollHeight<br>元素的宽度且包括滚动部分。(不包括border)</li><li>offsetLeft / offsetTop<br>该DOM对象的层级关系中离该对象最近的,设置了position的父对象”中的位置。(从最左最上到对象的border的距离,不包括border)</li><li>scrollLeft / scrollTop<br>置当前横向滚动条的坐标值(已经滚动了多少)<img src="/images/placeholder.png" data-src="/images/position_attr_10.gif" class="lazyload"></li></ul>]]></content>
<tags>
<tag> js </tag>
</tags>
</entry>
<entry>
<title>关于js“继承”的三二事</title>
<link href="/2018/01/10/extend/"/>
<content type="html"><![CDATA[<p>这一篇将介绍js继承的几种方式。<br><a id="more"></a></p><h2 id="原型继承"><a href="#原型继承" class="headerlink" title="原型继承"></a>原型继承</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> Super = <span class="function"><span class="keyword">function</span>(<span class="params">name = <span class="string">'eric'</span></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line"> <span class="keyword">this</span>.getName = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.name;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">Super.prototype.hello = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> alert(<span class="string">'Hello, '</span> + <span class="keyword">this</span>.name + <span class="string">'!'</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> Sub = <span class="function"><span class="keyword">function</span>(<span class="params">sex = <span class="string">'male'</span></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.sex = sex;</span><br><span class="line">}</span><br><span class="line">Sub.prototype = <span class="keyword">new</span> Super(<span class="string">'eric'</span>); <span class="comment">//通过改变原型对象实现继承</span></span><br><span class="line">Sub.prototype.constructor = Sub <span class="comment">// 保持构造函数和原型对象的完整性</span></span><br><span class="line"><span class="keyword">let</span> sub1 = <span class="keyword">new</span> Sub(<span class="string">'male'</span>)</span><br><span class="line"> sub2 = <span class="keyword">new</span> Sub(<span class="string">'female'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(sub1.getName()); <span class="comment">// eric</span></span><br><span class="line"><span class="built_in">console</span>.log(sub1.hasOwnProperty(<span class="string">'name'</span>)) <span class="comment">// false 说明是继承而来的属性</span></span><br><span class="line"><span class="built_in">console</span>.log(sub1.getName === sub2.getName) <span class="comment">// true,复用了方法</span></span><br><span class="line"><span class="built_in">console</span>.log(sub1.hello()) <span class="comment">//正常显示</span></span><br></pre></td></tr></table></figure><p>可以看出,父元素的方法和属性都得到了复用。但是子类实例没有自己的属性。父元素的原型也被继承了。</p><h2 id="构造函数继承"><a href="#构造函数继承" class="headerlink" title="构造函数继承"></a>构造函数继承</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> Super = <span class="function"><span class="keyword">function</span>(<span class="params">name = <span class="string">'eric'</span></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line"> <span class="keyword">this</span>.getName = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.name;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">Super.prototype.hello = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> alert(<span class="string">'Hello, '</span> + <span class="keyword">this</span>.name + <span class="string">'!'</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> Sub = <span class="function"><span class="keyword">function</span>(<span class="params">name, sex</span>) </span>{</span><br><span class="line"> Super.call(<span class="keyword">this</span>, name);</span><br><span class="line"> <span class="keyword">this</span>.sex = sex;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> sub1 = <span class="keyword">new</span> Sub(<span class="string">'eric'</span>, <span class="string">'male'</span>);</span><br><span class="line"><span class="keyword">let</span> sub2 = <span class="keyword">new</span> Sub(<span class="string">'ada'</span>, <span class="string">'female'</span>);</span><br><span class="line"><span class="built_in">console</span>.log(sub1.name) <span class="comment">// 'eric'</span></span><br><span class="line"><span class="built_in">console</span>.log(sub1.hasOwnProperty(<span class="string">'name'</span>)) <span class="comment">// true 说明不是继承而来,是自己的属性</span></span><br><span class="line"><span class="built_in">console</span>.log(sub1.getName === sub2.getName) <span class="comment">// false 方法没有得到复用</span></span><br><span class="line"><span class="built_in">console</span>.log(sub1.hello()) <span class="comment">//undefined 父元素的原型没被继承</span></span><br></pre></td></tr></table></figure><p>子类的每个实例都有自己的属性(name), super相当于把父元素的属性和方法传给子类(不包括原型的方法)。这些方法和属性属于子类自身的了。</p><h2 id="上面两种组合"><a href="#上面两种组合" class="headerlink" title="上面两种组合"></a>上面两种组合</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> Super = <span class="function"><span class="keyword">function</span>(<span class="params">name = <span class="string">'eric'</span></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line"> <span class="keyword">this</span>.getName = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.name;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">Super.prototype.hello = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> alert(<span class="string">'Hello, '</span> + <span class="keyword">this</span>.name + <span class="string">'!'</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> Sub = <span class="function"><span class="keyword">function</span>(<span class="params">sex = <span class="string">'male'</span></span>) </span>{</span><br><span class="line"> Super.call(<span class="keyword">this</span>, <span class="string">'eric'</span>) <span class="comment">//继承父类属性</span></span><br><span class="line"> <span class="keyword">this</span>.sex = sex;</span><br><span class="line">}</span><br><span class="line">Sub.prototype = <span class="keyword">new</span> Super(<span class="string">'eric'</span>); <span class="comment">//通过改变原型对象实现继承</span></span><br><span class="line">Sub.prototype.constructor = Sub <span class="comment">// 保持构造函数和原型对象的完整性</span></span><br><span class="line"><span class="keyword">let</span> sub1 = <span class="keyword">new</span> Sub(<span class="string">'male'</span>)</span><br><span class="line"> sub2 = <span class="keyword">new</span> Sub(<span class="string">'female'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(sub1.getName()); <span class="comment">// eric</span></span><br><span class="line"><span class="built_in">console</span>.log(sub1.hasOwnProperty(<span class="string">'name'</span>)) <span class="comment">// true 说明是属于自己的属性</span></span><br><span class="line"><span class="built_in">console</span>.log(sub1.getName === sub2.getName) <span class="comment">// true,复用了方法</span></span><br><span class="line"><span class="built_in">console</span>.log(sub1.hello()) <span class="comment">//正常显示</span></span><br></pre></td></tr></table></figure><p>综合了上面两种的优点,既让子类有了自己的属性,也实现了父类和原型方法的继承。</p><p>因为父类构造函数被执行了两次,子类的原型对象(Sub.prototype)中也有一份父类的实例属性(name),而且这些属性会被子类实例(sub1,sub2)的属性覆盖掉(即通过sub1.name访问不到Sub.prototype上的name属性),也存在内存浪费。</p><h2 id="寄生组合式继承"><a href="#寄生组合式继承" class="headerlink" title="寄生组合式继承"></a>寄生组合式继承</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> Super = <span class="function"><span class="keyword">function</span>(<span class="params">name = <span class="string">'eric'</span></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line"> <span class="keyword">this</span>.getName = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.name;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">Super.prototype = {</span><br><span class="line"> <span class="keyword">constructor</span>: Super,</span><br><span class="line"> hello() {</span><br><span class="line"> alert(<span class="string">'Hello, '</span> + <span class="keyword">this</span>.name + <span class="string">'!'</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> Sub = <span class="function"><span class="keyword">function</span>(<span class="params">sex, name</span>) </span>{</span><br><span class="line"> Super.call(<span class="keyword">this</span>, name);</span><br><span class="line"> <span class="keyword">this</span>.sex = sex;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 组合继承的缺点就是在继承父类方法的时候调用了父类构造函数,从而造成内存浪费,</span></span><br><span class="line"><span class="comment">// 现在只要解决了这个问题就完美了。那在复用父类方法的时候,</span></span><br><span class="line"><span class="comment">// 使用Object.create方法也可以达到目的,没有调用父类构造函数,问题解决。</span></span><br><span class="line">Sub.prototype = <span class="built_in">Object</span>.create(Super.prototype);</span><br><span class="line"><span class="comment">// 当然这个地方也可以使用Object.setPrototypeOf(Sub.prototype, Super.prototype)</span></span><br><span class="line"><span class="comment">// 因为更改一个对象的隐士原型(__proto__)对浏览器和js引擎都是很慢对操作,所以建议使用Object.create()创建一个具有指定原型对象的新对象</span></span><br><span class="line">Sub.prototype.constructor = Sub;</span><br><span class="line"><span class="keyword">let</span> sub1 = <span class="keyword">new</span> Sub(<span class="string">'male'</span>)</span><br><span class="line"> sub2 = <span class="keyword">new</span> Sub(<span class="string">'female'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(sub1.getName()); <span class="comment">// eric</span></span><br><span class="line"><span class="built_in">console</span>.log(sub1.hasOwnProperty(<span class="string">'name'</span>)) <span class="comment">// true 说明是属于自己的属性</span></span><br><span class="line"><span class="built_in">console</span>.log(sub1.getName === sub2.getName) <span class="comment">// false, 方法没被复用</span></span><br><span class="line"><span class="built_in">console</span>.log(sub1.hello()) <span class="comment">//正常显示</span></span><br></pre></td></tr></table></figure><p>就相当于用call把父元素的属性和方法给子类,然后父元素的原型给子类,这样子类可以用父元素的原型里面的方法。</p><h2 id="class"><a href="#class" class="headerlink" title="class"></a>class</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Super</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>(props = { name: <span class="string">'eric'</span> }) {</span><br><span class="line"> <span class="built_in">console</span>.log(props);</span><br><span class="line"> <span class="keyword">this</span>.name = props.name;</span><br><span class="line"> }</span><br><span class="line"> setName(name) {</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line"> }</span><br><span class="line"> getName() {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.name;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Sub</span> <span class="keyword">extends</span> <span class="title">Super</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>(props) {</span><br><span class="line"> <span class="keyword">super</span>(props = { <span class="attr">name</span>: <span class="string">'bob'</span> }); <span class="comment">// 创建实例,继承父类属性和方法</span></span><br><span class="line"> <span class="keyword">this</span>.sex = props.sex;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> sub1 = <span class="keyword">new</span> Sub({</span><br><span class="line"> name: <span class="string">'eric'</span>,</span><br><span class="line"> sex: <span class="string">'male'</span></span><br><span class="line">})</span><br><span class="line"><span class="keyword">let</span> sub2 = <span class="keyword">new</span> Sub({</span><br><span class="line"> name: <span class="string">'eric'</span>,</span><br><span class="line"> sex: <span class="string">'female'</span></span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(sub1.hasOwnProperty(<span class="string">'name'</span>)) <span class="comment">// true 说明是属于自己的属性</span></span><br><span class="line">sub1.setName(<span class="string">'ada'</span>);</span><br><span class="line"><span class="built_in">console</span>.log(sub1.getName(),sub2.getName()) <span class="comment">// ada,bob,属性没复用,各自实例都有自己的属性。</span></span><br><span class="line"><span class="built_in">console</span>.log(sub1.getName === sub2.getName) <span class="comment">// true; 复用了父类的方法</span></span><br><span class="line"><span class="built_in">console</span>.log(Sub.prototype.sex) <span class="comment">// undefined</span></span><br><span class="line"><span class="comment">// 子类原型对象上没有父类构造函数中赋值的属性,不是组合式继承</span></span><br></pre></td></tr></table></figure><p>可以看出es6 的class是组合继承的特点。</p>]]></content>
<tags>
<tag> js </tag>
</tags>
</entry>
<entry>
<title>使用html5拖放或绑定鼠标事件实现移动元素功能</title>
<link href="/2018/01/02/%E4%BD%BF%E7%94%A8html5%E6%8B%96%E6%94%BE%E6%88%96%E7%BB%91%E5%AE%9A%E9%BC%A0%E6%A0%87%E4%BA%8B%E4%BB%B6%E5%AE%9E%E7%8E%B0%E7%A7%BB%E5%8A%A8%E5%85%83%E7%B4%A0%E5%8A%9F%E8%83%BD/"/>
<content type="html"><![CDATA[<p>要实现一个点击元素,拖动元素到页面上你想要在的地方。如何实现这个功能呢?我想到了两种实现方法:第一种就是最近学习的html5的drag和drop功能;第二种是绑定鼠标的onmousedown,onmousemove事件。然后这两种实现方式又有什么区别呢?</p><a id="more"></a><h1 id="普通鼠标事件"><a href="#普通鼠标事件" class="headerlink" title="普通鼠标事件"></a>普通鼠标事件</h1><p>我们把body中的div元素设定为我们要移动的元素,移动的位置整个页面。这种鼠标事件的响应周期是350毫秒。具体实现如下:</p><ul><li>鼠标点击下时,计算鼠标点击的位置距离DIV左边和上边的距离</li><li>鼠标移动时,计算处DIV元素左上角是否在页面可以移动的区域内</li><li>合理设置DIV的top和left值,实现元素跟随鼠标移动的功能。</li></ul><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE html></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>实现鼠标拖放<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">style</span> <span class="attr">type</span>=<span class="string">"text/css"</span>></span><span class="undefined"></span></span><br><span class="line"><span class="undefined"> div{</span></span><br><span class="line"><span class="undefined"> position: absolute;</span></span><br><span class="line"><span class="undefined"> width: 200px;</span></span><br><span class="line"><span class="undefined"> height: 200px;</span></span><br><span class="line"><span class="css"> <span class="selector-tag">background</span>: <span class="selector-id">#369</span></span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> </span><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">div</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span>></span><span class="undefined"></span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"><span class="built_in">window</span>.onload = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> oDiv = <span class="built_in">document</span>.getElementsByTagName(<span class="string">"div"</span>)[<span class="number">0</span>];</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="comment">// 鼠标点击的位置距离DIV左边的距离 </span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> disX = <span class="number">0</span>;</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 鼠标点击的位置距离DIV顶部的距离</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> disY = <span class="number">0</span>;</span></span><br><span class="line"><span class="javascript"> oDiv.onmousedown = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> e = e || <span class="built_in">window</span>.event;</span></span><br><span class="line"><span class="javascript"> <span class="built_in">console</span>.log(e);</span></span><br><span class="line"><span class="undefined"> disX = e.clientX - oDiv.offsetLeft;</span></span><br><span class="line"><span class="undefined"> disY = e.clientY- oDiv.offsetTop;</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.onmousemove = <span class="function"><span class="keyword">function</span>(<span class="params">e</span>)</span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> e = e || <span class="built_in">window</span>.event;</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 横轴坐标</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> leftX = e.clientX - disX;</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 纵轴坐标</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">var</span> topY =e.clientY - disY;</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span>( leftX < <span class="number">0</span> ){</span></span><br><span class="line"><span class="undefined"> leftX = 0;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="comment">// 获取浏览器视口大小 document.document.documentElement.clientWidth</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">else</span> <span class="keyword">if</span>( leftX > <span class="built_in">document</span>.documentElement.clientWidth - oDiv.offsetWidth ){</span></span><br><span class="line"><span class="javascript"> leftX = <span class="built_in">document</span>.documentElement.clientWidth - oDiv.offsetWidth;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span>( topY < <span class="number">0</span> ){</span></span><br><span class="line"><span class="undefined"> topY = 0;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="keyword">else</span> <span class="keyword">if</span>( topY > <span class="built_in">document</span>.documentElement.clientHeight -oDiv.offsetHeight ){</span></span><br><span class="line"><span class="javascript"> topY = <span class="built_in">document</span>.documentElement.clientHeight - oDiv.offsetHeight;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> oDiv.style.left = leftX + <span class="string">"px"</span>;</span></span><br><span class="line"><span class="javascript"> oDiv.style.top = topY+<span class="string">"px"</span>;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.onmouseup = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.onmousemove = <span class="literal">null</span>;</span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.onmouseup = <span class="literal">null</span>;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><h1 id="利用html5的drag和drop功能"><a href="#利用html5的drag和drop功能" class="headerlink" title="利用html5的drag和drop功能"></a>利用html5的drag和drop功能</h1><p>重点是:</p><ul><li>要移动元素的draggable属性必须为true</li><li>dragover默认浏览器效果是drop失效,所以必须阻止默认事件。</li><li>dragover事件必须用addEventListener绑定,否则不生效。</li></ul><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE html></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>JSONP实现跨域2<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">style</span> <span class="attr">type</span>=<span class="string">"text/css"</span>></span><span class="undefined"></span></span><br><span class="line"><span class="undefined"> div{</span></span><br><span class="line"><span class="undefined"> position: absolute;</span></span><br><span class="line"><span class="undefined"> width: 200px;</span></span><br><span class="line"><span class="undefined"> height: 200px;</span></span><br><span class="line"><span class="css"> <span class="selector-tag">background</span>: <span class="selector-id">#369</span></span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> </span><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">draggable</span>=<span class="string">'true'</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span>></span><span class="undefined"></span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"><span class="comment">//第一种方法</span></span></span><br><span class="line"><span class="javascript"> <span class="built_in">window</span>.onload = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="comment">//获取被拖拽元素</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">const</span> item = <span class="built_in">document</span>.getElementsByTagName(<span class="string">'div'</span>)[<span class="number">0</span>];</span></span><br><span class="line"><span class="javascript"> <span class="comment">//鼠标点击的位置离div左边和上边的距离,现在设置为0</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">let</span> disX = <span class="number">0</span>, disY = <span class="number">0</span>;</span></span><br><span class="line"><span class="javascript"> <span class="comment">//div在页面上的位置。</span></span></span><br><span class="line"><span class="javascript"> <span class="keyword">let</span> leftX = <span class="number">0</span>, topY = <span class="number">0</span>;</span></span><br><span class="line"><span class="javascript"> item.ondragstart = <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">let</span> e = event || <span class="built_in">window</span>.event;</span></span><br><span class="line"><span class="javascript"> <span class="comment">//记录鼠标点击位置和div的距离,鼠标当前位置剪去div离body的位置</span></span></span><br><span class="line"><span class="undefined"> disX = e.clientX - item.offsetLeft;</span></span><br><span class="line"><span class="undefined"> disY = e.clientY - item.offsetTop;</span></span><br><span class="line"><span class="javascript"> event.dataTransfer.effectAllowed = <span class="string">'all'</span>;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> item.ondragend = <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>{</span></span><br><span class="line"><span class="javascript"> <span class="keyword">let</span> e = event || <span class="built_in">window</span>.event;</span></span><br><span class="line"><span class="undefined"> leftX = e.clientX - disX;</span></span><br><span class="line"><span class="undefined"> topY = e.clientY - disY;</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span>(leftX < <span class="number">0</span>) {</span></span><br><span class="line"><span class="undefined"> leftX = 0;</span></span><br><span class="line"><span class="javascript"> } <span class="keyword">else</span> <span class="keyword">if</span> (leftX > <span class="built_in">document</span>.documentElement.clientWidth - item.offsetWidth) {</span></span><br><span class="line"><span class="javascript"> leftX = <span class="built_in">document</span>.documentElement.clientWidth - item.offsetWidth;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> <span class="keyword">if</span> (topY < <span class="number">0</span>) {</span></span><br><span class="line"><span class="undefined"> topY = 0;</span></span><br><span class="line"><span class="javascript"> } <span class="keyword">else</span> <span class="keyword">if</span> (topY > <span class="built_in">document</span>.documentElement.clientHeight - item.offsetHeight) {</span></span><br><span class="line"><span class="javascript"> topY = <span class="built_in">document</span>.documentElement.clientHeight - item.offsetHeight;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="javascript"> item.style.left = leftX + <span class="string">"px"</span>;</span></span><br><span class="line"><span class="javascript"> item.style.top = topY + <span class="string">"px"</span>;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="javascript"> <span class="built_in">document</span>.addEventListener(<span class="string">"dragover"</span>, <span class="function"><span class="keyword">function</span>(<span class="params">event</span>) </span>{</span></span><br><span class="line"><span class="undefined"> event.preventDefault();</span></span><br><span class="line"><span class="javascript"> <span class="comment">// event.dataTransfer.dropEffect = 'link';</span></span></span><br><span class="line"><span class="undefined"> });</span></span><br><span class="line"><span class="undefined">}</span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="undefined"></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><h1 id="区别"><a href="#区别" class="headerlink" title="区别"></a>区别</h1><p>这两种实现方式究竟有什么区别?当然让你自己去尝试一下啦。<br>总之,第一种实现的效果元素跟随,第二种被拖动元素在没有放下前一直在原位置有占位,同时跟随鼠标的效果是半透明的。<br><img src="/images/drag_01.png"></p>]]></content>
<tags>
<tag> js </tag>
<tag> html </tag>
</tags>
</entry>
<entry>
<title>两栏布局和三栏布局</title>
<link href="/2017/12/13/columns/"/>
<content type="html"><![CDATA[<p>现实实现中,我们使用的较多的就是两栏布局和三栏布局,下面我们将介绍几种实现方式:</p><a id="more"></a><h2 id="两栏布局"><a href="#两栏布局" class="headerlink" title="两栏布局"></a>两栏布局</h2><p>要实现 左边固定,右边自适应 的布局效果。基本布局如下</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"wrapper"</span> <span class="attr">id</span>=<span class="string">"wrapper"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"left"</span>></span></span><br><span class="line"> 左边</span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"right"</span>></span></span><br><span class="line"> 右边</span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure><h3 id="使用float"><a href="#使用float" class="headerlink" title="使用float"></a>使用float</h3><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.left</span>{<span class="attribute">float</span>: left;<span class="attribute">width</span>: <span class="number">300px</span>;<span class="attribute">height</span>: <span class="number">150px</span>;<span class="attribute">background</span>: red;}</span><br><span class="line"><span class="selector-class">.right</span>{<span class="attribute">background</span>: blue;<span class="attribute">height</span>: <span class="number">150px</span>;}</span><br></pre></td></tr></table></figure><p>即使页面宽度比300px小左边也固定</p><h3 id="使用table"><a href="#使用table" class="headerlink" title="使用table"></a>使用table</h3><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.wrapper</span> {</span><br><span class="line"> <span class="attribute">display</span>: table;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.left</span>{</span><br><span class="line"> <span class="attribute">display</span>: table-cell;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">300px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">150px</span>;</span><br><span class="line"> <span class="attribute">background</span>: red;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.right</span>{</span><br><span class="line"> <span class="attribute">background</span>: blue;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">150px</span>;</span><br><span class="line"> <span class="attribute">display</span>: table-cell;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>如果页面宽度比300px小,左边会缩小,高度会随内容而变大。</p><h3 id="absolute-负边距"><a href="#absolute-负边距" class="headerlink" title="absolute + 负边距"></a>absolute + 负边距</h3><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.left</span>{</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">300px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">150px</span>;</span><br><span class="line"> <span class="attribute">background</span>: red;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.right</span>{</span><br><span class="line"> <span class="attribute">background</span>: blue;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">150px</span>;</span><br><span class="line"> <span class="attribute">margin-left</span>: <span class="number">300px</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="calc-方法"><a href="#calc-方法" class="headerlink" title="calc 方法"></a>calc 方法</h3><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.wrapper</span> {</span><br><span class="line"> <span class="attribute">box-sizing</span>: content-box;</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">0px</span>; </span><br><span class="line"> <span class="comment">/* 消除空格的影响*/</span></span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.left</span>{</span><br><span class="line"> <span class="attribute">display</span>: inline-block;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">300px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">150px</span>;</span><br><span class="line"> <span class="attribute">background</span>: red;</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">16px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.right</span>{</span><br><span class="line"> <span class="attribute">background</span>: blue;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">150px</span>;</span><br><span class="line"> <span class="attribute">display</span>: inline-block;</span><br><span class="line"> <span class="attribute">width</span>: <span class="built_in">calc</span>(100% - 301px);</span><br><span class="line"> <span class="attribute">font-size</span>: <span class="number">16px</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="flex-布局"><a href="#flex-布局" class="headerlink" title="flex 布局"></a>flex 布局</h3><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.wrapper</span> {</span><br><span class="line"> <span class="attribute">display</span>: flex;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.left</span>{</span><br><span class="line"> <span class="attribute">flex</span>: <span class="number">0</span> <span class="number">0</span> <span class="number">300px</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">150px</span>;</span><br><span class="line"> <span class="attribute">background</span>: red;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.right</span>{</span><br><span class="line"> <span class="attribute">background</span>: blue;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">150px</span>;</span><br><span class="line"> <span class="attribute">flex</span>: <span class="number">1</span> <span class="number">1</span> auto;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>不会撑高</p><h2 id="三栏布局"><a href="#三栏布局" class="headerlink" title="三栏布局"></a>三栏布局</h2><p>实现 左右模块 固定宽度,中间自适应。</p><h3 id="流体布局"><a href="#流体布局" class="headerlink" title="流体布局"></a>流体布局</h3><p>左边左浮动,右边右浮动, 中间自适应<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE html></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">style</span>></span><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.left</span> {</span></span><br><span class="line"><span class="undefined"> float: left;</span></span><br><span class="line"><span class="undefined"> height: 200px;</span></span><br><span class="line"><span class="undefined"> width: 100px;</span></span><br><span class="line"><span class="undefined"> background-color: red;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="css"> <span class="selector-class">.right</span> {</span></span><br><span class="line"><span class="undefined"> width: 200px;</span></span><br><span class="line"><span class="undefined"> height: 200px;</span></span><br><span class="line"><span class="undefined"> background-color: blue;</span></span><br><span class="line"><span class="undefined"> float: right;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="css"> <span class="selector-class">.main</span> {</span></span><br><span class="line"><span class="undefined"> margin-left: 120px;</span></span><br><span class="line"><span class="undefined"> margin-right: 220px;</span></span><br><span class="line"><span class="undefined"> height: 200px;</span></span><br><span class="line"><span class="undefined"> background-color: green;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> </span><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"left"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"right"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"main"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure></p><h3 id="BFC-三栏布局"><a href="#BFC-三栏布局" class="headerlink" title="BFC 三栏布局"></a>BFC 三栏布局</h3><p>BFC 区域,不会与浮动元素重叠。</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE html></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">style</span>></span><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.left</span> {</span></span><br><span class="line"><span class="undefined"> float: left;</span></span><br><span class="line"><span class="undefined"> height: 200px;</span></span><br><span class="line"><span class="undefined"> width: 100px;</span></span><br><span class="line"><span class="undefined"> margin-right: 20px;</span></span><br><span class="line"><span class="undefined"> background-color: red;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="css"> <span class="selector-class">.right</span> {</span></span><br><span class="line"><span class="undefined"> width: 200px;</span></span><br><span class="line"><span class="undefined"> height: 200px;</span></span><br><span class="line"><span class="undefined"> float: right;</span></span><br><span class="line"><span class="undefined"> margin-left: 20px;</span></span><br><span class="line"><span class="undefined"> background-color: blue;</span></span><br><span class="line"><span class="undefined"> } </span></span><br><span class="line"><span class="css"> <span class="selector-class">.main</span> {</span></span><br><span class="line"><span class="undefined"> height: 200px;</span></span><br><span class="line"><span class="undefined"> overflow: hidden;</span></span><br><span class="line"><span class="undefined"> background-color: green;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> </span><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"left"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"right"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"main"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><h3 id="双飞翼布局"><a href="#双飞翼布局" class="headerlink" title="双飞翼布局"></a>双飞翼布局</h3><p>左右和中间都float:left, 然后利用 margin 负值的应用<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE html></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">style</span>></span><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.content</span> {</span></span><br><span class="line"><span class="undefined"> float: left;</span></span><br><span class="line"><span class="undefined"> width: 100%;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="css"> <span class="selector-class">.main</span> {</span></span><br><span class="line"><span class="undefined"> height: 200px;</span></span><br><span class="line"><span class="undefined"> margin-left: 110px;</span></span><br><span class="line"><span class="undefined"> margin-right: 220px;</span></span><br><span class="line"><span class="undefined"> background-color: green;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> //用于清除浮动</span></span><br><span class="line"><span class="css"> <span class="selector-class">.main</span><span class="selector-pseudo">::after</span> {</span></span><br><span class="line"><span class="undefined"> display: block;</span></span><br><span class="line"><span class="undefined"> content: '';</span></span><br><span class="line"><span class="undefined"> font-size: 0;</span></span><br><span class="line"><span class="undefined"> height: 0;</span></span><br><span class="line"><span class="undefined"> clear: both;</span></span><br><span class="line"><span class="undefined"> zoom: 1;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="css"> <span class="selector-class">.left</span> {</span></span><br><span class="line"><span class="undefined"> float: left;</span></span><br><span class="line"><span class="undefined"> height: 200px;</span></span><br><span class="line"><span class="undefined"> width: 100px;</span></span><br><span class="line"><span class="undefined"> margin-left: -100%;</span></span><br><span class="line"><span class="undefined"> background-color: red;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="css"> <span class="selector-class">.right</span> {</span></span><br><span class="line"><span class="undefined"> width: 200px;</span></span><br><span class="line"><span class="undefined"> height: 200px;</span></span><br><span class="line"><span class="undefined"> float: right;</span></span><br><span class="line"><span class="undefined"> margin-left: -200px;</span></span><br><span class="line"><span class="undefined"> background-color: blue;</span></span><br><span class="line"><span class="undefined"> } </span></span><br><span class="line"><span class="undefined"> </span><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"content"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"main"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"left"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"right"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure></p><h3 id="圣杯布局"><a href="#圣杯布局" class="headerlink" title="圣杯布局"></a>圣杯布局</h3><p>html结构改变,将父元素的margin值变为和中间的一样,但也是左右和中间都float:left,再利用margin值改变位置。<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE html></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">style</span>></span><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.container</span> {</span></span><br><span class="line"><span class="undefined"> margin-left: 120px;</span></span><br><span class="line"><span class="undefined"> margin-right: 220px;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="css"> <span class="selector-class">.main</span> {</span></span><br><span class="line"><span class="undefined"> float: left;</span></span><br><span class="line"><span class="undefined"> width: 100%;</span></span><br><span class="line"><span class="undefined"> height: 300px;</span></span><br><span class="line"><span class="undefined"> background-color: red;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="css"> <span class="selector-class">.left</span> {</span></span><br><span class="line"><span class="undefined"> float: left;</span></span><br><span class="line"><span class="undefined"> width: 100px;</span></span><br><span class="line"><span class="undefined"> height: 300px;</span></span><br><span class="line"><span class="undefined"> margin-left: -100%;</span></span><br><span class="line"><span class="undefined"> position: relative;</span></span><br><span class="line"><span class="undefined"> left: -120px;</span></span><br><span class="line"><span class="undefined"> background-color: blue;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="css"> <span class="selector-class">.right</span> {</span></span><br><span class="line"><span class="undefined"> float: left;</span></span><br><span class="line"><span class="undefined"> width: 200px;</span></span><br><span class="line"><span class="undefined"> height: 300px;</span></span><br><span class="line"><span class="undefined"> margin-left: -200px;</span></span><br><span class="line"><span class="undefined"> position: relative;</span></span><br><span class="line"><span class="undefined"> right: -220px;</span></span><br><span class="line"><span class="undefined"> background-color: green;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> </span><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"main"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"left"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"right"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure></p><h3 id="flex布局"><a href="#flex布局" class="headerlink" title="flex布局"></a>flex布局</h3><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE html></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">style</span>></span><span class="undefined"></span></span><br><span class="line"><span class="css"> <span class="selector-class">.container</span> {</span></span><br><span class="line"><span class="undefined"> display: flex;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="css"> <span class="selector-class">.main</span> {</span></span><br><span class="line"><span class="undefined"> flex-grow: 1;</span></span><br><span class="line"><span class="undefined"> height: 300px;</span></span><br><span class="line"><span class="undefined"> background-color: red;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="css"> <span class="selector-class">.left</span> {</span></span><br><span class="line"><span class="undefined"> order: -1;</span></span><br><span class="line"><span class="undefined"> flex: 0 1 200px;</span></span><br><span class="line"><span class="undefined"> margin-right: 20px;</span></span><br><span class="line"><span class="undefined"> height: 300px;</span></span><br><span class="line"><span class="undefined"> background-color: blue;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="css"> <span class="selector-class">.right</span> {</span></span><br><span class="line"><span class="undefined"> flex: 0 1 100px;</span></span><br><span class="line"><span class="undefined"> margin-left: 20px;</span></span><br><span class="line"><span class="undefined"> height: 300px;</span></span><br><span class="line"><span class="undefined"> background-color: green;</span></span><br><span class="line"><span class="undefined"> }</span></span><br><span class="line"><span class="undefined"> </span><span class="tag"></<span class="name">style</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"main"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"left"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"right"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure>]]></content>
<tags>
<tag> css </tag>
</tags>
</entry>
<entry>
<title>git 的常用命令</title>
<link href="/2017/11/24/git/"/>
<content type="html"><![CDATA[<p>GitHub是个git仓库(相当于Git服务器),安装在远程服务器端。<br>git是一个客户端工具,安装在用户的电脑上,用户通过git命令从GitHub上pull和push数据。</p><p>接下来我们就来介绍git的常用指令</p><a id="more"></a><h2 id="重要的"><a href="#重要的" class="headerlink" title="重要的"></a>重要的</h2><p>在Git中,用HEAD表示当前版本。<br>上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100。</p><p>git我们的工作区有一个.git的文件夹,这是Git的版本库。Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。</p><h3 id="创建基础"><a href="#创建基础" class="headerlink" title="创建基础"></a>创建基础</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">git init 初始化本地仓库</span><br><span class="line">touch readme.txt 新建文件</span><br><span class="line"></span><br><span class="line">git add readme.txt 添加文件到暂存区</span><br><span class="line">git status 掌握仓库当前的状态</span><br><span class="line">git diff 查看修改 暂存区和工作区的代码差异</span><br><span class="line">git diff HEAD 查看工作区和版本库里面最新版本的区别</span><br><span class="line"></span><br><span class="line">git commit -m "first comomit" 提交文件到本地仓库,first comomit为备注</span><br><span class="line"></span><br><span class="line">git log --pretty=oneline 简单的描述从最近到最远的提交日志 </span><br><span class="line">git reset --hard HEAD^ 将提交返回到上一个版本,这是一个head指针的改变</span><br><span class="line">git reflog 记录我的每一次命令,可以查找恢复到最新的版本</span><br><span class="line"></span><br><span class="line">// HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git reset --hard commit_id。</span><br><span class="line">// 穿梭前,用git log可以查看提交历史,以便确定要回退到哪个版本。</span><br><span class="line">// 要重返未来,用git reflog查看命令历史,以便确定要回到以前的哪个版本。</span><br></pre></td></tr></table></figure><h3 id="修改"><a href="#修改" class="headerlink" title="修改"></a>修改</h3><p>场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout – file。</p><p>场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD <file>,就回到了场景1,第二步按场景1操作。</file></p><p>场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。git reset –hard HEAD^</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">git checkout -- readme.txt //把readme.txt文件在工作区的修改全部撤销,就是让这个文件回到最近一次git commit或git add时的状态。</span><br><span class="line"> // reset是返回到之前的版本,checkout是把本地的修改撤销</span><br><span class="line"> // -- 很重要,不然是切换至另一个分支的命令</span><br><span class="line">git reset HEAD <file> //把暂存区的修改撤销掉(unstage),重新放回工作区</span><br></pre></td></tr></table></figure><h3 id="删除"><a href="#删除" class="headerlink" title="删除"></a>删除</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">rm test.txt //删了文件</span><br><span class="line"></span><br><span class="line">git rm test.txt + git commit //告诉git,从版本库中删除该文件</span><br><span class="line"></span><br><span class="line">git checkout -- test.txt //还没有上传版本库,直接恢复</span><br></pre></td></tr></table></figure><h3 id="远程仓库"><a href="#远程仓库" class="headerlink" title="远程仓库"></a>远程仓库</h3><p>Git是分布式版本控制系统,同一个Git仓库,可以分布到不同的机器上。<br>由于你的本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以,需要一点设置:<br>第1步:创建SSH Key。GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">git remote add origin [email protected]:mooory/git-test.git 在本地仓库添加一个远程仓库。</span><br><span class="line"></span><br><span class="line">git push 或者 git push -u origin master //提交文件到远程仓库, 第一次加上-u参数,</span><br><span class="line"> // Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,</span><br><span class="line"> // 在以后的推送或者拉取时就可以简化命令。</span><br><span class="line"></span><br><span class="line">git push origin master 把本地master分支的最新修改推送至GitHub</span><br></pre></td></tr></table></figure><h3 id="克隆"><a href="#克隆" class="headerlink" title="克隆"></a>克隆</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git clone [email protected]:mooory/git-test.git</span><br></pre></td></tr></table></figure><h3 id="分支管理"><a href="#分支管理" class="headerlink" title="分支管理"></a>分支管理</h3><p>HEAD严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。<br>开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点<br>当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上<br><img src="https://cdn.liaoxuefeng.com/cdn/files/attachments/001384908811773187a597e2d844eefb11f5cf5d56135ca000/0" alt><br>不过,从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变<br><img src="https://cdn.liaoxuefeng.com/cdn/files/attachments/0013849088235627813efe7649b4f008900e5365bb72323000/0" alt></p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">git checkout -b dev 创建dev分支,然后切换到dev分支,相当于以下两条命令</span><br><span class="line">= git branch dev + git checkout dev</span><br><span class="line"></span><br><span class="line">git branch 查看当前所有分支</span><br><span class="line">git merge dev 把dev分支的工作成果合并到master分支上</span><br><span class="line"></span><br><span class="line">git branch -d dev 删除dev分支</span><br></pre></td></tr></table></figure><p>解决冲突:当Git无法自动合并分支时,就必须首先解决冲突。解决冲突后,再提交,合并完成。<br>合并分支时,一般Git会用Fast forward模式,但这种模式下,删除分支后,会丢掉分支信息。<br>如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git merge --no-ff -m "merge with no-ff" dev //--no-ff参数,表示禁用Fast forward</span><br><span class="line"> //因为本次合并要创建一个新的commit,所以加上-m参数,把commit描述写进去。</span><br></pre></td></tr></table></figure><p>在实际开发中,我们应该按照几个基本原则进行分支管理:</p><p>首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;</p><p>那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;</p><p>你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。<br><img src="https://cdn.liaoxuefeng.com/cdn/files/attachments/001384909239390d355eb07d9d64305b6322aaf4edac1e3000/0" alt></p><h3 id="解决bug分支"><a href="#解决bug分支" class="headerlink" title="解决bug分支"></a>解决bug分支</h3><p>当你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101来修复它,但是,等等,当前正在dev上进行的工作还没有提交。<br>并不是你不想提交,而是工作只进行到一半,还没法提交,预计完成还需1天时间。但是,必须在两个小时内修复该bug,怎么办?</p><p>幸好,Git还提供了一个stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">git stash //将当前工作现场储藏,工作环境就是干净的了</span><br><span class="line"></span><br><span class="line">git stash list // 查看我们存储的环境</span><br><span class="line"></span><br><span class="line">git stash apply stash@{0}</span><br></pre></td></tr></table></figure><p>恢复:<br>一是用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除;<br>另一种方式是用git stash pop,恢复的同时把stash内容也删了</p><h3 id="分支不合并"><a href="#分支不合并" class="headerlink" title="分支不合并"></a>分支不合并</h3><p>如果分支不合并就要被删除,会提示错误<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">$ git branch -d feature-vulcan</span><br><span class="line">error: The branch 'feature-vulcan' is not fully merged.</span><br><span class="line">If you are sure you want to delete it, run 'git branch -D feature-vulcan'.</span><br><span class="line"></span><br><span class="line">如果要强行删除,需要使用大写的-D参数</span><br><span class="line"></span><br><span class="line">git branch -D feature-vulcan</span><br><span class="line">Deleted branch feature-vulcan (was 287773e).</span><br></pre></td></tr></table></figure></p><h3 id="多人协作"><a href="#多人协作" class="headerlink" title="多人协作"></a>多人协作</h3><p>推送分支,就是把该分支上的所有本地提交推送到远程库。推送时,要指定本地分支,这样,Git就会把该分支推送到远程库对应的远程分支上<br>并不是一定要把本地分支往远程推送,那么,哪些分支需要推送,哪些不需要呢?</p><ul><li>master分支是主分支,因此要时刻与远程同步;</li><li>dev分支是开发分支,团队所有成员都需要在上面工作,所以也需要与远程同步;</li></ul><p>当你的小伙伴从远程库clone时,默认情况下,你的小伙伴只能看到本地的master分支。<br>现在,你的小伙伴要在dev分支上开发,就必须创建远程origin的dev分支到本地,于是他用这个命令创建本地dev分支:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git checkout -b dev origin/dev</span><br></pre></td></tr></table></figure></p><p>现在,他就可以在dev上继续修改,然后,时不时地把dev分支push到远程。</p><p>当有冲突时,先用git pull把最新的提交从origin/dev抓下来,然后,在本地合并,解决冲突,再推送<br><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span> git pull</span><br><span class="line">There is no tracking information for the current branch.</span><br><span class="line">Please specify which branch you want to merge with.</span><br><span class="line">See git-pull(1) for details.</span><br><span class="line"></span><br><span class="line"> git pull <remote> <branch></span><br><span class="line"></span><br><span class="line">If you wish to set tracking information for this branch you can do so with:</span><br><span class="line"></span><br><span class="line"> git branch --set-upstream-to=origin/<branch> dev</span><br></pre></td></tr></table></figure></p><p>git pull也失败了,原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置dev和origin/dev的链接</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch --set-upstream-to=origin/dev dev</span><br></pre></td></tr></table></figure><p>再进行pull。再解决冲突。</p><h2 id="新建代码库"><a href="#新建代码库" class="headerlink" title="新建代码库"></a>新建代码库</h2><p>remote:远程</p><p>repository :仓库</p><p>checkout:检出</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span> 在当前目录新建一个Git代码库</span><br><span class="line"><span class="meta">$</span> git init</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 新建一个目录,将其初始化为Git代码库</span><br><span class="line"><span class="meta">$</span> git init [project-name]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 下载一个项目和它的整个代码历史</span><br><span class="line"><span class="meta">$</span> git clone [url]</span><br></pre></td></tr></table></figure><h2 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h2><p>Git的设置文件为.gitconfig<br><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span> 显示当前的Git配置</span><br><span class="line"><span class="meta">$</span> git config --list</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 编辑Git配置文件</span><br><span class="line"><span class="meta">$</span> git config -e [--global]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 设置提交代码时的用户信息</span><br><span class="line"><span class="meta">$</span> git config [--global] user.name "[name]"</span><br><span class="line"><span class="meta">$</span> git config [--global] user.email "[email address]"</span><br></pre></td></tr></table></figure></p><h2 id="增删文件"><a href="#增删文件" class="headerlink" title="增删文件"></a>增删文件</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span> 添加指定文件到暂存区</span><br><span class="line"><span class="meta">$</span> git add [file1] [file2] ...</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 添加指定目录到暂存区,包括子目录</span><br><span class="line"><span class="meta">$</span> git add [dir]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 添加当前目录的所有文件到暂存区</span><br><span class="line"><span class="meta">$</span> git add .</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 添加每个变化前,都会要求确认</span><br><span class="line"><span class="meta">#</span> 对于同一个文件的多处变化,可以实现分次提交</span><br><span class="line"><span class="meta">$</span> git add -p</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 删除工作区文件,并且将这次删除放入暂存区</span><br><span class="line"><span class="meta">$</span> git rm [file1] [file2] ...</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 停止追踪指定文件,但该文件会保留在工作区</span><br><span class="line"><span class="meta">$</span> git rm --cached [file]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 改名文件,并且将这个改名放入暂存区</span><br><span class="line"><span class="meta">$</span> git mv [file-original] [file-renamed]</span><br></pre></td></tr></table></figure><h2 id="提交"><a href="#提交" class="headerlink" title="提交"></a>提交</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span> 提交暂存区到仓库区</span><br><span class="line"><span class="meta">$</span> git commit -m [message] //重要</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 提交暂存区的指定文件到仓库区</span><br><span class="line"><span class="meta">$</span> git commit [file1] [file2] ... -m [message]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 提交工作区自上次commit之后的变化,直接到仓库区</span><br><span class="line"><span class="meta">$</span> git commit -a</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 提交时显示所有diff信息</span><br><span class="line"><span class="meta">$</span> git commit -v</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 使用一次新的commit,替代上一次提交</span><br><span class="line"><span class="meta">#</span> 如果代码没有任何新变化,则用来改写上一次commit的提交信息</span><br><span class="line"><span class="meta">$</span> git commit --amend -m [message]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 重做上一次commit,并包括指定文件的新变化</span><br><span class="line"><span class="meta">$</span> git commit --amend [file1] [file2] ...</span><br></pre></td></tr></table></figure><h2 id="分支"><a href="#分支" class="headerlink" title="分支"></a>分支</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span> 列出所有本地分支</span><br><span class="line"><span class="meta">$</span> git branch</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 列出所有远程分支</span><br><span class="line"><span class="meta">$</span> git branch -r</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 列出所有本地分支和远程分支</span><br><span class="line"><span class="meta">$</span> git branch -a</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 新建一个分支,但依然停留在当前分支</span><br><span class="line"><span class="meta">$</span> git branch [branch-name]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 新建一个分支,并切换到该分支</span><br><span class="line"><span class="meta">$</span> git checkout -b [branch]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 新建一个分支,指向指定commit</span><br><span class="line"><span class="meta">$</span> git branch [branch] [commit]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 新建一个分支,与指定的远程分支建立追踪关系</span><br><span class="line"><span class="meta">$</span> git branch --track [branch] [remote-branch]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 切换到指定分支,并更新工作区</span><br><span class="line"><span class="meta">$</span> git checkout [branch-name]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 切换到上一个分支</span><br><span class="line"><span class="meta">$</span> git checkout -</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 建立追踪关系,在现有分支与指定的远程分支之间</span><br><span class="line"><span class="meta">$</span> git branch --set-upstream [branch] [remote-branch]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 合并指定分支到当前分支</span><br><span class="line"><span class="meta">$</span> git merge [branch]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 选择一个commit,合并进当前分支</span><br><span class="line"><span class="meta">$</span> git cherry-pick [commit]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 删除分支</span><br><span class="line"><span class="meta">$</span> git branch -d [branch-name]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 删除远程分支</span><br><span class="line"><span class="meta">$</span> git push origin --delete [branch-name]</span><br><span class="line"><span class="meta">$</span> git branch -dr [remote/branch]</span><br></pre></td></tr></table></figure><h2 id="查看"><a href="#查看" class="headerlink" title="查看"></a>查看</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span> 显示有变更的文件</span><br><span class="line"><span class="meta">$</span> git status</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 显示当前分支的版本历史</span><br><span class="line"><span class="meta">$</span> git log</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 显示commit历史,以及每次commit发生变更的文件</span><br><span class="line"><span class="meta">$</span> git log --stat</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 搜索提交历史,根据关键词</span><br><span class="line"><span class="meta">$</span> git log -S [keyword]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 显示某个commit之后的所有变动,每个commit占据一行</span><br><span class="line"><span class="meta">$</span> git log [tag] HEAD --pretty=format:%s</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 显示某个commit之后的所有变动,其"提交说明"必须符合搜索条件</span><br><span class="line"><span class="meta">$</span> git log [tag] HEAD --grep feature</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 显示某个文件的版本历史,包括文件改名</span><br><span class="line"><span class="meta">$</span> git log --follow [file]</span><br><span class="line"><span class="meta">$</span> git whatchanged [file]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 显示指定文件相关的每一次diff</span><br><span class="line"><span class="meta">$</span> git log -p [file]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 显示过去5次提交</span><br><span class="line"><span class="meta">$</span> git log -5 --pretty --oneline</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 显示所有提交过的用户,按提交次数排序</span><br><span class="line"><span class="meta">$</span> git shortlog -sn</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 显示指定文件是什么人在什么时间修改过</span><br><span class="line"><span class="meta">$</span> git blame [file]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 显示暂存区和工作区的代码差异</span><br><span class="line"><span class="meta">$</span> git diff // 重要</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 显示暂存区和上一个commit的差异</span><br><span class="line"><span class="meta">$</span> git diff --cached [file]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 显示工作区与当前分支最新commit之间的差异</span><br><span class="line"><span class="meta">$</span> git diff HEAD</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 显示两次提交之间的差异</span><br><span class="line"><span class="meta">$</span> git diff [first-branch]...[second-branch]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 显示今天你写了多少行代码</span><br><span class="line"><span class="meta">$</span> git diff --shortstat "@{0 day ago}"</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 显示某次提交的元数据和内容变化</span><br><span class="line"><span class="meta">$</span> git show [commit]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 显示某次提交发生变化的文件</span><br><span class="line"><span class="meta">$</span> git show --name-only [commit]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 显示某次提交时,某个文件的内容</span><br><span class="line"><span class="meta">$</span> git show [commit]:[filename]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 显示当前分支的最近几次提交</span><br><span class="line"><span class="meta">$</span> git reflog</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 从本地master拉取代码更新当前分支:branch 一般为master</span><br><span class="line"><span class="meta">$</span> git rebase [branch]</span><br></pre></td></tr></table></figure><h2 id="同步"><a href="#同步" class="headerlink" title="同步"></a>同步</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span> git remote update --更新远程仓储</span><br><span class="line"><span class="meta">#</span> 下载远程仓库的所有变动</span><br><span class="line"><span class="meta">$</span> git fetch [remote]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 显示所有远程仓库</span><br><span class="line"><span class="meta">$</span> git remote -v</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 显示某个远程仓库的信息</span><br><span class="line"><span class="meta">$</span> git remote show [remote]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 增加一个新的远程仓库,并命名</span><br><span class="line"><span class="meta">$</span> git remote add [shortname] [url]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 取回远程仓库的变化,并与本地分支合并</span><br><span class="line"><span class="meta">$</span> git pull [remote] [branch]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 上传本地指定分支到远程仓库</span><br><span class="line"><span class="meta">$</span> git push [remote] [branch]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 强行推送当前分支到远程仓库,即使有冲突</span><br><span class="line"><span class="meta">$</span> git push [remote] --force</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 推送所有分支到远程仓库</span><br><span class="line"><span class="meta">$</span> git push [remote] --all</span><br></pre></td></tr></table></figure><h2 id="撤销"><a href="#撤销" class="headerlink" title="撤销"></a>撤销</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span> 恢复暂存区的指定文件到工作区</span><br><span class="line"><span class="meta">$</span> git checkout [file]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 恢复某个commit的指定文件到暂存区和工作区</span><br><span class="line"><span class="meta">$</span> git checkout [commit] [file]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 恢复暂存区的所有文件到工作区</span><br><span class="line"><span class="meta">$</span> git checkout .</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 重置暂存区的指定文件,与上一次commit保持一致,但工作区不变</span><br><span class="line"><span class="meta">$</span> git reset [file]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 重置暂存区与工作区,与上一次commit保持一致</span><br><span class="line"><span class="meta">$</span> git reset --hard //重要</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 重置当前分支的指针为指定commit,同时重置暂存区,但工作区不变</span><br><span class="line"><span class="meta">$</span> git reset [commit]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 重置当前分支的HEAD为指定commit,同时重置暂存区和工作区,与指定commit一致</span><br><span class="line"><span class="meta">$</span> git reset --hard [commit]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 重置当前HEAD为指定commit,但保持暂存区和工作区不变</span><br><span class="line"><span class="meta">$</span> git reset --keep [commit]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 新建一个commit,用来撤销指定commit</span><br><span class="line"><span class="meta">#</span> 后者的所有变化都将被前者抵消,并且应用到当前分支</span><br><span class="line"><span class="meta">$</span> git revert [commit]</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 暂时将未提交的变化移除,稍后再移入</span><br><span class="line"><span class="meta">$</span> git stash // 重要</span><br><span class="line"><span class="meta">$</span> git stash pop</span><br></pre></td></tr></table></figure><h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span> 生成一个可供发布的压缩包</span><br><span class="line"><span class="meta">$</span> git archive</span><br></pre></td></tr></table></figure>]]></content>
<tags>
<tag> git </tag>
</tags>
</entry>
<entry>
<title>记录markdown的相关语法</title>
<link href="/2017/10/13/markdown/"/>
<content type="html"><![CDATA[<h1 id="一级标题"><a href="#一级标题" class="headerlink" title="一级标题"></a>一级标题</h1><h2 id="二级标题"><a href="#二级标题" class="headerlink" title="二级标题"></a>二级标题</h2><h3 id="三级标题"><a href="#三级标题" class="headerlink" title="三级标题"></a>三级标题</h3><p>我是正文</p><blockquote><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque hendrerit lacus ut purus iaculis feugiat. Sed nec tempor elit, quis aliquam neque. Curabitur sed diam eget dolor fermentum semper at eu lorem.</p></blockquote><a id="more"></a><hr><blockquote><p>这也是引用块</p></blockquote><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//这里是代码段</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">ss</span>(<span class="params"></span>) </span>{</span><br><span class="line"> </span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(<span class="string">'str'</span>);</span><br></pre></td></tr></table></figure><!-- 这里是获取资源文件夹下面的图片,普通的用souce文件夹下面的资源 --><img src="/images/timg2.jpg"><p>来源:<a href="https://www.baidu.com" target="_blank" rel="noopener">百度</a></p><ul><li>无序列表第一层<ul><li>这里是说明</li><li>第二层</li></ul></li><li>无序列表</li></ul><ol><li>有序列表</li><li>序号和内容之间要有空格</li></ol><table><thead><tr><th>语句</th><th style="text-align:center">说明</th><th style="text-align:right">备注</th></tr></thead><tbody><tr><td>hello</td><td style="text-align:center">你好</td><td style="text-align:right">英文</td></tr></tbody></table>]]></content>
<tags>
<tag> markdown </tag>
</tags>
</entry>
</search>