Toffee's Blog

  • 首页

  • 归档

  • 搜索

gulp配置示例

发表于 2018-11-20

Gulp是什么

Gulp是一个基于流的构建工具,可以自动执行指定的任务,简洁且高效

Gulp能做什么

  • 开发环境下,想要能够按模块组织代码,监听实时变化
  • css/js预编译,postcss等方案,浏览器前缀自动补全等
  • 条件输出不同的网页,比如app页面和mobile页面
  • 线上环境下,我想要合并、压缩 html/css/javascritp/图片,减少网络请求,同时降低网络负担
  • 等等…

gulp配置示例以及注释

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
/******************************************************
* gulp 组件 及 gulp task
******************************************************/

const gulp = require('gulp'); //引入gulp
const source = require('vinyl-source-stream'); //加载vinyl文件流处理
const browserify = require('browserify'); //加载预编译browserSync
const browserSync = require('browser-sync'); //实时更新插件
const buffer = require('vinyl-buffer');
const babel = require('babelify'); //babel语法转化
const reload = browserSync.reload;
const sourcemaps = require('gulp-sourcemaps'); //生成sourcemap文件
const del = require('del'); //引入删除模块
const argv = require('yargs').argv; // 引入参数 根据参数的不同 执行相应的任务 s 生产环境 p 测试环境 d 开发环境
const __ = require('gulp-load-plugins')(); //可以不用引入 直接使用已经在package.json里的插件

//先预定好各个元素的路径
const domSrc = {
//js 路径
jsSrc: 'app/js/**/*.js',
//js 库路径
jsLibsSrc: 'app/js/libs/**/*.js',
//jquery 文件
jquerySrc:'app/js/libs/jquery/*.js',
//config js路径
configSrc: 'app/js/config/*.js',
//index路径
indexHtmlSrc: 'app/index.html',
//页面路径
htmlSrc: 'app/**/*.html',
//css路径
cssSrc: ['app/assets/**/*.css', 'app/assets/scss/app.scss'],
//监听css路径
watchCssSrc: 'app/assets/**/*.{css,scss}',
//监听css路径
//fonts路径 排除css文件
fontSrc: ['app/assets/fonts/*', '!app/assets/fonts/*.css'],
//image路径
imgSrc: 'app/assets/images/**/*',
//res路径
resSrc: 'app/assets/res/**/*',
//dist 下的 JS
distJsSrc: 'dist/js',
//dist 下的 html
distHtmlSrc: 'dist/html',
//dist 下的 tpl
distTplSrc: 'dist/tpl',
//dist 下的css
distCssSrc: 'dist/css',
//dist 下的 fonts
distFontsSrc: 'dist/fonts'
}
//js 库文件 组文件 筛选
const libsfilter = __.filter(domSrc.jsLibsSrc, {restore: true, passthrough: false});
//js 业务 文件 筛选
const servicefilter = __.filter([domSrc.jsSrc, '!' + domSrc.jsLibsSrc], {restore: true, passthrough: false});
//html index.html 筛选
const indexHtmlfilter = __.filter(domSrc.indexHtmlSrc, {restore: true, passthrough: false});

//配置config
gulp.task('js:config', ()=> {
var configSource = 'app/js/config/dev.js';
if (argv.s) {
//测试环境
configSource = 'app/js/config/staging.js';
} else if (argv.p) {
//生产环境
configSource = 'app/js/config/prod.js';
}
return gulp.src(configSource)
.pipe(__.rename('config.js'))
.pipe(gulp.dest('app/js/config'));
});

//ES6 to ES5
gulp.task('js:es6-2-es5', ()=> {
return browserify({entries: 'app/js/myApp.js'})
.transform(babel) //{presets: ["es2015"]}
.bundle()
//.pipe(__.sourcemaps.init())
.pipe(source('myApp.js'))
.pipe(__.if(argv.p, __.streamify(__.uglify())))
.pipe(__.rename({suffix: '.min'}))
//.pipe(__.sourcemaps.write('./'))
.pipe(gulp.dest('dist/js'));
});

//JS 拷贝 libs ,components 文件夹
gulp.task('js:copy-libs', ()=> {
return gulp.src([domSrc.jsLibsSrc,'!'+domSrc.jquerySrc])
//.pipe(__.sourcemaps.init())
.pipe(__.flatten()) //移除目录结构
.pipe(__.concat("applibs.js")) //合并
.pipe(__.uglify()) //压缩
.pipe(__.rename({suffix: '.min'})) //重命名
//.pipe(__.sourcemaps.write('./'))
.pipe(gulp.dest('dist/js/libs'));
});

//解析HTML
gulp.task('html', ()=> {
return gulp.src(domSrc.htmlSrc)
.pipe(gulp.dest('dist/'))
});

//复制 fonts
gulp.task('fonts', ()=> {
return gulp.src(domSrc.fontSrc)
.pipe(gulp.dest('dist/css'));
});

//复制 图片
gulp.task('image', ()=> {
return gulp.src(domSrc.imgSrc)
.pipe(gulp.dest('dist/imgs'));
});

//复制 res
gulp.task('res', ()=> {
return gulp.src(domSrc.resSrc)
.pipe(gulp.dest('dist/res'));
});

//解析并合并压缩css
gulp.task('css', ()=> {
return gulp.src(domSrc.cssSrc)
.pipe(__.if('*.scss', __.sass()))
.pipe(__.concat('app.css'))
.pipe( __.minifyCss())
.pipe(__.rename((path)=> {
path.basename += '.min';
}))
.pipe(gulp.dest('dist/css'));
});

//清除dist下的JS文件
gulp.task('clean:js', ()=> {
return del([domSrc.distJsSrc]);
});

//清除dist 下的html文件
gulp.task('clean:html', ()=> {
return del([domSrc.distHtmlSrc, domSrc.distTplSrc]);
});

//清除dist下的 css 文件
gulp.task('clean:css', ()=> {
return del([domSrc.distCssSrc]);
});

//清除dist 下的 fonts文件
gulp.task('clean:fonts', ()=> {
return del([domSrc.distFontsSrc])
});

//本地服务器
gulp.task('server', ()=> {
browserSync({
// tunnel: true,
open: false,
port: 9996,
server: {
baseDir: ['dist'],
index: ['./html/index.html', 'index.html']
}
});

//js
gulp.watch([domSrc.jsSrc,"!"+domSrc.configSrc], gulp.series('js', reload));
//css
gulp.watch(domSrc.watchCssSrc, gulp.series('css', reload));
//html
gulp.watch(domSrc.htmlSrc, gulp.series('html', reload));
//image
gulp.watch(domSrc.imgSrc, gulp.series('image', reload));
});

gulp.task('clean', gulp.parallel('clean:js', 'clean:html', 'clean:css', 'clean:fonts'));

gulp.task('js', gulp.series('clean:js', 'js:config', 'js:es6-2-es5', 'js:copy-libs'));

gulp.task('copy', gulp.parallel('html', 'fonts', 'image', 'res'));

gulp.task('default', gulp.series('clean', 'copy', 'js', 'css', 'server'));

gulp.task('build', gulp.series('clean', 'copy', 'js', 'css'));

1px border边框实现

发表于 2018-11-16

由于移动端手机像素密度的差异,1px宽度的边框在不同的DPI下展示会有差异,可以通过下面这个方法来解决

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
<!--解决1px边框-->
<!--@ SASS语法-->
@mixin border-1px($color){
position: relative;
&:after{
display: block;
position: absolute;
left: 0;
bottom: 0;
width: 100%;
border-top: 1px solid $color;
content: '';
}
}

<!--然后设置 统一的类 缩放-->
@media (-webkit-min-device-pixel-ratio: 1.5),(min-device-pixel-ratio: 1.5) {
.border-1px{
&:after{
-webkit-transform: scaleY(0.7);
transform: scaleY(0.7);
}
}
}
@media (-webkit-min-device-pixel-ratio: 2),(min-device-pixel-ratio: 2) {
.border-1px{
&:after{
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
}
}
}

阅读全文 »

cron定时器写法

发表于 2018-11-14

corn命令常见于Unix和类Unix的操作系统之中,用于设置周期性被执行的指令,例如Jenkins定时任务等.

1
2
3
4
5
6
7
8
9
*    *    *    *    *    *
┬ ┬ ┬ ┬ ┬ ┬
│ │ │ │ │ |
│ │ │ │ │ └ day of week (0 - 7) (0 or 7 is Sun)
│ │ │ │ └───── month (1 - 12)
│ │ │ └────────── day of month (1 - 31)
│ │ └─────────────── hour (0 - 23)
│ └──────────────────── minute (0 - 59)
└───────────────────────── second (0 - 59, optional)

在一个区域里填写多个数值的方法:

  • 逗号(’,’)分开的值,例如:“1,3,4,7,8”
  • 连词符(’-‘)指定值的范围,例如:“1-6”,意思等同于“1,2,3,4,5,6”
  • 星号(’*’)代表任何可能的值。例如,在“小时域”里的星号等于是“每一个小时”,等等

示例

1
2
//每三小时准点执行一次
cron: '0 0 */3 * * *'

async库parallel/parallelLimit介绍

发表于 2018-11-13

parallel

async.js是一个功能强大的node异步流程控制库,parallel是async中用来执行并行任务流程的一个方法,parallel(tasks,callbackopt)接收两个参数:

Name Type Description
tasks Array/Iterable/Object 一个用于遍历的数据类型
callback Function (可选)返回一个数组或者对象,参数(err,result)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const parallel = require('async/parallel')
const time = new Date().getTime()

parallel([(callback)=>{
setTimeout(()=>{
callback(null,'one')
},1200)
},(callback)=>{
setTimeout(()=>{
callback(null,'two')
},1000)
}],(err,results)=>{
console.log('time cost:',new Date().getTime()-time)
console.log(results)
})

执行结果:

1
2
time cost: 1201
[ 'one', 'two' ]

parallel会等到所有函数返回结果后再返回最终的结果数组,并且按照顺序返回,不会因为第二个函数setTimeout的时间比第一个短就先返回

parallelLimit

parallelLimit基本功能和parallel是一样的,不过它新增一个参数,用来控制同时执行的函数数量

Name Type Description
tasks Array/Iterable/Object 一个用于遍历的数据类型
limit number 异步操作的最大数量
callback Function (可选)返回一个数组或者对象,参数(err,result)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const parallelLimit = require('async/parallelLimit')
const time = new Date().getTime()

parallelLimit([(callback)=>{
setTimeout(()=>{
callback(null,'one')
},1200)
},(callback)=>{
setTimeout(()=>{
callback(null,'two')
},1000)
}],1,(err,results)=>{
console.log('time cost:',new Date().getTime()-time)
console.log(results)
})

运行结果

1
2
time cost: 2205
["one", "two"]

因为设置的limit是1,所以运行时间是所有运行时间之和

使用for,set,map实现数组去重

发表于 2018-11-13

数组去重的场景在开发中还是经常遇见的,比如一个地图选房功能,在地图上标识一个区域,显示区域上的房型,移动区域后显示新增的房型以及原来还在区域内的房型保持不变,实现这一个场景,就需要得出每次不变的房型的数组,以及新增的房型的数组.一般的,后台返回的数据只是我们选中的区域的房型数据,我们要对数据进行加工处理.

构造数据

让我们先来生成模拟数据,并打乱我们生成的数据,模拟真实情况

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
function generateHouses(n) {
//生成数据
let newHouse = new Array(n)
let oldHouse = new Array(n)
for (let i = 0; i < n / 2; i++) {
newHouse[i] = oldHouse[i] = {
id: i
}
}
for (let i = n / 2; i < n; i++) {
newHouse[i] = {
id: n * 2 + i
}
oldHouse[i] = {
id: n + i
}
}
return {
newHouse: randomArr(newHouse),
oldHouse: randomArr(oldHouse)
}
}

function randomArr(arr) {
//数组洗牌
for (var i = arr.length - 1; i >= 0; i--) {
var randomIndex = Math.floor(Math.random() * (i + 1));
var itemAtIndex = arr[randomIndex];
arr[randomIndex] = arr[i];
arr[i] = itemAtIndex;
}
return arr
}
const {newHouse,oldHouse} = generateHouses(n) //n为需要构造的数据量
const newSetHouse = new Set(newHouse) //构造set数据
const newMapHouse = new Map() //构造map数据
for(let index in newHouse){
newMapHouse.set(newHouse[index].id,newHouse[index])
}

For循环

for循环方法进行两次循环,因为每判断一次新数据就需要遍历一遍老数据,所以时间复杂度为O(N^2),对于1000的数据,最坏的情况要判断1000000次

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
function filterHouse_for(newHouse, oldHouse) {
if (newHouse.length === 0) {
return {
remainHouse: oldHouse,
newHouse: newHouse
}
}
let remainHouse = []
let newOutPutouse = []
for (let nIndex in newHouse) {
let isNew = true
for (let oIndex in oldHouse) {
if (newHouse[nIndex].id === oldHouse[oIndex].id) {
remainHouse.push(newHouse[nIndex])
isNew = false
break
}
}
if (isNew) {
newOutPutouse.push(newHouse[nIndex])
}
}
return {
remainHouse,
newOutPutouse
}
}

Set方法

set是ES6新增的一种数据类型,它的成员是唯一的,所以用来进行去重是非常合适的,总体时间复杂度相较于for循环,它变成了O(NlogN),所以与for循环的时间长短比较取决于系数N,当系数较小时,差别不是很大,但当系数变大时,for循环消耗的时间是成指数级别增加的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function filterHouse_set(newHouse, oldHouse,newSetHouse) {
let remainHouse = []
let newOutPutouse = []
for (let index in oldHouse) {
if (newSetHouse.has(oldHouse[index])) {
remainHouse.push(oldHouse[index])
} else {
newOutPutouse.push(oldHouse[index])
}
}
return {
remainHouse,
newOutPutouse
}
}

Map方法

哈希的时间复杂度为O(1),因此总的时间复杂度为O(N),代价是哈希的存储空间通常为数据大小的两倍,并且需要手动先构造map数据

1
2
3
4
5
6
7
8
9
10
11
function filterHouse_map(newHouse,oldHouse,newMapHouse){
let remainHouse = []
let newOutPutouse = []
oldHouse.map(ele=>{
newMapHouse.has(ele.id)?remainHouse.push(ele):newOutPutouse.push(ele)
})
return {
remainHouse,
newOutPutouse
}
}

时间比较

写一个函数来比较三种方法需要的时间

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
function test(n){
const {
newHouse,
oldHouse
} = generateHouses(n)
/* for */
console.log('for Start')
var time = new Date().getTime()
filterHouse_for(newHouse, oldHouse)
console.log('for End', new Date().getTime() - time)

/* set */
const newSetHouse = new Set(newHouse) //构造set数据
console.log('set Start')
var time = new Date().getTime()
filterHouse_set(newHouse, oldHouse,newSetHouse)
console.log('set End', new Date().getTime() - time)

/* map */
const newMapHouse = new Map() //构造map数据
for(let index in newHouse){
newMapHouse.set(newHouse[index].id,newHouse[index])
}
console.log('map Start')
var time = new Date().getTime()
filterHouse_map(newHouse, oldHouse,newMapHouse)
console.log('map End', new Date().getTime() - time)
}

运行结果(chrome下运行(版本 70))

当 n 为10时:

1
2
3
4
5
6
7
test(100)
VM253:97 for Start
VM253:100 for End 3
VM253:104 set Start
VM253:107 set End 0
VM253:114 map Start
VM253:117 map End 0

当 n 为1000时:

1
2
3
4
5
6
for Start
VM253:100 for End 60
VM253:104 set Start
VM253:107 set End 1
VM253:114 map Start
VM253:117 map End 0

当 n 为10000时:

1
2
3
4
5
6
for Start
VM253:100 for End 4532
VM253:104 set Start
VM253:107 set End 5
VM253:114 map Start
VM253:117 map End 2

当 n 为100000时:
因为for循环等了几分钟都没有算出结果,就不比较for循环了

1
2
3
4
set Start
VM231:102 set End 37
VM231:109 map Start
VM231:112 map End 30

当 n 为10000000时:

1
2
3
4
5
6
test(1000000)

VM231:99 set Start
VM231:102 set End 466
VM231:109 map Start
VM231:112 map End 592

结论

可以看出,不论哪种情况,for循环用时都是最长的,所以不要使用for循环😋,数据量是十万级别以下时,使用map速度比set快一些,十万级别以上时使用set

1…789
Toffee

Toffee

44 日志
GitHub E-Mail 我的网站 StackOverflow
0%
© 2018 – 2021 Toffee