แอปพลิเคชันการส่งข้อความโต้ตอบแบบทันที รวมถึงเซิร์ฟเวอร์ การจัดการ และไคลเอนต์
ขณะนี้ปรับใช้และออนไลน์แล้ว ขอต้อนรับสู่ประสบการณ์ลูกค้าและเทอร์มินัลการจัดการ
โปรดอย่าเปลี่ยนบทบาทเริ่มต้นและการอนุญาตตามต้องการ โปรดมีน้ำใจและอย่าใช้ชื่อที่ไม่สุภาพมากนัก
สร้างขึ้นโดยใช้ @vue/cli ไคลเอ็นต์บริการ IM ใช้ Vant สำหรับส่วน UI
โปรเจ็กต์นี้มีไว้เพื่อสาธิตการใช้งานพื้นฐานของไคลเอนต์เป็นหลัก (คล้ายกับ 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 นั้นสะดวกเพียงใด ขั้นแรกให้สร้างลิงก์ จากนั้นคุณสามารถส่งและรับข้อความได้ เมื่อรวมกับสถานการณ์ IM ของเรา เราได้เตรียมการดังต่อไปนี้:
/v1/im/new-message
พบเห็นได้ทั่วไปในแอปบนมือถือเมื่อเลื่อนและคลิกเพื่อเข้า
เมื่อเราพัฒนาเว็บแอป เรามักจะประสบปัญหา เมื่อเราออกจากหน้ารายการที่เลื่อนได้ไปยังหน้ารายละเอียดถัดไป แล้วกลับไปที่หน้ารายการ เป็นการยากที่จะคืนค่าสถานะของแถบเลื่อนและจำไม่ได้ สถานที่เมื่อคุณย้ายเข้า
ฉันลองมาหลายวิธีแล้ว:
ไม่มีวิธีแก้ปัญหาข้างต้นใดที่เหมาะสมที่สุด
ต่อมา ฉันพัฒนา vue-page-stack โดยอ้างอิงถึง Keep-alive เพื่อบันทึกสแต็กของเพจ Vue นั่นคือ dom เสมือนใน Vue แต่ปัญหาแถบเลื่อนยังไม่ได้รับการแก้ไข เนื่องจาก DOM เสมือนไม่ได้บันทึกสถานะการเลื่อนของแต่ละส่วนประกอบ จึงไม่สามารถกู้คืนได้
เมื่อฉันใช้ cube-ui ฉันพบว่าการใช้คอนเทนเนอร์การเลื่อนในไลบรารีส่วนประกอบนี้สามารถกู้คืนแถบเลื่อนได้ ฉันค้นพบเพิ่มเติมว่านี่คือเหตุผลที่ครู Huang Yi เลื่อนได้ดีขึ้น
เมื่อดูซอร์สโค้ดของ 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 ซึ่งทำหน้าที่เป็นพร็อกซีแบบย้อนกลับ จะไม่ถูกแคชไว้ที่ส่วนหน้า ไฟล์แบบคงที่ เช่น 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 {
}
}