给webpack写个loader

何为loader

就是把一个文件读出来,然后根据自己的喜好各种操作字符串,但最终的字符串一定要是js代码,然后丢给webpack处理压缩打包。

例如vue-loader,是把文件组件里面改装成用Vue.component注册的组件,变成直接在html页面引入vue.js的开发模式的写法。

来写一个自己的loader

假设名为cn-loader,用来解析扩展名为.cn的文件

  • 第一步:先搭一个webpack项目,如下:

目录:

- loaders
  |- cn-loader.js //这个就是我们的loader
- public
  |- index.html  //html模板
- src
  |- index.cn //入口文件
  |- test-1.cn
  |- test-2.cn
- webpack.config.js
- package.json

package.json配置:

{
  "name": "loader",
  "version": "1.0.0",
  "description": "",
  "main": "index.cn",
  "scripts": {
    "start": "webpack-dev-server --mode development",
    "dev": "webpack --mode development",
    "build": "webpack --mode production"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.9.0",
    "@babel/preset-env": "^7.9.0",
    "babel-loader": "^8.1.0",
    "clean-webpack-plugin": "^3.0.0",
    "html-webpack-plugin": "^4.0.3",
    "webpack": "^4.42.1",
    "webpack-cli": "^3.3.11",
    "webpack-dev-server": "^3.10.3"
  }
}

webpack.config.js配置:

const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
    module: {
        rules: [{
            test: /\.js$/,
            loader: 'babel-loader',
            options: {
                presets: ['@babel/preset-env']
            }
        },{
            test: /\.cn$/,
            use: './loaders/cn-loader.js',
            exclude: /node_modules/
        }]
    },
    resolve: {
        extensions: ['.js', '.cn']
    },
    plugins: [
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            template: './public/index.html'
        })
    ],
    stats: {
        modules: false
    }
}

好,项目基本就搭好了。

  • 第二步:假设我们用.cn文件来实现中文写代码

先看看test-1.cn:

导入:./test-2.cn 为:test2
打印:test2
变量:计数 = 100
函数:增加
    计数 = 计数 + 1
    打印:"计数的值为:" + 计数
    返回值:计数
执行:增加
导出:增加

没错,这是一段代码,它最终会被cn-loader解析成为:

import test2 from './test-2.cn'
console.log(test2)
var 计数 = 100
function 增加(){
    计数 = 计数 + 1
    console.log("计数的值为:" + 计数)
    return 计数
}
增加()
export default 增加

再看看简单的index.cn和test-2.cn
index.cn:

导入:./test-1.cn 为:test1
执行:test1

test-2.cn:

变量:哦嚯 = "哦嚯,这是test-2"
导出:哦嚯

注释:我们甚至可以js和中文混写
document.body.innerHTML = '<h1>cn-loader 是无敌的</h1>'

那么,关键的loader来了:

function parse(code){
    return code
    .replace(/导入[::]\s*(.+?)\s*(?:为[::]\s*(.+?))?(\n|$)/g, ($0, $1, $2) => {
        if($2 !== undefined){
            return 'import '+ $2 +' from "' +$1 + '";\n'
        }else{
            return 'import'+$1+';\n'
        }
    })
    .replace(/注释[::]/g, '//')
    .replace(/变量[::]\s*(.+?)(?:\s*=\s*(.+?))?(\n|$)/g, ($0, $1, $2) => {
        if($2 !== undefined){
            return 'var '+ $1 + '=' + $2 + ';\n'
        }
        return 'var '+$1+';\n';
    })
    .replace(/打印[::]\s*(.+?)(\n|$)/g, 'console.log($1)\n')
    .replace(/返回值[::]\s*(.+?)(\n|$)/g, 'return $1\n')
    .replace(/执行[::]\s*(.+?)(\n|$)/g, '$1()\n')
    .replace(/导出[::]\s*(.+?)(\n|$)/g, 'export default $1\n')
}
function parseFn(code){
    let lines = code.split(/\n/), fnStart = false
    for(let i=0; i<lines.length; i++){
        if(fnStart) {
            if(/^\s+/.test(lines[i])){
                continue
            }else{
                lines[i] = '}\n' + lines[i]
                fnStart = false
            }
        }
        if(!fnStart && /^函数[::]/.test(lines[i])){
            fnStart = true
            lines[i] = lines[i].replace(/^函数[::]\s*(.+?)\s*$/, 'function $1(){')
        }
    }
    return lines.join('\n')
}

//===== 华丽分隔线上部分是各种造作代码的逻辑: 就是把中文翻译成js=====
//===== 华丽分隔线下面这里就是这么回事,简单得要命================
module.exports = (code) => {
    // 一顿操作猛如虎
    code = parseFn(code)
    code = parse(code)
    // console.log(code)
    // 之后把操作好的东西给回webpack
    return code
}

顿时没什么神秘感,loader里module.exports 出去的函数,会被webpack在读取文件之后调用,并且会传来读取到的内容code。你只管对code操作,最后返回webpack认识的代码即可。

可能有人好奇文件的各种导入层级是不是要自己写递归?不是,导入导出的逻辑完全不用管,那是webpack基本的能力,你只管把它给你的每一份code解析成webpack认识的,webpack会根据我们给它的code中是否有import去递归找文件,一份一份输出code给我们操作。

现在是不是可以想得通.vue、.ts、tsx这些文件的loader了,脑瓜不疼了。

OK,撒花,鼓掌,各种夸!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 构建工具逐渐成为前端工程必备的工具,Grunt、Gulp、Fis、Webpack等等,译者有幸使用过Fis、Gul...
    陈坚生阅读 6,056评论 4 64
  • webpack使用学习 本分享学习借鉴webpack中文官网,官网链接(中文文档):https://www.web...
    腿毛怪丶叔叔阅读 899评论 0 5
  • webpack 是什么? 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(mo...
    IT老马阅读 3,354评论 2 27
  • 33、JS中的本地存储 把一些信息存储在当前浏览器指定域下的某一个地方(存储到物理硬盘中)1、不能跨浏览器传输:在...
    萌妹撒阅读 2,112评论 0 2
  • 人一生,有将近30多年的时间都需要工作,不是每个人都能找到令自己甘之如饴、为之奋斗的好工作。随着社会的高速发展,各...
    小胖子Miss_Li阅读 1,533评论 9 10