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 |
前言:最近做前端资源监控,看了很多例子,没有达到想要的效果。首先的槽点是PhantomJS的官方文档,真鸡肋,其次是网上的例子,多数是介绍PhantomJS的用法,而并没有介绍怎么完整的去实现,跟官方文档好像也没啥区别...... 一、实现了什么? 本文只做一个简单的例子,获取网址加载时间,旨在实现一种思路,而不是很复杂的功能。 主要流程: 前端在页面加载时调用node接口 -> 将URL返回给接口 -> node获取url,然后通过子进程(child_process)启动系统命令 -> 启动PhantomJS-> PhantomJS打开url指定的网页,计算加载时间 -> 返回给node接口 -> 接口处理数据 -> 入库 二、主要实现 1、前端获取url let param = {'url': window.location.protocol + '//' + window.location.host}; 将url作为参数传递给接口。 2、NodeJS获取前端传递过来的url并启动phantomjs打开url,数据获取并处理 本文用的是sails框架。 首先是写models定义字段:ResourceMonitor.js module.exports = { connection: '数据库名', tableName: '表名', autoCreatedAt: true, autoUpdatedAt: true, attributes: { id: { type: 'integer', primaryKey: true, autoIncrement: true }, webAddress: { type: 'string' }, loadingTime: { type: 'string' } } } 然后写服务: resourceMonitorService.js module.exports = { async create(resourceMonitor){ try { try { if (resourceMonitor.id) { let oldResourceMonitor = await ResourceMonitor.findOne({id: resourceMonitor.id}); Object.assign(oldResourceMonitor, resourceMonitor); oldResourceMonitor.save(function (err) { if (err) throw err; return oldResourceMonitor; }) } else { return ResourceMonitor.create(resourceMonitor); } } catch (err) { throw err; } } catch (err) { throw err; } } } 接着是controller: resourceMonitorController.js const Cmd = require('cmd') module.exports = { async create(req, res){ try { let url = req.body['url'] //启动phantomJS let resource = await Cmd.resourceMonitorPhantom(url); if (!resource) return; //正则去除空格换行符回车符 resource = resource.replace(/[\s\n\r]+/g, "") //正则匹配'total:'和'ms'之间的所有非空白内容,并在返回结果后面加上'ms' let loadingTime = resource.match(/total:(\S*)ms/)[1] + ' ms'; //正则匹配'webAddress:'和'total:'之间的所有非空白内容 let webAddress = resource.match(/webAddress:(\S*)total:/)[1]; let resourceMonitor = {webAddress, loadingTime} if (!resourceMonitor) { sails.log.error("参数异常 " + JSON.stringify(resourceMonitor)) return res.send({code: -1, msg: "参数异常"}) } resourceMonitorService.create(resourceMonitor).then(function (data) { return res.send({code: 1, msg: "success", data: data}) }).catch(function (err) { sails.log.error("create resourceMonitor err" + err) return res.send({code: -1, msg: "get resourceMonitor list err"}) }) } catch (err) { sails.log.error("create resourceMonitor failure" + err) return res.send({code: -1, msg: "create resourceMonitor failure"}) } } } NodeJS命令行启动PhantonJS的方法:cmd.js //用的是node子进程中的execSync同步方法,可以等phantomjs处理完数据返回以后才执行接下来的内容 let execSync = require('child_process').execSync; module.exports = { async resourceMonitorPhantom(url){ try { let resourceMonitorCmd = `phantomjs ./phantom/resourceMonitorPhantom.js ${url}`; //由于phantomjs返回等数据是二进制流buffer,所以处理称字符串 let resourceMonitorOut = await execSync(resourceMonitorCmd).toString(); return resourceMonitorOut; } catch (err) { throw err; } } } PhantomJS获取资源加载时间:resourceMonitorPhantom.js var page = require('webpage').create(); var system = require('system'); var address; if (system.args.length === 1) { console.log(system.args) phantom.exit(); } else { var t = Date.now(); address = system.args[1]; page.open(address, function (status) { if (status !== 'success') { console.log('fail to load the address'); } else { t = Date.now() - t; console.log('webAddress:' + address + 'total:' + t + 'ms'); } phantom.exit(); }); } 三、涉及的框架及运行环境 1、NodeJS Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. Node.js' package ecosystem, npm, is the largest ecosystem of open source libraries in the world. Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。 Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。 Node.js 的包管理器 npm,是全球最大的开源库生态系统。 官网:https://nodejs.org/ 2、Sails Sails.js (or Sails) is a Model-View-Controller (MVC) web application framework developed atop the Node.js environment, released as free and open-source software under the MIT License. It is designed to make it easy to build custom, enterprise-grade Node.js web applications and APIs. Emulating the MVC architecture of other frameworks, like Ruby on Rails it offers similar pattern and familiarity, reducing the cognitive burden when switching between other frameworks/languages. Sails.js (or Sails)是一个开发的模型-视图-控制器(MVC)web应用程序框架的节点。js的环境,在MIT许可下免费和开源软件的发布。它的目的是使它容易构建定制的,企业级节点。js web应用程序和api。效仿其他框架的MVC架构,像Ruby on Rails提供了类似的模式和熟悉,减少认知负担当切换其他框架/语言。 官网:http://sailsjs.com/ 3、 PhantomJS PhantomJS is a headless WebKit scriptable with a JavaScript API. It has fast and native support for various web standards: DOM handling, CSS selector, JSON, Canvas, and SVG. PhantomJS,脚本化的WebKit内核的无头浏览器,支持各种web标准:DOM处理CSS选择器,JSON,Canvas,SVG。 简单的说,就是一个没有界面的webkit内核浏览器,只能在命令行下使用。可以做很多事,比如:生成网页截图、抓去网页数据、获取网络资源加载时间等。 官网:http://phantomjs.org/ |
Javascript异步编程
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 |
1、什么是异步(Asynchronous)编程? 相对于同步(Synchronous)而言,异步就是后一个任务不需要等待前一个任务结束就执行,而前一个任务结束以后执行回调函数。 最常见的异步编程有setTimeout函数、 ajax请求等。 如: for (var i = 1; i <= 3; i++) { setTimeout(function(){ console.log("#",i); }, 0); console.log("*",i); }; 输出的结果是:*1 *2 *3 #4 #4 #4 涉及到的知识点: 异步、闭包、作用域 setTimeout是一个注册事件,在主程序运行结束前,是不会调用的。 就像为某个div绑定一个点击事件一样,在没点击之前,这个事件是不会被触发的。 这就是一个常见的异步编程的例子。 2、异步编程的方法 Ⅰ、回调函数 假设有 f1(); f2(); 两个函数,其中f2()在等待f1()的执行结果。 function f1(f2){ setTimeout(function(){ f2(); }, 1000); } 代码执行顺序:f1(f2); 优点:f1不会堵塞程序运行,相当于先执行程序的主要逻辑,将耗时的操作推迟执行 缺点:不利于代码的阅读和维护,各个部分之间高度耦合(Coupling),而且每个任务只能指定一个回调函数 Ⅱ、事件监听 f1.on('done', f2);//jQuery写法,当f1发生done事件,就执行f2 function f1(){ setTimeout(function () { f1.trigger('done');//执行完成后,立即触发done事件 }, 1000); } 优点:可以绑定多个事件,每个事件可以指定多个回调函数,而且可以"去耦合"(Decoupling),有利于实现模块化 缺点:整个程序都要变成事件驱动型,运行流程会变得很不清晰 Ⅲ、发布/订阅 jQuery.subscribe("done", f2);//f2向jQuery订阅"done"信号 function f1(){ setTimeout(function () { jQuery.publish("done");//f1执行完成后,向jQuery发布"done"信号,从而引发f2的执行 }, 1000); } Ⅳ、Promises对象 它的思想是,每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。比如,f1的回调函数f2,可以写成: f1().then(f2); 对f1进行如下改写: function f1(){ var dfd = $.Deferred(); setTimeout(function () { dfd.resolve(); }, 500); return dfd.promise; } 优点:回调函数变成了链式写法,程序的流程可以看得很清楚,可以指定多个回调函数,如: f1().then(f2).then(f3); f1().then(f2).fail(f3); 缺点:编写和理解,都相对比较难 附几个概念: 一、事件 事件就是文档或浏览器窗口中发生的一些特定的交互瞬间。 二、注册事件 注册事件分为属性注册和方法注册。 属性注册:1.内嵌在DOM结构里的属性赋值 HTML: <button onclick="sayHello()">点击</button> JS: function sayHello() { console.log('hello') } 2.通过JS来指定元素对象的属性赋值 document.body.onclick = function (e) { alert("为body注册一个点击事件"); }; 方法注册:1.通过addEventListener()方法注册 说明:在JS中,window、document、HtmlElement等对象可以通过addEventListener()方法注册事件的处理程序。 语法:EventTarget.addEventListener(eventName, eventHandler, |useCapture ) 参数: ①eventName {string} :所要注册的事件名称,不区分大小写。此名称不需要像注册事件属性那样前缀加上"on"。如注册鼠标点击事件,写为click。 ②eventHandler {function | function Object} :函数或者函数对象。事件触发时所需要执行的函数;当使用函数对象多次注册同一事件时,只当注册一遍。 ③useCapture {boolean} 可选 :是否处于捕获阶段,默认为false。 多次注册:addEventListener()方法能为同一个对象的同一事件注册多次。当发生此事件时,注册的处理事件程序将按照注册先后顺序执行。 注意: ①IE9之前的IE的不支持此方法,可使用attachEvent()代替。 ②若使用相同的事件处理程序对象多次注册在同一个事件上,只算注册一次。 例: document.body.addEventListener('click',function(e){ console.log('被点击一'); }); document.body.addEventListener('click',function(e){ console.log('被点击二'); }); document.body.click(); // 输出: 被点击一 被点击二 2.通过attachEvent()方法注册 说明:IE9之前的IE版本可通过此方法注册事件。 语法:EventTarget.attachEvent(eventName, eventHandler) ①eventName {string} :所要注册的事件名称,区分大小写。这里的名称跟事件属性一样,以"on"开头,后面跟着事件名称。如:onclick、onload。 ②eventHandler {function | function Object} :函数或者函数对象。事件触发时所需要执行的函数;当使用函数对象多次注册同一事件时,可注册多次(addEventListener()方法只当注册一次)。 多次注册:attachEvent()方法能为同一个对象的同一事件注册多次。当触发此事件时,也会依次执行。 例: function sayHellow(){ console.log('hello'); } document.body.attachEvent('onclick',sayHellow); document.body.attachEvent('onclick',sayHellow); // sayHellow第二次注册同一事件 document.body.click(); //输出2次sayHellow函数 :hello hello 二、注销事件 JS中,可调用removeEventListener()[注销addEventListener()] 和 detachEvent()[注销attachEvent()] 来注销元素的某个事件指定的处理程序,也可以给事件属性赋值null来注销此事件的所有绑定。 1.removeEventListener(eventName, function Object) 说明:注销通过addEventListener()注册的事件处理程序。 语法:EventTarget.removeEventListener(eventName, eventHandlerObj) 参数: ①eventName {string} :所要注销的事件名称,不区分大小写。此名称不需要像注册事件属性那样前缀加上"on"。如注册鼠标点击事件,写为click。 ②eventHandlerObj {function Object} :函数对象。传入一个函数体是没有效果的。 例: // 注销body click事件的sayHello函数 document.body.removeEventListener('click',sayHello); 2.detachEvent(eventName, function Object) 说明:注销通过attachEvent()注册的事件处理程序。 语法:EventTarget.detachEvent(eventName, eventHandlerObj) 参数: ①eventName {string} :所要注销的事件名称,区分大小写。这里的名称跟事件属性一样,以"on"开头,后面跟着事件名称。如:onclick、onload。 ②eventHandlerObj {function Object} ::函数对象。传入一个函数体是没有效果的。 例: // 注销body click事件的sayHello函数 document.body.detachEvent('onclick', sayHello); 3.取消事件 给对象的事件属性赋值为null,可取消此事件的所有注册过的处理事件程序。 例: // onclick属性赋值为null,相当于注销了onclick事件 document.body.onclick=null; |
参考链接: http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html
将一张表的主键(ID)重置为从1开始自增排列
如果你有一张表,你的主键是ID,然后由于测来测去的原因,你的ID不是从1开始连续的自增了。
终于有一天,使用这张表的某个系统要导入正式数据了,强迫症这时候就表现的明显了,浑身不自在,
这时候你就需要将这个主键ID重置一波了,方法是在这张表中新增一个字段,将ID里[……]
微信小程序使用Socket
首先,一个小程序同时只能有一个WebSocket连接,如果当前已经存在一个WebSocket连接,会关闭当前连接,并重新建立一个连接。 其次,如果使用了appID,协议必须是 wss://… 最近团队用小程序做行情,在连接socket的时候,发现在还没有进行subscribe的情况下,就直接进行[……]
JS让任意图片垂直水平居中且页面不滚动
说一下以前遇到的一个问题: 假设有一张小图,要实现点击查看大图的功能,而这个图的宽高可能会超过浏览器的宽高,这时候我们通过JS来改变图片的宽高,从而实现图片在浏览器居中显示且不滚屏。
方法如下: 首先你要给小图添加一个点击事件,不多赘述。
showBigImg([……]
JS判断IE版本并在页面显示内容
<script type=”text/javascript”>
var isIE = function (ver) {
var b = document.createElement(‘b’)
b.innerHTML = ‘<!–[if IE ‘ + ver +[……]
禁止 iphone 网页上下拖动露底
1 2 3 |
document.addEventListener('touchmove', function(e) { e.preventDefault(); }); |
文本行的斑马条纹
周知,表格的斑马条纹只需简单的运用伪类 :nth-child()/:nth-of-type()来处理就好了:
1 2 3 4 5 |
tr:nth-child(even){ background-color: rgba(0,0,0,.2); } |
但是把这种效果运用在文本行的时候就有点力不从心了,尽管js可以实现这种功能,但是这在理论上有违纯粹原则(javascri[……]
自定义优雅的文本下划线
废话不多说:
1 2 3 4 5 |
.underline { background: linear-gradient(90deg, gray 66%, transparent 0) repeat-x; background-size: .2em 2px; background-position: 0 1.4em; } |
1 |
<div class="underline">原来你是这样的毛玻璃</div> |
效果:
Vue根据URL传参来控制全局 console.log 的开关
如果你的项目中console.log了很多信息,但是发到生产环境上又不想打印这些信息,这时候就需要设置一个全局变量,如:debug,
用正则匹配一下参数:
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 |
const getQueryStr = (name) => { var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i"); var r = window.location.search.substr(1).match(reg); if (r != null) return unescape(r[2]); return null; }; 当链接中含有这个参数时,将debug的状态置为true,这时console.log是正常可打印状态,否则将debug的状态置为false, 这时重写console.log函数: console.log = function () { return false; } 这时的全局变量就可以用在整个项目中了,用来控制一些调试窗口的显隐。 在Vue中可以直写在stores/index.js中,当然,写在其他入口文件里也可以: const getQueryStr = (name) => { var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i"); var r = window.location.search.substr(1).match(reg); if (r != null) return unescape(r[2]); return null; }; /* 若链接后面带上参数 envFlag=monitor * 则将 debug 置为true,否则置为false */ const monitor = 'monitor'; const envFlag = getQueryStr('envFlag'); let debug = monitor ? true : false; if (envFlag === 'monitor') { console.log("%cNow You Can Console Log...", "color:red;font-size:18px;font-style:oblique;"); debug = monitor; } else { debug = ''; console.log = function () { return false; } } const state = {debug: debug}; export const store = new vuex.Store({state, mutations}); 这时候如果你想控制一个组件的显示或隐藏,只需要在vuex的getters中声明一下就可以使用变量debug了: <monitor v-show="debug"></monitor> vuex: { getters: { <span style="color: #ff0000;">debug: state => state.debug</span> } } 做完以上的工作后,在URL后面带上参数 <strong>envFlag=monitor </strong>就可以看到组件 monitor 被显示出来,同时打开控制台的话, 就可以看到项目所有的 console.log 信息。 |