您当前的位置:网站首页>九重紫,懂编译真的能够随心所欲|不同前端框架下的代码转化,帝鳄

九重紫,懂编译真的能够随心所欲|不同前端框架下的代码转化,帝鳄

2019-04-06 21:53:44 投稿作者:admin 围观人数:304 评论人数:0次

布景

整个前端范畴在这几年迅速发展,前端结构也在不断改变,各团队挑选的解决方案都不太共同,此外像小程序这种跨端场景和以往的研制办法也不太相同。在日常开发中往往会由于投进coursera渠道的不相同需求进行从头编码。前段时间咱们需求在淘宝页面上投进闲鱼组九重紫,懂编译真的能够为所欲为|不同前端结构下的代码转化,帝鳄件,淘宝前端研制DSL首要是React(Rax),而闲鱼前端之前研制DSL首要是Vue(Weex),一般这种状况咱们都是从头用React开发,有没有办法一键将已有的Vue组件转化为React组件呢,闲鱼技能团队从代码编译的视点提出了一种解决方案。

编译器是怎么作业的

日常作业中咱们触摸最多的编译器便是Babel,Babel能够将最新的Javascript语法编译成当时浏览器兼容的JavaScript代码,Babel作业流程分为三个进程,由下图所示:

懂编译真的能够为所欲为|不同前端结构下的代码转化

笼统语法树AST是什么

在计算机科学中,笼统语法树(Abstract Syntax Tree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种笼统标明。它以树状的办法体现编程言语的语法结构,树上的每个节点都标明源代码中的一种结构,详见维基百科。这儿以const a = 1转成var a = 1操作为例看下Babel是怎么作业的。

将代码解析(parse)成笼统语法树AST

Babel供给了@babel/parser将代码解析成AST。

const parse = require('@babel/parser').parse;
const ast = parse('const a = 1');

经过遍历和剖析转化(transform)对AST进行处理

Babel供给了@babel/traverse对解析后的AST进行处理。@babel/traverse能够接纳AST以及visitor两个参数,AST是上一步parse得到的笼统语法树,visitor供给拜访不同节点的才能,当遍历到一个匹配的节点时水晶鞋,能够调用详细办法关于节点进行处理。@babel/types用于界说AST节点,在visitor里做节点处理的时分用于替换等操作。在这个比方中,咱们遍历上一步得到的AST,在匹配到变量声明(VariableDeclaration)的时分判别是否const操作时进行替换成var。t.variableDeclaratiocctv5节目表预告n(kind, declarations)接纳两个参数kind和declarations,这儿kind设为var,将const a = 1解析得到的AST里的declarations直接设置给declarations。

const traverse = require('@babel/traverse').default;
const t = require('@babel/types');
traverse(ast, {
VariableDeclaration: function(path) { //识别在变量雷双富声明的时分
if (path.node.kind === 'const') { //只要const的时分才处理
path.replaceWith(
t.variableDeclaration('var', path.node.declarations) //替换成var
);
}寿县气候
path.skip();
}
});

将终究转化的AST从头生成(generate)代码

Babel供给了@babel/generator将AST再还原成代码。

const generate = require('@babel/generator').default;
let code = generate(ast).code;

Vue和React的异同

咱们来看下Vue和React的异同,假如需求做转化需求有哪些处理,Vue的结构分为style、script、template三部分

style

款式这部分不必去做特别的转化,Web下都是通用的

script

Vue某些特点的称号和React不太共同,可是功能上是类似的。例如data需求转化为state,props需求转化为defaultProps和propTypes,components的引证需求提取到组件声明以外,methods里的办法需求提取到组件的特点上。还有一些特点比较特别,比方computed,React里是没有这个概念的,咱们能够考虑将computed里的值转化成函数办法,上面示例中的length,能够转化为length()这样的函数调用,在React的render()办法以及其他办法中调用。

Vue的生命周期和React的生命周期有些不同,可是根本都能映射上,下面列举了部分生命周期的映射

  • created -> componentWillMount
  • mounted -> componentDidMount
  • updated -> componentDidUpdate
  • beforeDestroy -> componentWillUnmount
  • 在Vue内函数的特点取值是经过this.xxx的办法,而在Rax内需求判别是否state、props仍是详细的办法,会转化成this.state、this.props或许this.xxx的办法。因而在对Vue特别特点的处理中,咱们关于data、props、method九重紫,懂编译真的能够为所欲为|不同前端结构下的代码转化,帝鳄s需求额定做符号。

template

针对文本节点和元素节点处理不共同,文本节点需求对内容{{title}}进行处理,变为{title}

Vue里有许多的增强指令,转化成React需求额定做处理,下面列举了部分指令的处理办法

  • 事情绑定的处理,@click -> onClick
  • 逻辑判别的处理,v-if="item.show" -> {item.show && ……}
  • 动态参数的处理,:title="title" -> title={title}

还有一些是正常的html特点,可是React下是不相同的,例如style -> className。

指令里和model里的特点值需求特别处理,这部分的逻辑其实和script里相同,例如需求{{title}}转变成{this.props.title}

Vue代码的解析

以下面的Vue代码为例



.title {font-size: 28px;color: #333;}
.name {font-size: 32px;color: #999;}


咱们需求先解析Vue代码变成AST值。这儿使用了Vue官方的vue-template-compiler来别离提取Vue组件代码里的template、style、script,考虑其他DSL的通用性后续能够迁移到愈加适用的html解析模块,例如parse5等。经过require('vue-template-compiler').p人肉叉烧包II之不得善终arseComponent得到了别离的template、style、script。style不必额定解析成AST了,能够直接用于React代码。template能够经过require('vue-template-compiler').compile转化为AST值。script用@babel/parser来处理,关于script的解析不仅仅需求取得整个script的AST值,还需求别离将data、props、computed、components、methods等参数提取出来,以便后边在转化的时分区别详细归于哪个特点。以data的处理为例:

const traverse = require('@babel/traverse').default;
const t = requi九重紫,懂编译真的能够为所欲为|不同前端结构下的代码转化,帝鳄re('@babel/types');
const analysis = (body, data, isObject) => {
data._statements = [].concat(body); // 整个表达式的AST值

let propNodes = [];
if (isObject) {
propNodes = body;
} else {
body.forEach(child => {
if (t.isReturnStatement(child)) { // return表达式的时分
propNodes = child.argument.properties;
data._statements = [].concat(child.argument.properties);大公报 // 整个表达式的AST值
}
});
}

propNodes.forEach(propNode => {
data[propNode.k周五气候ey.n名门闺秀在现代ame] = propNode; // 对data里的值进行提取,用于后续的特点取值
});
};
const parse = (ast) => {
let data = {
};
traverse(ast, {
ObjectMethod(path) 伊春{
/*
目标办法
data() {return {}庶人坊}
*/
const parent = path.parentPath.parent;
const name = path.node.ke圣人重返都市y.name;

if (parent && t.isExportDefaultDeclaration(parent)) {
if (name === 'data') {
const body = path.node.body.body;

analysis(body, data);
path.stop();
}
}
},
ObjectProperty(path) {
/*
目标特点,箭头函数
data: () => {return {}}
data: () => ({})
*/
const parent = path.parentPath.parent;
const name = path.node.key.name;

if (parent && t.isExportDefaultDeclaration(parent)) {
if (name === 'data') {
const node = path.node.value;

if (t.isArrowFunctionExpression(node)) {
/*
箭头函数
() => {return {}}
() => {}
*/
if (node.body.body) {
analysis(node.body.body, data);
} else if (node.body.properties) {
analysis(node.body.properties, data, true);
}
}
path.stop();
}
}
}
});
/*
终究得到的成果
{
_statements, //data解析AST值
list //data.list解析AST值
}
*/
return data;
};
module.exports = parse;

终究处理之后得到这样一个结构:

app: {
script: {
ast,
components,
computed,
data: {
_statements, //data解析AST值
list //data.list解析AST值
},
props,
methods
},
style, // style字金枝符串值
template: {
ast // template解析AST值
}
}

React代码的转化

终究转化的React代码会包括两个文件(css和js文件)。用style字符串直接生成index.css文件,index.js文件结构如下图,transform指将Vue AST值转化成React代码的伪函数。

import { createElement, Component, PropTypes } from 'React';
import './index.css';
export default class Mod extends Component {
${transform(Vue.script)}
render() {
${transform(Vue.template)}
}
}

script AST值的转化纷歧一阐明,思路根本都共同,这儿首要针对Vue data继续阐明怎么转化成React state,终究解析Vue data得到的是{_statements: AST}这样的一个结构,转化的时分只需求履行如下代码

const t = require('@babel/t国际之窗浏览器ypes');
module.exports = (app) => {
if (app.script.data && app.script.data._statements) {
// classProperty 类特点 identifier 标识符 objectExpression 目标表达式
return t.classProperty(t.identifier('state'), t.objectExpression(app.script.data._statements));
} else {
return null;
}
};

针对template AST值的转化,咱们先看下Vue template AST的结构:

{
tag: 'div',
children: [{
tag: 'text'
},{
tag: 'div',
children: [……]
}]
}

转化的进程便是遍历上面的结构针对每一个节点生成烘托代码,这儿以v-if的处理为例阐明下节点特点的处理,实践代码中会有两种状况:

  • 不包括v-else的状况,
    转化为{ xxx &&
    }
  • 包括v-else的状况,
    转化为{ xxx ?
    : }

经过vue-template-compiler解析后的template AST值里会包括ifConditions特点值,假如ifConditions的长度大于1,标明存在v-else,详细处理的逻辑如下:

if (ast.ifConditions && ast.ifConditions.length > 1) {
// 包括v-else的状况
let leftBlock = ast.ifConditions[0].block;
let rightBlock = ast.ifConditions[1].block;
let left = generatorJSXElement(leftBlock); //转化成JSX元素
let right = generatorJSXElement(rightBlock); //转化成JSX元素

child = t.jSXExpressionContainer( //JSX表达式容器
// 转化成条件表达式
t.conditionalExpression(
parseExpression(value),
left,
right
)
);
} else {
// 不包括v-else的状况
child = t.jSXExpressionContainer( //JSX表达式容器
// 转化成逻辑表达式
t.logicalExpression('&&', parseExpression(value), t.jsxElement(
t.jSXOpeningElement(
t.jSXIdentifier(tag), attrs),
t.jSXClosingElement(t.jSXIdentifier(tag)),
children
))
);
}

template里引证的特点/办法提取,在AST值体现上都是标识符(Identifier),能够在tr那个averse的时分将Identifier提取出来。这儿用了一个比较取巧的办法,在template AST值转化的时分咱们不对这些标识符做判别,而在终究转化的时分在render return之金铁霖前刺进一段引证。以下面的代码为例

{{title}}
list length:{{length}}

{{item.text}}九重紫,懂编译真的能够为所欲为|不同前端结构下的代码转化,帝鳄

咱们能解分出template里的特点/办法以下面这样一个结构标明:

{
title,
handleClick,
length,
list,
item,
index
}

在转化代码的时分将它与app.sc斯巴达ript.data、app.script.props、app.script.computed和app.script.computed别离比照判别,能得到title是props、list是state、handleClick是methods,length是computed,终究咱们在return前面刺进的代码如下:

let {title} = this.props;
let {state} = this.state;
let {handleClick} = this;
let lengt九重紫,懂编译真的能够为所欲为|不同前端结构下的代码转化,帝鳄h = this.length();

终究示例代码的转化成果

import { createElement, Component, PropTypes } from 'React';
export default class Mod extends Compone九重紫,懂编译真的能够为所欲为|不同前端结构下的代码转化,帝鳄nt {
static defaultProps = {
title: 'title'
}
static propTypes = {
titl比基尼图片e: PropTypes.string
}
state = {
show: true,
name: 'name'
}
componentDidMount() {
let {name} = this.state;
console.log(name);
}
handleClick() {}
render() {
let {title} = this.props;
let {show, name} = this.state;
let {handleClick} = this;

return (

{title}


{show && (

{name}


)}

);
}
}

总结与展望

本文从Vue组件转化为React组件的详细事例叙述了一种经过代码编译的办法进行不同前端结构代码的转化的思路。咱们在出产环境中现已将十多个之前的Vue组件直接转成React组件,可是实九重紫,懂编译真的能够为所欲为|不同前端结构下的代码转化,帝鳄际使用进程中研制同学的编码习气不同也比较大,需求处理许多特别状况。这套思路也能够用于小程序互转等场景,削减编码的重复劳动,可是在这类跨端的非保准Web场景需求考虑更多,例如小程序环境特有的组件以及API等,闲鱼技能团队也会继续在这块做测验。

作者:闲鱼技能-玉缜

the end
各种卧室布置方案,设计师的世界