1、Demo解析

1.1 HTML 页面

我们首先来看看这个页面的 HTML 文件,它位于 src/html 目录下,可以看到这个页面的 css 和 js 的引用方式。这里的 js 文件分成了两部分:lib@VERSION.jsindex@VERSION.js。前者是基础的框架和组件代码,例如 React、Yo-Router 和一些常用的 Yo 3 组件,后者则是余下的代码。这样就把框架和业务代码分开了,不仅减小了 bundle 的总体积同时也加快了编译速度(通常情况下只会编译 index.js 文件,所以如果你更新了 React、Yo 3 这些框架,需要在项目根目录手动执行 ykit dll 以更新 lib 文件)。

  1. <link rel="stylesheet" href="//q.qunarzz.com/yo-ykit-init/prd/page/index@VERSION.css">
  1. <script type="text/javascript" src="//q.qunarzz.com/yo-ykit-init/prd/lib@VERSION.js"></script>
  2. <script type="text/javascript" src="//q.qunarzz.com/yo-ykit-init/prd/page/index@VERSION.js"></script>

你可能注意到了 <head> 标签里有这么一段:

  1. <meta cache-files="qp" href="//q.qunarzz.com/yo-ykit-init/prd/chunk@VERSION.json">

这个是针对代码分割的离线包配置,具体可见 附录1:关于 chunk 模式,关于代码分割后面还会提到。

1.2 JS 入口文件

接下来我们来看看 js 的入口文件 /src/index.js,这里有几个地方需要注意一下。

  1. import 'babel-polyfill';
  2. import React, { Component } from 'react';
  3. import ReactDOM from 'react-dom';
  4. import '@qnpm/hysdk'; // 用来适配大客户端和微信,如果不需要可以去掉
  5. import { Router, Route, IndexRoute, Link } from '$router';
  6. import HomePage from './home';
  7. import yoHistory from '../common/history';
  8. const List = require.async('./list');
  9. const Detail = require.async('./detail');
  10. const nav = (text, right) => {
  11. const result = {
  12. title: { text, style: 'text' }
  13. }
  14. if (right) result.right = { text: right, style: 'text' }
  15. return result
  16. }
  17. const Root = () => (
  18. <Router history={yoHistory}>
  19. <Route path="/" navigation={nav('首页')}>
  20. <IndexRoute component={HomePage}/>
  21. <Route path="list" getComponent={List} navigation={nav('列表页', '点我')} />
  22. <Route path="detail" getComponent={Detail} navigation={nav('详情页')} />
  23. </Route>
  24. </Router>
  25. );
  26. ReactDOM.render(<Root />, document.getElementById('root'));

首先 HySdk 需要先于 Yo-Router 引入,因为在 Hy 2 环境和微信环境中 Yo-Router 依赖了 HySdk。

其次就是后面的 require.async 引用,这就是前面提到的代码分割动态引用,ykit 会将这些页面打成单独的 chunk 文件,只有在进入这个页面时才会去请求和加载,这样可以加快首页的渲染速度。

最后就是对应的 Route 配置,因为是异步的引用,所以对应 Route 的 component 也得用异步对应的语法:

  1. <Route path="list" getComponent={List} navigation={nav('列表页', '点我')} />

Yo-Router 使用 getComponent 来引用异步加载的页面,后面的 navigation 项用来配置该页面的导航参数,与 hysdk/QunarAPI 的配置方式基本相同,详情可见 HySDK 附录:导航参数

1.3 Yo-Router 的 History 配置

  1. import { createHashHistory } from '$router';
  2. const yoHistory = createHashHistory({
  3. uniqueKey: 'create-unique-hybridId'
  4. });
  5. export default yoHistory;

Yo-Router 支持两种历史的创建方式,分别为 createHashHistorycreateBrowserHistory,其中前者是带 hash 的历史,后者是需要后端支持的不带 hash 的历史。这里用了 hash 方式并给项目指定了在 sessionStorage 里进行存储的一个唯一 key,Yo-Router 为了让在页面刷新时所有的历史记录也不被清空,将历史记录放到 sessionStorage 里进行管理。

关于 Yo-Router 的这部分内容,你可以到 基本使用:创建历史 获得更多信息。

1.4 Home 页面

  1. import React, { Component } from 'react';
  2. import { Scroller, Touchable } from '$yo-component';
  3. import Header from '$component/header/index.js';
  4. import yoHistory from '$common/history';
  5. import './index.scss';
  6. class HomePage extends Component {
  7. render() {
  8. return (
  9. <div className="yo-flex">
  10. <Header title="首页" left={false}/>
  11. <Scroller extraClass="flex">
  12. <div className="m-content">
  13. <Touchable touchClass="m-content-active" onTap={() => {
  14. yoHistory.push('/list');
  15. }}>
  16. <div>
  17. <p className="title">Hello World!</p>
  18. <p className="notice">Try To Tap This Area!</p>
  19. </div>
  20. </Touchable>
  21. </div>
  22. </Scroller>
  23. </div>
  24. )
  25. }
  26. }
  27. export default HomePage;

这里使用了 Yo 3 的 Touchable 和 Scroller 组件,这两个组件也是 Yo 3 最常用的组件。

Touchable 组件是一个"虚拟"组件,它的作用在于绑定点击事件,它并不会创建 DOM 节点,而是将点击的事件绑定到它的子组件上。我们推荐你优先使用 Touchable 来实现一切的点击效果,因为这样不仅能让你的代码更清晰而且也通过组件的复用解决了一些移动端的手势"顽疾"。

Scroller 组件是一个提供滚动效果的容器,它的作用在于统一安卓和 iOS 的滚动效果。因为 Yo 3 默认禁止了原生的滚动,所以你需要在所有需要滚动的组件外加上 Scroller。这里需要尤其注意的是滚动的容器组件的高度,它必须有确定的高度,否则它的高度可能会和内部的容器的高度一样,这样就无法正常的滚动了,这里在 Scroll 文档 里有详细的说明。

可以看到该页面在 Touchable 组件的 onTap 方法中执行了 yoHistory.push('/list');,通过 push 方法转跳到了之前 Router 注册的 list 页面,关于 Yo Router 的跳转 api 可以看 API: History 文档。

接下来的列表页和详情页大致相同,这里也就不再赘述了。