node基于express的socket.io
前一段事件,我一个同学给他们公司用融云搭建了一套web及时通信系统,然后之前我的公司也用过环云来实现web及时通信,本人对web及时通信还是非常感兴趣的。私下读了融云和环信的开发文档,然后发现如果注册第三方貌似开发群聊就是个问题了(因为自己做个两个人聊天很简单,想做的在复杂些就要花钱了- -orz),包括后期我想开发表情包,群聊,和消息加密等等。本着节省的原则尝试去看了一下socket.io,结果一发不可收拾。。。好了闲话少说,今天来分享一份关于socket.io的东西!!
注:本篇文章建议有node基础的同学看,最好有一些express的开发经验
首先我们搭建了一个express的项目,我直接用了express的默认jade,小伙伴们喜欢ejs就自己创建成ejs模版的项目!
之后我们引进socket,代码如下:
{
"name": "jadeShop",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www",
"dev": "supervisor ./bin/www"
},
"dependencies": {
"body-parser": "~1.15.1",
"cookie-parser": "~1.4.3",
"debug": "~2.2.0",
"express": "~4.13.4",
"mysql":"*",
"jade": "~1.11.0",
"morgan": "~1.7.0",
"serve-favicon": "~2.3.0",
"socket.io": "*"
}
}
这个是我的package.json的配置,我安装了supervisor,,然后mysql(因为之前做过一段时间php开发所以本人还是比较喜欢mysql,如果喜欢nosql的小伙伴么,自己安装mongodb)和socket.io之后直接进入项目运行npm install自动加载包依赖。
我们加载了socket.io之后我们进入到app.js来建立socket.io的connect,代码如下
var users = {}
var server = http.createServer(app)
var io = require('socket.io').listen(server)
我们的users用来存所有的在线用户,第二行的app是express框架自动生成。之后我们的io就是socket的实力,我们可以调用io下的socket.on方法
代码如下:
io.sockets.on('connection', function (socket) { }
这样每当我们在iterm上启动服务器那么socket.io就会自动连接,然后我们方法里面有一个回调函数,socket这个参数我们就可以去on自己想要的事件了,这里的事件是自定义名称,你可以叫a,也可以叫b,但是最好语意话一些!
我们来说一下我们用的几个基本方法,因为socket.io是消息推送,而不像ajax长轮询那样子。所以我们肯定是需要一个方法去坚挺客户端发送的方法,这里不管是服务端还是客户端其实都是一个原理,然后我们还需要一个方法去推送数据!
监听方法代码如下:
socket.on('online', function (data) {}
推送方法如下代码:
1.向所有人推送:
io.sockets.emit('online', {users: users, user: data.user})
2.向客户端某个人推送:
var clients = io.sockets.clients()
clients.forEach(function (client) {
if (client.name === data.to) {
client.emit('say', data)
}
})
这里我们的clients就是所有在线用户,他是一个数组我们forEach来遍历这个数组,然后判断当我们的数组里面的某个人是你想发送的人,直接emit,这里也是一个回调函数。
3.向除了自己外的所有人推送:
socket.broadcast.emit('say', data)
好了基本上有这些方法我们就能做东西了,这些基本上就是服务端socket.io用的所有东西了。
太他妈太简单了,闲话少说,直接说客户端。
我们需要引入一些文件:
script(src='javascripts/layout/jquery.cookie.js')
script(src='/socket.io/socket.io.js')
注:我用的是jade模版,所以这么写
我们这里用到了jquery的cookie,这个看自己需求,也可以自己直接写原声js,然后我们引入socket.io我们就能用啦,是不是很简单?
然后我们来写客户端的代码:
var socket = window.io.connect()
var from = window.$.cookie('user')
socket实例和node端基本上一个用法,from是我们从cookie取出来的登录用户。
客户端不需要服务端那样好多发送,所以直接emit就可以了哈哈
socket.emit('online', {user: from})
socket.on('online', function (data) {
console.log(data)
})
现在代码写到这里,我们知道登陆页面,客户端就emit一个名字叫做online的方法,传的参数是user为cookie的登录用户。之后如果app.js中存在代码:
socket.on('online', function (data) {
socket.name = data.user
if (!users[data.user]) {
users[data.user] = data.user
}
io.sockets.emit('online', {users: users, user: data.user})
})
我们就监听到了服务端online方法了,之后运行逻辑在emit,之后客户端在监听,基本上就能实现所有功能了,具体设计自己实现。
下面来展示自己的代码和效果(仅供参考)。
node的socket.io:
var users = {}
var server = http.createServer(app)
var io = require('socket.io').listen(server)
io.sockets.on('connection', function (socket) {
socket.on('online', function (data) {
socket.name = data.user
if (!users[data.user]) {
users[data.user] = data.user
}
io.sockets.emit('online', {users: users, user: data.user})
})
socket.on('say', function (data) {
chatApi.insertChat(data, function (cont) {
if (cont) {
if (data.to === 'all') {
socket.broadcast.emit('say', data)
} else {
var clients = io.sockets.clients()
clients.forEach(function (client) {
if (client.name === data.to) {
client.emit('say', data)
}
})
}
chatApi.upDataChatList(data, function (conts) {
})
}
})
})
socket.on('focus', function (data) {
var clients = io.sockets.clients()
clients.forEach(function (client) {
if (client.name === data.to) {
client.emit('focus', data)
}
})
})
socket.on('blur', function (data) {
var clients = io.sockets.clients()
clients.forEach(function (client) {
if (client.name === data.to) {
client.emit('blur', data)
}
})
})
socket.on('see', function (data) {
chatApi.updateChat(data, function (conts) {
console.log('conts--->', conts)
var clients = io.sockets.clients()
clients.forEach(function (client) {
if (client.name === data.to) {
client.emit('see', data)
}
})
})
})
socket.on('disconnect', function () {
if (users[socket.name]) {
delete users[socket.name]
socket.broadcast.emit('offline', {users: users, user: socket.name})
}
})
})
node连接mysql做的后台:
var mysql = require('mysql')
var connection = mysql.createConnection({
host: 'localhost',
database: 'shop',
user: 'root',
password: ''
})
function handleDisconnect (connection) {
connection.on('error', function (err) {
if (!err.fatal) {
return
}
if (err.code !== 'PROTOCOL_CONNECTION_LOST') {
throw err
}
console.log('Re-connecting lost connection: ' + err.stack)
connection = mysql.createConnection(connection.config)
handleDisconnect(connection)
connection.connect()
})
}
handleDisconnect(connection)
connection.connect() module.exports = {
insertChat: function (data, callback) {
console.log([data.from, data.to, data.msg])
connection.query('insert into chat(chat_id,chat.from,chat.to,msg,chat.read)values(null,?,?,?,0)', [data.from, data.to, data.msg], function (err, rows, fields) {
callback(rows)
})
},
upDataChatList: function (data, callback) {
connection.query('select * from chatList where chatList_username = ? and chatList_chatname = ?', [data.to, data.from], function (err, rows, fields) {
console.log('rows--->', rows)
if (rows[]) {
console.log('info--->', [rows[].chatList_chat + , data.msg, data.to, data.from])
connection.query('UPDATE chatList SET chatList_chat = ? where chatList_username = ? and chatList_chatname = ? ', [rows[].chatList_chat + , data.to, data.from], function (err, cont, fields) {
})
connection.query('UPDATE chatList SET chatList_content = ? where chatList_username = ? and chatList_chatname = ? ', [data.msg, data.to, data.from], function (err, cont, fields) {
})
connection.query('UPDATE chatList SET chatList_time = ? where chatList_username = ? and chatList_chatname = ? ', [new Date().getTime(), data.to, data.from], function (err, cont, fields) {
callback(cont)
})
} else {
connection.query('insert into chatList(chatList_id,chatList_username,chatList_chatname,chatList_time,chatList_content,chatList_chat)values(null,?,?,?,?,1)', [data.to, data.from, new Date().getTime(), data.msg], function (err, cont, fields) {
callback(cont)
})
}
callback(rows)
})
},
getChatDate: function (req, res, callback) {
connection.query('select * from chat where (chat.from = ? and chat.to = ?) or (chat.from = ? and chat.to = ?) order by chat_id desc limit 0,6', [req.body.from, req.body.to, req.body.to, req.body.from], function (err, rows, fields) {
callback(rows)
})
},
getHistoryDate: function (req, res, callback) {
connection.query('select * from chat where (chat.from = ? and chat.to = ?) or (chat.from = ? and chat.to = ?) order by chat_id desc limit ?,?', [req.body.from, req.body.to, req.body.to, req.body.from, (req.body.index - ) * + , req.body.index * + ], function (err, rows, fields) {
callback(rows)
})
},
getChatListData: function (req, res, callback) {
connection.query('select * from chatList where chatList_username = ? order by chatList_time desc', [req.body.username], function (err, rows, fields) {
callback(rows)
})
},
updateChatList: function (req, res, callback) {
connection.query('UPDATE chatList SET chatList_chat = 0 where chatList_username = ? and chatList_chatname = ?', [req.body.username, req.body.chatname], function (err, rows, fields) {
callback(rows)
})
},
updateChat: function (data, callback) {
connection.query('UPDATE chat SET chat.read = 1 where chat.from = ? and chat.to = ? and chat.read=0', [data.to, data.from], function (err, rows, fields) {
callback(rows)
})
}
}
这里偷懒了- -,mysql的链接应该自己写一个config文件里面而不是这里面。
jade模版(list页面):
doctype html
html(lang="zh-CN" ng-app="hyyApp" ng-controller="chat")
head
title(ng-bind='chatName')
meta(name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no")
include ./includes/head
link(href="/stylesheets/chatList.css" rel='stylesheet')
body
.row
.col-lg-
.input-group
input.form-control(type="text")
span.input-group-btn
button.btn.btn-default(type="button") serach
.container
ul
li.chatListLi(ng-repeat='chatList in chatLists' ng-click='chatListClick(chatList)')
img(src='/images/patient.png')
.chatListInfo
.chatListTop
.chatListTitle(ng-bind='chatList.chatList_chatname')
.chatList(ng-bind='chatList.time')
.badge(ng-bind='chatList.chatList_chat' ng-if='chatList.chatList_chat')
.chatListContent(ng-bind='chatList.chatList_content')
include ./includes/body
script(src='javascripts/layout/zepto.js')
script(src='javascripts/layout/touch.js')
script(src='/javascripts/chatList.js')
js(list页面):
var TimeByClinic = function ($scope, $http) {
var socket = window.io.connect()
var from = window.$.cookie('user')
$scope.chatLists = []
$scope.timeStamp = new Date().getTime()
function getTime (date) {
for (var i = ; i < date.length; i++) {
date[i].year = new Date(parseInt(date[i].chatList_time)).getFullYear()
date[i].month = new Date(parseInt(date[i].chatList_time)).getMonth() +
date[i].data = new Date(parseInt(date[i].chatList_time)).getDate()
if ($scope.timeStamp - date[i].chatList_time <= ) {
if (new Date(parseInt(date[i].chatList_time)).getMinutes() < ) {
date[i].time = new Date(parseInt(date[i].chatList_time)).getHours() + ':0' + new Date(parseInt(date[i].chatList_time)).getMinutes()
} else {
date[i].time = new Date(parseInt(date[i].chatList_time)).getHours() + ':' + new Date(parseInt(date[i].chatList_time)).getMinutes()
}
} else {
date[i].time = date[i].data + '|' + date[i].month + '|' + date[i].year
}
}
console.log(date)
}
function chatList () {
$http({
url: '/getChatListData',
method: 'POST',
data: {
'username': window.utils.getQuery('username')
}
}).success(function (data) {
$scope.chatLists = data
getTime(data)
})
}
function updateChatList (o) {
$http({
url: '/updateChatList',
method: 'POST',
data: {
'username': window.utils.getQuery('username'),
'chatname': o.chatList_chatname
}
}).success(function (data) {
console.log(data)
})
}
chatList()
$scope.chatListClick = function (o) {
updateChatList(o)
var str = '/chat?' + 'username=' + o.chatList_username + '&chatName=' + o.chatList_chatname
window.location = str
}
socket.emit('online', {user: from})
socket.on('online', function (data) {
console.log(data)
})
socket.on('say', function (data) {
console.log(data)
chatList()
$scope.$apply()
})
}
window.hyyApp.controller('chat', ['$scope', '$http', TimeByClinic])
include进来公共body:
script(src='/javascripts/layout/angular.min.js')
script
|window.hyyApp = window.angular.module('hyyApp', [])
script(src='/layout/until.js')
script(src='javascripts/layout/jquery.cookie.js')
script(src='/socket.io/socket.io.js')
这里的前三行代码是自己为了实现angular模块发开,第四行是自己写的工具类,请忽略!!!
数据库(mysql):
效果图如下:
大概是这样子一个人和另一个人说话,就会产生一条数据:
safari的list表单如果要进入就会读取这条消息,同时chrome的chat页面最下面的消息就会变成已读
效果如下:
然后我们safari的页面在退回到list表单的时候我们就会发现消息提示消失,时间和内容更新
效果如下:
之后我们再来看chat页面,首先看一下代码:
jade:
doctype html
html(lang="zh-CN" ng-app="hyyApp" ng-controller="chat")
head
title(ng-bind='chatName')
meta(name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no")
include ./includes/head
link(href="/stylesheets/chat.css" rel='stylesheet')
body
p.chatCenter.title
span.titleBack(ng-click='back()') < back
span.titleName(ng-bind='showName')
.container#contentChat
div(ng-repeat='data in chatDate')
.chatContentLeft(ng-if='data.to === username')
.chatLeftFlag1
.chatLeftFlag2
img(src='/images/patient.png')
div
p(ng-bind='data.msg')
.chatContentRight(ng-if='data.from === username')
.chatRightFlag1
.chatRightFlag2
img(src='/images/patient.png')
div
p(ng-bind='data.msg' ng-click='until(data)')
span.chatStatus(ng-if='data.read') 已读
.clear
#chatInput
.input-group
input.form-control(type="text" ng-model='input' ng-blur="blur()" ng-focus="focus()")
span.input-group-btn
botton.btn.btn-default(type="button" ng-click='say()' ng-if='input') submit
botton.btn.btn-default(disabled type="button" ng-if='!input') submit
include ./includes/body
script(src='javascripts/layout/zepto.js')
script(src='javascripts/layout/touch.js')
script(src='javascripts/layout/jquery.cookie.js')
script(src='/socket.io/socket.io.js')
script(src='/javascripts/chat.js')
js代码:
var TimeByClinic = function ($scope, $http) {
$scope.input = ''
$scope.username = window.utils.getQuery('username')
$scope.chatName = window.utils.getQuery('chatName')
$scope.showName = window.utils.getQuery('chatName')
$scope.height = window.$(document).height()
$scope.chatDate = []
$scope.index =
$scope.flag = true
$scope.touchStart = []
$scope.touchMove = []
var socket = window.io.connect()
var from = window.$.cookie('user')
/* update chatlist for msg state. */
function updateChatList () {
$http({
url: '/updateChatList',
method: 'POST',
data: {
'username': window.utils.getQuery('username'),
'chatname': window.utils.getQuery('chatName')
}
}).success(function (data) {
console.log(data)
})
}
/* update chat for read state. */
function updateChat () { }
updateChat()
/* GET showchat for six data chat msg. */
function getDate () {
$http({
url: '/getChatDate',
method: 'POST',
data: {
'from': window.utils.getQuery('username'),
'to': window.utils.getQuery('chatName')
}
}).success(function (data) {
console.log(data)
$scope.chatDate = data.reverse()
})
}
/* touch event. */
function touchStart (event) {
var touch = event.touches[]
$scope.touchStart = [touch.pageX, touch.pageY, new Date().getTime()]
}
function touchMove (event) {
var touch = event.touches[]
$scope.touchMove = [touch.pageX, touch.pageY, new Date().getTime()]
}
function touchEnd (event) {
if ($scope.touchMove[] - $scope.touchStart[] >= && $scope.touchMove[] - $scope.touchStart[] <= ) {
if (window.$(document).scrollTop() <= ) {
historyData()
}
}
}
document.addEventListener('touchstart', touchStart, false)
document.addEventListener('touchmove', touchMove, false)
document.addEventListener('touchend', touchEnd, false)
/* GET historyData. */
function historyData () {
if ($scope.flag) {
$scope.flag = false
$http({
url: '/getHistoryDate',
method: 'POST',
data: {
'from': window.utils.getQuery('username'),
'to': window.utils.getQuery('chatName'),
'index': $scope.index
}
}).success(function (data) {
console.log(data)
if (data[]) {
$scope.chatDate = data.reverse().concat($scope.chatDate)
setTimeout(function () {
$scope.flag = true
$scope.index++
}, )
} else {
$scope.more = false
}
if (data.length < ) {
$scope.more = false
$scope.flag = false
}
})
}
}
/* UPDATE view data state. */
function readState () {
for (var i = ; i < $scope.chatDate.length; i++) {
if ($scope.chatDate[i].read === ) {
$scope.chatDate[i].read =
}
}
$scope.$apply()
}
/* GET now time. */
function nowTime () {
var date = new Date()
var time = date.getFullYear() + '-' + (date.getMonth() + ) + '-' + date.getDate() + ' ' + date.getHours() + ':' + (date.getMinutes() < ? ('' + date.getMinutes()) : date.getMinutes()) + ":" + (date.getSeconds() < ? ('' + date.getSeconds()) : date.getSeconds());
return time
}
getDate()
/* socket.io emit and on. */
socket.emit('online', {user: from})
socket.emit('see', {from: from, to: window.utils.getQuery('chatName')})
socket.on('online', function (data) {
console.log(data)
console.log(data.from)
var str = ''
if (data.user !== from) {
// str = '<p class="chatCenter">用户 ' + data.user + ' 上线了!</p>'
} else {
// str = '<p class="chatCenter">' + nowTime() + '</p>'
}
window.$('.container').append(str)
window.$(document).scrollTop(window.$(document).height() - $scope.height)
})
socket.on('see', function (data) {
readState()
})
socket.on('focus', function (data) {
if (data.from === window.utils.getQuery('chatName')) {
$scope.focusNumber =
$scope.showName = '对方正在讲话'
$scope.interval = setInterval(function () {
if ($scope.focusNumber === ) {
$scope.showName = '对方正在讲话'
$scope.focusNumber =
} else {
$scope.showName += '.'
$scope.focusNumber++
}
$scope.$apply()
}, )
$scope.$apply()
}
})
socket.on('blur', function (data) {
$scope.showName = window.utils.getQuery('chatName')
clearInterval($scope.interval)
$scope.$apply()
})
socket.on('say', function (data) {
updateChatList()
console.log(data)
var obj = {
'from': window.utils.getQuery('chatName'),
'to': window.utils.getQuery('username'),
'read': ,
'msg': data.msg
}
$scope.chatDate.push(obj)
// var str = '<div class="chatContentLeft">' +
// '<div class="chatLeftFlag1"></div>' +
// '<div class="chatLeftFlag2"></div>' +
// '<img src="/images/patient.png"/>' +
// '<div>' +
// '<p>' + data.msg + '</p>' +
// '</div>' +
// '</div>'
// window.$('.container').append(str)
socket.emit('see', {from: from, to: window.utils.getQuery('chatName')})
window.$(document).scrollTop(window.$(document).height() - $scope.height)
})
$scope.say = function () {
var obj = {
'from': window.utils.getQuery('username'),
'to': window.utils.getQuery('chatName'),
'read': ,
'msg': $scope.input
}
// var str = '<div class="chatContentRight">' +
// '<div class="chatRightFlag1"></div>' +
// '<div class="chatRightFlag2"></div>' +
// '<img src="/images/patient.png"/>' +
// '<div>' +
// '<p>' + $scope.input + '</p>' +
// '</div>' +
// '<div class="clear"></div>' +
// '</div>'
// window.$('.container').append(str)
$scope.chatDate.push(obj)
window.$(document).scrollTop(window.$(document).height() - $scope.height)
socket.emit('say', {from: from, to: window.utils.getQuery('chatName'), msg: $scope.input})
$scope.input = ''
}
$scope.until = function (o) {
console.log(o)
}
$scope.blur = function () {
socket.emit('blur', {from: from, to: window.utils.getQuery('chatName')})
}
$scope.focus = function () {
console.log()
socket.emit('focus', {from: from, to: window.utils.getQuery('chatName')})
}
$scope.back = function () {
var str = '/chatList?username=' + window.utils.getQuery('username')
window.location = str
}
}
window.hyyApp.controller('chat', ['$scope', '$http', TimeByClinic])
数据库:
数据有点多就截取了一部分。
之后我们就实现了两个页面的通信,当然页面和页面的通信消息会直接变成已读。然后我们上下滑动就能加载出我们的历史记录,这些逻辑都写在了js里面了。
效果如下:
另外我还模拟了微信的对方正在说话中
效果如下:
一方获取焦点另一方就会变成对方正在讲话的样式,然后等焦点blur的时候在变成正常的名字
具体的逻辑就随便你加了,是不是很简单呢?
之前和boss直聘leader聊到这的web及时通信,貌似他们用的是mqtt框架实现的,而不同公司用的肯定也是不一样的,我同学用的融云,而我们公司用的确实环信,不敢谈到这些东西的利弊,因为自己根本没有大规模的这种web即时通信的开发经验,所以还是处于井底之蛙的阶段,不敢妄加评论。但是这些开发的东西其实都是大同小异的,最起码开发文档都是类似的,学习成本也比node的这个原声socket.io要容易的多!
过段时间我会更新socket.io的blog(前提是有时间去继续开发- -),实现群组聊天,表情,消息回撤,消息加密等等更加高级的socket技术!有兴趣的可以继续期待。。。欢迎有不同意见的人前来讨论,希望我的blog对您有帮助!
node基于express的socket.io的更多相关文章
- Node+Express+MongoDB + Socket.io搭建实时聊天应用
Node+Express+MongoDB + Socket.io搭建实时聊天应用 前言 本来开始写博客的时候只是想写一下关于MongoDB的使用总结的,后来觉得还不如干脆写一个node项目实战教程实战 ...
- Node+Express+MongoDB + Socket.io搭建实时聊天应用实战教程(二)--node解析与环境搭建
前言 本来开始写博客的时候只是想写一下关于MongoDB的使用总结的,后来觉得还不如干脆写一个node项目实战教程实战.写教程一方面在自己写的过程中需要考虑更多的东西,另一方面希望能对node入门者有 ...
- Node+Express+MongoDB+Socket.io搭建实时聊天应用实战教程(一)--MongoDB入门
前言 本文并不是网上流传的多少天学会MongoDB那种全面的教程,而意在总结这几天使用MongoDB的心得,给出一个完整的Node+Express+MongoDB+Socket.io搭建实时聊天应用实 ...
- Node.js、Express、Socket.io 入门
前言 周末断断续续的写了第一个socket.io Demo.初次接触socket.io是从其官网看到的,看着get started做了一遍,根据官网的Demo能提供简单的服务端和客户端通讯. 这个De ...
- node.js中使用socket.io + express进行实时消息推送
socket.io是一个websocket库,包含客户端的js和服务端的node.js,可以在不同浏览器和移动设备上构建实时应用. 一.安装 socket.io npm install socket. ...
- express+mongodb+socket.io
node后端代码 // Setup basic express server var express = require('express'); var app = express(); var pa ...
- [Node.js] Level 6. Socket.io
6.2 Setting Up socket.io Server-Side So far we've created an Express server. Now we want to start bu ...
- Node+Express+MongoDB + Socket.io搭建实时聊天应用实战教程(三)--前后端环境配置
前言 之前都是介绍一些基础知识,在这一节,我们就要开始实战coding了.正所谓磨刀不误砍柴工,准备工作显得尤为重要.很多demo只是追求效果的实现,并不注重整个demo的架构性.从我个人的角度看来, ...
- NODE 基于express 框架和mongoDB的cookie和session认证 和图片的上传和删除
源码地址 https://gitee.com/zyqwasd/mongdbSession 本项目的mongodb是本地的mongodb 开启方法可以百度一下 端口是默认的27017 页面效果 1. 注 ...
随机推荐
- HSSFWorkBooK用法 —Excel表的导出和设置
HSSFWorkBooK用法 —Excel表的导出和设置 2013年02月21日 ⁄ 综合 ⁄ 共 9248字 ⁄ 字号 小 中 大 ⁄ 评论关闭 public ActionResult excelP ...
- postgresql的copy
https://www.postgresql.org/docs/current/static/sql-copy.html 一.Copy的基本语法 Copy的作用是复制数据在数据表和文件之间. Copy ...
- HTML5之pushstate、popstate操作history,无刷新改变当前url
一.认识window.history window.history表示window对象的历史记录,是由用户主动产生,并且接受javascript脚本控制的全局对象.window对象通过history对 ...
- C++设计模式之访问者模式
简述 访问者模式(Visitor Pattern)表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作. 代码实现: // Visitor.cpp : ...
- 在 windows 开发 reactNative 的环境 搭建过程 react-native-android
安装的东西挺多的, 从 jdk 到c++环境 到node , python, 各种模拟器 http://bbs.reactnative.cn/topic/10/%E5%9C%A8windows%E4% ...
- DataSetToJSON
unit FMX.DataSetToJSON; interface uses FireDAC.Comp.Client,Data.DB; function DataSetToJSON(DataSet:T ...
- py安装以及配置pip环境变量
安装python,安装包链接:https://pan.baidu.com/s/1u4tA-FJMxtrtJTap-zFh3g 密码:gh1c 默然安装到了C盘 安装pycharm:安装包链接:链接:h ...
- 配置海康相机SDK文件
前言 项目使用到海康摄像机,进行二次开发需要首先对SDK文件进行相关配置. 实现过程 1.下载SDK开发包: 网址:http://www.hikvision.com/cn/download_61.ht ...
- ubuntu16.04 中文输入法
https://blog.csdn.net/qq_21792169/article/details/53152700 在主文件夹目录即home目录,按快捷键Ctrl+H(显示隐藏文件),看到的.bas ...
- HDOJ 1061 Rightmost Digit
找出数学规律 原题: Rightmost Digit Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Ja ...