博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【速记】借助ES6的模版字符串,在不用Babel插件的情况下实现一个轻量级类JSX功能...
阅读量:6242 次
发布时间:2019-06-22

本文共 5412 字,大约阅读时间需要 18 分钟。

引发此篇文章是看到了阮一峰的twitter在掘金上的转载: 

Preact 的作者创建的利用字符串标签模版实现的类JSX的库,可以将标签模版的类JSX字符串转化成类React.createElement或h函数(用来创建 virtual DOM对象的函数): 

ES6字符串介绍(重点阅读模版字符串和标签模版两节):

浏览器级别API将字符串解析成DOM对象:


原始的思考原文如下:

JSX Quasi-Literal

I've been struggling to get the JSX transpiler playing nicely with the traceur compiler, specifically the flags hidden behind --experimental.

The problem is that the since both the JSX transpiler and the traceur compiler are actually parsing the full javascript AST, they would have to mutually agree on the syntax extensions you use: traceur can't parse the faux-xml syntax JSX adds, and JSX can't parse the async or await keywords, for example, or generator functions.

This proof-of-concept is a potential solution: instead of using an external JSX transpiler, we'll parse the faux-xml ourselves, using an ES6 feature called quasi-literals.

Example

define(function(require) {    var React   = require('react');    var jsx     = require('lib/jsxquasi');    var EchoComponent = React.createClass({        getInitialState: function() {            return { value: '' };        },        handleChange: function() {            this.setState({ value: this.refs.input.getDOMNode().value });        },        render: function() {            return jsx`                
                   
                   ${this.state.value}                
           `;       }   })    return function() {        var comp = jsx`<${EchoComponent} />`;        React.renderComponent(comp, document.body);   };});

A couple of things to notice:

This is valid javascript! Or harmony or es6 or whatever, but importantly, it's not happening outside the js environment. This also allows us to use our standard tooling: the traceur compiler knows how to turn jsx<div>Hello</div>; into the equivalent browser compatible es3, and hence we can use anything the traceur compile accepts!

This is not exactly the same as JSX according to the spec: it includes quotes around the attributes, etc. This is because this parser is based on DOMParser, and hence needs to be valid XML. It would be straighforward though to change it so it matched exactly, or to remove the browser dependency (so it could run on the server, eg.)

index.js

define(function(require) {    var React = require('react');    var paramRegex  = /__(\d)+/;    var parser      = new DOMParser();    var errorDoc    = parser.parseFromString('INVALID', 'text/xml');    var errorNs     = errorDoc.getElementsByTagName("parsererror")[0].namespaceURI;    // turns the array of string parts into a DOM    // throws if the result is an invalid XML document.    function quasiToDom(parts) {            // turn ["
Hi
"]        // into "
Hi
"        var xmlstr = parts.reduce((xmlstr, part, i) => {            xmlstr += part;            if (i != parts.length - 1) { // the last part has no ending param                xmlstr += `__${i}`;           }            return xmlstr;       }, "");       // parse into DOM, check for a parse error       // browser's DOMParser is neat, but error handling is awful       var doc      = parser.parseFromString(xmlstr, 'text/xml');       var errors   = doc.getElementsByTagNameNS(errorNs, 'parsererror');       var error    = '';       if (errors.length > 0) {           error = errors[0].textContent.split('\n')[0];           throw `invalid jsx: ${error}\n${xmlstr}`;       }       return doc;   }    // turn a document into a tree of react components    // replaces tags, attribute values and text nodes that look like the param    // placeholder we add above, with the values from the parameters array.    function domToReact(node, params) {        var match;                // text node, comment, etc        if (node.nodeValue) {            var value = node.nodeValue.trim();            if (value.length === 0) {                return undefined;           }            match = value.match(paramRegex);            return match ? params[parseInt(match[1])] : value;       }        // node to get react for        // if the node name is a placeholder, assume the param is a component class        var reactNode;        match = node.localName.match(paramRegex)        reactNode = match ? params[parseInt(match[1])] : React.DOM[node.localName];                    // if we don't have a component, give a better error message        if (reactNode === undefined) {            throw `Unknown React component: ${node.localName}, bailing.`;       }        // attributes of the node        var reactAttrs = {};        for (var i = node.attributes.length - 1; i >= 0; i--) {            var attr = node.attributes[i];            reactAttrs[attr.name] = attr.value;            match = attr.value.match(paramRegex);            if (match) {                reactAttrs[attr.name] = params[parseInt(match[1])];           }       }        // recursively turn children into react components        var reactChildren = [];        for (var i = 0; i < node.childNodes.length; i++) {            var child = node.childNodes[i];            var reactChild = domToReact(child, params);            if (reactChild) {                reactChildren.push(reactChild);           }       }        return reactNode(reactAttrs, reactChildren);   }    return function jsx(parts, ...params) {        var doc     = quasiToDom(parts);        var react   = domToReact(doc.firstChild, params);        return react;   }});
你可能感兴趣的文章
CSS学习笔记(一)深入理解position属性和float属性
查看>>
xml入门
查看>>
python Flask框架mysql数据库配置
查看>>
[20150529]用户与用户组管理
查看>>
baidu__git_android
查看>>
ZC_源码编译真机烧写_20160424
查看>>
day26-UDP协议无粘包问题
查看>>
使用HTML5的十大原因
查看>>
转发:修饰符
查看>>
【转载】Linux下configure命令详细介绍
查看>>
图片中转站
查看>>
DSP c6678的启动方式
查看>>
【Linux】解决Android Stadio报错:error in opening zip file
查看>>
功能(一):添加影像服务图层
查看>>
选择伊始
查看>>
PHP中继承
查看>>
总结各种容器特点
查看>>
SQL Server高级查询
查看>>
13-Flutter移动电商实战-ADBanner组件的编写
查看>>
ubuntu 16.04 启用root用户方法
查看>>