前端工程化---webpack从0到1搭建前端项目

151 阅读3分钟

image.png

一、初始化项目

  • 1、初始化package.json
// 生成package.json
npm init 

项目中新增目录结构和文件

├── build
|   ├── webpack.base.js # 公共配置
|   ├── webpack.dev.js  # 开发环境配置
|   └── webpack.prod.js # 打包环境配置
├── public
│   └── index.html # html模板
├── src
|   ├── App.tsx 
│   └── index.tsx # react应用入口页面
├── tsconfig.json  # ts配置
└── package.json

  • 2、安装依赖
npm i webpack webpack-cli -D

npm i react react-dom -S

npm i @types/react @types/react-dom -D
  • 3、添加public/index.html内容
<!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>webpack5-react-ts</title>
</head>
<body>
  <!-- 容器节点 -->
  <div id="root"></div>
</body>
</html>

  • 4、添加tsconfig.json内容
{
  "compilerOptions": {
    "target": "ESNext",
    "lib": ["DOM", "DOM.Iterable", "ESNext"],
    "allowJs": false,
    "skipLibCheck": false,
    "esModuleInterop": false,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "ESNext",
    "moduleResolution": "Node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react", // react18这里也可以改成react-jsx
  },
  "include": ["./src"]
}

  • 5、添加src/App.tsx内容
import React from 'react'

function App() {
  return <h2>webpack5-react-ts</h2>
}
export default App

  • 6、添加src/index.tsx内容
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

const root = document.getElementById('root');
if(root) {
  createRoot(root).render(<App />)
}

二、配置环境

2.1 webpack公共配置(webpack.base.js)

  • 1、配置入口文件
// webpack.base.js
const path = require('path')

module.exports = {
  entry: path.join(__dirname, '../src/index.tsx'), // 入口文件
}

  • 2、配置出口文件
// webpack.base.js
const path = require('path')

module.exports = {
  // ...
  // 打包文件出口
  output: {
    filename: 'static/js/[name].js', // 每个输出js的名称
    path: path.join(__dirname, '../dist'), // 打包结果输出路径
    clean: true, // webpack4需要配置clean-webpack-plugin来删除dist文件,webpack5内置了
    publicPath: '/' // 打包后文件的公共前缀路径
  },
}

  • 3、配置loader解析ts和jsx 由于webpack默认只能识别js文件,不能识别jsx语法,需要配置loader的预设预设 [ @babel/preset-typescript]来先ts语法转换为 js 语法,再借助预设 [ @babel/preset-react] 来识别jsx语法。
npm i babel-loader @babel/core @babel/preset-react @babel/preset-typescript -D

在webpack.base.js添加module.rules配置

// webpack.base.js
module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /.(ts|tsx)$/, // 匹配.ts, tsx文件
        use: {
          loader: 'babel-loader',
          options: {
            // 预设执行顺序由右往左,所以先处理ts,再处理jsx
            presets: [
              '@babel/preset-react',
              '@babel/preset-typescript'
            ]
          }
        }
      }
    ]
  }
}

  • 4、配置extensions

extensionswebpackresolve解析配置下的选项,在引入模块时不带文件后缀时,会来该配置数组里面依次添加后缀查找文件,因为ts不支持引入以 .ts, tsx为后缀的文件,所以要在extensions中配置,而第三方库里面很多引入js文件没有带后缀,所以也要配置下js

修改webpack.base.js,(把高频出现的文件后缀放在前面)

// webpack.base.js
module.exports = {
  // ...
  resolve: {
    extensions: ['.js', '.tsx', '.ts'],
  }
}

这里只配置jstsxts,其他文件引入都要求带后缀,可以提升构建速度。

  • 5、添加html-webpack-plugin插件
npm i html-webpack-plugin -D

// webpack.base.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  // ...
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, '../public/index.html'), // 模板取定义root节点的模板
      inject: true, // 自动注入静态资源
    })
  ]
}

2.2 webpack开发环境配置

  • 1、安装webpack-dev-server 开发环境配置代码在webpack.dev.js中,借助webpack-dev-server在开发环境启动服务器来辅助开发,还需要依赖webpack-merge来合并基本配置。
npm i webpack-dev-server webpack-merge -D

修改webpack.dev.js 代码,合并公共配置,添加开发模式配置

// webpack.dev.js
const path = require('path')
const { merge } = require('webpack-merge')
const baseConfig = require('./webpack.base.js')

// 合并公共配置,并添加开发环境配置
module.exports = merge(baseConfig, {
  mode: 'development', // 开发模式,打包更加快速,省了代码优化步骤
  devtool: 'eval-cheap-module-source-map', // 源码调试模式
  devServer: {
    port: 3000, // 服务端口号
    compress: false, // gzip压缩,开发环境不开启,提升热更新速度
    hot: true, // 开启热更新,后面会讲react模块热替换具体配置
    historyApiFallback: true, // 解决history路由404问题
    static: {
      directory: path.join(__dirname, "../public"), //托管静态资源public文件夹
    }
  }
})

  • 2、package.json添加dev脚本
// package.json
"scripts":{
 "dev":"webpack-dev-server -c build/webpack.dev.js"
}

2.3webpack打包环境配置

  • 1、修改webpack.prod.js代码
// webpack.prod.js

const { merge } = require('webpack-merge')
const baseConfig = require('./webpack.base.js')
module.exports = merge(baseConfig, {
  mode: 'production', // 生产模式,会开启tree-shaking和压缩代码,以及其他优化
})

  • 2、package.json添加build打包命令脚本
// package.json
"scripts":{
 "dev":"webpack-dev-server -c build/webpack.dev.js",
 “build”:"webpack -c build/webpack.prod.js"
}
  • 3、浏览器查看打包结果 打包后的dist文件可以在本地借助node服务器server打开,安装npm i serve -g,然后在项目根目录命令行执行serve -s dist,就可以启动打包后的项目。

三、基础功能配置

3.1配置环境变量

环境变量分两种:

  • 开发模式还是打包构建模式
  • 项目业务环境,开发/测试/预发布/正式

开发模式还是打包构建模式可以用process.env.NODE_ENV,

项目接口环境可以自定义一个环境变量process.env.BASE_ENV,设置环境变量可以借助cross-env和webpack.DefinePlugin来设置

  • cross-env:兼容各系统的设置环境变量的包
  • webpack.DefinePlugin:webpack内置的插件,为业务代码注入环境变量

安装cross-env

npm i cross-env -D

修改package.json的script脚本字段,删除原来的dev和build,修改为下面的:

"scripts": {
    "dev:dev": "cross-env NODE_ENV=development BASE_ENV=development webpack-dev-server -c build/webpack.dev.js",
    "dev:test": "cross-env NODE_ENV=development BASE_ENV=test webpack-dev-server -c build/webpack.dev.js",
    "dev:pre": "cross-env NODE_ENV=development BASE_ENV=pre webpack-dev-server -c build/webpack.dev.js",
    "dev:prod": "cross-env NODE_ENV=development BASE_ENV=production webpack-dev-server -c build/webpack.dev.js",
    
    "build:dev": "cross-env NODE_ENV=production BASE_ENV=development webpack -c build/webpack.prod.js",
    "build:test": "cross-env NODE_ENV=production BASE_ENV=test webpack -c build/webpack.prod.js",
    "build:pre": "cross-env NODE_ENV=production BASE_ENV=pre webpack -c build/webpack.prod.js",
    "build:prod": "cross-env NODE_ENV=production BASE_ENV=production webpack -c build/webpack.prod.js",
  },

dev开头是开发模式,build开头是打包模式,冒号后面对应的dev/test/pre/prod是对应的业务环境的开发/测试/预测/正式环境。

process.env.NODE_ENV环境变量webpack会自动根据设置的mode字段来给业务代码注入对应的developmentprodction,这里在命令中再次设置环境变量NODE_ENV是为了在webpackbabel的配置文件中访问到。

修改webpack.base.js

// webpack.base.js
// ...
const webpack = require('webpack')
module.export = {
  // ...
  plugins: [
    // ...
    new webpack.DefinePlugin({
      'process.env.BASE_ENV': JSON.stringify(process.env.BASE_ENV)
    })
  ]
}

3.2 处理css和less文件

安装loader解析CSS,为了更好的开发体验,一般会使用css超集less或scss。

npm i style-loader css-loader less -D
  • style-loader:把解析后的css代码从js中抽离,放到头部style标签中
  • css-loader:解析css文件代码

解析css的配置开发和打包环境都会用到,需要在webpack.base.js中添加配置

// webpack.base.js
// ...
module.exports = {
  // ...
  module: { 
    rules: [
      // ...
      {
        test:/.(css|less)&/, //匹配 css 文件
        use: ['style-loader','css-loader','less-loader']
      }
    ]
  },
  // ...
}

loader执行顺序是从右往左,从下往上的,匹配到css文件后先用css-loader解析css, 最后借助style-loadercss插入到头部style标签中。

3.3 处理CSS3前缀兼容

借助插件自动添加前缀,postcss-loader

安装依赖:

npm i postcss-loader autoprefixer -D
  • postcss-loader:处理css时自动加前缀
  • autoprefixer:添加哪些浏览器前缀到css中

修改webpack.base.js,解析css和less的规则中添加配置

module.exports = {
  // ...
  module: { 
    rules: [
      // ...
      {
        test: /.(css|less)$/, //匹配 css和less 文件
        use: [
          'style-loader',
          'css-loader',
          // 新增
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: ['autoprefixer']
              }
            }
          },
          'less-loader'
        ]
      }
    ]
  },
  // ...
}

配置完成后,需要一份兼容浏览器的清单,让postcss-loader知道要加哪些浏览器的前缀,在根目录创建.browserslistrc文件

IE 9 #兼容IE 9
chrome 35 #兼容chrome 35

postcss.config.js是postcss-loader的配置文件,会自动读取配置,根目录新建postcss.config.js

module.exports = {
 plugins:['autoprefixer']
}

修改webpack.base.js,取消postcss-loader的options配置


// webpack.base.js
// ...
module.exports = {
  // ...
  module: { 
    rules: [
      // ...
      {
        test: /.(css|less)$/, //匹配 css和less 文件
        use: [
          'style-loader',
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      },
    ]
  },
  // ...
}

3.4babel预设处理js兼容

安装依赖:

npm i babel-loader @babel/core @babel/preset-env core-js -D
  • babel-loader: 使用 babel 加载最新js代码并将其转换为 ES5(上面已经安装过)

  • @babel/corer: babel 编译的核心包

  • @babel/preset-env: babel 编译的预设,可以转换目前最新的js标准语法

  • core-js: 使用低版本js语法模拟高版本的库,也就是垫片

修改webpack.base.js

// webpack.base.js
module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /.(ts|tsx)$/,
        use: {
          loader: 'babel-loader',
          options: {
            // 执行顺序由右往左,所以先处理ts,再处理jsx,最后再试一下babel转换为低版本语法
            presets: [
              [
                "@babel/preset-env",
                {
                  // 设置兼容目标浏览器版本,这里可以不写,babel-loader会自动寻找上面配置好的文件.browserslistrc
                  // "targets": {
                  //  "chrome": 35,
                  //  "ie": 9
                  // },
                   "useBuiltIns": "usage", // 根据配置的浏览器兼容,以及代码中使用到的api进行引入polyfill按需添加
                   "corejs": 3, // 配置使用core-js低版本
                  }
                ],
              '@babel/preset-react',
              '@babel/preset-typescript'
            ]
          }
        }
      }
    ]
  }
}

避免webpack配置文件过于庞大,可以把babel-loader的配置抽离出来, 新建babel.config.js文件,使用js作为配置文件,是因为可以访问到process.env.NODE_ENV环境变量来区分是开发还是打包模式。

// babel.config.js
module.exports = {
  // 执行顺序由右往左,所以先处理ts,再处理jsx,最后再试一下babel转换为低版本语法
  "presets": [
    [
      "@babel/preset-env",
      {
        // 设置兼容目标浏览器版本,这里可以不写,babel-loader会自动寻找上面配置好的文件.browserslistrc
        // "targets": {
        //  "chrome": 35,
        //  "ie": 9
        // },
        "useBuiltIns": "usage", // 根据配置的浏览器兼容,以及代码中使用到的api进行引入polyfill按需添加
        "corejs": 3 // 配置使用core-js使用的版本
      }
    ],
    "@babel/preset-react",
    "@babel/preset-typescript"
  ]
}

移除webpack.base.jsbabel-loaderoptions配置

// webpack.base.js
module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /.(ts|tsx)$/,
        use: 'babel-loader'
      },
      // 如果node_moduels中也有要处理的语法,可以把js|jsx文件配置加上
      // {
      //  test: /.(js|jsx)$/,
      //  use: 'babel-loader'
      // }
      // ...
    ]
  }
}

```
```
```
```
```
```
```
```