如何在 Preact 中使用 React Router

How to use React Router with Preact

我正在使用 Preact 作为我的视图框架 (由于 Facebook IP 问题,不能 使用 React)。我需要使用 React Router 进行位置路由,因为它比同一个团队构建的 Preact Router 更灵活。

我设法让 React Router 接受 Preact 代替 React,但是,我无法让它匹配位置。不知道是兼容性问题,还是配置问题。我试过只使用一对路由( App 和 Account ),但它仍然不适用于简化的设置。

问:有没有人看到我在这里做错了什么?

我得到的错误是:Location "/account/12345/" did not match any routes

main.js

import { h, render } from 'preact';
import { Router, browserHistory } from 'react-router';
import { Provider } from 'react-redux';

import createStore from './createStore';
import createRoutes from './createRoutes';

process.env.DEBUG && console.log('Hello, developer!');

const history = browserHistory;
const store = createStore( history );
const routes = createRoutes( store );

render((
    <Provider store={ store } key="redux-provider">
        <Router history={ history } createElement={ h } routes={ routes } />
    </Provider>
), document.body );

createRoutes.js

import { h } from 'preact';
import { IndexRoute, Route } from 'react-router';

// App Component
import App from './components/app';

// Sub Components
import Account from './components/account';
import Conversation from './components/conversation';
import Dashboard from './components/dashboard';

// Error Components
import BadAccount from './components/bad-account';
import NotFound from './components/not-found';

// Routes
export default ()=> (
    <Route path="/" component={App}>
        {/* Get URL parameter */}
        <Route path="account/:accountID" component={Account}>
            {/* Index Route */}
            <IndexRoute component={Dashboard} />
            {/* Sub Routes ( Alphabetical Please ) */}
            <Route path="chat" component={Conversation} />
            {/* Catch-All Route */}
            <Route path="*" component={NotFound} />
        </Route>
        {/* Handle Invalid URIs */}
        <Route path="*" component={BadAccount} />
    </Route>
);

createStore.js

import { applyMiddleware, combineReducers, compose, createStore } from 'redux';
import thunk from 'redux-thunk';
import { routerMiddleware } from 'react-router-redux';

import messages from './resources/messages/reducer';
import conversation from './resources/conversation/reducer';
import layout from './resources/layout/reducer';
import profile from './resources/profile/reducer';
import contract from './resources/contract/reducer';

/*const { devToolsExtension } = window;*/

export default history => {

    // Sync dispatched route actions to the history
    const reduxRouterMiddleware = routerMiddleware( history );

    // Create single reducer from all modules
    const rootReducer = combineReducers({
        messages,
        conversation,
        layout,
        profile,
        contract
    });

    // List redux middleware to inject
    const middleware = [
        thunk,
        reduxRouterMiddleware
    ];

    // Compose the createStore function
    const createComposedStore = compose(
        applyMiddleware( ...middleware )/*, // Figure this out...
        ( process.env.DEBUG && devToolsExtension ) ? devToolsExtension() : f => f*/
    )( createStore );

    // Create the store
    const store = createComposedStore( rootReducer );

    // Hook up Redux Routing middleware
    // reduxRouterMiddleware.listenForReplays(store);

    // Return store
    return store;
};

找到问题,是 Preact 与 React 的兼容性问题:

未正确处理上下文:

https://github.com/developit/preact/issues/156

props.children 处理不当:

https://github.com/developit/preact-compat/issues/47#issuecomment-220128365

更新后的答案是现在有一个 preact-router 包:https://www.npmjs.com/package/preact-router

import Router from 'preact-router';
import { h } from 'preact';

const Main = () => (
  <Router>
    <Home path="/" />
    <About path="/about" />
    <Search path="/search/:query" />
  </Router>
);

render(<Main />, document.body);

(OP已经解决了他的问题,但是这个在Google中排名靠前,对新手帮助不大,所以我想我提供一些背景信息)

Preact 和 preact-compat

preact is a minimal version of React that weighs just 3Kb. It implements a subset of React's API, with some small differences here and there. It also comes with a helper library, preact-compat,通过填充缺失部分和修补 API 差异来提供与 React 的兼容性。

反应路由器

react-router 是一个为 React 而设计的路由库。但是您也可以使用 preact-compat.

使其与 Preact 一起使用

设置预兼容

npm i --save preact-compat

确保在 webpack/browserify 配置中为 reactreact-dom 设置了别名,或者编写一些代码来手动设置这些别名。

webpack 配置示例

{
    // ...
    resolve: {
        alias: {
            'react': 'preact-compat',
            'react-dom': 'preact-compat'
        }
    }
    // ...
}

然后你就可以按原样使用 React Components 了。他们不会知道他们正在被 Preact i.s.o 渲染。反应。看看这个 preact-compat-example.

兼容性问题

请记住,当您使用 Preact Compat 时,您是在冒险。 Jason 是一个非常聪明的人,但他的图书馆的规模只是 Facebook 提供的图书馆的一小部分,因此肯定会有一些差异。使用鲜为人知的 React 功能的组件可能无法正常工作。如果您遇到此类问题,请将它们报告给 preact-compat issue tracker(以 GitHub 存储库的形式进行最小复制)以帮助他改进它。

过去有一些这样的问题导致 React-Router 无法与 Preact 一起正常工作,但这些问题已经得到修复,您现在应该可以很好地一起使用这两者了。

Preact + React-Router

Fiddle

查看 this JS Fiddle 的工作示例。

这里是带有散列支持的 preact-router 的扩展解决方案。 适用于重新加载和直接访问。

https://www.webpackbin.com/bins/-KvgdLnM5ZoFXJ7d3hWi

import {Router, Link} from 'preact-router';
import {h, render} from 'preact';
import {createHashHistory} from 'history';

[cut]...[/cut]

const App = () => (
    <div>   
      <Header />
      <Router history={createHashHistory()}>            
        <Page1 default path="/" />
        <Page2 path="/page2" />
      </Router>
    </div>
);    
render(<App />, document.body);