Aplikasi pesan instan, termasuk server, manajemen dan klien
Sekarang diterapkan dan online, selamat datang untuk merasakan terminal klien dan manajemen
Mohon jangan mengubah peran dan izin default sesuka hati. Harap berbaik hati dan jangan menggunakan nama yang sangat tidak beradab.
Dibangun menggunakan @vue/cli, klien layanan IM menggunakan Vant untuk bagian UI
Proyek ini terutama untuk mendemonstrasikan aplikasi dasar klien (mirip dengan WeChat). Ini memiliki fungsi seperti registrasi, login, mengedit informasi pribadi, menambah teman, melamar bergabung dengan grup, mengobrol, dll. bagian belakang.
Semua orang familiar dengan proyek Vue Family Bucket (menurut saya ini lebih baik daripada React dalam hal ini, dan pilihannya tidak banyak).
Pertama perkenalkan paket socket.io-client. Anda dapat mengetahui dari namanya bahwa itu adalah bagian klien dari socket.io.
Jika Anda ingin mengirim dan menerima informasi dengan server, Anda harus membuat tautan terlebih dahulu.
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连接成功!');
// ....
});
Setelah tautan berhasil dalam proyek ini, saya mulai meminta daftar sesi dan catatan pesan, untuk memastikan bahwa tautan berhasil ketika menerima pesan.
Setelah terhubung ke server, Anda dapat mengirim dan menerima informasi dengan sangat sederhana:
// 发送消息,message是自己定义的消息体
this.socket.emit('/v1/im/new-message', message);
// 有新消息收到
this.socket.on('/v1/im/new-message', message => {
// 自己进行一系列的处理
});
Mengirim dan menerima pesan dapat digabungkan dengan backend. Sekalipun frontend mengirimkan pesannya sendiri, pesan tersebut tidak akan ditampilkan sampai ia menerima pesan grup dari latar belakang dikirim.
Saat memasukkan, itu adalah string murni seperti [酷]
. Saat menyimpan, itu juga berupa string. Saat ditampilkan, itu diganti dengan ikon yang sesuai. Inilah mengapa saya tidak dapat menemukan cara untuk menggantinya dengan ikon yang sesuai di kotak masukan. Bos awalnya meminta saya melakukan ini, dan saya mencobanya, tetapi ternyata hal ini adalah lubang besar. Saat ini, tampaknya hanya kotak input Weibo yang saya hubungi yang menampilkan ikon, bukan string, yang juga menimbulkan banyak pertanyaan:
Namun sangat bagus menggunakan string (WeChat dan DingTalk), semua fitur asli dapat digunakan, dan front-end mudah ditangani (poin penting)
Dari penjelasan di atas, kita dapat melihat betapa nyamannya penggunaan socket.io. Pertama buat tautan, lalu Anda dapat mengirim dan menerima pesan. Dikombinasikan dengan skenario IM kami, kami membuat pengaturan berikut:
/v1/im/new-message
Biasa terlihat di aplikasi seluler saat menggulir dan mengeklik untuk masuk
Saat kami mengembangkan aplikasi web, kami sering menemui masalah. Saat kami berpindah dari halaman daftar yang dapat digulir ke halaman detail berikutnya, lalu kembali ke halaman daftar, sulit untuk memulihkan status bilah gulir dan tidak dapat mengingatnya. Lokasi saat Anda pindah.
Saya sudah mencoba banyak metode sebelumnya:
Tidak satu pun solusi di atas yang ideal
Kemudian, saya mengembangkan vue-page-stack dengan referensi ke keep-alive untuk menyimpan tumpukan halaman Vue, yaitu dom virtual di Vue, tetapi masalah scroll bar masih belum terpecahkan. Karena DOM virtual tidak mencatat status pengguliran setiap komponen, maka tidak dapat dipulihkan.
Ketika saya menggunakan cube-ui, saya menemukan bahwa menggunakan wadah gulir di perpustakaan komponen ini dapat memulihkan bilah gulir. Saya selanjutnya menemukan bahwa itulah alasan gulir yang lebih baik dari guru Huang Yi.
Dengan melihat kode sumber bs, saya menemukan bahwa implementasi internal bs bukanlah pengguliran asli, tetapi mencatat beberapa informasi pengguliran, yang paling penting adalah x dan y, yang merupakan kumpulan nilai pengguliran perilaku bergulir melalui transformasi. Implementasi, saat memulihkan DOM virtual, informasi bergulir juga dipulihkan.
Pada akhirnya, vue—page-stack + bs dapat merealisasikan pemulihan tumpukan halaman dengan sempurna.
Masalah ini sebagian besar terlihat pada kueri seperti catatan pesan. Anda juga akan mengalami masalah ini dalam program kecil.
Sebagian besar adegan gulir adalah pemuatan pull-up. Selama pemuatan pull-up, konten yang dimuat muncul di bawah area gulir. Setelah memuat, kami menambahkan data ke daftar, dan Vue dan lainnya bertanggung jawab untuk merender konten yang baru dimuat untuk memuat.
Namun dalam skenario kami, saat menelusuri rekaman pesan dalam sesi tertentu, kami perlu menarik ke bawah untuk memuat lebih banyak pesan. Setelah memuat, terus tarik ke bawah dan gulir perlahan untuk melihatnya. Hal ini menyebabkan masalah yang sangat serius: konten yang muncul setelah pemuatan drop-down berada di atas area gulir. Jika tidak ada pemrosesan yang dilakukan, konten tersebut akan langsung melompat ke atas konten yang baru dimuat setelah memuat, karena jarak gulir belum berubah, yang menyebabkan masalah, yang tidak sesuai dengan apa yang ingin kita capai.
Saya juga memikirkan banyak metode, termasuk menghitung panjang total pesan yang baru ditambahkan dan kemudian memutarnya kembali. Namun, jenis dan tinggi pesan tidak konsisten, dan akan ada kesalahan dalam perhitungan.
Solusi terakhir yang saya pikirkan adalah:
Kedua masalah di atas tercermin pada gambar di bawah ini, dan efeknya OK, sebagai berikut:
Karena bandwidth server saya terlalu lemah, saya ingin menggunakan CDN sebanyak mungkin dan menggunakan beberapa ekstensi, yang akan mengurangi tekanan pada server.
// 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'
}
Ini juga menyebabkan https saya menunjukkan bahwa itu tidak aman karena SameSite, tetapi tidak ada cara lain.
Sumber daya front-end dikelola menggunakan nginx, yang bertindak sebagai proxy terbalik. index.html tidak di-cache di front-end. File statis seperti js dan css di-cache dengan kuat dan gzip dikompresi selama sebulan, dan sisanya /api , /public dan /socket .io perlu diteruskan ke server. Server saya berjalan di http://127.0.0.1:7001. Anda perlu memperhatikan pengaturan http dan dapat ditingkatkan ke websocket.
Sertifikat https adalah sertifikat yang diterapkan secara gratis di https://freessl.cn/ dan dikonfigurasi di 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 {
}
}