react头部组件开发

styled-components

css文件是全局引用的,一个组件中引用了,所有组件都可以用
建议用第三方组件管理css

yarn add styled-components
import { createGlobalStyle } from 'styled-components'

export const injectGlobal = createGlobalStyle`
    body {
    	margin: 0;
    	padding: 0;
    	font-family: sans-serif;
    }
`

以上为新版写法

reset.css 在所有浏览器上样式统一
把各个浏览器对标签自身样式进行统一
到网站复制代码,放到style.js里

用styled-components完成header组件布局

可以用chrome控制台那个带箭头的长方形按钮看页面各个原始的尺寸

两种跳转方式

export const Logo = styled.a.attrs({
	href: '/'
})`
    position: absolute;
    top: 0;
    left: 0;
    display: block;
    width: 100px;
    height: 56px;
    background: url(${logoPic});
    background-size: contain;
`;

点击a标签,会往首页跳转

<Logo href: '/'/>

两种写法

iconfont

使用iconfont嵌入头部图标

iconfont.cn

把iconfont.css改成js文件,按照styled-components全局变量的方式写IconfontStyle,按组件引用,最后按官方文档写个

  • .标签。
  • 过渡动画

    做过渡动画
    给Header类加个

    constructor(props){
        	super(props);
        	this.state = {
        		focused: false
        	}
    }
    
    <NavSearch
    	className = {this.state.focused ? 'focused' : ''}
    ></NavSearch>
    

    NavSearch里加样式

    &.focused {
       width: 200px;
    }
     
    <i className={this.state.focused ? 'focused iconfont' : 'iconfont'}>&#xe729;</i>
    

    SearchWrapper里的.iconfont修改

    加了handleInputFocus 和handleInputFlur

    结合动画

    yarn add react-transition-group
    <CSSTransition
    	in={this.state.focused}
    	timeout={200}
    	classNames="slide"
    ></CSSTransition>
    

    classNames!!!

    用redux和react-redux进行数据管理

    使用React-Redux进行应用数据的管理
    安装两个内容

    yarn add redux
    yarn add react-redux
    
    import { Provider } from 'react-redux';
    <Provider store = {store}>
          <Header/>
    </Provider>
    

    这样Header才能拿到store里的数据

    const mapDispatchToProps = (dispatch) => {
    	return {
    		handleInputFocus(){
    			const action = {
    				type: 'search_focus'
    			};
    			dispatch(action);
    		},
    		handleInputBlur(){
    			const action = {
    				type: 'search_blur'
    			};
    			dispatch(action);
    		}
    	}
    }
    

    改写之后

    constructor(props){
        	super(props);
        	this.state = {
        		focused: false
        	}
        	this.handleInputFocus = this.handleInputFocus.bind(this)
        	this.handleInputBlur = this.handleInputBlur.bind(this)
    
        }
        	
    

    这部分删除。

    使用combineReducers完成对数据的拆分管理

    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
    import {compose}from ‘redux’
    

    header文件下加一个reducer,放reducer的内容
    src下的reducer

    import { combineReducers } from 'redux';
    import headerReducer from '../common/header/store/reducer';
    
    export default combineReducers({
    	header: headerReducer
    })
    

    组合多个不同组件的reducer
    这时动画效果没有了
    因为数据结构多了一层header

    const mapStateToProps = (state) => {
        return {
        	focused: state.header.focused
        }
    }
    

    加上就好了

    header的store 下加一个index.js

    import reducer from './reducer';
    
    export { reducer }
    

    暴露reducer
    总reducer中改写成如下代码。

    import { combineReducers } from 'redux';
    import { reducer as headerReducer } from '../common/header/store';
    
    const reducer = combineReducers({
    	header: headerReducer
    })
    
    export default reducer;
    

    actionCreators与constants的拆分

    import * as actionCreators from './store/actionCreators';
    

    用ES6的语法导入

    使用immutable.js来管理store中的数据

    第三方模块,帮助我们生成immutable对象,防止改state。

    yarn add immutable
    import { fromJS } from 'immutable'; JS 对象转换成immutable对象
    focused: state.header.focused
    

    改成:

    state.header.get(‘focused’)
    
    return state.set('focused',true);
    

    用redux-immutable统一数据格式

    axios Ajax

    Ajax获取推荐数据

    const mapDispatchToProps = (dispatch) => {
    	return {
    		handleInputFocus(){
    			dispatch(actionCreators.getList());
    			dispatch(actionCreators.searchFocus());
    		},
    		handleInputBlur(){
    			dispatch(actionCreators.searchBlur());
    		}
    	}
    }
    

    actionCreators

    export const getList = () => {
    	return (dispatch) => {
    		console.log(123)
    	}
    };
    
    import axios
    

    index.js改成

    import { 
    	createStore,
    	compose,
    	applyMiddleware
    } from 'redux';
    import thunk from 'redux-thunk';
    import reducer from './reducer';
    
    const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
    
    const store = createStore(reducer,composeEnhancers(
    	applyMiddleware(thunk)
    ));
    
    export default store;
    

    public下建 api文件 建headerLIst.json
    src下找不到会去public下找,如果挨个查找。
    上线的时候删除API文件就行

    if(action.type === constants.CHANGE_LIST){
            return state.set('list', action.data);
    }
    

    data要变成immutable对象

    所以在createAction里要改写

    const changeList = (data) => ({
    	type: constants.CHANGE_LIST,
    	data: fromJS(data)
    })
    

    浏览器打开redux,切换到state,刷新,点击搜索框,state里展示了我们json中的内容。
    redux-thunk 的作用

    <SearchInfoList>
     {
     	this.props.list.map((Item) => {
     		return <SearchInfoItem key = {Item}>{Item}</SearchInfoItem>
     	})
     }
    </SearchInfoList>
    

    循环展示

    代码调优 actionCreator export统一放在最下

    const { focused, list } = this.props;
    

    focused就不用写成this.props.focused.

    换一批功能

    const defaultState = fromJS({
        focused: false,
        list: [],
        page: 1,
        totalPage: 1
    });
    加page和totalPage两个数据项
    const changeList = (data) => ({
    	type: constants.CHANGE_LIST,
    	data: fromJS(data),
    	totalPage: Math.ceil(data.length / 10)
    })
    
    case constants.CHANGE_LIST:
        return state.set('list', action.data).set('totalPage', action.totalPage);
    

    点换一批的时候input失焦

    header里的index.js

    const newList = list.toJS();
    		const pageList = [];
    
    		for(let i = ((page - 1)* 10); i < (page * 10); i++){
    			console.log(newList[i])
    			pageList.push(
    				<SearchInfoItem key ={newList[i]}>{newList[i]}</SearchInfoItem>
    			)
    		}
    

    直接输出10次undefined

    处理一下

    if(newList.length){
    			for(let i = ((page - 1)* 10); i < (page * 10); i++){
    				pageList.push(
    					<SearchInfoItem key ={newList[i]}>{newList[i]}</SearchInfoItem>
    				)
    			}
    	    }
    return state.merge({
        list: action.data,
        totalPage: action.totalPage
    })
    

    代替

    return state.set('list', action.data).set('totalPage', action.totalPage);
    

    换页旋转动画效果实现

    与前文不同,此处用css动画

    .spin{
            display: block;
            float: left;
            font-size: 12px;
            margin-right: 2px;
            transition: all .2s ease-in;
            transform-origin: center center;
    }
    
    handleChangePage(page,totalPage, 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)';
    
    			if(page < totalPage){
    				dispatch(actionCreators.changePage(page + 1));
    			}
    			else{
    				dispatch(actionCreators.changePage(1));
    			}
    	}
    
    <i ref={(icon) => {this.spinIcon = icon}} className='iconfont spin'>&#xebf2;</i>
    

    React的难点是面向数据设计
    多写几遍项目代码,熟练之后就是套路

    避免无意义的请求发送,提升组件性能

    改一下handleInputFocus

    (list.size === 0) && dispatch(actionCreators.getList());
    

    鼠标到换一批时显示成手型cursor: pointer

    export const SearchInfoSwitch = styled.span `
        float: right;
        font-size: 13px;
        cursor: pointer;
        .spin{
            display: block;
            float: left;
            font-size: 12px;
            margin-right: 2px;
            transition: all .2s ease-in;
            transform-origin: center center;
        }
    `;
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值