现代webpack/react/typescript/pnpm项目模板,从零到一搭建webpack项目

项目模板

模板地址 如果急用,直接使用当前模板即可。点击右上角Use This Template即可创建一个新的项目。

背景

当我每每创建一个新的webpack项目时,总是需要经过繁琐的webpack配置来完成项目的init。如果从网络上搜寻快速的setup总会遇到各种各样的问题(由于包的版本有更新,有些配置已经废弃掉了)所有我决定搭建自己的webpack配置模板。

搭建步骤

1. pnpm 开启webpack项目

1.1 生成package.json

bash 复制代码
pnpm init

1.2 引入webpack

bash 复制代码
pnpm add -D webpack webpack-cli webpack-dev-server

1.3 引入typescript

bash 复制代码
pnpm add -D typescript ts-node @types/node

1.4 引入react

bash 复制代码
pnpm add react react-dom
bash 复制代码
pnpm add -D @types/react @types/react-dom

2. 初始化react代码

2.1 创建src/app.tsx

ts 复制代码
const App = () => {
    return <div>Hello World</div>
}
export default App

2.2 创建src/index.tsx

ts 复制代码
import { createRoot } from 'react-dom/client'
import App from './app'

createRoot(document.getElementById('root')!).render(<App />)

3. webpack配置

3.1 创建webpack.config.ts

ts 复制代码
import path from 'path'
import { fileURLToPath } from 'url'
import type { Configuration } from 'webpack'

const rootDir = path.dirname(fileURLToPath(import.meta.url))

const config: Configuration = {
    entry: './src/index.tsx',
    output: {
        path: path.resolve(rootDir, 'dist'),
        filename: '[name].[contenthash].js'
    },
    resolve: {
        extensions: ['.ts', '.tsx', '.js', '.jsx']
    },
    devtool: 'source-map',
    module: {},
    mode: 'development'
}

export default config

3.2 设置webpack插件

bash 复制代码
pnpm add -D html-webpack-plugin clean-webpack-plugin

在public下创建index.html

html 复制代码
<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Webpack React Template</title>
    </head>
    <body>
        <div id="root"></div>
    </body>
</html>

webpack 补充插件配置以及devServer配置

ts 复制代码
import path from 'path'
import { fileURLToPath } from 'url'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import { CleanWebpackPlugin } from 'clean-webpack-plugin'
import type { Configuration } from 'webpack'
import 'webpack-dev-server'

const rootDir = path.dirname(fileURLToPath(import.meta.url))

const config: Configuration = {
    entry: './src/index.tsx',
    output: {
        path: path.resolve(rootDir, 'dist'),
        filename: '[name].[contenthash].js'
    },
    resolve: {
        extensions: ['.ts', '.tsx', '.js', '.jsx']
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './public/index.html'
        }),
        new CleanWebpackPlugin()
    ],
    devtool: 'source-map',
    devServer: {
        static: {
            directory: path.join(rootDir, 'public')
        },
        compress: true,
        historyApiFallback: true
    },
    mode: 'development'
}

export default config

3.3 设置webpack loader(style)

bash 复制代码
pnpm add -D style-loader css-loader sass sass-loader

引入到webpack config的rules中:

ts 复制代码
const config: Configuration = {
    entry: './src/index.tsx',
    output: {
        path: path.resolve(rootDir, 'dist'),
        filename: '[name].[contenthash].js'
    },
    resolve: {
        extensions: ['.ts', '.tsx', '.js', '.jsx']
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './public/index.html'
        }),
        new CleanWebpackPlugin()
    ],
    devtool: 'source-map',
    module: {
        rules: [
            {
                test: /\.css$/i,
                use: ['style-loader', 'css-loader']
            },
            {
                test: /\.scss$/i,
                use: ['style-loader', 'css-loader', 'sass-loader']
            },
            {
                test: /\.(png|jpg|jpeg|gif|svg)$/i,
                type: 'asset/resource'
            }
        ]
    },
    devServer: {
        static: {
            directory: path.join(rootDir, 'public')
        },
        compress: true,
        historyApiFallback: true
    },
    mode: 'development'
}

export default config

这里还引入静态资源的rules直接从asset/resource中获取。 因为我们引入了sass,这里我们还需要定义sass文件(.sass,.scss)的模块类型,在src/types里创建index.d.ts:

ts 复制代码
declare module '*.scss' {
    const content: { [className: string]: string }
    export default content
}

3.4 设置webpack babel

bash 复制代码
pnpm add -D @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript babel-loader

在config进行如下配置

ts 复制代码
import path from 'path'
import { fileURLToPath } from 'url'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import { CleanWebpackPlugin } from 'clean-webpack-plugin'
import type { Configuration } from 'webpack'
import 'webpack-dev-server'

const rootDir = path.dirname(fileURLToPath(import.meta.url))

const config: Configuration = {
    entry: './src/index.tsx',
    output: {
        path: path.resolve(rootDir, 'dist'),
        filename: '[name].[contenthash].js'
    },
    resolve: {
        extensions: ['.ts', '.tsx', '.js', '.jsx']
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: './public/index.html'
        }),
        new CleanWebpackPlugin()
    ],
    devtool: 'source-map',
    module: {
        rules: [
            {
                test: /\.(ts|js)x?$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                '@babel/preset-env',
                                ['@babel/preset-react', { runtime: 'automatic' }],
                                '@babel/preset-typescript'
                            ]
                        }
                    }
                ]
            },
            {
                test: /\.css$/i,
                use: ['style-loader', 'css-loader']
            },
            {
                test: /\.scss$/i,
                use: ['style-loader', 'css-loader', 'sass-loader']
            },
            {
                test: /\.(png|jpg|jpeg|gif|svg)$/i,
                type: 'asset/resource'
            }
        ]
    },
    devServer: {
        static: {
            directory: path.join(rootDir, 'public')
        },
        compress: true,
        historyApiFallback: true
    },
    mode: 'development'
}

export default config

4. typescript配置

根目录上创建tsconfg.json

ts 复制代码
{
    "compilerOptions": {
        "module": "esnext",
        "target": "esnext",
        "moduleResolution": "bundler",
        "lib": ["dom", "dom.iterable", "esnext"],

        "sourceMap": true,
        "declaration": true,
        "declarationMap": true,

        "noUncheckedIndexedAccess": true,
        "exactOptionalPropertyTypes": true,

        "strict": true,
        "jsx": "react-jsx",
        "jsxImportSource": "react",
        "verbatimModuleSyntax": true,
        "isolatedModules": true,
        "noUncheckedSideEffectImports": true,
        "moduleDetection": "force",
        "skipLibCheck": true
    }
}

通过上述配置,我们修改package.json的scripts

json 复制代码
 "scripts": {
 	"start": "webpack serve --open --port 3210",
    "build": "webpack"
}

此时运行pnpm run start即可在3210端口访问项目。

接下来的内容是锦上添花:优化工程,即代码风格格式化,typescript eslint规则校验,使用git hooks触发生命周期钩子

5. 使用prettier格式化代码

bash 复制代码
pnpm add -D prettier

在根目录创建.prettierrc

json 复制代码
{
    "semi": false,
    "singleQuote": true,
    "trailingComma": "none",
    "tabWidth": 4,
    "useTabs": false,
    "printWidth": 120,
    "bracketSpacing": true,
    "arrowParens": "avoid",
    "endOfLine": "auto"
}

在package.json配置格式化脚本

json 复制代码
 "scripts": {
        "start": "webpack serve --open --port 3210",
        "build": "webpack",
        "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,md}\""
    },

执行即可把src中所有代码文件格式化

6. 配置eslint

bash 复制代码
pnpm add -D eslint typescript-eslint eslint-plugin-react eslint-plugin-react-hooks eslint-webpack-plugin

根目录创建eslint.config.js

javascript 复制代码
import tseslint from 'typescript-eslint'
import reactPlugin from 'eslint-plugin-react'
import reactHooks from 'eslint-plugin-react-hooks'

export default [
    ...tseslint.configs.recommended,
    {
        files: ['**/*.{ts,tsx}'],
        plugins: {
            react: reactPlugin,
            'react-hooks': reactHooks
        },
        rules: {
            ...reactPlugin.configs.recommended.rules,
            ...reactHooks.configs.recommended.rules
        },
        settings: {
            react: { version: 'detect' }
        }
    }
]

在packages的scripts脚本中写入

json 复制代码
"scripts": {
        "start": "webpack serve --open --port 3210",
        "build": "webpack",
        "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,md}\"",
        "lint": "eslint --ext .ts,.tsx",
        "lint:fix": "eslint --ext .ts,.tsx --fix"
    },

7. husky 设置lint-staged

bash 复制代码
pnpm add -D husky lint-staged

7.1 初始化git仓库

bash 复制代码
git init

7.2 初始化husky

  1. npx方式
bash 复制代码
npx husky init
  1. pnpm方式
bash 复制代码
pnpm exec husky init

7.3 配置husky的pre-commit钩子

在.husky中创建pre-commit(无后缀)文件 写入

bash 复制代码
npx lint-staged

并在package.json中的根object里写入lint-staged配置

json 复制代码
 "lint-staged": {
        "src/**/*.{ts,tsx}": [
            "eslint --fix"
        ]
    }

此时,每当你git commit的时候它都会先执行eslint

8. husky设置commit message

为了规范每次提交记录的message,我们使用commitlint规范:

vbnet 复制代码
feat: add new feature
fix: bug fix
docs: documentation changes
style: formatting changes
refactor: code refactoring
test: adding tests
chore: maintenance tasks

引入commit-lint

bash 复制代码
pnpm add -D @commitlint/cli @commitlint/config-conventional

创建commitlint.config.js

javascript 复制代码
export default {
    extends: ['@commitlint/config-conventional']
}

在.husky目录中创建commit-msg(无后缀)文件并写入:

bash 复制代码
pnpm exec commitlint --edit $1

此后后续的commit提交的message都会匹配是否以上述规范中的lint的title相匹配,比如我提交一个需求必须以:feat: 开头

9. 创建.gitignore

屏蔽掉常见的本地配置/依赖项

bash 复制代码
# Dependencies
node_modules
.pnpm-store

# Build output
dist

# IDE
.idea
.vscode
*.swp
*.swo

# OS
.DS_Store
Thumbs.db

# Logs
*.log
npm-debug.log*

# Environment
.env
.env.local
.env.*.local
相关推荐
LYFlied8 小时前
【一句话概述】Webpack、Vite、Rollup 核心区别
前端·webpack·node.js·rollup·vite·打包·一句话概述
转转技术团队12 小时前
前端工程化实践:打包工具的选择与思考
前端·javascript·webpack
小明记账簿1 天前
项目启功需要添加SKIP_PREFLIGHT_CHECK=true该怎么办?
webpack·打包
拉不动的猪2 天前
webpack编译中为什么不建议load替换ast中节点删除consolg.log
前端·javascript·webpack
PAQQ3 天前
ubuntu22.04 搭建 Opencv & C++ 环境
前端·webpack·node.js
老前端的功夫4 天前
Webpack打包机制与Babel转译原理深度解析
前端·javascript·vue.js·webpack·架构·前端框架·node.js
小胖霞4 天前
企业级全栈 RBAC 实战 (11):菜单管理与无限层级树形表格
vue.js·前端框架·前端工程化
LYFlied4 天前
Webpack 深度解析:从原理到工程实践
前端·面试·webpack·vite·编译原理·打包·工程化
LYFlied4 天前
从循环依赖检查插件Circular Dependency Plugin源码详解Webpack生命周期以及插件开发
前端·webpack·node.js·编译原理·plugin插件开发