AST大法--解析reactjs为原生js

478 阅读1分钟

一边享受着react的各种舒畅,一边在一些简单的项目时,你不得不做出取舍,用原生+js模板引擎(预编译的那种)来实现尽量小体积与快速渲染。

那么是否有办法鱼和熊掌都兼得呢。基于这个想法,这里写了个小小的webpack loader,实现把简单的reactjs转成原生js,一方面你可以继续用着你喜欢的react,另一方面可以保持你项目的小体积(不需要引入任何react的库),同时还可以享受高性能(因为是原生js比较精简的逻辑,缓存、执行复杂度都是比较低的)。

安装

npm i p-jsx-loader

webpack中配置

// 这只是一个简单实例,请根据自己需要进行调整
module.exports = {
  entry: './index.js',
  mode: "development",
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name][hash].bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.jsx$/,
        loader: 'p-jsx-loader'
      }
    ]
  }
}

实例

  • 老规矩,第一个例子,先上hello wolrd:
// 源码
export default function (props) {
  return <div>hello world!</div>
}

// 如果是react,@babel/plugin-transform-react-jsx转换后
export default function (props) {
  return React.createElement("div", null, "hello world!");
}

// p-jsx-loader转换后
export default function (props) {
  return '<div>hello world!</div>';
}
  • 带变量的例子
// 源码
export default function (props) {
  return <div>{props.name}</div>
}

// 如果是react,@babel/plugin-transform-react-jsx转换后
export default function (props) {
  return React.createElement("div", null, props.name);
}

// p-jsx-loader转换后
export default function (props) {
  return '<div>'+(props.name)+'</div>';
}
  • 带逻辑的例子
// 源码
export default function (props) {
  let list = props.list
  if (list.length == 0) {
    return <div>暂无数据</div>
  }
  return list.map(function (item, index) {
    return <div>
      <span>任务{index}</span>
      <span>{item.hasDone ? "已完成" : "未完成"}</span>
    </div>
  }).join('')
}

// 如果是react,@babel/plugin-transform-react-jsx转换后
export default function (props) {
  let list = props.list;

  if (list.length == 0) {
    return React.createElement("div", null, "\u6682\u65E0\u6570\u636E");
  }

  return list.map(function (item, index) {
    return React.createElement("div", null, React.createElement("span", null, "\u5F53\u524D\u5E8F\u53F7\u4E3A\uFF1A", index), React.createElement("span", null, item.hasDone ? "已完成" : "未完成"));
  }).join('');
}

// p-jsx-loader转换后
export default function (props) {
  let list = props.list;

  if (list.length == 0) {
    return "<div>\u6682\u65E0\u6570\u636E</div>";
  }

  return list.map(function (item, index) {
    return "<div><span>\u4EFB\u52A1"+(index)+"</span><span>"+(item.hasDone ? "已完成" : "未完成")+"</span></div>";
  }).join('');
}
  • 带事件监听的例子
// 源码
function sayHi (event) {
  let { name } = event.target.dataset
  alert(`Hi,i am ${name}. Nice to meet you!`)
}
export default function (props) {
  return <button data-name={props.name} onClick={sayHi}>say hello</button>
}

// 如果是react,@babel/plugin-transform-react-jsx转换后
function sayHi(event) {
  let {
    name
  } = event.target.dataset;
  alert(`Hi,i am ${name}. Nice to meet you!`);
}

export default function (props) {
  return React.createElement("button", {
    "data-name": props.name,
    onClick: sayHi
  }, "say hello");
}

// p-jsx-loader转换后
function sayHi(event) {
  let {
    name
  } = event.target.dataset;
  alert(`Hi,i am ${name}. Nice to meet you!`);
}

export default function (props) {
  return "<button data-name=\""+(props.name)+"\" onclick=\"window.jsx_loader_event__0sayHi(event)\">say hello</button>";
}
window.jsx_loader_event__0sayHi = sayHi;
  • 带组件的例子
// 源码,main.jsx
import Sub from './sub.jsx'

export default function (props) {
  return <div>
    <p>你好</p>
    welcome to:<Sub name={props.name} cls={props.cls} />
  </div>
}
// 源码,sub.jsx
import './index.css'

export default function (props) {
  return <span className={props.cls}>{props.name}</span>
}

// 如果是react,@babel/plugin-transform-react-jsx转换后,main.jsx
import Sub from './sub.jsx';
export default function (props) {
  return React.createElement("div", null, React.createElement("p", null, "\u4F60\u597D"), "welcome to\uFF1A", React.createElement(Sub, {
    name: props.name,
    cls: props.cls
  }));
}
// 如果是react,@babel/plugin-transform-react-jsx转换后,sub.jsx
import './index.css';
export default function (props) {
  return React.createElement("span", {
    className: props.cls
  }, props.name);
}

// p-jsx-loader转换后,main.jsx
import Sub from './sub.jsx';
export default function (props) {
  return "<div><p>\u4F60\u597D</p>welcome to\uFF1A"+(Sub({"name":""+(props.name)+"","cls":""+(props.cls)+""}))+"</div>";
}
// p-jsx-loader转换后,sub.jsx
import './index.css';
export default function (props) {
  return "<span class=\""+(props.cls)+"\">"+(props.name)+"</span>";
}

觉得还行的话,请麻烦给我点个赞,感谢!