Kisah Belajar Mandiri JL #25

Seri [Kisah Belajar Mandiri JL]

[Net Ninja] JS Regular Expressions
[Net Ninja] Vue JS2 (Part1, Part2, Part3)
[Net Ninja] Vuex
[Net Ninja] Python3 (Part1, Part2, Part3)
[Net Ninja] Django (Part1, Part2, Part3)
[Net Ninja] Sass (Part1, Part2)
[Sean Larkin] Webpack4 (Part1, Part2, Part3, Part4)
[Sarah Drasner] VueJS (Part1, Part2, Part3, Part4, 
                       Part5, Part6, Part7)
[Evan You] Advanced VueJS (Part1(current), Part2, Part3,
                           Part4, Part5, Part6, Part7)

🌲 Ini adalah bagian pertama dari ringkasan saya tentang “FrontendMasters.

[1. Reactivity]
    1-1. Introducing Reactivity
    1-2. Challenge 1: Getters & Setters
    1-3. Challenge 2: Dependency Tracker
    1-4. Challenge 3: Mini Observer

[ 1–1. Memperkenalkan Reaktivitas ]

Bagaimana cara Vue melacak perubahan? = Bagaimana perubahan keadaan mencerminkan perubahan di DOM

Mari kita mulai dengan menulis program yang sangat sederhana. Kami memiliki variabel a yang memiliki nilai 3. Atasan Anda memberi Anda persyaratan: bahwa nilai variabel b harus selalu 10 kali nilai a.

let a = 3;
let b = a * 10;
console.log(b) // 30

> Semuanya terlihat bagus.

a = 4;
console.log(b) // 30

› Nanti diminta ganti a ke 4. Sekarang jadi tidak sinkron, karena lupa update b! ('b' tidak diperbarui secara otomatis karena bersifat prosedural. Tidak menjaga hubungan tetap sinkron.)

b = a * 10;
console.log(b) // 40

> Tetap. Namun, sekarang kami memiliki pengulangan dan potensi sumber bug kapan pun kami perlu mengubah a.

Daripada harus memperbarui b secara manual setiap kali Anda mengubah a, kami ingin hubungan tersebut bersifat deklaratif. Jadi, kami menyadari bahwa yang sebenarnya diinginkan manajer Anda untuk Anda buat adalah spreadsheet.

› Nilai sel B1 dinyatakan secara deklaratif dengan rumus, sehingga diperbarui secara otomatis setiap kali Anda mengedit sel A1.

Bagaimana kita mengungkapkannya dalam istilah pemrograman?

onAChanged(() => {
  b = a * 10
})

› Untuk memenuhi persyaratan program kita dengan cara yang paling sederhana, mari kita asumsikan kita mengimplementasikan fungsi ajaib ini: onAChanged. Anda dapat menganggapnya sebagai pengamat, panggilan balik acara, langganan — tidak masalah. Yang penting sekarang adalah b akan selalu memenuhi persyaratan 10 kali a— selama kita mengimplementasikan fungsi ini dengan benar.

Sebelum kita mendalami cara mengimplementasikan fungsi tersebut, mari kita terjemahkan masalahnya ke dalam sesuatu yang lebih dekat dengan pengembangan web.

<span class="cell b1"></span>
document
  .querySelector(‘.cell.b1’)
  .textContent = state.a * 10

› Kami ingin memastikan sel 'b1' selalu tersinkronisasi. Sama seperti kode imperatif awal, kita harus mengambil DOM dan memperbarui konten teks sel.

<span class="cell b1"></span>
onStateChanged(() => {
  document
    .querySelector(‘.cell.b1’)
    .textContent = state.a * 10
})

› Untuk menjadikannya deklaratif, cukup gabungkan dengan fungsi ajaib kami!

<span class="cell b1">
  {{ state.a * 10 }}
</span>
onStateChanged(() => {
  view = render(state)
})

Dengan mengabstraksi lebih jauh, pada dasarnya kita telah membuat pustaka Vue.

  • Lebih jauh lagi, mari ubah markup menjadi template. Template biasanya dikompilasi ke dalam fungsi render, sehingga kode JavaScript kita dapat disederhanakan.
  • Templat (atau fungsi render JSX) adalah konstruksi yang memungkinkan kita mendeklarasikan hubungan antara status dan tampilan. Dengan kata lain, templatnya setara dengan rumus di spreadsheet.
onStateChanged(() => {
  view = render(state)
})

Representasi internal “view = render(state)” ini adalah abstraksi tingkat tinggi tentang cara kerja semua sistem rendering Vue.

Penugasan ke view ini dapat ditafsirkan secara berbeda berdasarkan cara Anda melihatnya. Dalam konteks DOM virtual, kita dapat menganggap view sebagai pohon DOM virtual baru, namun secara lebih umum, lewati saja DOM virtual dan anggap ini sebagai penerapan efek samping dari mutasi DOM.

Agar adil, ini hanya separuh gambarannya: ada juga pertanyaan tentang bagaimana memetakan masukan pengguna secara deklaratif untuk menyatakan perubahan, bukan? Dalam istilah Siklus, itulah separuh yang memetakan Niat Pengguna ke Negara. Itu adalah domain yang sangat dikuasai Rx, namun mengingat panjangnya pembicaraan ini, Evan You fokus pada bagian state -> view.

Sekarang, pertanyaannya adalah “Bagaimana aplikasi mengetahui kapan harus menjalankan kembali fungsi pembaruan ini?”

onStateChanged(() => {
  view = render(state)
})

Mari kita cari tahu! Berikut adalah versi yang sangat dibuat-buat tentang cara kerjanya:

let update, state
const onStateChanged = _update => {
  update = _update
}

const setState = newState => {
  state = newState
  update()
}

› Daripada membiarkan pengguna memanipulasi keadaan secara sewenang-wenang, kami mengharuskan mereka untuk selalu memanggil fungsi untuk memanipulasi keadaan. Fungsi tersebut disebut
setState”.

…dan voila, kami telah mengimplementasikan React! (meskipun versinya sangat disederhanakan, hanya untuk menyampaikan maksudnya)

onStateChanged(() => {
  view = render(state)
})

setState({ a: 5 })

React memaksa Anda untuk memicu perubahan status setState Anda.

onStateChanged(() => {
  view = render(state)
})
state.a = 5

› Kita dapat memanipulasi status secara langsung tanpa harus memanggil setState. Di AngularJS, ia menggunakan pemeriksaan kotor yang memotong peristiwa seperti klik untuk melakukan siklus intisari dan memeriksa semua hal untuk melihat apakah ada yang berubah. Di VueJS, ia mengubah objek status menjadi reaktif. Dengan menggunakan API object.defineProperty ES5, Vue mengonversi semua properti ini menjadi getter dan setter. Dalam kasus state.a, Vue mengonversi a menjadi pengambil dan penyetel.

autorun(() => {
  console.log(state.count)
})

› Jika kita mengganti nama fungsi onStateChanged menjadi fungsi autorun, ini pada dasarnya adalah bentuk dasar dari sistem pelacakan ketergantunganyang umum digunakan di Knockout.js, Meteor Tracker , Vue.js dan MobX(pola pengelolaan status untuk React).

[ 1–2. Tantangan 1: Pengambil & Penyetel ]

PETUNJUK:

Menerapkan fungsi convert yang:

  • Mengambil Object sebagai argumen
  • Mengonversi properti Objek di tempatnya menjadi getter/setters menggunakan Object.defineProperty
  • Objek yang dikonversi harus mempertahankan perilaku aslinya, namun pada saat yang sama mencatat semua operasi get/set.

MENGHARAPKAN:

let realValue
Object.defineProperty(obj, 'foo', {
  get(){
    return 'bar'
  },
  set(newValue){
  
  }
})
const obj = { foo: 123 }
convert(obj)
obj.foo // should log: 'getting key "foo": 123'
obj.foo = 234 // should log: 'setting key "foo" to 234'
obj.foo // should log: 'getting key "foo": 234'

TEMPLAT:

<script>
function convert(obj){
  // Implement this!
</script>

LARUTAN:

[ 1–3. Tantangan 2: Pelacak Ketergantungan]

PETUNJUK:

Tugasnya adalah mengaitkan sebuah instance ketergantungan ke komputasi.

  • Buat kelas Dep dengan dua metode: depend dan notify.

›› Kaitkan perhitungan dengan ketergantungan. Perhitungan harus dianggap sebagai pelanggan dari ketergantungan.

  • Buat fungsi autorun yang menggunakan fungsi pembaru (perhitungan).

›› Saat Anda memasuki fungsinya, semuanya menjadi istimewa. Kita berada di zona reaktif.

  • Di dalam fungsi updater, Anda dapat secara eksplisit bergantung pada instance Dep dengan memanggil dep.depend()

›› Saat Anda berada di dalam zona reaktif, Anda dapat mendaftarkan dependensi.

  • Nantinya, Anda dapat memicu fungsi updater agar berjalan kembali dengan memanggil dep.notify().

MENGHARAPKAN:

const dep = new Dep()
autorun(() => {
  dep.depend()
  console.log('updated')
})
// should log: "updated"
dep.notify()
// should log: "updated"

TEMPLAT:

<script>
// a class representing a dependency
// exposing it on window is necessary for testing
window.Dep = class Dep {
  // Implement this!
}
function autorun (update) {
  // Implement this!
}
</script>

TEMPLATE DENGAN PETUNJUK:

  • JavaScript adalah utas tunggal. Pada waktu tertentu, hanya satu fungsi yang dapat dijalankan. Jadi, membuat suatu fungsi menandai dirinya sendiri sebagai fungsi yang sedang dijalankan. Dan kemudian, kita dapat mengetahui kapan saja apakah fungsi ini sedang berjalan (=apakah kita berada di dalam fungsi ini).
  • Setiap kali kita memanggil wrappedUpdate(), kode di dalam fungsi tersebut dijalankan. Kode dijamin dapat mengakses kode di luar fungsi melalui variabel global activeUpdate. Jadi, kelas ketergantungan kita dapat memiliki akses ke activeUpdate.
  • Objek Set memungkinkan Anda menyimpan nilai unik jenis apa pun, baik nilai primitif atau referensi objek.

LARUTAN:

  • Kami mendaftarkan wrappedUpdate() sebagai pembaruan aktif. Jadi, ketika ketergantungan kita berubah dan fungsi update dipanggil lagi, kita memanggil wrappedUpdate() lagi. Oleh karena itu, trik pelacakan ketergantungan kami akan tetap berfungsi pada iterasi mendatang. Jadi, ia terus mengumpulkan dependensi.

›› Ini penting, karena fungsi pembaruan kami mungkin berisi persyaratan dalam beberapa kasus. Jadi, ketika satu variabel bernilai benar, fungsi tersebut mungkin mengumpulkan dependensi tertentu. Dan ketika variabel lain dibalik, variabel tersebut mungkin mengumpulkan dependensi lain. Jadi, sistem pengumpulan ketergantungan kita perlu secara dinamis menyeimbangkan kembali ketergantungan untuk memastikan daftar ketergantungan selalu terbaru.

››› Meskipun pelacak ketergantungan kami berfungsi untuk kasus pengujian kami, pelacak ini melewatkan beberapa kasus edge. (misalnya, Bagaimana cara Anda menangani array? Bagaimana dengan properti yang baru ditambahkan?)

[ 1–4. Tantangan 3: Pengamat Mini]

PETUNJUK:

Sekaranglah waktunya untuk menggabungkan dua tantangan sebelumnya. Gabungkan dua fungsi sebelumnya, ganti nama convert() (dari Tantangan 1) menjadi observe() dan pertahankan autorun() (dari Tantangan 2):

  • observe() mengonversi properti dalam objek yang diterima dan menjadikannya reaktif. Untuk setiap properti yang dikonversi, ia akan diberi instance Dep yang melacak daftar fungsi update yang berlangganan, dan memicunya untuk dijalankan kembali ketika setter dipanggil.
  • autorun() mengambil fungsi update dan menjalankannya kembali ketika properti yang berlangganan fungsi update telah dimutasi. Fungsi update dikatakan “berlangganan” pada sebuah properti jika bergantung pada properti tersebut selama evaluasinya.

INSTRUKSI (DENGAN KATA LAIN):

  • Gunakan kelas Dep & autorun() dari Tantangan 2
  • Salin & modifikasi convert() dari Tantangan 1 untuk menghubungkan dep.depend(), sehingga dep.notify() memanggil getters dan setters.
  • Ketika autorun() dan convert()(ganti nama menjadi observe()) terhubung, kita dapat mengakses properti yang mengumpulkan dependensi dengan memanggil dep.depend(). Mutasi properti menyebabkan dep.notify() memicu perubahan.

MENGHARAPKAN:

const state = {
  count: 0
}
observe(state)
autorun(() => {
  console.log(state.count)
  // should call dep.depend() "getter"
})
// should immediately log "count is: 0"
state.count++
// should log "count is: 1"
// should call dep.notify() "setter"

TEMPLAT:

<script>
function observe (obj) {
  // Implement this!
}
function autorun (update) {
  // Implement this!
}
</script>

LARUTAN:

Instruktur kelas “Evan You” adalah Pencipta dan Pemimpin Proyek Vue.js.

Dia dapat ditemukan di situs pribadinya, GitHub, LinkedIn, Twitter, Medium(Evan You), dan Frontend Masters.

Terima kasih sudah membaca! 💕 Jika Anda menyukai postingan blog ini, silakan bertepuk tangan👏