webpack 简单配置

88 阅读8分钟

webpack 是什么

webpack 是一种前端资源构建工具,一个静态打包器

1,前端构建工具:前端资源就是浏览器不认识的web资源,比如 less,sass,ts,包扩 js 的高级语法。这些文件要能够在浏览器里正常工作,必须经过编译处理,webpack 就是可以集成这些编译工具的一个总的构建工具。

2,静态打包器:静态模块就是开发中的各种资源文件,webpack 根据依赖关系生成一个依赖关系图,这个关系图就包含着应用程序所需要的每个模块,然后将所有这些模块打包成少量的 bundle(通常只有一个),由浏览器加载。

为什么需要webpack

这个问题可以和没有使用构建工具对比一下,看下列出来的几项不使用构建工具的痛点:

  • web开发时调用后端接口时跨域,需要其他代理工具来规避 (webpack 可以设置代理解决跨域)
  • 代码改动后手动刷新浏览器,如果有缓存必须先清除缓存刷新比较麻烦 (webpack 启动时 node 服务会及时更新)
  • 像 js 和css 很多新语法有兼容问题不能使用使开发受影响 (webpack 打包时会考虑兼容问题)
  • 打包问题,需要额外的打包平台打包,自己编写打包脚本,对图片解压,打包 js,css 都要一一处理。(webpack 打包)

像这些问题 webpack 都做了处理,我们只要做些简单的配置就可以上手使用。

webpack 使用

webpack 核心配置

entry (入口)

webpack 以哪个文件作为入口起点开始打包,分析构建其内部依赖图。进入入口后 webpack 会找出有哪些模块和库是入口依赖的(直接或者间接)

webpack.config.js

// 用法
// string方式 这种形式为单入口,打包形成一个 chunck,chunck 的名称默认是 main.js。输出一个 bundle
const config = {
	entry: './src/index.js'
}

const config = {
	entry:{
		main: './src/index.js'
	}
}

// object方式 多入口, 有几个入口文件就形成几个 chunck,输出几个bundle。此时 chunck名称就是对象 key 值
const config = {
	entry:{
		app: './src/index.js',
		point: './src/point.js'
	}
}

//array方式 多入口,所有的入口只生成一个 chunck,输出一个 bundle文件。
const config = {
	entry: ['./src/index.js', './src/point.js']
}

output (输出)

webpack 打包后的资源bundles 输出到哪里以及如何命名。(基本上,整个应用程序解构都会被编译到指定的输出路径的文件夹中)

Webpack.config.js

//如果只有一个输出文件可以写成固定的静态不变的名称 fileName
const config = {
	output:{
		fileName: "bundle.js",
		path: '/'
	}
}

//有多个chunck 要输出时可以使用变量。
// 
const config = {
  entry:{
     app: './src/index.js',
     point: './src/point.js'
   }
	output:{
    //输出文件的文件名,[name]代表用内置的 name 来替换,可以看成一个模块字符串函数,每个生成的 chunck 都通过这个函数拼接出文件名称且是唯一的。
		fileName: "[name].js",
		//输出文件存放在本地的目录,必须是 string 类型的绝对路径。 路径为 './dist/app.js', './dist/point.js'
		path: path.resolve(_dirname, 'dist'),
    //所有资源引入公共路径前缀,是 string 类型,默认为空字符'',即使用相对路径.(一般用于生产环境,谨慎使用)
    publicPath: ''
	}
}

publicPath 举个栗子:把构建出来的资源文件加载到 cdn 服务上,利于加快页面打开速度。

 publicPath: 'https://cdn.example.com/assects/'

这时发布到线上的 html在引入js 时就需要

<script src='https://cdn.example.com/assects/a_12345.js'></script>
loader

webpack 自身只能理解 js 和 json 文件,loader让 webpack 能够处理其他文件。

loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader允许直接在 JavaScript 模块中 import CSS文件

  • loader 有三种方式

    配置:在webpack.config.json 中指定 loader (推荐)

    内联: 每个import 语句中显示指定

    CLI: 在命令中指定

Webpack.config.js

module.exports = {
	module: {
    //loader执行顺续,由上至下有有至左
		rules: [
			//打包样式文件,打包过程中只要打包到后缀为 css 文件都会使用css-loader和 style-loader去加载这个文件
			{
				// 正则匹配所有.css后缀的样式文件
				test: /\.css$/,
				use: [
					// 将 JS 字符串生成为 style 节点
					'style-loader',
					// 将 CSS 转化成 CommonJS 模块
					'css-loader',
					{
						//postcss 用 js工具 和插件转换 css 的工具。作用为 css 添加前缀,比如-webkit。编译使大多数浏览器都能认识,css-modules 解决全局命名冲突问题。css 兼容处理 postcss,注意需要在package.json配置browserslist
						loader: 'postcss-loader',
						//相关配置参数
						options: {
							postcssOptions: {
								//依赖插件 帮postcss找到package.json中的browserslist配置,根据配置加载指定的兼容性样式.
								plugins: [require("postcss-preset-env")()]
							}
						}
					},
					//将 sass 转换为 css
					'sass-loader'
				]
			},
      //
      {
        test: /\.js$/,
        //排除node_modules文件不要进行babel-loader
        exclude: /node_modules/,
        //只是用一个 loader 时推荐写法
        loader: 'babel-loader',
        options:{
          presets:[
            [
              // 预设:指示babel做怎么样的兼容处理 babel-preset-env支持es6,7,8的语法
              "@babel/preset-env",
               {
                  //按需加载
                 useBuiltIns: "usage",
                 // 一些浏览器不能正常解析es6 语法时,corejs帮我们自动进行兼容性处理
                 corejs: {
                   //版本
                   version: "3",
                 },
                 //需要兼容的浏览器
                 targets: "defaults",
               }
            ]
          ]
        }
      },
      {
        test: /\.(png|jpg|jpeg)$/,
        use: [
          {
            //将小于limit的图片转成base64来减少http请求,如果大于就用http请求
            loader: "url-loader",
            options: {
              name: "[name]_[hash:8].[ext]",
              // 单位为(b) 10240 => 10kb
              // 如果小于10kb则转换为base64打包进js文件,如果大于10kb则打包到dist目录
              limit: 10240, 
            }
          }
        ]
      },
		]
	}
}

样式:style-loader、css-loader、less-loader、sass-loader等

文件:raw-loader、file-loader 、url-loader等

编译:babel-loader、coffee-loader 、ts-loader等

校验测试:mocha-loader、jshint-loader 、eslint-loader等

plugin (插件)

扩展 webpack 的能力,包括:打包优化,资源管理,注入环境变量。

// CleanWebpackPlugin帮助你在打包时自动清除dist文件,学习时使用比较方便
const { CleanWebpackPlugin } = require("clean-webpack-plugin"); //从webpack5开始,webpack内置了该功能,只要在ouput中配置clear为true即可

// HtmlWebpackPlugin创建html文件,并自动引入打包输出的bundles文件。支持html压缩。
const HtmlWebpackPlugin = require("html-webpack-plugin");

// 将CSS提取到单独的文件中。它会为每个chunk创造一个css文件。需配合loader一起使用
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

// 在Webpack构建过程中搜索CSS资源,并优化\最小化CSS
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin");


module.exports = {
    entry: 'index.js',
    output: {
      path: __dirname + '/dist', 
      filename: 'index_bundle.js'
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    // MiniCssExtractPlugin.loader的作用就是把css-loader处理好的样式资源(js文件内),单独提取出来 成为css样式文件
                    MiniCssExtractPlugin.loader,//生产环境下使用,开发环境还是推荐使用style-loader
                    "css-loader",
                ],
            },
        ],
    },
    plugins: [
        new HtmlWebpackPlugin({
          	//可配置参数,可参考:https://segmentfault.com/a/1190000013883242
            title:"my app", //网页 document.title 的配置
          	filename: 'assets/admin.html',  // 在output.path 目录下生成 assets/admin.html 文件。默认 index.html
          	template: './src/index.html' 
        }),
        new MiniCssExtractPlugin({
            filename: "css/index_build.css",
        }),
        new OptimizeCssAssetsWebpackPlugin(),
    ]
}

mode (模式)

Webpack 使用相应模式配置。mode分为development/production 两种模式,默认为 peoduction

module.exports = {
	mode: 'development'
}

官网解释:

选项描述
development会将 definePlugin中的 process.env.NODE_ENV 设置成 development,为模块和 chunck 启用有效名称
production会将 definePlugin中的 process.env.NODE_ENV 设置成production,启用FlagDependencyUsagePlugin,FlagIncludedChunksPlugin,ModuleConcatenationPlugin,NoEmitOnErrorsPlugin 和 TerserPlugin 。
  • DefinePlugin:定义全局变量process.env.NODE_ENV,区分程序运行状态。

  • FlagDependencyUsagePlugin:标记没有用到的依赖。

  • FlagIncludedChunksPlugin:标记chunks,防止chunks多次加载。

  • ModuleConcatenationPlugin:作用域提升(scope hosting),预编译功能,提升或者预编译所有模块到一个闭包中,提升代码在浏览器中的执行速度。

  • NoEmitOnErrorsPlugin:防止程序报错,就算有错误也继续编译。

  • TerserPlugin:压缩js代码。

其他常用配置
module.exports = {
    // 解析模块的规则:
    resolve: {
        // 配置 给文件路径取别名。
        alias: {
          	//不论哪个文件用到 store 只需要 import Store from 'Stroe'就可以了
            "Store": path.resolve(__dirname, "../src/store")
        },
        // 配置 引入文件时省略文件路径的后缀名。默认省略js和json也是webpack默认认识的两种文件类型。
        extensions: [".js", ".json", ".css","jsx"], // 新加css,jsx文件
        // 告诉webpack解析模块是去找哪个目录,默认是 ["node_modules"],如果没有就去上一层
        // 该配置明确告诉webpack,直接去上一层找node_modules。
        modules: [path.resolve(__dirname, "../node_modules")],
    },
  
    // devServer(开发环境下配置):
  	//参考文档:https://blog.csdn.net/lin_fightin/article/details/115447016
    devServer: {
        // 指定被访问html页面所在的目录
        contentBase: path.resolve(__dirname, "build"),
        // 为每个静态文件开启gzip压缩
        compress: true,
        host: "localhost",
        port: 5000,
        open: true, // 自动打开浏览器
        hot: true, //开启HMR功能
        // 设置代理
        proxy: {
            // 一旦devServer(5000端口)接收到/api/xxx的请求,就会用devServer起的服务把请求转发到另外一个服务器(3000)
            // 以此来解决开发中的跨域问题
            api: {
                target: "htttp://localhost:3000", //映射的目标(代理成功域名)
                // 发送请求时,请求路径重写:将/api/xxx  --> /xxx (去掉/api)
                pathRewrite: {
                    "^api": "",
                },
            },
        },
    },

    // optimization(生产环境下优化配置)
    optimization: { 
        splitChunks: {
          // 提取公共代码 推荐(同步和异步都)
            chunks: "all",
          //引入的库必须大于30kb,才做代码分割(默认)
          	minSize: 30000, 
        },
        minimizer: [
            // 配置生产环境的压缩方案:js和css
            new TerserWebpackPlugin({
                // 多进程打包并发运行,提高构建速度。默认 true ?????
                parallel: true,
                terserOptions: {
                    // 启动source-map
                    sourceMap: true,
                },
            }),
        ],
    },
};

webpack打包优化

开发环境优化
1, 使用 source-map

为了更容易追踪错误和警告,webpack 提供了 source-map 功能,一种将构建后的代码映射回源代码。如果错误来自于某个文件,source-map 就会告诉我们。

配置开启 source-map 功能

devtool:'source-map',

source-map 各选项常用组成:[inline-|eval-][cheap-[module-]]source-map

参考:zhuanlan.zhihu.com/p/67858986

2, HMR(模块热替换 hot moudule replacement)

运行时更新各种模块不需要完全刷新,只更新变更内容,调整样式迅速,避免了大部分的网络请求、浏览器重新渲染、app解析编译显示。

配置实现

devserver:{
	hot:true,
},
plugin:[
	new webpack.HotModuleReplacementPlugin()
]

开启 HMR 后还需要一些配置

样式文件:style-loader 内部实现,所以只要loader中配置了style-loade就可直接使用HMR功能

js文件:需要修改源代码,接收更新通知

/***
 **  启用 webpack 内置的 HMR插件后, module.hot 接口就会暴露在 index.js 中, 
 **  接下来需要在 index.js 中配置告诉 webpack 接受HMR的模块( print.js ):
*/ 
if(module.hot){
    module.hot.accept('./print.js', function(){
        console.log('hot module replacement')
        printMe()
    })
}

参考:zhuanlan.zhihu.com/p/30669007

生产环境优化
1,oneOf

默认情况下,文件会匹配rules 下的每个规则,即使匹配到了也会继续向下匹配。使用 oneOf 时一旦匹配到则不会再向下执行

//如下在 oneOf中只有第一个 loader 会生效,@babel-loader没作用。
rules:[
	{
		oneOf:[
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "eslint-loader",
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "@babel-loader",
    	}
    ]
	}
]

//如果一个文件需要多个loader 处理可以放在外面
//一
rules:[
  {
    test: /\.js$/,
    exclude: /node_modules/,
    loader: "eslint-loader",
  },
	{
		oneOf:[
      //只会匹配成功一次,所以如果有一种类型的文件需要使用多个loader,要么使用use数组,要么放到oneOf之外
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "@babel-loader",
    	}
    ]
	}
]

//二

rules:[
	{
		oneOf:[
      //只会匹配成功一次,所以如果有一种类型的文件需要使用多个loader,要么使用use数组,要么放到oneOf之外
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use:[
          {loader: "eslint-loader",}
        ]
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use:[
          {loader: "@babel-loader",}
        ]
    	}
    ]
	}
]
2,缓存

在编译打包时可以对文件做缓存,缓存有两种方式:

1,解析文件 loader 自身待的缓存 (babel-loader)

2,专门的loader (cache-loader)

开启缓存后在打包编译时对于未更改的文件可以读取缓存,不再重新编译提高打包速度。

{
  test: /\.js$/,
    use: [
      //使用cache-loader,放在babel-loader前可对babel编译后的js文件做缓存。
      "cache-loader",
      {
        loader: "babel-loader",
        options: {
          presets: [
            [
              "@babel/preset-env",// 预设:指示babel做怎么样的兼容处理 
            ]
          ],
          // 开启babel缓存,第二次构建时,会读取之前的缓存。
          cacheDirectory: true,
        }
      }
    ]
}

######3, 多进程打包(thread-loader)

一般只有在打包编译花费时间较长时才会用 thred-loader,因为这个loader 启动和通信都是有开销的~

// "thread-loader"放在babel-loader前,就会在babel-loader工作时进行多进程工作。 ??????
{
    loader: "thread-loader",
    options: {
        workers: 2, // 启动进程个数,默认是电脑cpu核数-1
    },
},
{
    loader: "babel-loader",
    options: {
        presets: [
            [
                "@babel/preset-env",
            ],
        ],
    },
}

######4, 外部扩展(externals)

告诉 webpack 要构建的代码中使用了哪些不用被打包的模块,这些模块可以使用外部引入(cdn)

module.export = {
  externals: {
    // 把导入语句里的 jquery 替换成运行环境里的全局变量 jQuery
    jquery: 'jQuery'
  }
}


//源代码
import $ from 'jquery';

//配置了 externals 即使代码中引入了这个文件库,webpack 也不会打包到 bundle 中,而是使用全局变量
5, dll
6, 树摇
7,代码分割

[专栏原文]:(juejin.cn/post/697174…)