返回博客首页
← 所有文章

EloCute 的编写和重写:一个 NativeScript-Vue 故事

2018 年 3 月 29 日 — 作者:Jen Looper

对于某些人来说,学习一个新的框架需要费力地梳理书籍、Udemy 视频和逐步构建待办事项应用程序,循序渐进地完成每个更改。我,我就是那些喜欢通过构建一个打算尽快投入生产的应用程序来学习的怪人之一。虽然我确实学习了 Udemy 课程,阅读了相当多的入门博文,以及 Vue.js 文档,但我很早就决定要通过构建我一直梦想着构建的应用程序来真正深入学习 Vue.js:一个面向第二语言的教师和学生的应用程序,帮助数字化测试口语技能(例如第二语言的语音清晰度和口音完美度)的痛苦任务。

作为一名前法语语言和文学教师,以及几门外语的学生,我总是觉得坐在那些古董语言实验室里,戴着笨重的耳机和 20 世纪 80 年代(或更早!)的设备,对着麦克风练习口语是一件非常痛苦的事情。一些程序无法访问这些昂贵的硬件,因此似乎完全忽略了口语练习——例如,中文学校往往完全无法满足新语言学习者获得口语能力的愿望。缺乏即时反馈和孤立的体验似乎呼吁为语言实验室提供移动解决方案。

认识 Elocute!

Elocute 是一款面向教师的 Web 应用程序和面向学生的移动应用程序。学生在移动应用程序上创建帐户,教师在 Web 应用程序中搜索这些学生并将他们添加到他们的课堂。注册课堂后,学生可以查看教师在 Web 应用程序上创建的作业。教师可以输入一段文本供学生在移动应用程序中朗读,并且学生可以使用一个不错的界面观看语音识别插件(由插件大师 Eddy Verbruggen 开发!)在他们大声朗读时转录文本。算法会根据转录文本与教师书面文本的匹配程度快速给学生评分。通过界面,您将了解我为什么将应用程序命名为 Elocute——它是一款用于语音表达的可爱应用程序!我也喜欢海豹。

构建 Web 应用程序

对于双应用程序的初始发布,我决定不创建单体仓库,因为我们当前在 Web 和移动应用程序之间共享代码的能力不如我们的 Angular 代码共享故事成熟。因此,我通过首先构建教师的 Web 应用程序来深入学习 Vue.js。几个工具立即证明了它们的价值

  • Vue CLI:使用 Vue CLI 轻松搭建 Web 应用程序。在我们等待 CLI 3.x 版本的最终发布时,您可以使用当前版本(现在为 2.9.3)快速上手。可以选择多个模板;我想要 lint 和端到端测试,因此我选择了标准的 webpack 模板(https://github.com/vuejs-templates/webpack),因此使用命令vue init webpack elocute在我的仓库的web文件夹中搭建了我的应用程序。开始行动吧!

  • Firebase UI 库:我知道我需要身份验证并且知道我想使用 Firebase,但是当我开始这个项目时,VueFire 项目尚未更新。因此,我决定简单地将 Firebase 本身用于 Web 以及一个用于身份验证的辅助库:Firebase UI。在 Web 上,此库解决了大量的 UI 和验证问题;我强烈推荐它。只需几行代码,您就可以构建一个可靠的身份验证例程。

  • Vuetify:Vuetify 是 Web 的另一个问题解决者。我想要一个具有可预测 UI 的响应式网站,Vuetify 符合要求。Vuetify 作为构建 Vue.js 语义、材质设计风格 UI 的库而构建,提供了一种构建网站的简洁方法。安装 Vuetify 并在main.js中为其提供主题

    Vue.use(Vuetify, {
      theme: {
        primary: '#F5D1E9',
        accent: '#A5DAD2',
        secondary: '#66A59A',
        tile: '#F3F3F3',
      },
    });

    然后,开始构建您的 UI。在 Vue.js 单文件组件中的标记最终看起来像这样,用于我想要构建的卡片界面

最终结果是一个加载速度很快的 Web 应用程序,具有快速的身份验证和愉悦的用户体验

elocute-web 

构建移动应用程序

elocute-mobile 


在我需要构建移动应用程序时,我能够转向社区当前提供的几个模板,特别是用于构建“杂货店”应用程序克隆的一个模板,这是 NativeScript 用于身份验证和基本数据管理策略的基本演示应用程序。它似乎非常适合快速构建 Elocute,因为它使用后端服务、登录和注册 UI 策略、Vuex、路由、单文件组件和插件:所有这些都是 Elocute 所需的。使用此模板的开发体验可能有点不稳定,因为您需要通过两个终端运行应用程序

# terminal 1
cd groceries-ns-vue
webpack --watch --env.tns --env.android

# terminal 2
cd groceries-ns-vue/tns
tns debug android
# or
tns debug ios


此模板的一个问题是您需要更改目录到tns才能运行应用程序。每次都会让我中招!

在构建移动应用程序时证明其价值的有用工具包括使用 Firebase 插件和语音到文本插件,这两个插件均由 Eddy Verbruggen 开发。要使用 Firebase,请在main.js中导入它:import firebase from 'nativescript-plugin-firebase',然后初始化插件

firebase.init({
  onAuthStateChanged: data => { // optional
    console.log((data.loggedIn ? "Logged in to firebase" : "Logged out from firebase") + " (init's onAuthStateChanged callback)");
    if (data.loggedIn) {
      backendService.token = data.user.uid;
      store.commit('setUser', data.user)
      router.replace('/home')
    }
    else {
      backendService.token = ""
      router.replace('/login')
    }
  },
  persist: false,
}).then(() => 
  (error) => console.log(`firebase.init error: ${error}`));

然后,如果存在登录令牌,您可以强制应用程序重定向到home屏幕

mounted() {

    // force first redirect
    if (backendService.isLoggedIn()) {
      router.replace('/home')
    }
    else {
      router.replace('/login')
    }
  }
}).$start()


Vuex 也证明了其在移动应用程序中的价值。当您需要管理流经应用程序的状态和数据时,这是一个很棒的库。例如,Firebase 和 Vuex 协同工作以获取用户的课堂。

在表示层中,会提取课堂。例如,在Home.vue中,从 Vuex 导入mapStatemapActions,然后设置一个计算属性以映射用户的课堂

import { mapState, mapActions } from 'vuex';
...

computed: {
        ...mapState(['classrooms'])
  },


初始化路由时,会从存储中获取课堂

methods: {
      ...mapActions(['fetchClassrooms']),
    init() {
        this.fetchClassrooms();
    },
    ...
  }


然后查询 Firebase

fetchClassrooms: ({ state, commit }) => {
      firebase.getCurrentUser().then(        
        function (user) {          
          var path = "/Users/"+user.uid+"/Classes";
          var onValueEvent = function (result) {
            if (result.error) {
              console.log(result.error)
            } else {
              const obj = result.value;
              const classrooms = Object.keys(obj || {}).map(key => ({
                id: key,
                ClassName: obj[key].ClassName
              }));
              commit('setClassrooms', classrooms);
            }
          };
          firebase.addValueEventListener(onValueEvent, path).then(
            function (result) {
              that._listenerWrapper = result;
              console.log("firebase.addValueEventListener added");
            },
            function (error) {
              console.log("firebase.addValueEventListener error: " + error);
            }
        );
      }
    )
    }


然后,将数据提交到 Vuex 存储

setClassrooms: (state, classrooms) => {
      state.classrooms = classrooms;
    },


一旦开始使用 Firebase(或您选择的数据库)和 Vuex,您就可以真正开始使用 Vue.js 和 NativeScript 了!

是时候重写了!

虽然 Groceries 模型可以很好地帮助您使用 Webpack 友好的模板启动和运行,但这是一个提供了一些挑战性开发体验的代码库,通常需要在两个终端不同步时重新启动模拟器。是时候尝试一种新型模板了,该模板是在我在 Vue.Conf Amsterdam 上对 Elocute 进行初步演示后开发的:Vue CLI 模板。此惊人的模板由 Pascal Martineau(Slack 上的“LeWebSimple”)开发,并很快被采用作为 NativeScript-Vue 的官方“入门”模板。

在此实现中,路由更稳定,并且代码库更简单(例如,在src文件夹中编辑文件,并且在template文件夹中添加诸如图标和启动屏幕之类的资产。npm 命令负责在模拟器中运行您的应用程序:npm run watch:ios甚至只需npm run watch即可同时运行 iOS 和 Android 模拟器,前提是在您运行命令之前已打开它们。Vue CLI 模板是一种快速、简洁的构建方式。

展望未来

将来,围绕此项目的社区将仔细研究真正的代码共享选项,例如,创建单体仓库以同时构建 iOS、Android 和 Web 功能,并尽可能多地共享代码。在使用 Web 上的 Vuex 然后又在移动设备上使用 Vuex 时,很明显这两个代码库之间存在惊人的相似之处。为什么不努力将它们合并成一个呢?

一个WIP 模板正在执行此操作。我创建了一个带有海豹类型的简单演示应用程序,在 Web 上呈现为无序列表,在移动应用程序上呈现为标签的 StackLayout

shared_code 


此模板背后的架构使 webpack 能够定位 Web 或原生平台,根据目标平台切换入口点

entry.native.js中,导入 nativescript-vue 插件,在模板中创建 StackLayout,并启动应用程序

const Vue = require('nativescript-vue')
const App = require('./views/App').default
new Vue({
  template: `
    <Page>
        <StackLayout>
            <App/>
        </StackLayout>
    </Page>
  `,
  components: {
    App
  }
}).$start();


另一方面,在entry.web.js中,初始化一个典型的 Web Vue.js 应用程序并将其注入到 #app div 中

const Vue = require('vue')
const App = require('./views/App').default
new Vue({
  template: `
      <App/>
  `,
  components: {
    App
  }
}).$mount('#app');


然后,在App.vue和单文件组件中,根据所需的平台分叉模板

<template web>
    <div class="App" style="font-family: 'Roboto';">
        <h1>Types of Seals</h1>
        <SealGallery/>
    </div>
</template>
<template native>
    <StackLayout>
        <Label text="Types of Seals" style="font-size: 48;"/>
        <SealGallery/>
    </StackLayout>
</template>


这些片段由构建过程提取,并构建移动应用程序或 Web 应用程序。

此项目显示出巨大的前景,但它绝对是一个正在进行的工作!我预计将再次使用它来重建 Elocute,这次将使用正确共享的代码——以及像 Vuex 存储这样的重复部分。有兴趣帮助推动此项目向前发展吗?加入 NativeScript Slack 频道或 #vue 房间的论坛,让我们聊聊吧!

本文中各种演示的源代码在此处