Instant Messaging-Anwendung, einschließlich Server, Verwaltung und Client
Jetzt bereitgestellt und online: Willkommen zum Testen des Client- und Verwaltungsterminals
Bitte ändern Sie die Standardrollen und Berechtigungen nicht nach Belieben. Seien Sie bitte freundlich und verwenden Sie keine sehr unzivilisierten Namen.
Der mit @vue/cli erstellte IM-Service-Client verwendet Vant für den UI-Teil
Dieses Projekt dient hauptsächlich dazu, die grundlegende Anwendung eines Clients zu demonstrieren (ähnlich wie WeChat). Es verfügt über Funktionen wie Registrierung, Anmeldung, Bearbeiten persönlicher Informationen, Hinzufügen von Freunden, Bewerben für den Beitritt zu einer Gruppe, Chatten usw. Es muss mit verwendet werden das Backend.
Jeder kennt das Vue Family Bucket-Projekt (ich denke, es ist in dieser Hinsicht besser als React und es gibt nicht so viele Möglichkeiten). Hier sind einige Erweiterungen:
Stellen Sie zunächst das socket.io-client-Paket vor. Sie können anhand des Namens erkennen, dass es sich um den Client-Teil von socket.io handelt.
Wenn Sie Informationen mit dem Server senden und empfangen möchten, müssen Sie zunächst eine Verbindung herstellen.
import io from 'socket.io-client';
// 首先需要链接上后端的 socket.io,query是链接的参数
this.socket = io('http://127.0.0.1:7001', {
query: {
scene: 'im',
userId: '1'
}
});
// socket.on 就是监听事件,connect就是链接上了服务端
this.socket.on('connect', () => {
console.log('socket连接成功!');
// ....
});
Nachdem die Verknüpfung in diesem Projekt erfolgreich war, begann ich, die Sitzungsliste und Nachrichtendatensätze anzufordern, um sicherzustellen, dass die Verknüpfung beim Empfang der Nachricht erfolgreich war.
Nachdem Sie eine Verbindung zum Server hergestellt haben, können Sie Informationen senden und empfangen. Es ist ganz einfach:
// 发送消息,message是自己定义的消息体
this.socket.emit('/v1/im/new-message', message);
// 有新消息收到
this.socket.on('/v1/im/new-message', message => {
// 自己进行一系列的处理
});
Das Senden und Empfangen von Nachrichten kann mit dem Backend kombiniert werden. Auch wenn das Frontend seine eigenen Nachrichten sendet, werden diese erst angezeigt, wenn es die Gruppennachricht aus dem Hintergrund empfängt. Das heißt, wenn die Verbindung getrennt wird, können seine eigenen Nachrichten nicht angezeigt werden gesendet werden.
Bei der Eingabe ist es eine reine Zeichenfolge wie [酷]
. Beim Speichern wird es durch das entsprechende Symbol ersetzt. Aus diesem Grund kann ich es nicht durch das entsprechende Symbol ersetzen im Eingabefeld. Der Chef hat mich ursprünglich darum gebeten, und ich habe es versucht, aber es stellte sich heraus, dass das Ding eine große Grube war. Heutzutage scheint es, dass nur das Eingabefeld von Weibo, mit dem ich in Kontakt gekommen bin, Symbole anstelle von Zeichenfolgen anzeigt, was auch viele Fragen aufwirft:
Aber es ist toll, Strings zu verwenden (sowohl WeChat als auch DingTalk), alle nativen Funktionen können genutzt werden und das Frontend ist einfach zu handhaben (wichtiger Punkt)
Aus dem Obigen können wir ersehen, wie bequem die Verwendung von socket.io ist. Stellen Sie zunächst eine Verbindung her, und dann können Sie Nachrichten senden und empfangen. In Kombination mit unserem IM-Szenario haben wir die folgenden Vorkehrungen getroffen:
/v1/im/new-message
Wird häufig in mobilen Apps beim Scrollen und Klicken zur Eingabe angezeigt
Wenn wir Web-Apps entwickeln, stoßen wir häufig auf ein Problem, wenn wir von einer scrollbaren Listenseite zur nächsten Detailseite wechseln und dann zur Listenseite zurückkehren. Es ist schwierig, den Status der Bildlaufleiste wiederherzustellen, und wir können uns nicht daran erinnern. Der Ort, an dem Sie eingezogen sind.
Ich habe schon viele Methoden ausprobiert:
Keine der oben genannten Lösungen ist ideal
Später habe ich Vue-Page-Stack unter Bezugnahme auf Keep-Alive entwickelt, um den Stapel von Vue-Seiten, also den virtuellen Dom in Vue, zu speichern, aber das Problem mit der Bildlaufleiste wurde immer noch nicht gelöst. Da das virtuelle DOM den Bildlaufstatus jeder Komponente nicht aufzeichnet, kann er nicht wiederhergestellt werden.
Als ich Cube-UI verwendete, stellte ich fest, dass die Verwendung des Bildlaufcontainers in dieser Komponentenbibliothek die Bildlaufleiste wiederherstellen kann. Außerdem stellte ich fest, dass dies der Grund für die bessere Bildlaufleiste von Lehrer Huang Yi war.
Als ich mir den Quellcode von bs ansah, stellte ich fest, dass die interne Implementierung von bs kein natives Scrollen ist, sondern einige Scroll-Informationen aufzeichnet, von denen die wichtigsten x und y sind, die die Scroll-Werte sind, die ich implementiert habe Scrollverhalten durch Transformationsimplementierung: Beim Wiederherstellen des virtuellen DOM werden auch die Scrollinformationen wiederhergestellt.
Am Ende kann vue-page-stack + bs die Wiederherstellung des Seitenstapels perfekt realisieren.
Dieses Problem tritt hauptsächlich bei Abfragen wie Nachrichtendatensätzen auf. Dieses Problem tritt auch bei kleinen Programmen auf.
Die meisten Scroll-Szenen werden per Pull-Up geladen. Nach dem Laden werden die Daten zur Liste hinzugefügt, und Vue und andere sind für das Rendern des neu geladenen Inhalts verantwortlich Zum Laden ziehen.
In unserem Szenario müssen wir jedoch beim Durchsuchen der Nachrichtendatensätze in einer bestimmten Sitzung nach unten ziehen, um weitere Nachrichten zu laden. Nach dem Laden müssen wir weiter nach unten ziehen und langsam scrollen, um sie anzuzeigen. Dies führt zu einem sehr ernsten Problem: Der Inhalt, der nach dem Laden des Dropdown-Menüs angezeigt wird, befindet sich über dem Bildlaufbereich. Wenn keine Verarbeitung erfolgt, springt er nach dem Laden direkt an den Anfang des neu geladenen Inhalts, da dies bei der Bildlaufentfernung nicht der Fall ist geändert, was zu einem Problem führt, das nicht mit dem vereinbar ist, was wir erreichen wollen.
Ich habe auch über viele Methoden nachgedacht, darunter die Berechnung der Gesamtlänge der neu hinzugefügten Nachricht und deren anschließendes Zurücksetzen. Allerdings sind Typ und Höhe der Nachricht inkonsistent und es treten Fehler bei der Berechnung auf.
Die endgültige Lösung, die mir einfiel, war:
Die beiden oben genannten Probleme spiegeln sich im Bild unten wider, und der Effekt ist wie folgt in Ordnung:
Da die Bandbreite meines Servers zu schwach ist, möchte ich so oft wie möglich CDN verwenden und einige Erweiterungen verwenden, die den Server viel weniger belasten. Dieser Teil des Inhalts gehört zum Webpack-Teil.
// index.html
<script src="https://api.map.baidu.com/getscript?v=3.0&ak=ZHjk59sSOpM1eNWgNWyj9zpyAFTHdL5z"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue-router.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuex.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/vant.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/browser/index.js"></script>
// vue.config.js
externals: {
BMap: 'BMap',
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
vant: 'vant',
xgplayer: 'Player'
}
Dies führte auch dazu, dass mein https anzeigte, dass es aufgrund von SameSite unsicher sei, aber es gab keine andere Möglichkeit.
Die Front-End-Ressourcen werden von nginx verwaltet, das als Reverse-Proxy fungiert. Statische Dateien wie js und css werden einen Monat lang nicht zwischengespeichert und mit gzip komprimiert. API, /public und /socket .io müssen an den Server weitergeleitet werden: http://127.0.0.1:7001. Sie müssen auf die Einstellung http achten und ein Upgrade auf websocket durchführen.
Das https-Zertifikat ist ein kostenlos unter https://freessl.cn/ beantragtes und auf nginx konfiguriertes Zertifikat
server {
listen 80;
server_name im-client.hezf.online;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location / {
root /data/static/im-client;
index index.html;
try_files $uri $uri/ /index.html;
}
location ~* .(html)$ {
root /data/static/im-client;
access_log off;
add_header Cache-Control no-store;
}
location /static {
access_log off;
root /data/static/im-client;
gzip on;
gzip_buffers 32 8K;
gzip_comp_level 6;
gzip_min_length 100;
gzip_types application/javascript text/css text/xml;
gzip_disable "MSIE [1-6]."; #配置禁用gzip条件,支持正则。此处表示ie6及以下不启用gzip(因为ie低版本不支持)
gzip_vary on;
add_header Cache-Control max-age=2592000;
}
location /api {
proxy_pass http://127.0.0.1:7001;
proxy_connect_timeout 3;
proxy_send_timeout 30;
proxy_read_timeout 30;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto http;
proxy_set_header X-NginX-Proxy true;
client_max_body_size 100m;
}
location /public {
proxy_pass http://127.0.0.1:7001;
proxy_connect_timeout 3;
proxy_send_timeout 30;
proxy_read_timeout 30;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 100m;
}
location /socket.io {
proxy_pass http://127.0.0.1:7001;
proxy_connect_timeout 3;
proxy_send_timeout 30;
proxy_read_timeout 30;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 100m;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
server {
listen 443 ssl;
server_name im-client.hezf.online;
ssl_certificate /etc/nginx/conf.d/hezf-online/im-client.hezf.online_chain.crt;
ssl_certificate_key /etc/nginx/conf.d/hezf-online/im-client.hezf.online_key.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #按照这个协议配置
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4:!DH:!DHE;
ssl_prefer_server_ciphers on;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location / {
root /data/static/im-client;
index index.html;
try_files $uri $uri/ /index.html;
}
location ~* .(html)$ {
root /data/static/im-client;
access_log off;
add_header Cache-Control no-store;
}
location /static {
access_log off;
root /data/static/im-client;
gzip on;
gzip_buffers 32 8K;
gzip_comp_level 6;
gzip_min_length 100;
gzip_types application/javascript text/css text/xml;
gzip_disable "MSIE [1-6]."; #配置禁用gzip条件,支持正则。此处表示ie6及以下不启用gzip(因为ie低版本不支持)
gzip_vary on;
add_header Cache-Control max-age=2592000;
}
location /api {
proxy_pass http://127.0.0.1:7001;
proxy_connect_timeout 3;
proxy_send_timeout 30;
proxy_read_timeout 30;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-NginX-Proxy true;
proxy_redirect off;
client_max_body_size 100m;
}
location /public {
proxy_pass http://127.0.0.1:7001;
proxy_connect_timeout 3;
proxy_send_timeout 30;
proxy_read_timeout 30;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 100m;
}
location /socket.io {
proxy_pass http://127.0.0.1:7001;
proxy_connect_timeout 3;
proxy_send_timeout 30;
proxy_read_timeout 30;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 100m;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}