webpack4学习笔记

一、webpack是什么?

webpack其实就是一个JavaScript应用程序的静态打包器

二、webpack有什么用?

1.模块化打包
webpack会将项目的资源文件当成一个一个模块,模块之间会有依赖关系,webpack将会对这些有依赖关系的文件进行处理,让浏览器能够识别,
最后将应用程序需要的每个模块打包成一个或者多个bundle(包)。

三、entry

起点或是应用程序的起点入口。从这个起点开始,应用程序启动执行。如果传递一个数组,那么数组的每一项都会执行。

动态加载的模块不是入口起点。

简单规则:每个 HTML 页面都有一个入口起点。单页应用(SPA):一个入口起点,多页应用(MPA):多个入口起点。

单入口

 entry: './path/to/my/entry/file.js'

多入口

entry: {
  home: "./home.js",
  about: "./about.js",
  contact: "./contact.js"
}

四、output

output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist。基本上,整个应用程序结构,都会被编译到你指定的输出路径的文件夹中。你可以通过在配置中指定一个 output 字段,来配置这些处理过程:

  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-first-webpack.bundle.js',
    publicPath: 'https://www.aaa.com' // 给所以资源打包地址加上路径,如上传cdn时填cdn地址
  }

filename的相关配置

使用入口名称:name模块名称

filename: "[name].bundle.js"

使用内部 chunk id

filename: "[id].bundle.js"
filename: "[name].[hash].bundle.js"
// 或者限制hash长度
filename: "[name].[hash:8].bundle.js"

使用基于每个 chunk 内容的 hash:

filename: "[chunkhash].bundle.js"

五、loader

loader让webpack能够去处理那些非JavaScript文件(webpack自身只能理解JavaScript)。loader可以将所有类型的文件转换为webpack能够处理的有效模块,然后你就可以利用webpack的打包能力,对它们进行处理。

本质上,webpack loader将所有类型的文件,转换为应用程序的依赖图(和最终的bundle)可以直接引入的模块。

css打包解析:
在webpack的配置中loader有两个目标:
1. test属性,用于标识出应该被对应的loader进行转换的某个或某些文件。
2. use属性,表示进行转换时,应该使用哪个loader

需要安装依赖npm install style-loader css-loader --save-dev

代码:

module: {
	rules:[
		{
			/// 用于标识出应该被对应的loader进行转换的某个或某些文件。
			test:/\.css$/,
			// 表示进行转换时,应该使用哪个loader
			// 1.loader的顺序默认是从右向左执行的,从下到上执行
			// 2. css-loader 用于连接@import这种语法,解析路径
			// 3. style-loader 他是把css插入到head的标签中
			// 4. loader的用法 字符串只用一个loader,多个loader需要[],也可以写成对象
			// 5. loader的特点 希望功能单一,所以要分开两个loader转换css
			use:['style-loader', 'css-loader']
		}
	]
}

对象loader

module: {
	rules:[
		{
			test:/\.css$/,
			// 从下到上执行
			use: [
				{
					loader: 'style-loader',
					options: {
						// 插入到head的位置
						insertAt: 'top'
					}
				},
				{
					loader: 'css-loader'
				}
			]
		}
	]
}

less打包解析:

还需要安装依赖npm install less-loader less --save-dev

代码:

module: {
	rules:[
		{
			test:/\.less$/,
			use:['style-loader', 'css-loader', 'less-loader']
		}
	]
}

sass打包解析:
和less一样
还需要安装依赖npm install sass-loader sass–save-dev

代码:

module: {
	rules:[
		{
			test:/\.scss$/,
			use:['style-loader', 'css-loader', 'sass-loader']
		}
	]
}

单独打包成css文件,而不是js(抽离css)
用MiniCssExtractPlugin.loader 替换style-loader

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

plugins: [
	new MiniCssExtractPlugin({
		filename: 'main.css
		})
]

module: {
	rules:[
		{
			test:/\.css$/,
			use:[
			MiniCssExtractPlugin.loader,
			'css-loader'
			]
		}
	]
}

postcssLoader和autoprefixer
用于添加浏览器前缀

module: {
	rules:[
		{
			test:/\.css$/,
			use:[
			MiniCssExtractPlugin.loader,
			'css-loader',
			'postcss-loader'
			]
		}
	]
}

需要新建postcss.config.js配置文件
内容:

module.export = {
	plugons: [require('autoprefixer')]
}

css压缩
虽然单独打包了,但是没有压缩
需要用到插件 optimize-css-assets-webpack-plugin

const optimizeCss = require('optimize-css-assets-webpack-plugin')

module.exports = {
	// 优化项
	optimization: {
		minimizer: [new optimizeCss ()]
	}
}

js压缩
引入插件UglifyJsPlugin

const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

module.exports = {
	// 优化项
	optimization: {
		minimizer: [
			new UglifyJsPlugin({
				cache: true, // 是否缓存
				parallel: true, // 是否并发打包
				sourceMap: true // 是否源码映射
			}),
			new optimizeCss ()
		]
	}
}

转化es6语法

插件:

  • babel-loader
  • @babel/core —核心模块 加载程序
  • @babel/preset-env —转化模块
  • @babel/plugin-proposal-class-properties --------类的属性插件,处理class语法
  • @babel/plugin-proposal-decorators -------------装饰器语法插件
  • @babel/plugin-transform-runtime和@babel/runtime 解决模块引入和全局污染问题
  • @babel-polyfill -------------- 转换新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象。
  • eslint、eslint-loader -------代码校验
  • 需要新建.eslintrc.json文件在根目录
    配置:
module: {
	rules:[
		{
			test: /\.js$/, //匹配js文件
			use:{
				loader: 'babel-loader',
				options: { // 用babel-loader 需要把es6转es5
					presets:[
						'@babel/preset-env'
					]
				},
				plugins: [
				  ["@babel/plugin-proposal-decorators", { "legacy": true }],
                  ["@babel/plugin-proposal-class-properties", { "loose" : true }],
                  "@babel/plugin-transform-runtime"
				]	
			},
			include: path.resolve(_dirname, 'src'), // 包含
			exclude: /node_modules/       // 排除
		},
		{
			test: /\.js$/,
			use: {
				loader: 'eslint-loader',
				potions: {
					enforce: 'pre' // 执行顺序,pre强制提前 post后置 normal正常
				}
			}
		}
	]
}

全局变量的引入

const webpack require('webpack')
plugins: [
	new webpack.ProvidePlugin({
		$: 'jquery'
	})
]

cdn外部引入

externals: { // 表明是第三方引入,打包时不需打包
	jquery: '$'
}

webpack打包图片

  • file-loader 默认会在内部生成一张图片到build目录下,把生成的图片的名字返回回来
  • html-withimg-loader 编译html里的图片
  • url-loader 当图片小于多少k用base64,否则用file-loader产生真实图片
module: {
	rules:[
		{
			test:/\.html$/,
			use:'html-withimg-loader'
		},
		{
			test:/\.(png|jpg|gif)$/,
			use: {
				loader: 'url-loader',
				options: {
					limit: 200*1024,
					outputPath: 'img/' // 打包分类,指定到某个文件夹
				}
			}
		}
	]
}

打包多页面

  • html-webpack-plugin 用模板生成html,并自动引入js
const path = require('path')
const HtmlWebpackPlugin= require(' html-webpack-plugin')
module.exports = {
	entry: {
		home: './src/index.js',
		other: './src/other.js'
	},
	output: {
		filename: '[name].js', // 根据多入口的名字,输出文件命名
		path: path.resolve(__dirname, 'dist')
	},
	plugins: [
		new HtmlWebpackPlugin({
			template: './index.html',
			filename: 'home.html',
			chunks: ['home'] // 代码块按需引入
		}),
		new HtmlWebpackPlugin({
			template: './index.html',
			filename: 'other.html',
			chunks: ['other']
		})
	]
}

源码映射-帮助调试

  1. source-map 会单独生成sourcemap文件,可以显示行和列
  2. eval-source-map 不会产生单独文件,但是可以显示行和列
  3. cheap-module-source-map 会单独生成文件,不会产生列。产生后你可以保存起来调试
  4. cheap-module-eval-source-map 不会产生单独文件,不会产生列
module.exports = {
	devtool: 'source-map' // 开启源码映射
}

watch的用法-热更新

module.exports = {
	watch: true, // 开启热更新
	watchOptions: { // 配置项
		poll: 1000, // 每秒 监听1000次
		aggregateTimeout: 500, // 防抖动 500毫秒内输入不更新 
		ignored: /node_modules/  // 不需要监控
	}
}

webpack小插件

  1. cleanWebpackPlugin // 可以清除打包的旧文件
  2. copyWebpackPlugin // 复制指定文件到指定目录
  3. bannerPlugin // 内置 版权声明
const CleanWebpackPlugin = require('clean-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const webpack= require('webpack')
module.exports = {
	plugins: [
		new CleanWebpackPlugin('./dist'), // 先删除dist,再打包
		new CopyWebpackPlugin([
			{from: './doc', to: './'}
		]),
		new webpack.BannerPlugin('make 2020 by xxx')
	]
}

webpack跨域问题

  • 设置代理
module.exports = {
	 devServer: {
        proxy: {
            '/api': {
                target: 'http://localhost:3000',
                pathRewrite: { // 重写路径,相当于/api被替换成''
                    '^/api': ''
                }
            }
        }
    }
}
  • 单纯模拟数据
module.exports = {
	 devServer: {
        before(app) {
        	app.get('/user', (req, res) => {
        		res.json({name: 'aaaa'})
        	})
        }
    }
}

resolve属性的配置
解析 第三方包 common

module.exports = {
	 resolve: { // 解析 第三方包 common
      modules: [path.resolve('node_modules')],
      extensions:['.js', '.css', '.json'], // 指定查找文件后缀顺序
      mainFields:['style', 'main'], // 指定入口文件,先找style,在找main
      alias:{ // 别名
      	bootstrapCss: 'bootstrap/dist/dist/css/bootstrao.css'
      }
    }
}

// 用法
import bootstrapCss from 'bootstrapCss'

定义环境变量

  • webpack内置插件DefinePlugin
module.exports = {
	 plugins: [
		new webpack.DefinePlugin({
			DEV: JSON.stringify('dev')
		})
	]
}

// 在js文件中可直接用DEV变量if (DEV === 'dev') {
	console.log('我是开发环境')
}
else {
	console.log('我是线上环境')
}

区分不同环境

  • 插件 webpack-merge
  • 新建webpack.dev.js、webpack.prod.js;根据环境写不同配置
  • 将webpack.config.js重命名为webpack.base.js

// webpack.dev.js中写

const { smart } = require('webpack-merge')
const base = require('./webpack.base.js')
module.exports = smart(base, {
	mode: 'dev'
})

// 可按环境跑不同的命令,执行不同的配置文件
// 运行时命令修改为
npm run build -- --config webpack.dev.js

优化-noParse、 exclude

  • noParse: /jquery/ 打包是不去解析某个依赖,缩短打包时间
  • exclude: /node_modules/, 排除node_modules

需要依赖

npm i webpack webpack-cli html-webpack-plugin @babel/core babel-loader @babel/preset-env @babel/preset-react -D

webpack.config.js配置

import path from 'path'
import HtmlWebpackPluginfrom 'html-webpack-plugin'
module.exports = {
	mode: 'development',
	entry: './src/index.js',
	output: {
		filename: 'bundle.js',
		path: path.resolve(__dirname, 'dist')
	},
	module:{
		noParse: /jquery/, // 不去解析jquery的依赖库
		rule: [
			{
				test: /\.js$/,
				exclude: /node_modules/, 排除node_modules
				use: {
					loader: 'babel-loader',
					options: {
						presets: [
							'@babel/preset-env',
							'@babel/preset-react'
						]
					}
				}
			}
		]
	},
	plugins:[
		new HtmlWebpackPluginfrom ({
			template: './public/index.html'
		})
	]
}

优化-IgnorePlugin
// 使用第三方插件

例子moment时间插件

// 项目中

import moment from 'moment'
moment.locale('zh-cn')
moment().endOf('day').fromNow()

// 配置中排除其他不需要的引入

module.exports = {
	plugins:[
		// 当使用moment第三方模块moment时,排除打包.locale
		new webpack.IgnorePlugin(/\.\/locale, /moment/)
	]
}
// 这时需要在项目代码引入'zh-cn'

import 'moment/locale/zh-cn'

动态链接库-dllPlugin

新建一个webpack.react.js文件

module.exports = {
	mode: 'development',
	entry: {
		react: ['react', 'react-dom'] // 将这两个依赖打包
	},
	output: {
		filename: '_dll_[name].js',
		path: path.resolve(__dirname, 'dist'),
		library: '_dll_[name]'
	},
	plugins:[
		// 生成目录清单
		new webpack.DLLPlugin({
			name: '_dll_[name]',
			path: path.resolve(__dirname, 'dist', 'manifest.json')
		})
	]
}

在webpack.config.js中配置

module.exports = {
	plugins:[
		// 查找依赖时先搜索目录清单,看看有没有
		new webpack.DllReferencePlugin({
			manifest: path.resolve(__dirname, 'dist', 'manifest.json')
		})
	]
}

多线程打包-happypack

const Happypack= requier('happypack')

module.exports = {
	module:{
		noParse: /jquery/, // 不去解析jquery的依赖库
		rule: [
			{
				test: /\.js$/,
				use: 'happypack/loader?id=js'
			},
			{
				test: /\.css$/,
				use: 'happypack/loader?id=css'
			}
		]
	},
	plugins:[
		new Happypack ({
			id: 'js',
			use: [
					{
					loader: 'babel-loader',
					options: {
						presets: [
							'@babel/preset-env',
							'@babel/preset-react'
						]
					}
				}
			]
		}),
			new Happypack ({
			id: 'css',
			use: ['style-loader', 'css-loader']
		})
	]
}

webpack自带的优化

module.exports = {
	mode: 'production'
}
  • 当mode: 'production’时,import语法在生产环境下会自动去除没有的代码,又叫tree-shaking 把没用到的代码自动删除掉。require不支持。
  • 在webpack中会自动省略可以简化的代码

抽离公共代码

  • 新建index.js和other.js文件,新建a.js和b.js文件;index和other都引用a.js和b.js。这个时候a和b其实是公共代码。
  • 第三方公共模块也可以单独抽离到vendor上

如何抽离

module.exports = {
	optimization: {
		splitChunks: { // 分割代码块
			cacheGroups: { // 缓存组
				common: { // 公共模块
					chunks: 'initial',
					minSize: 0, // 超过多少抽离
					minChunks: 2 // 超过多少次引用抽离
				}
			}
		}
	}
}

懒加载

import('./a.js').then(data => {
	console.log('data', data)
})

// 想要支持上面的语法需要用到插件@babel/plugin-syntax-dynamic-import

module.exports = {
		module:{
		noParse: /jquery/, // 不去解析jquery的依赖库
		rule: [
			{
				test: /\.js$/,
				exclude: /node_modules/, 排除node_modules
				use: {
					loader: 'babel-loader',
					options: {
						presets: [
							'@babel/preset-env',
							'@babel/preset-react'
						]
					},
					plugins: ['@babel/plugin-syntax-dynamic-import']
				}
			}
		]
	}
}

热更新

module.exports = {
		devServer:{
			hot: true, // 开启热更新
			port: 3000,
			open: true,
			contentBase: './dist'
		},
		plugins: [
			new webpack.NamedModoluesPlugin(), // 告诉那个模块更新
			new webpack.HotModuleReplacementPlugin() // 热更新插件
		]
}

Tapable

Webpack本质上是一种事件流机制,它的工作流就是将各个插件串联起来,而实现这一切的核心就是Tapable,Tapable有点类似于node.js的events库,核心原理也是依赖于发布订阅模式

  • AsyncParralleHook异步并行钩子:所有注册的异步函数同时执行,执行完毕并返回。
  • AsyncSeriesHook异步串行钩子:所有注册的异步函数按顺序执行,执行完毕并返回。
  • AsyncSeriesWaterfallHook异步串行瀑布钩子:所有注册的异步函数按顺序执行,并且使用上一个函数的返回值作为下一个函数的参数,执行完毕并返回。

tapable库中有注册三种方法

  1. tab同步注册
  2. tapAsync回调函数callback
  3. tabPromise注册的是promise

webpack手写

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值