.
  • 最新新闻
  • 安卓手机app开发用什么软件_app开发_大鱼科技
    搅局者来了,他叫小程序(微信、支付宝),也叫直达服务(小米)。江湖的玩法变了。 右下角的墨迹天气,是工具类App里的老炮,用户每天开一次、每次瞅一眼的打开方式,让他很头疼。「这怎么卖广告?」 为了挣钱,他在【天气】下面放了一坨新闻,还搞了个【时景】的UGC体系,提升停留时长。又在【我】的页面放了一堆广告入口。
  • 制作手机app大概费用多少钱_app开发_大鱼科技
    有时为了突出名称的意义,可能会跟品牌不一致,但网民搜索又可能搜索不到,这时关键字优化就很重要了,核心优化点在于:内容描述中必须包含核心关键字,并获得较好排名,如搜索『简历』,你的简历相关的小程序是否比较靠前。
  • 开发一个app需要多少钱费用_app开发_大鱼科技
    微信的表情雨让中国人的社交充满了人情味儿。当你在对话框输入“么么哒”或者“想你了”,聊天页面会掉下相应的表情雨,成为大多情侣互传情愫的标准动作。中国传统情人节日“七夕节”已至,情侣们在微信“秀恩爱”的方式又多了一种——黄金红包。
  • 什么是小程序_小程序开发需要多少钱_app开发_大鱼科技
    什么是小程序:小程序是一种不需要下载安装即可使用的应用,它实现了应用“触手可及”的梦想,用户扫一扫或者搜一下即可打开应用。也体现了“用完即走”的理念,用户不关心是否安装太多应用的问题。应用将无处不在,随时随地可用,但又无需安装卸载。
  • 看牌器 透视在哪里买_app开发_大鱼科技
    这个微信小程序的创意始于2012年,当时仅仅开发了Win平台桌面应用,随着移动互联网发展,开发技术栈难度降低
  • 做一个平台app需要多少钱_app开发_大鱼科技
    访问次数:昨日访问小程序内所有页面总次数,多个页面之间的跳转、同一页面的重复访问计为多次访问。 访问人数:昨日访问小程序内所有页面的总用户数,同一用户多次访问不重复计。 新访问用户数:首次访问小程序页面的用户数,同一用户多次访问不重复计。
  • 一天能赚2万的黑方法_app开发_大鱼科技
    没深思小程序最近一个月内更新7次背后是微信内部前所未有的力度在支持小程序(公众号都不曾有此殊遇),特别是微信刚公布2017年Q1月活账号破9.38亿。面对如此巨大的流量洼地,我们如何借助小程序来低成本/0成本获取用户?
  • 网赌破解器哪里下载_app开发_大鱼科技
    小程序定位 对于公司而言,小程序究竟是一个什么产品定位?以目前的大环境而言,无非就是几个关键点: 蹭一波热点,吸引眼球,做小程序这件事情本身就是一个吸引眼球的事情; 延伸产品线,对已有产品线做一个开辟,确保整个线的完整性; 借助小程序,赶上这趟车,营销导流;
  • 定制一款app大概需要多少钱_app开发_大鱼科技
    很多人不禁要对此举的意义发问:以微信的算法而出的搜索结果,岂不是严重遏制了新生小程序的后来者居上?这样即使定义了关键词,对推广而言并没有实质性的意义?微信官方自然也想到了与之相辅相成的策略,我们甚至可以认为后来的这步棋才是微信的本来目的。那就是于6月中旬首次出现于人们视野中的「搜索关键词广告」
  • 软件外包公司_app开发_大鱼科技
    代码的复杂度是评估一个项目的重要标准之一。较低的复杂度既能减少项目的维护成本,又能避免一些不可控问题的出现。然而在日常的开发中却没有一个明确的标准去衡量代码结构的复杂程度,大家只能凭着经验去评估代码结构的复杂程度,比如,代码的程度、结构分支的多寡等等。当前代码的复杂度到底是个什么水平?什么时候就需要我们去优化代码结构、降低复杂度?这些问题我们不得而知。 因此,我们需要一个明确的标准去衡量代码的复杂度。
  • 热门新闻
开发一款网赌app_app开发_大鱼科技
时间:2021-01-11 23:57:23
开发一款网赌app_app开发_大鱼科技

如何将现有的微信原生小程序转其他平台的小程序?我想如果打算做这么一件事,或许大多数同学和我一样可能没什么思路。我第一次听说是在一次小程序生态技术大会上,一公司的一位前端技术人员谈到他们公司主要是将自己的微信小程序通过团队开发的工具转成其他平台的小程序,所以当时我也很想了解这个工具是怎么做的,实现过程是什么?

恰好近期有这么一次需求要将现有的微信小程序上开发别家的小程序,这个事要么通过现在比较好的方案uni-app来做,要么就用对应的原生小程序来写。但是从零开始做,工作量实在太大了,周期好长呀,那么多页面,得搞到啥时候。就在决定开始调研uni-app来做的时候,恰好有一天在微信上看见了一篇技术文章: 开源|wwto:小程序跨端迁移解决方案——微信转其他小程序 最后放弃了使用uni-app来做,尝试通过这个工具来转换。

下面也将围绕 wwto 这个工具库,通过将我们现有的微信小程序转 支付宝小程序 来了解其转换原理,同时呢会说下在转换的过程中遇到的各种问题是如何解决的,希望对有需要的同学能有所帮助

在了解 wwto 这个工具库后,它大致的架构是这样的,下面这张图是使用作者的,更权威一点。

通过了解 编译 以及 运行时 这两个模块的实现过程就能够明白小程序转换的过程中做了那些事情以及怎么去做的了

下面对这两个阶段所做的事简单说下:

1、在 编译 阶段主要对4个文件做了处理,分别是:*.js、 *.json、 *.wxml、 *wxss

*.wxml 的处理部分代码如下,可看源码 wxml.js

function convert(wxmlText, isWpy) {

return wxmlText

.replace(/wx:/g, 'a:')

.replace(/a:for-items/g, 'a:for')

.replace(/a:for-key/g, 'a:key')

// data-set 全部转为小写

.replace(/data-[^=\s]=/g, (match) => match.toLocaleLowerCase())

// // s:for-index="{{idx}}" -> s:for-index="idx"

.replace(/a:for-index=['"]({{\w+}})['"]/ig, (match) => match.replace('{{', '').replace('}}', ''))

// 自定义组件命名不能用驼峰

.replace(/<[\w]+/ig, (match) => {

return isWpy ? match : match.replace(/[A-Z]/g, (m) => ['-', m.toLowerCase()].join(''));

})

// 事件绑定名称对齐

.replace(/\s+catch[\w]+=['"]/ig, (match) => match.replace(/catchsubmit/ig, 'onSubmit')

.replace(/catch(\w)/g, (m, p1) => ['catch', p1.toUpperCase()].join('')));

... 省略

}

module.exports = convert;

复制代码

通过对文件的处理:例如

<view bind:cellTap='onTabMyCrad' wx:if="{{hasJoin}}">...</view> 变成了 <view onCellTap='onTabMyCrad' a:if="{{hasJoin}}">...</view>

也就是把微信的语法转换为目标小程序的语法结构。

复制代码

*.js 的处理部分代码如下,源代码 script.js

function convert(jsText, isWpy) {

return jsText

.replace(/(require\(['"])(\w+)/g, '$1./$2')

.replace(/(from\s+['"])(\w+)/g, (match, p1) => {

// 相对路径以./开头

return match.replace(p1, [p1, isWpy ? '' : './'].join(''));

})

.replace(/\.properties/g, (match) => {

return match.replace('.properties', '.props');

})

.replace(/Component\([\s\S]+methods:[^{]*{/, (match) => {

return [

match,

`,\r\ntriggerEvent: function(name, opt) {

this.props['on' + name[0].toUpperCase() + name.substring(1)]({detail:opt});

},\r\n`

].join('');

})

.replace(/[\s\S]+/, (match) => {

// 只处理组件

if (!match.match(/Component\(/)) return match;

... 省略

});

}

module.exports = convert;

复制代码

通过对组件的处理如图:

这么转换的目的也就是原文中 开源|wwto:小程序跨端迁移解决方案——微信转其他小程序 提到的 : 支付宝小程序组件的生命周期函数与微信小程序完全不一样,也没有一一对应的关系。这种情况无法使用简单的方法名正则替换,本方案是注入支付宝小程序组件的生命周期函数,在这些生命周期函数中在调用微信小程序的生命周期函数,这样以来就避免了方法名替换无法一一对应的问题,也能更方便地书写适配代码。

对 *.json 以及 *wxss 的处理就不列出代码了,可看源码: json.js 、 wxss.js

2、在 运行时 阶段又做了哪些事情呢?...

主要在每个js文件头部加入了适配代码adaptor.js

截取部分实现代码如下: 源代码可参考 converter.js

function convert(opt = {}) {

const src = opt.source || './src';

const dest = opt.target || './alibaba';

const assets = opt.assets || config.getAssets(src);

...省略

// 注入适配器代码

gulp.src(sysPath.resolve(__dirname, '../../../node_modules/mp-adaptor/lib/alibaba.js'))

.pipe(rename('adaptor.js'))

.pipe(gulp.dest(dest)).on('end', () => {

logger.info('复制 adaptor.js 完成!');

});

// 处理脚本文件

gulp.src(`${src}/**/*.js`)

.pipe(replace(/[\s\S]*/g, (match) => converter.script(match)))

.pipe(through2.obj(function(file, enc, cb) {

const path = file.history[0].replace(file.base, '');

const spec = path.split(sysPath.sep);

const adaptor = new Array(spec.length - 1).fill('..').concat('adaptor.js').join('/');

const str = [

`import wx from '${adaptor.replace(/^\.\./, '.')}';`,

ab2str(file.contents)

].join('\r\n');

file.contents = str2ab(str);

this.push(file);

cb();

}))

.pipe(gulp.dest(dest));

}

module.exports = convert;

复制代码

加入的adapter.js 代码是什么样的呢? 参考源码 alibaba.js

function getInstance() {

// eslint-disable-next-line no-undef

const wx = my;

wx.has_ali_hook_flag = true;

const {

getSystemInfo

} = wx;

wx.getSystemInfo = function(opt) {

...省略

return getSystemInfo.call(this, opt);

};

...省略

return wx;

}

export default getInstance();

上面的适配代码:主要就是包装抹平微信与支付宝两个平台间api的调用差异,既可以使用原微信wx.*的方式来调用,也可以使用支付宝小程序平台my.*的方式来调用api,说白了就是对微信的api包装了一层。

复制代码

通过分析 wwto 这个工具库的实现过程,也就学习到了如何基于现有的微信小程序转其他平台小程序的实现过程了。下面说下这次转换的过程中遇到了那些问题以及怎么解决的。

微信小程序代码转换阶段-实践

转换的时候遇见这么一些问题:

首先,wwto工具做不到运行时 diff 的抹平,也做不到一个 API 从无到有的过程

1、现阶段我们的微信小程序依赖 vantUI 组件库,使用wwto来转换压根就不支持

2、微信小程序中常用的api:selectComponent 在支付宝里小程序里面不支持

3、微信的分包加载是否支持?不支持又该如何处理等?

对于第二个问题,需要修改 wwto 工具库的代码,使其支持这个api,我这边的实现思路如下: 如Page A 页面依赖 Component B组件,可以在B组件的ready生命周期阶段把当前组件实例this挂载到全局对象getApp()中的某个属性上,然后在Page A中实现selectComponent这个api,这个api就来获取挂载到getApp()上对应的实例组件。

修改处在 script.js 代码中,可以打开文件比对 如下:

对于第三个问题,通过了解支付宝的分包机制文档,完全可以支持微信小程序的,但是,这里我在调试的时候支付宝开发者工具和到真机预览的时,两者差异完全不一样,在开发者工具完全运行正常,可是在真机预览的时候会遇见各种奇葩问题,大部分都是adaptor.js 重写wx.* 的api导致的问题,通过调试了好长时间,终于找到了问题的根源所在,我已经在githup上 向wwto 开源者提issue了,可查看 adaptor.js 重复执行 了解,并且我已提交了PR进行了修正

对于第二个大问题,做的事就相对比较多了,如果在不了解wwto这个工具库代码实现思路情况下,可能是无法解决的,当时能想到的解决办法就是,仿照 vantUI 组件库的代码实现,重新采用微信自定义组件的形式重写,但是这样做工作量又上去了,比起使用uni-app来,这个不可行,工作量也好大呀!这个时候,我几乎又要放弃使用这个工具来转换了。那这里能不能换别的思路去解决呢?答案肯定是有的,前提就是了解wwto工具的代码实现过程以及思路:wwto是在转换的时候,通过修改原微信小程序的文件,那么我就能够仿照其思想在小程序运行时添加兼容的代码来让vantUI微信小程序组件库能够跑在支付宝小程序中,听起来是多么一件有趣的事

如何去做呢?通过查看了vantUI组件库的代码实现,是可以按这种思路实现的,大致需要修改组件库中两处代码

1、源代码 basic.js 修改如下,主要是解决微信小程序triggerEvent api的功能,获取组件实例

let Behavior = p => p

export const basic = Behavior({

methods: {

$emit(...args) {

let name = args[0];

let onPropsfn = this.props['on' + name[0].toUpperCase() + name.substring(1)];

// 可以正则匹配 data-*值 ['datadata-mm', 'data-', 'data-1'].filter(v => v.match(/^data-\w+/))

let index = this.data && this.data['data-index'];

if (onPropsfn) {

if (args.length == 1) {

onPropsfn({detail: undefined})

} else if (args.length == 2) {

onPropsfn({detail: args[1], currentTarget:{dataset: {index: index}}})

} else if (args.length >= 3) {

onPropsfn.apply(this, args);

}

}

// this.triggerEvent(...args);

},

... 省略

}

});

添加的代码实现:都是参考wwto实现的思路

复制代码

2、源代码 component.js 修改如下,主要是解决微信小程序中一系特性功能如:externalClasses、properties、behaviors => 模拟到支付宝小程序中,如果有兴趣可以比对添加的代码,如何抹平这些特性差异,其中微信的relations组件特性,没法模拟,替代方案就只能用支付宝小程序相关组件了

import { basic } from '../mixins/basic';

import { observe } from '../mixins/observer/index';

function mapKeys(source, target, map) {

Object.keys(map).forEach(key => {

if (source[key]) {

target[map[key]] = source[key];

}

});

}

function noop() {}

function VantComponent(vantOptions = {}) {

const options = {};

mapKeys(vantOptions, options, {

data: 'data',

props: 'properties',

mixins: 'behaviors',

methods: 'methods',

beforeCreate: 'created',

created: 'attached',

mounted: 'ready',

relations: 'relations',

destroyed: 'detached',

classes: 'externalClasses'

});

options.properties = options.properties || {};

// relations 微信组件特性,暂时没法模拟到支付宝

const { relation } = vantOptions;

if (relation) {

options.relations = Object.assign(options.relations || {}, {

[`../${relation.name}/index`]: relation

});

}

// add default externalClasses

options.externalClasses = options.externalClasses || [];

options.externalClasses.push('custom-class');

// add default behaviors

options.behaviors = options.behaviors || [];

options.behaviors.push(basic);

// map field to form-field behavior

if (vantOptions.field) {

options.behaviors.push('wx://form-field');

}

// add default options

options.options = {

multipleSlots: true,

addGlobalClass: true

};

observe(vantOptions, options);

/**

* 参照wwto => 运行时调整options

*/

/**

* mixins

*/

options.mixins = options.mixins || [];

options.mixins = options.mixins.concat(options.behaviors);

/**

* const lifeCircleNames = ['created', 'attached', 'ready', 'detached'];

*/

options.methods = options.methods || {};

const lifeCircleNames = ['created', 'attached', 'ready', 'detached'];

lifeCircleNames.forEach(name => {

let methods = options.methods[name] = options.methods[name] || options[name] || noop;

// fix selectComponents api

if (name == 'ready') {

options.methods[name] = function() {

if(this.data.id){

var app = getApp();

app.globalData.insComp = app.globalData.insComp || {};

app.globalData.insComp[this.data.id] = this;

};

methods();

}

}

})

/**

* 处理this.__observers

*/

let has = Object.prototype.hasOwnProperty;

let propMap = {};

let observerMap = null;

let walkProps = obj => {

Object.keys(obj).forEach((key) => {

if (!has.call(obj, key)) return

let item = obj[key];

// String Number Boolean 设定默认值

if (item === String) {

propMap[key] = '';

} else if (item === Boolean) {

propMap[key] = false;

} else if (item === Number) {

propMap[key] = 0;

} else if (item && typeof item == 'object') {

let type = item.type;

if (!('value' in item)) {

if (type === String) {

propMap[key] = '';

} else if (type === Boolean) {

propMap[key] = false;

} else if (type === Number) {

propMap[key] = 0;

} else {

propMap[key] = ''; // 暂时默认值

}

} else {

propMap[key] = item.value;

}

if (item.observer) {

// debugger

observerMap = observerMap || {};

if (typeof item.observer === 'function') {

observerMap[key] = item.observer;

} else { // 微信小程序中observer也可以使用对象的形式

observerMap[key] = function() {

this[item.observer] && this[item.observer].apply(this, arguments);

};

}

}

} else {

propMap[key] = item;

}

});

}

// 处理properties => props

let properties = options.properties;

walkProps(properties);

let mininsProperties = options.mixins.filter(item => item.properties);

mininsProperties.forEach(item => {

walkProps(item.properties);

})

/**

* 处理 externalClasses 同时手动拷贝class

*/

let externalClasses = options.externalClasses;

externalClasses.forEach(clas => {

propMap[clas.replace(/-(\w)/g, (match, p) => p.toUpperCase())] = '';

})

options.props = propMap;

options.props.__observers = observerMap

/**

* my生命周期函数

*/

options.didMount = function(){

this.data = Object.assign({}, this.data, this.props);

this.created && this.created.apply(this, arguments);

this.attached && this.attached.apply(this, arguments);

this.ready && this.ready.apply(this, arguments);

/**

* 解决初始化observer component 组件

*/

if (this.props.__observers) {

Object.keys(this.props.__observers).forEach(key => {

this.props.__observers[key].call(this, this.props[key])

})

}

}

options.didUnmount = function(){

this.detached && this.detached.apply(this, arguments);

}

options.didUpdate = function(prevProps, preData) {

for (let key in this.props) {

if (this.props.__observers && typeof(this.props.__observers[key]) === 'function') {

if (JSON.stringify(prevProps[key]) !== JSON.stringify(this.props[key]) &&

JSON.stringify(preData[key]) !== JSON.stringify(this.props[key])) {

this.setData(Object.assign({}, this.data, {[key]: this.props[key]}));

this.props.__observers[key].apply(this, [this.props[key], prevProps[key]]);

}

} else if (this.props[key] !== prevProps[key]) {

this.data[key] = this.props[key];

this.setData(this.data);

}

}

}

Component(options);

}

export { VantComponent };

复制代码

到这里主要的问题解决了,其他一些微信小程序到支付宝小程序的差异就不都列出来了,可以灵活的修改wwto 的代码来实现转换时的差异, 如果后期有同样需求的同学尝试转换时有遇见问题,也可留言交流。

最后

最初在决定到底要不要使用wwto这个工具来转换微信小程序的时候,心里面也是没底的,毕竟刚开源,我估计是第一个在刚开源的时候来做转换的。而且本身也从未开发过支付宝小程序,也不知道支付宝小程序和微信小程序有哪些大致的差异,再加上调研技术上也没有给充裕的时间来决策到底用什么方案来实现其他平台的小程序。最后决定使用wwto来做这件事,主要是不想做重复的工作,对仅仅使用新的技术框架uni-app来重写,估计对我来说短期也不会有太多的技术积累收益,当然同时呢我也想快速的了解微信和支付宝的一些差异,重要的一点就是wwto开源的,每个部分的源代码都能debug。综合以上几点于是决定来趟一下这个浑水,总的结果来说,项目周期缩短了好多,大致花了两周的时间来完成了,也了解了支付宝小程序和微信小程序之间的一些差异,解决了好多问题,解决问题的过程中也很头疼...

最后转换后的效果图就不给出了,欢迎在微信和支付宝中搜索: “ 咔咔找车 ” 小程序看两者差异。