一些思考
在公司项目中,需要启一个新的前端工程(一个基于Webpack的React工程)。因为同一个项目中有其他的前端工程,我们最开始想的是参考另外一个工程的配置重启一个新的工程,但是又因为原来的工程用的库版本都比较老,而我们这个新的工程又需要安装最新的依赖,所以就只能从头开始启一个工程了。
但是这个过程也是比较曲折的,经历了几次推翻重启。
首先,第一版我们为了避免各种依赖之间的版本冲突,就将原来工程中的packages.json
文件中内容复制到新工程里,然后再将我们需要的Webpack配置以及其他的基础配置配好后,启动测试。测试通过后,我们尝试将我们想要升级的依赖升级到我们想要的版本,比如React18和Webpack5。在这个期间,也会出现一些需要修改的配置文件,比如旧版本的写法不适用于新版本,这时我们就需要用新版本的写法覆盖旧版本的内容。解决完这部分问题后,我们已确定我们的工程可以正确启动了,并且可以达到我们想要的启动后的结果,所以我们就认为这部分工作我们已经做完了。但是为了确保我们的思路是否正确,我们将产出的结果拿去给公司更senior的同事看了后,他提出了一些建设性的意见,比如:
- 由于我们的做法是将旧工程的配置文件都复制到新的工程里面来,所以里面会有很多我们暂时用不到的依赖、配置,那这时候就会有很多冗余代码,并且会降低构建速度,还会造成配置混乱,增加项目的复杂度和维护成本,也会存在一些潜在的错误或者问题;
- 对于我们启工程且后续需要在这个工程上编码的同学来说,一些没有用的配置也会给我们带来压力,比如会增加阅读和理解代码的成本,也会让我们的思路变得不清晰,不利于学习。
所以,基于此意见,我们选择重新启一个工程。
然后,第二版我们根据上一次的经验,选择用到什么再加什么配置,而不是一股脑的把所有需要用到的配置都加上去,最后再解决冲突。这样的好处就是能让我们的思路变得清晰,并且让我们能更好地理解这个工程,在其他人需要了解的时候我们也可以能清晰地给讲出某一个配置的由来及作用是什么。这个时候我们的步骤是:
- 先使用
create-react-app
脚手架搭建一个基础的React工程,测试运行通过后进行下一步。 - 在其中增加Webpack的相关配置,比如增加
Webpack.config.common.js
文件,编写如下文提到的Webpack四个核心概念的内容(entry、output、loader、plugins),然后再补充dev环境的Webpack配置Webpack.config.dev.js
文件,修改script运行脚本为webpack serve --config config/webpack.config.dev.js --progress
。然后进行测试运行。
(参考本片文章:从零开始创建react+webpack项目)
在这个过程期间,我们有遇到一些问题,也去求助了senior同事,他们告诉我们说,我们的方法也可以用,但是在一般正式的项目上如果需要重写使用create-react-app
脚手架搭建的工程中的Webpack相关配置时,会采用raect-app-rewired的方式创建,react-scripts 提供的 config-overrides.js 文件可以修改 Webpack 配置。config-overrides.js
文件通过导出一个函数来修改 CRA 的默认配置。这个函数接受一个 config 参数,可以在其中修改 Webpack 配置。例如,可以添加、修改或删除 Loaders、Plugins、Optimizations 等等。下面是一个简单的例子:
上述示例在 Webpack 配置中添加了一个处理 .txt 文件的 Loader,并将输出目录更改为 custom-build。
除此之外,也可以使用执行eject脚本的方式暴露出Webpack配置文件,然后进行自定义设置。
- 执行以下命令来暴露Webpack配置文件:
npm run eject
- 执行上述命令后,Create React App将会生成一个 config 文件夹和相应的配置文件,包括 webpack.config.js。可以在这些配置文件中进行修改和定制。
- 然后可以根据需要修改Webpack配置,例如添加额外的loader、plugins、自定义路径等。
- 修改配置后,运行
npm start
启动开发服务器。
以上的工作就算告一段落了。我们在总结的过程中,从另外一位同事那里了解到,一般比较大的项目并且需要的配置比较多的项目,比如测试,Webpack,antDesign、routes等,也会采用umi脚手架启动工程。umi是阿里推出的一个企业级的React应用开发框架,它提供了一系列工具和约定,帮助开发者快速构建可扩展、高效和易维护的React应用。
umi脚手架具有以下主要特点和功能:
- 路由管理:UMI内置了强大的路由系统,支持配置式路由和约定式路由。通过配置文件或目录结构自动生成路由,支持嵌套路由、动态路由、权限控制等功能。
- 插件机制:UMI提供了丰富的插件机制,可以通过引入插件来扩展和定制项目功能。UMI插件可以处理路由、构建、数据模拟、国际化等方面的需求,且插件具有良好的生态和社区支持。
- 开发模式支持:UMI支持多种开发模式,包括单页应用(SPA)、服务器渲染(SSR)、静态网站生成(SSG)等。可以根据项目需求选择合适的开发模式,并进行相应的配置。
- 状态管理:UMI天然支持多种状态管理方案,包括React Context、Redux、Mobx等。可以根据项目需求选择合适的状态管理方案。
- 构建工具:UMI集成了Webpack和Babel等构建工具,使得项目的构建和打包过程更加简化。可以通过配置文件对构建过程进行定制,并享受热更新、代码分割、按需加载等优秀的构建特性。
- 测试和部署:UMI提供了一系列测试工具和部署方案,可以帮助编写和运行单元测试、集成测试,并方便地将应用程序部署到各种环境中。
但与此同时,umi也存在一定的缺点:
- 配置复杂性:虽然umi提供了丰富的配置选项,但也会导致配置复杂性增加。对于初学者或简单项目来说,会过于复杂且"重",需要手动删除不需要的依赖及配置;
- 依赖关系:umi依赖于一些其他的工具或库,例如Webpack、babel、React等。这些依赖关系可能会导致一些版本兼容性或依赖冲突的问题,需要手动管理和解决。
所以,在工作中,可以根据项目的需要选择脚手架去创建工程。这个部分还需要再了解,跟着有经验的同事深入学习。
上述工作完成后,我也整理了一些关于Webapck的相关内容,整理一下思路,以便在以后的工作中用到。
为什么需要打包工具
开发时,我们会使用框架(React、Vue),ES6模块化语法,Less/Sass等css预处理器等语法 进行开发。
这样的代码要想在浏览器运行必须经过编译成浏览器能识别的JS、css等语法才能运行。所以打包工具就是帮我们完成这一步骤。除此之外,打包工具还能压缩代码、做兼容性处理、提升代码性能等。
Webpack的核心概念
在弄清楚Webpack之前需要了解清楚以下四个核心概念。
entry
在Webpack中,entry指定应用程序的入口点(entry point)。它是Webpack构建过程的起点,表示Webpack应该从哪个文件开始构建应用程序的依赖图。
在Webpack配置文件中,可以通过entry属性来指定一个或多个入口点。每个入口点对应一个文件路径,可以是单个JavaScript文件、CSS文件、HTML文件或其它类型的文件。Webpack会根据这些入口点开始分析和解析依赖关系,构建整个应用程序的依赖图。
例如:
module.exports = {
entry: './src/main.js',
};
在上面的例子中,entry属性指定了.src/main/js
作为应用程序的入口点。Webpack将从这份文件开始构建应用程序,并根据它的依赖关系进一步解析其他模块和资源。
output
在Webpack中,output指的是构建输出的配置选项。定义了Webpack打包后的文件输出位置和文件名等相关信息。如下是常用的配置选项:
-
path:指定输出文件的目标路径。例如
path: path.resolve(__dirname, 'dist)
,将输出文件保存在项目根目录下的dist文件夹中。 -
filename:指定输出文件的名称。默认为
bundle.js
,也可以使用占位符和变量来生成动态的文件名。例如,filename:'bundle.js'
将生成一个名为bundle.js
的输出文件。 -
publicPath:指定输出文件在被引用时的公共路径。用于配置在浏览器中加载静态资源的路径。例如,
publicPath: '/assets/'
会将输出文件的路径前缀设置为/assets/
。 -
chunkFilename: 配置非入口点的代码块(chunk)的文件名。这些代码块通常是按需加载时生成的。例如,
chunkFilename: '[name].js'
将生成基于代码块名称的文件名。module.exports = {
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/assets/',
},
};
loader
在Webpack中,loader指的是用于处理特定类型的文件的加载器。Webpack在打包过程中会遇到不同类型的文件,例如JavaScript文件、CSS文件、图片、字体文件等,而加载器会告诉Webpack如何处理这些文件。
Webpack的加载器通过转换文件内容或执行一些额外的任务来对文件进行处理。加载器可以将文件转换为JavaScript模块,将Sass文件转换为CSS,对图片进行压缩和优化等等。加载器可以链式调用,允许按顺序应用一系列转化和处理操作。
加载器通常在Webpack配置文件的module.rules
属性中定义。每个加载器规则由两部分组成:
- test:用于匹配文件路径的正则表达式,指定了应该由该加载器进行处理的文件类型。
- use:指定使用的加载器,可以是单个加载器或多个加载器组成的数组。加载器可以是内置的Webpack加载器,也可以是通过npm安装的第三方加载器。
例如:
module.exports = {
// 其他配置项...
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(png|jpg|gif)$/,
use: ['file-loader'],
},
],
},
};
上述示例中,定义了两个加载器规则:
- 对于以
.css
结尾的文件,使用style-loader和css-loader加载器进行处理。css-loader用于解析CSS文件,而style-loader用于将解析后的CSS添加到页面中。 - 对于以
.png
、.jpg
或.gif
结尾的文件,使用file-loader加载器进行处理。file-loader用于处理图片文件,将它们复制到输出目录并返回最终的URL。
plugins
在Webpack中,plugins指的是用于执行更广泛范围任务饿自定义构建流程的插件。插件可以用于从打包过程中的各个阶段添加额外的功能和扩展Webpack的功能。
Webpack的插件可以处理各种任务,包括但不限于:
- 优化输出文件,例如压缩、混淆和代码分割;
- 处理非JavaScript文件,例如将CSS提取为单独的文件、压缩图像等;
- 注入全局变量或环境变量;
- 生成HTML文件,自动引入打包后的资源;
- 清理输出目录,确保每次构建都是干净的;
- 收集统计信息,例如构建报告、Bundle分析等;
- 在构建过程中执行自定义逻辑和任务。
要使用插件,需要在Webpack配置文件中的plugins
属性中实例化和配置它们。插件可以是内置的Webpack插件,也可以是通过npm安装的第三方插件。
例如:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
// 其他配置项...
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
}),
new CleanWebpackPlugin(),
],
};
在上述示例中,使用到了两个插件:
HtmlWebpackPlugin
:用于生成HTML文件并自动将打包后的资源引入。通过指定template
选项,可以指定基于哪个模版文件生成的HTML文件;CleanWebackPlugin
:用于清理输出目录。每次构建之前,它会自动清除输出目录,确保每次构建都是干净的。