Aplicación de mensajería instantánea, incluyendo servidor, gestión y cliente.
Ahora implementado y en línea, bienvenido a experimentar el terminal de administración y cliente
No cambie los roles y permisos predeterminados a voluntad. Sea amable y no utilice nombres muy incivilizados.
Construido con @vue/cli, el cliente de servicio de mensajería instantánea usa Vant para la parte de la interfaz de usuario
Este proyecto es principalmente para demostrar la aplicación básica de un cliente (similar a WeChat. Tiene funciones como registro, inicio de sesión, edición de información personal, agregar amigos, solicitar unirse a un grupo, chatear, etc. Debe usarse con). el backend.
Todo el mundo está familiarizado con el proyecto Vue Family Bucket (creo que es mejor que React en este sentido y no hay tantas opciones). Aquí hay algunas extensiones:
Primero introduzca el paquete socket.io-client. Puede saber por el nombre que es la parte del cliente de socket.io. Aquí está el uso:
Si desea enviar y recibir información con el servidor, primero debe establecer un enlace.
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连接成功!');
// ....
});
Después de que el enlace tuvo éxito en este proyecto, comencé a solicitar la lista de sesiones y los registros de mensajes, para asegurarme de que el enlace fuera exitoso al recibir el mensaje.
Después de conectarte al servidor, podrás enviar y recibir información. Es muy sencillo:
// 发送消息,message是自己定义的消息体
this.socket.emit('/v1/im/new-message', message);
// 有新消息收到
this.socket.on('/v1/im/new-message', message => {
// 自己进行一系列的处理
});
El envío y la recepción de mensajes se pueden combinar con el backend. Incluso si el frontend envía sus propios mensajes, no se mostrará hasta que reciba el mensaje grupal desde el fondo, es decir, si el enlace está desconectado, sus propios mensajes no pueden. ser enviado.
Al ingresar, es una cadena pura como [酷]
Al guardar, también es una cadena. Cuando se muestra, se reemplaza con el ícono correspondiente. He aquí por qué no puedo encontrar una manera de reemplazarlo con el ícono correspondiente. en el cuadro de entrada. El jefe originalmente me pidió que hiciera esto y lo intenté, pero resultó que esto era un gran problema. Hoy en día, parece que sólo el cuadro de entrada de Weibo con el que he entrado en contacto muestra iconos en lugar de cadenas, lo que también genera muchas preguntas:
Pero es genial usar cadenas (tanto WeChat como DingTalk), se pueden usar todas las funciones nativas y la interfaz es fácil de manejar (punto clave)
De lo anterior, podemos ver lo conveniente que es usar socket.io. Primero establezca un enlace y luego podrá enviar y recibir mensajes. En combinación con nuestro escenario de mensajería instantánea, hicimos los siguientes arreglos:
/v1/im/new-message
Comúnmente visto en aplicaciones móviles al desplazarse y hacer clic para ingresar
Cuando desarrollamos aplicaciones web, a menudo encontramos un problema cuando pasamos de una página de lista desplazable a la siguiente página de detalles y luego volvemos a la página de lista, es difícil restaurar el estado de la barra de desplazamiento y no podemos recordarlo. La ubicación cuando te mudaste.
He probado muchos métodos antes:
Ninguna de las soluciones anteriores es ideal.
Más tarde, desarrollé vue-page-stack con referencia a keep-alive para guardar la pila de páginas Vue, es decir, el dom virtual en Vue, pero el problema de la barra de desplazamiento aún no se resolvió. Debido a que el DOM virtual no registra el estado de desplazamiento de cada componente, no se puede restaurar.
Cuando usé cube-ui, descubrí que usar el contenedor de desplazamiento en esta biblioteca de componentes puede restaurar la barra de desplazamiento. Además, descubrí que esa era la razón por la que el maestro Huang Yi se desplazaba mejor.
Al observar el código fuente de bs, descubrí que la implementación interna de bs no es un desplazamiento nativo, sino que registra cierta información de desplazamiento, la más importante de las cuales son x e y, que son los valores de desplazamiento que he implementado. Comportamientos de desplazamiento a través de la implementación de transformación, al restaurar el DOM virtual, la información de desplazamiento también se restaura.
Al final, vue—page-stack + bs puede realizar perfectamente la restauración de la pila de páginas.
Este problema se ve principalmente en consultas como registros de mensajes. También encontrará este problema en programas pequeños.
La mayoría de las escenas de desplazamiento son cargas pull-up. Durante la carga pull-up, el contenido cargado aparece debajo del área de desplazamiento. Después de la carga, agregamos los datos a la lista y Vue y otros son responsables de representar el contenido recién cargado. para cargar. Tire para continuar desplazándose.
Pero en nuestro escenario, al explorar los registros de mensajes en una determinada sesión, debemos bajar para cargar más mensajes. Después de cargar, continuar bajando y desplazándonos lentamente para verlos. Esto conduce a un problema muy grave: el contenido que aparece después de la carga del menú desplegable está por encima del área de desplazamiento. Si no se realiza ningún procesamiento, saltará directamente a la parte superior del contenido recién cargado después de la carga, porque la distancia de desplazamiento no ha aumentado. cambiado, lo que causa un problema, que es inconsistente con lo que queremos lograr.
También pensé en muchos métodos, incluido calcular la longitud total del mensaje recién agregado y luego revertirlo. Sin embargo, el tipo y la altura del mensaje son inconsistentes y habrá errores en el cálculo.
La solución final que pensé fue:
Los dos problemas anteriores se reflejan en la siguiente imagen y el efecto es correcto, de la siguiente manera:
Dado que el ancho de banda de mi servidor es demasiado débil, quiero usar CDN tanto como sea posible y algunas extensiones, lo que ejercerá mucha menos presión sobre el servidor. Esta parte del contenido pertenece a la parte del paquete web.
// 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'
}
Esto también provocó que mi https mostrara que no era seguro debido a SameSite, pero no había otra manera.
Los recursos de front-end se administran mediante nginx, que actúa como un proxy inverso. index.html no se almacena en caché en el front-end. Los archivos estáticos como js y css se almacenan en caché y se comprimen con gzip durante un mes, y el resto /api. , /public y /socket .io deben reenviarse al servidor. Mi servidor se ejecuta en http://127.0.0.1:7001. Debe prestar atención a la configuración de http y se puede actualizar a websocket.
El certificado https es un certificado que se solicita de forma gratuita en https://freessl.cn/ y se configura en nginx.
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 {
}
}