Skip to content

tfwww/Tips-in-React

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

这是我在用 React 重构项目的过程中,踩过的一些坑。
如果有哪里说得不准确的地方,欢迎指正。

目录

搭建 React 环境

  1. 安装脚手架
    npm install -g create-react-app
  2. 搭建项目 swiper-react 项目名字不能大写
    create-react-app swiper-react

以后的例子都是用的此脚手架搭建的环境,将不再做赘述。


运行环境

在项目目录结构,运行以下命令:

npm start

在开发模式下调试项目,将会自动打开本地服务器 http://localhost:3000

此时,代码的变动将会实时反映到浏览器中

npm run build

在文件夹 build 中打包用于生产环境的代码


引入并使用插件

以 Swiper 为例,手把手教你在 React 中使用 Swiper 4 做轮播图
实例

为啥要写这个东西

项目之前是用的 jq,现在打算用 React 重构,由于原项目已经引用了 Swpier 组件,故自然而然重构项目的时候也就考虑到怎么在 React 中使用这个组件。
由于 Swiper 官方文档并没有针对 React 的例子,而我又是个 React 新手,一下子懵逼,不知道如何使用,现在经过摸索之后,把相关经验总结并分享出来,供后来者参考。

安装 Swiper

npm install --save swiper

编写 Swiper 组件

  1. 首先引入 Swiper 以及样式
// 引入此路径,才不会打包失败
import Swiper from 'swiper/dist/js/swiper.js'

import 'swiper/dist/css/swiper.min.css'
  1. 在组件挂载完毕的时候生成 Swiper 对象
componentDidMount() {
    new Swiper(this.swiperID, {
        pagination: {
            el: this.paginateID,
        },
    });
}

this.swiperIDthis.paginateID 本来应该是 HTML 元素或者相应的元素选择器字符串,如 '#swiper'
在 React 中我用了 ref 方法去引用已到达引用元素的效果。

  1. 在 React 的 render 方法构造 html 结构,注意 ref 的引用
render() {
    const items = this.renderList()
    return (
        <div className="wxchat-banner">
            <section className="new_custom swiper-container index_tab_con" ref={self => this.swiperID = self}>
                <ul className="swiper-wrapper">
                    {items}
                </ul>
                <div className="swiper-pagination banner-pagination" ref={self => this.paginateID = self}></div>
            </section>
        </div>
    )
}

接下来引用插件的方法如下

ReactDOM.render(<Swiper />, document.getElementById('root'))

以上就是作为一个展示性组件如何引入 Swiper 并在 React 中实现轮播图的代码。

数据交互轮播图

如果作为有数据交互的轮播图,我们应该再写一个容器组件来获取数据然后更新到上面写的展示性组件中。

  1. 编写容器组件

数据通过 states 传给子组件 Swiper

class SwiperContainer extends Component {
    constructor(props) {
        super(props)
        this.state = {
            list: []
        }
    }

    componentDidMount() {
        // 这里只是写死数据,一般实际项目是用接口获取数据
        // const url = '/xxxx/xxxxx'
        // fetch(url).then(res => res.json()).then(
        //     (result) => {
        //         this.setState({
        //             list: result.list
        //         })
        //     },
        //     (error) => {
        //         this.setState({
        //             error
        //         });
        //     }
        // )
        this.setState({
            list: [img1, img2, img3]
        })
    }

    render() {

        const {list} = this.state
        return (
            <Swiper list={list} />
        )
    }
}

export default SwiperContainer
  1. 改动展示性组件 在 componentDidMount 周期函数中,添加 observer: true, 这一配置项。

这一点非常重要,如果不添加的话,Swiper 无法检测到数据变动,轮播图显示的就一直只有一张图片。

当初为了修复这个坑可是研究了好久!!!


最后,为啥要特地编写容器组件去获取数据,然后再传递给 Swiper 组件呢?
这么做是为了 Swiper 可以在多处地方复用,如果这个组件里面包含了数据交互,那么必然无法达到复用的要求,还需要再做改动。


配置路由表

效果图

实例

为啥要写这个东西

网上已经有很多文章阐述如何配置路由表,但是有些过时,且文章代码已经不能使用,故记录下自己配置路由表的过程。

安装依赖

  1. 安装 React Router 4
    npm install --save react-router-dom
  2. 安装 React Router Config, 配置路由表必须
    npm install --save react-router-config

配置路由表

const routes = [
    {
        path: '/',
        component: Home,
        exact: true,
    }, {
        path: '/Invest',
        component: Invest,
    }, {
        path: '/Mine',
        component: Mine,
        routes: [
            {
                path: '/Mine/Info',
                component: Info
            }, {
                path: '/Mine/Gender',
                component: GenderWrapper
            }
        ]
    }
]
  1. 值得注意的是,路由表的 component,如果拥有子路由,则需添加 renderRoutes 函数:
const Mine = ({route}) => (
    <div>
        <h2>进入了资料页(一级路由)</h2>
        <Link to={'/Mine/Info'}>资料</Link>
        <br />
        <Link to={'/Mine/Gender'}>性别</Link>
        {/* 如果有子路由,必须使用此函数,否则子路由对应的组件不会渲染 */}
        {renderRoutes(route.routes)}
    </div>
)
  1. 对于一些静态组件,没有数据传递的,可以如下直接定义:
// 静态组件,无法传值
const Info = () => (
    <div>个人资料(二级路由)(静态组件)</div>
)
  1. 那如果组件需要值传递,那么就需要包一层,再传入到路由表里:

假设我们现在有个组件 Gender 显示用户性别 Gender.js

import React, { Component } from 'react'

class Gender extends Component {
    render() {
        return (
            <div>{this.props.gender}</div>
        )
    }
}

export default Gender;

这个时候怎么把这个组件放到路由表里面呢,毕竟组件需要传值 <Gender gender={'男'} />
我们可以选择将这个组件包一层:

// 如果路由所对应渲染的组件需要传值的话,可以先包一层,然后再放到路由表里
const GenderWrapper = () => (
    <div>
        <div>性别(二级路由)(组件传值包了一层 wrapper)</div>
        <Gender gender={'男'} />
    </div>
)
  1. 而对于整个应用入口则为如下:
ReactDOM.render(
    <HashRouter>
        {renderRoutes(routes)}
    </HashRouter>,
    document.getElementById('root')
);

直接通过 routes 路由表来渲染整个应用。

配置全局组件

效果图

实例

为啥要写这个东西

在写项目的时候,总有一些组件需要全局使用的。
举个例子,调用弹窗的时候,是不是每次都要在父组件里放上弹窗组件,然后再通过 state 控制?
所以,我们现在的目标是通过调用一个函数,就能够显示弹窗。

步骤

  1. 先写好一个组件 DialogDialog 由于此处不是关键代码,故不作解释;
  2. 封装 tools 并暴露接口以方便全局调用,关键代码;
const dialogWrapper = document.createElement('div')
const tools = {
	// 显示弹窗
    showDialog: function(config) {
		// 先创建弹窗元素容器
		document.body.appendChild(dialogWrapper)
		// 然后通过 redner 方法加载组件
        ReactDOM.render((
                <Dialog {...config} />
            ),
            dialogWrapper
        )
        if (!document.body.className.includes('preventScroll')) {
            document.body.className += ' preventScroll'
        }
	},
	// 关闭弹窗
    closeDialog: function() {
		// 挂载组件
		ReactDOM.unmountComponentAtNode(dialogWrapper)
		// 删除节点
        if (dialogWrapper.parentNode != null) {
            document.body.removeChild(dialogWrapper)
        }
        document.body.className.replace(/\preventScroll\b/g, "")
    },
}

怎么使用 Dialog Demo

假设现在有个需求点击按钮出现弹窗,请看代码如下:

class DialogDemo extends Component {
	constructor(props) {
		super(props)
		this.show = this.show.bind(this)
	}

	show() {
		// 这里就是通过公共方法调用显示弹窗了
		tools.showDialog({
			type: 'tips',
			text: '演示弹框',
		})
	}

    render() {
        return (
            <button onClick={this.show}>点击出现弹窗</button>
        )
    }
}