开始学习前关注几个说明:
- 教程针对人群是有过React + Redux经验,并且想在新项目中使用TypeScript的人(或者是想自己从零开始配置开发环境的)
- 教程只配置了开发环境,并没有配置生产环境。但是给架构好了,只需要你参考生产环境在配置一份合适自己生产环境参数即可
- 本配置教程是在win环境下制作了
- 因为框架和各插件版本升级未来是否会出现兼容或其它问题就不向未来兼容了,如果因为各插件版本问题出现的问题,本文会给出我已经配置好的版本,可按照我的版本指定安装即可
PS:至于文档如有问题欢迎留言指出,文章偏长的教程,细节中可能会和自己实战中会有点偏差,尽量还原过程细节。
安装初始依赖
1、先创建项目目录,在进入目录内初始化项目 npm init,相信这个就不用我细说了? 输入项目名称和版本等信息
2、首先安装webpack和 webpack-dev-server启服务用的
1 |
npm i webpack webpack-cli webpack-dev-server --save-dev |
3、安装React和Types中React的声明类型文件,以及node环境中的声明类型文件
1 |
npm i react react-dom @types/react @types/react-dom @types/node |
上面@types开头的包都是typeScript的声明文件,你可以进入node_modules/@types/XX/index.d.ts进行查看。
像未来你使用react-router或是其它插件,你可以搜索一下npm包,是否有相应的@types/包名 声明类型文件,
4、接下来安装TypeScript,ts-loader和source-map-loader
1 |
npm i -D typescript ts-loader source-map-loader |
ts-loader可以让Webpack使用TypeScript的标准配置文件tsconfig.json编译TypeScript代码。
source-map-loader使用任意来自Typescript的sourcemap输出,以此通知webpack何时生成自己的sourcemaps。 这让你在调试最终生成的文件时就好像在调试TypeScript源码一样
5、安装自动打包HTML模板插件,这样就可以指定一个HTML模板样式文件,打包到dfist目录中,等会在webpack配置中有备注使用
1 |
npm install html-webpack-plugin --save-dev |
6、安装合并webpack配置的插件,等会在webpack配置中有备注使用
1 |
npm i -D webpack-merge yargs-parser |
7、可选安装babel系列(备用的,前期你不安装也行),如果将来你要分开处理打包,如编译node层的配置就要用到babel给gulp流式处理,或是你其它地方需要用到babel来编译
1 |
npm install -D babel-loader @babel/core @babel/preset-env 安装babel编译支持es8/9 |
8、开始创建项目目录中的各文件,以及相关内容
1 2 3 4 5 6 7 8 9 10 11 12 |
├─package.json ├─tsconfig.json ├─webpack.config.js ├─src | ├─index.template.html | ├─index.tsx | ├─components | | └─Hello.tsx ├─dist ├─config | ├─webpack.development.js 这是开发环境配置文件 | └─webpack.production.js 这是生产环境配置文件 |
到此你就可以启动服务,在react中使用TS语法写代码了,如果你碰到问题就安装我一样的版本插件
npm run start 直接启动服务,端口是8080
npm run dev 是打包文件到dist目录中去(生产环境配置打包的)
不用打包直接 启服务写代码就可以了。
不过后续还要在集成加上router路由和redux功能,至于为什么在单独分开说,看个人项目需求是否添加了。
集成router路由功能,单页应用使用的
1、安装路由模块功能组件,这里我选择的是react-router-dom版本的路由,你也可以选择react-router的,看个人了。
1 |
npm i react-router-dom @types/react-router-dom |
2、在src目录下创建routes目录,里面在创建一个index.tsx文件,注意:这不是入口的文件哦,我取index名是为了引入此文件时方便点而已
src/routers/index.tsx文件内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
import * as React from 'react'; // 引入路由相关一些组件 import { BrowserRouter, Switch, Route } from 'react-router-dom'; // 以下是各组件引入 import Hello from '../components/Hello'; import Box from '../components/Box'; // 配置路由页面 export default class RoutersConfig extends React.Component { render() { return ( <BrowserRouter basename="/"> <Switch> {/* 渲染book/页面 使用indexweb组件 */} {/* <Route path="/" component={Hello} /> */} {/* 首页面,利用render渲染,里面是CommentBox组件,还可以传递参数 */} <Route path="/" exact render={props => ( <Hello name="TypeScript" enthusiasmLevel={10} /> )} /> {/* box页面,利用render渲染,里面是CommentBox组件,还可以传递参数 */} <Route path="/box" render={props => ( <Box name="Box" enthusiasmLevel={1} /> )} /> </Switch> </BrowserRouter> ); } } |
在此文件内,你可以配置所有路由页面要渲染的组件,路由语法相信使用过react的都知道,我就不一一细说每个路由组件的功能了,
集成react-redux功能
这是统一状态管理器组件功能,插件功能说明具体官方有介绍,下面介绍集成细节:
这里说明一下在集成过程中,我把官方demo已经集成在内,当集成完可以测试redux使用功能,没有react-redux功能的是很容易搞晕来的哦,有基础的话跟着流程走整合
改造前期安装相关依赖
1、安装管理状态插件redux
1 |
npm install --save redux react-redux |
2、安装支持异步派遗功能(redux-thunk)和扩展redux存储的插件(redux-devtools-extension)
1 |
npm install --save redux-thunk redux-devtools-extension |
改造详细流程:
1、先熟悉一下完成后的整体目录样式(在上面基础上新增加的部份文件),后面介绍创建相关文件名的时候,希望不会被创建错了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
整体目录结构 ├─package.json ├─tsconfig.json ├─webpack.config.js ├─src | ├─index.template.html | ├─index.tsx | ├─views | | └ReduxDemo.tsx | ├─routes | | └index.tsx | ├─reduxConfig | | ├─store | | | └configureStore.tsx | | ├─reducers | | | ├─index.tsx | | | ├─reducers.js | | | ├─todos.tsx | | | └visibilityFilter.tsx | | ├─middleware | | | └logger.tsx | | ├─enhancers | | | └monitorReducer.tsx | | ├─constant | | | └ActionTypes.tsx | | ├─actions | | | └index.tsx | ├─components | | ├─AddTodo.tsx | | ├─Box.tsx | | ├─Footer.tsx | | ├─Hello.tsx | | ├─Link.tsx | | └TodoList.tsx ├─dist ├─config | ├─webpack.development.js | └webpack.production.js |
2、创建redux容器文件夹目录,在src/reduxConfig下
由于容器内文件过多,收缩展开观看,此集成是官网demo你也可以看官方demo粘入自己的文件
3、创建演示需要的组件功能,改变store属性状态,只要在组件内触发action即可。
3.1 进入components组件文件夹下,
AddTodo.tsx文件内容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
import * as React from 'react' import { connect } from 'react-redux' // 引入合并redux组件 import { addTodo } from '../reduxConfig/actions' // 引入action动作文件 /**定义组件props */ export interface Props { onSubmitAdd2: Function } class AddTodo extends React.Component<Props, object> { input constructor(props) { super(props); } render() {// 这是组件渲染的内容 return ( <div> <form onSubmit={this.onSubmitAdd.bind(this)}> <input ref={node => this.input = node} /> <button type="submit">Add Todo</button> </form> </div> ); } onSubmitAdd(e){ e.preventDefault() if (!this.input.value.trim()) { return } this.props.onSubmitAdd2(this.input.value) // 把用户当前输入的值传入 this.input.value = '' // 添加后清空输入框 } } // 这是核心干活的函数 // 把所有的actions的dispatch灌进去,第二个参数是用户传入组件的值 const mapDispatchToProps = (dispatch) => { // ownProps是使用此组件传入的参数(actions名称) // 每个点击事件你也可以理解成action用户的动作 return ({ // 添加处理函数事件(自定义函数),这个到时候会绑定到组件按钮上使用 onSubmitAdd2: (data:String):Object => dispatch(addTodo(data)) // 接收外部传入的参数,把参数又派发到actions里面去 }) } export default connect(null, mapDispatchToProps)(AddTodo) |
Link.tsx组件内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
import * as React from 'react' /**定义组件props */ export interface Props { onClick: ()=> void active: boolean } // 这是一个按钮组件,供其它组件会被调用多次 class Link extends React.Component<Props, object> { constructor(props) { super(props); // 父组件传入了参数, // console.log("PRO:",this.props.onClick); } render() {// 这是组件渲染的内容 return ( <button // 传入的点击事件 onClick={this.props.onClick} disabled={this.props.active} style={{ marginLeft: '4px', }} > {/* 当前事件按钮的名称 */} {this.props.children} </button> ); } } // 以下是可以单独在拆分一个文件,放在容器文件夹,也可放组件内整体使用 import { connect } from 'react-redux' import { setVisibilityFilter } from '../reduxConfig/actions' // 引入action动作 // 这是核心干活的二个函数 // 1、把所有的state转成props,放入对象里面 const mapStateToProps = (state, ownProps) => { return ({ // 你可以自定义你要存的属性和值,返回的整体是对象 active: ownProps.filter === state.visibilityFilter }) } // 2、把所有的actions的dispatch灌进去,第二个参数是用户传入组件的值 const mapDispatchToProps = (dispatch, ownProps) => { // ownProps是使用此组件传入的参数(actions名称) // 每个点击事件你也可以理解成action用户的动作 return ({ // 添加处理函数事件(自定义函数),这个到时候会绑定到组件按钮上使用 onClick: () => dispatch(setVisibilityFilter(ownProps.filter)) // dispatch里面派发action动作指令,当触发action时(传入的ownProps.filter),把相关用户动作送到 reducre中(index.js)-> visibilityFilter.js 去处理 }) } // 这是拆成二个文件(可以在组件内完成以上写法) // 最终 redux连接的时候合并进去,把二个函数和组件Link合并吐出一个state->props export default connect( mapStateToProps, mapDispatchToProps)(Link) // 在此组件上可以使用 this.props.active值, this.props.onClick函数 |
创建TodoList.tsx文件内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
import * as React from 'react' // 以下组件是可单独抽离放入containers容器文件夹 import { connect } from 'react-redux' import { toggleTodo } from '../reduxConfig/actions' // 引入需要使用的actions动作 import { VisibilityFilters } from '../reduxConfig/actions' // 这是常量动作、全部显示之类名称 /**定义组件props */ export interface Props { todos toggleTodo } class TodoList extends React.Component<Props, object> { constructor(props) { super(props); } render() {// 这是组件渲染的内容 return ( <ul> {this.props.todos.map(todo => <li key={todo.id} onClick={() => this.props.toggleTodo(todo.id)} style={{ textDecoration: todo.completed ? 'line-through' : 'none' }} > {/* 用户输入框输入的内容.显示到list列表这里 */} {todo.text} </li> )} </ul> ); } } // 容器文件内的文件,可放一起 // 这是一个封装的过滤函数,供下面使用 const getVisibleTodos = (todos, filter) => { switch (filter) { // 根据不同的常量名称,显示不同的信息数据,第一个默认全部 case VisibilityFilters.SHOW_ALL: return todos // 正常触发挂载上面的常量,没有触发第二个过滤方法 case VisibilityFilters.SHOW_COMPLETED: return todos.filter(t => t.completed) // 改变里面的completed的值 case VisibilityFilters.SHOW_ACTIVE: return todos.filter(t => !t.completed) default: throw new Error('Unknown filter: ' + filter) } } // 这是核心干活的二个函数 // 1、把所有的state转成props,放入对象里面 const mapStateToProps = state => ({ // 封装的过滤函数,处理不同动作显示不同的state.todos todos: getVisibleTodos(state.todos, state.visibilityFilter) }) // 2、把所有的actions的dispatch灌进去 const mapDispatchToProps = dispatch => ({ // 外部事件传入的当前id值 toggleTodo: id => dispatch(toggleTodo(id)) // 触发了action里的toggleTodo动作 // 触发更新到reducers/todos运作相关处理逻辑 }) // 这是拆成二个文件(可以在组件内完成以上写法) // 最终 redux连接的时候合并进去,把二个函数和组件todoList合并灌入 export default connect( mapStateToProps, mapDispatchToProps)(TodoList) |
创建Footer.tsx文件内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import * as React from 'react' import FilterLink from './Link' // 引入子组件 import { VisibilityFilters } from '../reduxConfig/actions' // 引入常量名称,显示全部或其它 // 这是一个组件 class Footer extends React.Component { render() { return ( <div> <span>Show: </span> {/* 给这个子组件传递了一个filter属性 */} <FilterLink filter={VisibilityFilters.SHOW_ALL}> All </FilterLink> <FilterLink filter={VisibilityFilters.SHOW_ACTIVE}> Active </FilterLink> <FilterLink filter={VisibilityFilters.SHOW_COMPLETED}> Completed </FilterLink> </div> ) } } export default Footer |
4、最终还要整合一下拼接起各组件显示出来,在src目录下创建views目录,
ReduxDemo.tsx文件内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import * as React from 'react' // 引入自定义封装好的三个组件,直接显示的 import AddTodo from '../components/AddTodo' // 输入内容添加到list import TodoList from '../components/TodoList' // 显示每个数据 import Footer from '../components/Footer' // 切换显示全部按钮,未打勾和打勾显示 class ReduxDemo extends React.Component { render() { return ( <div> <AddTodo /> <TodoList /> <Footer /> </div> ) } } export default ReduxDemo |
5、最后在改造一下入口文件,显示此拼接好的组件页面,在src/index.tsx文件内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import * as React from "react"; import * as ReactDOM from "react-dom"; // 引入路由配置文件 // import RoutersConfig from "./routes"; // 状态管理组件配置 import { Provider } from 'react-redux' // 管理所有状态的 import App from './views/ReduxDemo' // 这是显示根组件 import configureStore from './reduxConfig/store/configureStore' // 封装容器的逻辑 const store = configureStore() // 封装了容器的业务逻辑,直接()即可 // render页面写成一个函数 const renderApp = () => { return ReactDOM.render( // <RoutersConfig></RoutersConfig>, // flux实现方式,注入store状态到容器统一管理 provider(ReactRedux) <Provider store={store}> {/* 在APP组件内的所有组件都是可以使用store状态的 */} <App /> </Provider>, document.getElementById('root') as HTMLElement ); } renderApp() |
此时就整合完成,测试一下官方demo是否成功,思想整合进来能用了,具体就根据你的实现情况改造了。