-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathsearch.xml
307 lines (148 loc) · 212 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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>日常开发难点</title>
<link href="/2022/06/12/%E6%97%A5%E5%B8%B8%E5%BC%80%E5%8F%91%E9%9A%BE%E7%82%B9/"/>
<url>/2022/06/12/%E6%97%A5%E5%B8%B8%E5%BC%80%E5%8F%91%E9%9A%BE%E7%82%B9/</url>
<content type="html"><![CDATA[<h1 id="列表项上下键选中触发-scrollIntoView-behavior-‘smooth’-抖动问题"><a href="#列表项上下键选中触发-scrollIntoView-behavior-‘smooth’-抖动问题" class="headerlink" title="列表项上下键选中触发 scrollIntoView behavior: ‘smooth’ 抖动问题"></a>列表项上下键选中触发 scrollIntoView behavior: ‘smooth’ 抖动问题</h1>]]></content>
<categories>
<category> 难点 </category>
</categories>
<tags>
<tag> 难点 </tag>
<tag> 日常开发 </tag>
</tags>
</entry>
<entry>
<title>think of babel</title>
<link href="/2022/06/06/thinkofbabel/"/>
<url>/2022/06/06/thinkofbabel/</url>
<content type="html"><![CDATA[<h1 id="babel-配置"><a href="#babel-配置" class="headerlink" title="babel 配置"></a>babel 配置</h1><h2 id="babel-x2F-perset-env"><a href="#babel-x2F-perset-env" class="headerlink" title="@babel/perset-env"></a>@babel/perset-env</h2><h3 id="useBuiltIns"><a href="#useBuiltIns" class="headerlink" title="useBuiltIns"></a>useBuiltIns</h3><blockquote><p>介绍</p></blockquote><p> useBuiltIns是用于配合core-js,兼容旧版本浏览器原生不支持的新功能.枚举的值为: ‘entry’, ‘usage’ and false.</p><ul><li><p>‘entry’</p><ul><li>配合 corejs: 2 时,会在入口直接导入全部的兼容代码.</li><li>配合 corejs: 3 时,会在入口支持按需导入兼容代码.</li></ul></li><li><p>false</p><ul><li>在任何位置不导入任何兼容代码.</li></ul></li><li><p>‘usage’</p><ul><li>配合 corejs: 2时, 会在需要兼容的模块内导入全部的兼容代码.</li><li>配合 corejs: 3时, 根据使用时的旧版本浏览器原生不支持的新功能按需在需要兼容的模块内进行导入.</li></ul></li></ul>]]></content>
<categories>
<category> babel </category>
</categories>
<tags>
<tag> commonjs </tag>
<tag> esm </tag>
<tag> amd </tag>
<tag> cmd </tag>
<tag> umd </tag>
<tag> javascript module </tag>
<tag> webpack </tag>
<tag> babel </tag>
</tags>
</entry>
<entry>
<title>think of testing</title>
<link href="/2022/05/28/thinkoftesting/"/>
<url>/2022/05/28/thinkoftesting/</url>
<content type="html"><![CDATA[<h1 id="测试-testing"><a href="#测试-testing" class="headerlink" title="测试(testing)"></a>测试(testing)</h1><h2 id="跨终端自动化测试"><a href="#跨终端自动化测试" class="headerlink" title="跨终端自动化测试"></a>跨终端自动化测试</h2><p> 在介绍自动化测试之前,先来说一下跨终端测试工具,基于全方位测试需求的考虑,跨终端测试应该是最重要的类型之一.如今,各种类型的浏览器、操作系统、品牌手机以及设备可谓是琳琅满目.因此,需要确保用户在通过不同种类的浏览器、操作系统、品牌手机以及设备访问平台服务时,不会产生较大的体验落差.</p><p> 在市面上,诸如 LambdaTest 之类的在线工具,就能够帮助以一种轻松互动的方式,解决此方面的问题.LambdaTest 是一种非常流行的在线工具,可以通过它对超过3000多个真正的浏览器、操作系统、品牌手机以及设备进行跨终端式的测试.测试人员甚至可以使用该工具来自动捕捉屏幕上的截图,以加速对于目标平台网络布局的测试.另外,其他同类型比较流行的测试工具还有:Browserstack 和 Saucelabs.</p><h3 id="lambdaTest"><a href="#lambdaTest" class="headerlink" title="lambdaTest"></a>lambdaTest</h3><p> lambdaTest 能够为3000多种浏览器、不同的 Web 应用操作系统、品牌手机以及设备的组合提供支持,可以在线执行手动测试(Manual testing)、自动化测试(Automatic testing)以及真机测试(Real Device testing).</p><blockquote><p>手动测试(Manual testing)</p></blockquote><ul><li><p>Browser Testing.</p><p>可在线在不同的浏览器种类、版本、操作系统以及分辨率对线上网站进行实时交互式测试,每次真实浏览器sessions可测试10分钟,在测试期间可切换配置浏览器种类、版本、操作系统以及分辨率、可录屏(录屏可下载)、可标记bug(编辑bug截图、下载bug截图以及上传到lambdaTest衍生的SLACK、JIRA和ASANA项目管理系统).</p><ul><li><p>配置.</p><p><img src="https://image.white-than-wood.zone/lambdaTest/Manual/browser/Manual_home.png"></p></li><li><p>测试.</p><p><img src="https://image.white-than-wood.zone/lambdaTest/Manual/browser/Manual_testing.png"></p></li><li><p>Debug.</p><p><img src="https://image.white-than-wood.zone/lambdaTest/Manual/browser/Manual_debug.png"></p></li><li><p>录屏.</p><p><img src="https://image.white-than-wood.zone/lambdaTest/Manual/browser/Manual_video.png"></p></li></ul></li><li><p>App Testing.</p><p>可在线自由搭配不同的品牌手机、版本设备对App包或者App Url进行实时交互式测试,每次真机测试sessions可测试10分钟,在测试期间可切换配置品牌手机以及版本设备、可录屏(录屏可下载)、可标记bug(编辑bug截图、下载bug截图以及上传到lambdaTest衍生的SLACK、JIRA和ASANA项目管理系统).</p><ul><li><p>配置.</p><p><img src="https://image.white-than-wood.zone/lambdaTest/Manual/app/Manual_home.png"></p></li><li><p>测试.</p><p><img src="https://image.white-than-wood.zone/lambdaTest/Manual/app/Manual_testing.png"></p></li><li><p>Debug.</p><p><img src="https://image.white-than-wood.zone/lambdaTest/Manual/app/Manual_debug.png"></p></li><li><p>录屏.</p><p><img src="https://image.white-than-wood.zone/lambdaTest/Manual/app/Manual_video.png"></p></li></ul></li><li><p>Browser Testing开发环境在线测试.</p><p>可配合lambdaTest桌面应用(LT_Windows or LT_Macs)配合在线网站进行开发环境在线测试.</p><ul><li><p>配置.</p><p><a href='https://www.lambdatest.com/support/docs/testing-locally-hosted-pages/'>Browser Testing开发环境在线测试</a>. PS:注意看视频中的介绍步骤.</p></li></ul></li><li><p>结论.</p><p>lambdaTest手动测试简洁、易用且容易理解;测试过程流畅、无障碍性困难;功能完备,在线随时切换终端设备配置、录屏下载、Debug标记等应有尽有;其衍生的生态是成熟且完备的,例如其衍生的一套项目管理系统SLACK、JIRA和ASANA.</p><p>对于测试人员来说,无论是生产环境、开发环境和App包,如果尝试自己查看每个浏览器、操作系统、品牌手机以及设备,是一项非常繁琐的工作,但lambdaTest都可在线简捷做各种搭配兼容测试,提高了效率,大大节约了时间成本.</p></li><li><p>问题.</p><p>现阶段lambdaTest上手动测试中大部分浏览器版本、操作系统、品牌手机以及版本设备都是不开放的,开放的很小的部分仅仅也是为了给用户进行体验的,如果需要开放就需要付费,另外支出成本.</p></li></ul><blockquote><p>自动化测试(Automatic testing)</p></blockquote><p> 众所周知,软件测试人员平时的工作量既多且复杂.因此,为了给他们减负,以及加快测试周期,各种高效率的自动化测试工具往往是必须的.</p><ul><li><p>浏览器自动化操作标准-WebDriver.</p><p>WebDriver是W3C的一个标准,是一个远程控制协议,它提供了跨平台和跨语言的方式来远程操控浏览器;提供了一系列接口来访问和操作DOM,进而控制浏览器的行为,使web开发者能写一些自动化脚本来测试网页.后续的Selenium、Appium都是基于WebDriver协议并进行了扩展.</p><ul><li><p>WebDriver的工作过程.</p><p>浏览器在启动后会在某一个端口启动基于WebDriver协议的Web Service,接下来调用WebDriver的任何api时,都需要借助一个CommandExecutor发送一个命令(也就是给监听端口上的Web Service发送一个http请求),这个命令会告诉浏览器接下来要做什么.</p><p><img src="https://image.white-than-wood.zone/lambdaTest/Automatic/Automatic_webdriver.png"></p></li></ul></li><li><p>浏览器自动化测试工具.</p><ul><li><p>Selenium.</p><p>Selenium是浏览器自动化测试工具领域最为流行的一种测试套件,根据《针对自动化测试各种挑战的调查》一文,九成的测试人员已经或正在使用着Selenium;</p><ul><li>Selenium支持多浏览器平台(Chrome、Firefox、IE、Opera、Safari等);</li><li>Selenium支持多语言(python、java、ruby、js、c#等);</li><li>Selenium的Remote Control可以通过录制用户的操作,来简化Web测试人员的各项重复作业;</li><li>Selenium的Grid具有编写、运行和并行处理测试的功能;</li><li>Selenium的Core则是基于JsUnit,完全由JavaScript所编写,因此可以被运行在各种支持JavaScript的主流浏览器之上;</li><li>Selenium开源免费;</li></ul></li><li><p>Selenium IDE.</p><p>Selenium IDE能够以插件的形式被安装到测试者的浏览器中,从而方便地实现Web界面的测试,lambdaTest在浏览器自动化测试部分也极力推荐Selenium.</p></li><li><p>lambdaTest配合Selenium IDE.</p><ul><li><p>配置.</p><p>使用Selenium IDE以及配合lambdaTest的配置还是比较简单的,官方有具体的一步一步实现的文章: <a href='https://www.lambdatest.com/support/docs/run-selenium-ide-tests-on-lambdatest-selenium-cloud-grid/'>Run Selenium IDE Tests with LambdaTest Selenium Grid</a>.</p></li><li><p>selenium-side-runner命令行配合lambdaTest.</p><p>使用selenium-side-runner依赖包在命令行中运行导出的Selenium IDE测试套件,在lambdaTest在线平台上搭配不同的浏览器版本、浏览器类型、操作系统、品牌手机以及版本设备进行生成自动化测试录屏.</p><ul><li>可根据selenium-webdriver事件步骤分帧在录屏中在线查看测试套件自动化测试的情况;</li><li>可在线查看在自动化测试过程当中网络资源接口的加载情况;</li><li>可下载自动化测试的录屏;</li><li>可将标记bug(将有问题的selenium事件步骤分帧上传到lambdaTest衍生的SLACK、JIRA和ASANA项目管理系统);</li></ul></li><li><p>生成.</p><p><img src="https://image.white-than-wood.zone/lambdaTest/Automatic/Automatic_generate.png"></p><p>运行命令:</p><pre><code>npm run test</code></pre><p>生成自动化测试录屏列表.</p><p><img src="https://image.white-than-wood.zone/lambdaTest/Automatic/Automatic_view.png"></p><p>查看自动化测试录屏详情.</p><p><img src="https://image.white-than-wood.zone/lambdaTest/Automatic/Automatic_detail.png"></p></li><li><p>下载.</p><p><img src="https://image.white-than-wood.zone/lambdaTest/Automatic/Automatic_video.png"></p></li><li><p>Debug.</p><p><img src="https://image.white-than-wood.zone/lambdaTest/Automatic/Automatic_debug.png"></p></li><li><p>网络资源接口加载情况.</p><p><img src="https://image.white-than-wood.zone/lambdaTest/Automatic/Automatic_network.png"></p></li><li><p>结论.</p><p>Selenium IDE确实是测试工具领域最为流行的一种可视化、自动化测试工具,作为Chrome extensions,无需编辑一行代码就可实现可视化、自动化测试,可视化编辑测试用例流畅并且效果顶级.</p><p>而lambdaTest在线自动化测试更是与Selenium配合的相得益彰,做到了跨浏览器自动化可视化测试完备且成熟的流程,在不同的浏览器版本、浏览器类型、操作系统、品牌手机以及版本设备进行生成自动化测试都是简洁、易用且容易理解,只能说如果作为一个测试,现在可以说”大大真的解放了头脑和双手”.</p></li><li><p>问题.</p><p>其可视化测试,有时运行时会出现莫名问题,比如在寻找一些web页面上的DOM节点时查询不到,需要手动修改或者删除selenium-webdriver事件步骤,其定制性、精确性着实不如使用Jest配合selenium-webdriver编辑测试用例.</p></li></ul></li><li><p>selenium-webdriver.</p><p>浏览器自动化库,提供了许多浏览器自动化接口,用于测试web应用.除了通过npm安装selenium-webdriver之外,还需要安装浏览器相应的驱动.其相应的api和用法<a href='https://www.selenium.dev/selenium/docs/api/javascript/'>selenium-webdriver</a>.</p><p>在new一个WebDriver的过程中,selenium首先会确认浏览器的native component是否存在可用而且匹配的版本,然后就在目标浏览器里启动一整套Web Service,这套Web Service使用了selenium自己设计定义的协议,名字叫做The WebDriver Wire Protocol.这套协议非常之强大,几乎可以操作浏览器做任何事情,包括打开、关闭、最大化、最小化、元素定位、元素点击、上传文件等等.其配合Jest写测试用例再搭配lambdaTest,可自定义各种测试套件在不同的浏览器版本以及操作系统上实现定制且容易理解的自动化测试.</p></li></ul></li><li><p>App自动化测试工具.</p><ul><li><p>Appium.</p><p>Appium是一个开源的,适用于原生或混合移动应用(hybrid mobile apps)的自动化测试工具,Appium应用WebDriver:JSON wire protocol驱动安卓和iOS移动应用,也是App自动化测试工具领域最为流行的一种测试套件.</p><ul><li>Appium支持多App平台(Android、iOS等);</li><li>Appium支持多语言(python、java、ruby、js、c#等),Appium选择了Client/Server的设计模式,只要client能够发送http请求给server,那么client用什么语言来实现都是可以的,这就是如何做到支持多语言的原因;</li><li>Appium是跨平台的,可以用在OSX,Windows以及Linux桌面系统上运行;</li><li>Appium扩展了WebDriver的协议,这样的好处是以前的WebDriver API能够直接被继承过来,以前的WebDriver各种语言的binding都可以拿来就用,省去了为浏览器、App端各开发一个client的工作量;</li><li>Appium开源免费;</li></ul></li><li><p>lambdaTest配合Appium.</p><ul><li><p>配置.</p><p>使用JS WebDriverIO With Appium以及配合lambdaTest的配置是有点小复杂的,官方有具体的一步一步实现的文章: <a href='https://www.lambdatest.com/support/docs/appium-nodejs-webdriverio/'>WebDriverIO With Appium</a>.</p></li></ul></li></ul></li></ul>]]></content>
<categories>
<category> testing </category>
</categories>
<tags>
<tag> lambdaTest </tag>
<tag> testing </tag>
<tag> automation </tag>
<tag> manual </tag>
<tag> real device </tag>
<tag> selenium </tag>
</tags>
</entry>
<entry>
<title>think of typescript</title>
<link href="/2022/05/26/thinkoftypescript/"/>
<url>/2022/05/26/thinkoftypescript/</url>
<content type="html"><![CDATA[<h1 id="typescript-配置"><a href="#typescript-配置" class="headerlink" title="typescript 配置"></a>typescript 配置</h1><h2 id="“module”-“umd”"><a href="#“module”-“umd”" class="headerlink" title="{“module”: “umd”}"></a>{“module”: “umd”}</h2><blockquote><p>问题1</p></blockquote><p> 近期在配置 typescript 配合 webpack 构建打包项目时发现,tsconfig.json 里配置模块导入导出模式为 {“module”:”umd”},webpack 使用 ts-loader 来处理,导入导出的模块模式同样使用 {libraryTarget: ‘umd’} or output -> library: {type: ‘umd’},构建打包出来的模块竟然不识别(下图就是模块不识别的异常)……反之,我配置为 {“module”:”commonjs”},webpack 采用同样的配置,构建打包出来的模块就是可以识别的,并且运行很正常.</p><ul><li><p>奇事.</p><ul><li>标准 “umd” 中不是包含 “commonjs” 吗?</li><li>webpack 为啥在模块导入导出模式中会出现只识别 typescript {“module”:”commonjs”},而不识别 typescript {“module”:”umd”} 呢?</li></ul></li><li><p>异常.</p></li></ul><p> <img src="https://image.white-than-wood.zone/typescript/typescript_module_umd.png"></p><ul><li><p>原因.</p><p>其根本原因,是 typescript 的对于模块导入导出的 “umd” 模式并没有遵从统一的 “umd” 标准来.</p><ul><li><p>“umd” 标准.</p><p><a href='https://white-than-wood.github.io/2022/05/23/thinkofjsmodule/#%E6%A8%A1%E5%9D%97%E5%AF%BC%E5%85%A5%E5%AF%BC%E5%87%BA%E7%9A%84%E5%8E%86%E5%8F%B2'>think of JsModule -> 模块导入导出的历史 -> “umd”部分</a></p></li><li><p>typescript “umd”.</p><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></pre></td><td class="code"><pre><span class="line">(<span class="keyword">function</span> (<span class="params">factory</span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="variable language_">module</span> === <span class="string">'object'</span> && <span class="keyword">typeof</span> <span class="variable language_">module</span>.<span class="property">exports</span> === <span class="string">'object'</span>) {</span><br><span class="line"> <span class="keyword">var</span> v = <span class="title function_">factory</span>(<span class="built_in">require</span>, <span class="built_in">exports</span>); <span class="keyword">if</span> (v !== <span class="literal">undefined</span>) <span class="variable language_">module</span>.<span class="property">exports</span> = v;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="keyword">typeof</span> define === <span class="string">'function'</span> && define.<span class="property">amd</span>) {</span><br><span class="line"> <span class="title function_">define</span>([<span class="string">"require"</span>, <span class="string">"exports"</span>], factory);</span><br><span class="line"> }</span><br><span class="line">})(<span class="keyword">function</span> (<span class="params"><span class="built_in">require</span>, <span class="built_in">exports</span></span>) {});</span><br></pre></td></tr></table></figure></li></ul></li><li><p>结论.</p><p>通过两套 “umd” 配置可以看出,没有标准 “umd” 条件中的第三种处理,允许导出到 window.namesapace = export; 因此,当大量开发人员需要同时支持所有这三个时,当前的 typescript {“module”:”umd”} 模块导入导出机制是非常糟糕的,webpack也是基于 typescript {“module”:”umd”} 产生的这种问题,对 typescript {“module”:”umd”} 模块导入导出模式是不支持的,所以会出现只识别 typescript {“module”:”commonjs”},不识别 typescript {“module”:”umd”} 的奇事发生.</p></li></ul><h2 id="“baseUrl”-“-x2F-“-“path”-“-”-“src-x2F-module”"><a href="#“baseUrl”-“-x2F-“-“path”-“-”-“src-x2F-module”" class="headerlink" title="{“baseUrl”: “./“, “path”: {“@”: “src/module”}}"></a>{“baseUrl”: “./“, “path”: {“@”: “src/module”}}</h2><blockquote><p>问题1</p></blockquote><p> 近期在配置 webpack 时,发现为了省略前缀通过 webpack resolve 配置解析 “alias”,竟然对 “typescript” 没有任何用处.</p> <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></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="variable language_">module</span>.<span class="property">exports</span> = {</span><br><span class="line"><span class="comment">//...</span></span><br><span class="line"> <span class="attr">resolve</span>: {</span><br><span class="line"> <span class="attr">alias</span>: {</span><br><span class="line"> <span class="string">"@"</span>: path.<span class="title function_">resolve</span>(__dirname, <span class="string">"src"</span>, <span class="string">"module"</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><br></pre></td></tr></table></figure><p> 后来通过查阅发现,必须配置 tsconfig.json 中省略前缀才可生效.</p> <figure class="highlight json"><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="punctuation">{</span></span><br><span class="line"> <span class="attr">"compilerOptions"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"baseUrl"</span><span class="punctuation">:</span> <span class="string">"./"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"path"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"@"</span><span class="punctuation">:</span> <span class="string">"src/module"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category> typescript </category>
</categories>
<tags>
<tag> javascript module </tag>
<tag> webpack </tag>
<tag> typescript </tag>
</tags>
</entry>
<entry>
<title>think of postcss</title>
<link href="/2022/05/25/thinkofpostcss/"/>
<url>/2022/05/25/thinkofpostcss/</url>
<content type="html"><![CDATA[<h1 id="postcss-配置"><a href="#postcss-配置" class="headerlink" title="postcss 配置"></a>postcss 配置</h1><blockquote><p>介绍</p></blockquote><p> postcss 在概念上主要是为了使新的 css 样式以及特性在更多种类的浏览器上得到兼容.</p><ul><li>css 新样式.</li></ul> <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></pre></td><td class="code"><pre><span class="line"><span class="comment">/*经过 postcss 转译之前*/</span></span><br><span class="line"><span class="selector-class">.dom</span> {</span><br><span class="line"> user-select: none; </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></pre></td><td class="code"><pre><span class="line"><span class="comment">/*经过 postcss 转译之后*/</span></span><br><span class="line"><span class="selector-class">.dom</span> {</span><br><span class="line"> -webkit-user-select:none;</span><br><span class="line"> -moz-user-select:none;</span><br><span class="line"> -ms-user-select:none;</span><br><span class="line"> user-select:none </span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>css新特性.</li></ul> <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></pre></td><td class="code"><pre><span class="line"><span class="comment">/*经过 postcss 转译之前*/</span></span><br><span class="line"><span class="selector-class">.dom</span> {</span><br><span class="line"> <span class="attribute">color</span>: <span class="number">#12345678</span>; </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></pre></td><td class="code"><pre><span class="line"><span class="comment">/*经过 postcss 转译之后*/</span></span><br><span class="line"><span class="selector-class">.dom</span> {</span><br><span class="line"> <span class="attribute">color</span>: <span class="built_in">rgba</span>(<span class="number">18</span>,<span class="number">52</span>,<span class="number">86</span>,.<span class="number">471</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><blockquote><p>工具</p></blockquote><p> 现阶段 postcss 转译的工具使用率比较多的有两种: autoprefixer 以及 postcss-preset-env.</p><ul><li><p>autoprefixer.</p><p>autoprefixer 是将 css 的新样式在更多种类的浏览器中兼容.</p><ul><li><p>步骤.</p><p>首先下载 npm 依赖包 postcss-loader 和 autoprefixer.</p><pre><code>npm install autoprefixer postcss-loader css-loader style-loader -D</code></pre><p>之后创建 postcss.config.js 文件,配置使用插件.</p><pre><code>touch postcss.config.js</code></pre><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></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = {</span><br><span class="line"> <span class="attr">plugins</span>: [</span><br><span class="line"> <span class="built_in">require</span>(<span class="string">'autoprefixer'</span>)</span><br><span class="line"> ]</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>配置 webpack 构建打包 postcss-loader.</p><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></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = {</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> <span class="attr">module</span>: {</span><br><span class="line"> <span class="attr">rules</span>: [{</span><br><span class="line"> <span class="attr">test</span>: <span class="regexp">/.css$/</span>,</span><br><span class="line"> <span class="attr">use</span>: [{</span><br><span class="line"> <span class="attr">loader</span>: <span class="string">'style-loader'</span></span><br><span class="line"> }, {</span><br><span class="line"> <span class="attr">loader</span>: <span class="string">'css-loader'</span>,</span><br><span class="line"> <span class="attr">importLoaders</span>: <span class="number">1</span></span><br><span class="line"> }, {</span><br><span class="line"> <span class="attr">loader</span>: <span class="string">'postcss-loader'</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><br></pre></td></tr></table></figure><p>当然也可以直接不创建 postcss.config.js 文件,在 webpack 构建打包 postcss-loader 配置.</p><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></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = {</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> <span class="attr">module</span>: {</span><br><span class="line"> <span class="attr">rules</span>: [{</span><br><span class="line"> <span class="attr">test</span>: <span class="regexp">/.css$/</span>,</span><br><span class="line"> <span class="attr">use</span>: [{</span><br><span class="line"> <span class="attr">loader</span>: <span class="string">'style-loader'</span></span><br><span class="line"> }, {</span><br><span class="line"> <span class="attr">loader</span>: <span class="string">'css-loader'</span>,</span><br><span class="line"> <span class="attr">importLoaders</span>: <span class="number">1</span></span><br><span class="line"> }, {</span><br><span class="line"> <span class="attr">loader</span>: <span class="string">'postcss-loader'</span>,</span><br><span class="line"> <span class="attr">options</span>: {</span><br><span class="line"> <span class="attr">postcssOptions</span>: {</span><br><span class="line"> <span class="attr">plugins</span>: [</span><br><span class="line"> <span class="built_in">require</span>(<span class="string">'autoprefixer'</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><br><span class="line"> <span class="comment">//...</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>配置 package.json -> browserslist 属性,也就是 postcss 这种扩展兼容操作要选择适用哪些种类浏览器的版本,这一部分我们会拿出来在后续部分细讲.</p></li><li><p>缺点.</p><p>只是对 css 的新样式在更多种类的浏览器上做兼容,新特性被排除在外,所以 autoprefixer 所做出的扩展兼容是不完整的.</p></li></ul></li><li><p>postcss-preset-env.</p><p>postcss-preset-env 是将 css 的新样式以及新特性在更多种类的浏览器中兼容,内置 autoprefixer,含有 autoprefixer 的功能.</p><ul><li><p>步骤.</p><p>首先下载 npm 依赖包 postcss-preset-env 和 postcss-loader.</p><pre><code>npm install postcss-preset-env postcss-loader -D</code></pre><p>之后创建 postcss.config.js 文件,配置使用插件.</p><pre><code>touch postcss.config.js</code></pre><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></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = {</span><br><span class="line"> <span class="attr">plugins</span>: [</span><br><span class="line"> <span class="built_in">require</span>(<span class="string">'postcss-preset-env'</span>)</span><br><span class="line"> ]</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>配置 webpack 构建打包 postcss-loader.</p><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></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = {</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> <span class="attr">module</span>: {</span><br><span class="line"> <span class="attr">rules</span>: [{</span><br><span class="line"> <span class="attr">test</span>: <span class="regexp">/.css$/</span>,</span><br><span class="line"> <span class="attr">use</span>: [{</span><br><span class="line"> <span class="attr">loader</span>: <span class="string">'style-loader'</span></span><br><span class="line"> }, {</span><br><span class="line"> <span class="attr">loader</span>: <span class="string">'css-loader'</span>,</span><br><span class="line"> <span class="attr">importLoaders</span>: <span class="number">1</span></span><br><span class="line"> }, {</span><br><span class="line"> <span class="attr">loader</span>: <span class="string">'postcss-loader'</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><br></pre></td></tr></table></figure><p>当然也可以直接不创建 postcss.config.js 文件,在 webpack 构建打包 postcss-loader 配置.</p><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></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = {</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> <span class="attr">module</span>: {</span><br><span class="line"> <span class="attr">rules</span>: [{</span><br><span class="line"> <span class="attr">test</span>: <span class="regexp">/.css$/</span>,</span><br><span class="line"> <span class="attr">use</span>: [{</span><br><span class="line"> <span class="attr">loader</span>: <span class="string">'style-loader'</span></span><br><span class="line"> }, {</span><br><span class="line"> <span class="attr">loader</span>: <span class="string">'css-loader'</span>,</span><br><span class="line"> <span class="attr">importLoaders</span>: <span class="number">1</span></span><br><span class="line"> }, {</span><br><span class="line"> <span class="attr">loader</span>: <span class="string">'postcss-loader'</span>,</span><br><span class="line"> <span class="attr">options</span>: {</span><br><span class="line"> <span class="attr">postcssOptions</span>: {</span><br><span class="line"> <span class="attr">plugins</span>: [</span><br><span class="line"> <span class="built_in">require</span>(<span class="string">'postcss-preset-env'</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><br><span class="line"> <span class="comment">//...</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>配置 package.json -> browserslist 属性,也就是 postcss 这种扩展兼容操作要选择适用哪些种类浏览器的版本,这一部分我们会拿出来在后续部分细讲.</p></li></ul></li></ul><blockquote><p>browserslist</p></blockquote><p> postcss 对 css 新样式以及新特性要选择适用哪些种类浏览器的版本所对应的属性.推荐在 package.json 文件里直接进行配置.</p><ul><li><p>默认配置.</p><p>默认配置 [“defaults”] 实际上的含义是 [“>0.2%”,”last 2 versions”,”not dead”],是这三种筛选适用浏览器版本的方式的并集,这三种方式具体的含义在下一部分会具体介绍.</p></li></ul> <figure class="highlight json"><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="punctuation">{</span></span><br><span class="line"> <span class="attr">"browserslist"</span><span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line"> <span class="string">"defaults"</span></span><br><span class="line"> <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure><ul><li><p>筛选适用浏览器版本方式枚举.</p><ul><li>[“> n%”]: 选择用户比例大于n%的所有种类的浏览器版本.</li><li>[“last n versions”]: 选择所有种类的浏览器的最近n个版本.</li><li>[“not dead”]: 选择官方并没有舍弃删除掉的所有种类的浏览器版本.</li></ul><p>所以 [“defaults”] 的含义就是选择用户比例大于0.2%的、官方并没有舍弃删除掉的、所有种类的浏览器的最近2个版本的并集.</p></li><li><p>其他配置.</p><ul><li>[“>0.2% and last 2 versions and not dead”]: and表示”且”也就是交集,此🌰的含义为选择用户比例大于0.2%的、官方并没有舍弃删除掉的、所有种类的浏览器的最近2个版本的交集.</li><li>[“>0.2% or last 2 versions or not dead”]: 除了”,”之外,”or”也可以表示交集.跟 [“defaults”] 的含义相同.</li><li>[“not > 0.2%”]: “not”代表”非”也就是非集,此🌰的含义为选择用户比例小于等于0.2%的所有种类的浏览器的版本.</li></ul></li></ul>]]></content>
<categories>
<category> postcss </category>
</categories>
<tags>
<tag> webpack </tag>
<tag> postcss </tag>
</tags>
</entry>
<entry>
<title>think of JsModule</title>
<link href="/2022/05/23/thinkofjsmodule/"/>
<url>/2022/05/23/thinkofjsmodule/</url>
<content type="html"><![CDATA[<h1 id="模块导入导出的历史"><a href="#模块导入导出的历史" class="headerlink" title="模块导入导出的历史"></a>模块导入导出的历史</h1><blockquote><p>JsModule 的演化经历</p></blockquote><p> 下面这张图可以清晰的看出,javascript module 演化的历史,由最初的 commonjs 到最终方案 esm,而现在正处于 umd -> esm 阶段.</p><p> <img src="https://image.white-than-wood.zone/jsmodule/js_module_history.png"></p><blockquote><p>commonjs</p></blockquote><p> commonjs 的特性就是其导入不是在编译器编译时执行的,而是在代码执行时才实行的.其特性导致了两个特点: 动态导入和赋值复制.</p><ul><li>动态导入.</li></ul><p> 下面这段 js 代码完美诠释了此含义,在此判断为 true 的情况下的 commonjs,才会导入 selectivizr,并实行 selectivizr 中的脚本.</p> <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></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(browser.<span class="property">desktop</span> && browser.<span class="property">msie</span> && browser.<span class="property">versionNumber</span> < <span class="number">9</span>){</span><br><span class="line"> <span class="built_in">require</span>(<span class="string">'selectivizr'</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ul><li>赋值复制.</li></ul> <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></pre></td><td class="code"><pre><span class="line"><span class="comment">// module.js</span></span><br><span class="line"><span class="keyword">let</span> count = <span class="number">4</span>;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">add</span>(<span class="params"></span>) {</span><br><span class="line"> count++;</span><br><span class="line">}</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = {</span><br><span class="line"> count,</span><br><span class="line"> add</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><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">// index.js</span></span><br><span class="line"><span class="keyword">const</span> {count, add} = <span class="built_in">require</span>(<span class="string">'./module.js'</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'count:'</span>, count);</span><br><span class="line"><span class="title function_">add</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'count:'</span>, count); </span><br></pre></td></tr></table></figure><p> 上面这两段 js 代码完美诠释了此含义,其结果为:</p><pre><code>count: 4count: 4</code></pre><p> 可以看出 commonjs 对于导出的变量以及函数都是代码执行时直接复制其值,而不是连同引用一起导出,导致通过模块内部修改变量的值之后,在外部导入模块变量并没有发现其值发生变化.</p><ul><li><p>优势和劣势.</p><ul><li><p>优势: </p><ul><li>导入比较灵活;</li><li>NodeJS 模块导入导出完全采用 commonjs 模式,npm 上绝大部分的依赖库都会兼容 commonjs 模块导入导出,适用范围很广泛;</li><li>同步模块加载;</li><li>良好的团队维护,完备的社区/论坛;</li></ul></li><li><p>劣势: </p><ul><li>不支持静态分析,静态分析所带来的一系列福利不能在 commonjs 模块导入导出模式下实行;</li><li>不能实行异步模块加载;</li></ul></li></ul></li></ul><blockquote><p>amd(cmd)</p></blockquote><p> amd(cmd) 的适用范围很窄,受众面也远远没有 commonjs 和 esm 广泛,因为受限于第三方库的环境依赖(无论是 SeaJS,还是 RequireJS 都需要事先下载依赖).</p><ul><li><p>优势和劣势.</p><ul><li><p>优势: </p><ul><li>支持同步/异步模块加载,amd 近似于同步模块导入导出(与 commonjs 同步模块加载有着本质的不同),cmd 异步模块导入导出(与 esm 异步模块加载也有着本质的不同);</li></ul></li><li><p>劣势: </p><ul><li>不支持静态分析,静态分析所带来的一系列福利不能在 amd(cmd) 模块导入导出模式下实行;</li><li>受限于第三方库的环境依赖;</li><li>写法上很不友好;</li><li>适用范围很窄,没有类 NodeJS、npm 以及 ECMAScript 标准这种受众面很广泛的’推手’推动;</li><li>社区/论坛不成熟,维护一般;</li></ul></li></ul></li></ul><blockquote><p>umd</p></blockquote><p> umd,全称: Universal Module Definition,其标准为: <a href='https://github.com/umdjs/umd/blob/master/templates/commonjsStrictGlobal.js'>commonjsStrictGlobal</a>以及<a href='https://github.com/umdjs/umd/blob/master/templates/returnExportsGlobal.js'>returnExportsGlobal</a>.</p><ul><li>commonjsStrictGlobal.</li></ul> <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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Uses CommonJS, AMD or browser globals to create a module. This example</span></span><br><span class="line"><span class="comment">// creates a global even when AMD is used. This is useful if you have some</span></span><br><span class="line"><span class="comment">// scripts that are loaded by an AMD loader, but they still want access to</span></span><br><span class="line"><span class="comment">// globals. If you do not need to export a global for the AMD case, see</span></span><br><span class="line"><span class="comment">// commonjsStrict.js.</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// If you just want to support Node, or other CommonJS-like environments that</span></span><br><span class="line"><span class="comment">// support module.exports, and you are not creating a module that has a</span></span><br><span class="line"><span class="comment">// circular dependency, then see returnExportsGlobal.js instead. It will allow</span></span><br><span class="line"><span class="comment">// you to export a function as the module value.</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Defines a module "commonJsStrictGlobal" that depends another module called</span></span><br><span class="line"><span class="comment">// "b". Note that the name of the module is implied by the file name. It is</span></span><br><span class="line"><span class="comment">// best if the file name and the exported global have matching names.</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// If the 'b' module also uses this type of boilerplate, then</span></span><br><span class="line"><span class="comment">// in the browser, it will create a global .b that is used below.</span></span><br><span class="line"></span><br><span class="line">(<span class="keyword">function</span> (<span class="params">root, factory</span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> define === <span class="string">'function'</span> && define.<span class="property">amd</span>) {</span><br><span class="line"> <span class="comment">// AMD. Register as an anonymous module.</span></span><br><span class="line"> <span class="title function_">define</span>([<span class="string">'exports'</span>, <span class="string">'b'</span>], <span class="keyword">function</span> (<span class="params"><span class="built_in">exports</span>, b</span>) {</span><br><span class="line"> <span class="title function_">factory</span>((root.<span class="property">commonJsStrictGlobal</span> = <span class="built_in">exports</span>), b);</span><br><span class="line"> });</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="built_in">exports</span> === <span class="string">'object'</span> && <span class="keyword">typeof</span> <span class="built_in">exports</span>.<span class="property">nodeName</span> !== <span class="string">'string'</span>) {</span><br><span class="line"> <span class="comment">// CommonJS</span></span><br><span class="line"> <span class="title function_">factory</span>(<span class="built_in">exports</span>, <span class="built_in">require</span>(<span class="string">'b'</span>));</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// Browser globals</span></span><br><span class="line"> <span class="title function_">factory</span>((root.<span class="property">commonJsStrictGlobal</span> = {}), root.<span class="property">b</span>);</span><br><span class="line"> }</span><br><span class="line">}(<span class="keyword">typeof</span> self !== <span class="string">'undefined'</span> ? self : <span class="variable language_">this</span>, <span class="keyword">function</span> (<span class="params"><span class="built_in">exports</span>, b</span>) {</span><br><span class="line"> <span class="comment">// Use b in some fashion.</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// attach properties to the exports object to define</span></span><br><span class="line"> <span class="comment">// the exported module properties.</span></span><br><span class="line"> <span class="built_in">exports</span>.<span class="property">action</span> = <span class="keyword">function</span> (<span class="params"></span>) {};</span><br><span class="line">}));</span><br></pre></td></tr></table></figure><ul><li>returnExportsGlobal.</li></ul> <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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Uses CommonJS, AMD or browser globals to create a module.</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// If you just want to support Node, or other CommonJS-like environments that</span></span><br><span class="line"><span class="comment">// support module.exports, and you are not creating a module that has a</span></span><br><span class="line"><span class="comment">// circular dependency, then see returnExports.js instead. It will allow</span></span><br><span class="line"><span class="comment">// you to export a function as the module value.</span></span><br><span class="line"> </span><br><span class="line"><span class="comment">// Defines a module "commonJsStrict" that depends another module called "b".</span></span><br><span class="line"><span class="comment">// Note that the name of the module is implied by the file name. It is best</span></span><br><span class="line"><span class="comment">// if the file name and the exported global have matching names.</span></span><br><span class="line"> </span><br><span class="line"><span class="comment">// If the 'b' module also uses this type of boilerplate, then</span></span><br><span class="line"><span class="comment">// in the browser, it will create a global .b that is used below.</span></span><br><span class="line"> </span><br><span class="line"><span class="comment">// If you do not want to support the browser global path, then you</span></span><br><span class="line"><span class="comment">// can remove the `root` use and the passing `this` as the first arg to</span></span><br><span class="line"><span class="comment">// the top function.</span></span><br><span class="line"> </span><br><span class="line">(<span class="keyword">function</span> (<span class="params">root, factory</span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> define === <span class="string">'function'</span> && define.<span class="property">amd</span>) {</span><br><span class="line"> <span class="comment">// AMD. Register as an anonymous module.</span></span><br><span class="line"> <span class="title function_">define</span>([<span class="string">'exports'</span>, <span class="string">'b'</span>], factory);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="keyword">typeof</span> <span class="built_in">exports</span> === <span class="string">'object'</span> && <span class="keyword">typeof</span> <span class="built_in">exports</span>.<span class="property">nodeName</span> !== <span class="string">'string'</span>) {</span><br><span class="line"> <span class="comment">// CommonJS</span></span><br><span class="line"> <span class="title function_">factory</span>(<span class="built_in">exports</span>, <span class="built_in">require</span>(<span class="string">'b'</span>));</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// Browser globals</span></span><br><span class="line"> <span class="title function_">factory</span>((root.<span class="property">commonJsStrict</span> = {}), root.<span class="property">b</span>);</span><br><span class="line"> }</span><br><span class="line">}(<span class="keyword">typeof</span> self !== <span class="string">'undefined'</span> ? self : <span class="variable language_">this</span>, <span class="keyword">function</span> (<span class="params"><span class="built_in">exports</span>, b</span>) {</span><br><span class="line"> <span class="comment">// Use b in some fashion.</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// attach properties to the exports object to define</span></span><br><span class="line"> <span class="comment">// the exported module properties.</span></span><br><span class="line"> <span class="built_in">exports</span>.<span class="property">action</span> = <span class="keyword">function</span> (<span class="params"></span>) {};</span><br><span class="line">}));</span><br></pre></td></tr></table></figure><p> 从源码中可以看出 umd 是对于 commonjs、Node(webpack中值枚举为commonjs2)、amd 以及 Browser globals 的并集,是实行兼容的一种模块导入导出模式.</p><ul><li><p>优势和劣势.</p><ul><li><p>优势: </p><ul><li>兼容的这几种模块导入导出都支持同步模块加载;</li><li>导入比较灵活;</li><li>导出的类型更丰富;</li></ul></li><li><p>劣势: </p><ul><li>不支持静态分析,静态分析所带来的一系列福利也不能在 umd 模块导入导出模式下实行;</li><li>不能实行异步模块加载;</li></ul></li></ul></li></ul><blockquote><p>esm(ecmascript module)</p></blockquote><p> 模块导入导出的最终方案模式,也是现在 NodeJS、npm 以及 ECMAScript 标准这些受众面很广泛的’推手’主要推动的模块导入导出模式. 其导入是在编译器编译阶段,由此特性也导致了两个特点: 静态分析和赋值引用,与 commonjs 的特性与特点完全相反.</p><ul><li>NodeJS ESM.</li></ul><p> 混用阶段,也就是 import 配合 module.exports,require 配合 export. </p><p> 注意在 import 配合 module.export 这部分,import 必须导入 module.exports 导出的模块,不能导入 exports 导出的模块,由于还是以commonjs模块导出,那么esm导入就不能实行静态分析,只能整体导入.</p> <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></pre></td><td class="code"><pre><span class="line"><span class="comment">//module.js</span></span><br><span class="line"><span class="keyword">const</span> count = <span class="number">4</span>;</span><br><span class="line"><span class="comment">//NodeJS ESM 必须使用 module.exports 导出</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = {</span><br><span class="line"> count </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><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">//index.js</span></span><br><span class="line"><span class="comment">//以 commonjs 模块导出,esm 导入不能实行静态分析,只能整体导入.</span></span><br><span class="line"><span class="comment">//import {count} from './module.js';</span></span><br><span class="line"><span class="keyword">import</span> <span class="variable language_">module</span> <span class="keyword">from</span> <span class="string">'./module.js'</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">module</span>.<span class="property">count</span>);</span><br></pre></td></tr></table></figure><ul><li>静态分析.</li></ul><p> 其导入是在编译器编译阶段,所以需要将所使用的模块都在所要导入文件的头部进行导入.可对其引入的值、函数或者模块可进行静态分析.</p> <figure class="highlight javascript"><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">//module.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> count = <span class="number">4</span>;</span><br></pre></td></tr></table></figure> <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></pre></td><td class="code"><pre><span class="line"><span class="comment">//index.js</span></span><br><span class="line"><span class="comment">//对其引入的值、函数或者模块可进行静态分析</span></span><br><span class="line"><span class="keyword">import</span> {count} <span class="keyword">from</span> <span class="string">'./module.js'</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(count);</span><br></pre></td></tr></table></figure><ul><li>赋值引用.</li></ul> <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></pre></td><td class="code"><pre><span class="line"><span class="comment">//module.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">let</span> count = <span class="number">4</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">add</span>(<span class="params"></span>) {</span><br><span class="line"> count++;</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><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">//index.js</span></span><br><span class="line"><span class="keyword">import</span> {count, add} <span class="keyword">from</span> <span class="string">'./module.js'</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'count:'</span>, count);</span><br><span class="line"><span class="title function_">add</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'count:'</span>, count);</span><br></pre></td></tr></table></figure><p> 上面这两段 js 代码与 commonjs 赋值复制部分是同一个🌰,但是执行结果却是不相同的,其结果为:</p><pre><code>count: 4count: 5</code></pre><p> 可以看出 esm 对于导出的变量以及函数都是编译器编译时连同引用一起导出,导致通过模块内部修改变量的值之后,在外部导入模块变量的值也发生了改变.</p><ul><li><p>优势和劣势.</p><ul><li><p>优势: </p><ul><li>支持静态分析,静态分析所带来的一系列福利都可接收;</li><li>可实行异步模块加载;支持动态导入 import();</li><li>良好的团队维护,完备的社区/论坛;</li></ul></li><li><p>劣势:</p><ul><li>现阶段 NodeJS、npm 以及 ECMAScript 标准这些受众面很广泛的’推手’因历史、兼容、底层改动大等问题,实现的都不成熟,还需要 Webpack/Babel 等工具进行转译;</li></ul></li></ul></li></ul>]]></content>
<categories>
<category> javascript module </category>
</categories>
<tags>
<tag> commonjs </tag>
<tag> esm </tag>
<tag> amd </tag>
<tag> cmd </tag>
<tag> umd </tag>
<tag> javascript module </tag>
</tags>
</entry>
<entry>
<title>think of webpack</title>
<link href="/2022/05/23/thinkofwebpack/"/>
<url>/2022/05/23/thinkofwebpack/</url>
<content type="html"><![CDATA[<h1 id="webpack-配置"><a href="#webpack-配置" class="headerlink" title="webpack 配置"></a>webpack 配置</h1><h2 id="output"><a href="#output" class="headerlink" title="output"></a>output</h2><blockquote><p>问题1</p></blockquote><p> 近期在配置 webpack 时,发现 output -> libraryType: ‘umd’ or output -> library: {type: ‘umd’} 打包构建导出的模块无法实行 TreeShaking.为什么?</p><ul><li><p>介绍.</p><p>在配置 webpack 的输出 output 时,都会遇到输出模块的配置,也就是 webpack4 中的 libraryType 以及 webpack5 中的 libraryType(未删但新特性不更新) and library: {type: ‘xxx’},首先解释一下这几个属性的含义,libraryType 指的是每个 chunk 模块以怎样的模块形式构建打包输出,而 library: {type: ‘xxx’} 在webpack5 中属于对 libraryType 的复写,跟 libraryType 是一个含义.</p><ul><li><p>libraryType 的值枚举.</p><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></pre></td><td class="code"><pre><span class="line"><span class="variable language_">module</span>.<span class="property">exports</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">//'commonjs' | 'commonjs2' | 'amd' | 'umd' | 'this' | 'var' | 'global' | 'module'</span></span><br><span class="line"> <span class="attr">output</span>: {</span><br><span class="line"> <span class="attr">libraryType</span>: <span class="string">'commonjs'</span> </span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure></li><li><p>libraryType 中包含模块导入导出的值枚举.</p><p>‘commonjs’ | ‘commonjs2’ | ‘amd’ | ‘umd’ | ‘module’ 这五个值枚举代表了模块导入导出历史的发展轨迹,由最初的 commonjs 到最终方案 esm,关于它们的含义以及对比,这里把它放在了<a href='https://white-than-wood.github.io/2022/05/23/thinkofjsmodule/#%E6%A8%A1%E5%9D%97%E5%AF%BC%E5%85%A5%E5%AF%BC%E5%87%BA%E7%9A%84%E5%8E%86%E5%8F%B2'>think of JsModule#模块导入导出的历史</a>中,这部分非常重要,是为后续下文的理解做铺垫.</p></li><li><p>TreeShaking.</p><p>TreeShaking 最初是 <a href='https://rollupjs.org/guide/en/'>RollUp</a> 团队推出的一个方案,以 esm 模块导入导出模式的静态分析为依赖基础,在编译时对项目代码进行瘦身,将无用、空引入的代码模块不构建进打出的包中,减少代码体积,减少项目冗余,提高项目运行速度的模式.</p></li><li><p>import umd.</p><p>对于 umd 的 esm import 导入,在 commonjs、NodeJS 环境下都可以实现,但是必须注意 <a href='https://white-than-wood.github.io/2022/05/23/thinkofjsmodule/#%E6%A8%A1%E5%9D%97%E5%AF%BC%E5%85%A5%E5%AF%BC%E5%87%BA%E7%9A%84%E5%8E%86%E5%8F%B2'>esm(ecmascript module)</a> NodeJS ESM部分.</p></li></ul></li><li><p>结论.</p><p>由于上述 umd 所兼容的所有模块导入导出模式不能实现静态分析,包括 import 配合 module.exports (使用 esm import 导入 commonjs2 值枚举导出的模块),而 TreeShaking 又是以 esm 模块导入导出模式的静态分析为依赖基础的,所以 umd 模块导入导出模式不能实行 TreeShaking.</p></li></ul><h2 id="命令行"><a href="#命令行" class="headerlink" title="命令行"></a>命令行</h2><blockquote><p>问题1</p></blockquote><p> webpack5 中将命令行参数 –display-modules、–display-reasons 删掉了吗?</p><ul><li><p>介绍.</p><p>在 webpack5 中执行 webpack –display-modules –display-reasons 会报错.</p><pre><code>[webpack-cli] Error: Unknown option '--display-modules'[webpack-cli] Run 'webpack --help' to see available commands and options</code></pre></li><li><p>原因.</p><p>webpack5 对日志方面进行了规整,当然输出所有的模块以及输出所有的模块信息的原因也不例外,–display-modules、–display-reasons 迁移到了 <a href='https://webpack.js.org/configuration/stats/'>stats</a> 日志配置中,请查看 stats.modules、status.reasons.</p></li></ul><blockquote><p>问题2</p></blockquote><p> 在 package.json 中的 scripts 属性直接写 shell 脚本,为什么可以在命令行中直接执行而无需任何的目录前缀?</p><ul><li><p>介绍.</p><p>一般在使用 npm 的项目中,直接使用 node_modules/.bin 目录中的可执行文件即可运行 shell 脚本(bash 命令),那既然是这样,在 package.json 这个描述外部依赖的 json 文件中,为什么直接 scripts 属性写 shell 脚本,接着 npm run <scripts属性> 就可以直接运行可执行文件而无需通过 .bin 目录呢?</p></li><li><p>原因.</p><p>实际上在运行 npm run <scripts属性> 时,npm 会新建一个 shell 脚本,并把所对应的命令直接添加到 shell 脚本中执行,且将 node_modules/.bin 的子目录临时添加到 $PATH 全局变量中,执行完之后再恢复原状,故无需通过添加目录前缀直接执行.</p><p>PS: node_modules/.bin 目录中的可执行文件都是通过软链的方式连接依赖项目中的可执行文件的.</p></li></ul><h2 id="watch"><a href="#watch" class="headerlink" title="watch"></a>watch</h2><blockquote><p>介绍</p></blockquote><p> 在开发环境中,为了节约时间成本,提高开发人员的工作效率,根据项目内容的变化实时构建打包是很重要的,要不然,每一次都需要执行命令是很浪费时间. </p><p> watch 属性是针对这一服务来的,对项目内容实行监听,根据当前项目内容发生修改的时间点,与前一次进行内容对比(如果是第一次则直接构建打包),假如内容不一致,且在 aggregateTimeout 延时范围内没有再次发生内容修改,则触发 webpack 重新构建打包,由于过程建立作用在电脑磁盘,有 I/O 的过程,所以构建打包速度适中.</p><h2 id="watchOptions"><a href="#watchOptions" class="headerlink" title="watchOptions"></a>watchOptions</h2><blockquote><p>介绍</p></blockquote><p> 用于对 watch 属性配置参数的修改.</p><h3 id="ignored"><a href="#ignored" class="headerlink" title="ignored"></a>ignored</h3><blockquote><p>介绍</p></blockquote><p> 可配置不实行监听的目录,一般配置 /node_modules/,配置之后可提升构建打包的性能.</p><h3 id="poll"><a href="#poll" class="headerlink" title="poll"></a>poll</h3><blockquote><p>介绍</p></blockquote><p> 可理解为监听项目内容的频率,在 1s 时间段中查询项目内容发生修改时间点的次数.</p><h3 id="aggregateTimeout"><a href="#aggregateTimeout" class="headerlink" title="aggregateTimeout"></a>aggregateTimeout</h3><blockquote><p>介绍</p></blockquote><p> 当监听到两次发生修改的时间点的项目内容对比不一致时,不会立马触发重新构建打包,而是会有一段延时,也就是 aggregateTimeout,可以理解为传统意义上的’防抖’.</p><p> 如果在这段延时中再次出现项目内容发生修改,且进行对比后项目内容不一致,则合并到本次的构建打包中,aggregateTimeout 重新计算,以此闭环,直至在 aggregateTimeout 延时中不存在修改项目内容的情况,则执行构建打包.</p><h2 id="module"><a href="#module" class="headerlink" title="module"></a>module</h2><h3 id="enforce"><a href="#enforce" class="headerlink" title="enforce"></a>enforce</h3><blockquote><p>介绍</p></blockquote><p> module.rules 中的 enforce 属性可以设定 loader 装载转换的顺序,默认配置为 ‘pre’,也就是从右向左的顺序装载转换执行.如果想要设定转换时的顺序,可以配置为 ‘post’,这样会按照从左向右的顺序装载转换执行.</p><h3 id="css-loader"><a href="#css-loader" class="headerlink" title="css-loader"></a>css-loader</h3><blockquote><p>问题1</p></blockquote><p> css-loader 使用参数minimize进行样式表压缩处理,webpack 报错,为什么?</p><ul><li><p>介绍.</p><p>在 webpack css-loader 中参数使用 minimize 进行样式表压缩处理会报错.</p><pre><code>Module build failed (from ./node_modules/css-loader/dist/cjs.js):ValidationError: Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema.- options has an unknown property 'minimize'. These properties are valid: object { url?, import?, modules?, sourceMap?, importLoaders?, esModule?, exportType? }</code></pre></li><li><p>原因.</p><p>在 webpack 3.0 以及 css 1.0 以后,css-loader 不再支持参数 minimize 样式表压缩处理.</p></li></ul><h3 id="raw-loader"><a href="#raw-loader" class="headerlink" title="raw-loader"></a>raw-loader</h3><blockquote><p>问题1</p></blockquote><p> 在配置移动端分辨率适配时,采用的是手淘移动端适配策略: px2rem-loader (px 转换为 rem 转换器) + 内联( raw-loader )lib-flexible(动态计算元节点绝对像素值工具),期间发现网上大多数文章描述 raw-loader 内联资源不能下载使用最新版本,最新版本有问题,必须使用 raw-loader 0.5.1 版本,那按照正常思路来想,下载使用最新版本应该是更好的,有新的版本不使用,为什么还要使用旧版本呢?</p><ul><li><p>介绍.</p><p>使用最新版本 raw-loader,导出的结果变为了 [object Module],但如果使用 raw-loader 0.5.1 版本内联资源是正常的.</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></pre></td><td class="code"><pre><span class="line"><span class="comment"><!-- 最新版本 raw-loader 构建打包前 --></span></span><br><span class="line"><span class="meta"><!DOCTYPE <span class="keyword">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"zh-CN"</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">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, maximum-scalable=1.0, minimum-scalable=1.0"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>webpack<span class="tag"></<span class="name">title</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="language-javascript"><%= <span class="built_in">require</span>(<span class="string">'!!raw-loader!babel-loader!../node_modules/lib-flexible/flexible'</span>) %></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 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">"root"</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">html</span>></span></span><br></pre></td></tr></table></figure><figure class="highlight html"><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"><!-- 最新版本 raw-loader 构建打包后 --></span></span><br><span class="line"><span class="meta"><!doctype <span class="keyword">html</span>></span><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"zh-CN"</span>></span><span class="tag"><<span class="name">head</span>></span><span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span><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,maximum-scalable=1,minimum-scalable=1"</span>></span><span class="tag"><<span class="name">title</span>></span>webpack<span class="tag"></<span class="name">title</span>></span><span class="tag"><<span class="name">script</span>></span>[object Module]<span class="tag"></<span class="name">script</span>></span><span class="tag"><<span class="name">link</span> <span class="attr">href</span>=<span class="string">"./css/index.07954305.css"</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span>></span><span class="tag"></<span class="name">head</span>></span><span class="tag"><<span class="name">body</span>></span><span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"root"</span>></span><span class="tag"></<span class="name">div</span>></span><span class="tag"><<span class="name">script</span> <span class="attr">defer</span>=<span class="string">"defer"</span> <span class="attr">src</span>=<span class="string">"./js/index.9dbc4867b93d15abf8a6.js"</span>></span><span class="tag"></<span class="name">script</span>></span><span class="tag"></<span class="name">body</span>></span><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure></li><li><p>原因.</p><p>看了最新版本 raw-loader 的源码,它会默认将资源以 ESM 模式导出,故此在内联转化为字符串时,导出的结果变为了 [object Module].</p></li><li><p>改进.</p><p>raw-loader 参数 esModule 是用于配置资源是否以 ESM 模式导出,只要在内联转化资源时将参数 esModule 置为false,就可以正常的内联 lib-flexible 工具资源.</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></pre></td><td class="code"><pre><span class="line"><span class="comment"><!-- 最新版本 raw-loader 构建打包前 --></span></span><br><span class="line"><span class="meta"><!DOCTYPE <span class="keyword">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"zh-CN"</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">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, maximum-scalable=1.0, minimum-scalable=1.0"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>webpack<span class="tag"></<span class="name">title</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="language-javascript"><%= <span class="built_in">require</span>(<span class="string">'!!raw-loader?esModule=false!babel-loader!../node_modules/lib-flexible/flexible'</span>) %></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 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">"root"</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">html</span>></span></span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line"><span class="comment"><!-- 最新版本 raw-loader 构建打包后 --></span></span><br><span class="line"><span class="meta"><!doctype <span class="keyword">html</span>></span><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"zh-CN"</span>></span><span class="tag"><<span class="name">head</span>></span><span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span><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,maximum-scalable=1,minimum-scalable=1"</span>></span><span class="tag"><<span class="name">title</span>></span>webpack<span class="tag"></<span class="name">title</span>></span><span class="tag"><<span class="name">script</span>></span><span class="language-javascript">;</span></span><br><span class="line"><span class="language-javascript"> (<span class="keyword">function</span> (<span class="params">win, lib</span>) {</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">var</span> doc = win.<span class="property">document</span>;</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">var</span> docEl = doc.<span class="property">documentElement</span>;</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">var</span> metaEl = doc.<span class="title function_">querySelector</span>(<span class="string">'meta[name="viewport"]'</span>);</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">var</span> flexibleEl = doc.<span class="title function_">querySelector</span>(<span class="string">'meta[name="flexible"]'</span>);</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">var</span> dpr = <span class="number">0</span>;</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">var</span> scale = <span class="number">0</span>;</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">var</span> tid;</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">var</span> flexible = lib.<span class="property">flexible</span> || (lib.<span class="property">flexible</span> = {});</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">if</span> (metaEl) {</span></span><br><span class="line"><span class="language-javascript"> <span class="variable language_">console</span>.<span class="title function_">warn</span>(<span class="string">'将根据已有的meta标签来设置缩放比例'</span>);</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">var</span> match = metaEl.<span class="title function_">getAttribute</span>(<span class="string">'content'</span>).<span class="title function_">match</span>(<span class="regexp">/initial\-scale=([\d\.]+)/</span>);</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">if</span> (match) {</span></span><br><span class="line"><span class="language-javascript"> scale = <span class="built_in">parseFloat</span>(match[<span class="number">1</span>]);</span></span><br><span class="line"><span class="language-javascript"> dpr = <span class="built_in">parseInt</span>(<span class="number">1</span> / scale);</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> } <span class="keyword">else</span> <span class="keyword">if</span> (flexibleEl) {</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">var</span> content = flexibleEl.<span class="title function_">getAttribute</span>(<span class="string">'content'</span>);</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">if</span> (content) {</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">var</span> initialDpr = content.<span class="title function_">match</span>(<span class="regexp">/initial\-dpr=([\d\.]+)/</span>);</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">var</span> maximumDpr = content.<span class="title function_">match</span>(<span class="regexp">/maximum\-dpr=([\d\.]+)/</span>);</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">if</span> (initialDpr) {</span></span><br><span class="line"><span class="language-javascript"> dpr = <span class="built_in">parseFloat</span>(initialDpr[<span class="number">1</span>]);</span></span><br><span class="line"><span class="language-javascript"> scale = <span class="built_in">parseFloat</span>((<span class="number">1</span> / dpr).<span class="title function_">toFixed</span>(<span class="number">2</span>));</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">if</span> (maximumDpr) {</span></span><br><span class="line"><span class="language-javascript"> dpr = <span class="built_in">parseFloat</span>(maximumDpr[<span class="number">1</span>]);</span></span><br><span class="line"><span class="language-javascript"> scale = <span class="built_in">parseFloat</span>((<span class="number">1</span> / dpr).<span class="title function_">toFixed</span>(<span class="number">2</span>));</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">if</span> (!dpr && !scale) {</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">var</span> isAndroid = win.<span class="property">navigator</span>.<span class="property">appVersion</span>.<span class="title function_">match</span>(<span class="regexp">/android/gi</span>);</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">var</span> isIPhone = win.<span class="property">navigator</span>.<span class="property">appVersion</span>.<span class="title function_">match</span>(<span class="regexp">/iphone/gi</span>);</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">var</span> devicePixelRatio = win.<span class="property">devicePixelRatio</span>;</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">if</span> (isIPhone) {</span></span><br><span class="line"><span class="language-javascript"> <span class="comment">// iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案</span></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">if</span> (devicePixelRatio >= <span class="number">3</span> && (!dpr || dpr >= <span class="number">3</span>)) {</span></span><br><span class="line"><span class="language-javascript"> dpr = <span class="number">3</span>;</span></span><br><span class="line"><span class="language-javascript"> } <span class="keyword">else</span> <span class="keyword">if</span> (devicePixelRatio >= <span class="number">2</span> && (!dpr || dpr >= <span class="number">2</span>)) {</span></span><br><span class="line"><span class="language-javascript"> dpr = <span class="number">2</span>;</span></span><br><span class="line"><span class="language-javascript"> } <span class="keyword">else</span> {</span></span><br><span class="line"><span class="language-javascript"> dpr = <span class="number">1</span>;</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> } <span class="keyword">else</span> {</span></span><br><span class="line"><span class="language-javascript"> <span class="comment">// 其他设备下,仍旧使用1倍的方案</span></span></span><br><span class="line"><span class="language-javascript"> dpr = <span class="number">1</span>;</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> scale = <span class="number">1</span> / dpr;</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> docEl.<span class="title function_">setAttribute</span>(<span class="string">'data-dpr'</span>, dpr);</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">if</span> (!metaEl) {</span></span><br><span class="line"><span class="language-javascript"> metaEl = doc.<span class="title function_">createElement</span>(<span class="string">'meta'</span>);</span></span><br><span class="line"><span class="language-javascript"> metaEl.<span class="title function_">setAttribute</span>(<span class="string">'name'</span>, <span class="string">'viewport'</span>);</span></span><br><span class="line"><span class="language-javascript"> metaEl.<span class="title function_">setAttribute</span>(<span class="string">'content'</span>, <span class="string">'initial-scale='</span> + scale + <span class="string">', maximum-scale='</span> + scale + <span class="string">', minimum-scale='</span> + scale + <span class="string">', user-scalable=no'</span>);</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">if</span> (docEl.<span class="property">firstElementChild</span>) {</span></span><br><span class="line"><span class="language-javascript"> docEl.<span class="property">firstElementChild</span>.<span class="title function_">appendChild</span>(metaEl);</span></span><br><span class="line"><span class="language-javascript"> } <span class="keyword">else</span> {</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">var</span> wrap = doc.<span class="title function_">createElement</span>(<span class="string">'div'</span>);</span></span><br><span class="line"><span class="language-javascript"> wrap.<span class="title function_">appendChild</span>(metaEl);</span></span><br><span class="line"><span class="language-javascript"> doc.<span class="title function_">write</span>(wrap.<span class="property">innerHTML</span>);</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">function</span> <span class="title function_">refreshRem</span>(<span class="params"></span>) {</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">var</span> width = docEl.<span class="title function_">getBoundingClientRect</span>().<span class="property">width</span>;</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">if</span> (width / dpr > <span class="number">540</span>) {</span></span><br><span class="line"><span class="language-javascript"> width = <span class="number">540</span> * dpr;</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">var</span> rem = width / <span class="number">10</span>;</span></span><br><span class="line"><span class="language-javascript"> docEl.<span class="property">style</span>.<span class="property">fontSize</span> = rem + <span class="string">'px'</span>;</span></span><br><span class="line"><span class="language-javascript"> flexible.<span class="property">rem</span> = win.<span class="property">rem</span> = rem;</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> win.<span class="title function_">addEventListener</span>(<span class="string">'resize'</span>, <span class="keyword">function</span> (<span class="params"></span>) {</span></span><br><span class="line"><span class="language-javascript"> <span class="built_in">clearTimeout</span>(tid);</span></span><br><span class="line"><span class="language-javascript"> tid = <span class="built_in">setTimeout</span>(refreshRem, <span class="number">300</span>);</span></span><br><span class="line"><span class="language-javascript"> }, <span class="literal">false</span>);</span></span><br><span class="line"><span class="language-javascript"> win.<span class="title function_">addEventListener</span>(<span class="string">'pageshow'</span>, <span class="keyword">function</span> (<span class="params">e</span>) {</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">if</span> (e.<span class="property">persisted</span>) {</span></span><br><span class="line"><span class="language-javascript"> <span class="built_in">clearTimeout</span>(tid);</span></span><br><span class="line"><span class="language-javascript"> tid = <span class="built_in">setTimeout</span>(refreshRem, <span class="number">300</span>);</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> }, <span class="literal">false</span>);</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">if</span> (doc.<span class="property">readyState</span> === <span class="string">'complete'</span>) {</span></span><br><span class="line"><span class="language-javascript"> doc.<span class="property">body</span>.<span class="property">style</span>.<span class="property">fontSize</span> = <span class="number">12</span> * dpr + <span class="string">'px'</span>;</span></span><br><span class="line"><span class="language-javascript"> } <span class="keyword">else</span> {</span></span><br><span class="line"><span class="language-javascript"> doc.<span class="title function_">addEventListener</span>(<span class="string">'DOMContentLoaded'</span>, <span class="keyword">function</span> (<span class="params">e</span>) {</span></span><br><span class="line"><span class="language-javascript"> doc.<span class="property">body</span>.<span class="property">style</span>.<span class="property">fontSize</span> = <span class="number">12</span> * dpr + <span class="string">'px'</span>;</span></span><br><span class="line"><span class="language-javascript"> }, <span class="literal">false</span>);</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> <span class="title function_">refreshRem</span>();</span></span><br><span class="line"><span class="language-javascript"> flexible.<span class="property">dpr</span> = win.<span class="property">dpr</span> = dpr;</span></span><br><span class="line"><span class="language-javascript"> flexible.<span class="property">refreshRem</span> = refreshRem;</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> flexible.<span class="property">rem2px</span> = <span class="keyword">function</span> (<span class="params">d</span>) {</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">var</span> val = <span class="built_in">parseFloat</span>(d) * <span class="variable language_">this</span>.<span class="property">rem</span>;</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">if</span> (<span class="keyword">typeof</span> d === <span class="string">'string'</span> && d.<span class="title function_">match</span>(<span class="regexp">/rem$/</span>)) {</span></span><br><span class="line"><span class="language-javascript"> val += <span class="string">'px'</span>;</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">return</span> val;</span></span><br><span class="line"><span class="language-javascript"> };</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> flexible.<span class="property">px2rem</span> = <span class="keyword">function</span> (<span class="params">d</span>) {</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">var</span> val = <span class="built_in">parseFloat</span>(d) / <span class="variable language_">this</span>.<span class="property">rem</span>;</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">if</span> (<span class="keyword">typeof</span> d === <span class="string">'string'</span> && d.<span class="title function_">match</span>(<span class="regexp">/px$/</span>)) {</span></span><br><span class="line"><span class="language-javascript"> val += <span class="string">'rem'</span>;</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> </span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">return</span> val;</span></span><br><span class="line"><span class="language-javascript"> };</span></span><br><span class="line"><span class="language-javascript"> })(<span class="variable language_">window</span>, <span class="variable language_">window</span>[<span class="string">'lib'</span>] || (<span class="variable language_">window</span>[<span class="string">'lib'</span>] = {}));</span><span class="tag"></<span class="name">script</span>></span><span class="tag"><<span class="name">link</span> <span class="attr">href</span>=<span class="string">"./css/index.07954305.css"</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span>></span><span class="tag"></<span class="name">head</span>></span><span class="tag"><<span class="name">body</span>></span><span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"root"</span>></span><span class="tag"></<span class="name">div</span>></span><span class="tag"><<span class="name">script</span> <span class="attr">defer</span>=<span class="string">"defer"</span> <span class="attr">src</span>=<span class="string">"./js/index.9dbc4867b93d15abf8a6.js"</span>></span><span class="tag"></<span class="name">script</span>></span><span class="tag"></<span class="name">body</span>></span><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure></li></ul><h1 id="webpack-dev-server-配置"><a href="#webpack-dev-server-配置" class="headerlink" title="webpack-dev-server 配置"></a>webpack-dev-server 配置</h1><blockquote><p>原理</p></blockquote><p> webpack-dev-server 主要是运用了 websocket 长链接以及 webpack –watch 监听. webpack-dev-server 会将 websocket client 端注册至远程客户端中,当 –watch 监听到项目内容发生变化时,webpack 就会重新实行构建打包,websocket server 端通过长链接会告知 websocket client 端从而实行远程客户端页面的强制刷新.</p><ul><li><p>hot.</p><p>hot 是 webpack-dev-server 配置中的属性,意为是否开启模块热更新.实际上就是指当通过长链接会告知 websocket client 端从而实行远程客户端页面的强制刷新时,这时候不实行整个页面的全局刷新,而是实行发生局部改动模块的热替换,从而实现局部刷新.</p></li></ul><h2 id="devServer"><a href="#devServer" class="headerlink" title="devServer"></a>devServer</h2><h3 id="contentBase"><a href="#contentBase" class="headerlink" title="contentBase"></a>contentBase</h3><blockquote><p>问题1</p></blockquote><p> webpack5 中 devServer 的属性 contentBase 被删掉了吗?</p><ul><li><p>介绍.</p><p>在 webpack5 中配置 devServer contentBase 会报错. contentBase 意为配置 DevServer 服务的根目录,一般情况下 contentBase 为项目根目录.</p><pre><code>[webpack-cli] Invalid options object. Dev Server has been initialized using an options object that does not match the API schema.- options has an unknown property 'contentBase'. These properties are valid: object { allowedHosts?, bonjour?, client?, compress?, devMiddleware?, headers?, historyApiFallback?, host?, hot?, http2?, https?, ipc?, liveReload?, magicHtml?, onAfterSetupMiddleware?, onBeforeSetupMiddleware?, onListening?, open?, port?, proxy?, server?, setupExitSignals?, setupMiddlewares?, static?, watchFiles?, webSocketServer? }</code></pre></li><li><p>原因.</p><p>据 issues <a href='https://github.com/webpack/webpack-dev-server/issues/2958#issuecomment-757141969'>Multiple bugs in one (config-yargs is needed and invalid configuration object errors) </a>,contentBase 属性已经在 webpack5 中更名为 static.</p></li></ul><h3 id="open"><a href="#open" class="headerlink" title="open"></a>open</h3><blockquote><p>介绍</p></blockquote><p> open 属性配置源于<a href='https://www.npmjs.com/package/open'>open 依赖库</a>,跨平台,可打开URL和可执行文件. 当在开发环境使用webpack-dev-server时,配置使用 open 属性,运行则可自动打开浏览器标签页展示配置的页面.</p><blockquote><p>问题1</p></blockquote><p> open 属性配置可打开并复用浏览器同一个标签页吗?</p><ul><li><p>介绍</p><p>每次在开发环境执行 webpack-dev-server 命令,自动打开一个新的浏览器标签页,原来的标签页还在,但是并没有复用.</p><pre><code>webpack-dev-server --config=./webpack.config.js --color</code></pre></li><li><p>进度</p><p>据 issues <a href='https://github.com/webpack/webpack-dev-server/issues/1400'>Re-use current tab instead of open a new one</a>, 现阶段只能做到在 mac 平台上面做到打开并复用浏览器同一个标签页,而 facebook 的 create-react-app 框架也是一样 <a href='https://github.com/facebook/create-react-app/blob/25184c4e91ebabd16fe1cde3d8630830e4a36a01/packages/react-dev-utils/openBrowser.js#L65-L86'>create-react-app -> openBrowser.js</a>,目前还没有任何新的方案做到跨平台打开并复用浏览器同一个标签页.</p></li></ul><h2 id="resolve"><a href="#resolve" class="headerlink" title="resolve"></a>resolve</h2><h3 id="mainFields"><a href="#mainFields" class="headerlink" title="mainFields"></a>mainFields</h3><blockquote><p>介绍</p></blockquote><p> 根据 target 的不同,配置解析导入模块的字段也就不同. 默认配置解析导入模块的字段为: [‘browser’, ‘module’, ‘main’],按照从左向右的顺序配置解析.</p><h3 id="modules"><a href="#modules" class="headerlink" title="modules"></a>modules</h3><blockquote><p>介绍</p></blockquote><p> 通常解析导入模块时会直接通过 modules 默认配置 [‘node_modules’],但如果想要直接通过个人所写的业务组件库解析导入模块,可以自定义 modules 配置,例如: [path.join(__dirname, ‘src’, ‘components’), ‘node_modules’],这样会优先解析导入个人所写的业务组件库,实行解析导入模块.</p><h3 id="descriptionFiles"><a href="#descriptionFiles" class="headerlink" title="descriptionFiles"></a>descriptionFiles</h3><blockquote><p>介绍</p></blockquote><p> 用于描述项目依赖的 JSON 文件,也可以自定义,默认配置为[‘package.json’],建议不要随意修改.</p>]]></content>
<categories>
<category> webpack </category>
</categories>
<tags>
<tag> javascript module </tag>
<tag> webpack </tag>
</tags>
</entry>
<entry>
<title>think of homebrew</title>
<link href="/2022/05/18/thinkofhomebrew/"/>
<url>/2022/05/18/thinkofhomebrew/</url>
<content type="html"><![CDATA[<h1 id="download-homebrew"><a href="#download-homebrew" class="headerlink" title="download homebrew"></a>download homebrew</h1><blockquote><p>问题</p></blockquote><p> 最近在下载 homebrew 时,发现其总是出现连接被拒绝的情况.</p><pre><code>curl: (7) Failed to connect to raw.githubusercontent.com port 443: Connection refused</code></pre><blockquote><p>原因</p></blockquote><p> 后续是发现其脚本需要到 raw.githubusercontent.com 上拉取代码,原因是 github 的一些域名的DNS解析被污染,导致 DNS 解析过程无法通过域名取得正确的 IP 地址.</p><blockquote><p>方案</p></blockquote><p> 使用修改本机 hosts 文件,建立域名与 IP 的映射关系,当访问 hosts 文件列表中的域名时,依次尝试在其映射的 IP 进行请求,绕过 DNS.</p><ul><li>步骤.</li></ul><ol><li>使用<a href='https://www.ipaddress.com/'>https://www.ipaddress.com/</a>查找域名所对应的 IP 地址.</li></ol><p> <img src="https://image.white-than-wood.zone/homebrew/ipaddress.png"></p><ol start="2"><li>使用 switchHosts 修改 mac 的 hosts 文件.</li></ol><p> <img src="https://image.white-than-wood.zone/homebrew/switchhosts.png"></p><p> PS: 使用 switchHosts 无法对原有的 hosts 文件进行修改,只能添加新的 hosts 文件对原有的 hosts 文件进行合并覆盖,添加好后,将其配置的 switch 开关打开,允许其合并覆盖.</p><blockquote><p>次生问题</p></blockquote><p> 再次下载,被拒绝的情况不存在了,但是出现了新的异常情况.</p><pre><code>curl: (35) LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to raw.githubusercontent.com:443</code></pre><blockquote><p>次生原因</p></blockquote><p> 其根本原因,在于国内网络环境对于境外服务器的种种限制,只用解决这一问题才能真正意义上解决下载 homebrew 网络错误的问题.</p><blockquote><p>次生方案</p></blockquote><p> 所以我查找到了一条可以彻底解决的路,使用国内镜像,就跟 npm 的淘宝镜像相同,homebrew 在国内也有多条镜像途径.</p><ul><li>步骤.</li></ul><ol><li>首先下载 homebrew 国内镜像.</li></ol><pre><code>/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"</code></pre><p> <img src="https://image.white-than-wood.zone/homebrew/homebrew_mirror.png"></p><ol start="2"><li>选择任意一个镜像进行下载,最好是用’梯子’🐶.有时候用’梯子’也下载的非常慢,好在重新进行下载时,会在原来 downloaded 的基础之上进行下载.下载好之后,重启终端命令行工具,或者执行一下 source .bash_profile,使得配置文件在修改了环境变等配置的情况下进行重置.</li></ol><blockquote><p>brew install</p></blockquote><p> <img src="https://image.white-than-wood.zone/homebrew/homebrew_install_git.png"></p><p> 这样就可以愉快快捷的下载任意在 homebrew 上的资源了! PS: 每下载完一次资源,还是最好执行一下 source ~/.bash_profile,使得配置文件在修改了环境变量等配置的情况下进行重置.</p><blockquote><p>异常</p></blockquote><pre><code>fatal: not in a git directoryError: Command failed with exit 128: git</code></pre><p> 当出现这种异常时,就说明本地与远程并没有建立关联,并没有添加 origin 句柄简称映射远程的镜像或者 github 链接,所以我们需要重新设置一下.</p><ul><li>Bash 终端配置</li></ul><pre><code># 替换 brew.git:cd "$(brew --repo)"git remote set-url origin https://mirrors.aliyun.com/homebrew/brew.git# 替换homebrew-core.git:cd "$(brew --repo)/Library/Taps/homebrew/homebrew-core"git remote set-url origin https://mirrors.aliyun.com/homebrew/homebrew-core.git# 应用生效brew update# 替换homebrew-bottles:echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles' >> ~/.bash_profilesource ~/.bash_profile</code></pre><ul><li>Zsh 终端配置</li></ul><pre><code># 替换 brew.git:cd "$(brew --repo)"git remote set-url origin https://mirrors.aliyun.com/homebrew/brew.git# 替换homebrew-core.git:cd "$(brew --repo)/Library/Taps/homebrew/homebrew-core"git remote set-url origin https://mirrors.aliyun.com/homebrew/homebrew-core.git# 应用生效brew update# 替换homebrew-bottles:echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles' >> ~/.zshrcsource ~/.zshrc</code></pre><p> 这样就可解决本地与远程并没有建立关联的问题.</p>]]></content>
<categories>
<category> homebrew </category>
</categories>
<tags>
<tag> homebrew </tag>
</tags>
</entry>
<entry>
<title>think of git</title>
<link href="/2022/05/15/thinkofgit/"/>
<url>/2022/05/15/thinkofgit/</url>
<content type="html"><![CDATA[<h1 id="git命令"><a href="#git命令" class="headerlink" title="git命令"></a>git命令</h1><blockquote><p>git remote add origin <a href="mailto:git@github.com">git@github.com</a>:white-than-wood/white-than-wood.github.io.git</p></blockquote><p> 从官方对于 git remote 的定义来看,意思是列出本地拥有的每个指定远程句柄的简称.简单来说本地与远程交流的一个简化链接,当在本地建立起一个已经初始化的 git 项目时,势必要与远程 git 库建立联系(远程 git 库本来也是为了保存代码、保证多人开发时代码的同步以及简化其流程性而存在的),那么此命令就是为了在本地建立一个句柄简称与远程链接 <a href="mailto:git@github.com">git@github.com</a>:white-than-wood/white-than-wood.github.io.git 进行关联,实行通信,不需要再去 copy 使用 github 远程库上面的链接.</p><blockquote><p>git push –set-upstream origin master</p></blockquote><p> 在本地句柄简称 origin 建立了与远程代码库的关联之后,首次推送同步代码,需要设置推送、同步代码的上游远程分支,当首次设置之后,后续无需设置. –set-upstream origin master 就是设置推送、同步代码的本地 origin 句柄简称的上游分支为 master.</p><blockquote><p>git merge master –allow-unrelated-histories</p></blockquote><p> 当同一个仓库存在多个独立的分支并没有公共的上游交集父分支时,会出现无法合并的情况(多出现于本地 git 初始化时默认主分支为 master,而远程 github 默认主分支为 main ).</p><pre><code>fatal: refusing to merge unrelated histories</code></pre><p> 故此我们需要在人为确认合并分支安全的情况下,将多个独立的分支进行允许强制合并,也就是 –allow-unrelated-histories.</p><blockquote><p>git remote set-url origin <a href="https://mirrors.aliyun.com/homebrew/brew.git">https://mirrors.aliyun.com/homebrew/brew.git</a></p></blockquote><p> 修改本地句柄简称列表中 origin 所对应的远程仓库链接为 <a href="https://mirrors.aliyun.com/homebrew/brew.git">https://mirrors.aliyun.com/homebrew/brew.git</a>.</p><h1 id="SSH公私钥安全关联"><a href="#SSH公私钥安全关联" class="headerlink" title="SSH公私钥安全关联"></a>SSH公私钥安全关联</h1><p> 本地设置与 github 远程仓库的 ssh 安全关联.</p><blockquote><p>步骤</p></blockquote><p> 只有持有账号私钥的情况下才可以推送、同步代码到拥有公钥的 github 远程仓库,使用 ssh 命令产生公私钥.我这里用的是 rsa 的加解密方式,小伙伴们也可以选择自己喜欢的加密方式.</p><pre><code>#使用自己的 github 账号来作为 rsa 加解密的注释#-t: type,选用 rsa 的密钥类型#-b: byte,公私钥的长度大小4096比特#-C: comments,添加公私钥生成的注释ssh-keygen -t rsa -b 4096 -C '[email protected]'</code></pre><p> 在 mac 下,生成之后,前往自己账号目录下查询 .ssh/id_rsa.pub,将 id_rsa.pub 文件里面的内容复制添加到 github 的账号 settings 设置下的 SSH and GPG keys.</p> <img src="https://image.white-than-wood.zone/git/ssh_settings.png" style="width: 160px;float: left;"/> <img src="https://image.white-than-wood.zone/git/ssh_settings_SSHKeys.png" style="width: calc(100% - 180px);float: left;margin-left: 20px;"/> <div style="clear: both;display: block;"></div><p> 添加成功之后,我们测试一下,将远程 github 库(‘<a href="mailto:git@github.com">git@github.com</a>‘开头链接)克隆到本地,如果可以拉取到本地,那就说明 ssh-keygen 设置与 github 远程仓库 ssh 安全关联生效.</p><p> 本地设置多个 ssh 私钥与多个 github 账号远程仓库建立安全关联.</p><blockquote><p>示例</p></blockquote><ul><li><p>ssh-add 交与 ssh-agent 代理高速缓存管理.</p><ul><li><p>ssh-agent.</p><p>ssh-agent 实际上是一个本地的高速缓存代理机制,可以在用户不输入任何密码短语的情况下,将 session 缓存中的私钥与远程仓库建立 ssh 安全关联并实行通信.</p></li><li><p>ssh-add.</p><p>ssh-add 是将指定的 ssh 私钥置于 ssh-agent 的高速缓存中,执行时会在 ssh-agent 建立一个 session,并将 ssh 私钥放入其中.注意,此行为是一次性行为,也就是临时性行为,重启 ssh-agent 之后,会重置.</p><pre><code>#可用于查看 ssh-agent 的高速缓存中的私钥列表.ssh-add list#清空 ssh-agent 的高速缓存中的私钥列表.ssh-add -D#重启 ssh-agent.eval $(ssh-agent)</code></pre></li><li><p>步骤.</p><p>使用 ssh-keygen 与 ssh-add 联合使用.</p><pre><code>ssh-keygen -t rsa -b 4096 -C '[email protected]'#这时会出现需要你设置进行保存公私钥的文件名,默认还是为id_rsaGenerating public/private rsa key pair.Enter file in which to save the key (/Users/yinwk/.ssh/id_rsa): id_rsa_ano#设置过后,公私钥就会以id_rsa_ano.pub以及id_rsa_ano文件进行保存</code></pre><p>一系列操作结束后,我们还需要将新生成的私钥交与 ssh-agent 代理高速缓存管理.</p><pre><code>ssh-add ~/.ssh/id_rsa_ano</code></pre><p>在成功交与 ssh-agent 管理后,我们重复上一个部分中’本地设置与 github 远程仓库的 ssh 安全关联’的后续操作即可.</p></li><li><p>问题.</p><p>使用此方法是有比较多的问题的,首先就是重启 ssh-agent 或者电脑之后,ssh-agent 中的高速缓存会重置,也就是会被清空掉.再就是对于同一个 ssh 域名下的链接建立 ssh 安全关联时,ssh-agent 会选择高速缓存列表中的一个缓存私钥来建立通信.举个🌰:</p><pre><code>#这两个 github 库有着同样的域名别称,也就是 @github.com,在建立通信时,如果将两个账号下生成的私钥都放入 ssh-agent 高速缓存中,ssh-agent 会默认选择高速缓存列表中先放入的 ssh 私钥进行通信.#只有在切换不同的账号项目开发时,将 ssh-agent 重启,并且将当前项目所对应的 ssh 私钥放入 ssh-agent 高速缓存中才可以建立 ssh 安全关联并且实行通信[email protected]:white-than-wood/[email protected]:dreamthen/webpack-rebuild.github.io.git</code></pre></li><li><p>结论.</p><p>ssh-add 交与 ssh-agent 代理高速缓存管理这种方法,本质也不是为了让多个 ssh 公私钥与多个 github 远程仓库建立安全关联而产生的,作用是为了避免每次与远程建立 ssh 安全关联时必须填写密码短语.并且我们在生成 ssh 密钥时,一般也是不设置密码短语的,都是直接回车(密码短语为空).这种每次必须手动向高速缓存中添加私钥的行为,不仅麻烦,而且是临时性的、不合理的.</p></li></ul></li><li><p>配置 .ssh/config 文件使不同本地项目 ssh 私钥永久性与远程不同 github 账号仓库建立安全关联.</p><ul><li><p>.ssh/config.</p><p>通过配置 config 文件来辅助管理 ssh,通常 .ssh/config 文件是不存在的,需要自己创建配置.</p><pre><code>touch ~/.ssh/config</code></pre></li><li><p>步骤.</p><ul><li><p>配置config.</p><p>配置多个域名对应不同的ssh私钥,与本地自定义的域名别称建立联系.</p><pre><code>vim ~/.ssh/config</code></pre><figure class="highlight text"><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">#github</span><br><span class="line">Host github.com-white-than-wood</span><br><span class="line"> HostName github.com</span><br><span class="line"> User white-than-wood</span><br><span class="line"> IdentityFile "~/.ssh/id_rsa"</span><br><span class="line"> IdentitiesOnly yes</span><br><span class="line"> </span><br><span class="line">#github</span><br><span class="line">Host github.com-dreamthen</span><br><span class="line"> HostName github.com</span><br><span class="line"> User dreamthen</span><br><span class="line"> IdentityFile "~/.ssh/id_rsa_ano"</span><br><span class="line"> IdentitiesOnly yes</span><br><span class="line"> </span><br><span class="line">#Host: 自定义的域名别称,要与本地不同账号的项目域名关联</span><br><span class="line">#HostName: 对应的代码存储/协同网站域名,如 github.com,不同公司的 gitlab 域名不同</span><br><span class="line">#User: 对应的不同代码存储/协同网站域名账号名称</span><br><span class="line">#IdentityFile: 对应的不同代码存储/协同网站域名账号下的 ssh 私钥</span><br><span class="line">#IdentitiesOnly: 只能通过指定的 IdentityFile 来与远程建立 ssh 安全关联并实行通信</span><br></pre></td></tr></table></figure></li><li><p>配置本地不同账号的项目域名、账号名称以及 email 地址.</p><p>进入不同github账号的项目下,修改 .git/config 文件,注意自定义的域名别称要与 .ssh/config 中的 Host 对应,因为每次建立 ssh 安全关联实行通信都会通过 .ssh/config 文件进行辅助管理.</p><pre><code>vim .git/config</code></pre> <figure class="highlight text"><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">#white-than-wood 账号下的项目</span><br><span class="line">[remote "origin"]</span><br><span class="line"> url = [email protected]:white-than-wood/white-than-wood.github.io.git</span><br><span class="line"> fetch = +refs/heads/*:refs/remotes/origin/*</span><br><span class="line"> </span><br><span class="line">[user]</span><br><span class="line"> name = white-than-wood</span><br><span class="line"> email = [email protected] </span><br><span class="line"></span><br><span class="line">#dreamthen 账号下的项目</span><br><span class="line">[remote "origin"]</span><br><span class="line"> url = [email protected]:dreamthen/webpack-rebuild.github.io.git</span><br><span class="line"> fetch = +refs/heads/*:refs/remotes/origin/*</span><br><span class="line"> </span><br><span class="line">[user]</span><br><span class="line"> name = dreamthen</span><br><span class="line"> email = [email protected]</span><br></pre></td></tr></table></figure><p>也可以直接使用命令来进行修改 .git/config 中的内容.</p><pre><code>#white-than-wood 账号下的项目git remote set-url origin [email protected]:white-than-wood/white-than-wood.github.io.gitgit config user.name 'white-than-wood'git config user.email '[email protected]'#dreamthen 账号下的项目git remote set-url origin [email protected]:dreamthen/webpack-rebuild.github.io.gitgit config user.name 'dreamthen'git config user.email '[email protected]'</code></pre></li></ul></li><li><p>测试自定义域名别称.</p><pre><code># 若 Hi white-than-wood! You've successfully authenticated, but GitHub does not provide shell access. 说明测试与远程建立 ssh 安全关联实行通信身份验证通过.ssh -T [email protected]# 若 Hi dreamthen! You've successfully authenticated, but GitHub does not provide shell access. 说明测试与远程建立 ssh 安全关联实行通信身份验证通过.ssh -T [email protected]</code></pre></li><li><p>结论</p><p>此方法永久性的解决了不同的 github 账号下本地项目 ssh 私钥与远程建立安全关联并实行通信的问题.</p></li></ul></li></ul>]]></content>
<categories>
<category> git </category>
</categories>
<tags>
<tag> git </tag>
<tag> github </tag>
</tags>
</entry>
<entry>
<title>hexo搭建next博客</title>
<link href="/2022/05/14/hexo%E6%90%AD%E5%BB%BAnext%E5%8D%9A%E5%AE%A2/"/>
<url>/2022/05/14/hexo%E6%90%AD%E5%BB%BAnext%E5%8D%9A%E5%AE%A2/</url>
<content type="html"><![CDATA[<h1 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h1><p> <b>此博客用于搭建 hexo 的工具博客.基本不会探索深究配置的原因,敬请知悉~</b></p><h1 id="hexo-安装"><a href="#hexo-安装" class="headerlink" title="hexo 安装"></a>hexo 安装</h1><blockquote><p>步骤</p></blockquote><p> 首先要用 npm 依赖资源管理工具安装全局命令 hexo-cli.</p><pre><code>npm install hexo-cli -g</code></pre><p> 然后使用 hexo 初始化博客目录,比如 blog 目录,目录名要与后续个人建立的 github 上 hexo 托管代码的 repository 同名.</p><pre><code>hexo init blog</code></pre><p> 接着 github 建库.</p> <p style="background-color: #f3f3f3;padding: 10px;font-size: 0.875em;font-family: 'PingFang SC', 'Microsoft YaHei';">建立一个以 white-than-wood(我的用户名).github.io 结尾的 repository ,作为 hexo 托管代码的库, github 默认 .github.io 结尾作为用户的网站二级域名,建立一个新的分支作为创作分支(因为主分支是用来发布呈现网站的).</p><p> 之后,进入生成的 blog 文件夹,与远程 github repository 建立关联,并同步远程最新资源.</p><pre><code>git remote add origin [email protected]:white-than-wood/white-than-wood.github.io.gitgit pull</code></pre><p> 再 npm 下载外部依赖包.</p><pre><code>npm i/npm install</code></pre><p> 或者使用 yarn 下载外部依赖包.</p><pre><code>yarn</code></pre><p> 最后启动 hexo 自身搭建的服务,生成本地的博客网站,默认端口在本地 ip 地址下的 4000 端口,假如你不想启动在 4000 端口,也可使用-p 其他端口号进行配置,比如-p 9977.</p><pre><code>hexo serverhexo server -p 9977</code></pre><p> PS: 最好先将本地代码上传至远程之后,再执行 hexo 配置.</p><pre><code>git add .git commit 'build:搭建hexo个人Github Page博客'git push --set-upstream origin master</code></pre><p> 默认的主题风格 theme 是 landscape ,假如你想更换,可以通过<a href='https://hexo.io/themes/'>https://hexo.io/themes</a>进行筛选,筛选之后进行配置,主题配置见下文.</p><h1 id="hexo-命令"><a href="#hexo-命令" class="headerlink" title="hexo 命令"></a>hexo 命令</h1><blockquote><p>示例</p></blockquote><p> 首先建立一篇名为 ‘hexo搭建next博客’ 的博客.</p><pre><code>hexo new hexo搭建next博客</code></pre><p> 在根目录 -> source 目录底下的 _post 目录下找到名为 hexo 配置的博客文件,在里面用 markdown 进行记录个人的博客,当然可以在 hexo server 自带的服务器运行监听的情况下,进行添加和修改个人的博客.之后,进行生成静态文件.</p><pre><code>hexo generate</code></pre><p> 生成静态文件之后,进行部署.</p><pre><code>hexo deploy</code></pre><p> 生成静态文件和部署也可以使用一句命令执行.</p><pre><code>hexo generate -deployhexo deploy -generate</code></pre><p> 为了防止存在静态文件和缓存,造成没有重新渲染页面的问题,在每一次部署之前,要运行一下清理静态文件和缓存的命令.</p><pre><code>hexo cleanhexo generatehexo deploy</code></pre><p> hexo deploy 部署之后,根据项目根目录底下 _config.yml 配置文件的 deploy 配置,会提交到远程 github repository 库.</p><h1 id="hexo-配置"><a href="#hexo-配置" class="headerlink" title="hexo 配置"></a>hexo 配置</h1><p> PS: 以下所说的”根目录”指的就是当前创作的 repository 目录,”主题目录”就是根目录下/themes目录下的主题环境.</p><h4 id="hexo-deploy-发布配置"><a href="#hexo-deploy-发布配置" class="headerlink" title="hexo deploy 发布配置"></a>hexo deploy 发布配置</h4><p> 发布部署可以部署至 Github Page 个人网站,也可以部署至个人申请购买的云服务器中.</p> <p style="background-color: #f3f3f3;padding: 10px;font-size: 0.875em;font-family: 'PingFang SC', 'Microsoft YaHei';">部署至 Github Page 个人网站.</p><blockquote><p>步骤</p></blockquote><p> 要想部署到 Github Page 个人网站,首先要下载 hexo-git 插件.</p><pre><code>npm install hexo-deployer-git --save</code></pre><p> 然后就要在个人的本地hexo博客的项目里面,更改根目录底下的 _config.yml 文件,全局搜索 git ,更改 deploy 发布配置,将 source 目录下面的内容进行构建发布到 repo github 地址的分支上.</p><pre><code>deploy: type: git #你的个人网站 github 库的链接地址,最好使用 git@ 开头的, https@ 开头的会报错 repo: [email protected]:dreamthen/dreamthen.github.io.git #分支名 branch: master</code></pre><p style="background-color: #f3f3f3;padding: 10px;font-size: 0.875em;font-family: 'PingFang SC', 'Microsoft YaHei';">部署至个人申请购买的云服务器.</p><blockquote><p>步骤</p></blockquote><p> 将生成的网站博客项目直接通过 Royal TSX 远程链接管理工具放到云服务器中,链接成功后,下载 nginx 代理.</p><pre><code>#我这里直接使用阿里云CentOS服务器中默认的基于RPM的软件包管理器yum来下载nginxyum install nginx</code></pre><p> 下载之后查看全局命令 nginx 是否存在,查看 nginx 的版本.</p><pre><code>nginx -v</code></pre><p> 如果全局命令不存在 nginx,则运行 source ~/.bash_profile,使得配置在修改了环境变量的情况下进行重置.</p><pre><code>source ~/.bash_profile</code></pre><p> 启动 nginx.</p><pre><code>nginx</code></pre><ul><li><p>顺便提一下 nginx 的其他命令.重启 nginx.</p><pre><code>nginx -s reload</code></pre></li><li><p>停止 nginx.</p><pre><code>nginx -s stop</code></pre></li><li><p>通过流(pipe)查询全部用户组 nginx 完整的进程状态.</p><pre><code>ps -ef | grep nginx</code></pre></li><li><p>强制杀掉 nginx 进程.</p><pre><code>#28009是 nginx 进程号,通过 ps -ef | grep nginx 可以查询到kill -9 28009</code></pre></li></ul><p> 配置 nginx,将服务器代理 root 页面指向我们的网站博客项目.</p><pre><code>#通过测试 nginx,查询 nginx 配置文件所在目录nginx -tvim /etc/nginx/nginx.conf</code></pre> <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 prompt_">#</span><span class="language-bash">...</span></span><br><span class="line">server {</span><br><span class="line"> listen 80 default_server;</span><br><span class="line"> listen [::]:80 default_server;</span><br><span class="line"> #域名配置,需要申请购买备案(国内域名需要备案,国外域名则不需要)</span><br><span class="line"> server_name _;</span><br><span class="line"> #修改root页面向我们的网站博客项目</span><br><span class="line"> root /usr/share/nginx/html;</span><br><span class="line"></span><br><span class="line"> # Load configuration files for the default server block.</span><br><span class="line"> include /etc/nginx/default.d/*.conf;</span><br><span class="line"></span><br><span class="line"> location / {</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> error_page 404 /404.html;</span><br><span class="line"> location = /40x.html {</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> error_page 500 502 503 504 /50x.html;</span><br><span class="line"> location = /50x.html {</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash">...</span></span><br></pre></td></tr></table></figure><h4 id="hexo-theme-主题配置"><a href="#hexo-theme-主题配置" class="headerlink" title="hexo theme 主题配置"></a>hexo theme 主题配置</h4><blockquote><p>示例</p></blockquote><p> 筛选好个人选中的主题之后,就需要在项目里面进行配置更换。首先需要下载远程 github 库里面的主题目录到项目根目录里面的 themes 文件夹底下,比如本人用的是 next 主题的 hexo.</p><pre><code>git clone https://github.com/iissnan/hexo-theme-next.git themes/next</code></pre><p> 随后更改根目录里面的 _config.yml 文件,全局搜索 theme ,更改 theme 配置.</p><pre><code>theme: next</code></pre><p> 接着选择主题展示的方式,需要在主题的项目目录下,更改 _config.yml 文件,全局搜索 Scheme ,更改 Scheme 配置,比如 next 主题底下的 Scheme 配置.</p><pre><code>scheme: #scheme: Muse #scheme: Mist scheme: Pisces #scheme: Gemini</code></pre><h4 id="hexo-language语言配置"><a href="#hexo-language语言配置" class="headerlink" title="hexo language语言配置"></a>hexo language语言配置</h4><blockquote><p>步骤</p></blockquote><p> 每个主题的语言都是根据作者的母语来配置的,要想配置开发者个人国家的语言,还是更改项目根目录底下的 _config.yml 文件,全局搜索 language ,更改 language 配置.</p><pre><code>language: zh-CN</code></pre><p> 再更改主题目录底下的 _config.yml 文件,全局搜索 language ,更改 language 配置.</p><pre><code>language: zh-CN </code></pre><h4 id="hexo-page页面配置"><a href="#hexo-page页面配置" class="headerlink" title="hexo page页面配置"></a>hexo page页面配置</h4><blockquote><p>步骤</p></blockquote><p> hexo提供了几个可供筛选的页面,常用的有这么几个:home(首页)、tags(标签)、about(关于我)、archives(档案)和categories(分类),首先要创建页面.</p><pre><code>hexo new page tagshexo new page abouthexo new page archiveshexo new page categories</code></pre><p> 接着在主题目录下,更改 _config.yml 文件,全局搜索 menu ,更改 menu 配置,当然每个主题的配置不尽相同.</p><pre><code>menu: home: / || fa fa-home about: /about/ || fa fa-user tags: /tags/ || fa fa-tags categories: /categories/ || fa fa-th archives: /archives/ || fa fa-archive</code></pre><p> 随后更改 source 目录底下创建的页面配置,以 tags 为例.</p><pre><code>---------------------------- title: about date: 2018-04-24 17:57:26 type: "tags" comments: false----------------------------</code></pre><h4 id="hexo-avatar-头像配置"><a href="#hexo-avatar-头像配置" class="headerlink" title="hexo avatar 头像配置"></a>hexo avatar 头像配置</h4><blockquote><p>步骤</p></blockquote><p> 配置个人博客网站的头像,需要更改主题目录底下的 _config.yml 文件,添加 avatar 配置.</p><pre><code>#头像url链接avatar: https://avatars.githubusercontent.com/u/11425476?v=4</code></pre><h4 id="hexo-search-全站搜索配置"><a href="#hexo-search-全站搜索配置" class="headerlink" title="hexo search 全站搜索配置"></a>hexo search 全站搜索配置</h4><blockquote><p>步骤</p></blockquote><p> 假如想要配置hexo个人博客网站的全站搜索配置,首先要下载 hexo search 外部依赖包.</p><pre><code>npm install hexo-generator-search --savenpm install hexo-generator-searchdb --save</code></pre><p> 更改根目录底下的 _config.yml 文件,添加 search 配置.</p><pre><code>search: path: search.xml field: post format: html limit: 10000</code></pre><p> 开启主题目录底下的 _config.yml 文件中的 local_search 配置.</p><pre><code>local_search: enable: true </code></pre><h4 id="hexo-new-post-name配置"><a href="#hexo-new-post-name配置" class="headerlink" title="hexo new_post_name配置"></a>hexo new_post_name配置</h4><blockquote><p>步骤</p></blockquote><p> 假如想要更改每一篇博客的文件名称,不再是默认的:title.md的文件名,需要更改根目录底下的 _config.yml 文件,更改 new_post_name 配置.</p><pre><code>new_post_name: :year-:month-:day-:title.md</code></pre><h4 id="hexo-auto-excerpt阅读全文配置"><a href="#hexo-auto-excerpt阅读全文配置" class="headerlink" title="hexo auto_excerpt阅读全文配置"></a>hexo auto_excerpt阅读全文配置</h4><blockquote><p>步骤</p></blockquote><p> 博客文章一般都会很长的,所以在首页要对博客进行超长省略,要想看所有的内容,点击阅读全文或者文章标题进入全文查看.在 hexo-theme-next 主题版本7.6之前需要更改主题目录底下的 _config.yml 文件,全局搜索 auto_excerpt ,更改 auto_excerpt 配置.</p><pre><code>auto_excerpt: enable: true length: 200</code></pre><p> 在 hexo-theme-next 主题版本7.6之后,由于 auto_excerpt 这种超长省略不应该是主题插件应该做的,作者将此配置移除,并给出新的专门针对此配置的插件 <a href='https://github.com/chekun/hexo-excerpt'>hexo-excerpt</a>.</p><p> <img src="https://image.white-than-wood.zone/hexo/remove_auto_excerpt.png"></p><pre><code>excerpt: #显示的 markdown 代码块层数 depth: 5 excerpt_excludes: [] more_excludes: [] #设置为true: 显示超长省略,只展示部分,隐藏全文 #设置为false: 展示全文 hideWholePostExcerpts: true excerpt_description: true #是否显示阅读全文按钮 read_more_btn: true</code></pre><h4 id="hexo-browsersync开发环境创作自动更新配置"><a href="#hexo-browsersync开发环境创作自动更新配置" class="headerlink" title="hexo browsersync开发环境创作自动更新配置"></a>hexo browsersync开发环境创作自动更新配置</h4><blockquote><p>步骤</p></blockquote><p> 在创作博客时,需要每次手动刷新页面才能看到修改后的结果,感觉非常没有效率,如果存在类hrm热加载这种插件就太爽了! hexo-browsersync 可以直接解决这个问题.</p><p> 此插件原理基于 browser-sync ,与 hexo 建立关联,当创作的文件内容发生改变时, browser-sync 就会监听到并刷新浏览器整个页面的内容,做到不需手动刷新,大大提高了创作效率.</p><pre><code>#更改根目录底下的 _config.yml 文件,添加 browsersync 属性#设置监听 watch 属性为 true 就可以了!browsersync: watch: true logLevel: "warn"</code></pre><h4 id="hexo-busuanzi-count卜算子统计配置"><a href="#hexo-busuanzi-count卜算子统计配置" class="headerlink" title="hexo busuanzi_count卜算子统计配置"></a>hexo busuanzi_count卜算子统计配置</h4><blockquote><p>步骤</p></blockquote><p> 上线之后,需要对个人博客进行管理,阅读人数以及次数对于创作者来说是很重要反馈点.统计配置需要更改主题目录底下的 _config.yml 文件,配置 busuanzi_count 卜算子统计.</p><pre><code>#可配置查看个人博客的阅读人数、次数以及每篇博客文章的次数busuanzi_count: enable: true total_visitors: true total_visitors_icon: fa fa-user total_views: true total_views_icon: fa fa-eye post_views: true post_views_icon: fa fa-eye</code></pre><h4 id="hexo-baidu-analytics百度统计配置"><a href="#hexo-baidu-analytics百度统计配置" class="headerlink" title="hexo baidu_analytics百度统计配置"></a>hexo baidu_analytics百度统计配置</h4><blockquote><p>步骤</p></blockquote><p> 卜算子统计有时候会出现一些异常,比如pv莫名会加100,uv不再区分单个ip.为了追求更精确、更智能,我们采用百度统计. 统计配置需要更改主题目录底下的 _config.yml 文件,配置 baidu_analytics 百度统计.</p><p> 根据<a href='https://tongji.baidu.com/main/setting/10000339309/home/site/getjs?siteId=18040501'>百度统计-使用配置-代码获取</a>,将hm.js后方自动生成的id配置至baidu_analytics.</p><p> <img src="https://image.white-than-wood.zone/hexo/baidu_analytics.png"></p><pre><code># Baidu Analytics# See: https://tongji.baidu.combaidu_analytics: 31f07c2ec89d10385ec28e8eea5bbc3a</code></pre><h4 id="hexo-Gitalk留言板配置"><a href="#hexo-Gitalk留言板配置" class="headerlink" title="hexo Gitalk留言板配置"></a>hexo Gitalk留言板配置</h4><blockquote><p>步骤</p></blockquote><p> 留言板 comments 是创作者与阅读者进行互动反馈的窗口,可以使阅读者与创作者共同进步.对于 hexo 来说,支持的留言板模式有很多种: ‘disqus | disqusjs | changyan | livere | gitalk | utterances’ ,比较常见、用户量比较大且与github关联性比较强的就是 gitalk 以及 disqus,disqusjs 需要’梯子’🐶才能评论,那么还是选用 gitalk .留言板配置需要更改主题目录下的 _config.yml 文件.</p><p> 配置comments:</p><pre><code># Multiple Comment System Supportcomments: # Available values: tabs | buttons style: tabs # Choose a comment system to be displayed by default. # Available values: disqus | disqusjs | changyan | livere | gitalk | utterances active: gitalk # Setting `true` means remembering the comment system selected by the visitor. storage: true # Lazyload all comment systems. lazyload: false # Modify texts or order for any naves, here are some examples. nav: gitalk: order: -2 disqus: text: Load Disqus order: -1</code></pre><p> 配置gitalk:</p><pre><code># Gitalk# For more information: https://gitalk.github.iogitalk: enable: true github_id: white-than-wood # GitHub repo owner repo: white-than-wood.github.io # Repository name to store issues client_id: 1191ab5290535c1acb09 # GitHub Application Client ID client_secret: 8d2cbebac1ae1230f84d1f9f7a36f8008a42c14b # GitHub Application Client Secret admin_user: white-than-wood # GitHub repo owner and collaborators, only these guys can initialize gitHub issues distraction_free_mode: true # Facebook-like distraction free mode # When the official proxy is not available, you can change it to your own proxy address proxy: https://cors-anywhere.azm.workers.dev/https://github.com/login/oauth/access_token # proxy: login/oauth/access_token # This is official proxy address # Gitalk's display language depends on user's browser or system environment # If you want everyone visiting your site to see a uniform language, you can set a force language value # Available values: en | es-ES | fr | ru | zh-CN | zh-TW language: zh-CN</code></pre><p> 上面的配置项中有几个需要说明的:</p><ul><li>client_id 与 client_secret 是 Github 的 OAuth 认证(下一个部分会介绍).</li><li>github_id 与 admin_user 这里建议填一样,填成个人的 github 账号名(不是邮箱,也不是用户名).</li><li>proxy 默认即是上面的地址,它其实会回调到这里<a href='https://github.com/login/oauth/access_token'>https://github.com/login/oauth/access_token</a>.</li><li>无论是部署至 GitHub Page 个人网站,还是部署至个人申请购买的云服务器,上面三点是必须要实行的,proxy 403 问题主要是在部署至个人申请购买的云服务器中时会遇到,部署至 GitHub Page 个人网站不能进行自定义配置反向代理,所以只能使用 Gitalk 官方搭建的代理.</li></ul><p> Github 的 OAuth 认证.</p> <p style="background-color: #f3f3f3;padding: 10px;font-size: 0.875em;font-family: 'PingFang SC', 'Microsoft YaHei';">前提是得有一个 github 账号,才能注册 OAuth application ,这是<a href='https://github.com/settings/applications/new'> OAuth 应用注册地址</a>.</p><p> <img src="https://image.white-than-wood.zone/hexo/github_auth_register.png"></p><p> PS: 如果有自定义域名(如个人申请的阿里云域名),则在上图中填入自定义域名.</p><p> 注册之后,点击下图中’Generate a new client secret’按钮,在个人账号下的Settings -> Developer settings -> OAuth Apps下面可以查看 OAuth 认证 client_id 与 client_secret.</p><p> <img src="https://image.white-than-wood.zone/hexo/developer_settings.png"></p><p> Gitalk 自动初始化.</p> <p style="background-color: #f3f3f3;padding: 10px;font-size: 0.875em;font-family: 'PingFang SC', 'Microsoft YaHei';">原理: 通过 sitemap 中的信息,请求 github 开放 api 达到自动产生 issues 的目的.<br/> 基本的要求: github API 需要请求 token.</p><ul><li><p>申请 github Token.</p><p>我们需要使用 Personal access tokens 方式,这种方式限制每小时5000次,结合缓存功能,基本满足需求.<br>从 Github 的 <a href='https://github.com/settings/tokens'>Personal access tokens</a> 页面,点击 <a href='https://github.com/settings/tokens/new'>Generate new token</a>.</p></li></ul><p> <img src="https://image.white-than-wood.zone/hexo/access_tokens.png"></p><ul><li><p>安装 npm 依赖项.</p><pre><code>npm i -D md5 moment request xml-parsernpm i -S hexo-generator-sitemap</code></pre></li><li><p>配置 sitemap.</p><p>在根目录中创建 sitemap_template.xml.</p></li></ul> <figure class="highlight xml"><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="meta"><?xml version=<span class="string">"1.0"</span> encoding=<span class="string">"UTF-8"</span>?></span></span><br><span class="line"><span class="tag"><<span class="name">urlset</span> <span class="attr">xmlns</span>=<span class="string">"http://www.sitemaps.org/schemas/sitemap/0.9"</span>></span></span><br><span class="line"> {% for post in posts %}</span><br><span class="line"> <span class="tag"><<span class="name">url</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">loc</span>></span>{{ post.permalink | uriencode }}<span class="tag"></<span class="name">loc</span>></span></span><br><span class="line"> {% if post.updated %}</span><br><span class="line"> <span class="tag"><<span class="name">lastmod</span>></span>{{ post.updated.toISOString() }}<span class="tag"></<span class="name">lastmod</span>></span></span><br><span class="line"> {% elif post.date %}</span><br><span class="line"> <span class="tag"><<span class="name">lastmod</span>></span>{{ post.date.toISOString() }}<span class="tag"></<span class="name">lastmod</span>></span></span><br><span class="line"> {% endif %}</span><br><span class="line"> <span class="tag"><<span class="name">date</span>></span>{{ post.date }}<span class="tag"></<span class="name">date</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span>{{ post.title + ' | ' + config.title }}<span class="tag"></<span class="name">title</span>></span></span><br><span class="line"> {# nunjucks 模版语法 https://github.com/mozilla/nunjucks #}</span><br><span class="line"> <span class="tag"><<span class="name">desc</span>></span>{{ post.description | default(post.excerpt) | default(post.content) | default(config.description) | striptags | truncate(200, true, '') }}<span class="tag"></<span class="name">desc</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">url</span>></span></span><br><span class="line"> {% endfor %}</span><br><span class="line"><span class="tag"></<span class="name">urlset</span>></span></span><br></pre></td></tr></table></figure><ul><li>修改根目录下的 _config.yml.</li></ul><pre><code>#Sitemapsitemap: path: sitemap.xml template: ./sitemap_template.xml rel: false tag: true category: false</code></pre><ul><li>生成 sitemap.xml 文件.</li></ul><pre><code>hexo clean && hexo generate</code></pre><ul><li>根目录添加 talk-auto-init.js (注意还是不要将此文件置于github中,建议ignore).</li></ul> <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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>);</span><br><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> url = <span class="built_in">require</span>(<span class="string">'url'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> request = <span class="built_in">require</span>(<span class="string">'request'</span>);</span><br><span class="line"><span class="keyword">const</span> xmlParser = <span class="built_in">require</span>(<span class="string">'xml-parser'</span>);</span><br><span class="line"><span class="keyword">const</span> md5 = <span class="built_in">require</span>(<span class="string">'md5'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 配置信息</span></span><br><span class="line"><span class="keyword">const</span> config = {</span><br><span class="line"> <span class="attr">username</span>: <span class="string">'white-than-wood'</span>, <span class="comment">// GitHub repository 所有者,可以是个人或者组织。对应 Gitalk 配置中的 owner</span></span><br><span class="line"> <span class="attr">repo</span>: <span class="string">"white-than-wood.github.io"</span>, <span class="comment">// 储存评论 issue 的 github 仓库名,仅需要仓库名字即可。对应 Gitalk 配置中的 repo</span></span><br><span class="line"> <span class="attr">token</span>: <span class="string">'ghp_EuXHDkOxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'</span>, <span class="comment">// 前面申请的 personal access token</span></span><br><span class="line"> <span class="attr">sitemap</span>: path.<span class="title function_">join</span>(__dirname, <span class="string">'./public/sitemap.xml'</span>), <span class="comment">// 个人站点的 sitemap 文件地址</span></span><br><span class="line"> <span class="attr">cache</span>: <span class="literal">true</span>, <span class="comment">// 是否启用缓存,启用缓存会将已经初始化的数据写入配置的 gitalkCacheFile 文件,下一次直接通过缓存文件判断</span></span><br><span class="line"> <span class="attr">gitalkCacheFile</span>: path.<span class="title function_">join</span>(__dirname, <span class="string">'./gitalk-init-cache.json'</span>), <span class="comment">// 用于保存 gitalk 已经初始化的 id 列表</span></span><br><span class="line"> <span class="attr">gitalkErrorFile</span>: path.<span class="title function_">join</span>(__dirname, <span class="string">'./gitalk-init-error.json'</span>), <span class="comment">// 用于保存 gitalk 初始化报错的数据</span></span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> api = <span class="string">'https://api.github.com/repos/'</span> + config.<span class="property">username</span> + <span class="string">'/'</span> + config.<span class="property">repo</span> + <span class="string">'/issues'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 读取 sitemap 文件</span></span><br><span class="line"><span class="comment"> * 远程 sitemap 文件获取可参考 https://www.npmjs.com/package/sitemapper</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">sitemapXmlReader</span> = (<span class="params">file</span>) => {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">const</span> data = fs.<span class="title function_">readFileSync</span>(file, <span class="string">'utf8'</span>);</span><br><span class="line"> <span class="keyword">const</span> sitemap = <span class="title function_">xmlParser</span>(data);</span><br><span class="line"> <span class="keyword">let</span> ret = [];</span><br><span class="line"> sitemap.<span class="property">root</span>.<span class="property">children</span>.<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params">url</span>) {</span><br><span class="line"> <span class="keyword">const</span> loc = url.<span class="property">children</span>.<span class="title function_">find</span>(<span class="keyword">function</span> (<span class="params">item</span>) {</span><br><span class="line"> <span class="keyword">return</span> item.<span class="property">name</span> === <span class="string">'loc'</span>;</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">if</span> (!loc) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">const</span> title = url.<span class="property">children</span>.<span class="title function_">find</span>(<span class="keyword">function</span> (<span class="params">item</span>) {</span><br><span class="line"> <span class="keyword">return</span> item.<span class="property">name</span> === <span class="string">'title'</span>;</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">const</span> desc = url.<span class="property">children</span>.<span class="title function_">find</span>(<span class="keyword">function</span> (<span class="params">item</span>) {</span><br><span class="line"> <span class="keyword">return</span> item.<span class="property">name</span> === <span class="string">'desc'</span>;</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">const</span> date = url.<span class="property">children</span>.<span class="title function_">find</span>(<span class="keyword">function</span> (<span class="params">item</span>) {</span><br><span class="line"> <span class="keyword">return</span> item.<span class="property">name</span> === <span class="string">'date'</span>;</span><br><span class="line"> });</span><br><span class="line"> ret.<span class="title function_">push</span>({</span><br><span class="line"> <span class="attr">url</span>: loc.<span class="property">content</span>,</span><br><span class="line"> <span class="attr">title</span>: title.<span class="property">content</span>,</span><br><span class="line"> <span class="attr">desc</span>: desc.<span class="property">content</span>,</span><br><span class="line"> <span class="attr">date</span>: date.<span class="property">content</span>,</span><br><span class="line"> });</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</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="comment">// 获取 gitalk 使用的 id</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">getGitalkId</span> = (<span class="params">{</span></span><br><span class="line"><span class="params"> url: u,</span></span><br><span class="line"><span class="params"> date</span></span><br><span class="line"><span class="params"> }</span>) => {</span><br><span class="line"> <span class="keyword">const</span> link = url.<span class="title function_">parse</span>(u);</span><br><span class="line"> <span class="comment">// 链接不存在,不需要初始化</span></span><br><span class="line"> <span class="keyword">if</span> (!link || !link.<span class="property">pathname</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (!date) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">md5</span>(link.<span class="property">pathname</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="comment"> * 通过以请求判断是否已经初始化</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">string</span>} gitalk 初始化的id</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> {<span class="type">[boolean, boolean]</span>} 第一个值表示是否出错,第二个值 false 表示没初始化, true 表示已经初始化</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">getIsInitByRequest</span> = (<span class="params">id</span>) => {</span><br><span class="line"> <span class="keyword">const</span> options = {</span><br><span class="line"> <span class="attr">headers</span>: {</span><br><span class="line"> <span class="string">'Authorization'</span>: <span class="string">'token '</span> + config.<span class="property">token</span>,</span><br><span class="line"> <span class="string">'User-Agent'</span>: <span class="string">'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'</span>,</span><br><span class="line"> <span class="string">'Accept'</span>: <span class="string">'application/json'</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">url</span>: api + <span class="string">'?labels='</span> + id + <span class="string">',Gitalk'</span>,</span><br><span class="line"> <span class="attr">method</span>: <span class="string">'GET'</span></span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve</span>) =></span> {</span><br><span class="line"> <span class="title function_">request</span>(options, <span class="keyword">function</span> (<span class="params">err, response, body</span>) {</span><br><span class="line"> <span class="keyword">if</span> (err) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">resolve</span>([err, <span class="literal">false</span>]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (response.<span class="property">statusCode</span> != <span class="number">200</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">resolve</span>([response, <span class="literal">false</span>]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">const</span> res = <span class="title class_">JSON</span>.<span class="title function_">parse</span>(body);</span><br><span class="line"> <span class="keyword">if</span> (res.<span class="property">length</span> > <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">resolve</span>([<span class="literal">false</span>, <span class="literal">true</span>]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">resolve</span>([<span class="literal">false</span>, <span class="literal">false</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 class="comment">/**</span></span><br><span class="line"><span class="comment"> * 通过缓存判断是否已经初始化</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">string</span>} gitalk 初始化的id</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> {<span class="type">boolean</span>} false 表示没初始化, true 表示已经初始化</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> getIsInitByCache = (<span class="function">() =></span> {</span><br><span class="line"> <span class="comment">// 判断缓存文件是否存在</span></span><br><span class="line"> <span class="keyword">let</span> gitalkCache = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> gitalkCache = <span class="built_in">require</span>(config.<span class="property">gitalkCacheFile</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (e) {}</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">function</span> (<span class="params">id</span>) {</span><br><span class="line"> <span class="keyword">if</span> (!gitalkCache) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (gitalkCache.<span class="title function_">find</span>(<span class="function">(<span class="params">{</span></span></span><br><span class="line"><span class="params"><span class="function"> id: itemId</span></span></span><br><span class="line"><span class="params"><span class="function"> }</span>) =></span> (itemId === id))) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</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="comment">// 第一个值表示是否出错,第二个值 false 表示没初始化, true 表示已经初始化</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">idIsInit</span> = <span class="keyword">async</span> (<span class="params">id</span>) => {</span><br><span class="line"> <span class="keyword">if</span> (!config.<span class="property">cache</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">await</span> <span class="title function_">getIsInitByRequest</span>(id);</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="title function_">getIsInitByCache</span>(id) === <span class="literal">false</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">await</span> <span class="title function_">getIsInitByRequest</span>(id);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> [<span class="literal">false</span>, <span class="literal">true</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">const</span> <span class="title function_">gitalkInit</span> = (<span class="params">{</span></span><br><span class="line"><span class="params"> url,</span></span><br><span class="line"><span class="params"> id,</span></span><br><span class="line"><span class="params"> title,</span></span><br><span class="line"><span class="params"> desc</span></span><br><span class="line"><span class="params"> }</span>) => {</span><br><span class="line"> <span class="comment">//创建issue</span></span><br><span class="line"> <span class="keyword">const</span> reqBody = {</span><br><span class="line"> <span class="string">'title'</span>: title,</span><br><span class="line"> <span class="string">'labels'</span>: [id, <span class="string">'Gitalk'</span>],</span><br><span class="line"> <span class="string">'body'</span>: url + <span class="string">'\r\n\r\n'</span> + desc</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> options = {</span><br><span class="line"> <span class="attr">headers</span>: {</span><br><span class="line"> <span class="string">'Authorization'</span>: <span class="string">'token '</span> + config.<span class="property">token</span>,</span><br><span class="line"> <span class="string">'User-Agent'</span>: <span class="string">'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'</span>,</span><br><span class="line"> <span class="string">'Accept'</span>: <span class="string">'application/json'</span>,</span><br><span class="line"> <span class="string">'Content-Type'</span>: <span class="string">'application/json;charset=UTF-8'</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">url</span>: api,</span><br><span class="line"> <span class="attr">body</span>: <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(reqBody),</span><br><span class="line"> <span class="attr">method</span>: <span class="string">'POST'</span></span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve</span>) =></span> {</span><br><span class="line"> <span class="title function_">request</span>(options, <span class="keyword">function</span> (<span class="params">err, response, body</span>) {</span><br><span class="line"> <span class="keyword">if</span> (err) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">resolve</span>([err, <span class="literal">false</span>]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (response.<span class="property">statusCode</span> != <span class="number">201</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">resolve</span>([response, <span class="literal">false</span>]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="title function_">resolve</span>([<span class="literal">false</span>, <span class="literal">true</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="comment">/**</span></span><br><span class="line"><span class="comment"> * 写入内容</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">string</span>} fileName 文件名</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">string</span>} content 内容</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">write</span> = <span class="keyword">async</span> (<span class="params">fileName, content, flag = <span class="string">'w+'</span></span>) => {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve</span>) =></span> {</span><br><span class="line"> fs.<span class="title function_">open</span>(fileName, flag, <span class="keyword">function</span> (<span class="params">err, fd</span>) {</span><br><span class="line"> <span class="keyword">if</span> (err) {</span><br><span class="line"> <span class="title function_">resolve</span>([err, <span class="literal">false</span>]);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> fs.<span class="title function_">writeFile</span>(fd, content, <span class="keyword">function</span> (<span class="params">err</span>) {</span><br><span class="line"> <span class="keyword">if</span> (err) {</span><br><span class="line"> <span class="title function_">resolve</span>([err, <span class="literal">false</span>]);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> fs.<span class="title function_">close</span>(fd, <span class="function">(<span class="params">err</span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span> (err) {</span><br><span class="line"> <span class="title function_">resolve</span>([err, <span class="literal">false</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 class="title function_">resolve</span>([<span class="literal">false</span>, <span class="literal">true</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> <span class="title function_">init</span> = <span class="keyword">async</span> (<span class="params"></span>) => {</span><br><span class="line"> <span class="keyword">const</span> urls = <span class="title function_">sitemapXmlReader</span>(config.<span class="property">sitemap</span>);</span><br><span class="line"> <span class="comment">// 报错的数据</span></span><br><span class="line"> <span class="keyword">const</span> errorData = [];</span><br><span class="line"> <span class="comment">// 已经初始化的数据</span></span><br><span class="line"> <span class="keyword">const</span> initializedData = [];</span><br><span class="line"> <span class="comment">// 成功初始化数据</span></span><br><span class="line"> <span class="keyword">const</span> successData = [];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">const</span> item <span class="keyword">of</span> urls) {</span><br><span class="line"> <span class="keyword">const</span> {</span><br><span class="line"> url,</span><br><span class="line"> date,</span><br><span class="line"> title,</span><br><span class="line"> desc</span><br><span class="line"> } = item;</span><br><span class="line"> <span class="keyword">const</span> id = <span class="title function_">getGitalkId</span>({</span><br><span class="line"> url,</span><br><span class="line"> date</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">if</span> (!id) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`id: 生成失败 [ <span class="subst">${id}</span> ] `</span>);</span><br><span class="line"> errorData.<span class="title function_">push</span>({</span><br><span class="line"> ...item,</span><br><span class="line"> <span class="attr">info</span>: <span class="string">'id 生成失败'</span>,</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">const</span> [err, res] = <span class="keyword">await</span> <span class="title function_">idIsInit</span>(id);</span><br><span class="line"> <span class="keyword">if</span> (err) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`Error: 查询评论异常 [ <span class="subst">${title}</span> ] , 信息:`</span>, err || <span class="string">'无'</span>);</span><br><span class="line"> errorData.<span class="title function_">push</span>({</span><br><span class="line"> ...item,</span><br><span class="line"> <span class="attr">info</span>: <span class="string">'查询评论异常'</span>,</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (res === <span class="literal">true</span>) {</span><br><span class="line"> <span class="comment">// console.log(`--- Gitalk 已经初始化 --- [ ${title} ] `);</span></span><br><span class="line"> initializedData.<span class="title function_">push</span>({</span><br><span class="line"> id,</span><br><span class="line"> url,</span><br><span class="line"> title,</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`Gitalk 初始化开始... [ <span class="subst">${title}</span> ] `</span>);</span><br><span class="line"> <span class="keyword">const</span> [e, r] = <span class="keyword">await</span> <span class="title function_">gitalkInit</span>({</span><br><span class="line"> id,</span><br><span class="line"> url,</span><br><span class="line"> title,</span><br><span class="line"> desc</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">if</span> (e || !r) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`Error: Gitalk 初始化异常 [ <span class="subst">${title}</span> ] , 信息:`</span>, e || <span class="string">'无'</span>);</span><br><span class="line"> errorData.<span class="title function_">push</span>({</span><br><span class="line"> ...item,</span><br><span class="line"> <span class="attr">info</span>: <span class="string">'初始化异常'</span>,</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"> successData.<span class="title function_">push</span>({</span><br><span class="line"> id,</span><br><span class="line"> url,</span><br><span class="line"> title,</span><br><span class="line"> });</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`Gitalk 初始化成功! [ <span class="subst">${title}</span> ] - <span class="subst">${id}</span>`</span>);</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">''</span>); <span class="comment">// 空输出,用于换行</span></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'--------- 运行结果 ---------'</span>);</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">''</span>); <span class="comment">// 空输出,用于换行</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (errorData.<span class="property">length</span> !== <span class="number">0</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`报错数据: <span class="subst">${errorData.length}</span> 条。参考文件 <span class="subst">${config.gitalkErrorFile}</span>。`</span>);</span><br><span class="line"> <span class="keyword">await</span> <span class="title function_">write</span>(config.<span class="property">gitalkErrorFile</span>, <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(errorData, <span class="literal">null</span>, <span class="number">2</span>));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`本次成功: <span class="subst">${successData.length}</span> 条。`</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 写入缓存</span></span><br><span class="line"> <span class="keyword">if</span> (config.<span class="property">cache</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`写入缓存: <span class="subst">${(initializedData.length + successData.length)}</span> 条,已初始化 <span class="subst">${initializedData.length}</span> 条,本次成功: <span class="subst">${successData.length}</span> 条。参考文件 <span class="subst">${config.gitalkCacheFile}</span>。`</span>);</span><br><span class="line"> <span class="keyword">await</span> <span class="title function_">write</span>(config.<span class="property">gitalkCacheFile</span>, <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(initializedData.<span class="title function_">concat</span>(successData), <span class="literal">null</span>, <span class="number">2</span>));</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`已初始化: <span class="subst">${initializedData.length}</span> 条。`</span>);</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="title function_">init</span>();</span><br></pre></td></tr></table></figure><ul><li>修改 package.json 中 scripts 中的脚本,添加 “gitalk”:”node talk-auto-init.js”.</li></ul> <figure class="highlight json"><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="punctuation">{</span></span><br><span class="line"> <span class="attr">"scripts"</span><span class="punctuation">:</span> <span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"build"</span><span class="punctuation">:</span> <span class="string">"hexo generate"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"clean"</span><span class="punctuation">:</span> <span class="string">"hexo clean"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"deploy"</span><span class="punctuation">:</span> <span class="string">"hexo deploy"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"server"</span><span class="punctuation">:</span> <span class="string">"hexo server"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"publish"</span><span class="punctuation">:</span> <span class="string">"git push && hexo clean && hexo generate -deploy"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"gitalk"</span><span class="punctuation">:</span> <span class="string">"node talk-auto-init.js"</span></span><br><span class="line"> <span class="punctuation">}</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure><ul><li>测试 gitalk.</li></ul><pre><code>npm run talk</code></pre><ul><li>已经缓存过后的结果:</li></ul><pre><code>> [email protected] gitalk> node talk-auto-init.js--------- 运行结果 ---------本次成功: 0 条。写入缓存: 7 条,已初始化 7 条,本次成功: 0 条。参考文件 /Users/yinwk/keryi/white-than-wood.github.io/gitalk-init-cache.json.</code></pre><blockquote><p>proxy 403错误</p></blockquote><p> 部署至个人申请购买的云服务器时,由于跨域问题,会出现 proxy 403 forbidden.解决它主要有这么几个方案.</p><ul><li><p>nginx反向代理 解决方案(推荐).</p><p>在云服务器 nginx 的博客配置中加入如下内容(注意个人博客的域名必须经过 https ssl 安全证书备案).</p></li></ul> <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></pre></td><td class="code"><pre><span class="line">server {</span><br><span class="line"> listen 443 ssl http2;</span><br><span class="line"> #...</span><br><span class="line"> location = /login/oauth/access_token {</span><br><span class="line"> add_header Access-Control-Allow-Origin 'https://white-than-wood.zone/'; //这里改成个人的域名,并删除注释</span><br><span class="line"> add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';</span><br><span class="line"> add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';</span><br><span class="line"> if ($request_method = 'OPTIONS') {</span><br><span class="line"> return 204;</span><br><span class="line"> }</span><br><span class="line"> proxy_pass https://github.com/;</span><br><span class="line"> }</span><br><span class="line"> #...</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p> PS: nginx 反向代理之后,要将 proxy 指向代理域名下的链接回调进行修改.</p><pre><code>proxy: https://white-than-wood.zone/https://github.com/login/oauth/access_token</code></pre><ul><li><p>自建一个 workers.</p><p>地址: <a href='https://workers.cloudflare.com/'>CloudFlare Workers</a>.<br>参考文章: <a href='https://blog.dsrkafuu.net/post/2020/cloudflare-worker-cors-anywhere/'>使用 CloudFlare Workers 实现 CORS Anywhere</a>.</p></li><li><p>使用其他人搭建的代理.</p><p>比如这个 <a href='https://github.com/gitalk/gitalk/issues/429#issuecomment-778291781'>issues</a> 介绍到的:</p><pre><code> proxy: https://shielded-brushlands-08810.herokuapp.com/https://github.com/login/oauth/access_token</code></pre></li><li><p>使用 Gitalk 官方搭建的代理.</p><p>据gitalk issue<a href='https://github.com/gitalk/gitalk/issues/433'> gitalk 授权登录后报错403</a>,直接将使用 Gitalk 官方搭建的代理就可解决 403 forbidden 的问题,之前 Gitalk 官方搭建的代理由于太多人接入导致被官方限制使用,用户经常发现不能正常使用,现在已经对代理进行优化改进,不会出现限制使用的情况.</p><p><img src="https://image.white-than-wood.zone/hexo/gitalk_403.png"></p><pre><code> https://cors-anywhere.azm.workers.dev/https://github.com/login/oauth/access_token</code></pre></li><li><p>更新 gitalk 版本至1.7.2.</p><p>据gitalk issue<a href='https://github.com/gitalk/gitalk/issues/433'> gitalk 授权登录后报错403</a>,直接将 gitalk 版本升级至1.7.2就可解决 403 forbidden 的问题.</p><p><img src="https://image.white-than-wood.zone/hexo/gitalk_403.png"></p></li></ul><h4 id="hexo-GitHub-Page个人网站自定义域名"><a href="#hexo-GitHub-Page个人网站自定义域名" class="headerlink" title="hexo GitHub Page个人网站自定义域名"></a>hexo GitHub Page个人网站自定义域名</h4><p> 部署至 GitHub Page 个人网站配置自定义域名时,需要将个人申请购买的域名(最好是通过 https ssl 安全证书备案)进行域名解析,GitHub Page也需要CNAME配置.</p> <p style="background-color: #f3f3f3;padding: 10px;font-size: 0.875em;font-family: 'PingFang SC', 'Microsoft YaHei';">阿里云域名解析.</p><blockquote><p>步骤</p></blockquote><p> 域名解析简单来说实际上就是通过配置,可以使用不同的域名访问同一网站.首先进入阿里云服务管理平台,对已经申请购买的域名进行解析.</p><p> <img src="https://image.white-than-wood.zone/hexo/website_host_analy.png"></p><p> 添加记录或者对某一个列表项进行修改.可以使用 A记录 也可以使用 CNAME.</p> <p style="background-color: #f3f3f3;padding: 10px;font-size: 0.875em;font-family: 'PingFang SC', 'Microsoft YaHei';">A记录是将域名指向一个IPV4地址.<br/>CNAME是将域名指向另外一个域名.</p><p> <img src="https://image.white-than-wood.zone/hexo/website_host_analy_details.png"></p><p> 这里使用的是 A记录,将 white-than-wood.github.io 的 IP 地址作为记录值.</p><pre><code>ping white-than-wood.github.io</code></pre><p> <img src="https://image.white-than-wood.zone/hexo/website_host_analy_ping.png"></p><p> 主机记录,解析后的域名基本上就两种,‘@’和’www’.</p> <p style="background-color: #f3f3f3;padding: 10px;font-size: 0.875em;font-family: 'PingFang SC', 'Microsoft YaHei';">'@' 是源域名(主域名 white-than-wood.zone).<br/>'www' 则是 www.white-than-wood.zone.</p> <p style="background-color: #f3f3f3;padding: 10px;font-size: 0.875em;font-family: 'PingFang SC', 'Microsoft YaHei';">GitHub Page个人网站CNAME配置.</p><p> 在 source 目录下新建 CNAME 文件(不带任何后缀),在文件内写入你的主域名.</p> <figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">white-than-wood.zone</span><br></pre></td></tr></table></figure><p> 或者在 Github Page 个人配置下设置自定义域名,Github 会自动添加 CNAME 文件.</p><p> <img src="https://image.white-than-wood.zone/hexo/website_host_github_cname.png"></p><p> 这两种方式我这边是全部使用的.全部搞定之后,就可以通过 white-than-wood.zone 访问 white-than-wood.github.io GitHub Page 个人网站. </p><blockquote><p>延伸</p></blockquote> <p style="background-color: #f3f3f3;padding: 10px;font-size: 0.875em;font-family: 'PingFang SC', 'Microsoft YaHei';">既然已经通过阿里云服务 A记录 或者 CNAME 解析域名,为什么还需要在 GitHub Page 个人网站上配置 CNAME ?</p><p> 云服务 A记录 或者 CNAME 域名解析至 GitHub Page,通过 white-than-wood.zone 可以访问 GitHub Page 个人网站,那原来的 white-than-wood.github.io 域名怎么办呢? GitHub Page 也通过 CNAME 域名解析至 white-than-wood.zone. 使得 github.io 域名也可以直接访问个人申请购买的域名.</p><h4 id="hexo-其他配置"><a href="#hexo-其他配置" class="headerlink" title="hexo 其他配置"></a>hexo 其他配置</h4><blockquote><p>示例</p></blockquote><p> 更改根目录底下的 _config.yml 文件,更改网站title(标题)、author(作者)、keywords(关键字)、description(描述)配置,比如本人的配置.</p><pre><code>title: WTW's Frontendauthor: WTW(比木白)keywords: hexo,hexo-cli,hexo博客,hexo博客个人网站,hexo blogsubtitle: 道阻且长,行则将至description: 前端领域内所不知道的都探索于此!</code></pre><p> 更改主题目录底下的 _config.yml 文件,设置个人的github(github托管代码网址)、google(google个人资料网址)、gmail(gmail邮箱网址)、twitter(twitter个人微博网址)等等,比如本人的配置.</p><pre><code>social: GitHub: https://github.com/white-than-wood || fab fa-github E-Mail: https://[email protected] || fab fa-envelope Google: https://plus.google.com/u/0/103833130011211353424 || fab fa-google</code></pre><p> 发布到 GitHub Page 上之后,你会发现 README.md 莫名的消失,主要是因为 hexo 构建发布到远程的目录只是 source 目录,所以只要将 README.md copy 一份放到 source 目录下,并且配置渲染时跳过的 markdown 类型文件就可以了.</p><pre><code>#hexo构建渲染时会将source目录下markdown类型文件转译为html,我们不需要将README.md转译,所以直接skip render#更改根目录底下的_config.yml文件,修改skip_render属性skip_render: README.md</code></pre><p> 当创作中图片过小,阅读者无法看清的情况下,放大配置就已然成为必要 .fancybox 配置可以点击使图片放大,并可同时查看全文的图片.更改主题目录下的 _config.yml 文件 fancybox 属性.</p><pre><code># FancyBox is a tool that offers a nice and elegant way to add zooming functionality for images.# For more information: https://fancyapps.com/fancybox/fancybox: true</code></pre>]]></content>
<categories>
<category> hexo </category>
</categories>
<tags>
<tag> hexo </tag>
<tag> blog </tag>
<tag> next themes </tag>
</tags>
</entry>
</search>