ไลบรารี DOM เสมือนจริงที่มุ่งเน้นไปที่ความเรียบง่ายโมดูลคุณสมบัติที่ทรงพลังและประสิทธิภาพ
ขอบคุณ BrowserStack ที่ให้การเข้าถึงเครื่องมือทดสอบข้ามเบราว์เซอร์ที่ยอดเยี่ยม
ภาษาอังกฤษ | 简体中文 | ภาษาฮินดี
เสมือนจริง DOM นั้นยอดเยี่ยม มันช่วยให้เราสามารถแสดงมุมมองของแอปพลิเคชันของเราเป็นฟังก์ชั่นของรัฐ แต่วิธีแก้ปัญหาที่มีอยู่นั้นเป็นวิธีที่ป่องเกินไปช้าเกินไปขาดคุณสมบัติมี API เอนเอียงไปทาง OOP และ/หรือไม่มีคุณสมบัติที่ฉันต้องการ
Snabbdom ประกอบด้วยแกนกลางที่เรียบง่ายนักแสดงและขยายได้ซึ่งมีเพียง≈ 200 SLOC มันมีสถาปัตยกรรมแบบแยกส่วนที่มีฟังก์ชั่นที่หลากหลายสำหรับส่วนขยายผ่านโมดูลที่กำหนดเอง เพื่อให้แกนกลางง่ายขึ้นฟังก์ชั่นที่ไม่จำเป็นทั้งหมดจะถูกมอบหมายให้โมดูล
คุณสามารถปั้น snabbdom เป็นสิ่งที่คุณต้องการ! เลือกเลือกและปรับแต่งฟังก์ชั่นที่คุณต้องการ หรือคุณสามารถใช้ส่วนขยายเริ่มต้นและรับไลบรารี DOM เสมือนจริงที่มีประสิทธิภาพสูงขนาดเล็กและคุณสมบัติทั้งหมดที่แสดงด้านล่าง
h สำหรับการสร้างโหนด DOM เสมือนได้อย่างง่ายดายh Helper import {
init ,
classModule ,
propsModule ,
styleModule ,
eventListenersModule ,
h
} from "snabbdom" ;
const patch = init ( [
// Init patch function with chosen modules
classModule , // makes it easy to toggle classes
propsModule , // for setting properties on DOM elements
styleModule , // handles styling on elements with support for animations
eventListenersModule // attaches event listeners
] ) ;
const container = document . getElementById ( "container" ) ;
const vnode = h (
"div#container.two.classes" ,
{ on : { click : ( ) => console . log ( "div clicked" ) } } ,
[
h ( "span" , { style : { fontWeight : "bold" } } , "This is bold" ) ,
" and this is just normal text" ,
h ( "a" , { props : { href : "/foo" } } , "I'll take you places!" )
]
) ;
// Patch into empty DOM element – this modifies the DOM as a side effect
patch ( container , vnode ) ;
const newVnode = h (
"div#container.two.classes" ,
{ on : { click : ( ) => console . log ( "updated div clicked" ) } } ,
[
h (
"span" ,
{ style : { fontWeight : "normal" , fontStyle : "italic" } } ,
"This is now italic type"
) ,
" and this is still just normal text" ,
h ( "a" , { props : { href : "/bar" } } , "I'll take you places!" )
]
) ;
// Second `patch` invocation
patch ( vnode , newVnode ) ; // Snabbdom efficiently updates the old view to the new state initpatchhfragment (ทดลอง)toVNodeinitinsertremovedestroyremovedestroyแกนกลางของ snabbdom ให้ฟังก์ชั่นที่สำคัญที่สุดเท่านั้น มันถูกออกแบบมาให้เรียบง่ายที่สุดเท่าที่จะทำได้ในขณะที่ยังคงเร็วและขยายได้
init หลักแสดง init ก์ชั่นเดียวเพียงครั้งเดียว init นี้ใช้รายการของโมดูลและส่งคืนฟังก์ชัน patch ที่ใช้ชุดโมดูลที่ระบุ
import { classModule , styleModule } from "snabbdom" ;
const patch = init ( [ classModule , styleModule ] ) ;patch ฟังก์ชั่น patch ที่ส่งคืนโดย init ใช้สองอาร์กิวเมนต์ ครั้งแรกคือองค์ประกอบ DOM หรือ VNODE ที่แสดงถึงมุมมองปัจจุบัน อย่างที่สองคือ VNode ที่แสดงถึงมุมมองใหม่ที่อัปเดต
หากองค์ประกอบ DOM ที่มีพาเรนต์ถูกส่งผ่าน newVnode จะถูกเปลี่ยนเป็นโหนด DOM และองค์ประกอบที่ผ่านจะถูกแทนที่ด้วยโหนด DOM ที่สร้างขึ้น หากผ่าน vNode เก่า snabbdom จะปรับเปลี่ยนได้อย่างมีประสิทธิภาพเพื่อให้ตรงกับคำอธิบายใน vNode ใหม่
vNode เก่าใด ๆ ที่ส่งผ่านจะต้องเป็น vnode ที่ได้จากการโทรก่อนหน้าไปยัง patch สิ่งนี้จำเป็นเนื่องจาก Snabbdom เก็บข้อมูลใน vnode สิ่งนี้ทำให้สามารถใช้สถาปัตยกรรมที่เรียบง่ายและมีประสิทธิภาพมากขึ้น นอกจากนี้ยังหลีกเลี่ยงการสร้างต้นไม้ VNode เก่าใหม่
patch ( oldVnode , newVnode ) ; ในขณะที่ไม่มี API โดยเฉพาะสำหรับการลบต้นไม้ VNode ออกจากองค์ประกอบจุดเมานต์ของมันวิธีหนึ่งในการบรรลุเป้าหมายนี้คือการให้ความคิดเห็น vnode เป็นอาร์กิวเมนต์ที่สองใน patch เช่น:
patch (
oldVnode ,
h ( "!" , {
hooks : {
post : ( ) => {
/* patch complete */
}
}
} )
) ;แน่นอนว่ายังมีโหนดความคิดเห็นเดียวที่จุดเมานต์
h ขอแนะนำให้คุณใช้ h เพื่อสร้าง vNodes ยอมรับแท็ก/ตัวเลือกเป็นสตริงวัตถุข้อมูลเสริมและสตริงเสริมหรืออาร์เรย์ของเด็ก
import { h } from "snabbdom" ;
const vnode = h ( "div#container" , { style : { color : "#000" } } , [
h ( "h1.primary-title" , "Headline" ) ,
h ( "p" , "A paragraph" )
] ) ;fragment (ทดลอง)ข้อควรระวัง: ฟีเจอร์นี้กำลังทดลองและต้องเลือกใช้ API ของมันอาจเปลี่ยนแปลงได้โดยไม่มีการชนรุ่นใหญ่
const patch = init ( modules , undefined , {
experimental : {
fragments : true
}
} ) ;สร้างโหนดเสมือนจริงที่จะถูกแปลงเป็นชิ้นส่วนเอกสารที่มีเด็กที่ได้รับ
import { fragment , h } from "snabbdom" ;
const vnode = fragment ( [ "I am" , h ( "span" , [ " a" , " fragment" ] ) ] ) ;toVNodeแปลงโหนด DOM เป็นโหนดเสมือนจริง ดีโดยเฉพาะอย่างยิ่งสำหรับการแก้ไขเนื้อหา HTML ที่สร้างขึ้นก่อนหน้าเซิร์ฟเวอร์
import {
init ,
styleModule ,
attributesModule ,
h ,
toVNode
} from "snabbdom" ;
const patch = init ( [
// Initialize a `patch` function with the modules used by `toVNode`
attributesModule // handles attributes from the DOM node
datasetModule , // handles `data-*` attributes from the DOM node
] ) ;
const newVNode = h ( "div" , { style : { color : "#000" } } , [
h ( "h1" , "Headline" ) ,
h ( "p" , "A paragraph" ) ,
h ( "img" , { attrs : { src : "sunrise.png" , alt : "morning sunrise" } } )
] ) ;
patch ( toVNode ( document . querySelector ( ".container" ) ) , newVNode ) ;ตะขอเป็นวิธีที่จะเชื่อมต่อกับวงจรชีวิตของโหนด DOM Snabbdom มีตะขอให้เลือกมากมาย ตะขอถูกใช้ทั้งสองโดยโมดูลเพื่อขยาย snabbdom และในรหัสปกติสำหรับการดำเนินการรหัสโดยพลการที่จุดที่ต้องการในชีวิตของโหนดเสมือนจริง
| ชื่อ | ทริกเกอร์เมื่อ | ข้อโต้แย้งในการโทรกลับ |
|---|---|---|
pre | กระบวนการแก้ไขเริ่มต้นขึ้น | ไม่มี |
init | มีการเพิ่ม vNode แล้ว | vnode |
create | องค์ประกอบ DOM ถูกสร้างขึ้นตาม vNode | emptyVnode, vnode |
insert | องค์ประกอบได้รับการแทรกลงใน DOM | vnode |
prepatch | องค์ประกอบกำลังจะได้รับการแก้ไข | oldVnode, vnode |
update | องค์ประกอบกำลังได้รับการปรับปรุง | oldVnode, vnode |
postpatch | องค์ประกอบได้รับการแก้ไข | oldVnode, vnode |
destroy | องค์ประกอบจะถูกลบออกโดยตรงหรือโดยอ้อม | vnode |
remove | องค์ประกอบจะถูกลบโดยตรงจาก DOM | vnode, removeCallback |
post | กระบวนการแก้ไขเสร็จสิ้น | ไม่มี |
ตะขอต่อไปนี้พร้อมใช้งานสำหรับโมดูล: pre , create , update , destroy , remove , post
ตะขอต่อไปนี้มีอยู่ในคุณสมบัติ hook ของแต่ละองค์ประกอบ: init , create , insert , แทรก, prepatch , update , postpatch , destroy , remove
หากต้องการใช้ hooks ให้ส่งผ่านเป็นวัตถุเพื่อ hook ฟิลด์ของอาร์กิวเมนต์วัตถุข้อมูล
h ( "div.row" , {
key : movie . rank ,
hook : {
insert : ( vnode ) => {
movie . elmHeight = vnode . elm . offsetHeight ;
}
}
} ) ; initตะขอนี้จะถูกเรียกใช้ในระหว่างกระบวนการแก้ไขเมื่อพบโหนดเสมือนจริงใหม่ ตะขอถูกเรียกก่อน snabbdom ได้ประมวลผลโหนดในทางใดทางหนึ่ง เช่นก่อนที่มันจะสร้างโหนด DOM ตาม vNode
insertตะขอนี้จะถูกเรียกใช้เมื่อองค์ประกอบ DOM สำหรับ vNode ถูกแทรกลงในเอกสาร และ ส่วนที่เหลือของรอบแพตช์เสร็จสิ้น ซึ่งหมายความว่าคุณสามารถทำการวัด DOM (เช่นการใช้ getBoundingClientRect ในตะขอนี้อย่างปลอดภัยโดยรู้ว่าไม่มีองค์ประกอบใด ๆ ที่จะเปลี่ยนแปลงหลังจากนั้นอาจส่งผลกระทบต่อตำแหน่งขององค์ประกอบที่แทรก
remove ช่วยให้คุณสามารถเชื่อมต่อการกำจัดองค์ประกอบ ตะขอจะถูกเรียกเมื่อ VNode จะถูกลบออกจาก DOM ฟังก์ชั่นการจัดการได้รับทั้ง vNode และการโทรกลับ คุณสามารถควบคุมและชะลอการลบออกด้วยการโทรกลับ การเรียกกลับควรถูกเรียกใช้เมื่อตะขอทำธุรกิจเสร็จแล้วและองค์ประกอบจะถูกลบออกเมื่อ remove ตะขอทั้งหมดเรียกใช้การโทรกลับของพวกเขา
ตะขอจะถูกกระตุ้นเมื่อองค์ประกอบจะถูกลบออกจากผู้ปกครอง - ไม่ใช่ถ้าเป็นลูกขององค์ประกอบที่ถูกลบออก สำหรับสิ่งนั้นให้ดูเบ็ด destroy
destroyตะขอนี้จะถูกเรียกใช้บนโหนดเสมือนจริงเมื่อองค์ประกอบ DOM ของมันถูกลบออกจาก DOM หรือหากพาเรนต์ของมันถูกลบออกจาก DOM
หากต้องการดูความแตกต่างระหว่างตะขอนี้และเบ็ด remove ให้พิจารณาตัวอย่าง
const vnode1 = h ( "div" , [ h ( "div" , [ h ( "span" , "Hello" ) ] ) ] ) ;
const vnode2 = h ( "div" , [ ] ) ;
patch ( container , vnode1 ) ;
patch ( vnode1 , vnode2 ) ; destroy ที่นี่จะถูกกระตุ้นทั้งองค์ประกอบ div ภายใน และ องค์ประกอบ span ที่มีอยู่ ในทางกลับกัน remove จะถูกเรียกใช้เพียงองค์ประกอบ div เท่านั้นเพราะเป็นองค์ประกอบเดียวที่แยกออกจากพาเรนต์
ตัวอย่างเช่นคุณสามารถใช้ remove เพื่อทริกเกอร์ภาพเคลื่อนไหวเมื่อองค์ประกอบกำลังถูกลบออกและใช้เบ็ด destroy เพื่อทำให้การหายตัวไปของลูก ๆ ขององค์ประกอบที่ถูกลบ
โมดูลทำงานโดยการลงทะเบียนผู้ฟังทั่วโลกสำหรับ hooks โมดูลเป็นเพียงการทำแผนที่พจนานุกรมชื่อเบ็ดสำหรับฟังก์ชั่น
const myModule = {
create : ( oldVnode , vnode ) => {
// invoked whenever a new virtual node is created
} ,
update : ( oldVnode , vnode ) => {
// invoked whenever a virtual node is updated
}
} ;ด้วยกลไกนี้คุณสามารถเพิ่มพฤติกรรมของ snabbdom ได้อย่างง่ายดาย สำหรับการสาธิตลองดูที่การใช้งานของโมดูลเริ่มต้น
สิ่งนี้อธิบายโมดูลหลัก โมดูลทั้งหมดเป็นทางเลือก ตัวอย่าง JSX สมมติว่าคุณใช้ jsx Pragma ที่จัดทำโดยไลบรารีนี้
โมดูลคลาสเป็นวิธีที่ง่ายในการสลับคลาสแบบไดนามิกกับองค์ประกอบ คาดว่าวัตถุในคุณสมบัติข้อมูล class วัตถุควรแมปชื่อคลาสกับบูลีนที่ระบุว่าชั้นเรียนควรอยู่หรือไปบน vNode หรือไม่
h ( "a" , { class : { active : true , selected : false } } , "Toggle" ) ; ใน JSX คุณสามารถใช้ class เช่นนี้:
< div class = { { foo : true , bar : true } } />
// Renders as: <div class="foo bar"></div>อนุญาตให้คุณตั้งค่าคุณสมบัติในองค์ประกอบ DOM
h ( "a" , { props : { href : "/foo" } } , "Go to Foo" ) ; ใน JSX คุณสามารถใช้ props เช่นนี้:
< input props = { { name : "foo" } } />
// Renders as: <input name="foo" /> with input.name === "foo"คุณสมบัติสามารถตั้งค่าได้เท่านั้น ไม่ได้ลบออก แม้ว่าเบราว์เซอร์จะอนุญาตให้มีการเพิ่มและการลบคุณสมบัติที่กำหนดเอง แต่การลบจะไม่ถูกพยายามโดยโมดูลนี้ สิ่งนี้สมเหตุสมผลเพราะคุณสมบัติ DOM ดั้งเดิมไม่สามารถลบออกได้ และหากคุณใช้คุณสมบัติที่กำหนดเองสำหรับการจัดเก็บค่าหรือการอ้างอิงวัตถุใน DOM โปรดพิจารณาใช้แอตทริบิวต์ data-* แทน บางทีผ่านโมดูลชุดข้อมูล
เหมือนกับอุปกรณ์ประกอบฉาก แต่ตั้งค่าแอตทริบิวต์แทนคุณสมบัติในองค์ประกอบ DOM
h ( "a" , { attrs : { href : "/foo" } } , "Go to Foo" ) ; ใน JSX คุณสามารถใช้ attrs เช่นนี้:
< div attrs = { { "aria-label" : "I'm a div" } } />
// Renders as: <div aria-label="I'm a div"></div> แอตทริบิวต์จะถูกเพิ่มและอัปเดตโดยใช้ setAttribute ในกรณีของแอตทริบิวต์ที่เคยเพิ่ม/ตั้งไว้ก่อนหน้านี้และไม่มีอยู่ในวัตถุ attrs อีกต่อไปมันจะถูกลบออกจากรายการแอตทริบิวต์ขององค์ประกอบ DOM โดยใช้ removeAttribute
ในกรณีของแอตทริบิวต์บูลีน (เช่น disabled hidden selected ... ) ความหมายไม่ได้ขึ้นอยู่กับค่าแอตทริบิวต์ ( true หรือ false ) แต่ขึ้นอยู่กับการมี/ไม่มีแอตทริบิวต์ในองค์ประกอบ DOM คุณลักษณะเหล่านั้นได้รับการจัดการที่แตกต่างกันโดยโมดูล: หากแอตทริบิวต์บูลีนถูกตั้งค่าเป็นค่าเท็จ ( 0 , -0 , null , false , NaN , undefined หรือสตริงว่าง ( "" )) จากนั้นแอตทริบิวต์จะถูกลบออกจากรายการแอตทริบิวต์ขององค์ประกอบ DOM
อนุญาตให้คุณตั้งค่าแอตทริบิวต์ข้อมูลที่กำหนดเอง ( data-* ) บนองค์ประกอบ DOM สิ่งเหล่านี้สามารถเข้าถึงได้ด้วยคุณสมบัติ htmlelement.dataset
h ( "button" , { dataset : { action : "reset" } } , "Reset" ) ; ใน JSX คุณสามารถใช้ dataset เช่นนี้:
< div dataset = { { foo : "bar" } } />
// Renders as: <div data-foo="bar"></div>โมดูลสไตล์คือการทำให้ HTML ของคุณดูลื่นและเคลื่อนไหวได้อย่างราบรื่น ที่สำคัญมันช่วยให้คุณตั้งค่าคุณสมบัติ CSS ในองค์ประกอบ
h (
"span" ,
{
style : {
border : "1px solid #bada55" ,
color : "#c0ffee" ,
fontWeight : "bold"
}
} ,
"Say my name, and every colour illuminates"
) ; ใน JSX คุณสามารถใช้ style แบบนี้:
< div
style = { {
border : "1px solid #bada55" ,
color : "#c0ffee" ,
fontWeight : "bold"
} }
/>
// Renders as: <div style="border: 1px solid #bada55; color: #c0ffee; font-weight: bold"></div> รองรับคุณสมบัติที่กำหนดเอง CSS (AKA CSS ตัวแปร) ได้รับการสนับสนุนพวกเขาจะต้องนำหน้าด้วย --
h (
"div" ,
{
style : { "--warnColor" : "yellow" }
} ,
"Warning"
) ; คุณสามารถระบุคุณสมบัติว่าล่าช้า เมื่อใดก็ตามที่คุณสมบัติเหล่านี้เปลี่ยนไปการเปลี่ยนแปลงจะไม่ถูกนำไปใช้จนกว่าจะหลังจากเฟรมถัดไป
h (
"span" ,
{
style : {
opacity : "0" ,
transition : "opacity 1s" ,
delayed : { opacity : "1" }
}
} ,
"Imma fade right in!"
) ;สิ่งนี้ทำให้ง่ายต่อการเคลื่อนไหวอย่างต่อเนื่องการเข้ามาขององค์ประกอบ
ไม่รองรับค่า all ของ transition-property
remove สไตล์ที่ตั้งไว้ในคุณสมบัติ remove จะมีผลเมื่อองค์ประกอบกำลังจะถูกลบออกจาก DOM สไตล์ที่ใช้ควรเป็นภาพเคลื่อนไหวด้วยการเปลี่ยน CSS เพียงครั้งเดียวที่สไตล์ทั้งหมดจะเสร็จสิ้นองค์ประกอบจะถูกลบออกจาก DOM
h (
"span" ,
{
style : {
opacity : "1" ,
transition : "opacity 1s" ,
remove : { opacity : "0" }
}
} ,
"It's better to fade out than to burn away"
) ;สิ่งนี้ทำให้ง่ายต่อการเคลื่อนไหวการกำจัดองค์ประกอบ
ไม่รองรับค่า all ของ transition-property
destroy h (
"span" ,
{
style : {
opacity : "1" ,
transition : "opacity 1s" ,
destroy : { opacity : "0" }
}
} ,
"It's better to fade out than to burn away"
) ; ไม่รองรับค่า all ของ transition-property
โมดูลผู้ฟังเหตุการณ์ให้ความสามารถที่ทรงพลังสำหรับการแนบผู้ฟังเหตุการณ์
คุณสามารถแนบฟังก์ชั่นกับเหตุการณ์ใน vNode โดยการ on วัตถุด้วยคุณสมบัติที่สอดคล้องกับชื่อของเหตุการณ์ที่คุณต้องการฟัง ฟังก์ชั่นจะถูกเรียกเมื่อเหตุการณ์เกิดขึ้นและจะถูกส่งผ่านไปยังวัตถุเหตุการณ์ที่เป็นของมัน
function clickHandler ( ev ) {
console . log ( "got clicked" ) ;
}
h ( "div" , { on : { click : clickHandler } } ) ; ใน JSX คุณสามารถใช้ on นี้:
< div on = { { click : clickHandler } } />Snabbdom อนุญาตให้เปลี่ยนตัวจัดการเหตุการณ์ระหว่างการแสดงผล สิ่งนี้เกิดขึ้นโดยไม่ต้องสัมผัสตัวจัดการเหตุการณ์ที่แนบมากับ DOM
อย่างไรก็ตามโปรดทราบว่า คุณควรระวังเมื่อแชร์ตัวจัดการเหตุการณ์ระหว่าง VNODES เนื่องจากเทคนิคโมดูลนี้ใช้เพื่อหลีกเลี่ยงการเชื่อมโยงตัวจัดการเหตุการณ์กับ DOM อีกครั้ง (และโดยทั่วไปการแบ่งปันข้อมูลระหว่าง VNODES ไม่ได้รับประกันว่าจะทำงานได้เนื่องจากโมดูลได้รับอนุญาตให้กลายพันธุ์ข้อมูลที่กำหนด)
โดยเฉพาะอย่างยิ่งคุณ ไม่ ควรทำอะไรแบบนี้:
// Does not work
const sharedHandler = {
change : ( e ) => {
console . log ( "you chose: " + e . target . value ) ;
}
} ;
h ( "div" , [
h ( "input" , {
props : { type : "radio" , name : "test" , value : "0" } ,
on : sharedHandler
} ) ,
h ( "input" , {
props : { type : "radio" , name : "test" , value : "1" } ,
on : sharedHandler
} ) ,
h ( "input" , {
props : { type : "radio" , name : "test" , value : "2" } ,
on : sharedHandler
} )
] ) ; สำหรับหลายกรณีดังกล่าวคุณสามารถใช้ตัวจัดการอาร์เรย์ได้แทน (อธิบายไว้ข้างต้น) อีกทางเลือกหนึ่งเพียงตรวจสอบให้แน่ใจว่าแต่ละโหนดจะถูกส่งผ่านโดยเฉพาะ on ค่า:
// Works
const sharedHandler = ( e ) => {
console . log ( "you chose: " + e . target . value ) ;
} ;
h ( "div" , [
h ( "input" , {
props : { type : "radio" , name : "test" , value : "0" } ,
on : { change : sharedHandler }
} ) ,
h ( "input" , {
props : { type : "radio" , name : "test" , value : "1" } ,
on : { change : sharedHandler }
} ) ,
h ( "input" , {
props : { type : "radio" , name : "test" , value : "2" } ,
on : { change : sharedHandler }
} )
] ) ; SVG ใช้งานได้เมื่อใช้ฟังก์ชัน h สำหรับการสร้างโหนดเสมือนจริง องค์ประกอบ SVG ถูกสร้างขึ้นโดยอัตโนมัติด้วยเนมสเปซที่เหมาะสม
const vnode = h ( "div" , [
h ( "svg" , { attrs : { width : 100 , height : 100 } } , [
h ( "circle" , {
attrs : {
cx : 50 ,
cy : 50 ,
r : 40 ,
stroke : "green" ,
"stroke-width" : 4 ,
fill : "yellow"
}
} )
] )
] ) ;ดูตัวอย่าง SVG และตัวอย่าง SVG Carousel
เบราว์เซอร์บางตัว (เช่น IE <= 11) ไม่รองรับคุณสมบัติ classList ในองค์ประกอบ SVG เนื่องจากโมดูล คลาส ภายในใช้ classList ภายในจึงไม่ทำงานในกรณีนี้เว้นแต่ว่าคุณจะใช้ polyfill ในชั้นเรียน (หากคุณไม่ต้องการใช้ polyfill คุณสามารถใช้แอตทริบิวต์ class กับโมดูล แอตทริบิวต์ )
ฟังก์ชั่น thunk ใช้ตัวเลือกซึ่งเป็นคีย์สำหรับการระบุ thunk ซึ่งเป็นฟังก์ชันที่ส่งคืน vNode และจำนวนตัวแปรของพารามิเตอร์สถานะ หากเรียกใช้ฟังก์ชันการเรนเดอร์จะได้รับข้อโต้แย้งของรัฐ
thunk(selector, key, renderFn, [stateArguments])
renderFn จะถูกเรียกใช้เฉพาะในกรณีที่ renderFn มีการเปลี่ยนแปลงหรือ [stateArguments] ความยาวอาร์เรย์หรือองค์ประกอบของมันเปลี่ยนไป
key คือตัวเลือก มันควรจะจัดหาเมื่อ selector ไม่ซ้ำกันในหมู่พี่น้อง Thunks สิ่งนี้ทำให้มั่นใจได้ว่า thunk จะถูกจับคู่อย่างถูกต้องเสมอเมื่อแพร่กระจาย
Thunks เป็นกลยุทธ์การเพิ่มประสิทธิภาพที่สามารถใช้งานได้เมื่อมีการจัดการกับข้อมูลที่ไม่เปลี่ยนรูป
พิจารณาฟังก์ชั่นง่ายๆสำหรับการสร้างโหนดเสมือนจริงตามตัวเลข
function numberView ( n ) {
return h ( "div" , "Number is: " + n ) ;
} มุมมองขึ้นอยู่กับ n ซึ่งหมายความว่าหาก n ไม่เปลี่ยนแปลงแล้วการสร้างโหนด DOM เสมือนจริงและแก้ไขกับ vNode เก่านั้นสิ้นเปลือง เพื่อหลีกเลี่ยงค่าใช้จ่ายเราสามารถใช้ฟังก์ชั่น thunk HELPER
function render ( state ) {
return thunk ( "num" , numberView , [ state . number ] ) ;
} แทนที่จะเรียกใช้ฟังก์ชัน numberView จริง ๆ แล้วสิ่งนี้จะวาง vnode dummy ในทรีเสมือนเท่านั้น เมื่อ snabbdom แพทช์ vnode dummy นี้กับ vnode ก่อนหน้ามันจะเปรียบเทียบค่าของ n ถ้า n ไม่เปลี่ยนแปลงมันจะนำ VNode เก่ามาใช้ใหม่ สิ่งนี้จะหลีกเลี่ยงการสร้างมุมมองตัวเลขและกระบวนการ DIFF โดยสิ้นเชิง
ฟังก์ชั่นมุมมองที่นี่เป็นเพียงตัวอย่าง ในทางปฏิบัติ Thunks มีความเกี่ยวข้องเฉพาะในกรณีที่คุณกำลังแสดงมุมมองที่ซับซ้อนซึ่งใช้เวลาในการคำนวณที่สำคัญในการสร้าง
โปรดทราบว่าชิ้นส่วน JSX ยังคงทดลองอยู่และต้องเลือกเข้าร่วมดูส่วน fragment สำหรับรายละเอียด
เพิ่มตัวเลือกต่อไปนี้ใน tsconfig.json ของคุณ:
{
"compilerOptions" : {
"jsx" : " react " ,
"jsxFactory" : " jsx " ,
"jsxFragmentFactory" : " Fragment "
}
} จากนั้นตรวจสอบให้แน่ใจว่าคุณใช้ส่วนขยายไฟล์ .tsx และนำเข้าฟังก์ชั่น jsx และฟังก์ชัน Fragment ที่ด้านบนของไฟล์:
import { Fragment , jsx , VNode } from "snabbdom" ;
const node : VNode = (
< div >
< span > I was created with JSX </ span >
</ div >
) ;
const fragment : VNode = (
< >
< span > JSX fragments </ span >
are experimentally supported
</ >
) ;เพิ่มตัวเลือกต่อไปนี้ในการกำหนดค่า Babel ของคุณ:
{
"plugins" : [
[
" @babel/plugin-transform-react-jsx " ,
{
"pragma" : " jsx " ,
"pragmaFrag" : " Fragment "
}
]
]
} จากนั้นนำเข้าฟังก์ชั่น jsx และฟังก์ชัน Fragment ที่ด้านบนของไฟล์:
import { Fragment , jsx } from "snabbdom" ;
const node = (
< div >
< span > I was created with JSX </ span >
</ div >
) ;
const fragment = (
< >
< span > JSX fragments </ span >
are experimentally supported
</ >
) ; คุณสมบัติ
คุณสมบัติ sel ระบุองค์ประกอบ HTML ของ VNODE ซึ่งเป็นตัวเลือก id นำหน้าด้วย # และศูนย์หรือมากกว่าคลาสแต่ละชั้นนำหน้าด้วย . - ไวยากรณ์ได้รับแรงบันดาลใจจากตัวเลือก CSS นี่คือตัวอย่างบางส่วน:
div#container.bar.baz - องค์ประกอบ div ที่มี container ID และ bar คลาสและ bazli - องค์ประกอบ li ที่ไม่มี id หรือคลาสbutton.alert.primary - องค์ประกอบ button พร้อม alert สองคลาสและ primary ตัวเลือกนั้นหมายถึง การคงที่ นั่นคือไม่ควรเปลี่ยนแปลงตลอดอายุการใช้งานขององค์ประกอบ ในการตั้ง id แบบไดนามิกให้ใช้โมดูลอุปกรณ์ประกอบฉากและเพื่อตั้งค่าคลาสแบบไดนามิกให้ใช้โมดูลคลาส
เนื่องจากตัวเลือกนั้นคงที่ Snabbdom จึงใช้เป็นส่วนหนึ่งของตัวตน VNODES ตัวอย่างเช่นหากเด็กสองคน vnodes
[ h ( "div#container.padding" , children1 ) , h ( "div.padding" , children2 ) ] ;ถูกแก้ไข
[ h ( "div#container.padding" , children2 ) , h ( "div.padding" , children1 ) ] ;จากนั้น Snabbdom ใช้ตัวเลือกเพื่อระบุ vNodes และจัดลำดับใหม่ในแผนผัง DOM แทนที่จะสร้างองค์ประกอบ DOM ใหม่ การใช้ตัวเลือกนี้หลีกเลี่ยงความจำเป็นในการระบุคีย์ในหลายกรณี
คุณสมบัติ .data ของโหนดเสมือนเป็นสถานที่สำหรับเพิ่มข้อมูลสำหรับโมดูลเพื่อเข้าถึงและจัดการองค์ประกอบ DOM จริงเมื่อสร้างขึ้น เพิ่มสไตล์คลาส CSS คุณลักษณะ ฯลฯ
วัตถุข้อมูลคือพารามิเตอร์ที่สอง (ไม่บังคับ) เป็น h()
ตัวอย่างเช่น h('div', {props: {className: 'container'}}, [...]) จะสร้างโหนดเสมือนจริงด้วย
( {
props : {
className : "container"
}
} ) ; เป็นวัตถุ .data
คุณสมบัติ .children ของโหนดเสมือนเป็นพารามิเตอร์ที่สาม (ไม่บังคับ) ถึง h() ระหว่างการสร้าง .children เป็นเพียงอาร์เรย์ของโหนดเสมือนจริงที่ควรเพิ่มเป็นเด็กของโหนด DOM หลักเมื่อสร้าง
ตัวอย่างเช่น h('div', {}, [ h('h1', {}, 'Hello, World') ]) จะสร้างโหนดเสมือนจริงด้วย
[
{
sel : "h1" ,
data : { } ,
children : undefined ,
text : "Hello, World" ,
elm : Element ,
key : undefined
}
] ; เป็นทรัพย์สินของ .children
คุณสมบัติ .text ถูกสร้างขึ้นเมื่อโหนดเสมือนถูกสร้างขึ้นโดยมีเด็กเพียงคนเดียวที่มีข้อความและต้องการเฉพาะ document.createTextNode() ที่จะใช้
ตัวอย่างเช่น: h('h1', {}, 'Hello') จะสร้างโหนดเสมือนจริงด้วย Hello As .text Property
คุณสมบัติ .elm ของโหนดเสมือนเป็นตัวชี้ไปยังโหนด DOM จริงที่สร้างโดย snabbdom คุณสมบัตินี้มีประโยชน์มากในการคำนวณในตะขอเช่นเดียวกับโมดูล
คุณสมบัติ .key ถูกสร้างขึ้นเมื่อมีการจัดเตรียมคีย์ภายในวัตถุ .data ของคุณ คุณสมบัติ .key ใช้เพื่อให้พอยน์เตอร์ไปยังโหนด DOM ที่มีอยู่ก่อนหน้านี้เพื่อหลีกเลี่ยงการสร้างใหม่หากไม่จำเป็น สิ่งนี้มีประโยชน์มากสำหรับสิ่งต่าง ๆ เช่นการสั่งซื้อรายการใหม่ คีย์จะต้องเป็นสตริงหรือตัวเลขที่อนุญาตให้ทำการค้นหาที่เหมาะสมเนื่องจากเก็บไว้ภายในเป็นคู่คีย์/ค่าภายในของวัตถุโดยที่ .key เป็นคีย์และค่าคือคุณสมบัติ .elm ที่สร้างขึ้น
หากมีให้คุณสมบัติ .key จะต้องไม่ซ้ำกันในองค์ประกอบของพี่น้อง
ตัวอย่างเช่น: h('div', {key: 1}, []) จะสร้างวัตถุโหนดเสมือนจริงด้วยคุณสมบัติ .key ที่มีค่า 1
Snabbdom เป็นห้องสมุด Dom เสมือนระดับต่ำ มันไม่ได้รับการผ่าตัดเกี่ยวกับวิธีที่คุณควรจัดโครงสร้างแอปพลิเคชันของคุณ
นี่คือวิธีการบางอย่างในการสร้างแอปพลิเคชันด้วย snabbdom
อย่าลืมแบ่งปันหากคุณกำลังสร้างแอปพลิเคชันด้วยวิธีอื่นโดยใช้ snabbdom
แพ็คเกจที่เกี่ยวข้องกับ snabbdom ควรติดแท็กด้วยคำหลัก snabbdom และเผยแพร่ใน NPM พวกเขาสามารถพบได้โดยใช้ keywords:snabbdom
Uncaught NotFoundError: Failed to execute 'insertBefore' on 'Node':
The node before which the new node is to be inserted is not a child of this node.
เหตุผลสำหรับข้อผิดพลาดนี้คือการใช้ VNODEs ระหว่างแพตช์ (ดูตัวอย่างโค้ด), snabbdom เก็บโหนด DOM จริงภายในโหนด DOM เสมือนจริงที่ส่งผ่านไปเป็นการปรับปรุงประสิทธิภาพดังนั้นจึงไม่รองรับโหนดใหม่ระหว่างแพตช์
const sharedNode = h ( "div" , { } , "Selected" ) ;
const vnode1 = h ( "div" , [
h ( "div" , { } , [ "One" ] ) ,
h ( "div" , { } , [ "Two" ] ) ,
h ( "div" , { } , [ sharedNode ] )
] ) ;
const vnode2 = h ( "div" , [
h ( "div" , { } , [ "One" ] ) ,
h ( "div" , { } , [ sharedNode ] ) ,
h ( "div" , { } , [ "Three" ] )
] ) ;
patch ( container , vnode1 ) ;
patch ( vnode1 , vnode2 ) ;คุณสามารถแก้ไขปัญหานี้ได้โดยการสร้างสำเนาของวัตถุตื้น (ที่นี่ด้วยไวยากรณ์การแพร่กระจายวัตถุ):
const vnode2 = h ( "div" , [
h ( "div" , { } , [ "One" ] ) ,
h ( "div" , { } , [ { ... sharedNode } ] ) ,
h ( "div" , { } , [ "Three" ] )
] ) ;ทางออกอื่นคือการห่อ VNODES ที่ใช้ร่วมกันในฟังก์ชั่นโรงงาน:
const sharedNode = ( ) => h ( "div" , { } , "Selected" ) ;
const vnode1 = h ( "div" , [
h ( "div" , { } , [ "One" ] ) ,
h ( "div" , { } , [ "Two" ] ) ,
h ( "div" , { } , [ sharedNode ( ) ] )
] ) ; การขอให้ดึงว่าชุมชนอาจสนใจที่จะให้ข้อเสนอแนะควรรวมเข้าด้วยกันหลังจากมีโอกาสดังกล่าวสองสามวัน