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']
})
]
}
源码映射-帮助调试
- source-map 会单独生成sourcemap文件,可以显示行和列
- eval-source-map 不会产生单独文件,但是可以显示行和列
- cheap-module-source-map 会单独生成文件,不会产生列。产生后你可以保存起来调试
- 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小插件
- cleanWebpackPlugin // 可以清除打包的旧文件
- copyWebpackPlugin // 复制指定文件到指定目录
- 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库中有注册三种方法
- tab同步注册
- tapAsync回调函数callback
- tabPromise注册的是promise
webpack手写