引入@vue/reactivity失败

  • Creator 版本: 3.8.1

  • 目标平台: Chrome浏览器

  • 重现方式:
    在组件文件中引入@vue/reactivityref并使用

// GameManager.ts
import { ref } from "@vue/reactivity";
// ...

@ccclass("GameManager")
export class GameManager extends Component {

  test() {
    console.log("ref", ref);
  }

  protected async onLoad() {
    this.test();
  }

// ...
}

  • 首个报错:
ReferenceError: process is not defined
    at _cjsLoader.define../dist/reactivity.cjs.prod.js (index.js:12:2)
    at CjsLoader._load (cjs-loader.mjs:78:9)
    at CjsLoader._tryModuleLoad (cjs-loader.mjs:63:18)
    at CjsLoader._require (cjs-loader.mjs:42:14)
    at CjsLoader.require (cjs-loader.mjs:27:21)
    at Object.execute (index.mjs:7:8)
    at doExec (system-core.js:309:30)
    at postOrderExec (system-core.js:300:21)
    at system-core.js:285:28
    at Array.forEach (<anonymous>)
(anonymous) @ index.js:1
(anonymous) @ index.js:1
(anonymous) @ index.js:1
c @ index.js:1

点击报错信息定位到的相关代码:

 if (process.env.NODE_ENV === 'production') {
   module.exports = require('./dist/reactivity.cjs.prod.js')
 } else {
   module.exports = require('./dist/reactivity.cjs.js')
 }

如果修改导入语句为import具体的文件:

import { ref } from "@vue/reactivity/dist/reactivity.esm-browser.js";

则报错为

Error: Error: Unexpected export statement in CJS module.
  at :7456/@:1268:8
    at Object.execute (data:text/javascript,%0A%2F%2F%20This%20module%20is%20auto-generated%20to%20report%20error%20emitted%20when%20try%20to%20load%20module%20file%3A%2F%2F%2FC%3A%2FWork%2FPorjects%2Fmahjong-matching-game%2Fnode_modules%2F%40vue%2Freactivity%2Fdist%2Freactivity.esm-browser.js%20at%20runtime.%0Athrow%20new%20Error(%60Error%3A%20Unexpected%20export%20statement%20in%20CJS%20module.%0A%20%20at%20%40%3A1268%3A8%60)%3B%0A%20%20%20%20%20%20%20%20:3:7)
    at doExec (system-core.js:309:30)
    at postOrderExec (system-core.js:300:21)
    at system-core.js:285:28
    at Array.forEach (<anonymous>)
    at postOrderExec (system-core.js:283:10)
    at system-core.js:285:28
    at Array.forEach (<anonymous>)
    at postOrderExec (system-core.js:283:10)
    at system-core.js:285:28
(anonymous) @ index.js:1
(anonymous) @ index.js:1
(anonymous) @ index.js:1
c @ index.js:1

在HTML文件中这样导入时无报错:

  <script type="module">
    import { ref } from './node_modules/@vue/reactivity/dist/reactivity.esm-browser.js';

    console.log('ref', ref);
  </script>

加一个插件脚本,内容为:

globalThis.process = globalThis.process || {};

process.env = process.env || {};

记得插件脚本属性要全部勾选加载。

然后将引入模块改为:

import reactivity from “@vue/reactivity/dist/reactivity.cjs.js”;

为了获取类型提示,将以下内容拷贝至任何 .d.ts 文件中,并在 tsconfig.json 中引用该 .d.ts 文件。

declare module “@vue/reactivity/dist/reactivity.cjs.js” {
export * as default from ‘@vue/reactivity’;
}

2赞

大佬牛皮!

感谢解答,看来cocos creator无法将该包识别为esm类型。
我这里分享一下我的完整配置,除了自动导入时需要手动选择一下,其他的编码体验已经接近前端项目中使用该包的了。

1. 使用插件脚本避免@vue/reactivity报错

新建assets\plugin\declare-process.js文件,添加以下代码:

// 声明process对象,避免@vue/reactivity报错
globalThis.process = globalThis.process || {};

process.env = process.env || {
  NODE_ENV: 'production',
};

然后在cocos creator中将该文件的以下属性勾选,设置为插件脚本:
image

2. 对@vue/reactivity重新声明类型和重新导出

新建assets\types\env.d.ts文件用于声明全局类型,添加以下代码重新声明@vue/reactivity类型:

declare module '@vue/reactivity/dist/reactivity.cjs.js' {
  const reactivity: typeof import('@vue/reactivity/dist/reactivity');

  export default reactivity;
}

// 声明@vue/reactivity导出为空对象
declare module '@vue/reactivity' {
  export {};
}

新建assets\lib\vue\reactivity.ts文件,添加以下代码对@vue/reactivity中的方法进行具名导出:

// 重新导出@vue/reactivity为具名导出
import reactivity from '@vue/reactivity/dist/reactivity.cjs.js';
export const {
  ComputedRefSymbol,
  ITERATE_KEY,
  RawSymbol,
  RefSymbol,
  ShallowRefMarker,
  EffectScope,
  ReactiveEffect,
  ReactiveFlags,
  ShallowReactiveMarker,
  TrackOpTypes,
  TriggerOpTypes,
  computed,
  customRef,
  deferredComputed,
  effect,
  effectScope,
  enableTracking,
  getCurrentScope,
  isProxy,
  isReactive,
  isReadonly,
  isRef,
  isShallow,
  markRaw,
  onScopeDispose,
  pauseTracking,
  proxyRefs,
  reactive,
  readonly,
  ref,
  resetTracking,
  shallowReactive,
  shallowReadonly,
  shallowRef,
  stop,
  toRaw,
  toRef,
  toRefs,
  toValue,
  track,
  trigger,
  triggerRef,
  unref,
} = reactivity;

3. 添加typescript配置

在项目根目录下找到tsconfig.json文件,添加以下配置:

{
// ...
  "include": [
    "assets/script/**/*.ts",
    "assets/script/**/*.d.ts"
  ],
// ...
}

4. 使用

在ts文件中,输入@vue/reactivity内置方法时则可以正常提示并自动导入,不过需要手动切换到/lib/vue/reactivity来导入:
image

如果选择到了从@vue/reactivity导入,VScode中也会出现报错提示,防止错误导入:
image

第一步之后的完全多余!

只要在tsconfig.json里设置 compilerOptions.allowSyntheticDefaultImports 为 true即可

版本:3.8.4
解决方法:将引入@vue/reactivity和其依赖@vue/shared改成module,写了个脚本,在install后代码改动对应的package.json文件,加入“type":“module”

0.准备

在项目根目录创建文件夹scripts,创建文件cjs2esm.js,粘贴下面的代码。然后在项目package.json的script里新增 “postinstall” :“node scripts/cjs2esm.js”。

最后执行一次npm install

1.代码如下

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


function changePackageJsonTypeToModule(path) { 
  // 检查 package.json 文件是否存在
  if (fs.existsSync(path)) {
    // 读取 package.json 文件
    const packageJson = require(path);

    // 检查是否已经存在 "type": "module",如果不存在则添加
    if (!packageJson.type || packageJson.type !== 'module') {
      packageJson.type = 'module';

      // 将修改后的内容写回 package.json 文件
      fs.writeFileSync(path, JSON.stringify(packageJson, null, 2));
      console.log('"type": "module" has been added to package.json');
    } else {
      console.log('"type": "module" already exists in package.json');
    }
  } else {
    console.log('package.json does not exist in the target directory');
  }
}

function writeProcessEnvDefine(){
    let targetDir = path.resolve(__dirname, '../assets/plugins');
    let targetPath = path.join(targetDir, 'node_env_define.js');
    if (!fs.existsSync(targetPath)) {

        fs.mkdir(targetDir, { recursive: true }, (err) => {
            if (err) {
                return console.error('创建文件夹时出错:'+targetDir, err);
            }
        });
        // 读取 package.json 文件
        let code = `
globalThis.process = globalThis.process || {};
process.env = process.env || {};
process.env.NODE_ENV = 'production';
        `

        fs.writeFileSync(targetPath, code);
      }
}


//npm install @vue/reactivity@3.5.13
// 定义目标目录
const reactivityTargetDir = path.resolve(__dirname, '../node_modules', '@vue', 'reactivity');
const reactivityPackageJsonPath = path.join(reactivityTargetDir, 'package.json');

// 调用函数
changePackageJsonTypeToModule(reactivityPackageJsonPath);

const shareTargetDir = path.join(__dirname, '../node_modules', '@vue', 'shared');
const sharePackageJsonPath = path.join(shareTargetDir, 'package.json');
changePackageJsonTypeToModule(sharePackageJsonPath);

// 写入 process.env.NODE_ENV = 'production' 到 node_modules/@vue/reactivity/node_env_define.json
writeProcessEnvDefine()

2.在编辑器里将生成的assets/plugin/node_env_define.js文件设置为plugin

脚本更新:

const fs = require('fs');
const path = require('path');
const readline = require('readline');
//注意:此脚本是在3.5.13基础上操作的,如果是其他版本,可以按脚本的思路找到对应文件做出改变 @vue/reactivity@3.5.13

function changePackageJsonTypeToModule(path) { 
  // 检查 package.json 文件是否存在
  if (fs.existsSync(path)) {
    // 读取 package.json 文件
    const packageJson = require(path);

    // 检查是否已经存在 "type": "module",如果不存在则添加
    if (!packageJson.type || packageJson.type !== 'module') {
      packageJson.type = 'module';

      // 将修改后的内容写回 package.json 文件
      fs.writeFileSync(path, JSON.stringify(packageJson, null, 2));
      console.log('"type": "module" has been added to package.json');
    } else {
      console.log('"type": "module" already exists in package.json');
    }
  } else {
    console.log('package.json does not exist in the target directory');
  }
}

function writeProcessEnvDefine(){
    let targetDir = path.resolve(__dirname, '../assets/plugins');
    let targetPath = path.join(targetDir, 'node_env_define.js');
    if (!fs.existsSync(targetPath)) {

        fs.mkdir(targetDir, { recursive: true }, (err) => {
            if (err) {
                return console.error('创建文件夹时出错:'+targetDir, err);
            }
        });
        // 读取 package.json 文件
        let code = `
globalThis.process = globalThis.process || {};
process.env = process.env || {};
process.env.NODE_ENV = 'production';
        `
        fs.writeFileSync(targetPath, code);
      }
}


//npm install @vue/reactivity@3.5.13
// 定义目标目录
const reactivityTargetDir = path.resolve(__dirname, '../node_modules', '@vue', 'reactivity');
const reactivityPackageJsonPath = path.join(reactivityTargetDir, 'package.json');

// 调用函数
changePackageJsonTypeToModule(reactivityPackageJsonPath);

const shareTargetDir = path.join(__dirname, '../node_modules', '@vue', 'shared');
const sharePackageJsonPath = path.join(shareTargetDir, 'package.json');
changePackageJsonTypeToModule(sharePackageJsonPath);

// 写入 process.env.NODE_ENV = 'production' 到 node_modules/@vue/reactivity/node_env_define.json
writeProcessEnvDefine()

//导入的shared.esm-bundler.js和reactivity.esm-bundler.js文件最前面加一句 process = window 。不然在微信小游戏工具里用systemjs加载时,找不到process变量
function fileAddProcessDefine(dependFilePath){
  let filePath = path.resolve(__dirname, '../node_modules',dependFilePath);
  if (!fs.existsSync(filePath)) {
      console.log("依赖文件不存在:"+filePath);
      return
  }

  let addNewLine = "process = window.process"

  // 读取文件
  let data = fs.readFileSync(filePath, {
    encoding : "utf-8"
  });
  // 获取第一行内容
  const firstLine = data.split('\n')[0];
  console.log('第一行内容:', firstLine);
  if(firstLine===addNewLine){
    return
  }
  let newContent = addNewLine+"\n"+data;
  // 将修改后的内容写回文件
  fs.writeFile(filePath, newContent, 'utf8', (err) => {
    if (err) {
        console.error('写入文件失败:', err);
        return;
    }
    console.log(`${filePath} 写入内容成功:`+addNewLine);
  });
   
}

fileAddProcessDefine("@vue/reactivity/dist/reactivity.esm-bundler.js")
fileAddProcessDefine("@vue/shared/dist/shared.esm-bundler.js")