路由组件

路由组件是一种特殊类型的内容,当我们在使用属性指定路由内容时,路由可以在加载时加载它。componentcomponentUrl它应该有助于更好地组织我们的应用程序,将事情放在适当的位置,并以更快速、更清晰和更舒适的方式完成许多事情。

它应该有助于更好地组织我们的应用程序,将事情放在适当的位置,并以更快速、更清晰和更舒适的方式完成许多事情。

组件函数

组件是一个接收propscontext并应该返回渲染函数的函数。

组件渲染函数应该返回标记模板字面量带有组件 HTML 内容。

例如:

const MyComponent = (props, context) => {
  // some component logic
  let value = 'foo';

  // return render function
  return () => context.$h`
    <div class="page">
      <p>Value is ${value}</p>
    </div>
  `;
}

组件模板

如上所述,组件渲染函数应该返回标记模板字面量带有组件 HTML 内容。它有一些重要的东西要注意。

所有自闭合标签都必须关闭!如果没有关闭自闭合标签,如<br>, <img src="">, <input ...>,则编译器将抛出错误。

所有空元素都可以自闭合:

<div class="my-div"></div>

<!-- also valid as -->
<div class="my-div" />

组件属性

接收组件函数的第一个参数是props。该对象将包含您在导航方法中传递的所有属性,以及所有路由参数。

例如,如果我们有以下路由:

{
  path: '/blog/:id',
  component: MyComponent
}

当我们通过/blog/34/URL 导航到路由时,它将具有props.id等于'34'.

。此外,当我们使用 API 如此导航到组件时:

router.navigate('/blog/34/', {
  props: {
    foo: 'bar'
  }
})

然后props将是以下对象:{ id: '34', foo: 'bar' }

此外,属性将包含传递给自定义组件作为属性的属性。如果自定义组件有这样的属性:

<my-component foo="bar" id="25" user=${{name: 'John'}} number=${30}></my-component>

添加$props将是:

{
  foo: 'bar',
  id: '25',
  user: {
    name: 'John'
  },
  number: 30
}

组件上下文

context对象包含许多有用的辅助工具:

属性描述
$h

特殊标记模板字面量必须用于包装组件渲染函数结果和所有 HTML 条目:

const MyComponent = (props, { $h }) => {
  let list = ['item 1', 'item 2'];

  return () => $h`
    <div class="page">
      <ul>
        ${list.map((item) => $h`
          <li>${item}</li>
        `)}
      </ul>
    </div>
  `
}
$el

对象,其中.value属性包含包含组件 HTML 元素的 Dom7 实例。

$el.value将仅在组件挂载后(或在任何页面事件,如pageInit)中

const MyComponent = (props, { $el, $onMounted }) => {
  $onMounted(() => {
    $el.value.find('p').addClass('red')
  })
  // ...
}
$

Dom7库:

const MyComponent = (props, { $, $onMounted }) => {
  $onMounted(() => {
    $('p').text('hello world')
  })
  // ...
}
$f7

Framework7 应用实例

$f7.dialog.alert('Hello world!')
$store

Store 实例。查看Store 文档获取更多详细信息和示例。

$f7route当前路由。包含路由对象query, hash, params, pathurl
$f7router

相关路由实例

$f7router.back(); //navigate back
$theme

带有md, ios指示当前主题的布尔属性。例如:

if ($theme.ios) { /* do something when iOS theme is active */ }
if ($theme.md) { /* do something when MD theme is active */ }
$update(回调)

此方法表示此组件及其子组件需要使用更新后的状态重新渲染

const MyComponent = (props, { $update, $h }) => {
  // initial state
  let value = 'foo';

  const updateValue = () => {
    // update local state
    value = 'foo2';
    // call $update method
    $update();
  }

  return () => $h`
    <div class="page">
      <p>Value is ${value}</p>
      <button @click=${updateValue}>Update Value</button>
    </div>
  `;
}

它不保证 DOM 更改立即应用,因此如果您依赖于 DOM(例如,需要在状态更改后获取 HTML 内容或属性值),则将callback函数作为参数传递。

$ref(initialValue)

此方法创建响应式“变量”,在更新后自动更新组件,而无需调用$update()方法。

它返回一个对象,其中value属性必须分配新值。

const MyComponent = (props, { $ref, $h }) => {
  // create reactive object
  const someVar = $ref('foo'); //-> { value: 'foo' }

  const updateValue = () => {
    // update "value" property of the reactive object
    someVar.value = 'bar';
  }

  return () => $h`
    <div class="page">
      <p>Value is ${someVar.value}</p>
      <button @click=${updateValue}>Update Value</button>
    </div>
  `;
}

它不保证 DOM 更改立即应用,因此如果您依赖于 DOM(例如,需要在状态更改后获取 HTML 内容或属性值),则将callback函数作为参数传递。

$useState(initialValue)

此方法创建响应式“状态”。

$useState接受 3 种类型的数据:

  • array
  • object
  • atoms- 任何不是数组或对象的东西(字符串、数字、null 等)

array & object保持原样(它们已经是引用),

atoms通过 Object.defineProperty 包装在 obj 中(就像$ref)

对于atoms $useState返回:

{
  state:         // state.value
  update:  (v)   // [state.value = value]
  clear:   ()    // [state.value = undefined]
  method:  (f)   // [custom method]
  async:   (f)   // [custom method promise]
}

对于array $useState返回:

{
  state:         // state.value
  update:  (v)   // [state.length = 0, state.push(...value)]
  remove:  (x)   // [remove item/list by given position]
  clear:   ()    // [remove all items from array]
  insert:  (x,v) // [insert item/list at position x]
  replace: (x,v) // [replace item/list at position x]
  append:  (v)   // [append item/list]
  prepend: (v)   // [prepend item/list]
  swap:    (a,b) // [swap index a with index b]
  fromTo:  (a,b) // [move index a to index b]
  method:  (f)   // [custom method]
  async:   (f)   // [custom method promise]
}

对于object $useState返回:

{
  state:         // state.value
  update:  (v)   // [Object.assign(state,value)]
  remove:  (v)   // [remove key or list of keys]
  clear:   ()    // [remove all keys from object]
  method:  (f)   // [cunstom method]
  async:   (f)   // [cunstom method promise]
}

例如:

// atoms
const { state, update } = $useState('text');

state.value; // 'text'
update('new value');
state.value; // 'new value'
clear();
state.value; // undefined
update('text');
state.value; // 'text'
// object
const { state, update, remove, clear } = $useState({});

state; // {}
update({ foo: 'bar' });
state; // { foo: 'bar' }
update({ foo: 'qux', baz: 'quux' });
state; // { foo: 'qux', baz: 'quux' }
update({ baz: 'corge' });
state; // { foo: 'qux', baz: 'corge' }
clear();
state; // {}
update({ grault: 'garply', list: [1, 2, 3] });
state; // { grault: 'garply', list: [1, 2, 3] }
update({ grault: null, dummy: { key: 'value' } });
state; // { grault: null, list: [1, 2, 3], dummy: { key: 'value' } }
remove('grault');
state; // { list: [1, 2, 3], dummy: { key: 'value' } }
update({ foo: 'bar' });
state; // { list: [1, 2, 3], dummy: { key: 'value' }, foo: 'bar' }
remove(['list', 'dummy']);
state; // { foo: 'bar' }
// array
const {
  state, update, insert, replace, append,
  prepend, swap, fromTo, remove, clear
} = $useState([]);

state // []
update([1, 2, 3]);
state // [1, 2, 3]
append(4);
state // [1, 2, 3, 4]
append([5, 6]);
state // [1, 2, 3, 4, 5, 6]
prepend(-1);
state // [-1, 1, 2, 3, 4, 5, 6]
prepend([-3, -2]);
state // [-3, -2, -1, 1, 2, 3, 4, 5, 6]
insert(3,0);
state // [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6]
insert(4,[0.5, 0.9]);
state // [-3, -2, -1, 0, 0.5, 0.9, 1, 2, 3, 4, 5, 6]
replace(5,0.8);
state // [-3, -2, -1, 0, 0.5, 0.8, 1, 2, 3, 4, 5, 6]
replace(7,[22, 2.5]);
state // [-3, -2, -1, 0, 0.5, 0.8, 1, 22, 2.5, 3, 4, 5, 6]
swap(0,12);
state // [6, -2, -1, 0, 0.5, 0.8, 1, 22, 2.5, 3, 4, 5, -3]
fromTo(1,10);
state // [6, -1, 0, 0.5, 0.8, 1, 22, 2.5, 3, 4, -2, 5, -3]
remove(2);
state // [6, -1, 0.5, 0.8, 1, 22, 2.5, 3, 4, -2, 5, -3]
remove([0, 3, 1, 2]);
state // [1, 22, 2.5, 3, 4, -2, 5, -3]
clear();
state // []
update([1, 2, 3]);
state // [1, 2, 3]
$tick(回调)

如果您依赖于 DOM 并需要在调用$update()方法后确保组件状态和 DOM 更新,您也可以使用此方法。

传递的回调将在 DOM 更新时执行。

此方法返回在 DOM 更新时也会解析的 Promise。

因此,您可以使用它作为:

$update();

$tick(function () {
  console.log('DOM and state updated');
});

// Or as Promise
$tick().then(() => {
  console.log('DOM and state updated');
})

// Or in async function/method as:
await $tick();
console.log('DOM and state updated');
$f7ready(回调)

只有在使用主应用组件时才需要使用此方法,以确保在应用程序初始化时调用 Framework7 API。

const AppComponent = (props, { $f7, $f7ready }) => {
  $f7ready(() => {
    // now it is safe to call Framework7 APIs
    $f7.dialog.alert('Hello!');
  });

  // ...
}
事件
$on

用于将 DOM 事件处理程序附加到组件根元素的函数

const MyComponent = (props, { $on }) => {
  // attach 'pageInit' event handler
  $on('pageInit', (e, page) => {
    console.log(page.name)
  });
  // ...
}

这样的事件处理程序将在组件销毁时自动分离

$once

用于将 DOM 事件处理程序附加到组件根元素的函数。与$on相同,但这样的处理程序只执行一次。

$emit(事件上触发, data)

用于在可重用自定义组件中发出自定义 DOM 事件的函数:

const MyComponent = (props, { $emit }) => {
  // emits custom event
  $emit('myevent')
  // ...
}

在其他父组件中:

<my-component @myevent=${doSomething} />
生命周期钩子
$onBeforeMount在组件将要添加到 DOM 时调用
$onMounted

在组件被添加到 DOM 后立即调用

const MyComponent = (props, { $onMounted }) => {
  // do something when component mounted
  $onMounted(() => {
    console.log('component mounted')
  });
  // ...
}
$onBeforeUpdate在组件 VDOM 修补/更新之前立即调用
$onUpdated在组件 VDOM 已修补/更新后立即调用
$onBeforeUnmount在组件将要卸载(从 DOM 分离)之前调用
$onUnmounted在组件卸载和销毁时调用

因此,带有页面组件的示例路由可能如下所示:

routes = [
  // ...
  {
    path: '/some-page/',
    // Component
    component: (props, { $h, $f7, $on }) => {
      const title = 'Component Page';
      const names = ['John', 'Vladimir', 'Timo'];

      const openAlert = () => {
        $f7.dialog.alert('Hello world!');
      }

      $on('pageInit', (e, page) => {
        // do something on page init
      });
      $on('pageAfterOut', (e, page) => {
        // page has left the view
      });

      return () => $h`
        <div class="page">
          <div class="navbar">
            <div class="navbar-bg"></div>
            <div class="navbar-inner">
              <div class="title">${title}</div>
            </div>
          </div>
          <div class="page-content">
            <a @click=${openAlert} class="red-link">Open Alert</a>
            <div class="list simple-list">
              <ul>
                ${names.map((name) => $h`
                  <li>${name}</li>
                `)}
              </ul>
            </div>
          </div>
        </div>
      `;
    },
  },
  // ...
]

组件页面事件

组件页面事件处理程序可以传递在$on组件事件处理程序中。它们是普通的 DOM页面事件。因为它们是 DOM 事件,所以它们接受event作为第一个参数,以及页面数据作为第二个参数。它们与普通 DOM 事件的区别仅在于事件处理程序名称必须以驼峰格式指定(page:init->pageInit)访问:

const MyComponent = (props, { $on }) => {
  $on('pageMounted', (e, page) => {
    console.log('page mounted');
  });
  $on('pageInit', (e, page) => {
    console.log('page init');
  });
  $on('pageBeforeIn', (e, page) => {
    console.log('page before in');
  });
  $on('pageAfterIn', (e, page) => {
    console.log('page after in');
  });
  $on('pageBeforeOut', (e, page) => {
    console.log('page before out');
  });
  $on('pageAfterOut', (e, page) => {
    console.log('page after out');
  });
  $on('pageBeforeUnmount', (e, page) => {
    console.log('page before unmount');
  });
  $on('pageBeforeRemove', (e, page) => {
    console.log('page before remove');
  });
}

DOM 事件处理

注意组件模板中的额外@属性。它是分配事件监听器到指定元素的一种简写方法。指定的事件处理程序将在组件范围内搜索。

这样的事件处理程序属性值必须是一个函数:

const MyComponent = (props, { $h, $update }) => {
  let value = 10;
  const addValue = (number) => {
    value += number;
    $update();
  }
  const onClick = () => {
    console.log('click');
  }

  return () => $h`
    <div class="page">
      <!-- pass function to attribute -->
      <button @click=${onClick}>Button</button>

      <!-- also work -->
      <button @click=${() => onClick()}>Button</button>

      <!-- will not work, attribute value "onClick" is just a string -->
      <button @click="onClick">Button</button>

      <!-- passing dynamic data will work as expected -->
      <button @click=${() => addValue(15)}>Button</button>
    </div>
  `
}

事件处理程序仅在初始渲染时处理,或对于使用 VDOM 修补的元素。如果您手动将这样的元素添加到 DOM 中,它将不起作用!

const MyComponent = (props, { $h, $on }) => {
  const onClick = () => {
    console.log('click');
  }

  $on('pageInit', (e, page) => {
    // this won't work
    page.$el.append('<a @click="onClick">Link</a>');
  });

  return () => $h`
    <div class="page">
    </div>
  `
}

组件根元素

组件模板或渲染函数必须只返回单个 HTML 元素。并且它必须是路由支持的一个元素:

单文件组件

在同一个路由数组下指定所有组件路由不是很方便,特别是如果我们有很多这样的路由。这就是为什么我们可以使用componentUrl而将组件放入单文件中:

routes = [
  ...
  {
    path: '/some-page/',
    componentUrl: './some-page.f7',
  },
  ..
];

some-page.f7:

<!-- component template, uses same tagged template literals -->
<template>
  <div class="page">
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="title">${title}</div>
      </div>
    </div>
    <div class="page-content">
      <a @click=${openAlert}>Open Alert</a>
      <div class="list simple-list">
        <ul>
          ${names.map((name) => $h`
            <li>${name}</li>
          `)}
        </ul>
      </div>
    </div>
  </div>
</template>
<!-- component styles -->
<style>
  .red-link {
    color: red;
  }
</style>
<!-- rest of component logic -->
<script>
  // script must return/export component function
  export default (props, { $f7, $on }) => {
    const title = 'Component Page';
    const names = ['John', 'Vladimir', 'Timo'];

    const openAlert = () => {
      $f7.dialog.alert('Hello world!');
    }

    $on('pageInit', () => {
      // do something on page init
    });
    $on('pageAfterOut', () => {
      // page has left the view
    });

    // component function must return render function
    return $render;
  }
</script>

嗯,现在它更干净了。标签将被自动转换为导出组件的相同属性。<template><style>在组件函数的末尾必须有

It is mandatory to have return $render,因为它将被解析器用<template>标签的内容替换。

与 Webpack 和 Vite 的使用

对于Webpack,有一个特殊的framework7-loader插件,它允许将单文件组件捆绑到主捆绑包中,并且不需要每次都使用XHR(例如componentUrl)来加载和解析组件文件。

对于Vite.js,也有一个特殊的rollup-plugin-framework7插件来捆绑单文件组件。

这些插件解析单文件组件的文件,并在捆绑过程中将其转换为普通的JS对象。因此,它有可能提高应用性能,因为它不会在运行时进行解析和编译。

当插件配置完成后,我们需要将单文件组件存储在.f7(或对于Webpack在.f7.html)文件中,并使用export default来导出组件:

<template>
  <div class="page">
    ...
  </div>
</template>
<script>
  export default () => {
    let foo = 'bar';

    const doThis = () => {
      // ...
    }

    return $render;
  }
</script>

也可以导入所需的依赖项和样式:

<template>
  <div class="page">
    ...
  </div>
</template>
<script>
  import './path/to/some-styles.css';
  import utils from './path/to/utils.js';

  export default () => {
    let foo = 'bar';
    let now = utils.now();

    const doThis = () => {
      // ...
    }

    return $render;
  }
</script>

然后我们可以导入它并将其添加到路由中:

// routes.js

import NewsPage from './path/to/news.f7';
import ServicesPage from './path/to/services.f7';

export default [
  {
    path: '/news/',
    component: NewsPage,
  },
  {
    path: '/services/',
    component: ServicesPage,
  }
]

JSX

模板字面量在HTML文档中没有良好的语法高亮。但是,在使用webpack或Vite时,也可以使用JSX语法.

要使其工作,我们需要将组件存储在.f7.jsx文件中,并使用JSX编写它们:

export default (props, { $update }) => {
  let value = 10;
  const items = ['Item 1', 'Item 2'];

  const addValue = (number) => {
    value += number;
    $update();
  }

  //- render function should returns JSX
  return () => (
    <div class="page">
      <p>The value is {value}</p>
      <p>
        {/* JSX doesn't support @ in attribute name so event handlers should start from "on" */}
        <button onClick={() => addValue(10)}>Add Value</button>
      </p>
      <ul>
        {items.map((item) => (
          <li>{item}</li>
        ))}
      </ul>
    </div>
  )
}

然后在routes.js:

import NewsPage from './path/to/news.f7.jsx';
import ServicesPage from './path/to/services.f7.jsx';

export default [
  {
    path: '/news/',
    component: NewsPage,
  },
  {
    path: '/services/',
    component: ServicesPage,
  }
]

虚拟 DOM

虚拟DOM

虚拟DOM和所有与VDOM相关的功能从Framework7版本3.1.0开始可用。

虚拟DOM(VDOM)是一种编程概念,其中UI的理想或“虚拟”表示形式保存在内存中,并与“真实”DOM同步。它允许我们将应用程序的视图表示为其状态的一个函数。Snabbdom因为它极其轻量级、快速,非常适合Framework7环境。

那么,Framework7路由组件的VDOM渲染是如何工作的?组件模板被转换为VDOM,而不是直接插入到DOM中。稍后,当组件状态发生变化时,它会创建新的VDOM并将其与以前的VDOM进行比较。然后根据这些差异它通过仅更改需要更改的元素和属性来修补真实DOM。所有这一切都是自动发生的!

让我们看看那个用户配置文件组件示例,当请求用户数据时,它将自动更新布局:

<template>
  <div class="page">
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner">
        <div class="title">Profile</div>
      </div>
    </div>
    <div class="page-content">
      ${user && $h`
        <!-- Show user list when it is loaded -->
        <div class="list simple-list">
          <ul>
            <li>First Name: ${user.firstName}</li>
            <li>Last Name: ${user.lastName}</li>
            <li>Age: ${user.age}</li>
          </ul>
        </div>
      `}
      ${!user && $h`
        <!-- Otherwise show preloader -->
        <div class="block block-strong text-align-center">
          <div class="preloader"></div>
        </div>
      `}
    </div>
  </div>
</template>
<script>
  export default (props, { $on, $f7, $update }) => {
    // empty initial user data
    let user = null;

    $on('pageInit', () => {
      // request user data on page init
      fetch('https://api.website.com/get-user-profile')
        .then((res) => res.json())
        .then((data) => {
          // update user with new data
          user = data;
          // trigger re-render
          $update();
        });
    })

    return $render;
  }
</script>

注意,直接分配到组件状态不会触发布局更新。使用$update立即更新组件布局!

列表中的键和自动初始化组件

当VDOM更新元素列表时,默认情况下它使用“就地修补”策略。如果数据项的顺序发生了变化,它不会移动DOM元素以匹配项的顺序,而是就地修补每个元素,并确保它在特定索引处反映应该渲染的内容。

此默认模式效率很高,但仅适用于当您的渲染输出不依赖于子组件状态或临时DOM状态(例如表单输入值).

要给VDOM一个提示,以便它可以跟踪每个节点的身份,从而重用和重新排序现有元素,您需要为每个项目提供一个唯一的key属性。

在渲染列表时,key的理想值是每个项目的唯一ID:

<template>
  ...
  <ul>
    ${items.map((item) => $h`
      <li key=${item.id}>...</li>
    `)}
  </ul>
  ...
</template>
<script>
  export default () => {
    const items = [
      {
        id: 1,
        title: 'Item A'
      },
      {
        id: 2,
        title: 'Item B'
      },
    ];

    return $render;
  }
</script>

与自动初始化的组件(如范围滑块, 计量表和其他应该在使用时自动初始化(如果它们有range-slider-init, gauge-init)的组件)相同,当它们添加到DOM时自动初始化,当它们从DOM中移除时自动销毁。因此,这些元素也必须用唯一的键进行标识。

<template>
  <div class="page">
    ...
    <div class="page-content">
      ${gaugeVisible && $h`
        <!-- must have unique key -->
        <div key="gauge" class="gauge gauge-init" data-type="circle"
          data-value="0.60"
          data-value-text="60%"
          data-value-text-color="#ff9800"
          data-border-color="#ff9800"
        ></div>
      `}
      ...
      <a href="#" class="button" @click=${showGauge}>Show Gauge</a>
    </div>
  </div>
</template>
<script>
  export default (props, { $update }) => {
    let gaugeVisible = false;

    const showGauge = () => {
      gaugeVisible = true;
      $update();
    }

    return $render;
  }
</script>
  • 注意key属性必须在单个组件中唯一。
  • 如果key属性未指定,并且元素具有id属性,则id属性将用作虚拟节点唯一键。

innerHTML

如果我们需要插入HTML字符串(例如,从API端点接收的),我们需要使用特殊的innerHTML元素prop/属性:

<template>
  <div class="page">
    ...
    <div class="block" innerHTML=${customHTML}></div>
  </div>
</template>
<script>
  export default (props) => {
    const customHTML = '<p>Hello <b>World!</b></p>';

    return $render;
  }
</script>

使用innerHTML元素上的

传递到innerHTML中的HTML内容只是一个字符串,例如组件事件处理程序(如@click属性)不会工作。

主应用组件

可以将整个应用布局作为一个组件。

注意,由于VDOM实现,强烈建议为每个自动初始化的视图(具有idkey属性:view-init类的视图)添加唯一的

要启用它,首先,我们应该在index.html:

<body>
  <!-- empty app root element -->
  <div id="app"></div>
</body>

中保持应用根元素为空:

<!-- app.f7 -->
<template>
  <div id="app">
    ${loggedIn.value && $h`
      <div class="panel panel-left panel-reveal panel-init">
        <!-- every View has unique ID attribute -->
        <div class="view view-init" id="view-panel" data-url="/panel/"></div>
      </div>
      <div class="view view-main view-init" id="view-main" data-url="/"></div>
    `}
    ${!loggedIn.value && $h`
      <div class="login-screen modal-in">
        <div class="view view-init" id="view-auth" data-url="/auth/"></div>
      </div>
    `}
  </div>
</template>
<script>
  export default (props, { $store }) => {
    const loggedIn = $store.getters.loggedIn;

    return $render;
  }
</script>

然后,我们需要创建主应用程序组件,例如使用Vite的单文件组件:

// import main app component
import App from './path/to/app.f7';

var app = new Framework7({
  // specify main app component
  component: App,
})

最后,在初始化Framework7时,我们需要指定应用程序组件:

var app = new Framework7({
  // load main app component
  componentUrl: './path/to/app.f7',
})

还要注意,主应用程序组件将在应用初始化过程完成之前挂载(添加到DOM中)。所以如果您需要立即调用Framework7 API,请使用$f7ready回调:

<template>
  <div id="app">
    ...
  </div>
</template>
<script>
  export default (props, { $f7ready, $f7 }) => {
    $f7ready(() => {
      // now it is safe to call Framework7 APIs
      $f7.dialog.alert('Hello!');
    })
  }
</script>

自定义组件

注册组件

可以创建可重用的自定义组件。我们需要在 Framework7 初始化之前使用以下方法来完成:

Framework7.registerComponent(tagName, 组件上传递单独的属性)- 注册自定义组件

  • tagName - 字符串. 组件标签名,例如my-component(将用作<my-component>)中

    自定义组件标签名必须包含连字符/短划线字符 "-"

  • 组件上传递单独的属性 - 对象. 组件函数

注意,目前,自定义组件只能在路由组件中使用(由路由加载的组件)。

Framework7.registerComponent(
  // component name
  'my-list-item',

  // component function
  (props, { $h }) => {
    let foo = 'bar';

    return () => $h`
      <li class="item-content" id="${props.id}">...</li>
    `
  }
)

并在其他组件中使用,例如:

<div class="list">
  <ul>
    <my-list-item id="item-1"></my-list-item>
  </ul>
</div>

注意,传递给自定义组件元素的属性在组件中可用props.

本地组件

可以在组件中创建本地自定义组件:

<template>
  <ul>
    <!-- use tag names as variables -->
    <${ListItem} title="Item 1" />
    <${ListItem} title="Item 2" />
    <${ListItem} title="Item 3" />
  </ul>
</template>
<script>
  // create local component
  const ListItem = (props, { $h }) => {
    return () => $h`<li>${props.title}</li>`;
  }

  // export main component
  export default () => {
    return $render;
  }
</script>

或者可以导入:

<template>
  <ul>
    <!-- use tag names as variables -->
    <${ListItem} title="Item 1" />
    <${ListItem} title="Item 2" />
    <${ListItem} title="Item 3" />
  </ul>
</template>
<script>
  // import component
  import ListItem from 'path/to/list-item.f7';

  // export main component
  export default () => {
    return $render;
  }
</script>

使用 JSX:

const ListItem = (props) => {
  return (
    <li>{props.title}</li>
  )
}
/* or
import ListItem from 'path/to/list-item.f7.jsx'
*/

export default () => {
  return () => (
    <ul>
      <ListItem title="Item 1" />
      <ListItem title="Item 2" />
      <ListItem title="Item 3" />
    </ul>
  )
}

在 JSX 中,它可以在主组件内部创建:

export default () => {

  const ListItem = (props) => {
    return (
      <li>{props.title}</li>
    )
  }

  return () => (
    <ul>
      <ListItem title="Item 1" />
      <ListItem title="Item 2" />
      <ListItem title="Item 3" />
    </ul>
  )
}

事件

您可以在模板中为自定义组件分配 DOM 事件,使用相同的语法。事件处理程序实际上会附加到自定义组件的根元素。@{event} syntax. Event handler will be actually attached to custom component root element.

<template>
  <div class="page">
    ...
    <my-button @click="onClick">Click Me</my-button>
  </div>
</template>
<script>
  return {
    // ...
    methods: {
      onClick: function(e) {
        console.log('clicked');
      }
    },
    // ...
  }
</script>

插槽

如果我们需要将子元素(或文本)传递给自定义组件,我们需要使用插槽。这里的插槽实现与Web Components 插槽.

使用slot标签我们指定组件子元素应该放置的位置。例如my-button组件模板:

<a class="button button-fill">
  <slot></slot>
</a>

然后可以这样使用:

<my-button>Click Me</my-button>

要指定插槽的默认值(当没有传递子元素时),我们只需将其放在<slot>标签内:

<a class="button button-fill">
  <slot>Default Button Text</slot>
</a>

要在组件布局中分布元素,我们可以使用命名插槽。例如,的模板my-container组件:

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

我们可以像这样使用:

<my-container>
  <h1 slot="header">Title</h1>

  <p>Text for main content.</p>
  <p>More text for main content.</p>

  <p slot="footer">Footer content</p>
</my-container>

组件结果输出将是:

<div class="container">
  <header>
    <h1>Title</h1>
  </header>
  <main>
    <p>Text for main content.</p>
    <p>More text for main content.</p>
  </main>
  <footer>
    <p>Footer content</p>
  </footer>
</div>

模板配方

条件渲染

要在 JavaScript 中实现条件,我们通常使用if (if-else) 语句。在模板和 JSX 中,我们不能直接使用它们,而应该使用 JavaScript 运算符。

if

对于if语句,我们应该使用逻辑与(&&)运算符:

<template>
  <div class="page">
    ${someVar && $h`
      <p>Text will be visible when "someVar" is truthy</p>
    `}

    ${someVar === 1 && $h`
      <p>Text will be visible when "someVar" equals to 1</p>
    `}
  </div>
</template>
<script>
  export default () => {
    const someVar = 1;

    return $render;
  }
</script>

使用 JSX:

export default () => {
  const someVar = 1;

  return () => (
    <div class="page">
      {someVar && (
        <p>Text will be visible when "someVar" is truthy</p>
      )}

      {someVar === 1 && (
        <p>Text will be visible when "someVar" equals to 1</p>
      )}
    </div>
  )
}

if-else

对于if-else我们可以使用三元运算符(?:)或&&!运算符的组合:

<template>
  <div class="page">
    ${someVar ? $h`
      <p>Text will be visible when "someVar" is truthy</p>
    ` : $h`
      <p>Text will be visible when "someVar" is falsy</p>
    `}

    {someVar && (
      <p>Text will be visible when "someVar" is truthy</p>
    )}
    {!someVar && (
      <p>Text will be visible when "someVar" is falsy</p>
    )}
  </div>
</template>
<script>
  export default () => {
    const someVar = 1;

    return $render;
  }
</script>

使用 JSX:

export default () => {
  const someVar = 1;

  return () => (
    <div class="page">
      {someVar ? (
        <p>Text will be visible when "someVar" is truthy</p>
      ) : (
        <p>Text will be visible when "someVar" is falsy</p>
      )}

      {someVar && (
        <p>Text will be visible when "someVar" is truthy</p>
      )}
      {!someVar && (
        <p>Text will be visible when "someVar" is falsy</p>
      )}

    </div>
  )
}

将数组映射到元素

要将数组映射到元素,我们使用 Array 的.map()方法:

<template>
  <div class="page">
    <ul>
    ${items.map((item) => $h`
      <li>${item}</li>
    `)}
    </ul>
  </div>
</template>
<script>
  export default () => {
    const items = [
      'item 1',
      'item 2',
      'item 3',
    ];

    return $render;
  }
</script>

使用 JSX:

export default () => {
  const items = [
    'item 1',
    'item 2',
    'item 3',
  ];

  return () => (
    <div class="page">
      <ul>
        {items.map((item) => (
          <li>{item}</li>
        ))}
      </ul>
    </div>
  )
}