1. 指令语法的简写、完整写法和注意事项
因为项目中大多用简写,所以完整写法一般很少见到,
尤其是双向绑定,见到完整写法后才知道实际绑定到value属性上面。
1.1. 单向绑定(v-bind)
完整写法:v-bind:href="xxx"
简写::href="xxx"
1.2. 双向绑定(v-model)
完整写法:v-model:value="xxx"
简写:v-model="xxx"
1.3. 监听事件(v-on)
完整写法:v-on:click="xxx"
简写:@click="xxx"
注意事项:
- v-bind 和 v-on 是可以动态指定属性名或事件名的,但实际很少用到,而且动态的属性名和事件名是有要求的,具体见官方文档:https://cn.vuejs.org/guide/essentials/template-syntax.html#directives
<a :[attributeName]="url"> ... </a>
<a @[eventName]="doSomething">
v-model 只能绑定表单元素或组件
v-model 会忽略任何表单元素上初始的 value、checked 或 selected attribute。它将始终将当前绑定的 JavaScript 状态视为数据的正确来源。你应该在 JavaScript 中使用data 选项来声明该初始值。v-on 绑定方法时,如果既想要event事件参数,还想手动传一些参数,可以使用如下写法
<!-- 使用特殊的 $event 变量 -->
<button @click="warn('Form cannot be submitted yet.', $event)">
Submit
</button>
<!-- 使用内联箭头函数 -->
<button @click="(event) => warn('Form cannot be submitted yet.', event)">
Submit
</button>
2. 双向绑定修饰符
可以再v-model后加修饰符,简化开发
2.1. 修饰符 .lazy
在输入框上绑定v-model时,默认是在每次input事件后更新数据,加.lazy后会变成每次change事件后更新数据
<input v-model.lazy="msg" />
2.2. 修饰符 .number
如果你想让用户输入自动转换为数字,你可以在 v-model 后添加 .number 修饰符来管理输入
<input v-model.number="age" />
2.3. 修饰符 .trim
如果你想要默认自动去除用户输入内容中两端的空格,你可以在 v-model 后添加 .trim 修饰符
<input v-model.trim="msg" />
3. 监听事件修饰符
可以再v-on后加修饰符,简化开发
3.1. 修饰符 .stop
阻止事件冒泡
<!-- 单击事件将停止传递 -->
<a @click.stop="doThis"></a>
3.2. 修饰符 .prevent
阻止浏览器的默认事件
<!-- 提交事件将不再重新加载页面 -->
<form @submit.prevent="onSubmit"></form>
3.3. 修饰符 .self
只有触发事件元素为绑定事件元素本身时才有效,不处理冒泡的事件
<!-- 仅当 event.target 是元素本身时才会触发事件处理器 -->
<!-- 例如:事件处理器不来自子元素 -->
<div @click.self="doThat">...</div>
3.4. 修饰符 .native
.native事件修饰符是用来在父组件中给子组件[自定义组件]绑定一个原生的事件,就将子组件变成了普通的HTML标签看待。
<!-- 给Demo组件的根节点绑定click事件 -->
<Demo @click.native="doThat">...</Demo>
3.5. 修饰符 .capture
事件的阶段顺序是:外层捕获、内层捕获、内层冒泡、外层冒泡
v-on默认是冒泡阶段处理事件,加 .capture 后改为捕获阶段处理事件
<!-- 添加事件监听器时,使用 `capture` 捕获模式 -->
<!-- 例如:指向内部元素的事件,在被内部元素处理前,先被外部处理 -->
<div @click.capture="doThis">...</div>
3.6. 修饰符 .once
只能触发一次事件
<!-- 点击事件最多被触发一次 -->
<a @click.once="doThis"></a>
3.7. 修饰符 .passive
【浏览器只有等内核线程执行到事件监听器对应的JavaScript代码时,才能知道内部是否会调用preventDefault函数来阻止事件的默认行为,所以浏览器本身是没有办法对这种场景进行优化的。这种场景下,用户的手势事件无法快速产生,会导致页面无法快速执行滑动逻辑,从而让用户感觉到页面卡顿。】
通俗点说就是每次事件产生,浏览器都会去查询一下是否有preventDefault阻止该次事件的默认动作。我们加上passive就是为了告诉浏览器,不用查询了,我们没用preventDefault阻止默认动作。
这里一般用在滚动监听,@scoll,@touchmove 。因为滚动监听过程中,移动每个像素都会产生一次事件,每次都使用内核线程查询prevent会使滑动卡顿。我们通过passive将内核线程查询跳过,可以大大提升滑动的流畅度。
<!-- 滚动事件的默认行为 (scrolling) 将立即发生而非等待 `onScroll` 完成 -->
<!-- 以防其中包含 `event.preventDefault()` -->
<div @scroll.passive="onScroll">...</div>
注意事项:
- 修饰语可以使用链式书写,也可以只有修饰符
<!-- 修饰语可以使用链式书写 -->
<a @click.stop.prevent="doThat"></a>
<!-- 也可以只有修饰符 -->
<form @submit.prevent></form>
使用修饰符时需要注意调用顺序,因为相关代码是以相同的顺序生成的。
因此使用 @click.prevent.self 会阻止元素及其子元素的所有点击事件的默认行为。
而 @click.self.prevent 则只会阻止对元素本身的点击事件的默认行为。请勿同时使用 .passive 和 .prevent,因为 .passive 已经向浏览器表明了你不想阻止事件的默认行为。
如果你这么做了,则 .prevent 会被忽略,并且浏览器会抛出警告。
4. 绑定class和style
4.1. 对象方式绑定class
<!-- 对象的key为class名,value为布尔值,为true时绑定class,为false时不绑定class -->
<div :class="{ active: isActive, 'text-danger': hasError }"></div>
4.2. 数组方式绑定class
<!-- 固定的class -->
<div :class="['abc-class', 'def-class']"></div>
<!-- 动态的class -->
<div :class="[activeClass, errorClass]"></div>
<!-- 数组中有条件地渲染某个 class,你可以使用三元表达式 -->
<div :class="[isActive ? activeClass : '', errorClass]"></div>
<!-- 也可以在数组中嵌套对象 -->
<div :class="[{ active: isActive }, errorClass]"></div>
<!-- 自定义组件上也可以绑定class,class会绑定到该组件的根元素上,与该元素上已有的class合并 -->
<MyComponent :class="{ active: isActive }" />
4.3. 对象方式绑定style
<!-- 按照json格式写样式,例如font-size这种样式则属性名写为fontSize -->
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
<!-- 样式多值时写法 -->
<div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
4.4. 数组方式绑定style
<!-- 数组内是对象,对象写法见对象方式绑定style -->
<div :style="[baseStyles, overridingStyles]"></div>
注意事项:
- vue会合并我们写的class和style,所以可以同时写普通标签和单向绑定
<div
class="static"
:class="{ active: isActive, 'text-danger': hasError }"
></div>
- 组件内可以通过 $attrs.class 获取当前组件所绑定的class,例:
<!-- Test组件模板内容 -->
<template>
<Hello class="hello-class"></Hello>
</template>
<!-- Hello组件模板内容 -->
<template>
<div>
<div :class="$attrs.class">测试</div>
</div>
</template>
5. 过滤器
5.1. 在组件的选项中定义本地的过滤器
export default {
name: 'Test',
data: {
return {
mesg: '你好'
}
},
filters: {
// 把首字母转为大写的过滤器
capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
}
}
5.2. 定义全局过滤器
// 全局过滤器 - 独立于每个vm实例之外,用Vue.filter()方法定义
// Vue.filter()有两个参数,第一个参数是过滤器的名字,第二个参数是过滤器的处理函数
Vue.filter('capitalize', (str) => {
return str.chatAt(0).toUpperCase() + str.slice(1)
})
5.3. 使用过滤器
<!-- 在{{}}中通过“管道符”调用 capitalize 过滤器,对 message 的值进行格式化-->
<p>{{message | capitalize}}</p>
<!-- 在v-bind中通过管道符 调用 formatId 过滤器,对 rawId 的值进行格式化-->
<div v-bind:id="rawId | farmatId"></div>
注意事项:
- 过滤器传参
<!-- arg1 和 arg2 是传递给 filterA 的参数-->
<p>{{ message | filterA(arg1,arg2) }}</p>
// 过滤器处理函数中的形参列表中:
// 第一个参数:永远都是“管道符”前面待处理的值
// 第二个参数开始: 才是调用过滤器时传递过来的 arg1 和 arg2 参数
Vue.filter('filterA', (meg, arg1, arg2) => {
// 过滤器的处理逻辑
})
- 过滤器可链式调用,执行顺序为从左到右
<div v-bind:id="rawId | farmatId | capitalize"></div>
6. 自定义指令
6.1. 注册一个全局自定义指令
// 注册一个全局自定义指令 v-focus
Vue.directive('focus', {
// 当绑定元素插入到 DOM 中。
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
6.2 指令钩子
const myDirective = {
// 在绑定元素的 attribute 前
// 或事件监听器应用前调用
created(el, binding, vnode, prevVnode) {},
// 在元素被插入到 DOM 前调用
beforeMount(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都挂载完成后调用
mounted(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件更新前调用
beforeUpdate(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都更新后调用
updated(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载前调用
beforeUnmount(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载后调用
unmounted(el, binding, vnode, prevVnode) {}
}
Vue.directive('directive', myDirective)
7. ref获取组件对象(VueComponent)或DOM节点
一般我们用ref是获取组件对象,但ref还可以获取DOM节点,这样就不需要使用“document.getElementById(id)”获取DOM节点了
<!-- ref 用于给节点打标识 -->
<a ref="testA" />
// 获取组件对象(VueComponent)或DOM节点
this.$refs.testA
this.$refs.['testA']
8. Mixin混入
混入 (mixins)定义了一部分可复用的方法或者计算属性。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。
例:创建一个MyMixin.js
export const myMixin = {
data(){
return {
mesg: '我的混入',
}
},
created() {
console.log('mixin 对象的钩子被调用')
}
}
使用MyMixin.js
import { MyMixin } from '@/mixins/MyMixin'
export default {
name: 'Hello',
mixins: [MyMixin],
data () {
return {
}
},
created() {
console.log('组件钩子被调用')
}
}
注意事项:
- vue会将混入的js和vue组件的配置项做合并,当属性名或方法名(不包含生命周期钩子函数)冲突时,以vue组件定义的为准。
生命周期钩子函数是全都生效的,例如混入的js和vue组件都写了created方法,会先执行混入js的created方法,再执行vue组件的created方法。
9. 插件
插件 (Plugins) 是一种能为 Vue 添加全局功能的工具代码。
插件一般是封装了很多对Vue的配置,例如全局过滤器、全局自定义指令、通用变量和方法等...
例:创建一个Plugins.js
export default {
// 使用插件后,Vue会调用此方法
install(Vue, x, y, z) {
console.log(x, y, z)
// 全局过滤器
Vue.filter('mySlice', function (value) {
return value.slice(0, 4)
})
// 定义全局指令
Vue.directive('fbind', {
// 指令与元素成功绑定时(一上来)
bind(element, binding) {
element.value = binding.value
},
// 指令所在元素被插入页面时
inserted(element, binding) {
element.focus()
},
// 指令所在的模板被重新解析时
update(element, binding) {
element.value = binding.value
}
})
// 定义混入
Vue.mixin({
data() {
return {
x: 100,
y: 200
}
},
})
// 给Vue原型上添加一个方法(vm和vc就都能用了)
Vue.prototype.hello = () => { alert('你好啊') }
}
}
使用Plugins.js
// 引入Vue
import Vue from 'vue'
// 引入插件
import Plugins from './Plugins'
// 应用(使用)插件
Vue.use(Plugins, 1, 2, 3)
10. 全局事件总线
全局事件总线就是将VM或VueComponent绑定到所有组件都能看见的位置,组件根据需要去绑定监听事件或触发监听事件。
准备工作:在main.js中绑定全局变量$bus
// 引入Vue
import Vue from 'vue'
// 引入App
import App from './App.vue'
// 关闭Vue的生产提示
Vue.config.productionTip = false
// 创建vm
new Vue({
el: '#app',
render: h => h(App),
beforeCreate() {
// 安装全局事件总线,$bus就是当前应用的vm
Vue.prototype.$bus = this
},
})
使用$bus
mounted() {
// 绑定监听事件(hello)
this.$bus.$on('hello', (data) => {
console.log('收到了数据', data)
})
},
beforeDestroy() {
// 组件销毁前要解绑监听事件
this.$bus.$off('hello')
},
methods: {
test() {
// 触发监听事件(hello)
this.$bus.$emit('hello', 123)
}
},
11. 过度与动画
Vue提供了两个标签</transition>和</transition-group>来辅助实现过度与动画效果,
标签只是实现了进场和离场动画时样式class的动态绑定,具体动画效果是需要我们自己实现的。
transition的属性:
- name:默认进场动画class为“v-enter-active”,离场动画class为“v-leave-active”。
绑定name后会根据name决定class名称,例name="hello",
则进场动画class为“hello-enter-active”,离场动画class为“hello-leave-active”。 - appear:初始时,是否播放进场动画
- enter-active-class:指定进场动画的class,可以配合第三方样式库使用
- leave-active-class:指定离场动画的class,可以配合第三方样式库使用
11.1 动画效果
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏</button>
<!-- transition 标签内只能有一个根元素,在显示/隐藏时会给这个根元素绑定对应的class -->
<transition name="hello" appear>
<h1 v-show="isShow">你好啊!</h1>
</transition>
</div>
</template>
<script>
export default {
name: 'Test',
data() {
return {
isShow: true
}
},
}
</script>
<style scoped>
h1 {
background-color: orange;
}
/* 进场动画class样式,指定播放哪个动画、播放几秒、是否匀速播放 */
.hello-enter-active {
animation: atguigu 0.5s linear;
}
/* 退场动画class样式,指定播放哪个动画、播放几秒、是否匀速播放、设置倒放 */
.hello-leave-active {
animation: atguigu 0.5s linear reverse;
}
@keyframes atguigu {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0px);
}
}
</style>
11.2 过度效果
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏</button>
<transition-group name="hello" appear>
<h1 v-show="!isShow" key="1">你好啊!</h1>
<h1 v-show="isShow" key="2">拜拜咯!</h1>
</transition-group>
</div>
</template>
<script>
export default {
name:'Test',
data() {
return {
isShow:true
}
},
}
</script>
<style scoped>
h1 {
background-color: orange;
}
/* 指定播放动画的时间和匀速播放 */
.hello-enter-active, .hello-leave-active{
transition: 0.5s linear;
}
/* 进入的起点、离开的终点 */
.hello-enter, .hello-leave-to{
transform: translateX(-100%);
}
/* 进入的终点、离开的起点 */
.hello-enter-to, .hello-leave{
transform: translateX(0);
}
</style>
11.3 使用 animate.css 第三方动画库实现
animate.css官网:https://animate.style/
需要先安装动画库,命令:npm install animate.css
然后在组件中引入css就可以使用class了,命令:import 'animate.css'
例:
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏</button>
<transition-group
appear
name="animate__animated animate__bounce"
enter-active-class="animate__swing"
leave-active-class="animate__backOutUp"
>
<h1 v-show="!isShow" key="1">你好啊!</h1>
<h1 v-show="isShow" key="2">拜拜咯!</h1>
</transition-group>
</div>
</template>
<script>
import 'animate.css'
export default {
name:'Test',
data() {
return {
isShow:true
}
},
}
</script>
<style scoped>
h1 {
background-color: orange;
}
</style>
12. proxy代理
proxy代理是解决开发环境中的跨域问题,正式环境的跨域需要使用nginx反向代理或者是后端解决。
在vue中使用proxy进行跨域的原理是:将域名发送给本地的服务器(启动vue项目的服务,比如loclahost:8080),再由本地的服务器去请求真正的服务器。
跨域是指请求的协议、IP、端口与浏览器当前地址栏的协议、IP、端口不一致,
正常情况下浏览器会将此请求的返回拦截,并在控制台报出跨域的错误。
如果请求返回中加了一些允许跨域的请求头,浏览器就不会拦截。前台在报跨域错误的时候,实际上请求已经成功访问到后台,只是浏览器将请求的返回拦截了。
12.1 vue.config.js配置proxy代理
module.exports = {
pages: {
index: {
// 入口
entry: 'src/main.js',
},
},
lintOnSave:false, //关闭语法检查
// 开启代理服务器(方式一)
/* devServer: {
proxy: 'http://localhost:5000'
}, */
// 开启代理服务器(方式二)
devServer: {
proxy: {
// '/demo1' 为请求路径前缀,可自定义,比如 '/api'、'/test'
// 例:http://localhost:8080/demo1/test,请求路径前缀匹配成功则走此代理配置
'/demo1': {
// target 为实际接收请求的服务器地址,请求路径会动态从请求中截取
// 例:http://localhost:8080/demo1/test,实际访问地址为:http://localhost:5000/demo1/test
target: 'http://localhost:5000',
// pathRewrite 为路径重写,将路径中匹配正则表达式的字符串替换,json中的key为正则表达式,value为替换的字符串
// 例:http://localhost:5000/demo1/test,替换为:http://localhost:5000/test
pathRewrite: { '^/demo1': '' },
// ws 用于支持websocket,默认true
ws: true,
// 用于控制请求头中的host值,默认true(通常都配置true)
// true:请求头中的host为接收请求服务器的地址,false:请求头中的host为代理服务器的地址
changeOrigin: true
},
'/demo2': {
target: 'http://localhost:5001',
pathRewrite: { '^/demo2': '' },
}
}
}
}
13. 插槽
13.1 默认插槽
<!-- Category组件内容 -->
<template>
<div>
<h3>分类</h3>
<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
<slot>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
</div>
</template>
<!-- 使用Category组件,在组件内写插槽内容(Category组件开始和结束标签之间的所有内容都是插槽内容) -->
<Category>
<ul>
<li v-for="index in 10" :key="index">{{index}}</li>
</ul>
</Category>
13.2 具名插槽
<!-- Category组件内容 -->
<template>
<div>
<h3>分类</h3>
<!-- 定义一个插槽(给slot标签加name属性,name就是插槽名字) -->
<slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot>
<slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现2</slot>
</div>
</template>
<!-- 使用Category组件,在组件内写插槽内容(slot属性的值对应插槽名字,可以将多个元素定义同一个插槽名字) -->
<Category>
<ul slot="center">
<li v-for="index in 10" :key="index">{{index}}</li>
</ul>
<a slot="footer" href="http://www.baidu.com">单机游戏</a>
<a slot="footer" href="http://www.baidu.com">网络游戏</a>
<!--
设置插槽名字方法
1.在标签上加slot属性,例:<a slot="footer"></a>
2.在 template 标签上加 v-slot:,例:<template v-slot:footer></template>
-->
<template v-slot:footer>
<h4>欢迎体验</h4>
</template>
</Category>
13.3 插槽传数据
<!-- Category组件内容 -->
<template>
<div>
<h3>分类</h3>
<!-- slot标签上的属性就是给插槽使用者传的数据,games是Category组件上data中的变量 -->
<slot name="center" :games="games" msg="hello">我是默认的一些内容</slot>
</div>
</template>
<!-- 使用Category组件,要接收数据插槽必须定义为templete组件,使用scope属性接收(此处接收参数时使用了ES6的解构赋值) -->
<Category>
<template slot="center" scope="{games, msg}">
<ol>
<li style="color:red" v-for="(g,index) in games" :key="index">{{g}}</li>
</ol>
<h3>{{msg}}</h3>
</template>
</Category>
<!-- 也可使用slot-scope属性接收数据,效果一样(此处接收参数时使用了ES6的解构赋值) -->
<Category>
<template slot="center" slot-scope="{games, msg}">
<ol>
<li style="color:red" v-for="(g,index) in games" :key="index">{{g}}</li>
</ol>
<h3>{{msg}}</h3>
</template>
</Category>