• babel转换class时使用defineProperty导致的装饰器问题


    脑子一片混乱,搞了大半天虽然把问题解决了,但总高兴不起来,工程化能力太弱,每次都遇到各种奇葩的问题,让我都快要从入门到跑路了,说不定是从入门到入土(前段时间看到有人这样说,很好玩,抄袭一下,哈哈)。

    话不多说,先说结论,故事慢慢再讲。

    1. react-scripts中的webpack配置在打包时,使用的是@babel/plugin-transform-class,并不会用typescript进行编译
    2. @babel/plugin-transform-class对类中字段的转码使用的是defineProperty,大部分情况下没有问题,但是在跟类属性的装饰器一起使用时导致装饰器失效,并不会报错:(

    解决办法,在babel-loader处理之前添加ts-loader,先让typescript来转码。好了,开始讲故事了:)

    背景是要把现有的一个项目改造成create-react-app创建的项目时遇到的问题。原来项目用的是rollup打包的。使用npx create-react-app project-name --template typescript创建一个空的项目,然后把代码拷贝过来。做了简单的修改,项目就这样跑起来了,我以为这样就结束了,可是我却不知道噩梦刚刚开始。

    其实大部分功能都是正常的,唯独一个场景。最后分析下来是修改字段时并没有触发set,其背后的重绘逻辑也没有得到执行。经过提炼后的源码大概是这样的

    class Student {
    	@dirty
    	name: string = '';
    
    	dirty() {
    		console.log('学生属性变化了,需要重绘哈');
    	}
    }
    
    function dirty(target, key) {
    	const backedKey = `__${key}__`;
    	Object.defineProperty(target, key, {
    		get() {
    			return this[backedKey];
    		},
    		set(value) {
    			if (this[backedKey] !== value) {
    				this[backedKey] = createProxy(value, () => {
    					this.dirty();
    				});
    				this.dirty();
    			}
    		},
    		enumerable: true
    	});
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    经过对比前后两个项目打包后的结果我发现,老的打包结果如下

    function Student() {
    	...
    	this.name = '';
    	...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    而新的打包结果是

    function Student() {
    	Object.defineProperty(this, 'name', {});
    }
    
    • 1
    • 2
    • 3

    那么问题就找到了。前者在new时,会执行this.name = ‘’, 这样就会触发set函数,以后再修改实例的name属性时也会触发set。但是后者因为在构造里使用defineProperty给实例添加了name属性,那么以后再修改实例的name属性也不会执行原型链上的set,因为实例本身是有name属性的。下面就看怎么解决了。

    刚开始我是一头雾水,因为到此时其实我不知道react-scripts的项目打包时并不会用typescript进行转码的,因为我一直认为我创建项目时用了typescript的模板,而且我写代码也是按照typescript的预发写的,我就错误地认为也会使用typescript进行转码。后面是因为看了react-rescripts的源码中的webpack.config.js才知道。其实我感觉我这里说的不严谨,虽然webpack.config.js对于ts文件只用了babel-loader,但是应该不能说它没有用typescript转码,否则那么多ts语法是怎么转换的呢?

    我的解决方案是在webpack.config.js中添加ts-loader。众所周知,不能直接修改webpack.config.js文件,因为它在node_modules中。那么要怎么做呢?这一点难不倒聪明的开发者,已经有人提供了rescripts用来修改webpack的配置。前段时间我出于好奇浏览了一下rescripts的源码,对它的原理有所了解,感兴趣的小伙伴可以移步这里

    // 这是.rscriptsrc.js文件的内容
    module.exports = {
    	webpack: config => {
    		const { loader, options } = config.module.rules[1].oneOf[3];
    		config.module.rules[1].oneOf[3].use = [{ laoder, options}, 'ts-loader'];
    		delete config.module.rules[1].oneOf[3].loader;
    		delete config.module.rules[1].oneOf[3].options;
    		return config;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    不要忘记安装rescripts和ts-loader哈。至此问题就解决了。也让我对前端又害怕了一分,工程化这种东西太浪费精力了,而且解决之后也没有太大的成就感,琐碎的东西还挺多,有些时候现成的工具只能去巴拉源码,苦呀,还是自己能力不够啊!

    如果对你有帮助,请帮忙点赞哦,嘻嘻:)

  • 相关阅读:
    计算机毕业设计之java+springcloud基于vue的智慧养老平台-老人信息管理-敬老院管理系统
    459. 重复的子字符串(力扣LeetCode)
    PMP每日一练 | 考试不迷路-8.15(包含敏捷+多选)
    【BOOST C++】教程3: 数据类型
    重新理解RocketMQ Commit Log存储协议
    对程序员来说,技术能力和业务逻辑哪个更重要?
    tf.compat.v1.estimator.tpu.TPUEstimator参数说明
    联想Y9000P-2022款踩坑指南 加装固态和内存 win10系统安装
    计算机毕业设计ssm高校学科竞赛管理系统eolh8系统+程序+源码+lw+远程部署
    JAVA基础(JAVA SE)学习笔记(七)面向对象编程(进阶)
  • 原文地址:https://blog.csdn.net/Chinese521/article/details/126371768