تطبيق المراسلة الفورية، بما في ذلك الخادم والإدارة والعميل
تم نشره الآن وعبر الإنترنت، مرحبًا بكم في تجربة محطة العميل والإدارة
من فضلك لا تغير الأدوار والأذونات الافتراضية حسب الرغبة. يرجى أن تكون لطيفًا ولا تستخدم بعض الأسماء غير المتحضرة.
تم إنشاء عميل خدمة المراسلة الفورية باستخدام @vue/cli، ويستخدم Vant لجزء واجهة المستخدم
يهدف هذا المشروع بشكل أساسي إلى توضيح التطبيق الأساسي للعميل (على غرار WeChat)، ويحتوي على وظائف مثل التسجيل وتسجيل الدخول وتحرير المعلومات الشخصية وإضافة الأصدقاء والتقدم للانضمام إلى مجموعة والدردشة وما إلى ذلك. ويجب استخدامه مع الخلفية.
الجميع على دراية بمشروع Vue Family Bucket (أعتقد أنه أفضل من React في هذا الصدد، وليس هناك الكثير من الخيارات، وإليك بعض الإضافات:
قم أولاً بتقديم حزمة مأخذ التوصيل.io-client، ويمكنك أن تعرف من الاسم أنها جزء العميل من مأخذ التوصيل.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)، ويمكن استخدام جميع الميزات الأصلية، كما يسهل التعامل مع الواجهة الأمامية (النقطة الأساسية)
مما سبق، يمكننا أن نرى مدى ملاءمة استخدام مقبس io. قم أولاً بإنشاء رابط، ثم يمكنك إرسال واستقبال الرسائل، بالإضافة إلى سيناريو المراسلة الفورية الخاص بنا، وقمنا بالترتيبات التالية:
/v1/im/new-message
يتم مشاهدته بشكل شائع في تطبيقات الأجهزة المحمولة عند التمرير والنقر للدخول
عندما نقوم بتطوير تطبيقات الويب، غالبًا ما نواجه مشكلة عندما ننتقل من صفحة قائمة قابلة للتمرير إلى صفحة التفاصيل التالية، ثم نعود إلى صفحة القائمة، فمن الصعب استعادة حالة شريط التمرير ولا يمكننا تذكرها. الموقع الذي انتقلت فيه.
لقد جربت العديد من الطرق من قبل:
لا يعتبر أي من الحلول المذكورة أعلاه مثاليًا
لاحقًا، قمت بتطوير vue-page-stack مع الإشارة إلى الحفاظ على الحياة لحفظ مجموعة صفحات 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، الذي يعمل بمثابة وكيل عكسي. لا يتم تخزين ملف 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 {
}
}