React腳手架配置
建立config/webpack.dev.js config/webpack.prod.js
安裝依賴
npm i webpack-dev-server webpack webpack-cli -D
npm i eslint-webpack-plugin html-webpack-plugin style-loader css-loader postcss-loader
postcss-preset-env less-loader sass-loader sass stylus-loader -D
安裝babel
npm i babel-loader @babel/core babel-preset-react-app eslint-config-react-app -D
安裝 react
npm i react react-dom
"dependencies": {
"css-loader": "^6.7.1",
"eslint-webpack-plugin": "^3.1.1",
"html-webpack-plugin": "^5.5.0",
"style-loader": "^3.3.1"
},
"devDependencies": {
"@babel/core": "^7.18.2",
"babel-loader": "^8.2.5",
"babel-preset-react-app": "^10.0.1",
"eslint-config-react-app": "^7.0.1",
"less-loader": "^11.0.0",
"postcss-loader": "^7.0.0",
"postcss-preset-env": "^7.7.1",
"sass": "^1.52.3",
"sass-loader": "^13.0.0",
"stylus-loader": "^7.0.0",
"webpack": "^5.73.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.9.2"
}
}
配置啟動項 package.json
"scripts": {
"dev":"webpack serve --config ./config/webpack.dev.js"
},
安裝定義環境變量庫 cross-env
npm install --save-dev cross-env
"scripts": {
"start": "npm run dev",
"dev": "cross-env NODE_ENV=development webpack serve --config ./config/webpack.dev.js"
},
const path = require("path");
const EslintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const getStyleloaders=(pre)=>{
return [
"style-loader",
"css-loader",
{//css做相容處理 還需在package.json中browserslist來指定相容性
loader:'postcss-loader',
options:{
postcssOptions:{
plugins:["postcss-preset-env"],
},
},
},
pre
].filter(Boolean);
}
module.exports={
entry:'./src/main.js',
output:{
path:undefined,
filename:"static/js/[name].js",
chunkFilename:"static/js/[name].chunk.js",//動态import導入的js
assetModuleFilename:"static/media/[hash:10][ext][query]",
},
module:{
rules:[
//處理css
{
test:/\.css$/,
use:getStyleloaders(),
},
{
test:/\.less$/,
use:getStyleloaders('less-loader'),
},
{
test:/\.s[ac]ss$/,
use:getStyleloaders('sass-loader'),
},
{
test:/\.styl$/,
use:getStyleloaders('stylus-loader')
},
//處理圖檔
{
test:/\.(jpe?g|png|gif|webp|svg)/,
type:"asset",
parser:{
dataUrlCondition:{
maxSize:10*1024,
},
},
},
//處理其他資源
{
test:/\.(woff2?|ttf)/,
type:"asset/resource",
},
//處理js
{
test:/\.jsx?$/,
include:path.resolve(__dirname,'../src'),
loader:'babel-loader',
options:{
cacheDirectory:true,//開啟緩存
cacheCompression: false,//不要壓縮打包性能更快
},
},
],
},
plugins:[
new EslintWebpackPlugin({
context:path.resolve(__dirname,'../src'),
exclude:"node_modules",//包含排除 指定處理範圍
cache:true,//緩存
cacheLocation:path.resolve(__dirname,'../node_modules/.cache/.eslintcache'),//緩存目錄
//添加.eslintrc.js 配置
}),
//處理html
new HtmlWebpackPlugin({
template:path.resolve(__dirname,"../public/index.html"),
}),
],
mode: "development", //開發模式
devtool: "cheap-module-source-map", //調試設定
optimization: {
splitChunks:{
chunks:'all', //進行代碼分割 主要分割 動态導入的文法
},
//代碼分割會導緻緩存失效 避免檔案的頻繁變更導緻浏覽器緩存失效,
//是以其是更好的利用緩存。提升使用者體驗。
runtimeChunk: {
name: (entrypoint) => `runtime~${entrypoint.name}.js`,
},
},
//webpack解析子產品加載選項
resolve:{
//自動補全檔案擴充名
extensions:[".jsx",".js",".json"],
},
//熱更新
devServer: {
open: true,
host: "localhost",
port: 3000,
hot: true, //開啟HMR
},
}
JSX 配置HMR(熱更新) 功能 React Refresh Webpack Plugin
安裝依賴
npm install -D @pmmmwh/react-refresh-webpack-plugin react-refresh
<!-- webpack.dev.js 隻有開發環境使用-->
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
...
//處理js
{
test:/\.jsx?$/,
include:path.resolve(__dirname,'../src'),
loader:'babel-loader',
options:{
cacheDirectory:true,//開啟緩存
cacheCompression: false,//不要壓縮打包性能更快
plugins:[
'react-refresh/babel' //激活JSX 的HMR 功能
]
},
},
...
plugins:[
...
//react 熱更新
new ReactRefreshWebpackPlugin()
],
...
devServer: {
open: true,
host: "localhost",
port: 3000,
hot: true, //開啟HMR
},
前端路由修改
安裝配置: npm i react-router-dom
//main.js
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("app"));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>);
import React from "react";
import { Link,Route,Routes} from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";
function App(){
return (
<div>
<h1>App</h1>
<ul>
<li><Link to="/home">Home</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
<Routes>
<Route path="/home" element={<Home/>} />
<Route path="/about" element={<About/>} />
</Routes>
</div>
);
}
export default App;
前端路由重新整理404問題
配置:
devServer: {
open: true,
host: "localhost",
port: 3000,
hot: true, //開啟HMR
historyApiFallback:true,//前端路由重新整理404問題
},
}
處理路由元件單獨打包 使用路由懶加載
// import React from "react";
import { Link,Route,Routes} from "react-router-dom";
import React,{Suspense,lazy} from "react"; //lazy函數負責定義路由懶加載的元件
// import Home from "./pages/Home";
// import About from "./pages/About";
const Home=lazy(()=>import(/* webpackChunkName:'home'*/ "./pages/Home"));
const About=lazy(()=>import(/* webpackChunkName:'about'*/ "./pages/About"));
function App(){
return (
<div>
<h1>App</h1>
<ul>
<li><Link to="/home">Home</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
<Suspense fallback={<div>loading....</div>}>
<Routes>
<Route path="/home" element={<Home/>} />
<Route path="/about" element={<About/>} />
</Routes>
</Suspense>
</div>
);
}
export default App;
搭建生産模式配置
指定輸出路徑
output:{
path:path.resolve(__dirname,"../dist"),
filename:"static/js/[name].[contenthash:10].js",
chunkFilename:"static/js/[name].[contenthash:10].chunk.js",//動态import導入的js
assetModuleFilename:"static/media/[hash:10][ext][query]",
clean:true,
},
提取CSS成單獨檔案
、
const MiniCssExtractPlugin =require('mini-css-extract-plugin');
plugins:[
new MiniCssExtractPlugin({
filename:'static/css/[name].[contenthash:10].css',
chunkFilename:'static/css/[name].[contenthash:10].chunk.css'
})
]
處理JS 壓縮 CSS 壓縮
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); //css壓縮
const TerserWebpackPlugin = require("terser-webpack-plugin"); //js壓縮
...
optimization: {
splitChunks:{
chunks:'all', //進行代碼分割 主要分割 動态導入的文法
},
//代碼分割會導緻緩存失效 避免檔案的頻繁變更導緻浏覽器緩存失效,
//是以其是更好的利用緩存。提升使用者體驗。
runtimeChunk: {
name: (entrypoint) => `runtime~${entrypoint.name}.js`,
},
//js壓縮
minimizer:[new CssMinimizerPlugin(),new TerserWebpackPlugin()]
},
圖檔壓縮
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminGenerate,
options: {
plugins: [
["gifsicle", { interlaced: true }],
["jpegtran", { progressive: true }],
["optipng", { optimizationLevel: 5 }],
[
"svgo",
{
plugins: [
"preset-default",
"prefixIds",
{
name: "sortAttrs",
params: {
xmlnsOrder: "alphabetical",
},
},
],
},
],
],
},
},
}),
下載下傳依賴 npm i mini-css-extract-plugin css-minimizer-webpack-plugin -D
下載下傳圖檔依賴包
npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo --save-dev
修改建構啟動變量 package.json
"build": "cross-env NODE_ENV=production webpack --config ./config/webpack.prod.js"
運作npm run build 打包 會有路由重新整理404問題 需要專門配置
網站配置LOGO 圖示 copy-webpack-plugin
安裝: npm install copy-webpack-plugin --save-dev
把public下的資源拷貝到dist目錄下
const CopyPlugin = require("copy-webpack-plugin");
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, "../public"),
to: path.resolve(__dirname, "../dist"),
toType: "dir",
noErrorOnMissing: true, // 不生成錯誤
globOptions: {
// 忽略檔案
ignore: ["**/index.html"],
},
info: {
// 跳過terser壓縮js
minimized: true,
},
},
],
}),
//webpack.prod.js
const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const TerserWebpackPlugin = require("terser-webpack-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const CopyPlugin = require("copy-webpack-plugin");
const getStyleLoaders = (preProcessor) => {
return [
MiniCssExtractPlugin.loader,
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
"postcss-preset-env", // 能解決大多數樣式相容性問題
],
},
},
},
preProcessor,
].filter(Boolean);
};
module.exports = {
entry: "./src/main.js",
output: {
path: path.resolve(__dirname, "../dist"),
filename: "static/js/[name].[contenthash:10].js",
chunkFilename: "static/js/[name].[contenthash:10].chunk.js",
assetModuleFilename: "static/js/[hash:10][ext][query]",
clean: true,
},
module: {
rules: [
{
oneOf: [
{
// 用來比對 .css 結尾的檔案
test: /\.css$/,
// use 數組裡面 Loader 執行順序是從右到左
use: getStyleLoaders(),
},
{
test: /\.less$/,
use: getStyleLoaders("less-loader"),
},
{
test: /\.s[ac]ss$/,
use: getStyleLoaders("sass-loader"),
},
{
test: /\.styl$/,
use: getStyleLoaders("stylus-loader"),
},
{
test: /\.(png|jpe?g|gif|svg)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 10 * 1024, // 小于10kb的圖檔會被base64處理
},
},
},
{
test: /\.(ttf|woff2?)$/,
type: "asset/resource",
},
{
test: /\.(jsx|js)$/,
include: path.resolve(__dirname, "../src"),
loader: "babel-loader",
options: {
cacheDirectory: true,
cacheCompression: false,
plugins: [
// "@babel/plugin-transform-runtime" // presets中包含了
],
},
},
],
},
],
},
plugins: [
new ESLintWebpackPlugin({
context: path.resolve(__dirname, "../src"),
exclude: "node_modules",
cache: true,
cacheLocation: path.resolve(
__dirname,
"../node_modules/.cache/.eslintcache"
),
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname, "../public/index.html"),
}),
new MiniCssExtractPlugin({
filename: "static/css/[name].[contenthash:10].css",
chunkFilename: "static/css/[name].[contenthash:10].chunk.css",
}),
// 将public下面的資源複制到dist目錄去(除了index.html)
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, "../public"),
to: path.resolve(__dirname, "../dist"),
toType: "dir",
noErrorOnMissing: true, // 不生成錯誤
globOptions: {
// 忽略檔案
ignore: ["**/index.html"],
},
info: {
// 跳過terser壓縮js
minimized: true,
},
},
],
}),
],
optimization: {
// 壓縮的操作
minimizer: [
new CssMinimizerPlugin(),
new TerserWebpackPlugin(),
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminGenerate,
options: {
plugins: [
["gifsicle", { interlaced: true }],
["jpegtran", { progressive: true }],
["optipng", { optimizationLevel: 5 }],
[
"svgo",
{
plugins: [
"preset-default",
"prefixIds",
{
name: "sortAttrs",
params: {
xmlnsOrder: "alphabetical",
},
},
],
},
],
],
},
},
}),
],
splitChunks: {
chunks: "all",
},
runtimeChunk: {
name: (entrypoint) => `runtime~${entrypoint.name}`,
},
},
resolve: {
extensions: [".jsx", ".js", ".json"],
},
mode: "production",
devtool: "source-map",
};