vue xml转字符串_从零开始写一个 wepy 转 Vue 的工具

(给前端大全加星标,提升前端技能)

转自:大转转FE/张所勇

为什么需要 wepy 转 VUE

“转转二手”是我司用 wepy 开发的功能与 APP 相似度非常高的小程序,实现了大量的功能性页面,而新业务 H5 项目在开发过程中有时也经常需要一些公共页面和功能,但新项目又有自己的独特点,这些页面需求重新开发成本很高,但如果把小程序代码转换成 VUE 就会容易的多,因此需要这样一个转换工具。

本文将通过实战带你体验 HTML、css、JavaScript 的 AST 解析和转换过程

如果你看完觉得有用,请点个赞~

AST 概览

AST 全称是叫抽象语法树,网络上有很多对 AST 的概念阐述和 demo,其实可以跟 XML 类比,目前很多流行的语言都可以通过 AST 解析成一颗语法树,也可以认为是一个 JSON,这些语言包括且不限于:CSS、HTML、JavaScript、PHP、Java、SQL 等,举一个简单的例子:

var a = 1;

这句简单的 JavaScript 代码通过 AST 将被解析成一颗“有点复杂”的语法树:

62279ef92731cd858e05e27bae49f39c.png

这句话从语法层面分析是一次变量声明和赋值,所以父节点是一个 type 为 VariableDeclaration(变量声明)的类型节点,声明的内容又包括两部分,标识符:a 和 初始值:1

b76f08a16faab43c009efd27338a70fa.png

这就是一个简单的 AST 转换,你可以通过 astexplorer(https://astexplorer.net/)可视化的测试更多代码。

AST 有什么用

AST 可以将代码转换成 JSON 语法树,基于语法树可以进行代码转换、替换等很多操作,其实 AST 应用非常广泛,我们开发当中使用的 less/sass、eslint、TypeScript 等很多插件都是基于 AST 实现的。

本文的需求如果用文本替换的方式也可能可以实现,不过需要用到大量正则,且出错风险很高,如果用 AST 就能轻松完成这件事。

AST 原理

AST 处理代码一版分为以下两个步骤:

词法分析

词法分析会把你的代码进行大拆分,会根据你写的每一个字符进行拆分(会舍去注释、空白符等无用内容),然后把有效代码拆分成一个个 token。

语法分析

接下来 AST 会根据特定的“规则”把这些 token 加以处理和包装,这些规则每个解析器都不同,但做的事情大体相同,包括:

  • 把每个 token 对应到解析器内置的语法规则中,比如上文提到的 var a = 1;这段代码将被解析成 VariableDeclaration 类型。

  • 根据代码本身的语法结构,将 tokens 组装成树状结构。

各种 AST 解析器

每种语言都有很多解析器,使用方式和生成的结果各不相同,开发者可以根据需要选择合适的解析器。

JavaScript

  • 最知名的当属 babylon,因为他是 babel 的御用解析器,一般 JavaScript 的 AST 这个库比较常用

  • acron:babylon 就是从这个库 fork 来的

HTML

  • htmlparser2:比较常用

  • parse5:不太好用,还需要配合 jsdom 这个类库

CSS

  • cssom、csstree 等

  • less/sass

XML

  • XmlParser

wepy 转 VUE 工具

接下来我们开始实战了,这个需求我们用到的技术有:

  • node

  • commander:用来写命令行相关命令调用

  • fs-extra:fs 类库的升级版,主要提高了 node 文件操作的便利性,并且提供了 Promise 封装

  • XmlParser:解析 XML

  • htmlparser2:解析 HTML

  • less:解析 css(我们所有项目统一都是 less,所以直接解析 less 就可以了)

  • babylon:解析 JavaScript

  • @babel/types:js 的类型库,用于查找、校验、生成相应的代码树节点

  • @babel/traverse:方便对 JavaScript 的语法树进行各种形式的遍历

  • @babel/template:将你处理好的语法树打印到一个固定模板里

  • @babel/generator:生成处理好的 JavaScript 文本内容

转换目标

我们先看一段简单的 wepy 和 VUE 的代码对比:

//wepy版

class="userCard">

class="basic">

class="avatar">

"{ {info.portrait}}">

class="info">

class="name">{ {info.nickName}}

class="label" wx:if="{ {info.label}}">

class="label-text" wx:for="{ {info.label}}">{ {item}}

class="onsale">在售宝贝{ {sellingCount}}

class="follow " @tap="follow">{ {isFollow ? '取消关注' : '关注'}}

template>

rel="stylesheet/less" scoped>

.userCard {

position:relative;

background: #FFFFFF;

box-shadow: 0 0 10rpx 0 rgba(162,167,182,0.31);

border-radius: 3rpx;

padding:20rpx;

position: relative;

}

/* css太多了,省略其他内容 */

import wepy from 'wepy'

export default class UserCard extends wepy.component {

props = {

info:{

type:Object,

default:{}

}

}

data = {

isFollow: false,

}

methods = {

async follow() {

await someHttpRequest() //请求某个接口

this.isFollow = !this.isFollow

this.$apply()

}

}

computed = {

sellingCount(){

return this.info.sellingCount || 1

}

}

onLoad(){

this.$log('view')

}

}

//VUE版

class="userCard">

class="basic">

class="avatar">

"info.portrait">

class="info">

class="name">{ {info.nickName}}

class="label" v-if="info.label">

class="label-text" v-for="(item,key) in info.label">{ {item}}

class="onsale">在售宝贝{ {sellingCount}}

class="follow " @click="follow">{ {isFollow ? '取消关注' : '关注'}}

template>

rel="stylesheet/less" scoped>

.userCard {

position:relative;

background: #FFFFFF;

box-shadow: 0 0 10rpx 0 rgba(162,167,182,0.31);

border-radius: 3*@px;

padding:20*@px;

position: relative;

}

/* css太多了,省略其他内容 */

export default {

props : {

info:{

type:Object,

default:{}

}

}

data(){

return {

isFollow: false,

}

}

methods : {

async follow() {

await someHttpRequest() //请求某个接口

this.isFollow = !this.isFollow

}

}

computed : {

sellingCount(){

return this.info.sellingCount || 1

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值