vue自定义loader运行报错?

新手上路,请多包涵

写了个loader想对vue单文件的template预先做些处理,不过运行起来会有报错

vue.config.js

const { defineConfig } = require('@vue/cli-service')
const path = require('path')
// const { VueLoaderPlugin } = require('vue-loader')

module.exports = defineConfig({
  transpileDependencies: true,
  chainWebpack: config => {
   
    config.module
      .rule('vue')
      // .test(/\.vue$/)
      .use('vue-loader')
      .loader('vue-loader')
      .end()
      .use('my-loader')
      .loader(path.resolve(__dirname, './my-loader.js'))
      // .before('vue-loader')
      .end()

  }
})

my-loader.js

module.exports = function (content) {
  const templateMatch = content.match(/<template>([\s\S]*?)<\/template>/);
  let restContent = content
  if (templateMatch) {
    const templateContent = templateMatch[1];
    restContent = content.replace(/<template>[\s\S]*?<\/template>/, '');
    
    let returnContent = templateContent.replace(/<\/el-/g, '\n</el-')
    returnContent = returnContent.replace(/<el-/g, '\n<el-')
    returnContent = returnContent.replace(/<input/g, '\n<input')

    var regInput = /<(input)([^<>]*)>/g // tmp
    let contentArr = returnContent.split('\n')
    let resultArr = contentArr.map((item, index) => {
      if (regInput.test(item)) {
        item = item.replace(/<input/g, '<input name="input_name"')
      }
      return item
    })
    const result = resultArr.join('\n')
    console.log('result', result)
    // return result
    return `module.exports = ${JSON.stringify(result + restContent)};`
    // this.callback(null, result)
    // return
  } else {
    // return content;
    return `module.exports = ${JSON.stringify(content)};`
    // this.callback(null, content)
    // return
  }
}

自定义的loader只是在vue-loader前处理一下template,之后的处理应该会交给vue-loader继续执行。不过实际运行起来,vue单文件的js跟css都会有错误。

ERROR in ./src/App.vue?vue&type=style&index=0&id=7ba5bd90&lang=css

百思不得其解,不知道各位能不能不能帮忙指点迷津。项目代码已上传到github

看过相关文章,暂时不清楚具体原因

阅读 1.1k
2 个回答

代码微调了下,不报错了
1、自定义的loader要在vue-loader之前
2、loader的链式处理依赖了一个执行顺序,要确保经过所有的loader之后生成一个带module.exports 的js字符串代码文件;自定义的loader只是中间过程,直接返回内容交给vue-loader处理,不需要module.exports

const { defineConfig } = require('@vue/cli-service')
const path = require('path')
// const { VueLoaderPlugin } = require('vue-loader')

module.exports = defineConfig({
  transpileDependencies: true,
  chainWebpack: config => {
   
    config.module
      .rule('vue')
      // .test(/\.vue$/)
      .use('vue-loader')
      .loader('vue-loader')
      .end()
      .use('my-loader')
      .loader(path.resolve(__dirname, './my-loader.js'))
      .before('vue-loader')
      .end()

  }
})
const fs = require('fs');
const path = require('path');

module.exports = function (content) {
  var output = ''
  var moudle = this.resource.split('\\src\\')[1]
  console.log(moudle)
  var dir = moudle.replace(/[?&=]/g, '_')

  var type = 'js'
  if (moudle.includes('type=style')) {
    type = 'css'
  }
  if (moudle.includes('type=template')) {
    type = 'html'
  }

  if (type == 'html') {
    let returnContent = content.replace(/<\/el-/g, '\n</el-')
    returnContent = returnContent.replace(/<el-/g, '\n<el-')
    returnContent = returnContent.replace(/<input/g, '\n<input')

    var regInput = /<(input)([^<>]*)>/g // tmp
    let contentArr = returnContent.split('\n')
    let resultArr = contentArr.map((item, index) => {
      if (regInput.test(item)) {
        item = item.replace(/<input/g, '<input name="input_name"')
      }
      return item
    })
    const result = resultArr.join('\n')
    output = result
    
    createDirAndWriteFile(`.output/${dir}已处理`, `old.${type}`, content);
    createDirAndWriteFile(`.output/${dir}已处理`, `new.${type}`, output);
  } else {
    output = content;

    createDirAndWriteFile(`.output/${dir}未处理`, `old.${type}`, content);
    createDirAndWriteFile(`.output/${dir}未处理`, `new.${type}`, output);
    // for (const key in this) {
    //   if (Object.hasOwnProperty.call(this, key)) {
    //     const element = this[key];
    //     try {
    //       if (typeof element == 'object') {
    //         createDirAndWriteFile(`.output/${dir}未处理`, `${key}.json`, JSON.stringify(element, null, 2));
    //       } else {
    //         createDirAndWriteFile(`.output/${dir}未处理`, `${key}.txt`, element);
    //       }
          
    //     } catch (error) {

    //     }
    //   }
    // }
  }

  return output
}

function createDirAndWriteFile(dirPath, fileName, content) {
  // 创建目录  
  if (!fs.existsSync(dirPath)) {
    fs.mkdirSync(dirPath, { recursive: true });
  }

  // 拼接文件路径  
  const filePath = path.join(dirPath, fileName);

  // 写入文件  
  fs.writeFileSync(filePath, content);
}
新手上路,请多包涵

自定义的loader通过split再拼接,运行会有问题,具体原因不清楚。
不过直接通过正则去操作,就可以运行了

代码如下:

const tagRegList = [
  { tag: '<el-input', reg: /<el-input\b(?!-number)[^>]*>/g, attr: 'name' },
  { tag: '<el-input-number', reg: /<el-input-number\b[^>]*>/g, attr: 'name' },
  { tag: '<el-date-picker', reg: /<el-date-picker\b[^>]*>/g, attr: 'name' },
  { tag: '<el-select', reg: /<el-select\b(?!-v2)[^>]*>/g, attr: 'name' },
  { tag: '<el-button', reg: /<el-button\b[^>]*>/g, attr: 'btn-id' },
]

function getModel(str) {
  const match = str.match(/v-model="([^"]*)"/)
  const modelValue = match ? match[1] : null
  return modelValue
}

function getEventName(str) {
  const match = str.match(/@click="([^"()]*)/)
  const eventName = match ? match[1] : null
  return eventName
}

/* eslint-disable no-unreachable */
module.exports = function (content) {
  const templateMatch = content.match(/<template>([\s\S]*?)<\/template>/)
  if (templateMatch) {
    let transformCode = content
    tagRegList.forEach(item => {
      const matchTagList = content.match(item.reg)
      matchTagList?.forEach(matchTag => {
        // console.log('match', matchTag)
        if (matchTag.length && matchTag.indexOf(item.attr) === -1) {
          transformCode = transformCode.replace(
            matchTag,
            item.tag +
              ` ${item.attr}="${item.attr === 'btn-id' ? getEventName(matchTag) : getModel(matchTag)}" ` +
              matchTag.substring(item.tag.length, matchTag.length)
          )
        }
      })
    })

    return transformCode
  } else {
    return content
  }
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏