avm.js 是一个跨端开发框架,AVM(Application-View-Model)前端组件化开发模式基于规范Web Components组件化思维,提供蕴含虚构DOM和Runtime的编程框架avm.js以及多端对立编译工具,齐全兼容Web Components规范,同时兼容Vue和React语法糖编写代码,编译工具将Vue和React相干语法糖编译转换为avm.js代码。

有Vue和React 开发教训的很容易上手。

1. 组件的定义和援用:

     1.1 应用stml定义一个组件 / 页面

stml组件兼容Vue单文件组件(SFC)标准,应用语义化的Html模板及对象化js格调定义组件或页面。stml最终被编译为JS组件 / 页面,渲染到不同终端。

定义组件:

// api-test.stml:<template>      <view class='header'>        <text>{this.data.title}</text>    </view>  </template>  <script>    export default {          name: 'api-test',        data(){            return {                title: 'Hello APP'            }        }    }</script><style scoped>    .header{        height: 45px;    }</style>

  1.2 组件援用:

// app-index.stml:<template>      <view class="app">          <img src="./assets/logo.png" />          <api-test></api-test>     </view>  </template><script>    import './components/api-test.stml'          export default {          name: 'app-index',          data: function () {              return {                title: 'Hello APP'            }        }      }  </script>  <style>      .app {           text-align: center;          margin-top: 60px;      }  </style>

2. 向子组件传值

向子组件传值采纳 props 的形式,这里以一个示例来进行阐明。

定义子组件,在 props 外面注册一个 title 属性:

// api-test.stml:<template>    <text>{title}</text></template><script>    export default {        name:'api-test',        props:{            title: String        }    }</script>

这里定义的title属性类型为String,属性类型包含 String、Number、Boolean、Array、Object、Function等。

     2.1 在其它页面应用子组件时传递动态值:

// app-index.stml:<template>      <view>          <api-test title="Hello App!"></api-test>     </view>  </template><script>    import './components/api-test.stml'          export default {          name: 'app-index'    }  </script>

     2.2 通过数据绑定传递动静值:

// app-index.stml:<template>    <view>        <api-test v-bind:title="msg"></api-test>    </view></template><script>    import './components/api-test.stml'          export default {        name: 'app-index',        data() {            return {              msg: 'Hello App!'          }        }    }</script>

传递动态值时只能传递字符串类型数据,通过数据绑定的形式则能够传递任意类型的数据。

3. 监听子组件事件**

监听子组件事件和监听一般事件相似,如:

// api-index.stml:<template>    <view>        <api-test onresult="onGetResult"></api-test>    </view></template><script>    import './components/api-test.stml'          export default {        name: 'app-index',        methods: {            onGetResult(e){                console.log(e.detail.msg);            }        }    }</script>

以上示例中监听了子组件的result事件,子组件外面通过fire办法来触发监听的事件:

// app-test.stml:<template>    <text onclick="onclick">Hello App!</text></template><script>    export default {        name:'api-test',        methods:{            onclick(){                let detail = {msg:'Hi'};                this.fire('result', detail);            }        }    }</script>

fire办法有两个参数,第一个参数为事件名称,第二个参数为要传递的自定义数据,在父组件监听办法外面通过e.detail获取传递的数据。

// api-index.stml:methods: {  onGetResult(e){      console.log(e.detail.msg);  }}

4.  声网组件实例

理解了以上组件的规定和用法,就能够封装本人的组件了 。上面看一个基于agoraRtc声网模块,实现1对1语音通话的组件实例:

<template>    <view class="agorartc-call-voice_page">        <safe-area></safe-area>        <view class="agorartc-call-voice_list" v-for="(item,index) in userList">            <view class="agorartc-call-voice_userinfo">                <image class="agorartc-call-voice_userinfo-image" thumbnail='false'                    style="width: 64px; height: 64px; margin-right:10px" src={{item.userimg }}></image>                <text class="agorartc-call-voice_userinfo-text">{{item.username }}</text>            </view>            <view class="agorartc-call-voice_callimg">                <image @click="fnstart_voice_call(item.userid)" thumbnail='false' style="width: 50px; height: 50px"                    src="../../image/voice-call.png"></image>            </view>        </view>        <view class="agorartc-call-voice_connected" v-if="connected">            <image class="agorartc-call-voice_voice" thumbnail='false' style="width: 200px;"                src="../../image/video-voice.gif"></image>            <image class="agorartc-call-voice_hangup" @click="fnhangup()" thumbnail='false'                style="width: 64px; height: 64px;" src="../../image/video-hangup.png"></image>        </view>    </view></template><script>export default {    name: 'agorartc-call-voice',    props: {        channel: String,        userList: Array,        rtcAppId: String    },    installed() {        this.fnishasper_mic();    },    data() {        return {            connected: false        };    },    methods: {        fnishasper_mic(_userid) {            var resultList = api.hasPermission({                list: ["microphone"]            });            if (resultList[0].granted) {            } else {                api.toast({                    msg: "须要启用麦克风权限"                });                api.requestPermission({                    list: ["microphone"]                }, res => {                    if (res.list[0].granted) {                    }                });            }        },        fnstart_voice_call(_userid) {            this.fnrtc_init();            this.fnerr_listener();            this.fnjoin_channel(_userid);        },        fnrtc_init() {            console.log('初始化');            var agoraRtc = api.require('agoraRtc');            agoraRtc.init({                appId: this.props.rtcAppId            });        },        fnjoin_channel(_userid) {            console.log('121:---' + _userid);            this.data.connected = true;            var agoraRtc = api.require('agoraRtc');            agoraRtc.joinChannelSuccessListener(function (ret) {                console.log(ret.uid + 'uid------');            });            agoraRtc.remoteUserJoinedListener((ret) => {                console.log(ret.uid + 'remoteUserJoinedListener------');                if (ret.uid) {                    this.data.connected = true;                }            });            // 多人语音通话 ,需设置角色为主播             agoraRtc.setClientRole({                role: 1            }, function (ret) {                if (ret.code == 0) {                    //success                    console.log('设置主播模式胜利')                }            });            agoraRtc.enableAudio((ret) => {                if (ret.code == 0) {                    //success                    console.log('开启音频胜利---' + this.props.channel);                    agoraRtc.joinChannel({                        channel: this.props.channel,                        uid: _userid                    }, function (ret) {                        if (ret.code == 0) {                            console.log('退出频道胜利');                        }                    });                }            });            agoraRtc.remoteUserOfflineListener((ret) => {                api.toast({                    msg: '对方已挂断'                })                this.fnhangup();            });        },        fnerr_listener() {            var agoraRtc = api.require('agoraRtc');            agoraRtc.errorListener(function (ret) {                if (ret.errorCode == 0) {                    var agoraRtc = api.require('agoraRtc');                    agoraRtc.leaveChannel(function (ret) {                        if (ret.code == 0) { //success                        }                    });                    api.toast({                        msg: '通话出错!'                    });                }            });        },        fnhangup() {            var agoraRtc = api.require('agoraRtc');            agoraRtc.leaveChannel(function (ret) {                if (ret.code == 0) {                    //success                }            });            this.data.connected = false;        }    }};</script><style>.agorartc-call-voice_page {    height: 100%;    width: 100%;    background-color: #fff;}.agorartc-call-voice_list {    height: 64px;    width: 100%;    display: flex;    flex-direction: row;    flex-wrap: nowrap;    justify-content: flex-start;    margin-bottom: 10px;}.agorartc-call-voice_userinfo {    display: flex;    flex-direction: row;    flex-wrap: nowrap;    justify-content: flex-start;    align-items: center;    padding-left: 20px;}.agorartc-call-voice_callimg {    display: flex;    flex-direction: row;    flex-wrap: nowrap;    justify-content: flex-end;    align-items: center;    flex-grow: 2;    padding-right: 20px;}.agorartc-call-voice_connected {    position: absolute;    top: 0;    left: 0;    background-color: #fff;    width: 100%;    height: 100%;    display: flex;    flex-direction: column;    justify-content: space-around;    align-items: center;}.agorartc-call-voice_hangup {    margin-top: 30px;}</style>

avm.js 默认应用 flex 弹性盒子布局,实现UI时,应充分利用 flex 弹性布局原理进行布局。而实现声网语音通话的外围逻辑很简略:两个用户退出同一个频道即可 。