NervJS / taro

开放式跨端跨框架解决方案,支持使用 React/Vue/Nerv 等框架来开发微信/京东/百度/支付宝/字节跳动/ QQ 小程序/H5/React Native 等应用。 https://taro.zone/
https://docs.taro.zone/
Other
35.68k stars 4.79k forks source link

原生小程序混编,wxml中的图片资源不自动复制 #8489

Open tourze opened 3 years ago

tourze commented 3 years ago

相关平台

微信小程序

小程序基础库: 2.14.3 使用框架: React

复现步骤

使用原生语法创建一个页面,wxml大概如:

            <image class="go-right-icon" src="/images/1.png"></image>

使用taro起后,发现编译后的文件汇中并没有 images/1.png

期望结果

在混编时,如果发现wxml中有图片资源,应同步到dist

实际结果

开发者工具提示图片不存在,查看dist目录发现实际也是没有。

环境信息

� Taro v3.0.19

  Taro CLI 3.0.19 environment info:
    System:
      OS: Windows 10
    Binaries:
      Node: 14.15.3 - E:\nvmNode\node.EXE
      Yarn: 1.22.4 - E:\nvmNode\yarn.CMD
      npm: 6.14.4 - E:\nvmNode\npm.CMD

补充信息

正常没问题的,只有混编才有问题。其实在config中配置copy也能解决,但是这样就不够优雅了

tourze commented 3 years ago

这个问题,也是够久的了。。。最后没办法只能自己写插件来解决,创建 config/plugins/raw-weapp-enhance.js,内容是:

const fs = require('fs');
const path = require('path');
const copy = require('recursive-copy');

const projectRoot = path.resolve(__dirname, '../..'); // 本项目目录

// eslint-disable-next-line no-extend-native
String.prototype.trim = function (char, type = 'left') {
  if (char) {
    if (type === 'left') {
      return this.replace(new RegExp('^\\' + char + '+', 'g'), '');
    } else if (type === 'right') {
      return this.replace(new RegExp('\\' + char + '+$', 'g'), '');
    }
    return this.replace(new RegExp('^\\' + char + '+|\\' + char + '+$', 'g'), '');
  }
  return this.replace(/^\s+|\s+$/g, '');
};

const copyKeys = [];
const copyList = {};

function copyFiles(fileName, sourceRoot, outputRoot) {
  if (copyKeys.includes(fileName)) {
    return;
  }
  copyKeys.push(fileName);

  const extensions = ['jpg', 'png', 'gif', 'toff', 'jpeg', 'bmp', 'wxs'];
  extensions.map((ext) => {
    const srcExt = `${sourceRoot}/${fileName}.${ext}`;
    if (fs.existsSync(srcExt)) {
      copyList[srcExt] = srcExt.replace(`${sourceRoot}`, `${outputRoot}`);
    }
  });

  const srcJson = `${sourceRoot}/${fileName}.json`;
  if (fs.existsSync(srcJson)) {
    copyList[srcJson] = srcJson.replace(`${sourceRoot}`, `${outputRoot}`);
    const jsonConfig = require(srcJson);
    // 依赖组件要复制一次
    if (jsonConfig.usingComponents !== undefined) {
      Object.keys(jsonConfig.usingComponents).map((k) => {
        const compPath = jsonConfig.usingComponents[k];
        const srcPath = compPath.startsWith('/')
          ? compPath.trim('/')
          : path.relative(`${sourceRoot}`, path.resolve(path.dirname(srcJson), `${compPath}`));

        // 一般来说,wxml是必不可少的吧?
        if (fs.existsSync(`${sourceRoot}/${srcPath}.wxml`)) {
          copyFiles(srcPath, sourceRoot, outputRoot);
        }
      });
    }
  }

  // 依赖样式文件
  const srcWXSS = `${sourceRoot}/${fileName}.wxss`;
  if (fs.existsSync(srcWXSS)) {
    copyList[srcWXSS] = srcWXSS.replace(`${sourceRoot}`, `${outputRoot}`);
  }

  // 复制wxml
  const srcWXML = `${sourceRoot}/${fileName}.wxml`;
  if (fs.existsSync(srcWXML)) {
    copyList[srcWXML] = srcWXML.replace(`${sourceRoot}`, `${outputRoot}`);

    // 复制wxml中的图片等资源
    const code = fs.readFileSync(srcWXML, { encoding: 'utf8', flag: 'r' });
    const res1 = code.match(/src="(.*?)"/g);
    if (res1) {
      res1.map((text) => {
        text = text.replace('src="', '');
        text = text.trim('"', 'right');
        if (text.startsWith('//:')) {
          return;
        }
        if (text.startsWith('http://')) {
          return;
        }
        if (text.startsWith('https://')) {
          return;
        }
        if (text.includes('{')) {
          return;
        }
        if (text.includes('+')) {
          return;
        }
        text = text.startsWith('/')
          ? text.trim('/')
          : path.relative(`${sourceRoot}`, path.resolve(path.dirname(srcWXML), `${text}`));
        text = `${text.split('.').slice(0, -1).join('.')}`;
        copyFiles(text, sourceRoot, outputRoot);
      });
    }
    const res2 = code.match(/src='(.*?)'/g);
    if (res2) {
      res2.map((text) => {
        text = text.replace("src='", '');
        text = text.trim("'", 'right');
        if (text.startsWith('//:')) {
          return;
        }
        if (text.startsWith('http://')) {
          return;
        }
        if (text.startsWith('https://')) {
          return;
        }
        if (text.includes('{')) {
          return;
        }
        if (text.includes('+')) {
          return;
        }
        text = text.startsWith('/')
          ? text.trim('/')
          : path.relative(`${sourceRoot}`, path.resolve(path.dirname(srcWXML), `${text}`));
        text = `${text.split('.').slice(0, -1).join('.')}`;
        copyFiles(text, sourceRoot, outputRoot);
      });
    }
  }

  // 依赖JS
  const srcJS = `${sourceRoot}/${fileName}.js`;
  if (fs.existsSync(srcJS)) {
    copyList[srcJS] = srcJS.replace(`${sourceRoot}`, `${outputRoot}`);
  }
  const srcTS = `${sourceRoot}/${fileName}.ts`;
  if (fs.existsSync(srcTS)) {
    copyList[srcTS] = srcTS.replace(`${sourceRoot}`, `${outputRoot}`);
  }
}

module.exports = (ctx, options) => {
  // plugin 主体
  // 原生小程序混编功能优化
  // ctx.onBuildStart(() => {
  //   console.log('编译开始!');
  // });
  // ctx.onBuildFinish(() => {
  //   console.log('编译结束!');
  // });
  // console.log('原生小程序混编功能优化 ctx', ctx);
  const initialConfig = ctx.ctx.initialConfig;
  // console.log('原生小程序混编功能优化 initialConfig:', initialConfig);

  ctx.modifyBuildAssets(({ assets }) => {
    // console.log('modifyBuildAssets', assets);
    // 输出一个数组
    // 可以获得文件路径和文件内容

    const ignoreList = [
      'comp.wxml',
      'base.wxml',
      'custom-wrapper.wxml',
    ];

    Object.keys(assets).forEach(src => {
      if (ignoreList.includes(src)) {
        return;
      }

      if (src.endsWith('.wxml')) {
        // console.log('转换的文件', src);

        // source() 函数可以获得文件内容
        // 原生组件为 buffer 对象,需要 toString
        // const source = assets[src].source().toString();
        // parserXML 添加id
        // const domStr = parserXML(source);
        // 覆写source函数
        // assets[src].source = function() {
        //   return domStr;
        // };

        // 一般来说,要处理的内容主要是资源文件
        const fileName = src.split('.').slice(0, -1).join('.');
        copyFiles(fileName, `${projectRoot}/${initialConfig.sourceRoot}`, `${projectRoot}/${initialConfig.outputRoot}`);
      }
    });

    //console.log(copyList);
    Object.keys(copyList).map((src) => {
      const dest = copyList[src];
      if (fs.existsSync(dest)) {
        return;
      }
      copy(src, dest, function(error, results) {
        if (error) {
          console.error(`Copy ${src} failed: ${error}`);
        } else {
          console.info(`Copy ${src} to ${dest}`);
        }
      });
    });
  });
};

然后在 config/index.js 中修改配置:

  plugins: [
    [`${projectRoot}/config/plugins/raw-weapp-enhance`],
  ],

这样可以解决。