Mryao博客

Welcome to my little house


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签
Mryao博客

压缩js兼容ie8及以下版本

发表于 2017-01-16 | 分类于 打包

无论是用gulp还是grunt打包,都是调用了uglify.js的包,打包后的代码有时候会在ie8及以下版本中报错。

uglify.js兼容ie8的配置方法

gulp

下载项目依赖

1
2
npm install --save-dev uglify-js
npm install --save-dev gulp-uglify/minifier

options

1
2
3
4
5
6
7
8
9
10
gulp.src(paths.tmp.modules)
.pipe(minifier({
output:{
quote_keys:true
},
compress:{
screw_ie8:false
}
}, uglifyjs))
.pipe(gulp.dest(paths.dist.modules));
  • minifier options

    • quote_keys:key值自动加上引号,在ie中,关键字入delete作为索引值打包,浏览器会报错,如果设置为true,则自动加上引号不报错

    • screw_ie8:兼容ie8

grunt

下载项目依赖

1
npm install --save-dev grunt-uglify

options

screwIE8: false

Mryao博客

Node构建桌面应用教程

发表于 2017-01-13 | 分类于 node编程

Electron 提供了一个实时构建桌面应用的纯 JavaScript 环境。Electron 可以获取到你定义在 package.json 中 main 文件内容,然后执行它。通过这个文件(通常我们称之为main.js),可以创建一个应用窗口,这个应用窗口包含一个渲染好的 web 界面,还可以和系统原生的 GUI 交互。

直接启动main.js是无法显示应用窗口的,在main.js调用BrowserWindow模块才能使用窗口。每个窗口都将执行属于自己的渲染进程。渲染进程处理的是一个正真的web页面”HTML+CSS+JavaScript”。前端人员也可以用web的形式开发桌面应用啦。

electron工作示意图

开始electron编程

目录结构

  • app 项目目录
    • index.js index页面的渲染进程
    • list.js list页面的渲染进程
    • statics 静态资源目录
  • main.js 主进程
  • package.json

在项目文件夹中建造package.json文件,内容如下

1
2
3
4
5
6
7
8
{
"name": "yao",
"version": "0.1.0",
"main": "./main.js",
"scripts": {
"start": "electron ."
}
}

安装electron

1
npm install --save-dev electron

创建入口程序main.js

引入项目依赖,ES6写法

BrowserWindow管理窗口,子窗口可以向ipcMain发送事件,达到窗口间通信的效果

1
2
3
4
5
const {app, BrowserWindow , ipcMain} = require('electron')
const path = require('path')
const url = require('url')
//窗口全局变量
let win

创建主窗口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function createWindow () {
// 创建窗口
win = new BrowserWindow({width: 800, height: 600})

// 加载本地app的html
win.loadURL(url.format({
pathname: path.join(__dirname, 'index.html'),
protocol: 'file:',
slashes: true
}))

// 打开控制台
win.webContents.openDevTools()

// 绑定函数,当窗口关闭时触发
win.on('closed', () => {
win = null
})
}

绑定事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//当app加载完即启用回调函数
app.on('ready', createWindow)

//当所有窗口关闭启用回调函数
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})

//当app活跃时,启动回调
app.on('activate', () => {
if (win === null) {
createWindow()
}
})

创建页面

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>gulp打包</title>
<link rel="stylesheet" type="text/css" href="statics/styles/reset.css">
<link rel="stylesheet" type="text/css" href="statics/styles/index.css">
</head>
<body>
<h1>hello word</h1>
<button>open list</button>
</body>
<script>
//加载jquery
window.$ = window.jQuery = require('./statics/scripts/jquery-1.11.2.min.js');
</script>

<script>
// 加载管理自己渲染进程的js
require('./index.js')
</script>

</html>

list.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>List消息列表</title>
<link rel="stylesheet" type="text/css" href="statics/styles/reset.css">
<link rel="stylesheet" type="text/css" href="statics/styles/list.css">
</head>
<body>
<div class="list_content">
<span></span>
</div>
</body>
<script>
//加载jquery
window.$ = window.jQuery = require('./statics/scripts/jquery-1.11.2.min.js');
</script>

<script>
//家贼管理自己渲染进程的js
require('./list.js')
</script>

</html>

electron页面之间的消息传递

index.js

1
2
3
4
5
6
7
8
9
10
11
const {
ipcRenderer
} = require('electron');

var msg='i am yao'

// 点击按钮,打开List列表
// 向主进程传递open-list-window事件
$('button').on('click', () => {
ipcRenderer.send('open-list-window', msg);
})

main.js添加事件监听 open-list-window事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//子窗口
let listWindow

//监听渲染进程发过来的事件,执行回调 ,子窗口被打开

ipcMain.on('open-list-window', (event, arg) => {
if (listWindow) {
return;
}
listWindow = new BrowserWindow({
height: 500,
width: 500,
resizable: false
})
listWindow.loadURL(url.format({
pathname: path.join(__dirname, 'app/list.html'),
protocol: 'file:',
slashes: true
}))
listWindow.webContents.openDevTools();
listWindow.on('closed', () => {
listWindow = null;
})
})

list.js添加事件监听 hello事件

1
2
3
4
5
//子窗口发送消息给主进程

const {ipcRenderer}=require('electron');

ipcRenderer.send('hello','i am 子窗口')

main.js添加代码

1
2
3
ipcMain.on('hello', (event, arg) => {
console.log(arg)
})

控制台最后打印出i am 子窗口。

electron展望

electron主进程可以执行node的exec模块,调用系统命令,可以通过web页面构建良好的用户交互体验,通过点击按钮,执行一些系统命令。

比如: 建造一个项目, 利用localstorage去记录文件,选择项目文件,复制改文件到app中的source中,安装依赖,执行app里面的打包脚本,最后输出到文件目录中dist处。可以构建一个类似weflow的打包桌面应用。

目前正在进行中,敬请期待。

Mryao博客

gulp打包之解耦任务

发表于 2017-01-13 | 分类于 打包

如果项目太大,需要拆分任务,可能会导致gulpfile.js体积过大,难以维护。
为了解决这个问题,应将任务拆分到不同的文件中,即解耦。

安装gulp4.0

利用gulp.series控制串行任务
利用gulp.parallel控制并行任务
更好的利用node高并发的特点,更快的,明确的,进行构建

1
2
npm install gulpjs/gulp#4.0 -g
npm install gulpjs/gulp#4.0 --save-dev

创建gulpfile.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 //__dirname为当前目录
//deep可配置,控制递归的次数
//这段代码 会去自动加载本目录下的_tasks的index.js,传递gulp
//执行gulp --tasks可列出所有任务列表

var gulp=require('gulp');
var fs=require('fs');
var path=require('path');

var deep=3;
run_tasks('_tasks');
function run_tasks(tasks_path){
if (--deep < 0) {
throw new Error('something wrong in require tasks!');
return;
}
tasks_path=path.resolve(__dirname,tasks_path);
if (fs.existsSync(tasks_path)) {
require(tasks_path)(gulp)
} else {
run_tasks(tasks_path);
}
}

创建_tasks文件夹,并在该文件夹中创建index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//index.js
//gulpfile执行即加载了该文件,并执行了改文件
//当前目录下所有符合命名规则(见正则)会被加载
//即gulpfile.js依赖了这个文件夹下所有符合命名规则的文件
var fs = require('fs');
var path = require('path');

module.exports = function (gulp) {
fs.readdirSync(__dirname).filter(function (file) {
return (file.indexOf(".") !== 0) && (file.indexOf('Task') === 0);
}).forEach(function (file) {
var registerTask = require(path.join(__dirname, file));
registerTask(gulp);
});
};

建立TaskBuildDev.js(示例)

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
const gulp = require('gulp');
const spritesmith = require('gulp.spritesmith');
const sass = require('gulp-sass');
const moment = require('moment');
const del = require('del');
const merge = require('merge-stream');
const paths = {
src: {
dir: './',
images: ['./web/statics/images/icons/*.png', './web/statics/images/large/*.png', './web/statics/images/login/*.png', './web/statics/images/saas_console/icons/*.png'],
sass: ['./web/statics/sass/**/*.scss'],
source: './web/**/*'
},
dev: {
dir: './',
images: './web/statics/images/',
sass: './web/statics/sass',
css: './web/statics/styles',
sprite: './web/statics/images/icons-*',
}
};
const imageName=formatName('icons.png');
//格式化名称 版本号
function formatName(name) {
var version = moment().format('MMDDHHmm');
var tmp = name.split('.');
return tmp[0] + '-' + version + '.' + tmp[1];
}
module.exports=function(gulp){
// 删除老版本雪碧图
function delSprite() {
return del(paths.dev.sprite);
}
// 编译saas
function compileSass() {
return gulp.src(paths.src.sass)
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest(paths.dev.css));
}
// 生成雪碧图
function sprite() {
var spriteData = gulp.src(paths.src.images)
.pipe(spritesmith({
imgName:imageName,
cssName: '_sprite.scss',
cssSpritesheetName: 'icon',
padding: 5,
imgPath:'../images/'+imageName
}));
var imgStream = spriteData.img.pipe(gulp.dest(paths.dev.images));
var cssStream = spriteData.css.pipe(gulp.dest(paths.dev.sass));
return merge(imgStream, cssStream);
}
// 重新生成雪碧图
gulp.task('icons', gulp.series(
delSprite,
sprite
));
// 编译sass
gulp.task('sass',gulp.series(compileSass));
// 生成雪碧图 并生成 saas
gulp.task('build_dev',gulp.series(
delSprite,
sprite,
compileSass
));
// 监听sass
gulp.task('dev', () => {
gulp.watch(paths.src.sass, gulp.series(compileSass));
});
}

#建立TaskBuildWeb.js (示例)

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
const gulp = require('gulp');
const spritesmith = require('gulp.spritesmith');
const sass = require('gulp-sass');
const moment = require('moment');
const del = require('del');
const replace = require('gulp-replace');
const merge = require('merge-stream');
const gulpif = require('gulp-if');
const uglifyjs = require('uglify-js');
const minifier = require('gulp-uglify/minifier')
const cleanCSS = require('gulp-clean-css');
const rename = require('gulp-rename');
require('gulp-grunt')(gulp);
const version = moment().format('MMDDHHmm');
const paths = {
src: {
dir: './',
images: ['./web/statics/images/icons/*.png', './web/statics/images/large/*.png', './web/statics/images/login/*.png', './web/statics/images/saas_console/icons/*.png'],
source: ['./web/**/*', '!./web/plugins/**/*'],
modules: './web/modules/**/*',
sprite: './web/statics/sass/_sprite.scss'
},
dist: {
dir: './dist',
tpl: ['./dist/web/modules/**/*.tpl', '!./dist/web/modules/page/account/**/*.tpl'],
cleanmodules: './dist/web/modules/**/*',
modules: './dist/web/modules',
base: './dist/web/modules/base/**/*.js',
css: ['./dist/web/statics/styles/**/*.css', '!./dist/web/statics/**/*.min.css'],
html: ['./dist/web/*.html'],
statics: [
'./dist/web/statics/images/icons',
'./dist/web/statics/images/large',
'./dist/web/statics/images/login',
'./dist/web/statics/sass',
'./dist/web/tmp',
'./dist/web/modules/view'
],
others: ['./dist/web/statics/scripts/sha1.js', './dist/web/statics/scripts/sha1_worker.js'],
gif: './dist/web/statics/sass/_sprite_gif.scss',
sass: ['./dist/web/statics/sass/**/*.scss'],
styles: './dist/web/statics/styles'
},
tmp: {
dir: './dist/web/tmp',
base: './dist/web/tmp/modules/base',
modules: './dist/web/tmp/modules/**/*'
}
};
module.exports = function(gulp) {
// 复制modules
function copySource() {
return gulp.src(paths.src.source, {
base: paths.src.dir
}).pipe(gulp.dest(paths.dist.dir));
}

function copyBase() {
return gulp.src(paths.dist.base)
.pipe(gulp.dest(paths.tmp.base));
}


function replaceTpl() {
return gulp.src(paths.dist.tpl, {
base: paths.dist.dir
}).pipe(replace(/\\/g, '\\\\'))
.pipe(gulp.dest(paths.dist.dir))
}
// 清除dist文件夹中的文件
function cleanDist() {
return del([paths.dist.dir]);
}
// 压缩js
function uglifyJS() {
return gulp.src(paths.tmp.modules)
.pipe(minifier({
output:{
quote_keys:true
},
compress:{
screw_ie8:false
}
}, uglifyjs))
.pipe(gulp.dest(paths.dist.modules));
}
// 压缩其他
function uglifyOthers() {
return gulp.src(paths.dist.others, {
base: paths.dist.dir
})
.pipe(minifier({
output:{
quote_keys:true
},
compress:{
screw_ie8:false
}
}, uglifyjs))
.pipe(gulp.dest(paths.dist.dir))
}
// 压缩css
function uglifyCss() {
return gulp.src(paths.dist.css, {
base: paths.dist.dir
})
.pipe(cleanCSS())
.pipe(gulp.dest(paths.dist.dir));
}
// 删除dist中没有压缩合并的modules
function cleanModules() {
return del([paths.dist.cleanmodules]);
}
//删除无用的静态资源
function cleanStatics() {
return del(paths.dist.statics);
}
//添加版本号
function addVersion() {
return gulp.src(paths.dist.html, {
base: paths.dist.dir
})
.pipe(replace(/@@version/g, version))
.pipe(gulp.dest(paths.dist.dir));
}
// 支持IE
function createGif() {
return gulp.src(paths.src.sprite)
.pipe(replace(/.png/g, '.gif'))
.pipe(rename('web/statics/sass/_sprite_gif.scss'))
.pipe(gulp.dest(paths.dist.dir));
}
//删除
function cleanGif() {
return del(paths.dist.gif);
}

function compileSass() {
return gulp.src(paths.dist.sass)
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest(paths.dist.styles));
}
gulp.task('build_web', gulp.series(
cleanDist,
gulp.parallel('icons'),
copySource,
replaceTpl,
gulp.parallel('grunt-imagemagick-convert'),
gulp.parallel('grunt-transport:dist'),
gulp.parallel('grunt-concat'),
copyBase,
cleanModules,
cleanGif,
createGif,
compileSass,
uglifyJS,
uglifyOthers,
uglifyCss,
cleanStatics,
addVersion
));
gulp.task('build', gulp.series(
gulp.parallel('build_web'),
gulp.parallel('copyWap', 'copyMobile')
));
}

可以将不同功能的任务拆分成多个文件。每个任务都享有自己的私有变量。
更容易扩展和维护。

Mryao博客

Node图片服务器的简易实现

发表于 2017-01-12 | 分类于 node编程

http商业服务器遵循的原则

高扩展,低耦合
商业服务器

demo下载

Quick Start

1
2
3
"安装formidable模块"

npm install formidable --save-dev

创建一个入口文件 index.js

1
2
3
var server=require('./server.js');

server.start();

创建文件 server.js,创键服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var http = require('http');
var router=require('./router.js');

function start(){
function onRequest(request,response){
router.route(request,response);
}

http.createServer(function(request,response){
onRequest(request,response);
}).listen(8080,function(){
console.log('server start on port 8080');
});
}

exports.start=start;

创键文件route.js,负责路由分发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var requestHandler = require('./requestHandler.js');
var url = require('url');
var handle = requestHandler.handle;

function route(request, response) {
var pathname = url.parse(request.url).pathname;
if (typeof(handle[pathname]) === 'function') {
console.log('recieve a handle ' + pathname);
handle[pathname](request, response);
} else {
response.writeHead(404, {
'content-type': 'text/plain'
});
response.write('Not Found');
response.end();
}
}
exports.route = route;

创键文件requestHandler.js,集中管理相关操作

1
2
3
4
5
6
7
8
var startHandler=require('./startHandler');
var uploadHandler=require('./uploadHandler');
var showHandler=require('./showHandler');
var handle={};
handle['/']=startHandler.start;
handle['/upload']=uploadHandler.upload;
handle['/show']=showHandler.show;
exports.handle=handle;

创键startHandler.js,具体负责start路由下,服务器的操作

1
2
3
4
5
6
7
8
9
10
11
12
var fs=require('fs');

function start(request,response){
var body=fs.readFileSync('./post.html');
response.writeHead(200,{
'content-type':'text/html'
});
response.write(body);
response.end();
}

exports.start=start;

创键uploadHandler.js,具体负责上传路由,服务器的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var formidable = require('formidable');
var fs=require('fs');

function upload(request, response) {
var form = new formidable.IncomingForm();
form.uploadDir='pubilc/upload/';
form.parse(request, function(err, fields, files) {
fs.renameSync(files.myFile.path,'tmp/test.png');
response.writeHead(200, {
'content-type': 'text/html'
});
response.write('received upload:<br/>');
response.write('<img src="/show"/>');
response.end();
});
}

exports.upload = upload;

创键showHandler.js,负责显示图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var fs = require('fs');

function show(request, response) {
fs.readFile('tmp/test.png', 'binary', function(error, file) {
if (error) {
response.writeHead(500, {
'content-type': 'text/plain'
});
console.log(error);
response.write('500 服务器内部错误');
response.end();
} else {
response.writeHead(200, {
'content-type': 'image/png'
});
response.write(file, "binary");
response.end();
}
})
}
exports.show = show;
Mryao博客

爬虫简单示例

发表于 2017-01-12 | 分类于 node编程
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
/*
* one:
* 目标站点:动脑论坛
* 目标数据:会员名
* two:
* 目标站点:起点小说
* 目标数据:玄幻小说
* three:
* 目标站点:http://www.mm131.com/
* 目标数据:美女图片
* 作者:yao
* 时间:2017.1.11
*/


var cheerio = require('cheerio');
var request = require('request');
var fs = require('fs');
var mkdirp = require('mkdirp')


// charpter one 抓取会员名, output: name.txt
request('http://101.200.129.112:4567/users/latest', function(error, response, body) {
if (!error && response.statusCode == 200) {
var $ = cheerio.load(body);
var items = $('#users-container .user-info a');
var names = [];
items.each(function() {
names.push($(this).text());
})
fs.writeFile('name.txt', names.join(','));
console.log('会员名抓取完毕');
}
})

// charpter two 抓取小说, output: xiaoshuo.txt
function getContent(url) {
request(url, function(error, response, body) {
if (!error && response.statusCode == 200) {
var $ = cheerio.load(body);
var str = '';
var next = $('#j_chapterNext').attr('href');
next = 'http:' + next;
str += $('.read-content').text();
fs.appendFile('xiaoshuo.txt', str);
getContent(next);
} else {
console.log('抓取完毕');
}
})
}
var url = 'http://read.qidian.com/chapter/OOqJX-uOLbLmkXioLmMPXw2/fwup5h1VDGlMs5iq0oQwLQ2';
getContent(url);


//创建目录
mkdirp('./images', function(err) {
if(err){
console.log(err);
}
});
// charpter three 抓取美女图片,output:images文件夹
function getImages(url, base) {
request(url, function(error, response, body) {
if (!error && response.statusCode == 200) {
var $ = cheerio.load(body);
var items = $('.public-box dd a img');
var $next = $('.page-en').eq(-2);
nextUrl = base + $next.attr('href');
items.each(function() {
var src = $(this).attr('src');
if (/pic/.test(src) > 0) {
download(src, './images', Math.floor(Math.random() * 1000000000) + src.split('/').pop());
console.log('正在下载' + src);
}
})
if (isNaN($next.text())) {
getImages(nextUrl, base);
} else {
console.log('抓取完毕');
}
}
})
}

function download(url, dir, filename) {
request.head(url, function(err, res, body) {
request(url).pipe(fs.createWriteStream(dir + '/' + filename));
})
}

getImages('http://www.mm131.com/qingchun/', 'http://www.mm131.com/qingchun/');
TianCheng Yao

TianCheng Yao

技术收集

5 日志
2 分类
5 标签
Github Weibo
© 2017 TianCheng Yao
由 Hexo 强力驱动
主题 - NexT.Muse