1.0.0 • Published 5 years ago

babel-plugin-label-switch v1.0.0

Weekly downloads
2
License
ISC
Repository
github
Last release
5 years ago

babel-plugin-label-switch

一个根据labeled statement 标记语句的标识符进行选择性输出的插件。

适用场景: 单文件/单项目书写多平台代码,针对特定平台,在保留该平台代码的同时移除其他平台代码。

参数

  • prefix:一个用于识别标识符前缀的正则表达式。为了防止作用到其他标识语句,必须使用特定前缀区分,默认的前缀是case$_*
    • type: RegExp
    • default: /^case\$_/
  • map:一个标识符的映射表,用来决定哪些标记语句需要被移除。若某个标记语句的标识符存在该表里面,且其值为true,则保留标记语句的内容。否则整体移除
    • type: Object
    • default: {}

例子

  • babel 配置:
{
	"plugins": [
		[
			"babel-plugin-label-switch",
			{
				"prefix": /^myPrefix_/,
				"map": {
					"isNode": false,
					"isBrowser": true,
					"isWxProgram": false
				}
			}
		]
	]
}
  • 在 js 里面使用上述的标识符:
function request(method, url) {
	return new Promise((resolve, reject) => {
		myPrefix_isNode: {
			//node环境发起请求
			require('http')
				.request(
					url,
					{
						method
					},
					function() {
						resolve();
					}
				)
				.end();
		}

		myPrefix_isBrowser: {
			//浏览器环境发起请求
			var xhr = new XMLHttpRequest();
			xhr.open(method, url);
			xhr.onload = function() {
				resolve();
			};
			xhr.send();
		}

		myPrefix_isWxProgram: {
			//小程序环境发起请求
			wx.request({
				url,
				method,
				success: function() {
					resolve();
				}
			});
		}
	});
}
  • 经过 babel 转换后:
function request(method, url) {
	return new Promise((resolve, reject) => {
		var xhr = new XMLHttpRequest();
		xhr.open(method, url);
		xhr.onload = function() {
			resolve();
		};
		xhr.send();
	});
}

注意事项

不要对插件作用的标识语句使用 break/continue

不管当前代码块是保留还是移除,其标识符总会被移除,实际上标识符与其标识语句并不存在于 js 的运行时中,在编译阶段会被移除。

//bad
case$_isNode: for (let i = 0; i < 9; i++) {
	loop: for (let j = 0; j < 9; j++) {
		if (i == 1 && j == 1) {
			continue case$_isNode;
		}
	}
}

//good
case$_isNode: {
	loop1: for (let i = 0; i < 9; i++) {
		loop2: for (let j = 0; j < 9; j++) {
			if (i == 1 && j == 1) {
				continue loop1;
			}
		}
	}
}

作用域问题

  • 局部作用域变量 如果采用了形如label:{ statement }的标记语法,则需要注意一下作用域问题。代码经过插件转换后,会移除掉标记语法。若标记语法所标记内容存在let/const局部作用域变量,则需要谨慎检查这些变量是否与其他变量同名。

    const a = 10;
    case$_isNode: {
    	let a = 20;
    	console.log(a);
    }
    
    // ===>转换后:
    const a = 10;
    let a = 20; //<====Error!!!!
    console.log(a);
  • import 问题 js 语法规定,import必须在最外层的作用域里面执行,如果需要动态选择引入的文件,请使用require

  • export 问题 js 语法规定,export必须在最外层的作用域里面执行,因此,需要一些小小的技巧来规避这个问题

    export default (function() {
    	case$_isNode: {
    		let a = 20;
    		return a;
    	}
    	case$_isBrowser: {
    		let a = 20;
    		return a;
    	}
    })();

在 typescript 中使用

在 ts 中的使用与 js 类型,需要注意的是,ts 的引入与 js 有些许不同:

import * as React from 'react';
import * as TReactDOM from 'react-dom';
import * as TReactDOMServer from 'react-dom/server/';
import App from './app';
export default (function(){
	case$_isBrowser:{
		const ReactDOM:typeof TReactDOM=require('react-dom');
		ReactDOM.render(<App/>,document.getElementById("app"));
	}
	
	case$_isNode:{
		const ReactDOMServer:typeof TReactDOMServer=require('react-dom/server/');
		const str = ReactDOMServer.renderToString(<App/>);
		return str
	}
})();

在上述代码中,TReactDOMTReactDOMServer分别来自@types/react-dom@types/react-dom/server/,仅仅是引入typescript的类型文件。真正的引入来自于require语句,这样既保留了ts的类型,也实现了动态引入。