Vue2构建项目实战
转载整理自http://blog.csdn.net/fungleo/article/details/77575077
vue构建单页应用原理
SPA
不是指水疗。是 single page web application
的缩写。中文翻译为 单页应用程序 或 单页Web应用。更多解释清参看 百度百科:SPA
所有的前端人员都应该明白我们的页面的 url
构成:
http://www.fengcms.com/index.html?name=fungleo&old=32#mylove/is/world/peace
如上的 url
由以下部分组成:
http://
规定了页面采用的协议。www.fengcms.com
为页面所属的域名。index.html
为读取的文件名称。?name=fungleo&old=32
给页面通过GET
方式传送的参数#mylove/is/world/peace
为页面的锚点区域
前面四个发生改变的时候,会触发浏览器的跳转亦或是刷新行为,而更改 url
中的锚点,并不会出现这种行为,因此,几乎所有的 spa
应用都是利用锚点的这个特性来实现路由的转换。以我们的 vue
项目为例,它的本地 url
结构一般为以下格式:
http://localhost:8080/#/setting/task
可以明显的看到我们所谓的路由地址是在 #
号后面的,也就是利用了锚点的特性。
RESTful 风格接口
实际情况是,我们在前后端在约定接口的时候,可以约定各种风格的接口,但是,RESTful
接口是目前来说比较流行的,并且在运用中比较方便和常见的接口。
虽然它有一些缺陷,目前 github
也在主推 GraphQL
这种新的接口风格,但目前国内来说还是 RESTful
接口风格比较普遍。
并且,在掌握了 RESTful
接口风格之后,会深入的理解这种接口的优缺点,到时候,你自然会去想解决方案,并且在项目中实行新的更好的理念,所以,我这系列的博文,依然采用 http://cnodejs.org/
网站提供的 RESTful
接口来实战。
了解程序开发的都应该知道,我们所做的大多数操作都是对数据库的四格操作 “增删改查” 对应到我们的接口操作分别是:
post
插入新数据delete
删除数据put
修改数据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
除外- 避免无意中使用到了这些命名看上去很普通的全局变量,
open
,length
,event
还有name
。
- 避免无意中使用到了这些命名看上去很普通的全局变量,
- 查看更多 – 为何不试试
standard
规范呢!
三、项目目录及文件结构
之前我们已经新建好了一个基于vue+webpack
的项目。我们在 IDE(Eclipse、Atom等) 中打开该目录,结构如下所示:
├── README.md // 项目说明文档
├── node_modules // 项目依赖包文件夹
├── build // 编译配置文件,一般不用管
│ ├── build.js
│ ├── check-versions.js
│ ├── dev-client.js
│ ├── dev-server.js
│ ├── utils.js
│ ├── vue-loader.conf.js
│ ├── webpack.base.conf.js
│ ├── webpack.dev.conf.js
│ └── webpack.prod.conf.js
├── config // 项目基本设置文件夹
│ ├── dev.env.js // 开发配置文件
│ ├── index.js // 配置主文件
│ └── prod.env.js // 编译配置文件
├── index.html // 项目入口文件
├── package-lock.json // npm5 新增文件,优化性能
├── package.json // 项目依赖包配置文件
├── src // 我们的项目的源码编写文件
│ ├── App.vue // APP入口文件
│ ├── assets // 初始项目资源目录,回头删掉
│ │ └── logo.png
│ ├── components // 组件目录
│ │ └── Hello.vue // 测试组件,回头删除
│ ├── main.js // 主配置文件
│ └── router // 路由配置文件夹
│ └── index.js // 路由配置文件
└── static // 资源放置目录
如上图所示,我们的目录结构就是这样的了。
我们绝大多数的操作,就是在 src
这个目录下面。默认的 src
结构比较简单,我们需要重新整理。
另外 static
资源目录,我们也需要根据放置不同的资源,在这边构建不同的子文件夹。
我们来配置 src 目录
先不要管这些文件的内容,我们先建立这些空的文件在这边。然后我们后面去完善它。
我们的这个项目是要做两个页面,一个是 cnodejs
的列表页面,一个是详情页面。
所以,我把项目文件夹整理成如下的结构
├── App.vue // APP入口文件
├── api // 接口调用工具文件夹
│ └── index.js // 接口调用工具
├── components // 组件文件夹,目前为空
├── config // 项目配置文件夹
│ └── index.js // 项目配置文件
├── frame // 子路由文件夹
│ └── frame.vue // 默认子路由文件
├── main.js // 项目配置文件
├── page // 我们的页面组件文件夹
│ ├── content.vue // 准备些 cnodejs 的内容页面
│ └── index.vue // 准备些 cnodejs 的列表页面
├── router // 路由配置文件夹
│ └── index.js // 路由配置文件
├── style // scss 样式存放目录
│ ├── base // 基础样式存放目录
│ │ ├── _base.scss // 基础样式文件
│ │ ├── _color.scss // 项目颜色配置变量文件
│ │ ├── _mixin.scss // scss 混入文件
│ │ └── _reset.scss // 浏览器初始化文件
│ ├── scss // 页面样式文件夹
│ │ ├── _content.scss // 内容页面样式文件
│ │ └── _index.scss // 列表样式文件
│ └── style.scss // 主样式文件
└── utils // 常用工具文件夹
└── index.js // 常用工具文件
因为我们删除了一些默认的文件,所以这个时候项目一定是报错的,先不管他,我们根据我们的需求,新建如上的项目结构。这些都是在 src
目录里面的结构。
四、调整app.vue和设置路由
1.调整 App.vue 文件
我们先把默认项目里面没用的东西先删除掉,把代码调整为下面的样子。
<template>
<div id="app">
<router-view></router-view>
</div>
</template> <script>
export default {
name: 'app'
}
</script> <style lang="css">
@import "./style/index.css";
</style>
入口,只有一个空的路由视窗,我们的项目的所有内容,都基于这个视窗来展现。因为scss文件容易出现语法错误,这里先用css文件替代。后期熟悉之后再更改
我们的样式,都将从 src/style/index.css
这个文件中引用,如果没有请按路径新建!
2.调整 index.vue 和 content.vue 文件
昨天,我们在 page
文件夹下面建立了两个空文本文件 index.vue
和 content.vue
文件,是我们准备用来放列表和内容的。
这里,我们先去填写一点基础内容在里面。
index.vue
<template>
<div>index page</div>
</template>
content.vue
<template>
<div>content page</div>
</template>
好,写上如上的代码就行,我们后面再丰富这些内容。
3.调整 router 路由文件
现在,这个项目还跑不起来呢,如果你运行 npm run dev
还是会出错的。因为我们还没有配置路由。
import Vue from 'vue'
import Router from 'vue-router'
import Hello from '@/components/Hello' Vue.use(Router) export default new Router({
routes: [
{
path: '/',
name: 'Hello',
component: Hello
}
]
})
以上,是默认的路由文件,引用了一个 Hello
的组件,这个组件被我们昨天的博文中整理文件结构的时候删除掉了。所以,这里就报错啦。
我们根据我们的需要,来调整这个路由文件,如下:
import Vue from 'vue'
import Router from 'vue-router'
import Index from '@/page/index'
import Content from '@/page/content' Vue.use(Router) export default new Router({
routes: [
{
path: '/',
component: Index
}, {
path: '/content/:id',
component: Content
}
]
})
默认,我们的首页,就是我们的 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
文件,因为原始文件就配置得比较好,我就没有刻意的想要调整它。
原始文件如下:
import Vue from 'vue'
import App from './App'
import router from './router' Vue.config.productionTip = false /* eslint-disable no-new */
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})
我们插入以下代码:
// 引用API文件
import api from './api/index.js'
// 将API方法绑定到全局
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
文件,将代码调整为以下代码:
<template>
<div>index page</div>
</template>
<script>
export default {
created () {
this.$api.get('topics', null, r => {
console.log(r)
})
}
}
</script>
好,这里是调用 cnodejs.org
的 topics
列表接口,并且将结果打印出来。
我们在浏览器中打开控制台,看看 console
下面有没有输出入下图一样的内容。如果有的话,就说明我们的接口配置已经成功了。
好,如果你操作正确,代码没有格式错误的话,那么现在应该得到的结果是和我一样的。如果出错或者怎么样,请仔细的检查代码,看看有没有什么问题。
六、将接口用webpack代理到本地
我们已经非常顺利的调用到了 cnodejs.org
的接口了。但是,我们可以注意到我们的 src/api/index.js
的第一句,就是:
// 配置API接口地址
var root = 'https://cnodejs.org/api/v1'
这里,我们将接口地址写死了。
当然,这并不是最重要的事情,而是 cnodejs.org
帮我们把接口处理得很好,解决了跨域的问题。而我们的实际项目中,很多接口都是不允许我们这样跨域请求的。
而我们的开发环境下,不可能跑到服务器上直接开发,或者在本地直接搞一个服务器环境,这样就违背了我们前后端分离开发的初衷了。
如何解决这个问题呢?其实非常好办,要知道 跨域不是接口的限制 而是浏览器为了保障数据安全做的限制。因此,一种方法可以解决,那就是打开浏览器的限制,让我们可以顺利的进行开发。但是无奈的是,最新的 chrome
浏览器好像已经关闭了这个选项,那么我们只能采用另外一种方法了——将接口代理到本地。
1.配置 webpack 将接口代理到本地
好在,vue-cli
脚手架工具,已经充分的考虑了这个问题,我们只要进行简单的设置,就可以实现我们的目的。
我们打开 config/index.js
文件,找到以下代码:
dev: {
env: require('./dev.env'),
port: 8080,
autoOpenBrowser: true,
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {},
cssSourceMap: false
}
其中,proxyTable: {},
这一行,就是给我们配置代理的。
根据 cnodejs.org
的接口,我们把这里调整为:
proxyTable: {
'/api/v1/**': {
target: 'https://cnodejs.org', // 你接口的域名
secure: false,
changeOrigin: false,
}
}
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
文件,还是直接调用的人家的地址呢,我们要调整为我们的地址,调整如下:
// 配置API接口地址
var root = '/api/v1'
之前我有一篇博文,说过如何配置开发接口地址和生产接口地址,当时是利用了 webpack
的不同的配置文件来进行配置的。如果我们采用这种代理模式呢,那么就没有必要那么做了。因为我们的系统放到生产环境的时候,一般是没有这个跨域问题的。这个问题一般仅仅是存在于我们的开发环境下面。
值得注意的事情是,配置完成后,是不会立即生效的,我们需要重启我们的项目。
我们按 ctrl + c
停止掉之前的服务,然后重新输入命令 npm run dev
重启项目,就可以了。
如上图所示,我们可以清晰的看到,我们跑的服务,先开启了一个代理。
重新跑起来之后,我们看下我们的项目在浏览器中的表现:
我们打开浏览器控制台,切换到 network
选项卡中,选中我们调用的接口 topics
接口,我们可以清晰的看到,我们读取的接口地址是我们的本地代理过来的地址。
状态码为 304
代表这个数据没有发生变化,直接读取本地缓存了。关于 http 状态码
的部分,请参考 百度百科 http 状态码,这里不做过多的说明。
我们再看一下数据是不是正常的过来了。切换到 Previdw
选项卡查看:
没有问题,数据过来了。
好,到这里,我们已经顺利的将接口代理到本地,并且数据读取出来了。我们开始准备下面的工作吧!
7.初识*.vue文件,即VUE组件
*.vue
文件,是一个自定义的文件类型,用类 HTML
语法描述一个 Vue
组件。每个 .vue
文件包含三种类型的顶级语言块 <template>
, <script>
和 <style>
。这三个部分分别代表了 html
,js
,css
。
其中 <template>
和 <style>
是支持用预编译语言来写的。比如,在我们的项目中,我们可以用 scss
预编译,我们可以这样写的:
<style lang="scss">
html
也有自己的预编译语言, vue
也是支持的,不过一般来说,我们前端人员还是比较中意 html
原生语言,所以,我就不过多阐述了。
另外,我在
App.vue
文件中,已经用一句@import "./style/style";
将我们的样式给写到指定的地方去了。所以,在后面所有的我的文章中,是不会出现这个部分的内容的。所有样式,都会在src/style/
文件夹中对应的位置去写。
我这样做的好处是,不需要重复的引入各种scss
基础文件,并且做到了项目的样式代码的可管控
1.一个常见的 *.vue 文件代码解析
首先,我们来简单看一下:
<template>
<div>
<Header></Header>
<div class="article_list">
<ul>
<li></li>
</ul>
</div>
<Footer></Footer>
</div>
</template>
<script>
import Header from '../components/header.vue'
import Footer from '../components/footer.vue'
export default {
components: { Header, Footer },
data () {
return {
list: []
}
},
created () {
this.getData()
},
methods: {
getData () {
this.$api.get('topics', null, r => {
console.log(r)
})
}
}
}
</script>
<style>
.article_list {margin: auto;}
</style>
好,以上就是一个简单的 *.vue
文件的基本结构。我们一部分一部分的来解释。
以下,我不再称呼它为 *.vue
文件了。改成为 vue
组件。
2.template 部分
首先,一个 vue
组件,他的 template
则代表它的 html
结构,相信大家可以理解了。但是需要注意的是,我们不是说把代码包裹在 <template></template>
中就可以了,而是必须在里面放置一个 html
标签来包裹所有的代码。 本例子中,我们采用了 <div></div>
标签。vue2.x
开始,就必须这样去写。这是规定
大家看到 <Header></Header>
这个代码的时候肯定很奇怪,这是个什么玩意儿。其实,这是一个自定义组件。我们在其他地方写好了一个组件,然后就可以用这种方式引入进来。
同样 <Footer></Footer>
也是一个组件。不再累述。
其他的内容,我们就想这么写就怎么写了。和写我们的 html
代码没有什么太大的区别。
3.script 部分
首先,我们需要两个自定义组件,我们先引用进来。如下格式,比较好理解吧。
import Header from '../components/header.vue'
import Footer from '../components/footer.vue'
其次,除了引用的文件,我们将所有的代码包裹于如下的代码中间:
export default {
// 这里写你的代码,外面要包起来。
}
4.style 部分
这里比较简单,就是针对我们的 template
里内容出现的 html
元素写一些样式。如上,我的代码:
<style>
.article_list {margin: auto;}
</style>
就是给我们的 .article_list
元素随便加了一个样式。
参考资料:《vue 模板语法》
八、渲染列表
1.制作 header.vue 和 footer.vue 组件文件。
不是本篇文章的重点,但是还是有比较讲一下。在第三篇博文中,我们规划了我们的项目文件结构,当时保留了一个 components
的空文件夹。这里,就是准备放我们的自定义组件的。
首先,我们去创建两个空文本文件,分别是 header.vue
文件和 footer.vue
文件。
然后,往里面输入下面的内容:
header.vue
<template>
<header class="header">
<h1 class="logo">Vue Demo by DEMO</h1>
</header>
</template>
footer.vue
<template>
<footer class="copy">
Copy © DEMO
</footer> </template>
非常简单的两个文件,表示我们的组件已经弄好了
2.编写 src/page/index.vue 文件
<template>
<div>
<Header></Header>
<div class="article_list">
<ul>
<li v-for="i in list" :key="i.id">
<time v-text="i.create_at"></time>
<router-link :to="'/content/' + i.id">
{{ i.title }}
</router-link>
</li>
</ul>
</div>
<Footer></Footer>
</div>
</template>
<script>
import Header from '../components/header.vue'
import Footer from '../components/footer.vue'
export default {
components: { Header, Footer },
data () {
return {
list: []
}
},
created () {
this.getData()
},
methods: {
getData () {
this.$api.get('topics', null, r => {
this.list = r.data
})
}
}
}
</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
的空文本文件吗?这里,我们要用上了。
直接给代码如下:
export default {
goodTime (str) {
let now = new Date().getTime()
let oldTime = new Date(str).getTime()
let difference = now - oldTime
let result = ''
let minute = 1000 * 60
let hour = minute * 60
let day = hour * 24
let month = day * 30
let year = month * 12
let _year = difference / year
let _month = difference / month
let _week = difference / (7 * day)
let _day = difference / day
let _hour = difference / hour
let _min = difference / minute if (_year >= 1) {
result = '发表于 ' + ~~(_year) + ' 年前'
} else if (_month >= 1) {
result = '发表于 ' + ~~(_month) + ' 个月前'
} else if (_week >= 1) {
result = '发表于 ' + ~~(_week) + ' 周前'
} else if (_day >= 1) {
result = '发表于 ' + ~~(_day) + ' 天前'
} else if (_hour >= 1) {
result = '发表于 ' + ~~(_hour) + ' 个小时前'
} else if (_min >= 1) {
result = '发表于 ' + ~~(_min) + ' 分钟前'
} else {
result = '刚刚'
}
return result
}
}
好,代码恶心了点,我拿我以前写的代码改的,没有深入优化,大家就随便看看,大概就是这么个东西。
写好代码之后,我们保存文件。但是此时,我们还不能使用我们的这个方法函数。我们必须在 main.js
中将我们的方法函数给绑定上。如下代码:
// 引用工具文件
import utils from './utils/index.js'
// 将工具方法绑定到全局
Vue.prototype.$utils = utils
好了,这样,我们写的这个函数,就可以随便被我们调用了。我们再来修改一下我们上面的 index.vue
中的代码,将 time
调整为:
<time v-text="$utils.goodTime(i.create_at)"></time>
然后,我们再来看一下实际的效果:
好,我们已经看到,时间已经搞的挺好的了。
九、把内容页面渲染出来
前面我们从文章中留下的引子 <router-link :to="'/content/' + i.id">
应该知道,我们还是要做内容页面的。
1.编写内容页面
<template>
<div>
<myHeader></myHeader>
<h2 v-text="dat.title"></h2>
<p v-if="dat.author">作者:{{dat.author.loginname}}发表于:{{$utils.goodTime(dat.create_at)}}</p>
<hr>
<article v-html="dat.content"></article>
<h3>网友回复:</h3>
<ul>
<li v-for="i in dat.replies">
<p>评论者:{{i.author.loginname}}评论于:{{$utils.goodTime(i.create_at)}}</p>
<article v-html="i.content"></article>
</li>
</ul>
<myFooter></myFooter>
</div>
</template>
<script>
import myHeader from "../components/header.vue";
import myFooter from "../components/footer.vue";
export default {
components: { myHeader, myFooter },
data() {
return {
id: this.$route.params.id,
dat: {}
};
},
created() {
this.getData();
},
methods: {
getData() {
this.$api.get("topic/" + this.id, null, r => {
this.dat = r.data;
});
}
}
};
</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
文件,找到其中的
productionSourceMap: true,
修改为:
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/
目录下面。于是,我就把这里修改为
assetsPublicPath: '/dist/',
然后,重新运行
npm run build
进行打包。
很快,就打包好了。
还记得,我们在项目文件夹中用 npm run dev
就可以开启一个 http
服务吗?并且那里,我们还代理了接口的。
好,我们就这么做。
然后我们访问二级目录 /dist/
我们就可以看到效果了。
注意,我访问的不是根目录,而是 /dist/
这个子目录哦,这里是访问的我们打包的文件的。
├── index.html
└── static
├── css
│ └── app.d41d8cd98f00b204e9800998ecf8427e.css
└── js
├── app.8ffccad49e36e43a4e9b.js
├── manifest.7a471601ff5a8b26ee49.js
└── vendor.057dd4249604e1e9c3b5.js
好,到这里,我们的打包工作,就讲完了。
实际开发中,你只需要把 dist
文件夹中打包好的文件,给运维他们,让他们去部署到真实的服务器环境就好了。
十一、打包项目图片等资源的处理
1.在 vue 文件中,引用图片
例如,我们将一张图片放到资源目录 /static/image/lyf.jpg
我们在 vue
文件中用下面的代码来使用这张图片。
<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 文件等,都是这样的路数。不在赘述。
Vue2构建项目实战的更多相关文章
- Vue2+VueRouter2+webpack 构建项目实战(三):配置路由,运行页面
制作.vue模板文件 通过前面的两篇博文的学习,我们已经建立好了一个项目.问题是,我们还没有开始制作页面.下面,我们要来做页面了. 我们还是利用 http://cnodejs.org/api 这里公开 ...
- Vue2+VueRouter2+webpack 构建项目实战(二):目录以及文件结构
通过上一篇博文<Vue2+VueRouter2+webpack 构建项目实战(一):准备工作>,我们已经新建好了一个基于vue+webpack的项目.本篇文章详细介绍下项目的结构. 项目目 ...
- Vue2+VueRouter2+webpack 构建项目实战(四)接通api,先渲染个列表
Vue2+VueRouter2+webpack 构建项目实战(四)接通api,先渲染个列表: Vue2+VueRouter2+Webpack+Axios 构建项目实战2017重制版(一)基础知识概述
- 【大话QT之十六】使用ctkPluginFramework插件系统构建项目实战
"使用ctkPluginFramework插件系统构建项目实战",这篇文章是写博客以来最纠结的一篇文章. 倒不是由于技术都多么困难,而是想去描写叙述一个项目架构採用ctkPlugi ...
- Vue2+VueRouter2+webpack 构建项目实战(五):配置子路由
前言 通过前面几章的实战,我们已经顺利的构建项目,并且从API接口获取到数据并且渲染出来了.制作更多的页面,更复杂的应用,就是各位自己根据自己的项目去调整的事情了. 本章讲一下如何配置子路由,因为我们 ...
- Vue2+VueRouter2+webpack 构建项目实战(一):准备工作
环境准备 首先,要开始工作之前,还是需要把环境搭建好.需要的环境是nodejs+npm,当然现在安装node都自带了npm. 在终端下面输入命令node -v会有版本号出来.就说明安装成功了.输入np ...
- Vue2+VueRouter2+webpack 构建项目实战(四):接通api,渲染列表
通过前面几篇教程,我们已经顺利搭建起来了,并且已经组建好路由了.本章节,我们需要做一个列表页面,然后利用获取 http://cnodejs.org/api 的公开API,渲染出来. 我们打开src/p ...
- vue2.0项目实战(1)基础入门
最近公司的H5项目准备重构,部门老大说前端使用vue2.0来开发,所以就准备把整个项目的开发过程记录下来,一方面是为了记录开发过程中遇到的坑,另一方面也加强自己写作的能力. 什么是 Vue? 简要介绍 ...
- vue2.0项目实战(4)生命周期和钩子函数详解
最近的项目都使用vue2.0来开发,不得不说,vue真的非常好用,大大减少了项目的开发周期.在踩坑的过程中,因为对vue的生命周期不是特别了解,所以有时候会在几个钩子函数里做一些事情,什么时候做,在哪 ...
随机推荐
- 选择排序(1)——简单选择排序(selection sort)
选择排序是一种很常见的排序算法,它需要对数组 中的元素进行多次遍历.每经过一次循环,选择最小的元素并把它放在靠近数组前端的位置. 代码实现: public static void selectionS ...
- StackExchange.Redis 使用资料
在StackExchange.Redis中最重要的对象是ConnectionMultiplexer类, 它存在于StackExchange.redis命名空间中.这个类隐藏了Redis服务的操作细节, ...
- 删除".SVN"文件夹方法(转载)
转自:http://www.cnblogs.com/lr-ting/archive/2012/09/03/2666271.html 一.在linux下 删除这些目录是很简单的,命令如下 find . ...
- E20170623-hm
verbose adj. 冗长的,啰唆的,累赘的; reverse vt. (使) 反转; (使) 颠倒; 掉换,交换; [法] 撤消,推翻; adj. 反面的; ...
- yum 安装redis
1.yum install redis 2.yum install php-redis 3service redis start chkconfig redis on
- [Swift通天遁地]八、媒体与动画-(1)实现音频的播放和停止
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...
- 大圆那些事 LIBRARY_PATH和LD_LIBRARY_PATH环境变量的区别
LIBRARY_PATH和LD_LIBRARY_PATH环境变量的区别 LIBRARY_PATH和LD_LIBRARY_PATH是Linux下的两个环境变量,二者的含义和作用分别如下: LIBRARY ...
- hdu---3177 Crixalis's Equipment 根据 两个元素 之间的权衡进行排序
Crixalis's Equipment Problem Description Crixalis - Sand King used to be a giant scorpion(蝎子) in the ...
- Boost.Build特点(译)
Boost.Build Boost.Build makes it easy to build C++ projects, everywhere. Boost.Build让构建C++项目在任何地方都很容 ...
- MySQL的DML和DQL 增删改查
DML和DQL 增删改查 SELECT * FROM grade --新增 insert -- 向年级表中新增3条数据INSERT INTO grade(gradeID,gradeName) VA ...