#从零搭建一个react-hooks项目(三)

  • 上一篇咱们配置了项目相关,包括代码压缩混淆,开发生产配置抽离,图片与文字的引入使用等
  • 接下来咱们就配置一下react-router,react-redux与typescript
  • 配置以前咱们先补充一下webpack的部分配置,用于方便咱们的开发
  • 在webpack.common.js中配置webpack的查找规则,也就是resolve,以下
...
module.exports = {
  entry: {
    ...
  },
  module: {
    ...
  },
  resolve: {
    extensions: ['.jsx', '.js', '.json'],
    alias: {
      "@": path.resolve(__dirname, 'src')
    }
  },
  plugins: [
    ...
  ],
  optimization: { // 公共代码抽离
    ...
  }
}
复制代码
  • 上面新增了一个resolve的解析规则,使webpack方便查找,第一个extensions中的配置表示咱们在引入后缀为.jsx,.js,.json的文件时,能够直接写文件名而不用加后缀,好比引入'./index.jsx',咱们就能够直接写为'./index'
  • 下面的alias就是别名配置,用于在引入路径时方便使用,好比咱们在项目中有以下的代码结构
src 
├── lib 
│ └── utils.js 
└── pages 
└── demo 
└── index.js
复制代码
  • 在 src/pages/demo/index.js 中若是要引用 src/lib/utils.js 那么能够经过:import utils from '../../lib/utils' ,若是目录更深一些,会愈来愈难看,这时能够经过设置 alias 来缩短这种写法,例如:
module.exports = {
  resolve: {
      '@': path.resolve(__dirname, 'src'),
      '@lib': path.resolve(__dirname, 'src/lib')
  }
}

复制代码
  • 这样咱们就能够直接使用 '@lib/utils'来进行文件的引入了

react-router的使用

  • 配置完resolve以后咱们就开始进行react-router的配置了,仍是先下载react-router-dom模块,命令为: npm i react-router-dom -S
  • 而后在src下新建两个页面,pages/login.jsx与pages/home.jsx做为咱们的路由页面
// pages/home.jsx
import React from 'react'
const Home = () => {
  return (
    <div>我是首页</div>
  )
}
export default Home

// pages/login.jsx
import React from 'react'
const Login = () => {
  return (
    <div>我是登陆页</div>
  )
}
export default Login
复制代码
  • 接着在src下新建routes/index.jsx文件做为咱们的路由管理页面
  • 在文件内引入react与react-router-dom,而后引入新建的login页面与home页面
  • 页面代码以下
// routes/index.jsx
import React from 'react'
import { HashRouter as Router, Route, Switch, Redirect } from 'react-router-dom'
import Login from '@/pages/login'
import Home from '@/pages/home'
const RouteConfig = () => {
  return (
    <Router> <Switch> <Route path="/home" component={ Home }></Route> <Route path="/login" component={ Login }></Route> <Redirect to="/home" from="/"></Redirect> </Switch> </Router>
  )
}
export default RouteConfig
复制代码
  • 而后在app.jsx里面以组件的形式引入咱们的路由便可,以下
// app.jsx
import * as React from 'react'
import './app.less'
import RouteConfig from '@/route/index'
class App extends React.Component{
  render(){
    return (
      <RouteConfig></RouteConfig>
    )
  }
}
export default App
复制代码
  • 而后运行npm run dev就能发现自动帮咱们定位到/home页面,咱们手动修改路由为/login就能够发现页面跳转到了login页面
  • 至此,咱们的react-router就配置完成了

react-redux的使用

  • redux是经常使用的状态管理组件,用于维护全局的数据变量,react项目中天然也须要它的存在,下面咱们就配置一下redux
  • 在react中使用redux须要引入redux与react-redux,仍是先安装包: npm i redux react-redux -S
  • 而后在src下新建文件夹store用于放置咱们的redux相关文件
  • redux在数据比较复杂的时候会按照模块来放置文件,最后再用redux提供的api组合起来,方便后续的维护,这里咱们创立数字模块number与聊天模块chat,分别在store下面创建两个文件夹store/chat与store/number
  • 在每一个文件夹下面创建咱们会用到的操做该数据的操做类型与该操做类型下的数据变化状况,分别对应types.js与reducers.js
  • 先构建store/number下的types与reducers,对外提供加减两个方法,先在types下构建常量用于标识对应的方法
// store/number/types
export const INCREMENT = 'INCREMENT'
export const DECREMENT = 'DECREMENT'
复制代码
  • 而后再编写该方法下的数据变化状况reducers,并为这个数字模块设立初始值0,当执行方法为加减的时候就在原基础上加减对应的值,不然就原样返回
// store/number/reducers.js
import { INCREMENT, DECREMENT } from './types'
const initialState = 0
const numberReducer = (state = initialState, action) => {
  switch(action.type) {
    case INCREMENT:
      return state + action.payload
    case DECREMENT:
      return state - action.payload
    default:
      return state
  }
}
export default numberReducer
复制代码
  • 上面就是简单的数字模块的两个方法了,而后咱们一样实现一下chat模块的方法
// store/chat/types
export const SEND = 'SEND'
export const DELETE = 'DELETE'

// store/chat/reducers
import { SEND, DELETE } from './types'

const initialState = []
const chatReducer = (state = initialState, action) => {
  switch(action.type) {
    case SEND:
      return [...state, action.payload]
    case DELETE:
      return state.length ? state.slice(0, state.length - 1) : []
    default:
      return state
  }
}
export default chatReducer

复制代码
  • chat模块提供了一个发送方法与删除方法用于操做聊天列表
  • 接下来咱们就要使用redux提供的combineReducers方法将其合并起来
  • 在store下新建index.js文件用于最后的导出
  • 在index.js中引入combineReducers,chat模块,number模块,而后合并并导出,以下
// store/index.js
import { combineReducers } from 'redux'
import ChatReducer from './chat/reducers'
import NumberReducer from './number/reducer'

const rootReducer = combineReducers({
  number: NumberReducer,
  chat: ChatReducer
})

export default rootReducer
复制代码
  • 上面就完成了redux的编写,接下来就要在react根组件上将其使用
  • 在index.jsx中引入store/index.js,引入redux提供的方法createStore建立全局状态,引入react-redux中的Provider包装根组件,以下
// index.jsx
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import App from './src/app.jsx'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import RootReducer from '@/store/index'
const store = createStore(RootReducer)
ReactDOM.render(
  <Provider store={ store }> <App/> </Provider>
  ,document.querySelector('#app')
)
复制代码
  • 上面就在咱们的项目中引入了redux,接下来就是使用,咱们在login组件中尝试使用number并修改
  • 在login组件中引入对应的操做方法与react-redux提供的connect方法,而后修改咱们的login组件以下
// src/pages/login.jsx
import React from 'react'
import { connect } from 'react-redux'
import { INCREMENT, DECREMENT } from '@/store/number/types'
const Login = (props) => {
  const { number, increment, decrement } = props
  return (
    <div> <button>我是数字{ number }</button> <button onClick={() => { increment() } }>点我加10</button> <button onClick={() => { decrement() } }>点我减10</button> </div>
  )
}
function mapStateToProps(state){ // 将redux中的state整合到props中
  return {
    number: state.number 
  }
}

function mapDispatchToProps(dispatch){ // 将操做state的函数整合到props中
  return {
    increment: () => dispatch({ type: INCREMENT, payload: 10 }),
    decrement: () => dispatch({ type: DECREMENT, payload: 10 })
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(Login)
复制代码
  • connect中的第一个参数接受两个函数,分别用于整合state到props中跟整合操做方法到props中,这样咱们就能够在页面中使用该方法与该值了
  • 修改以后咱们点击两个按钮就能够看到对应值的变化了

react中引入typescript

  • typescript当下如此潮流咱们天然也要接入一下啦,下面就是简单的接入过程
  • 得益于咱们强大的babel-7,咱们再也不须要去单独的使用ts-loader或者awesome-typescript-loader解析ts文件,只须要在babel中进行typescript的相关配置便可
  • 首先仍是先安装咱们的typescript与@babel/preset-typescript,命令为: npm i typescript @babel/preset-typescript -D
  • 而后再.babelrc文件中进行typescript的配置,以下
// .babelrc
{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react",
    "@babel/preset-rtpescript", // 处理typescript
  ],
  "plugins": [
    "@babel/proposal-object-rest-spread", // 处理对象的rest写法
    "@babel/proposal-class-properties" // 处理类里面的箭头函数绑定,static属性等 
  ]
}
复制代码
  • 而后在webpack中对typescript文件配置babel解析便可,这是一个通用配置,因此仍是在webpack.common.js上修改,以下
// webpack.common.js
module.exports = {
  entry: {
  },
  module: {
    rules: [
      {
        test: /\.(js|ts)x?$/, // tsx或者ts文件也使用babel解析
        use: ['babel-loader'],
        exclude: /node-modules/
      }
    ]
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.jsx', '.js', '.json'], // 添加.tsx与.ts后缀解析
    alias: {
      "@": path.resolve(__dirname, 'src')
    }
  },
  plugins: [
    ...
  ],
  optimization: { // 公共代码抽离
    ...
  }
}
复制代码
  • 而后就要配置typescript中的编译规则,在根目录下新建tsconfig.json文件,而后添加以下配置
{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true, // 容许从没有设置默认导出的模块中默认导入。这并不影响代码的输出,仅为了类型检查。
    "outDir": "./dist/", // 重定向输出目录
    "noImplicitAny": true,// 在表达式和声明上有隐含的 any类型时报错。(默认为false,我的建议也为false,能够兼容以前的js代码,这里改成true是为了我本身检测哪些类型须要处理)
    "module": "es6", // 模块引入方式
    "target": "esnext",// 指定ECMAScript目标版本
    "jsx": "react", // 在 .tsx文件里支持JSX
    "allowJs": true,
    "sourceMap": true, // 生成相应的 .map文件
    "lib": [
      "es2015",
      "dom.iterable",
      "es2016.array.include",
      "es2017.object",
      "dom"
    ], // 编译过程当中须要引入的库文件的列表。
    "moduleResolution": "node",// 用于解析一些加载器,如css-type, source-map 
    "baseUrl": "./src", // typescript的模块解析基础路径
    "paths": { // 对应的模块解析路径
      "store/*": ["store/*"],
    },
  },
  "include": [
    "src"
  ],
  "exclude": [
    "./node_modules"
  ]
}
复制代码
  • 由于咱们以前配置了别名,因此须要加上后面的配置中的baseUrl与paths保证typescript可以找到对应的配置,其余的都是typescript的基本配置
  • 接下来咱们尝试将入口文件修改成tsx尝试一下,在webpack.common.js中将入口文件改成app:'index.tsx',而后再将index.jsx后缀改成tsx,以后运行npm run dev发现报错 can not find module 'react',就说明咱们的typescript已经配置完成了,能够正常检查错误
  • 上述错误是由于咱们在TS中进行的开发,TS并不知道咱们的react是什么类型,导出了什么,因此就报错了,这里咱们就须要安装react相关的类型文件
  • 项目中咱们使用了react,react-dom,react-redux,react-router-dom这四个依赖,因此咱们就须要安装依赖包,命令为:npm i @types/react @types/react-dom @types/react-redux @types/react-router-dom -S
  • 以后咱们再次编译,就发现项目能够正常运行了。
  • 可是使用上面的配置以后发现,在咱们编译过程当中类型出错webpack并不会提示咱们出错,这是由于babel7中在编译过程会移除typescript,彻底以javascript的形式进行打包,详见(https://iamturns.com/typescript-babel)。由于,要把babel跟typescript同时编译,两个编译器会致使过程十分缓慢,这但是开发者不能容忍的。针对这种状况,咱们能够新建一个命令 "check": "tsc -watch",而后使用vscode带给咱们的终端启用两个终端,一个运行命令 npm run check,另外一个运行命令npm run dev,这样就发现咱们在开发中出现的类型错误都会在check的终端对咱们进行提示。
  • 接下来咱们只须要将全部的jsx文件转为tsx文件就能够进行typescript的开发了,项目地址react-admin,能够自行获取配置进行修改
相关文章
相关标签/搜索