分类目录归档:react

使用Gatsby生成静态网站并部署在GitHub上

什么是Gatsby

Blazing-fast static site generator for React

Gatsby是一个基于React极其快的静态网站生成工具

支持各种数据源,markdown、Wordpress等

创建网站

  1. 安装Gatsby命令行工具:
    npm install --global gatsby-cli
    
  2. 创建一个新的网站
    gatsby new sheng00.cn
    
  3. 运行刚才创建的网站
    cd sheng00.cn
    gatsby develop
    

    打开localhost:8000即可看到

部署在GitHub上

  1. 在GitHub上创建一个repository

  2. 在刚才生成的网站运行下面的命令

    git add -A
    git commit -m "first commit"
    git remote add origin git@github.com:shengoo/sheng00.cn.git
    git push -u origin master
    
  3. 安装gh-pages
    yarn add gh-pages
    
  4. 在package.json里增加一个脚本
    "deploy": "gatsby build --prefix-paths && gh-pages -d public"
    
  5. 部署到GitHub
    yarn deploy
    

设置自定义域名

  1. 从域名注册商那里,把域名指向yourusername.github.io

  2. 在GitHub的repository的设置里,设置Custom Domain:www.sheng00.cn

  3. 在Gatsby生成的网站中,新建一个目录static,创建一个文件CNAME

    www.sheng00.cn
    
  4. 再次部署
    yarn deploy
    

源代码网址:https://github.com/shengoo/sheng00.cn

在react应用中使用模块化css

什么是模块化CSS?

模块化管理CSS,避免全局污染,实现模块化、可服用。

在react中应用

启用CSS Modules

css-loader中增加一个选项:

{
    loader: require.resolve('css-loader'),
    options: {
        importLoaders: 1,
        minimize: true,
        sourceMap: shouldUseSourceMap,
        modules: true, // 启用CSS Modules
    },
},

CSS文件

app.css:

.title{
    color: red;
}

最后会编译成:

._2L1SLeGPg5sisdRmqO9mCH {
  color: red;
}

JavaScript文件

app.js:

import styles from './app.css';

class App extends Component {
  render() {
    return <div className={styles.title}>Hello World.</div>;
  }
}

最后会编译成:

<div class="_2L1SLeGPg5sisdRmqO9mCH">Hello World.</div>

通过修改react-scripts来自定义create-react-app的模板

create-react-app是一个无需任何配置就能轻松创建react应用使用的命令行工具,它主要是使用react-scripts来配置需要的webpackbabel等一系列工具。

react-scripts创建的应用可以满足大部分的需求,但是有时候我们需要修改或者创建自己的配置项。react-scripts提供了一个命令eject,使用eject命令可以将react-scripts内置的各种配置项暴露出来,这时候我们就可以通过更改配置文件。

eject可以让你自定义所有配置项。但是如果有很多相似的项目,建议fork一份react-scripts和其他需要的packges,做一个自己的模板使用。

自定义create-react-app的模板

Fork create-react-app

打开create-react-app,fork出自己的一份create-react-app

建议fork一个稳定的分支,master不是稳定的。

packages目录里,有一个目录react-scriptsreact-scripts这个目录里包含了buildteststart你的react app的脚本。

修改配置

把我们fork好的create-react-app clone到本地,checkout一个稳定版的tag,打开react-scripts/scripts/init.js这个文件。

在里面加一行console.log('Hello world.');

把我们自定义的react-scripts发布到NPM

先把react-scripts目录下的package.json文件里的name等字段的值改成我们自己的。

现在从命令行切换到react-scripts目录里,使用npm publish命令行进行发布。如果npm提示你登陆,就登陆上自己的npm账号。

测试我们自己的react-scripts

在命令行中使用我们自己的模板创建一个react-app

create-react-app test-app --scripts-version shengoo-react-scripts

可以看到,我们的增加的那行console.log生效了。

接下来就可以通过修改react-scripts增加我们自己需要的功能了。

使用create-react-app生成react多页面应用

  1. 初始化react app
    npx create-react-app multiple-page-app
    
  2. eject(eject前要commit)
    yarn eject
    
  3. 在src文件夹里新建一个about.css(假如我们要做的另一个页面是about.html)
    body{
        background-color: yellow;
    }
    
  4. 在src文件夹里新建一个about.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import './about.css';
    ReactDOM.render(<div>about</div>, document.getElementById('root'));
    
  5. 增加入口配置,config/webpack.config.dev.js
    entry: {
        index:[
            require.resolve('./polyfills'),
            require.resolve('react-dev-utils/webpackHotDevClient'),
            paths.appIndexJs
        ],
        about: [
            require.resolve('./polyfills'),
            require.resolve('react-dev-utils/webpackHotDevClient'),
            paths.appSrc + "/about.js",
        ]
    },
    
  6. 修改输出配置output选项
    filename: 'static/js/[name].bundle.js',
    
  7. 增加HtmlWebpackPlugin
    new HtmlWebpackPlugin({
        inject: true,
        chunks: ["index"],
        template: paths.appHtml,
    }),
    new HtmlWebpackPlugin({
        inject: true,
        chunks: ["about"],
        template: paths.appHtml,
        filename: 'about.html',
    }),
    
  8. 效果如下

  9. 然后按照对dev.js的修改,同样修改好prod.js,就可以build出两个页面了。

完整代码可以在github上看到:
https://github.com/shengoo/react-demo/tree/master/multiple-page-app

在create-react-app中使用Code Splitting

在create-react-app中使用Code Splitting实现按需加载js

为了减少HTTP请求,我们会把代码打包到一个文件里。
Code splitting可以把打包的文件分割成不同的块,并实现按需加载。
下面是在create-react-app中使用方法。

简单示例

  1. 创建项目
    npx create-react-app code-splitting
    
  2. 在src文件夹里创建文件texts.js
    const hello = 'Hello World!';
    export { hello };
    
  3. 修改App.js
    class App extends Component {
        constructor(props){
            super(props);
            this.state = {};
        }
        componentDidMount(){
            import('./texts')
                .then(({hello}) => {
                    this.setState({
                        msg: hello,
                    })
                })
                .catch(err => {
                });
        }
        render() {
            return (
                <div className="App">
                    <div>{this.state.msg || 'loading...'}</div>
                </div>
            );
        }
    }
    export default App;
    
  4. Chrome中network

分割react的组件

使用React Loadable分割,React Loadable还能创建 loading states, error states, timeouts, preloading,等状态。
原来的引用方式:

import OtherComponent from './OtherComponent';
const MyComponent = () => (
    <OtherComponent/>
);

使用React Loadable启用了Code splitting的方式:

import Loadable from 'react-loadable';
const LoadableOtherComponent = Loadable({
    loader: () => import('./OtherComponent'),
    loading: () => <div>Loading...</div>,
});
const MyComponent = () => (
    <LoadableOtherComponent/>
);

例子:

  1. 添加依赖
    yarn add react-loadable

  2. 新建一个组件Hello.js

    import React from 'react';
    export default () => (
        <div>Hello world.</div>
    )
    
  3. 在App.js中引用Hello
    import Loadable from 'react-loadable';
    const LoadableOtherComponent = Loadable({
        loader: () => import('./Hello'),
        loading: () => <div>Loading...</div>,
    });
    const Hello = () => (
        <LoadableOtherComponent/>
    );
    
  4. 在render函数里渲染

  5. 从network里可以看到又多了一个js文件

根据路由分割

import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Loadable from 'react-loadable';
const Loading = () => <div>Loading...</div>;
const Home = Loadable({
  loader: () => import('./routes/Home'),
  loading: Loading,
});
const About = Loadable({
  loader: () => import('./routes/About'),
  loading: Loading,
});
const App = () => (
  <Router>
    <Switch>
      <Route exact path="/" component={Home}/>
      <Route path="/about" component={About}/>
    </Switch>
  </Router>
);
  1. 安装依赖
    yarn add react-router-dom

  2. 创建路由页面
    routes/Home.js

    import React from 'react';
    export default () => (
        <div>Home</div>
    )
    

    routes/About.js

    import React from 'react';
    export default () => (
        <div>About</div>
    )
    
  3. 在App.js中引用路由页面
    const Loading = () => <div>Loading...</div>;
    const Home = Loadable({
        loader: () => import('./routes/Home'),
        loading: Loading,
    });
    const About = Loadable({
        loader: () => import('./routes/About'),
        loading: Loading,
    });
    
  4. 配置路由
    import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom';
    ...
    render() {
        return (
            <Router>
                <div>
                    <div>{this.state.msg || 'hello'}</div>
                    <Hello/>
                    <div>
                        <Link to={'/'}>home</Link>
                        <Link to={'/about'}>about</Link>
                    </div>
                    <Switch>
                        <Route exact path="/" component={Home}/>
                        <Route path="/about" component={About}/>
                    </Switch>
                </div>
            </Router>
        );
    }
    
  5. 在Chrome中查看network,可以看到第一次点击about的时候,浏览器会异步加载about组件的js

完整代码可以在github上看到:
https://github.com/shengoo/react-demo/tree/master/code-splitting

React中的容器组件(Container Component)和展示组件(Presentational Component)

React中的容器组件(Container Component)和展示组件(Presentational Component)

React的思想里,UI=render(data)
所以,React的组件一般是完成两种功能:

  1. 读取数据
  2. 渲染界面

如果我们把两个功能都放在同一个组件内,那么这个组件做的事情就太多了,考虑到组件复用和业务变更,让一个组件只专注做一件事,我们可以把这个组件拆分成多个组件,让每个组件只专注做一件事,所以就衍生了一种React常用的模式:容器组件和展示组件。

容器组件

容器组件在外层,有以下特点:

  • 提供数据
  • 关注业务处理
  • 与状态管理工具交互
  • 有自己的状态(state)
  • 几乎没有dom和样式

展示组件

展示组件在容器组件内部,有以下特点:

  • 关注UI层
  • 有自己等dom和样式
  • 没有自己的状态
  • 不关心数据是如何获取和变化的
  • 通过props接收数据和回调函数
  • 不依赖其他组件
  • 不需要生命周期函数

展示组件一般可以使用纯函数的声明方式:

export default (props)=>(
    <div>Hello {props.name}</div>
);

这样做的好处

  • 分离关注,你可以更好的理解app和UI。
  • 更易复用,同样的展示组件可以在不同的状态源、数据源中使用。也可以封装成容器组件,在未来重用它们。
  • 数据结构和页面结构保持一致性。