手把手用typescript写React项目

最近写了一个基于React的UI库项目,本意是希望使用TypeScript去写。正好Webpack4.x也没有配过,正好能看一下比起webpack3的配置有哪些区别。这次的教程就主要写下创建项目的顺序,还有尿点。
首先创建一个文件夹,然后使用yarn创建项目和package.json文件

1
yarn init

接下去就是各种回车和输入,知道package.json文件创建完成。

接着创建目录
-build
|-webpack.base.js
|-webpack.dev.js
|-webpack.prd.js
-src
|-component
|-style
|-main.tsx
-index.html
-index.js

index.html: 模板文件
index.js: npm插件通过import引入的入口文件,配合package.json里的main字段使用,注意路径
src-component: react的组件目录
src-style: 存放scss样式文件
main.tsx: typescript的入口文件

然后我们需要webpack进行打包

1
yarn add -D webpack webpack-dev-middleware webpack-hot-middleware@next

配置webpack.base.js这个开发和生产环境都需要的各种loader和通用配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
module.exports = {
// 开发端口
devPort: 3000,
// 关联文件类型
resolve: {
extensions: ['.ts', '.tsx', '.js', '.scss', '.css', '.png', '.gif', '.jpg']
},
// loader
rules: [{
test: /\.(eot|svg|ttf|woff|woff2)$/,
loader: 'file-loader?name=fonts/[name].[ext]'
},
{
test: /\.(png|jpg|gif)$/,
loader: process.env.NODE_ENV=='prd' ?
ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader!postcss-loader!sass-loader"
})
:
'url-loader?limit=8192&name=images/[name].[ext]'
},
{
test: /\.css$/,
loader: "style-loader!css-loader"
},
{
test: /\.(scss|sass)$/,
loader: "style-loader!css-loader!postcss-loader!sass-loader"
},
{
test: /\.js?$/, //表示要变异的文件的类型,这里要编译的是js文件
loader: 'babel-loader', //装载的哪些模块
exclude: /node_modules/, //标示不变异node_modules文件夹下面的内容
query: { //具体的编译的类型,
compact: false, //表示不压缩
}
},
{
test: /\.tsx$/,
enforce: 'pre',
loader: ['awesome-typescript-loader', 'tslint-loader']
}
]
}

安装各种loader到依赖里,还有babel,autoprefixer和typescript的支持

1
yarn add -D autoprefixer awesome-typescript-loader babel-core babel-preset-react babel-loader babel-polyfill babel-preset-env babel-preset-typescript css-loader file-loader node-sass postcss-loader sass-loader style-loader ts-loader typescript url-loader

安装完成后,需要在根目录下配置TypeScript配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// tsconfig.json
{
"compilerOptions": {
"sourceMap": true,
"allowJs": true,
"module": "commonjs",
"removeComments": true,
// 可以使用 Promise, async, await等新语法
"target": "es2017",
"jsx": "react"
},
"exclude": [
"node_modules" // 这个目录下的代码不会被 typescript 处理
],
"include": [
"./src/**/*"
]
}

autoprefixer的配置文件,需要依赖postcss-loader,已安装,在根目录下创建

1
2
3
4
5
6
// postcss.config.js
module.exports = {
plugins: [
require('autoprefixer')({ browsers: ['iOS>7', 'Android>4'] })
]
};

我们还需要使用tslint规则来检验代码的统一性和可读性

1
yarn add -D tslint tslint-loader tslint-react

然后再根目录下创建tslint.json文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
{
"extends": [
"tslint:latest",
"tslint-react"
],
"rules": {
// 是否允许使用字符串直接访问object的属性
"no-string-literal": false,
// 检查分号,忽略interfaces
"semicolon": [
true,
"always",
"ignore-interfaces"
],
// 引号,双单使用,一般使用单引号,jsx里使用双引号
"quotemark": [
true,
"single",
"jsx-double",
"avoid-template",
"avoid-escape"
],
// 4个空格代替tab键
"indent": [
true,
"spaces",
4
],
// 禁止出现重复的import
"no-duplicate-imports": true,
// 禁止一个文件中出现多个相同的 namespace
"no-mergeable-namespace": true,
// 文件类型必须时 utf-8
"encoding": true,
// import 语句中,关键字之间的间距必须是一个空格
"import-spacing": true,
// 接口可以 implement extend 和 merge
"interface-over-type-literal": true,
// new后面要有空格
"new-parens": true,
// 禁止debugger
"no-debugger": false,
// 禁止行尾有空格
"no-trailing-whitespace": false,
// 禁止无用的表达式
"no-unused-expression": true,
// 禁止未使用的变量
"no-unused-variable": true,
// 不能未申明就使用
"no-use-before-declare": true,
// 不能使用var
"no-var-keyword": true,
// interface的命名不用必须带大写“I”
"interface-name": [
true,
"never-prefix"
],
"no-console": ["error", { "allow": ["warn", "error"] }]
}
}

babel语法的支持也需要在根目录下创建.babelrc文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// .babelrc
{
"presets": [
[
"env",
{
"targets": {
"browsers": [
"last 2 versions",
"safari >= 7"
]
}
}
],
[
"react"
],
[
"typescript"
]
],
"plugins": [
"react-hot-loader/babel"
]
}

配置webpack的开发配置文件
要安装依赖先,还需要支持热更新

1
yarn add -D html-webpack-plugin react-hot-loader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// build/webpack.dev.js
var path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var autoprefixer = require('autoprefixer');
var global = require('./webpack.base.js');
module.exports = {
// Enable sourcemaps for debugging webpack's output.
devtool: "source-map",
entry: {
app: [
// `webpack-dev-server/client?http://localhost:8080/`,
'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=10000&reload=true', // 热更新
'babel-polyfill',
'webpack-hot-middleware/client',
path.join(__dirname, '../src/main.tsx')
]
},
output: { //输出
path: path.join(__dirname, '../dist'),
filename: "bundle.js",
publicPath: '/'
},
mode: "development", // webpack4.x后需要加入的新选项,必须写
performance: {
hints: false
},
module: {
rules: global.rules
},
resolve: global.resolve,
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
inject: 'body',
template: path.join(__dirname, '../index.html') //模板地址
}),
// OccurenceOrderPlugin is needed for webpack 1.x only
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
// Use NoErrorsPlugin for webpack 1.x
new webpack.NoEmitOnErrorsPlugin()
],
cache: true
}

配置webpack.prd.js文件,因为要把css分离出来单独从外部引入,所以需要安装插件

1
yarn add -D extract-text-webpack-plugin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
var path = require('path');
var webpack = require('webpack');
var autoprefixer = require('autoprefixer');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
devtool: 'cheap-module-eval-source-map',
entry: {
vendor: ['react', 'react-dom'],
app: [
'babel-polyfill',
path.join(__dirname, '../src/main.tsx')
]
},
//入口
output: { //输出
path: path.join(__dirname, '../public'),
filename: "cui.min.js",
publicPath: './'
},
mode: "production",
module: {
rules: global.rules
},
resolve: global.resolve,
plugins: [
new webpack.LoaderOptionsPlugin({
options: {
postcss: [
autoprefixer(),
]
}
}),
new ExtractTextPlugin({
filename: './cui.min.css'
}),
],
// 分片及vendor提取
optimization: {
splitChunks: {
cacheGroups: {
commons: {
chunks: 'initial',
minChunks: 2,
maxInitialRequests: 5,
minSize: 0
},
vendor: {
test: /node_modules/,
chunks: 'initial',
name: 'vendor',
priority: 10,
enforce: true
}
}
}
},
cache: true
}

然后编辑入口文件,因为使用了TypeScript来编写文件,react之前并未使用TypeScript编写,所以不支持,那我们在安装依赖时需要多安装type包

1
yarn add react react-dom @types/react @types/react-dom

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// src/app.tsx
import * as React from 'react';
import Input from './component/Input';
class App extends React.Component<{}, {}> {
public render() {
return (
<Input text="Hello world!!" />
);
}
}
export default App;
// src/component/Input.tsx
import * as React from 'react';
interface HelloText { text: string; }
interface ReactState { name: any; }
class Input extends React.Component<HelloText, ReactState> {
constructor(props) {
super(props);
this.state = {
name: 'King',
};
this.onClickHandler = this.onClickHandler.bind(this);
}
public onClickHandler(): void {
this.setState({
name: 'Tina',
});
}
public render() {
return (
<div>
<h1>First Demo</h1>
<div>Props: {this.props['text']}</div>
<div>state: {this.state['name']}</div>
<button
onClick={this.onClickHandler}
>
click me!
</button>
</div>
);
}
}
export default Input;
// src/main.tsx
import * as React from 'react';
import * as ReactDom from 'react-dom';
import App from './app';
import './style/index.scss';
ReactDom.render(<App />, document.getElementById('app'));
if (module.hot) {
module.hot.accept();
}

鉴于TypeScript的严格模式,热更新需要在main文件中监听webpack传入的module,但是这个变量在目前环境中拿不到,所以我们还需要一个支持types的webpack插件

1
yarn add -D @types/webpack-env

1
2
3
4
5
6
7
// 在if(module.hot)前对module定义下
declare var module: __WebpackModuleApi.Module;
if (module.hot) {
module.hot.accept();
}

总结

文档先写到这,如果有不清晰的,我会再补充,详细的项目地址可以看这里

© 2018 Qing的前端开发Blog All Rights Reserved. 本站访客数人次 本站总访问量
Theme by hiero