vueVue项目vue

《基于Vue2.0的Web移动应用开发》- 基础

2019-04-18  本文已影响185人  健叔_31a3

目录


一、前言

本文主要描述开发一个基于vue2.0的移动应用所需的环境安装、配置、代码获取、调试、业务逻辑开发、与企业微信集成等等整个过程;通读全文后可以完成一个移动应用从无到有的搭建、开发和部署。主要用到的技术栈如下:

文中用到了开源社区基于微信团队weUI的Vux UI组件库,在此表示对Vux作者的万分感谢。


二、环境配置

2.1 Nodejs下载与安装地址
下载并安装成功后,在cmd下执行如下指令,确认是否安装成功:

C:\Users\xxx> node -v
v10.15.0

输出版本号则表明安装成功了。

本文仅举例了Windows平台的安装过程,Linux/Unix等其他操作系统请从查找其他网络资源参考。

2.2 Visual Studio Code安装地址
vscode的安装就不赘述了。安装完成后,可以一并安装vscode的下列插件:
  2.1.Vetur
  2.2.Chinese (Simplified) Language Pack for Visual Studio Code
  2.3.CSS Formatter
等等实用插件。

2.3 npm淘宝镜像配置:
在cmd中执行代码:npm install -g cnpm --registry=https://registry.npm.taobao.org
稍等几分钟,完成npm淘宝镜像的安装。

如在日常的开发工作中,发现npm或cnpm相关指令执行非常慢,甚至获取版本号都非常慢直至卡死,可以执行如下语句:npm config set registry "http://registry.npm.taobao.org/"


三、代码运行

同时按下Ctrl+鼠标点击图中http://172.xx.xx.xxx,浏览器则打开如下图的页面:

image.png

开发小技巧:在Visual Studio Code下进行开发,无需另外打开一个cmd窗口进行指令执行,可在Visual Studio Code通过点击【查看】-【终端】打开Visual Studio Code自带的cmd入口进行指令执行和监控代码编译进度等。详情参考Visual Studio Code的使用说明。


四、代码讲解

image.png

其中:

4.1 config-配置

'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')

module.exports = merge(prodEnv, {
  NODE_ENV: '"development"',
  API_ROOT:'"http://172.0.0.1:8443/yyy/rest/"',
  LOGMODE:true,
})

prod文件:\config\prod.env.js

'use strict'
module.exports = {
  NODE_ENV: '"production"',
  LOGMODE: false,
  API_ROOT: '"https://xxx.xx.xx.xxx:8443/yyy/rest/"',
}

如此,在其他上下文中,可以通过process.env.LOGMODE来访问当前环境变量下的值了。即、在npm run devprocess.env.LOGMODE=true;而在npm run buildprocess.env.LOGMODE=false;

使用实例:

//日志记录,在调试模式下才打印日期,而在build下不会打印日志。
    log(msg) {
        if (process.env.LOGMODE) {
            console.log("Gac Logger:" + msg);
        }
    },
proxyTable: {
      //配置代理,解决跨域问题 by LeoFeng 2018-9-13 09:27:15
      '/api': {
        target: 'http://172.31.254.125:8443/gacrdp/rest/', 
        changeOrigin: true, 
        pathRewrite: {
            '^/api': ''   //路径重写
        }
    }
}
...

如此一来,在dev环境下访问相关后台的api时,可以用'/api'前缀来替代出现跨域问题的后台接口前缀,nodejs将为应用处理中转。

如当前需要调用后台的getList的Rest API接口,值可以这样:

axios.get('/api/getList')
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

nodejs会把请求的/api/getList地址,中转到http://172.31.254.125:8443/gacrdp/rest/getList这个接口地址上,并最终完成数据请求。

4.2 代码结构-src

image.png

4.2.1 程序入口-main.js

公共UI组件注册:

import {
  AlertPlugin,
  AjaxPlugin, WechatPlugin, LoadingPlugin, ToastPlugin, ConfirmPlugin, Popup, PopupPicker, Popover, XButton, PopupHeader, Cell, Group, CellBox, Badge, XHeader, Search
  , XInput
} from 'vux'
Vue.component('popup', Popup)
Vue.component('popover', Popover)
Vue.component('x-button', XButton)
Vue.component('popup-header', PopupHeader)
Vue.component('cell', Cell)
Vue.component('group', Group)
Vue.component('cell-box', CellBox)
Vue.component('badge', Badge)
Vue.component('x-header', XHeader)
Vue.component('search', Search)
Vue.component('popup-picker', PopupPicker)
Vue.component('x-input', XInput)

状态管理与多语言引入:

import store from './store/index'//状态管理(全局使用) LeoFeng 2018-10-8 15:09:39
import LangEn from '../static/lang/en'
import LangZhCHS from '../static/lang/zh-CNS'
import LangZhCHT from '../static/lang/zh-CNT'
//多语言
const i18n = new VueI18n({
  locale: 'zh-CHS',
  messages: {
    'en': LangEn,
    'zh-CHS': LangZhCHS,
    'zh-CHT': LangZhCHT
  }
})

渲染挂载:

new Vue({
  router,
  i18n,
  store,
  render: h => h(App)
}).$mount('#app-box')

4.2.2 路由配置-router

本框架采用了Vue-Router进行页面导航,每一个需要展示的页面都需要在路由配置中申明。\src\router\index.js代码节选如下:

import Vue from 'vue'
import Router from 'vue-router'
import GacDefault from '@/views/Default/Default'
import DeviceMarket from '@/views/DeviceMarket/DeviceMarket'
import DeviceDetail from '@/views/DeviceDetail/DeviceDetail'
Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'default',
      meta: {
        title: '设备仓库'
      },
      component: GacDefault,
      children: [{
        path: '/',
        name: 'DeviceMarket',
        meta: {
          title: '设备仓库'
        },
        component: DeviceMarket,
        children: []
      }
      ]
    },
    {
      path: '/device-detail/:id',
      name: 'device-detail',//path跟name最好相同。
      meta: {
        title: '设备明细'
      },
      component: DeviceDetail,
      children: []
    }
  ]
})

使用方式:
普通导航方式:<router-link to="/">default</router-link>
编程式的导航[带参数]方式:this.$router.push({ name: "device-detail", params: { id } });

更多导航方式和参数获取方式请参考Vue-Router官方文档。

4.2.3 页面布局-views

应用开发的页面,集中存放在\src\views\下:

image.png
如业务上需要新增一个页面并展示出来,如名称为student的页面,步骤如下:
import Student from '@/views/Student/Student'

然后在路由根节点中申明【非嵌套路由场景】:

{
        path: '/student',
        name: 'student',
        meta: {
          title: '学生记录'
        },
        component: Student,
        children: []
}

展示时只需添加如下导航标签即可:

<router-link to="/student">学生记录</router-link>

4.2.4 组件-components

当应用的业务包含较多通用功能,且这些功能都相对独立时,我们就自然而然的会想到将这些通用的功能独立出来,写成一个通用的组件。

Vue的组件无非包含以下几个常用的概念:

当涉及到父、子组件通信时,通常采用props实现父组件向子组件传递数据;而子组件则通过$emit()上报事件到父组件,父组件再通过@event-name来捕捉子组件上报的事件。

属性-定义:代码节选自:\src\components\gacDropItem.vue

...
  props: {
    options: {
      type: [Object],//申明属性类型
      required: false//申明是否为必选属性
    },
    value: {
      default: ""//value的定义,是为了给当前组件实现v-model的功能
    }
  },
  methods: {
    onCheck: function(eqId) {
      ...
      this.$emit("input", param); //v-model必备!配合props中的value,组件的v-model功能就实现了。
      this.$emit("on-checked", param);//上报on-checked事件
    },
...

属性-插槽
例举:\src\components\gacScroller.vue

<template>
  <div class="cScroller">
    <scroller ...[省略属性设置]>
      <slot></slot><!--定义插槽-->
    </scroller>
  </div>
</template>

属性-应用
属性、事件和v-model的应用:

<gacDropItem
        v-for="(item2,index2) in item.data"
        :key="item2.equipmentId"
        :options="item2"
        v-model="toApproveEqIds[parseInt(index1.toString()+index2.toString())]"
        @on-checked="onDropItemCheck"
      >
</gacDropItem>

插槽的应用:

<gacScroller
          ref="gacScroller"
          :xHeight="'-162'"
          @on-load-more="loadMore"
          @on-refresh="refresh"
        >
          <div>
            <!--这里就是插槽的容器,放置要显示的内容-->
          </div>
</gacScroller>

注意!自定义组件的应用,首先需要完成组件的开发;其次在需要引用组件的页面引用组件:import gacDropItem from "./../../components/gacDropItem";;接着在页面中注册组件:components: { gacDropItem },;最后才参考上图进行组件的应用。

4.2.5 服务

服务,专注于应用的数据请求,主要包括:

4.2.5.1 api url 配置: \src\service\api.js,详细如下:

const api = {
  GetToken: '/api/tokens',
  GetUserInfo: '/api/meetingApi/getUserInfo',

  /************************业务系统api************************/
...
  //获取用户类型
  nvh_getUserType: '/nvh-api/getUserType',
  //获取设备分类
  nvh_getTypeList: '/nvh-api/typeList',
...
  /************************业务系统api************************/

}
export default api

4.2.5.2 API封装:

import Vue from 'vue'
import api from '../api'
import { USER_INFO } from '@/store/mutation-types'
import { axios } from '@/utils/request'

//获取类型列表
export function getTypeList(deptCode = '') {
  return axios({
    url: api.nvh_getTypeList + "?deptCode=",
    method: 'GET',
    data: {}
  })
}
...
//确认下单
export function doSubmitBorrow(params) {
  return axios({
    url: api.nvh_doSubmitBorrow ,
    method: 'POST',
    data: params
  })
}
...

4.2.5.3 API请求:

import { getEquipmentList } from "./../../service/modules/nvhDevice";
...
      getEquipmentList(this.equipmentParams).then(
        res => {
          //todo with res.data
        },
        err => {
          //todo with err
        }
      );
...

4.2.6 状态管理

无论是Vue还是React,状态管理都是一个非常重要的概念。本项目应用了Vuex的状态管理组件,需要更深入了解的请参考Vuex官方文档。以下主要讲解Vuex的应用,在项目中的代码结构如下:

image.png

状态管理主要包括:

4.2.6.1 状态定义

例举:\src\store\modules\app.js

import Vue from 'vue'
import { ACCESS_TOKEN, USER_INFO } from '@/store/mutation-types'
import { getToken, getUserInfo } from './../../service/modules/app'
const app = {
    state: {
        token: '',//最终存储Token的状态
        userinfo :'',//用户信息的状态
        ...
    },
    mutations: {
        SET_ACCESS_TOKEN: (state, _token) => {
            state.token = _token
            Vue.ls.set(ACCESS_TOKEN, _token)
        },
        ...
    },
    actions: {
        GetAccessToken({ commit }, params) {
            return new Promise((resolve, reject) => {
                console.log('innnnnnnnn Action.')
                getToken(params).then(response => {
                    console.log(JSON.stringify(response))
                    if (response.length <= 128) {
                        Vue.ls.set(ACCESS_TOKEN, response, 7 * 24 * 60 * 60 * 1000)
                        commit('SET_ACCESS_TOKEN', response)
                        console.log("get Token success.")
                    } else {
                        console.log('Token 长度明显异常。')
                    }
                    resolve()
                }).catch(error => {
                    reject(error)
                })
            })
        },
        ...
    },
    getters: {
        gCoverList: state => { return state.coverList },
        gUserInfo: state => { return state.userinfo }
    }
}

export default app

4.2.6.2 模块组合
完成上述模块的定义后,在\src\store\index.js中进行集成。

import Vue from 'vue'
import Vuex from 'vuex'

import app from './modules/app'
import wechat from './modules/wechat'
import carList from './modules/deviceCar'
import nvhDevice from './modules/nvhDevice'
Vue.use(Vuex)

export default new Vuex.Store({
    modules: {
        app,
        wechat,
        carList,
        nvhDevice
    },
    state: {

    },
    mutations: {

    },
    actions: {

    },
    getters: {

    }
})

4.2.6.3 状态应用
步骤如下:

import { mapGetters, mapActions } from "vuex";

然后,向当前上下文的methods下注册如下方法:

//具体应用-action
...mapActions(["RemoveFromCarList", "AddToCarList", "ClearCarList"]),

接着向当前上下文中的computed注册如下计算属性:

//具体应用-getters
...mapGetters(["carList", "gUserInfo"])

此时,在当前页面的上下文下,即可直接调用方法和获取属性:

//action的调用
this.RemoveFromCarList(_id);

//carList属性的使用
this.carList

另外,提交状态也可以通过直接提交(commit) Mutation来实现:

this.$store.commit("mutation-name", 'params')

getter和state也可以直接通过$store来访问:

//直接访问getter
this.$store.getters.[getter名];

//直接访问state
this.$store.state.[module-name].[state-name]

注意:状态的变更响应机制常常会给我们带来很大的便捷,但是,并不是所有的数据都有必要存储在store中,否则会大大增加系统的开销,影响性能。另外,如果一个非常小的应用,也不一定非要使用Vuex这样的状态管理框架,反倒是可以通过简单的数据总线就可以实现类似的功能。

4.2.7 模拟数据

应用在调试过程中需要很多模拟数据,集中放在\src\mock下:

image.png

在需要模拟数据的页面,添加类似引用:

import TabMenuitems from "./../../mock/app/tabMenus";

便可在上下文中直接引用这些模拟数据,如:

computed: {
    ...mapGetters(["carListCount"]),
    tabItems: () => {
      return TabMenuitems;
    }
  },

4.2.8 其他

module.exports = vuxLoader.merge(webpackConfig, {
  plugins: [
    'vux-ui',
    'progress-bar',
    ...,
    { name: 'less-theme', path: 'src/assets/theme/theme.less' }/*提供重写Vux框架皮肤功能 add by LeoFeng 2019-3-29 15:51:19*/
  ]
})

接着,在\src\assets\theme目录下新增theme.less文件,这样就可以重载vux的样式了:

/*提供重写Vux框架皮肤功能
**add by LeoFeng 2019-3-29 15:51:19
*/
@search-cancel-font-color: #80b1f0;
@search-bg-color:#EFEFF4;
@tabbar-text-active-color:#80b1f0;

@header-background-color:#fff;
@header-title-color:#333;
@header-text-color:#333;

五、业务开发


六、项目部署


七、总结

上一篇下一篇

猜你喜欢

热点阅读