Vue
1. 概述
Vue是一套用于构建用户界面的额渐进式JavaScript框架。被设计为可以自底向上逐层应用。
Vue的核心库只关心视图层。
第三方库(vue-router跳转,vue-resource通信,vuex管理)
方便与第三方库或既有项目整合。
是MVVM模式的实现者。
2. 几个模式
MVC(同步通信为主) :Model、View、Controller
MVP(异步通信为主) :Model、View、Presenter
MVVM(异步通信为主):Model、View、View Model,是一种简化用户界面的事件驱动编程方式。MVVM源自于经典的MVC(Model-View-Controller)模式。MVVM的核心是ViewModel层,负责转换Model中的数据对象来让数据变得更容易管理和使用。其作用如下:
- 该层向上与视图层进行双向数据绑定
- 向下与Model层通过接口请求进行数据交互
MVVM已经相当成熟了,主要运用但不仅仅在网络应用程序开发中。当下流行的MVVM框架有
Vue.js
,Anfular JS
使用MVVM的好处:
- 低耦合:视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
- 可复用:你可以把一些视图逻辑放在一个ViewModel里面,让很多View重用这段视图逻辑。
- 独立开发:开发人员可以专注于业务逻辑和数据的开发(ViewMode),设计人员可以专注于页面设计。
- 可测试:界面素来是比较难以测试的,而现在测试可以针对ViewModel来写。
随着Node JS的兴起, JavaScript开始有能力运行在服务端。这意味着可以有一种新的研发模式:
在这种研发模式下,前后端的职责很清晰。对前端来说,两个UI层各司其职:
- Front-end Ul layer处理浏览器层的展现逻辑。通过CSS渲染样式, 通过JavaScript添加交互功能, HTML的生成也可以放在这层, 具体看应用场景。
- Back-end Ul layer处理路由、模板、数据获取、Cookie等。通过路由, 前端终于可以自主把控URL Design, 这样无论是单页面应用还是多页面应用, 前端都可以自由调控。后端也终于可以摆脱对展现的强关注,转而可以专心于业务逻辑层的开发。
通过Node, WebServer层也是JavaScript代码, 这意味着部分代码可前后复用, 需要SEO的场景可以在服务端同步渲染,由于异步请求太多导致的性能问题也可以通过服务端来缓解。前一种模式的不足,通过这种模式几乎都能完美解决掉。
前后分离的开发思想主要是基于SoC(关注度分离原则)。
数据双向绑定
- ViewModel能够观察到数据的变化, 并对视图对应的内容进行更新
- ViewModel能够监听到视图的变化, 并能够通知数据发生改变
3. 入门
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!-- 引入Vue的js文件 -->
<script src="js/vue.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<!-- View 模版 -->
<!-- 将数据保定到页面元素 -->
<div id="app">
<span>
{{message}}
</span>
</div>
<script type="text/javascript">
var vm = new Vue({
// 绑定元素,这里使用id绑定
el: "#app",
// Model 数据
data: {
message: "hello, world!"
}
});
</script>
</body>
</html>
只需要在绑定的元素中使用双花括号将Vue创建的名为message属性包裹起来, 即可实现数据绑定功能, 也就实现了View Model层所需的效果。
打开页面可以发现message的属性被替换成了"hello, world!"的内容。控制台可以动态更改vm.message的内容,页面会随之动态更改。这是借助了Vue的数据绑定功能实现的。MVVM模式中要求View Model层使用观察者模式来实现数据的绑定和监听,以做到数据和视图的快速响应。现在数据和DOM已经被建立了关联,所有东西都是响应式的。可以在控制台操作对象属性,界面实时更新。
4. 基础语法
使用v-属性绑定数据是*不需要双花括号包裹的
4.1. v-bind
绑定元素特性。
<!-- 将这个元素节点的title特性和Vue实例的message属性保持一致 -->
<!--可以缩写为<div id="app" :title="message">-->
<div id="app" v-bind:title="message">
<span>
鼠标悬停以查看内容
</span>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
data: {
message: "hello, world!"
}
});
</script>\
4.2. v-if, v-else-if, v-else
<div id="app">
<span v-if="score>90">A</span>
<span v-else-if="score>80">B</span>
<span v-else-if="score>70">C</span>
<span v-else>D</span>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
data: {
score: 80
}
});
</script>
4.3. v-for
<div id="app">
<!-- 在items数组中逐个获取item和下标 -->
<!-- 获取多个值的时候,需要用,分隔,同时需要用括号括起来 -->
<li v-for="(item, index) in items">
[{{index}}]. {{item.message}}
</li>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
data: {
items: [
{message: "hello"},
{message: "world"},
{message: "!"}
]
}
});
</script>
4.4. v-on
监听事件。
这里的click是vue的事件,可以绑定到vue的methods中的方法事件。
<div id="app">
<!--可以缩写为<button type="button" @click="click()">Click Me</button>-->
<button type="button" v-on:click="click()">Click Me</button>
</div>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
data: {
message: "hello world!"
},
// 绑定方法,注意这里是methods,不是method
methods:{
click: function() {
console.log(this.message);
}
}
});
</script>
5. 双向绑定
Vue基于MVVM框架。当数据发生变化的时候, 视图也就发生变化, 当视图发生变化的时候,数据也会跟着同步变化。
如果使用vuex, 实际上数据还是单向的, 之所以说是数据双向绑定,这是用的UI控件来说, 对于我们处理表单, Vue.js的双向数据绑定用起来就特别舒服了。即两者并不互斥,在全局性数据流使用单项,方便跟踪;局部性数据流使用双向,简单易操作。
5.1. 使用
可以用v-model指令在表单、及元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
注意:v-model
会忽略所有表单元素的value
、checked
、selected
特性的初始值而总是将Vue
实例的数据作为数据来源。你应该通过JavaScript
在组件的data
选项中声明初始值!
文本
此后,不管是在视图改变数字或者在数据改变数字,两端都会同步发生变化。
<div id="app">
<input type="text" name="username" value="" v-model="val"/>
{{val}}
</div>
<script type="text/javascript">
let vm = new Vue({
el: "#app",
data: {
val: "root"
}
});
</script>
textarea同理。
在文本区域插值不起作用,应该使用 v-model
来代替。
<textarea v-model="text"></textarea>
单复选框
<div id="app">
<input type="radio" name="gender" value="male" v-model="val"/>男
<input type="radio" name="gender" value="female" v-model="val"/>女
{{val}}
</div>
<script type="text/javascript">
let vm = new Vue({
el: "#app",
data: {
// 默认值,相当于在value=female的标签加上checked
val: "female"
}
});
</script>
<div id="app">
<input type="checkbox" name="hobby" value="sing" v-model="val"/>唱
{{val}}
</div>
<script type="text/javascript">
let vm = new Vue({
el: "#app",
data: {
// true选中 flase不选中
val: false
}
});
</script>
<!--单个复选框,绑定到具体值-->
<div id="app">
<input type="checkbox" name="hobby" value="sing" v-model="val"/>唱
<input type="checkbox" name="hobby" value="jump" v-model="val"/>跳
<input type="checkbox" name="hobby" value="rap" v-model="val"/>rap
<input type="checkbox" name="hobby" value="basketball" v-model="val"/>篮球
{{val}}
</div>
<script type="text/javascript">
let vm = new Vue({
el: "#app",
data: {
// 默认值为空,后面视图选中的值都会保存到val里面
val: []
}
});
</script>
下拉框
v-model表达式的初始值未能匹配到任何选项的时候,元素会被渲染成“未选中”的状态。在IOS中会使用户无法选择第一个选项,所以需要提供一个值为空的禁用选项。
<select v-model="val">
<!--值为空的禁用选项-->
<option value="" disabled>---请选择---</option>
<option>A</option>
<option>B</option>
<option>C</option>
<option>D</option>
</select>
<span>val:{{val}}</span>
</div>
<script type="text/javascript">
let vm = new Vue({
el: "#app",
data: {
// 默认值为A
val: 'A'
}
});
</script>
6. 组件
组件是可复用的Vue
实例, 说白了就是一组可以重复使用的模板, 跟JSTL
的自定义标签、Thymeleal
的th:fragment
等框架有着异曲同工之妙,通常一个应用会以一棵嵌套的组件树的形式来组织:
例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。
6.1. 第一个Vue组件
注意:在实际开发中,并不会用以下方式开发组件,而是采用vue-cli
创建,vue
模板文件的方式开发。
使用Vue.component()方法注册组件。
使用props属性传递参数。注意:默认规则下props属性里的值不能为大写。
<div id="app">
<!-- 中间商,一边遍历获取vm.items中的值,一边传递给component的props中,进行展示 -->
<mycomponent v-for="(item,index) in items" v-bind:val="item"></mycomponent>
</div>
<script type="text/javascript">
Vue.component("mycomponent",
// 定义组件的名字为 mycomponent,并设置模版
props: ['val'],
template: `<li>{{val}}</li>`
});
let vm = new Vue({
el: "#app",
data: {
items: [1, 2, 3, 4, 5]
}
});
</script>
说明:
v-for="item in items"
:遍历Vue
实例中定义的名为items
的数组,并创建同等数量的组件v-bind:itemChild="item"
:将遍历的item
项绑定到组件中props
定义名为item
属性上;= 号左边的itemChild
为props
定义的属性名,右边的为item in items
中遍历的item项的值
7. Vue实例生命周期钩子
每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。 比如 created 钩子可以用来在一个实例被创建之后执行代码:
new Vue({
data: {
a: 1
},
created: function () {
// `this` 指向 vm 实例
console.log('a is: ' + this.a)
}
})
// => "a is: 1"
也有一些其它的钩子,在实例生命周期的不同阶段被调用,如 mounted、updated 和 destroyed。生命周期钩子的 this 上下文指向调用它的 Vue 实例。
8. Axios异步通信
8.1. 概述
Axios是一个开源的可以用在浏览器端和Node JS
的异步通信框架, 她的主要作用就是实现AJAX异步通信,其功能特点如下:
- 从浏览器中创建
XMLHttpRequests
- 从node.js创建http请求
- 支持Promise API[JS中链式编程]
- 拦截请求和响应
- 转换请求数据和响应数据
- 取消请求
- 自动转换JSON数据
- 客户端支持防御XSRF(跨站请求伪造)
8.2. 为什么使用Axios
由于Vue.js
是一个视图层框架并且作者(尤雨溪) 严格准守SoC(关注度分离原则)所以Vue.js
并不包含AJAX的通信功能, 为了解决通信问题, 作者单独开发了一个名为vue-resource
的插件, 不过在进入2.0版本以后停止了对该插件的维护并推荐了Axios
框架。少用jQuery, 因为它操作Dom太频繁!
8.3. 开始
引入Axios的js文件
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
模拟JSON数据
{ "name": "rootwhois", "url": "https://rootwhois.cn", "page": 1, "isNonProfit": true }
html
<div id="app"> <span></span><br> <a v-bind:href="info.url">Click Me</a><br> <span></span><br> <span></span> </div> <script type="text/javascript"> var vm = new Vue({ el: "#app", data() { return { info: { name: null, url: null, page: null, isNonProfit: null } } }, // 编译完成的html挂载到对应的dom时触发的钩子 mounted() { axios.get("data.json") .then(response => this.info = response.data); } }); </script>
说明:
v-cloak解决页面闪烁问题(不会出现这样的信息)
<style> [v-cloak]{ display: none; } </style> <div id="app" v-cloak>
在这里使用了v-bind将a:href的属性值与Vue实例中的数据进行绑定
使用axios框架的get方法请求AJAX并自动将数据封装进了Vue实例的数据对象中
我们在data中的数据结构必须和
Ajax
响应回来的数据格式匹配
9. 计算属性
计算属性的重点突出属性两个字上(属性是名词),这个属性有计算的能力,这里的计算就是个函数。它就是一个能够将计算结果缓存起来的属性(将行为转化成了静态的属性),可以理解为缓存。
<div id="app">
<!-- methods中的是方法,所以需要通过()调用 -->
<span>{{currentTime1()}}</span><br>
<!-- computed中虽然是一个方法,但是已经变成了一个计算属性,当作属性调用 -->
<span>{{currentTime2}}</span>
</div>
<script type="text/javascript">
let vue = new Vue({
el:"#app",
data:{
message: "message"
},
methods:{
// 在方法区中的方法,每调用一次就会产生一个新的数值。
currentTime1: function() {
return Date.now();
}
},
computed:{
// 计算属性有缓存机制,只有当方法区内的参数有变动后,才会产生一个新的值。
currentTime2: function() {
// this.message是为了能够让currentTime2观察到数据变化而变化
this.message;
return Date.now();
}
}
});
</script>
注意点:
- 方法区和计算属性区的方法不能重名,否则只会调用方法区的函数。
总结
调用方法时,每次都需要讲行计算,既然有计算过程则必定产生系统开销,那如果这个结果是不经常变化的,就可以考虑将这个结果缓存起来,采用计算属性可以很方便的做到这点,计算属性的主要特性就是为了将不经常变化的计算结果进行缓存,以节约我们的系统开销。
10. 插槽(内容分发)
在Vue中使用<slot>
元素作为承载分发内容的出口。
<div id="app">
<language>
<my-title slot="language-title" :title="title"></my-title>
<my-item slot="language-items" v-for="(item,index) in items" :item="item" :index="index"></my-item>
</language>
</div>
<script type="text/javascript"></script>
11. 自定义事件
数据项在Vue的实例中, 但删除操作要在组件中完成, 那么组件来删除Vue实例中的数据,就涉及到参数传递与事件分发了。Vue为我们提供了自定义事件的功能很好的帮助我们解决了这个问题; 使用this.$emit(‘自定义事件名’, 参数) 。
步骤:
- 在对象的methods中定义删除方法
- 对应组件添加删除按钮,绑定监听事件
- 增加自定义事件,通过自定义事件调用监听事件,实现调用在步骤1中定义的删除方法。
<div id="app">
<language>
<my-title slot="language-title" :title="title"></my-title>
<!-- v-on:remove="removeItems(index)"绑定监听事件 -->
<my-item slot="language-items" v-for="(item,index) in items"
:index="index" :item="item" v-on:remove="removeItems(index)"></my-item>
</language>
</div>
<script type="text/javascript">
Vue.component("language", {
template: '<div>\
<slot name="language-title"></slot>\
<ul>\
<slot name="language-items"></slot>\
</ul>\
</div>'
});
Vue.component("my-title", {
props: ['title'],
template: '<div>{{title}}</div>'
});
Vue.component("my-item", {
props: ['item', 'index'],
template: `<li>{{index}}--{{item}}<button @click="remove">删除</button></li>`,
methods:{
remove: function() {
// 绑定指定的监听事件,实现调用对象里面的removeItems方法。
this.$emit('remove');
}
}
});
let vm = new Vue({
el: "#app",
data: {
title: "语言",
items: ["Java", "Python", "C++"]
},
methods:{
// 删除items中指定索引的元素
removeItems: function(index) {
console.log("删除了" + this.items[index]);
this.items.splice(index,1);
}
}
});
</script>
12. 创建vue-cli项目
vue-cli官方提供的一个脚手架,用于快速生成一个vue的项目模板。
主要的功能
- 统一的目录结构
- 本地调试
- 热部署
- 单元测试
- 集成打包上线
12.1. 需要的环境
- Nodejs
- Git
12.2. 安装vue-cli
npm install vue-cli -g
vue list
12.3. 第一个vue-cli程序
初始化项目
说明:
- Project name:项目名称,默认回车即可
- Project description:项目描述,默认回车即可
- Author:项目作者,默认回车即可
- Install vue-router:是否安装vue-router,选择n不安装(后期需要再手动添加)
- Use ESLint to lint your code:是否使用ESLint做代码检查,选择n不安装(后期需要再手动添加)
- Set up unit tests:单元测试相关,选择n不安装(后期需要再手动添加)
- Setupe2etests with Nightwatch:单元测试相关,选择n不安装(后期需要再手动添加)
- Should we run npm install for you after the,project has been created:创建完成后直接初始化,选择n,我们手动执行;运行结果!
admin@admindeMacBook-Pro vue % vue init webpack myvue ? Project name myvue ? Project description A Vue.js project ? Author rootwhois ? Vue build standalone ? Install vue-router? No ? Use ESLint to lint your code? No ? Set up unit tests No ? Setup e2e tests with Nightwatch? No ? Should we run `npm install` for you after the project has been created? (recom mended) no vue-cli · Generated "myvue". # Project initialization finished! # ======================== To get started: cd myvue npm install (or if using yarn: yarn) npm run dev Documentation can be found at https://vuejs-templates.github.io/webpack
安装依赖
cd myvue npm install
运行
npm run dev
13. webpack
详见这里。
14. vue-router
Vue Router是Vue.js官方的路由管理器。它和Vue.js的核心深度集成, 让构建单页面应用变得易如反掌。
包含的功能:
- 嵌套的路由/视图表
- 模块化的、基于组件的路由配置
- 路由参数、查询、通配符
- 基于Vue js过渡系统的视图过渡效果
- 细粒度的导航控制
- 带有自动激活的CSS class的链接
- HTML5 历史模式或hash模式, 在IE 9中自动降级
- 自定义的滚动行为
14.1. 安装
npm install vue-router --save-dev
如果在一个模块化工程中使用它,必须要通过Vue.use()明确地安装路由功能:
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter);
14.2. 测试
components下创建组件
Content.vue
<template> <h1>Content</h1> </template> <script> export default { name: "Content" } </script>
HelloWorld.vue
<template> <h1>hello</h1> </template> <script> export default { name: 'HelloWorld' } </script>
安装路由,在src目录下新建文件夹router,专门用于存放路由。
index.js
import Vue from 'vue'; // 导入路由插件 import Router from 'vue-router'; import Hello from '../components/HelloWorld.vue'; import Content from '../components/Content.vue'; // 安装路由 Vue.use(Router); // 配置路由 export default new Router({ routes: [ { // 路由路径 path:'/content', // 路由名称 name:'content', // 跳转到的组件 component: Content }, { path:'/hello', name:'hello', component: Hello } ] });
src/main.js 导入路由
import Vue from 'vue' import App from './App' // 导入路由配置目录,自动扫描里面的路由配置 import router from './router'; new Vue({ el: '#app', // 配置路由 router, components: { App }, template: '<App/>' })
App.vue使用路由
<template> <div id="app"> <h1>vue-router</h1> <!-- router-link:默认渲染成一个a标签,to属性为指定链接 router-view:用于渲染路由匹配到的组件 --> <router-link to="/hello">Hello</router-link> <router-link to="/Content">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>
15. 实战
15.1. 简单的Vue
创建工程
admin@admindeMacBook-Pro ~ % mkdir vue admin@admindeMacBook-Pro ~ % cd vue admin@admindeMacBook-Pro vue % vue init webpack hello-vue ? Project name hello-vue ? Project description A Vue.js project ? Author rootwhois ? Vue build standalone ? Install vue-router? No ? Use ESLint to lint your code? No ? Set up unit tests No ? Setup e2e tests with Nightwatch? No ? Should we run `npm install` for you after the project has been created? (recom mended) no vue-cli · Generated "hello-vue". # Project initialization finished! # ======================== To get started: cd hello-vue npm install (or if using yarn: yarn) npm run dev Documentation can be found at https://vuejs-templates.github.io/webpack
cd hello-vue # 安装vue-router npm install vue-router --save-dev # 安装element-ui npm i element-ui -S # 安装依赖 npm install # 安装SASS加载器,这里npm报错 cnpm install [email protected] [email protected] --save-dev #启动测试 npm run dev
在src下创建如下结构:
- assets:用于存放资源文件
- components:用于存放Vue功能组件
- views:用于存放Vue视图组件
router:用于存放vue-router配置
views/Main.vue
<template> <h1>首页</h1> </template> <script> export default { name:"Main" } </script> <style> </style>
views/Login.vue
其中el-form的元素为ElementUI组件;
<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%"> <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(formName) { // 为表单绑定验证功能 this.$refs[formName].validate((valid) => { if (valid) { // 使用 vue-router 路由到指定页面,该方式称之为编程式导航 this.$router.push("/main"); } else { this.dialogVisible = true; return false; } }); } } } </script> <style lang="scss" 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 Router from "vue-router" // 导入视图 import Main from "../views/Main.vue" import Login from "../views/Login.vue" // 显式声明使用的模块 Vue.use(Router); // 进行路由 export default new Router({ routes:[ { path: "/login", component: Login }, { path: "/main", component: Main } ] });
main.js
import Vue from 'vue' import App from './App' import router from "./router" // 导入element-ui import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.use(router); Vue.use(ElementUI); new Vue({ el: '#app', router, // ElementUI render: h => h(App) })
App.vue
<template> <div id="app"> <router-link to="/main">Main</router-link> <router-link to="/login">Login</router-link> <router-view></router-view> </div> </template> <script> export default { name: 'App', } </script> <style> </style>
运行
npm run dev
可能遇到的问题
Module build failed: TypeError: this.getOptions is not a function
原因:sass-loader版本过高
解决方法:sass-loader降级
npm uninstall sass-loader cnpm install [email protected] --save-dev
Module build failed: Error: Node Sass version 6.0.1 is incompatible with ^4.0.0.
原因:node-sass版本过高
解决方法:node-sass降级
npm uninstall node-sass npm install [email protected] --save-dev
15.2. 路由嵌套
嵌套路由又称子路由,在实际应用中,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件,例如:
/user/foo/profile /user/foo/posts
+------------------+ +-----------------+
| User | | User |
| +--------------+ | | +-------------+ |
| | Profile | | +------------> | | Posts | |
| | | | | | | |
| +--------------+ | | +-------------+ |
+------------------+ +-----------------+
views/user/Profile.vue
<template> <h1>个人信息</h1> </template> <script> export default { name: "UserProfile" } </script> <style> </style>
views/user/List.vue
<template> <h1>用户列表页</h1> </template> <script> export default { name: "UserList" } </script> <style> </style>
index.js
import Vue from "vue" import Router from "vue-router" // 导入视图 import Main from "../views/Main.vue" import Login from "../views/Login.vue" import UserProfile from "../views/user/Profile.vue" import UserList from "../views/user/List.vue" // 显式声明使用的模块 Vue.use(Router); // 进行路由 export default new Router({ routes:[ { path: "/login", component: Login }, { path: "/main", component: Main, // 子路由 children: [ { path:"/user/profile", component: UserProfile }, { path:"/user/list", component: UserList } ] } ] });
Main.vue
在元素中配置了用于展示嵌套路由,主要使用个人信息展示嵌套路由内容。
<template> <div> <el-container> <el-aside width="200px"> <el-menu :default-openeds="['1']"> <el-submenu index="1"> <template slot="title"><i class="el-icon-caret-right"></i>用户管理</template> <el-menu-item-group> <el-menu-item index="1-1"> <router-link to="/user/profile">个人信息</router-link> </el-menu-item> <el-menu-item index="1-2"> <router-link to="/user/list">用户列表</router-link> </el-menu-item> </el-menu-item-group> </el-submenu> <el-submenu index="2"> <template slot="title"><i class="el-icon-caret-right"></i>内容管理</template> <el-menu-item-group> <el-menu-item index="2-1">分类管理</el-menu-item> <el-menu-item index="2-2">内容列表</el-menu-item> </el-menu-item-group> </el-submenu> </el-menu> </el-aside> <el-container> <el-header style="text-align: right; font-size: 12px"> <el-dropdown> <i class="el-icon-setting" style="margin-right: 15px"></i> <el-dropdown-menu slot="dropdown"> <el-dropdown-item>个人信息</el-dropdown-item> <el-dropdown-item>退出登录</el-dropdown-item> </el-dropdown-menu> </el-dropdown> </el-header> <el-main> <router-view /> </el-main> </el-container> </el-container> </div> </template> <script> export default { name: "Main" } </script> <style scoped lang="scss"> .el-header { background-color: #B3C0D1; color: #333; line-height: 60px; } .el-aside { color: #333; } </style>
15.3. 参数传递
我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。此时我们就需要传递参数了。
传递参数有两种方式,一种是$route,一种是props。以下都有进行演示。
修改路由配置
/goHome实现组件重定向,访问后会跳转到/main
// 进行路由 export default new Router({ routes:[ { path: "/login", component: Login }, { path: "/main", component: Main, // 子路由 children: [ { // 这里的传参的方式为RestFul风格 path:"/user/profile/:id/:name", // 绑定to值的name属性,传参数的时候需要设置 name: "UserProfile", component: UserProfile, // 支持传递参数(第二种) props: true }, { path:"/user/list", component: UserList }, { path:"/goHome", // 重定向 redirect: "/main" } ] } ] });
传递参数
将 to 改为了 :to,是为了将这一属性当成对象使用,
注意 router-link 中的 name 属性名称一定要和路由中的name 属性名称 匹配,因为这样 Vue 才能找到对应的路由路径;
<el-menu-item-group> <el-menu-item index="1-1"> <!-- <router-link v-bind:to="{name:'UserProfile', params:{id:1}}">个人信息</router-link> 带参数的格式--> <router-link v-bind:to="{name:'UserProfile', params:{id:1, name:'张三'}}">个人信息</router-link> </el-menu-item> <el-menu-item index="1-2"> <router-link to="/user/list">用户列表</router-link> </el-menu-item> <el-menu-item index="1-3"> <router-link to="/goHome">回到首页</router-link> </el-menu-item> </el-menu-item-group>
接收参数并显示
<template> <div> <h1>个人信息</h1> <!-- 通过直接传递 --> <span></span><br> <span></span><br> <!-- 通过传递参数方式(第二种) --> <span></span><br> <span></span> </span> </div> </template> <script> export default { // props: true设置后,可以直接接收参数(第二种方式) props:["id","name"], name: "UserProfile" } </script> <style> </style>
15.4. 去除地址栏中的/
默认为带/#,mode: "hash"
如果要更改为不带/#,则mode: "hostory"
export default new Router({
mode:"history",
...
};
15.5. 404
定制404页面
<template> <div>404 页面不存在</div> </template> <script> export default { name: "NotFound" } </script> <style> </style>
添加路由
import Vue from "vue" import Router from "vue-router" // 导入视图 // ... import NotFound from "../views/NotFound.vue" Vue.use(Router); export default new Router routes:[ // ... { path: "*", component: NotFound } ] });
15.6. 路由钩子和异步请求
axios
axios依赖安装和导入
npm install axios
npm install vue-axios
cnpm install [email protected] [email protected] --save-dev
// 导入axios
import axios from "axios"
import VueAxios from "vue-axios"
Vue.use(VueAxios, axios)
在static/mock/data.json下准备json文件
{
"name": "rootwhois",
"url": "https://rootwhois.cn",
"page": 1,
"isNonProfit": true
}
在static目录下的静态资源不会经过路由,可以直接访问。
路由钩子
beforeRouteEnter
:在进入路由前执行beforeRouteLeave
:在离开路由前执行
参数说明:
- to:路由将要跳转的路径信息
- from:路径跳转前的路径信息
- next:路由的控制参数
- next() 跳入下一个页面
- next(’/path’) 改变路由的跳转方向,使其跳到另一个路由
- next(false) 返回原来的页面
- next((vm)=>{}) 仅在 beforeRouteEnter 中可用,vm 是组件实例
示例:
Profile.vue
<template>
<div>
<h1>个人信息</h1>
</span>
</div>
</template>
<script>
export default {
name: "UserProfile",
// 使用路由钩子
beforeRouteEnter: (to, from, next) => {
console.log("进入路由之前");
// 通过vm调用当前对象下的方法
next(vm => {
vm.getData();
});
},
beforeRouteLeave: (to, from, next) => {
console.log("进入路由之后");
next();
},
methods:{
getData: function() {
// 调用axios异步请求
this.axios({
method: 'get',
url: 'http://localhost:8080/static/mock/data.json'
}).then(function(response){
console.log(response);
});
}
}
}
</script>
<style>
</style>
运行后,可以在控制台看到请求体response被打印出来,此时可以通过上面的Axios异步通信进行更多设置。
16. 值得注意的问题
<template>
中,必须要有唯一一个根结点。- 尽量用npm,少用cnpm。但是有些软件必须cnpm才能装得上。