怎样写一个webpack loader

div{display:table-cell;vertical-align:middle}#crayon-theme-info .content *{float:left}#crayon-theme-info .field{font-weight:bold}#crayon-theme-info .field,#crayon-theme-info .value{margin-left:5px}#crayon-theme-info .description.value{font-style:italic;color:#999}#crayon-theme-info .type{text-align:center;min-width:120px;font-weight:bold;border-right:1px solid #ccc;padding-right:5px}#crayon-theme-info .type.stock{color:#666}#crayon-theme-info .type.user{color:#5b9a00}#crayon-editor-table td{vertical-align:top}.small-icon{width:24px;height:24px;display:inline-block;margin:5px 5px 0 0}#twitter-icon{background:url(../images/twitter.png)}#gmail-icon{background:url(../images/google.png)}#docs-icon{background:url(../images/docs.png)}#git-icon{background:url(../images/github.png)}#wp-icon{background:url(../images/wordpress-blue.png)}#donate-icon{background:url(../images/donate.png);width:75px}#crayon-donate,#crayon-donate input{margin:0;display:inline;padding:0}#crayon-theme-editor-info a{text-decoration:none!important;font-style:italic!important;color:#666!important}#crayon-main-wrap .form-table .note{font-style:italic;color:#999}#crayon-change-code-text{width:400px;height:300px}.crayon-syntax{overflow:hidden!important;position:relative!important;direction:ltr;text-align:left;box-sizing:border-box;direction:ltr!important;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;-webkit-text-size-adjust:none}.crayon-syntax div{background:0;border:0;padding:0;margin:0;text-align:left}.crayon-syntax.crayon-loading{visibility:hidden}.crayon-syntax,.crayon-syntax .crayon-main,.crayon-syntax .crayon-toolbar,.crayon-syntax .crayon-info,.crayon-syntax .crayon-plain,.crayon-syntax .crayon-code{width:100%}.crayon-syntax .crayon-main,.crayon-syntax .crayon-plain{overflow:auto}.crayon-syntax,.crayon-syntax .crayon-main,.crayon-syntax .crayon-plain,.crayon-syntax .crayon-table{padding:0;margin:0}.crayon-syntax-inline{margin:0 2px;padding:0 2px}.crayon-syntax .crayon-table{border:none!important;background:none!important;padding:0!important;margin-top:0!important;margin-right:0!important;margin-bottom:0!important;width:auto!important;border-spacing:0!important;border-collapse:collapse!important;table-layout:auto!important}.crayon-syntax .crayon-table td,.crayon-syntax .crayon-table tr{padding:0!important;border:none!important;background:0;vertical-align:top!important;margin:0!important}.crayon-syntax .crayon-invisible{display:none!important}.crayon-plain-tag{margin-bottom:12px}.crayon-popup .crayon-plain{display:block!important;width:100%!important;height:100%!important;opacity:100!important;position:relative!important}.crayon-popup-window{background:#fff}.crayon-syntax .crayon-num{text-align:center;padding:0 5px;margin:0}.crayon-syntax .crayon-toolbar{position:relative;overflow:hidden;z-index:4}.crayon-syntax .crayon-info{position:absolute;overflow:hidden;display:none;z-index:3;padding:0;min-height:18px;line-height:18px}.crayon-syntax .crayon-info div{padding:2px!important;text-align:center}.crayon-syntax .crayon-toolbar span{padding:0 4px!important}.crayon-syntax .crayon-toolbar .crayon-button{display:inline;float:left!important;position:relative;width:24px;background-repeat:no-repeat;line-height:15px;border:0;text-decoration:none}.crayon-toolbar .crayon-button,.crayon-toolbar .crayon-button:hover,.crayon-toolbar .crayon-button.crayon-pressed:hover{background-position:0 center}.crayon-toolbar .crayon-button.crayon-pressed,.crayon-toolbar .crayon-button:active,.crayon-toolbar .crayon-button.crayon-pressed:active{background-position:-24px 0}.crayon-toolbar .crayon-button.crayon-popup-button .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-popup-button:hover .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-popup-button.crayon-pressed:hover .crayon-button-icon{background-position:0 0}.crayon-toolbar .crayon-button.crayon-copy-button .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-copy-button:hover .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-copy-button.crayon-pressed:hover .crayon-button-icon{background-position:0 -16px}.crayon-toolbar .crayon-button.crayon-nums-button .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-nums-button:hover .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-nums-button.crayon-pressed:hover .crayon-button-icon{background-position:0 -32px}.crayon-toolbar .crayon-button.crayon-plain-button .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-plain-button:hover .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-plain-button.crayon-pressed:hover .crayon-button-icon{background-position:0 -48px}.crayon-toolbar .crayon-button.crayon-mixed-button .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-mixed-button:hover .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-mixed-button.crayon-pressed:hover .crayon-button-icon{background-position:0 -64px}.crayon-toolbar .crayon-button.crayon-minimize .crayon-button-icon{background-position:0 -80px;background-color:transparent!important}.crayon-toolbar .crayon-button.crayon-expand-button .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-expand-button:hover .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-expand-button.crayon-pressed:hover .crayon-button-icon{background-position:0 -96px}.crayon-toolbar .crayon-button.crayon-wrap-button .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-wrap-button:hover .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-wrap-button.crayon-pressed:hover .crayon-button-icon{background-position:0 -112px}.crayon-toolbar .crayon-button.crayon-popup-button.crayon-pressed .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-popup-button:active .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-popup-button.crayon-pressed:active .crayon-button-icon{background-position:-24px 0}.crayon-toolbar .crayon-button.crayon-copy-button.crayon-pressed .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-copy-button:active .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-copy-button.crayon-pressed:active .crayon-button-icon{background-position:-24px -16px}.crayon-toolbar .crayon-button.crayon-nums-button.crayon-pressed .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-nums-button:active .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-nums-button.crayon-pressed:active .crayon-button-icon{background-position:-24px -32px}.crayon-toolbar .crayon-button.crayon-plain-button.crayon-pressed .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-plain-button:active .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-plain-button.crayon-pressed:active .crayon-button-icon{background-position:-24px -48px}.crayon-toolbar .crayon-button.crayon-mixed-button.crayon-pressed .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-mixed-button:active .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-mixed-button.crayon-pressed:active .crayon-button-icon{background-position:-24px -64px}.crayon-toolbar .crayon-button.crayon-minimize .crayon-button-icon{background-position:-24px -80px;background-color:transparent!important}.crayon-toolbar .crayon-button.crayon-expand-button.crayon-pressed .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-expand-button:active .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-expand-button.crayon-pressed:active .crayon-button-icon{background-position:-24px -96px}.crayon-toolbar .crayon-button.crayon-wrap-button.crayon-pressed .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-wrap-button:active .crayon-button-icon,.crayon-toolbar .crayon-button.crayon-wrap-button.crayon-pressed:active .crayon-button-icon{background-position:-24px -112px}.crayon-syntax .crayon-toolbar .crayon-language{padding-right:8px!important}.crayon-syntax .crayon-title,.crayon-syntax .crayon-language{float:left}.crayon-main::-webkit-scrollbar,.crayon-plain::-webkit-scrollbar{height:6px;overflow:visible;width:6px;background:#EEE}.crayon-main::-webkit-scrollbar-thumb,.crayon-plain::-webkit-scrollbar-thumb{background-color:#CCC;background-clip:padding-box;border:1px solid #AAA;box-shadow:inset 0 0 2px #999;min-height:8px;padding:0;border-width:1px}.crayon-main::-webkit-scrollbar-button,.crayon-plain::-webkit-scrollbar-button{height:0;width:0;padding:0}.crayon-main::-webkit-scrollbar-track,.crayon-plain::-webkit-scrollbar-track{background-clip:padding-box;border:solid transparent;border-width:0 0 0 4px;border:1px solid #BBB;border-right:0;border-bottom:0}.crayon-main::-webkit-scrollbar-corner,.crayon-plain::-webkit-scrollbar-corner{background:#EEE}.crayon-main::-webkit-scrollbar-thumb:hover,.crayon-plain::-webkit-scrollbar-thumb:hover{background:#AAA;border:1px solid #777;box-shadow:inset 0 0 2px #777}.crayon-syntax .crayon-pre,.crayon-syntax pre{color:#000;white-space:pre;margin:0;padding:0;overflow:visible;background:none!important;border:none!important;tab-size:4}.crayon-syntax .crayon-line{padding:0 5px}.crayon-syntax.crayon-wrapped .crayon-line{white-space:pre-wrap!important;height:auto;word-break:break-all}.crayon-syntax-inline .crayon-pre,.crayon-syntax-inline pre{white-space:normal}.crayon-syntax-inline-nowrap .crayon-pre,.crayon-syntax-inline-nowrap pre{white-space:pre}.crayon-syntax{font-family:Monaco,'MonacoRegular','Courier New',monospace;font-weight:500}.crayon-syntax .crayon-toolbar *::selection,.crayon-syntax .crayon-nums *::selection{background:transparent}.crayon-table .crayon-nums-content{white-space:nowrap}.crayon-syntax .crayon-num,.crayon-syntax .crayon-pre .crayon-line,.crayon-syntax .crayon-toolbar *,.crayon-syntax .crayon-pre *{font-family:inherit;font-size:inherit!important;line-height:inherit!important;font-weight:inherit!important;height:inherit}.crayon-syntax .crayon-toolbar .crayon-button .crayon-button-icon{background-image:url('../images/toolbar/buttons.png');height:16px!important;width:100%;position:absolute;left:0;top:50%;margin-top:-8px}.crayon-syntax .crayon-toolbar .crayon-tools{position:absolute;right:0}.crayon-syntax.crayon-expanded{position:absolute!important;margin:0!important}.crayon-syntax.crayon-expanded .crayon-main{overflow:hidden!important}.crayon-placeholder{width:100%!important}.crayon-toolbar-visible .crayon-toolbar{position:relative!important;margin-top:0!important;display:block!important}.crayon-syntax.crayon-expanded .crayon-toolbar .crayon-tools{position:relative;right:auto;float:left!important}.crayon-syntax .crayon-plain-wrap{height:auto!important;padding:0!important;margin:0!important}.crayon-syntax .crayon-plain{width:100%;height:100%;position:absolute;opacity:0;padding:0 5px;margin:0;border:0;box-sizing:border-box;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-shadow:none;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;white-space:pre;word-wrap:normal;overflow:auto;resize:none;color:#000;background:#FFF}.crayon-wrapped .crayon-plain{white-space:pre-wrap}.bbp-body .crayon-syntax{clear:none!important}.crayon-minimized .crayon-toolbar{cursor:pointer}.crayon-minimized .crayon-plain-wrap,.crayon-minimized .crayon-main,.crayon-minimized .crayon-toolbar .crayon-tools *{display:none!important}.crayon-minimized .crayon-toolbar .crayon-tools .crayon-minimize{display:block!important}.crayon-minimized .crayon-toolbar{position:relative!important}.crayon-syntax.crayon-minimized .crayon-toolbar{border-bottom:none!important}.crayon-te *,#crayon-te-bar-content{font-family:"Lucida Grande",Arial,sans-serif!important;font-size:12px}.crayon-te input[type="text"],.crayon-te textarea{background:#f9f9f9;border:1px solid #CCC;box-shadow:inset 1px 1px 1px rgba(0,0,0,0.1);-moz-box-shadow:inset 1px 1px 1px rgba(0,0,0,0.1);-webkit-box-shadow:inset 1px 1px 1px rgba(0,0,0,0.1);padding:2px 4px;-webkit-border-radius:3px;border-radius:3px;border-width:1px;border-style:solid}.crayon-te #crayon-code{font-family:monospace!important}#crayon-te-content,#crayon-te-table{width:100%;height:auto!important}#crayon-range,#crayon-mark{width:100px}#crayon-te-table th,#crayon-te-table td{vertical-align:top;text-align:left}.rtl #crayon-te-table th,.rtl #crayon-te-table td{text-align:right}#crayon-te-table .crayon-tr-center td,#crayon-te-table .crayon-tr-center th{vertical-align:middle}#crayon-te-table .crayon-nowrap{white-space:nowrap}#crayon-te-bar{position:absolute;top:0;left:0;width:100%}#crayon-te-bar-content{border:1px solid #666;border-bottom:0;height:26px;line-height:25px;padding:0 8px;padding-right:0;background-color:#222;color:#cfcfcf}#crayon-te-bar-content a{line-height:25px;padding:5px 10px;color:#DDD;font-weight:bold;text-decoration:none!important}#crayon-te-bar-content a:hover{color:#FFF}.crayon-te-seperator{color:#666;margin:0;padding:0}#crayon-te-bar-block{height:34px;width:100%}#crayon-te-title{float:left}#crayon-te-controls{float:right}#crayon-url-th{vertical-align:top!important;padding-top:5px}.crayon-te-heading{font-size:14px;font-weight:bold}#crayon-te-settings-info{text-align:center}.crayon-te-section{font-weight:bold;padding:0 10px}#crayon-te-sub-section{margin-left:10px}#crayon-te-sub-section .crayon-te-section{font-weight:normal;padding:0}#crayon-code{height:200px;white-space:pre}#crayon-code,#crayon-url{width:555px!important}.crayon-disabled{background:#EEE!important}.qt_crayon_highlight{background-image:-ms-linear-gradient(bottom,#daf2ff,white)!important;background-image:-moz-linear-gradient(bottom,#daf2ff,white)!important;background-image:-o-linear-gradient(bottom,#daf2ff,white)!important;background-image:-webkit-linear-gradient(bottom,#daf2ff,white)!important;background-image:linear-gradient(bottom,#daf2ff,white)!important}.qt_crayon_highlight:hover{background:#ddebf2!important}.crayon-tag-editor-button-wrapper{display:inline-block}.mce_crayon_tinymce{padding:0!important;margin:2px 3px!important}.mce-i-crayon_tinymce,.mce_crayon_tinymce{background:url(../images/crayon_tinymce.png) 0 0!important}a.mce_crayon_tinymce{background-position:2px 0!important}.wp_themeSkin .mceButtonEnabled:hover span.mce_crayon_tinymce,.wp_themeSkin .mceButtonActive span.mce_crayon_tinymce{background-position:-20px 0}.wp_themeSkin span.mce_crayon_tinymce{background:none!important}#crayon-te-table{margin-top:26px;padding:10px;border-collapse:separate!important;border-spacing:2px!important}#crayon-te-table th{width:100px}#crayon-te-clear{margin-left:10px;color:#666;background-color:#f4f4f4;border:1px solid #CCC;border-radius:3px;margin-left:8px}#crayon-title{width:360px}#TB_window.crayon-te-ajax{overflow:auto!important}#TB_window.crayon-te-ajax,#TB_window.crayon-te-ajax #TB_ajaxContent,#TB_window.crayon-te-ajax #TB_title{width:680px!important}#TB_window.crayon-te-ajax #TB_ajaxContent{padding:0!important;margin:0!important;width:100%!important;height:auto!important;margin-top:28px!important}#TB_window.crayon-te-ajax #TB_title{position:fixed!important}#TB_window.crayon-te-ajax #TB_title .crayon-te-submit{margin-top:3px!important;float:right!important}#TB_window.crayon-te-ajax a{color:#2587e2;text-decoration:none}#TB_window.crayon-te-ajax a:hover{color:#499ce9}.crayon-te-quote{background:#DDD;padding:0 2px}#crayon-te-submit-wrapper{display:none}#crayon-te-clear{display:none;margin:0;margin-top:10px}.crayon-syntax-pre{background:red;white-space:pre;overflow:auto;display:block;word-wrap:break-word}.crayon-question{padding:1px 4px!important;text-decoration:none!important;color:#83b3cb!important;border-radius:10px!important;height:15px!important;width:15px!important}.crayon-question:hover{background:#83b3cb!important;color:white!important;height:15px!important;width:15px!important}.crayon-setting-changed,.crayon-setting-selected{background:#fffaad!important}.crayon-question:hover{color:white;background:#a6d6ef}#crayon-te-warning{display:none}.crayon-te-info{padding:5px!important;margin:2px 0!important}#crayon-te-submit{margin-bottom:5px}
/*
Name: Github
Description: Github like color.
Version: 1.0
Author: Dai Akatsuka
URL: http://firn.jp/
*/
.crayon-theme-github {
margin-bottom: 25px !important;
border: 1px solid #dedede !important;
background-color: #f8f8ff !important;
font-size: 100% !important;
line-height: 130% !important;
}

.crayon-theme-github .crayon-toolbar {
border-bottom: 1px solid #dedede !important;
background-color: #eee !important;
}

.crayon-theme-github .crayon-toolbar .crayon-language,
.crayon-theme-github .crayon-toolbar .crayon-title {
font-size: 80% !important;
color: #666 !important;
}

.crayon-theme-github .crayon-table .crayon-nums {
background-color: #eee !important;
}

.crayon-theme-github .crayon-table .crayon-nums-content {
padding-top: 5px !important;
padding-bottom: 3px !important;
}

.crayon-theme-github .crayon-table .crayon-num {
min-width: 1.2em !important;
border-right: 1px solid #dedede !important;
text-align: right !important;
color: #aaa !important;
}

.crayon-theme-github .crayon-pre {
padding-top: 5px !important;
padding-bottom: 3px !important;
}

.crayon-theme-github .crayon-marked-line {
background: #fffee2 !important;

}
.crayon-theme-github .crayon-marked-num {
color: #1561ac !important;
background: #b3d3f4 !important;
border-width: 1px !important;
border-color: #5999d9 !important;
}

.crayon-theme-github .crayon-pre .crayon-c {
color: #999 !important;
font-style: italic !important;
}
.crayon-theme-github .crayon-pre .crayon-s {
color: #d14 !important;
}
.crayon-theme-github .crayon-pre .crayon-p {
color: #b85c00 !important;
}
.crayon-theme-github .crayon-pre .crayon-ta {
color: #FF0000 !important;
}
.crayon-theme-github .crayon-pre .crayon-k {
color: teal !important;
}
.crayon-theme-github .crayon-pre .crayon-st {
color: #000 !important;
font-weight: bold !important;
}
.crayon-theme-github .crayon-pre .crayon-r {
color: #000 !important;
font-weight: bold !important;
}
.crayon-theme-github .crayon-pre .crayon-t {
color: #800080 !important;
font-weight: bold !important;
}
.crayon-theme-github .crayon-pre .crayon-m {
color: #800080 !important;
}
.crayon-theme-github .crayon-pre .crayon-i {
color: #000 !important;
}
.crayon-theme-github .crayon-pre .crayon-e {
color: teal !important;
}
.crayon-theme-github .crayon-pre .crayon-v {
color: #002D7A !important;
}
.crayon-theme-github .crayon-pre .crayon-cn {
color: #099 !important;
}
.crayon-theme-github .crayon-pre .crayon-o {
color: #006fe0 !important;
}
.crayon-theme-github .crayon-pre .crayon-sy {
color: #333 !important;
}
.crayon-theme-github .crayon-pre .crayon-n {
color: #666 !important;
font-style: italic;
}
.crayon-theme-github .crayon-pre .crayon-f {
color: #999 !important;
}
.crayon-theme-github .crayon-pre .crayon-h {
color: #006fe0 !important;
}
.crayon-syntax .crayon-pre, .crayon-syntax pre{
white-space: initial;
}
-->

在上一篇《webpack从入门到上线》介绍了wepack的配置和相关的概念,这一篇介绍怎样写一个webpack loader. 通过写一个js的html模板为例子。

上篇文章已提及,loader加载器就是对各种非正常资源的解析,转化成浏览器可以识别的js/css文件等,甚至可以说loader就是一个小型的编译器。例如sass loader:将sass格式编译成css,在安装sass的过程中你会发现,其实sass用的是C++,sass本身是面向对象的。但是本文不会介绍怎样写一个编译器(我也不知道),只是简单介绍下怎样写一个loader,抛砖引玉。

有什么样的场景需要写一个loader呢?

有一个就是在js里面写html的模板,会有点不太方便,一个是语法不是高亮的,第二个是需要双引号把每一行包起来,或者用数组push之类的,所以如果能够require一个html文件,然后转化成相应的js字符串,那就比较方便了。(如果你用react开发的,用jsx是比较自然的事情)。我在网上找了下,没有找到能够比较符合上面两点需求的loader,所以就自己尝试写一个吧。

js模板通常有三种场景:

  1. 纯html,一个文件就是一个模板,直接转成字符串即可;
  2. 需要有变量,一个文件有几个模板,每个模板对应一个变量
  3. 再复杂一点,模板是require了其它的工厂模块生成的字符串

现在开始来写loader

loader的基本框架

首先在工程的node_modules下面新建一个文件夹html-template-loader,在里面创建一个index.js做为这个loader的js文件。这样就可以在webpack.config.js里面添加一个loader了:

添加loader配置
 
 
 
 
 

JavaScript

 
{
test:/\.tpl\.html$/,
loader: 'html-template-loader'
}
1
2
3
4
{
     test:/\.tpl\.html$/,
     loader:'html-template-loader'
}

以.tpl.html作为后缀名,也就是说在逻辑代码里面引入一个.tpl.html结尾的文件,就归这个loader处理

require tpl html
 
 
 
 
 

JavaScript

 
var tpl = require("./dialog.tpl.html");
1
vartpl=require("./dialog.tpl.html");

重点就在于,这个loader怎么写呢?

loader也需要写成一个模块,一个基本的loader的写法:

html-template-loader/index.js
 
 
 
 
 

JavaScript

 
/**
* source为原文件的字符串格式
*/
module.exports = function(source, map){
//对source进行解析
var exports = process(source);
return "module.exports = " + exports;
}
1
2
3
4
5
6
7
8
/**
* source为原文件的字符串格式
*/
module.exports=function(source,map){
    //对source进行解析
    varexports=process(source);
    return"module.exports = "+exports;
}

是的,你没看错,一个loader就是这么简单。关键在于理解loader的机制——其实loader最后会创建一个模块,当我们require一个需要让loader的解析的文件之后,通过上面第7行的return——return里面的内容就是自动创建的模块的内容,跟平时自已写的模块的区别就在于这个要自己拼成一个js语法合法的字符串。

如上面生成的exports为:

 
 
 
 
 

JavaScript

 
var exports = "var tpl = {email: '<div>1</div>', hello: '<p>2</p>'}";
return "var tpl = " + tpl + ";\n module.exports = tpl;";
1
2
var exports="var tpl = {email: '<div>1</div>', hello: '<p>2</p>'}";
return"var tpl = "+tpl+";\n module.exports = tpl;";

那么webpack自动创建这样一个模块:

webpack自动创建的模块
 
 
 
 
 

JavaScript

 
/***/ 109:
/***/ function(module, exports) {
var tpl = {email: '<div>1</div>', hello: '<p>2</p>'};
module.exports = tpl;
/***/ }
1
2
3
4
5
/***/109:
/***/function(module,exports){
var tpl={email:'<div>1</div>',hello:'<p>2</p>'};
module.exports=tpl;
/***/}

在我这个工程,这个模块的id为109,然后require的返回结果就是这个tpl的object。

另外一方面,初始化的时候可以拿到source,这个source就是webpack以utf-8读的整个文件的内容,以字符串的形式作为一个参数传进来。

所以现在目标就很明确了,我们需要处理这个source的字符串,然后再返回一个可以eval的字符串。需要定义loader的输入和输出的格式,这是作为loader开发者的权利,同时中间的处理过程对用户屏蔽。

loader的具体实现

把source这整一个文件,拆成一行一行处理:

把source拆成一行一行
 
 
 
 
 

JavaScript

 
var tpl = "";
source.split(/\r?\n/).forEach(function(line){
line = line.trim();
if(!line.length){
return;
}
//对line进行处理...
tpl += process(line);
}
return "module.exports = ...";
1
2
3
4
5
6
7
8
9
10
    var tpl="";
    source.split(/\r?\n/).forEach(function(line){
        line=line.trim();
        if(!line.length){
            return;
        }
        //对line进行处理...
        tpl+=process(line);
    }
    return "module.exports = ...";

难点就在于第8行的process函数怎么写,怎样拼成一个合法的tpl字符串。

如果是上面提到的第一种场景,则很简单,直接加就好了;

如果是第二种场景需要用变量区分的,那么需要自定义一个语法,由于我们用的是html,因为有高亮的功能。所以可考虑用html的注释的标签:

用<!--%变量名%-->的格式定义变量名
 
 
<!--%email%-->
<div>
<p>hello, world</p>
</div>

<!--%alert%-->
<p>hi</p>
<span>man</span>

1
2
3
4
5
6
7
8
<!--%email%-->
<div>
    <p>hello,world</p>
</div>
 
<!--%alert%-->
<p>hi</p>
<span>man</span>

我们的语法就是<!–%变量名%–>,像上面定义了两个变量email和alert,接下来在process函数里面就可以进行识别、拼一个object的字符串。

如果是第三种场景需要引入第三方模块的,则需要在最后一行return的时候加上require这个函数的代码,即:

 
 
 
 
 

JavaScript

 
return "var widget = require(../widgetFactory");\n" +
"module.exports = widget.make()";
1
2
return "var widget = require(../widgetFactory");\n" +
       "module.exports=widget.make()";

同样地,需要定义一个语法,require的代码用script的方式:

需要依赖的代码格式
 
 
 
 
 

JavaScript

 
<script generate>var SELECT = require("js/select");</script>
1
<script generate>varSELECT=require("js/select");</script>

以一个generate的属性标志,这块是需要依赖的代码,也就是说要把它拼在前面。

正常的调用就用一个script,不带属性:

 
 
<!--%email%-->
<div>
<p>hello, world</p>
</div>
<script>
widget.make()
</script>
1
2
3
4
5
6
7
<!--%email%-->
<div>
    <p>hello,world</p>
</div>
<script>
    widget.make()
</script>

最后把它转成一个object.

经过合适的处理之后,最后生成的模块是这样的:

 
 
 
 
 

JavaScript

 
/***/ 110:
/***/ function(module, exports, __webpack_require__) {

var widget = __webpack_require__(111);
var tpl = {
email:'<div>'+
'<p>\'tpl\'</p>'+
'<p>hello, world</p>'+
'</div>'+
widget.make() +
'<p>good</p>'+
'',
alert:'<p>hi</p>'+
'<span>man</span>'+
''
}
module.exports = tpl

/***/ },

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/***/110:
/***/function(module,exports,__webpack_require__){
 
var widget=__webpack_require__(111);
var tpl={
    email:'<div>'+
        '<p>\'tpl\'</p>'+
        '<p>hello, world</p>'+
        '</div>'+
        widget.make()+
        '<p>good</p>'+
        '',
    alert:'<p>hi</p>'+
        '<span>man</span>'+
        ''
         }
module.exports=tpl
 
/***/},

具体处理代码略,详见github

loader的高阶话题

1.  loader支持链式,上一个loader的处理结果可以给下一个loader,像sass的loader是这样写的:

sass loader
 
 
 
 
 

JavaScript

 
require("!style!css!sass!./file.scss");
1
require("!style!css!sass!./file.scss");

或者是写在配置文件里面:

webpack.config.js
 
 
 
 
 

JavaScript

 
{
test: /\.scss$/,
loaders: ["style", "css", "sass"]
}
1
2
3
4
{
        test:/\.scss$/,
        loaders:["style","css","sass"]
}

sass处理完之后给css的loader,css的loader处理完后给style的loader,loader间的数据传递通过loader的定义的callback函数,例如上面的loader可以在return之前再加一行:

 
 
 
 
 

JavaScript

 
this.callback(null, source, map);
1
this.callback(null,source,map);

这样就可以传给下一个loader,最后一个loader一定要有return的内容;

2. loader可以缓存,可以加快速度

 
 
 
 
 

JavaScript

 
this.cacheable();
1
this.cacheable();

3. 其它:loader支持异步,loader的加载可以用异步的方式,loader可以传参数等等,详见官方文档:HOW TO WRITE A LOADER,官方文档比较绕,不是从0开始,也没有给一个完整的demo,具体可以再另外查查。

写loader关键在于怎么编译这个文件,上面是用的把它拆成一行一行的方式,然后做了个简单的处理,一般编译是要用到语法树。

我的个人博客:http://yincheng.site/webpack-loader

上一篇:易百教程人工智能python修正-人工智能NLTK性别发现器


下一篇:用js实现web端录屏