Deprecated: parse_url(): Passing null to parameter #1 ($url) of type string is deprecated in /www/wwwroot/blog_qqvbc_com/usr/plugins/Access/Access_Core.php on line 339

Deprecated: parse_url(): Passing null to parameter #1 ($url) of type string is deprecated in /www/wwwroot/blog_qqvbc_com/usr/plugins/Access/Access_Core.php on line 392

Deprecated: parse_url(): Passing null to parameter #1 ($url) of type string is deprecated in /www/wwwroot/blog_qqvbc_com/usr/plugins/Access/Access_Core.php on line 394
Joyber 的博客

image.png

报错解读

ERR_PNPM_IGNORED_BUILDS
pnpm 9+供应链安全策略,禁止自动运行依赖包的build脚本,部分包需要编译构建,被拦截,导致 pnpm dev 内部触发的install失败退出码1。

提示文字也给出方案:

Run "pnpm approve-builds" to pick which dependencies should be allowed to run scripts.

方案1:官方推荐(执行这个命令)

pnpm approve-builds

交互式选择需要允许构建的包,截图里列表:
@parcel/watcher core-js es5-ext esbuild tlbs‑map‑vue vue‑demi
全部勾选允许,完成后再执行

pnpm dev

方案2:一次性全局放行(快速调试,服务器本地开发用)

pnpm dev --ignore-builds=false

或者执行install阶段直接允许所有构建脚本

pnpm install --ignore-builds=false
pnpm dev

方案3:写到 .npmrc 永久关闭该拦截(当前项目)

echo "ignore-builds=false" >> .npmrc

然后再启动

pnpm dev

补充说明

  1. 这个不是依赖损坏,是pnpm新版本安全防护,防止恶意包执行脚本。
  2. 生产环境不建议直接全局关闭,优先用pnpm approve‑builds按需放行。
  3. 如果上面操作完依旧报错,删除锁文件与node_modules重来:

    rm -rf node_modules pnpm-lock.yaml
    pnpm install --ignore-builds=false
    pnpm dev

阿奇索(Agiso)开放平台

open.agiso.com,是面向虚拟商品电商商家的API开放接口平台,和91卡券是同一套产品体系,主打多平台店铺自动发货、订单推送对接。

主要用途

  1. 多电商平台订单接收
    通过API接收淘宝、天猫、闲鱼、拼多多、抖店、快手、有赞等店铺的订单消息推送,把店铺订单同步到自己的系统/第三方货源系统。
  2. 虚拟商品自动发货对接
    专门用于卡密、兑换券、会员充值、餐饮代下单这类虚拟货源。
  3. 店铺买家下单后,把订单推送给你的货源系统;
  4. 货源返回卡密/充值链接,自动发给买家,店铺自动标记已发货,实现无人值守自动发货。
  5. 开发者自建业务系统对接
    开发者创建应用拿到AppId/AppSecret,获取订单推送回调、授权token,把阿奇索能力集成到自己开发的ERP、货源系统、自动发货软件里。
  6. 联动91卡券后台
    开放平台负责接口授权,91卡券后台做卡券库存、货源管理、发货规则配置,两者配合使用。

使用门槛

  • 需要做个人/企业资质认证(企业需上传营业执照);
  • 创建应用后,需要找客服开通接口权限;长时间不调用接口,AppId会自动封禁;
  • 适合卖虚拟权益的商家、开发自动发货工具的开发者;实物电商基本用不到。

简单流程

注册账号→资质认证→创建应用→找客服开通权限→拿到AppId/AppSecret→店铺后台授权绑定→配置订单回调地址,接收店铺订单、完成自动发货。

提示:该平台主要服务虚拟卡券、充值类电商,不适合普通实物店铺。

鱼店长

不是闲鱼官方产品
归属:厦门阿奇索(Agiso) 的第三方PC客户端,和你刚才问的阿奇索开放平台、91卡券是同一家公司的产品。

区分清楚

  1. 闲鱼官方电脑后台:鱼小铺后台 seller.goofish.com,网页版,免费,阿里官方。
  2. 闲管家:闲鱼官方合作PC工作台,阿里侧认证第三方工具。
  3. 鱼店长:阿奇索自研付费软件,不属于阿里/闲鱼官方,只是做闲鱼商家工具对接。

鱼店长能干什么

面向闲鱼虚拟商品卖家(卡密、充值会员):

  • PC端多账号登录闲鱼、集中聊天客服、批量管理商品、自动擦亮、智能回复
  • 对接91卡券货源,实现闲鱼自动发货、直充,和阿奇索开放平台打通API接口
  • 是收费工具:年费/月费,部分直充业务还要扣“奇豆”手续费

风险提醒

  1. 属于第三方登录工具,是账号密码授权登录,不是闲鱼开放API授权,有账号风控、限流、封号风险。
  2. 自动擦亮、自动回复这类自动化操作,违反闲鱼用户协议,有概率触发处罚。
  3. 官方不会维护鱼店长,一旦闲鱼更新协议/接口,工具可能直接失效。
简单记:鱼小铺=闲鱼原生;闲管家=官方合作;鱼店长=阿奇索第三方付费工具。

ES5 vs ES6(ES2015+)核心区别

ES5:2009发布;ES6正式名称ES2015,后续每年迭代ES2016~ES2025,日常统称ES6。下面梳理最常用、工作高频差异

一、变量声明

ES5

只有 var

  1. 函数作用域,存在变量提升
  2. 允许重复声明
  3. 没有块级作用域(if/for循环无法隔离变量)

    var a = 1;
    var a = 2; // 合法
    for(var i=0;i<3;i++){}
    console.log(i); // 3 泄露到外层

ES6

新增 let / const

  1. 块级作用域 { } 隔离
  2. 暂时性死区TDZ,声明前不能访问
  3. 不允许同作用域重复声明
  4. const 声明常量(引用类型可修改内部属性,不能重赋值)

    let a = 1;
    const PI = 3.14;
    {
      let b = 10
    }
    console.log(b) // 报错

二、函数

1. 箭头函数 ()=>{} 【ES6新增】

ES5只有普通function

// ES5
var add = function(a,b){return a+b}
// ES6
const add = (a,b) => a + b;

箭头函数特点

  • 不绑定 this:继承外层作用域this(最常用,解决回调this指向问题)
  • 没有 arguments,可用剩余参数 ...args 替代
  • 不能作为构造函数,不能new
  • 没有 prototype

2. 函数参数增强 ES6

  • 默认参数

    // ES5
    function fn(x){x = x || 0}
    // ES6
    function fn(x = 0){}
  • 剩余参数 ...rest

    function sum(a,...rest){}
  • 参数解构

三、解构赋值(ES6新增)

快速提取数组、对象属性

// 对象解构
const user = {name:"张三",age:20}
// ES5
const name = user.name;
// ES6
const {name,age} = user;

//数组解构
const [a,b] = [10,20]

四、展开运算符 ... ES6

const arr2 = [...arr1,4,5]; //数组拷贝/合并
const obj2 = {...obj1,c:10}; //对象浅拷贝

五、字符串

  1. 模板字符串 ` (反引号) ES6

    // ES5
    var str = "name:" + name + ",age:"+age;
    // ES6
    const str = `name:${name},age:${age}`

    支持换行、插值 ${表达式}

  2. ES6新增字符串方法:includes()、startsWith()、endsWith()

六、对象字面量增强 ES6

const name = "小明"
// ES5
const obj = {name:name, say:function(){}}
// ES6 属性简写、方法简写
const obj = {
  name,
  say(){}
}
// 计算属性名
const obj = {["key"+1]:100}

七、数组新特性

  • ES6:Array.from() 类数组转数组
  • find()、findIndex()、some/every原有ES5就有
  • entries() keys() values() 迭代器

八、迭代器 & for...of(ES6新增)

  • ES5:for循环、forEach、for...in(遍历对象key,不适合数组)
  • ES6:for...of 遍历可迭代对象(数组、Set、Map、字符串)

    for(const item of arr){}
    for...in:遍历键;for...of:遍历值

九、新增数据结构 ES6

ES5仅有:Object、Array
ES6新增:

  • Set:集合,元素唯一
  • Map:键可以是任意类型(ES5对象key只能字符串/Symbol)
  • WeakSet / WeakMap 弱引用,利于GC垃圾回收
  • Symbol:唯一不可重复的值,用作私有属性标识

十、类 Class(ES6语法糖)

ES5 原型模拟面向对象

// ES5
function Person(name){this.name = name}
Person.prototype.say = function(){}

// ES6 class
class Person{
  constructor(name){
    this.name = name
  }
  say(){}
}

⚠️ class只是原型的语法糖,不是真正面向对象,不存在继承隔离;支持 extends 继承、super()

十一、模块化 Module import / export ES6

ES5没有原生模块,依靠:IIFE、RequireJS、CommonJS(Node.js)
ES6 原生ESM标准

//导出
export const a = 1;
export default fn;
//导入
import fn, {a} from './mod.js'
CommonJS:运行时加载;ESM:编译期静态导入,支持Tree-Shaking

十二、Promise 异步方案 ES6

ES5异步:回调函数 → 回调地狱
ES6 原生Promise,解决嵌套回调
后续ES2017新增 async / await(Promise语法糖)

new Promise((resolve,reject)=>{})
async function query(){
  await fetch()
}

十三、其他重要特性

  1. 赋值简写:对象解构、数组解构
  2. Promise、async/await
  3. Object.assign() 对象合并(ES6)
  4. 尾调用优化
  5. 二进制/八进制数值字面量
  6. Proxy、Reflect(ES6,实现数据劫持,Vue3底层原理)

精简对比总结表

特性ES5ES6
变量var,函数作用域let/const,块级作用域
函数function箭头函数、默认参数、rest参数
字符串字符串拼接模板字符串 `...${}`
对象基础字面量属性简写、方法简写、计算属性
循环for / forEach / for..infor...of 迭代器
异步回调函数Promise + async/await
面向对象原型手写构造函数class / extends 继承语法糖
模块无原生模块(CMD/AMD)import / export ESM
数据结构Array、ObjectSet、Map、Symbol、Weak系列
解构手动取值对象/数组解构赋值
运算符无展开语法...展开运算符

开发最需要记住的重点(面试高频)

  1. var / let / const 区别、块级作用域、暂时性死区
  2. 箭头函数this指向问题
  3. class是语法糖,底层依旧原型
  4. ESM 和 CommonJS 的差异
  5. Promise解决回调地狱,async await简化Promise
  6. 解构、模板字符串、展开运算符日常使用最多

JsonOb 响应式:数据劫持 + 变化监听完整技术细节

Screenshot_20260729_085722_tv_danmaku_bili_UnitedBizDetailsActivity.jpg

先纠正PPT笔误: Object.Defefirbairty  →  Object.defineProperty 
这套方案是Vue2 同源响应式方案,也是很多游戏引擎、小程序自研状态库 JsonOb 的底层实现。

一、整体流程对照PPT架构

plaintext

原始JS对象

Object.defineProperty 深度劫持(get/set)【数据拦截】

数据发生修改 → set触发 → 发送变更信号

信号广播系统(订阅发布模式)

通知所有依赖当前字段的UI组件,执行UI更新
 

二、核心1:Object.defineProperty 数据劫持原理

  1. API基础作用

 Object.defineProperty(obj, key, descriptor) 
对对象的指定属性重新定义,拦截 读取(get)、赋值(set)。

javascript

let rawData = { name:"test" };
// 劫持name属性
Object.defineProperty(rawData, 'name', {
get(){

// 读取时:收集当前正在执行的UI渲染函数(依赖收集)
track() 
return this._value;

},
set(newVal){

if(newVal === this._value) return; // 值无变化跳过
this._value = newVal;
// 修改时:触发信号派发,通知订阅者更新
trigger()

}
})
 

  1. 深度劫持(深层对象递归处理)

普通 defineProperty 只能劫持一层属性,嵌套对象  {a:{b:1}}  内层b不会监听。
实现方案:

1. 递归遍历对象所有属性;
2. 如果属性值依然是  Object/Array ,继续对它执行  defineProperty  包装;

这就是PPT写的【深度属性劫持】。

  1. 致命短板(defineProperty 天生缺陷)

1. 无法监听新增属性、删除属性
 obj.xxx = 1  新增字段不会触发set; delete obj.xxx  无钩子。
解决方案:框架提供  $set  /  $del  API。
2. 不能原生监听数组下标修改
 arr[2] = 999  不会触发set。

三、核心2:数组监听怎么实现(PPT第二条)

原生数组无法靠defineProperty监听下标变更,JsonOb采取 方法重写拦截:

1. 复制数组原型  Array.prototype ;
2. 重写会改变数组内容的变异方法: push pop shift unshift splice sort reverse ;
3. 当业务调用  arr.push()  时,先走重写后的包装函数:

  • 执行原生数组操作
  • 手动调用  trigger()  发送变更信号

javascript

// 伪代码思路
const rawPush = Array.prototype.push;
Array.prototype.push = function(...args){
const res = rawPush.apply(this, args);
trigger(this); // 广播数组变更
return res;
}
 

注意: arr[0] = xxx 、 arr.length = 10  依然无法被捕获,框架一般同样提供API规避。

四、核心3:依赖收集 & 信号广播(订阅发布)

劫持只是拿到“数据变化通知”,还需要知道哪些UI依赖这个字段。

  1. track() 依赖收集(get阶段)

1. UI组件执行渲染函数,读取响应式字段  data.name ;
2. 触发  get() ,执行 track ;
3. 把「当前渲染函数」存入当前属性的依赖列表。
数据结构简易模型:

plaintext

targetMap = {
对象: {

属性名: [组件更新回调1, 组件更新回调2]

}
}
 

  1. trigger() 信号广播(set阶段)

当 set 检测数值改变:

1. 获取该属性保存的所有依赖回调;
2. 批量执行回调 → UI组件执行刷新。
也就是PPT里的【信号广播】。

五、PPT重点特性:enableBatch 批量变更

场景:一帧内连续多次修改数据

plaintext

data.a = 1
data.b = 2
data.c = 3
 

不做批处理:每次赋值立刻触发UI刷新 → 3次渲染,性能浪费。
enableBatch实现逻辑:

1. 开启批量模式后, trigger 不会立刻执行更新;
2. 将所有变更任务存入队列;
3. 等到当前JS执行栈结束(下一帧/微任务时机);
4. 去重、合并所有变更,统一执行一次UI更新。

游戏引擎、前端渲染库高频修改场景极其重要,减少重绘次数。

六、内存管理(PPT最后一条)

这套响应式很容易产生内存泄漏:
对象销毁后,依赖列表依然保存UI组件引用,组件无法GC。
JsonOb处理方案:

1. 组件销毁时,主动清除对应属性的依赖回调;
2. 使用弱引用容器(WeakMap)保存targetMap;
3. 对外提供销毁API,解除对象劫持,清空订阅列表。

七、拓展对比:defineProperty vs Proxy

你这套JsonOb使用 Object.defineProperty(Vue2方案)
现代方案用  Proxy (Vue3):

1. Proxy原生支持监听新增属性、数组下标、删除;
2. 不需要递归深度劫持,可以惰性代理;
缺点:不支持低版本运行环境。
很多小游戏引擎、老框架继续沿用defineProperty实现的JsonOb。

八、完整数据流总结(对应PPT框图)

1. 开发者修改响应式数据;
2.  Object.defineProperty set  拦截赋值(数据拦截);
3. 判断值是否真正变化,避免无效通知;
4. 发送变更信号(批量模式下先入队列);
5. 信号广播遍历所有订阅该字段的UI组件;
6. 各个UI组件执行自身更新逻辑;
7. 组件卸载后自动清理依赖引用,防止内存泄露。

如果你需要,我可以写一份精简可运行的 JsonOb最小实现Demo(defineProperty版本),直观复现这套劫持+广播逻辑。