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 pagediv>
3 template>

content.vue

1 <template>
2   <div>content pagediv>
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: '',
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: '',
  components: { App }
})

好了,这样,我们就可以在项目中使用我们封装的 api 接口调用文件了。

3.测试一下看看能不能调通

我们来修改一下 src/page/index.vue 文件,将代码调整为以下代码:

 1 <template>
 2   <div>index pagediv>
 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 下面有没有输出入下图一样的内容。如果有的话,就说明我们的接口配置已经成功了。

Vue2构建项目实战_第1张图片

好,如果你操作正确,代码没有格式错误的话,那么现在应该得到的结果是和我一样的。如果出错或者怎么样,请仔细的检查代码,看看有没有什么问题。

六、将接口用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 重启项目,就可以了。

如上图所示,我们可以清晰的看到,我们跑的服务,先开启了一个代理。

重新跑起来之后,我们看下我们的项目在浏览器中的表现:

Vue2构建项目实战_第2张图片

我们打开浏览器控制台,切换到 network 选项卡中,选中我们调用的接口 topics 接口,我们可以清晰的看到,我们读取的接口地址是我们的本地代理过来的地址。

状态码为 304 代表这个数据没有发生变化,直接读取本地缓存了。关于 http 状态码 的部分,请参考 百度百科 http 状态码,这里不做过多的说明。

我们再看一下数据是不是正常的过来了。切换到 Previdw 选项卡查看:

Previdw

没有问题,数据过来了。

好,到这里,我们已经顺利的将接口代理到本地,并且数据读取出来了。我们开始准备下面的工作吧!

7.初识*.vue文件,即VUE组件

*.vue 文件,是一个自定义的文件类型,用类 HTML 语法描述一个 Vue 组件。每个 .vue文件包含三种类型的顶级语言块