Skip to content
On this page

组件与单向数据流

Alins 组件的使用与 JSX 组件基本一致,组件本身是一个返回 JSX对象的函数,所以组件返回的也是原生DOM元素。组件的第一个参数为属性,第二个参数为组件内的元素。

组件内部的代码只会被执行一次,绑定数据的变更只会导致细粒度的元素更新,整个组件不会被重新渲染。组件仅仅作为UI逻辑的组织形式。

1. 属性

组件函数的第一个参数为组件传入的所有属性。

运行结果 显示编译产物
jsx
function Component(props){
    return <div>
        a = {props.a}; b = {props.b};
    </div>;
}

let a = 1, b = 2;
<div $mount='#App'>
    <Component a={a} b={b} />
    <button onclick={()=>{
        a ++;
        b ++;
    }}>Increase Data</button>
</div>

属性解构

属性的传入和使用都支持使用属性解构,并且不会使属性丢失响应式能力。

运行结果 显示编译产物
jsx
function Component({a, b}){
    return <div>
        a = {a}; b = {b};
    </div>;
}

let data = {a: 1, b: 2};
<div $mount='#App'>
    <Component {...data} />
    <button onclick={()=>{
        data.a ++;
        data.b ++;
    }}>Increase Data</button>
</div>

2. JSX扩展属性

Alins组件不支持以下JSX扩展属性:$attributes、$html、$ref,因为这几个属性是直接作用于DOM元素上的。组件由于本身并不直接表示DOM元素,所以无法使用上述属性。

由于组件的返回值可以是DOM元素或者 DocumentFragment,而 DocumentFragment 在 mounted和removed监听上存在一定局限性,所以 Alins 对于生命周期属性做了一些处理。当返回值是DOM元素时,直接作用于组件返回值;当返回值是 DocumentFragment,作用于第一个是DOM元素的子节点。

组件使用$mount 以及 生命周期属性例子如下:

运行结果 显示编译产物
jsx
function Component () {
    let dom;
    return <div $ref={dom}>
        <span>Hello!</span>
        <button onclick={dom.remove()}>Remove</button>
    </div>;
}

<Component $mount='#App'
    $mounted={(dom) => {console.log('mounted', dom.tagName);}}
    $appended={(dom) => {console.log('appended', dom.tagName);}}
    $removed={(dom) => {console.log('removed', dom.tagName);}}
    $created={(dom) => {console.log('created', dom.tagName);}}
></Component>;

3. 子节点

组件内部可以添加子节点,子节点会被传入组件函数的第二个参数,是一个节点数组,可以插入到组件函数的返回值JSX对象中。

运行结果 显示编译产物
jsx
function Counter ({count}, children) {
    return <div>
        <span>Count is {count}</span>
        {children}
    </div>;
}

let count = 0;
<Counter $mount='#App' count={count}>
    <button onclick={count ++}>Increase Count</button>
</Counter>;

4. 异步组件

异步函数也可以作为组件使用,我们称为异步组件,Alins遇到异步组件时,会先生成一个空节点作为锚点返回,在异步组件执行完毕返回真正的节点时,再替换掉锚点。

运行结果 显示编译产物
jsx
function mockFetch(){
    return new Promise(resolve => {
        setTimeout(()=>{
            resolve({name: 'Bob', age: 10})
        }, 1000)
    });
}

async function Component(){
    const data = await mockFetch();
    return <div>name={data.name}; age={data.age}</div>
}

<button onclick={<Component $mount='#App'/>} $mount='#App'>
    Mount Async Component
</button>

5. 单向数据流

Alins遵循单向数据流原则,父组件传入子组件的数据也具有响应式,但是无法在子组件中变更,如果需要在子组件中修改父组件数据,可以通过传递方法进行修改,也可以使用后续章节中介绍到的状态共享方案。

运行结果 显示编译产物
jsx
function Component({msg, modifyMsg}, children){
    return <div>
        <div>msg = {msg}</div>
        <button onclick={msg += '!'}>子组件直接修改msg[无效]</button>
        <button onclick={modifyMsg()}>子组件通过方法修改msg</button>
        {children}
    </div>
}

let msg = 'Hello';
function modifyMsg(){
    msg += '!';
}
<Component msg={msg} modifyMsg={modifyMsg} $mount='#App'>
    <button onclick={msg += '!'}>父组件修改msg</button>
</Component>

Alins 2022-present