# react-router > [reactrouter官方](https://reactrouter.com/web/api/Route/component) > 主要功能為:**path與當前網址相同時呈現UI** ## Switch > 用於包裝Route。switch會依照Route順序進行匹配,當第一個符合則不繼續匹配且顯示該頁component;反之若無使用switch包裝,則匹配符合者皆render於頁面 ``` <Switch> ... </Switch> ``` ### Why do we need to use Switch ? 1. 在不使用switch時,若path符合者皆會render顯示 ![](https://i.imgur.com/HeqmGRT.png) ``` < > < Route path={} component={} /> < Route path={} component={} /> ... < /> //可利用containers/routes/index.js將<switch>改成<> ``` 2. 使用switch時當第一個path是"/"因此網頁一直無法正常顯示,就算更改網址處仍會匹配到"/"畫面(無使用exact) ``` < Switch> <Route path="/" component={Home} /> ... < /Switch> //可利用containers/routes/index.js將Route login,logout移除使home於第一個匹配位置,並移除exact參數 ``` - 以containers/routes/index.js為例,會造成網址處無論輸入/library、/capture 等等,皆會傳回home再重導向至live 3. 使用switch時若將live移動至"/"上,第一個path則為"/live"因此網頁首先匹配到"/live"頁面成功進入但切換頁面仍為live頁面 ``` < Switch> <Route path="/live" component={Live} /> <Route path="/" component={Home} /> ... < /Switch> //可利用containers/routes/index.js將AuthenticatedRoute live移動至Route home上方 ``` - 以containers/routes/index.js為例,第一個匹配path為live後為home,且由於home重導向至live,因此無論網址處輸入/library、/capture等等,仍會傳回至live頁面 4. 依照第三點,將alert頁面移動至第一個,則輸入匹配順序依照alert -> live -> home ``` < Switch> <Route path="/alert" component={Alert} /> <Route path="/live" component={Live} /> <Route path="/" component={Home} /> ... < /Switch> //可利用containers/routes/index.js將AuthenticatedRoute alert移動至Route live及home上方 ``` - 以containers/routes/index.js為例,第一個匹配path為alert後為live再至home,且由於home重導向至live,因此無論網址處輸入/library、/capture等等,仍會傳回至live頁面,除了alert頁面會成功引導。 5. 依照第四點,將home加上exact參數,使網址處完全符合home path時才會render ``` < Switch> <Route path="/alert" component={Alert} /> <Route path="/live" component={Live} /> <Route exact path="/" component={Home} /> ... < /Switch> //可利用containers/routes/index.js將AuthenticatedRoute 將Route home加上exact參數 ``` - 以containers/routes/index.js為例,原本網址處輸入/library、/capture等等會回傳至live頁面,加入exact後頁面皆能成功引導。 ## Route > 當Route判斷到網址的路徑(path與網址)相符,要渲染(route render methods)時都會將物件(route.props)給傳進該組件中。 > **Route props:** > - match :url、path、isExact > -- url 匹配url,用於Link > -- path 匹配path,用於Route > -- isExact 是否精確,若Route時有加入exact,則返回true;反之則false > > - location (object) : { pathname, search, hash, key } > - history - exact 精確 - path 路徑 > 設定與網址匹配的路徑,相同則render > **Route render methods:** > - component > -- Route會利用React.createElement創建一個新的React元素。每個渲染中創建一個新組件。因此在搭配inline fuction時會不斷unmount,mount,使效率變低。 > ``` > FaceAI: // path匹配成功時,render component > < Route path="/login" component={SignIn} /> > ``` > - render > -- 使用inline function時使用,避免組件重複創建,有效達到更新。 > ``` > FaceAI: // render={()=>(< >< />)} > < Route render={props => ( > < BasicLayout> > < NoMatch {...props} /> > < /BasicLayout> > )}/> > ``` > - v5後新增:children > -- 無論path是否match,皆會渲染。使你可以根據是否match來動態調整界面。 > ``` > 官網EX: // match匹配時active則新增,反之為null > < Route > path={to} > children={({ match }) => ( > < li className={match ? "active" : ""}> > < Link to={to} {...rest} /> > < /li> > )} > /> > ``` ### FaceAI應用 - 利用<switch包裝,使第一個path符合時則不繼續匹配 - 使用<Route設定path,component ``` ## containers/routes/index.js const Routes = () => ( <Switch> <Route path="/login" component={SignIn} /> <Route path="/logout" component={Logout} /> <AuthenticatedRoute exact path="/" component={Home} layout={BasicLayout} /> {/* Live */} <AuthenticatedRoute path={ROUTES.live} component={Live} layout={BasicLayout} /> {/* Alert */} <AuthenticatedRoute path={ROUTES.alert} component={Alert} layout={BasicLayout} /> {/* Comparison */} <AuthenticatedRoute path={ROUTES.comparison} component={Comparison} layout={BasicLayout} /> {/* Library */} <AuthenticatedRoute path={ROUTES.library} component={Library} layout={BasicLayout} /> {/* Face */} <AuthenticatedRoute path={ROUTES.face} component={Face} layout={BasicLayout} /> {/* Capture */} <AuthenticatedRoute path={ROUTES.capture} component={Capture} layout={BasicLayout} /> {/* Settings */} <AuthenticatedRoute path={ROUTES.settings} component={Settings} layout={BasicLayout} /> {/* Doc */} <AuthenticatedRoute path="/doc" component={Doc} layout={BasicLayout} /> {/* NoMatch */} <Route // Route render render={props => ( <BasicLayout> <NoMatch {...props} /> </BasicLayout> )} /> </Switch> ) ``` - Routes中用到< AuthenticatedRoute /> - 判斷routeAuthenticated回傳權限為true/false - true: < Component {...props} /> - Component為LoadableComponent(props) 將{...props}皆render出來 - false: < NoAuth> - containers/routes/NoAuth -> 傳送至403頁面 ``` ## components/AuthenticatedRoute/index.js render() { const { layout: Layout, component: Component, routeAuthenticated, ...rest } = this.props return ( <Route {...rest} render={props => ( <Layout>{routeAuthenticated ? <Component {...props} /> : <NoAuth />}</Layout> )} /> ) } ``` - AuthenticatedRoute使用routeAuthenticated - 利用checkAuth回傳值判斷有無權限true/false ``` ## containers/routes/AuthenticatedRoute const routeAuthenticated = checkAuth(role.code, `route.${path}`) ``` - routeAuthenticated使用checkAuth(role.code, 'route.${path}') - checkAuth(role(str),name(str)) - role為登入者角色ex:root / admin / staff / faceadmin - name為路徑ex:route./live / route./library / route./capture - name拆分為key=>[route /capture] - ROLES_AUTHORITY是每個path對應到的使用者登入權限ex: <path>:[root,admin,staff,faceadmin] // json{route: {…}, action: {…}} - 判斷ROLES_AUTHORITY內key(path)與傳送來的name(path) - auth && auth.indexOf(role) > -1 - 判斷auth(權限角色)內是否存在role(role.code~=登入者角色) ``` ## utils/index.js export const checkAuth = (role, name = '') => { const keys = name.split('.') let auth = ROLES_AUTHORITY keys.forEach(key => { if (key && auth[key]) { auth = auth[key] } else { auth = [] } }) return auth && auth.indexOf(role) > -1 } ``` ## WithRouter > Higher Order Component > 能將Route內location、 history等資訊 作為props傳入component,一般只有包在Route裡面的component才能拿到router的資訊,如果是沒有被包在Route裡面的component需要取得router的資訊就可以透過withRouter ### FaceAI應用 ``` ## containers/layout/ScrollToTop.js class ScrollToTop extends PureComponent { static propTypes = { location: object.isRequired, children: node.isRequired } componentDidUpdate(prevProps) { const { location, location: { pathname } } = this.props // pathname 新path const { location: { pathname: prevPathname } } = prevProps // prevPathname 原path if (pathname !== prevPathname) { this.scrollTo(location) // location: state.router.location } console.log('withRouter', withRouter) } scrollTo = ({ hash } = {}) => { // hash: state.router.location.hash // 網址處若有#字符號為hash console.log('type', hash) if (hash) { const hashDom = window.document.querySelector(hash) hashDom && hashDom.scrollIntoView() } else { window.document.body.scrollTop = 0 // 獲取當前頁面滾動條縱坐標的位置 chrome只認得: document.body.scrollTop;標準瀏覽器認得:document.documentElement.scrollTop // var scrollTop=document.body.scrollTop+document.documentElement.scrollTop -> 當一個值取得時令一個一定為0,因此可使用此公式判斷 window.scrollTo(0, 0) // 滾動至某座標 window.scrollTo(x-coord橫座標, y-coord縱座標 ) } } render() { const { location, children } = this.props // location: router.location.key return <div key={location.key}>{children}</div> // location.key代表每一次render的頁面key children為每頁component內容 } } export default withRouter(ScrollToTop) ``` ``` ## component/BasicLayout <ScrollToTop>{children}</ScrollToTop> // -> <div key={location.key}>{children}</div> ``` # react-router-dom > 依賴react-router套件且核心亦為react-router, ### Link > 像html中<a> 透過點擊來跳轉頁面 > - to 移動至路徑 ``` ## component/Header/index.js <Link to="/logout"> <Icon type="logout" /> <FormattedMessage id="App.header.logout" /> </Link> ## component/NoAuth/index.js <Link to="/"> <Button type="danger" size="large" ghost> {intl.formatMessage({ id: 'App.403.backBtn' })} </Button> </Link> ``` ### Redirect > 用於重導向至某路徑 > - from 若沒設定則為當前路徑 > - to 移動至路徑 ``` ## component/Home/index.js const Home = () => <Redirect to="/live" /> ```