Instant messaging application, including server, management and client
Now deployed and online, welcome to experience the client and management terminal
Please don't change the default roles and permissions at will. Please be kind and don't use some very uncivilized names.
Built using @vue/cli, the IM service client uses Vant for the UI part
This project is mainly to demonstrate the basic application of a client (similar to WeChat). It has functions such as registration, login, editing personal information, adding friends, applying to join a group, chatting, etc. It needs to be used with the backend.
Everyone is familiar with the Vue Family Bucket project (I think it is better than React in this regard, and there are not so many choices). Here are some extensions:
First introduce the socket.io-client package. You can know from the name that it is the client part of socket.io. Here is the usage:
If you want to send and receive information with the server, you must first establish a link.
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连接成功!');
// ....
});
After the link was successful in this project, I started to request the session list and message records, so as to ensure that the link was successful when getting the message.
After connecting to the server, you can send and receive information. It is very simple:
// 发送消息,message是自己定义的消息体
this.socket.emit('/v1/im/new-message', message);
// 有新消息收到
this.socket.on('/v1/im/new-message', message => {
// 自己进行一系列的处理
});
Sending and receiving messages can be combined with the backend. Even if the frontend sends its own messages, it will not be displayed until it receives the group message from the background. That is to say, if the link is disconnected, its own messages cannot be sent.
When inputting, it is a pure string like [酷]
. When saving, it is also a string. When displayed, it is replaced with the corresponding icon. Here is why I can't find a way to replace it with the corresponding icon in the input box. The boss originally asked me to do this, and I tried it, but it turned out that this thing was a big pit. Nowadays, it seems that only the input box of Weibo that I have come into contact with displays icons instead of strings, which also leads to a lot of questions:
But it’s great to use strings (both WeChat and DingTalk), all native features can be used, and the front-end is easy to handle (key point)
From the above, we can see how convenient socket.io is to use. First establish a link, and then you can send and receive messages. Combined with our IM scenario, we made the following arrangements:
/v1/im/new-message
Commonly seen in mobile apps when scrolling and clicking to enter
When we develop web apps, we often encounter a problem. When we go from a scrollable list page to the next details page, and then return to the list page, it is difficult to restore the state of the scroll bar and cannot remember it. The location when you moved in.
I've tried many methods before:
None of the above solutions are ideal
Later, I developed vue-page-stack with reference to keep-alive to save the stack of Vue pages, that is, the virtual dom in Vue, but the scroll bar problem was still not solved. Because the virtual DOM does not record the scrolling status of each component, it cannot be restored.
When I used cube-ui, I found that using the scroll container in this component library can restore the scroll bar. I further discovered that it was the reason for teacher Huang Yi's better-scroll.
By looking at the source code of bs, I found that the internal implementation of bs is not native scrolling, but records some scrolling information, the most important of which are x and y, which are the scrolling values. I have implemented a set of scrolling behaviors through transform. Implementation, when restoring the virtual DOM, the scrolling information is also restored.
In the end, vue—page-stack + bs can perfectly realize the restoration of the page stack.
This problem is mostly seen in queries such as message records. You will also encounter this problem in small programs.
Most scrolling scenes are pull-up loading. During pull-up loading, the loaded content appears below the scroll area. After loading, we add the data to the list, and Vue and others are responsible for rendering the newly loaded content. We continue to load. Pull to continue scrolling.
But in our scenario, when browsing the message records in a certain session, we need to pull down to load more messages. After loading, continue to pull down and scroll slowly to view. This leads to a very serious problem: the content that appears after drop-down loading is above the scrolling area. If no processing is done, it will jump directly to the top of the newly loaded content after loading, because the scrolling distance has not changed, which causes a problem. , which is inconsistent with what we want to achieve.
I also thought of many methods, including calculating the total length of the newly added message and then rolling it back. However, the type and height of the message are inconsistent, and there will be errors in the calculation.
The final solution I thought of was:
The above two problems are reflected in the picture below, and the effect is OK, as follows:
Since my server bandwidth is too weak, I want to use CDN as much as possible and use some extensions, which will put a lot less pressure on the server. This part of the content belongs to the webpack part.
// 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'
}
This also caused my https to show that it was unsafe because of SameSite, but there was no other way.
Front-end resources are managed using nginx, which acts as a reverse proxy. index.html is not cached on the front-end. Static files such as js and css are strongly cached and gzip compressed for a month, and the remaining /api, /public and /socket .io needs to be forwarded to the server. My server runs at http://127.0.0.1:7001. You need to pay attention to setting http and it can be upgraded to websocket.
The https certificate is a certificate applied for free at https://freessl.cn/ and configured on 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 {
}
}