- 修饰符
修饰符是vue功能的拓展,是对vue事件或者系统操作等等进行功能的补充
-
- 事件修饰符
看下面的案例
<div class="outer" @click="outer">
<div class="center" @click="center">
<div class="inner" @click="inner"></div>
</div>
</div>
此时如果点击了inner,其实就相当于点击了center和outer,此时如果都绑定了事件监听,就势必都会被触发
我们现在想阻止这种事件冒泡,通过两种方法
1 stop修饰符(常用)
<div class="outer" @click="outer">
<div class="center" @click="center">
<div class="inner" @click.stop="inner"></div>
</div>
</div>
通过上面的案例,我们了解了.stop修饰符的作用就是阻止事件的一个冒泡
其作用类似原生JavaScript的event.stopPropagation()方法
inner(event) {
event.stopPropagation();
console.log("内层")
}
2 self修饰符
.self修饰符的作用是只有点击元素本身的时候才能触发事件,不接受冒泡上来的事件,同时也不能阻止事件的冒泡
<div class="outer" @click.self="outer">
<div class="center" @click.self="center">
<div class="inner" @click.self="inner"></div>
</div>
</div>
.self修饰实际是不能阻止事件向上冒泡的
<div class="outer" @click="outer">
<div class="center" @click.self="center">
<div class="inner" @click.self="inner"></div>
</div>
</div>
<div class="outer" @click="outer">
<div class="center" @click.self="center">
<div class="inner" @click.self="inner"></div>
</div>
</div>
3 prevent修饰符
如果一个超级链接,既有链接,又有事件,此时点击超级链接后会发生什么?
<a href="http://www.baidu.com" @click.prevent="alertDialog">点击跳转到百度</a>
答案是先会执行事件的内容,然后会执行a默认的跳转事件
此时我们不需要a标签跳转到百度,执行完事件监听后就停止,通过.prevent修饰符阻止
<a href="http://www.baidu.com" @click.prevent="alertDialog">点击跳转到百度</a>
和stop修饰符一样,prevent也有原生的事件方法
event.preventDefault();
4 capture修饰符
capture修饰符是对事件捕获的监听,vue的事件监听,默认都是获取冒泡阶段的,所以使用capture去监听捕获阶段的事件
<div class="outer" @click.capture="outer">
<div class="center" @click.capture="center">
<div class="inner" @click="inner"></div>
</div>
</div>
5 once修饰符
once修饰符的作用就是使事件只触发一次
<button @click.once="add">按我加1</button>
methods:{
add(event) {
this.a++
console.log(this.a)
}
}
- 2按键修饰符
我们之前在JavaScript的课程中接触过onkeydown和onokeyup,这两个是键盘的事件监听,在vue中有对应的事件修饰符
基本使用方法
<input type="text" @keyup.space='add'>
.space是vue封装的对keyCode别名,和下面的代码表示内容相同
<input type="text" @keyup.32='add'>
常用的keyCode别名
keyCode值 | 别名 | 键盘按键 |
13 | .enter | 回车 |
32 | .space | 空格 |
37 | .left | 键盘左键 |
38 | .up | 键盘上键 |
39 | .right | 键盘右键 |
40 | .down | 键盘下键 |
9 | .tab | 键盘tab |
46或者8 | .delete | delete或者backspace |
- 3系统修饰符
系统修饰符指的是通过一些指定的按键配合鼠标点击或者键盘事件进行事件监听
比如.ctrl系统修饰符的使用
<button @click.ctrl='add'>按我加1 </button>
此时按住键盘ctrl键然后鼠标点击才能实现add加1
常用的系统修饰符
修饰符名称 | 对应的键盘键名称 |
.ctrl | ctrl |
.alt | alt |
.shfit | shift |
.meta | 如果是windows系统代表的是键盘的徽标键,如果是IOS系统,单表的是common键 |
此时有一个问题,如果我们设置了系统修饰符,并不希望有其他的按键组合,比如我们设置ctrl修饰符,此时如果按住ctrl和其他键,然后再点击鼠标,此时也会触发事件监听,所以我需要使用其他的修饰符来设置精确匹配事件
- 4exact修饰符
exact修饰符的作用是辅助其他修饰符进行精确匹配的
<button @click.ctrl.exact='add'>按我加1 </button>
方法就是在对应的修饰符的后面进行连续打点
- 5鼠标按键修饰符
鼠标按键修饰符修饰的是鼠标的左、滚轮、右键
如果我们需要点击某个事件,但是我们可以需要使用鼠标右键
<button @click.right='add'>按我加1 </button>
此时我们发现右键点击按钮的时候确实可以实现点击,但是也会出现右键的默认弹窗
此时我们可以结合prevent修饰符实现右键的事件监听
<button @click.right.prevent='add'>按我加1 </button>
需要注意的是,prevent修饰符必须在.right修饰符的后面,否则会失效
还有鼠标滚轮键按下
<button @click.middle='add'>按我加1 </button>
1.6表单修饰符
表单修饰符一共有三个,分别是.lazy,.number,.trim
表单修饰符都是用来修饰v-model属性的
.lazy修饰符是将Input输入框的数据绑定v-model从实时监听的状态变为change状态
<div id="app">
<input type="text" v-model.lazy='a'>
<p>{{a}}</p>
</div>
当鼠标的光标失去焦点之后
.number修饰符的作用是将内容转化为数字类型
<input type="text" v-model.number='a' @input='add'>
需要注意的是.number修饰符会将非数字内容进行过滤,得到的是纯数字类型,是可以进行计算的非NaN数字
如果没有使用number修饰符,得到的输入框的数字都是string类型
.trim修饰符的作用是过滤用户输入的收尾空格
比如我们没有设置trim之前
此时我们给v-model修饰trim
<input type="text" v-model.trim='a' @input='add'>
得到的是纯没有收尾空白的内容
- vue-cli
vue-cli是vue的脚手架工具,帮我们快速生成了vue的起步项目,内置一些必备的比如打包工具,比如配置文件等等
地址:Vue CLI
如何安装cli?
第一步
npm install -g @vue/cli
如何查看是否安装成功
vue --version
我们使用vue create命令创建自己的项目
vue create my-project
安装成功后会有成功的提示语
在你的安装目录中有相应的文件夹
为了方便后期的开发,如果使用vsCode 的同学,老师推荐安装一些常用vue插件
Vue 2 Snippets、Vue Theme、Vue VS Code Extension Pack、Vue VSCode Snippets、Vuter
我们使用的是vsCode的终端面板
此时在浏览器中打开
2.1 cli目录结构
- node_modules项目的依赖中心
- public:静态资源文件夹,和assets不同的是public不会被webpack进行打包,使用路径的时候要使用绝对路径
- src:项目的核心区域,所有的开发文件的核心内容区域,包括组件,静态资源等等
- asstet:静态资源文件夹,和public不同的是assets文件夹会被webpack打包,所以要使用相对路径
- components:Vue功能组件的存放位置,内部存放的是公用的组件
- views:公共文件,主要以各个独立页面为主
- APP.vue:整个vue的根组件,所有的vue组件都是从这个组件中拓展的,App根组件最后通过编译后将内容渲染到.html文件中
- main.js:vue的入口文件,vue文件初始化位置
- .gitignore:GitHub相关配置文件,作用是git再提交代码的时候指定相关忽略格式文件
- babel.config.js:babel的配置文件
- package.json:配置(记录了)项目的相关模块,项目相关配置信息等等
- package-lock.json:作用是记录的当前项目安装的相关依赖版本,并且记住了当前所有依赖的关联关系,如果下次vue进行编译的时候会按照这个文件进行编译
- README.md:文件的作用是项目相关的使用方法,和使用说明
2.2 cli文件的编译过程
我们知道vue-cli的起步命令是npm run serve,那么这个命令干了一些什么,为什么可以执行相关的编译程序?
首先我们要知道npm run 执行一个命令或者是一个文件内容
我们看package.json文件中
{
"name": "my-project",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint",
},
"dependencies": {
"core-js": "^3.6.5",
"vue": "^2.6.11"
},
"devDependencies": {
....
},
"eslintConfig": {
...
},
"browserslist": [
...
]
}
我们知道scripts是npm执行目录,也就是在scripts这个对象中的key就是npm run的命令值
vue-cli提供了三个命令
- serve:这个是我们开发使用的命令,执行编译和热更新(ctrl+s的时候浏览器会实时更新)
- build:工作中如果我们本地调试完后需要部署代码前,进行打包的命令
- lint:命令的作用是检验文件代码的合格性(对eslint的校验)
我们以npm run serve为例
第一步命令会找到node_modules文件夹中.bin文件夹内部的vue-cli-service文件
然后我们根据路径查找到了相关文件
核心代码路径
severice.js文件是整个编译过程的核心文件
2.3 vue-cli的起步文件
1 main.js文件
import Vue from 'vue'
// 相对路径引入的App.vue文件
import App from './App.vue'
// 这行命令的作用是给生产环境配置的提示消息,如果为true或者默认不配置,会有相关提示语
Vue.config.productionTip = false
new Vue({
// 渲染节点
render: h => h(App),
// 挂载函数,内部#app是vue的根节点
}).$mount('#app')
2 public>index.html根文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
noscript标签的作用是当script标签加载不出来或者抛出错误的时候替换内容,目的是在浏览器中提供友好提示
<div id="app"></div>是整个vue项目的根标签,vue需要挂载的标签
3 App.vue文件
App.vue文件是整个项目的根组件,项目中所有的页面切换页面显示都是在这个组件基础上渲染的
我们将整个App.vue文件清空后发现
<template>
<div id="app">
</div>
</template>
<script>
export default {
components: {
}
}
</script>
<style>
上面的结构是.vue文件的基础结构
- <template>元素的作用是搭建vue文件的结构
- <script>元素的作用是对当前文件逻辑进行交互
- <style>元素的作用是对当前文件的样式进行修饰
我们之前开发中.js文件只有逻辑.css文件只有样式.html文件只有结构,但是.vue的组建就是将视图和逻辑进行了一个整合
- 组件
所有的vue组件都是以.vue格式结尾的文件
我们看到的vue-cli中的App.vue文件就是整个vue的根组件
<template>
<div>
</div>
</template>
<script>
export default {
data(){
}
}
</script>
<style>
一个vue文件就是一个类
需要注意的地方是data必须是一个函数
最外层必须有默认暴露
<script>
export default {
data(){
return {
}
}
}
</script>
我们后面写的所
有的vue的对象清单都必须在export default里面去罗列
在我们之前写data时候是一个对象
var app = new Vue{
data: {
name:'小明'
}
}
我们发现data是一个对象
如果在我们组件中data必须是一个函数,是因为防止数据之间冲突
<script>
export default {
data(){
return {
a: '小明'
}
}
}
</script>
data是一个函数,返回的是一个对象,目的是为了让每个组件数据隔离,这个是JavaScript的原理,并不是vue去设计的
为什么vue的data必须是一个函数
我们从JavaScript的面向对象开始
此时浏览器会输出两个小红
我们知道复杂数据类型指的都是同一个地址,所以,当xiaohong.data.name修改的时候,xiaoming.data.name也就随之修改了
<script>
var config = {
data:{
name: '小明'
}
}
var People = function(config) {
this.data = config.data
}
var xiaoming = new People(config)
var xiaohong = new People(config)
xiaohong.data.name = '小红'
console.log(xiaoming.data.name)
console.log(xiaohong.data.name)
</script>
解决办法就是config对象返回一个data函数,这个函数return一个对象
var config = {
data(){
return {
name: '小明'
}
}
}
var People = function(config) {
this.data = config.data()
}
此时People的data返回的是一个对象,不是一个地址,每一个实例拿到的都是个独立,个体的对象,互相不会数据冲突,这个就是vue组件data为什么是函数的原因
如何使用自定义组件?
第一步创建自己的组件
比如在components文件夹中创建一个Mashibing.vue文件
App.vue文件
<template>
<div>
<Mashibing></Mashibing>
</div>
</template>
<script>
// 引入组件
import Mashibing from './components/Mashibing.vue'
export default {
// 注册组件
components:{
Mashibing
},
data(){
}
}
</script>
总结使用组件三步走
- 创建vue文件
- 在要渲染的地方引入该组件
- 注册该组件
<Mashibing></Mashibing>组件内容
<template>
<div>
<h1>马士兵教育{{a}}</h1>
<button @click="add">按我加1</button>
</div>
</template>
<script>
export default {
data() {
return {
a: 100
}
},
methods:{
add() {
this.a ++
}
}
}
</script>
<style>
</style>
组件是可以复用的,并且数据是隔离的
在给App.vue多添加几个Mashibing.vue文件
<template>
<div>
<Mashibing></Mashibing>
<Mashibing></Mashibing>
<Mashibing></Mashibing>
</div>
</template>
- 父子组件传值
4.1 父组件传值给子组件
我们使用组件一定是依托在某一个父组件的身上,比如我们上面写的Mashibing.vue文件就是依托在App.vue文件中,此时我们如果要使用App.vue文件中的值怎么办
vue提供了一个props的入口,也是父子组件之间唯一的传值方式,父组件通过v-bind自定义属性传入值,子组件通过props接受对应的参数
父组件
<template>
<div>
<Mashibing :msb-value="a"></Mashibing>
</div>
</template>
公共v-bind自定义属性传值,注意由于vue的属性对大小写不敏感,所以如果需要写驼峰命名,需要使用-隔开
比如msb-value代表的就是msbValue
data(){
return {
a: 100
}
}
子组件
<script>
export default {
// 罗列父组件传进的属性值
props:['msbValue'],
data() {
return {
a: 100
}
},
methods:{
add() {
this.a ++
}
}
}
</script>
<template>
<div>
<h1>马士兵教育,我接收到的父组件的值是:{{msbValue}}</h1>
</div>
</template>
子组件通过props罗列父组件的传值,接收的参数可以有多个,props可是数组,也可以是对象
父组件
<div>
<Mashibing :msb-value="a" :value-b="b" :value-c="c"></Mashibing>
</div>
子组件接受,数组方式
<template>
<div>
<h1>马士兵教育,我接收到的父组件的值是:{{msbValue}} {{valueB}}
{{valueC}}</h1>
</div>
</template>
<script>
export default {
// 罗列父组件传进的属性值
props:['msbValue','valueB','valueC'],
data() {
return {
a: 100
}
},
methods:{
add() {
this.a ++
}
}
}
</script>
可以使用对象去接收参数
props:{
msbValue: Number,
valueB: Number,
valueC: Number
}
如果props是一个对象,那么这个对象的key是自定义传入参数属性,value是希望传入的参数类型
比如我们限制的是数字类型
但是我们传入的是一个字符串,此时,控制台后报错,提示你希望传入number类型
如果props是对象的话,参数可以配置
比如配置参数必填项
props:{
msbValue: {
type: Number,
required: true
},
valueB: Number,
valueC: Number
},
如果props的值也是一个对象,type代表的是类型,required是一个布尔值,如果为ture代表的是设置必填项,如果不填,则会抛出错误
比如default代表的是默认的不传入的值的参数
msbValue: {
type: Number,
default: 500
}
如果props的default值是Object或者Array,需要使用函数return
msbValue: {
type: Object,
default: ()=>{
return {a:100}
}
}
validator是数据的校验
msbValue: {
type: Number,
// 数据的校验
validator: function(value) {
return value > 101
}
}
4.2 子组件修改父组件的参数
需要注意的是子组件不可以直接修改父组件的值,只能传出一个自定义事件,父组件通过调用这个自定义事件后,然后在外部修改值
子组件代码
<template>
<div>
<h1>马士兵教育,我接收到的父组件的值是:{{msbValue}} {{valueB}}
{{valueC}}</h1>
<button @click="add">按我加1</button>
</div>
</template>
<script>
export default {
// 罗列父组件传进的属性值
props:{
msbValue: Number,
valueB: Number,
valueC: Number
},
data() {
return {
}
},
methods:{
add() {
this.$emit("add")
}
}
}
</script>
<style>
</style>
$emit方法是vue封装的,用来想父组件返回对应的自定义事件,父组件通过在子组件身上设置对应的自定义事件后设置事件名称
父组件代码
<template>
<div>
<Mashibing @add="sumNum" :msb-value='a' :value-b="b" :value-c="c"></Mashibing>
</div>
</template>
<script>
// 引入组件
import Mashibing from './components/Mashibing.vue'
export default {
// 注册组件
components:{
Mashibing
},
data(){
return {
a: 100,
b: 200,
c: 300
}
},
methods: {
sumNum(){
this.a++
}
}
}
</script>
<style>
</style>
@add自定义事件是子组件通过$emit传出来的自定义事件,父组件通过这个事件设置事件方法sunNum,然后修改自己的值,从而时间子组件修改父组件的值
这么做的目的就是为了让数据可观察性更强,可维护性更高,有一个更清晰的清单