用weexplus从0到1写一个app

说明

基于wexplus开发app是来新公司才接触的,之前只是用过weex体验过写demo,当时就被用vue技术栈来开发app的开发体验惊艳到了,这个开发体验比react native要好很多,对于我这个纯web前端来说简直不要太好!

weexplus是基于weex官方的二次开发版本,旨在解决weex官方配置麻烦、性能不好、开发体验不好等问题。weexplus框架是这边同事根据实际的项目抽离出来的开源框架,已经帮我们趟过很多坑了,具体组件用法在此不再赘述,link-放出文档。本文仅为本人视角开发一个吉他学习app的踩坑之路记录,记下以免后面再踩坑。

文章思路

文章思路
文章可能会很长,在此分几篇文章来写,先占个坑:

先看东西

app的ui界面与h5、小程序公用一套,所以做出来的界面也是基本一样,这里感谢以下杨伯伯提供的设计稿。

爱尚吉他设计稿

环境搭建

  • node环境安装(windows、mac稍有不同,具体安装自行百度即可);
  • weex环境安装(前提是必须有node环境);
$ npm install  weex-toolkit -g // -g表示全局安装,下同
  • weexplus环境的安装,weexplus工具为我们提供了很多常用的开发功能,具体详情查看weexplus文档即可;
$ npm install weexplus -g
  • 创建项目,按照官方文档用weexplus create会遇到网速很慢的问题,我这里是下载官方的boilerplate然后改名;
$ 浏览器访问 https://github.com/weexplus/boilerplate,下载压缩包得到文件boilerplate-master.zip;
$ 解压boilerplate-master.zip得到文件夹boilerplate-master;
$ cd到跟boilerplate-master平级的目录;
$ weexplus rename loveguitar com.loveguitar.23jt loveguitar;
$ cd loveguitar
$ npm install //安装依赖包
$ npm run dev
$ weexplus start //运行改命令需要另外开一个终端,运行成功后谷歌浏览器会跳出一个新页面,必须安装谷歌浏览器
$ 下载安卓apk调试包(真机扫码调试)地址 https://pan.baidu.com/s/16kJfMuyXX-Y_yhm5fHt79Q 

至此,weexplus开发环境基本搭建完毕,如果需要打包安卓、ios的话,与原生开发一样,自行百度即可解决。

项目目录结构

weexplus项目目录结构

如图为weexplus脚手架项目目录结构图,我们平常开发主要在src目录里写代码,可以看到该目录与vue项目目录结构基本差不多。

开始写代码

在写代码前可以把脚手架里无用的代码删除掉,留下component文件夹即可。先做的第一件事个人建议是依照原型或者设计稿的业务逻辑在src/busi文件夹中按照业务模块建好文件夹(以爱尚吉他为例):

爱尚吉他业务目录结构

map:琴行地图功能模块,里面分为琴行地图首页、琴行详情、琴行导航
news:文章模块,里面分为文章列表、文章详情、标签列表
search:文章搜索模块
video:视频教程模块,里面分为视频模块首页,视频列表、视频详情

写一个首页看看

子曰:“工欲善其事,必先利其器。”在写首页的代码前在此安利一款切图标注工具--蓝湖, 大大提高设计师和开发的工作效率,具体使用参见官网介绍即可http://sos.lanhuapp.com/#/

首页效果图

以上为首页的设计图,先来分析一下页面结构,看看哪些可以复并且可以封装为公共组件。如图所示可以分为如下几个模块:

1(banner模块),在componet文件夹下新建my-banner.vue文件
2(模块标题),在componet文件夹下新建my-title.vue文件
3(菜单模块),在componet文件夹下新建my-nav.vue文件
4(文章列表模块两个类型)在componet文件夹下新建news-item.vue文件

模块分析

按照vue的规范分别在componet文件夹下新建my-banner.vuemy-title.vue·my-nav.vuenews-item.vue四个文件。

轮播图组件my-banner.vue封装

//src/component/my-banner.vue
<template>
  <div class="banner-box">
    <slider class="slider" :style="{'height':height,'width':width}" interval="1500" @change="onchange">
      <div class="frame" :style="{'height':height,'width':width}" v-for="(item,index) in bannerList" :key="index">
        <image :style="{'height':height,'width':width}" class="image" resize="cover" :src="item.pic" />
      </div>
      <indicator class="indicator"></indicator>
    </slider>
  </div>
</template>

这里用到了weex官方的sliderindicator组件,具体的属性用法参见weex文档slider用法。考虑到轮播图的尺寸不固定,在组件中暴露height(图片高度)width(图片宽度)两个属性提供给父组件传入。

在此注意几个问题:
1.注意图片标签的写法与web中使用vue稍有不同,web中是img,在weex中用image;
2.weex中不支持padding、margin、border属性值的简写,如不支持padding:10px
和border:1px solid #dcdcdc这样的写法。

标题组件my-title.vue的封装

//component/my-title.vue
<template>
  <div class="about-title">
    <text class="title-text">{{title}}</text>
    <div v-if="url!=null&&url!=''" @click="goto(url)" class="more">
      <text>更多</text>
      <image src="http://hurely.u.qiniudn.com/20180601152782173548498.png" class="form-icon-r"/>
    </div>
  </div>
</template>

样式方面没什么好说的,考虑到有些标题是没有跳转更多的,需要做一下判断为空的情况。

在此注意几个问题:
1.注意在weex中文字需要使用text标签,考虑到后续可能会移植为web或者小程序,
text标签最好用class来控制样式;
2.weex默认支持flex布局,考虑到后续可能会移植为web或者小程序,在需要
用到flex布局的地方写上display:flex属性。

菜单组件my-nav.vue的封装

//componet/my-nav.vue
<template>
  <div class="nav">
    <div class="nav-item" @click="goto(item.url)" v-for="(item,index) in navList" :key="index">
      <image class="nav-icon" :style="{'width':width,'height':height}" :src="item.src" />
      <text class="nav-text">{{item.text}}</text>
    </div>
  </div>
</template>

样式方面没什么说的,考虑到会跳转不同的页面,注意在跳转方法里做判断即可。

列表组件news-item.vue的封装

//componet/news-item.vue
<template>
  <div v-if="type==1">
    <div class="item-box" @click="click">
      <div class="item-left">
        <text class="left-text">{{item.title}}</text>
        <div class="left-line"></div>
        <text class="left-time">{{item.pubdate}}</text>
      </div>
      <div class="item-right">
        <image :src="item.pic.src" mode="aspectFill" class="litpic" />
      </div>
    </div>
  </div>
  <div class="item-box2" v-else-if="type==2"  @click="click">
    <image :src="item.pic.src" mode="aspectFill" class="litpic2" />
    <text class="box2-text">{{item.title}}</text>
  </div>
</template>

可以看到我标记4的地方有两处,在组件里加一个type作为判断即可,列表点击事件通过this.$emit传递到父组件调用。至此首页四个公共组件封装完毕,下面开始编写首页代码。

首页编写

//busi/home.vue
<template>
  <div class="app">
    <scroller>
    <banner-item :bannerList=bannerList></banner-item>
    <div class="home-nav border">
      <title-item title="热门栏目" url=""></title-item>
      <div class="nav-items">
        <div class="nav-item" v-for="(item,index) in navList" :key="index" @click="goto(item.url)">
          <image mode="aspectFit" :src="item.pic" class="nav-icon"/>
          <text class="nav-text">{{item.title}}</text>
        </div>
      </div>
    </div>
    <div class="home-news border">
      <title-item title="热门文章" url="../news/list"></title-item>
      <div v-for="(item,index) in newsItems" :key="index">
        <homenews-item
          type=1
          @click="gotonews(item.id)"
          :item="item"
        ></homenews-item>
      </div>
    </div>
    <title-item title="热门专辑" more="../video/index"></title-item>
    <div class="hot-box">
      <video-item
          v-for="(ite,index) in videoitems" :key="index"
          @click="gotovideo(ite)"
          :item="ite"
          type=2
        ></video-item>
    </div>
    <support-item></support-item>
    </scroller>
  </div>
</template>

样式方面无需说明,这里说一下数据请求的封装。分别在busi/util文件夹新建文件request.jsapi.js(非必须),其中request.js基于fly库封装(考虑到weex官方的数据请求库有点坑,在此弃用),便于管理后端接口建议在api.js文件中统一管理。

以下为fly.js库的封装,具体使用参照fly.js官方文档,如果需要增加登录拦截什么的,可以在fly.interceptors.request.use中增加即可。

//request.js
var Fly = require("flyio/dist/npm/weex");
var fly = new Fly;

//bmob云数据库的配置,非必须
const bmobConfig = {
  applicationId:'applicationId',
  restApiKey:'restApiKey',
  secretKey:'secretKey',
  masterKey:'masterKey'
}

var progress = weex.requireModule("progress")
var modal = weex.requireModule("modal")
//添加请求拦截器
fly.interceptors.request.use((request)=>{
  progress.show();
  //给所有请求添加自定义header
  request.headers["X-Tag"]="flyio";
  request.headers['X-Bmob-Application-Id'] = bmobConfig.applicationId;
  request.headers['X-Bmob-REST-API-Key'] = bmobConfig.restApiKey;
  request.headers['Content-Type'] = 'application/json';  
  //可以显式返回request, 也可以不返回,没有返回值时拦截器中默认返回request
  return request;
})

//添加响应拦截器,响应拦截器会在then/catch处理之前执行
fly.interceptors.response.use(
  (response) => {
      //只将请求结果的data字段返回
      progress.dismiss();
      return response.data
  },
  (err) => {
      //发生网络错误后会走到这里
      progress.dismiss();
      //return Promise.resolve("ssss")
  }
)

module.exports  = fly;

以下为后端接口统一管理文件api.js

/**
 * @description 请求地址
 */

const baseUrl = 'http://baidu.com/';
const urls = {
  videoList:'videoList',
  videoContent:'videoContent',
  amapGetaddress:'amapGetaddress',//高德地图经纬度转地址
  home: baseUrl + 'home',//首页
  categoryIndex:baseUrl+'categoryIndex',//菜单分类 type=list显示
  categoryList:baseUrl+'categoryList',//参数cid通过categoryIndex获得 page为分页
  tagList:baseUrl+'tagList',//标签列表&id=7656&page=1
  articleDetails:baseUrl+'articleDetails',//文章详情
  about:'about',//关于
  search:baseUrl+'search',//&q=周杰伦&page=1
};
export default urls

数据请求实例,用过axios库的应该很熟悉这种写法

getData() {
    const that = this;
    fly.get(apis.home, {})
    .then(res => {
        let bannerList = [];
        JSON.parse(res).article_hot.data.map((item,index)=>{
            item.pic = item.pic.src;
            bannerList.push(item)
        })
        that.bannerList = bannerList;
        that.newsItems = JSON.parse(res).article_list;
    })
    .catch(error => {});
}

参考文档

一点私货

基于同一套ui开发出来的爱尚吉他微信小程序版已经上线喜欢弹吉他的小伙伴可以关注一波

爱尚吉他微信小程序二维码

posted @ 2019-03-09 12:48  kung2016  阅读(706)  评论(0编辑  收藏  举报