Vue 入门
0. 概述
Vue
Vue(读音 /vju/,类似于 view)是一套用于构建用户界面的渐进式框架,发布于2014年2月。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库(如:vue-router,vue-resource,vuex)或既有项目整合。
MVVM 模式的实现者
-
Model:模型层,在这里表示 JavaScript 对象
-
View:视图层,在这里表示 DOM(HTML 操作的元素)
-
ViewModel:连接视图和数据的中间件。Vue.js 就是 MVVM 中的 ViewModel 层的实现者。
在 MVVM 架构中,是不允许数据和视图直接通信的,只能通过 ViewModel 来通信。而 ViewModel 就是定义了一个 Observer 观察者。
-
ViewModel 能够观察到数据的变化,并对视图对应的内容进行更新
-
ViewModel 能够监听到视图的变化,并能够通知数据发生改变
至此,我们就明白了:Vue.js 就是一个 MVVM 的实现者。他的核心就是实现了 DOM 监听与数据绑定。
为什么要使用 MVVM
MVVM 模式和 MVC 模式一样,主要目的是分离视图(View)和模型(Model),有几大好处:
- 低耦合:视图(View)可以独立于 Model 变化和修改。一个 ViewModel 可以绑定到不同的 View 上,当 View 变化的时候 Model 可以不变,当 Model 变化的时候 View 也可以不变。
- 可复用:你可以把一些视图逻辑放在一个 ViewModel 里面,让很多 View 重用这段视图逻辑。
- 独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
- 可测试:界面素来是比较难于测试的,而现在测试可以针对 ViewModel 来写。
为什么要使用 Vue.js
- 轻量级,体积小是一个重要指标。Vue.js 压缩后有只有20多 kb(Angular 压缩后56kb+,React 压缩后44kb+)。
- 移动优先。更适合移动端,比如移动端的 Touch 事件。
- 易上手,学习曲线平稳,文档齐全。
- 吸取了 Angular(模块化)和 React(虚拟DOM)的长处,并拥有自己独特的功能,如:计算属性。
- 开源,社区活跃度高。
1. 第一个 Vue 应用
尝试 Vue.js 最简单的方法是使用 Hello World 例子。我们可以创建一个 .html
文件,然后通过如下方式引入 Vue:
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
以上是开发环境版本。如果想要舍弃命令行警告来换取加载速度,也可以选择生产环境版本:
<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统。我们可以编写这样一个页面来测试:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- View层 -->
<div id="app">
{{message}}
</div>
<!-- 1.导入vue.js -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm = new Vue({
el: "#app",
//Model层
data:{
message: "Hello, Vue!"
}
});
</script>
</body>
</html>
在浏览器中打开页面,可以看到页面中输出了 “Hello, Vue!” 这几个字符。
这就是我们的第一个 Vue 应用了。看起来这跟渲染一个字符串模板非常类似,但是 Vue 在背后做了大量工作。现在数据和 DOM 已经被建立了关联,所有东西都是响应式的。我们要怎么确认呢?打开你的浏览器的 JavaScript 控制台 (就在这个页面打开),并修改 vm.message
的值,你将看到上例相应地更新。
页面显示:Hello, Vue!
控制台输入:vm.message="abcd"
页面显示:abcd
注意我们不再和 HTML 直接交互了。一个 Vue 应用会将其挂载到一个 DOM 元素上(对于这个例子是 #vm
)然后对其进行完全控制。那个 HTML 是我们的入口,但其余都会发生在新创建的 Vue 实例内部。
2. Vue 基本语法
2.1 v-bind
除了 1. 中代码展示的文本插值,我们还可以像这样来绑定元素 attribute:
<div id="app2">
<input type="button" v-bind:value="message">
</div>
<script>
let vm2 = new Vue({
el: "#app2",
//Model层
data:{
message: "Show the message."
}
});
</script>
在浏览器中打开页面,可以看到页面中出现了一个按钮,按钮的标题为 “Show the message.” 现在我们打开浏览器的控制台,输入如下内容:
vm2.message="new name"
可以看到,按钮的标题立刻就变成了 “new name”。
这里我们遇到了一点新东西。你看到的 v-bind
attribute 被称为指令。指令带有前缀 v-
,以表示它们是 Vue 提供的特殊 attribute。可能你已经猜到了,它们会在渲染的 DOM 上应用特殊的响应式行为。在这里,该指令的意思是:“将这个元素节点的 value
attribute 和 Vue 实例的 message
property 保持一致”。
2.2 v-if / v-else-if / v-else
v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。同时,也可以用 v-else
添加一个 “else 块”。如下例所示,使用 Vue 之后,控制切换一个元素的内容是否显示其实也相当简单:
<div id="app">
<h1 v-if="show">你现在看得到我</h1>
<h1 v-else></h1>
</div>
<script>
let vm = new Vue({
el: "#app",
data:{
show: true
}
});
</script>
当我们在浏览器中打开此页面,并在控制台中输入 “vm.show=false” 后,即可发现:<h1>
标签中的文字消失了!
这个例子演示了我们不仅可以把数据绑定到 DOM 文本或属性,还可以绑定到 DOM 结构。此外,Vue 也提供一个强大的过渡效果系统,可以在 Vue 插入/更新/移除元素时自动应用过渡效果。
2.3 v-for
我们可以用 v-for
指令基于一个数组来渲染一个列表。v-for
指令需要使用 item in items
形式的特殊语法,其中 items
是源数据数组,而 item
则是被迭代的数组元素的别名。在 v-for
块中,我们可以访问所有父作用域的 property。v-for
还支持一个可选的第二个参数,即当前项的索引。
如下例所示,使用 v-for
指令可以绑定数组的数据来渲染一个项目列表:
<div id="app">
<ol>
<li v-for="(army, index) in armies">{{army.element}}——{{index}}</li>
</ol>
</div>
<script>
let vm = new Vue({
el: "#app",
data:{
armies: [
{element: "风"},
{element: "林"},
{element: "火"},
{element: "山"},
{element: "阴"}
]
}
});
</script>
在浏览器中打开,可以看到页面中显示出了如下的字样:
1.风——0
2.林——1
3.火——2
4.山——3
5.阴——4
此时,如果我们在控制台中输入 vm.armies.push({element:"雷"})
,那么就可以看到页面中显示的文字新增了一条:
1.风——0
2.林——1
3.火——2
4.山——3
5.阴——4
6.雷——5
2.4 v-on
使用 v-on
指令可以监听 DOM 事件,并在触发时运行一些 JavaScript 代码。
示例:
<div id="app">
<button v-on:click="sayHello">Click Me</button>
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
message: "欢迎使用 Vue!"
},
methods: {
//方法必须定义在Vue的methods对象中
sayHello: function () {
alert(this.message);
}
}
});
</script>
在浏览器中打开页面,可以看到一个按钮,标题是 “Click Me”。点击按钮,会弹出一个消息提示框,上面写着 “欢迎使用 Vue!”
2.5 v-model
使用 v-model
指令可以在 <input>
、<checkbox>
及 <select>
等表单元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。事实上, v-model
本质不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
示例 <input>
:
<div id="app">
请输入文本:<input type="text" v-model="message"> {{message}}
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
message: ""
}
});
</script>
打开浏览器进行测试,可以看到页面中有一个文本框。在其中输入 “123456”,每敲一个字符,文本框的右边就会相应地多一个字符出来。接着,打开控制台,输入 vm.message="121212"
,可以发现,文本框中的文本与其后面的文本均立刻变为了 “121212”。
示例 <select>
:
<div id="app2">
<select v-model="selected">
<option disabled value="">--请选择--</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
你的选择是:{{selected}}
</div>
<script>
let vm2 = new Vue({
el: "#app2",
data: {
selected: ""
}
});
</script>
经测试不难发现,如果 v-model
表达式的初始值未能匹配任何选项,<select>
元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。因此,更推荐像上面这样提供一个值为空的禁用选项。
3. 组件
组件是可复用的 Vue 实例,通过 Vue.component
进行全局注册,带有一个名字。我们可以在一个通过 new Vue
创建的 Vue 根实例中,把这个组件作为自定义元素来使用。Props 属性则使你可以在组件上注册一些自定义的属性。当一个值传递给 props 的时候,它就变成了那个组件实例的一个属性。一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。我们可以使用 v-bind
来动态传递 prop,这在我们一开始不清楚要渲染的具体内容的时候非常有用。
示例:
<div id="app">
<li-diy v-for="item in items" v-bind:args="item.weapon"></li-diy>
</div>
<script>
Vue.component('li-diy', {
props: ["args"],
template: '<li>{{args}}</li>'
});
let vm = new Vue({
el: "#app",
data: {
items: [
{weapon: "开天辟地斧"},
{weapon: "抽魂打魄鞭"},
{weapon: "引魑招魅幡"}
]
}
});
</script>
在浏览器中打开页面,可以看到页面上显示出了如下文字:
● 开天辟地斧
● 抽魂打魄鞭
● 引魑招魅幡
4. Axios
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。
使用前,需要先在页面中导入 Axios:
<!-- 导入Axios的CDN -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
示例(GET请求):
data.json:
{
"name":"惟妙惟霄",
"url": "https://blog.csdn.net/whlwtj",
"page": 1,
"isNonProfit":true,
"address": {
"street": "龙蟠路",
"city":"江苏省南京市",
"country": "中国"
},
"links": [
{
"name": "B站",
"url": "https://www.bilibili.com/"
},
{
"name": "4399",
"url": "https://www.4399.com/"
},
{
"name": "百度",
"url": "https://www.baidu.com/"
}
]
}
demo.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<div>{{info.name}}</div>
<div>{{info.address.street}}</div>
<a v-bind:href="info.url">CSDN博客</a>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!-- 导入Axios的CDN -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
let vm = new Vue({
el: "#app",
data(){
return{
info: {
name: null,
address: {
street: null,
city: null,
country: null
},
url: null
}
}
},
mounted(){
//钩子函数,链式编程,ES6新特性
// ../ 代表返回上一级目录
axios.get("../data.json").then(response=>(this.info=response.data));
}
});
</script>
</body>
</html>
最终页面输出结果如下:
惟妙惟霄
龙蟠路
CSDN博客(这是一个链接,点进去可以进入我的CSDN博客)
5. 计算属性
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。所以,对于任何复杂逻辑,你都应当使用计算属性。
计算属性的重点突出在属性两个字上(属性是名词):首先它是个属性,其次这个属性有计算的能力(计算是动词),这里的计算就是个函数。简单点说,它就是一个能够将计算结果缓存起来的属性(将行为转化成了静态的属性)。
示例:
<div id="app">
<div>currentTime1:{{currentTime1()}}</div>
<div>currentTime2:{{currentTime2}}</div>
</div>
<script>
let vm = new Vue({
el: "#app",
data: {
message: "hello",
},
methods: {
currentTime1: function () {
return Date.now(); //返回一个时间戳
}
},
computed: {
//计算属性,与methods中的方法不能重名,否则只会调用methods
currentTime2: function () {
this.message;
return Date.now(); //返回一个时间戳
}
}
});
</script>
在浏览器中打开页面,可以看到如下输出结果:
currentTime1:1622343717241
currentTime2:1622343717241
接着,打开控制台,进行如下输入:
vm.currentTime1()
控制台返回的输出结果为:
1622343718431
可以看到,返回值已经发生了改变。而当我们在控制台中进行如下输入时:
vm.currentTime2
控制台返回的结果仍然为:
1622343717241
由此可见,计算属性的值被存入了缓存中,并没有发生变化。
这就是方法与计算属性的区别:计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 currentTime2
中的 message
还没有发生改变,多次访问 currentTime2
计算属性只会立即返回之前的计算结果,而不会再次执行函数。
说明:
- methods:定义方法,调用方法使用
currentTime1()
,需要带括号 - computed:定义计算属性,调用属性使用
currentTime2
,不需要带括号。通过改变 this.message 可以观察到 currentTime2 的数据变化:即,当方法中的值发生了变化时,缓存就会刷新。例如,当我们在控制台中输入vm.message="abc"
后,再次输出currentTime2
就可以看到值发生了变化。
6. 插槽
有些时候,我们需要在组件中预留出一些位置,来存放一些未定的内容。此时,就可以使用 <slot>
标签,也就是插槽,来起到一个占位符的作用。
示例:
<div id="app">
<todo slot="todo">
<!-- 根据name填充对应的插槽 -->
<todo-title slot="todo-title" v-bind:title="title"></todo-title>
<todo-items slot="todo-items" v-for="item in items" v-bind:item="item"></todo-items>
</todo>
</div>
<script>
Vue.component("todo",{
template:
'<div>\
<!-- 设置插槽 -->\
<slot name="todo-title"></slot>\
<ul>\
<slot name="todo-items"></slot>\
</ul>\
</div>'
});
Vue.component("todo-title",{
props: ["title"],
template: '<div>{{title}}</div>'
});
Vue.component("todo-items",{
props: ["item"],
template: '<li>{{item}}</li>'
});
let vm = new Vue({
el: "#app",
data: {
title: "军争",
items: [
"其疾如风",
"其徐如林",
"侵略如火",
"不动如山",
"难知如阴",
"动如雷震"
]
}
});
</script>
在浏览器中打开页面,可以看到如下输出结果:
军争
● 其疾如风
● 其徐如林
● 侵略如火
● 不动如山
● 难知如阴
● 动如雷震
7. 自定义事件内容分发
在 6. 的例子中,如果我们想要实现在每一项旁边增加一个按钮,点击后就删除该项的功能,那么我们多半会这么写:
<div id="app">
<todo slot="todo">
<!-- 根据name填充对应的插槽 -->
<todo-title slot="todo-title" v-bind:title="title"></todo-title>
<todo-items slot="todo-items" v-for="(item, index) in items" :item="item" :index="index"></todo-items>
</todo>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
Vue.component("todo",{
template:
'<div>\
<!-- 设置插槽 -->\
<slot name="todo-title"></slot>\
<ul>\
<slot name="todo-items"></slot>\
</ul>\
</div>'
});
Vue.component("todo-title",{
props: ["title"],
template: '<div>{{title}}</div>'
});
Vue.component("todo-items",{
props: ["item", "index"],
template: '<li>{{index}}---{{item}} <button @click="remove">删除</button></li>',
methods: {
remove: function () {
//希望能够在这里调用removeItem()
}
}
});
let vm = new Vue({
el: "#app",
data: {
title: "军争",
items: [
"其疾如风",
"其徐如林",
"侵略如火",
"不动如山",
"难知如阴",
"动如雷震"
]
},
methods: {
removeItem: function (index) {
this.items.splice(index,1);
}
}
});
</script>
在浏览器中打开页面,可以看到如下输出结果:
军争
● 0---其疾如风 [删除(按钮)]
● 1---其徐如林 [删除(按钮)]
● 2---侵略如火 [删除(按钮)]
● 3---不动如山 [删除(按钮)]
● 4---难知如阴 [删除(按钮)]
● 5---动如雷震 [删除(按钮)]
此时,点击删除按钮是没有用的。不过,当我们在控制台中输入 vm.removeItem(0)
时,“其疾如风”这一项还是被我们成功删掉了的。现在,我们所希望的,就是当我们点击按钮,调用 remove()
方法的时候,可以在该方法中去调用 removeItem()
方法。
想要实现这一点,就需要使用自定义事件了。自定义事件的步骤如下:
- 父组件可以使用
v-on
声明自定义事件 - 子组件可以使用
props
接收父组件传递来的参数。 - 子组件可以使用
$emit()
方法触发父组件的自定义事件。其中第一个参数为父组件v-on
定义的时机,第二个参数为子组件props
接收到的参数。
示例:
<div id="app">
<todo slot="todo">
<!-- 根据name填充对应的插槽 -->
<todo-title slot="todo-title" v-bind:title="title"></todo-title>
<todo-items slot="todo-items" v-for="(item, index) in items" :item="item" :index="index" v-on:remove_func="removeItem(index)"></todo-items>
</todo>
</div>
<script>
Vue.component("todo",{
template:
'<div>\
<!-- 设置插槽 -->\
<slot name="todo-title"></slot>\
<ul>\
<slot name="todo-items"></slot>\
</ul>\
</div>'
});
Vue.component("todo-title",{
props: ["title"],
template: '<div>{{title}}</div>'
});
Vue.component("todo-items",{
props: ["item", "index"],
template: '<li>{{index}}---{{item}} <button @click="remove">删除</button></li>',
methods: {
remove: function (index) {
this.$emit('remove_func', index);
}
}
});
let vm = new Vue({
el: "#app",
data: {
title: "军争",
items: [
"其疾如风",
"其徐如林",
"侵略如火",
"不动如山",
"难知如阴",
"动如雷震"
]
},
methods: {
removeItem: function (index) {
this.items.splice(index,1);
}
}
});
</script>
在浏览器中打开页面,点击每一项旁边的按钮,对应的列表项果然被删掉了。并且,列表项前端的序号也自动地发生了改变。
8. Vue-CLI
8.1 什么是 Vue-CLI
Vue-CLI 是官方提供的一个脚手架,用于快速生成一个 vue 的项目模板。它可以预先定义好目录结构及基础代码,就像在创建 Maven 项目时可以选择创建一个骨架项目,这个骨架项目就是脚手架,可以使我们的开发更加的快速。
主要的功能:
- 统一的目录结构
- 本地调试
- 热部署
- 单元测试
- 集成打包上线
8.2 准备工作
想要使用 Vue-CLI,需要 node.js 的支持:node.js 官方下载链接
需要注意的是,自 14.0.0 之后的版本都不再支持 Win 7。因此,Win 7 用户请移步历史版本进行下载:node.js 官方历史版本
除此之外,我们最好再去配置一个淘宝镜像 cnpm,以备不时之需:
npm install cnpm -g
不过,淘宝镜像在打包时有时会出现问题,因此还是推荐优先使用 npm,如果 npm 的下载速度过于缓慢,再换用 cnpm。
接下来,我们就可以通过 cnpm 来安装 Vue-CLI 了:
cnpm install vue-cli -g
安装完成之后,可以通过 vue list
命令查看 Vue-CLI 为我们提供了哪些创建项目的模板:
browserify
browserify-simple
pwa
simple
webpack
webpack-simple
通常情况下,我们会选择使用 webpack
,它可以帮助我们在打包时把基于 ES6 的工程降级为 ES5 的版本。
8.3 第一个 Vue-CLI 程序
-
在一个空闲位置创建一个文件夹作为改程序的目录
-
以管理员身份打开 cmd,通过
cd
命令进入该文件夹,并输入以下命令vue init webpack 项目名称
接着,跟着指示对项目进行相关配置即可,如项目名称、项目描述、作者信息等等。遇到 Y/N 的选择时,统一选 NO。
-
进入新生成的目录
cd 项目名称
-
安装相关依赖环境
npm install
如果安装遇到了错误,只需输入提示的改错命令即可。
-
启动项目
npm run dev
-
如果想要停止项目的运行,只需在 cmd 窗口中按下
ctrl+c
,并在 Y/N 中选择 Y 即可。 -
以管理员身份启动 IDEA 并 open 这个项目文件夹,然后在 Terminal 中输入
npm run dev
,同样可以完成项目的启动。
8.4 Webpack
webpack 可以帮助我们把基于 ES6 开发的应用程序打包成可以在 ES5 浏览器中运行的网页。
8.4.1 安装
安装方法:
npm install webpack -g
和
npm install webpack-cli -g
测试是否安装成功:
webpack -v
或
webpack-cli -v
8.4.2 配置
创建 webpack.config.js
配置文件:
- entry:入口文件,指定 WebPack 用哪个文件作为项目的入口
- output:输出,指定 WebPack 把处理完成的文件放置到指定路径
- module:模块,用于处理各种类型的文件
- plugins:插件,如热更新、代码重用等
- resolve:设置路径指向
- watch:监听,用于设置文件改动后直接打包
8.4.3 使用步骤
-
创建一个空项目
-
创建一个名为 modules 的目录,用于放置 JS 模块等资源文件
-
在 modules 下创建模块文件,如 hello.js,用于编写 JS 模块相关代码
//暴露一个方法 exports.sayHi = function () { document.write("<h1>欢迎</h1>"); }
-
在 modules 下创建一个名为 main.js 的入口文件,用于打包时设置 entry 属性
let hello = require("./hello"); hello.sayHi();
-
在项目目录下创建 webpack.config.js 配置文件
module.exports = { entry: './modules/main.js', output: { filename: './js/bundle.js' } };
-
在 Terminal 中使用 webpack 命令打包
-
在项目目录下创建 index.html 文件,并在其中导入打包后的 js 文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!-- 前端的模块化开发 --> <script src="./dist/js/bundle.js"></script> </body> </html>
-
在浏览器中打开页面,可以看到如下输出结果:
欢迎
9. Vue Router
Vue Router 是 Vue.js 官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。包含的功能主要有:
- 嵌套的路由/视图表
- 模块化的、基于组件的路由配置
- 路由参数、查询、通配符
- 基于 Vue.js 过渡系统的视图过渡效果
- 细粒度的导航控制
- 带有自动激活的 css class 的链接
- HTML5 历史模式或 hash 模式,在 IE9 中自动降级
- 自定义的滚动条行为
9.1 安装
Vue Router 是一个插件包,需要进入某个项目中进行安装。可以在 cmd 中通过 cd 进入某个项目路径进行安装,也可以在 IDEA 中打开项目,然后在 Terminal 中直接安装。安装方法如下:
npm install vue-router --save-dev
如果安装出错,只需按照提示执行相应的改错命令即可。
9.2 测试
-
在 8.3 的代码基础上,删除
src
目录中多余的文件,留下 App.vue 和 main.js 即可。 -
在 Components 目录下编写一个
Content.vue
组件:<template> <h1>内容页</h1> </template> <script> export default { name: "Content" } </script> <style scoped> </style>
-
在
src
目录中新建一个router
文件夹,用于存放路由,并在其中新建一个index.js
文件:import Vue from 'vue'; import VueRouter from "vue-router"; import Content from '../components/Content' import Main from '../components/main' //安装路由 Vue.use(VueRouter); //配置导出路由 export default new VueRouter({ routes: [ { //路由路径 path: '/content', name: 'content', //跳转到组件 component: Content }, { //路由路径 path: '/main', name: 'main', //跳转到组件 component: Main } ] });
-
在
main.js
中配置路由:import Vue from 'vue' import App from './App' import router from './router' //自动扫描路径里的路由配置 Vue.config.productionTip = false new Vue({ el: '#app', //配置路由 router, components: { App }, template: '<App/>' });
-
在
App.vue
中使用路由<template> <div id="app"> <h1>Vue-Router</h1> <router-link to="/main">首页</router-link> <router-link to="/content">内容页</router-link> <router-view></router-view> </div> </template> <script> export default { name: 'App', } </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>
-
启动测试
npm run dev
10. Element UI
10.1 创建工程
-
创建一个名为
element-ui-study
的工程:vue init webpack element-ui-study
-
安装依赖
# 进入工程目录 cd element-ui-study # 安装 vue-router npm install vue-router --save-dev # 安装 element-ui npm i element-ui -S # 安装依赖 npm install # 安装 SASS 加载器 cnpm install sass-loader # 启动测试 npm run dev
10.2 测试
-
启动 IDEA 并 open 此项目文件夹
-
删除
src
目录中多余的文件,留下 App.vue 和 main.js 即可 -
在
src
目录下创建router
文件夹和views
文件夹 -
在
views
目录下创建 Main.vue:<template> <h1>首页</h1> </template> <script> export default { name: "Main" } </script> <style scoped> </style>
-
在
views
目录下创建 Login.vue:<template> <div> <el-form ref='loginForm' :model="form" :rules="rules" label-width='80px' class='login-box'> <h3 class='login-title'>欢迎 登录</h3> <el-form-item label="账号" prop="username"> <el-input type="text" placeholder="请输入账号" v-model="form.username"/> </el-form-item> <el-form-item label="密码" prop="password"> <el-input type="password" placeholder="请输入密码" v-model="form.password"/> </el-form-item> <el-form-item> <el-button type="primary" v-on:click="onSubmit(loginForm)">登录</el-button> </el-form-item> </el-form> <el-dialog title="温馨提示" :visible.sync="dialogVisible" width="30%" :before-close="handLeClose"> <span>请输入账号和密码</span> <span slot="footer" class="dialog-footer"> <el-button type="primary" @click="dialogVisible = false">确定</el-button> </span> </el-dialog> </div> </template> <script> export default { name: "Login", data() { return { form: { username: '', password: '' }, //表单验证,需要在el-form-item 元素中增加prop 属性 rules: { username: [ {required: true, message: " 账号不可为空", trigger: 'blur'} ], password: [ {required: true, message: " 密码不可为空 ", trigger: 'blur'} ] }, //对话框显示和隐藏 dialogVisible: false } }, methods: { onSubmit(loginForm) { //为表单绑定验证功能 this.$refs.loginForm.validate((valid) => { if (valid) { //使用vue-router路由到指定页面,该方式称之为编程式导航 this.$router.push("/main"); } else { this.dialogVisible = true; return false; } }); } } } </script> <style scoped> .login-box { border: 1px solid #DCDFE6; width: 350px; margin: 180px auto; padding: 35px 35px 15px 35px; border-radius: 5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; box-shadow: 0 0 25px #909399; } .login-title { text-align: center; margin: 0 auto 40px auto; color: #303133; } </style>
-
在
router
目录下创建index.js
文件:import Vue from 'vue'; import VueRouter from "vue-router"; import Main from '../views/Main'; import Login from '../views/Login'; Vue.use(VueRouter); export default new VueRouter({ routes: [ { path: '/main', component: Main }, { path: '/login', component: Login } ] });
-
修改
main.js
文件:import Vue from 'vue' import App from './App' import router from './router' //引入Element-UI import Element from 'element-ui' import 'element-ui/lib/theme-chalk/index.css'; Vue.use(router); Vue.use(Element); new Vue({ el: '#app', router, //配置路由 render: h => h(App) //Element-UI的配置 });
-
修改 App.vue:
<template> <div id="app"> <router-view></router-view> </div> </template> <script> export default { name: 'App', } </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>
-
启动测试
npm run dev
在浏览器地址栏中输入
http://localhost:8080/#/login
即可看到登录界面。任意输入用户名和密码后点击登录,即可跳转到主页。
11. 路由嵌套
借助路由,我们可以很方便地实现页面之间的跳转。不过,有时我们会希望跳转后的页面仍可以继续跳转至其他页面,那么此时我们就可以使用嵌套路由。
-
在
view
目录下新建一个user
文件夹,并在其中创建两个 Vue 组件:Profile.vue:
<template> <h1>个人信息</h1> </template> <script> export default { name: "UserProfile" } </script> <style scoped> </style>
List.vue:
<template> <h1>用户列表</h1> </template> <script> export default { name: "List" } </script> <style scoped> </style>
-
在
index.js
中注册路由,并将其嵌套进 Main 路由之中:import Vue from 'vue'; import VueRouter from "vue-router"; import Main from '../views/Main'; import Login from '../views/Login'; import UserProfile from '../views/user/Profile'; import UserList from '../views/user/List'; Vue.use(VueRouter); export default new VueRouter({ routes: [ { path: '/main', component: Main, //嵌套路由 children: [ { path: '/user/profile', component: UserProfile }, { path: '/user/list', component: UserList } ] }, { path: '/login', component: Login } ] });
-
修改
Main.vue
,令其显示一个导航栏,用于对页面的切换。其中,当我们为
<el-menu>
添加router
属性后,便可在<el-menu-item>
中通过属性<index>
来进行页面的跳转显示:<template> <div> <el-row class="tac"> <el-col :span="4"> <h5>菜单</h5> <el-menu default-active="this.$route.path" class="el-menu-vertical-demo" @open="handleOpen" @close="handleClose" router > <el-menu-item index="/user/profile"> <i class="el-icon-document"></i> <span slot="title">个人信息</span> </el-menu-item> <el-menu-item index="/user/list"> <i class="el-icon-setting"></i> <span slot="title">用户列表</span> </el-menu-item> </el-menu> </el-col> <el-col :span="20"> <el-main> <router-view /> </el-main> </el-col> </el-row> </div> </template> <script> export default { data() { return { activeIndex: '/user/profile', }; }, methods: { handleSelect(key, keyPath) { console.log(key, keyPath); } } } </script> <style scoped> </style>
-
打开浏览器,在地址栏中输入
localhost:8080/#/main
即可看到测试结果。
12. 路由钩子与404
12.1 路由模式
路由模式有两种:
- hash:路径带
#
符号,如http://localhost/#/login
- history:路径不带
#
符号,如http://localhost/login
想要改变路由模式,只需在 index.js
中做出如下修改即可:
export default new VueRouter({
mode: 'history',
routes: [
{
//...
}
]
});
12.2 404页面
如果我们在浏览器中输入了一个并不存在的地址,比如 localhost:8080/user/abc
,会发现页面显示为一片空白。那么,我们该如何建立一个 404 页面来提醒用户进入了一个不存在的地址呢?
方法其实很简单:
-
在
views
目录下建立一个NotFound.vue
文件:<template> <div> <h1>404 Not Found</h1> </div> </template> <script> export default { name: "NotFound" } </script> <style scoped> </style>
-
在
index.js
中配置相关路由,并令其路径为'*'
。这样一来,只要进入的路径不在前面配置的路径当中,页面自然就会跳转至 NotFound 了:import Vue from 'vue'; import VueRouter from "vue-router"; import Main from '../views/Main'; import Login from '../views/Login'; import UserProfile from '../views/user/Profile'; import UserList from '../views/user/List'; import NotFound from "../views/NotFound"; Vue.use(VueRouter); export default new VueRouter({ mode: 'history', routes: [ { path: '/main', props: true, component: Main, //嵌套路由 children: [ { path: '/user/profile', component: UserProfile }, { path: '/user/list', component: UserList } ] }, { path: '/login', component: Login }, { path: '*', component: NotFound } ] });
-
在浏览器的地址栏中输入
localhost:8080/user/abc
,发现页面输出如下:404 Not Found
12.3 路由钩子
12.3.1 钩子函数
beforeRouteEnter
:进入路由前自动执行beforeRouteLeave
:离开路由前自动执行
示例:
修改 Profile.vue 中 <script>
部分的代码:
<script>
export default {
name: "UserProfile",
beforeRouteEnter:(to,from,next)=>{
console.log("进入路由前");
next();
},
beforeRouteLeave:(to,from,next)=>{
console.log("离开路由前");
next();
}
}
</script>
测试:
在浏览器中打开 localhost:8080/main
页面,点击 “个人信息”,控制台输出如下:
进入路由前
点击 “用户列表”,控制台输出如下:
离开路由前
其中,三个参数的作用分别如下:
- to:路由将要跳转的路径信息
- from:路径跳转前的路径信息
- next:路由的控制参数
- next():跳入下一个页面
- next(’/path’):改变路由的跳转方向,使其跳到另一个路由
- next(false):返回原来的页面
- next((vm)=>0)):仅在 beforeRouteEnter 中可用,vm 是组件实例
12.3.2 在钩子函数中使用异步请求
-
安装 Axios 和 Vue-Axios
cnpm install axios vue-axios --save-dev
-
在
main.js
中引用 Axiosimport axios from 'axios' import VueAxios from 'vue-axios' Vue.use(VueAxios, axios);
-
在
static
目录下建立一个mock
文件夹用于测试,并在其中建立data.json
:{ "name":"惟妙惟霄", "url": "https://blog.csdn.net/whlwtj", "page": 1, "isNonProfit":true, "address": { "street": "龙蟠路", "city":"江苏省南京市", "country": "中国" }, "links": [ { "name": "B站", "url": "https://www.bilibili.com/" }, { "name": "4399", "url": "https://www.4399.com/" }, { "name": "百度", "url": "https://www.baidu.com/" } ] }
-
在浏览器地址栏中输入
http://localhost:8080/static/mock/data.json
,可以看到页面中输出了此文件 -
在
beforeRouteEnter
中进行异步请求<script> export default { name: "UserProfile", beforeRouteEnter:(to,from,next)=>{ //进入路由前加载数据 next(vm => { vm.getData(); }); }, beforeRouteLeave:(to,from,next)=>{ console.log("离开路由前"); next(); }, methods: { getData: function () { this.axios({ method: "GET", url: "http://localhost:8080/static/mock/data.json" }).then(function (response) { console.log(response); }); } } } </script>
-
在浏览器地址栏中输入
http://localhost:8080/user/profile
,即可在控制台中看到 json 对象的输出结果。