一个完整的TypeScript + React + React-Router + Redux + Webpack开发环境的demo

从零开始配置 TypeScript + React + React-Router + Redux + Webpack 开发环境 说在前面的话: 1

本文包含相关资料包-----> 点击直达获取<-------

从零开始配置 TypeScript + React + React-Router + Redux + Webpack 开发环境

说在前面的话:

1、为什么不使用现成的脚手架?脚手架配置的东西太多太重了,一股脑全塞给你,我只想先用一些我能懂的库和插件,然后慢慢的添加其他的。而且自己从零开始配置也能学到更多的东西不是么。

2、教程只配置了开发环境,并没有配置生产环境。

3、教程针对人群是有过 React + Redux 经验,并且想在新项目中使用 TypeScript 的人(或者是想自己从零开始配置开发环境的)

4、因为前端发展日新月异,今天能用的配置到明天可能就不能用了(比如 React-Router 就有 V4 了,而且官方说是完全重写的),所以本文中安装的包都是指定版本的。

5、教程遵循最小可用原则,所以能不用的库和插件就没用(主要是会的就没多少,怕用出问题,逃~~)。

6、基于 5,所以教程不会一开始就把所有东西全装上,会一步一步慢慢来。

7、教程在 macOS 下完成,win 环境系可能会有一些其他的问题。

初始环境

node 版本为 6.9.0

初始化项目

创建并进入项目

mkdir demo && cd demo

初始化项目

npm init

安装初始依赖

首先是安装 webpack 和 webpack-dev-server(全局安装过的请忽略)

npm i -D webpack@3.6.0

然后安装 React 和 Types 中 React 的声明文件

npm i --S react@15.5.4 react-dom@15.5.4 @types/react@15.6.0 @types/react-dom@15.5.0

上面@types 开头的包都是 typeScript 的声明文件,你可以进入 node_modules/@types/XX/index.d.ts 进行查看。

关于声明文件的具体介绍可以在 GitHub 上的 DefinitelyTyped 库看到。

接下来安装 TypeScript,ts-loader 和 source-map-loader

npm i -D typescript@2.5.3 ts-loader@2.3.7 source-map-loader@0.2.2

ts-loader 可以让 Webpack 使用 TypeScript 的标准配置文件 tsconfig.json 编译 TypeScript 代码。

source-map-loader 使用任意来自 Typescript 的 sourcemap 输出,以此通知 webpack 何时生成自己的 sourcemaps。 这让你在调试最终生成的文件时就好像在调试 TypeScript 源码一样。

添加 TypeScript 配置文件

我们需要一个 tsconfig.json 文件来告诉 ts-loader 如何编译代码 TypeScript 代码。

在当前根目录下创建 tsconfig.json 文件,并添加如下内容:

``` { "compilerOptions": { "outDir": "./dist/", "sourceMap": true, "noImplicitAny": true, "module": "commonjs", "target": "es5", "jsx": "react" }, "include": [ "./src/* / " ]

```

outDir:输出目录。

sourceMap:把 ts 文件编译成 js 文件的时候,同时生成对应的 sourceMap 文件。

noImplicitAny:如果为 true 的话,TypeScript 编译器无法推断出类型时,它仍然会生成 JavaScript 文件,但是它也会报告一个错误。为了找到错误还是设置为 true 比较好。

module:代码规范,也可以选 amd。

target:转换成 es5

jsx:TypeScript 具有三种 JSX 模式: preserve react react-native 。 这些模式只在代码生成阶段起作用 - 类型检查并不受影响。 在 preserve 模式下生成代码中会保留 JSX 以供后续的转换操作使用(比如: Babel )。 另外,输出文件会带有 .jsx 扩展名。 react 模式会生成 React.createElement ,在使用前不需要再进行转换操作了,输出文件的扩展名为 .js react-native 相当于 preserve ,它也保留了所有的 JSX,但是输出文件的扩展名是 .js 。我们这里因为不会用 babel 再转,所以用 react 就行。

include:需要编译的目录。

写些代码

首先创建目录

mkdir src && cd src mkdir components && cd components

在此文件夹下添加一个 Hello.tsx 文件,代码如下:

``` import * as React from 'react';

export interface Props { name: string; enthusiasmLevel?: number; }

export default class Hello extends React.Component { render() { const { name, enthusiasmLevel = 1 } = this.props;

if (enthusiasmLevel <= 0) {
  throw new Error('You could be a little more enthusiastic. :D');
}

return (
  <div className="hello">
    <div className="greeting">
      Hello {name + getExclamationMarks(enthusiasmLevel)}
    </div>
  </div>
);

} }

function getExclamationMarks(numChars: number) { return Array(numChars + 1).join('!'); } ```

接下来,在 src 下创建 index.tsx 文件,代码如下:

``` import * as React from "react"; import * as ReactDOM from "react-dom";

import Hello from "./components/Hello";

ReactDOM.render( , document.getElementById('root') as HTMLElement ); ```

我们还需要一个页面来显示 Hello 组件。 在根目录创建一个名为 index.html 的文件,如下:

```

demo

```

编写 webpack 配置文件

在根目录下创建一个名为 webpack.common.config.js 文件,并添加一下内容:

``` module.exports = { entry: "./src/index.tsx", output: { filename: "bundle.js", path: __dirname + "/dist" },

devtool: "source-map",

resolve: { extensions: [".ts", ".tsx", ".js", ".json"] },

module: { rules: [ { test: /.tsx?$/, loader: "ts-loader" },

  { enforce: "pre", test: /\.js$/, loader: "source-map-loader" }
]

},

plugins: [ ], }; ```

这里不做过多的解释了。基本上有 webpack 经验的都看得懂。至于为什么是 webpack.common.config.js 而不是 webpack.config.js。是因为我们现在要配置的是开发环境,以后需要配置生产环境,所以我们就需要多个配置文件,并且将这两个的通用部分放入到 webpack.common.config.js

在根目录下运行一下命令:

webpack --config webpack.common.config.js

然后打开 index.html 就能看到我们写的页面了。

编写 webpack 开发环境配置文件

如果是正式做开发,上面的代码肯定是不够的,我们需要 webpacl-dev-server 提供的最基本也是最好用的热更新功能。

npm i -D webpack-dev-server@2.9.1

在根目录下创建 webpack.dev.config.js,并添加以下配置:

const webpack = require('webpack'); const config = require('./webpack.common.config'); config.devServer = { hot: true, publicPath: '/dist/' } config.plugins.push(new webpack.HotModuleReplacementPlugin()); module.exports = config

首先需要引入公共的配置,然后在基础之上进行修改。

devServer 就是 webpack-dev-server 的配置项。

hot:开启热更新,开启热更新之后,我们需要在 plugins 中加入 webpack.HotModuleReplacementPlugin 来完全启用。同时官方文档中指出,如果在命令中使用--hot 来启动 webpack-dev-server 的话,就会自动加载这个插件,不再需要在 config.js 中进行引入。

关于 HMR 的相关部分可以点击 webpack HMR 查看。

publicPath:资源目录,因为 webpack-dev-server 启动之后会把编译后的资源放在内存当中,那这些资源在哪呢?就是在 publicPath 指定的目录里,因为我们在 webpack.common.config.js 中配置的 output.path 是当前目录的/dist 目录下,为了不再去更改根目录下的 index.html 文件,所以我们这里也设置成/dist/。 这部分内容具体可以参照 详解 Webpack2 的那些路径

运行命令:

webpack-dev-server --config webpack.dev.config.js

打开网页,进入 localhots:8080 就可以看到我们的页面了。打开浏览器的开发者工具,在 console 部分能看到以下两句提示就说明热更新启动成功了,

然后把这部分很长的命令加入到 npm scripts。在 package.json 的 scripts 下添加 "start": "webpack-dev-server --config webpack.dev.config.js"

输入 npm start 就可以开启我们的服务了。

添加一个简单的 redux(非新手向)

安装 redux 的依赖

npm i -S redux@3.7.2 react-redux@5.0.5 @types/react-redux@5.0.6

为了能体现 redux,我们接下来给我们的网页添加两个按钮来增加/删除文字后面的感叹号。

首先,我们来创建一个文件来存放 store 的接口声明,放入 src/types/index.tsx 中,代码如下:

export interface StoreState { languageName: string; enthusiasmLevel?: number; }

定义一些常量供 action 和 reducer 使用,放入 src/constants/index.tsx

``` export const INCREMENT_ENTHUSIASM = 'INCREMENT_ENTHUSIASM'; export type INCREMENT_ENTHUSIASM = typeof INCREMENT_ENTHUSIASM;

export const DECREMENT_ENTHUSIASM = 'DECREMENT_ENTHUSIASM'; export type DECREMENT_ENTHUSIASM = typeof DECREMENT_ENTHUSIASM; ```

添加 action,放入 src/actions/index.tsx

``` import * as constants from '../constants'

export interface IncrementEnthusiasm { type: constants.INCREMENT_ENTHUSIASM; }

export interface DecrementEnthusiasm { type: constants.DECREMENT_ENTHUSIASM; }

export type EnthusiasmAction = IncrementEnthusiasm | DecrementEnthusiasm;

export function incrementEnthusiasm(): IncrementEnthusiasm { return { type: constants.INCREMENT_ENTHUSIASM } }

export function decrementEnthusiasm(): DecrementEnthusiasm { return { type: constants.DECREMENT_ENTHUSIASM } } ```

添加 reducer,放入 src/reducers/index.tsx

``` import { EnthusiasmAction } from '../actions'; import { StoreState } from '../types/index'; import { INCREMENT_ENTHUSIASM, DECREMENT_ENTHUSIASM } from '../constants/index';

export function enthusiasm(state: StoreState, action: EnthusiasmAction): StoreState { switch (action.type) { case INCREMENT_ENTHUSIASM: return { ...state, enthusiasmLevel: state.enthusiasmLevel + 1 }; case DECREMENT_ENTHUSIASM: return { ...state, enthusiasmLevel: Math.max(1, state.enthusiasmLevel - 1) }; } return state; } ```

修改一下 Hello 组件,以下是修改后的代码:

``` import * as React from 'react';

export interface Props { name: string; enthusiasmLevel?: number; onIncrement?: () => void; onDecrement?: () => void; }

export default function Hello({ name, enthusiasmLevel = 1, onIncrement, onDecrement }: Props) { if (enthusiasmLevel <= 0) { throw new Error('You could be a little more enthusiastic. :D'); }

return (

Hello {name + getExclamationMarks(enthusiasmLevel)}
); }

function getExclamationMarks(numChars: number) { return Array(numChars + 1).join('!'); } ```

此时我们页面已经修改成功了,但点击没有反应,因为我们还没有连接到 redux 的 store 中。

添加一个 container 来链接 Hello 组件,放入 src/containers/Hello.tsx 中

``` import Hello from '../components/Hello'; import * as actions from '../actions/'; import { StoreState } from '../types/index'; import { connect, Dispatch } from 'react-redux';

export function mapStateToProps({ enthusiasmLevel, languageName }: StoreState) { return { enthusiasmLevel, name: languageName, } }

export function mapDispatchToProps(dispatch: Dispatch ) { return { onIncrement: () => dispatch(actions.incrementEnthusiasm()), onDecrement: () => dispatch(actions.decrementEnthusiasm()), } }

export default connect(mapStateToProps, mapDispatchToProps)(Hello); ```

创建一个 initState,来定义 store 初始的值,放入/src/store/initState.tsx 中

export default { enthusiasmLevel: 1, languageName: 'TypeScript', }

创建一个 store,放入/src/store/configureStore.tsx 中

import { createStore } from 'redux'; import { enthusiasm } from '../reducers/index'; import { StoreState } from '../types/index'; import initState from './initState'; export default function () { const store = createStore<StoreState>(enthusiasm, initState); return store; }

最后修改一下入口文件 index.tsx

``` import * as React from "react"; import * as ReactDOM from "react-dom"; import Hello from './containers/Hello'; import { Provider } from 'react-redux'; import configureStore from './store/configureStore';

const store = configureStore(); ReactDOM.render( , document.getElementById('root') as HTMLElement ); ```

至此,一个简单的 redux 就弄好了。可以点击按钮增加/删除感叹号了。

但是现在还有很多不完善的地方,比如 Hello 组件竟然是一个函数,再比如 reducer 竟然只有一个(解决这两个问题的过程中会有一些 bug 待我们解决)。

放心,这些都将在下面的“添加一个够用的 Redux”中解决。

添加一个够用的 Redux

很明显,一个简单的 redux 在我们稍微大一点的开发中是明显不够用的。

所以我们来改写一下我们的代码。

首当其冲的就是我们的 Hello 组件。我们把 Hello 组件改成 class 的形式

``` export default class Hello extends React.Component { constructor(props: Props) { super(props); } render() { const { name, enthusiasmLevel = 1, onIncrement, onDecrement } = this.props;

if (enthusiasmLevel <= 0) {
  throw new Error('You could be a little more enthusiastic. :D');
}

return (
  <div className="hello">
    <div className="greeting">
      Hello {name + getExclamationMarks(enthusiasmLevel)}
    </div>
    <div>
      <button onClick={onDecrement}>-</button>
      <button onClick={onIncrement}>+</button>
    </div>
  </div>
);

} } ```

保存,编译中,然后就报错了!

------------------------------------------------------------------------------------------------

ERROR in ./src/containers/Hello.tsx (20,61): error TS2345: Argument of type 'typeof Hello' is not assignable to parameter of type 'ComponentType<{ enthusiasmLevel: number; name: string; } & { onIncrement: () => IncrementEnthusia...'. Type 'typeof Hello' is not assignable to type 'StatelessComponent<{ enthusiasmLevel: number; name: string; } & { onIncrement: () => IncrementEnt...'. Type 'typeof Hello' provides no match for the signature '(props: { enthusiasmLevel: number; name: string; } & { onIncrement: () => IncrementEnthusiasm; onDecrement: () => DecrementEnthusiasm; } & { children?: ReactNode; }, context?: any): ReactElement '.

------------------------------------------------------------------------------------------------

赶紧复制,然后 Google 一下,就能找到我们要的答案 TypeScript-React-Starter | Issues#29 ,从别人的回答来看貌似是一个 bug?那我们就按回答来更改一下我们的 Hello 容器

``` export function mergeProps(stateProps: Object, dispatchProps: Object, ownProps: Object) { return Object.assign({}, ownProps, stateProps, dispatchProps); }

export default connect( mapStateToProps, mapDispatchToProps, mergeProps)(Hello); ```

刚改完,还没等保存,IDE 就提醒我们有一个错误:

Property 'assign' does not exist on type 'ObjectConstructor'.

很明显,是因为 Object 没有 assign 这个方法,有三种解决方式:

1、安装 Object-assign 这个 npm 包,用这个包去替代。

2、在 tsconfig.json 中把 target 从"es5"修改为"es6"。

3、在 tsconfig.json 的 compilerOptions 中添加属性"lib": [

"dom",

"es6",

"dom.iterable",

"scripthost"

]

(推荐,感谢大佬同事提供的解决方法)

第三个方法中的 lib 就是你写代码用的语言的标准,如果要用 async 的话,还可以加一个 es7

然后再次编译,发现还是依旧报错。只不过这次错误信息改了:

------------------------------------------------------------------------------------------------

ERROR in ./src/index.tsx (10,5): error TS2322: Type '{}' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<Pick<Props, "name" | "enthusiasmLevel" |...'. Type '{}' is not assignable to type 'Readonly & Object>'. Property 'name' is missing in type '{}'.

------------------------------------------------------------------------------------------------

这次的报错是在 index.tsx 中,可以看到是因为在 Hello 组件我们定义的接口中 name 的属性是必须传的,但是在 index.tsx 中没有显示的传过去。

但是如果你的浏览器是 Chrome 并安装了 react 插件的话可以看到编译后的代码是有传的

姑且就当做是一个 bug 吧,解决方案有两种,一种是的 index.tsx 中给 Hello 容器加上一个 name

ReactDOM.render( <Provider store={store}> <Hello name="123"/> </Provider>, document.getElementById('root') as HTMLElement );

这里就算加上 name 也还是直接显示的 store 中的 name。所以我们这里采用这种方式,并且后面加上 React-router 之后这段代码就会改掉,就不会有这个问题了。

另一种是在 Hello 组件中把 name 属性改成非必要属性:

export interface Props { name?: string; enthusiasmLevel?: number; onIncrement?: () => void; onDecrement?: () => void; }

这种方式不推荐。

好了,到现在为止组件更改完成了。

接下来我们解决多个 reducer 的问题。

首先我们把 initState 的默认值更改一下:

export default { demo: { enthusiasmLevel: 1, languageName: 'TypeScript', } }

当然还有/src/types/index.tsx 也要更改:

export interface demo { languageName: string; enthusiasmLevel?: number; } export interface StoreState { demo: demo; }

然后讲/src/reducers/index.tsx 命名为 demo.tsx,并对内容进行修改:

import { EnthusiasmAction } from '../actions'; import { demo } from '../types/index'; import { INCREMENT_ENTHUSIASM, DECREMENT_ENTHUSIASM } from '../constants/index'; import initState from '../store/initState'; export function enthusiasm(state: demo = initState.demo, action: EnthusiasmAction): demo { switch (action.type) { case INCREMENT_ENTHUSIASM: return { ...state, enthusiasmLevel: state.enthusiasmLevel + 1 }; case DECREMENT_ENTHUSIASM: return { ...state, enthusiasmLevel: Math.max(1, state.enthusiasmLevel - 1) }; } return state; }

其实就是把对应的接口类型进行了更改,并给 state 添加了默认值。

然后新建一个 index.tsx 文件,并添加以下内容:

``` import { combineReducers } from 'redux'; import { enthusiasm } from './demo'; const rootReducer = combineReducers({ demo: enthusiasm });

export default rootReducer; ```

相对应的,也需要修改 Hello 容器中的引用的值:

export function mapStateToProps({ demo: { enthusiasmLevel, languageName } }: StoreState) { return { enthusiasmLevel, name: languageName, } }

最后修改一下 configureStore 中的引用的 reducer:

import { createStore } from 'redux'; import reducers from '../reducers/index'; import { StoreState } from '../types/index'; import initState from './initState'; export default function () { const store = createStore<StoreState>(reducers, initState); return store; }

更改完毕,保存。报错...

------------------------------------------------------------------------------------

ERROR in ./src/store/configureStore.tsx (6,41): error TS2345: Argument of type 'Reducer<{}>' is not assignable to parameter of type 'Reducer '. Type '{}' is not assignable to type 'StoreState'. Property 'demo' is missing in type '{}'.

------------------------------------------------------------------------------------

是不是感觉很熟悉,和之前 index.tsx 中关于 Hello 组件的报错几乎一样。

这里也有两种解决方案,一种是找到 configureStore.tsx 中的 const store = createStore (reducers, initState); 把 泛型删除。

第二种是和之前一样的,找到/src/types/index.tsx,将 demo: demo; 加上一个?使之变为非必须的属性 demo?: demo; 我们这里就采用这种方法。

这里究竟是 bug 还是其他什么原因,希望有大神能解答。

至此,我们够用的 redux 就已经完成了。

添加一个 React-Router

需要注意的是,现在 react-router 已经到了 V4 版本了,并且官方说这是一个完全重写的版本。所以在我不太熟悉的情况下,保险起见还是先选择 V3 版本,等以后再更新。

安装依赖

npm i -S react-router@3.0.5 @types/react-router@3.0.5

在 src 目录下创建文件 routers.tsx,并添加以下内容:

``` import * as React from 'react'; import { Route, IndexRoute } from 'react-router'; import Hello from './containers/Hello';

export default ( ); ```

为了显示路由的作用,就加了一个 demo 路径。

然后在 index.tsx 中加上我们的路由

import * as React from "react"; import * as ReactDOM from "react-dom"; import { Provider } from 'react-redux'; import configureStore from './store/configureStore'; import { Router, browserHistory } from 'react-router'; import routes from './routes'; const store = configureStore(); ReactDOM.render( <Provider store={store}> <Router history={browserHistory} routes={routes} /> </Provider>, document.getElementById('root') as HTMLElement );

由于我们添加的是 browserHistory 作为路由,不是 hashHistory,所以我们需要对服务器做一些路由配置才行。至于为什么,请自行搜索,这里不做说明了。如果不想用过多设置,也可以直接把 browserHistory 替换为 hashHistory 即可。

这里我们的开发服务器就是 webpack-dev-server,所以我们对 webpack.dev.congfig.js 进行更改:

const webpack = require('webpack'); const config = require('./webpack.common.config'); config.devServer = { hot: true, publicPath: '/dist/', historyApiFallback: { index: './index.html' }, } config.plugins.push(new webpack.HotModuleReplacementPlugin()); module.exports = config;

其实就是当服务器找不到路由目录时将页面指向 index.html 即可。

因为更改了配置,所以我们需要重启服务器 npm start

进入 localhost:8080/demo

页面有显示 Hello 组件,说明配置成功了。

添加 React-Router-Redux

这里同样由于 React-Router 版本大更新的问题,所以也要严格控制版本。

安装依赖

npm i -S react-router-redux@4.0.8 @types/react-router-redux@4.0.48

更改 index.tsx 代码如下:

``` import * as React from "react"; import * as ReactDOM from "react-dom"; import { Provider } from 'react-redux'; import configureStore from './store/configureStore'; import { Router, browserHistory } from 'react-router'; import routes from './routes'; import { syncHistoryWithStore } from 'react-router-redux';

const store = configureStore(); const history = syncHistoryWithStore(browserHistory, store);

ReactDOM.render( , document.getElementById('root') as HTMLElement ); ```

然后在 src/reducers/index.tsx 中添加上 routerReducer

``` import { combineReducers } from 'redux'; import { routerReducer } from 'react-router-redux'; import { enthusiasm } from './demo'; const rootReducer = combineReducers({ demo: enthusiasm, routing: routerReducer });

export default rootReducer; ```

OK,非常简单。

添加开发必备配置

本来是不打算写一这部分的,毕竟整个基础环境搭下来就只剩 loader 部分没有写了,而 loader 配置基本上 GitHub 对应的库上都有写。

但是我自己装载 loader 的时候遇到了一些问题,所以这里写出来,避免更多的人踩坑了。

css-loader 配置

安装依赖

npm i -D css-loader@0.28.7 style-loader@0.19.0

css-loader 用来加载 CSS 文件,style-loader 用来把加载好的文件放入 HTML 中的 style 标签里,所以这两个 loader 必须配合使用。

编写匹配规则,在 webpack.common.config.js 中的 module.rules 中添加如下规则:

{ test: /\.css$/, loader: 'style-loader!css-loader' }

然后我们创建/src/components/hello.css,并输入以下内容:

.hello{ background:#000; } .greeting{ color:#fff; }

然后在/src/components/Hello.tsx 中引入:

import './hello.css';

ok,到目前为止我们的 loader 还没有出现问题。

但是你以为这样就结束了么?那是不可能的,不然我写这部分的目的是什么,手动滑稽。

现在我们想要用 CSS modules,不知道什么是 CSS modules 的请点击 CSS Modules 用法教程

所以把 webpack.common.config.js 中刚刚添加的规则修改成以下内容:

{ test: /\.css$/, loader: "style-loader!css-loader?modules" }

然后更改下/src/components/Hello.tsx 对 CSS 的引用

import style from "./hello.css";

然后重新编译,报错 TS2307: Cannot find module './hello.css'.

什么鬼?找不到 CSS?惊喜不惊喜?

顺手把问题往谷歌一丢,就能找到别人的也碰到了这个问题,下面也有一些解决方案 在这

看下来大概就是有两种解决方案:

1、使用 typed-css-modules 解决。

2、使用 require 的方式。

我毫不犹豫的选择了第二种,因为第一种只能解决 CSS 的引入问题。那如果我要引入图片文件呢?所以最终还是要用 require。

这部分如果有其他解决方案的话,请大神告诉我一下,不甚感激。

这个报错是 typescript 报错,我们只需要在 src 目录下增加 index.d.ts 文件即可,内容如下:

declare module '*.scss' { const content: any export default content }

接下来我们改一下/src/components/Hello.tsx 中的引入方式:

const style = require('./hello.css');

如果你是一步步跟着来的话,应该会碰到和我一样的报错:error TS2304: Cannot find name 'require'.

这是因为我们没有引入@types/node 声明文件,所以我们需要安装一下。

npm i -D @types/node@8.0.34

重新编译一下,终于没有问题了。

接下来继续修改/src/components/Hello.tsx,将 class 部分修改成 CSS modules 的模式

```

Hello {name + getExclamationMarks(enthusiasmLevel)}

```

保存,ok,样式生效。

需要注意的是,如果我们引入了其他的组件库,比如 antd 的话,就不能这样直接使用 CSS modules,如果想要使用必须配置如下两条规则:

{ test: /\.css$/, loader: "style-loader!css-loader", include: /node_modules/ }, { test: /\.css$/, loader: "style-loader!css-loader?modules", exclude: /node_modules/ },

意思应该也都懂了,使用 modules 的时候需要排除 node_modules 里引入的那些库。

file-loader 配置

安装依赖

npm i -D file-loader@1.1.5

在 webpack.common.config.js 的 module.rules 中添加规则:

{ test: /\.(png|jpe?g|gif)/, loader: "file-loader" }

然后把在 src 下创建文件夹 img,并放入一张图片,我这里是 x.png

在/src/components/Hello.tsx 中引入图片,并在 JSX 中添加一个 img 标签,同样也需要用 require 引入

``` //引入 const imgX = require('../img/x.png');

//JSX部分

Hello {name + getExclamationMarks(enthusiasmLevel)}
imgX

```

保存,进入 localhost:8080,然而图片并没有出现。

但是如果我们审查元素的话是能看到有 img 这个元素的,也就是说引入的位置出了问题。我们之前说过,webpack-dev-server 编译出来的文件是在内存中的,并且目录是/dist/,但是我们可以很明显的看到我们 img 的 src 值是没有/dist/目录的

所以,我们需要在 webpack.common.config.js 的 output 中再加一条属性 publicPath:'/dist/' ,这个属性的具体含义请看 详解 Webpack2 的那些路径

重新编译,ok,图片显示出来了。

其他的常用 loader,比如 babel,postcss 我添加的时候没有遇到什么问题,所以就不贴出来了,如果有人反馈,我再解答吧。

按需加载

typescript + react-router + webpack 实现按需打包/加载

结束语

如果在 React-Router 和 React-Router-Redux 的配置中有什么报错,基本上是 npm 包的版本问题,请删除 node_modules 并按照我指定的版本重新安装。

总结:安装过程中确实碰到了各种各样的问题,尤其是 Router 的包附带的 history 版本问题,弄了很久。看似很简单的教程,背后有我踩过无数的坑,不过好在还是完成了。

之后还要继续集成 ant-design,以及生产环境的配置,这些都将会在本教程继续更新。

转载于 从零开始配置 TypeScript + React + React-Router + Redux + Webpack 开发环境

参考文献

  • Ajax设计模式下Web开发的研究与应用(大连海事大学·白璐)
  • 一种Web应用框架的设计与实现(·河北师范大学)
  • 基于Vue的前端开发框架的设计与实现(山东大学·徐鹏涛)
  • 一种Web应用框架的设计与实现(·河北师范大学)
  • 敏捷开发任务管理系统的设计与实现(华中科技大学·张洪阳)
  • 基于SSH框架和ExtJs框架的资质管理系统的设计与实现(云南大学·职辉)
  • 基于Vue.js的开发平台的设计与实现(广东工业大学·王志任)
  • 企业项目管理系统设计与实现(吉林大学·刘舒杨)
  • 基于J2EE的JCup框架集成与应用(西南交通大学·范强)
  • 基于SSH框架和ExtJs框架的资质管理系统的设计与实现(云南大学·职辉)
  • 敏捷开发任务管理系统的设计与实现(华中科技大学·张洪阳)
  • SSH2与ExtJS框架结合的应用研究(辽宁科技大学·韩国敬)
  • 基于平台无关模型到iOS平台相关模型的转换研究与实现(电子科技大学·李滨)
  • 基于MVC设计模式的Web应用框架研究及其实例(吉林大学·王耀辉)
  • 基于TypeScript的前端MVVM框架的设计与研究(北京邮电大学·樊鼎威)

本文内容包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主题。发布者:毕设货栈 ,原文地址:https://bishedaima.com/yuanma/35804.html

相关推荐

发表回复

登录后才能评论