尚硅谷Vue2.0+Vue3.0全套教程丨vuejs从入门到精通
初识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
| <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>初识Vue</title> <script type="text/javascript" src="../js/vue.js"></script> </head> <body> <div id="demo"> <h1>Hello,{{name.toUpperCase()}},{{address}}</h1> </div>
<script type="text/javascript" > Vue.config.productionTip = false
new Vue({ el:'#demo', data:{ name:'atguigu', address:'北京' } })
</script> </body> </html>
|
模板语法
插值语法:
指令语法:
1
| v-bind:href='js表达式' // 简写为:href='xxx'
|
v-开头的都是Vue指令。
1
| <a v-bind:href="school.url.toUpperCase()">点我去{{school.name}}学习1</a>
|
数据绑定
单向绑定(v-bind):数据只能从data流向页面。
双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data。
双向绑定一般都应用在表单类元素上(如:input、select等)
v-model:value 可以简写为 v-model,因为v-model默认收集的就是value值。
1 2 3 4 5 6 7
| 单向数据绑定:<input type="text" v-bind:value="name"><br/> 双向数据绑定:<input type="text" v-model:value="name"><br/>
单向数据绑定:<input type="text" :value="name"><br/> 双向数据绑定:<input type="text" v-model="name"><br/>
|
el与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 25 26 27 28 29 30 31 32 33
|
const v = new Vue({ el:'#root', data:{ name:'尚硅谷' } })
const v = new Vue({ data:{ name:'尚硅谷' } }) v.$mount('#root')
new Vue({ el:'#root', data:{ name:'尚硅谷' } })
new Vue({ el:'#root', data(){ return{ name:'尚硅谷' } } })
|
理解MVVM
MVVM模型
- M:模型(Model) :data中的数据
- V:视图(View) :模板代码
- VM:视图模型(ViewModel):Vue实例
事件处理
使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名
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
| <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>事件的基本使用</title> <script type="text/javascript" src="../js/vue.js"></script> </head> <body> <div id="root"> <h2>欢迎来到{{name}}学习</h2> <button v-on:click="showInfo1">点我提示信息</button> <button @click="showInfo1">点我提示信息1(不传参)</button> <button @click="showInfo2($event,66)">点我提示信息2(传参)</button> </div> </body>
<script type="text/javascript"> Vue.config.productionTip = false
const vm = new Vue({ el:'#root', data:{ name:'尚硅谷', }, methods:{ showInfo1(event){ alert('同学你好!') }, showInfo2(event,number){ console.log(event,number) alert('同学你好!!') } } }) </script> </html>
|
Vue中的事件修饰符:
- prevent:阻止默认事件(常用);
- stop:阻止事件冒泡(常用);
- once:事件只触发一次(常用);
- capture:使用事件的捕获模式;
- self:只有event.target是当前操作的元素时才触发事件;
- passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
键盘事件:
1
| @keydown.enter="showInfo"
|
Vue中常用的按键别名:
- 回车 => enter
- 删除 => delete (捕获“删除”和“退格”键)
- 退出 => esc
- 空格 => space
- 换行 => tab (特殊,必须配合keydown去使用)
- 上 => up
- 下 => down
- 左 => left
- 右 => right
计算属性
要用的属性不存在,要通过已有属性计算得来。
优势:与 methods 实现相比,内部有缓存机制(复用),效率更高,调试方便。
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
| const vm = new Vue({ el:'#root', data:{ firstName:'张', lastName:'三', x:'你好' }, computed:{ fullName:{ get(){ console.log('get被调用了') return this.firstName + '-' + this.lastName }, set(value){ console.log('set',value) const arr = value.split('-') this.firstName = arr[0] this.lastName = arr[1] }
} } })
|
监视属性
监视属性 watch
- 当被监视的属性变化时, 回调函数自动调用, 进行相关操作
- 监视的属性必须存在,才能进行监视
- 监视的两种写法:(1).new Vue时传入watch配置 (2).通过vm.$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
| const vm = new Vue({ el:'#root', data:{ isHot:true, }, watch:{ isHot:{ immediate:true, handler(newValue,oldValue){ console.log('isHot被修改了',newValue,oldValue) } }
} })
vm.$watch('isHot',{ immediate:true, handler(newValue,oldValue){ console.log('isHot被修改了',newValue,oldValue) } })
|
deep:true
启用深度监视,可监视对象深层值的改变。
绑定样式
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <div class="basic" :class="mood" @click="changeMood">{{name}}</div> <br/><br/>
<div class="basic" :class="classArr">{{name}}</div> <br/><br/>
<div class="basic" :class="classObj">{{name}}</div> <br/><br/>
<div class="basic" :style="styleObj">{{name}}</div> <br/><br/>
<div class="basic" :style="styleArr">{{name}}</div>
|
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
| const vm = new Vue({ el:'#root', data:{ name:'尚硅谷', mood:'normal', classArr:['atguigu1','atguigu2','atguigu3'], classObj:{ atguigu1:false, atguigu2:false, }, styleObj:{ fontSize: '40px', color:'red', }, styleObj2:{ backgroundColor:'orange' }, styleArr:[ { fontSize: '40px', color:'blue', }, { backgroundColor:'gray' } ] }, methods: { changeMood(){ const arr = ['happy','sad','normal'] const index = Math.floor(Math.random()*3) this.mood = arr[index] } }, })
|
条件渲染
v-if 适用于切换频率较低的场景,不展示的DOM元素直接被移除
v-show 适用于切换频率较高的场景,不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <h2 v-show="false">欢迎来到{{name}}</h2> <h2 v-show="1 === 1">欢迎来到{{name}}</h2>
<h2 v-if="false">欢迎来到{{name}}</h2> <h2 v-if="1 === 1">欢迎来到{{name}}</h2>
<div v-if="n === 1">Angular</div> <div v-else-if="n === 2">React</div> <div v-else-if="n === 3">Vue</div> <div v-else>哈哈</div>
<template v-if="n === 1"> <h2>你好</h2> <h2>尚硅谷</h2> <h2>北京</h2> </template>
|
列表渲染
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
| <h2>人员列表(遍历数组)</h2> <ul> <li v-for="(p,index) of persons" :key="index"> {{p.name}}-{{p.age}} </li> </ul>
<h2>汽车信息(遍历对象)</h2> <ul> <li v-for="(value,k) of car" :key="k"> {{k}}-{{value}} </li> </ul>
<h2>测试遍历字符串(用得少)</h2> <ul> <li v-for="(char,index) of str" :key="index"> {{char}}-{{index}} </li> </ul>
<h2>测试遍历指定次数(用得少)</h2> <ul> <li v-for="(number,index) of 5" :key="index"> {{index}}-{{number}} </li> </ul>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| new Vue({ el:'#root', data:{ persons:[ {id:'001',name:'张三',age:18}, {id:'002',name:'李四',age:19}, {id:'003',name:'王五',age:20} ], car:{ name:'奥迪A8', price:'70万', color:'黑色' }, str:'hello' } })
|
开发中如何选择key:
- 最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
- 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
更新时的一个问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const vm = new Vue({ el:'#root', data:{ persons:[ {id:'001',name:'马冬梅',age:30,sex:'女'}, {id:'002',name:'周冬雨',age:31,sex:'女'}, {id:'003',name:'周杰伦',age:18,sex:'男'}, {id:'004',name:'温兆伦',age:19,sex:'男'} ] }, methods: { updateMei(){ this.persons.splice(0,1,{id:'001',name:'马老师',age:50,sex:'男'}) } } })
|
动态添加响应式属性:
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
| const vm = new Vue({ el:'#root', data:{ school:{ name:'尚硅谷', address:'北京', }, student:{ name:'tom', age:{ rAge:40, sAge:29, }, friends:[ {name:'jerry',age:35}, {name:'tony',age:36} ] } }, methods: { addSex(){ this.$set(this.student,'sex','男') } } })
|
在Vue修改数组中的某个元素一定要用如下方法:
- 使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
- Vue.set() 或 vm.$set()
特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性!
收集表单数据
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="root"> <form @submit.prevent="demo"> 账号:<input type="text" v-model.trim="userInfo.account"> <br/><br/> 密码:<input type="password" v-model="userInfo.password"> <br/><br/> 年龄:<input type="number" v-model.number="userInfo.age"> <br/><br/> 性别: 男<input type="radio" name="sex" v-model="userInfo.sex" value="male"> 女<input type="radio" name="sex" v-model="userInfo.sex" value="female"> <br/><br/> 爱好: 学习<input type="checkbox" v-model="userInfo.hobby" value="study"> 打游戏<input type="checkbox" v-model="userInfo.hobby" value="game"> 吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat"> <br/><br/> 所属校区 <select v-model="userInfo.city"> <option value="">请选择校区</option> <option value="beijing">北京</option> <option value="shanghai">上海</option> <option value="shenzhen">深圳</option> <option value="wuhan">武汉</option> </select> <br/><br/> 其他信息: <textarea v-model.lazy="userInfo.other"></textarea> <br/><br/> <input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="http://www.atguigu.com">《用户协议》</a> <button>提交</button> </form> </div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| new Vue({ el:'#root', data:{ userInfo:{ account:'', password:'', age:18, sex:'female', hobby:[], city:'beijing', other:'', agree:'' } }, methods: { demo(){ console.log(JSON.stringify(this.userInfo)) } } })
|
过滤器
1 2 3 4 5 6 7 8 9 10 11 12
| <div id="root"> <h2>显示格式化后的时间</h2> <h3>现在是:{{fmtTime}}</h3> <h3>现在是:{{getFmtTime()}}</h3> <h3>现在是:{{time | timeFormater}}</h3> <h3>现在是:{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3> <h3 :x="msg | mySlice">尚硅谷</h3> </div>
|
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
| Vue.filter('mySlice',function(value){ return value.slice(0,4) })
new Vue({ el:'#root', data:{ time:1621561377603, msg:'你好,尚硅谷' }, computed: { fmtTime(){ return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss') } }, methods: { getFmtTime(){ return dayjs(this.time).format('YYYY年MM月DD日 HH:mm:ss') } }, filters:{ timeFormater(value,str='YYYY年MM月DD日 HH:mm:ss'){ return dayjs(value).format(str) } } })
new Vue({ el:'#root2', data:{ msg:'hello,atguigu!' } })
|
内置指令
- v-bind : 单向绑定解析表达式, 可简写为 :xxx
- v-model : 双向数据绑定
- v-for : 遍历数组/对象/字符串
- v-on : 绑定事件监听, 可简写为@
- v-if : 条件渲染(动态控制节点是否存存在)
- v-else : 条件渲染(动态控制节点是否存存在)
- v-show : 条件渲染 (动态控制节点是否展示)
- v-text : 向其所在的节点中渲染文本内容,
<div v-text="name"></div>
- v-html : 向指定节点中渲染包含html结构的内容
- v-cloak : Vue实例创建完毕并接管容器后,会删掉v-cloak属性
- v-once : 所在节点在初次动态渲染后,就视为静态内容了,可以用于优化性能
- v-pre : 跳过其所在节点的编译过程
严重注意:v-html有安全性问题!
在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
使用css配合v-cloak可以解决网速慢时页面展示出的问题:
1 2 3 4 5
| <style> [v-cloak]{ display:none; } </style>
|
自定义指令
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
| Vue.directive('fbind',{ bind(element,binding){ element.value = binding.value }, inserted(element,binding){ element.focus() }, update(element,binding){ element.value = binding.value } })
new Vue({ el:'#root', data:{ name:'尚硅谷', n:1 }, directives:{ 'big-number'(element,binding){ element.innerText = binding.value * 10 }, big(element,binding){ console.log('big',this) element.innerText = binding.value * 10 }, fbind:{ bind(element,binding){ element.value = binding.value }, inserted(element,binding){ element.focus() }, update(element,binding){ element.value = binding.value } } } })
|
生命周期
- beforeCreate
- created
- beforeMount
- mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
- beforeUpdate
- updated
- beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。
- destroyed
非单文件组件
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
| const student = Vue.extend({ template:` <div> <h2>学生姓名:{{studentName}}</h2> <h2>学生年龄:{{age}}</h2> </div> `, data(){ return { studentName:'张三', age:18 } } })
Vue.component('student',student)
new Vue({ el:'#root', data:{ msg:'你好啊!' }, components:{ student } })
|
使用自定义组件:
关于组件名:
一个单词组成:
- 第一种写法(首字母小写):school
- 第二种写法(首字母大写):School
多个单词组成:
- 第一种写法(kebab-case命名):my-school
- 第二种写法(CamelCase命名):MySchool (需要Vue脚手架支持)
一个简写方式:
1 2 3
| const school = Vue.extend(options)
const school = options
|
单文件组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <template> <div> <h2>学生姓名:{{name}}</h2> <h2>学生年龄:{{age}}</h2> </div> </template>
<script> export default { name:'Student', data(){ return { name:'张三', age:18 } } } </script>
<style> </style>
|
日期处理三方库
moment.js
day.js 轻量
npm 安装指定版本软件包
1 2
| npm view webpack versions npm i webpack@5
|
Vue脚手架
安装脚手架:
创建项目:
启动项目:
配置npm淘宝镜像:
1
| npm config set registry https://registry.npm.taobao.org
|
输出脚手架配置:
vue.js与vue.runtime.xxx.js的区别:
- vue.js是完整版的Vue,包含:核心功能+模板解析器。
- vue.runtime.xxx.js是运行版的Vue,只包含:核心功能;没有模板解析器。
因为vue.runtime.xxx.js没有模板解析器,所以不能使用template配置项,需要使用render函数接收到的createElement函数去指定具体内容。
1 2 3 4 5 6 7 8 9 10 11 12
| import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({ el:'#app', render: h => h(App), })
|
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
| <template> <div> <h1 v-text="msg" ref="title"></h1> <button ref="btn" @click="showDOM">点我输出上方的DOM元素</button> <School ref="sch"/> </div> </template>
<script> //引入School组件 import School from './components/School'
export default { name:'App', components:{School}, data() { return { msg:'欢迎学习Vue!' } }, methods: { showDOM(){ console.log(this.$refs.title) //真实DOM元素 console.log(this.$refs.btn) //真实DOM元素 console.log(this.$refs.sch) //School组件的实例对象(vc) } }, } </script>
|
props配置
通过 props,父组件可以向子组件传递数据。
子组件:
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
| <template> <div> <h1>{{msg}}</h1> <h2>学生姓名:{{name}}</h2> <h2>学生性别:{{sex}}</h2> <h2>学生年龄:{{myAge+1}}</h2> <button @click="updateAge">尝试修改收到的年龄</button> </div> </template>
<script> export default { name:'Student', data() { console.log(this) return { msg:'我是一个尚硅谷的学生', myAge:this.age } }, methods: { updateAge(){ this.myAge++ } }, // 方式1:简单声明接收 // props:['name','age','sex']
// 方式2:接收的同时对数据进行类型限制 /* props:{ name:String, age:Number, sex:String } */
// 方式3:接收的同时对数据:进行类型限制+默认值的指定+必要性的限制 props:{ name:{ type:String, //name的类型是字符串 required:true, //name是必要的 }, age:{ type:Number, default:99 //默认值 }, sex:{ type:String, required:true } } } </script>
|
父组件:
1 2 3
| <div> <Student name="李四" sex="女" :age="18"/> </div>
|
mixin混入
编写需要混入的配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| export const hunhe = { methods: { showName(){ alert(this.name) } }, mounted() { console.log('你好啊!') }, } export const hunhe2 = { data() { return { x:100, y:200 } }, }
|
全局混入,所有组件都能使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import Vue from 'vue'
import App from './App.vue' import {hunhe,hunhe2} from './mixin'
Vue.config.productionTip = false
Vue.mixin(hunhe) Vue.mixin(hunhe2)
new Vue({ el:'#app', render: h => h(App) })
|
局部混入:
1 2 3 4 5 6 7 8 9 10 11 12
| import {hunhe,hunhe2} from '../mixin'
export default { name:'Student', data() { return { name:'张三', sex:'男' } }, mixins:[hunhe,hunhe2] }
|
插件
编写自定义插件:
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
| export default { 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.prototype.hello = ()=>{alert('你好啊')} } }
|
应用插件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import Vue from 'vue'
import App from './App.vue'
import plugins from './plugins'
Vue.config.productionTip = false
Vue.use(plugins,1,2,3)
new Vue({ el:'#app', render: h => h(App) })
|
scoped样式
避免组件之间同名class样式冲突:
1 2 3 4 5
| <style scoped> .title{ color: red; } </style>
|
浏览器本地存储
localStorage:
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
| <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>localStorage</title> </head> <body> <h2>localStorage</h2> <button onclick="saveData()">点我保存一个数据</button> <button onclick="readData()">点我读取一个数据</button> <button onclick="deleteData()">点我删除一个数据</button> <button onclick="deleteAllData()">点我清空一个数据</button>
<script type="text/javascript" > let p = {name:'张三',age:18}
function saveData(){ localStorage.setItem('msg','hello!!!') localStorage.setItem('msg2',666) localStorage.setItem('person',JSON.stringify(p)) } function readData(){ console.log(localStorage.getItem('msg')) console.log(localStorage.getItem('msg2'))
const result = localStorage.getItem('person') console.log(JSON.parse(result))
} function deleteData(){ localStorage.removeItem('msg2') } function deleteAllData(){ localStorage.clear() } </script> </body> </html>
|
sessionStorage,页面关闭后数据丢失:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| let p = {name:'张三',age:18}
function saveData(){ sessionStorage.setItem('msg','hello!!!') sessionStorage.setItem('msg2',666) sessionStorage.setItem('person',JSON.stringify(p)) } function readData(){ console.log(sessionStorage.getItem('msg')) console.log(sessionStorage.getItem('msg2'))
const result = sessionStorage.getItem('person') console.log(JSON.parse(result))
} function deleteData(){ sessionStorage.removeItem('msg2') } function deleteAllData(){ sessionStorage.clear() }
|
组件自定义事件
子组件向父组件传递数据。
通过props实现
父组件通过props向子组件传递一个函数,子组件通过调用该函数,利用函数参数向父组件传递数据。
$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
| <script> export default { name:'Student', data() { return { name:'张三', sex:'男', number:0 } }, methods: { add(){ console.log('add回调被调用了') this.number++ }, sendStudentlName(){ //触发Student组件实例身上的atguigu事件 this.$emit('atguigu',this.name,666,888,900) // this.$emit('demo') // this.$emit('click') }, unbind(){ this.$off('atguigu') //解绑一个自定义事件 // this.$off(['atguigu','demo']) //解绑多个自定义事件 // this.$off() //解绑所有的自定义事件 }, death(){ this.$destroy() //销毁了当前Student组件的实例,销毁后所有Student实例的自定义事件全都不奏效。 } }, } </script>
|
父组件绑定子组件的事件:
方式一:
1 2 3 4 5
| <template> <div class="app"> <Student @atguigu="getStudentName" @demo="m1"/> </div> </template>
|
方式二:
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
| <template> <div class="app"> <Student ref="student" @click.native="show"/> </div> </template>
<script> import Student from './components/Student'
export default { name:'App', components:{Student}, data() { return { msg:'你好啊!', studentName:'' } }, methods: { getStudentName(name,...params){ console.log('App收到了学生名:',name,params) this.studentName = name }, show(){ alert(123) } }, mounted() { this.$refs.student.$on('atguigu',this.getStudentName) //绑定自定义事件 // this.$refs.student.$once('atguigu',this.getStudentName) //绑定自定义事件(一次性) }, } </script>
|
全局事件总线
用于组件与组件之间传递数据。
安装全局事件总线:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({ el:'#app', render: h => h(App), beforeCreate() { Vue.prototype.$bus = this }, })
|
发送数据的一方触发事件:
1
| this.$bus.$emit('hello',this.name)
|
接收数据的一方绑定事件:
1 2 3
| this.$bus.$on('hello',(data)=>{ console.log('我是School组件,收到了数据',data) })
|
解绑事件:
消息订阅与发布
安装 pubsub-js :
导入:
1
| import pubsub from 'pubsub-js'
|
发布:
1
| pubsub.publish('hello', 666)
|
订阅和取消订阅:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import pubsub from 'pubsub-js' export default { name:'School', data() { return { name:'尚硅谷', address:'北京', } }, mounted() { this.pubId = pubsub.subscribe('hello',(msgName,data)=>{ }) }, beforeDestroy() { pubsub.unsubscribe(this.pubId) }, }
|
nextTick
下一次DOM更新结束后调用。
1 2 3
| this.$nextTick(function(){ this.$refs.inputTitle.focus() })
|
过渡与动画
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
| <template> <div> <button @click="isShow = !isShow">显示/隐藏</button> <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; }
.hello-enter-active{ animation: atguigu 0.5s linear; }
.hello-leave-active{ animation: atguigu 0.5s linear reverse; }
@keyframes atguigu { from{ transform: translateX(-100%); } to{ transform: translateX(0px); } } </style>
|
transition中如果不使用name,则样式类名默认为v-enter-active。
方法二,使用过渡:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <style scoped> h1{ background-color: orange; } /* 进入的起点、离开的终点 */ .hello-enter,.hello-leave-to{ transform: translateX(-100%); } .hello-enter-active,.hello-leave-active{ transition: 0.5s linear; } /* 进入的终点、离开的起点 */ .hello-enter-to,.hello-leave{ transform: translateX(0); } </style>
|
包含多个元素时使用transition-group:
1 2 3 4
| <transition-group name="hello" appear> <h1 v-show="!isShow" key="1">你好啊!</h1> <h1 v-show="isShow" key="2">尚硅谷!</h1> </transition-group>
|
方法三,使用animate.css:
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
| <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>
|
配置代理服务器
修改vue.config.js:
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
| module.exports = { pages: { index: { entry: 'src/main.js', }, }, lintOnSave:false,
devServer: { proxy: { '/atguigu': { target: 'http://localhost:5000', pathRewrite:{'^/atguigu':''}, }, '/demo': { target: 'http://localhost:5001', pathRewrite:{'^/demo':''}, } } } }
|