日常开发中通常会有一些需求,需要定义一些全局通用的组件,在 Vue 里是有这样的功能,但是在 React 里,没有见过有类似的做法,通常都是在需要的时候引入组件,原则上在Jsx
里只允许html
的文本标签以及function
类型的标签,本文的做法只做参考
背景
最近在考虑做国际化,但是系统构建之初,产品并没有做国际化的计划,系统开发上线以后,又有了国际化的需求,但是系统已经上线,这时候在去用 react-i18n
之类的方案太重了,所有的页面、组件都要引用这个库,需要国际化的地方都需要修改,同时要维护语言的映射文件,工作量太大了,所有考虑一种更好的方案,思考如何能无侵入的实现国际化,这里的构建自定义标签的目的是希望通过标签收集系统文字。
示例代码:
render(){
return (
<div>
<local>需要翻译的文字</local>
<span translate="yes">需要翻译的文字</span>
</div>
)
}
所有 local
标签或者加了 translate="yes"
的标签里的文字都需要被翻译,translate
是 HTML5 的新属性,这里借用它。
自定义标签有哪些方案
编译期转换
一般通过编译期转换是可以做到的,但是需要开发相关 webpack loader
,调整生成的代码,这么做比较麻烦。
运行期实现
运行期做的话,可以在 render 里拿到虚拟DOM子节点,遍历,找到自定义的标签,然后特殊处理,返回新的虚拟 DOM,之前做了一个表单的方案,就是用这种方案来做的。
另一种做法,最近想到的,也是本文的重点,就是重写 React.createElement
,重写 React.createElement
这个操作在某些场景还是挺有用的,
这些操作在 React 开发下都是非常规操作,让代码看起来有点儿怪,但是用起来还是比较香的。
自定义标签
参考实现:
function createTag(React, tags: ITags = {}) {
if (!tags) {
return;
}
const createElement = React.createElement;
React.createElement = function (type, props, ...children) {
if (tags[type]) {
return createElement(tags[type], props, ...children);
}
return createElement(type, props, ...children);
}
}
在应用的入口处使用:
import React, { Component } from 'react';
const Local=(props)=>{
// 各种操作
return props.children;
}
createTag(React,{
'local':Local
})
这样,就可以在应用的任何地方使用 local
标签了,比如可以自定义一个 loading
标签。
这里不仅是加了一个文本标签,同时也接管里这个标签的内部实现逻辑,也可以有自己的状态,和下面的代码还是有区别的,下面的代码是动态修改 type 参数。
const Wrap = ({ tagName, content }) => {
const Tag = `${tagName}` // variable name must begin with capital letters
return <Tag>{content}</Tag>
}
自定义属性
这里是对虚拟DOM做了过滤,如果有对应的属性,那么执行回调,做相关的操作。
function createAttribute(React, attribute, callback) {
if (!attribute) {
return;
}
const createElement = React.createElement;
React.createElement = function (type, props, ...children) {
if (props && typeof props[attribute] !== 'undefined') {
return callback(type, props, ...children);
}
return createElement(type, props, ...children);
}
}
使用:
import React, { Component } from 'react';
createAttribute(React,'translate',(type,props,children)=>{
// 各种操作
return (
<span>
{children}
</span>
)
})
这样,应用里所有加了 translate
属性标签,都会被收集到,返回的内容就可以自定义了。
看起来挺有意思吧。。。
总结
本文通过重写 React.createElement 实现全局的标签和属性定义,某些场景下是挺有意义的,挺有意思,在这分享一下。
补一条招聘信息,团队正在招人,我们是58集团招聘事业部前端团队,主要是做招聘行业的sass系统,内部也有些有意思的项目,有意向的话可以私信我或发简历到 lihongyin@58.com