手写 webpack loader 练习

511 阅读1分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

关于 webpack loader

在学习 webpack 的过程中,避免不了要与 loaderplugin 打交道,而手写一个自定义的 loader 或是 plugin,则是检验自己学习成果的最好的方式之一。

那么接下来就来手写一个简单的 markdown-loader 吧!

初始化项目

创建文件

  • 首先,需要初始化我们的项目,包含 html、和 main.ts 文件,并用 webpack 进行相关配置。

package.json

{
  "name": "webpack-loader-demo-1",
  "version": "1.0.0",
  "license": "MIT",
  "dependencies": {
    "@babel/core": "7.12.3",
    "@babel/generator": "7.12.5",
    "@babel/parser": "7.12.5",
    "@babel/preset-env": "7.12.1",
    "@babel/traverse": "7.12.5",
    "@types/babel__traverse": "7.0.15",
    "html-loader": "^2.1.2",
    "html-webpack-plugin": "^5.4.0",
    "marked": "^3.0.7",
    "shelljs": "0.8.4",
    "ts-node": "9.0.0",
    "typescript": "4.0.5",
    "webpack": "^5.59.0"
  },
  "devDependencies": {
    "@types/babel__core": "7.1.12",
    "@types/babel__generator": "7.6.2",
    "@types/babel__parser": "7.1.1",
    "@types/babel__preset-env": "7.9.1",
    "@types/node": "14.14.6",
    "@types/shelljs": "0.8.8",
    "babel-loader": "^8.2.2",
    "ts-loader": "^9.2.6",
    "webpack-cli": "^4.9.1"
  }
}

webpack.config.js

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const rootDir = process.cwd()

module.exports = {
  mode: 'production',
  entry: './src/main.ts',
  output: {
    filename: 'bundle.js',
    path: path.join(__dirname, 'dist'),
  },
  module: {
    rules: [
      {
        test: /.(ts|tsx)$/,
        use: [
          'babel-loader',
          {
            loader: 'ts-loader',
            options: {
              happyPackMode: true,
              transpileOnly: true,
            },
          },
        ],
        exclude: /node_modules/,
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(rootDir, 'public/index.html'),
      inject: 'body',
      scriptLoading: 'blocking',
      minify: {
        removeComments: true, //移除HTML中的注释
        collapseWhitespace: true, //删除空白符与换行符
      },
    })
  ]
}

通过 yarn install 安装相关依赖

创建入口文件

在根目录下创建 public/index.html文件,写入:

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
</body>
</html>

在根目录下创建 src/main.tssrc/README.md 文件

// src/main.ts
import readme from './README.md'

const div = document.createElement('div')
div.innerHTML = readme
document.body.appendChild(div)
// src/README.md
# Hello
测试 markdown-loader

# Hi
这里是第二行

到这里,准备工作已经全部完成,接下来就让我们来写我们的 loader 吧!

开始写 loader!

编写 markdown-loader.js

  • 在根目录创建 markdown-loader.js 文件:

function transform (source) {
  const html = 'test'
  return `export default ${JSON.stringify(html)}`; // webpack 官网推荐写法
}

module.exports = transform
  • webpack.config.js 中使用我们的 loader
module.exports = {
    //...
    module: {
        rules: [
            //...
            {
              test: /.md$/,// 匹配入口文件中加载到的md文件
              use: [
                './markdown-loader'
              ]
            }
        ] 
    }
}

第一次测试

运行 yarn build,并打开 dist 目录下的 index.html 文件,查看内容

image.png 可以看到,页面中正是我们写入的 test 的字符串

使用 marked 来转换字符串

yarn add marked
//markdown-loader.js
const marked = require('marked')

function transform (source) {
  const html = marked(source)
  return `export default ${JSON.stringify(html)}`;
}

module.exports = transform

第四次测试

运行 yarn build,并打开 dist 目录下的 index.html 文件,查看内容

image.png

完成目标!

总结

webpackloader本质就是把不是 JS 的内容,包装成能运行的 export default ${JSON.stringify(text)}形式的 JS 代码

源码仓库

github.com/wbh13285517…