Vue學習(十三)模版引擎算是預處理器嗎?

前言

今天在看vue-loader預處理器配置相關的內容,突然看到了Pug,然後有了一個疑問:模版引擎原來是預處理器嗎?

答案是:YES

說明

這裡重點討論使用不同的js模板引擎作為預處理器,

下面示例使用了pug模板引擎

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

1. 支持哪些模板引擎

v14 或更低版本使用 consolidate 來編譯 <template lang=”xxx”>, 所以支持的模板引擎,從consolidate的支持列表中可以找到,包括了大部分引擎,

vue-loader/preprocessor.js 文件裏面

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 字符串,

在最新的[email protected] 配置中,默認已配置好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標籤解析後輸出。

參考

vue-loader中引入模板預處理器的實現