从零开始搭建react-ts-app之基础架构

2020-08-06  本文已影响0人  Matthew5

code repo: https://github.com/matthew5-template/react-ts-app
branch: basic

1. 引入react react-dom

yarn add react react-dom

// package.json
"dependencies": {
    "react": "^16.13.1",
    "react-dom": "^16.13.1"
  }

2. 创建基础代码

// src/root.js
import React from 'react'

const Root = () => {
  return (
    <div>
      <span>hello root.js</span>
    </div>
  )
}

export default Root

// src/index.js
import React from 'react'
import ReactDom from 'react-dom'
import Root from './root'

const appNode = document.getElementById('app')
ReactDom.render(<Root />, appNode)

3. 使用webpack编译代码

"devDependencies": {
    "@babel/core": "^7.11.0", // babel-loader相关需要引入
    "@babel/preset-env": "^7.11.0",
    "@babel/preset-react": "^7.10.4",
    "babel-loader": "^8.1.0",
    "html-webpack-plugin": "^4.3.0", // webpack html入口插件
    "webpack": "^4.44.1",
    "webpack-cli": "^3.3.12",
    "webpack-dev-server": "^3.11.0"
  }
// build/webpack.base.js
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: './src/index.js',
  devServer: {
    host: 'localhost',
    port: 3000
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        use: 'babel-loader'
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './index.html'
    })
  ]
}

// .babelrc.js
module.exports = {
  presets: ['@babel/preset-env', '@babel/preset-react'],
  plugins: [['@babel/plugin-proposal-class-properties', { loose: true }]] 
}
"scripts": {
    "start": "webpack-dev-server --config build/webpack.dev.js",
    "build": "webpack --config build/webpack.prod.js"
  }

4. 支持scss和css module以及分离css文件

// child.scss
.wrapper {
  .child {
    background-color: green;
  }
}
// child.js
import style from './child.scss'

const Child = () => {
  return (
    <div className={style.wrapper}>
      <span className={style.child}>hello child.js</span>
    </div>
  )
}
"devDependencies": {
    "css-loader": "^4.1.1",
    "postcss-loader": "^3.0.0",
    "postcss-preset-env": "^6.7.0",
    "precss": "^4.0.0",
    "style-loader": "^1.2.1",
    "mini-css-extract-plugin": "^0.11.0",
  }
// postcss.config.js
module.exports = {
  plugins: {
    precss: {}, // 解析scss语法
    'postcss-preset-env': {} // 默认解析css next stage2语法
  }
}

// build/webpack.base.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
rules: [
      {
        test: /\.scss$/,
        use: [
          build && MiniCssExtractPlugin.loader,
          !build && 'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: {
                localIdentName: '[path][name]__[local]--[hash:base64:5]'
              }
            }
          },
          'postcss-loader'
        ].filter(Boolean)
      }
    ],
plugins: [
      new webpack.ProgressPlugin(),
      build &&
        new MiniCssExtractPlugin({
          filename: 'css/[name].[chunkhash:8].css',
          chunkFilename: 'css/[name].[chunkhash:8].css'
        })
    ].filter(Boolean)

5. 支持image等文件引用

yarn add file-loader url-loader -D
url-loader可以将文件优先转为base64
当超过设置的limit时优先使用file-loader处理,limit单位为bytes

"devDependencies": {
    "file-loader": "^6.0.0",
    "url-loader": "^4.1.0",
}

import venom from './assets/venom.png'
<img src={venom} style={{ width: 60 }} />

webpack中添加rule配置

module: {
  rules: [
      {
        test: /\.(jpe?g|png|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 1024,
              name: 'img/[name]_[hash:6].[ext]'
            }
          }
        ]
      }
  ]
}

6. 使用路由react router

yarn add react-router react-router-dom react-router-config

"dependencies": {
    "react-router": "^5.2.0",
    "react-router-config": "^5.1.1",
    "react-router-dom": "^5.2.0"
  }
// routes.js
import React from 'react'

const routes = [
  {
    path: '/',
    exact: true,
    component: React.lazy(() => import(/* webpackChunkName: "info" */ './info'))
  },
  {
    path: '/child',
    exact: true,
    component: React.lazy(() =>
      import(/* webpackChunkName: "child" */ './child')
    )
    // 不支持嵌套
    // routes: [
    //   {
    //     path: '/info',
    //     component: Info
    //   }
    // ]
  },
  {
    path: '/child/info',
    component: React.lazy(() => import(/* webpackChunkName: "info" */ './info'))
  }
]

export default routes
<!-- 一般方式 非集中路由 -->
<HashRouter>
 <Route exact path="/child" component={Child} />
 <Route path="/child/info" component={Info} />
</HashRouter>
// 集中路由配置方式
// root.js
import React, { Suspense } from 'react'
import { HashRouter, Switch } from 'react-router-dom'
import { renderRoutes } from 'react-router-config'
import routes from './routes'

// import Child from './child'
// import Info from './info'

const Root = () => {
  return (
    <div>
      <span>hello root.js</span>
      <Suspense fallback={<div>Loading...</div>}>
        <HashRouter>
          <Switch>
            {/* <Route exact path="/child" component={Child} /> */}
            {/* <Route path="/child/info" component={Info} /> */}
            {renderRoutes(routes)}
          </Switch>
        </HashRouter>
      </Suspense>
    </div>
  )
}

export default Root

7. webpack HMR - hot module replacement

在webpack配置中添加devServer.hot: true
在plugin中添加hmr plugin (目前发现plugin不添加也可以生效)

// build/webpack.base.js
devServer: {
    hot: true
},
plugins: [
    // new webpack.HotModuleReplacementPlugin(),
    // new webpack.NamedModulesPlugin(),
]

在代码入口处监听module change

if (module.hot) {
  module.hot.accept('./root', function () {
    ReactDom.render(<Root />, appNode)
  })
}

##### react-hot-loader (TODO: 不起作用)
webpack提供的HMR可以无刷新更新页面但却无法保存页面中的state数据

上一篇下一篇

猜你喜欢

热点阅读