此文已由作者张磊授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。

前言

使用 webpack 的时候,难免需要写一些 loader,接着就会遇到一个很纠结的问题。该 loader 会生成一个文件,一般这个文件的生成时机都是在 loader 处理所有的文件后。一般有两种处理方案。一种是写一个 plugin,监听对应的事件;一种是生成一个临时文件,将每次读到的内容都写在 临时文件 中。第一种在使用的时候也很麻烦,需要同时在 loader 和 plugin 加一下对应的逻辑。第二种,写入临时文件的这个过程很是让人纠结。很明显,两种方案对于有一定洁癖的人来说,都不优雅,那么就来寻找一种方案,既不需要写 plugin,又不需要写入 临时文件 中。

解决方案

在 github 上找到一个可用解决方案的 loader,这个 loader 看起来是关于虚拟文件生成的,使用很简单,指定名字,指定内容,生成一个虚拟文件,研究了一下,对解决问题很有帮助。关键代码如下:

// index.jsimport * as fsPatch from './fs-patch';// 省略...fsPatch.add( this.fs, {    path:    file,    content: src
});// 省略...

这个文件传入 loader.fs,看起来是对 fs 打补丁,接着再来看 fs-patch.js

// fs-patch.jsimport path from 'path';const NS   = __filename;

export function patch( fs ) {    if ( fs[ NS ] )        return;    const virtualFS = {
        files: {},         add( options ) {            const file = path.resolve( options.path );
            virtualFS.files[ file ] = {
                path:    file,
                content: options.content
            };
        }     };
    fs[ NS ] = virtualFS;     createPatchFn( fs, 'readFile', function( orig, args, file, encoding, cb ) {        var rfile = path.resolve( file );        var vfile = virtualFS.files[ rfile ];        if ( vfile ) {            if ( typeof(encoding) === 'function' ) {
                cb       = encoding;
                encoding = null;
            }            var content = vfile.content;            if ( encoding != null )
                content = content.toString( encoding );             cb( null, content )            return;
        }        return orig.apply( this, args );
    });
    createPatchFn( fs, 'readFileSync', function( orig, args, file, encoding ) {        var rfile = path.resolve( file );        var vfile = virtualFS.files[ rfile ];        if ( vfile ) {            var content = vfile.content;            if ( encoding != null )
                content = content.toString( encoding );            return content;
        }        return orig.apply( this, args );
    });     createPatchFn( fs, 'stat', function( orig, args, p, cb ) {        var rp = path.resolve( p );        var vfile = virtualFS.files[ rp ];        if ( vfile ) {            var vstat = {
                dev: 8675309,
                nlink: 1,
                uid: 501,
                gid: 20,
                rdev: 0,
                blksize: 4096,
                ino: 44700000,
                mode: 33188,
                size: vfile.content.length,
                 isFile() { return true; },
                isDirectory() { return false; },
                isBlockDevice() { return false; },
                isCharacterDevice() { return false; },
                isSymbolicLink() { return false; },
                isFIFO() { return false; },
                isSocket() { return false; },
            };
            cb( null, vstat );            return;
        }        return orig.apply( this, args );
    });
    createPatchFn( fs, 'statSync', function( orig, args, p ) {        var rp = path.resolve( p );        var vfile = virtualFS.files[ rp ];        if ( vfile ) {            var vstat = {
                dev: 8675309,
                nlink: 1,
                uid: 501,
                gid: 20,
                rdev: 0,
                blksize: 4096,
                ino: 44700000,
                mode: 33188,
                size: vfile.content.length,
                 isFile() { return true; },
                isDirectory() { return false; },
                isBlockDevice() { return false; },
                isCharacterDevice() { return false; },
                isSymbolicLink() { return false; },
                isFIFO() { return false; },
                isSocket() { return false; },
            };            return vstat;
        }        return orig.apply( this, args );
    }); }; export function add( fs, options ) {
    patch( fs );
    fs[ NS ].add( options );
}function createPatchFn( obj, name, fn ) {    const origin  = obj[ name ];
    obj[ name ] = function() {        const args = Array.prototype.slice.call( arguments );        return fn.apply( this, [origin, args].concat( args ) );
    };
}

代码分析

可以看到 fs-patch.js 直接劫持了 loader.fs,重写了 fs 的一些方法,而重写的这些方法就是生成虚拟文件的关键。劫持后的 fs 在访问这些方法的时候,首先去从缓存中获取路径对应的内容,不存在则再从硬盘中读取。

优缺点

优点是不需要生成临时文件或者另写一个 plugin,缺点在文件比较大或者计算比较频繁,对机器的要求会比较高。

免费体验云安全(易盾)内容安全、验证码等服务

更多网易技术、产品、运营经验分享请点击

相关文章:
【推荐】 云架构师进阶攻略(1)
【推荐】 BRVAH(让RecyclerView变得更高效)(1)

webpack loader 生成虚拟文件的方案的更多相关文章

  1. webpack4 系列教程(十三):自动生成HTML文件

    作者按:因为教程所示图片使用的是 github 仓库图片,网速过慢的朋友请移步<webpack4 系列教程(十三):自动生成 HTML 文件>原文地址.更欢迎来我的小站看更多原创内容:go ...

  2. IIs 网站应用程序与虚拟目录的区别及高级应用说明(文件分布式存储方案)

    原文 IIs 网站应用程序与虚拟目录的区别及高级应用说明(文件分布式存储方案) 对于IIS网站,大伙用的比较多,就不啰嗦了.   今天和说说大伙比较少使用的"IIS应用程序”和虚拟目录的区别 ...

  3. webpack中使用html-webpack-plugin生成HTML文件并主动插入css和js引入标签

    html-webpack-plugin clean-webpack-plugin 一.html-webpack-plugin 由于打包时生成的css样式文件和js脚本文件会采用hash值作为文件命名的 ...

  4. shell 编程生成日期文件;Server虚拟机上进行Web服务器配置

    shell 编程生成日期文件 1. 请编写一个脚本,命名为sh01.sh,其功能是: 键盘输入文件名(要求使用名字全拼作为文件名). 自动创建3个文件. 1个为系统当天日期(CCYYMMDD). 1个 ...

  5. 在CabloyJS中将Webpack生成的文件自动上传到阿里云OSS

    背景 阿里云OSS提供了一个Webpack插件,可在Webpack打包结束后将webpack生成的文件自动上传到阿里云OSS中 下面看看在CabloyJS中如何使用该插件 新建项目,并配置MySQL连 ...

  6. vue项目通过webpack打包生成的dist文件放到express环境里运行(vue+webpack+express)

    1.首先需要的原料肯定是vue打包生成的dist文件 在vue项目目录下运行:npm run build,等待运行结束,会在项目目录下生成一个dist文件夹,里面会生成一些文件(如下图示) 小的项目文 ...

  7. 揭秘webpack loader

    前言 Loader(加载器) 是 webpack 的核心之一.它用于将不同类型的文件转换为 webpack 可识别的模块.本文将尝试深入探索 webpack 中的 loader,揭秘它的工作原理,以及 ...

  8. 怎样写一个webpack loader

    div{display:table-cell;vertical-align:middle}#crayon-theme-info .content *{float:left}#crayon-theme- ...

  9. 手把手教你撸一个 Webpack Loader

    文:小 boy(沪江网校Web前端工程师) 本文原创,转载请注明作者及出处 经常逛 webpack 官网的同学应该会很眼熟上面的图.正如它宣传的一样,webpack 能把左侧各种类型的文件(webpa ...

随机推荐

  1. 函数响应式编程(FRP)思想-Callback风格

    序 ReactiveCocoa是IOS广为使用的技术框架,而ReactiveCocoa的核心思想就FRP.FRP不同于JAVA的object-oriented和AOP,FRP能让你的代码像数学一样简洁 ...

  2. WIN10下的Docker安装

    1.什么是Docker Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化.容器是完全使用沙箱 ...

  3. linux c 获取当前时间 毫秒级的 unix网络编程

    #include <time.h> #inlcude <sys/time.h> char *gf_time(void) /* get the time */{ struct t ...

  4. Unity 游戏框架搭建 (二十三) 重构小工具 Platform

    在日常开发中,我们经常遇到或者写出这样的代码 var sTrAngeNamingVariable = "a variable"; #if UNITY_IOS || UNITY_AN ...

  5. Jquery中绑定事件与普通事件的区别

    (“#panel”).bind(“click”,function(){ 与$(“#panel”).click(function(){ 有什么区别 ? 绑定可以同时加多个事件 如:$(“#panel”) ...

  6. ThinkPHP5.0图片上传生成缩略图实例代码

    很多朋友遇到这样一个问题,图片上传生成缩略图,很多人在本机(win)测试成功,上传到linux 服务器后错误. 我也遇到同样的问题.网上一查,有无数的人说是服务器临时文件目录权限问题. 几经思考后,发 ...

  7. nginx的docker化部署

    nginx的docker化有一个隐藏的坑,就是其默认的配置目录(/etc/nginx)需要先从容器中拷贝出来. 拉取镜像 docker pull nginx 启动容器 docker run -d -- ...

  8. redis的主从配置

    redis的主备配置比较简单,只需要在配置上新增slaveof属性即可,如果主节点需要密码验证,则在加上masterauth属性. 测试安装一个备用redis,备份前一章的节点redis的docker ...

  9. Mac下PHP的环境搭建

    * 前段时间手欠 ... 入手了一个二手的Macbook pro ! 配置挺高的 16款13寸的基本顶配了 ... 只差 硬盘不是1T的 ... 可以脑补一下配置了* 话说 不是所有程序猿都说 每个程 ...

  10. 关于一个flask的服务接口实战(flask-migrate,flask-script,SQLAlchemy)

    前言 最近接到一个接收前端请求的需求,需要使用python编写,之前没有写过python,很多技术没有用过,在这里做一个学习记录,如有错误,请不了赐教. Flask Api文档管理 使用Falsk A ...