一、webpack的loader实现特殊资源加载
1. 常见loader
- babel-loader: ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,js代码兼容低版本浏览器处理
{
"presets": [
[
"@babel/preset-env"
]
]
}
{
"presets": [
["@babel/preset-env",{
"useBuiltIns":"usage",
"corejs":3
}]
],
"plugins":[
"@babel/plugin-transform-runtime",
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose" : true }]
]
}
const path = require('path')
module.exports={
...
module:{
rules: [
{
test: /\.js$/i,
exclude: '/(node_modules)/',
loader: 'babel-loader'
}
]
}
}
- style-loader: 把 CSS 插入到 DOM 中。
- css-loader: 会对 @import 和 url() 进行处理,就像 js 解析 import/require() 一样
- scss-loader: 加载 Sass/SCSS 文件并将他们编译为 CSS
2. 自定义一个简单loader
-
- 创建处理某种资源文件的处理逻辑文件,如(处理md文件资源): markdown-loader.js
module.exports = source => {
console.log('加载到的模块内容', source)
return `module.exports = ${JSON.stringify({a: 1, b: 2})}`
}
const { marked } = require("marked");
marked(source)
-
- 使用loader处理webpack不能处理自定义loader处理的资源
module.exports = {
...
module: {
rules: [
{
test: /\.md$/,
use: './markdown-loader'
}
]
}
}
import b from './b.md'
console.log(b);
-
- 自定义 loader 就能处理 md 文件了
-
- 多个loader配合处理同一种资源文件
module.exports = {
...
module: {
rules: [
{
test: /\.md$/,
use: [
'html-loader',
'./markdown-loader'
]
}
]
}
}
二、webpack的plugin插件机制横向扩展webpack的构建能力
1. webpack插件机制的目的: 为了增强webpack在项目自动化构建方面的能力
2. 插件的常见应用场景:
- 实现自动在打包之前清除dist目录 (clean-webpack-plugin)
- 自动生成应用所需要的HTML文件 (html-webpack-plugin)
- 根据不同环境为代码注入类似API地址这种可能变化的部分 - 定义全局变量的区分当前环境 (webpack.DefinePlugin)
- 拷贝不需要参与打包的资源文件到输入目录 (copy-webpack-plugin)
- 压缩Webpack打包完成后输出的文件 (terser-webpack-plugin)
- 自动发布打包结果到服务器实现自动化部署
......
3. 插件的常用使用方式
const { cleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
...
plugins: [
new cleanWebpackPlugin()
]
}
4. 自定义一个简单webpack插件
- 开发一个插件,如: 功能-清除打包生成的bundle.js中每一行前面的注释 /******/
- 创建实现该插件功能的文件: remove-comments-plugin.js
class RemoveCommentsPlugin {
apply(compiler) {
console.log('RemoveCommentsPlugin插件的apply方法触发', compiler)
}
}
- 通过 compiler 对象的 hooks 属性访问到 emit 钩子,再通过 tap 方法注册一个钩子函数
- 这个方法接收两个参数:
-
- 第一个是插件的名称: RemoveCommentsPlugin
-
- 第二个是要挂载到这个钩子上的函数
class RemoveCommentsPlugin {
apply(compiler) {
compiler.hooks.emit.tap('RemoveCommentsPlugin', compilation => {
for(let name in compilation.assets) {
if(name.endsWith('.js')) {
let contents = compilation.assets[name].source()
let handleResult = contents.replace(/\/\*{2,}\/\s?/g, '')
compilation.assets[name] = {
source: () => {
return handleResult
},
size: () => handleResult.length
}
}
}
})
}
}
5. 总结:
- webpack为每一个工作环节都预留了合适的钩子
- 扩展webpack插件功能时,只需要找到合适的时机(即webpack提供的钩子函数)去做合适的事情
- 插件机制又叫做:
面向切面编程-AOP