如何优雅的管理你的模板

作为一个『库』,Regular肯定不会涉及到模板的加载,你只需保证传给template的值是个字符串或是预解析AST即可。

也就是说,所有可以加载纯文本的方式都可以用来加载Regular的模板。

Regularjs本身已经提供了尽可能多的方式来方便你在不同的场景下都能管理你的模板。

方案清单:

1. 简单管理方案

在regularjs提供的所有例子中,为了方便起见,基本都使用了以下两种方式来管理你的模板:

  1. 直接在js中声明模板字符串 {#raw}

    var Component = Regular.extend({
     tempalte: "<h2>{title}</h2>"
    })
    

    当模板非常简单时,这样做确实非常方便,

    当模板结构稍微复杂点时,一般也可以使用页面的模板容器节点

  2. 引用页面节点里的内容 {#script}

    var Component = Regular.extend({
     tempalte: document.getElementById("app")
    })
    

    #app节点(一般是修改了type的script标签,即不会render其中内容的容器):

    
    <script id='app' type='text/rgl'>
    <nav class="navbar navbar-inverse navbar-fixed-top">
     <div class="container-fluid">
       <div class="navbar-header">
       //...
        </div>
     </div>
    </nav>
    <div class="container-fluid">
     <div class="row">
       <div class="col-sm-3 col-md-2 sidebar">
       //...
       </div>
     </div>
    </div>
    </script>
    

    这种方式相较于方式1其实有利有弊。例在于它解决了在js中拼接字符串模板的肮脏行为,弊则在于模板本身变成了一个「全局」的东西,组件这个统一的整体也被打碎了,从项目规模庞大后,维护这些散落在页面中的容器节点也会成为随时引爆的导火索

  3. 使用多行字符串 {#mult}

    大家都知道ES6和coffee都可以直接使用多行字符串,免去了字符串拼接的开销

    ES6

     var template = `
       <h2></h2>
       <div class="container">{content}</div>
       `
    

    鉴于现在ES6的打包工具普及度非常高,非常推荐开发者直接使用ES6进行开发

    对于ES5或ES3,其实也有一种小技巧来帮助我们书写多行字符串——sindresorhus/multiline

    你可以这样来声明一个多行字符串

     var str = multiline(function(){/*
       <div>
           <h1>❤ unicorns</h1>
       </div>
     */});
    

    但是!!,不建议大家在实际项目中使用这种提取注释的方式来书写,因为注释是可能会被压缩工具移除的。

上述简单管理方案都有一些小问题,即无法对模板做预解析(除非在超大项目,否则parser的开销相较于实例化是较小的)

『 是否有解决上述问题的方案呢?』

答案是肯定的,即将模板作为一种资源集成到模块系统中

2. Regular模板与模块系统结合

下面会介绍Regular与市面上常见模块系统的结合方案,它们分别是

本文只是介绍如何将Regular集成进这些系统中,而不是普及这些模块系统的使用方法,请大家自行学习。

2.1. webpack

webpack是近年来兴起的当红炸子鸡,支持多种模块规范共存,支持增量编译,社区强大。作者非常推荐直接使用ES6 + webpack的方式来开发你的项目。

webpack对于非内建模块支持,是通过自定义loader的方式。

✨所有DEMO可以在regularjs/example 找到范例

配置

var path = require('path');

module.exports = {
    entry: "./src/index.js",
    output: {
        path: __dirname ,
        filename: "bundle.js"
    },
    module: {
        loaders: [
            { test: /\.html$/, loader: "raw" },
            { test: /\.rgl$/, loader: 'rgl' },
            // In fact, we can use template string for keep regularjs template.
            { test: /\.js$/, loader: 'babel?cacheDirectory'}
        ]

    }
};

使用



import tpl from './login.html';
import Regular from 'regularjs';

const LoginBabel = Regular.extend({

  name: 'login-babel',

  template: tpl
})

export { LoginBabel }

上述代码列举了两种方式

raw-loader 加载纯文本模板

小贴士: 事实上如果你使用了babel-loader 就可以直接使用多行字符串来管理你的模板,无需配置raw-loader

rgl-loader 加载并与解析模板

2.2. browserify

browserify在前些年几乎是唯一的『bundle-all-in-one』同步模块打包方案(即使这种方案算是不温不火),它依托了NPM以及Commonjs(你仍然可以通过书写transform来支持其他模块标准)来构建自己的生态圈。

同样的,你可以通过两种transform来实现对Regular模板的加载。

var html2string = require('browserify-html2string');

browserify-html2string

配置

var browserify = require('browserify');
var html2string = require('browserify-html2string');

browserify(['./src/index.js'], {})
  .transform(html2string) // 使用html2string transform
  .pipe(source('bundle.js'))
  .pipe(gulp.dest('public/js'))

模块写法

var tpl = require("foo.html");

var Component = Regular.extend({
  template: tpl
})

regularify

Regular提供一个browserify的transform:regularify,它可以用来处理两种格式rglcrgl(后缀都是可配置的).

配置

browserify(['./src/index.js'], {})
  .transform(html2string) // you can have multiply transform in one building
  .transform(regularify({ END: '}', BEGIN: '{' }))
  .pipe(source('bundle.js'))
  .pipe(gulp.dest('public/js'))

模块写法

var tpl = require("foo.rgl");

var Component = Regular.extend({
  template: tpl,
  // ....
})

点击regularify主页了解更多

2.3. NEJ

对于网易的同事(特别是杭研),由于是近水楼台的原因,regularjs已_直接集成进了NEJ框架及其配套打包工具之中。与上述任意工具一样,你可以通过两种方式

text!:纯文本加载

define(['text!path/to/foo.html', 'path/to/regularjs'], function(tpl, _p){

    var Foo = Regular.extend({
      name: 'foo',
      template: tpl
    })

    return Foo;

});

regular! 预解析

define(['regular!path/to/foo.html', 'path/to/regularjs'], function(tpl, _p){

    var Foo = Regular.extend({
      name: 'foo',
      template: tpl
    })

    return Foo;

});

注意由于Regular本身打包的是UMD格式,对于非Commonjs和AMD格式的统一是以全局变量的方式,所以Regular无法在模块系统中完成注入,直接从全局获得即可(这里的_p属于占位符)

网易的同事在使用中有疑问可以私泡我(杭州研究院|前端技术部|郑海波)

2.4. Requirejs

如果需要基于老家伙requirejs来管理模板,开发者可以有两种选择。

requirejs-text直接加载模板字符串

安装:

bower install requirejs-text

配置


require.config({
  paths : {
      "text": '../bower_components/requirejs-regular/rgl',
      "regularjs": '../bower_components/regularjs/dist/regular',
      "restate": '../restate',
      "stateman": '../bower_components/stateman/stateman'
  }
});

使用


require(['text!foo.html', 'regularjs'], function(tpl, Regular){

    var Foo = Regular.extend({
      name: 'foo',
      template: tpl
    })

    return Foo;

});

requirejs-regular加载和预处理模板

requirejs-text插件的text!不同,这个插件配置的前缀是rgl!.

配置

require.config({
   ...
    paths : {
        "rgl": 'path/to/requirejs-regular/rgl',
        //...
    }
    ...
});

使用


require(['rgl!foo.html', 'regularjs'], function( tpl, Regular){

    var Foo = Regular.extend({
      name: 'foo',
      template: tpl
    })

    return Foo;

});

点击https://github.com/regularjs/requirejs-regular 查看更多详细说明

3. 你不用上述模块方案?

如果以上几种模块体系都是不是你的选择,也许你需要自己实现一个处理插件了,regularjs本身以打包了umd模块,可以同时在node和browser环境被使用, 使用 Regular.parse来处理你的模板字符串吧,

const Regular = require("regularjs");

const AST = Regular.parse("{title}", {
  BEGIN: '{',
  END: '}'
})

具体可以参考基于webpack的rgl-loader的实现方案,寥寥几行代码。

当然如果你不需要预解析,直接传入字符串给Regular即可。

4. 总结

上面的解决方案真是让人眼花缭乱,但共性也很明显:

  • 所有可以加载纯文本的方式都可以用来加载Regular的模板。比如requirejs-text等插件,而加载文本几乎是模块化工具的标配了
  • 如果你需要预先解析,可以使用Regular.parse封装出对应的工具。已经预先提供了requirejs, webpack, browserifyNEJ的相关工具。
  • 如果需要类似Vue的单文件组件的写法,请参考单文件组件;

results matching ""

    No results matching ""