Backbone: Kelas Tulang Punggung dan ES6

Dibuat pada 7 Apr 2015  ·  63Komentar  ·  Sumber: jashkenas/backbone

Dengan perubahan terakhir pada spesifikasi kelas ES6 (detail di sini ), tidak mungkin lagi menggunakan kelas ES6 dengan Backbone tanpa membuat kompromi yang signifikan dalam hal sintaksis. Saya telah menulis deskripsi lengkap tentang situasi di sini (pastikan untuk mengklik komentar di bagian bawah untuk opsi mitigasi tambahan), tetapi pada dasarnya tidak ada cara untuk menambahkan properti ke instance subclass sebelum orang tua subclass konstruktor yang dijalankan.

Jadi ini:

class DocumentRow extends Backbone.View {

    constructor() {
        this.tagName =  "li";
        this.className = "document-row";
        this.events = {
            "click .icon":          "open",
            "click .button.edit":   "openEditDialog",
            "click .button.delete": "destroy"
        };
        super();
    }

    initialize() {
        this.listenTo(this.model, "change", this.render);
    }

    render() {
        //...
    }
}

tidak lagi valid dalam spesifikasi ES6 akhir. Alih-alih, Anda secara efektif memiliki 3 opsi (tidak terlalu menarik) jika Anda ingin mencoba membuatnya berfungsi:

Lampirkan semua properti sebagai fungsi

Backbone memungkinkan ini, tetapi rasanya bodoh untuk menulis sesuatu seperti ini:

class DocumentRow extends Backbone.View {

    tagName() { return "li"; }

    className() { return "document-row";}

    events() {
        return {
            "click .icon":          "open",
            "click .button.edit":   "openEditDialog",
            "click .button.delete": "destroy"
        };
    }

    initialize() {
        this.listenTo(this.model, "change", this.render);
    }

    render() {
        //...
    }
}

dibandingkan dengan sintaks ekstensi saat ini

Jalankan konstruktor dua kali

Saya tidak melihat ini sebagai opsi nyata karena masalah yang akan menyebabkan menjalankan inisialisasi untuk kedua kalinya dengan cid yang berbeda, dll.

Berikan semua properti sebagai opsi default ke konstruktor superclass

Ini disarankan oleh seorang komentator di blog saya dan mungkin merupakan opsi paling praktis saat ini. Ini terlihat seperti ini:

class MyView extends Backbone.View {
  constructor(options) {
    _.defaults(options, {
      // These options are assigned to the instance by Backbone
      tagName: 'li',
      className: 'document-row',
      events: {
        "click .icon": "open",
        "click .button.edit": "openEditDialog",
        "click .button.delete": "destroy"
      },
      // This option I'll have to assign to the instance myself
      foo: 'bar'
    });


    super(options);


    this.foo = options.foo;
  }
}

Karena semua opsi saat ini melibatkan kompromi yang jelas terkait dengan sintaks ekstensi Backbone saat ini, akan sangat bagus jika solusi yang lebih baik dapat dikembangkan. Saya tidak sepenuhnya yakin seperti apa tampilannya, tetapi satu ide yang muncul di benak saya saat saya menulis untuk blog saya adalah penambahan fungsi "properti" yang akan menampilkan hash properti. Konstruktor kemudian dapat menjalankan fungsi itu dan menambahkannya ke instance sebelum pemrosesan lain yang dilakukan oleh konstruktor.

change

Komentar yang paling membantu

Membaca ini saya menemukan https://github.com/epicmiller/es2015-default-class-properties pendekatan yang baik. Saat mencoba, saya menyadari Backbone memiliki dukungan bawaan untuk ini. Sebagai contoh:

class MyModel extends Backbone.Model.extend({
   idAttribute: 'id'
}) {
   // ...
};

Kode di atas akan mengatur MyModel.prototype.idAttribute dengan benar. Perhatikan, untuk TypeScript, file deklarasi perlu sedikit disesuaikan untuk mengembalikan antarmuka fungsi konstruktor, tapi itu detail yang tidak relevan dengan pengguna ES6...

Semua 63 komentar

Ya ini pasti merepotkan. Terima kasih telah melakukan kerja keras.

Saya kira moral dari ceritanya adalah jangan gunakan kelas ES6 dengan Backbone, setidaknya sampai dukungan properti statis mendarat. Dari opsi fallback yang Anda usulkan, solusi pilihan saya adalah mendefinisikan string/objek sebagai nilai pengembalian. Bagian penting dari desain API Backbone adalah dalam string dan objek yang dibagikan prototipe ini, dan itu akan mengotori API untuk meminta pengembang menetapkan setiap properti ke instance di konstruktor (belum lagi boros memori).

Selain konsistensi, apakah ada alasan untuk menggunakan kata kunci class dengan Backbone lebih dari extend ?

Posting blog yang bagus. Saya bertanya-tanya bagaimana kelas ES6 dan Backbone akan bermain bersama. Adapun solusi Anda:

  1. Lampirkan semua properti sebagai fungsi : Saya tidak terlalu menentang ini. Ini tidak sebersih mengatur objek secara langsung pada prototipe, tetapi saya telah melihat banyak kode tersandung pada objek prototipe yang bermutasi. Cara ini kebal, itulah sebabnya saya pikir ES6 memilih untuk tidak menyertakan properti kelas.
  2. Lewati semua properti sebagai opsi default : Bukankah ini cara Anda melakukan sesuatu dalam bahasa yang lebih klasik? Saya merasa ini adalah solusi yang bahkan kurang bersih daripada yang di atas.
  3. Jalankan konstruktor dua kali : Ick.

Saya kira moral dari ceritanya adalah jangan gunakan kelas ES6 dengan Backbone, setidaknya sampai dukungan properti statis mendarat.

Bahkan properti kelas muncul setelah panggilan super() . :kecewa:

Selain konsistensi, apakah ada alasan untuk menggunakan kata kunci class dengan Backbone over extend?

Saya membahas ini di posting blog. Praktis? Tidak. Secara teori itu akan memungkinkan Backbone dalam jangka panjang untuk mengurangi kode dan konsep tambahan, tetapi secara realistis itu akan menjadi setidaknya beberapa tahun sebelum kelas ES6 didukung secara luas di semua browser yang relevan tanpa transpiling, dan pengurangan kode akan menjadi berikutnya untuk apa-apa.

Tapi jangan meremehkan aspek konsistensi. Jika ini menjadi "cara" melakukan pemrograman Berorientasi Objek dalam JavaScript (tampaknya diberikan standarisasi ini dari Ember/Angular/React/Typescript/Aurelia dll), Backbone tidak menggunakannya akan menjadi kurva pembelajaran tambahan untuk perpustakaan relatif terhadap pilihan lain. Khusus untuk pengembang Junior. Saya tidak yakin itu perlu perubahan. Tapi itu bukan hanya untuk konsistensi "hobgoblin dari pikiran kecil" yang bertele-tele.

Saya setuju dengan @akre54 dan @jridgewell bahwa pendekatan "lampirkan semua properti sebagai fungsi" mungkin adalah yang terbaik dari opsi yang diusulkan. FWIW, saya ingat bahwa ketika saya awalnya belajar tulang punggung sebagai pendatang baru relatif js, saya agak bingung dengan properti "statis" ini dan bagaimana mereka harus digunakan.

ES7 akan memiliki properti kelas yang benar, saya kira https://Gist.github.com/jeffmo/054df782c05639da2adb

Proposal ES7 hanya itu, proposal berbasis komunitas yang sangat awal. Sama sekali tidak jelas itu akan benar-benar menjadi bagian dari spesifikasi resmi. Implementasi saat ini menyebabkan properti ditambahkan ke instance SETELAH konstruktor berjalan, sehingga tidak membantu dengan Backbone. (lihat tautan jridgewell di atas atau coba sendiri dengan Babel 5.0.0)

@jridgewell Saya merujuk ke bagian ini dari posting @benmccormick :

React Developers telah mencatat masalah yang sama dengan inisialisasi properti yang dihadapi pengguna Backbone. Sebagai bagian dari React versi 0.13, mereka mendukung sintaks inisialisasi properti khusus untuk kelas, yang pada akhirnya dapat distandarisasi. Ada info lebih lanjut tentang itu di utas ESDiscuss ini . Standar ini masih dalam pengerjaan tetapi versi dukungan eksperimental tersedia di Babel 5.0.0. Sayangnya versi itu mendefinisikan properti kelas sebagai yang dipakai setelah konstruktor superclass dijalankan, jadi ini tidak menyelesaikan masalah Backbone di sini.

Lihat misalnya wycats' js-decorators strawman atau proposal kelas harmoni asli (digantikan).

Saya mungkin menyarankan agar kita menggunakan getter dengan properti kelas:

class Row extends Backbone.View {
  get tagName() { return 'li'; }
}

Sebagai upaya terakhir yang mutlak, kami dapat memeriksa misalnya atau alat peraga statis dengan pembantu ala _.result :

_.instOrStaticVar = function(instance, property) {
  if (instance == null) return void 0;
  var value = instance[property] || instance.constructor[property];
  return _.isFunction(value) ? value.call(instance) : value;
}

Ya, tapi:

Sayangnya versi itu mendefinisikan properti kelas sebagai yang dipakai setelah konstruktor superclass dijalankan, jadi ini tidak menyelesaikan masalah Backbone di sini.

Jadi, ES5 akan:

// ES6
class View extends Backbone.View {
  tagName = 'li';

  constructor() {
    // Do anything that doesn't touch `this`
    super();
    // Do anything that touches `this`
  }
}

// ES5
function View() {
  // Do anything that doesn't touch `this`
  Backbone.View.apply(this, arguments);

  // Add class properties
  this.tagName = 'li';

  // Do anything that touches `this`
}
View.prototype = _.create(Backbone.View.prototype, {
  constructor: View
});

Elemen kami masih akan dibangun sebelum kami mendapat perubahan untuk mengatur variabel instan.

Lihat misalnya wycats' js-decorators strawman...

Bisakah Anda menjelaskan bagaimana dekorator akan menerapkan?

Saya mungkin menyarankan agar kita menggunakan getter dengan properti kelas:

:+1:. Saya melihat itu sebagai perahu yang sama dengan melampirkan semua properti sebagai fungsi . Tidak sebersih apa yang kita miliki saat ini, tetapi sangat dapat diterima dan bukti mutasi.

Sebagai upaya terakhir mutlak, kami dapat memeriksa misalnya atau alat peraga statis dengan pembantu ala _.hasil:

Itu bisa menarik...

Anda dapat melakukan:

class MyView extends Backbone.View {
  constructor() {
    super({ tagName: 'h1' });
    this.el.textContent = 'Hello World';
  }
}

@thejameskyle Itu Pass semua properti sebagai opsi default ke opsi

Alih-alih mengandalkan super() untuk menyiapkan kelas, Anda cukup memiliki fungsi init() atau sesuatu.

class DocumentRow extends Backbone.View {

    constructor() {
        super();
        this.tagName =  "li";
        this.className = "document-row";
        this.events = {
            "click .icon":          "open",
            "click .button.edit":   "openEditDialog",
            "click .button.delete": "destroy"
        };
        this.init();
    }

    initialize() {
        this.listenTo(this.model, "change", this.render);
    }

    render() {
        //...
    }
}

@milesj hmm? Itu akan langsung error dengan spesifikasi kelas ES6 akhir

Di kelas turunan, Anda harus memanggil super() sebelum Anda dapat menggunakan ini

Bahkan jika berhasil, Anda tidak pernah benar-benar memanggil konstruktor Backbone dan tidak akan mendapatkan kode inisialisasi.

Lihat tautan ini dari posting pertama saya: http://www.2ality.com/2015/02/es6-classes-final.html

@milesj : super() sebelum setting this.tagName atau sejenisnya. Dan, karena kami memastikan elemen dalam konstruktor View, kami telah membuat elemen sebelum kami menetapkan this.tagName .

@milesj itu masih tidak diizinkan saat Anda melakukan subkelas.

@jridgewell Oh maaf, saya melewatkannya. Itu memang tampak seperti pilihan paling alami. Saya berbicara dengan jeffmo dan sebmck tentang ini.

Untuk memberikan beberapa latar belakang, alasannya adalah karena untuk mendukung perluasan tipe asli (yaitu Array) this tidak ditentukan sampai Anda memanggil metode super() . Kalau tidak, Anda mengalami masalah inisialisasi di DOM (dan mungkin di tempat lain).

@jridgewell @thejameskyle Kemudian cukup panggil super() terlebih dahulu (contoh yang diperbarui). Saya benar-benar tidak melihat masalah di sini karena saya telah melakukan hal yang sama di kelas ES6 saya. Cukup pindahkan logika konstruktor tampilan ke metode init() .

Itu banyak kode yang sangat mahal untuk dijalankan dua kali.

@milesj apakah Anda membaca posting blog asli? Menjalankan super pertama berarti properti tidak diproses. Lihat di sini untuk penjelasan mendalam yang lengkap: http://benmccormick.org/2015/04/07/es6-classes-and-backbone-js/

Ya, saya sudah membacanya, dan saya masih penasaran mengapa ini bukan solusi. Semua orang terus berbicara tentang konstruktor pandangan yang perlu dipanggil, tetapi itu belum tentu demikian. Mengapa sesuatu seperti berikut ini bukan solusi (walaupun agak dibuat-buat)?

var View = Backbone.View = function(options) {
    this.cid = _.uniqueId('view');
    // extend()ing options is no longer needed if properties are set directly
};

View.prototype.setup = function() {
    this._ensureElement();
    this.initialize.call(this, arguments);
};

class DocumentRow extends Backbone.View {
    constructor() {
        super();
        this.tagName =  "li";
        this.className = "document-row";
        this.events = {
            "click .icon":          "open",
            "click .button.edit":   "openEditDialog",
            "click .button.delete": "destroy"
        };
        this.setup(...arguments);
    }
}

Saya menduga karena kompatibilitas mundur dengan non-ES6?

Maka kelas View default tidak akan berfungsi karena konstruktor tidak pernah memanggil #setup . Dan, memaksa panggilan subkelas selain super() akan sangat mengganggu.

Itu adalah masalah yang harus dihadapi semua kelas ES6, bukan hanya Backbone. Saya pribadi menyelesaikannya dengan menggunakan spesifikasi properti kelas Babel ES7.

@milesj Seperti yang dinyatakan sebelumnya, properti kelas ES7 tidak menyelesaikan masalah ini karena tidak dipakai hingga akhir konstruktor.

Saya berbicara dengan jeffmo dan sebmck tentang melakukan ini:

class Root {
  rootProp = 'root';
  constructor() {
    console.log('Root', this.rootProp);
    console.log('Root', this.derivedProp);
  }
}

class Derived extends Root {
  derivedProp = 'derived';
  constructor() {
    super();
    console.log('Derived', this.rootProp);
    console.log('Derived', this.derivedProp);
  }
}

Desugaring ke:

function Root() {
  this.rootProp = 'root';
  console.log('Root', this.rootProp);
  console.log('Root', this.derivedProp);
}

function Derived() {
  super();
  this.derivedProp = 'derived';
  console.log('Derived', this.rootProp);
  console.log('Derived', this.derivedProp);
}

Tapi itu masih tidak memperbaiki masalah di sini dan menyebabkan inkonsistensi:

new Derived();
// >> 'Root' 'root'
// >> 'Root' undefined
// >> 'Derived' 'root'
// >> 'Derived' 'derived'

Itu adalah masalah yang harus dihadapi semua kelas ES6, bukan hanya Backbone.

Hm?

Saya pribadi menyelesaikannya dengan menggunakan spesifikasi properti kelas Babel ES7.

Anda akan memiliki banyak elemen DIV tanpa className s. Lihat poin terakhir dari https://github.com/jashkenas/backbone/issues/3560#issuecomment -90739676, https://github.com/jashkenas/backbone/issues/3560#issuecomment -91601515 dan https://github .com/jashkenas/backbone/issues/3560#issuecomment -98827719.

Jadi begitu. Dalam hal ini, saya sarankan menggunakan opsi "Luluskan semua properti sebagai opsi default ke konstruktor superclass", atau baris terakhir tentang membuat metode "properti" (yang tidak menyentuh konstruktor).

class DocumentRow extends Backbone.View {
    loadProperties() {
        return {
            tagName: 'li',
            className: 'document-row',
            events: {
                "click .icon": "open",
                "click .button.edit": "openEditDialog",
                "click .button.delete": "destroy"
            },
            foo: 'bar'
        };
    }
}

// Contrived example
var View = Backbone.View = function(options) {
    this.cid = _.uniqueId('view');
    options || (options = {});
    _.extend(this, this.loadProperties(), _.pick(options, viewOptions));
    this._ensureElement();
    this.initialize.apply(this, arguments);
};

Saya melakukan sesuatu yang serupa di Toolkit, yang dapat dilihat di sini: https://github.com/titon/toolkit/issues/107

Hai.

Jika saya memahami dengan benar diskusi di sini - pengembang Backbone sedang mendiskusikan solusi dan praktik terbaik, tetapi tidak berniat untuk benar-benar membuat perubahan pada inti BB untuk menangani masalah ini? (Saya tidak menyarankan mereka harus melakukannya, saya juga tidak tahu apa perubahan itu). Dengan kata lain, apakah saran untuk menggunakan semua properti sebagai fungsi atau mendapatkan kata akhir pada topik? Terima kasih.

@gotofritz Kami sedang mendiskusikan solusi karena solusi ES6 untuk memaksa semua properti untuk hidup di instans tidak berskala. Sistem kelas Backbone melakukan hal yang benar di sini.

Ada beberapa diskusi tentang menambahkan properti prototipe statis ke kelas ES7 tetapi sejauh ini tidak ada yang konkret. Sementara itu saya akan mengatakan tetap menggunakan Backbone extend .

Terima kasih. Saya akan mencoba kelas ES6 sedikit lebih lama... :-)

Untuk kepentingan siapa pun yang menemukan ini, dalam praktiknya saya menemukan "Luluskan semua properti sebagai opsi default ke konstruktor superclass" lebih baik - misalnya, aplikasi kami memiliki rute dinamis (dilokalkan) yang perlu diteruskan pada waktu instantiasi, dan memiliki metode route() tidak berfungsi. Sedangkan berikut ini tidak

class Router extends Backbone.Router {

 constructor (localizedRoutes) {
    _.defaults(localizedRoutes, {
        "nonLocalizedRouteA/": "routeA"
        "*actions": "defaultRoute"
     });
 super({ routes: localizedRoutes });
}

Saya baru saja melihat ini, dan saya pikir kedua solusi tidak berfungsi untuk properti idAttribute yang dimiliki Model. Metode tidak akan berfungsi karena Backbone menggunakan model.idAttribute untuk mengakses properti; Dan konstruktor model tampaknya tidak mendukung penambahan properti sebagai opsi sama sekali.

Saya pikir kedua solusi tidak berfungsi untuk properti idAttribute

Tangkapan yang bagus, saya akan mengerjakan PR yang menangani ini. Sementara itu, Anda dapat menggunakan notasi pengambil untuk menyediakan idAttribute kustom (dan cidPrefix ):

class Model extends Backbone.Model {
  get idAttribute() {
    return '_id';
  }

  get cidPrefix() {
    return '__c';
  }
}

Metode tidak akan berfungsi karena Backbone menggunakan model.idAttribute untuk mengakses properti

get idAttribute() { return '_id'; } adalah metode pengambil, yang diakses seperti properti biasa. this.idAttribute === '_id'; .

Ini mulai terdengar seperti penulisan ulang besar-besaran diperlukan. Tulang punggung v2 mungkin?

Ini mulai terdengar seperti penulisan ulang besar-besaran diperlukan. Tulang punggung v2 mungkin?

Tidak sama sekali, kami sudah mendukung subkelas ES6 (dengan pengecualian Models ). Saya pikir akan menarik jika seseorang menjelajahi saran properti statis @ akre54 , tetapi itu pun tidak perlu dengan dua solusi di pos asli.

@jridgewell , terima kasih banyak atas solusi cepatnya!

Dekorator disebutkan di atas di utas ini (khususnya proposal Yehuda Katz ), dan belum diputuskan apakah itu akan menyelesaikan masalah ini.

Saya hanya bermain-main dengan mereka seperti yang diusulkan, dan Anda dapat menulis dekorator seperti ini:

function props(value) {
    return function decorator(target) {
        _.extend(target.prototype, value);
    }
}

dan kemudian contoh yang kami gunakan dapat ditulis seperti ini

@props({
      tagName: 'li',
      className: 'document-row',
      events: {
        "click .icon": "open",
        "click .button.edit": "openEditDialog",
        "click .button.delete": "destroy"
      }
    })
class DocumentRow extends Backbone.View {

    initialize() {
        this.listenTo(this.model, "change", this.render);
    }

    render() {
        //...
    }
}

Ini tampaknya bekerja dengan baik untuk saya. Dekorator diterapkan ke kelas sebelum konstruktor kelas dieksekusi. Ini hanya versi deklaratif dari perkataan

class DocumentRow extends Backbone.View {

    initialize() {
        this.listenTo(this.model, "change", this.render);
    }

    render() {
        //...
    }
}
_.extend(DocumentRow.prototype, {
      tagName: 'li',
      className: 'document-row',
      events: {
        "click .icon": "open",
        "click .button.edit": "openEditDialog",
        "click .button.delete": "destroy"
      }
})

Sebenarnya, saya belum mengujinya, tetapi Anda mungkin dapat menjadikan seluruh fungsi perluasan tulang punggung sebagai dekorator jika Anda menginginkan alat peraga statis dan prototipe.

Sayangnya ini hanya proposal untuk saat ini, tetapi Babel mendukungnya di balik bendera eksperimental, jadi jika orang merasa ingin bertualang, ini adalah solusi yang mungkin di sini.

@benmccormick , teknik dekorator bekerja dengan baik untuk saya. Selain ini hanya menjadi proposal untuk saat ini, apakah ada kekhawatiran lain tentang pergi dengan pendekatan ini?

@andrewrota Saya benar-benar menulis posting blog yang menindaklanjuti hal ini sekarang (sedang membaca utas ini ketika Anda berkomentar). Itu adalah "selain" yang besar, tetapi saya pribadi tidak melihatnya. Saya benar-benar berpikir kita bisa melakukan lebih baik dari apa yang saya jelaskan di atas, dan membuat beberapa antarmuka baru yang bagus untuk Backbone dengan dekorator.

Lihat inti dari @StevenLangbroek yang membuat saya berpikir tentang ini awalnya: https://Gist.github.com/StevenLangbroek/6bd28d8201839434b843

Berikut adalah pratinjau dari posting tindak lanjut yang saya buat: http://benmccormick.org/2015/07/06/backbone-and-es6-classes-revisited/ Diperbarui dengan tautan permanen sekarang

Ini akan pindah ke url permanen awal minggu ini kapan-kapan. Tetapi ringkasan dasar dari utas ini dan apa yang telah saya pelajari adalah:

Ada 3 pendekatan untuk membuat properti Backbone bekerja dengan spesifikasi kelas ES6 saat ini (2 yang pertama membutuhkan #3684 untuk dianggap didukung sepenuhnya):

  1. Berikan semua properti ke super di konstruktor
  2. Perlakukan semua properti sebagai metode
  3. Tambahkan properti langsung ke prototipe setelah kelas dideklarasikan

Saya masih melihat semua ini sebagai ekspresi yang membatasi sampai batas tertentu. Tapi saya kira masalahnya akan sedikit banyak terpecahkan jika dekorator menjadi spesifikasi resmi. Dengan dekorator ada 2 opsi lagi.

  1. Tambahkan dekorator alat peraga yang mengambil alat peraga di bagian atas kelas dan menambahkannya ke prototipe
  2. Buat beberapa dekorator tujuan khusus yang memungkinkan antarmuka yang lebih ekspresif/halus.

Saya tidak berpikir salah satu dari solusi ini memerlukan modifikasi tambahan untuk Backbone selain #3684, tetapi akan ada peran yang menarik untuk perpustakaan backbone-decorator jika/ketika dekorator menjadi standar.

Akan sangat senang jika ada umpan balik pada posting sebelum saya mempublikasikannya pada hari Senin/Selasa.

@benmccormick Saya pikir dekorator dievaluasi sebelum konstruksi terjadi, terima kasih atas koreksinya. Saya akan memperbarui intinya sebentar lagi. juga: terima kasih satu juta untuk menyebutkan di posting blog :) ping saya di twitter ketika Anda mempublikasikannya? :+1:

Kita bisa menggunakan sintaks yang sama untuk modelEvents dan collectionEvents di Marionette, hanya saja tidak untuk trigger. Itu dapat diekspos melalui dekorator kelas (seperti tagName dan templat di posting blog Anda), tetapi saya berpikir: tidak bisakah kita menggunakan properti statis untuk ini? Atau apakah itu tidak berfungsi di Backbone?

Saya mengerti dekorator masih tahap 0, tapi saya pikir mereka akan menjadi peningkatan yang bagus dalam cara kami menulis aplikasi Backbone, terutama dekorator metode di atas hash acara, itu adalah jenis gaya pemrograman yang membuat saya lebih suka menelan daripada mendengus juga.

@StevenLangbroek lihat di atas untuk diskusi tentang properti statis.

Sintaks seperti yang ditentukan saat ini membuat properti lokal pada setiap instance daripada menambahkan ke prototipe. Properti tersebut ditambahkan setelah konstruktor super dijalankan.

@benmccormick , posnya terlihat bagus dan saya pikir melakukan pekerjaan yang baik dalam menjelaskan trade off dengan masing-masing opsi. Pada titik ini, saya sangat menyukai pendekatan dekorator tujuan khusus, dan itu tampaknya menjadi pendekatan terbaik dengan asumsi dekorator membuatnya menjadi spesifikasi.

Haruskah dekorator @benmccormick memanggil _#extend dengan konstruktor bukan prototipe dan kemudian metode _.instOrStaticVar @akre54 digunakan sebagai pengganti _#result ? Saya menyadari itu akan menjadi perubahan yang melanggar, tetapi tampaknya lebih bersih seperti itu IMHO. Seperti yang ditunjukkan @akre54, properti yang didefinisikan seperti itu adalah string dan objek yang dibagikan prototipe (yaitu dibagikan di semua instance) oleh karena itu mereka harus diakses melalui kelas, bukan?

Saya melangkah lebih jauh dan membuat properti kelas kerja seperti yang kita butuhkan. Properti kelas dapat dianotasi juga, dan kita dapat membuat dekorator khusus, yang menempelkan properti yang didekorasi ke prototipe.

class TodoView extends Backbone.View {
  <strong i="6">@protoprop</strong>
  static tagName = 'li';
}

function protoprop(target, name, descriptor) {
  target.prototype[name] = descriptor.initializer()
}

Lihat Babel REPL dengan contoh. Itu mengandalkan hal-hal eksperimental, tetapi berfungsi.

@just-boris seperti yang dibahas dalam komentar blog saya, perilaku yang Anda lihat di sana adalah detail implementasi penanganan Babel terhadap properti kelas dan spesifikasi dekorator. Perilakunya tidak ditentukan dalam proposal apa pun saat ini. Jika Anda ingin melakukan hal-hal seperti itu, Anda ingin membuat masalah di sini dan/atau di sini untuk membuat dekorator di properti kelas menjadi perilaku standar. Jika tidak, apa yang Anda lakukan dapat (dan mungkin akan) rusak kapan saja.

@benmccormick wycats/javascript-decorators sudah memiliki definisi tambahan mengenai properti initializers .

Perhatian utama di sana bahwa inisialisasi properti adalah deskriptor biasa, serta metode kelas, sehingga dekorator dapat membungkusnya juga. Saya tidak melihat alasan untuk khawatir, sementara spesifikasi di bagian itu tetap tidak berubah

Ah sangat keren, saya belum melihatnya. Terima kasih telah menunjukkan hal itu.

Pada hari Senin, 21 Sep 2015 jam 11:29, Boris Serdiuk [email protected]
menulis:

@benmccormick https://github.com/benmccormick
https://github.com/wycats/javascript-decorators sudah memiliki ekstra
definisi tentang inisialisasi properti
https://github.com/wycats/javascript-decorators/blob/master/INITIALIZER_INTEROP.md
.

Perhatian utama di sana bahwa inisialisasi properti adalah deskriptor biasa,
serta metode kelas, sehingga dekorator dapat membungkusnya juga. saya tidak melihat
alasan untuk khawatir, sementara spesifikasi di bagian itu tetap tidak berubah


Balas email ini secara langsung atau lihat di GitHub
https://github.com/jashkenas/backbone/issues/3560#issuecomment -142015454
.

Hanya ingin tahu pro/kontra menggunakan https://github.com/typhonjs/backbone-es6 vs teknik metode yang disarankan oleh @benmccormick.

Btw, terima kasih @benmccormick untuk posting blog yang luar biasa!

Selain Proposal (#121) pull-request yang dilampirkan di sini metode properties beraksi https://github.com/dsheiko/backbone-abstract/tree/master/demo-es6/src/Js
Seperti yang disebutkan @akre54, Justin telah mengusulkan solusi serupa ( metode preInitialize ). Saat sudah menggunakannya di cabang saya, itu benar-benar memecahkan masalah bagi saya. Tampaknya juga berguna dalam TypeScript meskipun mereka tidak melarang properti kelas deklaratif.

PS preInitialize terdengar lebih umum dan karenanya lebih baik dalam konteks ini. Meskipun, lebih seperti preConstruct jika kita memanggil metode sebelum semua pekerjaan konstruktor

Benar-benar berharap kita akan melihat proposal properti kelas baru yang menetapkannya pada prototipe. Tampaknya banyak yang terlibat dengan proposal khawatir tentang implikasinya, tetapi saya merasa sangat tidak konsisten bahwa metode kelas langsung dilampirkan ke prototipe, sementara proposal jeffmo menempatkannya di konstruktor.

Seandainya mereka pergi dengan melampirkan properti langsung ke prototipe, Anda akan dapat memigrasikan hampir semua kode React/Backbone ke kelas ES2015.

posting blog yang luar biasa @benmccormick !! akan menggunakan dekorator itu di proyek saya

@benmccormick , saya menyiapkan cara lain untuk mendeklarasikan kelas dengan properti default, lihat: https://github.com/epicmiller/es2015-default-class-properties

Ini berjalan secara normal di lingkungan apa pun yang secara asli mendukung kelas, mentranspilasikan dengan baik, dan terlihat _far_ lebih bagus daripada mendefinisikannya di konstruktor atau setelah deklarasi. Dengan proposal untuk dekorator dan properti kelas yang akan datang untuk ES2016/ES2017, ini mungkin lebih merupakan latihan akademis daripada solusi jangka panjang untuk Backbone, tetapi sesuatu seperti ini jelas merupakan pilihan yang layak jika 2-3 tahun terlalu lama tunggu.

Nah, soalnya Class Properties masih tahap 1 di sistem tahap proposal Ecmascript. Saya tidak tahu mengapa, karena sepertinya gimme dalam hal "apa yang didapat pengguna". Tentu saja, saya tidak tahu hal-hal seperti apa yang mungkin rusak di bawah tenda baik secara sintaksis maupun dalam hal implementasi referensi.

https://github.com/tc39/ecma262
https://github.com/jeffmo/es-class-fields-and-static-properties

Membaca ini saya menemukan https://github.com/epicmiller/es2015-default-class-properties pendekatan yang baik. Saat mencoba, saya menyadari Backbone memiliki dukungan bawaan untuk ini. Sebagai contoh:

class MyModel extends Backbone.Model.extend({
   idAttribute: 'id'
}) {
   // ...
};

Kode di atas akan mengatur MyModel.prototype.idAttribute dengan benar. Perhatikan, untuk TypeScript, file deklarasi perlu sedikit disesuaikan untuk mengembalikan antarmuka fungsi konstruktor, tapi itu detail yang tidak relevan dengan pengguna ES6...

@t-beckmann itu solusi yang cukup bagus - terlihat mudah dibaca dan membutuhkan sedikit perubahan. Terima kasih!

Saya menyadari utas ini sedang berlangsung 2 tahun sekarang, tetapi itu masih merupakan salah satu hasil teratas (dan satu-satunya) ketika mencari Kelas Tulang Belakang & ES6, dan saya pikir saya akan membagikan solusi potensial dengan menggunakan properti kelas yang disebutkan beberapa kali di sini .

Sekarang properti kelas berada di Tahap 2 dan tersedia secara luas dengan preset babel, saya pikir saya akan melihatnya lagi. Seperti yang dinyatakan, masalah dengan properti instance/anggota adalah bahwa properti tersebut tidak diterapkan ke prototipe sampai _after_ constructor() , tetapi banyak properti yang perlu disetel digunakan di dalam konstruktor. Properti statis diterapkan segera, tetapi (berdasarkan desain) tidak disalin ke instance kelas.

Shim berikut menyalin properti statis dari konstruktor ke instance sebelum menjalankan konstruktor (secara efektif membuat konstruktor baru, menerapkan properti, dan kemudian mengeksekusi konstruktor asli). Meskipun ini benar-benar peretasan, saya cukup senang dengan hasilnya:

Shim:

export default function StaticShim(Ctor) {
    const NewCtor = function shim(...args) {
       Object.keys(Ctor).forEach((key) => {
            if (this[key] === undefined) {
                this[key] = toApply[key];
            }
        });

        Object.assign(this, this.constructor);

        Ctor.apply(this, args);
    };

    NewCtor.prototype = Object.create(Ctor.prototype);
    NewCtor.prototype.constructor = NewCtor;

    Object.keys(Ctor).forEach((key) => {
        if (NewCtor[key] === undefined) {
            NewCtor[key] = Ctor[key];
        }
    });

    return NewCtor;
}

Dan kemudian dalam penggunaan:

class TestModel extends StaticShim(Backbone.Model) {
    static idAttribute = '_id';
    static urlRoot = '/posts';

    initialize() {
        console.log(this.url()); // Correctly logs "/posts/{id}"
    }
}

Hanya ingin meletakkannya di sini kalau-kalau itu membantu orang lain, atau ada yang punya pemikiran tentang itu. Terima kasih!

Wajib maaf untuk menghidupkan kembali masalah lama.

Apakah mungkin atau layak untuk menulis plugin babel yang mengubah deklarasi kelas ES6 untuk menggunakan Backbone.*.extend({...})?

@enzious sepertinya mungkin. Apakah itu layak terserah Anda :)

Solusi @ t-beckmann tampaknya yang paling mudah. haruskah kita mengintegrasikannya ke dalam tulang punggung itu sendiri?

Bagi saya itu terlihat tidak benar, bukankah lebih tepat untuk memiliki metode yang menetapkan idAttribute?

Selain itu, akan luar biasa jika ada dukungan Promise. yang merupakan pendekatan yang lebih asli daripada menggunakan jquery Deferred, yang secara pribadi saya ingin lihat ditinggalkan dalam Backbone.

Cerita di sini masih sangat tidak jelas untuk menyegarkan aplikasi Backbone lama untuk memanfaatkan fitur perkakas dan bahasa modern. Sangat mengecewakan melihat hal-hal seperti Symbol.iterator diimplementasikan dan tidak tersedia dalam rilis produksi.

Bagi mereka yang masih mencari jawaban yang lebih jelas untuk pertanyaan ini, saya menambahkan TypeScript ke aplikasi tulang punggung dan menemukan solusi dari komentar ini paling membantu.

Sejauh ini bekerja cukup baik, dengan kelemahan harus secara eksplisit membubuhi keterangan properti melewati dekorator daripada memiliki inferensi yang lebih bagus.

export function Props<T extends Function>(props: { [x:string]: any }) {
  return function decorator(ctor: T) {
    Object.assign(ctor.prototype, props);
  };
}

@Props({
  routes: {
    home: "home",
    about: "about",
    dashboard: "dashboard",
    blog: "blog",
    products: "products",
    accountSettings: "accountSettings",
    signOut: "signOut",
  },
})
export class Router extends Backbone.Router {
  home() {}
  about() {}
  // ...
}

@Props({
  model: CategoryModel,
  comparator: (item: CategoryModel) => item.display_value,
})
export class CategoryCollection extends Backbone.Collection<CategoryModel> {}

Contoh anotasi properti eksplisit:

image

@raffomania , @jridgewell & Co., untuk apa nilainya, tim saya mengatasi masalah ini dengan menambahkan idAttribute ke prototipe di luar kelas.

class Contoh meluas ParentExample {
// Metode kelas dll di sini
}

x.Contoh = Contoh;

x.Contoh.prototype.idAttribute = 'customIdAttr';

@kamsci saya melakukan hal yang sama di cabang ini di mana saya mengonversi kelas Backbone ke ES6

Backbone menggunakan _configuration_ hingga objek konfigurasi menjadi _declarative_. Ini bagus tapi tidak akan pernah bermain bagus dengan warisan. (Klon kelas, lalu konfigurasikan. Itu bukan warisan.)

Jika kita akan menulis kode baru menggunakan tulang punggung, tidak apa-apa untuk berpikir secara berbeda. Memotong dan menempelkan kode ES5 lalu membuatnya seolah-olah ES6 tidak berfungsi. Terus?

Saya tidak punya masalah sama sekali melewati segala sesuatu melalui objek konfigurasi. Bagaimana kami mengekspos konten konfigurasi itu, atau membuatnya lebih mudah untuk dibaca/dikerjakan, adalah masalah yang harus dipecahkan, bukan untuk ditangisi.

Tidak ada yang ingin menjalankan konstruktor dua kali. Itu konyol. Tapi, pola

Foo = BackboneThing.extend({LONG DECLARATIVE OBJECT LITERAL}) juga jelek sayang ibu. Anda semua baru saja melakukannya begitu lama sehingga Anda tidak melihat betapa jeleknya itu.

FYI: Saya memiliki proyek Marionette yang besar, dan ingin menggunakan sintaks ES6. Saya membuat trafo jscodeshift yang menerjemahkan Backbone memperluas deklarasi ke kelas ES6. Itu membuat banyak asumsi penyederhanaan, tetapi mungkin masih berguna bagi sebagian dari Anda, jika hanya sebagai titik awal. Ini mengikuti sintaks yang diusulkan oleh @t-beckmann saat saya mengalami masalah dengan dekorator.
https://Gist.github.com/maparent/83dfd65a37aaaabc4072b30b67d5a05d

Bagi saya sepertinya ada istilah yang aneh di utas ini. 'properti statis' untuk ES6 adalah properti pada konstruktor yang ada di Kelas tanpa instantiasi (Class.extend misalnya). Di utas ini 'properti statis' tampaknya merujuk ke atribut bernama pada prototipe dengan nilai 'statis' (bukan getter atau fungsi). Apakah saya sudah benar?

Untuk properti prototipe dengan nilai statis, mendeklarasikan nilai Backbone pra inisialisasi sebagai nilai pengembalian fungsi adalah transisi yang cukup mudah dan berfungsi dengan baik karena _.result melakukan seperti yang diharapkan untuk default, className, id, dll. Properti instance lain tampaknya baik-baik saja dideklarasikan di bagian atas fungsi inisialisasi seperti biasa. Masalah ini tampaknya hanya muncul karena di kelas ES6 Anda tidak dapat mendefinisikan properti prototipe dengan nilai statis saat ini, hanya getter, setter, dan fungsi.

Either way, konstruktor/properti statis kelas (Class.extend) tidak diwariskan di tulang punggung seperti di ES6. Backbone menyalin properti statis kelas ke kelas/konstruktor baru setiap kali melakukan fungsi perluasan daripada mewarisi properti ini seperti yang dilakukan ES6. Saya telah membuat pr untuk memperbaikinya di sini https://github.com/jashkenas/backbone/pull/4235

Saya akan menghargai beberapa komentar/umpan balik, saya tidak yakin apakah itu akan merusak apa pun, saya sudah mengujinya sedikit dan tampaknya berfungsi dengan baik. Kelas backbone mewarisi Class.extend setelahnya daripada menyalin referensi ke setiap konstruktor baru.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat