vue的梳理对照react展开,文章:React相关技术梳理
Vue
概览
核心概念
Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统:
<div id="app">
{{ message }}
</div>
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
一个 Vue 应用会将其挂载到一个 DOM 元素上 (对于这个例子是 #app
) 然后对其进行完全控制。那个 HTML 是我们的入口,但其余都会发生在新创建的 Vue 实例内部。
在这里有了2个基本元素,html的模板,以及Vue示例。html模板相当于JSX元素,Vue示例是控制模板的。这与react不同,react组件渲染之后就是元素。Vue是一种组合方式,React是一种类封装方式。
Vue实例中有2个属性:el(挂载的元素)、data。这里的data与react相比不再分props与state,它俩都是data
vue指令
-
v-bind
绑定模板中的attribute与Vue实例中的property。
<div id="app-2">
<span v-bind:title="message">
鼠标悬停几秒钟查看此处动态绑定的提示信息!
</span>
</div>
var app2 = new Vue({
el: '#app-2',
data: {
message: '页面加载于 ' + new Date().toLocaleString()
}
})
“将这个元素节点的 title
attribute 和 Vue 实例的 message
property 保持一致”。
v-bind大多是对显示类组件绑定,表单类组件一般用的v-model
-
v-if
把数据绑定到 DOM 文本或 attribute,还可以绑定到 DOM 结构。控制切换一个元素是否显示。
-
v-for
v-for
指令可以绑定数组的数据来渲染一个项目列表li
-
v-on
为了让用户和你的应用进行交互,可以用 v-on
指令添加一个事件监听器,通过它调用在 Vue 实例中定义的方法。
<div id="app-5">
<p>{{ message }}</p>
<button v-on:click="reverseMessage">反转消息</button>
</div>
var app5 = new Vue({
el: '#app-5',
data: {
message: 'Hello Vue.js!'
},
methods: {
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
}
}
})
这里我们看到了Vue实例第3个属性:methods,这里定义函数。
组件系统
这一部分与react的理念部分类似,都是讲解怎么来使用vue,下边来看看:
-
组件拆分
几乎任意类型的应用界面都可以抽象为一个组件树,
-
模板、Vue示例、Vue组件3元素
为了能够重用页面,这里也必须需要组件(Component),那这样Vue就有3个内容:模板、Vue示例、Vue组件3元素。
Vue组件更偏View,是将部分的View做了封装,这样可以进行重用。Vue示例做的绑定,并且可以做控制。
<!-- 模板 -->
<div id="app-7">
<ol>
<todo-item
v-for="item in groceryList"
v-bind:todo="item"
v-bind:key="item.id"
></todo-item>
</ol>
</div>
// Vue组件
Vue.component('todo-item', {
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
})
// Vue实例
var app7 = new Vue({
el: '#app-7',
data: {
groceryList: [
{ id: 0, text: '蔬菜' },
{ id: 1, text: '奶酪' },
{ id: 2, text: '随便其它什么人吃的东西' }
]
}
})
Vue组件有2个属性:props与template,子单元通过 prop 接口与父单元进行了良好的解耦。
下边分别来看看Vue的基本内容
Vue实例
本节来看看Vue实例。虽然没有完全遵循 MVVM 模型,但是 Vue 的设计也受到了它的启发。因此在文档中经常会使用 vm
(ViewModel 的缩写) 这个变量名表示 Vue 实例。反观Vue实例,确实做了data-binding的事情,将数据(model)与模板(view)做了绑定。但又不仅仅是数据绑定,应为它还有methods,所以也做了部分controller的事情。
在这里我们看到:你只需要明白所有的 Vue 组件都是 Vue 实例
,它俩确实很接近,都做了data-binding的事情。定义的方式不同,Vue实例是绑定到模板,Vue组件是被模板所使用。到目前为止,Vue组件中还没有出现methods,算是低配的Vue实例。
响应式
vue示例中的数据发生变化,模板中的数据就相应变化。这也是mvvm的好处。
可以通过Object.freeze(obj)来阻止这种binding
它将 data
对象中的所有的 property 加入到 Vue 的响应式系统中。当这些 property 的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。
// 我们的数据对象
var data = { a: 1 }
// 该对象被加入到一个 Vue 实例中
var vm = new Vue({
data: data
})
// 获得这个实例上的 property
// 返回源数据中对应的字段
vm.a == data.a // => true
$ 方法
为了区别Vue示例中自带的,与用户自定义的,通过$
符号来加以区分。
var data = { a: 1 }
var vm = new Vue({
el: '#example',
data: data
})
vm.$data === data // => true
vm.$el === document.getElementById('example') // => true
// $watch 是一个实例方法
vm.$watch('a', function (newValue, oldValue) {
// 这个回调将在 `vm.a` 改变后调用
})
生命周期
new Vue({
data: {
a: 1
},
created: function () {
// `this` 指向 vm 实例
console.log('a is: ' + this.a)
}
updated: function(){...}
destroyed: function(){...}
})
create => mount => update => destroy
computed属性*
这个计算属性与react中的计算属性类似。
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。这时候就用计算属性。
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
这里我们看到vue示例的第4个属性:computed、el、 data、 method(除生命周期函数)。
你可以像绑定普通 property 一样在模板中绑定计算属性。Vue 知道 vm.reversedMessage
依赖于 vm.message
,因此当 vm.message
发生改变时,所有依赖 vm.reversedMessage
的绑定也会更新。
与react类似,computed是响应式的,只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message
还没有发生改变,多次访问 reversedMessage
计算属性会立即返回之前的计算结果,而不必再次执行函数。
计算属性默认只有getter,我们可以在需要时增加一个setter
// ...
computed: {
fullName: {
// getter
get: function () {
return this.firstName + ' ' + this.lastName
},
// setter
set: function (newValue) {
var names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
// ...
现在再运行 vm.fullName = 'John Doe'
时,setter 会被调用,vm.firstName
和 vm.lastName
也会相应地被更新。
watch
当需要在数据变化时执行异步或开销较大的操作时,watch是最有用的。
官方例子中,将axios请求放到了watch中执行。地址
这里看到了Vue的第5个属性watch(el、data、methods、computed、watch)。
它通过录入内容,实时向后端请求数据,通过v-model,绑定input的数据到Vue的question属性上,然后通过watch来监听该propterty,当该property发生变化后,调用this.debouncedGetAnswer()。
模板语法
模板语法是vue特有的,上边介绍的vue指令,就是模板语法。
在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。
插值
将vue示例中的数据,绑定在模板上,可以绑定在content中,也可以绑定在attribute上。
-
双大括号
绑定在内容上
<span>Message: {{ msg }}</span>
Mustache 标签(“双大括号”)将会被替代为对应数据对象上 msg
property 的值。
双大括号中,可以使表达式,如:{{ number + 1 }}
-
v-html
双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html 指令
绑定在attribute上
-
v-bind
Mustache 语法不能作用在 HTML attribute 上,遇到这种情况应该使用 v-bind
绑定在attribute上
指令
指令 (Directives) 是带有 v-
前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 (v-for
是例外情况,稍后我们再讨论)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,v-bind
指令可以用于响应式地更新 HTML attribute。
-
v-bind
v-bind是一种单向绑定,将vue示例的property渲染到模板上。
-
v-if
-
v-for
-
v-on
-
动态参数
<a v-bind:[attributeName]="url"> ... </a>
这里的 attributeName
会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。例如,如果你的 Vue 实例有一个 data
property attributeName
,其值为 "href"
,那么这个绑定将等价于 v-bind:href
。
<a v-on:[eventName]="doSomething"> ... </a>
在这个示例中,当 eventName
的值为 "focus"
时,v-on:[eventName]
将等价于 v-on:focus
。
注意:
动态参数表达式有一些语法约束,因为某些字符,如空格和引号,放在 HTML attribute 名里是无效的。因此要避免使用在动态参数中使用空格与引号
在 DOM 中使用模板时 (直接在一个 HTML 文件里撰写模板),还需要避免使用大写字符来命名键名,因为浏览器会把 attribute 名全部强制转为小写
<!--
在 DOM 中使用模板时这段代码会被转换为 `v-bind:[someattr]`。
除非在实例中有一个名为“someattr”的 property,否则代码不会工作。
-->
<a v-bind:[someAttr]="value"> ... </a>
ps:通过directive自定义命令。
修饰符*
修饰符 (modifier) 是以半角句号 .
指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。
<form v-on:submit.prevent="onSubmit">...</form>
事件修饰符
.prevent
修饰符告诉 v-on
指令对于触发的事件调用 event.preventDefault()
.
.stop
, 阻止事件冒泡
.once
,只触发一次
.self
, 事件绑定的元素本身触发时才触发回调
.native
,将一个vue组件变成一个普通的html,使其可以监听click等原生事件
表单修饰符
.lazy
在输入框输入完内容,光标离开时才更新视图
.trim 过滤首尾空格
.number
如果先输入数字,那它就会限制你输入的只能是数字;如果先输入字符串,那就相当于没有加.number
缩写
Vue 为 v-bind
和 v-on
这两个最常用的指令,提供了特定简写。还有一个#
v-bind:
<!-- 完整语法 -->
<a v-bind:href="url">...</a>
<!-- 缩写 -->
<a :href="url">...</a>
<!-- 动态参数的缩写 (2.6.0+) -->
<a :[key]="url"> ... </a>
v-on@
<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>
<!-- 缩写 -->
<a @click="doSomething">...</a>
<!-- 动态参数的缩写 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>
v-model? #
class**
操作元素的 class 列表和内联样式是数据绑定的一个常见需求。因为它们都是 attribute,**所以我们可以用 v-bind
处理它们:只需要通过表达式计算出字符串结果即可。**不过,字符串拼接麻烦且易错。因此,在将 v-bind
用于 class
和 style
时,Vue.js 做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。
我们可以传给 v-bind:class
一个对象,以动态地切换 class:
<div v-bind:class="{ active: isActive }"></div>
上面的语法表示 active
这个 class 存在与否将取决于数据 property isActive
的 truthiness。
当isActive为true事,会被渲染成:
<div class="active"></div>
除了对象,还可以把数组传给
<div v-bind:class="[activeClass, errorClass]"></div>
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
会被渲染成:
<div class="active text-danger"></div>
style
<div v-bind:style="styleObject"></div>
data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
表单绑定 v-model
以上对条件渲染(v-if)、列表渲染(v-for)、事件(v-on)介绍挺多了,这里不再赘言了。
v-on有几个修饰符在这里.
你可以用 v-model
指令在表单 <input>
、<textarea>
及 <select>
元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但 v-model
本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
v-model
在内部为不同的输入元素使用不同的 property 并抛出不同的事件:
- text 和 textarea 元素使用
value
property 和 input
事件;
- checkbox 和 radio 使用
checked
property 和 change
事件;
- select 字段将
value
作为 prop 并将 change
作为事件。
下边来看看:
-
单行录入 input
<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>
-
多行录入 textare
<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<br>
<textarea v-model="message" placeholder="add multiple lines"></textarea>
-
复选框 input checkbox
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
-
单选框 input radio
<div id="example-4">
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>Picked: {{ picked }}</span>
</div>
-
下拉 select
<div id="example-5">
<select v-model="selected">
<option disabled value="">请选择</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
<span>Selected: {{ selected }}</span>
</div>
组件基础
组件是可复用的 Vue 实例,且带有一个名字。
因为组件是可复用的 Vue 实例,所以它们与 new Vue
接收相同的选项,例如 data
、computed
、watch
、methods
以及生命周期钩子等。仅有的例外是像 el
这样根实例特有的选项。
这里讲清楚了,vue组件包含这与vue实例相同的属性,除了vue实例用el进行挂载,而vue组件有自己名字之外,其余相同。
data必须是一个函数
因为组件可以复用,而data如果是一个对象,就会发现所有共用的是一个状态。一个组件的 data
选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝。
data: function () {
return {
count: 0
}
}
注册
为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册和局部注册。至此,我们的组件都只是通过 Vue.component
全局注册的
Vue.component('my-component-name', {
// ... options ...
})
全局注册的组件可以用在其被注册之后的任何 (通过 new Vue
) 新创建的 Vue 根实例,也包括其组件树中的所有子组件的模板中。
局部注册:
全局注册往往是不够理想的。比如,如果你使用一个像 webpack 这样的构建系统,全局注册所有的组件意味着即便你已经不再使用一个组件了,它仍然会被包含在你最终的构建结果中。这造成了用户下载的 JavaScript 的无谓的增加。
可以先定义组件
var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }
然后在components中定义组件
new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})
注意局部注册的组件在其子组件中*不可用*。如果想用,就需要注册进该组件。
通过props向子组件传递数据**
这个与React的相同
Vue.component('blog-post', {
props: ['title'],
template: '<h3>{{ title }}</h3>'
})
监听子组件事件(自定义事件)*
Vue 实例提供了一个自定义事件的系统,使父级组件可以像处理 native DOM 事件一样通过 v-on
监听子组件实例的任意事件,子组件中用$emit('事件名')
来触发事件。
父组件:
<blog-post
...
v-on:enlarge-text="postFontSize += 0.1"
></blog-post>
子组件:
<button v-on:click="$emit('enlarge-text')">
Enlarge text
</button>
疑问:这种方式是否可以在兄弟组件之间传参?
不可以直接在兄弟组件之间传递数据
$emit()函数可以抛出第二个参数,用于给相应函数的参数,如下:
<button v-on:click="$emit('enlarge-text', 0.1)">
Enlarge text
</button>
组件上使用v-model
<input v-model="searchText">
等价于:
<input
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
>
v-model做了2个事,绑定到数据property上,接收数据改变;监听输入,修改对应的数据。自定义组件上,v-model是这样:
疑问:v-bind不就是双向的吗?
<custom-input
v-bind:value="searchText"
v-on:input="searchText = $event"
></custom-input>
为了让它正常工作,这个组件内的 <input>
必须:
- 将其
value
attribute 绑定到一个名叫 value
的 prop 上
- 在其
input
事件被触发时,将新的值通过自定义的 input
事件抛出
写成之后是这样:
Vue.component('custom-input', {
props: ['value'],
template: `
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
})
内部通过emit将事件与数据抛出,外部通过v-on绑定到searchText上。
通过插槽向子组件传递内容
和 HTML 元素一样,我们经常需要向一个组件传递内容,将content传递到相应的位置上
<alert-box>
Something bad happened.
</alert-box>
Vue.component('alert-box', {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
`
})
插槽是把内容插到组件的相应位置上。
动态组件*
有的时候,在不同组件之间进行动态切换是非常有用的,比如在一个多标签的界面里,可以过 Vue 的 <component>
元素加一个特殊的 is
attribute 来实现。
如下:
<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>
在上述示例中,currentTabComponent
可以包括
示例
通过keep-alive来缓存失活的组件
<!-- 失活的组件将会被缓存!-->
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
异步组件
在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了简化,Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。
一个推荐的做法是将异步组件和 webpack 的 code-splitting 功能一起配合使用:
Vue.component('async-webpack-example', function (resolve) {
// 这个特殊的 `require` 语法将会告诉 webpack
// 自动将你的构建代码切割成多个包,这些包
// 会通过 Ajax 请求加载
require(['./my-async-component'], resolve)
})
或者
Vue.component(
'async-webpack-example',
// 这个动态导入会返回一个 `Promise` 对象。
() => import('./my-async-component')
)
这一部分理解的不深刻,暂时先这样。
Vuex
对比Redux,来看看vuex。
基础概念

在redux中,action被dispatch到reducer中去执行,在ruducer中决定新的状态,并返回给store,然后触发view发生变化。
这里也类似,view与store之间也是view向上发送action,而在mutation中改变state,然后触发view发生变化。
这里的action并不直接触发state的改变,而是先触发了一个mutaion(reducer)然后再触发state的改变,state触发view的改变。
state
mapState
getters
mapGetters
mutations
mapMutations
Vuex 中的 mutation 非常类似于事件,各mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler).
mutations的实现就是基于事件的,emit与on。
state与getters都是从store中获取数据,mutations则是改变store中的数据。
mutations必须是同步函数。
mutations对标的reducer,计算新的state。
actions*
mapActions
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
可以在 action 内部执行异步操作。
action通过store.dispatch
方法触发
Action通常是异步的,如何才能组合多个 action,以处理更加复杂的异步流程?
store.dispatch
可以处理被触发的 action 的处理函数返回的 Promise
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
然后就可以
store.dispatch('actionA').then(() => {
// ...
})
module
于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。
对于模块内部的 action,局部状态通过 context.state
暴露出来,根节点状态则为 context.rootState
.
const moduleA = {
// ...
actions: {
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
它还有命名空间,这个先不做深入的学习。
Router
router与react的router相比很类似,也是有link、router、route3元素,以及栈结构组成,然后就是动态路由、路由传参等内容,下边简单看一下:
还有一个router-view,router-view像是一个占位符,将route带的componet渲染到router-view的位置上。
基础概念
在模板中只用router-link来做导航:
<div id="app">
<h1>Hello App!</h1>
<p>
<!-- 使用 router-link 组件来导航. -->
<!-- 通过传入 `to` 属性指定链接. -->
<!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<!-- 路由出口 -->
<!-- 路由匹配到的组件将渲染在这里 -->
<router-view></router-view>
</div>
然后通过创建VueRouter的实例来创建路由,并将其注入到Vue实例中
// 1. 定义 (路由) 组件。
// 可以从其他文件 import 进来
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
// 2. 定义路由
// 每个路由应该映射一个组件。 其中"component" 可以是
// 通过 Vue.extend() 创建的组件构造器,
// 或者,只是一个组件配置对象。
// 我们晚点再讨论嵌套路由。
const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
// 3. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({
routes // (缩写) 相当于 routes: routes
})
// 4. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
const app = new Vue({
router
}).$mount('#app')
// 现在,应用已经启动了!
最后在组件中,可以通过this.$route
来访问当前路由,也可以通过this.$router
来访问路由器如下:
// Home.vue
export default {
computed: {
username() {
// 我们很快就会看到 `params` 是什么
return this.$route.params.username
}
},
methods: {
goBack() {
window.history.length > 1 ? this.$router.go(-1) : this.$router.push('/')
}
}
}
动态路由
一个“路径参数”使用冒号 :
标记。当匹配到一个路由时,参数值会被设置到 this.$route.params
,可以在每个组件内使用
const User = {
template: '<div>User {{ $route.params.id }}</div>'
}
const router = new VueRouter({
routes: [
// 动态路径参数 以冒号开头
{ path: '/user/:id', component: User }
]
})
可以通过 watch来监测$route
对象的变化
const User = {
template: '...',
watch: {
$route(to, from) {
// 对路由变化作出响应...
}
}
}
路由的配置同样是按顺序进行匹配的,所以需要注意顺序。最后用path:'*' 来匹配404错误。
嵌套路由*
实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件。
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User,
children: [
{
// 当 /user/:id 匹配成功,
// UserHome 会被渲染在 User 的 <router-view> 中
path: '',
component: UserHome
},
{
// 当 /user/:id/profile 匹配成功,
// UserProfile 会被渲染在 User 的 <router-view> 中
path: 'profile',
component: UserProfile
},
{
// 当 /user/:id/posts 匹配成功
// UserPosts 会被渲染在 User 的 <router-view> 中
path: 'posts',
component: UserPosts
}
]
}
]
})
children
配置就是像 routes
配置一样的路由配置数组,所以可以嵌套多层路由。
对于在children中增加path:'',这条有点疑惑:那匹配的User还是UserHome?这样留到实践中。
编程式导航
router本质是个栈,除了用 <router-link :to="...">
外还可以使用router.push(...)
const userId = '123'
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 这里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user
// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
注意:如果提供了 path
,params
会被忽略
与react-router类似,除了push还包括replace、go,这里不赘述了。
命名路由
可以在创建 Router 实例的时候,在 routes
配置中给某个路由设置名称。
const router = new VueRouter({
routes: [
{
path: '/user/:userId',
name: 'user',
component: User
}
]
})
这样可以使用name来进行导航:
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
或者:
router.push({ name: 'user', params: { userId: 123 }})
命名视图*
有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar
(侧导航) 和 main
(主内容) 两个视图,这个时候命名视图就派上用场了。你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view
没有设置名字,那么默认为 default
router-view可以有多个,这样在router中可以带多个component与其相匹配。
<!-- UserSettings.vue -->
<div>
<h1>User Settings</h1>
<NavBar/>
<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
</div>
这样,在componets中可以携带多个组件
const router = new VueRouter({
routes: [
{
path: '/',
components: {
default: Foo,
a: Bar,
b: Baz
}
}
]
})
router-view是在模板中占位,通过router将相应的componet渲染到对应的位置上。
路由传参
重定向与别名与react-router是相同的,这里不做介绍了。
const router = new VueRouter({
routes: [
{ path: '/a', redirect: '/b' }
]
})
下边来简单来看看路由传参。
在组件中使用 $route
会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。
使用 props
将组件和路由解耦:
const User = {
template: '<div>User {{ $route.params.id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User }
]
})
以上代码将组件User与router做了绑定,修改如下:
const User = {
props: ['id'],
template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({
routes: [
{ path: '/user/:id', component: User, props: true },
// 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:
{
path: '/user/:id',
components: { default: User, sidebar: Sidebar },
props: { default: true, sidebar: false }
}
]
})
上例通过props设置为ture,route.params
将会被设置为组件属性。
通过props解耦有3种方式,除了上例的布尔模式,还有函数模式,如下:
const router = new VueRouter({
routes: [
{ path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) }
]
})
URL /search?q=vue
会将 {query: 'vue'}
作为属性传递给 SearchUser
组件。