react+typescript+wbpack5项目搭建

步骤一:初始化webpack

npm init -y

创建目录 public,scropts,src
在这里插入图片描述

步骤二:创建文件 .gitignore 配置忽略提交文件

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*

步骤三:

创建文件 webpack.common.js:用于编写公共配置
创建文件 webpack.dev.js:用于编写测试环境配置
创建文件 webpack.prod.js:用于编写生产打包环境配置

虽然都分开了配置,但是在公共配置中,还是可能会出现某个配置的某个选项在开发环境和生产环境中采用不同的配置,这个时候有两种选择:

  • 分别在 dev 和 prod 配置文件中写一遍,common 中就不写了
  • 设置某个环境变量,根据这个环境变量来判别不同环境

为了代码优雅性选择第二种方案,下载所需第三方包cross-env :统一配置Node环境变量

npm i cross-env

步骤四:配置webpack

1,安装webpack相关包
npm install --save-dev webpack
npm install --save-dev webpack-cli
npm install --save-dev webpack-dev-server
npm i webpack-merge@5.7.3
npm install  --save-dev html-webpack-plugin
2,添加html模板文件和webpack入口文件index.js
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div id="root"></div>
</body>

</html>
// src/index.js
const ele = document.querySelector('#root')
ele.innerHTML = '测试'
3,添加启动项目配置
// package.json
"scripts": {
    "start": "cross-env NODE_ENV=development webpack-dev-server --config ./scripts/config/webpack.dev.js",
    "build": "cross-env NODE_ENV=production webpack --config ./scripts/config/webpack.prod.js"
},
4,配置公共配置,然后分别引入开发、生产环境配置文件。
// webpack.common.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { PROJECT_PATH } = require('../constant')

module.exports = {
  entry: {
    app: path.resolve(PROJECT_PATH, './src/app.js')
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(PROJECT_PATH, './index.html'),
    }),
  ]
}
5,配置开发环境,引入公共配置并且添加 webpack-dev-server的配置
const { merge } = require('webpack-merge')
const path = require('path')
const webpack = require('webpack')

const common = require('./webpack.common')
const { PROJECT_PATH, SERVER_HOST, SERVER_PORT } = require('../constant')

module.exports = merge(common, {
  mode: 'development',
  devtool: 'cheap-module-source-map',
  output: {
    filename: 'js/[name].js',
    path: path.resolve(PROJECT_PATH, './dist')
  },
  devServer: {
    host: SERVER_HOST, // 服务ip
    port: SERVER_PORT, // 服务端口
    // stats: 'errors-only', // 设为errors-only表示终端只打印错误类型的日志,不会打印warning以及其他信息影响阅读
    // clientLogLevel: 'none', // 设为none表示去除多余网页console信息
    compress: true, // 设为true表示启用gzip压缩,加快网站打开速度
    open: true, // 设为true表示第一次启动项目时自动打开默认浏览器
    hot: true, // 设为true表示启用服务热替换配置
    // noInfo: true, // 设为true表示去除启动项目时显示的打包信息
    https: true, // 启动项目的时候,默认是https协议
  },
  plugins: [
    // 实际上只开启 hot:true 就会自动识别有无声明该插件,没有则自动引入,但是怕有隐藏问题这里还是手动加上了
    new webpack.HotModuleReplacementPlugin()
  ]
})

小工具
webpackbar:用于显示打包进度条,会更有体验感

npm install webpackbar -D
// webpack.common.js
const WebpackBar = require('webpackbar')
module.exports = {
  plugins: [
    new WebpackBar({
      name: 'Link Startou!!!',
      color: '#52c41a'
    }),
  ]
}
6,配置生产环境

同样引入公共配置,生产环境一般输出文件都会加上哈希值,而开发环境不需要,开发环境使用 webpack-dev-server 启动时不会在项目中真正的产生文件,而是存在了内存中,主要是为了打包。

// webpack.prod.js
const { merge } = require('webpack-merge')
const path = require('path')

const common = require('./webpack.common')
const { PROJECT_PATH } = require('../constant')

module.exports = merge(common, {
  mode: 'production',
  devtool: false,
  output: {
    filename: 'js/[name].[contenthash:8].js',
    path: path.resolve(PROJECT_PATH, './dist')
  },
})

步骤五:CSS相关配置

1,配置css
npm install --save-dev style-loader
npm install --save-dev css-loader
npm install --save-dev postcss-loader postcss
npm install postcss-preset-env --save-dev

style-loader:将 js 文件中引入的 css 代码插入到 html 模板文件,使网页可以正常展示样式
css-loader:令 js 可以通过 import 或者 require 等命令导入 css 代码
postcss-loader postcss:与 sass/less 不同,不是预处理器,相当于一个工具箱,可以使用它配合插件去转换css
postcss-preset-env:将最新的 css 语法转换为目标环境的浏览器能够理解的语法,不用考虑浏览器兼容问题,以前需要配合 autoprefixer 第三方包自动补全前缀,现在新版本已经内置autoprefixer功能了

2,配置预处理器
npm install less less-loader --save-dev
npm install node-sass --save-dev
npm install sass-loader sass webpack --save-dev

less:为 less-loader 提供依赖
less-loader:将 less 代码转换为 css 代码
node-sass:为 sass-loader 提供依赖
sass-loader:将 sass 代码转换为 css 代码

配置less

// webpack.common.js module.rules
{
  test: /\.less$/,
  use: [
    {
      loader: 'style-loader',
    },
    {
      loader: 'css-loader',
      options: {
        modules: {
          localIdentName: '[name]-[local]-[hash:base64:8]',
        },
      },
    },
    {
      loader: 'postcss-loader',
    },
    {
      loader: 'less-loader',
    },
  ],
},

配置sass

// webpack.common.js module.rules
{
  test: /\.scss$/,
  use: [
    {
      loader: 'style-loader',
    },
    {
      loader: 'css-loader',
      options: {
        modules: {
          localIdentName: '[name]-[local]-[hash:base64:8]',
        },
      },
    },
    {
      loader: 'postcss-loader',
    },
    {
      loader: 'sass-loader',
    },
  ],
},

步骤六:Babel相关配置

兼容 ES6+

npm install -D babel-loader @babel/core @babel/preset-env
npm install @babel/runtime-corejs3
npm install --save-dev @babel/plugin-transform-runtime

babel-loader:用于处理 ES6+ 语法,将其编译为浏览器可以执行的 js
@babel/core:babel 所需依赖
@babel/preset-env:是一组ES6转译的plugins,会根据设置的目标浏览器环境(browserslist),选择该环境不支持的新特性进行转译,这样就减少了不必要转译,增快打包速度。
@babel/plugin-transform-runtime:提供 ES6+ 的 api,如 es6 新的数组方法等,和 @babel/polyfill 不同的是该包可以实现按需加载,不会全部引入影响打包速度,需要依赖 runtime-corejs。
@babel/runtime-corejs3:相当于 @babel/polyfill 的功能,在 7.4.0 版本后的 babel 使用 runtime-core 代替了 babel/polyfill。

1,Babel 在执行编译的过程中,会从项目的根目录下的 .babelrc 文件中读取配置(本质上是json格式文件),因此此时需要在项目根目录新建该文件

//  .babelrc 
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules": false
      }
    ]
  ],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": {
          "version": 3,
          "proposals": true
        }
      }
    ]
  ]
}

2,给 webpack 配上对 js 文件应用,一并配置tsx和jsx

// webpack.common.js module.rules
{
  test: /\.[jt]sx?$/,
   exclude: /node_modules/, // 排除 node_modules 文件夹
   use: {
     loader: 'babel-loader', // babel-loader  babel-loader处理JSX语法的。
     options: {
       babelrc: true,
       cacheDirectory: true,
     },
   },
 },

步骤七:支持React

npm i react react-dom @types/react @types/react-dom @babel/preset-react

react:react核心依赖
react-dom:负责处理web端的dom的渲染
@types/react :react 类型声明文件,用于 tsx
@types/react-dom:react-dom 类型声明文件,用于 tsx
@babel/preset-react :用于让 babel 可以转译 jsx 语法

配置.babelrc

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules": false
      }
    ],
    "@babel/preset-react"
  ],
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": {
          "version": 3,
          "proposals": true
        }
      }
    ]
  ]
}

步骤八:支持ts

npm install --save typescript @babel/preset-typescript

typescript:支持 ts
@babel/preset-typescript:处理ts文件,原理是去掉ts的类型声明,然后再用其他 babel 插件进行编译

1,修改 .babelrc

"presets": [
    [
      "@babel/preset-env",
      {
        "modules": false
      }
    ],
    "@babel/preset-react",
    "@babel/preset-typescript"
  ],

2,配置 tsconfig.json

{
  "compilerOptions": {
    "target": "ES5", /* 指定 ECMAScript 目标版本:'ES3'、'ES5'(默认)、'ES2015'、'ES2016'、'ES2017'、'ES2018'、'ES2019'、'ES2020' 或 'ESNEXT'。 */
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ], /* 指定要包含在编译中的库文件。 */
    // "allowJs": true, /* 允许编译 javascript 文件。打开会导致check到nvm中的一些js文件 */
    "skipLibCheck": true, /* 跳过声明文件的类型检查。 */
    "esModuleInterop": true, /* 通过为所有导入创建命名空间对象,在 CommonJS 和 ES 模块之间启用发射互操作性。 暗示“allowSyntheticDefaultImports”。 */
    "allowSyntheticDefaultImports": true, /* 允许从没有默认导出的模块中默认导入。 这不会影响代码发出,只是类型检查。 */
    "strict": true, /* 启用所有严格的类型检查选项。 在开发中,建议将stricet这类选项都开启。 */
    "forceConsistentCasingInFileNames": true, /* 禁止对同一文件的大小写不一致的引用。 */
    "noFallthroughCasesInSwitch": true, /* 在 switch 语句中报告失败情况的错误。 */
    "module": "esnext", /* 指定模块代码生成:“none”、“commonjs”、“amd”、“system”、“umd”、“es2015”、“es2020”或“ESNext”。 */
    "moduleResolution": "node", /* 指定模块解析策略:'node' (Node.js) 或 'classic' (TypeScript pre-1.6)。 */
    "resolveJsonModule": true, //允许导入扩展名为“.json”的模块
    "isolatedModules": true, //将每个文件作为单独的模块(与“ts.transpileModule”类似)。
    // "noEmit": true, /* 不发出输出(不生成编译后的文件)。 */
    "jsx": "react", /* 指定 JSX 代码生成:'preserve'、'react-native' 或 'react'。 */
  },
  "include": [
    "src/**/*"
  ], //包含的文件
}

由于使用了 ts,在导入文件的时候可能会出现类型错误,如 css module 的 import style from ‘./index.scss’ 报错 找不到模块“./index.scss”或其相应的类型声明,因此需要手动编写声明类型文件,在 src 目录下新建 file.d.ts 文件

// src/file.d.ts
declare module "*.css" {
  const style: any;
  export default style
}

declare module "*.scss" {
  const style: any;
  export default style
}

declare module "*.less" {
  const style: any;
  export default style
}

步骤九:优化

1,webpack 可以省略后缀名查找并处理文件

module.exports = {
  resolve: {
    extensions: ['.js', '.jsx', '.tsx', '.ts', '.json'],
  },
}

2,terser-webpack-plugin:用去去除生产环境的无用js代码,webpack5 之后自带,不需要另行安装,直接引入使用

// webpack.prod.js
const TerserPlugin = require("terser-webpack-plugin")

module.exports = merge(common, {
  optimization: {
    minimize: true,
    minimizer:[
      new TerserPlugin({
        extractComments: false,
        terserOptions: {
          compress: { pure_funcs: ['console.log'] },
        }
      }),
    ]
  }
})

extractComments:设为 false 表示去除所有注释,除了有特殊标志的注释如 @preserve 标记
pure_funcs:去除函数,如上述配置的意思是将所有 console.log 函数去除

3,fork-ts-checker-webpack-plugin:ts 类型检查,让终端可以显示类型错误

npm install --save-dev fork-ts-checker-webpack-plugin
// webpack.common.js
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')
module.exports = {
  plugins: [
    new ForkTsCheckerWebpackPlugin({
      typescript: {
        configFile: path.resolve(PROJECT_PATH, './tsconfig.json'),
      },
    }),
  ],
}

步骤十:配置资源

之前处理图片字体之类的资源的时候等,需要用到url-loader,file-loader
现在webpack5 已内置资源模块,因此无需再下载直接配置即可,更多详情参考 【webpack5 新特性asset】

asset/resource:file-loader的功能。
asset/inline:url-loader的功能。
asset:使用这个配置,默认小于 8kb 的文件将被视为inline模块类型,将转为 Data URI 以内联的方式使用;大于 8kb 的文件将被视为resource 模块类型,打包成单独的文件,之前有url-loader设置asset size limit 限制实现

{
  test: /\.(png|jpg|jpeg|mp4|gif|mov)$/i,
  type: "asset",
  parser: {
    dataUrlCondition: {
      maxSize: 10 * 1024, // 小于10kb的会被base64处理
    },
  },
},
{
  test: /\.(dae|eot|woff|woff2|svg|ttf|otf)$/,
  type: 'asset/resource'
},

规定打包目录,配置一下生产环境的打包出口

// scripts/config/webpack.prod.js
module.exports = merge(common, {
  output: {
    // ...other
    assetModuleFilename: 'images/[name].[contenthash:8].[ext]',
  },
})

因为使用的是 ts,导入上述文件后缀名会产生不存在的错误,因此继续添加声明文件

// src/file.d.ts
declare module '*.png' {
  const path: string
  export default path
}
declare module '*.jpg' {
  const path: string
  export default path
}
declare module '*.jpeg' {
  const path: string
  export default path
}
declare module '*.gif' {
  const path: string
  export default path
}
declare module '*.mov' {
  const path: string
  export default path
}
declare module '*.svg' {
  const path: string
  export default path
}

//.......

处理非动态导入静态资源

copy-webpack-plugin:处理不需要动态导入的静态资源文件,将其复制进打包目录

npm install copy-webpack-plugin --save-dev
const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
  plugins: [
	new CopyWebpackPlugin({
	  patterns: [
	    {
	      from: path.join(__dirname, '../../src/resource'),
	      to: path.join(__dirname, '../../dist/resource'),
	    },
	  ],
	}),
  ]
}

测试

将src下的index.js修改为index.tsx并且修改webpack.common.js入口文件
注意:react18之后废弃了ReactDOM.render,改用createRoot

import React from 'react'
import ReactDOM from 'react-dom/client'
import styles from './app.scss'

const container = document.getElementById('root') as HTMLElement
const root = ReactDOM.createRoot(container)
root.render(<div className={styles.title}>阶段测试</div>)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值