一、前言
构建React项目一般分为脚手架构建的方式和webpack逐步构建的方式,本期就是对webpack一步一步构建项目进行逐步详细讲解。
二、新建文件夹并进入文件夹
mkdir react-demo
cd react-demo
三、初始化项目
npm init
此步骤会生成 package.json
文件;
四、安装webpack及插件
npm i webpack webpack-cli webpack-dev-server webpack-merge -D
npm i html-webpack-plugin clean-webpack-plugin mini-css-extract-plugin file-loader less-loader css-loader style-loader cross-env -D
1、webpack
Webpack 的主要作用是将各种模块(如 JavaScript、CSS、图片等进行打包和处理,使得项目能够在浏览器中高效运行。它可以实现以下功能:
- 模块打包:将多个模块成一个或多个包,减少网络请求。
- 代码转换:例如将高版本的 ES 语法转换为低版本的 ES 语法,以便在更多浏览器中运行。
- 资源处理:处理各种类型的资源,如图片、字体等。
- 开发服务器:提供开发服务器,支持热更新等功能,方便开发调试。
2、Webpack-cli
Webpack-cli 是在命令行中运行 Webpack 的工具,通过命令行指令来启动和配置 Webpack 的构建过程。
3、Webpack-merge
Webpack-merge 用于将分离的配置进行合并,方便对不同环境(如开发环境和生产环境)的 Webpack 配置进行管理和组织。
4、webpack-dev-server
webpack-server
是 Webpack 提供的一个开发服务器,具有以下主要作用:
- 支持热更新:在开发过程中,代码发生更改时能够自动编译并实时刷新页面,无需手动刷新浏览器。
- 提供开发环境的配置选项,如设置端口、等。
- 可以在浏览器中百分比显示编译进度。
- 支持压缩和不压缩的模式。
- 可以自动打开指定的浏览器,并支持无痕和新窗口等选项。
文件或功能 | 解析器或插件 | 备注 |
---|---|---|
html | html-webpack-plugin | html解析和打包插件 |
css | css-loader | css解析器 |
less | less-loader | less解析器 |
sass | sass-loader | sass解析器 |
file | file-loader | 文件解析器 |
thread | thread-loader | 多线程打包 |
mini-css-extract-plugin | mini-css-extract-plugin | css文件提取插件 |
postcss | postcss | css浏览器兼容处理 |
babel-loader | babel-loader | webpack使用babel |
五、安装React 和 React-Dom
npm i --save react react-dom
六、Babel 的安装配置
Babel 主要用于将高版本的 ES 语法转换为低版本的 ES 语法,以便在更多浏览器中运行。它可以将新的 JavaScript 特性和语法转换为向后兼容的形式,确保代码能够在不支持最新特性的环境中正常工作。
npm i @babel/core core-js @babel/preset-env babel-loader @babel/preset-react @babel/preset-typescript babel-plugin-dynamic-import-node @babel/plugin-transform-runtime @babel/plugin-transform-runtime @babel/plugin-proposal-decorators -D
解析器或插件 | 功能 | 备注 |
---|---|---|
@babel/core | babel核心 | Babel 的核心模块,例如对代码进行语法分析、转换和生成新的代码。在整个 Babel 转码过程中起到关键的支撑作用。 |
babel-loader | webpack中使用babel | 用于在 webpack中使用Babel代码转换的加载器。 |
@babel/preset-env | 语法转化 | 将现代 JavaScript 语法转换为目标浏览器或环境能够理解的语法 |
core-js | JavaScript的模块化标准库 | 在 Babel 配置中用于提供 JavaScript 标准库的功能 |
@babel/preset-react | react转码插件 | 将 React 的特定语法(如 JSX)转换为普通的 JavaScript。 |
@babel/preset-typescript | typescript转码插件 | 用于编译 TypeScript 代码 |
babel-plugin-dynamic-import-node | 将import() 转为 require() | 如果代码中有使用动态导入(import())语法,这个插件可以将其转换为 Node.js 环境中的 require() 函数调用,以便在服务器端(如 Node.js)中使用。 |
@babel/plugin-transform-runtime | JavaScript标准库注入 | 向 JavaScript 标准库注入兼容性方法。 |
@babel/plugin-proposal-decorators | ES7修饰器转码器 | ES7 修饰器(Decorators)是一种实验性的特性,它允许你修改类及其成员的行为。修饰器是一个函数,它可以用来修改类的行为,或者类的属性和方法。修饰器可以在类定义的时候被应用,也可以在类的实例化的时候被应用。 |
1、babel.config.json
配置
/*babel.config.json*/
////Babel 的主要作用是将高版本的语法转换为低版本的ES语法,以便在不支持最新语法的环境中能够正常代码。
{
"comments": false, //注释状态- //表示在编译过程中,Babel 将忽略掉代码中的注释,这可以减少最终输出文件的大小。
"presets": [//定义了一系列的预设
[
"@babel/preset-env", //语法转化-//语法转化-将现代 JavaScript 语法转换为目标浏览器或环境能够理解的语法
{
"targets": {//通过配置"targets"字段,可以指定需要支持的浏览器版本。
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
},
"useBuiltIns": "usage",//让 Babel 在转换时自动使用需要的 polyfill。这样能在保证兼容性的同时,减少不必要的模块引入。
"corejs": 3,//模块化标准库-CoreJS 的版本为 3
"loose": true//表示启用宽松模式。在这种模式下,一些语法转换可能会采用更宽松的规则。
}
],
"@babel/preset-react", //将 React 的特定语法(如 JSX)转换为普通的 JavaScript。
"@babel/preset-typescript" //用于编译 TypeScript 代码。
],
"plugins": [//列出了一些额外的插件,用于提供特定的编译功能。
"dynamic-import-node", //将import() 转为 require()-如果代码中有使用动态导入(import())语法,这个插件可以将其转换为 Node.js 环境中的 require() 函数调用,以便在服务器端(如 Node.js)中使用。
"@babel/plugin-transform-runtime", //向 JavaScript 标准库注入兼容性方法。
[
"@babel/plugin-proposal-decorators", //ES7修饰器转化- 用于处理 ES7 中的装饰器(decorators)语法。
{
"legacy": true//使用传统的装饰器实现。
}
]
]
}
2、 import()
和 require()
的区别
-
动态性:
import()
是动态的,可以在代码的任何位置使用,并且可以根据条件来决定是否导入模块。它返回一个 Promise,当模块加载完成后,Promise 会被解决。require()
是静态的,通常在文件的开头使用,并且在运行时就会加载并执行模块。
-
模块类型:
import()
用于导入 ES6 模块。require()
用于导入 CommonJS 模块。
-
浏览器支持:
import()
需要通过构建工具(如 Webpack、Rollup 等)将 ES6 模块转换为浏览器可以理解的格式。require()
在 Node.js 环境中广泛使用,并且可以直接在浏览器中通过使用 Browserify 或 RequireJS 等工具来支持。
-
使用场景:
import()
通常用于现代 JavaScript 项目,特别是在使用构建工具的前端项目中。require()
通常用于 Node.js 项目,以及一些需要在浏览器中运行的旧项目。
七、安装Typescript(TS配置)
安装
npm i typescript -D
配置
1、初始化
tsc --init
执行命令会生成一个tsconfig.json文件
2、tsconfig.json文件配置
{
"compilerOptions": {
"module": "commonjs", //设置程序的模块系统。
"target": "es5", //编译目标
"jsx": "react", //控制 JSX 在 JavaScript 文件中的输出方式。
"sourceMap": true, //调试显示原始的 TypeScript 代码。
"removeComments": true, //移除注释
/**
* TypeScript 开启严格模式 ,开启 "noImplicitAny": true,"strictNullChecks": true, "strictFunctionTypes": true,
* "strictBindCallApply": true,"strictPropertyInitialization": true,"noImplicitThis": true,"alwaysStrict": true
*/
"strict": true,
"noImplicitAny": true, //ypeScript无法推断变量的类型时,TypeScript将返回到变量的any。
"strictNullChecks": true, //null和undefined有各自不同的类型
"moduleResolution": "node", //指定模块解析策略
"baseUrl": "./", //基准目录
"typeRoots": ["node_modules/@types"], //类型根路径
"allowSyntheticDefaultImports": true, //允许合成默认导入
"esModuleInterop": true, //ES 模块互操作性
"experimentalDecorators": true //ES7类修饰器允许
},
"exclude": ["node_modules", "config/*"], //忽略文件
"include": ["src/**/*"] //包含文件
}
八、文件组装
1、新建index.html
文件
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="pragma" content="no-cache" />
<meta http-equiv="Cache-Control" content="no-store, must-revalidate" />
<meta http-equiv="expires" content="Wed, 26 Feb 1997 08:21:57 GMT" />
<meta
name="viewport"
content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no"
/>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="app_root"></div>
</body>
</html>
2、新建源码文件夹和入口
(1)根目录下新建文件夹src
(2)src下新建index.tsx
(3)src下新建APP.tsx
index.tsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
createRoot(document.querySelector('#app_root') as Element).render(<App />);
APP.tsx
import React, { FC } from 'react';
const App: FC<any> = (props) => {
return <>app</>;
};
export default App;
3、新建webpack配置文件夹和配置文件
- 在根目录下创建Webpack文件
- 在Webpack文件夹下创建webpack.base.js、webpack.dev.js、webpack.prod.js
- 编写webpack配置文件
说明:
- webpack.base.js 公共部分配置
- webpack.dev.js 开发模式配置
- webpack.prod.js 生产模式配置
生产模式和开发模式的区别
-
功能:
- 生产模式:通常只包含最终用户需要的功能,去除了开发和调试过程中使用的工具和功能,以提高性能和安全性。
- 开发模式:包含了所有的开发工具和功能,如源代码映射、热更新、错误报告等,以便开发者能够更高效地进行开发和调试。
-
性能:
- 生产模式:优化了性能,减少了不必要的代码和资源加载,提高了应用的运行速度和响应时间。
- 开发模式:为了方便调试,可能会包含一些额外的代码和资源,这可能会影响应用的性能。
-
安全性:
- 生产模式:更加注重安全性,可能会对敏感信息进行加密处理,并且会限制一些可能存在安全风险的操作。
- 开发模式:为了方便开发,可能会降低一些安全限制,以便开发者能够更自由地进行测试和调试。
-
调试便利性:
- 生产模式:通常不提供详细的错误信息和调试工具,因为这些信息可能会被攻击者利用。
- 开发模式:提供了丰富的错误信息和调试工具,以便开发者能够快速定位和解决问题。
3.1 webpack.base.js
公共部分配置
概括:入口文件、ts,js,图片,其他文件,less,html相同
//项目的公共配置部分
const HtmlWebpackPlugin = require('html-webpack-plugin');//处理 HTML 文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');//在生产环境提取 CSS
const path = require('path');//引入了 Node.js 中的 `path 模块,该模块了用于处理文件和目录路径的实用方法。
const devMode = process.env.NODE_ENV !== 'production';//process.env.NODE_ENV环境变量,用于标识当前应用运行的环境,常见的值有 development(开发环境)和 production(生产环境)。
const resolve = (dir) => path.resolve(__dirname, dir);//其作用是使用 path.resolve 方法当前脚本所在的目录(__dirname)和传入的 dir 参数组合起来,返回一个绝对路径。
module.exports = {
target: 'web',//目标平台
entry: resolve('../src/index.tsx'), //入口文件
plugins: [
/**
* html文件处理
*/
new HtmlWebpackPlugin({
title: 'Wizard-RUI',//设置生成的 HTML 页面的标题
filename: 'index.html',//指定生成的 HTML 文件名。
template: resolve('../index.html'),//指定模板文件的路径。
hash: true,//为生成的资源添加哈希值。
cache: false,//控制缓存策略。
inject: true,//决定资源(如 JavaScript 和 CSS)的注入方式。
minify: {//用于对生成的 HTML 进行压缩,包含了一些具体的压缩选项
removeComments: true,//移除注释
removeAttributeQuotes: true,//移除属性引号
collapseWhitespace: true,//压缩空白
minifyJS: true, // 在脚本元素和事件属性中缩小JavaScript(使用UglifyJS)
minifyCSS: true // 缩小CSS样式元素和样式属性
},
nodeModules: resolve('../node_modules')//资源的存放位置。
}),
/**
* MiniCss插件,在生产环境使用
*/
!devMode
? new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',//指定生成的 CSS 文件的名称格式
chunkFilename: 'css/[id].[contenthash].css',//指定分块的 CSS 文件的名称格式
ignoreOrder: true//忽略模块加载的顺序
})
: function(){}
],
module: {//定义了模块的规则module.rules,用于处理不同类型的文件,如css、ts、tsx、图片、字体等文件的加载和处理方式
rules: [
/**
* 处理less,css 为dev模式下使用style-loader 为pod模式下使用MIniCss
*/
{
test: /\.(le|c)ss$/i,
use: [devMode ? 'style-loader' : MiniCssExtractPlugin.loader, 'css-loader', 'less-loader']
},
/**
* ts,tsx,js,jsx解析
*/
{
test: /\.(ts|tsx)$/,
exclude: /(node_modules|bower_components)/,
use: [
{
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}
]
},
/**
* 图片处理
*/
{
test: /\.(png|svg|jpg|gif)$/, // 图片
use: [
{
loader: 'file-loader',
options: {
name: 'assets/images/[name].[ext]' // 存放的位置: dist/assets/images/文件
}
}
]
},
/**
* 字体文件处理
*/
{
test: /\.(woff|woff2|eot|ttf|otf)$/, // 字体
use: [
{
loader: 'file-loader',
options: {
name: 'assets/fonts/[name].[ext]' // 存放的位置: dist/assets/fonts/文件
}
}
]
}
]
}
};
3.2 webpack.dev.js
开发环境配置
概括:dev模式 less 略微不同,devServer相关配置,代理,resove路径,优化模式
//开发环境配置
const base = require('./webpack.base'); //取出公共部分
const { merge } = require('webpack-merge');//从 webpack-merge 模块中导入 merge 函数。在这个 React 项目构建的配置中,使用 merge 来合并不同的 Webpack 配置,例如在开发环境配置 webpack.dev.js和生产环境配置webpack.prod.js中,通过merge(base, {…})` 的方式将公共的基础配置和特定环境的配置进行合并。
const path = require('path');
const resolve = (dir) => path.resolve(__dirname, dir);
module.exports = merge(base, {
mode: 'development', //开发环境webpack内置优化
devtool: 'inline-source-map', //控制台调试代码------用于在开发环境中为 Webpack 配置提供源代码映射功能。这在浏览器的控制台中调试代码时,可以更方便地查看的源代码,而不是经过打包处理后的代码,有助于更高效地定位解决代码中的问题。
devServer: {
client: {
progress: true //在浏览器中以百分比显示编译进度。
},
compress: false, //gzip压缩
hot: true, //热更新------指在开发过程,当代码发生修改时,无需重新启动整个应用程序,而是能够实时地将修改的部分应用正在运行的应用中,让开发者能够立即看到修改后的效果,提高开发效率。
open: {//指定打开浏览器
app: {
name: 'goole-chrome', //指定打开chrome
arguments: ['--incognito', '--new-window'] //无痕,新的窗口
}
},
port: 8081, //监听端口
proxy: {} //代理配置
},
optimization: {
//优化模式---表示在开发环境中不进行代码的最小化压缩。在生产环境中,通常会将 minimize 设置为 true 以减小代码体积,提高加载性能。
minimize: false
},
/**
* 路径别名
*/
resolve: {
alias: {
// "@": ["../src"],
'@': resolve('../src/'),
src: resolve('../src/'),
components: resolve('../src/components'),
config: resolve('../src/config'),
hook: resolve('../src/hook'),
apis: resolve('../src/apis'),
router: resolve('../src/router'),
store: resolve('../src/store'),
theme: resolve('../src/theme'),
util: resolve('../src/util'),
i18n: resolve('../src/i18n'),
assets: resolve('../src/assets'),
views: resolve('../src/views')
},
extensions: ['.tsx', '.ts', '.wasm', '.mjs', '.js', '.json']//指定模块解析时可接受的文件扩展名。
}
});
3.3 webpack.prod.js
生产环境配置
概括:出口,优化模式, 公共部分配置
//生产环境配置
const base = require('./webpack.base'); //取出公共部分
const { merge } = require('webpack-merge');
const CleanWebpackPlugin = require('clean-webpack-plugin').CleanWebpackPlugin;//它的作用是在每次构建前清理输出目录,确保生成的新构建文件不会受到旧文件的干扰。
const path = require('path');
const resolve = (dir) => path.resolve(__dirname, dir);
module.exports = merge(base, {
mode: 'production', // 环境 development 和 production 环境
output: {
filename: 'index.js', // 文件名
path: resolve('../dist'), // 文件输出地址
library: {//library 用于指定构建后的模块发布方式,这里设置为 umd ,以实现兼容多种运行环境。
/**
* 发布运行环境
* umd——兼容浏览器
* commonjs,commonjs2,module——适用于node.js环境
* amd——适用于require.js环境
* cmd——适用sea.js环境
*/
type: 'umd'
},
clean: true//用于在构建生产时清理输出目录(通常是 dist 文件夹)中的旧文件,以确保每次构建的输出都是干净和最新的。
},
optimization: {
//优化模式
minimize: false
},
/**若通过CDN引入React和ReactDOM可以使用 */
// externals: {
// react: 'React',
// 'react-dom': 'ReactDOM'
// },
plugins: [new CleanWebpackPlugin()],
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx']//指定模块解析时可接受的文件扩展名。
}
});
3.4 配置运行命令及打包命令
package.json 的script加入
{
"dev": "webpack-dev-server --config ./webpack/webpack.dev.js",
"build": "webpack --config ./webpack/webpack.prod.js",
"test": "echo \"Error: no test specified\" && exit 1"
}
测试npm run dev
,npm run build
是否成功运行;
说明
devtool
选项值及其作用:
eval
:每个模块都使用eval()
执行,并且source map被嵌入到eval()中。这是最快的选项,但它不提供列映射(column mapping),这意味着你只能看到行号,而不能看到具体的列信息。cheap-eval-source-map
:与eval
类似,但它提供了列映射,并且只在开发环境中使用。这是一个不错的选择,因为它在提供调试信息的同时,保持了较快的构建速度。cheap-module-eval-source-map
:与cheap-eval-source-map
类似,但它适用于使用import
和export
语句的模块系统。inline-source-map
:将生成的source map嵌入到打包后的文件中,而不是生成一个单独的文件。这可以在浏览器的开发者工具中直接看到原始的源代码,但会增加文件的大小。cheap-source-map
:生成一个不带列映射的source map文件。这比完整的source map文件小,但仍然提供了足够的调试信息。cheap-module-source-map
:生成一个带有列映射的source map文件,适用于使用import
和export
语句的模块系统。source-map
:生成一个完整的source map文件。这是最详细的选项,但它会生成一个单独的文件,并且可能会减慢构建速度。hidden-source-map
:生成一个source map文件,但不将其嵌入到打包后的文件中。这通常用于在生产环境中调试,但不希望用户看到source map文件。nosources-source-map
:生成一个source map文件,但不包含源代码。这可以用于在生产环境中调试,但不希望用户看到原始的源代码。