نظرًا لأن نظام موقع الويب يتزايد حجمه، فقد تحتاج ملفات تعريف الارتباط من أسماء النطاقات المختلفة وحتى مواقع الويب الشريكة المختلفة إلى المشاركة بشكل أو بآخر. عند مواجهة هذا الموقف، فإن ما يفكر فيه الجميع عادةً هو استخدام مركز تسجيل الدخول لتوزيع حالة ملف تعريف الارتباط ثم قم بمزامنته، والتكلفة أعلى والتنفيذ أكثر تعقيدًا وإزعاجًا.
نظرًا لأن ملفات تعريف الارتباط هي عبر النطاقات، فإن المتصفحات لا تسمح بالوصول المتبادل على الإطلاق. ومن أجل كسر هذا القيد، يتم استخدام خطة التنفيذ التالية لمشاركة البيانات عبر المجالات باستخدام postmessage وlocalstorage.
المبدأ بسيط نسبيًا، ولكن هناك العديد من المخاطر التي نواجهها، فلنحلها هنا ونقوم بعمل نسخة احتياطية.
2. تصميم APIكما هو مذكور في الخلفية، فإننا نستخدم التخزين المحلي بدلاً من ملفات تعريف الارتباط. هناك بعض الاختلافات في الاستخدام بين التخزين المحلي وملفات تعريف الارتباط، على سبيل المثال، يتمتع التخزين المحلي بسعة أكبر، ولكن لا يوجد وقت لانتهاء الصلاحية، على الرغم من أن السعة كبيرة، إلا أنها متاحة على الحد الأعلى للمساحة يجعل من السهل التعطل إذا لم تكن العملية جيدة. بالإضافة إلى ذلك، على الرغم من أن Postmessage يدعم النطاقات المشتركة، إلا أن مشكلات الأمان وواجهة برمجة التطبيقات غير المتزامنة تسبب أيضًا بعض المشاكل في الاستخدام أسهل في الاستخدام؟
دعونا نلقي نظرة على واجهة برمجة التطبيقات (API) التي صممتها أولاً:
import { crosData } from 'base-tools-crossDomainData';var store = new crosData({ iframeUrl:somefile.html, // عنوان iframe مشترك، iframe له متطلبات خاصة، راجع انتهاء صلاحية ملف القالب:'d,h,s' / / وقت انتهاء الصلاحية الافتراضي بالأيام والساعات والثواني، ويمكن أيضًا الكتابة فوقه عند الزراعة});store.set('key','val',{ experience:'d,h,s' //option يمكن أن يؤدي إلى وقت انتهاء الصلاحية، وتجاوز انتهاء الصلاحية}).then((data)=>{ // طريقة غير متزامنة، إذا فشلت، فسوف تدخل إلى حدث الالتقاط //data {val:'val',key:'key',domain :' domain'};}).catch((err)=>{ console.log(err);}); store.get('key',{ domain:'(.*).sina.cn' // يمكنك تحديد اسم المجال، أو يمكنك استخدام (.*) لمطابقة السلاسل العادية. ستتضمن معلومات val التي تم إرجاعها معلومات المجال إذا لم يتم ملؤها، فسوف تُرجع المجال المحلي }).then((vals) =>{ console.log (val) // احصل على البيانات المخزنة بشكل غير متزامن، قد يكون هناك عدة، إنها مصفوفة [{},{}]}).catch((err)=>{});store.clear ("مفتاح").ثم التقاط (). // امسح المفاتيح الموجودة ضمن المجال الحالي فقط ولا يُسمح بمسح المفاتيح الموجودة ضمن المجالات الأخرى.يعتمد ما إذا كانت الوحدة النمطية سريعة الاستخدام على واجهة برمجة التطبيقات (API)، لذلك بالنسبة لوحدة مشاركة البيانات، أعتقد أنه من المقبول دعم الطرق الثلاث للتعيين والحصول والمسح، لأن رسالة البريد نفسها هي سلوك غير متزامن يتم تنفيذه مرة واحدة. ويجب تعبئتها في شكل أكثر ملاءمة وأسهل في الاستخدام. نظرًا لأن التخزين المحلي لا يدعم وقت انتهاء الصلاحية، فإن تكوين وقت انتهاء الصلاحية العام مطلوب بالطبع، ويمكن أيضًا تهيئته بشكل فردي أثناء التعيين، يمكننا تحديد الحصول على البيانات ضمن مجال معين أو البيانات ضمن مجالات متعددة، لأن أسماء المفاتيح قد تتكرر، ولكن هناك مجال واحد فقط. يتضمن ذلك إدارة البيانات، فلنتحدث عنها بشكل منفصل لاحقًا، وأخيرًا، يمكن لواجهات برمجة التطبيقات الواضحة والمحددة زرع البيانات في هذا المجال فقط ولا يمكنها تشغيل البيانات في المجالات الأخرى.
دعونا نلقي نظرة على إعدادات العميل وواجهة برمجة التطبيقات:
<!DOCTYPE html><html> <head> <meta charset=utf-8> <title>crosData</title> </head> <body> <script> window.CROS = { domain:/(.*). sina.cn/, // أو اسم المجال الذي تسمح به، يدعم أحرف البدل العادية و * lz:false // ما إذا كان سيتم تمكين ضغط lz لأحرف val}; src=http://cdn/sdk.js></script> </body></html>يمكنك إدخال js sdk الخاص بالعميل بمرونة في مستند html في أي مجال، ثم تكوين قائمة بيضاء للمجال تسمح لك بالزراعة في المجال حيث يوجد هذا المستند من خلال السمات العامة، وهو يدعم التعبيرات العادية، ثم lz هو سواء ابدأ بضغط سلسلة lz وسأقدم لك ضغط lz لاحقًا.
في هذه المرحلة، تم الانتهاء من تصميم واجهة برمجة التطبيقات (API) العام نسبيًا، فلنلقي نظرة على مبادئ التنفيذ وبعض المشكلات المحددة.
3. مبدأ التنفيذيبدو الأمر بسيطًا جدًا، لكنه غير مكتوب فعليًا، نحتاج أولاً إلى معرفة كيفية استخدام postMessage، وهي واجهة برمجة تطبيقات شائعة جدًا. هناك نقطة مهمة يجب إخبارك بها هنا، وهي أنه لا يمكن استخدام postMessage إلا في إطار iframe أو استخدام نافذة .open هي طريقة لفتح صفحة جديدة للتواصل مع بعضنا البعض. بالطبع، نحتاج هنا أولاً إلى إنشاء إطار iframe مخفي للنطاقات المشتركة.
أنا كسول جدًا لاستخدام الأدوات لرسم الصور، لأن العملية واضحة نسبيًا. هنا سأعيد سرد عملية الاتصال بأكملها بالكلمات. أولاً، تقوم الصفحة الرئيسية بإنشاء إطار iframe مخفي، ثم عندما يتم تنفيذ أوامر مثل set، get، Clear وما إلى ذلك، يتم بث الرسالة من خلال postMessage. بعد أن تتلقى الصفحة الرسالة، تقوم بتحليل الأمر والبيانات ومعرف رد الاتصال (لا يمكن لـ postMessage تمرير الوظائف والمراجع بسبب مشكلات التوافق. من الأفضل تمرير نوع السلسلة فقط ، لذلك تحتاج البيانات إلى التوثيق). ثم عندما تكمل الصفحة الفرعية عملية التخزين المحلي، فإنها تُرجع عرض الأسعار والبيانات المقابلة إلى الصفحة الرئيسية من خلال postMessage. وتستمع الصفحة الرئيسية إلى حدث الرسالة وتعالج النتائج.
4. الترميزحسنًا، هناك بضعة أسطر فقط، فلنبدأ بالبرمجة:
أولاً، دعونا نقدم حزم الطرف الثالث التي نستخدمها ولماذا نستخدمها:
1. يقوم تحليل url بتحليل عنوان url، وذلك باستخدام سمة الأصل فيه بشكل أساسي، لأن postMessage نفسه لديه تحقق صارم من الأصل، ونحتاج أيضًا إلى دعم إدارة القائمة البيضاء واسم المجال.
2.ms عبارة عن مكتبة أدوات لتحويل اختصار الوقت إلى ميلي ثانية.
3. lz-string عبارة عن مجموعة أدوات لضغط السلاسل. إليك مقدمة علمية شائعة لخوارزمية ضغط LZ. أولاً، لفهم LZ، عليك أن تفهم RLZ، وRun Length Encoding، وهي خوارزمية بسيطة جدًا للضغط بدون فقدان البيانات. فهو يستبدل البايتات المتكررة بوصف بسيط للبايتات المتكررة وعدد التكرارات. الفكرة وراء خوارزمية ضغط LZ هي استخدام خوارزمية RLE لاستبدال التكرار السابق لمرجع إلى نفس تسلسل البايت. ببساطة، تعتبر خوارزمية LZ خوارزمية مطابقة للسلسلة. على سبيل المثال: تظهر سلسلة معينة بشكل متكرر في جزء من النص ويمكن تمثيلها بمؤشر سلسلة يظهر في النص السابق.
ميزة lz-string نفسها هي أنها يمكن أن تقلل بشكل كبير من سعة التخزين لديك. إذا تم استخدام التخزين المحلي الذي يبلغ 5 ميجابايت لدعم تخزين البيانات لأسماء النطاقات المتعددة، فسيتم ضغطها واستخدامها بسرعة، ومع ذلك، فإن lz-string نفسها أبطأ ويستهلك المزيد من المال. إذا كانت لديك متطلبات الحجم لكمية البيانات التي سيتم نقلها في العمل، فيمكنك محاولة استخدام خوارزمية الضغط هذه لتحسين طول السلسلة.
4. تعد واجهة برمجة تطبيقات التخزين المحلي الخاصة بـ store2 نفسها بسيطة نسبيًا لتقليل تعقيد منطق الكود، ويتم تحديد مكتبة تنفيذ تخزين محلية شائعة لتنفيذ عمليات المتجر.
بعد الحديث عن حزمة الطرف الثالث، دعونا نلقي نظرة على كيفية كتابة js للصفحة الرئيسية:
class crosData { buildor(options) { supportCheck(); this.options = Object.assi({ iframeUrl: '',expire: '30d' }, options); .iframeBeforeFuns = []; this.parent = window; this.origin = new url(this.options.iframeUrl).origin; this.createIframe(this.options.iframeUrl); addEvent(this.parent, 'message', (evt) => { var data = JSON.parse(evt.data); var Origin = evt.origin || evt.originalEvent .origin; // أتلقى فقط رسالة إطار iframe الذي فتحته، أما الرسائل الأخرى فهي غير قانونية، وسيتم الإبلاغ عن الخطأ مباشرة if (origin !== this.origin) { return("أصل غير قانوني!"); return } if (data.err) { this.cbs[data.cbid].reject(data.err); .ret); } حذف this.cbs[data.cbid] }); } createIframe(url) { addEvent(document, 'domready', () => { var frame = document.createElement('iframe');frame.style.cssText = 'width:1px;height:1px;border:0;position:absolute;left:-9999px;top:-9999px;'; 'src', url);frame.onload = () => { this.child =frame.contentWindow; this.iframeBeforeFuns.forEach(item => item()); } document.body.appendChild(frame }); } postHandle(type, args) { return new Promise((الحل، الرفض) => { var cbid = this.cid; var message = { cbid: cbid, Origin: new url(location.href).origin, action: type, args: args } this.child.postMessage(JSON.stringify(message), this.origin); this.cbs[cbid] = { حل, رفض } this.cid++ }); => { if (this.child) { return this.postHandle(type, args).then(resolve } else { var self = this; this.iframeBeforeFuns.push(function() { self.postHandle(type, args).then(resolve); } } }) } set(key, val, options) { options = Object.assi({ انتهاء الصلاحية: مللي ثانية) (this.options.expire) }, options); return this.send('set', [key, val, options]); get(key, options) { options = Object.assi({ domain: new url(location.href).origin }, options); return this.send('get', [key, options]); Clear(key) { return this.send('clear '، [مفتاح])؛ }}ربما لا يوجد سوى عدد قليل من الأساليب، وهنا بعض النقاط الرئيسية، واسمحوا لي أن أتحدث عنها.
1. تُسمى جميع أساليب الحصول على البيانات وضبطها ومسحها بشكل موحد طرق الإرسال، ولكن يتم استكمال جزء الخيارات.
2. تقوم طريقة الإرسال بإرجاع كائن وعد. إذا تم تحميل iframe بنجاح، فسيتم استدعاء طريقة postHandle مباشرة لتنفيذ عملية postMessage. إذا كان iframe لا يزال قيد التحميل، فسيتم دفع العملية الحالية إلى مصفوفة iframeBeforeFuns، ملفوفة بـ وظيفة، وينتظر انتهاء تحميل iframe بعد الاستدعاء الموحد، يتم تغليف الوظيفة أيضًا في طريقة postHandle.
3. تقوم طريقة postHandle بتغليف البيانات قبل إرسال الطلب وإنشاء cbid والأصل والإجراء والوسائط. يحفظ كائن cbs القرار والرفض ضمن كل عرض أسعار، وينتظر عودة رسالة postMessage للصفحة الفرعية قبل المعالجة. نظرًا لأن postMessage لا يمكنه الاحتفاظ بالمراجع ولا يمكنه تمرير الوظائف، فقد تم اختيار هذه الطريقة هنا للارتباط.
4. من السهل فهم المنشئ. عند تهيئة هذه الفئة، نحدد بعض سمات الخيارات التي نحتاجها، وننشئ إطار iframe، ثم نستمع إلى حدث الرسالة ونعالج الرسالة التي ترجعها الصفحة الفرعية.
5. في حدث الرسالة للصفحة الرئيسية، نحتاج إلى التحقق من أن الرسالة المرسلة إلي يجب أن تكون نافذة iframe التي فتحتها، وإلا سيتم الإبلاغ عن خطأ، ومن ثم سيتم تنفيذ القرار والرفض في cbs وفقًا لـ معرف الخطأ في البيانات.
6. في طريقة createIframe، يعالج رد الاتصال في iframe onload طريقة استدعاء التخزين المؤقت قبل الإنشاء. انتبه إلى استخدام domready هنا، لأنه قد يتم تنفيذ sdk قبل تحليل النص.
هنا هو رمز الجزء الفرعي:
class iframe { set(key, val, options, Origin) { // تحقق من حجم val الذي لا يمكن أن يتجاوز 20 كيلو val = val.toString(); val = this.lz ? valsize = sizeof(val, 'utf16'); //localStorage يخزن البايتات باستخدام ترميز utf16 if (valsize > this.maxsize) { return { err: 'قيمة متجرك: ' + valstr + ' الحجم هو ' + valsize + 'b، maxsize :' + this.maxsize + 'b، استخدم utf16' } } key = `${this.prefix}_${key}, ${new url(origin).origin}`; var data = { val: val, lasttime: Date.now(),expire: Date.now() + options.expire }; store.set(key, data); // إذا كان أكبر من الحد الأقصى لعدد التخزين، فاحذف آخر تحديث if (store.size() > this.storemax) { varkeys = store.keys(); keys.sort( (a, b) => { var item1 = store.get(a), item2 = store.get(b); return item2.lasttime - item1.lasttime; }); var Removesize = Math.abs( هذا.ستورماكس - store.size()); while (removesize) { store.remove(keys.pop()); Removesize-- } } return { ret: data } } get(key, options) { var message = {}; key = store.keys(); var regexp = new RegExp('^' + this.prefix + '_' + key + ',' + options.domain + '$'); keys.filter((key) => { return regexp.test(key); }).map((storeKey) => { var data = store.get(storeKey); data.key = key; data.domain = storeKey .split(',')[1]; if (data.expire < Date.now()) { store.remove(storeKey); return unfinite; val: data.val, lasttime: Date.now(),نتهي: data.expire }); } data.val = this.lz ? lzstring.decompressFromUTF16(data.val) : data.val }); filter(item => { return !!item; // تصفية غير محددة }); return message } Clear(key, Origin) { store.remove(`${this.prefix}_${key},${origin}`); return {}; } ClearOtherKey() { // حذف المفاتيح غير القانونية varkeys = store.keys(); new RegExp('^' + this.prefixkeys.forEach(key => { if (!keyReg.test(key)) { store.remove(key); } }); } buildor(safeDomain, lz) { supportCheck(); this.safeDomain = SafeDomain || /.*/; this.prefix = '_cros'; SafeDomain) !== '[object RegExp]') { throw new Error('safeDomain must be regexp'); this.storemax = 100; this.maxsize = 20 * 1024; //byte addEvent(window, 'message', (evt) => { var data = JSON.parse(evt.data); var OriginHostName = new url( evt) .origin).hostname; var Origin = evt.origin, action = data.action, cbid = data.cbid, args = data.args; // بث قانوني if (evt.origin === data.origin && this.safeDomain.test(originHostName)) { args.push(origin); varwhiteAction = ['set', 'get', 'clear'] ; if (whiteAction.indexOf(action) > -1) { var message = this[action].apply(this, args); window.top.postMessage(JSON.stringify(message), Origin); } } else { window.top.postMessage(JSON.stringify({ cbid: cbid, err: 'Illegal domain' }), Origin)); }}لا يوجد الكثير من التعليمات البرمجية فيما يلي مقدمة مختصرة لاستخدام كل طريقة والعلاقة التنظيمية بينها:
1. في الجزء المنشئ، تتحقق الفئة أعلاه أيضًا من دعم ميزات المتصفح، ثم تحدد سمات مثل قيمة بادئة المتجر والحد الأقصى للعدد والحد الأقصى لحجم كل مفتاح. ثم نقوم بإنشاء قناة رسائل وننتظر حتى تتصل بها الصفحة الرئيسية.
2. في الرسالة، نتحقق من أصل البث، ثم نتحقق من الطريقة التي تم الاتصال بها، ونستدعي المجموعة المقابلة، ونحصل على الطرق، ونمسحها، ثم نحصل على نتيجة التنفيذ، ونربط cbid، ونرسل أخيرًا الصفحة الرئيسية لـ postMessage.
3. يقوم ClearOtherKey بحذف بعض بيانات المتجر غير القانونية ويحتفظ فقط بالبيانات المطابقة للتنسيق.
4. في الطريقة المحددة، يتم إجراء التحقق من الحجم وضغط lz على كل جزء من البيانات. تتضمن البيانات المحفوظة القيمة والمفتاح ووقت انتهاء الصلاحية ووقت التحديث (المستخدم لحساب LRU).
5. في الطريقة المحددة، إذا تجاوز عدد ls المخزن الحد الأقصى، فستكون عملية الحذف مطلوبة في هذا الوقت. LRU هو اختصار للأقل استخدامًا مؤخرًا، أي الأقل استخدامًا مؤخرًا. نقوم باجتياز جميع قيم المفاتيح، وفرز قيم المفاتيح، وتمرير المرة الأخيرة، ثم إجراء العملية المنبثقة لمصفوفة المفاتيح للحصول على المفاتيح التي يجب مسحها في نهاية المكدس، ثم حذفها واحدة تلو الأخرى .
6. في طريقة get، نقوم باجتياز جميع القيم الرئيسية، ومطابقة مفتاح المجال الذي نحتاج إلى الحصول عليه، ثم تفكيك المفتاح في قيمة الإرجاع (نقوم بتخزينه بتنسيق المفتاح والمجال)، لأن واجهة برمجة التطبيقات (API) يتطلب إرجاع قيم مطابقة متعددة، ونقوم بإجراء تصفية نهائية على البيانات منتهية الصلاحية، ثم نستخدم lz لفك ضغط قيمة val للتأكد من حصول المستخدم على النتيجة الصحيحة.
ما ورد أعلاه هو عملية ترميز التنفيذ الشاملة ومراجعتها، فلنتحدث عن المخاطر التي واجهتنا.
5. واجهت بعض المزالقنظرًا لأن الكود الرئيسي فقط هو المذكور أعلاه، فهو ليس الكود الكامل، لأن المنطق نفسه واضح نسبيًا، ويمكن كتابته في وقت قصير. دعونا نتحدث عن المزالق أدناه.
1. احسب قيمة تخزين التخزين المحلي.
نظرًا لأننا نعلم جميعًا أن هناك حدًا يبلغ 5 ميجا بايت، فإن الحد الأقصى لمتطلبات كل جزء من البيانات لا يمكن أن يتجاوز 20*1024 بايت لحساب البايتات، يحتاج التخزين المحلي إلى استخدام ترميز utf16 للتحويل تشغلها سلاسل عدد الأقسام
2. التوافق
من الأفضل تمرير السلاسل في postMessage ضمن IE8. يجب سلاسة الأحداث ويحتاج JSON إلى سلاسة.
3. المعالجة غير المتزامنة عند إنشاء إطار iframe
هنا، قمنا سابقًا بإجراء انتظار متكرر لـ setTimeout، ولكننا قمنا بتغييره لاحقًا إلى طريقة التنفيذ المذكورة أعلاه، بعد التحميل، تتم معالجة إعادة حب الوعد بشكل موحد لضمان توحيد واجهة برمجة تطبيقات الوعد.
4. عند حفظ البيانات، التعقيد المكاني مقابل التعقيد الزمني.
الإصدار الأول ليس هو التنفيذ أعلاه، قمت بتنفيذ 3 إصدارات:
يحفظ الإصدار الأول مصفوفة LRU من أجل تقليل تعقيد الوقت، ولكنه يهدر تعقيد المساحة، علاوة على ذلك، بعد الاختبار، تستغرق طريقة الحصول على المتجر وقتًا طويلاً نسبيًا، ويرجع ذلك أساسًا إلى استهلاك التحليل للوقت.
في الإصدار الثاني، من أجل زيادة معدل ضغط lz-string، قمت بحفظ جميع البيانات بما في ذلك مصفوفة LRU إلى قيمة رئيسية. ونتيجة لذلك، فإن استهلاك وقت التحليل لـ lz-string وgetItem كبير جدًا عندما يكون هناك الكثير من البيانات على الرغم من أن حساب التعقيد الزمني هو الأدنى.
الإصدار الأخير هو الإصدار أعلاه، لقد ضحيت ببعض التعقيد الزمني والتعقيد المكاني، ولكن نظرًا لأن عنق الزجاجة يكمن في سرعة القراءة والكتابة في المجموعة والحصول عليها، فإن سرعة القراءة والكتابة للحفظ الفردي سريعة للغاية المفاتيح لأن الطبقة السفلية تستخدم في التخزين المحلي، ولا يزال الأداء جيدًا جدًا، ويمكن تخزين 100 إدخال في 20 كيلو بايت، ووقت القراءة والكتابة حوالي ثانية واحدة، والأداء جيد جدًا.
6. الملخص والمقارنةبعد كتابة الوحدة، أدركت أن هناك مثل هذه المكتبة: zendesk/cross-storage
لكنني تحققت من واجهة برمجة التطبيقات والكود المصدري الخاص به وقارنت طرق التنفيذ وأعتقد أن الإصدار الخاص بي أكثر أهمية.
1. يتمتع الإصدار الخاص بي بالتحكم في اسم النطاق وإدارة البيانات.
2. إصدار واجهة برمجة تطبيقات الوعد الخاص بي أكثر تبسيطًا، مع وجود إصدار أقل من OnConnect منه، ويمكنك الرجوع إلى تنفيذه، وهو أكثر بكثير مما كتبته، ولا يحل مشكلة انتظار iframe غير المتزامن.
3. البيانات المضغوطة LZ غير مدعومة.
4. إدارة مجمع تخزين LRU غير مدعومة، لذلك قد يكون هناك مساحة تخزين كبيرة جدًا مما قد يتسبب في فشل الكتابة.
5. يبدو أنه ينشئ إطار iframe لكل تفاعل، وهو ما يعد مضيعة لعمليات DOM والبث، وأعتقد أنه لا توجد مشكلة في تركه قيد التشغيل، بالطبع، قد يحتاج إلى الاتصال بالعديد من العملاء حتى يتعامل مع الأمر بهذه الطريقة .
تلخيصما ورد أعلاه هو مقدمة المحرر لمشكلة استخدام التخزين المحلي بدلاً من ملفات تعريف الارتباط لتحقيق مشاركة البيانات عبر النطاقات، وآمل أن يكون ذلك مفيدًا لك. إذا كانت لديك أي أسئلة، فيرجى ترك رسالة لي وسيقوم المحرر بالرد عليك في الوقت المناسب. أود أيضًا أن أشكر الجميع على دعمكم لموقع VeVb للفنون القتالية!