Fix component rendering process (#913)

* wip

* Fix component rendering process
This commit is contained in:
Justin Starry 2020-02-02 23:12:45 +08:00 committed by GitHub
parent c422a5ed1b
commit 3dca0c7758
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 73 additions and 50 deletions

View File

@ -739,7 +739,7 @@ impl<AGN: Agent> AgentScope<AGN> {
update, update,
}; };
let runnable: Box<dyn Runnable> = Box::new(envelope); let runnable: Box<dyn Runnable> = Box::new(envelope);
scheduler().put_and_try_run(runnable); scheduler().push(runnable);
} }
} }

View File

@ -304,12 +304,19 @@ where
/// } /// }
/// } /// }
#[derive(PartialEq, Debug, Default, Clone)] #[derive(PartialEq, Debug, Default, Clone)]
pub struct NodeRef(Rc<RefCell<Option<Node>>>); pub struct NodeRef(Rc<RefCell<NodeRefInner>>);
#[derive(PartialEq, Debug, Default, Clone)]
struct NodeRefInner {
node: Option<Node>,
link: Option<NodeRef>,
}
impl NodeRef { impl NodeRef {
/// Get the wrapped Node reference if it exists /// Get the wrapped Node reference if it exists
pub fn get(&self) -> Option<Node> { pub fn get(&self) -> Option<Node> {
self.0.borrow().clone() let inner = self.0.borrow();
inner.node.clone().or_else(|| inner.link.as_ref()?.get())
} }
/// Try converting the node reference into another form /// Try converting the node reference into another form
@ -319,7 +326,12 @@ impl NodeRef {
/// Place a Node in a reference for later use /// Place a Node in a reference for later use
pub(crate) fn set(&self, node: Option<Node>) { pub(crate) fn set(&self, node: Option<Node>) {
*self.0.borrow_mut() = node; self.0.borrow_mut().node = node;
}
/// Link a downstream `NodeRef`
pub(crate) fn link(&self, node_ref: Self) {
self.0.borrow_mut().link = Some(node_ref);
} }
} }

View File

@ -77,15 +77,15 @@ impl<COMP: Component> Scope<COMP> {
/// Schedules a task to call the mounted method on a component and optionally re-render /// Schedules a task to call the mounted method on a component and optionally re-render
pub(crate) fn mounted(&mut self) { pub(crate) fn mounted(&mut self) {
let shared_state = self.shared_state.clone(); let shared_state = self.shared_state.clone();
let mounted = Box::new(MountedComponent { shared_state }); let mounted = MountedComponent { shared_state };
scheduler().put_and_try_run(mounted); scheduler().push_mount(Box::new(mounted));
} }
/// Schedules a task to create and render a component and then mount it to the DOM /// Schedules a task to create and render a component and then mount it to the DOM
pub(crate) fn create(&mut self) { pub(crate) fn create(&mut self) {
let shared_state = self.shared_state.clone(); let shared_state = self.shared_state.clone();
let create = CreateComponent { shared_state }; let create = CreateComponent { shared_state };
scheduler().put_and_try_run(Box::new(create)); scheduler().push_create(Box::new(create));
} }
/// Schedules a task to send a message or new props to a component /// Schedules a task to send a message or new props to a component
@ -94,14 +94,14 @@ impl<COMP: Component> Scope<COMP> {
shared_state: self.shared_state.clone(), shared_state: self.shared_state.clone(),
update, update,
}; };
scheduler().put_and_try_run(Box::new(update)); scheduler().push(Box::new(update));
} }
/// Schedules a task to destroy a component /// Schedules a task to destroy a component
pub(crate) fn destroy(&mut self) { pub(crate) fn destroy(&mut self) {
let shared_state = self.shared_state.clone(); let shared_state = self.shared_state.clone();
let destroy = DestroyComponent { shared_state }; let destroy = DestroyComponent { shared_state };
scheduler().put_and_try_run(Box::new(destroy)); scheduler().push(Box::new(destroy));
} }
/// Send a message to the component /// Send a message to the component
@ -173,10 +173,17 @@ impl<COMP: Component> CreatedState<COMP> {
} }
fn update(mut self) -> Self { fn update(mut self) -> Self {
let mut vnode = self.component.render(); let mut root = self.component.render();
let node = vnode.apply(&self.element, None, self.last_frame); if let Some(node) = root.apply(&self.element, None, self.last_frame) {
self.node_ref.set(node); self.node_ref.set(Some(node));
self.last_frame = Some(vnode); } else if let VNode::VComp(child) = &root {
// If the root VNode is a VComp, we won't have access to the rendered DOM node
// because components render asynchronously. In order to bubble up the DOM node
// from the VComp, we need to link the currently rendering component with its
// root child component.
self.node_ref.link(child.node_ref.clone());
}
self.last_frame = Some(root);
self self
} }
} }

View File

@ -3,7 +3,6 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::rc::Rc; use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
pub(crate) type Shared<T> = Rc<RefCell<T>>; pub(crate) type Shared<T> = Rc<RefCell<T>>;
@ -23,44 +22,57 @@ pub(crate) trait Runnable {
} }
/// This is a global scheduler suitable to schedule and run any tasks. /// This is a global scheduler suitable to schedule and run any tasks.
#[derive(Clone)]
pub(crate) struct Scheduler { pub(crate) struct Scheduler {
lock: Rc<AtomicBool>, lock: Rc<RefCell<()>>,
sequence: Shared<VecDeque<Box<dyn Runnable>>>, main: Shared<VecDeque<Box<dyn Runnable>>>,
} create_component: Shared<VecDeque<Box<dyn Runnable>>>,
mount_component: Shared<Vec<Box<dyn Runnable>>>,
impl Clone for Scheduler {
fn clone(&self) -> Self {
Scheduler {
lock: self.lock.clone(),
sequence: self.sequence.clone(),
}
}
} }
impl Scheduler { impl Scheduler {
/// Creates a new scheduler with a context.
fn new() -> Self { fn new() -> Self {
let sequence = VecDeque::new();
Scheduler { Scheduler {
lock: Rc::new(AtomicBool::new(false)), lock: Rc::new(RefCell::new(())),
sequence: Rc::new(RefCell::new(sequence)), main: Rc::new(RefCell::new(VecDeque::new())),
create_component: Rc::new(RefCell::new(VecDeque::new())),
mount_component: Rc::new(RefCell::new(Vec::new())),
} }
} }
pub(crate) fn put_and_try_run(&self, runnable: Box<dyn Runnable>) { pub(crate) fn push(&self, runnable: Box<dyn Runnable>) {
self.sequence.borrow_mut().push_back(runnable); self.main.borrow_mut().push_back(runnable);
if self.lock.compare_and_swap(false, true, Ordering::Relaxed) { self.start();
}
pub(crate) fn push_create(&self, runnable: Box<dyn Runnable>) {
self.create_component.borrow_mut().push_back(runnable);
self.start();
}
pub(crate) fn push_mount(&self, runnable: Box<dyn Runnable>) {
self.mount_component.borrow_mut().push(runnable);
self.start();
}
pub(crate) fn start(&self) {
let lock = self.lock.try_borrow_mut();
if lock.is_err() {
return; return;
} }
loop { loop {
let do_next = self.sequence.borrow_mut().pop_front(); let do_next = self
.create_component
.borrow_mut()
.pop_front()
.or_else(|| self.mount_component.borrow_mut().pop())
.or_else(|| self.main.borrow_mut().pop_front());
if let Some(runnable) = do_next { if let Some(runnable) = do_next {
runnable.run(); runnable.run();
} else { } else {
break; break;
} }
} }
self.lock.store(false, Ordering::Relaxed);
} }
} }

View File

@ -18,18 +18,11 @@ enum GeneratorType {
} }
/// A virtual component. /// A virtual component.
#[derive(Clone)]
pub struct VComp { pub struct VComp {
type_id: TypeId, type_id: TypeId,
state: Rc<RefCell<MountState>>, state: Rc<RefCell<MountState>>,
} pub(crate) node_ref: NodeRef,
impl Clone for VComp {
fn clone(&self) -> Self {
VComp {
type_id: self.type_id,
state: self.state.clone(),
}
}
} }
/// A virtual child component. /// A virtual child component.
@ -92,6 +85,7 @@ impl VComp {
where where
COMP: Component, COMP: Component,
{ {
let node_ref_clone = node_ref.clone();
let generator = move |generator_type: GeneratorType| -> Mounted { let generator = move |generator_type: GeneratorType| -> Mounted {
match generator_type { match generator_type {
GeneratorType::Mount(element, dummy_node) => { GeneratorType::Mount(element, dummy_node) => {
@ -100,12 +94,12 @@ impl VComp {
let mut scope = scope.mount_in_place( let mut scope = scope.mount_in_place(
element, element,
Some(VNode::VRef(dummy_node.into())), Some(VNode::VRef(dummy_node.into())),
node_ref.clone(), node_ref_clone.clone(),
props.clone(), props.clone(),
); );
Mounted { Mounted {
node_ref: node_ref.clone(), node_ref: node_ref_clone.clone(),
scope: scope.clone().into(), scope: scope.clone().into(),
destroyer: Box::new(move || scope.destroy()), destroyer: Box::new(move || scope.destroy()),
} }
@ -115,7 +109,7 @@ impl VComp {
scope.update(ComponentUpdate::Properties(props.clone())); scope.update(ComponentUpdate::Properties(props.clone()));
Mounted { Mounted {
node_ref: node_ref.clone(), node_ref: node_ref_clone.clone(),
scope: scope.clone().into(), scope: scope.clone().into(),
destroyer: Box::new(move || scope.destroy()), destroyer: Box::new(move || scope.destroy()),
} }
@ -128,6 +122,7 @@ impl VComp {
state: Rc::new(RefCell::new(MountState::Unmounted(Unmounted { state: Rc::new(RefCell::new(MountState::Unmounted(Unmounted {
generator: Box::new(generator), generator: Box::new(generator),
}))), }))),
node_ref,
} }
} }
} }
@ -215,16 +210,13 @@ impl VDiff for VComp {
this.mount(parent.to_owned(), dummy_node) this.mount(parent.to_owned(), dummy_node)
} }
}; };
let node = mounted.node_ref.get();
self.state.replace(MountState::Mounted(mounted)); self.state.replace(MountState::Mounted(mounted));
node
} }
state => { state => {
self.state.replace(state); self.state.replace(state);
None
} }
} }
None
} }
} }