Quasar 學習筆記 Ep 01 - Vue3 與 Quasar 程式架構比較

 Vue3 與 Quasar 程式架構比較

坊間其實關於 Vue.js 的書籍很多,但是很多都是由 Vue 2.0 改版過來的,所以在學習的過程中,看到的範例都程式片段,很難把所有的東西連貫起來。這篇文章的目的,就是實際使用 Visual Studio Code 一步一步地建立出樣版程式,然後以實際的操作來說明 Vue 以及 Quasar 的整體程式架構。相信對於初學者會有一定的幫助。

今天會談到以下幾點:
  1. 使用 VSC 內的終端機建立一個標準的 Vue 3 Project
  2. Vue 3 Project 程式起始點說明與簡單的導覽
  3. 使用 VSC 內的終端機建立一個標準的 Quasar Project
  4. 比較兩者差異
  5. 執行展示

以 Visual Studio Code 建立 Vue Project


要建立一個樣板 Vue Project,請先開啟cmd視窗,在這裡我直接使用 VSC 裡面的 Command Prompt 終端機亦可。然後進入project 根目錄,然後輸入指令 vue create hellovue,然後就會出現下面的詢問畫面:


由於Vue有 2, 3 兩個版本,對於新手而言,當然直接學 Vue 3吧。移動游標將 > 向下移動到 (Vue 3) ,然後按下 Enter。


接下來就會開始建置專案,請耐心等待。建置完畢之後,則會出現下面畫面:


請按照指示,cd 到專案目錄,然後執行 npm run serve,就可以在網頁上看到結果。請 copy http://localhost:8080/ 然後貼到瀏覽器上,即可執行。下圖就是執行結果,顯示一個圖示並有一些說明與鏈結。


Vue 3 的程式起始點與架構探討


使用VSC 開啟專案的方式,是直接開啟專案目錄,打開之後,所有專案目錄裡面的檔案就會呈現在左邊,我們只要看兩個目錄,public 是對外公開的網頁程式, src 則是我們的程式碼。


如果各位有寫網頁的經驗,通常網站的首頁為 index.html,這是網頁的進入點,是自動產生的,我們並不需要做修改,其中的註解已經有說明:<!-- built files will be auto injected -->

至於Vue 程式的進入點,則是 main.js,這跟 C 語言的起始點是 main() 很類似,程式內容如下:

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

主要只做兩件事情,import 根元件 App.vue 並建立 App 實體物件(Model),然後綁定到 #app 這個網頁節點(View)。也就是說,main.js 會負責將 "App" 這個元件所要顯示的內容,顯示在 index.html 裡面 <div id="app"></div> (對應到 '#app' 名稱) 這個節點裡面。main.js 也完全不需要更動程式碼,重點就在 App.vue 這個根元件之中。

<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <HelloWorld msg="Welcome to Your Vue.js App"/>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    HelloWorld
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

既然 index.html main.js 都不需更動,請把重點放在 App.vue 元件上面,由於這是第一個元件,我把它定義為根元件,其他的子元件像是 HelloWorld 則是放在 components 目錄底下。

我的建議是,遵照樣板程式的架構與寫法來撰寫我們的程式,不要嫌麻煩,也就是說,不要把所有程式都寫在App.vue裡面,而要拆分成不同的子元件,實際程式內容都寫在子元件裡面。

元件檔 (.vue) 的結構分成三個部分,<template> 是要顯示在網頁裡面的 html 內容,<style> 是拆分出來的CSS,<script> 則是程式碼。

網頁顯示的 html 內容很單純,就是<img> 標籤顯示一個圖示,以及 <HelloWorld> </HelloWorld> 我們自定義的標籤。這種大駝峰的命名在 html 的標籤裡面並不標準,所以我們也可以用 <hello-world> </hello-world> 來代替。這裡很奇妙,物件定義的名稱是 HelloWorld,但我們在html 裡面寫 hello-world 是相容的。

至於 <hello-world> 標籤做什麼事情,則是寫在 <script> 裡面。這裡只做一件事情,就是去執行 HelloWorld.vue,我們看看 HelloWorld 的內容:

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <p>
      For a guide and recipes on how to configure / customize this project,<br>
      check out the
      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
    </p>
    <h3>Installed CLI Plugins</h3>
    <ul>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
    </ul>
    <h3>Essential Links</h3>
    <ul>
      <li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
      <li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
      <li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
      <li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
      <li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
    </ul>
    <h3>Ecosystem</h3>
    <ul>
      <li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
      <li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
      <li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
      <li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
      <li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>


謎底解開了,所有要顯示的內容都在這裡。

建立一個標準的 Quasar Project

同樣的,我們在projects 根目錄底下輸入指定 quasar create helloquasar 即可建立專案。


前面的問題,都可以使用 Default 值,直接按 Enter,只有問到 ? Continue to install project dependencies after the project has been created? 的時候要注意一下,像我是使用 npm,所以我會選擇 > Yes, use NPM。

Project 建立好之後,進入 helloquasar 目錄,輸入 quasar dev,便開始編譯並執行:


Quasar 的樣板程式比Vue 多了一個東西,就是左上角有一個 "三" 按鈕,裏面包含了一個選單。這樣看起來是否更像一個完整的網頁,透過選單我們可以在不同子網頁之間切換。

接下來,Quasar程式由哪裡執行?我們必須擺脫 Vue 的思維,因為 index.html 與 main.js 都不存在,我們就跳到第一個會執行的根元件 App.vue,這裡就是程式的起點。

<template>
  <router-view />
</template>
<script>
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'App'
})
</script>

App.vue 裡面,定義了網頁實際要渲染的內容為 <router-view/>,以 vue 的架構而言,路由的定義會在 ./router 目錄中,當此目錄被執行時,第一個會被執行的檔案就是 index.js,所以,接下來,程式就會執行到 ./router/index.js。打開此檔案,你就會發現終於可以找到線索了,請看第三行。

import { route } from 'quasar/wrappers'
import { createRouter, createMemoryHistory, createWebHistory, createWebHashHistory } from 'vue-router'
import routes from './routes'

在 index.js 的第三行,又 import 了定義路由的程式 routes.js。以下是程式內容:

const routes = [
  {
    path: '/',
    component: () => import('layouts/MainLayout.vue'),
    children: [
      { path: '', component: () => import('pages/Index.vue') }
    ]
  },

  // Always leave this as last one,
  // but you can also remove it
  {
    path: '/:catchAll(.*)*',
    component: () => import('pages/Error404.vue')
  }
]

export default routes

Router 的用法我們未來的章節在討論,想像你的網頁有一個組織架構圖,最頂層的 component 就是 MainLayout.vue,下面有好幾頁,而第一頁就是 Index.vue。

先看簡單的,開啟 Index.vue 首頁元件,內容很簡單,就是顯示 <q-page> </q-page> 裡面的內容:

<template>
  <q-page class="flex flex-center">
    <img
      alt="Quasar logo"
      src="~assets/quasar-logo-vertical.svg"
      style="width: 200px; height: 200px"
    >
  </q-page>
</template>

<script>
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'PageIndex'
})
</script>


重點會是在 MainLayout.vue,它在 layouts 目錄裡面,在這裡定義了路由的 Layout:

<template>
  <q-layout view="lHh Lpr lFf">
    <q-header elevated>
      <q-toolbar>
        <q-btn
          flat
          dense
          round
          icon="menu"
          aria-label="Menu"
          @click="toggleLeftDrawer"
        />

        <q-toolbar-title>
          Quasar App
        </q-toolbar-title>

        <div>Quasar v{{ $q.version }}</div>
      </q-toolbar>
    </q-header>

    <q-drawer
      v-model="leftDrawerOpen"
      show-if-above
      bordered
    >
      <q-list>
        <q-item-label
          header
        >
          Essential Links
        </q-item-label>

        <EssentialLink
          v-for="link in essentialLinks"
          :key="link.title"
          v-bind="link"
        />
      </q-list>
    </q-drawer>

    <q-page-container>
      <router-view />
    </q-page-container>
  </q-layout>
</template>

<script>
import EssentialLink from 'components/EssentialLink.vue'

const linksList = [
  {
    title: 'Docs',
    caption: 'quasar.dev',
    icon: 'school',
    link: 'https://quasar.dev'
  },
  {
    title: 'Github',
    caption: 'github.com/quasarframework',
    icon: 'code',
    link: 'https://github.com/quasarframework'
  },
  {
    title: 'Discord Chat Channel',
    caption: 'chat.quasar.dev',
    icon: 'chat',
    link: 'https://chat.quasar.dev'
  },
  {
    title: 'Forum',
    caption: 'forum.quasar.dev',
    icon: 'record_voice_over',
    link: 'https://forum.quasar.dev'
  },
  {
    title: 'Twitter',
    caption: '@quasarframework',
    icon: 'rss_feed',
    link: 'https://twitter.quasar.dev'
  },
  {
    title: 'Facebook',
    caption: '@QuasarFramework',
    icon: 'public',
    link: 'https://facebook.quasar.dev'
  },
  {
    title: 'Quasar Awesome',
    caption: 'Community Quasar projects',
    icon: 'favorite',
    link: 'https://awesome.quasar.dev'
  }
];

import { defineComponent, ref } from 'vue'

export default defineComponent({
  name: 'MainLayout',

  components: {
    EssentialLink
  },

  setup () {
    const leftDrawerOpen = ref(false)

    return {
      essentialLinks: linksList,
      leftDrawerOpen,
      toggleLeftDrawer () {
        leftDrawerOpen.value = !leftDrawerOpen.value
      }
    }
  }
})
</script>

如何渲染出選單的項目呢?就定義在 EssentialLinks.vue 裡面。

<template>
  <q-item
    clickable
    tag="a"
    target="_blank"
    :href="link"
  >
    <q-item-section
      v-if="icon"
      avatar
    >
      <q-icon :name="icon" />
    </q-item-section>

    <q-item-section>
      <q-item-label>{{ title }}</q-item-label>
      <q-item-label caption>
        {{ caption }}
      </q-item-label>
    </q-item-section>
  </q-item>
</template>

<script>
import { defineComponent } from 'vue'

export default defineComponent({
  name: 'EssentialLink',
  props: {
    title: {
      type: String,
      required: true
    },

    caption: {
      type: String,
      default: ''
    },

    link: {
      type: String,
      default: '#'
    },

    icon: {
      type: String,
      default: ''
    }
  }
})
</script>


結論

今天的筆記單純分享 Vue 與 Quasar 的程式架構,還沒有動到程式,至於要如何在這個標準框架下寫程式,我們日後再分享。

留言

這個網誌中的熱門文章

Python可以這樣玩(16):共陰/共陽七段顯示器

Python可以這樣玩(11):數學繪圖

Python可以這樣玩(15):蜂鳴器與音樂