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,
};
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)]
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 {
/// Get the wrapped Node reference if it exists
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
@ -319,7 +326,12 @@ impl NodeRef {
/// Place a Node in a reference for later use
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
pub(crate) fn mounted(&mut self) {
let shared_state = self.shared_state.clone();
let mounted = Box::new(MountedComponent { shared_state });
scheduler().put_and_try_run(mounted);
let mounted = MountedComponent { shared_state };
scheduler().push_mount(Box::new(mounted));
}
/// Schedules a task to create and render a component and then mount it to the DOM
pub(crate) fn create(&mut self) {
let shared_state = self.shared_state.clone();
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
@ -94,14 +94,14 @@ impl<COMP: Component> Scope<COMP> {
shared_state: self.shared_state.clone(),
update,
};
scheduler().put_and_try_run(Box::new(update));
scheduler().push(Box::new(update));
}
/// Schedules a task to destroy a component
pub(crate) fn destroy(&mut self) {
let shared_state = self.shared_state.clone();
let destroy = DestroyComponent { shared_state };
scheduler().put_and_try_run(Box::new(destroy));
scheduler().push(Box::new(destroy));
}
/// Send a message to the component
@ -173,10 +173,17 @@ impl<COMP: Component> CreatedState<COMP> {
}
fn update(mut self) -> Self {
let mut vnode = self.component.render();
let node = vnode.apply(&self.element, None, self.last_frame);
self.node_ref.set(node);
self.last_frame = Some(vnode);
let mut root = self.component.render();
if let Some(node) = root.apply(&self.element, None, self.last_frame) {
self.node_ref.set(Some(node));
} 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
}
}

View File

@ -3,7 +3,6 @@
use std::cell::RefCell;
use std::collections::VecDeque;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
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.
#[derive(Clone)]
pub(crate) struct Scheduler {
lock: Rc<AtomicBool>,
sequence: Shared<VecDeque<Box<dyn Runnable>>>,
}
impl Clone for Scheduler {
fn clone(&self) -> Self {
Scheduler {
lock: self.lock.clone(),
sequence: self.sequence.clone(),
}
}
lock: Rc<RefCell<()>>,
main: Shared<VecDeque<Box<dyn Runnable>>>,
create_component: Shared<VecDeque<Box<dyn Runnable>>>,
mount_component: Shared<Vec<Box<dyn Runnable>>>,
}
impl Scheduler {
/// Creates a new scheduler with a context.
fn new() -> Self {
let sequence = VecDeque::new();
Scheduler {
lock: Rc::new(AtomicBool::new(false)),
sequence: Rc::new(RefCell::new(sequence)),
lock: Rc::new(RefCell::new(())),
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>) {
self.sequence.borrow_mut().push_back(runnable);
if self.lock.compare_and_swap(false, true, Ordering::Relaxed) {
pub(crate) fn push(&self, runnable: Box<dyn Runnable>) {
self.main.borrow_mut().push_back(runnable);
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;
}
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 {
runnable.run();
} else {
break;
}
}
self.lock.store(false, Ordering::Relaxed);
}
}

View File

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