#從零搭建一個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,能夠自行獲取配置進行修改
相關文章
相關標籤/搜索