React 仿简书项目实战

项目源码

github.com/astak16/act…

项目结构

src/common; // 公共组件

src/pages; // 页面

src/static; // 静态资源

src/store; // 主 store

App.js; // 根组件

index.css; // 样式

index.js; // 入口文件

重置 css

搜索reset.css,复制过来

styled-components

css文件一旦在一个文件中被引入,会在全局中生效

这样写css会有些问题,当页面中有多个组件时,样式存在被覆盖的风险

我们希望在写样式的时候,每个组件的样式是独立的,不会互相的影响

使用

  1. index.css重命名为style.js

  2. index.js引入style.js

    import"style.js";

  3. style.js

    import { injectGlobal } from"styled-components";

    injectGlobal`

    body{

    padding: 10px

    }

    `;

在组件中使用

组件声明

// Header/style.js

import styled from"styled-components";

exportconst HeaderWrapper = styled.div`

height: 56px;

`;

// Header/index.js

import { HeaderWrapper } from"./style";

classHeaderextendsComponent{

render() {

return<HeaderWrapper></HeaderWrapper>;

}

}

图片使用

style.js中使用背景图片的话,直接使用background: url('/images/logo.png')是没有效果的,因为webpack在打包的时候,不知道工程目录是啥样的,它会把路径当成字符串

需要这样写

import logoPic from"/images/logo.png";

exportconst Logo = style.a`

background: url(${logoPic})

`;

设置属性

import logoPic from"/images/logo.png";

exportconst Logo = style.a.attr({

href: "/",

})`

background: url(${logoPic})

`;

参数传递

组件中要传递不同的参数进来,styled提供了函数功能

<RecommendItem key='1' imgURL='/images/1.png'>1</RecommendItem>

<RecommendItem key='2' imgURL='/images/2.png'>1</RecommendItem>

// style.js

export const RecommendItem = styled.div`

width: 280px;

height: 50px;

background: url(${props => props.imgURL});

background-size: contain;

`

immutable.js

reducer中,state的数据是不希望被改变的,在写项目的时候,很容易在不知不觉中把state给改了,从而造成异常。

immutable.js可以帮助解决问题,它会生成一个immutable对象,这个对象是不可被改变的。

immutable提供了fromJS的方法,可以把js对象转换成immutable对象

const a = fromJS({ age: 18 });

immutable提供了getgetIn两种方法来读取数据

const a = fromJS({

age: 18

feature: {

height: 180

}

})

const age = a.get('age')

const height = a.getIn(['feature', 'height'])

// 或者

const height = a.get('feature').get('height')

immutable提供了setmerge两种方法来设置数据。

immutable对象的set方法,会结合之前的immutable对象的值,和设置的值,返回一个全新的对象。它并不会去改之前的immutable数据。

const a = fromJS({

age: 18,

name: "uccs",

});

a.set("age", 19).set("name", "tiantain");

// 或者

a.merge({

age: 1,

name: "tiantian",

});

当最外面使用immutable对象时,内部的Object类型的数据也会变成immutable类型,所以在使用set设置时,先要把普通对象变成immutable对象才行。

const a = fromJS({

list: [], // 这个 list 也是 immutable 数组

});

const data = [1, 2, 3];

a.set("list", fromJS(data));

immutable.js提供了toJS方法,是为了将immutable对象转换成js对象

const a = fromJS({

list: [1, 2, 3],

});

const b = a.toJS();

b[1];

redux-thunk

redux-thunk的作用是可以在action中写函数

redux

首页需要安装reduxreact-redux

react-redux是方便在react中使用redux

redux的使用可以看:redux 学习笔记

store/index.js

// store/index.js

import { createStore, applyMiddleware, compose } from"redux";

import thunk from"redux-thunk";

import reducer from"./reducer";

const store = createStore(reducer, compose(applyMiddleware(thunk))); // 在 store 中使用 redux-thunk

exportdefault store;

store/reducer.js

// 主 store/reducer.js

import { combineReducers } from"redux-immutable";

import { headerReducer } from"../common/header/store";

exportdefault combineReducers({

header: headerReducer,

});

// 分 header/store/reducer.js

import { Search_Focus } from"./actionType";

import { fromJS } from"immutable";

const defaultState = fromJS({

focused: false, // 默认数据

});

exportdefault (state = defaultState, action) => {

switch (action.type) {

case Search_Focus:

return state.set("focused", action.data);

default:

return state;

}

};

redux-immutable提供了一个combineReducers方法,这个方法用来组合各个拆分出来的reducer

header/store/actionCreators.js

// header/store/actionCreators.js

import {Search_Focus} from'./actionType'

const searchFocus = (data) => {

type: Search_Focus,

data

}

exportconst getList = async () => {

return(dispatch) => {

const res = await axios.get('api/header.json')

dispatch(searchFocus(res.data))

}

}

header/store/actionType.js

// header/store/actionType.js

exportconst Search_Focus = "header/search_focus";

App.js

// App.js

import { Provider } from"react-redux";

import store from"./store";

<Providerstore={store}>

<BrowserRouter>

<div>

<Header />

</div>

</BrowserRouter>

</Provider>;

react-redux有一个核心组件Provider,这个组件有一个store属性,将我们定义的store传给它,这样Provider里面所有组件都有能力去使用store中的数据了

组件连接 store

Providerstore提供给了各组件,但是组件想使用store中的数据,还要做连接

import { connect } from"react-redux";

classHeaderextendsReact.Component{}

exportdefault connect()(Header);

react-redux提供了connect方法,connect就是帮助组件和store建立连接的。

connect接收两个参数:mapStateToPropsmapDispatchToProps

import { actionCreators } from"./store/actionCreators";

const mapStateToProps = (state) => {

return {

focused: state.getIn(["header", "focused"]),

};

};

const mapDispatchToProps = (dispatch) => {

return {

handleInputFocus() {

dispatch(actionCreators.getList());

},

};

};

exportdefault connect(mapStateToProps, mapDispatchToProps)(Header);

组件中使用this.props.focused

react-router-dom

Route

  • render方法可以渲染一个东西

  • exact完全匹配路径

    • exactfalse时,//detail都能匹配上

  • component渲染组件

import { BrowserRouter, Route } from"react-router-dom";

import Home from"./pages/home";

import Detail from"./pages/detail/loadable";

functionApp() {

return (

<Provider store={store}>

<BrowserRouter>

<div>

<Route path="/" exact component={Home} />

<Route path="/detail/:id" exact component={Detail} />

</div>

</BrowserRouter>

</Provider>

);

}

export default App;

动画

换一换旁边有个icon,每次点击的时候,这个icon需要转动起来。

每次点击的时候只要改变icontrannform: rotate() 的值就可以了。每次增加 360°

通过ref可以获取到react渲染出来的真实节点。

<SearchInfoSwitch onClick={() => handleChangePage(this.spinIcon)}>

<i className='iconfont spin'

ref={icon => this.spinIcon = icon}

></i>

换一批</SearchInfoSwitch>

handleChangePage(spin) {

let originAngle = spin.style.transform.replace(/[^0-9]/ig, '')

if (originAngle)

originAngle = parseInt(originAngle, 10)

else

originAngle = 0

spin.style.transform = `rotate(${originAngle + 360}deg)`

}

其他

  1. Link代替a做跳转,可以做到按需加载

  2. PureComponent组件等价于Component+shouldComponentUpdate

  3. dangerouslySetInnerHTML={{__html: '<div>文本</div>'}}可以渲染HTML内容

  4. <Route path='/detail/:id'/>路由参数获取this.props.match.params.id

  5. 异步组件redux-immutable,使用异步组件时需要用withRouterexport default connect(mapState, mapDispatch)(withRouter(Detail));

    // detail/loadable.js

    import React from"react";

    import Loadable from"react-loadable";

    const LoadableComponent = Loadable({

    loader: () =>import("./"),

    loading() {

    return<div>正在加载</div>;

    },

    });

    exportdefault () => <LoadableComponent />;

    // App.js

    import Detail from "./pages/detail/loadable";

总结

路由

router.png

项目技术栈:

  • react-router:路由

  • react-redux:数据挂你工具

  • react-loadable: 按需加载

  • react-transition-group:动画

  • redux-immutable:将state变成immutable对象

  • redux-thunk:可以在action中使用函数

  • styled-components:局部样式

  • axiosajax请求

以上是 React 仿简书项目实战 的全部内容, 来源链接: utcz.com/a/36364.html

回到顶部