记录 React 从 0 到 1 搭建过程
1、安装 node 环境
搭建前端项目前,首先要安装本机的 node 环境,直接去 node 官网下载对应的安装包手动安装即可。 官网传送门;
安装完成后,在命令行工具执行以下命令,查看是否安装成功:
node -v
npm -v
image.png
2、项目搭建
2.1、初始化项目
新建文件夹 react-demo;
进入 react-demo 内,在命令行工具内执行 npm init ,初始化项目。
按照提示操作完成后,文件夹内会生成一个 package.json 文件。
image.png
2.2、安装 webpack
执行一下命令安装 webpack,推荐使用 yarn 安装。
使用 yarn 前需要先全局安装:
npm i -g yarn
安装webpack:
yarn add webpack webpack-cli
安装完后会在项目根目录生成一个 yarn.lock 文件,该文件用来锁定当前依赖的版本号,后续再执行install 时,会直接安装该文件内的依赖版本,不会再自动更新 package.json 中的依赖版本。
要想更新 package.json 中的依赖版本需要执行如下命令覆盖:
yarn add xxx@xxx
2.3、初始化项目结构
在src目录下,新增 index.js,作为项目的入口文件,也是 webpack 配置的入口文件;
根目录下新增 src 文件夹,用于存放源码;
根目录下新增 config 文件夹,用于存放 webpack 和 项目相关配置的文件;
在 config 文件加下新增 webpack.config.js 文件,在该文件内加入如下内容,配置 webpack 基本信息
const path = require('path');
module.exports = {
mode: 'development', // 指定 webpack 打包环境
entry: './src/index.js', // 项目的入口文件,路径相对于项目根路径
output: {
filename: 'bundle.js', // 打包输出的文件名,实际项目中需要按一定规则动态生成
path: path.resolve(__dirname, '../dist') // 打包完成后输出的路径,这里的路径针对的是当前目录
}
};
在根目录 index.js 文件中,加入一个打印日志,用来测试打包是否成功:
image.png
配置 package.json 内的 script 命令,用来执行打包操作:
增加如下命令:
"build": "webpack --config ./config/webpack.config.js",
image.png
在命令行工具内 执行如下命令,来进行打包:
yarn build
打包完成后,在根目录会生成一个 dist 目录,里面有 bundle.js 文件,即打包后的文件。
image.png
打包过程中可能出现的报错:
-
invalid or unexpected token
产生该问题的原因可能有以下几种;
- webpack 配置文件配置错误或者是语法错误
- 缺少loader
某些非 js 文件的资源,缺少对应的 loader ,也可能会导致此问题。 - 文件编码问题
排除问题时,可以尝试是否文件编码的问题,增加 webpack-encoding-plugin 配置,修改编码。
首先安装 webpack-encoding-plugin 依赖,
yarn add webpack-encoding-plugin
在 webpack 配置文件中增加如下配置:
const EncodingPlugin = require('webpack-encoding-plugin');
// 增加 plugin 配置
plugins: [
new EncodingPlugin({
encoding: 'utf-8'
})
]
重新打包
2.4 增加核心配置
安装 babel 核心依赖,这样依赖会将 ES5+ 的语法转换成 ES5,目前ES5+的语法还不能直接在浏览器编译,因此需要安装这些依赖对ES5+的代码进行一个转换。
yarn add babel-loader @babel/core @babel/preset-env @babel/preset-react
依赖安装完成后,在项目根目录新建文件 .babelrc,并加入如下内容:
{
"presets": [ // 相当于插件集合,不需要在 plugins 中再配置多个插件
[
"@babel/preset-env", {
"useBuiltIns": "entry", //如果用了@babel/polyfill的话,配置这个属性可以将@babel/polyfill按需引入
"corejs": 2
}
],
"@babel/preset-react"
],
"plugins": []
}
module.exports = babelConfig;
修改 webpack.config.js,新增module,配置 babel-loader:
module: {
rules: [
// 配置babel-loader
{
test: /\.(js|jsx)$/,
use: ["babel-loader"],
include: path.join(__dirname, "../src"),
exclude: /node_modules/,
},
],
},
配置完成后,在入口文件内新增 es6+ 的内容,并重新打包,测试配置是否成功。
上述配置完成后,因为 babel 只会转换 es6 语法,而不会转换新的 api (如 promise,generator 函数等),让新的 api 生效的方法是使用传统的 polyfill,为此需要安装 @babel/polyfill 这个模块。
安装 @babel/polyfill
yarn add @babel/polyfill
@babel/polyfill 安装完成后不再需要进行额外的配置。
安装完 @babel/polyfill 之后,还需要安装以下3个库,来实现按需加载的效果:
yarn add @babel/runtime @babel/plugin-transform-runtime @babel/plugin-syntax-dynamic-import @babel/plugin-proposal-class-properties
这几个库的作用可参考知乎文章
一文谈完前端项目中的Babel配置
上述依赖安装完成后,增加 babel 相关配置,在 .babelrc 文件中增加以下配置:
plugins: [
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-runtime"
]
入口文件内,新增 Promise 代码,重新打包,测试打包效果。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('sync log');
}, 1000);
});
promise.then(res => {
console.log(res);
});
新增sass/less 等css预处理器解析配置
安装以下依赖
yarn add stylus stylus-loader less less-loader sass-loader node-sass css-loader style-loader
在webpack 配置文件中,配置解析器, 新增loader:
// 配置css预处理器
{
test: /\.less$/,
use: [
"style-loader",
"css-loader",
"less-loader",
],
},
配置完成后,根目录下新增 index.less 文件,加入样式配置:
@primary-color: #29BEDB;
div {
color: @primary-color;
font-size: 14px;
font-weight: 600;
}
在 index.js 入口文件中,引入新增的样式文件,重新打包,查看打包效果。
在使用到 css3 的某些新特性时,为了适应不同浏览器的兼容性,需要为不同的浏览器增加不同的前缀,此时就需要用到 postcss-loader 来为css代码自动添加前缀。
安装依赖
yarn add postcss-loader autoprefixer cssnano
配置loader:
{
test: /\.less$/,
use: [
"style-loader",
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
require("autoprefixer"),
require("cssnano")
],
},
},
},
"less-loader",
],
},
配置静态资源解析
在项目中用到的图片等资源,都需要配置相关的解析工具,安装如下依赖:
yarn add file-loader url-loader
file-loader可以用来帮助 webpack 打包处理一系列的图片文件。打包后的每张图片都会生成一个随机的 hash 值作为图片名;url-loader 封装了 file-loader,其工作原理:1、文件大小小于limit 数值,url-loader 将会把文件转为Base64;2、文件大小大于 limit,url-loader 会调用 file-loader 进行处理,参数也会直接传给file-loader。
配置 loader,新增图片资源loader:
//配置图片资源loader
{
test: /\.(jpg|png|jpeg|gif)$/,
use: [
{
loader: "url-loader",
options: {
limit: 1024,
fallback: {
loader: "file-loader",
options: {
name: "img/[name].[hash:8].[ext]",
},
},
},
},
],
},
配置完成后,在 src 目录下新增 assets 静态资源目录,在目录内放入一张图片,在入口文件 index.js 文件中引入图片,重新打包。
const img = require('./assets/img/profile.jpg');
打包完成后,可以看到在dist目录内生成一个新的 img 目录,里面即是用到的图片文件,说明打包成功。
配置压缩js、css等文件,减小打包体积
安装依赖:
yarn add mini-css-extract-plugin
在webpack配置文件中新增相关配置
// 引入插件依赖
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// plugins内 实例化插件
plugins: [
// 实例化插件 MiniCssExtractPlugin
new MiniCssExtractPlugin({
filename: "css/[name].[contenthash].css",
chunkFilename: "css/[id].[contenthash].css",
}),
],
// 配置loader, module内新增loader
// 配置css压缩插件
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: "../",
},
},
"css-loader",
],
},
配置完成后,在根目录新增 test.css 文件,在入口文件内引入,进行打包,查看效果。
打包成功后会在 dist 目录内出现 css 文件目录。
image.png
该插件之后压缩 css 文件,原 less 文件还会继续打包到 js 文件内。
抽离公共代码
打包后的 js 、css 文件中会有很多公共代码,如果不将这些代码进行抽离,最后打包文件会变得很大,所以需要使用 SplitChunksPlugin 插件抽离公共代码,只需在 webpack 配置文件中加入如下配置:
optimization: {
// 抽离公共代码插件的配置
splitChunks: {
cacheGroups: {
// 打包公共模块
commons: {
chunks: "initial", // 代码分割时默认对异步代码生效 async,all:所有代码有效,initial:同步代码有效
minChunks: 2, // 分割之前模块在块与块之间共享的最小次数
minSize: 20000, // 提取公共部分的最小的大小(以字节为单位)
name: "common", // 设为 false 将保持块的相同名称,因此不会不必要地更改名称(生产版本推荐)
},
},
},
},
配置解析器Resolve
Resolvers 解析器=用于告诉 webpack 去哪些目录下查找引用的模块,默认值是["node_modules"]。
webpack 配置文件下,新增如下内容:
resolve: {
extensions: [".js", ".jsx"], // 引入 js jsx文件时,引入路径不需携带文件后缀
// 设置别名,@ 直接指向 src 目录
alias: {
"@": path.resolve(__dirname, "../src"),
},
},
** 配置热更新 **
新增如下依赖
yarn add webpack-dev-server html-webpack-plugin
webpack-dev-server 用于热更新,html-webpack-plugin 用于动态的将打包后的 css、js文件加入到 html 中,相关配置如下:
在 plugins 内新增 HtmlWebpackPlugin 实例化
// 引入html模板插件
const HtmlWebpackPlugin = require('html-webpack-plugin');
//实例化Html模板模块
// 不指定模板会在dist目录自动生成一个html文件
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../index.html"),
}),
// 配置 devServer
devServer: {
// 配置热更新模块
static: {
directory: path.join(__dirname, "public"),
},
compress: true,
hot: true,
port: 3001,
},
在根目录新增 index.html 文件,加入如下内容
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Webpack Demo</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div id="root"></div>
</body>
</html>
在 package.json 文件中 script 内新增启动命令:
"start": "webpack-dev-server --config ./config/webpack.config.js",
控制台执行 yarn start 启动项目。
删除上一次的打包结果及记录
安装依赖
yarn add clean-webpack-plugin
clean-webpack-plugin 帮我们在每次打包时,清除上一次打包的内容。
配置如下,直接实例化插件即可:
// 引入插件
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
// 实例化插件
new CleanWebpackPlugin(),
3、集成 React
安装React、react-dom
首先安装 react、react-dom 依赖
yarn add react react-dom
在入口文件内挂载 React
import React from "react";
import ReactDOM from "react-dom/client";
import App from "@/App";
// v18 的新方法
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
在 src 目录下新增 App.js 文件:
import React from "react";
const App = () => {
return <div> React18 + Webpack5 from 0 to 1 </div>;
};
export default App;
重新 yarn start 启动项目,启动成功
image.png
安装react-router-dom
yarn add react-router-dom
依赖安装完成后,在 src 目录下新增 routes 目录,用来存放页面组件代码。
在 routes 下新增 List.js,Detail.js 两个文件。
image.png
两个文件内分别加入不同的内容
List.js
import React from 'react';
export default function List() {
return (
<>
this is list page.
</>
)
}
Detail.js
import React from "react";
export default function Detail() {
return <div style={{color: 'red'}}>this is detail page.</div>;
}
此时在 src 目录下新增 config 目录,config目录下新增 routers.js 文件,用于存放页面路由。
routers.js 文件内加入如下内容:
// 存放页面路由
import List from "@/routes/List";
import Detail from "@/routes/Detail";
// 存放页面路由
const routes = [
{
path: "/",
component: List,
exact: true,
},
{
path: "/detail",
component: Detail,
},
];
export default routes;
修改入口文件 index.js 如下:
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
import routes from "./config/routers";
// v18 的新方法
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<Router>
<Link to="/">列表</Link>
<Link to="/detail">详情</Link>
<Routes>
{routes.map((item, key) => {
if (item.exact) {
return (
<Route
exact
path={item.path}
element={<item.component />}
key={key}
/>
);
} else {
return (
<Route path={item.path} element={<item.component />} key={key} />
);
}
})}
</Routes>
</Router>
</React.StrictMode>
);
启动项目,可点击列表 或详情 加载对应的内容:
image.png
网友评论