手写一个webpack-loader

目录结构

1
2
3
4
5
6
7
8
9
10
11
12
.
├── index.html
├── node_modules
├── loaders
│ ├── tpl-loader
│ │ └── index.js
│ └── utils.js
├── package.json
├── src
│ ├── app.js
│ └── test.tpl
└── webpack.config.js

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang='zh-CN'>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0,maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet">
<title></title>
</head>
<body>
<div id='app'></div>
<script>
</script>
</body>
</html>

package.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"name": "loader",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "webpack-dev-server",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.12.9",
"babel-loader": "^8.2.2",
"html-webpack-plugin": "^4.5.0",
"webpack": "^4.30.0",
"webpack-cli": "^3.30",
"webpack-dev-server": "^3.7.2"
}
}

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

const { resolve } = require('path')
const HTMLWebpackPlugin = require('html-webpack-plugin')

module.exports = {
mode: 'development',
entry: resolve(__dirname, 'src/app.js'),
output: {
path: resolve(__dirname, 'dist'),
filename: 'main.js'
},
devtool: 'cheap-module-source-map',
resolveLoader: {
modules: ['node_modules', resolve(__dirname, 'loaders')]
},
module: {
// webpack-loader 执行顺序 从后到前
rules: [
{
test: /\.tpl$/,
use: [
'babel-loader',
{
loader: 'tpl-loader',
options: {
log: true
}
}
]
}
]
},
plugins: [
new HTMLWebpackPlugin({
template: resolve(__dirname, 'index.html'),
filename: 'index.html',
})
],
devServer: {
port: 3000,
open: true
}
}

app.js

1
2
3
4
5
6
import tpl from './test.tpl'
const app = document.querySelector('#app');
app.innerHTML = tpl({
name: 'test',
joke: `如果有来生,要做一只鸟,飞越永恒,没有迷途的苦恼。东方有火红的希望南方有温暖的巢床,向西逐退残阳,向北唤醒芬芳。如果有来生,希望每次相遇,都能化为永恒。`
})

test.tpl

1
2
3
4
<div>
<h1>{{name}}</h1>
<h1>{{joke}}</h1>
</div>

tpl-loader/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const { tplReplace } = require('../utils');
const { getOptions } = require('loader-utils');
function tplLoader(source) {
source = source.replace(/\s+/g, '');
const { log } = getOptions(this)
const _log = log ? `console.log('compiled the file which is from ${this.resourcePath}')` : ''
console.log(log)
return `
export default (options) => {
${tplReplace.toString()}
${_log.toString()}
return tplReplace('${source}',options)
}
`

}
module.exports = tplLoader;

utils.js

1
2
3
4
5
6
7
8
function tplReplace(template, replaceObject) {
return template.replace(/\{\{(.*?)\}\}/g, (node, key) => {
return replaceObject[key]
})
}
module.exports = {
tplReplace
}