乐趣区

vueloader中引入模板预处理器

vue-loader 是一个 webpack 的 loader,可以将指定格式编写的 Vue 组件转换为 JavaScript
模块

同时,vue-loader 支持使用非默认语言,通过设置语言块的 lang 属性,就可以使用指定的预处理器,比如最常见的 sass 语法:

<style lang="sass">
    ...
</style>

这里重点讨论使用不同的 js 模板引擎作为预处理器,
下面示例使用了 pug 模板引擎

<template lang="pug">
 div
   h1 Hello world!
</template>

1. 支持哪些模板引擎

v14 或更低版本使用 consolidate 来编译 <template lang=”xxx”>, 所以支持的模板引擎,从 consolidate 的支持列表中可以找到,包括了大部分引擎,
在 vue-loader/preprocessor.js 文件里面,

// loader for pre-processing templates with e.g. pug
const cons = require('consolidate')
const loaderUtils = require('loader-utils')
const {loadOptions} = require('../utils/options-cache')

module.exports = function (content) {const callback = this.async()
  const opt = loaderUtils.getOptions(this) || {}

  if (!cons[opt.engine]) {
    return callback(
      new Error(
        "Template engine'" +
          opt.engine +
          "'" +"isn't available in Consolidate.js"
      )
    )
  }

  // allow passing options to the template preprocessor via `template` option
  const vueOptions = loadOptions(opt.optionsId)
  if (vueOptions.template) {Object.assign(opt, vueOptions.template)
  }

  // for relative includes
  opt.filename = this.resourcePath

  cons[opt.engine].render(content, opt, (err, html) => {if (err) {return callback(err)
    }
    callback(null, html)
  })
}

可以看到,使用 consolidate 进行预处理。

v15 及以上版本,允许对 vue 组件中的每个部分使用其他的 webpack loader,可以正常使用各种模板引擎。

使用 @vue/component-compiler-utils 工具编译模板,实际在 component-compiler-utils 中编译 template 时,也把 consolidate 作为预处理器,使用 consolidate.render 编译成字符串。

2. 引入 pug

需安装 pug-plain-loader,利用它返回一个编译好的 HTML 字符串,
在最新的 vue-cli@3.x 配置中,默认已配置好 pug 的相关 loader,所以安装完可以直接在 <template/> 中使用,

/* config.module.rule('pug') */
      {
        test: /\.pug$/,
        oneOf: [/* config.module.rule('pug').oneOf('pug-vue') */
          {
            resourceQuery: /vue/,
            use: [/* config.module.rule('pug').oneOf('pug-vue').use('pug-plain-loader') */
              {loader: 'pug-plain-loader'}
            ]
          },
          /* config.module.rule('pug').oneOf('pug-template') */
          {
            use: [/* config.module.rule('pug').oneOf('pug-template').use('raw') */
              {loader: 'raw-loader'},
              /* config.module.rule('pug').oneOf('pug-template').use('pug-plain') */
              {loader: 'pug-plain-loader'}
            ]
          }
        ]
      },

3. 引入 dotjs 或其他模板引擎,

需在 vue.confg.js 里面手动配置 loader, 配置规则跟引入 pug 类似,修改相关 loader 即可

还有一点比较特殊,该模板引擎对应的 loader,必须返回字符串
比如我们使用 dotjs-loader,来解析 dotjs 模板,就会报错,然后查看 dotjs-loader, 发现

return 'export default' + doT.template(source);

最后返回导出结果, doT.template(source)执行成功后,返回一个匿名函数,

所以想要返回最终的字符串,只有传入数据,执行函数 doT.template(source)(data)

直接使用 dotjs-loader 无法达到上面的要求,只有修改 loader 中的返回格式,具体可以参考 pug-plain-loader, 逻辑比较简单,传入模板引擎相关参数,options 对应 webpack 配置中的 options 参数,最后返回编译后的字符串。

const pug = require('pug')
const loaderUtils = require('loader-utils')

module.exports = function (source) {
  const options = Object.assign({
    filename: this.resourcePath,
    doctype: 'html',
    compileDebug: this.debug || false
  }, loaderUtils.getOptions(this))

  const template = pug.compile(source, options)
  template.dependencies.forEach(this.addDependency)
  return template(options.data || {})
}

这里可以发现问题,上面代码中 options.data 只是在 webpack 配置时传入的,并不是正式的下发数据,使用预处理模板引擎,为了返回字符串,编译函数执行在 loader 中进行,没有办法传入数据 data,参与编译

而且模板引擎的相关语法,不能与 vue 的模板语法冲突,这样会导致 js 模板引擎解析后,再进行 vue 模板解析时报错

如果只是纯静态页面,可以直接把需要经过模板引擎编译的内容部分抽离出去,使用 require 引入时,webpack会自动对应 loader,解析完成后,只需在当前组件中传入data,通过v-html 把生成的字符串当成 HTML 标签解析后输出。

退出移动版