如何將原生微信小程式頁面改成原生VUE框架的H5頁面

来源:https://www.cnblogs.com/cbw-mango/archive/2022/09/05/16657778.html
-Advertisement-
Play Games

目前h5新增一個文字轉語音的功能(但是正在完善中,勉強能用),h5新增的SpeechSynthesisUtterance實例 首先new一個SpeechSynthesisUtterance對象 使用實例對象的一些屬性,包括: text – 要合成的文字內容,字元串。 lang – 使用的語言,字元串 ...


項目背景:

       公司為了實現小程式與H5頁面共同覆蓋,全面推廣。特此想將已有的小程式進行快速改造上線(二周內),研發出H5版本。目前公司前端技術較為薄弱,現有的技術解決方案還停留在JSP。

問題:

       如何將目前已經上線運行的原生小程式(40多個頁面)快速轉化為原生H5頁面,實現快速上線,是採用我們較為熟悉的JSP還是引用目前最熱門的前後端分離VUE框架呢???

想法:

1)採用熟練的JSP框架,每人一天可以改造2個頁面左右,計劃投入2人,風險最小;

2)採用主流分散式Vue框架,時間未知,風險未知;

       首先自我介紹下,本人是一名JAVA開發工程師,平時喜歡研究相關主流技術和挑戰自己。對此我還是比較傾向於第二種解決方案,但是第二種解決方案無疑是最複雜,最耗時,最未知,風險最大。公司內沒有人願意承接。於是我抱著學習和研究的態度以及對主流技術的嚮往,我找到我們領導我是這樣說的:我還是比較建議公司採用第二種方案。1)這無疑是給我們進行敲門磚及學習的機會;2)這是公司提升前端技術能力與主流技術看齊的機會。最後公司同意了我建議,採用方案二,有我來承接此事,進行牽頭負責。

       中間心酸過程忽略,剛接下來第一天就後悔了,VUE用都沒用過,還怎麼玩。於是我花了大量的時間,看了大量文獻,我這裡使用到是Vue 2 + Vant 2 + axios + router。

  (1)VUE官方技術文檔:

  https://v2.cn.vuejs.org/v2/guide/

  https://cli.vuejs.org/zh/guide/creating-a-project.html

       (2)引用了Vant 2組件:

   https://youzan.github.io/vant/v2/#/zh-CN/toast

       (3)同時也參考很多成熟開源VUE框架:

        若依(也是從這個項目中進行借鑒與參考):http://doc.ruoyi.vip/ruoyi/

        於是我花費了3天時間終於完成了VUE項目框架搭建及第一個小程式頁面轉H5頁面。心想如果是這樣的進度,指定時間完成是不可能的了,有沒有什麼方法可以快速將小程式組件替換成VUE呢,報著這個想法就開乾,也是網上找了很多案例都不是很齊全,沒辦法我只能自己進行編寫工具方法,廢話不多說之前上乾貨。

         首先要想編寫工具方法必須要知道小程式和VUE有哪些特性,以及哪些是等價效果。這裡我進行了簡單梳理:

頁面事件替換:

view 替換 div

image替換img

bindinput=替換v-on:input=

bindtap=替換@click=

<rich-text nodes="{{detail.prizeDetailContent}}"></rich-text>替換<div class="fuwenben" v-html="detail.prizeDetailContent"></div>

Js替換:

e.detail.value替換e.target.value

app.globalData.替換this.$globalData.(globalData這裡是需要在main.js定義替換的全局變數)

wx.showLoading({title: "載入中",mask: true,});

替代寫法

this.$toast.loading({ message: '載入中...',forbidClick: true, duration: 0,});(這裡是引用了Vant 2)

wx.getStorageSync替代寫法localStorage.getItem

wx.setStorageSync替代寫法localStorage.setItem

wx.navigateTo替換window.location.href=" "

wx.redirectTo替換window.open();

wx.hideLoading();替換this.$toast.clear();(這裡是引用了Vant 2)

樣式替換:

//100rpx -> 5rem rpx替換rem(考慮到移動端相容引入rem計量單位) 等等,我這裡不一一列舉了。其次介紹下實現步驟及原理: VUE各個文件目錄含義說明:

 

 具體步驟如下:

(1)VUE採用的是單頁面設計思路,所有js,html,css都在一個頁面內完成,對此我們需要先將小程式指定文件夾下麵的內容進行粘貼過來。

 

 

 (2)進入“replace”目錄,將複製完成後的代碼,粘貼到“replace.vue” 點擊“replaceUtil.js” 右鍵,在集成終端中打開,運行代碼“node .\replaceUtil.js”

 

以上兩步完成絕大部分的小程式轉VUE特性功能點,個別較為複雜的可能需要人工進行修改調整。(列入wx.request這些目前我們的寫法是人工進行修改改為axios,當然也可以進行二次封裝,減少代碼改動地方,歡迎大家進行嘗試。我這裡這裡主要是為了去wx化防止誤導,選擇了人工進行修改調整)

具體後臺請求介面方面我們採用的axios具體寫法如下:

1、 使用axios組件完成後臺請求,第一步“service”目錄下新建“.js”

 

2、 引用js。

 

3、調用方式

 

以上就是我對VUE轉H5相關理解和具體實現思路,歡迎大家進行評論和指導!!!

 

相關源碼如下:

(1)核心替換代碼replaceUtil.js

const fs = require("fs");
let fileName = "replace.vue"; // 需要替換字元的文件名

fs.readFile(`${__dirname}/${fileName}`, "utf8", (err, data) => {
  console.log(__dirname);
  console.log(fileName);
  if (err) {
    console.log("readFile error");
    console.log(err);
  }

  console.log("readFile sussce");
  // 替換字元
  let rs = changeWxToVue(data, ["label", "rpx", "if", "for", "tap", "labjs"]);
  // console.log(rs)
  fs.writeFile(`${__dirname}/${fileName}`, rs, "utf8", (err) => {
    if (err) {
      console.log("writeFile error");
    }
  });
});

/**
 *
 * @param {*} data vue文件字元
 * @param {*} changeArr 數組格式,需要替換的字元,以數組格式傳遞;
 * ['label']:只替換view、text、block標簽
 * ['label','rpx']:替換view、text、block標簽 和 rpx,rpx的數值需要是基準為750px的標準設計稿,100rpx->50px
 * ['for']:for標簽替換不包括key,否則正則表達式太長了
 * @returns
 */
function changeWxToVue(data, changeArr) {
  for (let i = 0; i < changeArr.length; i++) {
    if (changeArr[i] == "label") {
      // 替換view -> div, text -> span
      data = data.replace(/<(\/(view)|view.*)>/g, (a) => {
        return a.replace(/view/g, "div");
      });
      data = data.replace(/<(\/(text)|text.*)>/g, (a) => {
        return a.replace(/text/g, "span");
      });
      data = data.replace(/<(\/(block)|block.*)>/g, (a) => {
        return a.replace(/template/g, "span");
      });
    }
    if (changeArr[i] == "rpx") {
      // 替換100rpx -> 5rem
      data = data.replace(/(\d*)rpx/g, (num) => {
        return (parseInt(num) / 20).toFixed(2).toString() + "rem";
      });
    }
    if (changeArr[i] == "if") {
      // 替換wx:if -> v-if
      data = data.replace(
        /(wx:if|wx:elif|wx:else)="{{(((?![-=]).)*)}}"/g,
        (a, b, c) => {
          console.log(a,b,c);

          c = c.replace(/(^\s*)|(\s*$)/g, "");
          let vue = "";
          switch (b) {
            case "wx:if":
              vue = "v-if";
              break;
            case "wx:elif":
              vue = "v-else-if";
              break;
            case "wx:else":
              vue = "v-else";
              break;
          }
          return `${vue}="${c}"`;
        }
      );
    }

    if (changeArr[i] == "for") {
      // 替換wx:for="{{list}}" wx:for-item="item1" wx:for-index="index1" -> v-for=""
      let forRegArr = [
        /\swx:for="{{(((?![-=]).)*)}}"([^key].*)wx:for-item="(((?!-).)*)"([^key].*)wx:for-index="(((?![-=]).)*)"/g,
        /\swx:for="{{(((?![-=]).)*)}}"([^key].*)wx:for-index="(((?!-).)*)"([^key].*)wx:for-item="(((?![-=]).)*)"/g,
        /\swx:for="{{(((?![-=]).)*)}}"/g,
      ];
      data = data.replace(forRegArr[0], (a, b, c, d, e, f, g, h, i, j, k) => {
        // console.log('a-',a,'\nb-',b,'\nc-',c,'\nd-',d,'\ne-',e,'\nf-',f,'\ng-',g,'\nh-',h,'\ni-',i,'\nj-',j,'\nk-',k);
        if (e) {
          if (h) {
            return ` v-for="(${e}, ${h}) in ${b}" `;
          } else {
            return ` v-for="${e} in ${b}" `;
          }
        }
      });
      data = data.replace(forRegArr[1], (a, b, c, d, e, f, g, h, i, j, k) => {
        // console.log('a-',a,'\nb-',b,'\nc-',c,'\nd-',d,'\ne-',e,'\nf-',f,'\ng-',g,'\nh-',h,'\ni-',i,'\nj-',j,'\nk-',k);
        if (h) {
          if (e) {
            return ` v-for="(${h}, ${e}) in ${b}" `;
          } else {
            return ` v-for="${h} in ${b}" `;
          }
        }
      });
      data = data.replace(forRegArr[2], (a, b) => {
        return ` v-for="item in ${b}" `;
      });
    }
    if (changeArr[i] == "tap") {
      // bindtap || catchtap -> @click
      data = data.replace(
        /(bindtap|bind:tap|catchtap)="(((?!-).)*)"/g,
        (a, b, c) => {
          return `@click="${c}"`;
        }
      );

      // bindinput -> v-on:input=
      data = data.replace(/(bindinput)="(((?!-).)*)"/g, (a, b, c) => {
        return `v-on:input="${c}"`;
      });
    }
    if (changeArr[i] == "data") {
      // data-index="{{index}}" -> data-index="index"
      data = data.replace(
        /data-(((?!-).)*)="{{(((?!-).)*)}}"/g,
        (a, b, c, d) => {
          return `data-${b}="${d}"`;
        }
      );
    }
    if (changeArr[i] == "labjs") {
      // e.detail.value -> e.target.value
      data = data.replace(/e.detail.value/g, "e.target.value");
      // app.globalData. -> this.$globalData.
      data = data.replace(/app.globalData./g, "this.$globalData.");
      // wx.getStorageSync -> localStorage.getItem
      data = data.replace(/wx.getStorageSync/g, "localStorage.getItem");
      // wx.setStorageSync -> localStorage.setItem
      data = data.replace(/wx.setStorageSync/g, "localStorage.setItem");
      //  wx.hideLoading -> localStorage.setItem
      data = data.replace(/wx.setStorageSync/g, "localStorage.setItem");
      //  wx.hideLoading -> this.$toast.clear
      data = data.replace(/wx.hideLoading/g, "this.$toast.clear");

      data = data.replace(/wx:key=\"\*this\"/g, "");
      data = data.replace(
        /wx:for=["|']\s*\{\{([^\}]+)\}\}\s*["|']/g,
        ($0, $1) => 'v-for="(item, index) in ' + $1 + '"'
      );
      data = data.replace(
        /wx:for-items=["|']\s*\{\{([^\}]+)\}\}\s*["|']/g,
        ($0, $1) => 'v-for="(item, index) in ' + $1 + '"'
      );
      data = data.replace(
        /wx:key=["|']([^"|']+)["|']/g,
        ($0, $1) => ':key="' + $1 + '"'
      );
      // if
      data = data.replace(
        /wx:if=["|']\s*\{\{([^\}]+)\}\}\s*["|']/g,
        ($0, $1) => 'v-if="' + $1 + '"'
      );
       // elif
       data = data.replace(
        /wx:elif=["|']\s*\{\{([^\}]+)\}\}\s*["|']/g,
        ($0, $1) => 'v-else-if="' + $1 + '"'
      );
       // else
       data = data.replace(/wx:else/g, "v-else");


      data = data.replace(/<view/g, "<div");
      data = data.replace(/\/view>/g, "/div>");
      data = data.replace(/<text/g, "<span");
      data = data.replace(/\/text>/g, "/text>");
      data = data.replace(/<image([^>]+)/g, ($0, $1) => "<img " + $1 + "/");
      data = data.replace(/<input([^>]+)/g, ($0, $1) => "<input " + $1 + "/");

      data = data.replace(/<\/image>/g, "");
      data = data.replace(/<\/input>/g, "");
      data = data.replace(
        /<icon([^>]+)/g,
        ($0, $1) => '<i style="display:inline-block" ' + $1 + "/>"
      );
      data = data.replace(
        /bind([^=|\s]+)=["|']([^"|'|\s]+)["|']/g,
        ($0, $1, $2) => "@" + $1 + '="' + $2 + '"'
      );
      data = data.replace(/navigator/g, "router-link");
      data = data.replace(/\"\/images/g, '"../../assets');
      data = data.replace(/this.data./g, "this.");
      data = data.replace(/that.data./g, "this.");
      data = data.replace(/..\/..\/utils\/wxml\/common.wxml/g, "../../utils/vue/common.vue");
      data = data.replace(/..\/..\/utils\/wxss\/common.wxss/g, "../../utils/css/common.css");

            // 變數
      // data = data.replace(/data-([^=|\s]+)=["|']\{\{([^\}]+)\}\}["|']/g, ($0,$1,$2)=> ':data-'+$1+'="'+$2+'"')
      data = data.replace(
        /([^\s|=]+)=["|']\{\{([^\}]+)\}\}["|']/g,
        ($0, $1, $2) => ":" + $1 + '="' + $2 + '"'
      );

      // 變數輪播圖替換
      data = data.replace(/<swiper-item/g, "<van-swipe-item");
      data = data.replace(/\/swiper-item>/g, "/van-swipe-item>");
      data = data.replace(/<swiper/g, "<van-swipe");
      data = data.replace(/\/swiper>/g, "/van-swipe>");
      data = data.replace(/:autoplay=\"autoplay\"/g, ":autoplay=\"interval\"");

    }
  }
  return data;
}

(2)axios.js:

/**
 * 嚴肅聲明:
 * 開源版本請務必保留此註釋頭信息,若刪除我方將保留所有法律責任追究!
 * 本系統已申請軟體著作權,受國家版權局知識產權以及國家電腦軟體著作權保護!
 * 可正常分享和學習源碼,不得用於違法犯罪活動,違者必究!
 * Copyright (c) 2020 陳尼克 all rights reserved.
 * 版權所有,侵權必究!
 */
import axios from "axios";
import { Toast } from "vant";
import router from "../router";
import globalData from "../utils/global.js";
import qs from 'qs';


axios.defaults.baseURL = '';
axios.defaults.timeout = 30000;
axios.defaults.withCredentials = true;
axios.defaults.headers["X-Requested-With"] = "XMLHttpRequest";
axios.defaults.headers["token"] = localStorage.getItem("token") || "";
axios.defaults.headers.post["Content-Type"] =
  "application/x-www-form-urlencoded;charset=UTF-8";

// 請求攔截器
axios.interceptors.request.use(
  (config) => {
    // config 請求的所有信息
    // console.log(config);
    // 設置請求頭
    if (config.method === "post" && !!config.data && config.data !== "") {
      config.headers = {
        Accept: "*/*",
        "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
      };
      if(config.url === '/pages/sri/order/uploadImg'){
        config.headers["Content-Type"] = "multipart/form-data"
      } else {
        config.data = qs.stringify(config.data, {arrayFormat:'comma'})
      }
    }

    // if (config.method === "put" && !!config.params && config.params !== "") {
    //   config.headers = {
    //     Accept: "*/*",
    //     "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
    //   };
    // }

    // if (config.method === "delete") {
    //   if (!!config.params && !!config.params.data) {
    //     config.headers = {
    //       Accept: "*/*",
    //       "Content-Type": "application/json;charset=UTF-8",
    //     };
    //     config.data = config.params;
    //     config.params = "";
    //   }
    // }

    return config; // 將配置完成的config對象返回出去 如果不返回 請求講不會進行
  },
  (err) => {
    // 請求發生錯誤時的相關處理 拋出錯誤
    console.log("服務端請求異常!");
    return Promise.reject(err);
  }
);

// 響應攔截器
axios.interceptors.response.use(
  (res) => {
    return res;
  },
  (err) => {
    console.log("服務端響應異常!");
    return Promise.reject(err);
  }
);

export default axios;

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 2022-09-05 MySQL常用的命令語句 表的操作語句 1、查詢某個表的內容 select * from xxx(表名); 2、向某個表內添加數據 以“students”表為例: students表的屬性有:id,name,age,sex insert into students values ...
  • 在最新一屆國際資料庫頂級會議 ACM SIGMOD 2022 上,來自清華大學的李國良和張超兩位老師發表了一篇論文:《HTAP Database: What is New and What is Next》,並做了 《HTAP Database:A Tutorial》 的專項報告。 本篇文章,我們將 ...
  • GreatSQL社區原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。 GreatSQL是MySQL的國產分支版本,使用上與MySQL一致。 作者介紹:孫黎,GreatDB 認證DBA 問題現象描述 測試MySQL單機時,無意發現,MySQL 8.0的 ib_logfilesN的顯示如下: l ...
  • 項目需要求在移動端H5需要展示一些數據統計的圖表,雖然第一時間想到的是echarts,常用還有Highcharts,D3等,antv家族的圖表UI好看一些,再加上F2是移動端可視化方案於是就選擇F2,打開官網果然眼前一亮F2提供的正能滿足需求,F2官網地址:https://antv.alipay.c ...
  • 開發好APP瞭如何上架apple store市場? 1、進入蘋果的開發者中選項,就會看到以下畫面,點擊紅框內的選項 點擊之後,就會看到以下畫面,如下圖所示選擇相應的選項 之後就會看以下畫面,根據相應的要求填入相應的信息,之後點擊ok。 之後,把證書下載下來 之後打開HBuilder這個軟體,在上方找 ...
  • 1、安裝iOS上架輔助軟體Appuploader 2、申請iOS發佈證書(p12) 3、申請iOS發佈描述文件(mobileprovision) 4、打包ipa 5、上傳ipa到iTunes Connect 6、TestFlight方式安裝到蘋果手機測試 7、設置APP各項信息提交審核 一、下載安裝 ...
  • HMS Core推送服務支持開發者使用HTTPS協議接入Push服務端。Postman是一款介面測試工具,它可以模擬用戶發起的各類HTTP請求,將請求數據發送至服務端,獲取對應的響應結果。Postman可以模擬開發者伺服器申請Access Token,調用鑒權等介面的請求。 因此有很多開發者在測試端 ...
  • What CSS動畫: 就是指元素從一種樣式逐漸過渡為另一種樣式的過程。 實現方式 transition 實現漸變動畫 transform 實現轉變動畫 animation 實現自定義動畫 實現方式 transition 實現漸變動畫 思維導圖 案例 點擊查看代碼 <!DOCTYPE html> < ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...