webpack快速上手

针对仅有js文件设置打包入口和出口

const path = require('path')
module.exports = {
  entry: './src/main.js',
  output: {
    filename: 'bundle.js',
    path: path.join(__dirname, 'output')
  }
}

package.json的scripts增加如下

"build": "webpack"

安装依赖webpack webpack-cli
将output文件夹下的bundle.js通过标签引入至index.html

mode

module.exports = {
  // 这个属性有三种取值,分别是 production、development 和 none。
  // 1. 生产模式下,Webpack 会自动优化打包结果;
  // 2. 开发模式下,Webpack 会自动优化打包速度,添加一些调试过程中的辅助;
  // 3. None 模式下,Webpack 就是运行最原始的打包,不做任何额外处理;
  mode: 'development',
}

含有其他文件打包构建 在 Webpack 中,loader 的执行顺序是从右到左、从下到上

module.exports = {
  module: {
    rules: [
      {
        test: /.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      },
      {
        test: /.png$/,
        use: 'file-loader'
      },
      {
        test: /.png$/,
        use: {
          loader: 'url-loader',
          options: {
            limit: 10 * 1024 // 10 KB
          }
        }
      }
    ]
  }
}

babel转译高阶语法

首先,安装必要的依赖:
npm install babel-loader @babel/preset-env @babel/core --save-dev

在 Webpack 配置中配置 babel-loader:

module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env']
        }
      }
    }
  ]
}

在项目根目录下创建 .babelrc 文件,配置 @babel/preset-env(可省略):

{
  "presets": ["@babel/preset-env"]
}

html-loader用于解析html,markdown文件的解析获取

  module: {
    rules: [
      {
        test: /.md$/,
        use: [
          'html-loader',
          './markdown-loader'
        ]
      }
    ]
  }

创建js文件,通过marked包来解析成html交给下一个loader处理

const marked = require('marked')

module.exports = source => {
  const html = marked(source)
  // return html
  // return `module.exports = "${html}"`
  // return `export default ${JSON.stringify(html)}`

  // 返回 html 字符串交给下一个 loader 处理
  return html
}

插件

module.exports = {
  plugins: [
    new webpack.ProgressPlugin(),
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin()
  ]
}

webpack.ProgressPlugin() 是 Webpack 的内置插件,用于在控制台中显示构建进度信息。当构建项目时,它会输出构建进度的百分比、模块数量等信息
CleanWebpackPlugin() 是一个第三方插件,用于在每次构建之前清理输出目录。
CopyWebpackPlugin() 在构建过程中将指定的文件或目录复制到输出目录
HtmlWebpackPlugin() 是第三方插件,用于简化创建 HTML 文件的过程。它会自动在构建过程中生成一个 HTML 文件,并将打包后的资源(如 JavaScript 文件)自动注入到 HTML 中,同时还可以设置模板、标题、meta 标签等内容,可多次调用生成多个HTML文件,设置模板填充内容实例如下

// 用于生成 index.html
    new HtmlWebpackPlugin({
      title: 'Webpack Plugin Sample',
      meta: {
        viewport: 'width=device-width'
      },
      template: './src/index.html'
    }),
    // 用于生成 about.html
    new HtmlWebpackPlugin({
      filename: 'about.html'
    })

template中的html文件含有如下内容可自动填充
<h1><%= htmlWebpackPlugin.options.title %></h1>

自己插件的开发 TO DO:

16-my-webpack-plugin

webpack配置实现项目启动和热更新

安装devServer,添加如下配置

module.exports = {
  devServer: {
    contentBase: './dist',
    hot: true,
	open:true,
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
  ],
};

webpack.HotModuleReplacementPlugin() 被添加到了插件列表中,以启用模块热替换功能。
在 devServer 配置中,设置了 contentBase,指定了服务器从哪里提供内容。通常这里设置为输出目录,以便 webpack-dev-server 可以从输出目录中提供生成的文件。
hot: true 选项告诉 webpack-dev-server 在发生代码更改时启用模块热替换,open:true浏览器自执行
确保在入口文件(例如 src/index.js)中添加了模块热替换的代码,以便接受更新的模块。

if (module.hot) {
  module.hot.accept();
}

source map

Source map 是一种文件,它将编译后的代码映射回原始源代码,这样在调试时可以更容易地追踪到问题所在。
webpack配置如下

devtool: 'inline-source-map'| 'eval' |  | 'cheap-source-map' | 'cheap-module-source-map'

webpack的配置可以是一个数组

生成多个输出文件

module.exports = [
	{
		entry: './src/main.js',
		output: {
			filename: 'a.js'
		}
	},
	{
		entry: './src/main.js',
		output: {
			filename: 'b.js'
		}
	}
]

不同sourceMap模式编译

const allModes = [
	'eval',
	'cheap-eval-source-map',
	'cheap-module-eval-source-map',
	'eval-source-map',
	'cheap-source-map',
	'cheap-module-source-map',
	'inline-cheap-source-map',
	'inline-cheap-module-source-map',
	'source-map',
	'inline-source-map',
	'hidden-source-map',
	'nosources-source-map'
]
module.exports = allModes.map(item => {
	return {
		devtool: item,
		mode: 'none',
		entry: './src/main.js',
		output: {
			filename: `js/${item}.js`
		},
		module: {
			rules: [
				{
					test: /\.js$/,
					use: {
						loader: 'babel-loader',
						options: {
							presets: ['@babel/preset-env']
						}
					}
				}
			]
		},
		plugins: [
			new HtmlWebpackPlugin({
				filename: `${item}.html`
			})
		]
	}
})

webpack-dev-server自动刷新来实现页面更新含有问题

若编辑器中已编辑数据,再写一些样式刷新后刚才输入文字没有了,这种情况需要手动刷新,入口文件中HMRAPI

// ================================================================
// HMR 手动处理模块热更新
// 不用担心这些代码在生产环境冗余的问题,因为通过 webpack 打包后,
// 这些代码全部会被移除,这些只是开发阶段用到
if (module.hot) {
  let hotEditor = editor
  module.hot.accept('./editor.js', () => {
    // 当 editor.js 更新,自动执行此函数
    // 临时记录编辑器内容
    const value = hotEditor.innerHTML
    // 移除更新前的元素
    document.body.removeChild(hotEditor)
    // 创建新的编辑器
    // 此时 createEditor 已经是更新过后的函数了
    hotEditor = createEditor()
    // 还原编辑器内容
    hotEditor.innerHTML = value
    // 追加到页面
    document.body.appendChild(hotEditor)
  })

  module.hot.accept('./better.png', () => {
    // 当 better.png 更新后执行
    // 重写设置 src 会触发图片元素重新加载,从而局部更新图片
    img.src = background
  })

  // style-loader 内部自动处理更新样式,所以不需要手动处理样式模块

HMRAPI中出现错误,则又会执行热更新自动刷新,错误不出现解决

devServer: {
    hot: true
    hotOnly: true // 只使用 HMR,不会 fallback 到 live reloading
  },

生产环境webpack处理

module.exports = (env, argv) => {
  const config = {
    //开发环境下配置
  }

  if (env === 'production') {
    config.mode = 'production'
    config.devtool = false
    config.plugins = [
      ...config.plugins,
      new CleanWebpackPlugin(),
      new CopyWebpackPlugin(['public'])
    ]
  }

  return config
}

合并webpack配置

将生产环境,开发环境配置分开,先写公共配置,将公共配置和生产或者开发环境配置合并在一起

const webpack = require('webpack')
const merge = require('webpack-merge')
const common = require('./webpack.common')

module.exports = merge(common, {
  mode: 'development',
  devtool: 'cheap-eval-module-source-map',
  devServer: {
    hot: true,
    contentBase: 'public'
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ]
})

package.json启动命令
"build": "webpack --config webpack.prod.js"

DefinePlugin

用于在编译时创建全局常量,这些常量可以在项目的源代码中使用

module.exports = {
  // other webpack configuration options

  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('production')
    })
  ]
};

在项目的源代码中,你可以通过 process.env.NODE_ENV 来访问这个值

Tree shaking是删除应用程序中未使用的代码

webpack配置和babel注意事项

module.exports = {
  mode: 'none',
  entry: './src/index.js',
  output: {
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              // 如果 Babel 加载模块时已经转换了 ESM,则会导致 Tree Shaking 失效
              // ['@babel/preset-env', { modules: 'commonjs' }]
              // ['@babel/preset-env', { modules: false }]
              // 也可以使用默认配置,也就是 auto,这样 babel-loader 会自动关闭 ESM 转换
              ['@babel/preset-env', { modules: 'auto' }]
            ]
          }
        }
      }
    ]
  },
  optimization: {
    // 模块只导出被使用的成员
    usedExports: true,
    // 尽可能合并每一个模块到一个函数中
    // concatenateModules: true,
    // 压缩输出结果
    // minimize: true
  }
}

多入口打包

module.exports = {
  mode: 'none',
  entry: {
    index: './src/index.js',
    album: './src/album.js'
  },
  output: {
    filename: '[name].bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: 'Multi Entry',
      template: './src/index.html',
      filename: 'index.html',
      chunks: ['index']
    }),
    new HtmlWebpackPlugin({
      title: 'Multi Entry',
      template: './src/album.html',
      filename: 'album.html',
      chunks: ['album']
    })
  ]
}

打包后的文件夹

image

optimization: {
    splitChunks: {
      // 自动提取所有公共模块到单独 bundle
      chunks: 'all'
    }
  },

加上这个配置打包后的文件夹
image

webpack动态导入 动态导入模块被自动分包

在入口文件中

if (hash === '#posts') {
    // mainElement.appendChild(posts())
    import(/* webpackChunkName: 'components' */'./posts/posts').then(({ default: posts }) => {
      mainElement.appendChild(posts())
    })
  } else if (hash === '#album') {
    // mainElement.appendChild(album())
    import(/* webpackChunkName: 'components' */'./album/album').then(({ default: album }) => {
      mainElement.appendChild(album())
    })
  }

/* webpackChunkName: 'components' */用于给分包的bundle起名字。

MiniCssExtractPlugin用于提取单独css文件

style-loader不再适用//将样式通过 style 标签注入

module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          // 'style-loader', // 将样式通过 style 标签注入
          MiniCssExtractPlugin.loader,
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin()
  ]

压缩js和css插件

optimization: {
    minimizer: [
      new TerserWebpackPlugin(), // 使用 TerserWebpackPlugin 压缩 JavaScript
      new OptimizeCssAssetsWebpackPlugin() // 使用 OptimizeCssAssetsWebpackPlugin 压缩 CSS
    ]
  }
  

生产环境文件名称加哈希值防止缓存策略文件不更新

对客户端而言,全新的文件名就是全新的请求,没有缓存问题

	output: {
    filename: '[name]-[contenthash:8].bundle.js'
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name]-[contenthash:8].bundle.css'
    })
  ]

在服务器端,设置 Cache-Control: max-age=31536000 表示静态资源可以在浏览器中缓存一年。

posted @ 2024-03-28 18:02  穹顶之下影  阅读(6)  评论(0编辑  收藏  举报