react+typescript+antd随笔(一)

最近在学习typescript,所以试着用typescript来写react项目,记录一下过程中遇到的一些问题及解决方法。

一、项目搭建

这里使用create-react-app脚手架搭建基于typescript的react项目,根据文档执行以下命令:

npx create-react-app my-app --typescript

创建好了之后会是如下的结构:

注:ts-demo对应my-app

现在执行

cd my-app && yarn start

可以看到项目正常运行了。
如果遇到启动报错问题,一般删除node_modules然后重新yarn install即可。

二、常见问题

项目搭建好之后我们就要开始着手项目开发,下面写一些开发中常见的问题。

1、路径别名设置

在开发中我们经常需要在当前文件内引入别的组件或者一些方法,如果都使用绝对或者相对路径来引用会变得很麻烦,所以一般都会在webpack的配置里面添加路径别名来解决这个问题:

resolve:{
  //一些其他的配置
  alias:{
    '@': paths.appSrc //这里的paths.appSrc就是src路径,详细请自己参阅config/paths.js
  }
 //一些其他的配置
}

如果是js版本,这样的话已经ok了,我们也可以正常的使用如下方式引入

 import { someComponent } from '@/components/someComponent';

但是在ts版本,这里会报错提示找不到模块,解决办法为:需要在tsconfig.json里加上如下配置:

"compilerOptions":{
  //这里是一些已经存在的其他配置
  //如下是需要添加的配置
  "baseUrl": "src",
    "paths": {
      "@/*": [
        "./*"
      ]
   }
}
2、css模块化

脚手架搭建好的项目已经配置了css模块化,预编译器采用的sass,所以你可以直接使用:

//app.tsx
import React from 'react';
import logo from './logo.svg';
import './App.css';
import styles from './app.module.scss';
import MceLayout from './routes/layout/layout';
import { Button } from 'antd';

const App: React.FC = () => {
  return (
    <div className={styles.app}>
      <MceLayout/>
    </div>
  );
}

export default App;
//app.module.scss
.app {
  color: #fff
}

但是你需要遵循后缀以.module.scss || .module.sass为后缀,正如webpack里配置好的规则:

//webpack.config.js
40 // style files regexes
41 const cssRegex = /\.css$/;
42 const cssModuleRegex = /\.module\.css$/;
43 const sassRegex = /\.(scss|sass)$/;
44 const sassModuleRegex = /\.module\.(scss|sass)$/;

423 // Opt-in support for SASS (using .scss or .sass extensions).
424 // By default we support SASS Modules with the
425 // extensions .module.scss or .module.sass
426{
427  test: sassRegex,
428  exclude: sassModuleRegex,
429  use: getStyleLoaders(
430    {
431      importLoaders: 2,
432      sourceMap: isEnvProduction && shouldUseSourceMap,
433    },
434    'sass-loader'
435  ),
436  // Don't consider CSS imports dead code even if the
437  // containing package claims to have no side effects.
438  // Remove this when webpack adds a warning or an error for this.
439  // See https://github.com/webpack/webpack/issues/6571
440  sideEffects: true,
441},
3、antd组件样式问题

如果项目用了antd,为了让组件的样式和组件一样模块化加载(不做配置的话需要全局引入antd的样式文件,这样有些没用到的组件的样式也加载了,影响性能),需要在package.json里进行如下配置:

"babel": {
    //一些已经存在的配置
    //添加如下配置
    "plugins": [
      [
        "import",
        {
          "libraryName": "antd",
          "style": "css"
        }
      ]
    ]
 }

4、react组件写法问题

在js版本(es6)的有状态组件里,写法大致如下:

//子组件
import React from 'react';
export default class Child extends React.Component{
  constructor(props){
    super(props);
    this.state = {
      count: 0
    }
  }
  render(){
    return(
      <div onClick={this.props.add}>click to add</div>
    )
  }
}
//父组件
import React from 'react';
import Child from './child';
export default class Parent extends React.Component{
  constructor(props){
    super(props);
    this.state = {
      count: 0
    }
  }
  add = ()=>{
    this.setState({
      count: this.state.count + 1
    })
  }
  render(){
    return(
      <Child add={this.add}/>
    )
  }
}

这样在子组件内调用父组件传递的值是可以的。但是,在typescript版本,需要在子组件内定义接口来声明props和state的类型,否则this.props.say()会报错提示say不存在:

类型“Readonly<{}> & Readonly<{ children?: ReactNode; }>”上不存在属性“add”。ts(2339)

如下解决,修改child.tsx(typescript版的react组件后缀为.tsx):

import React from 'react';
//定义props和state的类型接口
interface IProps {
  add: () => void; // () => void表示函数类型
}
interface IState {
  msg: string;
}
//把props和state的类型接口传入组件
export default class Child extends React.Component<IProps, IState>{
  constructor(props: IProps){
    super(props);
    this.state = {
      msg: 'hello world'
    }
  }
  render(){
    return(
      <div onClick={this.props.add}>click to add</div>
    )
  }
}
注意:你可能会发现就算不传入state的类型,也可以正常访问this.state.xx,但是这样typescript就无法对state进行类型检查。导致也许你访问了一个state里不存在的属性也不会报错,就失去了使用typescript的意义。

5、http代理

业务场景:我们本地开发的时候调用某个接口,会存在跨域问题,此时我们需要采用代理来规避跨域。
如果只代理一个api地址,则只需要在package.json内添加如下内容:

"proxy": "http://api02.aliyun.venuscn.com" // 这里是以阿里云的免费api地址为例,换成你自己需要的地址即可

如果需要代理多个api地址,需要安装http-proxy-middleware:

yarn add @types/http-proxy-middleware
注意:在typescript环境下,安装所有的依赖包都需要以@types/为前缀,否则无法引入。

然后在src目录下新建setupProxy.js文件,并按照如下格式填写:

//setupProxy.js
//注:下面的例子都是以阿里云的免费api,实际项目中改为自己的地址即可
const proxy = require('http-proxy-middleware');
module.exports = function(app) {
  app.use(proxy(
    '/province',
    {
        target: 'http://api02.aliyun.venuscn.com',
        changeOrigin: true,
        secure: false,
        pathRewrite: {
            '^/province': '/',
        },
    },
  ));
  app.use(proxy(
    '/poetry',
    {
        target: 'http://jisutssbs.market.alicloudapi.com',
        changeOrigin: true,
        secure: false,
        pathRewrite: {
            '^/poetry': '/',
        },
    },
  ));
};

你的http请求文件中:


axios.get('/province/area/all?level=0');//此请求最终为:http://api02.aliyun.venuscn.com/area/all?level=0

axios.get('/poetry/tangshi/search?keyword=无');//此请求最终为:http://jisutssbs.market.alicloudapi.com/tangshi/search?keyword=无

注意:虽然本文是基于typescript,但是这个配置文件目前看来只能以.js为后缀(待验证),如果你执行了npm run eject后,会发现在config目录下的paths.js里配置的是setupProxy.js:
 //...
module.exports = {
  dotenv: resolveApp('.env'),
  appPath: resolveApp('.'),
  appBuild: resolveApp('build'),
  appPublic: resolveApp('public'),
  appHtml: resolveApp('public/index.html'),
  appIndexJs: resolveModule(resolveApp, 'src/index'),
  appPackageJson: resolveApp('package.json'),
  appSrc: resolveApp('src'),
  appTsConfig: resolveApp('tsconfig.json'),
  appJsConfig: resolveApp('jsconfig.json'),
  yarnLockFile: resolveApp('yarn.lock'),
  testsSetup: resolveModule(resolveApp, 'src/setupTests'),
  proxySetup: resolveApp('src/setupProxy.js'), //这里是引用的setupProxy.js文件
  appNodeModules: resolveApp('node_modules'),
  publicUrl: getPublicUrl(resolveApp('package.json')),
  servedPath: getServedPath(resolveApp('package.json')),
};

更多内容请参阅官方传送门

6、代码split,按需加载

这里我们使用react-loadable来进行代码分割,用法如下:

//router.ts
import React from 'react';
import { Route } from 'react-router-dom';
import Loadable from 'react-loadable';
import loading from './loading';

const Home = Loadable({
  loader: () => import('@/routes/home/view'),
  loading,
});
const routes = [
    <Route
      key={'home'}
      path={`/home`}
      render={(props) => <Home {...props} />}
    />,
];

export default routes;

这里在安装的时候遇到一个问题,首先执行:

yarn add @types/react-loadable

会发现我们的引入的地方没有提示找不到该模块了,但是运行却报错:


我们查看一下node_modules,发现实际上node_modules里面并没有安装该模块,仅仅是在@types里面添加了该模块的声明,所以还需要安装一下该模块:

yarn add react-loadable

重新启动即可。

源码github地址

该demo持续更新,所以github上面的代码和文章内的可能会有一些出入,最终以github上的为准,未完待续...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容