NervJS / taro

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

taro3.x 引入 lodash 错误 #8098

Open JiaLe1 opened 3 years ago

JiaLe1 commented 3 years ago

相关平台

支付宝小程序

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

复现步骤

import React, { useState } from 'react'
import { View } from '@tarojs/components'
import { AtSearchBar } from "taro-ui"
// import { debounce } from 'lodash'   此方法引入,页面加载失败,报错为 TypeError: Cannot read property 'prototype' of undefined
import { debounce } from 'lodash/function'  此方法引入,页面正常加载,当触发onChange 事件时,报错为 TypeError: Cannot read property 'now' of undefined

import "taro-ui/dist/style/components/search-bar.scss"
import "taro-ui/dist/style/components/list.scss"
import "taro-ui/dist/style/components/icon.scss"

import './index.scss'

export default function Search() {
  const [mine, setMine] = useState({ list: [] }),
    [search, setSearch] = useState({ list: [], search: { value: '' }, disabled: false, fixed: true }),
    [debounceOptions, setDebounceOptions] = useState({
      wait: 800, options: {
        'leading': true,
        'trailing': false
      }
    })

  const handleSearch = ((e) => {
    return debounce((e, options) => {
      onSearch(e, options)
    }, debounceOptions.wait, {
      'leading': false,
      'trailing': true
    })
  })()

  const onSearch = (e, options) => {
    console.log('onChange', e, options)
  }

  return (
    <View className="page">
      <AtSearchBar
        actionName='搜一下'
        value={search.search.value}
        disabled={search.disabled}
        fixed={search.fixed}
        onChange={handleSearch}
        onActionClick={handleSearch}
      />
    </View>
  )

}

期望结果

期望无报错

实际结果

h5与微信小程序无报错,支付宝小程序报错为: import { debounce } from 'lodash' 此方法引入,页面加载失败,报错为 TypeError: Cannot read property 'prototype' of undefined or import { debounce } from 'lodash/function' 此方法引入,页面正常加载,当触发onChange 事件时,报错为 TypeError: Cannot read property 'now' of undefined

环境信息

👽 Taro v3.0.16

  Taro CLI 3.0.16 environment info:
    System:
      OS: macOS 11.0.1
      Shell: 3.2.57 - /bin/bash
    Binaries:
      Node: 12.0.0 - ~/.nvm/versions/node/v12.0.0/bin/node
      Yarn: 1.19.1 - ~/.nvm/versions/node/v10.0.0/bin/yarn
      npm: 6.9.0 - ~/.nvm/versions/node/v12.0.0/bin/npm
    npmPackages:
      @tarojs/components: 3.0.16 => 3.0.16 
      @tarojs/mini-runner: 3.0.16 => 3.0.16 
      @tarojs/react: 3.0.16 => 3.0.16 
      @tarojs/runtime: 3.0.16 => 3.0.16 
      @tarojs/taro: 3.0.16 => 3.0.16 
      @tarojs/webpack-runner: 3.0.16 => 3.0.16 
      babel-preset-taro: 3.0.16 => 3.0.16 
      eslint-config-taro: 3.0.16 => 3.0.16 
      react: ^16.10.0 => 16.14.0 
      taro-ui: ^3.0.0-alpha.3 => 3.0.0-alpha.3 
JiaLe1 commented 3 years ago

import { debounce } from 'lodash/function'

目前通过在 app.js(app.tsx) 中 写入

Object.assign(global, {
  Array: Array,
  Date: Date,
  Error: Error,
  Function: Function,
  Math: Math,
  Object: Object,
  RegExp: RegExp,
  String: String,
  TypeError: TypeError,
  setTimeout: setTimeout,
  clearTimeout: clearTimeout,
  setInterval: setInterval,
  clearInterval: clearInterval
})

处理支付宝小程序引入 lodash 错误问题

everlose commented 1 year ago

我调试得知访问 Array.prototype 时报错,此 Array 来自于 root.Array,root 又来自于

 var root = _freeGlobal || freeSelf || Function('return this')();

这个就无解了,要么篡改这个 lodash 的 root 的赋值

var root = {
  Array: Array,
  Date: Date,
  Error: Error,
  Function: Function,
  Math: Math,
  Object: Object,
  RegExp: RegExp,
  String: String,
  TypeError: TypeError,
  setTimeout: setTimeout,
  clearTimeout: clearTimeout,
  setInterval: setInterval,
  clearInterval: clearInterval
};

要么就只能如你代码所说按需引入,不能全局引入。

wenfangdu commented 1 year ago

Taro 3.6.6 依然可以复现,大佬们来修下呀

wenfangdu commented 1 year ago

import { debounce } from 'lodash/function'

目前通过在 app.js(app.tsx) 中 写入

Object.assign(global, {
  Array: Array,
  Date: Date,
  Error: Error,
  Function: Function,
  Math: Math,
  Object: Object,
  RegExp: RegExp,
  String: String,
  TypeError: TypeError,
  setTimeout: setTimeout,
  clearTimeout: clearTimeout,
  setInterval: setInterval,
  clearInterval: clearInterval
})

处理支付宝小程序引入 lodash 错误问题

Taro 3.6.6 里对于 lodash 有效,对于 lodash-es 无效。

wenfangdu commented 1 year ago

最后换成了 https://www.npmjs.com/package/@github/mini-throttle

codMeing commented 1 year ago

尝试在小程序入口文件引入lodash-fix.js 参考:https://blog.51cto.com/u_13567403/4842786

jackple commented 1 year ago
const obj = {
    Array: Array,
    Date: Date,
    Error: Error,
    Function: Function,
    Math: Math,
    Object: Object,
    RegExp: RegExp,
    String: String,
    TypeError: TypeError,
    setTimeout: setTimeout,
    clearTimeout: clearTimeout,
    setInterval: setInterval,
    clearInterval: clearInterval
}

Object.assign(global, obj)

if (typeof window === 'object' && typeof window.global === 'object') {
    Object.assign(window.global, obj)
}

试试这样?

codMeing commented 1 year ago

这是来自QQ邮箱的假期自动回复邮件。   您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。

agileago commented 1 year ago

`const obj = { Array: Array, Date: Date, Error: Error, Function: Function, Math: Math, Object: Object, RegExp: RegExp, String: String, TypeError: TypeError, setTimeout: setTimeout, clearTimeout: clearTimeout, setInterval: setInterval, clearInterval: clearInterval }

Object.assign(global, obj)

if (typeof window === 'object' && typeof window.global === 'object') { Object.assign(window.global, obj) } `

试试这样?

大兄弟这个可以

specialCoder commented 9 months ago

Taro3 版本下使用 lodash 或者 lodash-es 都会报错。 image 原因是 lodash/debounce 下引入的 now.js 文件有段代码:

var now = function() {
   return root.Date.now();
};

root 在运行时发现是 undefined。 用underscore 平替 lodash / lodash-es 吧。 https://github.com/jashkenas/underscore underscore 下的 now.js 是这样实现的:

export default Date.now || function() {
  return new Date().getTime();
};

这样不会有问题

yuuk commented 2 months ago

微信小程序下也遇到了,taro@3.6.32,lodash-es@4.17.21

codMeing commented 2 months ago

这是来自QQ邮箱的假期自动回复邮件。   您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。

anyesu commented 1 month ago

基础库版本问题


项目中使用了 lodash-es ,在切换 微信小程序基础库版本 时报了这个错误( 真机基础库版本比较新,预览正常 ),微信开发者工具中测试了基础库 3.2.4 及以上版本不会报错。

TypeError: Cannot read property 'now' of undefined
  at now (._node_modules_.pnpm_lodash-es@4.17.21_node_modules_lodash-es_now.js:20)

3.2.4 中,打断点调用 Function('return this')() 返回的是 Window 对象。

3.2.4

3.2.3 及以下版本中,返回的是空对象 {}

直接在控制台调用 Function('return this')() 时返回的是 Window 对象。

3.2.3

微信小程序中使用 lodash 不会报错


关闭 sourceMap 后可以方便从控制台直接进入实际代码。

export default {
  mini: {
    enableSourceMap: false,
  },
} satisfies UserConfigExport;

打开 vendors.js 可以看到 node_modules/lodash/lodash.js 中的 global 被替换成了 __webpack_require__.g 。( 参考 https://github.com/NervJS/taro/issues/14033#issuecomment-1605953773

  • node.global

    If you are using a module which needs global variables in it, use ProvidePlugin instead of global.

// 编译前
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;

// 编译后
var freeGlobal = typeof __webpack_require__.g == 'object' && __webpack_require__.g && __webpack_require__.g.Object === Object && __webpack_require__.g;

微信小程序中存在 globalThis 所以不会报错。如果 globalThis 不存在就会出现和题主一样的错误。

import { debounce } from 'lodash' 会引入完整的 lodash.js ,出错是因为下面这段代码:

var runInContext = (function runInContext(context) {
  context = context == null ? root : _.defaults(root.Object(), context, _.pick(root, contextProps));

  /** Built-in constructor references. */
  var Array = context.Array,
    Date = context.Date,
    Error = context.Error,
    Function = context.Function,
    Math = context.Math,
    Object = context.Object,
    RegExp = context.RegExp,
    String = context.String,
    TypeError = context.TypeError;

  /** Used for built-in method references. */
  var arrayProto = Array.prototype,
    funcProto = Function.prototype,
    objectProto = Object.prototype;
  // ...
});

root 是空对象了,那么 context.Array 自然是 undefined 。而 lodash-es 中没有 runInContext 就没有这个问题。

好奇研究了一下 lodash-es 中的 global 为什么没有被替换。因为 DefinePlugin 能对 lodash-es 生效,就对比了 DefinePluginNodeStuffPlugin 的代码,发现 NodeStuffPlugin 中缺少下面这段代码:

normalModuleFactory.hooks.parser
  .for(JAVASCRIPT_MODULE_TYPE_ESM)
  .tap(PLUGIN_NAME, handler);

说明 NodeStuffPlugin 不解析 ESM 包 ( 参考 https://github.com/webpack/webpack/issues/14210#issuecomment-917419144 ),而 lodash-es@4.17.20 正好是 ESM 包

解决办法


上面这些方法目的都是为了让 global 统一指向 globalThis ,对于不支持 globalThis 的环境需要额外处理,比如:

记得比较修改前后的 vendors.js 内容,查看具体替换了哪些代码。

lodash 简易版按需引入


export default {
  alias: {
    lodash: 'lodash-es',
  },
} satisfies UserConfigExport;

相关问题


最后


以上分析主要针对 微信小程序 ,其他小程序环境没试过,但思路应该一样的。

虽然没做过支付宝小程序不太了解,但看 文档 目前应该已经支持 globalThis 了。

默认情况下,小程序代码中禁止访问 globalThisglobal 等全局上下文对象;这可能会破坏 core-js 的正常工作。

因此请先通过 mini.project.json 中的 globalObjectMode 配置项开启全局上下文对象,具体参考 globalObjectMode 的相关说明。

支付宝小程序 - globalObjectMode

codMeing commented 1 month ago

这是来自QQ邮箱的假期自动回复邮件。   您好,我最近正在休假中,无法亲自回复您的邮件。我将在假期结束后,尽快给您回复。