如何将原生微信小程序页面改成原生VUE框架的H5页面
项目背景:
公司为了实现小程序与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)
样式替换:
具体步骤如下:
(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;
如何将原生微信小程序页面改成原生VUE框架的H5页面的更多相关文章
- 微信小程序学习笔记(二)--框架-全局及页面配置
描述和功能 框架提供了自己的视图层描述语言 WXML 和 WXSS,以及基于 JavaScript 的逻辑层框架,并在视图层与逻辑层间提供了数据传输和事件系统,让开发者能够专注于数据与逻辑. 响应的数 ...
- 微信小程序-06-详解介绍.js 逻辑层文件-注册页面
上一篇介绍的是 app.js 逻辑层文件中注册程序,对应的每个分页面都会有的 js 文件中 page() 函数注册页面 微信小程序-06-详解介绍.js 逻辑层文件-注册页面 宝典官方文档: http ...
- 第一章 “我要点爆”微信小程序云开发之项目建立与我的页面功能实现
第一章 “我要点爆”微信小程序云开发之项目建立与我的页面功能实现 开发环境搭建 使用自己的AppID新建小程序项目,后端服务选择小程序·云开发,点击新建,完成项目新建. 新建成功后跳转到开发者工具界面 ...
- 微信小程序从零开始开发步骤(六)4种页面跳转的方法
用法:用于页面跳转,相当于html里面的<a></a>标签. API教程:https://mp.weixin.qq.com/debug/wxadoc/dev/component ...
- Taro -- 原生微信小程序转taro
微信小程序转Taro (转发https://nervjs.github.io/taro/docs/taroize.html) Taro 可以将你的原生微信小程序应用转换为 Taro 代码,进而你可以 ...
- 微信小程序裁剪图片成圆形
代码地址如下:http://www.demodashi.com/demo/14453.html 前言 最近在开发小程序,产品经理提了一个需求,要求微信小程序换头像,用户剪裁图片必须是圆形,也在gith ...
- 原生微信小程序数据渲染
一直在写vue,第一次接触微信小程序,还是原生,最开始做的时候真的很闹心啊啊啊啊啊啊啊啊啊啊啊啊!!所以最近大概更新的都是微信小程序原生的内容了~~么么哒!!一定会继续努力的!!tips:在小程序项目 ...
- 如何在原生微信小程序中实现数据双向绑定
官网:https://qiu8310.github.io/minapp/ 作者:Mora 在原生小程序开发中,数据流是单向的,无法双向绑定,但是要实现双向绑定的功能还是蛮简单的! 下文要讲的是小程序框 ...
- 微信小程序苹果手机调用camera原生组件拍照后不退出
最近做微信小程序时,用到小程序的原生组件camera时,踩到一个bug. 在给camera设置样式position:absolute;绝对定位后,IOS调用camera原生组件拍照后退不出来. 不使用 ...
随机推荐
- 【Redis】事件驱动框架源码分析(单线程)
aeEventLoop初始化 在server.c文件的initServer函数中,对aeEventLoop进行了初始化: 调用aeCreateEventLoop函数创建aeEventLoop结构体,对 ...
- 你真的了解git的分支管理跟其他概念吗?
现在前端要学的只是太多了,你是不是有时会有这个想法,如果我有两个大脑.一个学Vue,一个学React,然后到最后把两个大脑学的知识再合并在一起,这样就能省时间了. 哈哈,这个好像不能实现.现实点吧!年 ...
- node线上项目连接mysql出现 504 Gateway Time-Out
var connection = mysql.createConnection({host : 'localhost',user : 'root',password : '123456',port: ...
- Linux系统安全配置
1.物理安全 硬件服务器,关闭从CD/DVD等这些方面的软启动方式.同时也可以设置BIOS密码,并且要有限制访问的策略与各类流程管控. 还可以禁用USB设备来达到安全的目的: centos7x 安装d ...
- Web开发小妙招:巧用ThreadLocal规避层层传值
摘要:我们可以在处理每次请求的过程中,无需从Controller以及Service中的方法层层传值,只需要直接通过该局部变量取值即可. 本文分享自华为云社区<拦截器中巧用ThreadLocal规 ...
- PTA(BasicLevel)-1031 查验身份证
一.问题定义 一个合法的身份证号码由17位地区.日期编号和顺序编号加1位校验码组成.校验码的计算规则如下:首先对前17位数字加权求和,权重分配为:{7,9,10,5,8,4,2,1,6,3,7,9,1 ...
- HBase学习(四) 二级索引 rowkey设计
HBase学习(四) 一.HBase的读写流程 画出架构 1.1 HBase读流程 Hbase读取数据的流程:1)是由客户端发起读取数据的请求,首先会与zookeeper建立连接2)从zookeepe ...
- AOV网的实现(数据结构)
#include <stdio.h> #include <stdlib.h> #include <string.h>//我这里的头以及尾巴与书上的不一样. int ...
- springmvc异常处理解析#ExceptionHandlerExceptionResolver
开头 试想一下我们一般怎么统一处理异常呢,答:切面.但抛开切面不讲,如果对每一个controller方法抛出的异常做专门处理,那么着实太费劲了,有没有更好的方法呢?当然有,就是本篇文章接下来要介绍的s ...
- ASP.NET Core 6框架揭秘实例演示[29]:搭建文件服务器
通过HTTP请求获取的Web资源很多都来源于存储在服务器磁盘上的静态文件.对于ASP.NET应用来说,如果将静态文件存储到约定的目录下,绝大部分文件类型都是可以通过Web的形式对外发布的." ...