Vue2構建項目實戰


轉載整理自http://blog.csdn.net/fungleo/article/details/77575077

vue構建單頁應用原理

SPA

不是指水療。是 single page web application 的縮寫。中文翻譯為 單頁應用程序 或 單頁Web應用。更多解釋清參看 百度百科:SPA

所有的前端人員都應該明白我們的頁面的 url 構成:

1 http://www.fengcms.com/index.html?name=fungleo&old=32#mylove/is/world/peace

 

如上的 url 由以下部分組成:

  1. http:// 規定了頁面采用的協議。
  2. www.fengcms.com 為頁面所屬的域名。
  3. index.html 為讀取的文件名稱。
  4. ?name=fungleo&old=32 給頁面通過 GET 方式傳送的參數
  5. #mylove/is/world/peace 為頁面的錨點區域

前面四個發生改變的時候,會觸發瀏覽器的跳轉亦或是刷新行為,而更改 url 中的錨點,並不會出現這種行為,因此,幾乎所有的 spa 應用都是利用錨點的這個特性來實現路由的轉換。以我們的 vue 項目為例,它的本地 url 結構一般為以下格式:

1 http://localhost:8080/#/setting/task

 

可以明顯的看到我們所謂的路由地址是在 # 號后面的,也就是利用了錨點的特性。

RESTful 風格接口

實際情況是,我們在前后端在約定接口的時候,可以約定各種風格的接口,但是,RESTful 接口是目前來說比較流行的,並且在運用中比較方便和常見的接口。

雖然它有一些缺陷,目前 github 也在主推 GraphQL 這種新的接口風格,但目前國內來說還是 RESTful 接口風格比較普遍。

並且,在掌握了 RESTful 接口風格之后,會深入的理解這種接口的優缺點,到時候,你自然會去想解決方案,並且在項目中實行新的更好的理念,所以,我這系列的博文,依然采用 http://cnodejs.org/ 網站提供的 RESTful 接口來實戰。

了解程序開發的都應該知道,我們所做的大多數操作都是對數據庫的四格操作 “增刪改查” 對應到我們的接口操作分別是:

  1. post 插入新數據
  2. delete 刪除數據
  3. put 修改數據
  4. get 查詢數據

注意,這里是我們約定,並非這些動作只能干這件事情。從表層來說,除get外的其他方法,沒有什么區別,都是一樣的。從深層來說包括 get在內的所有方法都是一模一樣的,沒有任何區別。但是,我們約定,每種動作對應不同的操作,這樣方便我們統一規范我們的所有操作。

假設,我們的接口是 /api/v1/love 這樣的接口,采用 RESTful 接口風格對應操作是如下的:


get 操作 /api/v1/love

獲取 /api/v1/love 的分頁列表數據,得到的主體,將是一個數組,我們可以用數據來遍歷循環列表

post 操作 /api/v1/love

我們會往 /api/v1/love 插入一條新的數據,我們插入的數據,將是JOSN利用對象傳輸的。

get 操作 /api/v1/love/1

我們獲取到一個 ID 為 1 的的數據,數據一般為一個對象,里面包含了 1 的各項字段信息。

put 操作 /api/v1/love/1

我們向接口提交了一個新的信息,來修改 ID 為 1 的這條信息

delete 操作 /api/v1/love/1

我們向接口請求,刪除 ID 為 1 的這一條數據


由上述例子可知,我們實現了5種操作,但只用了兩個接口地址, /api/v1/love 和 /api/v1/love/1 。所以,采用這種接口風格,可以大幅的簡化我們的接口設計。

一、安裝 nodejs 環境

你可以在 https://nodejs.org/ nodejs 官方網站下載安裝包,然后進行安裝。如果是 linux 或者 mac 命令行的用戶,也可以使用命令行安裝。

在安裝好了 nodejs 之后,我們在終端中輸入以下兩個命令:

node -v

npm -v

能夠得到版本號信息,則說明你的 nodejs 環境已經安裝完成了。

二、安裝 vue-cli VUE的腳手架工具

vue-cil是vue的腳手架工具。其模板可以通過 vuejs-templates 來查看。

我們首先安裝全局vue-cil腳手架,用於幫助搭建所需的模板框架,命令如下:

npm install -g vue-cli
# 回車,等待安裝...
vue
# 回車,若出現vue信息說明表示成功

這個命令只需要運行一次就可以了。安裝上之后,以后就不用安裝了。

下面,我們來用vue-cil構建一個項目。

首先,我們在終端中把當前目錄定位到你准備存放項目的地方。如我是准備放在~/Sites/MyWork/這個目錄下面,那么我的命令如下:

cd ~/Sites/MyWork

(也可以直接在目錄內打開終端)進入到目錄之后,我們按照下面的代碼逐條輸入,新建一個自己的vue項目,vuedemo是自己起的項目名稱

vue init webpack vuedemo
# 這里需要進行一些配置,默認回車即可
? Project name vuedemo
? Project description A Vue.js project
? Author
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? Yes
? Pick an ESLint preset Standard
? Set up unit tests Yes
? Pick a test runner jest
? Setup e2e tests with Nightwatch? Yes
? Should we run `npm install` for you after the project has been created? (reco
mmended) npm

  vue-cli · Generated "vuedemo".

# Project initialization finished!
# ========================

To get started:

cd vuedemo
npm install
npm run dev

Documentation can be found at https://vuejs-templates.github.io/webpack

如上圖所示,就說明我們的項目構建成功了。並且,給出了下面要執行的命令,我們進入項目,安裝並運行:


#在cmd輸入項目名
cd vuedemo
#回車,進入到具體項目文件夾
npm install
#回車,等待一小會兒
#回到項目文件夾,會發現項目結構里,多了一個node_modules文件夾(該文件里的內容就是之前安裝的依賴)
最后,執行下面一句,把項目跑起來
npm run dev
DONE Compiled successfully in 4388ms

> Listening at http://localhost:8080

成功執行以上命令后訪問 http://localhost:8080/,輸出結果

總上所述,安裝vue-cil后執行四行命令,就可以把一個名為vuedemo的項目跑起來。

進入項目文件夾后vue init webpack vuedemo新建一個項目

進入項目cd vuedemo

創建項目npm install

運行項目npm run dev

當然,這個名字你是可以隨便起的,根據你的實際項目來定。

通過上面的這些命令,我們就實現了新建一個vue+webpack的項目。在運行了npm run dev之后,會自動打開一個瀏覽器窗口,就可以看到實際的效果了。

JavaScript Standard

在創建項目時我們有開啟了一個standard語法效驗,JavaScript 代碼規范,自帶 linter & 代碼自動修正。

官網:https://standardjs.com/readme-zhcn.html

主要的語法規則有:

  • 使用兩個空格 – 進行縮進
  • 字符串使用單引號 – 需要轉義的地方除外
  • 不再有冗余的變量 – 這是導致 大量 bug 的源頭!
  • 無分號 – 沒什么不好。不騙你!
  • 行首不要以 ([, or ` 開頭
    • 這是省略分號時唯一會造成問題的地方 – 工具里已加了自動檢測!
    • 詳情
  • 關鍵字后加空格 if (condition) { ... }
  • 函數名后加空格 function name (arg) { ... }
  • 堅持使用全等 === 摒棄 == 一但在需要檢查 null || undefined 時可以使用 obj == null
  • 一定要處理 Node.js 中錯誤回調傳遞進來的 err 參數。
  • 文件末尾留一空行
  • 使用瀏覽器全局變量時加上 window 前綴 – document 和 navigator 除外
    •   避免無意中使用到了這些命名看上去很普通的全局變量, openlengthevent 還有 name
  • 查看更多 – 為何不試試 standard 規范呢!

三、項目目錄及文件結構

之前我們已經新建好了一個基於vue+webpack的項目。我們在 IDE(Eclipse、Atom等) 中打開該目錄,結構如下所示:

 1 ├── README.md                       // 項目說明文檔
 2 ├── node_modules                    // 項目依賴包文件夾
 3 ├── build                           // 編譯配置文件,一般不用管
 4 │   ├── build.js
 5 │   ├── check-versions.js
 6 │   ├── dev-client.js
 7 │   ├── dev-server.js
 8 │   ├── utils.js
 9 │   ├── vue-loader.conf.js
10 │   ├── webpack.base.conf.js
11 │   ├── webpack.dev.conf.js
12 │   └── webpack.prod.conf.js
13 ├── config                          // 項目基本設置文件夾
14 │   ├── dev.env.js              // 開發配置文件
15 │   ├── index.js                    // 配置主文件
16 │   └── prod.env.js             // 編譯配置文件
17 ├── index.html                      // 項目入口文件
18 ├── package-lock.json           // npm5 新增文件,優化性能
19 ├── package.json                    // 項目依賴包配置文件
20 ├── src                             // 我們的項目的源碼編寫文件
21 │   ├── App.vue                 // APP入口文件
22 │   ├── assets                      // 初始項目資源目錄,回頭刪掉
23 │   │   └── logo.png
24 │   ├── components              // 組件目錄
25 │   │   └── Hello.vue           // 測試組件,回頭刪除
26 │   ├── main.js                 // 主配置文件
27 │   └── router                      // 路由配置文件夾
28 │       └── index.js            // 路由配置文件
29 └── static                          // 資源放置目錄

 

 

如上圖所示,我們的目錄結構就是這樣的了。

我們絕大多數的操作,就是在 src 這個目錄下面。默認的 src 結構比較簡單,我們需要重新整理。

另外 static 資源目錄,我們也需要根據放置不同的資源,在這邊構建不同的子文件夾。

我們來配置 src 目錄

先不要管這些文件的內容,我們先建立這些空的文件在這邊。然后我們后面去完善它。

我們的這個項目是要做兩個頁面,一個是 cnodejs 的列表頁面,一個是詳情頁面。

所以,我把項目文件夾整理成如下的結構

 1 ├── App.vue                         // APP入口文件
 2 ├── api                             // 接口調用工具文件夾
 3 │   └── index.js                    // 接口調用工具
 4 ├── components                      // 組件文件夾,目前為空
 5 ├── config                          // 項目配置文件夾
 6 │   └── index.js                    // 項目配置文件
 7 ├── frame                           // 子路由文件夾
 8 │   └── frame.vue                   // 默認子路由文件
 9 ├── main.js                         // 項目配置文件
10 ├── page                                // 我們的頁面組件文件夾
11 │   ├── content.vue             // 准備些 cnodejs 的內容頁面
12 │   └── index.vue                   // 准備些 cnodejs 的列表頁面
13 ├── router                          // 路由配置文件夾
14 │   └── index.js                    // 路由配置文件
15 ├── style                           // scss 樣式存放目錄
16 │   ├── base                        // 基礎樣式存放目錄
17 │   │   ├── _base.scss          // 基礎樣式文件
18 │   │   ├── _color.scss     // 項目顏色配置變量文件
19 │   │   ├── _mixin.scss     // scss 混入文件
20 │   │   └── _reset.scss     // 瀏覽器初始化文件
21 │   ├── scss                        // 頁面樣式文件夾
22 │   │   ├── _content.scss       // 內容頁面樣式文件
23 │   │   └── _index.scss     // 列表樣式文件
24 │   └── style.scss              // 主樣式文件
25 └── utils                           // 常用工具文件夾
26     └── index.js                    // 常用工具文件

 

因為我們刪除了一些默認的文件,所以這個時候項目一定是報錯的,先不管他,我們根據我們的需求,新建如上的項目結構。這些都是在 src 目錄里面的結構。

四、調整app.vue和設置路由

1.調整 App.vue 文件

 我們先把默認項目里面沒用的東西先刪除掉,把代碼調整為下面的樣子。

 1 <template>
 2   <div id="app">
 3     <router-view></router-view>
 4   </div>
 5 </template>
 6 
 7 <script>
 8 export default {
 9   name: 'app'
10 }
11 </script>
12 
13 <style lang="css">
14   @import "./style/index.css";
15 </style>

入口,只有一個空的路由視窗,我們的項目的所有內容,都基於這個視窗來展現。因為scss文件容易出現語法錯誤,這里先用css文件替代。后期熟悉之后再更改

我們的樣式,都將從 src/style/index.css這個文件中引用,如果沒有請按路徑新建!

2.調整 index.vue 和 content.vue 文件

昨天,我們在 page 文件夾下面建立了兩個空文本文件 index.vue 和 content.vue 文件,是我們准備用來放列表和內容的。

這里,我們先去填寫一點基礎內容在里面。

index.vue

1 <template>
2   <div>index page</div>
3 </template>

content.vue

1 <template>
2   <div>content page</div>
3 </template>

好,寫上如上的代碼就行,我們后面再豐富這些內容。

3.調整 router 路由文件

現在,這個項目還跑不起來呢,如果你運行 npm run dev 還是會出錯的。因為我們還沒有配置路由。

 1 import Vue from 'vue'
 2 import Router from 'vue-router'
 3 import Hello from '@/components/Hello'
 4 
 5 Vue.use(Router)
 6 
 7 export default new Router({
 8   routes: [
 9     {
10       path: '/',
11       name: 'Hello',
12       component: Hello
13     }
14   ]
15 })

以上,是默認的路由文件,引用了一個 Hello 的組件,這個組件被我們昨天的博文中整理文件結構的時候刪除掉了。所以,這里就報錯啦。

我們根據我們的需要,來調整這個路由文件,如下:

 1 import Vue from 'vue'
 2 import Router from 'vue-router'
 3 import Index from '@/page/index'
 4 import Content from '@/page/content'
 5 
 6 Vue.use(Router)
 7 
 8 export default new Router({
 9   routes: [
10     {
11       path: '/',
12       component: Index
13     }, {
14       path: '/content/:id',
15       component: Content
16     }
17   ]
18 })

默認,我們的首頁,就是我們的 index.vue 組件,這里,你可能要問 :id 是什么意思?

因為我們的內容頁面是要展示N條內容的,我們如何來區分這些內容呢,就是根據ID來進行區分。所以,這里使用了動態路由匹配。

更多內容,可以參考官方文檔《動態路由匹配

好,我們現在,項目應該是沒有任何錯誤,可以跑起來了。忘記跑起來的命令了?如下:

npm run dev

五。配置axios api接口調用文件

我們通過配置基本的信息,已經讓我們的項目能夠正常的跑起來了。但是,這里還沒有涉及到 AJAX 請求接口的內容。

vue 本身是不支持 ajax 接口請求的,所以我們需要安裝一個接口請求的 npm 包,來使我們的項目擁有這個功能。

這其實是一個重要的 unix 思想,就是一個工具只做好一件事情,你需要額外的功能的時候,則需要安裝對應的軟件來執行。如果你以前是一個 jquery 重度用戶,那么可能理解這個思想一定要深入的理解。

支持 ajax 的工具有很多。一開始,我使用的是 superagent 這個工具。但是我發現近一年來,絕大多數的教程都是使用的 axios 這個接口請求工具。其實,這本來是沒有什么差別的。但是為了防止你們在看了我的博文和其他的文章之后,產生理念上的沖突。因此,我也就改用 axios 這個工具了

 本身, axios 這個工具已經做了很好的優化和封裝。但是,在使用的時候,還是略顯繁瑣,因此,我重新封裝了一下。當然,更重要的是,封裝 axios 這個工具是為了和我以前寫的代碼的兼容。不過我封裝得很好,也推薦大家使用。

1.封裝 axios 工具,編輯 src/api/index.js 文件

首先,我們要使用 axios 工具,就必須先安裝 axios 工具。執行下面的命令進行安裝

npm install axios -D

這樣,我們就安裝好了 axios 工具了。

還記得我們在第三篇博文中整理的系統結構嗎?我們新建了一個 src/api/index.js 這個空文本文件,就那么放在那里了。這里,我們給它填寫上內容。

// 配置API接口地址
var root = 'https://cnodejs.org/api/v1'
// 引用axios
var axios = require('axios')
// 自定義判斷元素類型JS
function toType (obj) {
  return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}
// 參數過濾函數
function filterNull (o) {
  for (var key in o) {
    if (o[key] === null) {
      delete o[key]
    }
    if (toType(o[key]) === 'string') {
      o[key] = o[key].trim()
    } else if (toType(o[key]) === 'object') {
      o[key] = filterNull(o[key])
    } else if (toType(o[key]) === 'array') {
      o[key] = filterNull(o[key])
    }
  }
  return o
}
/*
  接口處理函數
  這個函數每個項目都是不一樣的,我現在調整的是適用於
  https://cnodejs.org/api/v1 的接口,如果是其他接口
  需要根據接口的參數進行調整。參考說明文檔地址:
  https://cnodejs.org/topic/5378720ed6e2d16149fa16bd
  主要是,不同的接口的成功標識和失敗提示是不一致的。
  另外,不同的項目的處理方法也是不一致的,這里出錯就是簡單的alert
*/

function apiAxios (method, url, params, success, failure) {
  if (params) {
    params = filterNull(params)
  }
  axios({
    method: method,
    url: url,
    data: method === 'POST' || method === 'PUT' ? params : null,
    params: method === 'GET' || method === 'DELETE' ? params : null,
    baseURL: root,
    withCredentials: false
  })
  .then(function (res) {
    if (res.data.success === true) {
      if (success) {
        success(res.data)
      }
    } else {
      if (failure) {
        failure(res.data)
      } else {
        window.alert('error: ' + JSON.stringify(res.data))
      }
    }
  })
  .catch(function (err) {
    let res = err.response
    if (err) {
      window.alert('api error, HTTP CODE: ' + res.status)
    }
  })
}

// 返回在vue模板中的調用接口
export default {
  get: function (url, params, success, failure) {
    return apiAxios('GET', url, params, success, failure)
  },
  post: function (url, params, success, failure) {
    return apiAxios('POST', url, params, success, failure)
  },
  put: function (url, params, success, failure) {
    return apiAxios('PUT', url, params, success, failure)
  },
  delete: function (url, params, success, failure) {
    return apiAxios('DELETE', url, params, success, failure)
  }
}

有關 axios 的更多內容,請參考官方 github: https://github.com/mzabriskie/axios ,中文資料自行百度。

但就是這樣,我們還不能再 vue 模板文件中使用這個工具,還需要調整一下 main.js 文件。

2.調整 main.js 綁定 api/index.js 文件

這次呢,我們沒有上來就調整 main.js 文件,因為原始文件就配置得比較好,我就沒有刻意的想要調整它。

原始文件如下:

 1 import Vue from 'vue'
 2 import App from './App'
 3 import router from './router'
 4 
 5 Vue.config.productionTip = false
 6 
 7 /* eslint-disable no-new */
 8 new Vue({
 9   el: '#app',
10   router,
11   template: '<App/>',
12   components: { App }
13 })

我們插入以下代碼:

1 // 引用API文件
2 import api from './api/index.js'
3 // 將API方法綁定到全局
4 Vue.prototype.$api = api

也就是講代碼調整為:

import Vue from 'vue'
import App from './App'
import router from './router'

// 引用API文件
import api from './api/index.js'
// 將API方法綁定到全局
Vue.prototype.$api = api

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  template: '<App/>',
  components: { App }
})

好了,這樣,我們就可以在項目中使用我們封裝的 api 接口調用文件了。

3.測試一下看看能不能調通

我們來修改一下 src/page/index.vue 文件,將代碼調整為以下代碼:

 1 <template>
 2   <div>index page</div>
 3 </template>
 4 <script>
 5 export default {
 6   created () {
 7     this.$api.get('topics', null, r => {
 8       console.log(r)
 9     })
10   }
11 }
12 </script>

好,這里是調用 cnodejs.org 的 topics 列表接口,並且將結果打印出來。

我們在瀏覽器中打開控制台,看看 console 下面有沒有輸出入下圖一樣的內容。如果有的話,就說明我們的接口配置已經成功了。

好,如果你操作正確,代碼沒有格式錯誤的話,那么現在應該得到的結果是和我一樣的。如果出錯或者怎么樣,請仔細的檢查代碼,看看有沒有什么問題。

六、將接口用webpack代理到本地

我們已經非常順利的調用到了 cnodejs.org 的接口了。但是,我們可以注意到我們的 src/api/index.js 的第一句,就是:

1 // 配置API接口地址
2 var root = 'https://cnodejs.org/api/v1'

這里,我們將接口地址寫死了。

當然,這並不是最重要的事情,而是 cnodejs.org 幫我們把接口處理得很好,解決了跨域的問題。而我們的實際項目中,很多接口都是不允許我們這樣跨域請求的。

而我們的開發環境下,不可能跑到服務器上直接開發,或者在本地直接搞一個服務器環境,這樣就違背了我們前后端分離開發的初衷了。

如何解決這個問題呢?其實非常好辦,要知道 跨域不是接口的限制 而是瀏覽器為了保障數據安全做的限制。因此,一種方法可以解決,那就是打開瀏覽器的限制,讓我們可以順利的進行開發。但是無奈的是,最新的 chrome 瀏覽器好像已經關閉了這個選項,那么我們只能采用另外一種方法了——將接口代理到本地。

1.配置 webpack 將接口代理到本地

好在,vue-cli 腳手架工具,已經充分的考慮了這個問題,我們只要進行簡單的設置,就可以實現我們的目的。

我們打開 config/index.js 文件,找到以下代碼:

1   dev: {
2     env: require('./dev.env'),
3     port: 8080,
4     autoOpenBrowser: true,
5     assetsSubDirectory: 'static',
6     assetsPublicPath: '/',
7     proxyTable: {},
8     cssSourceMap: false
9   }

其中,proxyTable: {}, 這一行,就是給我們配置代理的。

根據 cnodejs.org 的接口,我們把這里調整為:

1 proxyTable: {
2   '/api/v1/**': {
3     target: 'https://cnodejs.org', // 你接口的域名
4     secure: false,
5     changeOrigin: false,
6   }
7 }

OK,我們這樣配置好后,就可以將接口代理到本地了。

更多接口參數配置,請參考 https://github.com/chimurai/http-proxy-middleware#options

webpack 接口配置文檔 https://webpack.js.org/configuration/dev-server/#devserver-proxy

2.重新配置 src/api/index.js 文件

好,上面已經代理成功了,但是我們的 src/api/index.js 文件,還是直接調用的人家的地址呢,我們要調整為我們的地址,調整如下:

1 // 配置API接口地址
2 var root = '/api/v1'

之前我有一篇博文,說過如何配置開發接口地址和生產接口地址,當時是利用了 webpack 的不同的配置文件來進行配置的。如果我們采用這種代理模式呢,那么就沒有必要那么做了。因為我們的系統放到生產環境的時候,一般是沒有這個跨域問題的。這個問題一般僅僅是存在於我們的開發環境下面。

值得注意的事情是,配置完成后,是不會立即生效的,我們需要重啟我們的項目。

我們按 ctrl + c 停止掉之前的服務,然后重新輸入命令 npm run dev 重啟項目,就可以了。

如上圖所示,我們可以清晰的看到,我們跑的服務,先開啟了一個代理。

重新跑起來之后,我們看下我們的項目在瀏覽器中的表現:

我們打開瀏覽器控制台,切換到 network 選項卡中,選中我們調用的接口 topics 接口,我們可以清晰的看到,我們讀取的接口地址是我們的本地代理過來的地址。

狀態碼為 304 代表這個數據沒有發生變化,直接讀取本地緩存了。關於 http 狀態碼 的部分,請參考 百度百科 http 狀態碼,這里不做過多的說明。

我們再看一下數據是不是正常的過來了。切換到 Previdw 選項卡查看:

Previdw

沒有問題,數據過來了。

好,到這里,我們已經順利的將接口代理到本地,並且數據讀取出來了。我們開始准備下面的工作吧!

7.初識*.vue文件,即VUE組件

*.vue 文件,是一個自定義的文件類型,用類 HTML 語法描述一個 Vue 組件。每個 .vue文件包含三種類型的頂級語言塊 <template><script> 和 <style>。這三個部分分別代表了 html,js,css

其中 <template> 和 <style> 是支持用預編譯語言來寫的。比如,在我們的項目中,我們可以用 scss 預編譯,我們可以這樣寫的:

1 <style lang="scss">

html 也有自己的預編譯語言, vue 也是支持的,不過一般來說,我們前端人員還是比較中意 html 原生語言,所以,我就不過多闡述了。

另外,我在 App.vue 文件中,已經用一句 @import "./style/style"; 將我們的樣式給寫到指定的地方去了。所以,在后面所有的我的文章中,是不會出現這個部分的內容的。所有樣式,都會在 src/style/ 文件夾中對應的位置去寫。 
我這樣做的好處是,不需要重復的引入各種 scss 基礎文件,並且做到了項目的樣式代碼的可管控

1.一個常見的 *.vue 文件代碼解析

首先,我們來簡單看一下:

 1 <template>
 2   <div>
 3     <Header></Header>
 4     <div class="article_list">
 5       <ul>
 6         <li></li>
 7       </ul>
 8     </div>
 9     <Footer></Footer>
10   </div>
11 </template>
12 <script>
13 import Header from '../components/header.vue'
14 import Footer from '../components/footer.vue'
15 export default {
16   components: { Header, Footer },
17   data () {
18     return {
19       list: []
20     }
21   },
22   created () {
23     this.getData()
24   },
25   methods: {
26     getData () {
27       this.$api.get('topics', null, r => {
28         console.log(r)
29       })
30     }
31   }
32 }
33 </script>
34 <style>
35   .article_list {margin: auto;}
36 </style>

好,以上就是一個簡單的 *.vue 文件的基本結構。我們一部分一部分的來解釋。

以下,我不再稱呼它為 *.vue 文件了。改成為 vue 組件。

2.template 部分

首先,一個 vue 組件,他的 template 則代表它的 html 結構,相信大家可以理解了。但是需要注意的是,我們不是說把代碼包裹在 <template></template> 中就可以了,而是必須在里面放置一個 html 標簽來包裹所有的代碼。 本例子中,我們采用了 <div></div> 標簽。vue2.x 開始,就必須這樣去寫。這是規定

大家看到 <Header></Header> 這個代碼的時候肯定很奇怪,這是個什么玩意兒。其實,這是一個自定義組件。我們在其他地方寫好了一個組件,然后就可以用這種方式引入進來。

同樣 <Footer></Footer> 也是一個組件。不再累述。

其他的內容,我們就想這么寫就怎么寫了。和寫我們的 html 代碼沒有什么太大的區別。

3.script 部分

首先,我們需要兩個自定義組件,我們先引用進來。如下格式,比較好理解吧。

1 import Header from '../components/header.vue'
2 import Footer from '../components/footer.vue'

其次,除了引用的文件,我們將所有的代碼包裹於如下的代碼中間:

1 export default {
2   // 這里寫你的代碼,外面要包起來。
3 }

4.style 部分

這里比較簡單,就是針對我們的 template 里內容出現的 html 元素寫一些樣式。如上,我的代碼:

1 <style>
2   .article_list {margin: auto;}
3 </style>

就是給我們的 .article_list 元素隨便加了一個樣式。

參考資料:《vue 模板語法

八、渲染列表

1.制作 header.vue 和 footer.vue 組件文件。

不是本篇文章的重點,但是還是有比較講一下。在第三篇博文中,我們規划了我們的項目文件結構,當時保留了一個 components 的空文件夾。這里,就是准備放我們的自定義組件的。

首先,我們去創建兩個空文本文件,分別是 header.vue 文件和 footer.vue 文件。

然后,往里面輸入下面的內容:

header.vue

1 <template>
2   <header class="header">
3     <h1 class="logo">Vue Demo by DEMO</h1>
4   </header>
5 </template>

footer.vue

1 <template>
2   <footer class="copy">
3     Copy &copy; DEMO
4 </footer> 5 </template>

非常簡單的兩個文件,表示我們的組件已經弄好了

2.編寫 src/page/index.vue 文件

 1 <template>
 2   <div>
 3     <Header></Header>
 4     <div class="article_list">
 5       <ul>
 6         <li v-for="i in list" :key="i.id">
 7           <time v-text="i.create_at"></time>
 8           <router-link :to="'/content/' + i.id">
 9             {{ i.title }}
10           </router-link>
11         </li>
12       </ul>
13     </div>
14     <Footer></Footer>
15   </div>
16 </template>
17 <script>
18 import Header from '../components/header.vue'
19 import Footer from '../components/footer.vue'
20 export default {
21   components: { Header, Footer },
22   data () {
23     return {
24       list: []
25     }
26   },
27   created () {
28     this.getData()
29   },
30   methods: {
31     getData () {
32       this.$api.get('topics', null, r => {
33         this.list = r.data
34       })
35     }
36   }
37 }
38 </script>

如上,代碼,我就把頁面渲染出來了。我們看下實際的效果:

如上,我先用了一個 v-for 的循環,來循環數據。這里可以參考:https://cn.vuejs.org/v2/api/#v-for 文檔。

在 time 標簽中,我使用了 v-text="i.create_at" 來渲染時間數據。參考文檔: https://cn.vuejs.org/v2/api/#v-text

router-link 是 VueRouter2 “聲明式導航”的寫法,在實際轉換為 html 標簽的時候,會轉化為 <a></a>,里面的 to 屬性,就相當於 a 的 href 屬性。參考文檔:https://router.vuejs.org/zh-cn/essentials/getting-started.html

3.編寫 src/utils/index.js 文件

如上面的圖片所示,由於拿到的數據是一個標准的時間格式,直接渲染在頁面上,這個效果不是很理想。因此,我們可以把時間給處理一下,然后再渲染出來。

這里,我們可以直接在 getData () {...} 后面再寫一個方法即可。但是,在一個項目中,如果所有的地方都是這樣的時間格式,我們在每一個組件中都來寫這樣的處理方法,很顯然就顯得我們比較愚蠢了。

因此,我們可以獨立出來寫一個方法,然后在所有的地方都可以使用,這樣就比較方便了。

還記得我們在第三篇博文中,我們建立了一個 src/utils/index.js 的空文本文件嗎?這里,我們要用上了。

直接給代碼如下:

 1 export default {
 2   goodTime (str) {
 3     let now = new Date().getTime()
 4     let oldTime = new Date(str).getTime()
 5     let difference = now - oldTime
 6     let result = ''
 7     let minute = 1000 * 60
 8     let hour = minute * 60
 9     let day = hour * 24
10     let month = day * 30
11     let year = month * 12
12     let _year = difference / year
13     let _month = difference / month
14     let _week = difference / (7 * day)
15     let _day = difference / day
16     let _hour = difference / hour
17     let _min = difference / minute
18 
19     if (_year >= 1) {
20       result = '發表於 ' + ~~(_year) + ' 年前'
21     } else if (_month >= 1) {
22       result = '發表於 ' + ~~(_month) + ' 個月前'
23     } else if (_week >= 1) {
24       result = '發表於 ' + ~~(_week) + ' 周前'
25     } else if (_day >= 1) {
26       result = '發表於 ' + ~~(_day) + ' 天前'
27     } else if (_hour >= 1) {
28       result = '發表於 ' + ~~(_hour) + ' 個小時前'
29     } else if (_min >= 1) {
30       result = '發表於 ' + ~~(_min) + ' 分鍾前'
31     } else {
32       result = '剛剛'
33     }
34     return result
35   }
36 }

好,代碼惡心了點,我拿我以前寫的代碼改的,沒有深入優化,大家就隨便看看,大概就是這么個東西。

寫好代碼之后,我們保存文件。但是此時,我們還不能使用我們的這個方法函數。我們必須在 main.js 中將我們的方法函數給綁定上。如下代碼:

1 // 引用工具文件
2 import utils from './utils/index.js'
3 // 將工具方法綁定到全局
4 Vue.prototype.$utils = utils

好了,這樣,我們寫的這個函數,就可以隨便被我們調用了。我們再來修改一下我們上面的 index.vue 中的代碼,將 time 調整為:

1 <time v-text="$utils.goodTime(i.create_at)"></time>

然后,我們再來看一下實際的效果:

vue demo by fungleo

好,我們已經看到,時間已經搞的挺好的了。

九、把內容頁面渲染出來

前面我們從文章中留下的引子 <router-link :to="'/content/' + i.id"> 應該知道,我們還是要做內容頁面的。

1.編寫內容頁面

 1 <template>
 2   <div>
 3     <myHeader></myHeader>
 4     <h2 v-text="dat.title"></h2>
 5     <p v-if="dat.author">作者:{{dat.author.loginname}}發表於:{{$utils.goodTime(dat.create_at)}}</p>
 6     <hr>
 7     <article v-html="dat.content"></article>
 8     <h3>網友回復:</h3>
 9     <ul>
10       <li v-for="i in dat.replies">
11         <p>評論者:{{i.author.loginname}}評論於:{{$utils.goodTime(i.create_at)}}</p>
12         <article v-html="i.content"></article>
13       </li>
14     </ul>
15     <myFooter></myFooter>
16   </div>
17 </template>
18 <script>
19 import myHeader from "../components/header.vue";
20 import myFooter from "../components/footer.vue";
21 export default {
22   components: { myHeader, myFooter },
23   data() {
24     return {
25       id: this.$route.params.id,
26       dat: {}
27     };
28   },
29   created() {
30     this.getData();
31   },
32   methods: {
33     getData() {
34       this.$api.get("topic/" + this.id, null, r => {
35         this.dat = r.data;
36       });
37     }
38   }
39 };
40 </script>

好,我們這邊把代碼寫進 src/page/content.vue 文件。然后保存,我在我們先前的列表頁面隨便點開一篇文章,然后我們看下結果:

十、打包項目並發布到子目錄

我們已經完成了我們設想的項目的開發。但是,我們做好的這套東西,是基於 nodejs 開發的。而我們最終希望,我們開發的項目,生成好一堆文件,然后隨便通過任何一個 http 服務就能跑起來,也就是,還原成我們熟悉的 html+css+js 的模式。

1.打包項目

們進入到終端,並且進入到我們的開發目錄:

 cd ~/Site/MyWork/vue-demo-cnodejs

然后運行如下代碼,進行打包:

pm run build

運行結果如下:

 

好,我們已經打包好了。文件打包位置於項目目錄里面的 dist 文件夾內。

但是,我們從上圖可以看到,我們生成了一些 .map 的文件。這就有點惡心了。當我們的項目變得比較大的時候,這些文件,第一個是,非常大,第二個,編譯時間非常長。所以,我們要把這個文件給去掉。

我們編輯 /config/index.js 文件,找到其中的

1 productionSourceMap: true,

修改為:

1 productionSourceMap: false,

然后我們重新運行打包命令:

npm run build

 

沒用的 map 文件已經沒有了。

好,我們可以從上圖中看出,有一個 tip 。它告訴我們,打包出來的文件,必須在 http 服務中運行,否則,不會工作。

2.安裝 http-server 啟動 http 服務

我們進入 dist 文件夾,然后啟動一個 http 服務,來看看可以不可以訪問。

你可能不知道如何啟動這樣一個 http 服務,或者,你現在已經到 apache 里面去進行配置去了。不用那么麻煩,我們有 nodejs 環境,只要全局安裝一個 http-server 服務就好了呀。

npm install http-server -g

好,通過這個命令,我們就安裝好了。安裝好了之后正常我們就能夠使用 http-server 命令來跑服務了。但是,這個世界不正常的時候是很多的嘛!

在終端里面輸入,

http-server

看能否正常啟動,還是爆 -bash: http-server: command not found 錯誤,這里,是說沒有找到這個命令,沒有關系,這是表示,我們的 nodejs 的程序執行路徑,沒有添加到環境變量中去。

首先,如上圖所示,我們的 http-server 安裝到了 /usr/local/Cellar/node/7.6.0/bin/ 這個目錄下面,我們只要把這個目錄,添加到環境變量即可。

請注意,你的安裝路徑可能和我的是不一致的,請注意調整。

我們在終端內執行下面兩個命令,就可以了。

echo 'export PATH="$PATH:/usr/local/Cellar/node/7.6.0/bin/"' >> ~/.bash_profile

. ~/.bash_profile

第一條命令是追加環境變量,第二個命令是,使我們的追加立即生效。

當然,你也可以直接編輯 ~/.bash_profile 文件,把上面命令中的單引號里面的內容插入到文件最后面,然后執行第二個命令生效。隨便。

好,一個插曲結束。我們要把我們打包出來的東西跑起來呀!

cd dist

http-server -p 3000

如果你是嚴格按照我的教程來的,那么現在已經可以順利的跑起來了。我們在瀏覽器中輸入 http://127.0.0.1:3000 就應該可以訪問了。

當然,會報錯,說是接口找不到,404錯誤。因為我們把接口給通過代理的方式開啟到了本地,但是這里我們並沒有開啟代理,所以就跑不起來了。很正常的。

這是因為示例的接口的問題。實際開發你還要按照我的這個做。只不過,最終代碼放到真實的服務器環境去和后端接口在一個 http 服務下面的話,就不存在這個問題了。

好,我們就跑起來了。

3.將項目打包到子目錄

剛剛,我們是將文件,打包為根目錄訪問的。也就是說,必須在 dist 文件夾下面啟動一個服務,才能把項目跑起來。

但是我們開發的大多數項目,可能是必須跑在二級目錄,甚至更深層次的目錄的。怎么做呢?

我們編輯 config/index.js 文件,找到:

assetsPublicPath: '/',

把 '/' 修改為你要放的子目錄的路徑就行了。這里,我要放到 /dist/ 目錄下面。於是,我就把這里修改為

1 assetsPublicPath: '/dist/',

然后,重新運行

 

npm run build

進行打包。

很快,就打包好了。

還記得,我們在項目文件夾中用 npm run dev 就可以開啟一個 http 服務嗎?並且那里,我們還代理了接口的。

好,我們就這么做。

然后我們訪問二級目錄 /dist/ 我們就可以看到效果了。

注意,我訪問的不是根目錄,而是 /dist/ 這個子目錄哦,這里是訪問的我們打包的文件的。

1 ├── index.html
2 └── static
3     ├── css
4     │   └── app.d41d8cd98f00b204e9800998ecf8427e.css
5     └── js
6         ├── app.8ffccad49e36e43a4e9b.js
7         ├── manifest.7a471601ff5a8b26ee49.js
8         └── vendor.057dd4249604e1e9c3b5.js

好,到這里,我們的打包工作,就講完了。

實際開發中,你只需要把 dist 文件夾中打包好的文件,給運維他們,讓他們去部署到真實的服務器環境就好了。

十一、打包項目圖片等資源的處理

1.在 vue 文件中,引用圖片

例如,我們將一張圖片放到資源目錄 /static/image/lyf.jpg 我們在 vue 文件中用下面的代碼來使用這張圖片。

1 <img src="static/image/lyf.jpg" alt="劉亦菲">

注意,最前面不要加 / 如果是這樣操作的話,會變成相對根目錄調用圖片。如果你的項目要打包到子目錄的話,這樣做就會出現問題。

2.在 css 文件中,引用圖片的處理

還是上面那張圖片,我們需要在 css 中來引用,如何來寫呢?

.love {
  background-image: url('../static/image/lyf.jpg');
}

好,這里為什么要加上 ../ 呢?

如果是最終打包到根目錄的話,可以使用 / 這種路徑。這個是完全可以理解的。

但,如果是打包到子目錄,我們必須看下生成的最終路徑:

├── index.html
└── static
    ├── css
    │   └── app.a7a745952a8ca7f8c9413d53b431b8c8.css
    ├── image
    │   └── lyf.jpg
    ├── img
    │   └── lyf.9125a01.jpg
    └── js
        ├── app.39ccc604caeb34166b49.js
        ├── manifest.b1ad113c36e077a9b54d.js
        └── vendor.0b8d67613e49db91b787.js

如上,我們可以看到這個 css 相對 圖片 的路徑的地址。

你要疑問了,這樣的相對路徑,我們可以使用 ../image/lyf.jpg 來進行調用呀。嗯,看上去可以,但是,如果是這樣的話,在開發模式中又會出錯了。

所以,還是要用 '../static/image/lyf.jpg' 這樣的路徑方式來調用圖片。

字體圖標,js 文件等,都是這樣的路數。不在贅述。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM