webpack项目配置

1.webpack的介绍

webpack所解决的问题时:如何在前端项目中更高效地管理和维护项目中的每一个资源。
更为理想的方式是在页面中引入一个js入口文件,其余用到的模块可以通过代码控制,按需加载。

2.如何使用webpack

npm init -y
npm i webpack webpack-cli --save--dev

2.webpack的配置

devServer

import axios from 'axios';  //13.9k(gzipped:4.9k)
axios.get('/api/Yixiantong/getHomeDatas')
	.then(({data}) => {
		console.log('data',data);
	})
//webpack.dev.js
const devConfig = {
	mode: 'development',
	devServer: {
		port:8080, //服务器启动的端口 8080
		contentBase: './dist', //服务器静态资源文件夹
		progress: true, //打包时显示进度条
		open:true, //启动服务器后,自动打开浏览器
		compress: true, //开启gzip压缩
		//虽然源代码写了路径,但是做真实请求的时候,
		//我们会去掉/api
		proxy: {
			//以'/api'请求开头的路径做请求转发
			'/api':{
				//代理的域名
				target:'http://study.jsplusplus.com',
				//将'/api'路径置为空
				pathRewrite:{
					'^/api':''
				},
				changeOrigin:true
			}
		}
}

3.webpack的优化

1)构建时间的优化

(1)thread-loader

多进程打包,可以大大提高构建的速度,使用方法是将thread-loader放在比较费时间的loader之前,比如说babel-loader。

npm i thread-loader -D
//由于开发环境和生产环境都需要加速
//所以配置在webpack.common.js文件中
module.exports = {
	test:/\.js$/,
	use:[
		'thread-loader',
		'babel-loader'
		]
}

(2)cache-loader

缓存资源,提高构建的速度,使用方法将cache-loader放在比较费时的loader之前,比如babel-loader

npm i cache-loader -D
//由于开发环境和生产环境都需要加速
//所以配置在webpack.common.js文件中
module.exports = {
	test:/\.js$/,
	use:[
		'cache-loader',
		'thread-loader',
		'babel-loader'
		]
}

(3)开启热模块更新

比如你修改了项目中某一个文件,会导致整个项目刷新,这非常耗时间。如果只刷新修改的这个模块,其他保持状态,那将大大提高修改代码的重新构建时间。

//webpack.dev.js
//引入webpack
const webpack = require('webpack')
module.exports = {
	//使用webpack提供的热更新插件
	plugins:[
		new webpack.HotModuleReplacementPlugin()
	],
	devServer: {
		hot:true
	}
}

(4)exclude&&include

  • exclude:不需要处理的文件
  • include:需要处理的文件
    合理设置这两个属性,可以大大提高构建速度
// webpack.common.js
module.exports = {
	test:/\.js$/,
	//使用include来指定编译文件夹
	include:path.resoleve(__dirname,'.../src'),
	//使用exclude排除指定文件夹
	exclude:/node_modules/,
	use: [
		'babel-loader'
	]
}
  • 开发环境去除代码压缩,gzip,体积分析等优化配置,大大提高构建速度。
  • 生产环境需要代码压缩,gzip,体积分析等优化配置,大大降低最终项目打包体积。

2)打包体积的优化

主要是打包后项目整体体积的优化,有利于项目上线后的页面加载速度提升。

(1)css-minimizer-webpack-plugin

css代码压缩,去重

//代码的压缩比较消耗时间
//所以只需要在webpack.prod.js中配置
npm i css-minimizer-webpack-plugin -D
//webpack.prod.js
module.exports = {
	const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
	optimization: {
		minimizer: [
			new CssMinimizerPlugin(), //去重压缩css
		],
	}
}

(2)terser-webpack-plugin

实现打包好的js代码压缩
压缩代码的原理主要是1.长变量变短变量2.去除空格和换行符

npm i terser-webpack-plugin -D
//webpack.prod.js
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
	optimization:{
		minimizer:[
			new CssMinimizerPlugin(), //去重压缩css
			new TerserPlugin({ //压缩JS代码
				terserOptions:{
					compress:{
						drop_console: true, //去除console
					}}})
		],
	}
}

(3)tree-shaking

tree-shaking简单说作用就是:只打包用到的代码,没有用到的代码不打包,而webpack5默认开启tree-shaking,当打包的modeproduction时,自动开启tree-shaking进行优化。

module.exports = {
	mode:'produciton'
}

(4)source-map类型

source-map的作用是:方便你报错的时候能定位到错误代码的位置。它的体积不容小觑,所以对于不同的环境要设置不同的类型。
开发环境:开发环境的时候我们需要能精准定位错误代码的位置

// webpack.dev.js
module.exports = {
	mode: 'development',
	devtool: 'eval-cheap-module-source-map'
}

生产环境:我们想开启source-map,但又想体积不要太大

// webpack.prod.js
module.exports = {
	mode:'production',
	devtool:'nosources-source-map'
}

3)打包体积分析

webpack-bundle-analyzer

使用webpack-bundle-analyzer可以审查打包后的体积分布,进而进行相应的体积优化,只需要看打包时的体积,所以只需要在webpack.prod.js中配置。

npm i webpack-bundle-analyzer -D
// webpack.prod.js
const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer')
module.exports = {
	plugins: [
		new BundleAnalyzerPlugin(),
	]
}

4)用户体验优化

(1)模块懒加载

如果不进行模块懒加载的话,最后整个项目代码都会被打包到一个js文件里,单个js文件体积非常大,那么当用户页面请求的时候,首屏加载时间会比较长,使用模块懒加载之后,大js文件会分成多个小js文件,网页加载时会按需加载,大大提升首屏加载速度。

// src/router/index.js
const routes = [
	{
		path:'/login',
		name:'login',
		component:login
	},
	{
		path:'/home',
		name:'home',
		component: ()=>import('../views/home/home.vue')
	}
]

(2)Gzip

开启Gzip后,大大提高用户的页面加载速度,因为gzip的体积比源文件小很多,当然需要后端的配合,使用compression-webpack-plugin
只需要打包时优化体积,所以只需在webpack.prod.js中配置

npm i compression-webpack-plugin -D
//webpack.prod.js
const CompressPlugin = require('compression-webpack-plugin')
module.exports = {
	plugins: [
	//之前的代码...
	
	//gzip
	const CompressPlugin({
		algorithm:'gzip',
		threshold:10240,
		minRatio:0.8
	})
}

(3)小图片转base64

对于一些小图片,可以转base64,这样可以减少用户的http网络请求次数,提高用户的体验。webpack5中url-loader已被废弃,改用asset-module

// webpack.common.js
module.exports = {
	test:/\.(png|jpe?g|gif|svg|webp)$/,
	type:'asset',
	parser:{
		//转base64的条件
		dataUrlCondition:{
			maxSize:25*1024, //25kb
		}
	},
	generator:{
		//打包image文件下
		filename:'images/[contenthash][ext][query]',
	},
}

(4)合理配置hash

我们要保证,改过的文件需要更新hash值,而没有改过的文件依然保持原来的hash值,这样才能保证在上线后,浏览器访问时没有改变的文件会命中缓存,从而达到性能优化的目的。
是在webpack.common.js中配置呢?还是在webpack.prod.js中配置呢?

// webpack.common.js
moudle.exports = {
	output: {
		path: path.resolve(__dirname,'../dist'),
		//给js文件上加上contenthash
		filename:'js/chunk-[contenthash].js',
		clean: true,
	}
}

4.项目实战

配置项目时,比较不同配置的webpack的编译速度和打包体积的大小

config.js

const isDevelopment = process.env.NODE_ENV === "development"
const isProduction = process.env.NODE_ENV === "production"

const SERVER_HOST = "0.0.0.0"
const SERVER_PORT = 8001

module.exports = {
  isDevelopment,
  isProduction,
  SERVER_HOST,
  SERVER_PORT,
}

webpack.common.js

//引入,html-webpack-plugin包,打包HTML资源
const HtmlWebpackPlugin = require("html-webpack-plugin")
//引入path模块
const path = require("path")
//引入webpack
const webpack = require("webpack")
//打包时可以看到优雅的进度条
const WebpackBar = require("webpackbar")
//从./config中解构赋值出,isDevelopment和isProduction
const { isDevelopment, isProduction } = require("./config")
//提取css成为单独文件(css资源合并)
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
//fork-ts-checker-webpack-plugin可以加速ts类型检查和eslint linting
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin")
//fork-ts-checker-webpack-plugin用于eslint查找和修复js代码中的问题
const ESLintPlugin = require("eslint-webpack-plugin")
//antd-dayjs-webpack-plugin可以使用Day.js替换Moment.js
const AntdDayjsWebpackPlugin = require("antd-dayjs-webpack-plugin")
//从path的文件中解构出一些路径
const {
  moduleFileExtensions,
  appSrc,
  appIndex,
  appDirectory,
  appTsConfig,
  appHtml,
  appPublic,
} = require("./paths")
//将获取cssloader写成一个函数
const getCssLoaders = () => {
  const cssLoaders = [
    isDevelopment ? "style-loader" : MiniCssExtractPlugin.loader,
    {
      loader: "css-loader",
      options: {
        modules: {
          localIdentName: "[local]",
        },
        sourceMap: isDevelopment,
      },
    },
  ]
  isProduction &&
    cssLoaders.push({
      loader: "postcss-loader",
      options: {
        postcssOptions: {
          plugins: [
            isProduction && [
              "postcss-preset-env",
              {
                autoprefixer: {
                  grid: true,
                },
              },
            ],
          ],
        },
      },
    })
  return cssLoaders
}
//下面写webpack的公共配置
const config = {
  // 文件的入口 5大核心组件之一
  entry: {
    app: appIndex,
  }, 

  context: appDirectory,

  resolve: {
    extensions: moduleFileExtensions,
    alias: {
      "@": appSrc,
    },
  },
  
  //loader加载器 5大核心组件之一
  //loader让webpack能够去处理哪些非Javascript文件
  //webpack自身只能理解js
  module: {
    rules: [
      // 处理tsx和jsx文件
      {
        test: /\.(ts|js)x?$/,
        exclude: /node_modules/,
        loader: "babel-loader",
        options: {
          cacheDirectory: true,
          include: appSrc,
          plugins: [
            isDevelopment && require.resolve("react-refresh/babel"),
          ].filter(Boolean),
        },
      },
      //处理less
      {
        test: /\.less$/,
        // exclude: /node_modules/,
        use: [
          ...getCssLoaders(),
          {
            loader: "less-loader",
            options: {
              sourceMap: isDevelopment,
              lessOptions: {
                path: [
                  appSrc,
                  // path.resolve(PROJECT_PATH, 'node_modules/antd'),
                ],
                javascriptEnabled: true,
              },
            },
          },
        ],
      },
      //处理css
      {
        test: /\.css$/,
        exclude: /node_modules/,
        use: [...getCssLoaders()],
      },
      //处理scss
      {
        test: /\.scss$/,
        exclude: /node_modules/,
        use: [
          ...getCssLoaders(),
          {
            loader: "sass-loader",
            options: {
              sourceMap: isDevelopment,
            },
          },
        ],
      },
      //处理图片资源
      {
        test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
        exclude: /node_modules/,
        type: "asset",
        parser: {
          dataUrlCondition: {
            maxSize: 4 * 1024,
          },
        },
      },
      //处理字体格式
      {
        test: /\.(eot|svg|ttf|woff|woff2?)$/,
        exclude: /node_modules/,
        type: "asset/resource",
        //解析
        // parser: {
        //   //转base64的条件
        //   dataUrlCondition: {
        //     maxSize: 25 * 1024, // 25kb
        //   }
        // },
        // generator: {
        //   //与output.assetModuleFilename是相同的,这个写法引入的时候也会添加好这个路径
        //   filename: 'img/[name].[hash:6][ext]',
        //   //打包后对资源的引入,文件命名已经有/img了
        //   publicPath: './'
        // },
      },
    ],
  },

  //插件  五大核心组件之一
  //插件都需要require引进,而加载器却不需要
  plugins: [
    //DefinePlugin这个插件是用来定义全局变量的,在webpack打包的时候会对这些变量做替换
    // 介绍了这么多,在实际使用中, DefinePlugin 最为常用的用途就是用来处理我们开发环境和生产环境的不同。
    // 比如一些 debug 的功能在生产环境中需要关闭、开发环境中和生产环境中 api 地址的不同。
    new webpack.DefinePlugin({
      "process.env.RUN_ENV": JSON.stringify(process.env.RUN_ENV),
    }),
    //打包HTML资源
    new HtmlWebpackPlugin({
      template: appHtml,
      templateParameters: {
        title: "IOT后台管理系统",
      },
      favicon: path.resolve(appPublic, "favicon.png"),
      scriptLoading: "blocking", // 'blocking'|'defer'
    }),
    //打包时的进度条
    new WebpackBar({
      name: "构建进度",
      color: "#52c41a",
    }),
    //fork-ts-checker-webpack-plugin可以加速ts类型检查和eslint linting
    new ForkTsCheckerWebpackPlugin({
      typescript: {
        configFile: appTsConfig,
      },
    }),
    new ESLintPlugin({
      extensions: moduleFileExtensions,
    }),
    new AntdDayjsWebpackPlugin(),
  ].filter(Boolean),
}

module.exports = config

webpack.dev.js

const common = require("./webpack.common")
const { merge } = require("webpack-merge")
const { appBuild, appPublic } = require("./paths")
const { SERVER_HOST, SERVER_PORT } = require("./config")
const path = require("path")
const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin")
const webpack = require("webpack")

//merge合并两个对象
module.exports = merge(common, {
  //模式 5大核心模块之一
  mode: "development",
  //代码报错,调试
  devtool: "eval-cheap-module-source-map",
  //DevServer是webpack开发服务器
  devServer: {
    historyApiFallback: true,
    quiet: true,
    // host: SERVER_HOST,
    port: SERVER_PORT,
    watchContentBase: true,
    // stats: 'errors-only',
    clientLogLevel: "none",
    compress: true,
    // open: true,
    //webpack优化配置1:开启热更新 HMR
    hot: true,
    inline: true,
    noInfo: true,
    // contentBase: `http://${SERVER_HOST}:${SERVER_PORT}`,
    // publicPath: appPublic,
    contentBase: appBuild,
    contentBasePublicPath: "/",
    //服务器配置
    proxy: {
      "/api": {
        //target为服务器的地址和端口号,本题的端口号为4093
        target: "http://localhost:4093",
        // changeOrigin: false,
        // secure: false,
      }
    },
    //自动打开浏览器
    open:true
  },
  target: "web",
  cache: {
    type: "memory",
  },
  optimization: {
    moduleIds: "named",
    chunkIds: "named",
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new ReactRefreshWebpackPlugin(),
  ].filter(Boolean),
})

webpack.prod.js

const webpack = require("webpack")
const { appDll } = require("./constant")
const path = require("path")

module.exports = {
  entry: {
    vendor: ["react", "react-dom", "react-router-dom", "axios"],
  },
  output: {
    path: appDll,
    filename: "[name].dll.js",
    libraryTarget: "var",
    library: "[name]_dll_[hash]",
    // clean: true
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.resolve(appDll, "[name]-manifest.json"),
      name: "[name]_dll_[hash]",
      context: __dirname,
    }),
  ],
}

webpack.dll.js

const webpack = require("webpack")
const { appDll } = require("./constant")
const path = require("path")

module.exports = {
  entry: {
    vendor: ["react", "react-dom", "react-router-dom", "axios"],
  },
  output: {
    path: appDll,
    filename: "[name].dll.js",
    libraryTarget: "var",
    library: "[name]_dll_[hash]",
    // clean: true
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.resolve(appDll, "[name]-manifest.json"),
      name: "[name]_dll_[hash]",
      context: __dirname,
    }),
  ],
}

path.js

const path = require("path")
const fs = require("fs")
//process是node的一个全局变量,即global对象的属性。
//cwd()返回当前进程的工作目录
const appDirectory = fs.realpathSync(process.cwd())

function resolveApp(relativePath) {
  //path.resolve()返回的是当前的文件的绝对路径
  //path.resolve不带/开头的参数,返回的是当前绝对路径拼接现在的参数
  //path.resolve('a')  ==> /Users/xxx/a
  return path.resolve(appDirectory, relativePath)
}

//文件后缀
const moduleFileExtensions = [".tsx", ".ts", ".jsx", ".js", ".json"]

/**
 * Resolve module path
 * @param {function} resolveFn resolve function
 * @param {string} filePath file path
 */
function resolveModule(resolveFn, filePath) {
  const extension = moduleFileExtensions.find((ex) =>
    fs.existsSync(resolveFn(`${filePath}.${ex}`)),
  )
  if (extension) {
    return resolveFn(`${filePath}${extension}`)
  }
  return resolveFn(`${filePath}.tsx`) // default is .ts
}

module.exports = {
  appBuild: resolveApp("dist"),
  appPublic: resolveApp("public"),
  appIndex: resolveModule(resolveApp, "src/index"), // Package entry path
  appHtml: resolveApp("src/index.ejs"),
  appNodeModules: resolveApp("node_modules"), // node_modules path
  appSrc: resolveApp("src"),
  appDll: resolveApp("dll"),
  appSrcComponents: resolveApp("src/components"),
  appSrcUtils: resolveApp("src/utils"),
  appProxySetup: resolveModule(resolveApp, "src/setProxy"),
  appPackageJson: resolveApp("package.json"),
  appTsConfig: resolveApp("tsconfig.json"),
  appDirectory,
  moduleFileExtensions,
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值