使用create-react+app --typescript配置less以及immer数据流

3,218 阅读4分钟

这个例子的模板的三元大佬写的小册,之前用js写过了一遍,最近在看ts,就试着写了一个推荐页面。demo地址

一、初始化项目配置less

首先使用React的官方脚手架创建项目

npx create-react-app demo --typescript
cd demo
npm run eject //将webpack,babel等相关配置的封装弹射出来,注意此操作不可逆

在config文件夹中打开webpack.config.js文件,在此文件里面,getStyleLoaders函数接受两个参数,第一个为传入css-loader的Options,第二个为可选,就是需要添加的loader,最后返回配置后的loader数组。我们添加如下代码:

const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
const lessRegex = /\.less$/;
const lessModuleRegex = /\.module\.less$/;

然后在oneOf数组中,添加如下代码:

            {
              test: lessRegex,
              exclude: lessModuleRegex,
              use: getStyleLoaders(
                {
                  importLoaders: 2,
                  // modules: true, ////使用模块方式访问样式
                  sourceMap: isEnvProduction && shouldUseSourceMap,
                },
                "less-loader"
              ),
              sideEffects: true,
            },
            {
              test: lessModuleRegex,
              use: getStyleLoaders(
                {
                  importLoaders: 2,
                  sourceMap: isEnvProduction && shouldUseSourceMap,
                  // modules: true,
                  getLocalIdent: getCSSModuleLocalIdent,
                },
                "less-loader"
              ),
            },

这里面的modules: true,如果设置了在文件中需要这么使用less

import styles from './style.less';
<div className={styles.div}></div>

如果不设置此项,则按照使用css的方式即可:

import './style.less';
<div className='div'></div>

需要注意的是,如果没有配置modules: true,在组件中引入第三方库的css样式时,要在.less文件中引入。比如一个轮播图组件需要引入swiper的样式,那么就要在该组件引入的.less文件中引入如下代码:

@import '../../../node_modules/swiper/css/swiper.css';

最后,在src目录下的react-app-env.d.ts文件添加如下代码即可使用less

declare module "*.less" {
  const less: any;
  export default less;
}

二、react-router

HashRouter和BrowserRouter

HashRouter前面的Hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分),即它的路径包含了#,都是ip:port/#/xx的形式,无论#后面的路径怎么变化,请求的资源路径永远为/,相当于index.html,后端API接口不会和/冲突,可以很容易进行前后端不分离的部署。

BrowserRouter原理是H5的history API,IE9一下不兼容,需要web server支持。它的请求路径都是ip:port/xxx的形式。

BrowserRouter进行组件跳转时可以传递任意参数实现组件间的通信,而HashRouter不行(除非手动拼接URL字符串),因此一般配合Redux使用,实现组件间的数据通信。HashRouter不需要web server支持,常用来兼容旧版本浏览器。

BrowserRouter的使用方法

import { createBrowserHistory } from "history";
<Router history={history}>
    <Route path="/" component={Layout}></Route>
</Router>

三、连接react-redux并使用immer

redux中文文档

创建store文件,引入组件的reducer,使用combineReducers将所有的reducer合成一个大的reducer;applyMiddleware中间件的作用是将redux中的action => reducer的过程变成了action => middleware => reducer,我们可以用它来实现异步action、打印日志、错误报告等功能。具体代码如下:

import { createStore, compose, applyMiddleware, combineReducers } from 'redux';
import thunk from 'redux-thunk';
import recommendReducer from './page/Recommend/store/reducer';

const Reducer = combineReducers({
  recommend: recommendReducer,
});

export const Store = createStore(
  Reducer,
  compose(applyMiddleware(thunk))
);

export type initState = ReturnType<typeof Reducer>;

这里面的initState就是reducer里面的初始state,不同于js版本,我们把它的定义放在具体的reducer里面。在组件的mapStateToProps方法中可以用到它:

const mapStateToProps = (state: initState) => ({
  bannerList: state.recommend.bannerList,
  recommendList: state.recommend.recommendList,
});

当然,如果嫌麻烦,你可以直接用any代替。

接下来就需要把store注入到全局中了,在index文件中,我们需要借助Provider组件。

Provider原理是React组件的context属性,源码如下:

class Provider extends Component {
  getChildContext() {
    return {
      store: this.props.store
    };
  }
  render() {
    return this.props.children;
  }
}

Provider.childContextTypes = {
  store: React.PropTypes.object
}

上面代码中,store放在了上下文对象context上面。然后,子组件就可以从context拿到store;

index的代码如下:

const history = createBrowserHistory();
ReactDOM.render(
  <Provider store={Store}>
    <Router history={history}>
      <Route path="/" component={Layout}></Route>
    </Router>
  </Provider>,
  document.getElementById("root")
);

immer

网上关于immer的讲解已经很多了,这里就不在赘述,直接贴代码,在reducer中使用

import * as actionTypes from './actionType';
import produce from 'immer';
import { RecommendStateType } from './data';

const defaultState: RecommendStateType = {
  bannerList: [],
  recommendList: [],
}

export default (state = defaultState, action: any) => {
  return produce(state, draft => {
    switch (action.type) {
      case actionTypes.CHANGE_BANNER:
        draft.bannerList = action.data;
        break;
      case actionTypes.CHANGE_RECOMMEND_LIST:
        draft.recommendList = action.data;
        break;
      default:
        return state;
    }
  })
}

四、总结

总的来说,用redux写会觉得有些繁琐,因此社区中出现了很多基于redux封装的框架,这里我推荐dva。三元大佬的小册,我写的js版本就是使用了dva。当然诚如我的一个同学说的,面试的时候,他们会问你redux的东西,不会问你dva,所以就算是对redux进行复习了吧。

笔者还是个前端小萌新,难免会有纰漏错误,欢迎大佬们进行指点!