webpack搭建react脚手架

537 阅读7分钟

1.生成 package.json 文件

npm init -y

2.安装

webpack

webpack ebpack-cli

cnpm install --save-dev webpack webpack-cli
//config/webpack.common.config.js 共有的配置
const path = require('path');//依次cd

module.exports = {
  entry: { //entry(项目入口)
    index: './src/index.js', //目录
    framework: ['react','react-dom'], //模块
  },
  
  output: {//output(出口文件)
    filename: 'js/[name].[chunkhash:8].bundle.js', //不确定打包名字
    path: path.resolve(__dirname, '../dist'),//__dirname总是指向文件绝对路径
    clean: true, // 每次打包清除输出目录.
  }
  
  module: {//module(模块的处理)
    rules: [//在rules中配置:识别文件类型 并 使用相关loader进行转换
      ···
    ]
  }
  
  plugins: [ //plugin(loader不能做的处理都能交给plugin来做)
    new HtmlWebpackPlugin({ //一种插件就是一种函数
      ···
      },
    })
  ]
  optimization: { //优化
    ···
  },
}

webpack-merge

webpack-merge

通过“通用”配置,配置生产/开发环境。

cnpm install --save-dev webpack-merge

webpack.config文件中通过const {merge} = require('webpack-merge');引入merge函数使用

//config/ebpack.prod.config.js 生产环境的配置
const {merge} = require('webpack-merge'); //引入merge(函数)
const common = require('./webpack.common.config.js');//引入共有的配置

//merge类似与字符串连接
module.exports = merge(common, {
  ···
});

HtmlWebpackPlugin 自动编译html并引入js文件

html-webpack-plugin

cnpm install --save-dev html-webpack-plugin

在webpack.config中先引入插件 const HtmlWebpackPlugin = require('html-webpack-plugin');,然后再配置 plugins 属性使用

//config/ebpack.prod.config.js 生产环境的配置
const HtmlWebpackPlugin = require('html-webpack-plugin');

//merge类似与字符串连接
module.exports = merge(common, {
  ···
  //一种插件就是一种函数
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html', \\打包后输出的html文件名
      template: 'public/index.html', \\以(输入的html)为模板
      inject: 'body', \\在body最底部引入js(head就是head中引入)
      minify: { \\[压缩html文件](https://github.com/jantimon/html-webpack-plugin#minification)
        removeComments: true, \\去除注释
        collapseWhitespace: true, \\去除空格
      },
    })
  ]
});

React

react react-dom react-router-dom

cnpm install --save react react-dom react-router-dom

babel 转译js/jsx

babel-loader @babel/preset-react @babel/preset-env @babel/core

  • babel-loader:使用Babel和webpack来转译JavaScript文件。
  • @babel/preset-react:转译react的JSX
  • @babel/preset-env:转译ES2015+的语法
  • @babel/core:babel的核心模块
cnpm install --save-dev babel-loader @babel/preset-react @babel/preset-env @babel/core

根目录新增babel.config.json配置文件,在module.rules中通过use:'babel-loader'使用

//config/webpack.common.config.js 共有的配置
module.exports = {
  ···
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/, //表示匹配规则,是一个正则表达式
        use: 'babel-loader', //表示针对匹配文件将使用处理的loader
        exclude: /node_modules/, //exclude 告诉我们不需要去转译"node_modules"这里面的文件
      }
    ]
  }
}

style-loader和css-loader

style-loader css-loader

cnpm install --save-dev style-loader css-loader  

webpack loader的执行顺序是从右到左,遇到后缀为.css的文件,遇到“@import”等语句就将相应样式文件引入,webpack先用css-loader加载器解析,计算完的css,使用style-loader生成最终css代码的style标签,放到head标签里。
把style-loader放在css-loader的前面

//config/webpack.common.config.js 共有的配置
module.exports = {
  ···
  module: {
    rules: [
      ···
      {
        test: /\.css$/,
        use: [ 
          'style-loader', //用MiniCssExtractPlugin.loader代替
          'css-loader' 
        ]
      }
    ]
  }
}

mini-css-extract-plugin 打包出CSS独立文件

mini-css-extract-plugin

在webpack.config中先引入插件 const MiniCssExtractPlugin = require('mini-css-extract-plugin');,然后再配置 plugins , 最后修改loader使用。

cnpm install --save-dev mini-css-extract-plugin
//config/webpack.common.config.js 共有的配置
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); //引入
module.exports = {
  ···
  plugins: [
    //...
    new MiniCssExtractPlugin({
      filename: 'css/[name].[hash].css',
      chunkFilename: 'css/[id].[hash].css',
    }),
  ]
}
//config/webpack.common.config.js 共有的配置
module.exports = {
  ···
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [ 
          MiniCssExtractPlugin.loader,
          'css-loader' 
        ]
      }
    ]
  }
}

CssMinimizerWebpackPlugin 压缩css

css-minimizer-webpack-plugin

cnpm install css-minimizer-webpack-plugin --save-dev

在webpack.config中先引入插件 const MiniCssExtractPlugin = require('mini-css-extract-plugin');,然后再配置 optimization 使用。

//config/webpack.prod.config.js 共有的配置
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');

module.exports = {
  ···
  optimization: {
    ···
    minimizer: [
      `...`, //使用 `...` 语法来扩展现有的 minimizer
      new CssMinimizerPlugin(),
    ],
  },
}

less-loader 转译less

less less-loader

cnpm install --save-dev less less-loader

rules中设置loader使用。

//config/webpack.prod.config.js 共有的配置
module.exports = {
  ···
  module: {
    rules: [
      ···
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'less-loader'
        ]
      },
    ]
  }
}

sass-loader 转译sass

sass-loader sass 这里的sass就是dart sass

cnpm install sass-loader sass --save-dev

rules中设置loader使用。

//config/webpack.prod.config.js 共有的配置
module.exports = {
  ···
  module: {
    rules: [
      ···
      {
        test: /\.(sass|scss)$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'sass-loader'
        ]
      },
    ]
  }
}

postcss-loader 转译css

postcss-loader postcss

cnpm install --save-dev postcss-loader postcss

postcss 一种对css编译的工具,类似babel对js的处理,常见的功能如:
1 . 使用下一代css语法
2 . 自动补全浏览器前缀
3 . 自动把px代为转换成rem
4 . css 代码压缩等等
postcss 只是一个工具,本身不会对css一顿操作,它通过插件实现功能,autoprefixer 就是其一。

1.安装postcss某个插件,以Autoprefixer举例cnpm install autoprefixer --save-dev
2.在根目录新建postcss.config.js
3.rules中设置loader

//postcss.config.js #postcss配置文件
module.exports = {
  plugins: [
    require('autoprefixer')({ browsers: ['last 5 version', '>1%', 'ie >=8'] })
  ]
};
//config/webpack.prod.config.js 共有的配置
module.exports = merge(common, {
  //...
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [ 
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader'
        ]
      },
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      },
      {
        test: /\.(scss|sass)$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'sass-loader'
        ]
      },
    ]
  },
  //...
});

内置的 Asset Modules 管理资源(加载图像、font等)

在rules中设置
asset/resource 将资源分割为单独的文件,并导出url
asset/inline 将资源导出为dataURL(url(data:))的形式
asset/source 将资源导出为源码(source code)
asset 自动选择导出为单独文件或者 dataURL形式(默认为8KB)

//postcss.config.js #postcss配置文件
module.exports = {
   ···
   module: {
     rules: [
       ···
      { // 加载 images 图像
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
      },
      { //加载fonts字体
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: 'asset/resource',
      },
     ],
   },
 };

eslint-plugin-react-hooks 强制执行两条hook规则

eslint eslint-plugin-react-hooks

cnpm install eslint eslint-plugin-react-hooks --save-dev

在package.json中设置

//package.json
{
  ···
  // 你的 ESLint 配置
  "eslintConfig": {
    "plugins": [
      "react-hooks"
    ],
    "rules": {
      "react-hooks/rules-of-hooks": "error", // 检查 Hook 的规则
      "react-hooks/exhaustive-deps": "warn" // 检查 effect 的依赖
    }
  },
  ···
}

React Router Dom

react-router-dom

cnpm install react-router-dom --save

配置开发服务器解决BrowserRouter刷新"cannot GET /URL" 问题

//config/webpack.common.config.js 默认环境的配置
output: {
    publicPath: '/',//向index.html织入<script>时src的前缀。"app.js"-->"/app.js",转为绝对位置
    ···
  },
//config/webpack.dev.config.js 开发环境的配置
···
module.exports = merge(common, {
  ···
  devServer: {
    historyApiFallback: true,//默认指向index,解决BrowserRouter刷新"cannot GET /URL" 问题
    ···
  },
    ···
  },

SplitChunksPlugin 代码分割及去重

webpack v4+ 自带

配置optimization.splitChunks使用

//config/ebpack.prod.config.js 生产环境的配置
module.exports = merge(common, {
  ···
  optimization: {
    splitChunks: {
      chunks: 'all', //选择哪些 chunk 进行优化。
      minSize: 20000, //生成 chunk 的最小体积(以 bytes 为单位)。
      minChunks: 1, //拆分前必须共享模块的最小 chunks 数。
      //maxSize: 0, // 尝试将大于 maxSize 个字节的 chunk 分割成较小的部分。
      maxAsyncRequests: 30, //按需加载时的最大并行请求数。
      maxInitialRequests: 30, //入口点的最大并行请求数。
      enforceSizeThreshold: 50000, //强制执行拆分的体积阈值和其他限制(minRemainingSize,maxAsyncRequests,maxInitialRequests)将被忽略。
      cacheGroups: {  //定义了需要被抽离的模块
        framework: {
          test: "framework", //匹配入口名
          name: "framework",
          enforce: true
        },
        vendors: {
          priority: -10,
          test: /node_modules/, //匹配目录下引入的模块
          filename: 'js/vendors/[name].[chunkhash:8].bundle.js',// 输出路径
          name: "vendor",
          enforce: true,
        },
      },
    },
  },
});

terser-webpack-plugin 压缩js

webpack v5 自带

//config/ebpack.prod.config.js 生产环境的配置
const TerserPlugin = require("terser-webpack-plugin");

module.exports = {
  optimization: {
    minimize: true,
    minimizer: [new TerserPlugin()],
  },
};

webpack-dev-server 开发服务器

webpack-dev-server

cnpm install webpack-dev-server --save-dev
//config/webpack.dev.config.js 开发环境的配置
···
const webpack = require('webpack');

module.exports = merge(common, {
  ···
  devServer: {
    historyApiFallback: true,//BrowserRouter解决刷新"cannot GET /URL" 问题
    contentBase: path.resolve(__dirname, '../dist'),
    open: true,
    port: 9000,
    compress: true,
    hot: true //开启热更新 需要热更新插件
  },
  plugins: [
    ···
    new webpack.HotModuleReplacementPlugin(), // webpack热更新的插件
  ]
});
//package.json 开发环境的配置
  "scripts": {
    ···
    "start": "webpack serve --inline --config ./config/webpack.dev.config.js",
    ···
  },

3.目录

  frontend
  |- config #使用webpack-merge配置不同环境
      |- webpack.common.config.js
      |- webpack.prod.config.js
      |- webpack.dev.config.js
      
  |- node_modules # 依赖包
      |- ···
  |- public #公共文件存放地址
      |- index.html
  |- src
      |- images
          |-111.jpg
      |- index.js
      |- app.js
      |- app.less
  |- babel.config.json #babel配置文件
  |- package.json
  |- postcss.config.js


4.配置

package.json

//package.json
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    //命令:webpack --config选项来指定配置文件
    "start": "webpack serve --inline --config ./config/webpack.dev.config.js",
    "build": "webpack --config ./config/webpack.prod.config.js"
  },
  // 你的 ESLint 配置
  "eslintConfig": {
    "plugins": [
      "react-hooks"
    ],
    "rules": {
      "react-hooks/rules-of-hooks": "error", // 检查 Hook 的规则
      "react-hooks/exhaustive-deps": "warn" // 检查 effect 的依赖
    }
  },

postcss.config.js

//postcss.config.js
module.exports = {
    plugins: [
      require('autoprefixer')({ browsers: ['last 5 version', '>1%', 'ie >=8'] })
    ]
  };

babel 配置

根目录新建babel.config.json配置相关的"presets":

//babel.config.json
{
    "presets": [
      [
        "@babel/env",
        {
          "targets": {
            "edge": "17",
            "firefox": "60",
            "chrome": "67",
            "safari": "11.1"
          },
          "useBuiltIns": "usage",
          "corejs": "3.6.4"
        }
      ],
      "@babel/preset-react"
    ]
  }

webpack配置 webpack-merge使用 HtmlWebpackPlugin使用 代码分离(及去重)

- 共有的配置
//config/webpack.common.config.js 共有的配置
const path = require('path');//依次cd

module.exports = {
  //entry(项目入口)
  entry: {
    index: './src/index.js',
    framework: ['react','react-dom'],
  },
  
  //output(出口文件)
  output: {
    publicPath: '/',//向index.html织入<script>时src的前缀。"app.js"-->"/app.js",转为绝对位置
    filename: 'js/bundle.js',
    path: path.resolve(__dirname, '../dist'),//__dirname总是指向文件绝对路径
    clean: true, // 每次打包清除输出目录.
  }
  
  //module(模块的处理)
  //在rules中配置:识别文件类型 并 使用相关loader进行转换
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/, //表示匹配规则,是一个正则表达式
        use: 'babel-loader', //表示针对匹配文件将使用处理的loader
        exclude: /node_modules/, //exclude 告诉我们不需要去转译"node_modules"这里面的文件
      },
      {
        test: /\.css$/,
        use: [ 
          MiniCssExtractPlugin.loader,
          'css-loader' ,
          'postcss-loader',
        ]
      },
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      },
      {
        test: /\.(sass|scss)$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
          'sass-loader'
        ]
      },
      { // 加载 images 图像
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
      },
      { //加载fonts字体
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: 'asset/resource',
      },
    ]
  }
  
  //plugin(loader不能做的处理都能交给plugin来做)
  plugins: [
    //...
    new MiniCssExtractPlugin({
      filename: 'css/[name].[hash].css',
      chunkFilename: 'css/[id].[hash].css',
    }),
  ]
}
- 生产环境的配置
//config/ebpack.prod.config.js 生产环境的配置
const {merge} = require('webpack-merge'); //引入merge(函数)
const common = require('./webpack.common.config.js');//引入共有的配置

const HtmlWebpackPlugin = require('html-webpack-plugin');

//merge类似与字符串连接
module.exports = merge(common, {
  mode: 'production',
  
  devtool: 'source-map',
  
  output: { //给打包出的js文件换个不确定名字
    filename: 'js/[name].[chunkhash:8].bundle.js',
  },
  
  //一种插件就是一种函数
  plugins: [
    new HtmlWebpackPlugin({ \\自动编译html并引入js文件
      filename: 'index.html', \\打包后输出的html文件名
      template: 'public/index.html', \\以(输入的html)为模板
      inject: 'body', \\在body最底部引入js(head就是head中引入)
      minify: { \\[压缩html文件](https://github.com/jantimon/html-webpack-plugin#minification)
        removeComments: true, \\去除注释
        collapseWhitespace: true, \\去除空格
      },
    })
  ],
  optimization: {
    splitChunks: {
      chunks: 'all', //选择哪些 chunk 进行优化。
      minSize: 20000, //生成 chunk 的最小体积(以 bytes 为单位)。
      minChunks: 1, //拆分前必须共享模块的最小 chunks 数。
      //maxSize: 0, // 尝试将大于 maxSize 个字节的 chunk 分割成较小的部分。
      maxAsyncRequests: 30, //按需加载时的最大并行请求数。
      maxInitialRequests: 30, //入口点的最大并行请求数。
      enforceSizeThreshold: 50000, //强制执行拆分的体积阈值和其他限制(minRemainingSize,maxAsyncRequests,maxInitialRequests)将被忽略。
      cacheGroups: { //定义了需要被抽离的模块
        framework: {
          test: "framework",
          name: "framework",
          enforce: true
        },
        vendors: {
          priority: -10,
          test: /node_modules/, //匹配目录下引入的模块
          filename: 'js/vendors/[name].[chunkhash:8].bundle.js',// 输出路径
          name: "vendor",
          enforce: true,
        },
      },
    },
  
  minimize: true, //压缩
  minimizer: [new TerserPlugin(),], //压缩
  },
});
- 开发环境的配置
//config/webpack.prod.config.js 开发环境的配置
const path = require('path');
const {merge} = require('webpack-merge');
const common = require('./webpack.common.config.js');

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = merge(common, {
  mode: 'development',

  devtool: 'eval-source-map',

  output: {
    filename: 'js/[name].[hash:8].bundle.js',
  },

  devServer: {
    contentBase: path.resolve(__dirname, '../dist'),
    open: true,
    port: 9000,
    compress: true,
    hot: true, //开启热更新 需要热更新插件
  },

  plugins: [
    new HtmlWebpackPlugin({
      template: 'public/index.html',
      inject: 'body',
      hash: false
    }),
    new webpack.HotModuleReplacementPlugin(),// webpack热更新的插件
  ]
});