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>
256 byte
运行结果 显示编译产物
a = 1; b = 2;
import { _$ce, _$r } from "alins";
function Component(props) {
  return _$ce("div", null, "a = ", () => props.a.v, "; b = ", () => props.b.v, ";");
}
let a = _$r(1),
  b = _$r(2);
_$ce("div", {
  $mount: "#App"
}, _$ce(Component, {
  a: a,
  b: b
}), _$ce("button", {
  onclick: () => {
    a.v++;
    b.v++;
  }
}, "Increase Data"));
334 byte

属性解构

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

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>
260 byte
运行结果 显示编译产物
a = 1; b = 2;
import { _$ce, _$es, _$r } from "alins";
function Component({
  a,
  b
}) {
  return _$ce("div", null, "a = ", a, "; b = ", b, ";");
}
let data = _$r({
  a: 1,
  b: 2
});
_$ce("div", {
  $mount: "#App"
}, _$ce(Component, _$es(data.v)), _$ce("button", {
  onclick: () => {
    data.v.a++;
    data.v.b++;
  }
}, "Increase Data"));
329 byte

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>;
456 byte
运行结果 显示编译产物
Hello!
created DIV
appended DIV
mounted DIV
import { _$ce } from "alins";
function Component() {
  let dom = void 0;
  return _$ce("div", {
    $ref: _$ref => dom = _$ref
  }, _$ce("span", null, "Hello!"), _$ce("button", {
    onclick: $e => dom.remove()
  }, "Remove"));
}
_$ce(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);
  }
});
538 byte

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>;
247 byte
运行结果 显示编译产物
Count is 0
import { _$ce, _$r } from "alins";
function Counter({
  count
}, children) {
  return _$ce("div", null, _$ce("span", null, "Count is ", count), children);
}
let count = _$r(0);
_$ce(Counter, {
  $mount: "#App",
  count: count
}, _$ce("button", {
  onclick: $e => count.v++
}, "Increase Count"));
295 byte

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>
372 byte
运行结果 显示编译产物
import { _$ce } from "alins";
function mockFetch() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({
        name: 'Bob',
        age: 10
      });
    }, 1000);
  });
}
async function Component() {
  const data = await mockFetch();
  return _$ce("div", null, "name=", () => data.name, "; age=", () => data.age);
}
_$ce("button", {
  onclick: $e => _$ce(Component, {
    $mount: "#App"
  }),
  $mount: "#App"
}, "Mount Async Component");
462 byte

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>
476 byte
运行结果 显示编译产物
msg = Hello
import { _$ce, _$r } from "alins";
function Component({
  msg,
  modifyMsg
}, children) {
  return _$ce("div", null, _$ce("div", null, "msg = ", msg), _$ce("button", {
    onclick: $e => msg.v += '!'
  }, "\u5B50\u7EC4\u4EF6\u76F4\u63A5\u4FEE\u6539msg[\u65E0\u6548]"), _$ce("button", {
    onclick: $e => modifyMsg.v()
  }, "\u5B50\u7EC4\u4EF6\u901A\u8FC7\u65B9\u6CD5\u4FEE\u6539msg"), children);
}
let msg = _$r('Hello');
function modifyMsg() {
  msg.v += '!';
}
_$ce(Component, {
  msg: msg,
  modifyMsg: modifyMsg,
  $mount: "#App"
}, _$ce("button", {
  onclick: $e => msg.v += '!'
}, "\u7236\u7EC4\u4EF6\u4FEE\u6539msg"));
626 byte

Alins 2022-present