Приложение для обмена мгновенными сообщениями, включая сервер, управление и клиент
Теперь развернуто и подключено к сети, добро пожаловать в клиент и терминал управления.
Пожалуйста, не меняйте роли и разрешения по умолчанию по своему желанию. Будьте добры и не используйте очень нецивилизованные имена.
Клиент службы обмена мгновенными сообщениями, созданный с использованием @vue/cli, использует Vant для части пользовательского интерфейса.
Этот проект в основном предназначен для демонстрации базового приложения клиента (аналогично WeChat). Он имеет такие функции, как регистрация, вход в систему, редактирование личной информации, добавление друзей, подача заявки на вступление в группу, общение в чате и т. д. Его необходимо использовать с. бэкэнд.
Все знакомы с проектом Vue Family Bucket (думаю, он в этом плане лучше, чем React, да и вариантов не так уж и много. Вот некоторые расширения):
Сначала представьте пакет socket.io-client. По названию вы можете понять, что это клиентская часть socket.io. Вот его использование:
Если вы хотите отправлять и получать информацию с сервера, вам необходимо сначала установить связь.
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连接成功!');
// ....
});
После того, как ссылка была успешной в этом проекте, я начал запрашивать список сеансов и записи сообщений, чтобы убедиться, что ссылка была успешной при получении сообщения.
После подключения к серверу вы можете отправлять и получать информацию. Это очень просто:
// 发送消息,message是自己定义的消息体
this.socket.emit('/v1/im/new-message', message);
// 有新消息收到
this.socket.on('/v1/im/new-message', message => {
// 自己进行一系列的处理
});
Отправку и получение сообщений можно объединить с бэкендом. Даже если фронтенд отправляет свои собственные сообщения, он не будет отображаться до тех пор, пока не получит групповое сообщение из фона. То есть, если ссылка отключена, его собственные сообщения не смогут. быть отправлено.
При вводе это чистая строка типа [酷]
. При сохранении она также является строкой. При отображении она заменяется соответствующим значком. Вот почему я не могу найти способ заменить ее соответствующим значком. в поле ввода. Начальник изначально попросил меня это сделать, и я попробовал, но оказалось, что это большая яма. Сейчас кажется, что только поле ввода Weibo, с которым я столкнулся, отображает значки вместо строк, что также вызывает множество вопросов:
Но использовать строки (как WeChat, так и DingTalk) — это здорово, можно использовать все встроенные функции, а интерфейс прост в обращении (ключевой момент).
Из вышесказанного мы видим, насколько удобно использовать Socket.io. Сначала установите ссылку, а затем вы сможете отправлять и получать сообщения. В сочетании с нашим сценарием обмена мгновенными сообщениями мы предприняли следующие меры:
/v1/im/new-message
Обычно встречается в мобильных приложениях при прокрутке и нажатии для входа.
Когда мы разрабатываем веб-приложения, мы часто сталкиваемся с проблемой. Когда мы переходим от прокручиваемой страницы списка к следующей странице сведений, а затем возвращаемся к странице списка, нам трудно восстановить состояние полосы прокрутки и мы не можем его запомнить. Место, где вы въехали.
Раньше я пробовал много методов:
Ни одно из вышеперечисленных решений не является идеальным
Позже я разработал vue-page-stack со ссылкой на Keep-Alive для сохранения стека страниц Vue, то есть виртуального dom во Vue, но проблема с полосой прокрутки так и не была решена. Поскольку виртуальный DOM не записывает статус прокрутки каждого компонента, его невозможно восстановить.
Когда я использовал Cube-UI, я обнаружил, что использование контейнера прокрутки в этой библиотеке компонентов может восстановить полосу прокрутки. Кроме того, я обнаружил, что это было причиной лучшей прокрутки учителя Хуан И.
Посмотрев исходный код bs, я обнаружил, что внутренняя реализация bs не является встроенной прокруткой, а записывает некоторую информацию о прокрутке, наиболее важными из которых являются x и y, которые представляют собой набор значений прокрутки, которые я реализовал. поведение прокрутки посредством преобразования. При восстановлении виртуального DOM также восстанавливается информация о прокрутке.
В конце концов, vue—page-stack + bs прекрасно может реализовать восстановление стека страниц.
Эта проблема чаще всего проявляется в таких запросах, как записи сообщений. Вы также можете столкнуться с этой проблемой в небольших программах.
Большинство сцен с прокруткой загружаются по запросу. Во время загрузки по запросу загруженный контент появляется под областью прокрутки. После загрузки мы добавляем данные в список, а за рендеринг вновь загруженного контента отвечают Vue и другие. для загрузки. Потяните, чтобы продолжить прокрутку.
Но в нашем сценарии при просмотре записей сообщений в определенном сеансе нам нужно потянуть вниз, чтобы загрузить больше сообщений. После загрузки продолжайте протягивать вниз и медленно прокручивать для просмотра. Это приводит к очень серьезной проблеме: контент, который появляется после загрузки раскрывающегося списка, находится над областью прокрутки. Если никакая обработка не выполняется, после загрузки он перейдет прямо к вершине вновь загруженного контента, поскольку расстояние прокрутки не изменилось. изменилось, что вызывает проблему, которая не соответствует тому, чего мы хотим достичь.
Я также думал о многих методах, включая вычисление общей длины вновь добавленного сообщения и последующий его откат. Однако тип и высота сообщения не соответствуют друг другу, и в расчетах будут ошибки.
Окончательное решение, о котором я думал, было:
Две вышеуказанные проблемы отражены на рисунке ниже, и эффект в порядке:
Поскольку пропускная способность моего сервера слишком слаба, я хочу использовать CDN как можно чаще и использовать некоторые расширения, которые будут оказывать гораздо меньшую нагрузку на сервер. Эта часть контента относится к части веб-пакета.
// 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'
}
Это также привело к тому, что мой https показал, что это небезопасно из-за SameSite, но другого пути не было.
Ресурсы внешнего интерфейса управляются с помощью nginx, который действует как обратный прокси-сервер. index.html не кэшируется на внешнем интерфейсе. Статические файлы, такие как js и css, строго кэшируются и сжимаются gzip в течение месяца, а остальные файлы /api. , /public и /socket .io необходимо перенаправить на сервер. Мой сервер работает по адресу http://127.0.0.1:7001. Вам необходимо обратить внимание на настройку http, и его можно обновить до websocket.
Сертификат https — это сертификат, который бесплатно применяется на https://freessl.cn/ и настраивается на 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 {
}
}