Panduan untuk pengaturcaraan dengan JavaScript menggunakan AmplifyJS components_Basics

WBOY
Lepaskan: 2016-05-16 15:48:24
asal
988 orang telah melayarinya

Peranan pengedaran acara

Apabila menambahkan pelbagai fungsi interaktif pada halaman, cara paling mudah yang kita biasa gunakan ialah mengikat acara pada elemen halaman, dan kemudian melakukan tindakan yang ingin kita lakukan dalam fungsi pengendalian acara. Kod seperti ini:

element.onclick = function(event){
  // Do anything.
};

Salin selepas log masuk

Jika tindakan yang ingin kita lakukan tidak rumit, maka kod fungsi logik sebenar boleh diletakkan di sini. Jika anda perlu mengubah suainya pada masa hadapan, pergi ke lokasi fungsi pengendalian acara ini untuk mengubah suainya.

Selain itu, untuk mencapai penggunaan semula kod yang betul, kami boleh membahagikan sebahagian daripada fungsi logik kepada fungsi:

element.onclick = function(event){
  // Other code here.
  doSomethingElse();
};

Salin selepas log masuk

Fungsi yang sepadan dengan fungsi doSomethingElse di sini boleh digunakan di tempat lain, jadi ia akan berpecah seperti ini. Di samping itu, mungkin terdapat fungsi seperti menetapkan koordinat (dengan mengandaikan fungsi itu dipanggil setPosition), dan anda juga perlu menggunakan maklumat seperti kedudukan penunjuk yang disediakan oleh acara objek acara penyemak imbas:

element.onclick = function(event){
  // Other code here.
  doSomethingElse();
  setPosition(event.clientX, event.clientY);
};

Salin selepas log masuk

Pendekatan yang tidak disyorkan di sini ialah menghantar objek acara terus ke setPosition. Ini kerana ia adalah amalan yang baik untuk memisahkan tanggungjawab fungsi logik dan mendengar peristiwa. Hanya membiarkan fungsi pemprosesan acara itu sendiri mengakses objek acara penyemak imbas membantu mengurangkan gandingan kod dan memudahkan ujian dan penyelenggaraan bebas.

Jadi, apa yang akan berlaku jika fungsi menjadi semakin kompleks? Jika anda mengikuti pendekatan sebelumnya, ia mungkin kelihatan seperti ini:

element.onclick = function(event){
  doMission1();
  doMission2(event.clientX, event.clientY);
  doMission3();
  // ...
  doMissionXX();
};

Salin selepas log masuk

Walaupun tidak mengapa untuk menggunakannya dengan cara ini, dalam kes ini anda sebenarnya boleh mempertimbangkan cara penulisan yang lebih elegan:

element.onclick = function(event){
  amplify.publish( "aya:clicked", {
    x: event.clientX,
    y: event.clientY
  });
};

Salin selepas log masuk

Borang ini ialah pengedaran acara Sila ambil perhatian bahawa peristiwa di sini tidak merujuk kepada peristiwa asli penyemak imbas (objek acara), tetapi peristiwa tersuai pada tahap logik. Aya:diklik di atas ialah nama acara tersuai yang ditulis secara santai (betul ke?).

Jelas sekali ini belum berakhir Untuk melengkapkan fungsi kompleks sebelumnya, kami masih perlu mengaitkan acara tersuai dengan perkara yang perlu dilakukan:

amplify.subscribe( "aya:clicked", doMission1);
// ...
amplify.subscribe( "aya:clicked", doMission2);
// ...

Salin selepas log masuk

Nampaknya ia muncul lagi? Benar, tetapi ia berfungsi. Di satu pihak, pendengaran peristiwa asli penyemak imbas telah dipisahkan dan diperkukuh Jika fungsi logik berubah pada masa hadapan, contohnya, beberapa fungsi dikurangkan, anda hanya perlu memadamkan bahagian kod yang berkaitan dengan acara tersuai, dan di sana. tidak perlu risau tentangnya lagi. Sebaliknya, pelarasan fungsi logik telah menjadi lebih fleksibel Fungsi boleh ditambah melalui langganan di mana-mana lokasi kod, dan pengurusan klasifikasi (nama acara tersuai) boleh dilakukan sendiri.

Ringkasnya, pengedaran acara mengurangkan gandingan antara modul kod dengan menambahkan lapisan lebihan peristiwa tersuai (apabila terdapat hanya fungsi logik mudah, anda akan menganggap ia berlebihan), menjadikan logik Fungsinya lebih jelas dan teratur, menjadikan penyelenggaraan seterusnya lebih mudah.

Tunggu sebentar, apakah yang dilakukan oleh terkenal di hadapan saya yang telah beberapa kali melancong ke luar negara?

Bagus, akhirnya tiba masanya untuk memperkenalkan ini.
AmplifyJS

Pengagihan acara memerlukan kaedah tertentu untuk dilaksanakan. Salah satu corak reka bentuk untuk pengedaran acara ialah Terbitkan/Langgan.

AmplifyJS ialah perpustakaan JavaScript ringkas yang menyediakan tiga fungsi terutamanya: permintaan Ajax, penyimpanan data dan terbitkan/langgan (yang setiap satunya boleh digunakan secara bebas). Antaranya, terbitkan/langgan ialah fungsi teras, dan nama yang sepadan ialah amplify.core.

2015728151503102.jpg (342×85)

amplify.core ialah pelaksanaan yang ringkas dan jelas bagi corak reka bentuk terbitkan/langganan, dengan lebih daripada 100 baris kesemuanya termasuk ulasan. Selepas membaca kod sumber amplify, anda boleh lebih memahami cara melaksanakan corak reka bentuk terbitkan/langganan.
Ikhtisar kod

Struktur keseluruhan kod sumber amplify.core adalah seperti berikut:

(function( global, undefined ) {

var slice = [].slice,
  subscriptions = {};

var amplify = global.amplify = {
  publish: function( topic ) {
    // ...
  },

  subscribe: function( topic, context, callback, priority ) {
    // ...
  },

  unsubscribe: function( topic, context, callback ) {
    // ...
  }
};

}( this ) );

Salin selepas log masuk

Seperti yang anda lihat, amplify mentakrifkan pembolehubah global bernama amplify (sebagai atribut global), yang mempunyai tiga kaedah menerbitkan, melanggan dan menyahlanggan. Selain itu, langganan berfungsi sebagai pembolehubah setempat yang akan menyimpan semua nama acara tersuai dan fungsi berkaitan yang terlibat dalam mod terbitkan/langganan.
terbitkan

terbitkan diterbitkan. Ia memerlukan penentuan topik, iaitu nama acara tersuai (atau hanya dipanggil topik selepas membuat panggilan, semua fungsi yang dikaitkan dengan topik tertentu akan dipanggil mengikut urutan:

).
publish: function( topic ) {
  // [1]
  if ( typeof topic !== "string" ) {
    throw new Error( "You must provide a valid topic to publish." );
  }
  // [2]
  var args = slice.call( arguments, 1 ),
    topicSubscriptions,
    subscription,
    length,
    i = 0,
    ret;

  if ( !subscriptions[ topic ] ) {
    return true;
  }
  // [3]
  topicSubscriptions = subscriptions[ topic ].slice();
  for ( length = topicSubscriptions.length; i < length; i++ ) {
    subscription = topicSubscriptions[ i ];
    ret = subscription.callback.apply( subscription.context, args );
    if ( ret === false ) {
      break;
    }
  }
  return ret !== false;
},

Salin selepas log masuk

[1],参数topic必须要求是字符串,否则抛出一个错误。

[2],args将取得除topic之外的其他所有传递给publish函数的参数,并以数组形式保存。如果对应topic在subscriptions中没有找到,则直接返回。

[3],topicSubscriptions作为一个数组,取得某一个topic下的所有关联元素,其中每一个元素都包括callback及context两部分。然后,遍历元素,调用每一个关联元素的callback,同时带入元素的context和前面的额外参数args。如果任意一个关联元素的回调函数返回false,则停止运行其他的并返回false。
subscribe

订阅,如这个词自己的含义那样(就像订本杂志什么的),是建立topic和callback的关联的步骤。比较特别的是,amplify在这里还加入了priority(优先级)的概念,优先级的值越小,优先级越高,默认是10。优先级高的callback,将会在publish的时候,被先调用。这个顺序的原理可以从前面的publish的源码中看到,其实就是预先按照优先级从高到低依次排列好了某一topic的所有关联元素。

subscribe: function( topic, context, callback, priority ) {
    if ( typeof topic !== "string" ) {
      throw new Error( "You must provide a valid topic to create a subscription." );
    }
    // [1]
    if ( arguments.length === 3 && typeof callback === "number" ) {
      priority = callback;
      callback = context;
      context = null;
    }
    if ( arguments.length === 2 ) {
      callback = context;
      context = null;
    }
    priority = priority || 10;
    // [2]
    var topicIndex = 0,
      topics = topic.split( /\s/ ),
      topicLength = topics.length,
      added;
    for ( ; topicIndex < topicLength; topicIndex++ ) {
      topic = topics[ topicIndex ];
      added = false;
      if ( !subscriptions[ topic ] ) {
        subscriptions[ topic ] = [];
      }
      // [3]
      var i = subscriptions[ topic ].length - 1,
        subscriptionInfo = {
          callback: callback,
          context: context,
          priority: priority
        };
      // [4]
      for ( ; i >= 0; i-- ) {
        if ( subscriptions[ topic ][ i ].priority <= priority ) {
          subscriptions[ topic ].splice( i + 1, 0, subscriptionInfo );
          added = true;
          break;
        }
      }
      // [5]
      if ( !added ) {
        subscriptions[ topic ].unshift( subscriptionInfo );
      }
    }

    return callback;
  },

Salin selepas log masuk

[1],要理解这一部分,请看amplify提供的API示意:

amplify.subscribe( string topic, function callback )
amplify.subscribe( string topic, object context, function callback )
amplify.subscribe( string topic, function callback, number priority )
amplify.subscribe(
  string topic, object context, function callback, number priority )

Salin selepas log masuk

可以看到,amplify允许多种参数形式,而当参数数目和类型不同的时候,位于特定位置的参数可能会被当做不同的内容。这也在其他很多JavaScript库中可以见到。像这样,通过参数数目和类型的判断,就可以做到这种多参数形式的设计。

[2],订阅的时候,topic是允许空格的,空白符将被当做分隔符,认为是将一个callback关联到多个topic上,所以会使用一个循环。added用作标识符,表明新加入的这个元素是否已经添加到数组内,初始为false。

[3],每一个callback的保存,实际是一个对象,除callback外还带上了context(默认为null)和priority。

[4],这个循环是在根据priority的值,找到关联元素应处的位置。任何topic的关联元素都是从无到有,且依照priority数值从小到大排列(已排序的)。因此,在比较的时候,是先假设新加入的元素的priority数值较大(优先级低),从数组尾端向前比较,只要原数组中有关联元素的priority数值比新加入元素的小,循环就可以中断,且可以确定地用数组的splice方法将新加入的元素添加在此。如果循环一直运行到完毕,则可以确定新加入的元素的priority数值是最小的,此时added将保持为初始值false。

[5],如果到这个位置,元素还没有被添加,那么执行添加,切可以确定元素应该位于数组的最前面(或者是第一个元素)。
unsubscribe

虽然发布和订阅是最主要的,但也会有需要退订的时候(杂志不想看了果断退!)。所以,还会需要一个unsubscribe。

unsubscribe: function( topic, context, callback ) {
  if ( typeof topic !== "string" ) {
    throw new Error( "You must provide a valid topic to remove a subscription." );
  }

  if ( arguments.length === 2 ) {
    callback = context;
    context = null;
  }

  if ( !subscriptions[ topic ] ) {
    return;
  }

  var length = subscriptions[ topic ].length,
    i = 0;

  for ( ; i < length; i++ ) {
    if ( subscriptions[ topic ][ i ].callback === callback ) {
      if ( !context || subscriptions[ topic ][ i ].context === context ) {
        subscriptions[ topic ].splice( i, 1 );
        
        // Adjust counter and length for removed item
        i--;
        length--;
      }
    }
  }
}

Salin selepas log masuk

读过前面的源码后,这部分看起来就很容易理解了。根据指定的topic遍历关联元素,找到callback一致的,然后删除它。由于使用的是splice方法,会直接修改原始数组,因此需要手工对i和length再做一次调整。
Amplify使用示例

官方提供的其中一个使用示例是:

amplify.subscribe( "dataexample", function( data ) {
  alert( data.foo ); // bar
});

//...

amplify.publish( "dataexample", { foo: "bar" } );

Salin selepas log masuk

结合前面的源码部分,是否对发布/订阅这一设计模式有了更明确的体会呢?
补充说明

你可能也注意到了,AmplifyJS所实现的典型的发布/订阅是同步的(synchronous)。也就是说,在运行amplify.publish(topic)的时候,是会没有任何延迟地把某一个topic附带的所有回调,全部都运行一遍。
结语

Pub/Sub是一个比较容易理解的设计模式,但非常有用,可以应对大型应用的复杂逻辑。本文简析的AmplifyJS是我觉得写得比较有章法而且简明切题(针对单一功能)的JavaScript库,所以在此分享给大家。

Label berkaitan:
sumber:php.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan