常见的修饰符及作用如下:
- .stop:阻止事件冒泡,等价于调用 event.stopPropagation();
- .prevent:阻止默认事件,等价于 event.preventDefault();
- .capture:以捕获模式监听事件,即 addEventListener(event, fn, true);
- .self:只有当 event.target 为自身时才出发,从内部元素冒泡出来时不触发;
- .once:绑定后只触发一次。
还有按键修饰符,简化了我们对按键事件的处理。
- keyCode:如 @keyup.13,即监听 keyCode 为 13(回车键)的按键抬起事件。
别名:直接使用 keyCode 也比较麻烦,毕竟我们不会去记所有按键的 keyCode,这里给常见的按键提供了别名方便使用,如
@keyup.enter
,常见的别名有:- .enter
- .tab
- .delete
- .esc
- .space
- .up
- .down
- .left
- .right
系统修饰键:修饰键与常规按键会有所不同,在和
keyup
事件一起用时,事件触发时修饰键必须处于按下状态。例如:@keyup.ctrl.enter,即为按住ctrl
键,抬起enter
键才会触发,单抬起ctrl
不会被触发。Vue 总共包含以下四种系统修饰键:- .ctrl
- .alt
- .shift
- .meta:Mac 系统键盘上,meta 对应 command 键(⌘)。在 Windows 系统键盘 meta 对应 Windows 徽标键 (⊞)。
另外,修饰符支持叠加,且按顺序执行,例如:
@click.stop.prevent="doSth"
即为阻止默认事件且阻止冒泡;@keyup.ctrl.enter
即监听 ctrl+ 回车键释放事件;@click.self.prevent
仅阻止对元素自身的默认事件,而@click.prevent.self
则会阻止所有的默认操作(后面的.self
其实没有起作用)。- v-text:更新元素的
textContent
,与直接使用{{}}
相比,可以避免 Vue 实例尚未作用于元素上,用户能直接看到{{ data }}
这样的模板语法。 - v-html:更新元素的
innerHTML
。 - v-pre: 跳过元素和其子元素的编译过程,加快编译速度,如
<div v-pre>...</div>
。 - v-cloak:保持该属性存在于元素上,直到关联的实例结束编译,常用于隐藏未编译的标签直到编译结束。例如:
v-once:只渲染元素和组件一次,随后即使触发重发渲染,该作用的元素或组件内的节点都将被视为静态内容,不会随之更新。
最早 Vue 在实现组件间通信的时候是采用派发(dispatch,事件向上冒泡,触发后停止冒泡)和广播(broadcast,事件向下传递给所有后代)的方式实现组件间通信。但这种方式在父子间通信时较为适用,但存在两个问题:
- 兄弟组件间通信流程过长:兄弟组件间通信需要通过父组件中转。例如,兄弟组件 A、B,父组件 C,A 派发事件冒泡到 C,C 接受到后再次广播到 A、B,B 监听到后触发自己的事件,流程相对而言较长。
- 通信流程依赖于 DOM 结构:派发和冒泡的流程都会依赖于组件间的结构,这样就形成了耦合,不利于组件的拆分。
所以在 Vue 2.0 后就取消了这种通信方式的支持,而推荐使用事件总线的方式,在简单场景下,可以使用一个空的 Vue 实例作为事件总线,例如:
`var bus = new Vue()
组件A:
bus.$emit('action', data) // 触发action事件
组件B:
bus.$on('action', funtion (data) { ... }) // 监听action事件`
复制
这样无论组件 A 在哪个位置,B 都能监听到 A 触发的 action 事件,与 DOM 结构就解耦了。在更加复杂的场景下,我们会使用状态管理: vuex 来进行开发~~~~
Vue.js 也提供了在父组件内渲染子组件数据的方法,那就是作用域插槽,通常会用在一些表格、列表类型的插件上,这样使用者就可以自定义渲染具体的每一项
// List.vue
<ul>
<slot name="item" v-for="item in items" :item="item"></slot>
</ul>
// ListDemo.vue
<list :items="items">
<template slot-scope="props" slot="item">
<li>{{ props.item.text }}</li>
// 渲染具体属性的控制权交由给了父组件
// 在不改变子组件的情况下,我们可以根据父组件的情况,来渲染不同的属性,例如 props.item.value 之类
// 这比在子组件间直接渲染提升了扩展性,但相对而言,子组件封装的能力也相对减弱了(并不负责渲染的逻辑,都需父组件完成)
</template>
</list>
在父级中,需要使用具有slot-scope
的<template>
标签,表示它是作用域插槽的模板。slot-scope
的值将被用作一个临时变量名,此变量接收从子组件传递过来的prop
对象。<list :items="items"><li slot-scope="props" slot="item">{{ props.item }}</li></list>
1、如何理解Vue的作用域插槽
举个例子,比如我写了一个可以实现条纹相间的列表组件,发布后,使用者可以自定义每一行的内容或样式(普通的slot就可以完成这个工作)。而作用域插槽的关键之处就在于,父组件能接收来自子组件的slot传递过来的参数,具体看案例和注释。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue作用域插槽</title>
<script src="https://cdn.bootcss.com/vue/2.3.4/vue.js"></script>
</head>
<body>
<div id="app2">
<!-- 组件使用者只需传递users数据即可 -->
<my-stripe-list :items="users" odd-bgcolor="#D3DCE6" even-bgcolor="#E5E9F2">
<!-- props对象接收来自子组件slot的$index参数 -->
<template slot="cont" scope="props">
<span>{{users[props.$index].id}}</span>
<span>{{users[props.$index].name}}</span>
<span>{{users[props.$index].age}}</span>
<!-- 这里可以自定[编辑][删除]按钮的链接和样式 -->
<a :href="'#edit/id/'+users[props.$index].id">编辑</a>
<a :href="'#del/id/'+users[props.$index].id">删除</a>
</template>
</my-stripe-list>
</div>
<script> Vue.component('my-stripe-list', {
/*slot的$index可以传递到父组件中*/
template: `
<div>
<div v-for="(item, index) in items" style="line-height:2.2;" :style="index % 2 === 0 ? 'background:'+oddBgcolor : 'background:'+evenBgcolor">
<slot name="cont" :$index="index"></slot>
</div>
</div>
`,
props: {
items: Array,
oddBgcolor: String,
evenBgcolor: String
}
});
new Vue({
el: '#app2',
data: {
users: [
{id: 1, name: '张三', age: 20},
{id: 2, name: '李四', age: 22},
{id: 3, name: '王五', age: 27},
{id: 4, name: '张龙', age: 27},
{id: 5, name: '赵虎', age: 27}
]
}
}); </script>
</body>
</html>
mixins:
如果mixins
中的选项与组件选项定义了相同的属性的话,根据属性的不同,有两种处理方式:
- 合并:生命周期钩子函数,两者的函数会合并成一个数组,依次调用,混合属性的钩子函数先于组件的同名钩子函数调用。
watch
也按此规则处理。 - 覆盖:
data
computed
methods
components
directives
,则会被混合成一个对象,键名冲突时,取组件选项键值对。
axios
,它封装了 XMLHttpRequest 对象和 Node.js 的 http 模块,使得我们的数据请求代码可以在浏览器端和服务器(Node 环境)下复用
XMLHttpRequest 从早期只是微软浏览器提供的一个接口,到如今 W3C 对其进行标准化,并且各大浏览器也都纷纷实现了这个功能,也经历了一次标准升级。相对于Level 1
、Level 2
新增了以下功能:
- 支持发送和接受二进制数据
- 新增 formData 对象,支持发送表单数据
- 发送和获取数据时,可以获取进度信息
从这几个功能可以看出,我们可以通过异步接口进行文件的上传,并获取上传进度信息:
...
<input type="file" @change="upload">
...
import axios from 'axios'
...
methods: {
upload (e) {
const file = e.target.files[0];
var formData = new FormData();
formData.append('file', file);
axios({
url: '/file/upload', // 填写自己的文件上传接口api
method: 'post',
data: formData, // 表单数据
onUploadProgress (progressEvent) {
// 接受上传进度,可以用于处理上传进度条等逻辑
}
})
}
}
路由:
URL 中的 hash 指的是#
右边的字符,例如http://host.com/path#abc
,abc
即为 hash 值,可以通过location.hash
来获取。修改 hash 值并不会引起浏览器刷新,加上我们可以使用document.body.onhashchange
来监听 hash 值的变化,那一个简单的路由雏形就形成了:
// 监听hash变化
window.onhashchange = function (event) {
// 根据变化后的hash值去切换对应的视图
}
// 或者可以使用document.body.onhashchange监听
// 使用链接修改hash值
<a href='#/home'>首页</a>
// 或者直接修改hash
location.hash = '#/home'
使用 hash 来实现前端路由会使得 URL 始终带着一个#
,略显得不美观,并且也就不能使用锚点链接了
。HTML5 中对history
增加了pushState
这个新特性,可以直接修改 URL 但不会引起浏览器刷新,并且可以通过window.onpopstate
对此进行监听,例如:
// 监听历史记录条目变化
window.onpopstate = function (e) {
var currentState = history.state;
// 根据当前state展示对应视图
}
// 新增历史条目,会修改URL值
var state = { title: 'Home', url: '/home' }
history.pushState(state, 'Home', '/home');
在使用Vue.use(Router)
后,vue-router
给每个组件实例都注入了$router
和$route
两个对象,在实例中可以通过this
来访问。其中$router
就可以进行页面跳转(也就是<router-view>
的组件切换)
路由传参有两种方式
params
,命名路由的动态参数,例如:
`const router = new Router({
routes: [
{
path: '/products/:productId',
name: 'product',
component: Product
}
]
})
// 在<router-link>和$router中均可使用params: { productId: 1 }
// 使路径导航到/products/1
<router-link :to="{ name: 'product', params: { productId: 1 } }"></router-link>
$router.push({ name: 'product', params: { productId: 1 } })`
复制
query
,无需提前声明,即 URL 中?
后参数,例如:
`<router-link :to="{ name: 'home', query: { search: 'jdxk' } }"
$router.push({ name: 'home', query: { search: 'jdxk' } })
// 路径即为 /home?search=jdxk`
复制
而在组件实例中,我们如果要使用这些参数,可以使用注入的$route
对象:$route.params
和$route.query
即包含了当前路径的参数:
// 例如上述的/products/1?search=jdxk
// $route.params 即为 { productId: 1 }
// $route.query 即为 { search: jdxk }
- 命名视图:同级展示多个视图,若不设置
name
,则默认为default
。
// template
<router-view name="navigator"></router-view>
<router-view name="sidebar"></router-view>
<router-view></router-view>
// routes
const router = new Router({
routes: [
{
path: '/',
components: {
default: Dashboard,
navigator: Navigator,
sidebar: Sidebar
}
}
]
})
导航守卫(Navigation Guards)是指路由变化时,vue-router
提供的钩子函数可以对当前状态进行一定的逻辑判断,进而决定是否继续或取消此次路由,常用于权限校验这类业务场景。
全局守卫
顾名思义,在所有 route 变化时均生效,注册方式如下:
const router = new Router({ ... })
// 前置钩子,在路由触发变化时调用,此时并没有进入到下一个路由
router.beforeEeach((to, from, next) => { ... })
// 后置钩子,在路由变化完成后出发
router.afterEach((to, from) => { ... })
// 2.5.0 新增
// 同beforeEach类似,区别是调用时机在导航被确认前,同时在所有组件内守卫和异步路由组件被解析之后。
rotuer.beforeResolve((to, from, next) => { ... })
路由守卫
仅在某个特定路由下生效:
...
routes: [
{
path: '/'
component: 'Home',
beforeEnter (to, from, next) => {
// 作用与全局beforeEnter类似
}
}
]
...
组件内守卫
在组件内定义钩子函数:
`...
data () { return { ... } }
beforeRouteEnter (to, from, next) {}
beforeRouteUpdate (to, from, next) {} // v2.2 新增
beforeRouteLeave (to, from, next) {}
...`
复制
- beforeRouteEnter:当前组件对应路由被确认前调用,此时不能访问 this。
- beforeRouteUpdate:当前组件对应路由改变,但目标路由仍是该组件时调用。如上例中的
/products/:productId
,从/products/1
跳转到/products/2
。 - beforeRouteLeave:导航离开改组件对应路由时调用。
vue-router
v2.2.0 后,添加了 addRoutes 方法,允许动态加载路由规则,这样我们就可以根据当前用户的权限返回可使用的路由即可:
// login.vue
...
methods: {
login () {
const { username, password, $router } = this;
userSrv
.login(username, password)
.then(rep => {
$router.addRoutes(rep.routes)
})
}
}
组件与路由解耦
在对应路由的组件中,我们往往会用到$route
的params
和query
,这样这个组件其实就和当前 URL 以及$route
耦合了起来,你无法在其他地方复用这个组件。对此,vue-router
提供了props
属性,使得组件能与路由解耦。
...
routes: [
// props设置为true后,$route.params会被加入到组件的props中
// 当然,你需要在组件的props中显示声明params的属性
{ path: '/products/:productId', component: Product, props: true },
// 设定固定参数
{ path: '/products/:productId', component: Product, props: { fixed: true } }
// 自定义参数,使用函数返回props
{ path: '/products/:productId', component: Product,
props: (route) => ({ search: route.query.search, productId: route.params.productId })
}
]
...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。