React-Router的动态加载实例与createElement

    xiaoxiao2021-03-25  76

    1.我们首先看看下面的代码

    'use strict'; const chain = require('ramda/src/chain'); const toReactComponent = require('jsonml-to-react-component'); //jsonml-to-react-component const exist = require('exist.js'); const NotFound = require('{{ themePath }}/template/NotFound'); //replace dynamic param with truely passed param value function calcPropsPath(dataPath, params) { return Object.keys(params).reduce( (path, param) => path.replace(`:${param}`, params[param]), dataPath ); } //Some dynamic parameter injected function hasParams(dataPath) { return dataPath.split('/').some((snippet) => snippet.startsWith(':')); } //Do nothing function defaultCollect(nextProps, callback) { callback(null, nextProps); } /* We receive something like this: var Promise = require('bluebird'); module.exports = { markdown: { 'posts\demo2': require('C:/Users/Administrator/Desktop/mdw/posts/demo2.md'), 'posts\demos\demo1': require('C:/Users/Administrator/Desktop/mdw/posts/demos/demo1.md'), }, plugins: [ ], picked: {}, }; */ module.exports = function getRoutes(data) { const plugins = data.plugins; //All 'browser' module const converters = chain((plugin) => plugin.converters || [], plugins); //converters is for jsonml converter in `browser` mode const utils = { get: exist.get, toReactComponent(jsonml) { return toReactComponent(jsonml, converters); }, }; //utils`s method toReactComponent is for converting jsonml to react component plugins.map((plugin) => plugin.utils || {}) .forEach((u) => Object.assign(utils, u)); //plugins`s personal util function is also assiged to final utils object //@template is path of component while dataPath is `path` parameter function templateWrapper(template, dataPath = '') { //template is ususally ./template/NotFound const Template = require('{{ themePath }}/template' + template.replace(/^\.\/template/, '')); //We get final path of component return (nextState, callback) => { const propsPath = calcPropsPath(dataPath, nextState.params); //replace dynamic parameter of url, such as `path: 'docs/pattern/:children'` const pageData = exist.get(data.markdown, propsPath.replace(/^\//, '').split('/')); //Urls are mapped to component instantiation const collect = Template.collect || defaultCollect; //We firstly invoke our template`s collect function, passed value are combined with nextState //and previous object with data, picked ,pageData,utils contained collect(Object.assign({}, nextState, { data: data.markdown, picked: data.picked, pageData, //page data is for special html page based on url utils, }), (err, nextProps) => { const Comp = (hasParams(dataPath) || pageData) && err !== 404 ? Template.default || Template : NotFound.default || NotFound; //ES6 based component, we need to import .default while module.exports do not need to const dynamicPropsKey = nextState.location.pathname; //we get pathname Comp[dynamicPropsKey] = nextProps; //In create-element.js, we do that. <Component {...props} {...Component[dynamicPropsKey]} /> //something as bellow /* <ReactRouter.Router history={ReactRouter.useRouterHistory(history.createHistory)({ basename })} routes={routes} createElement={createElement}//that is createElement Function />; module.exports = function createElement(Component, props) { const dynamicPropsKey = props.location.pathname; return <Component {...props} {...Component[dynamicPropsKey]} />; }; */ callback(err === 404 ? null : err, Comp); //https://react-guide.github.io/react-router-cn/docs/guides/advanced/DynamicRouting.html //callback of react-router natively }); }; } const theme = require('{{ themePath }}'); // We get index file from themePath which is configured as core part of react-router const routes = Array.isArray(theme.routes) ? theme.routes : [theme.routes]; //Get router part of react-router, routes part ususally configured as follows: /* routes: { path: '/', component: './template/Layout/index', indexRoute: { component: homeTmpl }, childRoutes: [{ path: 'index-cn', component: homeTmpl, }], */ function processRoutes(route) { if (Array.isArray(route)) { //map is used to manipulate some object return route.map(processRoutes); } return Object.assign({}, route, { onEnter: () => { if (typeof document !== 'undefined') { } }, component: undefined, //Same as component but asynchronous, useful for code-splitting. //https://github.com/liangklfang/react-router/blob/master/docs/API.md#getcomponentsnextstate-callback //http://www.mtons.com/content/61.htm getComponent: templateWrapper(route.component, route.dataPath || route.path), //Get component file path and 'path' for url indexRoute: route.indexRoute && Object.assign({}, route.indexRoute, { component: undefined, getComponent: templateWrapper( route.indexRoute.component, route.indexRoute.dataPath || route.indexRoute.path ), }), childRoutes: route.childRoutes && route.childRoutes.map(processRoutes), }); } const processedRoutes = processRoutes(routes); //Here , We process Routes configuration processedRoutes.push({ path: '*', getComponents: templateWrapper('./template/NotFound'), }); //Here is default router! return processedRoutes; };

    2.可以学到的内容

    2.1 动态加载的方法getComponents,getIndexRoute,getChildRoutes等,可以参考react-router

    function processRoutes(route) { if (Array.isArray(route)) { //map is used to manipulate some object return route.map(processRoutes); } return Object.assign({}, route, { onEnter: () => { if (typeof document !== 'undefined') { } }, component: undefined, //Same as component but asynchronous, useful for code-splitting. //https://github.com/liangklfang/react-router/blob/master/docs/API.md#getcomponentsnextstate-callback //http://www.mtons.com/content/61.htm getComponent: templateWrapper(route.component, route.dataPath || route.path), //Get component file path and 'path' for url indexRoute: route.indexRoute && Object.assign({}, route.indexRoute, { component: undefined, getComponent: templateWrapper( route.indexRoute.component, route.indexRoute.dataPath || route.indexRoute.path ), }), childRoutes: route.childRoutes && route.childRoutes.map(processRoutes), }); }

    2.2 传入默认的路由方式

    processedRoutes.push({ path: '*', getComponents: templateWrapper('./template/NotFound'), });

    2.3 react-router的createElement配置

    ReactRouter.match({ routes, location, basename }, () => { const router = <ReactRouter.Router history={ReactRouter.useRouterHistory(history.createHistory)({ basename })} routes={routes} createElement={createElement} />; ReactDOM.render( router, document.getElementById('react-content') ); });

    其中这里我们采用的是下面的函数,这个函数指定了当访问一个url的时候如何为我们的Component组件添加props:

    module.exports = function createElement(Component, props) { const dynamicPropsKey = props.location.pathname; return <Component {...props} {...Component[dynamicPropsKey]} />; };

    之所以我们这样做是上面通过了Component[dynamicPropsKey]传入了需要的props

    const Comp = (hasParams(dataPath) || pageData) && err !== 404 ? Template.default || Template : NotFound.default || NotFound; //ES6 based component, we need to import .default while module.exports do not need to const dynamicPropsKey = nextState.location.pathname; //we get pathname Comp[dynamicPropsKey] = nextProps;

    2.4 React中ES6模式导出要通过.default完成,而Commonjs规范不需要

    const Comp = (hasParams(dataPath) || pageData) && err !== 404 ? Template.default || Template : NotFound.default || NotFound;

    2.5 URL映射到组件实例化

    function templateWrapper(template, dataPath = '') { //template is ususally ./template/NotFound const Template = require('{{ themePath }}/template' + template.replace(/^\.\/template/, '')); //We get final path of component return (nextState, callback) => { const propsPath = calcPropsPath(dataPath, nextState.params); //replace dynamic parameter of url, such as `path: 'docs/pattern/:children'` const pageData = exist.get(data.markdown, propsPath.replace(/^\//, '').split('/')); //Urls are mapped to component instantiation const collect = Template.collect || defaultCollect; //We firstly invoke our template`s collect function, passed value are combined with nextState //and previous object with data, picked ,pageData,utils contained collect(Object.assign({}, nextState, { data: data.markdown, picked: data.picked, pageData, //page data is for special html page based on url utils, }), (err, nextProps) => { const Comp = (hasParams(dataPath) || pageData) && err !== 404 ? Template.default || Template : NotFound.default || NotFound; //ES6 based component, we need to import .default while module.exports do not need to const dynamicPropsKey = nextState.location.pathname; //we get pathname Comp[dynamicPropsKey] = nextProps; //In create-element.js, we do that. <Component {...props} {...Component[dynamicPropsKey]} /> //something as bellow /* <ReactRouter.Router history={ReactRouter.useRouterHistory(history.createHistory)({ basename })} routes={routes} createElement={createElement}//that is createElement Function />; module.exports = function createElement(Component, props) { const dynamicPropsKey = props.location.pathname; return <Component {...props} {...Component[dynamicPropsKey]} />; }; */ callback(err === 404 ? null : err, Comp); //https://react-guide.github.io/react-router-cn/docs/guides/advanced/DynamicRouting.html //callback of react-router natively }); }; }
    转载请注明原文地址: https://ju.6miu.com/read-14602.html

    最新回复(0)