-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearch.xml
More file actions
319 lines (319 loc) · 105 KB
/
search.xml
File metadata and controls
319 lines (319 loc) · 105 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[移动端数据打通常见手段]]></title>
<url>%2F2020%2F05%2F28%2F%E7%A7%BB%E5%8A%A8%E7%AB%AF%E6%95%B0%E6%8D%AE%E6%89%93%E9%80%9A%E5%B8%B8%E8%A7%81%E6%89%8B%E6%AE%B5%2F</url>
<content type="text"><![CDATA[用户增长之数据打通的常见手段(技术篇) 提起用户增长,我们常会提到《增长黑客》中的AARRR增长模型(Acquisition 拉新,Activation 活跃,Retention 留存, Revenue营销,Referral 传播),用户增长不仅仅只是增加用户,而是基于数据分析实现业绩增长的综合策略。 前言在用户增长中,我们常说让数据来说话。让数据说话的前提是有准确连续的数据,但在有些场景下准确且连续的数据并没有那么简单能够获取。举个例子,在产品的拉新阶段中,常常会在不同渠道投放引导下载链接,但由于渠道引流到下载安装到注册使用,整个链路比较长,存在数据无法连续打通的情况,原因是没有一个统一不变的唯一值(可以类比身份证,可以针对一个人,也可以针对渠道)作为身份传递。 例如从h5下载页到用户安装,h5无法直接拿到设备的身份证(ios: IDFA, android: IMEI),导致无法通过设备号去打通数据,或许会想到使用用户id如uin,但是对于新用户来讲,h5页无登录态是常态。那么我们应该如何获取用户设备的身份证去传递并打通链路上的数据呢?下面来介绍下业界常用的几种方式。 常用手段1、用户主动传递用户主动传递身份的方式通常有两种,一个是手机号,一个是邀请码,手机号相对来讲更精细化一些,原因是在手机号大部分情况下可以对应一个人,但邀请码一般可以对应多人。但是这两者都可以作为身份证(手机号是个人,邀请码是群体)去拉通数据。 手机号流程:用户输入手机号下载 =》 使用手机号注册登录 邀请码流程:用户获取邀请码 =》 注册登录后输入邀请码 优势:准确,无平台兼容性 劣势:用户体验不好,多的操作步骤让用户更易流失 2、用户无感知(推荐)设备唯一值常见的有 Android: imei(一批出货山寨机重复)、sim卡信息: IMSI、ICCID(双卡双待机,或获取卡槽顺序不一致,导致两次获取卡槽不同)、mac地址(模拟器可生成)、ANDROID_ID(刷机、root、恢复出厂设置可改变) iOS: MAC(iOS7已封杀)、IDFA-identifierForIdentifier(>=iOS6 有效,且用户可关闭)、IDFV-identifierForVendor(重装或升级系统可改变)、OpenUdid(重装或升级系统可改变) 对于Android和iOS来讲都有可以拿到的唯一值,足以满足大部分场景去识别设备。 也有将这些设备值聚合后生成唯一设备值(灯塔qimei) 但是仍然存在场景无法拿到,比如H5推广页中,如果是在自家产品中,可以通过app为h5赋能,比如将设备唯一值,种入cookie等等,在这种情况下,通过都是h5通过能获取到的设备信息(屏幕宽高比、GPU、UA等 + 指纹算法)生成设备指纹,上报到服务器,app在安装后也按同样算法生成指纹,然后匹配对应指纹行为(例如来自哪个渠道下载等)。这里也可以借助fingerprintjs2去生成。 优势:聚合后适用场景广 劣势:准确率取决于算法 动态打包动态打包是根据Android可以在应用商店外自主下载apk安装的特性可以根据不同渠道(不同人)生成不同的apk。在apk中写入身份信息,在安装后app直接读取即可完成匹配。 那么该如何动态打包,将信息写入apk,而且不影响签名校验和安装呢? 在Andorid中签名一共有三种方案V1、V2(Android7)、V3(Android9)。 在V1中,签名信息是放在META_INFO文件夹下的三个文件中(MANIFEST.MF、CERT.SF、CERT.RSA),此方案不会保护apk内所有文件,比如修改META_INFO下内容,是不会导致签名失效的,所以在META_INFO文件夹中加入其他文件(身份信息),是不会影响签名校验的。 在V2中,签名信息是写入V2的分块中,它比V1验证的更广,是检查整个apk,但是可以在V2签名块是一个Key-Value的结构,可以向其插入数据而不破坏签名,故我们这里可以往里加入身份信息。 在V3中,签名分块格式与V2相同,也是键值对,其中V3的键为0xf05368c0,所以实现与V2类似。 签名校验流程 优势:准确 劣势:适用范围小,仅限于android非应用商店场景 剪贴板利用h5可以操作剪贴板,app能读取剪贴板特性传递身份信息。 写入方式:document.execCommand('copy') 或 Clipboard API 如何让用户无感知剪贴板信息? 将身份信息(业务参数)传入服务器加密,获取凭证(md5值等),然后往剪贴板写入text/html类型值,text中写入凭证,这样用户粘贴出来也无法看到内容。app读取剪切板获取凭证便可以通过请求获取身份信息。 兼容性 优势:准确,跨平台 劣势:用户可以操作剪贴板,有兼容性问题 全局cookie在iOS9后,推出了新的控件SFSafariViewController,利用的特性是可以跨app与safari共享cookie。 使用safari打开下载地址时候,将身份信息写入到cookie。当用户下载安装app,启动app的时候,在app里面使用SFSafariViewController访问同一地址,app就可以读取cookie中的身份信息。 优势:准确,用户无感知 劣势:适用范围小(iOS9+, safari) 对比 用户主动传递 用户无感知 用户无感知 用户无感知 用户无感知 方案 手机号、注册码 设备唯一值 动态打包 剪贴板 全局cookie 优势 准确 聚合后适用场景广 准确 准确,跨平台 准确,用户无感知 劣势 触达路径长,用户易流失 准确率取决于算法 适用范围小,仅限于android非应用商店场景 用户可以操作剪贴板,有兼容性问题 适用范围小(iOS9+, safari) 最后每个方案都有自己的限制,就像我们常说的没有银弹,更好的策略应该是将其聚合起来,在不同场景下用不同方案权重判断后去决定使用那一套。 参考文章apk签名方案]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>用户增长 数据打通</tag>
</tags>
</entry>
<entry>
<title><![CDATA[学以致用之webpack(v4)]]></title>
<url>%2F2019%2F08%2F13%2F%E5%AD%A6%E4%BB%A5%E8%87%B4%E7%94%A8%E4%B9%8Bwebpack-v4%2F</url>
<content type="text"><![CDATA[学以致用之webpack(v4)背景 前端代码组织由命名空间(特别代表jquery, $)变为模块化(commonjs, amd, es6等),各类前端框架层出不穷(三驾马车等),为了解决开发大型项目所暴露出的语言缺陷及提高开发效率,各类新语言应运而生,(ts,flow,less,scss等)。但他们的源码都无法直接在浏览器中直接运行,而构建做的事情就是将源码转换成可执行的js,css,html代码。构建是工程化,自动化思想在前端开发中的体现,由于作为前端熟悉js,而nodejs又可以胜任所有构建需求,故大多数构建工具都是由nodejs开发的。 前端构建工具npm scriptnpm script npm是安装node附带的包管理器,npm script是npm内置功能,允许在package.json文件里使用scripts字段定义任务,实现原理为通过调用shell去运行脚本。 Grunt Grunt 有大量现成插件封装了常见的任务,也能管理任务之间的依赖关系,自动化地执行依赖的任务,灵活。相当于进化版的npm script,诞生是为了弥补npm script的不足。 1234567891011121314151617181920212223module.exports = function(grunt) { // Project configuration. grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), uglify: { options: { banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' }, build: { src: 'src/<%= pkg.name %>.js', dest: 'build/<%= pkg.name %>.min.js' } } }); // Load the plugin that provides the "uglify" task. grunt.loadNpmTasks('grunt-contrib-uglify'); // Default task(s). grunt.registerTask('default', ['uglify']);}; GulpGulp 一个基于流的自动化构建工具。除了可以管理和执行任务,还支持监听文件、读写文件。好用且不失灵活,可以看做是Grunt的加强版。相对于Grunt,增加了监听文件,读写文件、流式处理。 Fis3Fis3 来自百度的优秀国产构建工具,fis3集成了web开发中的常用构建功能。 读写文件 资源定位 文件指纹(通过useHash配置输出文件时为文件url+md5戳,来优化浏览器缓存) 文件编译(如es6=>es5) 压缩文件 图片合并(雪碧图,通过spriter配置合并css里导入的图片到一个文件中,来减少http请求数) fi3很强大,内置了许多功能,是一种专注于web开发的完整解决方案,如果将Grunt、Gulp比作汽车发动机,那么fis3就是一辆完整汽车 Webpackwebpack webpack是一个现代 JavaScript 应用程序的静态模块打包器(module bundler),在webpack中一切文件如js,css,scss,图片,模板等皆模块,这样的好处是能清晰的描述各个模块之前的依赖关系,以方便webpack对模块进行组合和打包,最终输入浏览器能使用的静态资源。 rolluprollup rollup是一个跟webpack类似但是专注于es6的模块打包工具,它的亮点在于Tree Shaking,以除去已定义但未被使用代码并进行Scope Hoisting(作用域提升),以减小输出文件的大小及提升运行性能。然后这些亮点随后就被webpack模仿并实现。使用起来与webpack及其相似。 对比 npm script Grunt Gulp Fis3 Webpack Rollup 优势 内置 灵活,只负责执行我们定义的任务。大量可复用的插件封装好了常见的构建任务 监听文件,读写文件、流式处理。 集成强大,配置简单,开箱即用 1.开箱即用,一步到位2.可通过plugin扩展,完整好用且不失灵活3.社区活跃庞大,良好的开发体验 打包js库更有优势(没有webpack打包后的那段模块加载,执行,缓存代码) 缺点 功能较简单,无法方便管理多个任务之间的依赖 集成度不高,要写很多配置后才可以用,无法做到开箱即用 集成度不高,要写很多配置后才可以用,无法做到开箱即用 目前官方已不再维护,且不支持最新版本Node 只能用于采用模块化开发的项目 生态链不完善,体验不如webpack,功能不如webpack完善 why webpack 提供一站式的解决方案 良好的生态链和维护团队,提供良好的开发体验并保证质量 被广泛使用和验证,可以很轻松找到各个场景下的经验分享 start webpack核心概念 Entry: 入口 Module: 模块 Chunk: 代码块,一个Chunk由多个模块组合而成,用于代码合并与分割 Loader: 模块转换器 Plugin: 扩展插件 Output: 输出结果 Resolve: webpack如何寻找模块所对应文件 关于webpack的配置项有很多,这里大家通过文档去了解。 通常我们可以用如下经验判断如何配置webpack 若想让源文件加入构建流程被webpack控制,则配置entry 若想自定义输出文件的位置和名称,则配置output 若想自定义寻找依赖模块时的策略,则配置resolve 若想自定义解析和转换文件的策略,则配置module,通常是配置module.rules里的loaders 若其他大部分需求可能通过plugin去实现,则配置plugin 实战使用es6部分浏览器支持es6或者更高版本不全,故需要做转换为支持良好的es5代码包含如下两件事情: es6及更新版本语法用es5实现 为新的Api注入polyfill es6语法 babel就是用来满足上述需求的。它是一个javascript编译器,让我们使用最新语言特性而不用担心兼容问题。 在babel 7.0以上版本官方推荐配置文件由之前.babelrc命名转为babel.config.js。 以编程方式创建配置文件,编译node_module目录下的模块? babel.config.js 1234567891011module.exports = function (api) { api.cache(true); const presets = [ ... ]; const plugins = [ ... ]; return { presets, plugins };} plugins: 配置插件,配置的插件可以控制如何转换代码。默认前缀babel-plugin (bable-plugin-myPlugin 等于 myPlugin) 注:插件在presets前运行,插件顺序是从前往后执行,而presets顺序是颠倒的(从后往前) 学习开发一个babel插件 presets: 告诉babel要转换的源码使用了哪些新的语法特性,一个presets对一组新语法的特性提供了支持,多个presets可以叠加,其实就是一组Plugins的集合。 通常为分为三大类 已经被写入EMCAScript标准里的特性:ES2015, ES2016, ES2017,Env(包括当前所有EMCAScript标准里的最新特性) 被社区提出来但还未写入标准里的特性:stage0(不确定是否会纳入标准),stage1(值得被纳入),stage2(已被起草,将会被纳入),stage3(已定稿,各大浏览器厂商和node.js社区已开始着手实现),stage4(在接下来的一年将会加入标准)。 用于支持一些特定场景下的语法的特性,和ECMAScript标准无关,如babel-preset-react用于支持React开发里的JSX语法 webpack中如何接入babel? 通过loader去接入babel 12345678910111213141516171819202122232425262728293031// npm install -D babel-loader @babel/core @babel/preset-env webpack// webpack.config.jsmodule: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader', // options: { // presets: ['@babel/preset-env'], // plugins: ['@babel/plugin-transform-runtime'] // } } } ]}// babel.config.jsmodule.exports = function (api) { api.cache(true); const presets = ['@babel/preset-env']; const plugins = ['@babel/plugin-transform-runtime']; return { presets, plugins };} 使用postcsspostcss是一个css处理工具,包括向css自动加前缀,使用下一代css语法等。postcss和css的关系就像babel和javascript的关系。 webpack中如何接入postcss? 12345678910111213141516171819// webpack.config.jsmodule.exports = { module: { rules: [ { test: /\.css$/, exclude: /node_modules/, use: ['style-loader','css-loader','postcss-loader'] } ] }}// postcss.config.jsmodule.exports = { plugins: [ require('autoprefixer') ]} 使用react核心:jsx解析,引入babel-preset-react 使用vue核心:vue-loader,提取.vue文件中script、style、template,然后将他们交给对应的loader处理,vue-template-compiler:将template编译成对应可执行的js代码,预先编译好的html模板相对于在浏览器中编译html模板性能更好。 webpack之优化前言优化主要可以分为两个层面 优化开发体验 优化构建速度 优化使用体验,通过自动化手段完成一些重复工作 优化输出质量 减少加载时间 提升代码性能 积少成多,水滴石穿! 开发体验篇优化构建速度缩小文件的搜索范围webpack启动后从entry出发,解析出文件中的导入语句,再递归解析。 遇到导入语句做两件事: 根据导入语句找文件,例如require('vue') => ./node_modules/vue/dist/vue/runtime/common.js(一般为package.json中定义的main),require('./util') => ./util.js 根据找到文件的后缀,使用配置中Loader去处理文件。 虽然这两件事情对于处理一个文件来讲是非常快的,但是当项目日益庞大后文件量会变得非常大,此时构建速度慢的问题就会暴露出来。 优化loader配置由于loader对文件的转换操作很耗时,故让尽可能少的文件被loader处理,也是一个优化构建速度的方法。 由于loader是通过test, include, exclude来命中,故我们应该缩小匹配范围。 12345678910111213modules:{ rules: [{ // 准确匹配,缩小命中范围。 test: /\.js$/, use: { loader: ’babel-loader‘, options: { cacheDirectory: true, // babel-loader可以通过开启此选项缓存转换出的结果 } }, include: path.resolve(__dirname,"src"), }]} tip: rule.inlcude|rule.test|rule.exclude是rule.resource.xxx的缩写 优化resolve.modules配置此配置用于配置webpack寻找第三方模块的目录,默认值为node_modules,是相对路径,故默认行为是先去当前目录找,然后依次往上。但是在实际项目中,第三方模块都放在根目录下,故应使用绝对路径,使用绝对路径时,只会搜索给定目录。 12345module.exports={ resolve:{ modules: [path.resolve(__dirname, 'node_modules')] }} 优化resolve.mainFields配置此配置用于配置第三模块使用的入口文件,通常在第三方模块的package.json中有main, broswer, module字段来描述入口文件。而该配置可以约束这一范围,通常配合target使用。 12345module.exports={ target: 'web', // 当target配置为web, webworker或未指定 mainFileds默认值为['broswer','module','main'] // 当target为其他(包括node),mainFileds默认值为['module', 'main']} webpack采用策略,是从数组中依次查找,没有就查找下一个,为了减少搜索步骤,在明确第三方模块的入口文件描述字段时,我们可以将它设置的尽量少,如大部分第三方模块都会申明main字段,故可配置mainFileds:['main']。 tip: 此方法有风险,只要有一个第三方模块无main字段导致出错,也会造成构建的代码无法正常运行 优化resolve.extensions配置在导入语句没带文件后缀时,webpack会尝试添加后缀询问文件是否存在,此配置用于配置webpack在尝试过程中用到的后缀列表,默认值为['.wasm', '.mjs', '.js', '.json']。 故在配置时应遵守如下几点 后缀列表要尽可能小,不存在的后缀就无需添加上 频率高的文件放前面 在写入导入语句时,尽可能的带上后缀,避免寻找过程。 配置如下: 123resolve: { extensions: ['js']} tip: 按照数组顺序依次询问,询问存在则停止 优化module.noParse配置此配置可以让webpack忽略对部分没有采用模块化的文件的递归解析处理,从而提高构建性能。如jquery lodash就是如此。 123module: { noParse: /jquery|lodash/,} tip: 被忽略的文件不应包含import, require, define等模块化语句,不然还导致构建出的代码中包括无法在浏览器环境下执行的模块化语句。 使用DllPlugindll: 最初由microsoft引入的动态链接库,一个动态链接库包含为其他模块调用的函数和数据。 核心思想为: 基础模块抽离,打包为一个个单独的动态链接库,在一个动态链接库中可包含多个模块 当需要导入的模块存在于某个动态链接库时,这个模块不再被打包,而是从动态链接库中获取 页面依赖的所有动态链接库都需要被加载 其实,提升构建效率的核心就在于大量复用的模块只需编译一次。 在webpack中使用主要通过如下两个内置的插件接入。 DllPlugin插件,用于打包出一个个单独的动态链接库,生成mainfest.json供DllReferencePlugin映射依赖项。 DllReferencePlugin插件,用于在主要的配置文件中引入DllPlugin插件打包好的动态链接库文件 1234567891011121314151617181920212223242526272829303132333435// webpack.dll.config.jsconst path = require('path');const DllPlugin = require('webpack/lib/DllPlugin');module.exports = { entry: { react: ['react', 'react-dom'], }, output: { // 输出的动态库链接名,[name]代表当前动态链接库的名称 filename: '[name].dll.js', path: path.resolve(__dirname, 'dist'), // 存放动态链接库的全局变量名称,为了防止全局变量名冲突 library: '_dll_[name]', }, plugins: [ new DllPlugin({ // 全局变量名称,需与output.library中保持一致 // 该字段的值将输出在对应的mainfest.json文件中的name字段 name: '_dll_[name]', // 描述动态链接库中的mainfest.json文件输出时的文件名称 path: path.join(__dirname, 'dist', '[name].mainfest.json') }) ]}// webpack.config.jsconst path = require('path');const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');plugins:[ new DllReferencePlugin({ // 描述定义的动态链接库的内容 mainfest: require(path.join(__dirname, 'dist','react.mainfest.json')) })] tip: DllPlugin中的name必须与output.library中保持一致,name会影响输出mainfest.json中的name字段的值,而DllReferencePlugin会去mainfest.json文件中读取name的值,将值的内容作为在从全局变量中获取动态链接库的内容时的全局变量名 使用HappyPack?thread-loaderHappyPack可以将任务分解给多个子进程去并发执行,子进程处理完后再将结果发给主进程。 tip: 由于js是单线程模型,想发挥多cpu的功能,只能通过多进程来实现,而无法通过多线程。 但是请注意,在webpack4中,HappyPack不一定适用,下面节选了该开源项目的README。 维护模式通知 Webpack’s native performance is improving and (I hope) it will soon make this plugin unnecessary. // webpack4的性能正在不断提高,我希望这个插件将变得没有必要。 FAQ Is it necessary for Webpack 4?Short answer: maybe not. Look at thread-loader and if it works for you - that’s great, otherwise you can try HappyPack and see which fares better for you. // 如果thread-loader能满足你的需求,那最好,否则你可以试试HappyPack看看哪一种更合适。 主要原因是webpack4自身的优化提升和官方发布了一个和它做相同事情的loaderthread-loader. 虽然如此,我们还是简单了解下HappyPack的使用 12345678910111213141516171819202122232425262728293031// webpack.config.jsconst HappyPack = require('happypack');// 构造出共享进程池,包含5个子进程,以防止资源占用过多const happyThreadPool = HappyPack.ThreadPool({size: 5});exports.plugins = [ new HappyPack({ id: 'jsx', // 实例id threads: 4, // 开启的子进程数量,默认为3 threadPool: happyThreadPool, // 使用共享进程池中的子进程去处理任务 loaders: [ 'babel-loader' ] // 用法与Loader配置一致 }), new HappyPack({ id: 'styles', threads: 2, threadPool: happyThreadPool, loaders: [ 'style-loader', 'css-loader', 'less-loader' ] })];exports.module.rules = [ { test: /\.js$/, use: 'happypack/loader?id=jsx' // 文件的处理都交给happypack/loader,id为上述声明的实例id,其中实例的loaders代表happypack处理完后接下来执行的loader }, { test: /\.less$/, use: 'happypack/loader?id=styles' },] 接下来我们来了解下thread-loader的使用 12345678910111213141516171819202122232425262728293031323334353637383940414243444546module.exports = { module: { rules: [ { test: /\.js$/, include: path.resolve("src"), use: [ "thread-loader", // your expensive loader (e.g babel-loader) ] } ] }}// thread-loader选项options: { workers: 2, // 每一个worker都是一个单独的nodejs进程,开销约为600ms,还存在进程间通信的开销,默认为cpu-1的数量 // 一个worker并行处理任务的数量 // defaults to 20 workerParallelJobs: 50, // additional node.js arguments workerNodeArgs: ['--max-old-space-size=1024'], // Allow to respawn a dead worker pool // respawning slows down the entire compilation // and should be set to false for development poolRespawn: false, // timeout for killing the worker processes when idle // defaults to 500 (ms) // can be set to Infinity for watching builds to keep workers alive poolTimeout: 2000, // number of jobs the poll distributes to the workers // defaults to 200 // decrease of less efficient but more fair distribution poolParallelJobs: 50, // name of the pool // can be used to create different pools with elsewise identical options name: "my-pool" } 使用UglifyjsWebpackPlugin?TerserWebpackPluginUglifyjsWebpackPlugin通过uglifyjs来压缩js。 1234567const UglifyJsPlugin = require('uglifyjs-webpack-plugin');module.exports = { optimization: { minimizer: [new UglifyJsPlugin()], },}; 但是只支持es5,而且由于之前uglifyjs的分支uplifyjs-es不再维护,但该分支的性能测试中优于master三倍,故从此分支fork出了个新项目terser,terser最大程度兼容了uglifyjs-es和uglifyjs。同时支持es6. TerserWebpackPlugin通过terser来压缩js,而且被应用至webpack.optimization.minimize中。 如果想要覆盖默认配置的话 12345678910111213141516const TerserPlugin = require('terser-webpack-plugin');module.exports = { optimization: { minimizer: [ new TerserPlugin({ cache: true, parallel: true, sourceMap: true, // Must be set to true if using source-maps in production terserOptions: { // https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions } }), ], }}; 优化使用体验文件监听优化文件监听是由webpack提供,其原理为webpack定时获取文件最后编辑时间并保存,若当前保存与获取不一致则视为文件发生变化。 可以直接配置watch或设置devServer(推荐)。 1234567devServer: { watchOptions: { poll: 1000, // 轮询频率 1s 一千次 aggregateTimeout: 300, // 节流,监听到变化发生后等300ms再去执行 防止重新编译频率过快 ignored: /node_modules/, // 排除 大多数情况下我们不需要修改第三方模块 }} tip: poll 的 aggregateTimeout的优化会导致监听的灵敏度变低 热更新原理:向开发的网页中注入一个代理客户端来连接devServer和网页(network中的websoket) 1234 devServer: { hot: true }// 开启hot后,webpack4自动会添加webpack.HotModuleReplacementPlugin插件,无需像3需要手动引入 输出质量篇优化加载时间区分环境webpack4推出的mode极大的促进了开箱即用性,每个mode下的默认配置都是很便利,在大多数情况下无需更多配置,所以一定要把握住。根据实际场景使用development、production。 Option Description development Sets process.env.NODE_ENV on DefinePlugin to value development. Enables NamedChunksPlugin and NamedModulesPlugin . production Sets process.env.NODE_ENV on DefinePlugin to value production . Enables FlagDependencyUsagePlugin , FlagIncludedChunksPlugin ,ModuleConcatenationPlugin , NoEmitOnErrorsPlugin ,OccurrenceOrderPlugin , SideEffectsFlagPlugin andTerserPlugin . none Opts out of any default optimization options 接入CDNCDN一般会为资源开启很长时间的缓存,如何避免? 业界做法:其实就是只将静态资源js、css、图片等文件,开启cdn和缓存,同时为每个文件名带上由文件内容算出的hash值。 核心在于 output.publicPath设置为对应cdn域名,我们目前做法也是类似,只是多了ng映射。 使用Tree Shakingproduction mode 默认会采用,但是为什么会无效呢? tree shaking依赖于静态的es6模块化语法,故让tree shaking正常工作的前提是,提交给webpack的js代码必须要采用了es6的模块化语法。 而在我们的项目中常常会使用到babel,故需要babel保留es6模块化语句。 1234567"presets": [ ["env": { "modules": false, } ]] tip: 提取公共代码主要目的是为了避免相同资源重复加载,利用浏览器缓存去减少网络传输流量和降低服务器成本。 CommonsChunkPlugin(v3) => SplitChunksPlugin(v4) chunk是一系列文件的集合,在一个chunk中会包含这个chunk的入口文件及其所依赖的文件。 使用为optimization.splitChunks,webpack的默认配置为 123456789101112131415161718192021222324252627module.exports = { //... optimization: { splitChunks: { chunks: 'async', // 从哪些chunks里抽取代码 initial、 async、 all 也可以为函数(自定义) minSize: 30000, // 满足30k及以上才提取 maxSize: 0, // 不限制 minChunks: 1, // 模块最小被引用次数 maxAsyncRequests: 5, // 按需加载次数不得超过5次 maxInitialRequests: 3, // 初始化加载次数不得超过3次 automaticNameDelimiter: '~', automaticNameMaxLength: 30, name: true, // 自动生成文件名 cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: -10 }, default: { minChunks: 2, priority: -20, // 权重 reuseExistingChunk: true // 是否使用已有的chunk, 若当前chunk包含模块已经被抽取,则不会重新生成新的。 } } } }}; 我们的核心在于cacheGroups,缓存组会继承splitChunks的配置,但是test、priorty和reuseExistingChunk只能用于配置缓存组。默认的缓存组权重为负数,自定义默认权重为0,故很容易覆盖。当然你也可以直接关闭默认配置。 实际使用 123456789101112131415161718192021222324{ chunks: 'all', cacheGroups: { default: false, // 关闭默认配置 vendors: false, // 关闭默认配置 commons: { name: 'commons', chunks: 'all', minChunks: 15 }, react: { name: 'commons', chunks: 'all', test: /[\\\/]node_modules[\\\/](react|react-dom)[\\\/]/ }, styles: { name: 'styles', test: /\.+(css|less)$/, chunks: 'all', // enforce: true, // 让webpack始终忽略splitChunks.minSize, splitChunks.minChunks, splitChunks.maxAsyncRequests and splitChunks.maxInitialRequests这些条件约束。即样式会提出成公共 minChunks: 2 } }} Prefetching/Preloading modulestip: webpack4.6+支持 预拉取/加载模块: prefetch: 将来某些导航可能需要的资源 preload: 当前导航可能需要的资源 12import(/* webpackPrefetch: true */ 'LoginModal');import(/* webpackPreload: true */ 'LoginModal'); 对比: preload的模块是与父模块并行加载,而prefetch是等父模块加载完成再加载。 preload具有中等优先级并且是即时加载,prefetch是浏览器空闲加载。 浏览器支持程度不一样 tip: 使用preload加载跨域资源如字体等需要加上crossorign,否则会加载两次,主要是跟浏览器加载不同资源的优先级规则相关 更多关于prefetch、preload、资源优先级规则 提升代码性能作用域提升Scope Hoisting :分析模块之间的依赖关系,尽可能将被打散的模块合并到一个函数中,但是前提是不能造成代码冗余,因此只有那些被引用了一次的模块才能被合并。 通过ModuleConcatenationPlugin去实现,现在product mode也会默认开启。 tip: 也是去分析模块之间的依赖关系,故仅适用于es6模块,注意babel中modules:false 打包分析工具1webpack --mode production --profile --json > stats.json webpack-chart: 交互式饼图 webpack-visualizer: 可视化和分析. webpack-bundle-analyzer: 一个插件和CLI实用程序,(推荐,现在我们项目也是使用该插件) webpack bundle optimize helper: 此工具将分析您的打包,并为您提供有关改进措施的可操作建议,以减少打包大小。 bundle-stats: 生成打包报告(包大小,资产,模块)并比较不同构建之间的结果。 展望webpack5变动说明 核心变更点: 使用持久化缓存提高构建性能; 使用更好的算法和默认值改进长期缓存(long-term caching); 清理内部结构而不引入任何破坏性的变化; 引入一些breaking changes,以便尽可能长的使用v5版本。 v3 => v4的重点在于webpack添加了默认值。(改革开放) v4 => v5更像是优化内部问题,加强算法及缓存。(反腐维稳)]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>webpack</tag>
</tags>
</entry>
<entry>
<title><![CDATA[本地配置https证书]]></title>
<url>%2F2019%2F04%2F04%2F%E6%9C%AC%E5%9C%B0%E9%85%8D%E7%BD%AEhttps%E8%AF%81%E4%B9%A6%2F</url>
<content type="text"><![CDATA[本地配置https在某些场景下,我们可能会需要使用在本地配置https去调试代码。 可以通过openssl+nginx去实现。 1.安装openssl下载链接有两种方式去下载使用openssl 自己构建 安装预编译版本 1.自己构建需要一个构造环境(visual studio等),和一些先决条件 源码:https://www.openssl.org/source/ 安装手册:https://github.com/openssl/openssl/blob/OpenSSL_1_1_0-stable/INSTALL 2.预编译版本window用户下载链接:http://apecoderddd.oss-cn-beijing.aliyuncs.com/Win64OpenSSL-1_1_0i.exe 其他用户及版本更多:https://oomake.com/download/openssl windows用户注意 使用预编译版本下载安装完成后,记得配置path,安装目录/bin。 2.生成https证书1.生成server.key 基于des3算法生成的私钥,生成时需输入至少4位密码 openssl genrsa -des3 -out server.key 2048 2.生成无密码的server.key openssl rsa -in server.key -out server.key 3.生成server.csr openssl req -new -key server.key -out server.csr 因为你是在本地调试使用所以common name配置localhost就好 4.生成crt openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt 3.配置nginx12345678910111213141516server { listen 80; server_name open.cs.test.zbjdev.com; location / { root html; index index.html index.htm; proxy_pass http://127.0.0.1:3005; } listen 443 ssl; ssl_certificate /workspace/nginx/ssl/server.crt; ssl_certificate_key /workspace/nginx/ssl/server.key; error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } 关键在于listen 443 ssl; /workspace/nginx/ssl/server.crt这个位置是相对于当前盘符的。 弱弱吐槽一句 还是翻墙看国外文档吧 参考文档: https://www.htpcguides.com/generate-openssl-certificates-nginx-win-linux-mac/]]></content>
<categories>
<category>工具</category>
</categories>
<tags>
<tag>巧计</tag>
</tags>
</entry>
<entry>
<title><![CDATA[函数式编程]]></title>
<url>%2F2019%2F03%2F11%2F%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B%2F</url>
<content type="text"><![CDATA[函数式编程含义函数式编程属于声明式编程的一部分,我们常常会拿命令式编程与函数式编程作比较 初步定义命令式编程语言泛指所有把修改变量的值当作最基本计算方式的语言。 函数式编程语言指把一个程序的输出定义为其输入的数学函数的语言,纯函数式编程没有内部状态的概念,也没有副作用。 区别1.计算模型的区别 命令式模型:图灵-图灵机 通过修改变量的值来影响后续的计算 函数式模型:邱奇-lambda演算 强调变换规则的应用 2.设计的区别 命令式:冯诺依曼体系结构(存储程序原理,把程序本身当作数据来对待,程序和该程序处理的数据用同样的方式储存。) 函数式:数学函数(把程序输出定义为其输入的一个数学函数,无内部状态无副作用) 副作用:在计算机科学中,一个函数或表达式,如果除了返回值之外,还修改了某个状态或者和调用它的函数或外部环境进行了明显的交互,就被称为是有副作用的。 3.风格区别 命令式语言:面向过程(c等) 面向对象(c++、java等,在不断进化,向函数式语言靠拢) 函数式语言:函数被当做第一类对象处理(作为参数传递,从函数中返回等) jsjs没有明确的归类,多范式编程语言。 核心概念: 不可变性 纯函数 数据转换 高阶函数 递归 1. 不可变性: 数据是不可变的12345678910//赋值对象某个字段const rateColor = (color, rating) => ({...color, rating});let lawnColor = { rating: 5, title: 'lawn'};console.log(rateColor(lawnColor, 5));//数组添加新元素// const addColor = (color, array) => array.concat({color});const addColor = (color, array) => [...array,{color}];console.log(addColor('blue', [{color: 'red'}])); 2. 纯函数:一个返回结果只依赖于输入参数的函数。 纯函数至少需要接受一个参数并且总是返回一个值或者其他函数,不会产生任何副作用,不修改全局变量。 将输入的参数当做不可变的数据 1234567891011121314151617181920212223242526 let frank = { name: 'frank', canRead: false, canWrite: false,}//非纯函数const selfEducate1 = function(){ frank.canRead = true; frank.canWrite = true; return frank;}//首先没有接受任何参数,还修改了除其作用域之外的变量//纯函数const selfEducate2 = function(person){ return { ...person, canRead: true, canWrite: true, }}console.log(selfEducate2(frank));console.log(frank); 3. 数据转换:使用函数生成转换后的副本如果数据不可变,那如何进行状态转换, 函数式编程的做法是将一种数据转换成另外一种数据,我们使用函数生成转换后的副本123456789101112131415161718192021222324252627282930// 当从数组中移除某个元素时 我们倾向于使用 Array.filter 替代 Array.pop 或 Array.splice 因为filter不会改变原数组// Array.map 与 Array.reduceconst schools = ['Yorktown', 'Washington & Lee', 'Wakefield'];// remove itemconst cutSchool = (cut, list) => list.filter(school => school !== cut);const highSchools = schools.map(school => {name: school});// 改变数组中某个值const editName = (oldName, name, arr) => arr.map(item => (item.name === oldName) ? ({...item, name}) : item);// 将对象转换成数组const schoolObj = { 'Yorktown': 10, 'Washington & Lee': 2, 'Wakefield': 5,}Object.keys(schoolObj).map(item => ({ name: item, wins: schoolObj[item],}))// 数组去重const colors = ['red', 'blue', 'red', 'green'];colors.reduce((distinctColors, color) => (distinctColors.includes(color) ? distinctColors : [...distinctColors, color]), []); 4. 高阶函数 可以操作其他函数的函数可以将函数作为参数传递,也可以返回一个函数,或者二者兼而有之12const invokeIf = (conditon, fnTrue, fnFalse) => conditon ? fnTrue() : fnFalse();console.log(invokeIf(false, () => 'success', () => 'failed')); 柯里化: 一种将某个操作中已经完成的状态保留,直到其余部分完成后可以一并提供的机制通过在一个函数中返回另外一个函数实现1234const userLogs = userName => message => console.log(`${userName} => ${message}`);const log = userLogs('xiao ming');log('play Basketball'); 5. 递归 自己调用自己是用户创建的函数调用自身的一种技术1234567// 倒计时const countdown = (value, fn, delay=1000) => { fn(value); return value > 0 ? setTimeout(() => countdown(value - 1, fn),delay) : value;}countdown(10, value => console.log(value)); 总结 保持数据的不可变性 确保尽量使用纯函数,只接受一个参数,返回数据或者其他函数 尽量使用递归处理循环(如果有可能的话) 综合应用构建一个时钟,显示hh:mm:ss tt(tt 为 am 或 pm)日期 每个字段保证是双位数字(如1 补为 01)显示每秒的时钟变化 命令式编程1234567891011121314151617181920212223242526272829const oneSecond = 1000;const prependZero = (value) => { return value < 10 ? `0${value}` : `${value}`;}const getColckTime = () => { const now = new Date(); const nowTime = { hours: now.getHours(), minutes: now.getMinutes(), seconds: now.getSeconds(), ampm: 'am', }; if(nowTime.hours > 12){ nowTime.ampm = 'pm'; nowTime.hours -= 12; } return `${prependZero(nowTime.hours)}:${prependZero(nowTime.minutes)}:${prependZero(nowTime.seconds)} ${nowTime.ampm}`;}const logColckTime = () => { console.clear(); console.log(getColckTime());}setInterval(logColckTime, oneSecond); 函数式编程123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354// 将这个程序分解成小的函数,最后组合成时钟程序// 函数作为参数 返回一个参数为初始结果 返回值为顺序调用传入函数后的最终结果的函数const compose = (...fns) => (args) => fns.reduce((result, fn) => fn(result), args);const oneSecond = () => 1000;const log = (message) => console.log(message);const getCurrentTime = () => new Date();const clear = () => console.clear();const serializeColckTime = date => ({ hours: now.getHours(), minutes: now.getMinutes(), seconds: now.getSeconds(),});const civilianHours = clockTime => ({ ...clockTime, hours: (clockTime.hours > 12) ? (clockTime.hours - 12) : clockTime.hours,})const appendAMPM = clockTime => ({ ...clockTime, ampm: (clockTime.hours > 12) ? 'pm' : 'am',})const display = target => time => target(time);const formatClock = format => time => format.replace('hh', time.hours).replace('mm', time.minutes).replace('ss', time.seconds).replace('tt', time.ampm);const prependZero = key => clockTime => ({ ...clockTime, [key]: (clockTime[key] < 10) ? `0${clockTime[key]}` : clockTime[key],})const convertToCivilianTime = clockTime => compose(appendAMPM,civilianHours)(clockTime);const doubleDigits = civilianTime => compose( prependZero('hours'), prependZero('minutes'), prependZero('seconds'))(civilianTime);const startTricking = () => setInterval(compose( clear, getCurrentTime, serializeColckTime, convertToCivilianTime, doubleDigits, formatClock('hh:mm:ss tt'), display(log),), oneSecond());startTricking(); 落地优点1.单元测试2.调试查错3.并发执行4.利于维护(受主观因素影响)5.复用性好 没有银弹混合使用(函数,对象) 参考 浅析函数式编程与命令式编程 wiki命令式编程 wiki函数式编程 傻瓜函数式编程 react学习手册]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>js 编程思想</tag>
</tags>
</entry>
<entry>
<title><![CDATA[如何写一个js模块打包器]]></title>
<url>%2F2018%2F08%2F03%2FmoduleBundler%2F</url>
<content type="text"><![CDATA[如何写一个js模块打包器前言在看阮一峰老师的每周分享后,看到了一篇关于如何写一个模块打包器的一篇英文文章,之前基本没有了解过,只知道如何使用webpack等,所以这一篇对我来讲很及时,好记性不如烂笔头,所以先尝试着把它翻译出来。 人生已如此艰难,有些事情就不要拆穿(其实使用google翻译就好了) 这里先强烈安利一波:阮一峰老师的每周分享系列,可以了解很多新的东西,个人觉得非常nice,第一手的技术资讯网站:hacker news 原文原文请看我 翻译错误处请大家指正 译文让我们写个模块打包器大家好!。。。(客套话)欢迎来到我的酒馆,今晚累的够呛,但只要有客人来玩我都欢迎(炉石手动滑稽,原文无此段)。今天我们将构建一个非常简单的js模块打包器。 在我们开始之前,我想确认下你们看了下面这些文章没有,本文依赖于此。 Unbundling the JavaScript module bundler Luciano - Mammino Minipack - Ronen Amiel 好了,让我们开始了解模块打包器到底是什么? 什么是模块打包器你可能用过像Browserify,Webpack,Rollup等工具,但一个模块打包器是一个获取js及其依赖项并将他们转换为单独的文件,通常使用在浏览器端。 它通常开始于入口文件,并从入口文件的依赖项中获取所有的代码 下面是打包器主要的两个阶段 依赖解析 打包 从入口点(上图中app.js)开始,依赖解析的目标是寻找你的代码中的所有依赖,也就是代码运行需要的其他代码片段,并构建出上图(依赖图) 一旦完成后,你就可以开始打包,或者将你的依赖图中的代码合并至一个你可以使用的文件中。 让我们开始导入一些我们的代码(我待会会给出原因)1234const detective = require('detective')const resolve = require('resolve').syncconst fs = require('fs')const path = require('path') 依赖解析我们要做的第一件事是思考在依赖解析阶段我们用什么来代表一个模块。 模块表示我们需要下面四个东西 文件名字和文件标识 在文件系统中文件的位置 文件中的代码 该文件需要哪些依赖 依赖图的结构构建需要递归文件的依赖 在js中,最简单表示这一组数据的方式是一个对象,那么我们先这样做12345678let ID = 0function createModuleObject(filepath) { const source = fs.readFileSync(filepath, 'utf-8') const requires = detective(source) const id = ID++ return { id, filepath, source, requires }} 看看createModuleObject方法,需要注意的是调用了一个detective的方法。detective是个一个库用于查找所有对require的调用,无论嵌套有多深,使用它意味着我们可以避免自己进行AST遍历得出文件的所有的依赖。 有一点需要注意(几乎在所有的模块打包器中都是一样的),如果你想做一些奇怪的事情 12const libName = 'lodash'const lib = require(libName) 依赖解析时将无法找到这个模块(因为这需要执行代码) 那么在给出一个模块后运行这个方法会等到什么呢? 下一步是什么,依赖解析!! 好吧,还没到,我首先想要讲一个东西-模块图(module map) 模块图当你在node引入模块时,你可以使用相对路径,比如require('./utils')。当你的代码执行到这时,打包器怎么知道正确的./utils文件在哪。 这是一个模块图解决的问题 我们的模块对象有一个id来标识来源,所以当我们开始依赖解析时,对于每一个模块,我们都将保留一份清单,列出所需的名字和id,所以在运行时我们可以等到正确的模块。 那意味着我们可以将所有模块存储在用id作为键的非嵌套对象中! 依赖解析12345678910111213141516171819202122function getModules(entry) { const rootModule = createModuleObject(entry) const modules = [rootModule] // Iterate over the modules, even when new // ones are being added for (const module of modules) { module.map = {} // Where we will keep the module maps module.requires.forEach(dependency => { const basedir = path.dirname(module.filepath) const dependencyPath = resolve(dependency, { basedir }) const dependencyObject = createModuleObject(dependencyPath) module.map[dependency] = dependencyObject.id modules.push(dependencyObject) }) } return modules} 好的,getModules方法里面会有相当多的模块,这个方法主要用于从入口模块开始,以递归的方式查找和解析依赖项。 解析依赖是什么意思? 在node里有个东西叫require.resolve,这就是node怎么样找到你需要文件的位置的原因。这使得我们可以导入相对或者从node_modules中导入模块。 幸运的是,有一个叫resolve的npm模块可以为我们实现这样的算法,我们只需要把引入的文件和位置作为参数传递,它就可以帮我们完成其他复杂的工作。 所以我们开始解析项目中每一个模块的每一个依赖项 我们也可以构建我之前提到的模块图 在这个方法的最后,我们返回了一个叫modules的数组,里面存储了我们项目中每个模块/依赖项的模块对象。 打包在浏览器中没有modules,这意味着没有require函数和module.exports,所以即使我们拿到了我们所需要的所有依赖项,也没把他们作为模块来使用。 模块工厂函数工厂函数 工厂函数是一个返回对象的函数(不是构造函数),它是面向对象编程的模式,其用途之一是进行封装和依赖注入。 听上去不错? 使用工厂函数,我们要注入可以在打包后的代码中使用的require函数和module.exports对象,并且给出这个模块的作用域。 1234// A factory function(require, module) => { /* Module Source */} 打包我现在跟你展示打包方法,之后我会解释其余的。 123456789101112131415161718192021222324function pack(modules) { const modulesSource = modules.map(module => `${module.id}: { factory: (module, require) => { ${module.source} }, map: ${JSON.stringify(module.map)} }` ).join() return `(modules => { const require = id => { const { factory, map } = modules[id] const localRequire = name => require(map[name]) const module = { exports: {} } factory(module, localRequire) return module.exports } require(0) })({ ${modulesSource} })`} 大多数都只是js模板语言,所以让我们来讨论它在做什么 首先是modulesSource,这里,我们将遍历每个模块,并将其转换为一串源代码。 那么一个模块对象最后会变成什么 现在它有点难以阅读,但是你可以看到目标被封装了,我们为之前提到的factory函数提供了modules和require。 同时还包括了在依赖解析阶段我们构造的模块映射图 在下一步,我们把这些所有的依赖对象数组构建成了一个大的对象 下一串代码是IIFE(立即执行函数表达式),这意味你在浏览器或者别的地方运行代码时,这个函数将会被立即执行,IIFE是封装作用域的另外的一种模式,所以在这里我们担心require和moduels会污染全局作用域。 你也可以看到我们定义了两个require函数,require和localRequire。 require把模块对象的id作为参数,但源代码是没有id的,我们使用其他函数localRequire通过传入任何参数并转成正确的id来获取模块,正是通过模块图来实现的。 在这之后,我们定义了一个可以填充的模块对象,把对象和localRequire作为参数传入factory,然后返回module.exports。 最后,我们执行require(0)去引入id为0的模块作为我们的入口模块。 搞定,我们的模块打包器就已经完成了。 1module.exports = entry => pack(getModules(entry)) 最后所以我们现在已经拥有了一个模块打包器。 现在这个可能不能用于生产,因为它缺少了大量的功能(管理循环依赖,确保每个文件只被解析一次,es-modules等等),但希望能使你对模块打包器的实际工作方式有所了解。 实际上,你删除所有模块中的源代码,实现这个模块打包器才大约60行。 感谢阅读,希望您对我们这个简单的模块打包器如何工作有所了解]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>js</tag>
</tags>
</entry>
<entry>
<title><![CDATA[js模块规范]]></title>
<url>%2F2018%2F08%2F02%2Fjs%E6%A8%A1%E5%9D%97%E8%A7%84%E8%8C%83%2F</url>
<content type="text"><![CDATA[js模块规范简单地介绍下常见的js模块规范,之前跟同事讨论过相关问题,最后总结下来一篇, 常见的模块规范 CommonJS AMD CMD UMD ES6 CommonJSCommonJS有Mozilla工程师Kevin Dangoor于2009年开始的一个项目,最初叫ServerJS,从最开始的命名我们就可以知道,这个项目的目标是在服务端为js指定模块规范,2009年诞生的nodejs也是参照CommonJS规范实现的。 CommonJS定义的模块分为: 模块引用(require) 模块定义(exports) 模块标识(module) 特点 所有代码都运行在模块作用域,不会污染全局作用域。 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。 模块加载的顺序,按照其在代码中出现的顺序。 demo123456789101112//module1.jsfunction test(){ ...}module.exports = { test,}//example.jsconst {test} = require('module1');test(); AMDAsynchronous Module Definition (AMD) 诞生的背景是由于commonjs的同步性不适用于浏览器环境,而AMD采用异步方式加载模块,模块的加载不影响后面语句的运行,所有依赖该模块的语句,都定义在一个回调函数中,等到加载完成,该回调函数才执行。 AMD api)1define(id?, dependencies?, factory); requirejs和curl.js实现了amd规范 CMDCommon Module Definition 由国内玉伯大神在开发seajs中提出。和amd相近。 cmd和amd的区别 cmd是延迟执行,amd是提前执行( RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)) cmd推崇依赖就近(用到的某个模块时再去声明依赖) amd推崇依赖前置(定义模块的时候就声明依赖的模块) 12345678910111213141516171819// CMDdefine(function(require, exports, module) { var a = require('./a') a.doSomething() // 此处略去 100 行 var b = require('./b') // 依赖可以就近书写 b.doSomething() // ...})// AMD 默认推荐的是define(['./a', './b'], function(a, b) { // 依赖必须一开始就写好 a.doSomething() // 此处略去 100 行 b.doSomething() //...})//虽然 AMD 也支持 CMD 的写法,同时还支持将 require 作为依赖项传递,但 RequireJS 的作者默认是最喜欢上面的写法,也是官方文档里默认的模块定义写法。 cmd推崇单一职责模式 amd的api默认是一个多用 UMDUniversal Module Definition (UMD) 是 amd和commonjs兼容,并支持旧式‘全局’变量定义 ES6es6(es2015)的模块规范: 一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。export 命令用于规定模块的对外接口。import 命令用于输入其他模块提供的功能。 ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。 demo1234567891011//a.jsfunction test(){ ...}export { test}//b.jsimport {test} from 'a';test(); 参考资料 What Is AMD, CommonJS, and UMD? Web前端工程化 Javascript模块化编程 知乎-玉伯-amd与cmd区别]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>js</tag>
</tags>
</entry>
<entry>
<title><![CDATA[升级webpack4]]></title>
<url>%2F2018%2F07%2F19%2F%E5%8D%87%E7%BA%A7webpack4%2F</url>
<content type="text"><![CDATA[升级webpack4最近在折腾公司项目webpack4升级问题,在这里简单介绍下webpack4之后的不同和那些需要注意修改的地方。 先给出webpack官方更新说明链接:https://github.com/webpack/webpack/releases/tag/v4.0.0 node版本依赖调整依赖的node环境版本>=6.11.5,已不再支持node 4,建议最佳es6体验node版本更新至8.9.4 12345//wepback - package.json "engines": { "node": ">=6.11.5" }, modewebpack新增了mode选项,可配置也可以作为cli参数传递。mode的值如下 选项 描述 development 启用NamedChunksPlugin和NamedModulesPlugin。 production 启用FlagDependencyUsagePlugin,FlagIncludedChunksPlugin,ModuleConcatenationPlugin,NoEmitOnErrorsPlugin,OccurrenceOrderPlugin,SideEffectsFlagPlugin和UglifyJsPlugin。 none 退出任何默认优化选项 mode默认值为production webpack4之前需要配置很多东西,但是4之后新增了默认项,实际上我们可以不用太关心优化的相关配置,因为–mode production配置会开启所有的优化插件,我们应该更关心配置业务相关的entry,output,module。 plugins变化12345You no longer need to use these plugins:NoEmitOnErrorsPlugin -> optimization.noEmitOnErrors (on by default in production mode)ModuleConcatenationPlugin -> optimization.concatenateModules (on by default in production mode)NamedModulesPlugin -> optimization.namedModules (on by default in develoment mode)CommonsChunkPlugin was removed -> optimization.splitChunks, optimization.runtimeChunk 1.bye commonchunkcommonChunkPlugin已经被移除,之前需要通过配置两次来分别获取vendor和manifest的方式做了整合,直接在optimization中配置splitChunks和runtimeChunk即可 before 12345678new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: 3}),new webpack.optimize.CommonsChunkPlugin({ name: 'manifest', minChunks: Infinity}), after 12345678910optimization:{ runtimeChunk: { name: 'manifest' }, splitChunks:{ name: 'vendor', chunk: 'all', minChunks: 3 } }, 2.UglifyjsWebpackPlugin1234567891011// mode-production默认开启了,会使用uglifyjs插件最小化捆绑optimization:{ minimize:true}//如果你确实有特殊需要,同样可以自定义配置optimization:{ minimizer: [ new UglifyJsPlugin({ /* your config */ }) ]} 3.bye extract-text-webpack-plugin extract-text-webpack-plugin 在webpack4中被替换成 mini-css-extract-plugin before 1234567891011121314new ExtractTextPlugin({ filename: runtime === 'local' ? '[name].css' : '[name].[contenthash:7].css' }) loaders: { css: ExtractTextPlugin.extract({ use: 'css-loader', fallback: 'vue-style-loader' }), less: ExtractTextPlugin.extract({ use: 'css-loader!less-loader', fallback: 'vue-style-loader' }) }, after 1234567891011121314151617181920new MiniCssExtractPlugin({ filename: runtime === 'local' ? '[name].css' : '[name].[contenthash:7].css' }) rules: [ { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, "css-loader" ] }, { test: /\.less/, use: [ MiniCssExtractPlugin.loader, "css-loader", "less-loader" ] } ], vue-loadervue-loader在15之后需要在webpack配置引入当做插件,参考文档,就像这样 12345const VueLoaderPlugin = require('vue-loader/lib/plugin') plugins: [ new VueLoaderPlugin() ], 同时可以去掉vue-loader中的options,Vue Loader 允许你使用其它 webpack loader 处理 Vue 组件的某一部分。它会根据 lang 特性以及你 webpack 配置中的规则自动推断出要使用的 loader。 123456789101112131415161718192021222324252627282930313233343536373839{ test: /\.vue$/, loader: 'vue-loader', <!--options:{--> <!-- rules:[--> <!-- { --> <!-- test: /\.css$/,--> <!-- use: [--> <!-- MiniCssExtractPlugin.loader,--> <!-- "css-loader"--> <!-- ]--> <!-- }--> <!-- ....--> <!-- ]--> <!--}--> }, { test: /\.js$/, loader: 'babel-loader', exclude: ['node_modules'] }, { test: /\.css$/, use: [ process.env.NODE_ENV !== 'production' ? 'vue-style-loader' : MiniCssExtractPlugin.loader, "css-loader" ] }, { test: /\.less$/, use: [ process.env.NODE_ENV !== 'production' ? 'vue-style-loader' : MiniCssExtractPlugin.loader, "css-loader" ] }, 其他package.json配置123"dev": "webpack-dev-server --mode development --progress --hot --hotOnly --config build/webpack.config.js --hide-modules","build": "webpack --mode production --progress --config build/webpack.config.js --hide-modules", 其他包的升级 vue-loader vue-style-loader webpack-cli webpack-dev-server file-loader css-loader 成果 webpack 2.7.0 4.16.1 提升 大小 (474.27k)parsed (291.15k)parsed (100.65k)gzipped 38.61% 编译时间 8728ms 5678ms 34.94%]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>webpack</tag>
</tags>
</entry>
<entry>
<title><![CDATA[debounce and throttle]]></title>
<url>%2F2018%2F06%2F05%2FdebounceAndThrottle%2F</url>
<content type="text"><![CDATA[debounce函数防抖: 1当调用动作经过执行时间间隔后,才会执行该动作,若在这时间间隔内又调用此动作则将重新计算执行时间间隔 下述为一个防抖的例子 12345678910111213141516function debounce(fn, time) { let timer; return function(...args) { clearTimeout(timer); let context = this; timer = setTimeout(function() { fn.apply(context, args); }, time) }}function fn(event) { console.log(event);}window.onscroll = debounce(fn, 300); 思路:在闭包中使用变量存储定时器的id,如果在预设时间间隔内再次触发,则清除定时器后,重建一个新的定时器,这里就相当于重置执行时间间隔。 比喻:有人进入电梯(触发函数),电梯等10s后才启动(执行时间间隔),当第二个在10s内进入了电梯,那么需要再等10s后电梯才启动(重置执行时间间隔) throttle函数节流: 1当调用动作经过执行时间间隔后,执行该动作,若在这时间间隔内又调用此动作不重新计算执行时间间隔 下述为节流的一个例子 123456789101112131415161718function throttle(fn, time){ let isExecute = true; return function(...args){ if(!isExecute) return; let context = this; isExecute = false; setTimeout(()=>{ fn.apply(context, args); isExcute = true; },time) }}function fn(event) { console.log(event);}window.onresize = throttle(fn, 300); 思路:在闭包中使用变量存储是否执行函数的标识,初始化函数时标识为true,当触发函数时,标识为false时return,为true时将标识赋值为false,防止后面触发函数时进入执行逻辑,直到第一次触发经过了执行时间间隔后再讲标识赋值为true,相当于进入下一周期。 比喻:有人进入电梯(触发函数),电梯等10s后才启动(执行时间间隔),无论这10s内有多少人进入,时间到了电梯准时启动。 common限制函数调用频率 常用于 onscroll(全屏滚动等) onresize(自适应窗口大小) mouseover (拖拽) keyup (文字自动补全)等 difference在规定的时间间隔内,debounce:每触发一次函数重新计算执行时间间隔,throttle:不会重新计算执行时间间隔。]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>js</tag>
</tags>
</entry>
<entry>
<title><![CDATA[popular software license]]></title>
<url>%2F2018%2F03%2F29%2Flicense%2F</url>
<content type="text"><![CDATA[起因如何看待百度要求内部全面停止使用 React / React Native? 于是对软件协议展开了解,这里主要是对流行的开源协议作出描述 干货何谓开源:(Open Source)用于描述那些源代码或源设计可以被公众使用的软件或设计体,并且此软件或设计体的使用、修改和发行也不受许可证的限制。 关于为什么的回答:为什么要开源 简单描述: 乌克兰程序员Paul Bagwell 画的分析图 (图片引用自原文)侵删 国内大神 阮一峰制作的中文版 (图片引用自原文)侵删]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>license</tag>
</tags>
</entry>
<entry>
<title><![CDATA[pm2]]></title>
<url>%2F2018%2F03%2F01%2Fpm2%2F</url>
<content type="text"><![CDATA[pm2git地址:https://github.com/Unitech/PM2/ PM2 is a General Purpose Process Manager and a Production Runtime for Node.js apps with a built-in Load Balancer. pm2是一个管理node进程的工具,安装 1npm i -g pm2 常用指令12345678910111213141516171819202122$ pm2 start app.js # 启动app.js应用程序$ pm2 start app.js -i 4 # cluster mode 模式启动4个app.js的应用实例 4个应用程序会自动进行负载均衡$ pm2 start app.js --name="api" # 启动应用程序并命名为 "api"$ pm2 start app.js --watch # 当文件变化时自动重启应用$ pm2 list # 列表 PM2 启动的所有的应用程序$ pm2 monit # 显示每个应用程序的CPU和内存占用情况$ pm2 show [app-name] # 显示应用程序的所有信息$ pm2 logs # 显示所有应用程序的日志$ pm2 logs [app-name] # 显示指定应用程序的日志$ pm2 stop all # 停止所有的应用程序$ pm2 stop 0 # 停止 id为 0的指定应用程序$ pm2 restart all # 重启所有应用$ pm2 reload all # 重启 cluster mode下的所有应用$ pm2 gracefulReload all # Graceful reload all apps in cluster mode$ pm2 delete all # 关闭并删除所有应用$ pm2 delete 0 # 删除指定应用 id 0$ pm2 startup # 创建开机自启动命令$ pm2 save # 保存当前应用列表]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>node</tag>
</tags>
</entry>
<entry>
<title><![CDATA[cookies]]></title>
<url>%2F2018%2F03%2F01%2Fcookies%2F</url>
<content type="text"><![CDATA[前言之前在项目中使用跨域传cookies时,对cookies进行了一些了解,所以整理出来分享给大家 MDNMDN对http cookies的解释为 An HTTP cookie (web cookie, browser cookie) is a small piece of data that a server sends to the user’s web browser. Cookies are mainly used for three purposes: Session managementLogins, shopping carts, game scores, or anything else the server should remember PersonalizationUser preferences, themes, and other settings TrackingRecording and analyzing user behavior request 和 response总所周知,request和repsonse上都可以携带cookies,当我们对response.cookies添加和删除时,request.cookies也会跟着变化,那么他们是引用的同一个实例么? Request.Cookies,Response.Cookies是两个不同的实例,两个集合实例的添加、删除操作就不同了,对Response.Cookies添加、删除都会及时的反映到Request.Cookies,对Request.Cookies的添加、删除操作,Response.Cookies没有反应 具体分析过程点我 侵删 cookies在这里主要谈到nodejs如何操作cookies 1234567request.setHeader('Set-Cookie', ['type=ninja', 'language=javascript']);request.headers.cookie //字符串 ;分隔response.setHeader('Set-Cookie', ['type=ninja', 'language=javascript']);response.headers.cookie //字符串 ;分隔 cookie属性 HttpOnly 属性: 这是微软对Cookie做的扩展。如果在Cookie中设置了”HttpOnly”属性,那么通过程序(JS脚本、Applet等)将无法读取到Cookie信息,这样能有效的防止XSS攻击。 Secure属性: 当设置为true时,表示创建的 Cookie 会被以安全的形式向服务器传输,也就是只能在 HTTPS 连接中被浏览器传递到服务器端进行会话验证,如果是 HTTP 连接则不会传递该信息,所以不会被窃取到Cookie 的具体内容。同上,在客户端我们也无法在document.Cookie找到被设置了Secure=true的Cookie键值对。Secure属性是防止信息在传递的过程中被监听捕获后信息泄漏,HttpOnly属性的目的是防止程序获取Cookie后进行攻击。我们可以把Secure=true看成比HttpOnly更严格的访问控制。 path属性: 指定可访问Cookie的目录。例如:”userId=320; path=/shop”;就表示当前Cookie仅能在shop目录下使用。 domain属性: 指定可访问Cookie的主机名.主机名是指同一个域下的不同主机,例如:www.google.com和gmail.google.com就是两个不同的主机名。默认情况下,一个主机中创建的Cookie在另一个主机下是不能被访问的, 但可以通过domain参数来实现对其的控制,其语法格式为:”name=value; domain=CookieDomain”;以google为例,要实现跨主机访问,可以写为: “name=value;domain=.google.com”;这样,所有google.com下的主机都可以访问该Cookie。 Expires属性:指定过期时间,格式为”name=value;; expires=GMT_String”; 其中GMT_String是以GMT格式表示的时间字符串,超过这个时间,Cookie将消失,不可访问。 Max-Age属性:指定过期时间,用来替代expires, 可以为正数,表示此Cookie从创建到过期所能存在的时间,以秒为单位,此Cookie会存储到客户端电脑,以Cookie文件形式保存,不论关闭浏览器或关闭电脑,直到时间到才会过期。 可以为负数,表示此Cookie只是存储在浏览器内存里,只要关闭浏览器,此Cookie就会消失。maxAge默认值为-1。 还可以为0,表示从客户端电脑或浏览器内存中删除此Cookie。 Cookie的限制 一、浏览器允许每个域名所包含的 Cookie 数: Microsoft 指出 Internet Explorer 8 增加 Cookie 限制为每个域名 50 个,但 IE7 似乎也允许每个域名 50 个 Cookie(《Update to Internet Explorer’s Cookie Jar》)。 Firefox 每个域名 Cookie 限制为 50 个。 Opera 每个域名 Cookie 限制为 30 个。 Safari/WebKit 貌似没有 Cookie 限制。但是如果 Cookie 很多,则会使 header 大小超过服务器的处理的限制,会导致错误发生。 二、不同浏览器间 Cookie 总大小也不同: Firefox 和 Safari 允许 Cookie 多达 4097 个字节, 包括名(name)、值(value)和等号。Opera 允许 Cookie 多达 4096 个字节, 包括:名(name)、值(value)和等号。Internet Explorer 允许 Cookie 多达 4095 个字节, 包括:名(name)、值(value)和等号。注:多字节字符计算为两个字节。在所有浏览器中,任何 Cookie 大小超过限制都被忽略,且永远不会被设置。]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>http cookie</tag>
</tags>
</entry>
<entry>
<title><![CDATA[hexo增加文章排序]]></title>
<url>%2F2018%2F02%2F02%2Fhexo%E5%A2%9E%E5%8A%A0%E6%96%87%E7%AB%A0%E6%8E%92%E5%BA%8F%2F</url>
<content type="text"><![CDATA[在写博客时苦于没有排序,置顶?别慌,老司机来了。 正文其实很简单 1.找到hexo-generator-index/lib/generator.js 2.对posts.data(储存着所有文章数据)进行自定义排序 代码如下 12345678910111213posts.data = posts.data.sort(function (a, b) { if (a.top && b.top) { if (a.top == b.top) return b.updated - a.updated; // 若top值一样则按照文章更新日期降序排 else return b.top - a.top; // 否则按照top值降序排 } else if (a.top && !b.top) { // 以下是只有一篇文章top有定义,那么将有top的排在前面 return -1; } else if (!a.top && b.top) { return 1; } else return b.updated - a.updated; // 都没定义按照文章更新日期降序排 }); 我这里采用了更新时间来作为默认排序,如果大家对posts感兴趣,可以console.log出来 ==弊端==:由于是修改的依赖包的源码,在代码迁移时要注意 其实我也是搜索出来的解决方案,分享一手。 原文链接 侵删]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>hexo</tag>
</tags>
</entry>
<entry>
<title><![CDATA[天干地支]]></title>
<url>%2F2018%2F01%2F29%2F%E5%A4%A9%E5%B9%B2%E5%9C%B0%E6%94%AF%2F</url>
<content type="text"><![CDATA[偶然看见朋友圈在谈论丁酉年,于是便想写一点。 天干地支天干地支,简称为干支,源自中国远古时代对天象的观测。”甲、乙、丙、丁、戊、己、庚、辛、壬、癸”称为十天干,”子、丑、寅、卯、辰、巳、午、未、申、酉、戌、亥”称为十二地支。天干地支组成形成了古代纪年历法。十干和十二支依次相配,组成六十个基本单位,两者按固定的顺序相互配合,组成了干支纪元法。从殷墟出土的甲骨文来看,天干地支在中国古代主要用于纪日,此外还曾用来纪月、纪年、纪时等。(出自百度百科) 十天干看完上述大家心里有点底了,是不是发现有些字的音没有把握。 甲(jiǎ)、乙(yǐ)、丙(bǐng)、丁(dīng)、戊(wù)、己(jǐ)、庚(gēng)、辛(xīn)、壬(rén)、癸(guǐ);其中甲、丙、戊、庚、壬为阳干,乙、丁、己、辛、癸为阴干。 十二地支子(zǐ)、丑(chǒu)、寅(yín)、卯(mǎo)、辰(chén)、巳(sì)、午(wǔ)、未(wèi)、申(shēn)、酉(yǒu)、戌(xū)、亥(hài)。其中子、寅、辰、午、申、戌为阳支,丑、卯、巳、未、酉、亥为阴支。 此外十二地支还对应着十二生肖 干支表]]></content>
<categories>
<category>杂谈</category>
</categories>
<tags>
<tag>传统</tag>
</tags>
</entry>
<entry>
<title><![CDATA[csrf和xss]]></title>
<url>%2F2018%2F01%2F04%2Fcsrf%E5%92%8Cxss%2F</url>
<content type="text"><![CDATA[维基百科解释XSS:跨站脚本(Cross-site scripting,通常简称为XSS)是一种网站应用程序的安全漏洞攻击,是代码注入的一种。它允许恶意用户将代码注入到网页上,其他用户在观看网页时就会受到影响。这类攻击通常包含了HTML以及用户端脚本语言。 CSRF:跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。 解释XSS:是实现csrf的诸多途径中的一种(其中还有命令行模式,直接伪造请求等),xss偏向于代码实现,实际上就是html注入 CSRF:偏向于结果,冒充用户身份发起请求,要完成一次csrf攻击需要用户登录受信任的网站A,在不登出A的情况下访问危险网站B 具体场景XSS:在某个论坛网站,提交了含有js代码的评论,假设该网站未过滤直接存入数据库,当其他用户访问含有该评论的页面时,页面会直接执行该评论中的代码,如 123while(true){ alert('hello world');} 这样就会导致用户无法进入正常的操作,假如代码中含有危险逻辑,可想而知。目前有一种electron的技术,其核心是使用chrome的内核构建出自定义浏览器,像这种就可以在访问某个网站时渲染html注入js脚本执行。 CSRF:假如你登录了某个银行网站,而当你访问某个危险网站时,里面嵌入了一个form表单提交,恰好是转账操作,此时你的账户就少了钱。 防范XSS:对用户输入的内容进入过滤 CSRF:1.给请求加入随机token(1.服务器给客户端发送一个token 2.客户端发送请求时带上token 3.token不合法,拒绝该请求) 2.使用cors跨域请求时 限制请求域名]]></content>
<categories>
<category>技术</category>
</categories>
</entry>
<entry>
<title><![CDATA[vue-devtools]]></title>
<url>%2F2017%2F12%2F07%2Fvue-devtools%2F</url>
<content type="text"><![CDATA[vue-devtools地址: https://github.com/vuejs/vue-devtools clone完成后 npm i -> npm run build 修改项目中的mainifest.json里presistant为true 打开chrome -> 更多工具 -> 扩展程序 -> (勾选开发者模式)加载已解压的扩展程序 -> 选择目标文件夹(vue-devtools\shells\chrome)]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>vue开发工具</tag>
</tags>
</entry>
<entry>
<title><![CDATA[windows powerShell 快速使用]]></title>
<url>%2F2017%2F12%2F06%2Fpowershell%2F</url>
<content type="text"><![CDATA[PowerShell下的Alias(别名)get-alias //浏览alias列表 set-alias name realName //设置alias name为别名 realName为真实名字(若要指向特定exe,需加文件位置如:C:\index.exe) 这种使用存在一个问题,你设置的alias只会保存在当前处于会话期的powershell,意味着下次你重新打开powershell使用时,之前的alias已经失效了,这时有两种解决办法 导入及导出alias 使用powershell配置文件来自定义alias Method oneexport-alias -path anyname.txt // 导出 import-alias -path anyname.txt // 导入 这种方法在稍显愚钝,每次打开都需导入,那么接着介绍常用的第二种方法 Method twoPowerShell在第一次安装时默认的执行策略是”Restricted”,也就是“受限制的”,这意味着PowerShell将不能运行任何脚本和配置文件。PowerShell的执行策略分级: Restricted - 不能运行任何脚本和配置文件 AllSigned - 所有脚本和配置文件必须拥有受信任的发布者的签名 RemoteSigned - 所有脚本和配置文件从可以是互联网上下载,但必须拥有受信任的发布者的签名 Unrestricted - 所有脚本和配置文件都将运行,从互联网上下载的脚本在运行前会有提示。1234get-executionPolicy // 查看当前所处策略级别set-executionPolicy Unrestricted // 修改策略级别如果目前已经是此策略则无需变更,**修改时请使用管理员模式运行PowerShell**$profile //配置文件位置存储在$profile变量中 1.验证 $profile是否存在1test-path $profile //若存在可直接进行3,不存在继续2 2.创建新的配置文件1new-item -path $profile -itemtype file -force //创建文件,重复1 3.开始配置打开文件所在位置开始编辑或 输入12notepad $profilenew-item alias:name -value realName // name为别名 realName为真实名字 4.重启powershell即可开始使用]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>powerShell</tag>
</tags>
</entry>
<entry>
<title><![CDATA[hexo常用命令]]></title>
<url>%2F2017%2F11%2F15%2Fhexo%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4%2F</url>
<content type="text"><![CDATA[常用命令hexo new ‘name’ 新增文章hexo deploy 部署至git(前提配置git)hexo g -d 生成完毕后自动部署网站hexo g –watch 监视文件变动并立即重新生成静态文件hexo s 启动网站]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>hexo</tag>
</tags>
</entry>
<entry>
<title><![CDATA[tinymce的使用]]></title>
<url>%2F2017%2F11%2F15%2Ftinymce%E7%9A%84%E4%BD%BF%E7%94%A8%2F</url>
<content type="text"><![CDATA[配置12345678selector: 'textarea#editor', // 选择器statusbar: false, //禁止编辑器拉伸大小object_resizing: false, //禁止图片缩放height: 500, //设定高度plugins: ["advlist autolink lists link imageuploadtoqiniu preview", "insertdatetime contextmenu code"], //插件toolbar: "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist| link image", //工具栏language: 'zh_CN', //语言设置 注:通过npm安装后需到官网下载对应语言包,并引入即可language_url: "" // 或者通过引入对应语言js位置 获取及设置内容1.只有一个tinymce实例get:tinyMCE.activeEditor.getContent()set:tinyMCE.activeEditor.setContent(“content”) 2、多个get:tinyMCE.editors[0].getContent()set:tinyMCE.editors[0].setContent(“content”) 3、获取纯文本let activeEditor = tinymce.activeEditor;let editBody = activeEditor.getBody();activeEditor.selection.select(editBody);let text = activeEditor.selection.getContent( { ‘format’ : ‘text’ } ); 注:对tinymce设置内容时(场景如编辑),使用window.onload]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>tinymce 富文本编辑器</tag>
</tags>
</entry>
<entry>
<title><![CDATA[党的十九大报告之关键句外文翻译]]></title>
<url>%2F2017%2F10%2F19%2F%E5%8D%81%E4%B9%9D%E5%A4%A7%E4%B9%8B%E5%85%B3%E9%94%AE%E5%8F%A5%2F</url>
<content type="text"><![CDATA[本文为转载文章,转载地址:https://m.weibo.cn/1887344341/4164474044677344党的十九大报告,外文翻译 ①新时代中国特色社会主义Socialism with Chinese Characteristics for a New Era. ②不忘初心,方得始终Never forget why you started, and your mission can be accomplished. ③登高望远,居安思危Aim high and look far, be alert to dangers even in times of calm. ④全面从严治党Seeing Party self-governance exercised fully and with rigor. ⑤坚持反腐败无禁区、全覆盖、零容忍No place has been out of bounds, no ground left unturned, and no tolerance shown in the fight against corruption. ⑥坚定不移“打虎”“拍蝇”“猎狐”We have taken firm action to “take out tigers”, “swat flies” and “hunt down foxes”. ⑦不想腐的堤坝正在构筑Moral defenses against corruption are in the making. ⑧行百里者半九十The last leg of a journey marks the halfway point. ⑨大道之行,天下为公We should pursue a just cause for common good. ⑩打铁还需自身硬It takes a good blacksmith to make steel.]]></content>
<categories>
<category>杂谈</category>
</categories>
<tags>
<tag>翻译</tag>
</tags>
</entry>
<entry>
<title><![CDATA[踩electron的坑]]></title>
<url>%2F2017%2F10%2F17%2F%E8%B8%A9electron%E7%9A%84%E5%9D%91%2F</url>
<content type="text"><![CDATA[踩坑系列背景: 在做一个基于electron的一键报税的项目踩过的坑注:我会持续更新 electron不支持xp之前有客户使用打包后的electron程序(一键报税),弹出 不是有效的win32程序 第一感觉是系统环境问题,于是google,将得到的方案去尝试无果,于是去github上的electron项目去查找issue,发现了这样的回答 @likenamehaojie Chromium dropped support for Windows XP a long time ago and therefore Electron cannot continue supporting it.Even Microsoft no longer supports the majority of XP installsfelixrieseberg commented on 24 May 2016Yep, sorry to say that this is a clear “wontfix” from the Electron team. 可自行去electron项目issue搜索xp electron打印及预览 在查看官方文档后,你会发现BrowserWindow123456789win.loadURL(url[, options])url Stringoptions Object (optional)httpReferrer String (optional) - A HTTP Referrer url.userAgent String (optional) - A user agent originating the request.extraHeaders String (optional) - Extra headers separated by “\n”postData (UploadRawData[] | UploadFile[] | UploadFileSystem[] | UploadBlob[]) - (optional)baseURLForDataURL String (optional) - Base url (with trailing path separator) for files to be loaded by the data url. This is needed only if the specified url is a data url and needs to load other files.Same as webContents.loadURL(url[, options]). 通过淘宝镜像安装electron失败例如这样: 通过给出的链接访问并不存在,那么到淘宝镜像官网查询得出原来是地址出错了。下面给出解决方案 windows1set ELECTRON_MIRROR=https://npm.taobao.org/mirrors/electron/ 设置环境变量 mac or linux1export ELECTRON_MIRROR="https://npm.taobao.org/mirrors/electron/" 重新npm i 你会发现无比畅爽]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>electron</tag>
</tags>
</entry>
<entry>
<title><![CDATA[巧技之webstorm]]></title>
<url>%2F2017%2F10%2F10%2F%E5%B7%A7%E6%8A%80%E4%B9%8Bwebstorm%2F</url>
<content type="text"><![CDATA[快捷键12345678910/** + enter :快速生成方法注释ctrl + shift + k : push codectrl + tab : 查看所有打开窗口并切换ctrl + alt + l : 格式化代码ctrl + h : 指定目录下查找代码ctrl + shift + r : 全局按文件名查找文件alt + insert : 新建文件ctrl + l : 跳到指定行ctrl + f : 文件内代码查找及替换shift + enter : 重新开始下一行 破解及汉化1.激活 点击help栏下register,选择“License server”,在输入框输入下列的网址之一均可: http://idea.iteblog.com/key.php http://idea.singee77.com http://im.js.cn:8888然后点击Activate,那么注册就成功了 2.汉化 汉化包路径:http://download.csdn.net/detail/qq_26394087/9825387 1)简单粗暴直接将汉化包中的resources_cn.jar直接拷贝到Webstorm安装目录下的lib目录里。 2)循序渐进1、找到Webstorm的安装路径,将/lib目录下的resources_en.jar文件复制出来,并更名为resources_cn.jar。2、双击打开resources_cn.jar(注意是打开而不是解压出来),将下载的汉化包zh_CN目录下的所有文件拖到刚才打开的resources_cn.jar文件内的messages目录中,并保存。3、将resources_cn.jar文件复制回\lib目录。4、汉化完毕,重新打开Webstorm就可以显示中文。 注:如果打开后出现中文乱码,不要慌。首先将lib目录下resources_cn删除,然后依次点击File -> Settings -> Appearance&Behavior -> Appearance -> 选中Override default fonts by(not recommended)在Name那一栏选择选择任意中文字体:如 Microsoft YaHei UI,然后重新将resources_cn.jar包拷贝到lib目录下,最后重启试试,重启Webstorm!]]></content>
<categories>
<category>工具</category>
</categories>
<tags>
<tag>巧技</tag>
</tags>
</entry>
<entry>
<title><![CDATA[js闭包]]></title>
<url>%2F2017%2F10%2F10%2Fjs%E9%97%AD%E5%8C%85%2F</url>
<content type="text"><![CDATA[js闭包简单理解就是定义在一个人函数内部的函数,在本质上闭包就是连接函数外部与函数内部的桥梁。 闭包的优缺点 优点: 可以让一个变量常驻内存 (如果用的多了就成了缺点 避免全局变量的污染 私有化变量 缺点 因为闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存 引起内存泄露 例:12345678910111213function a() { var i = 0; function b() { alert(++i); } return b;}var c = a();c(); 深入理解闭包 如果要更加深入的了解闭包以及函数a和嵌套函数b的关系,我们需要引入另外几个概念:函数的执行环境(excution context)、活动对象(call object)、作用域(scope)、作用域链(scope chain)。以函数a从定义到执行的过程为例阐述这几个概念。 当定义函数a的时候,js解释器会将函数a的作用域链(scopechain)设置为定义a时a所在的“环境”,如果a是一个全局函数,则scope chain中只有window对象。 当执行函数a的时候,a会进入相应的执行环境(excution context)。 在创建执行环境的过程中,首先会为a添加一个scope属性,即a的作用域,其值就为第1步中的scopechain。即a.scope=a的作用域链。 然后执行环境会创建一个活动对象(callobject)。活动对象也是一个拥有属性的对象,但它不具有原型而且不能通过JavaScript代码直接访问。创建完活动对象后,把活动对象添加到a的作用域链的最顶端。此时a的作用域链包含了两个对象:a的活动对象和window对象。 下一步是在活动对象上添加一个arguments属性,它保存着调用函数a时所传递的参数。 最后把所有函数a的形参和内部的函数b的引用也添加到a的活动对象上。在这一步中,完成了函数b的的定义,因此如同第3步,函数b的作用域链被设置为b所被定义的环境,即a的作用域。 到此,整个函数a从定义到执行的步骤就完成了。此时a返回函数b的引用给c,又函数b的作用域链包含了对函数a的活动对象的引用,也就是说b可以访问到a中定义的所有变量和函数。函数b被c引用,函数b又依赖函数a,因此函数a在返回后不会被GC回收。 当函数b执行的时候亦会像以上步骤一样。因此,执行时b的作用域链包含了3个对象:b的活动对象、a的活动对象和window对象,如下图所示:如图所示,当在函数b中访问一个变量的时候,搜索顺序是:先搜索自身的活动对象,如果存在则返回,如果不存在将继续搜索函数a的活动对象,依次查找,直到找到为止。 如果函数b存在prototype原型对象,则在查找完自身的活动对象后先查找自身的原型对象,再继续查找。这就是Javascript中的变量查找机制。 如果整个作用域链上都无法找到,则返回undefined。 闭包的运用1.匿名自执行函数 我们在实际情况下经常遇到这样一种情况,即有的函数只需要执行一次,其内部变量无需维护,比如UI的初始化,那么我们可以使用闭包: 123456> //将全部li字体变为红色 > (function(){ > var els = document.getElementsByTagName('li');> for(var i = 0,lng = els.length;i < lng;i++){> els[i].style.color = 'red';> } })(); 我们创建了一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量, 因此els,i,lng这些局部变量在执行完后很快就会被释放,节省内存! 关键是这种机制不会污染全局对象。 2.实现封装/模块化代码 1234567891011121314151617var person= function(){ //变量作用域为函数内部,外部无法访问 var name = "default"; return { getName : function(){ return name; }, setName : function(newName){ name = newName; } } }();console.log(person.name);//直接访问,结果为undefined console.log(person.getName()); //default person.setName("jozo"); console.log(person.getName()); //jozo 3.实现面向对象中的对象 这样不同的对象(类的实例)拥有独立的成员及状态,互不干涉。虽然JavaScript中没有类这样的机制,但是通过使用闭包,我们可以模拟出这样的机制。还是以上边的例子来讲: 1234567891011121314151617181920212223function Person(){ var name = "default"; return { getName : function(){ return name; }, setName : function(newName){ name = newName; } } }; var person1= Person(); print(person1.getName()); john.setName("person1"); print(person1.getName()); // person1 var person2= Person(); print(person2.getName()); jack.setName("erson2"); print(erson2.getName()); //person2 Person的两个实例person1 和 person2 互不干扰!因为这两个实例对name这个成员的访问是独立的 。 js的垃圾回收机制 主要有两种策略:标记清除与引用计数标记清除:将所有存储在内存中的变量加上标记,然后去掉环境中变量的标记和被环境中变量引用的变量的标记,如果变量再次被标记,则被回收引用计数:记录每个值被引用的次数,如果声明将一个引用类型的值赋给一个变量时,这个引用类型的值计数加一,若这个变量脱离了对这个引用类型的值的引用时,则减一,当引用计数为0时被回收 在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。因为函数a被b引用,b又被a外的c引用,这就是为什么函数a执行后不会被回收的原因。 参考链接,查看更多 = =]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>js</tag>
</tags>
</entry>
<entry>
<title><![CDATA[常用的git命令]]></title>
<url>%2F2017%2F10%2F10%2F%E5%B8%B8%E7%94%A8%E7%9A%84git%E5%91%BD%E4%BB%A4%2F</url>
<content type="text"><![CDATA[1.前提 Remote 远程仓库 Repository 本地仓库 Index/Stage 暂存区 Workspace 工作区 2.常用的指令[]:参数 git init [project-name] 新建git代码库 git clone [url] 下载一个项目和整个代码历史 git config –list 显示当前git配置 git config –global user.name = “[name]” 设置全局的用户账号 将name换成password即为密码 git add . 将当前目录下添加到暂存区 git commit -m “[message]” 提交暂存区的文件到本地仓库 git commit -v 提交时显示所有的diff信息 git branch 列出所有本地分支 -r 远程分支 -a所有分支 git checkout -b [branch] 新建一个分支并切换到这个分支 git branch -d [branch] 删除本地分支 git push origin –delete [branch] 删除远程仓库分支 git tag 列出所有tag git tag [tag] 新建一个tag在当前commit git push origin [tag] 提交指定的tag git status 显示有变更的文件 git log 查看当前分支的历史版本 git diff 显示暂存区与工作区的差异 git fetch origin 下载远程仓库的所有变动 git remote -v 显示所有远程仓库 git pull origin [branch] 拉取远程仓库的变化与本地当前分支合并 git push origin [branch] 上传本地指定分支到远程仓库 git checkout 恢复暂存区的所有文件到工作区 git stash 暂时将未提交的变化移除 参考地址为http://www.ruanyifeng.com/blog/2015/12/git-cheat-sheet.html]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>git</tag>
</tags>
</entry>
<entry>
<title><![CDATA[常用css]]></title>
<url>%2F2017%2F10%2F09%2F%E5%B8%B8%E7%94%A8css%2F</url>
<content type="text"><![CDATA[css样式123456// 文本单行 超出宽度显示为省略号text-single{ overflow: hidden; text-overflow:ellipsis; white-space: nowrap;}]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>css</tag>
</tags>
</entry>
<entry>
<title><![CDATA[this]]></title>
<url>%2F2017%2F10%2F09%2Fthis%2F</url>
<content type="text"><![CDATA[主要分为以下四种情况1.方法调用模式当一个函数被保存为对象的一个属性时, 我们称它为一个方法, 当一个方法被调用时, this指向该对象, 如: 1234567var obj = { value: 1, getValue: function() { alert(this.value); }};obj.getValue(); // 输出1, 此时的this指向obj 注意: 该模式中, this到对象的绑定发生在方法被调用的时候. 2.函数调用模式当一个函数并非一个对象的属性时, 它被当作一个函数来调用, 此时的this指向全局对象(window), 如: 123window.value = 1;function getValue() { alert(this.value); }getValue(); // 输出1, 此时的this指向window. 3.构造器调用模式结合new前缀调用的函数被称为构造器函数, 此时的this指向该构造器函数的实例对象, 如: 12345678910function show(val) { this.value = val;};show.prototype.getVal = function() { alert(this.value);};var func = new show(1);func.getVal(); // 输出1alert(func.value) // 输出1// 从上面的结果, 可以看出, 此时的this指向了func对象. 4.apply/call调用模式apply和call方法可以让我们设定调用者中的this指向谁, 如: 12345678910var fun = function(str) { this.status = str;}fun.prototype.getStatus = function() { alert(this.status);}var obj = { status: "loading"};fun.prototype.getStatus.apply(obj); // 输出"loading", 此时getStatus方法中的this指向了obj]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>js</tag>
</tags>
</entry>
<entry>
<title><![CDATA[浏览器缓存那些事]]></title>
<url>%2F2017%2F09%2F30%2F%E6%B5%8F%E8%A7%88%E5%99%A8%E7%BC%93%E5%AD%98%E9%82%A3%E4%BA%9B%E4%BA%8B%2F</url>
<content type="text"><![CDATA[缓存的分类:服务端缓存与客户端缓存(浏览器缓存)缓存的好处:1.降低延迟 2.降低网络传输 浏览器缓存机制浏览器缓存控制机制有两种:HTML Meta标签 vs. HTTP头信息 1. HTML meta标签1<META HTTP-EQUIV="Pragma" CONTENT="no-cache"> 浏览器当前页面不被缓存,每次访问都需要去服务器拉取 - 2. http头信息控制缓存 浏览器第一次请求 浏览器第二次请求 Expires策略:Expires是Web服务器响应消息头字段,在响应http请求时告诉浏览器在过期时间前浏览器可以直接从浏览器缓存取数据,而无需再次请求。不过Expires 是HTTP 1.0的东西,现在默认浏览器均默认使用HTTP 1.1,所以它的作用基本忽略。Expires 的一个缺点就是,返回的到期时间是服务器端的时间。 Cache-control策略(重点关注):Cache-Control与Expires的作用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据。只不过Cache-Control的选择更多,设置更细致,如果同时设置的话,其优先级高于Expires。值可以是public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age Last-Modified/If-Modified-Since:Last-Modified/If-Modified-Since要配合Cache-Control使用。Last-Modified:标示这个响应资源的最后修改时间。If-Modified-Since:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Last-Modified声明,则再次向web服务器请求时带上头 If-Modified-Since,表示请求时间。web服务器收到请求后发现有头If-Modified-Since 则与被请求资源的最后修改时间进行比对。若最后修改时间较新,说明资源又被改动过,则响应整片资源内容(写在响应消息包体内),HTTP 200;若最后修改时间较旧,说明资源无新修改,则响应HTTP 304 (无需包体,节省浏览),告知浏览器继续使用所保存的cache。 Etag/If-None-Match:Etag/If-None-Match也要配合Cache-Control使用。(优先级高)Etag:web服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器决定)。Apache中,ETag的值,默认是对文件的索引节(INode),大小(Size)和最后修改时间(MTime)进行Hash后得到的。If-None-Match:当资源过期时(使用Cache-Control标识的max-age),发现资源具有Etage声明,则再次向web服务器请求时带上头If-None-Match (Etag的值)。web服务器收到请求后发现有头If-None-Match 则与被请求资源的相应校验串进行比对,决定返回200或304。 开发环境下经常会因为强缓存导致资源没有及时更新而看不到最新的效果的解决办法 ctrl+f5 浏览器的隐私模式开发 f12在network那里把缓存给禁掉 给资源加上一个动态的参数,如css/index.css?v=0.0001,由于每次资源的修改都要更新引用的位置 部分状态码解释200 代表是的 响应成功的状态码,还有其他的状态码如下:1xx: 信息性状态码 100, 1012xx: 成功状态码 200:OK3xx: 重定向状态码301: 永久重定向, Location响应首部的值仍为当前URL,因此为隐藏重定向;302: 临时重定向,显式重定向, Location响应首部的值为新的URL304:Not Modified 未修改,比如本地缓存的资源文件和服务器上比较时,发现并没有修改,服务器返回一个304状态码, 告诉浏览器,你不用请求该资源,直接使用本地的资源即可。4xx: 客户端错误状态码404: Not Found 请求的URL资源并不存在5xx: 服务器端错误状态码500: Internal Server Error 服务器内部错误502: Bad Gateway 前面代理服务器联系不到后端的服务器时出现504:Gateway Timeout 这个是代理能联系到后端的服务器,但是后端的服务器在规定的时间内没有给代理服务器响应]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>浏览器</tag>
</tags>
</entry>
<entry>
<title><![CDATA[js事件委托]]></title>
<url>%2F2017%2F09%2F30%2Fjs%E4%BA%8B%E4%BB%B6%E5%A7%94%E6%89%98%2F</url>
<content type="text"><![CDATA[浏览器处理DOM事件的过程对于事件的捕获和处理,不同的浏览器厂商有不同的处理机制,这里我们主要介绍W3C对DOM2.0定义的标准事件。 DOM2.0模型将事件处理流程分为三个阶段:1.事件捕获:当某个元素触发某个事件(如onclick),顶层对象document就会发出一个事件流,随着DOM树的节点向目标元素节点流去,直到到达事件真正发生的目标元素。在这个过程中,事件相应的监听函数是不会被触发的。 2.事件目标:当到达目标元素之后,执行目标元素该事件相应的处理函数。如果没有绑定监听函数,那就不执行。 3.事件冒泡:从目标元素开始,往顶层元素传播。途中如果有节点绑定了相应的事件处理函数,这些函数都会被一次触发。如果想阻止事件起泡,可以使用e.stopPropagation()(Firefox)或者e.cancelBubble=true(IE)来组织事件的冒泡传播。如图: js事件委托/代理实际上指的是利用js冒泡机制,当事件抛到目标节点的父节点时,通过检查事件的目标对象(target)来判断并获取事件源。如在ul下所有li添加点击事件代码如下 12345678// 获取父节点,并为它添加一个click事件document.getElementById("ul-parent").addEventListener("click",function(e) { // 检查事件源e.targe是否为Li if(e.target && e.target.nodeName.toUpperCase == "LI") { // 真正的处理过程在这里 console.log("Children li"+ e.target.id + " was clicked!"); }});]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>js</tag>
</tags>
</entry>
<entry>
<title><![CDATA[前端常见面试题]]></title>
<url>%2F2017%2F09%2F30%2F%E9%9D%A2%E8%AF%95%E9%A2%98%2F</url>
<content type="text"><![CDATA[1.js如何判断一个对象为数组? 方法一 instanceof instanceof 用于判断一个变量是否某个对象的实例 a instanceof b?alert(“true”):alert(“false”)//注意b值是你想要判断的那种数据类型,不是一个字符串,比如Array 举个栗子: var a=[]; console.log(a instanceof Array) //返回true 方法二 constructor在W3C定义中的定义:constructor 属性返回对创建此对象的数组函数的引用,就是返回对象相对应的构造函数。 (a.constructor == Array) // a实例所对应的构造函数是否为Array? true or false 较为严谨并且通用的方法: function isArray(object){ return object && typeof object===’object’ && Array == object.constructor; } 方法三 特性判断法 function isArray(object){ return object && typeof object===’object’ && typeof object.length===’number’ && typeof object.splice===’function’ && //判断length属性是否是可枚举的 对于数组 将得到false !(object.propertyIsEnumerable(‘length’)); } 复制代码 有length和splice并不一定是数组,因为可以为对象添加属性,而不能枚举length属性,才是最重要的判断因子。 方法四 最简单的方法 对于这种方法,以下有几个链接可供参考解释: http://blog.csdn.net/zhangw428/article/details/4171630 http://my.oschina.net/sfm/blog/33197 http://openxtiger.iteye.com/blog/1893378 function isArray(o) { return Object.prototype.toString.call(o) === ‘[object Array]‘; } 2.js如何将任意字符串转换成字符数组 字符串转数组1234> var str = "adsfasdfasdf";> console.log(str.split(""));> console.log(str.replace(/(.)(?=[^$])/g,"$1,").split(","));> //正则表达式--匹配任意字符除开$将其替换成$1变量(指定的任意字符本身)加上, 数组转字符串123> var array = str.split("");> console.log(array.join(""));> console.log(array.toString().replace(/,/g,"")); 3.js操作数组的方法 concat() 连接两个或更多的数组,并返回结果。join() 把数组的所有元素放入一个字符串。元素通过指定的分隔符进行分隔。pop() 删除并返回数组的最后一个元素push() 向数组的末尾添加一个或更多元素,并返回新的长度。reverse() 颠倒数组中元素的顺序。shift() 删除并返回数组的第一个元素slice() 从某个已有的数组返回选定的元素sort() 对数组的元素进行排序splice() 删除元素,并向数组添加新元素。toSource() 返回该对象的源代码。toString() 把数组转换为字符串,并返回结果。toLocaleString() 把数组转换为本地数组,并返回结果。unshift() 向数组的开头添加一个或更多元素,并返回新的长度。valueOf() 返回数组对象的原始值 4.css布局右侧固定,左侧自适应 1234567891011121314151617181920212223242526272829303132333435363738<div class="ff"> <div class="box left"></div> <div class="box right"></div></div>//1.固定区域绝对定位,自适应区域设置margin.left{ margin-left: 310px; width:100%;}.right{ position: absolute; left: 0; top: 0; width: 300px;}//2.float加margin.left{ float: left; margin-left: -310px; width: 100%;}.right{ float: right; width: 300px;}//3.标准处理方式,ie7以下不兼容,table加浮动.ff{ display: table; width: 100%;}.left{ display: table-cell; width: 100%;}.right{ width: 300px; float: right;}]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>面试</tag>
</tags>
</entry>
<entry>
<title><![CDATA[begin]]></title>
<url>%2F2017%2F09%2F22%2Fbegin%2F</url>
<content type="text"><![CDATA[begin第一次用hexo搭了一个自己博客以往都是用的诸如csdn等之类的博客网站在搭建的时候遇到中文字符乱码的问题除了在根目录下的_config.yml设置language:zh-CN之外一定要记得把项目所有文件设置utf-8格式]]></content>
<categories>
<category>杂谈</category>
</categories>
<tags>
<tag>闲聊</tag>
</tags>
</entry>
</search>