资源
- React-16.8.*
- react-router-dom-4.3.*
- TypeScript-3.5.*
- webpack-4.*
- eslint-5.16.*
项目目录
├── dist # 打包结果目录
│ ├── demo1 // 类别 demo1 的打包结果
│ │ ├── demo1.himl
│ │ ├── demo1.js
│ │ └── demo1.css
│ └── demo2 ... // 类别 demo2 的打包结果
├── src # 业务资源文件目录
│ ├── category // 项目分类
│ │ ├── demo1
│ │ ├── demo2
│ │ └── ...
│ ├── components // 公共组件
│ ├── util // 公共资源
│ └── custom.d.ts // 项目全局变量声明文件
├── index.html // 项目启动入口
├── .gitignore //git 忽略文件
├── .eslintrc.js //eslint 校验配置
├── package.json // 依赖包
├── tsconfig.json //ts 配置
├── webpack.config.build.js //webpack 打包
├── webpack.config.base.js //webpack 基础配置
└── webpack.config.js // 项目启动配置
前言
对于复杂或多人开发的 React
项目来说,管理和使用每个组件的 props
、state
或许会成为一件让人头痛的事情,而为每一个组件写文档,成本也会比较大,对项目的开发效率也不是最理想的。
Typescript
给 React
带来很多好处:
- 在组件头部定义
interface
,让每个人在拿到组件的第一时间就可以很明确知道该组件需要使用的props
和state
; - 在编译中发现问题,减少运行时的报错;
- 可以在编辑器中实现实时类型校验、引用查询;
- 约束类型,在混合多语言环境中降低风险,等。
需求
要搭建一个 React+TypeScript+webpack 的项目的话,一般都是团队开发多人多文件项目,在搭建之前需要优先考虑以下几个方面:
- 开发体验
- 项目打包
- 团队规范
安装
-
前置安装
首先需要全局安装 typescript,这里默认大家都已经安装了 node 以及 npmnpm install -g typescript
-
首先新建文件夹并进入
mkdir tsDemo && cd tsDemo
-
然后进行初始化,生成
package.json
和tsconfig.json
npm init -y && tsc --init
-
安装开发工具
npm install-D webpack webpack-cli webpack-dev-server
-
安装 react 相关
因为需要整合 ts,而 react 原本的包是不包含验证包的,所以这里也需要安装相关 ts 验证包npm install -S react react-dom npm install -D @types/react @types/react-dom
-
安装
ts-loader
npm install -D ts-loader
- 以上是基本的 后续会贴出项目 demo 里面包含所有依赖包
webpack 配置
添加 webpack 文件
根目录下新建 webpack.config.base.js、webpack.config.build.js、webpack.config.js 文件
touch webpack.config.base.js webpack.config.build.js webpack.config.js
-
entry
:入口文件(你要打包,就告诉我打包哪些) -
output
:出口文件(我打包完了,给你放到哪里) -
resolve
: 寻找模块所对应的文件 -
module
:模块(放 lorder,编译浏览器不认识的东西) -
plugins
:插件(辅助开发,提高开发效率) -
externals
: 打包忽略 -
devServer
:服务器(webpack 提供的本地服务器) -
mode
:模式,分为开发模式、生产模式。此为 4.X 里新增的
配置 entry 入口文件
因为大部分项目是多入口,多类别的,所有入口配置时不要配置单一入口
const fs = require("fs");
const path = require("path");
const optimist = require("optimist");
const cateName = optimist.argv.cate;
let entryObj = {};
const srcPath = `${__dirname}/src`;
// 获取当前项目要启动或者打包的基础路径
const entryPath = `${srcPath}/category/`;
// 未指定类别 启动或者打包所有类别
// 如:npm run dev 或者 npm run build
if (cateName == true) {fs.readdirSync(entryPath).forEach((cateName, index) => {
// cateName/cateName 指定输出路径为 entryname
if (cateName != "index.html" && cateName != ".DS_Store") entryObj[`${cateName}/${cateName}`] = `${entryPath + cateName}/${cateName}.tsx`;
});
} else if (cateName.indexOf(",")) {
// 一次指定多个类别 类别之间以 "," 分割
// 如:npm run dev erhsouche,huoche
let cateNameArray = cateName.split(",");
for (let i = 0; i < cateNameArray.length; i++) {entryObj[`${cateNameArray[i]}/${cateNameArray[i]}`] = `${entryPath + cateNameArray[i]}/${cateNameArray[i]
}.tsx`;
}
} else {
// 打包单个入口文件
// 如:npm run dev ershouche
entryObj[`${cateName}/${cateName}`] = `${entryPath + cateName}/${cateName}.tsx`;
}
const webpackConfig = {entry: entryObj,}
module.exports = {
webpackConfig,
entryObj
};
配置 output 出口文件
const webpackConfig = {
output: {
// 输出文件名称以当前传入的 cate 类别名称命名
filename: "[name].js",
// 输出到根目录下的 dist 目录中
path: path.resolve(__dirname, "dist"),
publicPath: "/",
},
}
配置 resolve
需要 import xxx from 'xxx'
这样的文件的话需要在 webpack 中的 resolve
项中配置extensions
,这样以后引入文件就不需要带扩展名
const webpackConfig = {
resolve: {extensions: [".tsx", ".ts", ".js", ".jsx", ".json"],
// 配置项通过别名来把原导入路径映射成一个新的导入路径。alias: {images: path.join(__dirname, "src/util/img")
},
// 使用绝对路径指明第三方模块存放的位置,以减少搜索步骤
modules: [path.resolve(__dirname, "node_modules")]
},
}
配置 module
概念
在 webpack
中任何一个东西都称为模块,js
就不用说了。一个 css
文件,一张图片、一个 less
文件都是一个模块,都能用导入模块的语法(commonjs
的 require
,ES6
的import
)导入进来。webpack
自身只能读懂 js
类型的文件,其它的都不认识。但是 webpack
却能编译打包其它类型的文件,像 ES6
、JSX
、less
、typeScript
等,甚至 css
、images
也是 Ok 的,而想要编译打包这些文件就需要借助loader
loader
就像是一个翻译员,浏览器不是不认识这些东西么? 那好交给 loader
来办,它能把这些东西都翻译成浏览器认识的语言。loader
描述了 webpack
如何处理非 js
模块,而这些模块想要打包 loader
必不可少,所以它在 webpack
里显得异常重要。loader
跟插件一样都是模块,想要用它需要先安装它,使用的时候把它放在 module.rules
参数里,rules
翻译过来的意思就是规则,所以也可以认为 loader
就是一个用来处理不同文件的规则
所需 loader
-
ts-loader
编译 TypeScript 文件
npm install ts-loader -D
-
url-loader
处理 css 中的图片资源时,我们常用的两种 loader 是 file-loader 或者 url-loader,两者的主要差异在于。url-loader 可以设置图片大小限制,当图片超过限制时,其表现行为等同于 file-loader,而当图片不超过限制时,则会将图片以 base64 的形式打包进 css 文件,以减少请求次数。
npm install url-loader -D
- css 处理所需 loader
css-loader 处理 csssass-loader 编译处理 scss
sass-resources-loader 全局注册变量
- html-loader
处理.html 文件
module 完整配置
const webpackConfig = {
module: {
rules: [
// 处理 tsx 文件
{test: /\.(tsx|ts)?$/, use: ["ts-loader"], include: path.resolve(__dirname, "src") },
// 处理图片资源
{test: /\.(png|jpe?g|jpg|gif|woff|eot|ttf|svg)/,
use: [
// 对非文本文件采用 file-loader 加载
{
loader: "url-loader",
options: {
limit: 1024 * 30, // 30KB 以下的文件
name: "images/[name].[hash:8].[ext]",
}
}
],
},
// 处理 css 和 scss
{test: /\.(css|scss)$/,
use: [
//css 单独打包
MiniCssExtractPlugin.loader,
{loader: "css-loader"},
{
loader: "postcss-loader",
options: {plugins: () => [require("autoprefixer")],
sourceMap: true
}
},
{
loader: "sass-loader",
options: {sourceMap: true}
},
{
loader: "sass-resources-loader",
options: {resources: ["./skin/mixin.scss", "./skin/base.scss"]
}
}
],
exclude: path.resolve(__dirname, "node_modules")
},
{
test: /\.html$/,
use: {loader: "html-loader",}
},
{test: /src\/containers(\/.*).(tsx|ts)/, loader: "bundle-loader?lazy!ts-loader" },
{enforce: "pre", test: /\.js$/, loader: "source-map-loader"}
]
},
}
配置 plugins
plugins
里面放的是插件,插件的作用在于提高开发效率,能够解放双手,让我们去做更多有意义的事情。一些很 low 的事就统统交给插件去完成。
const webpackConfig = {
plugins: [
// 清除文件
new CleanWebpackPlugin(),
//css 单独打包
new MiniCssExtractPlugin({filename: "[name].css",
chunkFilename: "[name].css"
}),
// 引入热更新插件
new webpack.HotModuleReplacementPlugin()]
}
配置 externals
如果需要引用一个库,但是又不想让 webpack 打包(减少打包的时间),并且又不影响我们在程序中以 CMD、AMD 或者 window/global 全局等方式进行使用(一般都以 import 方式引用使用),那就可以通过配置 externals。
const webpackConfig = {
// 项目编译打包是忽略这些依赖包
externals: {
react: "React",
"react-dom": "ReactDOM",
"react-redux": "ReactRedux",
}
}
配置 mode
mode
是 webpack4
新增的一条属性,它的意思为当前开发的环境。mode
的到来减少了很多的配置,它内置了很多的功能。相较以前的版本提升了很多,减少了很多专门的配置
- 提升了构建速度
- 默认为开发环境,不需要专门配置
- 提供压缩功能,不需要借助插件
- 提供
SouceMap
,不需要专门配置
mode
分为两种环境,一种是开发环境(development
), 一种是生产环境(production
)。开发环境就是我们写代码的环境,生产环境就是代码放到线上的环境。这两种环境的最直观区别就是,开发环境的代码不提供压缩,生产环境的代码提供压缩。
配置 devServer
const webpackConfig = {
devServer: {
// 本地服务器所加载的页面所在的目录
contentBase: srcPath,
// 热更新
hot: true,
// 服务端口
port: "7788",
// 是否向 Chunk 中注入代理客户端,默认注入
inline: true,
// publicPath: '/dist/',
historyApiFallback: {index: "template.html",},
// 默认检查 hostname
disableHostCheck: true,
compress: true,
open: true // 自动打开首页
}
}