Notice: Undefined index: seotitled in /www/wwwroot/www.wap.openlaihk.cn/wechat-details.php on line 16
久久一本精品久久精品66,日韩精品无码区免费专区,高潮又爽又黄又无遮挡免费

国语自产少妇精品视频蜜桃,国产日韩欧美视频网址,女人摸下面自熨视频在线播放,婷婷色香合缴缴情av第三区

通過一個需求揭秘多端編譯

發(fā)布日期:2020-09-05  瀏覽:2109 

過年在家辦公期間,接到了一個需求,需要將目前的 微信小程序自定義組件 擴展到 支付寶小程序 平臺。關(guān)于需求的背景和歷史這邊就暫不多說了,就從上面已說明的內(nèi)容來看待這個需求吧。 接到需求的第一時間,筆者就思考,這不就是多端編譯嗎?話不多說,那就開搞吧。

背景介紹

由于筆者的項目是一個單純的微信小程序自定義組件,打包工具是rollup,所以,筆者的技術(shù)方案是編寫一個rollup插件,來支持多端編譯。關(guān)于rollup和rollup插件的寫法本次不作過多介紹,有興趣的可以看它的 官方文檔 ,這邊只是介紹一下核心的多端編譯流程。

流程介紹

微信小程序組件包含 *.json 、 *.js 、 *.wxml 、 *.wxss 這4個文件,要轉(zhuǎn)換成支付寶小程序,其中json文件和wxss文件比較簡單,前者原封不動,后者改一下后綴名就好了,主要要修改js和wxml兩個文件。

大致流程基本就是如下

差異整理

將代碼轉(zhuǎn)成AST樹

替換樹上的節(jié)點

根據(jù)新的AST樹生成代碼

acorn

對于js文件,要實現(xiàn)這些功能的話,業(yè)界已經(jīng)有一些出色的工具了。筆者選擇了 babel ,babel內(nèi)置acron作為javascript解釋器,生成符合estree標準的AST樹(可以在 astexplorer.net/ 中查看效果)。其次babel的封裝很漂亮,除了搭配webpack完成日常的構(gòu)建工作外,它還提供了 @babel/parser , @babel/generator , @babel/traverse , @babel/types 等優(yōu)秀的工具包,每個工具包都是單一職責,職責很明確,幫助實現(xiàn)以上的流程(其實rollup內(nèi)置了acron實例,不過babel會更好用一些)。 其中 @babel/parser 可以將js代碼解釋為AST樹, @babel/generator 將根據(jù)AST樹生成js代碼, @babel/traverse 支持高效地操作AST樹的節(jié)點, @babel/types 則提供一些判斷函數(shù),幫助開發(fā)者快速定位節(jié)點。

看一個簡單的示例

function sayHello() {

  console.log('hello')

}

 

sayHello();

復制代碼

對于以上這段代碼,通過acron轉(zhuǎn)換后,得出的AST樹如下

{

  "type": "Program",

  "start": 0,

  "end": 58,

  "body": [

    {

      "type": "FunctionDeclaration",

      "start": 0,

      "end": 45,

      "id": {

        "type": "Identifier",

        "start": 9,

        "end": 17,

        "name": "sayHello"

      },

      "expression": false,

      "generator": false,

      "async": false,

      "params": [],

      "body": {

        "type": "BlockStatement",

        "start": 20,

        "end": 45,

        "body": [

          {

            "type": "ExpressionStatement",

            "start": 23,

            "end": 43,

            "expression": {

              "type": "CallExpression",

              "start": 23,

              "end": 43,

              "callee": {

                "type": "MemberExpression",

                "start": 23,

                "end": 34,

                "object": {

                  "type": "Identifier",

                  "start": 23,

                  "end": 30,

                  "name": "console"

                },

                "property": {

                  "type": "Identifier",

                  "start": 31,

                  "end": 34,

                  "name": "log"

                },

                "computed": false

              },

              "arguments": [

                {

                  "type": "Literal",

                  "start": 35,

                  "end": 42,

                  "value": "hello",

                  "raw": "'hello'"

                }

              ]

            }

          }

        ]

      }

    },

    {

      "type": "ExpressionStatement",

      "start": 47,

      "end": 58,

      "expression": {

        "type": "CallExpression",

        "start": 47,

        "end": 57,

        "callee": {

          "type": "Identifier",

          "start": 47,

          "end": 55,

          "name": "sayHello"

        },

        "arguments": []

      }

    }

  ],

  "sourceType": "module"

}

復制代碼

對于這段js代碼,如果要替換它的方法名為 sayHi 、打印出的 hello 替換為 Hi ,通過babel,只需要這樣做就可以了。

import { parse } from "@babel/parser";

import traverse from "@babel/traverse";

import generate from "@babel/generator";

import * as t from "@babel/types";

 

const code = `

function sayHello() {

  console.log('hello')

}

 

sayHello();

`;

 

const transform = code => {

  const ast = parse(code);

  traverse(ast, {

    enter(path) {

      if (t.isIdentifier(path.node, { name: "sayHello" })) {

        path.node.name = "sayHi";

      }

      if (t.isLiteral(path.node, { value: "hello" })) {

        path.node.value = "Hi";

      }

    }

  });

  const output = generate(ast, {}, code);

  return output;

};

 

console.log(transform(code).code);

復制代碼

也可以在 codeSandbox 中查看效果。

關(guān)于包的其它使用,可以查看 官方手冊 。

himalaya

對于wxml文件,筆者選擇了 himalaya-wxml ,它提供了 parse 和 stringify 兩個方法,前者將wxml解釋成AST樹,后者反之(可以在 jew.ski/himalaya/ 中查看效果)。通過 parse 將wxml代碼轉(zhuǎn)換成AST樹之后,接下去只需要手動遞歸遍歷AST樹去替換節(jié)點,再將其轉(zhuǎn)換回wxml代碼就可以完成工作了。

同樣,看一個簡單的示例

<div id='main'>

  <span>hello world</span>

</div>

復制代碼

對于以上html代碼,通過 himalaya 轉(zhuǎn)換后,生成的AST樹如下

[

  {

    "type": "element",

    "tagName": "div",

    "attributes": [],

    "children": [

      {

        "type": "text",

        "content": "\n  "

      },

      {

        "type": "element",

        "tagName": "span",

        "attributes": [],

        "children": [

          {

            "type": "text",

            "content": "hello world"

          }

        ]

      },

      {

        "type": "text",

        "content": "\n"

      }

    ]

  }

]

復制代碼

對于這段代碼html代碼,如果要替換它外層 div 的 id 為 container ,只需要這樣做就可以了。

import { parse, stringify } from "himalaya";

 

const code = `

<div id='main'>

  <span>hello world</span>

</div>

`;

 

const traverse = ast => {

  return ast.map(item => {

    if (item.type === "element" && item.attributes) {

      return {

        ...item,

        attributes: item.attributes.map(attr => {

          if (attr.key !== "id") {

            return attr;

          }

          return {

            ...attr,

            value: "container"

          };

        })

      };

    }

    return item;

  });

};

 

const transform = code => {

  const ast = parse(code);

  const json = traverse(ast);

  return stringify(json);

};

 

console.log(transform(code));

復制代碼

也可以在 codeSandbox 中查看效果。

核心介紹

流程和工具介紹的差不多了,接下來就開始正題吧。 首先是整理差異,根據(jù)筆者的調(diào)研,微信小程序組件要轉(zhuǎn)換成支付寶小程序組件,大致有以下幾個改動(只是符合筆者的需求,如果不完全,歡迎補充):

wxml后綴名要改成axml

wxss后綴名要改成acss

wxml中的屬性wx-xxx要改成a-xxx

wxml中的事件屬性bindxxx要改成onXxx

生命周期attached要替換成onInit

生命周期detached要替換成didUnmount

生命周期pageLifetimes.show要替換成didMount

生命周期pageLifetimes要刪除

改后綴名的工作相對簡單,交給構(gòu)建工具,output配置里面指定一下就好了,重點是替換屬性。

轉(zhuǎn)換js部分代碼如下

import { parse } from '@babel/parser';

import traverse from '@babel/traverse';

import generate from '@babel/generator';

import * as t from '@babel/types';

 

function transformJs(code: string) {

  const ast = parse(code);

  let pp;

 

  traverse(ast, {

    enter(path) {

      if (t.isIdentifier(path.node, {name: 'attached'})) {

        path.node.name = 'onInit';

      }

      if (t.isIdentifier(path.node, {name: 'detached'})) {

        path.node.name = 'didUnmount';

        pp = path.parentPath;

      }

      if(t.isIdentifier(path.node.key, {name: 'show'})){

        path.node.key.name = 'didMount';

        pp.insertAfter(path.node);

      }

    },

    exit(path) {

      if(t.isIdentifier(path.node.key, {name: 'pageLifetimes'})){

        path.remove();

      }

    }

  });

  const output = generate(ast, {}, code);

  return output

}

 

export default transformJs

 

復制代碼

轉(zhuǎn)換wxml部分如下:

import { parse, stringify } from 'himalaya-wxml';

 

const traverseKey = (key: string) => {

  if(key.startsWith('wx:')){

    const postfix = key.slice(3);

    return `a:${postfix}`;

  }

  if(key === 'catchtouchmove'){

    return 'catchTouchMove';

  }

  if(key === 'bindtap'){

    return 'onTap';

  }

  if(key === 'bindload'){

    return 'onLoad';

  }

  if(key === 'binderror'){

    return 'onError';

  }

  if(key === 'bindchange'){

    return 'onChange';

  }

  return key

}

 

const traverseAst = (ast: any) => {

  return ast.map(item => {

    if(item.type !== 'element'){

      return item;

    }

    let res = item;

    if(item.attributes){

      res = {

        ...item,

        attributes: item.attributes.map(attr => ({

          ...attr,

          key: traverseKey(attr.key)

        }))

      }

    }

    if(item.children){

      res.children = traverseAst(item.children);

    }

    return res

  });

}

 

const transformWxml = (code: string) => {

  const ast = parse(code);

  const json = traverseAst(ast);

  return stringify(json)

}

 

export default transformWxml

復制代碼

以上,就擁有了兩個轉(zhuǎn)換函數(shù),再之后的工作,就是將這兩個函數(shù)運行在rollup里,就完成了將微信小程序組件轉(zhuǎn)換成支付寶小程序組件的功能。

總結(jié)

javascript作為前端最常用的語言,我們不僅要熟悉它,更要能操控它,通過javascript解釋器,我們就擁有了操控它的能力?;乇敬T源,鞏固基礎(chǔ),才能在寒冬之中保持內(nèi)心的平靜。