Cordova+Vue快速搭建Hybrid App

前言

最近項目迭代須要開發一個app,因爲項目組其餘系統前端技術棧都是Vue,因此本身在需求評估的時候就初步敲定了Cordova+Vue的前端架構,後來查閱了很多資料,也掉了很多坑,這裏總結一下,也算是對本身這段時間摸索的回顧吧。javascript

項目腳手架搭建

首先安裝nodecordova,下面是我項目的版本號 css

mac配置Android sdk,此處有 具體教程

vue-cli項目搭建

安裝vue-cli

npm install -g @vue/cli
vue init webpack vue-app
cd vue-app
npm i
複製代碼

執行成功以後項目目錄以下: 前端

以後執行 npm run dev 看到瀏覽器以下頁面,能夠說明項目搭建成功了。

cordova項目搭建

在項目同級目錄下建立cordova項目vue

執行cordova create cordova-appjava

項目總體目錄以下:node

www目錄存放編譯後的前端代碼,包括Html,CSS,JS

項目整合

下面是將vue項目編譯代碼的目錄指向cordova的www目錄,這樣就能夠實現項目整合了,vue項目負責頁面代碼編寫,cordova項目負責打包和原生接口調用。 webpack

修改以後執行npm run build 就能夠看到vue-app項目的代碼編譯打包到cordova-app的www目錄了。git

引入sass-loader

由於vue-cli默認生成的項目是不支持sass語法的,因此須要引入sass-loadergithub

npm install sass-loader node-sass webpack --save-devweb

安裝成功以後就能夠在vue組件中愉快地編寫樣式了

<style lang="scss">
    @import 'assets/style/reset.scss';
    @import 'assets/style/variable.scss';
    @import 'assets/style/common.scss';
</style>
複製代碼

抽離公共組件

項目是基於平板的應用,因此須要用到一些通用UI組件,在src目錄新建base文件夾,存放通用組件,此處以移動端經常使用的toast組件爲例,加入了transition動畫效果:

<template>
    <transition name="fade">
        <div class="wrapper" v-if="show">
            <div class="container">
                <p class="title tc">{{title}}</p>
                <p class="content tc" v-for="msg in content" :key="msg">{{msg}}</p>
                <p class="action tc" @click="confirm" v-if="type == 'toast'">{{action}}</p>
                <p class="confirm tc" v-if="type == 'confirm'">
                    <span @click="cancel">{{cancelText}}</span>
                    <span @click="ok">{{okText}}</span>
                </p>
            </div>
        </div>
    </transition>
</template>

<script>
    export default {
        // 彈窗組件
        name: 'Toast',
        props: {
            type: {
                type: String,
                default: 'toast'
            },
            show: {
                type: Boolean,
                default: false
            },
            title: {
                type: String,
                default: ''
            },
            content: {
                type: Array,
                default: null
            },
            action: {
                type: String,
                default: '肯定'
            },
            cancelText: {
                type: String,
                default: '取消'
            },
            okText: {
                type: String,
                default: '肯定'
            }
        },
        methods: {
            confirm() {
                this.$emit('confirm')
            },
            cancel() {
                this.$emit('cancel')
            },
            ok() {
                this.$emit('ok')
            }
        }
    }
</script>

<style scoped lang="scss">
    @import '../assets/style/variable.scss';
    .wrapper {
        z-index: 999;
        background-color: $black-color3;
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
        .container {
            width: 400px;
            border-radius: 4px;
            background-color: #eee;
            .title {
                color: #333;
                font-size: 28px;
                line-height: 28px;
                margin: 40px 0 20px 0;
            }
            .content {
                color: #666;
                font-size: 24px;
                line-height: 31px;
                font-weight: 200;
                padding: 0 32px;
            }
            .action, .confirm {
                border-top: 2px solid #ddd;
                height: 80px;
                line-height: 80px;
                font-size: 28px;
                color: #007AFF;
                margin-top: 40px;
            }
            .confirm {
                display: flex;
                span {
                    flex-grow: 1;
                    &:first-child {
                        border-right: 2px solid #ddd;
                        color: #333;
                    }
                }
            }
        }
    }
</style>
複製代碼

調用Cordova插件

之因此要開發成app,天然是須要調用設備原生api,cordova有至關多的插件供開發者使用,只須要安裝添加到cordova-app項目便可調用。

相似掃碼功能cordova plugin add phonegap-plugin-barcodescanner

vue-app當中調用時也很簡單:

if (window.cordova && window.cordova.plugins.barcodeScanner) {
    window.cordova.plugins.barcodeScanner.scan((result) => {
        if (result && result.text) {
            alert(result.text)
        }
    }, (err) => {
        console.log(err)
    }, {
        prompt: '', // 提示文字
        resultDisplayDuration: 0// 掃描成功文字停留時間
    })
}
複製代碼

不過,當你打包出來會發現window.cordovaundefined,其實你還漏了一步,cordova打包以後調用插件須要手動引入cordova.js,而咱們的vue代碼並無這一步操做,因此咱們須要在main.js裏面加入:

// 增長cordova文件
if (window.location.protocol === 'file:') {
    let cordovaScript = document.createElement('script')
    cordovaScript.setAttribute('type', 'text/javascript')
    cordovaScript.setAttribute('src', 'cordova.js')
    document.body.appendChild(cordovaScript)
}
複製代碼

這樣打包就大功告成了。

引入Vuex

單頁應用在共享數據上存在必定的麻煩,因此此時Vuex就登場了。

在src增長如下目錄文件:

此處咱們實現一個wifi鏈接狀態以及名稱的管理。具體看如下代碼:

getter.js

export const wifi = state => state.wifi
複製代碼

index.js

import Vue from 'vue'
import Vuex from 'vuex'
import * as getters from './getters'
import state from './state'
import mutations from './mutations'
import createLogger from 'vuex/dist/logger'

Vue.use(Vuex)

const debug = process.env.NODE_ENV !== 'production'

export default new Vuex.Store({
    getters,
    state,
    mutations,
    strict: debug,
    plugins: debug ? [createLogger()] : []
})
複製代碼

mutation-types.js

export const SET_WIFI_STATUS = 'SET_WIFI_STATUS'
export const SET_WIFI_NAME = 'SET_WIFI_NAME'
複製代碼

mutations.js

import * as types from './mutation-types'

const matutaions = {
    [types.SET_WIFI_STATUS](state, status) {
        state.wifi.status = status
    },
    [types.SET_WIFI_NAME](state, name) {
        state.wifi.name = name
    }
}

export default matutaions
複製代碼

state.js

const state = {
    wifi: {
        status: false,
        name: ''
    }
}

export default state
複製代碼

結語

代碼創造世界,世界屬於三體。後會有期。

相關文章
相關標籤/搜索