使用JS+socket.io+WebRTC+nodejs+express搭建一个简易版远程视频聊天

简介: 使用JS+socket.io+WebRTC+nodejs+express搭建一个简易版远程视频聊天

WebRTC

网页即时通信,是Web Real-Time Communication 的缩写,它支持peer-to-peer(浏览器与浏览器之间)进行视频,音频传输,并保证传输质量,将其发送至本地audio标签,video标签或发送到另一个浏览器中,本文使用到navigator.mediaDevices,RTCPeerConnection对象配合socket+node构建远程实时视频聊天功能,文章有一个不足之处,后面会讲到。


相关文档:

MediaDevices

WebRTC API


参考文章:

https://rtcdeveloper.com/t/topic/13777


代码原理及流程

2.png


源码:https://gitee.com/DieHunter/myCode/tree/master/videoSteam

前端

要实现点对点就需要用到socket长链接从服务端进行寻呼

这是我以前的一篇关于socket简单使用的小案例:https://blog.csdn.net/time_____/article/details/86748679

首先引入socket.io,这里我将前端js分成三部分,分别是socket.js(socket相关操作),userList.js(页面操作),video.js(视频聊天)


2.png


先附上HTML和CSS

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="./style/main.css">
    <script src="./js/socket.io.js"></script>
    <script src="./js/socket.js"></script>
    <script src="./js/userList.js"></script>
    <script src="./js/video.js"></script>
</head>
<body>
    <div id="login" hidden class="loginBox">
        <input id="userName" autocomplete="false" class="userName" type="text" placeholder="请输入英文用户名">
        <button id="submit">提交</button>
    </div>
    <div id="chatBox" class="chatBox" hidden>
        <h1 id="myName" class="myName"></h1>
        <ul id="userList" class="userList"></ul>
    </div>
    <div id="videoChat" hidden class="videoChat">
        <button id="back" hidden>结束</button>
        <video id="myVideo" src="" class="myVideo"></video>
        <video id="otherVideo" src="" class="otherVideo"></video>
    </div>
    <script>
        checkToken()
        function checkToken() { //判断用户是否已有用户名
            if (localStorage.token) {
                login.hidden = true
                chatBox.hidden = false
                initSocket(localStorage.token) //初始化socket连接
            } else {
                login.hidden = false
                chatBox.hidden = true
                submit.addEventListener('click', function (e) {
                    initSocket(userName.value) //初始化socket连接
                })
            }
        }
    </script>
</body>
</html>
* {
    margin: 0;
    padding: 0;
}
.loginBox {
    width: 300px;
    height: 200px;
    margin: 50px auto 0;
}
.userName,
.loginBox button {
    width: 300px;
    height: 60px;
    border-radius: 10px;
    outline: none;
    font-size: 26px;
}
.userName {
    border: 1px solid lightcoral;
    text-align: center;
}
.loginBox button {
    margin-top: 30px;
    display: block;
}
input::placeholder {
    font-size: 26px;
    text-align: center;
}
.chatBox {
    width: 200px;
    margin: 50px auto 0;
    position: relative;
}
.myName {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 50px;
    font-size: 40px;
    text-align: center;
    line-height: 50px;
    background: lightcoral;
}
.userList {
    height: 500px;
    width: 100%;
    padding-top: 50px;
    overflow-y: scroll;
    list-style: none;
}
.userList>li {
    background: lightblue;
    height: 50px;
    font-size: 20px;
    line-height: 50px;
    text-align: center;
}
.videoChat {
    background: lightgreen;
    width: 500px;
    height: 400px;
    margin: 50px auto 0;
}
.videoChat button {
    width: 500px;
    height: 60px;
    border-radius: 10px;
    outline: none;
    float: left;
    font-size: 26px;
}
.myVideo,.otherVideo{
    width: 250px;
    height: 250px;
    float: left;
    overflow: hidden;
}

大致效果


2.png2.png2.png


socket.js

首先建立socket连接,添加连接和断开的事件

let socket //供其他页面调用
function initSocket(token) {//获取到用户输入的id并传到服务端
    socket = io('http://127.0.0.1:1024?token=' + token, {
        autoConnect: false
    });
    socket.open();
    socket.on('open', socketOpen); //连接登录
    socket.on('disconnect', socketClose); //连接断开
}
function socketClose(reason) { //主动或被动关闭socket
    console.log(reason)
    localStorage.removeItem("token")
}
function socketOpen(data) { //socket开启
    if (!data.result) { //当服务端找到相同id时跳出连接
        console.log(data.msg)
        return;
    }
createChatList(data) //创建用户列表
    localStorage.setItem('token', data.token)
    login.hidden = true
    chatBox.hidden = false
    videoChat.hidden = true
    myName.textContent = localStorage.token
}

之后在socket中添加几个事件监听

    socket.on('dataChange', createChatList); //新增人员
    socket.on('inviteVideoHandler', inviteVideoHandler); //被邀请视频
    socket.on('askVideoHandler', askVideoHandler); //视频邀请结果
    socket.on('ice', getIce); //从服务端接收ice
    socket.on('offer', getOffer); //从服务端接收offer
    socket.on('answer', getAnswer); //从服务端接收answer
    socket.on('break', stopVideoStream) //挂断视频通话

若用户接到对方邀请时,弹出确认框,并将结果返回给对方

function inviteVideoHandler(data) { //当用户被邀请时执行
    let allow = 0
    if (isCalling) {
        allow = -1 //正在通话
    } else {
        let res = confirm(data.msg);
        if (res) {
            allow = 1
            startVideoChat(data.token) //用户点击同意后开始初始化视频聊天
            localStorage.setItem('roomNo', data.roomNo) //将房间号保存
        }
    }
    socket.emit('askVideo', {
        myId: localStorage.token,
        otherId: data.token,
        type: 'askVideo',
        allow
    });
}

当收到返回邀请结果时,后端创建视频聊天房间后,开始初始化聊天室

function askVideoHandler(data) { //获取被邀请用户的回复
    console.log(data.msg)
    if (data.allow == -1) return //通话中
    if (data.allow) {
        localStorage.setItem('roomNo', data.roomNo) //将房间号保存
        startVideoChat(data.token)
    }
}

当用户挂断时

function breakVideoConnect(e) {
    console.log(localStorage.getItem('roomNo'))
    socket.emit('_break', {
        roomNo: localStorage.getItem('roomNo')
    });
}

完整的socket.js

let socket //供其他页面调用
function initSocket(token) {//获取到用户输入的id并传到服务端
    socket = io('http://127.0.0.1:1024?token=' + token, {
        autoConnect: false
    });
    socket.open();
    socket.on('open', socketOpen); //连接登录
    socket.on('disconnect', socketClose); //连接断开
    socket.on('dataChange', createChatList); //新增人员
    socket.on('inviteVideoHandler', inviteVideoHandler); //被邀请视频
    socket.on('askVideoHandler', askVideoHandler); //视频邀请结果
    socket.on('ice', getIce); //从服务端接收ice
    socket.on('offer', getOffer); //从服务端接收offer
    socket.on('answer', getAnswer); //从服务端接收answer
    socket.on('break', stopVideoStream) //挂断视频通话
}
function socketClose(reason) { //主动或被动关闭socket
    console.log(reason)
    localStorage.removeItem("token")
}
function socketOpen(data) { //socket开启
    if (!data.result) { //当服务端找到相同id时跳出连接
        console.log(data.msg)
        return;
    }
    createChatList(data) //创建用户列表
    localStorage.setItem('token', data.token)
    login.hidden = true
    chatBox.hidden = false
    videoChat.hidden = true
    myName.textContent = localStorage.token
}
function inviteVideoHandler(data) { //当用户被邀请时执行
    let allow = 0
    if (isCalling) {
        allow = -1 //正在通话
    } else {
        let res = confirm(data.msg);
        if (res) {
            allow = 1
            startVideoChat(data.token) //用户点击同意后开始初始化视频聊天
            localStorage.setItem('roomNo', data.roomNo) //将房间号保存
        }
    }
    socket.emit('askVideo', {
        myId: localStorage.token,
        otherId: data.token,
        type: 'askVideo',
        allow
    });
}
function askVideoHandler(data) { //获取被邀请用户的回复
    console.log(data.msg)
    if (data.allow == -1) return //通话中
    if (data.allow) {
        localStorage.setItem('roomNo', data.roomNo) //将房间号保存
        startVideoChat(data.token)
    }
}
function breakVideoConnect(e) {
    console.log(localStorage.getItem('roomNo'))
    socket.emit('_break', {
        roomNo: localStorage.getItem('roomNo')
    });
}


完整的userList.js(创建用户在线列表,添加邀请事件,初始化聊天室)

function createChatList(data) { //新建用户列表
    console.log(data.msg)
    let userData = data.userIds
    let userList = document.querySelector('#userList')
    if (userList) {
        userList.remove()
        userList = null
    }
    userList = createEle('ul', {}, {
        id: 'userList',
        className: 'userList'
    })
    chatBox.appendChild(userList)
    for (let key in userData) {
        if (userData[key] != localStorage.token) {
            var li = createEle('li', {}, {
                textContent: userData[key]
            })
            li.addEventListener('click', videoStart)
            userList.appendChild(li)
        }
    }
}
function createEle(ele, style, attribute) { //新增标签,设置属性及样式
    let element = document.createElement(ele)
    if (style) {
        for (let key in style) {
            element.style[key] = style[key];
        }
    }
    if (attribute) {
        for (let key in attribute) {
            element[key] = attribute[key];
        }
    }
    return element
}
function videoStart(e) { //用户点击列表某个用户时发送邀请至服务端
    socket.emit('inviteVideo', {
        myId: localStorage.token,
        otherId: this.textContent,
        type: 'inviteVideo'
    });
}
function startVideoChat(otherId) { //初始化视频聊天
    videoChat.hidden = false
    login.hidden = true
    chatBox.hidden = true
    localStorage.setItem('otherId', otherId) //将对方的id保存
    startVideoStream()
}

video.js


初始化媒体对象,并将Stream存到全局,这里由于navigator.mediaDevices.getUserMedia是异步方法,需要同步执行,先获取stream,然后进行后续操作

async function createMedia() { //同步创建本地媒体流
    if (!stream) {
        stream = await navigator.mediaDevices.getUserMedia({
            audio: true,
            video: true
        })
    }
    console.log(stream)
    let video = document.querySelector('#myVideo');
    video.srcObject = stream; //将媒体流输出到本地video以显示自己
    video.onloadedmetadata = function (e) {
        video.play();
    };
    createPeerConnection()
}

创建stream后,初始化RTCPeerConnection,用于建立视频连接,同样需要同步获取,并且在peer获取之后发送offer给对方

async function createPeerConnection() { //同步初始化描述文件并添加事件
    if (!peer) {
        peer = new RTCPeerConnection()
    }
    await stream.getTracks().forEach(async track => {
        await peer.addTrack(track, stream); //将本地流附属至peer中
    });
    // await peer.addStream(stream); //旧方法(将本地流附属至peer中)
    peer.addEventListener('addstream', setVideo) //当peer收到其他流时显示另一个video以显示对方
    peer.addEventListener('icecandidate', sendIce) //获取到candidate时,将其发送至服务端,传至对方
    peer.addEventListener('negotiationneeded', sendOffer) //双方约定的协商被完成时才触发该方法
}

当收到对方发送过来的stream时,即触发addstream事件时,通过setVideo将对方的视频流放到本地video中

function setVideo(data) { //播放对方的视频流
    console.log(data.stream)
    let back = document.getElementById('back')
    back.hidden = false //显示挂断按钮
    back.addEventListener('click', breakVideoConnect) //挂断事件
    isCalling = true //正在通话
    let video = document.querySelector('#otherVideo');
    video.srcObject = data.stream;
    video.onloadedmetadata = function (e) {
        video.play();
    };
}

创建offer,保存本地offer,并发送offer给对方

async function sendOffer() { //同步发送offer到服务端,发送给对方
    let offer = await peer.createOffer();
    await peer.setLocalDescription(offer); //peer本地附属offer
    socket.emit('_offer', {
        streamData: offer
    });
}

收到对方的offer后,保存远程offer,但是这里有一个小问题,如果peer还没有创建好,也就是如果对方先创建,就会马上发offer过来,这时我们这边的peer可能还没有创建成功,如果直接调用setRemoteDescription的话会报错,所以可以用try  catch来调用,或使用if(!peer) return的方式运行

async function getOffer(data) { //接收到offer后,返回answer给对方
    await peer.setRemoteDescription(data.streamData); //peer远程附属offer
    sendAnswer()
}
//优化后
async function getOffer(data) { //接收到offer后,返回answer给对方
    if (!peer) return //等待对方响应,也可以用try catch
    await peer.setRemoteDescription(data.streamData); //peer远程附属offer
    sendAnswer()
}

创建answer,保存本地answer,发送answer给对方

async function sendAnswer() {
    let answer = await peer.createAnswer();
    await peer.setLocalDescription(answer); //peer附属本地answer
    socket.emit('_answer', {
        streamData: answer
    });
}

接收到answer时,保存本地answer

async function getAnswer(data) { //接收到answer后,peer远程附属answer
    await peer.setRemoteDescription(data.streamData);
}

peer触发icecandidate事件时,即本地触发过setLocalDescription时,也就是将本地offer和本地answer保存时,触发方法

function sendIce(e) { //setLocalDescription触发时,发送ICE给对方
    if (e.candidate) {
        socket.emit('_ice', {
            streamData: e.candidate
        });
    }
}

接收对方的ICE,但是这里有一个和上面一样的小问题如果在ICE事件中,peer还没有创建好,也就是如果对方先创建,就会马上发offer过来,这时我们这边的peer可能还没有创建成功,如果直接调用addIceCandidate的话会报错,所以可以用try  catch来调用,或使用if(!peer) return的方式运行

async function getIce(data) { //获取对方的ICE
    var candidate = new RTCIceCandidate(data.streamData)
    await peer.addIceCandidate(candidate)
}
//优化后
async function getIce(data) { //获取对方的ICE
    if (!peer) return //等待对方响应,也可以用try catch
    var candidate = new RTCIceCandidate(data.streamData)
    await peer.addIceCandidate(candidate)
}

遇到的问题

最后是挂断的方法,这里有个小问题,当挂断时,原来的stream无法删除,导致摄像头虽然没有调用,但是导航栏仍然会有摄像头图标(没有真正关闭),下一次打开时会传输前面的流(叠加),网上没有解决方式,如果有知道的同学,希望能补充优化,感谢

function stopVideoStream(data) { //停止传输视频流
    console.log(data.msg)
    stream.getTracks().forEach(async function (track) { //这里得到视频或音频对象
        await track.stop();
        await stream.removeTrack(track)
        stream = null
    })
    peer.close();
    peer = null;
    isCalling = false
    videoChat.hidden = true
    login.hidden = true
    chatBox.hidden = false
}

优化后完整的video.js

var stream, peer, isCalling = false //初始化要发送的流,和描述文件,通话状态
function startVideoStream(e) { //开始传输视频流
    createMedia()
}
function stopVideoStream(data) { //停止传输视频流
    console.log(data.msg)
    stream.getTracks().forEach(async function (track) { //这里得到视频或音频对象
        await track.stop();
        await stream.removeTrack(track)
        stream = null
    })
    peer.close();
    peer = null;
    isCalling = false
    videoChat.hidden = true
    login.hidden = true
    chatBox.hidden = false
}
async function createMedia() { //同步创建本地媒体流
    if (!stream) {
        stream = await navigator.mediaDevices.getUserMedia({
            audio: true,
            video: true
        })
    }
    console.log(stream)
    let video = document.querySelector('#myVideo');
    video.srcObject = stream; //将媒体流输出到本地video以显示自己
    video.onloadedmetadata = function (e) {
        video.play();
    };
    createPeerConnection()
}
async function createPeerConnection() { //同步初始化描述文件并添加事件
    if (!peer) {
        peer = new RTCPeerConnection()
    }
    await stream.getTracks().forEach(async track => {
        await peer.addTrack(track, stream); //将本地流附属至peer中
    });
    // await peer.addStream(stream); //旧方法(将本地流附属至peer中)
    peer.addEventListener('addstream', setVideo) //当peer收到其他流时显示另一个video以显示对方
    peer.addEventListener('icecandidate', sendIce) //获取到candidate时,将其发送至服务端,传至对方
    peer.addEventListener('negotiationneeded', sendOffer) //双方约定的协商被完成时才触发该方法
}
function setVideo(data) { //播放对方的视频流
    console.log(data.stream)
    let back = document.getElementById('back')
    back.hidden = false //显示挂断按钮
    back.addEventListener('click', breakVideoConnect) //挂断事件
    isCalling = true //正在通话
    let video = document.querySelector('#otherVideo');
    video.srcObject = data.stream;
    video.onloadedmetadata = function (e) {
        video.play();
    };
}
async function sendOffer() { //同步发送offer到服务端,发送给对方
    let offer = await peer.createOffer();
    await peer.setLocalDescription(offer); //peer本地附属offer
    socket.emit('_offer', {
        streamData: offer
    });
}
async function getOffer(data) { //接收到offer后,返回answer给对方
    if (!peer) return //等待对方响应,也可以用try catch
    await peer.setRemoteDescription(data.streamData); //peer远程附属offer
    sendAnswer()
}
async function sendAnswer() {
    let answer = await peer.createAnswer();
    await peer.setLocalDescription(answer); //peer附属本地answer
    socket.emit('_answer', {
        streamData: answer
    });
}
async function getAnswer(data) { //接收到answer后,peer远程附属answer
    await peer.setRemoteDescription(data.streamData);
}
function sendIce(e) { //setLocalDescription触发时,发送ICE给对方
    if (!e || !e.candidate) return
    socket.emit('_ice', {
        streamData: e.candidate
    });
}
async function getIce(data) { //获取对方的ICE
    if (!peer) return //等待对方响应,也可以用try catch
    var candidate = new RTCIceCandidate(data.streamData)
    await peer.addIceCandidate(candidate)
}

服务端

后端部分同样使用socketio进行通信

首先在npm初始化后下载express,socket.io

npm i express --save-dev
npm i socket.io --save-dev

之后引入至server.js中

const express = require('express')
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);

并监听1024端口

server.listen(1024, function () {
    console.log('Socket Open')
});

配置socket

给socket添加一些事件

io.on('connect', socket => {
    let {
        token
    } = socket.handshake.query
    socket.on('disconnect', (exit) => { //socket断开
        delFormList(token) //清除用户
        broadCast(socket, token, 'leave') //广播给其他用户
    })
});

这样,我们最简单的一个socket就搭好了


完整的server.js

const express = require('express')
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);
let userList = {} //用户列表,所有连接的用户
let userIds = {} //用户id列表,显示到前端
let roomList = {} //房间列表,视频聊天
io.on('connect', socket => {
    let {
        token
    } = socket.handshake.query
    socket.on('disconnect', (exit) => { //socket断开
        delFormList(token) //清除用户
        broadCast(socket, token, 'leave') //广播给其他用户
    })
    socket.on('inviteVideo', inviteVideoHandler) //邀请用户
    socket.on('askVideo', inviteVideoHandler); //回应用户是否邀请成功
    if (userList[token]) { //找到相同用户名就跳出函数
        socket.emit('open', {
            result: 0,
            msg: token + '已存在'
        });
        socket.disconnect()
        return;
    }
    addToList(token, socket) //用户连接时,添加到userList
    broadCast(socket, token, 'enter') //广告其他用户,有人加入
});
function addToList(key, item) { //添加到userList
    item.emit('open', {
        result: 1,
        msg: '你已加入聊天',
        userIds,
        token: key
    });
    userList[key] = item
    userIds[key] = key
}
function delFormList(key) { //断开时,删除用户
    delete userList[key];
    delete userIds[key]
}
function broadCast(target, token, type) { //广播功能
    let msg = '加入聊天'
    if (type !== 'enter') {
        msg = '离开聊天'
    }
    target.broadcast.emit('dataChange', {
        result: 1,
        msg: token + msg,
        userIds
    });
}
function inviteVideoHandler(data) { //邀请方法
    let {
        myId,
        otherId,
        type,
        allow
    } = data, msg = '邀请你进入聊天室', event = 'inviteVideoHandler', roomNo = otherId //默认房间号为邀请方id
    if (type == 'askVideo') {
        event = 'askVideoHandler'
        if (allow == 1) {
            addRoom(myId, otherId)
            roomNo = myId //保存房间号
            msg = '接受了你的邀请'
        } else if (allow == -1) {
            msg = '正在通话'
        } else {
            msg = '拒绝了你的邀请'
        }
    }
    userList[otherId].emit(event, {
        msg: myId + msg,
        token: myId,
        allow,
        roomNo
    });
}
async function addRoom(myId, otherId) { //用户同意后添加到视频聊天室,只做1对1聊天功能
    roomList[myId] = [userList[myId], userList[otherId]]
    startVideoChat(roomList[myId])
}
function startVideoChat(roomItem) { //视频聊天初始化
    for (let i = 0; i < roomItem.length; i++) {
        roomItem[i].room = roomItem
        roomItem[i].id = i
        roomItem[i].on('_ice', _iceHandler)
        roomItem[i].on('_offer', _offerHandler)
        roomItem[i].on('_answer', _answerHandler)
        roomItem[i].on('_break', _breakRoom)
    }
}
function _iceHandler(data) { //用户发送ice到服务端,服务端转发给另一个用户
    let id = this.id == 0 ? 1 : 0 //判断用户二者其一
    this.room[id].emit('ice', data);
}
function _offerHandler(data) { //用户发送offer到服务端,服务端转发给另一个用户
    let id = this.id == 0 ? 1 : 0
    this.room[id].emit('offer', data);
}
function _answerHandler(data) { //用户发送answer到服务端,服务端转发给另一个用户
    let id = this.id == 0 ? 1 : 0
    this.room[id].emit('answer', data);
}
function _breakRoom(data) { //挂断聊天
    for (let i = 0; i < roomList[data.roomNo].length || 0; i++) {
        roomList[data.roomNo][i].emit('break', {
            msg: '聊天挂断'
        });
    }
}
server.listen(1024, function () {
    console.log('Socket Open')
});


实现效果

分别在我方发送stream之前打印stream,收到对方stream后打印stream,我们会发现,双方的stream互换了位置,也就是说整个媒体对象进行了交换

2.png2.png



和同事在两台电脑上测试效果

1.gif


注意:

前端项目必须运行在本地或者https服务下,因为navigator.mediaDevices.getUserMedia需要运行在安全模式下,用户第一次使用需要授权摄像头或音频权限,所以双方电脑需要有相关功能


相关文章
|
2月前
|
监控 算法 JavaScript
基于 Node.js Socket 算法搭建局域网屏幕监控系统
在数字化办公环境中,局域网屏幕监控系统至关重要。基于Node.js的Socket算法实现高效、稳定的实时屏幕数据传输,助力企业保障信息安全、监督工作状态和远程技术支持。通过Socket建立监控端与被监控端的数据桥梁,确保实时画面呈现。实际部署需合理分配带宽并加密传输,确保信息安全。企业在使用时应权衡利弊,遵循法规,保障员工权益。
52 7
|
3月前
|
Web App开发 JSON JavaScript
Node.js 中的中间件机制与 Express 应用
Node.js 中的中间件机制与 Express 应用
|
3月前
|
缓存 负载均衡 JavaScript
构建高效后端服务:Node.js与Express框架实践
在数字化时代的浪潮中,后端服务的重要性不言而喻。本文将通过深入浅出的方式介绍如何利用Node.js及其强大的Express框架来搭建一个高效的后端服务。我们将从零开始,逐步深入,不仅涉及基础的代码编写,更会探讨如何优化性能和处理高并发场景。无论你是后端新手还是希望提高现有技能的开发者,这篇文章都将为你提供宝贵的知识和启示。
|
3月前
|
JavaScript
使用node.js搭建一个express后端服务器
Express 是 Node.js 的一个库,用于搭建后端服务器。本文将指导你从零开始构建一个简易的 Express 服务器,包括项目初始化、代码编写、服务启动与项目结构优化。通过创建 handler 和 router 文件夹分离路由和处理逻辑,使项目更清晰易维护。最后,通过 Postman 测试确保服务正常运行。
168 1
|
3月前
|
Web App开发 JavaScript 前端开发
探索后端开发:Node.js与Express的完美结合
【10月更文挑战第33天】本文将带领读者深入了解Node.js和Express的强强联手,通过实际案例揭示它们如何简化后端开发流程,提升应用性能。我们将一起探索这两个技术的核心概念、优势以及它们如何共同作用于现代Web开发中。准备好,让我们一起开启这场技术之旅!
93 0
|
3月前
|
Web App开发 JavaScript 前端开发
构建高效后端服务:Node.js与Express框架的实践
【10月更文挑战第33天】在数字化时代的浪潮中,后端服务的效率和可靠性成为企业竞争的关键。本文将深入探讨如何利用Node.js和Express框架构建高效且易于维护的后端服务。通过实践案例和代码示例,我们将揭示这一组合如何简化开发流程、优化性能,并提升用户体验。无论你是初学者还是有经验的开发者,这篇文章都将为你提供宝贵的见解和实用技巧。
|
3月前
|
Web App开发 JavaScript 中间件
构建高效后端服务:Node.js与Express框架的融合之道
【10月更文挑战第31天】在追求快速、灵活和高效的后端开发领域,Node.js与Express框架的结合如同咖啡遇见了奶油——完美融合。本文将带你探索这一组合如何让后端服务搭建变得既轻松又充满乐趣,同时确保你的应用能够以光速运行。
64 0
|
3月前
|
JavaScript 中间件 关系型数据库
构建高效的后端服务:Node.js 与 Express 的实践指南
在后端开发领域,Node.js 与 Express 的组合因其轻量级和高效性而广受欢迎。本文将深入探讨如何利用这一组合构建高性能的后端服务。我们将从 Node.js 的事件驱动和非阻塞 I/O 模型出发,解释其如何优化网络请求处理。接着,通过 Express 框架的简洁 API,展示如何快速搭建 RESTful API。文章还将涉及中间件的使用,以及如何结合 MySQL 数据库进行数据操作。最后,我们将讨论性能优化技巧,包括异步编程模式和缓存策略,以确保服务的稳定性和扩展性。
|
4月前
|
Web App开发 JavaScript 中间件
构建高效后端服务:Node.js与Express框架的完美结合
【10月更文挑战第21天】本文将引导你走进Node.js和Express框架的世界,探索它们如何共同打造一个高效、可扩展的后端服务。通过深入浅出的解释和实际代码示例,我们将一起理解这一组合的魅力所在,并学习如何利用它们来构建现代Web应用。
114 1
|
4月前
|
JavaScript 前端开发 中间件
探索后端技术:Node.js与Express框架的完美融合
【10月更文挑战第7天】 在当今数字化时代,Web应用已成为日常生活不可或缺的一部分。本文将深入探讨后端技术的两大重要角色——Node.js和Express框架,分析它们如何通过其独特的特性和优势,为现代Web开发提供强大支持。我们将从Node.js的非阻塞I/O和事件驱动机制,到Express框架的简洁路由和中间件特性,全面解析它们的工作原理及应用场景。此外,本文还将分享一些实际开发中的小技巧,帮助你更有效地利用这些技术构建高效、可扩展的Web应用。无论你是刚入门的新手,还是经验丰富的开发者,相信这篇文章都能为你带来新的启发和思考。

热门文章

最新文章