近期react最振奋人心的更新hooks迅速流行于社区,工作之余,出于对新技术的兴趣和探索精神,搭建了这个简单的脚手架工具,方便后续的迭代和小伙伴之间的交流。
原材料:
1、react16.7.0-alpha.2
2、webpack4.26.1 + webpack-cli3.1.2
3、react-router-dom4.3.1
相信很多小伙伴已经在社区看到很多有关以上各个技术的最新进展和分享了,但是对于想轻松上手,却又缺乏一定的脚手架配置经验的小伙伴依然无所适从,想实践起来又容易踩到各种坑。
于是,我先踩为敬了。
我们一起学猫叫,一起喵喵喵喵喵~
本脚手架主要采用较新的库搭建,请注意node版本也要跟上哦~
目录结构
请忽略package文件夹,它是存放打包文件的
一、入口开始
import { render } from 'react-dom';
import { applyMiddleware, createStore} from 'redux'
import { Provider } from 'react-redux'
import thunk from 'redux-thunk'
import promise from 'redux-promise-middleware'
import Routers from './router'
import reducers from './reducers'
const middleware = applyMiddleware(promise(), thunk)
const store = createStore(reducers, middleware)
render(
<Provider store={store}>
<Routers />
</Provider>,
document.getElementById("app")
)
复制代码
(⊙o⊙)…好像没啥好说的额,重点在Router,reducers这里。我们只要知道,这里是打包/程序运行的切入点就可以了。
二、承上,Router
import React from 'react'
import { connect } from 'react-redux'
import { replace } from 'react-router-redux'
import { BrowserRouter as Router, Route, Link, Redirect, Switch } from 'react-router-dom';
import AsyncComponent from 'components/AsyncComponent';
const NotMatch = AsyncComponent(() => import('components/NotMatch'));
const App = AsyncComponent(() => import('pages/app'));
@connect(null, {replace})
export default class RootRouter extends React.Component{
render(){
const props = this.props;
//如果没有权限,在此进行判定并replace跳转
return (
<Router>
<div>
<ul>
<li><Link to='/'>首页</Link></li>
<li><Link to='/about'>关于</Link></li>
<li><Link to='/topics'>主题列表</Link></li>
</ul>
<hr/>
<Switch>
<Route exact path="/" render={() => <Redirect to='/login'/>}/>
<Route path="/login" component={App} />
<Route path="/about" component={App} />
<Route path="/topics" component={App} />
<Route component={NotMatch} />
</Switch>
</div>
</Router>
)
}
}
复制代码
react-router4.x后,Broswerhistory已经从react-router中移除,此时需要使用Broswerhistory时,需要安装react-router-dom,这时候,路由看起来更像是一个常见的react组件了。之前的版本打包需要异步加载路由模块使用webpack的require.ensure方法,比如
const App = (location, cb) => {
require.ensure([], require => {
cb(null, require('./components/App'))
})
}
复制代码
再配合 getComponent属性:
<Route path="/" getComponent={ App }/>
而现在getComponent也没啦,咋整?
写个方法吧,没错,就是路由文件中的AsyncComponent方法:
AsyncComponent.jsx
import React, { Component } from "react";
export default importComponent => class AsyncComponent extends Component {
state = {
component: null
}
async componentDidMount(){
const {default: component} = await importComponent();
this.setState({component});
}
render() {
const C = this.state.component;
return C ? <C {...this.props} /> : null;
}
};
复制代码
其实这也是一个组件,只不过是该组件加载完成后,再导入真正需要加载的组件。此时还要配合webpack打包配置的output.chunkFilename属性,稍后会贴出代码。
@connect(null, {replace})是类的装饰器,不了解的可以单独百度一下。
三、启下,component
再往后就很清晰了,./components/App下的组件就是我们的业务组件了。
直接看截图中的SubCom.jsx组件代码
import React, { useState, useEffect, useRef } from 'react';
const effect = count => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
return () => document.title = 'clear'
};
export default ({change}) => {
let [count, setCount] = useState(2);
const click = e => {
count++;
change(count);
setCount(count);
};
useEffect(effect.bind(null, count));
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return <React.Fragment>
<div onClick={click}>
{count}
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</div>
<p>你稳定不点个赞再走吗?</p>
</React.Fragment>
}
复制代码
我们期待的hooks登场啦啦啦~~~但今天不是讲hooks的,社区讲它的很多很详细,这里直接用了。顺便把useRef,React.Fragment等用法也贴了上来。
同时为了方便直接开展业务,reducer,action,接口格式,axios二次封装以及示例函数均已写好,因为有代码洁癖,所以文件夹布局以及重复逻辑均做了处理。
配置文件也比较基本,为了加快编译速度,特意添加了动态链接库的配置,这里强调一下:除了babel-loader,并没有配置css有关的loader,url-loader,file-loader以及样式抽离、压缩的插件,小伙伴们根据自己的需要添加配置即可。代码太多就不贴了。
以前有面试官问我怎样使用ES6的语法书写配置文件,当时确实没有思考过这个问题,直接告诉她我对这个不感兴趣~~~不过今天的配置文件用的ES6来写的,配置文件名称后加上.babel后缀即可......就是这么简单。 完了,再发一张我的猫吧