2018-12-21 14:44:31 weixin_41111068 阅读数 468
  • 完全征服React Native

    React Native是Facebook于2015年推出的跨平台开发工具,可用于开发Android和iOS App,并且同时具有混合开发的优点(热更新,跨平台)以及本地App的性能。 本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合

    58321 人正在学习 去看看 李宁

最近在学习react,之前做的一个项目首屏加载速度很慢,便搜集了一些优化方法,react-loadable这个库是我在研究路由组件按需加载的过程中发现的。 import() 是es6的一种异步加载的方法。

npm i react-loadable --save

routeMap.jsx 

import React from 'react';
import {Router, Route, IndexRoute} from 'react-router';
import Loadable from 'react-loadable'; // 引入react-loadable

import Loading from '../components/loading/loading.jsx';
import App from '../containers/App.jsx';
const Home = Loadable({
  loader: () => import('../containers/Home/index.jsx'),
  loading: Loading
});
const List = Loadable({
  loader: () => import('../containers/List/index.jsx'),
  loading: Loading
});
const Detail = Loadable({
  loader: () => import('../containers/Detail/index.jsx'),
  loading: Loading
});
const NotFound = Loadable({loader: () => import('../containers/NotFound/index.jsx'),loading: Loading});

class RouteMap extends React.Component {
  render() {
    return (
      <Router history={this.props.history} onUpdate={this.updateRoute}>
        <Route path='/' component={App}>
          <IndexRoute component={Home}></IndexRoute>
          <Route path='detail/:id' component={Detail}></Route>
          <Route path='list' component={List}></Route>
          <Route path='*' component={NotFound}></Route>
        </Route>
      </Router>
    )
  }
  updateRoute () {
    console.log('路由更新了');
  }
}

export default RouteMap;

第二步:准备一个Loding组件。

loading.jsx

import React from 'react';

class Loading extends React.Component {
  render() {
    return (
      <div>
        <p>loading</p>
      </div>
    )
  }
}

export default Loading;

 

2019-07-29 10:00:23 hzxOnlineOk 阅读数 118
  • 完全征服React Native

    React Native是Facebook于2015年推出的跨平台开发工具,可用于开发Android和iOS App,并且同时具有混合开发的优点(热更新,跨平台)以及本地App的性能。 本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合

    58321 人正在学习 去看看 李宁

打包

大多数 React 应用都会使用 Webpack 或 Browserify 这类的构建工具来打包文件。打包是一个将文件引入并合并到一个单独文件的过程,最终形成一个 “bundle”。接着在页面上引入该 bundle,整个应用即可一次性加载。

示例

App文件:

// app.js
import { add } from './math.js';

console.log(add(16, 26)); // 42
// math.js
export function add(a, b) {
  return a + b;
}

打包后文件:

function add(a, b) {
  return a + b;
}

console.log(add(16, 26)); // 42

注意:

最终你的打包文件看起来会和上面的例子区别很大。

如果你正在使用 Create React AppNext.jsGatsby,或者类似的工具,你会拥有一个可以直接使用的 Webpack 配置来进行打包工作。

如果你没有使用这类工具,你就需要自己来进行配置。例如,查看 Webpack 文档上的安装入门教程

代码分割

打包是个非常棒的技术,但随着你的应用增长,你的代码包也将随之增长。尤其是在整合了体积巨大的第三方库的情况下。你需要关注你代码包中所包含的代码,以避免因体积过大而导致加载时间过长。

为了避免搞出大体积的代码包,在前期就思考该问题并对代码包进行分割是个不错的选择。代码分割是由诸如 Webpack(代码分割)和 Browserify(factor-bundle)这类打包器支持的一项技术,能够创建多个包并在运行时动态加载。

对你的应用进行代码分割能够帮助你“懒加载”当前用户所需要的内容,能够显著地提高你的应用性能。尽管并没有减少应用整体的代码体积,但你可以避免加载用户永远不需要的代码,并在初始加载的时候减少所需加载的代码量。

import()

在你的应用中引入代码分割的最佳方式是通过动态 import() 语法。

使用之前:

import { add } from './math';

console.log(add(16, 26));

使用之后:

import("./math").then(math => {
  console.log(math.add(16, 26));
});

注意:

动态 import() 语法目前只是一个 ECMAScript (JavaScript) 提案, 而不是正式的语法标准。预计在不远的将来就会被正式接受。

当 Webpack 解析到该语法时,它会自动地开始进行代码分割。如果你使用 Create React App,该功能已配置好,你能立刻使用这个特性。Next.js 也已支持该特性而无需再配置。

如果你自己配置 Webpack,你可能要阅读下 Webpack 关于代码分割的指南。你的 Webpack 配置应该类似于此

当使用 Babel 时,你要确保 Babel 能够解析动态 import 语法而不是将其进行转换。对于这一要求你需要 babel-plugin-syntax-dynamic-import 插件。

React.lazy

注意:

React.lazy 和 Suspense 技术还不支持服务端渲染。如果你想要在使用服务端渲染的应用中使用,我们推荐 Loadable Components 这个库。它有一个很棒的服务端渲染打包指南

React.lazy 函数能让你像渲染常规组件一样处理动态引入(的组件)。

使用之前:

import OtherComponent from './OtherComponent';

function MyComponent() {
  return (
    <div>
      <OtherComponent />
    </div>
  );
}

使用之后:

const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <div>
      <OtherComponent />
    </div>
  );
}

这个代码将会在渲染组件时,自动导入包含 OtherComponent 组件的包。

React.lazy 接受一个函数,这个函数需要动态调用 import()。它必须返回一个 Promise,该 Promise 需要 resolve 一个 defalut export 的 React 组件。

Suspense

如果在 MyComponent 渲染完成后,包含 OtherComponent 的模块还没有被加载完成,我们可以使用加载指示器为此组件做优雅降级。这里我们使用 Suspense 组件来解决。

const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <OtherComponent />
      </Suspense>
    </div>
  );
}

fallback 属性接受任何在组件加载过程中你想展示的 React 元素。你可以将 Suspense 组件置于懒加载组件之上的任何位置。你甚至可以用一个 Suspense 组件包裹多个懒加载组件。

const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <section>
          <OtherComponent />
          <AnotherComponent />
        </section>
      </Suspense>
    </div>
  );
}

异常捕获边界(Error boundaries)

如果模块加载失败(如网络问题),它会触发一个错误。你可以通过异常捕获边界(Error boundaries)技术来处理这些情况,以显示良好的用户体验并管理恢复事宜。

import MyErrorBoundary from './MyErrorBoundary';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));

const MyComponent = () => (
  <div>
    <MyErrorBoundary>
      <Suspense fallback={<div>Loading...</div>}>
        <section>
          <OtherComponent />
          <AnotherComponent />
        </section>
      </Suspense>
    </MyErrorBoundary>
  </div>
);

基于路由的代码分割

决定在哪引入代码分割需要一些技巧。你需要确保选择的位置能够均匀地分割代码包而不会影响用户体验。

一个不错的选择是从路由开始。大多数网络用户习惯于页面之间能有个加载切换过程。你也可以选择重新渲染整个页面,这样您的用户就不必在渲染的同时再和页面上的其他元素进行交互。

这里是一个例子,展示如何在你的应用中使用 React.lazy 和 React Router 这类的第三方库,来配置基于路由的代码分割。

import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import React, { Suspense, lazy } from 'react';

const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));

const App = () => (
  <Router>
    <Suspense fallback={<div>Loading...</div>}>
      <Switch>
        <Route exact path="/" component={Home}/>
        <Route path="/about" component={About}/>
      </Switch>
    </Suspense>
  </Router>
);

命名导出(Named Exports)

React.lazy 目前只支持默认导出(default exports)。如果你想被引入的模块使用命名导出(named exports),你可以创建一个中间模块,来重新导出为默认模块。这能保证 tree shaking 不会出错,并且不必引入不需要的组件。

// ManyComponents.js
export const MyComponent = /* ... */;
export const MyUnusedComponent = /* ... */;
// MyComponent.js
export { MyComponent as default } from "./ManyComponents.js";
// MyApp.js
import React, { lazy } from 'react';
const MyComponent = lazy(() => import("./MyComponent.js"));
2018-05-30 10:26:48 qq_35484341 阅读数 5595
  • 完全征服React Native

    React Native是Facebook于2015年推出的跨平台开发工具,可用于开发Android和iOS App,并且同时具有混合开发的优点(热更新,跨平台)以及本地App的性能。 本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合

    58321 人正在学习 去看看 李宁

众所周知,在使用webpack打包react应用时,webpack将整个应用打包成一个js文件,当用户访问首屏时,会一次性加载整个js文件,当应用的规模变得越来越庞大的时候,首屏渲染速度变慢,影响用户体验。

于是,webpack开发了代码分割的特性, 此特性能够把代码分割为不同的bundle文件,然后可以通过路由按需加载或并行加载这些文件。

代码分割可以用于获取更小的bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。有三种常用的代码分割方法:

1、拆分入口:使用 entry 配置手动地分割代码。

2、防止重复:使用 CommonsChunkPlugin 去重和分离chunk。

3、动态导入:通过模块的内联函数调用来分离代码。本文只讨论动态导入(dynamic imports)的方法。

动态导入

当涉及到动态代码拆分时,webpack提供了两个类似的技术。对于动态导入,第一种,也是优先选择的方式是,使用符合ECMA提案的 import() 语法。第二种,则是使用 webpack 特定的 require.ensure。本文使用第一种方式。

注意:import() 调用会在内部用到promise。如果在旧有版本浏览器中使用 import(),记得使用一个polyfill 库(例如 es6-promise 或 promise-polyfill),来 shim Promise。

下面结合react-router 4来实现react的代码分割。

在React应用中实现

React应用的代码分割需要结合路由库react-router使用,当前react-router的版本是V4,在使用react-router4进行代码分割的路上,社区已经有成熟的第三方库进行了实现,如react-loadable。在此处将介绍如何不借助第三方库实现代码分割。

此处假设你已经对react、react-router4、webpack有基本的了解,可以搭建简单的开发环境。下面是本项目的基本目录结构:

项目入口文件src/index.js:

 

src/App.js:

在App.js中,引入react-router-dom路由模块,以及路由配置文件routes.js,App组件主要负责通过路由配置遍历生成一系列路由组件。

下面是路由配置src/routes.js:

routes.js中配置了路由组件需要的参数,需要注意的是在路由参数中使用了异步组件AsyncComponent,注意这里并没有直接引入组件,而是传递一个函数参数给AsyncComponent,它将在AsyncComponent(() => import('./containers/home'))组件被创建时进行动态引入。

同时,这种传入一个函数作为参数,而非直接传入一个字符串的写法能够让webpack意识到此处需要进行代码分割。

使用import()需要使用Babel预处理器和动态import的语法插件(Syntax Dynamic Import Babel Plugin)。由于 import() 会返回一个 promise,因此它可以和ES7的async函数一起使用,使用acync函数需要安装babel-plugin-transform-runtime插件。

安装babel插件:

本项目使用的其他babel插件还有babel-core、babel-loader、babel-preset-env、babel-preset-react等,主要用于React的jsx语法编译。

下面需要编写babel配置文件.babelrc,在根目录下新建.babelrc,配置如下:

异步组件AsyncComponent

代码分割的核心部分就是实现AsyncComponent,本项目的AsyncComponent放在src/components/async-component/index.js中,代码如下:

整个模块是一个高阶组件,返回一个新的组件,传入两个参数,一个是需要动态加载组件的方法,第二个是动态加载时的占位符,占位符的默认参数为一个字符串,也可以传入一个Loading组件。

在返回的AsyncComponent组件内部,constructor中,初始化一个state为Child,值为null,并定义this.unmount =false,用于表示组件是否被卸载。

使用acync定义异步方法,componentDidMount中,使用await异步执行传入的第一个参数,用于动态加载当前路由的组件。

注意:

当调用ES6模块的import()方法(引入模块)时,必须指向模块的.default值,因为它才是promise 被处理后返回的实际的module对象。

故此处使用ES6的对象解构获取到模块的default并赋值到Child上。

然后判断组件被卸载的状态,被卸载即返回。

下面将Child设置到state上。

在render方法中,从state中获取Child,然后使用三元运算符判断Child是否存在,存在则渲染Child组件,并传入this.props,否则渲染占位符。

组件componentWillUnmount时,设置this.unmout为true。

测试

现在开始编写一些简单的业务组件用于测试,在containers中新建两个文件夹home和detail,在两个文件夹下编写index.js作为两个路由组件。代码如下:

containers/home/index.js:

containers/detail/index.js:

在根目录下package.json配置启动脚本:

然后运行npm start启动项目:

打开浏览器访问localhost:8080

查看右侧network面板,可以看到页面先加载了main.js和0.js,点击详情按钮跳转到http://localhost:8080/detail

随后加载了1.js,这样就实现了代码分割,每个路由都是动态加载的。在大型React应用中,将bundle进行细粒度的拆分,可以极大提升首屏渲染速度,提升用户体验。

2017-10-28 18:49:00 weixin_44619017 阅读数 178
  • 完全征服React Native

    React Native是Facebook于2015年推出的跨平台开发工具,可用于开发Android和iOS App,并且同时具有混合开发的优点(热更新,跨平台)以及本地App的性能。 本课程采用新的ES6开发,主要内容包括ReactNative的基础知识,ReactNative的布局,组件,API,封装本地API和组件,发布ReactNative App,本地与ReactNative深度结合

    58321 人正在学习 去看看 李宁

react-router4.x 实用例子(路由过渡动画、代码分割)

react-router4.2.0实用例子

  1. 代码分割

    官网上面写的代码分割是不支持create-react-app脚手架的,要使用import实现
    创建一个bundle.js文件
        import { Component } from 'react'
        class Bundle extends Component {
            constructor(props) {
                super(props);
                this.state = {
                    mod: null
                };
            }
            componentWillMount() {
                this.load(this.props)
            }
            componentWillReceiveProps(nextProps) {
                if (nextProps.load !== this.props.load) {
                    this.load(nextProps)
                }
            }
            load(props) {
                this.setState({
                    mod: null
                });
                props.load().then((mod) => {
                    this.setState({
                        mod: mod.default ? mod.default : mod
                    });
                });
            }
            render() {
                return this.state.mod ? this.props.children(this.state.mod) : null;
            }
        }
        export default Bundle
    假设有一个自己的子组件需要代码分割
        demo.js
        import React from 'react'
        const MyComponent = () => (
            <div>我是子组件</div>
        )
        export default MyComponent;
    在需要代码分割的文件中导入这个Bundle
        import Bundle from './test.js'
        const MyComponent = (props) => (
            <Bundle load={() => import('./demo.js')}>
                {(MyComponent) => <MyComponent {...props} />}
            </Bundle>
        )
        只有当MyComponent在文件中使用到了,才会去异步加载需要的组件
    ** 补充
        如果你要和代码分割一起使用建议将Bundle.js文件修改成如下,如果不修改,亲测有bug
        import React, { Component } from 'react'
            class Bundle extends Component {
            constructor() {
                super()
                this.state = {
                    mod: null
                }
            }
            async componentDidMount() {
                const {default: mod} = await this.props.load()
                this.setState({
                    mod: mod.default || mod
                })
            }
            render() {
                return (
                this.state.mod ? this.props.children(this.state.mod) : null
                )
            }
        }
    
        export default Bundle
  2. ScrollTop实现方式一

    默认情况下,react中的组件在路由之间切换时不会默认回到页面的最顶部,这时需要自己动手编写一个组件实现这个功能
    创建ScrollTop.js文件
        import { Component } from 'react'
        import { withRouter } from 'react-router'
        class ScrollToTop extends Component {
            componentDidUpdate(prevProps) {
                if (this.props.location !== prevProps.location) {
                    window.scrollTo(0, 0)
                }
            }
            render() {
                return this.props.children
            }
        }
        export default withRouter(ScrollToTop)
    使用
        import ScrollTop from './ScrollTop.js'
        将router内部的子元素全部使用ScrollTop包裹即可
        <Router>
            <ScrollTop>
                <div>
                    <div style={{"position":"fixed","top": 0,"left": 0}}>
                        <Link to="/a">组件一</Link>
                        <Link to="/b">组件二</Link>
                    </div>
                    <Route path="/a" component={Demo} />
                    <Route path="/b" component={Demo1} />
                </div>
            </ScrollTop>
        </Router>
  3. ScrollTop实现方式二

    创建ScrollTop.js文件,添加如下内容
        import { Component } from 'react'
            import { withRouter } from 'react-router'
            class ScrollToTopOnMount extends Component {
            componentDidMount(prevProps) {
                window.scrollTo(0, 0)
            }
            render() {
                return null
            }
        }
        export default ScrollToTopOnMount
    使用
        import ScrollToTopOnMount from './scrollTop.js'
        将你原本的组件重新封装一个组件来使用即可
        class LongContent extends React.Component {
            render() {
                return (
                    <div>
                        <ScrollToTopOnMount/>
                        <Demo />
                    </div>
                )
            }
        }   
  4. 路由之间的动画切换

    结合react-transition-group插件使用,官方推荐,详细点击http://www.cnblogs.com/ye-hcj/p/7723104.html
    demo如下
        import React from 'react'
        import ReactDOM from 'react-dom'
        import {TransitionGroup,CSSTransition } from 'react-transition-group'
        import { withRouter } from 'react-router'
        import './index.css'
        import {
            BrowserRouter as Router,
            Route,
            Link,
            Switch
        } from 'react-router-dom'
        const MyComponent1 = () => (
            <div style={{"height": "200px","width": "200px","background": "red","position": "absolute","top": 0, "left": 0}}>我是组件一</div>
        )
        const MyComponent2 = () => (
            <div style={{"height": "200px","width": "200px","background": "yellow","position": "absolute","top": 0, "left": 0}}>我是组件二</div>
        )
        const MyComponent3 = () => (
            <div style={{"height": "200px","width": "200px","background": "green","position": "absolute","top": 0, "left": 0}}>我是组件三</div>
        )
        const AnimateComponent = withRouter(( {location} ) => (
            <div>
                <p><Link to="/a">组件一</Link></p>
                <p><Link to="/b">组件二</Link></p>
                <p><Link to="/c">组件三</Link></p>
                <hr />
                <TransitionGroup>
                    {/*这里使用location.key会在所有的路由之间使用相同的动画效果,如果你不想子路由也应用动画效果请使用location.pathname.split('/')[1]*/}
                    <CSSTransition key={location.key} timeout={500} classNames="fade" mountOnEnter={true} unmountOnExit={true}>
                        <div style={{"position": "relative","top": 0,"left": 0}}>
                            <Switch location={location}>
                                <Route path="/a" exact component={MyComponent1}/>
                                <Route path="/b" component={MyComponent2}/>
                                <Route path="/c" component={MyComponent3}/>
                            </Switch>
                        </div>
                    </CSSTransition>
                </TransitionGroup>
            </div>
        ))
        class MyComponent extends React.Component {
            render () {
                return (
                    <Router>
                        <AnimateComponent></AnimateComponent>
                    </Router>
                )
            }
        }
        ReactDOM.render(
            <MyComponent/>,
            document.getElementById('root')
        ) 
    注:
        你可能使用的动画
        .fade-enter {
            opacity: 0;
            z-index: 1;
        }
        .fade-enter.fade-enter-active {
            opacity: 1;
            transition: opacity 250ms ease-in;
        }
posted @ 2017-10-28 18:49 qz奔跑的马 阅读(...) 评论(...) 编辑 收藏
没有更多推荐了,返回首页