导航


HTML

CSS

JavaScript

浏览器 & 网络

版本管理

框架

构建工具

TypeScript

性能优化

软实力

算法

UI、组件库

Node

业务技能

针对性攻坚

AI


步骤

  1. 根目录新建 loaders 文件夹,创建 tpl-loader.js函数(loader 就是个函数
  2. webpack 中配置 resolveLoader.module 让其除了从 node_modules 中找 loader 外,还能从我们创建的 loaders文件夹下找对应 loader
  3. 还得在 webpack 中配置 rules ,当遇见 .tpl 文件时,用我们的 tpl-loader 解析
  4. tpl-loader 其实就是个函数,会有个 source 参数,内容就是 tpl 文件中的源码
  5. 我们要做的就是在函数中 return 一个字符串,不过字符串中的内容是导出一个函数(也就是 MyTitleView({}),最终交给 babel-loader 程序会给我们把字符串转为 JS 程序

tpl-loader

function tplLoader(source) {
  // webpack找到 .tpl 文件后,会找我们这个loader
  // 并把文件中的资源放到 source 变量 中来

  return `
    export default (component) => {
      component.template = \\`${source}\\`;
      return component;
    }
  `
}

使用

import './MyTitle.scss';
import MyTitleView from './MyTitle.tpl';

export default MyTitleView({
  data() {
    return {
      title: 'This is my TITLE',
      subTitle: 'This is my SUB_TITLE'
    }
  },
  methods: {
    handleTitleClick(e) {
      console.log(e.target.innerText);
    }
  }
}, {
  template: true, // 开发时是否打印 template、data、methods
  data: true,
  methods: true
});

components/MyTitle

MyTitle.tpl

<div>
  <h1 @click="handleTitleClick($event)">{{ title }}</h1>
  <h2 @click="handleTitleClick($event)">{{ subTitle }}</h2>
</div>

MyTitle.scss

h1 {
  color: red;
}

h2 {
  color: purple;
}

index.js

import './MyTitle.scss';
import MyTitleView from './MyTitle.tpl';

export default MyTitleView({
  data() {
    return {
      title: 'This is my TITLE',
      subTitle: 'This is my SUB_TITLE'
    }
  },
  methods: {
    handleTitleClick(e) {
      console.log(e.target.innerText);
    }
  }
});

src/App.vue

<template>
  <div>
    <my-title></my-title>
  </div>
</template>

<script>
import MyTitle from './components/MyTitle/index';
export default {
  name: 'App',
  components: {
    MyTitle
  }
}
</script>

<style>

</style>

修改 webpack.config.js

让 webpack 使用 tpl-loader 去解析我们的文件:

...
module.exports = {
  ...
  resolveLoader: {
    modules: [
      'node_modules',
      resolve(__dirname, './loaders')
    ]
  },
  // 合并loader,先在 node_modules 中找
  // 找不到 就在 loaders 文件夹下找
  module: {
    rules: [
      {
        test: /\\.tpl$/,
        loader: 'tpl-loader'
      }, // 处理 .tpl 文件
      ...
    ]
  },
  ...
}

loaders 文件夹

接下来我们在根目录创建 loaders 文件夹,并创建 tpl-loader :

tpl-loader/index.js

function tplLoader(source) {
  // webpack找到 .tpl 文件后,会找我们这个loader
  // 并把文件中的资源放到 source 变量 中来

  return `
    export default (component) => {
      component.template = \\`${source}\\`;
      return component;
    }
  `
}

// component.template = \\`${source}\\`; 这一段为什么要转义,因为我们实际在写模板时就用了 `` 这个东西:
// template: `
//  为了写多行,我们template用了 ``
// `

// loader 在 node 环境运行,所以用 COMMONJS 语法
module.exports = tplLoader;

Untitled

我们再 npm run dev 就能正常显示了:

Untitled

优化 tpl-loader

判断传递的组件是不是个对象

function tplLoader(source) {
  // webpack找到 .tpl 文件后,会找我们这个loader
  // 并把文件中的资源放到 source 变量 中来

  return `
    export default (component) => {

      if (Object.prototype.toString.call(component) !== '[object Object]') {
        throw new Error('component must be the type of Object.');
      }

      component.template = \\`${source}\\`;

      return component;
    }
  `
}

// component.template = \\`${source}\\`; 这一段为什么要转义,因为我们实际在写模板时就用了 `` 这个东西:
// template: `
//  为了写多行,我们template用了 ``
// `

// loader 在 node 环境运行,所以用 COMMONJS 语法
module.exports = tplLoader;

此时当传递空就会报错:

import './MyTitle.scss';
import  MyTitleView from './MyTitle.tpl';

export default MyTitleView();

Untitled

优化可调试性

tpl-loader

function tplLoader(source) {
  // webpack找到 .tpl 文件后,会找我们这个loader
  // 并把文件中的资源放到 source 变量 中来

  return `
    export default (component, { template, data, methods }) => {

      if (Object.prototype.toString.call(component) !== '[object Object]') {
        throw new Error('component must be the type of Object.');
      }

      if (template) {
        console.log(\\`${source}\\`);
      }

      if (component.data && data) {
        console.log(component.data());
      }

      if (component.methods && methods) {
        console.log(component.methods);
      }

      component.template = \\`${source}\\`;

      return component;
    }
  `
}

// component.template = \\`${source}\\`; 这一段为什么要转义,因为我们实际在写模板时就用了 `` 这个东西:
// template: `
//  为了写多行,我们template用了 ``
// `

// loader 在 node 环境运行,所以用 COMMONJS 语法
module.exports = tplLoader;

MyTitle/index.js

import './MyTitle.scss';
import  MyTitleView from './MyTitle.tpl';

export default MyTitleView({
  data() {
    return {
      title: 'This is my TITLE',
      subTitle: 'This is my SUB_TITLE'
    }
  },
  methods: {
    handleTitleClick(e) {
      console.log(e.target.innerText);
    }
  }
}, {
  template: true, // 开发时是否打印 template、data、methods
  data: true,
  methods: true
});

Untitled

给 tpl-loader 添加 webpack 可配置性

webpack.config.js

...
module.exports = {
  ...
  resolveLoader: {
    modules: [
      'node_modules',
      resolve(__dirname, './loaders')
    ]
  },
  // 合并loader,先在 node_modules 中找
  // 找不到 就在 loaders 文件夹下找
  module: {
    rules: [
      {
        test: /\\.tpl$/,
        loader: 'tpl-loader',
        options: {
        	consoleLog: true, // 是否展示调试log信息
        }
      }, // 处理 .tpl 文件
      ...
    ]
  },
  ...
}

tpl-loader

const { getOptions } = require('loader-utils');
function tplLoader(source) {
  // webpack找到 .tpl 文件后,会找我们这个loader
  // 并把文件中的资源放到 source 变量 中来

  // 拿到 webpack 中的配置设置
  const { consoleLog } = getOptions(this);

  if (consoleLog) {
    return `
      export default (component, { template, data, methods }) => {

        if (Object.prototype.toString.call(component) !== '[object Object]') {
          throw new Error('component must be the type of Object.');
        }

        if (template) {
          console.log(\\`${source}\\`);
        }

        if (component.data && data) {
          console.log(component.data());
        }

        if (component.methods && methods) {
          console.log(component.methods);
        }

        component.template = \\`${source}\\`;

        return component;
      }
    `
  } else {
    return `
      export default (component, { template, data, methods }) => {

        if (Object.prototype.toString.call(component) !== '[object Object]') {
          throw new Error('component must be the type of Object.');
        }

        component.template = \\`${source}\\`;

        return component;
      }
    `
  }
}

// component.template = \\`${source}\\`; 这一段为什么要转义,因为我们实际在写模板时就用了 `` 这个东西:
// template: `
//  为了写多行,我们template用了 ``
// `

// loader 在 node 环境运行,所以用 COMMONJS 语法
module.exports = tplLoader;