Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,Vue 也完全能够为复杂的单页应用提供驱动。
一、Vue 基础使用
vue 的使用
下载 Vue:npm i vue
- 首先下载 vue,引入 vue.js
- js 中创建一个 Vue 对象实例
- 通过 el 指定 vue 管理页面的边界
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <!-- 引入vue.js文件 --> <script src="lib/vue.js"></script> <div id="app"> <input v-model="message" type="text"><br> <h1>{{message}}</h1> </div>
<!-- 下载Vue:npm i vue --> <script> var vm = new Vue({ /** * el是element的简写,用来指定vue管理页面的边界, * 也就是说只有包裹在 #app内部的元素,才会收到Vue的管理!!! */ el: '#app',
/** * 页面中用的到数据都放到data对象中 */ data: { message: '邓紫棋喜欢你' }
// 写Vue可能会遇到的错误: // 1 注意:Vue 是以大写字母开头的,它是一个构造函数!!! // 2 注意:在 Vue 中,HTML属性值无法使用 `{{}}`!!! // 3 开发期间一定要使用未压缩版的Vue(开发版) })
/** * vm是Vue的对象实例,可以通过vm.$data.属性名获取data中的属性值,可以省略$data */ console.log(vm.message) console.log(vm.$data.message) console.log(vm.message === vm.$data.message)
|
Mustache 表达式使用
双花括号{{}}
就是 mustache 语法,用于展示 data 中的内容,mustache 中可以出现任意的 JS 表达式;
- 表达式
{{}}
只能从数据对象 data 中获取数据;
- mustache 中不能出现语句,比如:if () {} / for(var i =0 …) {} / var num = 1;
- Mustache 语法不能作用在 HTML 元素的属性上;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <div id="app"> <h1>{{ msg }}</h1> <p>{{ 1 + 2 }}</p> <p>{{ ['a', 'c', 'b'] }}</p> <p>{{ ['a', 'c', 'b'].join('-') }}</p> <p>{{ msg + ' -- 拼接内容' }}</p> <p>{{ age > 18 ? '成年了' : '未成年' }}</p> <p>{{ Math.random() }}</p> </div> <script> var vm = new Vue({ el:'#app', data:{ msg:'邓紫棋金鱼嘴', age:19 } }) </script>
|
指令 v-使用
指令 (Directives) 是带有 v- 前缀的特殊属性,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
1.v-text
用来设置当前元素的文本内容,相当于 DOM 对象的 innerText 或 textContent
1
| <div v-text='msg'></div>
|
2.v-html
更新 DOM 对象的 innerHTML
1
| <div v-html='htmlMsg'></div>
|
3.v-bind
通过 v-bind 为 HTML 元素绑定属性,使用 data 中提供的数据;
因为 v-bind:title 这种使用方式很繁琐,所以,vue 提供了一个简化语法 :title
1 2
| <img v-bind:title='msg' v-bind:src='imgPath' v-bind:name='name'> <img :title='msg' :src='imgPath' :name='name'>
|
4.v-on
绑定事件,支持 js 所有的事件类型, v-on 绑定的事件方法都要写在 Vue 实例中的 methods 对象中;
v-on:省略写 @
1 2
| <button v-on:click='getData'>点我</button><input v-on:onfocus='getFocus'> <button @click='getData'>点我</button><input @onfocus='getFocus'>
|
5.v-model
在表单元素上创建双向数据绑定;
只能用在表单元素中,注意:不同的表单元素,v-model 的表现可能会有所不同。
比如:v-model 操作文本框的 value 属性,而复选框 v-model 就是操作其选中状态;
1 2 3 4
| <!-- 绑定的是文本框输入的内容 --> <input type="text" v-model='msg'> <!-- 绑定的是复选框是否选中 --> <input type="checkbox" v-model='isCheck'>
|
6.v-for
基于源数据多次渲染元素或模板块,不仅可以渲染集合 List 也可以遍历对象 Obj;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <!-- v-for 遍历list集合--> <ul> <!--1.item是每一项对象 使用 v-for 的时候提供 key 属性,以获得性能提升。 --> <li v-for='item in list' :key='item.key'> 姓名:{{item.name}} -- 年龄:{{item.age}} </li>
<!--2.item 为当前项,index 为索引 --> <li v-for='(item,index) in list'> 姓名:{{item.name}} -- 年龄:{{item.age}} -- 下标:{{index}} </li> </ul>
<!-- v-for Obj对象 value,key,index顺序不能变 --> <ul> <li v-for='(value,key,index) in csObj'> key={{key}} -- value={{value}} -- index={{index}} </li> </ul>
|
7.v-bind:class 和 v-bind:style
表达式的类型:字符串、数组、对象(重点)
1 2 3 4 5
| <!-- 可以是对象,key是类名 value是布尔值,如果是true就添加这个类,否则就不添加--> <h2 :class='{pink:true,green:true}'>中国惊奇先生</h2> <!-- 可以是数组,--> <h2 :class='['pink','fz','green']'>斗罗大陆</h2> <h2 :style="{ color: activeColor, 'font-size': fontSize + 'px' }">不良人</h2>
|
8.v-if
根据表达式布尔值的真假条件是否加载这段代码, true:DOM 中会加载这段代码,false:DOM 中不会加载这段代码;
1
| <h3 v-if='isIF'>我是v-if,是否会加载我</h3>
|
9.v-show
根据表达式之真假值,切换元素的 display CSS 属性,无论 true 还是 false DOM 中都会加载这段代码;
1 2
| <h3 v-show='isShow'>我是v-show,是否会显示出来 </h3>
|
10.v-pre
跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。
1 2
| <!--测试:页面中的msg不会显示data中的内容,因为跳过了表达式编译--> <div v-pre>v-pre跳过编译过程 {{msg}}</div>
|
11.v-once
只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。
1 2
| <!--测试:在控制台通过vm对象修改msg,页面显示中的msg内容不会有变化--> <div v-once>v-once跳过编译过程 {{msg}}</div>
|
12.v-cloak
页面中使用 {{}}
的时候,经历了由 {{}}
-> 具体内容,这么一个过程,所以页面会造成“闪烁”
解决:通过添加 v-cloak 指令,配合 [v-cloak] { display: none; } 避免了页面闪烁
1
| <div v-cloak>{{msg}}</div>
|
动态添加数据到 data、异步更新 DOM
1.动态添加数据到 data
只有 data 中的数据才是响应式的,动态添加进来的数据默认为非响应式
可以通过以下方式实现动态添加数据的响应式:
- 1 Vue.set(object, key, value) - 适用于添加单个属性
- 2 Object.assign() - 适用于添加多个属性
2.异步更新 DOM
当绑定的数据发生变动时,Vue 异步执行 DOM 更新,监视所有数据改变,一次性更新 DOM;
解决方法:
- Vue.nextTick
- this.$nextTick
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| <div id="app"> <!-- 点击按钮之前data中还没有age属性 --> <button @click='addAge'>给data添加age</button> <div>名字:{{stu.name}}</div> <!-- 这里使用的表达式中的属性必须是响应式的 只有data中的数据才是响应式的,动态添加进来的数据默认为非响应式 --> <div>年龄:{{stu.age}}</div> <div>性别:{{stu.sex}}</div> </div>
<script> var vm = new Vue({ el: '#app', data: { msg: 'hello vue', stu: { name: 'jack', } }, methods: { addAge: function () { //可以通过以下方式实现动态添加数据的响应式 //1.添加单个属性 // 第一个参数:表示要给哪个对象添加响应式属性 $data可以省略 // 第二个参数:表示要添加的属性名称 // 第三个参数:表示属性的值 Vue.set(this.stu, "age", 18) //2.添加多个属性 //第一个参数:是一个空对象 //第二个参数:添加到哪个对象 //第三个参数:添加属性的对象 this.stu = Object.assign({},this.stu,{"name":"邓紫棋","age":18,"sex":"man"})
//此时打印的内容为 名字:jack // //为什么呢? Vue 异步执行 DOM 更新,监视所有数据改变,一次性更新DOM console.log(this.$el.children[1].innerText)
//解决方法 Vue.nextTick 和 this.$nextTick 是相同的 //在DOM更新后,回调执行某个操作(DOM操作) this.$nextTick(function(){ console.log(this.$el.children[1].innerText) }) } } }) </script>
|
filter 过滤器
- 作用:文本数据格式化 , 也就是: 将数据按照我们指定的一种格式输出
- 过滤器可以用在两个地方:
{{}}
表达式 和 v-bind 指令中
- 两种过滤器:1 全局过滤器 2 局部过滤器
1.全局过滤器
- 说明:通过全局方式创建的过滤器,在任何一个 vue 实例中都可以使用
- 注意:使用全局过滤器的时候,需要先创建全局过滤器,再创建 Vue 实例
1 2 3 4 5 6 7 8 9 10 11
| <div>{{ dateStr | date }}</div> <div>{{ dateStr | date('YYYY-MM-DD hh:mm:ss') }}</div> <script> Vue.filter('date', function(value, format) { // value 要过滤的字符串内容,比如:dateStr // format 过滤器的参数,比如:'YYYY-MM-DD hh:mm:ss' })
var vm = new Vue({ }) </script>
|
2.局部过滤器
- 说明:局部过滤器是在某一个 vue 实例的内容创建的,只在当前实例中起作用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <div>{{msg | fi("九")}}</div> var vm = new Vue({ el:'#app', data:{ msg:'八百个标兵奔北坡,八百个标兵奔北坡' }, //2. 局部过滤器 只有在当前Vue实例中才起作用 // 通过 filters 配置项, 来创建过滤器 filters:{ // content是内容,format是过滤的规则可以多个参数 fi:function(content,format){ return content.replace(/八/g,format); } } })
|
watch 监听配置项
- 概述:watch 是一个对象,键是需要观察的表达式,值是对应回调函数
- 作用:当表达式的值发生变化后,会调用对应的回调函数完成响应的监视操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <div id="app"> <input type="text" v-model='userName'> <p v-show='isError'>请输入4-8位字符</p> <input type="text" v-model='stu.age'> </div> <script> var vm = new Vue({ el: '#app', data: { userName: '', isError: false, stu: { age: 10, } }, // 通过 watch 配置项,来监视数据变化 // 只能监视 data 中的数据,要监视的数据,作为watch的属性 watch: { // 监视userName值的变化,方法名要用要监视的值的名字 userName:function(curVal, oldVal){ console.log('当前值为:', curVal, '上一次值为:', oldVal); if(curVal.length>=4 && curVal.length<=8){ this.isError = false; }else{ this.isError = true; } }, // 监听对象,加上deep:true // 注意:如果监视对象的变化,那么,curVal 和 oldVal 是相同的,指向同一个对象 stu:{ handler:function(curVal, oldVal){ console.log('当前值为:', curVal, '上一次值为:', oldVal); }, deep: true }, // 一般都是监听对象中的属性 // 只需要监视某个属性的变化,而不是整个对象中所有的属性的变化 'stu.age':function(curVal, oldVal){ console.log('当前值为:', curVal, '上一次值为:', oldVal); } } }) </script>
|
computed 计算属性配置项
- 说明:计算属性是基于它们的依赖进行缓存的,只有在它的依赖发生改变时才会重新求值
- 注意:Mustache 语法(
{{}}
)中不要放入太多的逻辑,否则会让模板过重、难以理解和维护
- 注意:computed 中的属性不能与 data 中的属性同名,否则会报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| <div id="app"> <input type="text" v-model='num1'>+ <input type="text" v-model='num2'>= <input type="text" v-model='result'> </div> <script> var vm = new Vue({ el: '#app', data: { num1: 0, num2: 0, // result: 0 计算属性名不能和data中的属性相同 }, // 计算属性,通过 computed 配置项来指定 // 注意:计算属性不能与data中的属性相同!!!否则会报错 // 特点:计算属性依赖的属性(比如:num1 和 num2)发生改变,那么计算属性就会被重新计算 // 优势:内部使用缓存机制,如果页面中多个地方都用到了计算属性,那么计算属性只会被重新计算一次!!! computed: { result:function(){ return (this.num1-0)+(this.num2-0); } } }) </script>
|
事件修饰符
- .stop 阻止向上冒泡 不会调用父的事件
- .prevent 阻止默认行为,调用 event.preventDefault()
- .capture 捕获冒泡
- .self 只当事件在该元素本身触发时,才会触发事件
- .once 事件只触发一次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <!-- .stop 阻止向上冒泡 不会调用父的事件 --> <div @click='cathFather'>我是父事件修饰符 <!-- .stop 阻止冒泡,调用 event.stopPropagation() --> <div @click.stop='catchSon'>我是子事件修饰符</div> </div>
<!-- .prevent 阻止默认行为,调用 event.preventDefault() --> <a href="http://www.baidu" @click.prevent='onPrevent'>我是prevent事件</a>
<!-- .capture捕获冒泡, 即有冒泡发生时,有该修饰符的dom元素会先执行,如果有多个,从外到内依次执行,然后再按自然顺序执行触发的事件。 --> <!-- 如果不给爷爷添加capture 点击儿子触发的顺序是 儿子、爸爸、爷爷 --> <!-- 给爷爷添加了capture事件后 点击儿子触发的顺序是 爷爷、儿子、爸爸 --> <div @click.capture='grandpa'>我是爷爷 <div @click='father'>我是爸爸 <div @click='son'> 我是儿子 </div> </div> </div>
<!-- .self 只当事件在该元素本身触发时,才会触发事件 --> <div @click.self='onSelfFather'>self事件爸爸 <div @click='onSelfSon'>self事件儿子</div> </div>
<!-- .once 事件只触发一次 --> <div @click.once='onOnce'>再点我一次试试</div>
|
键值修饰符
- 说明:在监听键盘事件时,Vue 允许为 v-on 在监听键盘事件时添加关键修饰符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <div id="app"> <!-- 键值修饰符 包括键盘、鼠标 --> <!-- 13是Enter键的code值 --> <input type="text" v-model='msg' @keyup.13='submit'> <input type="text" v-model='msg2' @keyup.enter='submit2'><br> <!-- 使用自定义键值修饰符 --> <input type="text" v-model='msg' @keyup.f2='submit'> </div>
<script> // 自定义键值修饰符 有时候写code值是数字的时候并没有语义,所有我们给它定义一下 Vue.config.keyCodes.f2 = 113; var vm = new Vue({ el:'#app', data:{ msg:'', msg2:'' }, methods:{ submit:function(){ console.log('提交数据='+this.msg) }, submit2:function(){ console.log('提交数据='+this.msg2) }, } }) </script>
|
vue 声明周期钩子函数
1. beforeCreate()
- 说明:在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用
- 注意:此时,无法获取 data 中的数据、methods 中的方法
- 使用场景:可以在这个钩子函数中开启页面加载的 loading 效果
2. created()
- 注意:这是一个常用的生命周期,可以调用 methods 中的方法、改变 data 中的数据
- 使用场景:发送请求获取数据
3. beforeMounted()
- 说明:组件将要挂载到页面中,也就是说:组件的内容还没有被挂载到页面中
- 注意:此时,获取不到页面中 DOM 元素
4. mounted()
- 说明:组件已经被挂载到页面中,此时,可以进行 DOM 操作了
5. beforeUpdate()
- 说明:数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
6. updated()
- 说明:组件 DOM 已经更新完成,所以你现在可以执行依赖于 DOM 的操作。
7. beforeDestroy()
- 说明:实例销毁之前调用。在这一步,实例仍然完全可用。
- 使用:实例销毁之前,执行清理任务,比如:清除定时器等
8. destroyed()
- 说明:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
自定义指令
Vue 这种 MVVM 模式的框架不推荐开发人员直接手动操作 DOM 有些情况, 还是需要操作 DOM 的, 如果需要操作 DOM, 就通过 Vue 中的自定义指令来操作!!!
通过 Vue.directive()方法自定义指令:
- 第一个参数: 表示自定义指令的名称;
- 第二个参数 1.表示自定义指令运行的时候, 需要执行的逻辑操作;
1 2 3
| Vue.directive('ff1', function (el) { console.log(el) })
|
- 第二个参数 2.还可以是一个对象,对象中是指令的钩子函数;
1 2 3 4 5 6 7 8 9 10
| Vue.directive('ff2', { // bind 和 inserted 这两个钩子函数, 都是进入页面就立即执行的 // 区别:inserted 能获取到指令所在元素的父元素,bind 获取不到父元素 bind(el) { console.log('bind', el.parentNode) }, inserted(el) { console.log('inserted', el.parentNode) } )}
|
指令函数的入参:
1 2 3 4 5 6 7 8 9 10 11
| <!-- 标识放到自定义指令的后面 .表示名 --> <!-- 注意:如果 v-color="red" 那么,red指的值:data中的red属性 --> <div v-color.back=" 'blue' ">{{ msg }}</div> <div v-color.col=" 'red' ">{{ msg }}</div> Vue.directive('color', function (el, binding) { if (binding.modifiers.col) { el.style.color = binding.value } else { el.style.backgroundColor = binding.value } })
|
小案例
案例请到https://github.com/pengjunshan/WebPJS中查看
二、Vue 组件
组件是可复用的 Vue 实例,组件分为全局组件和局部组件。因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等,el 是根实例特有的属性,组件中没有。
全局组件
全局组件在所有的 vue 实例中都可以使用,注意:先注册组件,再初始化根实例。
Vue.component(‘name’,{配置项})
- 第一个参数是组件名
- 第二个参数是组件的配置项,与 Vue 根实例配置项差不多
- 组件中的 data 必须是个函数数据用 return 返回,Vue 根实例中的 data 是个对象
- 组件中 template 模板有两种方式,“字符串” “html 模板”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| <body> <div id="app"> <hello></hello> </div>
<template id="temp"> <div> <p>我是组件:{{msg}}</p> <button @click='fn'> 点击消灭新冠 </button> </div> </template> <script>
/** * 第一个参数是组件名 * 第二个参数是配置项,与Vue实例的配置几乎一样 */ Vue.component('hello', { //template是组件的模板 也就是要展示的内容 //组件中template模板有两种方式,“字符串” “html模板” //方式一:字符串 // template: ` // <div> // <p>我是组件:{{msg}}</p> // <button @click='fn'> 点击消灭新冠 </button> // </div> // `, //方式二:html模板 template: '#temp',
//组件中的data必须是个函数数据用return返回,Vue根实例中的data是个对象 data() { return { msg: '武汉加油 中国加油' } }, methods: { fn() { this.msg = '新冠被消灭了,中国威武' } } }) var vm = new Vue({ el: '#app', data: {} }) </script> </body>
|
局部组件
局部组件,是在某一个具体的 vue 实例中定义的,只能在这个 vue 实例中使用;在 Vue 实例中使用 components 对象创建组件,可以创建多个组件;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| <body> <div id="app"> <!-- hello组件 --> <hello></hello> <!-- love组件 --> <love></love> </div>
<template id="temp"> <div> <p>我是组件222:{{msg}}</p> <button @click='fn'> 点击消灭新冠 </button> </div> </template>
<script>
//1.局部组件,是在某一个具体的vue实例中定义的,只能在这个vue实例中使用 //2.在Vue实例中使用components对象创建组件,可以创建多个组件 //3.组件中template模板有两种方式,“字符串” “html模板”
var vm = new Vue({ el: '#app', data: { }, components: { //hello 组件名 'hello': { //方式一 字符串 template: ` <div> <p>我是组件111:{{msg}}</p> <button @click='fn'> 点击消灭新冠 </button> </div> `, data() { return { msg: '武汉加油 中国加油' } }, methods: { fn() { this.msg = '新冠被消灭了,中国威武' } } }, 'love': { //方式二 引用html中的代码模板 template: '#temp', data() { return { msg: '我们爱中国 爱武汉' } }, methods: { fn() { this.msg = '我们爱中华民族' } } }
} }) </script> </body>
|
父组件传递子组件数据
- 方式:通过 props 属性来传递数据
- 注意:属性的值必须在组件中通过 props 属性显示指定,否则,不会生效
- 说明:传递过来的 props 属性的用法与 data 属性的用法相同
- 如果传递的数据是 data 中的属性时,必须使用 v-bind 绑定属性才可以传递过去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| <body> <div id="app"> <hello mm='中华民族万岁'></hello> <!-- 如果传递的数据是data中的属性时,必须使用v-bind绑定属性才可以传递过去 --> <!-- <hello zz='msg'></hello> --> <hello v-bind:zz='msg'></hello> </div>
<script>
// 父组件 传递数据给 子组件:(父组件:Vue的实例对象,子组件:hello组件) // 原理:通过 props 属性来传递 // 注意:使用父组件传递的属性方式和使用data中的属性方式一样 Vue.component('hello', { template: ` <div> <p>我是组件:{{msg}}</p> <p v-if='mm'>我接收到父组件的内容:{{mm}}</P> <p v-if='zz'>我接收到父组件的内容:{{zz}}</P> <button @click='fn'> 点击消灭新冠 </button> </div> `, //指定props中的值,来接收父组件传递过来的值 props:['mm','zz'], data() { return { msg: '武汉加油 中国加油' } }, methods: { fn() { this.msg=this.msg+'-----'+(this.mm===undefined? this.zz:this.mm); } } }) var vm = new Vue({ el: '#app', data: { msg:'浙江杭州' } }) </script> </body>
|
子组件传递父组件数据
- 方式:父组件给子组件传递一个函数,由子组件调用这个函数
- 说明:借助 vue 中的自定义事件(v-on:cunstomFn=”fn”)
- $emit():触发事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| <body> <div id="app"> <p>{{msg}}</p> <hello @pfn='parentFn'></hello> </div> <template id="temp"> <div> <p>我是组件:{{msg}}</p> <button @click='sonFn'> 点击消灭新冠 </button> </div> </template> <script> //组件传递 子》父 //1.由父组件定义一个方法,通过@pfn传给子组件 //2.子组件通过$emit方法把数据传递给父组件定义的方法
Vue.component('hello', { template: '#temp', data() { return { msg: '武汉加油 中国加油' } }, methods: { sonFn() { //通过$emit方法传递数据给父组件的方法,参数可以为多个 this.$emit('pfn', '新冠被消灭了,中国威武', '测试') } } })
var vm = new Vue({ el: '#app', data: { msg: '标题' }, methods: { parentFn(data, data2) { this.msg = data; console.log(data2) } }, }) </script> </body>
|
非父子组件传递数据
- 可以使用一个空的 Vue 实例作为事件总线 bus
- A 组件传递 B 组件数据 1.B 先通过 bus.$on绑定事件,2.A通过bus.$emit 方法调用 B 绑定的事件方法
- $on和$emit 都是 bus 调用的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| <body> <div id="app"> <aaa></aaa> <bbb></bbb> </div> <script> //可以使用一个空的 Vue 实例作为事件总线 var bus = new Vue() //A组件传递B组件数据 1.B先通过bus.$on绑定事件,2.A通过bus.$emit方法调用B绑定的事件方法 //$on和$emit都是bus调用的 var vm = new Vue({ el: '#app', data: {}, components: { aaa: { template: ` <div> <h3>我是组件AA</h3> <button @click='fn'> 点我传给B数据 </button> </div> `, data() { return { msg: 'A组件' } }, methods: { fn() { bus.$emit('bfn', '组件A说:你好组件B') } }
}, bbb: { template: ` <div> <h3>我是组件BB</h3> <p>{{msg}}</p> </div> `, data() { return { msg: '我在等待数据...' } }, created() { //绑定事件 接收数据,当进入页面时走到这个钩子函数后就自动绑定事件了 bus.$on('bfn', data => { this.msg = data }) } } } }) </script> </body>
|
组件中插槽使用
有时在使用组件的时候希望往组件中加入其它内容,在组件中通过插槽来接收内容。插槽内可以包含任何模板代码,包括 HTML,甚至其它的组件;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <body> <div id="app"> <hello> <p>赶走新冠</p> </hello> </div>
<template id="temp"> <div> <p>{{msg}}</p> <slot></slot> <slot></slot> </div> </template>
<script>
//当组件渲染的时候,<slot></slot> 将会被替换为"赶走新冠"。插槽内可以包含任何模板代码 //可以有多个插槽 Vue.component('hello',{ template:'#temp', data(){ return{ msg:'武汉加油 中国加油' } } }) var vm = new Vue({ el: '#app', data: { msg:'赶走新冠' } }) </script> </body>
|
vue 中 ref 使用
使用 ref 注册后 可以使用 this.$refs 获取当前 DOM 对象,必须在 mounted()钩子函数之后才可以获取 DOM 对象;元素和组件都可以使用 ref 注册;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <body> <div id="app"> <!-- 使用ref注册后 可以使用this.$refs.pp获取当前DOM对象 --> <p ref='pp'>哈喽,大家好</p>
<!-- 组件也可以 --> <hello ref="ho"></hello> </div>
<script> var vm = new Vue({ el: '#app', components: { hello: { template: `<h1>大家好</h1>`, data() { return { msg: 'hello message' } }, methods: { fn() { console.log("触发了事件11111") } } } }, mounted() { console.log(this.$refs.pp) this.$refs.pp.style.color = 'red';
console.log(this.$refs.ho.msg) this.$refs.ho.fn() } }) </script> </body>
|
三、vue-router 路由
路由基本使用
- 1.在当前文件夹下执行 npm init、npm init -y 初始化 package.json
- 2.然后执行 npm i -s vue、npm i -s vue-router 安装 vue 和 vue-router
- 3.在 node_modules 下找到 vue 和 vue-router 中的 js 并引入到项目中
- 4.创建组件
- 5.创建路由对象,并配置路由
- 6.在 Vue 实例中关联 router
- 7.路由入口
- 8.路由出口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <body> <div id="app"> <!-- 6.路由入口 --> <router-link to='/home'>首页</router-link> <router-link to='/me'>我的</router-link> <!-- 7.路由出口 --> <router-view></router-view> </div> <script src="./node_modules/vue/dist/vue.js"></script> <script src="./node_modules/vue-router/dist/vue-router.js"></script> <script> //首先先安装vue和vue-router //1.在当前文件夹下执行npm init、npm init -y初始化package.json //2.然后执行npm i -s vue、npm i -s vue-router安装vue和vue-router //3.在node_modules下找到vue和vue-router中的js并引入到项目中 //4.先创建两个组件 const Home = Vue.component('home', { template: `<h1>我是Home组件</h1>` }) const Me = Vue.component('me', { template: `<h1>我是Me组件</h1>` }) //5.创建路由对象 const router = new VueRouter({ routes: [ { path: '/home', component: Home }, { path: '/me', component: Me } ] }) var vm = new Vue({ el: '#app', data: {}, //将vue和router router: router }) </script> </body>
|
重定向、高亮
当第一次打开页面时,想要默认打开一个路由,就可以使用路由中的重定向;按钮的高亮样式不是我们喜欢的,可以使用 linkActiveClass 来自定义高亮元素的类名,然后再设置 css 样式就可以了;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| //5.创建路由对象 const router = new VueRouter({ routes: [ //如果当前路径是'/'就redirect重定向 默认home组件 { path: '/', redirect: '/home' }, { path: '/home', component: Home }, { path: '/me', component: Me } ], // 修改默认高亮的a标签的类名 // 如果是配合第三方组件库来实现菜单高亮,此时,只需要将类名设置为 第三方组件的类名即可 linkActiveClass: 'now' }) <style> /* .router-link-exact-active, */ /* .router-link-active { */ .now { color: hotpink; font-size: 30px; text-decoration: none; } </style>
|
路由传参方式
有时候多个路由都打开同一个组件,可以通过给组件传不同的参数展示不同的内容就可以了;通过路由打开不同组件传参也是一样的;
导航分为两种
- 1.声明式导航(router-link)
- 2.编程式导航($router.push)
声明式导航(router-link)
- to 字符串:只能传递字符串
- :to 对象:可以传递对象,可以通过 name、path 方式
编程式导航($router.push)
- this.router.push(name,params);通过 name 跳转的状态栏里看不到参数类似 post;组件通过 route.params 获取参数;
- this.router.push(path,query);通过 path 跳转的状态栏里可以看到参数类似 get;组件通过 route.query 获取参数;
监听路由变化
在组件中通过 watch 对象中监听路由的变化$route(to, from) {},在这里可进行监听数据变化 进行网络请求等等;
详细使用方式请看下面代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| <body> <div id="app"> <!-- 通过to字符串 --> <router-link to="/home/1001">1001号赛车</router-link> <router-link to="/home/1002">1002号赛车</router-link> <!-- 通过$router.push{} --> <a @click='fn'>1003号赛车</a> <a @click='fn1'>1004号赛车</a> <!-- 通过:to对象 --> <router-link :to="{ name:'Home',params:{id:1005,title:'pjs'}}">1005</router-link> <router-link :to="{ path:'/home',query:{id:1006,title:'pjs'}}">1006</router-link>
<router-view></router-view> </div>
<script> //导航分为两种:1.声明式导航(router-link) 2.编程式导航($router.push) //1.通过to字符串 //2.$router.push{} //3.通过:to对象
//通过name跳转的状态栏里看不到参数类似post,通过path跳转的状态栏里可以看到参数类似get //通过$route.params或$route.query来获取参数 const Home = Vue.component('home', { template: ` <div> <h1 v-if='$route.params.id'>欢迎来到主页面{{ $route.params.id}}</h1> <h1 v-if='$route.query.id'>欢迎来到主页面{{ $route.query.id}}</h1> </div> `, //监听路由变化,获取参数进行操作 watch: { //只要路由发生的变化就会执行这个方法,to 跳转的目的地, from 从哪里来 $route(to, from) { //在这里可进行监听数据变化 进行网络请求 console.log(to) console.log(from) console.log(to.params.id) console.log(to.query.id) } } })
const router = new VueRouter({ routes: [ { path: '/home/:id', component: Home }, { path: '/home', name: 'Home', component: Home } ] })
var vm = new Vue({ el: '#app', router, methods: { fn() { this.$router.push({ name: 'Home',//找到routes里匹配到name为Home params: { id: 1003, title: 'pjs' } }) }, fn1() { this.$router.push({ path: '/home', query: { id: 1004, title: 'pjs' } }) } } }) </script> </body>
|
路由嵌套-子路由
- 路由是可以嵌套的,即:路由中又包含子路由
- 规则:父组件中包含 router-view,在路由规则中使用 children 配置
- 使用 children 里配置子路由,子路由的 path 里不需要加/符号了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| <body> <div id="app"> <router-link to='/home'>首页</router-link> <router-link to='/user'>我的</router-link> <router-view></router-view> </div>
<script>
//路由是可以嵌套的,即:路由中又包含子路由 //规则:父组件中包含 router-view,在路由规则中使用 children 配置 const Home = Vue.component('home', { template: ` <div> <router-link to='/home/cartA'>买车</router-link> <router-link to='/home/cartB'>卖车</router-link> <router-view></router-view> </div> ` }) const CartA = { template: `<h3>买什么样的车?</h3>` } const CartB = { template: `<h3>卖什么样的车?</h3>` } const User = Vue.component('user', { template: ` <div> 这里是个人信息 </div> ` }) const router = new VueRouter({ routes: [ { path: '/home', component: Home, //使用children里配置子路由,子路由的path里不需要加/符号了 children: [ { path: 'cartA', component: CartA }, { path:'cartB', component:CartB } ] }, { path: '/user', component: User } ] }) var vm = new Vue({ el: '#app', router }) </script> </body>
|
四、vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。意思就是所有组件共同操作一份数据,都可以对它进行增删改查。
属性 |
作用 |
State |
共享数据都存在这里 |
Mutation |
更改 State 中数据的唯一方法,同步操作 |
Action |
异步操作 Mutation 来更改 State 中的数据 |
Getter |
基于 state 的派生状态,可理解为组件中的计算属性 |
State
- 1.安装 vuex npm i -s vuex
- 2.创建 store 对象
- 3.把 vue 和 store 进行关联
- 4.使用$store.state 中的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <body> <div id="app"> <!-- 4.使用$store.state中的数据 --> <h1>{{this.$store.state.name}}</h1> </div> <script> //1.安装vuex npm i -s vuex //2.创建store对象 let store = new Vuex.Store({ /** * state中可以存储任何类型的值 */ state: { name: '邓紫棋' } }) let vm = new Vue({ el: '#app', data: {}, //3.把vue和store进行关联 store, mounted() { //可以获取store对象 console.log(this.$store) } }) </script> </body>
|
Mutation
mutation 是更改 store 中状态的唯一方法,vuex 中规定只能通过提交 mutation 的方式去更改 store 中的状态,store.commit()方法更改数据。
1 2 3 4 5 6
| //调用方 只传一个方法名 this.$store.commit('changeName') //接收方 默认第一个参数是state,无参接收 changeName(state) { state.name = '张韶涵' }
|
1 2 3 4 5 6
| //调用方,第一个参数:方法名,第二个参数:参数 this.$store.commit('changeName',this.msg) //接收方 changeName(state, name) { state.name = name ? name : '张韶涵'; }
|
1 2 3 4 5 6 7 8 9
| //调用方,传递一个对象 this.$store.commit('changeName',{ name:this.msg }) //接收方 changeName(state, payload) { state.name = payload.name ; state.sex = payload.sex//给state新增一个属性 }
|
1 2 3 4 5 6 7 8 9 10 11
| //提交一个纯对象当做参数,type值为方法名 this.$store.commit({ type: 'changeName', name: this.msg, sex: '男' }) //接收方 changeName(state, payload) { state.name = payload.name ; state.sex = payload.sex//给state新增一个属性 }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| <body> <div id="app"> <!-- 组件AAA和组件BBB共同操作$store.state中的数据 --> <!-- 比我们之前学的组件之间通讯更加方便 --> <AAA></AAA> <BBB></BBB> </div> <script> /** * mutation是更改store中状态的唯一方法 * vuex中规定只能通过提交mutation的方式去更改store中的状态 * store.commit()更改数据 */ let store = new Vuex.Store({ state: { name: '邓紫棋' },
/** * mutations对象中自定义方法来操作state中的数据 * 并且每个方法会接受 state 作为第一个参数 * 注意:Store对象中写mutations;Mutation 必须是同步函数 */ mutations: { //不接收参数 changeName(state) { state.name = '张韶涵' }, //只接收一个参数 changeName(state, name) { state.name = name ? name : '张韶涵'; }, //接收参数对象 changeName(state, payload) { state.name = payload.name ? payload.name : '张韶涵'; state.sex = payload.sex//给state新增一个属性 } } })
//创建两个组件 const AAA = Vue.component('aaa', { template: ` <div> <!-- 4.使用$store.state中的数据 --> <h1>{{this.$store.state.name}}</h1> <h2>{{this.$store.state.sex}}</h2> </div> ` }) const BBB = Vue.component('bbb', { template: ` <div> <input type="text" placeholder="请输入姓名" v-model='msg'> <input type="button" value="确定" @click='change'> </div> `, data() { return { msg: '' } }, methods: { change() { //1.无参数 // this.$store.commit('changeName')
//2.载荷提交,只能提交一个参数 // this.$store.commit('changeName',this.msg)
//3.载荷对象提交 // this.$store.commit('changeName',{ // name:this.msg // })
//4.纯对象风格提交 type值是方法名 this.$store.commit({ type: 'changeName', name: this.msg, sex: '男' }) } } }) let vm = new Vue({ el: '#app', //把vue和store进行关联 store }) </script> </body>
|
Action
mutation 中规则上是不允许异步操作的,于是 vuex 为我们提供了 action。actions 对象中自定义方法来操作 mutations,并且每个方法会接受 context 作为第一个参数,context 对象与 store 对象具有相同的方法和属性;action 与 mutation 除了使用了异步操作和调用 mutation,其它使用并无差别 ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| //action事件的触发同样可以使用载荷和对象两种方式,其它方式就不写了和mutations方式一样 this.$store.dispatch({ type:'changeNameAsync',//Store.actions中的方法名 name: this.msg, sex: '男' }) actions: { //接收数据 延迟一秒提交数据 changeNameAsync(context, payload) { //异步操作 setTimeout(() => { context.commit('changeName',payload) }, 1000) } } mutations: { //接收参数对象 changeName(state, payload) { state.name = payload.name ; state.sex = payload.sex//给state新增一个属性 } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| <body> <div id="app"> <AAA></AAA> <BBB></BBB> </div> <script> /** * action--异步更改状态 * mutation中规则上是不允许异步操作的,于是vuex为我们提供了action。 * store.dispatch() 方法触发 */ let store = new Vuex.Store({ state: { name: '邓紫棋' },
/** * mutations只能同步执行 */ mutations: { //接收参数对象 changeName(state, payload) { state.name = payload.name ? payload.name : '张韶涵'; state.sex = payload.sex//给state新增一个属性 } }, /** * actions对象中自定义方法来操作mutations,并且每个方法会接受 context 作为第一个参数; * context对象与store对象具有相同的方法和属性 * action 与 mutation 除了使用了异步操作和调用mutation,其它使用并无差别 */ actions: { //接收数据 changeNameAsync(context, payload) { //异步操作 setTimeout(() => { context.commit({ type: 'changeName', name: payload.name, sex: '男' }) }, 1000) } } })
//创建两个组件 const AAA = Vue.component('aaa', { template: ` <div> <!-- 4.使用$store.state中的数据 --> <h1>{{this.$store.state.name}}</h1> <h2>{{this.$store.state.sex}}</h2> </div> ` }) const BBB = Vue.component('bbb', { template: ` <div> <input type="text" placeholder="请输入姓名" v-model='msg'> <input type="button" value="确定" @click='change'> </div> `, data() { return { msg: '' } }, methods: { change() { //action事件的触发同样可以使用载荷和对象两种方式,其它方式就不写了和mutations方式一样 this.$store.dispatch({ type:'changeNameAsync',//Store.actions中的方法名 name: this.msg }) } } }) let vm = new Vue({ el: '#app', store }) </script> </body>
|
getter
getters 类似 Vue 实例中的计算属性,当绑定的属性发生变化后才会重新计算;每个方法都默认接收 state 参数。
1 2 3 4 5 6 7 8 9 10 11 12
| //在getters下创建一个getName方法,默认接收state,此方法和计算属性用法一样,当state中的name发生改变时会重新计算这个方法return结果 getters:{ getName(state){ let myName=''; if(state.name === '彭俊山'){ myName = '你是最帅的!' } return state.name + myName; } } //在A组件中使用getters下的getName属性,当B组件修改了state中的name后A组件中h2数据也会变化 <h2>{{this.$store.getters.getName}}</h2>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| <body> <div id="app"> <AAA></AAA> <BBB></BBB> </div> <script> let store = new Vuex.Store({ state: { name: '邓紫棋' }, mutations: { //接收参数对象 changeName(state, payload) { state.name = payload.name ? payload.name : '张韶涵'; } }, /** * getters类似Vue实例中的计算属性,当绑定的属性发生变化后才会重新计算 * 每个方法都默认接收state参数 */ getters:{ getName(state){ let myName=''; if(state.name === '彭俊山'){ myName = '你是最帅的!' } return state.name + myName; } } })
//创建两个组件 const AAA = Vue.component('aaa', { template: ` <div> <!-- 使用$store.state中的数据 --> <h1>{{this.$store.state.name}}</h1> <!-- 使用$store.getters中的属性 --> <h2>{{this.$store.getters.getName}}</h2> </div> ` }) const BBB = Vue.component('bbb', { template: ` <div> <input type="text" placeholder="请输入姓名" v-model='msg'> <input type="button" value="确定" @click='change'> </div> `, data() { return { msg: '' } }, methods: { change() { //纯对象风格提交 type值是方法名 this.$store.commit({ type: 'changeName', name: this.msg }) } } }) let vm = new Vue({ el: '#app', store }) </script> </body>
|
vuex 刷新页面 store 数据丢失
当刷新页面后,store 中的数据都会丢失;将 store 的数据存储在 storage 里,由于 vue 多为单页面应用,且每次重新打开页面需要保持数据为空 所以这里我们不选用 localStorage,用 sessionStorage 会话机制;
1 2 3 4 5 6 7 8 9 10 11
| created() { //在页面加载时读取sessionStorage里的状态信息 if (sessionStorage.getItem("store")) { this.$store.replaceState(Object.assign({}, this.$store.state, JSON.parse(sessionStorage.getItem("store")))) }
//在页面刷新时将vuex里的信息保存到sessionStorage里 window.addEventListener("beforeunload", () => { sessionStorage.setItem("store", JSON.stringify(this.$store.state)) }) }
|