持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第8天,点击查看活动详情
上一次我们讲了如何用Webpack从零搭建一个Vue3 + TypeScript开发环境,本期我们来讲一下如何用Webpack从零搭建一个React开发环境
还没看过上期的小伙伴可以去看看~
👉用Webpack从零搭建一个 Vue3 + TypeScript 开发环境
前言
传统的 Web 开发中,往往是强调结构、样式和逻辑三者分离,降低代码的复杂度。
但是在 React 框架当中,其认为渲染逻辑实际上与 UI 逻辑之间是有内在的耦合关系的,于是乎提出了使用JSX的方式,将结构、样式和逻辑组合在一起,形成“组件”,以“组件”为单位来维护代码,实现关注点分离
JSX写起来十分容易上手,它能够允许我们在js中编写html,并且又能够在html中插入js,十分灵活,用起来仿佛就像一切都本该如此
import React from 'react'
const Component = () => {
return <div className="container">Hello React</div>
}
以上就是一段JSX代码,相信用过React的读者对它都不陌生,我们今天的重点不是介绍JSX的语法,而是要关注如何使用Webpack从零搭建一个React的开发环境
要知道,JSX这种语法浏览器是无法识别的,因此我们要想办法将其转成纯js代码,从而能够在浏览器中执行
那么这个该如何实现呢?这就要用到咱们的Babel了
让 Webpack 支持处理 JSX
首先咱们把Webpack装上
pnpm i webpack webpack-cli -D
JSX转js交给Babel实现,因此我们再装个Babel
pnpm i @babel/core @babel/preset-react babel-loader -D
然后新建一个webpack.config.js去配置jsx用babel-loader处理
/**
* @type {import('webpack').Configuration}
*/
const config = {
mode: 'development',
devtool: false,
entry: './src/main.jsx',
resolve: {
// 导入 js 和 jsx 时可以不用写后缀
extensions: ['.js', '.jsx'],
},
module: {
rules: [
{
test: /\.jsx$/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-react'],
},
},
],
},
],
},
}
module.exports = config
main.jsx中的内容如下:
import ReactDOM from 'react-dom/client'
import Component from './components/Component'
ReactDOM.createRoot(document.getElementById('app')).render(<Component />)
Component.jsx中是一个组件,代码如下:
const Component = () => {
return <div className="container">Hello React</div>
}
export default Component
然后我们使用webpack进行打包
可以看到打包后的JSX被转成了React.createElement的js函数,这样就能够在浏览器中运行了
自动注入 React 导入代码
在上面的示例中,我们编写JSX的时候总是需要写一个import React from 'react'这样的导入语句,十分麻烦,实际上babel-loader支持配置自动注入React运行时代码
我们只需要修改babel-loader中@babel/preset-react的预设配置即可
/**
* @type {import('webpack').Configuration}
*/
const config = {
module: {
rules: [
{
test: /\.jsx$/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-react',
{
runtime: 'automatic',
},
],
],
},
},
],
},
],
},
}
现在我们将index.jsx中的import React from 'react'去掉后再打包看看
依然可以打包成功,这样一来我们就可以随心所欲使用JSX了,不需要管有没有导入React了
配置打包后自动生成对应 html
目前我们打包后的结果只有一个js文件,如果想要在浏览器中预览效果还得手动创建一个html,比较麻烦
这里我们可以使用html-webpack-plugin去帮我们自动生成html
pnpm i html-webpack-plugin -D
然后在webpack.config.js中加上插件配置
/**
* @type {import('webpack').Configuration}
*/
const config = {
// ...
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
}
配置开发环境服务器
为了方便在开发环境下更改代码后立刻看到运行效果,也就是HMR热更新的能力,我们还需要用到webpack-dev-server
pnpm i webpack-dev-server -D
然后修改webpack.config.js修改devServer配置
/**
* @type {import('webpack').Configuration}
*/
const config = {
// ...
devServer: {
hot: true,
open: true,
},
}
运行npx webpack serve即可开启开发环境服务器了
配置 css
目前我们还没法使用css,因此也需要配置一下相关loader
pnpm i css-loader style-loader -D
还可以顺便把预处理器sass和sass-loader装上
pnpm i sass sass-loader -D
并添加相关配置
/**
* @type {import('webpack').Configuration}
*/
const config = {
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(scss|sass)$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
},
],
},
}
现在我们就可以随意在项目中导入css/sass/scss资源了
CSS Module
为了做到像Vue的scoped特性的效果,我们可以使用CSS Module的方式,它的原理就是对导入的css类名做特殊处理,默认是用哈希值
这样的话就可以避免类名重复导致的样式冲突问题了,开启该功能很简单,只需要在css-loader中进行一下简单的配置即可
/**
* @type {import('webpack').Configuration}
*/
const config = {
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
},
},
],
},
{
test: /\.(scss|sass)$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
},
},
'sass-loader',
],
},
],
},
}
使用的时候就可以像这样使用
import style from './style.scss'
console.log(style)
const Component = () => {
return <div className={style.container}>Hello React</div>
}
export default Component
这里导入的style.scss变成了一个js对象
这里的container就是样式文件中的类名,其值是一个哈希值,会作为对应的类名
这就是CSS Module避免样式冲突的原理
集成 TypeScript
项目中往往会使用到TypeScript,这样也就能够顺便支持tsx,那么如何集成TypeScript呢?
社区中目前有两种主流的方案:
- 使用
Babel的@babel/preset-typescript - 使用
ts-loader
我们两种方案都尝试一下
@babel/preset-typescript
首先安装依赖
pnpm i typescript @babel/preset-typescript -D
然后添加对应loader配置
**
* @type {import('webpack').Configuration}
*/
const config = {
resolve: {
// 不要忘记加上对 .ts 和 .tsx 的后缀忽略支持
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
module: {
rules: [
// 集成 TypeScript
{
test: /\.ts$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-typescript'],
},
},
},
{
test: /\.tsx$/,
use: {
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-react',
{
runtime: 'automatic',
},
],
'@babel/preset-typescript',
],
},
},
},
],
},
}
这样就可以在项目中使用ts和tsx了,我们可以随便建立一个tsx组件试试
可以看到会报错,这是因为还需要配置一下TypeScript,让它知道我们使用的是React17之后的版本,不需要导入React
新建tsconfig.json
{
"compilerOptions": {
"jsx": "react-jsx"
}
}
这样就不会报错啦
ts-loader
至于第二种方式就简单多了,直接使用即可
/**
* @type {import('webpack').Configuration}
*/
const config = {
module: {
rules: [
// 集成 TypeScript -- 使用 babel-loader
// {
// test: /\.ts$/,
// use: {
// loader: 'babel-loader',
// options: {
// presets: ['@babel/preset-typescript'],
// },
// },
// },
// {
// test: /\.tsx$/,
// use: {
// loader: 'babel-loader',
// options: {
// presets: [
// [
// '@babel/preset-react',
// {
// runtime: 'automatic',
// },
// ],
// '@babel/preset-typescript',
// ],
// },
// },
// },
// 集成 TypeScript -- 使用 ts-loader
{
test: /\.ts$/,
use: ['ts-loader'],
},
{
test: /\.tsx$/,
use: ['ts-loader'],
},
],
},
}
至此,整个React开发环境就搭建完毕啦