DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>初识Vuetitle>
<script type="text/javascript" src="./vue.js">script>
<script type="text/javascript">
Vue.config.productionTip = false //设置为 false 以阻止 vue 在启动时生成生产提示。
script>
head>
<body>
<div id="root">
<h1>Hello,{{ name }}h1>
div>
body>
<script>
//创建Vue实例
new Vue({
el: '#root', //el用于指定当前Vue实例为那个容器服务,值为CSS选择器字符串
data: { //data中用于存储数据,数据供el所指容器使用,之暂时先写成一个对象
name: '尚硅谷'
}
})
script>
html>
1.想让Vue工作就必须创建一个Vue实例,且要传入一个配置对象
2.root容器里的代码依然符合HTML规范,只不过加入了特殊Vue语法
3.root容器中代码被称为Vue模板
4.Vue实例和容器是一一对应的
5.真实开发中只有一个Vue实例,并且会配合着组件一起使用
6.{{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中所有属性;
7.一旦data中数据发生改变,模板中用到该数据的地方也会自动更新。
DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>模板语法title>
<script type="text/javascript" src="./vue.js">script>
<script type="text/javascript">
Vue.config.productionTip = false //设置为 false 以阻止 vue 在启动时生成生产提示。
script>
head>
<body>
<div id="root">
<h1>插值语法h1>
<h3>你好,{{ name }}h3>
<hr />
<h1>指令语法h1>
<a v-bind:href="school.url" v-bind:x="hello">点我去{{school.name}}学习1a>
div>
body>
<script>
new Vue({
el: '#root',
data: {
name: 'jack',
school: {
url: 'http://www.atguigu.com',
name: '尚硅谷'
}
}
})
script>
html>
Vue模板语法有2大类:
1.插值语法:
功能:用于解析标签体内容
写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性
2.指令语法:
功能:用于解析标签(标签属性,标签体内容,绑定事件…)
备注:Vue中有很多指令,形式都为v-???,v-bind只是其中一个
DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>数据绑定title>
<script type="text/javascript" src="./vue.js">script>
<script type="text/javascript">
Vue.config.productionTip = false //设置为 false 以阻止 vue 在启动时生成生产提示。
script>
head>
<body>
<div id="root">
单向数据绑定:<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/>
div>
body>
<script>
new Vue({
el:'#root',
data:{
name:'尚硅谷'
}
})
script>
html>
Vue中有2种数据绑定的方式:
1.单向绑定(v-bind):数据只能从data流向页面
2.双向绑定(v-model):数据不仅能从data流向页面,也可以从页面流向data
备注:
1.双向绑定一般都应用在表单元素上(如:input,select)
2.v-model:value可以简写为v-model,v-model默认收集的就是value。
DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>title>
<script type="text/javascript" src="./vue.js">script>
<script type="text/javascript">
Vue.config.productionTip = false //设置为 false 以阻止 vue 在启动时生成生产提示。
script>
head>
<body>
<div id="root">
<h1>你好,{{name}}h1>
div>
body>
<script>
// el的两种写法
const v = new Vue({
// el:'#root', //第一种写法
data:{
name:'尚硅谷'
}
})
console.log(v);
v.$mount('#root') //灵活,第二种写法
// setTimeout(()=>{
// v.$mount('#root')
// },1000)
script>
html>
DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>title>
<script type="text/javascript" src="./vue.js">script>
<script type="text/javascript">
Vue.config.productionTip = false //设置为 false 以阻止 vue 在启动时生成生产提示。
script>
head>
<body>
<div id="root">
<h1>你好,{{name}}h1>
div>
body>
<script>
// data的两种写法
const v = new Vue({
el:'#root',
//第一种写法:对象式
// data:{
// name:'尚硅谷'
// }
//第二种写法:函数式
data(){
return{
name:'尚硅谷',
}
}
})
script>
html>
- data两种写法如何选择:以后学习到组件,data必须使用函数式,否则会报错
- 重要原则:一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了
DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>理解MVVMtitle>
<script type="text/javascript" src="./vue.js">script>
<script type="text/javascript">
Vue.config.productionTip = false //设置为 false 以阻止 vue 在启动时生成生产提示。
script>
head>
<body>
<div id="root">
<h1>学校名称:{{name}}h1>
<h1>学校地址:{{address}}h1>
<h1>测试1:{{1+1}}h1>
<h1>测试2:{{$options}}h1>
<h1>测试3:{{_c}}h1>
div>
body>
<script>
// ViewModel
const vm = new Vue({
el:'#root',
// Model
data:{
name:'尚硅谷',
address:'北京'
}
})
console.log(vm);
script>
html>
data中所有属性,最后都出现在vm身上
vm身上所有的属性及Vue原型上所有的属性,在Vue模板中都可以直接使用
DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>回顾Object.defineproperty方法title>
<script type="text/javascript" src="./vue.js">script>
<script type="text/javascript">
Vue.config.productionTip = false //设置为 false 以阻止 vue 在启动时生成生产提示。
script>
head>
<body>
<script type="text/javascript">
let num = 18
let person = {
name: '张三',
sex: '男',
// age:18
}
// 此方法添加属性不可枚举
Object.defineProperty(person, 'age', {
// value: 18,
// enumerable: true, //控制属性是否可以枚举,默认false
// writable: true, //控制属性是否可以被修改,默认false
// configurable: true, //控制属性是否可以被删除,默认false
// 当有人读取person 的age属性时,get函数(getter)就会被调用,且返回值就是age值
get() {
return num
},
// 当有人修改person 的age属性时,set函数(setter)就会被调用,且返回修改的具体值
set(value){
console.log('有人修改了age值,qie修改后值为:',value)
number = value
}
})
// console.log(Object.keys(person))
console.log(person);
script>
body>
html>
DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>什么是数据代理title>
head>
<body>
<script type="text/javascript">
let obj = {x:100}
let obj1 = {y:200}
Object.defineProperty(obj1,'x',{
get(){
return obj.x
},
set(value){
obj.x = value
}
})
script>
body>
html>
- Vue中数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)
- 好处:更加方便操作data中数据
- 原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作data中对应的属性。
DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>事件的基本使用title>
<script type="text/javascript" src="./vue.js">script>
<script>Vue.config.productionTip = false //阻止vue在启动时生成生产提示sscript>
head>
<body>
<div id="root">
<h2>欢迎来到{{name}}学习h2>
<button v-on:click="showInfo1">点我提示信息1(不传参)button>
<button @click="showInfo2($event,66)">点我提示信息2(传参)button>
div>
body>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
name:'尚硅谷',
},
methods:{
showInfo1(event){
// console.log(this); //此处this是vm
alert('同学你好!')
},
showInfo2(event,number){
console.log(event,number);
// console.log(this); //此处this是vm
alert('同学你好!!')
}
}
})
script>
html>
事件的基本使用:
- 使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名
- 事件的回调需要配置在methods对象中,最终会在vm上
- methods中配置的函数,不要用箭头函数,否则this就不是vm
- methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象
- @click=“demo” 和 @click=“demo($event)” 效果一致,但后者可以传参
Vue中的事件修饰符
1)prevent: 阻止默认事件(常用);
2)stop:阻止事件冒泡(常用);
3)once:事件只触发一次(常用);
4)capture:使用事件的捕获模式;
5)self:只有event.target是当前操作的元素时才触发事件;
6)passive: 时间的默认行为立即执行,无需等待事件回调执行完毕。
DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>事件修饰符title>
<script type="text/javascript" src="./vue.js">script>
<script>Vue.config.productionTip = false //阻止vue在启动时生成生产提示script>
<style>
* {
margin-top: 20px;
}
.demo1 {
height: 50px;
background-color: skyblue;
}
.box1 {
padding: 5px;
background-color: skyblue;
}
.box2 {
padding: 5px;
background-color: orange;
}
.list {
width: 200px;
height: 200px;
background-color: peru;
overflow: auto;
}
li {
height: 100px;
}
style>
head>
<body>
<div id="root">
<h2>欢迎来到{{name}}学习h2>
<a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息a>
<div class="demo1" @click="showInfo">
<button @click.stop="showInfo">点我提示信息button>
div>
<button @click.once="showInfo">点我提示信息button>
<div class="box1" @click.capture="showMsg(1)">
div1
<div class="box2" @click.capture="showMsg(2)">
div2
div>
div>
<div class="demo1" @click.self="showInfo">
<button @click="showInfo">点我提示信息button>
div>
<ul @wheel.passive="demo" class="list">
<li>1li>
<li>2li>
<li>3li>
<li>4li>
ul>
div>
body>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
name:'尚硅谷',
},
methods:{
showInfo(e){
// alert('同学你好!')
console.log(e.target)
},
showMsg(msg){
console.log(msg);
},
demo(){
for (let index = 0; index < 10000; index++) {
console.log('#')
}
console.log('累坏了')
}
}
})
script>
html>
- Vue中常用按键别名
回车=>enter
删除=>delete(捕获“删除”和“退格”键)
退出=>esc
空格=>space
换行=>tab(必须配合keydown使用)
上 => up
下 => down
左 => left
右 => right- 如果vue中没有定义想要使用的键的话,可以通过e输出e.key,e.keyCode来查看键盘上按键的名称和编码。
如果按键名称是驼峰命名的话,要把单词的首字母都改成小写,并且用-连接。- 系统修饰符(用法特殊):ctrl、alt、shift、meta
(1)、配合keyup使用:按下修饰符键的同时,再按下其他键,随后释放其他键,事件才被触发。
(2)、配合keydown使用:正常触发事件。
DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>姓名案例_插值语法实现title>
<script type="text/javascript" src="./vue.js">script>
<script>Vue.config.productionTip = false //阻止vue在启动时生成生产提示script>
head>
<body>
<div id="root">
姓:<input type="text" v-model="firstname"><br/><br/>
名:<input type="text" v-model="lastname"><br/><br/>
姓名:<span>{{firstname.slice(0,3)}}-{{lastname}}span>
div>
body>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
firstname:'张',
lastname:'三'
},
methods:{
showInfo(e){
console.log(e.key,e.keyCode);
// console.log(e.target.value);
}
}
})
script>
html>
DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>姓名案例_methods实现title>
<script type="text/javascript" src="./vue.js">script>
<script>Vue.config.productionTip = false //阻止vue在启动时生成生产提示script>
head>
<body>
<div id="root">
姓:<input type="text" v-model="firstname"><br/><br/>
名:<input type="text" v-model="lastname"><br/><br/>
姓名:<span>{{fullName()}}span>
div>
body>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
firstname:'张',
lastname:'三'
},
methods:{
fullName(){
return this.firstname+'-'+this.lastname
}
}
})
script>
html>
DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>姓名案例_计算属性实现title>
<script type="text/javascript" src="./vue.js">script>
<script>Vue.config.productionTip = false //阻止vue在启动时生成生产提示script>
head>
<body>
<div id="root">
姓:<input type="text" v-model="firstname"><br/><br/>
名:<input type="text" v-model="lastname"><br/><br/>
姓名:<span>{{fullName}}span><br/><br/>
姓名:<span>{{fullName}}span><br/><br/>
姓名:<span>{{fullName}}span><br/><br/>
姓名:<span>{{fullName}}span>
div>
body>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
firstname:'张',
lastname:'三',
},
computed:{
fullName:{
// get作用:当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
// get什么时候调用:
// 1.初次读取fullName时
// 2.所依赖数据发生变化时
get(){
console.log('get被调用了')
return this.firstname+'-'+this.lastname
},
// set什么时候调用:
// 1.fullName被修改时
set(value){
console.log('set',value)
const arr = value.split('-')
this.firstname = arr[0]
this.lastname = arr[1]
}
}
}
})
script>
html>
计算属性:
- 定义:要用的属性不存在,要通过已有属性计算得来。
- 原理:底层借助了Object.defineproperty方法提供的getter和setter。
- get函数什么时候执行?
初次读取时会执行一次。
当依赖的数据产生变化时会再次调用- 优势:与methods方法相比,内部具有缓存的机制,效率更高,调试方便
- 备注:
计算属性最终会出现在vm上,直接读取使用即可。
如果计算属性需要被修改,则必须去写set函数去响应修改,且set中要引起计算时依赖的数据发生变化。
计算属性简写(只考虑读取不考虑修改时才可以使用简写)
DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>计算属性简写title>
<script type="text/javascript" src="./vue.js">script>
<script>Vue.config.productionTip = false //阻止vue在启动时生成生产提示script>
head>
<body>
<div id="root">
姓:<input type="text" v-model="firstname"><br/><br/>
名:<input type="text" v-model="lastname"><br/><br/>
姓名:<span>{{fullName}}span><br/><br/>
div>
body>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
firstname:'张',
lastname:'三',
},
computed:{
/*
// 完整写法:
fullName:{
// get作用:当有人读取fullName时,get就会被调用,且返回值就作为fullName的值
// get什么时候调用:
// 1.初次读取fullName时
// 2.所依赖数据发生变化时
get(){
console.log('get被调用了')
return this.firstname+'-'+this.lastname
},
// set什么时候调用:
// 1.fullName被修改时
set(value){
console.log('set',value)
const arr = value.split('-')
this.firstname = arr[0]
this.lastname = arr[1]
}
}
*/
// 简写(只考虑读取不考虑修改时才可以使用简写)
fullName(){
console.log('get被调用了')
return this.firstname+'-'+this.lastname
}
}
})
script>
html>
DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>监视属性title>
<script type="text/javascript" src="./vue.js">script>
<script>Vue.config.productionTip = false //阻止vue在启动时生成生产提示script>
head>
<body>
<div id="root">
<h2>今天天气很{{info}}h2>
<button @click="changeWeather">切换天气button>
div>
body>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
isHot: true
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.isHot = !this.isHot
}
},
// watch:{
// isHot:{
// // 初始化时让handler调用一下
// immediate:true,
// // handler什么时候调用?当isHot发生改变时
// handler(newValue,oldValue){
// console.log('isHot被修改了',newValue,oldValue)
// }
// }
// }
})
vm.$watch('isHot', {
// 初始化时让handler调用一下
immediate: true,
// handler什么时候调用?当isHot发生改变时
handler(newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue)
}
}
)
script>
html>
监视属性watch:
- 当被监视的属性变化时,回调函数自动调用,进行相关操作
- 监视属性必须存在才能进行监视
- 两种写法:
(1).new Vue时传入watch配置
(2)通过vm.$watch监视
深度监视
DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>深度监视title>
<script type="text/javascript" src="./vue.js">script>
<script>Vue.config.productionTip = false //阻止vue在启动时生成生产提示script>
head>
<body>
<div id="root">
<h2>今天天气很{{info}}h2>
<button @click="changeWeather">切换天气button>
<hr/>
<h3>a的值时:{{numbers.a}}h3>
<button @click="numbers.a++">点我让a+1button>
<hr/>
<h3>b的值时:{{numbers.b}}h3>
<button @click="numbers.b++">点我让b+1button>
div>
body>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
isHot: true,
numbers:{
a:1,
b:1
}
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.isHot = !this.isHot
}
},
watch:{
isHot:{
// 初始化时让handler调用一下
// immediate:true,
// handler什么时候调用?当isHot发生改变时
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
},
// 监视多级结构中某个属性变化
// 'numbers.a':{
// handler(newValue,oldValue){
// console.log('a被修改了')
// }
// },
// 监视多级结构中所有属性的变化
numbers:{
deep:true,
handler(){
console.log('numbers改变了')
}
}
}
})
script>
html>
深度监视:
(1) Vue中的watch默认监测对象内部值的改变(一层)
(2)配置deep:true可以监测对象内部值的改变(多层)
备注:
(1)Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以
(2)使用watch时根据数据的具体结构,决定是否采用深度监视
深度监视简写
DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>深度监视title>
<script type="text/javascript" src="./vue.js">script>
<script>Vue.config.productionTip = false //阻止vue在启动时生成生产提示script>
head>
<body>
<div id="root">
<h2>今天天气很{{info}}h2>
<button @click="changeWeather">切换天气button>
div>
body>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
isHot: true,
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.isHot = !this.isHot
}
},
watch: {
// 完整写法
/*
isHot:{
// immediate:true,
// deep:true, //深度监视
// 当配置项中只有handler时可以简写
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
},
*/
// 简写
// isHot(newValue,oldValue){
// console.log('isHot被修改了',newValue,oldValue)
// }
}
})
// 正常写法
vm.$watch('isHot', {
immediate: true,
deep: true, //深度监视
// 当配置项中只有handler时可以简写
handler(newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue)
}
})
// 简写(不可以写成箭头函数)
vm.$watch('isHot',function(newValue,oldValue){
console.log('isHot被修改了', newValue, oldValue)
})
script>
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>
<script type="text/javascript" src="../js/vue.js">script>
head>
<body>
<div id="root">
姓: <input type="text" v-model="firstName"><br><br>
名: <input type="text" v-model="lastName"><br><br>
姓名: <span>{{fullName}}span>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示
new Vue({
el:"#root",
data:{
firstName:'振华中学',
lastName:'林杨',
fullName:'振华中学的林杨'
},
// methods: {
// fullname(){
// return this.firstname + '的' + this.lastname
// }
// }
watch:{
firstName(val){
setTimeout(() => {
this.fullName = val + '的' + this.lastName
},1000);
},
lastName(val){
this.fullName = this.firstName + '的' + val
}
}
})
script>
html>
computed与watch之间的区别:
- computed能完成的功能,watch都可以完成
- watch能完成的功能,computed不一定能完成。例如:watch可以进行异步操作
两个重要的小原则:
所有被Vue管理的函数,最好写成普通函数,这样this指向才是vm或者组件实例对象。
所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等)最好写成箭头函数,这样this的指向才是vm或组件实例对象。
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>
<script type="text/javascript" src="../js/vue.js">script>
<style>
.basic{
width: 400px;
height: 200px;
border: 2px solid cornflowerblue;
}
.normal{
background-color: lightsteelblue;
}
.happy{
background-color: cornflowerblue;
}
.sad{
background-color: chartreuse;
}
.sgg1{
font-size: 20px;
text-align: center;
}
.sgg2{
border-radius: 10px;
}
.sgg3{
font-style: inherit;
background-color: red;
}
style>
head>
<body>
<div id="root" >
<div class="basic" :class="mood" @click="changeMood">{{name}}>div><br><br>
<div class="basic" :class="arr" >{{name}}>div><br><br>
<div class="basic" :class="obj" >{{name}}>div><br><br>
<div class="basic" :style="style" >{{name}}>div><br><br>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示
new Vue({
el:"#root",
data:{
name:"尚硅谷",
mood:'normal',
arr:['sgg1','sgg2','sgg3'],
obj:{
sgg1:false,
sgg2:false,
sgg3:false
},
style:{
fontSize: '40px',
color: 'blue'
}
},
methods: {
changeMood(){
const arr = ['normal','happy','sad']
this.mood = arr[Math.floor(Math.random()*3)]
}
},
})
script>
html>
1.class样式
写法:class=“xxx” xxx可以是字符串、对象、数组。
对象写法适用于:要绑定多个样式,个数不确定、名字也不确定。
数组写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。2.style样式
:style="{fontSize:xxx}“其中xxx是动态的
:style=”[a,b]"其中a,b是样式对象
DOCTYPE html>
<html lang="zh-CN">
<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">
<script type="text/javascript" src="./vue.js">script>
<title>Documenttitle>
head>
<body>
<div id="root">
<h2>当前的n值时{{n}}h2>
<button @click="n++">点我n+1button>
<template v-if="n === 1">
<h2>你好h2>
<h2>尚硅谷h2>
<h2>北京h2>
template>
div>
body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
name: '尚硅谷',
n: 0
}
})
script>
html>
条件渲染:
- v-if
写法:
v-if=“表达式”
v-else-if=“表达式”
v-else=“表达式”
适用于:切换频率比较低的场景
特点:不展示的DOM元素直接被移除
注意:v-if可以和v-else-if、v-else一起使用,但结构不能被打断。- v-show
写法:v-show=“表达式”
适用于:切换频率较高的场景
特点:不能展示的DOM元素未被移除,仅仅是使用样式隐藏掉。- 备注:使用v-if时,元素可能无法获取到,而使用v-show一定可以获取到
DOCTYPE html>
<html lang="zh-CN">
<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">
<script type="text/javascript" src="./vue.js">script>
<title>基本列表title>
head>
<body>
<div id="root">
<h2>人员h2>
<ul>
<li v-for="(p,index) in persons" :key="index">
{{p.name}}-{{p.age}}
li>
ul>
<h2>汽车信息h2>
<ul>
<li v-for="(value,key) in car" :key="key">
{{key}}-{{value}}
li>
ul>
<h2>字符串信息h2>
<ul>
<li v-for="(value,key) in str" :key="key">
{{key}}-{{value}}
li>
ul>
<h2>字符串信息h2>
<ul>
<li v-for="(number,index) in 10" :key="index">
{{index}}-{{number}}
li>
ul>
div>
body>
<script type="text/javascript">
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'
}
})
script>
html>
v-for指令:
- 用于展示列表数据
- 语法:v-for=“(item,index) in xxx” :key=“yyy”
- 可遍历:数组、对象、字符串(少)、指定次数(少)
DOCTYPE html>
<html lang="zh-CN">
<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">
<script type="text/javascript" src="./vue.js">script>
<title>key原理title>
head>
<body>
<div id="root">
<h2>人员h2>
<button @click.once="add">添加一个王二麻子button>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}
<input type="text">
li>
ul>
div>
body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
persons:[
{id:'001',name:'张三',age:18},
{id:'002',name:'李四',age:19},
{id:'003',name:'王五',age:20},
]
},
methods:{
add(){
const p = {id:'004',name:'王二麻子',age:21}
this.persons.unshift(p)
}
}
})
script>
html>
面试题:react、vue中的key有什么作用?(key的内部原理)
虚拟DOM中key的作用:key是虚拟DOM中对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
对比规则:
旧虚拟DOM中找到了与新虚拟DOM相同的key:
- 若虚拟DOM中内容没变, 直接使用之前的真实DOM
- 若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
- 旧虚拟DOM中未找到与新虚拟DOM相同的key:创建新的真实DOM,随后渲染到到页面
用index作为key可能会引发的问题:
若对数据进行逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ——> 界面效果没问题, 但效率低
若结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题 开发中如何选择key?最好使用每条数据的唯一标识作为key,比如id、手机号、身份证号、学号等唯一值
如果不存在对数据的逆序添加、逆序删除等破坏顺序的操作,仅用于渲染列表,使用index作为key是没有问题的
DOCTYPE html>
<html lang="zh-CN">
<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">
<script type="text/javascript" src="./vue.js">script>
<title>列表过滤title>
head>
<body>
<div id="root">
<h2>人员h2>
<input type="text" placeholder="请输入名字:" v-model="keyWord">
<ul>
<li v-for="(p,index) in filPersons" :key="index">
{{p.name}}-{{p.age}}-{{p.sex}}
li>
ul>
div>
body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
keyWord: '',
persons: [
{ id: '001', name: '马冬梅', age: 18, sex: '女' },
{ id: '002', name: '周冬雨', age: 19, sex: '女' },
{ id: '003', name: '周杰伦', age: 20, sex: '男' },
{ id: '004', name: '温兆伦', age: 21, sex: '男' },
],
filPersons: []
},
watch: {
keyWord:{
immediate:true,
handler(val) {
this.filPersons = this.persons.filter((p) => {
return p.name.indexOf(val) !== -1
})
}
}
}
})
script>
html>
DOCTYPE html>
<html lang="zh-CN">
<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">
<script type="text/javascript" src="./vue.js">script>
<title>列表过滤title>
head>
<body>
<div id="root">
<h2>人员h2>
<input type="text" placeholder="请输入名字:" v-model="keyWord">
<ul>
<li v-for="(p,index) in filPersons" :key="index">
{{p.name}}-{{p.age}}-{{p.sex}}
li>
ul>
div>
body>
<script type="text/javascript">
//用watch实现
//#region
/*
new Vue({
el: '#root',
data: {
keyWord: '',
persons: [
{ id: '001', name: '马冬梅', age: 18, sex: '女' },
{ id: '002', name: '周冬雨', age: 19, sex: '女' },
{ id: '003', name: '周杰伦', age: 20, sex: '男' },
{ id: '004', name: '温兆伦', age: 21, sex: '男' },
],
filPersons: []
},
watch: {
keyWord:{
immediate:true,
handler(val) {
this.filPersons = this.persons.filter((p) => {
return p.name.indexOf(val) !== -1
})
}
}
}
})
*/
//#endregion
// 用computed实现
new Vue({
el: '#root',
data: {
keyWord: '',
persons: [
{ id: '001', name: '马冬梅', age: 18, sex: '女' },
{ id: '002', name: '周冬雨', age: 19, sex: '女' },
{ id: '003', name: '周杰伦', age: 20, sex: '男' },
{ id: '004', name: '温兆伦', age: 21, sex: '男' },
]
},
computed:{
filPersons(){
return this.filPersons = this.persons.filter((p)=>{
return p.name.indexOf(this.keyWord) !== -1
})
}
}
})
script>
html>
DOCTYPE html>
<html lang="zh-CN">
<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">
<script type="text/javascript" src="./vue.js">script>
<title>列表排序title>
head>
<body>
<div id="root">
<h2>人员h2>
<input type="text" placeholder="请输入名字:" v-model="keyWord">
<button @click="sortType = 2">年龄升序button>
<button @click="sortType = 1">年龄降序button>
<button @click="sortType = 0">原顺序button>
<ul>
<li v-for="(p,index) in filPersons" :key="index">
{{p.name}}-{{p.age}}-{{p.sex}}
li>
ul>
div>
body>
<script type="text/javascript">
// 用computed实现
new Vue({
el: '#root',
data: {
keyWord: '',
sortType:1, //0原顺序,1降序,2升序
persons: [
{ id: '001', name: '马冬梅', age: 18, sex: '女' },
{ id: '002', name: '周冬雨', age: 30, sex: '女' },
{ id: '003', name: '周杰伦', age: 20, sex: '男' },
{ id: '004', name: '温兆伦', age: 50, sex: '男' },
]
},
computed:{
filPersons(){
const arr = this.persons.filter((p)=>{
return p.name.indexOf(this.keyWord) !== -1
})
// 判断是否排序
if(this.sortType){
arr.sort((a,b)=>{
return this.sortType == 1 ? b.age-a.age : a.age-b.age
})
}
return arr
}
}
})
script>
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>
<script type="text/javascript" src="../js/vue.js">script>
head>
<body>
<div id="root">
<h1>用户信息h1>
<button @click="student.age++">年龄加1岁button>
<button @click="addSex">添加性别属性,默认值为男button>
<button @click.once="addFriend">在列表首位添加一个朋友button>
<button @click="changeFirstFriendName">修改第一个朋友的名字为赵六button>
<button @click.once="addHobby">添加一个爱好button>
<button @click="changeFirstHobby">修改第一个爱好为练字button>
<h3>姓名:{{student.name}}h3>
<h3>年龄:{{student.age}}h3>
<h3 v-if="student.sex">性别:{{student.sex}}h3>
<h3>爱好:h3>
<ul>
<li v-for="(h,index) in student.hobbies" :key="index">
{{h}}
li>
ul>
<h3>朋友们:h3>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}}--{{f.age}}
li>
ul>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示
new Vue({
el:"#root",
data:{
name:"振华中学林杨",
student:{
name:'张三',
age:18,
hobbies:['打游戏','敲代码','打篮球'],
friends:[
{name:'李四',age:19},
{name:'王五',age:20}
]
}
},
methods:{
addSex(){
Vue.set(this.student,'sex','男')
//this.$set(this.student,'sex','男')
},
addFriend(){
this.student.friends.unshift({name:'玉祁',age:21})
},
changeFirstFriendName(){
this.student.friends[0].name = '赵六'
},
addHobby(){
this.student.hobbies.push('听歌')
},
changeFirstHobby(){
// this.student.hobbies.splice(0,1,'练字')
Vue.set(this.student.hobby,0,'练字s')
}
}
})
script>
html>
Vue数据监测的原理
Vue会监视data中所有层次的数据。
如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。对象中后追加的属性,Vue默认不做响应式处理。 如需给后添加的属性做响应式,请使用如下API: Vue.set(target,prpertyName/index,value)或 vm.$set(target,prpertyName/index,value)
如何监测数组中的数据?
通过包裹数组更新元素的方式实现,本质是:调用原生对应的方法对数组进行更新。 重新解析模板,进而更新页面。
在Vue修改数组中的某个元素时一定要使用如下方法:
使用API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
Vue.set或者vm.$set注意:Vue.set 和 vm.$set不能给vm或vm的根数据对象添加属性
DOCTYPE html>
<html lang="zh-CN">
<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">
<script type="text/javascript" src="./vue.js">script>
<title>收集表单数据title>
head>
<body>
<div id="root">
<form @submit="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="guangzhou">广州option>
<option value="shenzhen">深圳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>
body>
<script type="text/javascript">
// 用computed实现
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));
}
}
})
script>
html>
收集表单数据:
若:<input type="text"/> 则v-model收集的是value值,用户输入的就是value值
若:<input type="radio"/> 则v-model收集的是value值,且要给标签配置value值
若:<input type="checkbox"/>
1、没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
2、配置input的value属性:
(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
(2)v-model的初始值是数组,那么收集的就是value组成的数组
备注:v-model的三个修饰符:
lazy:失去焦点再收集数据
number: 输入字符串转为有效的数字
trim: 输入首尾空格过滤
过滤器:
定义:对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)。
语法:
1. 注册过滤器:Vue.filter(name,callback) 或 new Vue{filters:{}}
2. 使用过滤器:{{xxx | 过滤器名}} 或 v-bind:属性 = “xxx | 过滤器名”备注:
1. 过滤器也可以接收额外参数,多个过滤器能够串联。
2. 并没有改变原本的数据,是产生新的对应的数据。
v-bind:单向绑定解析表达式,可简写为:
v-model:双向数据绑定
v-for:遍历数组 / 对象 / 字符串
v-on:绑定事件监听,可简写为@
v-if:条件渲染(动态控制节点是否存存在)
v-else:条件渲染(动态控制节点是否存存在)
v-show:条件渲染 (动态控制节点是否展示)
v-text:向其所在的节点中渲染文本内容
与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会
v-html指令:向指定节点中渲染包含html结构的内容
与插值语法的区别:
1. v-html会替换掉节点中所有的内容,{{xx}}则不会
2. v-html可以识别html结构
严重注意:v-html有安全性问题!!!在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击!!!一定要在可信的内容上使用v-html,永远不要用在用户提交的内容上!!!
v-cloak指令(没有值):
本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性
使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题
v-once指令:v-once所在节点在初次动态渲染后,就视为静态内容了,以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能
v-pre指令:跳过其所在节点的编译过程
可利用它跳过没有使用指令语法、没有使用插值语法的节点,会加快编译
DOCTYPE html>
<html lang="zh-CN">
<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">
<script type="text/javascript" src="./js/vue.js">script>
<title>v-texttitle>
head>
<body>
<div id="root">
<div>{{name}}div>
<div v-text="name">div>
<div v-text="str">div>
div>
body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
name:'尚硅谷',
str:'你好啊!'
},
})
script>
html>
DOCTYPE html>
<html lang="zh-CN">
<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">
<script type="text/javascript" src="./js/vue.js">script>
<title>v-htmltitle>
head>
<body>
<div id="root">
<div>{{name}}div>
<div v-html="str">div>
<div v-html="str2">div>
div>
body>
<script type="text/javascript">
new Vue({
el: '#root',
data: {
name:'尚硅谷',
str:'你好啊!'
,
str2:'August always safe!',
},
})
script>
html>
DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-cloaktitle>
<style>
[v-cloak]{
display:none;
}
style>
head>
<body>
<div id="root">
<h2 v-cloak>{{name}}h2>
div>
<script type="text/javascript" src="../js/vue.js">script>
body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
name:'尚硅谷'
}
})
script>
html>
DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-oncetitle>
head>
<body>
<div id="root">
<h2 v-once>初始化的n值是:{{n}}h2>
<h2>当前n值是:{{n}}h2>
<button @click="n++">点我n+1button>
div>
<script type="text/javascript" src="./js/vue.js">script>
body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
n:1
}
})
script>
html>
DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-pretitle>
head>
<body>
<div id="root">
<h2 v-pre>Vue其实很简单h2>
<h2>当前n值是:{{n}}h2>
<button @click="n++">点我n+1button>
div>
<script type="text/javascript" src="./js/vue.js">script>
body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
n:1
}
})
script>
html>
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>当前的n值是:<span v-text="n">span> h2>
<h2>放大10倍后的n值是:<span v-big="n">span> h2>
<button @click="n++">点我n+1button>
<hr/>
<input type="text" v-fbind:value="n">
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
n:1
},
directives:{
//big函数何时会被调用?1.指令与元素成功绑定时(一上来) 2.指令所在的模板被重新解析时
big(element,binding){
console.log('big',this) //注意此处的this是window
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
}
}
}
})
script>
html>
自定义指令定义语法:
一、定义语法:
(1) 局部指令:new Vue({ directives:{指令名:配置对象} })
new Vue({ directives:{指令名:回调函数} })(2) 全局指令:
Vue.directive(指令名,配置对象) Vue.directive(指令名,回调函数)二、配置对象中常用的3个回调函数:
(1) bind(element,binding):指令与元素成功绑定时调用
(2) inserted(element,binding):指令所在元素被插入页面时调用
(3) update(element,binding):指令所在模板结构被重新解析时调用
三、备注:
DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>生命周期title>
<script type="text/javascript" src="./js/vue.js">script>
head>
<body>
<div id="root" :x="n">
<h2 v-text="n">h2>
<h2>当前的n值是:{{n}}h2>
<button @click="add">点我n+1button>
<button @click="bye">点我销毁vmbutton>
div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: '#root',
// template: `
//
// 当前的n值是:{{n}}
//
// `,
data: {
n:1,
},
methods: {
add(){
console.log('add')
this.n++
},
bye(){
console.log('bye')
this.$destroy()
}
},
beforeCreate(){
console.log('beforeCreate')
},
created(){
console.log('created')
},
beforeMount(){
console.log('beforeMount')
},
mounted(){
console.log('mounted')
},
beforeUpdate(){
console.log('beforeUpdate')
},
updated(){
console.log('updated')
},
beforeDestroy(){
console.log('beforeDestroy')
},
destroyed(){
console.log('destroyed')
}
})
script>
body>
html>
常见生命周期钩子:
mounted:发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。
beforeDestroy:清除定时器,解绑在定义事件、取消订阅消息等【收尾工作】。
关于销毁Vue实例:
1. 销毁后借助Vue开发者工具看不到任何信息。
2. 销毁后自定义事件会失效,但原生DOM依然有效。
3. 一般不会在beforeDestroy操作数据,因为此时操作了数据也不会再触发更新流程了。
4. 总结:生命周期钩子共有八个,包括beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroy。
非单文件组件:一个文件中包含有n个组件
单文件组件:一个文件中包含有1个组件
DOCTYPE html>
<html lang="zh-CN">
<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>
<script type="text/javascript" src="./js/vue.js">script>
head>
<body>
<div id="root">
<h1>{{msg}}h1>
<hr>
<school>school>
<hr>
<student>student>
<hello>hello>
<hr>
div>
<div id="root2">
<hello>hello>
div>
body>
<script>
// 创建school组件
const school = Vue.extend({
template:
`
学校名称:{{schoolName}}
学校地址:{{address}}
`,
// el:'#root', 一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器
data() {
return {
schoolName: '尚硅谷',
address: '广东广州'
}
},
methods:{
showName(){
alert(this.schoolName)
}
}
})
// 创建student组件
const student = Vue.extend({
template:
`
学校姓名:{{studentName}}
学校年龄:{{age}}
`,
data() {
return {
studentName: '张三',
age: 18
}
}
})
const hello = Vue.extend({
template:`
你哈有
`,
data(){
return {
name:'Tom'
}
}
})
// 全局注册
Vue.component('hello',hello)
// 创建vm
new Vue({
el: '#root',
data: {
msg: '你好呀',
},
// 注册组件(局部注册)
components: {
school,
student
}
})
new Vue({
el: '#root2'
})
script>
html>
Vue使用组件的三大步骤:
一、定义组件(创建组件)
二、注册组件
三、使用组件(写组件标签)
一、如何定义一个组件?
使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但区别如下:
(1)el不要写。因为最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器
(2)data必须写成函数。避免组件被复用时,数据存在引用关系。
备注:使用template可以配置组件结构
二、如何注册组件?
局部注册:靠new Vue的时候传入components选项
全局注册:靠Vue,component(‘组件名’,组件)
三、编写组件标签
<组件名>组件名>
注意
1.关于组件名:
一个单词组成:
第一种写法(首字母小写):study
第二种写法(首字母大写):Study
多个单词组成:
第一种写法(Kebab-case命名):my-study
第二种写法(CamelCase命名):MyStudy(需要Vue脚手架支持)
备注:
组件名尽可能回避HTML中已有的元素名。例如:h2、H2等。
可以使用name配置项指定组件周期开发者工具中呈现的名字。
2.关于组件标签:
第一种写法:<study><study>
第二种写法:<study/>
备注:不使用脚手架时,<study/>会导致后续组件不能渲染。
3.一个简写方式:
const study = Vue.extend(options) ==> const study = options
DOCTYPE html>
<html lang="zh-CN">
<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>
<script type="text/javascript" src="./js/vue.js">script>
head>
<body>
<div id="root">
<app>app>
div>
body>
<script>
const student = Vue.extend({
template:
`
学生姓名:{{studentName}}
学生年龄:{{age}}
`,
// el:'#root', 一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器
data() {
return {
studentName: '晓谷',
age:18
}
}
})
const school = Vue.extend({
template:
`
学校名称:{{schoolName}}
学校地址:{{address}}
`,
data() {
return {
schoolName: '尚硅谷',
address: '广东广州'
}
},
// 注册组件(局部注册)
components:{
student
}
})
const hello = Vue.extend({
template:`{{msg}}
`,
data(){
return {
msg:'欢迎来到振华中学'
}
}
})
const app = Vue.extend({
template:`
`,
components:{
school,
hello
}
})
new Vue({
el: '#root',
// 注册组件(局部注册)
components: {
app
}
})
script>
html>
关于VueComponent:
- school组件本质是一个名为VueComponent的构造函数,且不是程序定义的,是Vue.extend生成的。
- 我们只需要写,Vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)
- 特别注意:每次调用Vue.extend,返回的都是一个全新的VueComponent!!!
- 关于this指向:
(1)组件配置中:
data函数,methods中的函数,watch中的函数,computed中的函数,它们的this均是[VueComponent实例对象]
(2)new Vue(options)配置中:
data函数,methods中的函数,watch中的函数,computed中的函数,它们的this均是[Vue实例对象]- VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。
Vue的实例对象,以后简称vm1.一个重要的内置关系:
VueComponent.prototype.proto===Vue.protptype2.为什么要有这个关系:
让组件实例对象(vc)可以访问到Vue原型上的属性,方法。
写法:
<template>
template>
<script>
// 组件交互相关的代码(数据、方法)
script>
<style>
style>
main.js
// 该文件是项目入口文件
// 引入Vue
import Vue from 'vue'
// 引入App组件,是所有组件的父组件
import App from './App.vue'
// 关闭Vue生产提示
Vue.config.productionTip = false
// 创建Vue实例对象——vm
new Vue({
// 将App组件放入容器
render: h => h(App),
}).$mount('#app')
**ref属性**
1. 被用来给元素或子组件注册引用信息(id的替代者)
2. 应用在html标签上获取的是真实DOM元素,应用在组件标签上是组件实例对象(vc)
3. 使用方式:
打标识:<h1 ref="xxx">.....</h1> 或 <School ref="xxx"></School>
获取:this.$refs.xxx
<template>
<div>
<h1 v-text="msg" ref="title">h1>
<button ref="btn" @click="showDOM">点我输出上方dom元素button>
<School ref="sch">School>
div>
template>
<script>
// 引入组件
import School from './components/School.vue'
export default {
name: 'App',
data(){
return {
msg: '欢迎学习Vue'
}
},
components: {
School
},
methods: {
showDOM(){
console.log(this.$refs.title) //真实dom元素
console.log(this.$refs.btn) //真实dom元素
console.log(this.$refs.sch) //School组建的实例对象vc
}
}
}
script>
<style>style>
功能:让组件接收外部传过来的数据
传递数据:
接收数据:
第一种方式(只接收):props:[‘name’]
第二种方式(限制类型):props:{name:String}
第三种方式(限制类型、限制必要性、指定默认值):
props:{
name:{
type:String, //类型
required:true, //必要性
default:‘老王’ //默认值
}
}
备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。
<template>
<div class="demo">
<h1>{{ msg }}h1>
<h2>学生姓名:{{ studentName }}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++
}
},
// 简单接受
// props:['studentName,'age','sex']
/*
props:{
studentName:String,
age:Number,
sex:String
}
*/
// 接收时同时对数据进行类型限制+默认值的制定+必要性的限制
props: {
studentName: {
type: String,
required: true
},
age: {
type: Number,
default: 99
},
sex: {
type: String,
required: true
}
}
}
script>
功能:可以把多个组件共用的配置提取成一个混入对象
使用方式:
第一步,定义混合:
{
data(){....},
methods:{....}
....
}
export const hunhe = {
methods: {
showName(){
alert(this.name)
}
},
mounted() {
console.log('你好啊!')
},
}
export const hunhe2 = {
data() {
return {
x:100,
y:200
}
},
}
第二步,使用混入:
全局混入:Vue.mixin(xxx)
局部混入:mixins:[‘xxx’]
本质:包含install方法的一个对象,install的第一个参数是Vue,第二个以后的参数是插件使用者传递的数据。
定义插件:
对象.install = function (Vue, options) {
// 1. 添加全局过滤器
Vue.filter(....)
// 2. 添加全局指令
Vue.directive(....)
// 3. 配置全局混入(合)
Vue.mixin(....)
// 4. 添加实例方法
Vue.prototype.$myMethod = function () {...}
Vue.prototype.$myProperty = xxxx
}
使用插件:Vue.use()
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原型上添加一个方法(vm和vc就都能用了)
Vue.prototype.hello = ()=>{alert('你好啊')}
}
}
Vue.prototype.hello = ()=>{alert('你好啊')}
}
}
作用:让样式在局部生效,防止冲突
写法:< style scoped >
Item.vue
<template>
<li>
<label>
<input type="checkbox" />
<span>xxxxx</span>
</label>
<button class="btn btn-danger" style="display:none">删除</button>
</li>
</template>
<script>
export default {
name: 'Item',
}
</script>
<style scoped>
/*item*/
li {
list-style: none;
height: 36px;
line-height: 36px;
padding: 0 5px;
border-bottom: 1px solid #ddd;
}
li label {
float: left;
cursor: pointer;
}
li label li input {
vertical-align: middle;
margin-right: 6px;
position: relative;
top: -1px;
}
li button {
float: right;
display: none;
margin-top: 3px;
}
li:before {
content: initial;
}
li:last-child {
border-bottom: none;
}
</style>
List.vue
<template>
<ul class="todo-main">
<Item></Item>
<Item></Item>
<Item></Item>
<Item></Item>
</ul>
</template>
<script>
import Item from '../components/Item.vue'
export default {
name: 'List',
components: { Item },
}
</script>
<style scoped>
/*main*/
.todo-main {
margin-left: 0px;
border: 1px solid #ddd;
border-radius: 2px;
padding: 0px;
}
.todo-empty {
height: 40px;
line-height: 40px;
border: 1px solid #ddd;
border-radius: 2px;
padding-left: 5px;
margin-top: 10px;
}
</style>
MyFooter.vue
<template>
<div class="todo-footer">
<label>
<input type="checkbox" />
</label>
<span>
<span>已完成0</span> / 全部2
</span>
<button class="btn btn-danger">清除已完成任务</button>
</div>
</template>
<script>
export default {
name: 'MyFooter',
}
</script>
<style scoped>
/*footer*/
.todo-footer {
height: 40px;
line-height: 40px;
padding-left: 6px;
margin-top: 5px;
}
.todo-footer label {
display: inline-block;
margin-right: 20px;
cursor: pointer;
}
.todo-footer label input {
position: relative;
top: -1px;
vertical-align: middle;
margin-right: 5px;
}
.todo-footer button {
float: right;
margin-top: 5px;
}
</style>
MyHeader.vue
<template>
<div class="todo-header">
<input type="text" placeholder="请输入你的任务名称,按回车键确认" />
</div>
</template>
<script>
export default {
name: 'MyHeader',
}
</script>
<style scoped>
/*header*/
.todo-header input {
width: 560px;
height: 28px;
font-size: 14px;
border: 1px solid #ccc;
border-radius: 4px;
padding: 4px 7px;
}
.todo-header input:focus {
outline: none;
border-color: rgba(82, 168, 236, 0.8);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
}
</style>
App.vue
<template>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<MyHeader></MyHeader>
<List></List>
<MyFooter></MyFooter>
</div>
</div>
</div>
</template>
<script>
// 引入组件
import MyHeader from './components/MyHeader.vue'
import List from './components/List.vue'
import MyFooter from './components/MyFooter.vue'
export default {
name: 'App',
components: {
MyHeader,
List,
MyFooter
}
}
</script>
<style>
/*base*/
body {
background: #fff;
}
.btn {
display: inline-block;
padding: 4px 12px;
margin-bottom: 0;
font-size: 14px;
line-height: 20px;
text-align: center;
vertical-align: middle;
cursor: pointer;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
border-radius: 4px;
}
.btn-danger {
color: #fff;
background-color: #da4f49;
border: 1px solid #bd362f;
}
.btn-danger:hover {
color: #fff;
background-color: #bd362f;
}
.btn:focus {
outline: none;
}
.todo-container {
width: 600px;
margin: 0 auto;
}
.todo-container .todo-wrap {
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
}
</style>
TodoList.vue
<template>
<div>
<ul class="todo-main">
<TodoItem v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj"></TodoItem>
</ul>
</div>
</template>
<script>
import TodoItem from "@/components/TodoItem";
export default {
name: "TodoList",
components:{
TodoItem
},
data(){
return {
todos:[
{id:'001',title:'ielts',done:true},
{id:'002',title:'meal',done:true},
{id:'003',title:'Vue',done:true}
]
}
}
}
</script>
TodoItem.vue
<template>
<li>
<label>
<input type="checkbox" :checked="false"/>
<span>{{todo.title}}</span>
</label>
<button class="btn btn-danger" style="display:none">删除</button>
</li>
</template>
<script>
export default {
name: "TodoItem",
//接收todo对象
props:['todo'],
}
</script>
TodoHeader.vue
<template>
<div class="todo-header">
<input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/>
</div>
</template>
<script>
import {nanoid} from 'nanoid' //需安装 npm i nanoid
export default {
props:['addTodo'], //父亲给儿子传一个函数
name: "TodoHeader",
data(){
return {title: ''}
},
methods:{
add(){
if (this.title.trim() ===''){ return alert("不能输入空值"); }
// const text = event.target.value; //通过传入事件对象获取
const todoObj = {id:nanoid(),title:this.title,done:false};
this.addTodo(todoObj); //儿子调用传递参数
this.title='';//输入完之后清空
// event.target.value=''; //输入完之后清空
}
}
}
</script>
显示删除按钮
<!--去掉display-->
<button class="btn btn-danger" >删除</button>
<style scoped>
/*添加选中变灰*/
li:hover{
background-color: #cccccc;
}
li:hover button{
display: block;
}
</style>
App.vue
<TodoList :todos="todos" :updateTodo="updateTodo" :deleteTodo="deleteTodo"></TodoList>
deleteTodo(id){
this.todos = this.todos.filter((todo)=>{
return todo.id !==id
})
// this.todos = this.todos.filter( todo=> todo.id !==id) //简写
}
TodoList.vue
<TodoItem v-for="todoObj in todos"
:key="todoObj.id" :todo="todoObj"
:updateTodo="updateTodo"
:deleteTodo="deleteTodo"></TodoItem>
props:['todos','updateTodo','deleteTodo'],
TodoItem.vue
<button class="btn btn-danger" @click="deleteItem(todo.id)">删除</button>
props:['todo','updateTodo','deleteTodo'],
methods:{
deleteItem(id){
if (confirm('确定删除?')){ //点确定为真, 取消为假
this.deleteTodo(id);
}
}
}
App.vue, App传todos到footer
<TodoFooter :todos="todos"></TodoFooter>
TodoFooter.vue
<template>
<div class="todo-footer">
<label>
<input type="checkbox"/>
</label>
<span>
<span>已完成{{todoCount}}</span> / 全部{{todos.length}}
</span>
<button class="btn btn-danger">清除已完成任务</button>
</div>
</template>
<script>
export default {
props:['todos'],
name: "TodoFooter",
computed:{
todoCount(){
return this.todos.reduce((pre,todo)=>{
return pre + (todo.done ? 1 : 0); //如果勾选了就+1
},0)
// this.todos.reduce((pre,todo) => pre + (todo.done ? 1 : 0),0) //简写
/* forEach方式
let i = 0;
this.todos.forEach((todo)=>{
if (todo.done){
i++
}
});
return i;*/
/* filter方法
return this.todos.filter((todo)=>{
return todo.done !== false;
}).length*/
}
}
}
</script>
reduce方法
this.todos.reduce((pre,todo)=>{
return pre + (todo.done ? 1 : 0); //如果勾选了就+1
},0)
// this.todos.reduce((pre,todo) => pre + (todo.done ? 1 : 0),0) //简写
/*
this.todos.reduce((pre,current)=>{},n) ,
调用次数= 数组长度
pre : 上一次调用函数的返回值
current: 数组里的每个对象, 这是是todo
n:统计的初始值
*/
App.vue
<template>
<div>
<div id="root">
<div class="todo-container">
<div class="todo-wrap">
<TdHeader :addTodo="addTodo"></TdHeader>
<!-- 传递给item需要通过list传 -->
<TodoList :todos="todos" :updateTodo="updateTodo" :deleteTodo="deleteTodo"></TodoList>
<TodoFooter :todos="todos" :isAllTotal="isAllTotal" :clearAllTodo="clearAllTodo"></TodoFooter>
</div>
</div>
</div>
</div>
</template>
<script>
//引入组件
import TodoFooter from "@/components/TodoFooter";
import TdHeader from "@/components/TodoHeader";
import TodoList from "@/components/TodoList";
export default {
name:'App',
components:{
TdHeader,TodoList,TodoFooter
},
data() {
return {
todos: [
{id: '001', title: '吃饭', done: false},
{id: '002', title: '睡觉', done: true},
{id: '003', title: '开车', done: true}
]
}
},
methods:{
//添加待办项
addTodo(todoObj){ //接收儿子传递的参数
this.todos.unshift(todoObj);
},
//修改待办项状态, done
updateTodo(id){
this.todos.forEach((todo)=>{
if (todo.id === id){
todo.done = !todo.done;
}
});
},
//删除待办项
deleteTodo(id){
this.todos = this.todos.filter((todo)=>{
return todo.id !==id
})
// this.todos = this.todos.filter( todo=> todo.id !==id) //简写
},
//修改待办项状态
isAllTotal(done){
this.todos.forEach((todo)=>{
todo.done = done
})
},
//清除已完成的事项
clearAllTodo(){
this.todos = this.todos.filter((todo)=>{
return !todo.done
})
}
}
}
</script>
TodoFooter.vue
<template>
<div class="todo-footer" v-show="total">
<label>
<input type="checkbox" v-model="isAll"/>
</label>
<span>
<span>已完成{{todoCount}}</span> / 全部{{total}}
</span>
<button class="btn btn-danger" @click="clearTodo">清除已完成任务</button>
</div>
</template>
<script>
export default {
props:['todos','isAllTotal','clearAllTodo'],
name: "TodoFooter",
computed:{
total(){
return this.todos.length
},
isAll:{
get(){
return this.todoCount === this.total && this.total >0;
},
set(value){
this.isAllTotal(value)
}
},
todoCount(){
return this.todos.reduce((pre,todo)=>{
return pre + (todo.done ? 1 : 0); //如果勾选了就+1
},0)
// this.todos.reduce((pre,todo) => pre + (todo.done ? 1 : 0),0) //简写
/*
this.todos.reduce((pre,current)=>{},n) ,
调用次数= 数组长度
pre : 上一次调用函数的返回值
current: 数组里的每个对象, 这是是todo
n:统计的初始值
*/
/* forEach方式
let i = 0;
this.todos.forEach((todo)=>{
if (todo.done){
i++
}
});
return i;*/
/* filter方法
return this.todos.filter((todo)=>{
return todo.done !== false;
}).length*/
}
},
methods:{
clearTodo(){
if (this.todoCount<=0){ //没有勾选就不执行
return
}
if (confirm('是否清除?')){
this.clearAllTodo();
}
}
}
}
</script>
- 组件化编码流程:
(1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突。
(2).实现动态组件:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用:
(1).一个组件在用:放在组件自身即可。
(2). 一些组件在用:放在他们共同的父组件上(状态提升)。
(3).实现交互:从绑定事件开始。
- props适用于:
(1).父组件 ==> 子组件 通信
(2).子组件 ==> 父组件 通信(要求父先给子一个函数)
使用v-model时要切记:v-model绑定的值不能是props传过来的值,因为props是不可以修改的!
props传过来的若是对象类型的值,修改对象中的属性时Vue不会报错,但不推荐这样做。
localStroage
<!DOCTYPE html>
<html lang="zh-CN">
<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>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:'zs',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('msg')
}
function deleteAllData(){
localStorage.clear()
}
</script>
</body>
</html>
浏览器关闭数据仍不会消失,5M容量, 一直存在, 除非用户清空缓存, 或调用api清除
sessionStorage
<!DOCTYPE html>
<html lang="zh-CN">
<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>sessionStorage</title>
</head>
<body>
<h2>sessionStorage</h2>
<button onclick="saveData()">点我保存数据</button>
<button onclick="readData()">点我读取数据</button>
<button onclick="deleteData()">点我删除数据</button>
<button onclick="deleteAllData()">点我全部删除数据</button>
<script type="text/javascript">
let p = {name:'zs',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('msg')
}
function deleteAllData(){
sessionStorage.clear()
}
</script>
</body>
</html>
会话结束就清空, 关闭浏览器清空
webStorage
- 存储内容大小一般支持5MB左右(不同浏览器可能还不一样)
- 浏览器端通过 Window.sessionStorage 和 Window.localStorage 属性来实现本地存储机制。
- 相关API:
(1)xxxxxStorage.setItem(‘key’, ‘value’); 该方法接受一个键和值作为参数,会把键值对添加到存储中,如果键名存在,则更新其对应的值。
(2)xxxxxStorage.getItem(‘person’); 该方法接受一个键名作为参数,返回键名对应的值。
(3)xxxxxStorage.removeItem(‘key’); 该方法接受一个键名作为参数,并把该键名从存储中删除。
(4)xxxxxStorage.clear() 该方法会清空存储中的所有数据。备注:
- SessionStorage存储的内容会随着浏览器窗口关闭而消失。
- LocalStorage存储的内容,需要手动清除才会消失。
- xxxxxStorage.getItem(xxx)如果xxx对应的value获取不到,那么getItem的返回值是null。
- JSON.parse(null)的结果依然是null。
将Todo案例改成浏览器存储
<script>
export default {
name:'App',
components:{
},
data() {
return {
todos: JSON.parse(localStorage.getItem('todos')) || [] //到浏览器查找, 找不到为空数组[]
}
},
methods:{
//添加待办项
},
watch:{
todos:{
deep:true,//开启深度检测, 不然发现不了done改变
handler(newValue){
localStorage.setItem('todos',JSON.stringify(newValue));//检测到有修改就把新的todos更新到localStorage
/* if (this.todos.length<=0){
localStorage.removeItem('todos')
}*/
}
}
}
}
</script>
School.vue
<template>
<div class="school">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="sendSchoolName">把学校名给App</button>
</div>
</template>
<script>
export default {
name:'School',
props:['getSchoolName'],
data(){
return {
name:'尚硅谷',
address:'广州'
}
},
methods:{
sendSchoolName(){
this.getSchoolName(this.name)
}
}
}
</script>
<style scoped>
.school {
background-color: skyblue;
padding: 5px;
}
</style>
Student.vue
<template>
<div class="student">
<h2>学生姓名:{{name}}</h2>
<h2>学生年龄:{{age}}</h2>
<button @click="sendStudentName">把学生名给App</button>
</div>
</template>
<script>
export default {
name:'Student',
data(){
return {
name:'余周周',
age:18
}
},
methods:{
sendStudentName(){
// 触发Student组件实例身上的atguigu事件
this.$emit('atguigu',this.name,666,888,900)
}
}
}
</script>
<style scoped>
.student {
background-color: orange;
padding: 5px;
margin-top: 30px;
}
</style>
App.vue
<template>
<div class="app">
<h1>{{ msg }}</h1>
<!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 -->
<School :getSchoolName="getSchoolName"/>
<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法:使用@/v-on) -->
<Student @atguigu.once="getStudentName"/>
<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法:使用ref) -->
<!-- <Student ref="student"/> -->
</div>
</template>
<script>
// 引入组件
import School from './components/School.vue'
import Student from './components/Student.vue'
export default {
name: 'App',
components: {
School,
Student
},
data() {
return {
msg: '你好呀'
}
},
methods: {
getSchoolName(name){
console.log('App收到了学校名:',name)
},
getStudentName(name,...params){
console.log('App收到了学生名:',name,params)
}
},
mounted(){
setTimeout(()=>{
// this.$refs.student.$on('atguigu',this.getStudentName)
// this.$refs.student.$once('atguigu',this.getStudentName)//只触发一次
},3000)
}
}
</script>
<style>
.app {
background-color: gray;
padding: 5px;
}
</style>
Student.vue
<template>
<div class="student">
<h2>学生姓名:{{name}}</h2>
<h2>学生年龄:{{age}}</h2>
<button @click="sendStudentName">把学生名给App</button>
<button @click="unbind">解绑atguigu事件</button>
</div>
</template>
<script>
export default {
name:'Student',
data(){
return {
name:'余周周',
age:18
}
},
methods:{
sendStudentName(){
// 触发Student组件实例身上的atguigu事件
this.$emit('atguigu',this.name,666,888,900)
this.$emit('demo')
},
unbind(){
// 解绑一个自定义事件
// this.$off('atguigu')
// 解绑多个自定义事件
// this.$off(['atguigu','demo'])
// 所有自定义事件都被解绑
this.$off()
}
}
}
</script>
<style scoped>
.student {
background-color: orange;
padding: 5px;
margin-top: 30px;
}
</style>
App.vue
<template>
<div class="app">
<h1>{{ msg }}</h1>
<!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 -->
<School :getSchoolName="getSchoolName"/>
<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法:使用@/v-on) -->
<Student @atguigu.once="getStudentName" @demo="m1"/>
<!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法:使用ref) -->
<!-- <Student ref="student"/> -->
</div>
</template>
<script>
// 引入组件
import School from './components/School.vue'
import Student from './components/Student.vue'
export default {
name: 'App',
components: {
School,
Student
},
data() {
return {
msg: '你好呀'
}
},
methods: {
getSchoolName(name){
console.log('App收到了学校名:',name)
},
getStudentName(name,...params){
console.log('App收到了学生名:',name,params)
},
m1(){
console.log('demo事件被触发了');
}
},
mounted(){
setTimeout(()=>{
// this.$refs.student.$on('atguigu',this.getStudentName)
// this.$refs.student.$once('atguigu',this.getStudentName)//只触发一次
},3000)
}
}
</script>
<style>
.app {
background-color: gray;
padding: 5px;
}
</style>
一种组件间通信的方式,适用于:子组件 ===> 父组件
使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件(事件的回调在A中)。
绑定自定义事件:
第一种方式,在父组件中:
第二种方式,在父组件中:
<Demo ref="demo"/>
......
mounted(){
this.$refs.xxx.$on('atguigu',this.test)
}
若想让自定义事件只能触发一次,可以使用once
修饰符,或$once
方法。
触发自定义事件:this.$emit('atguigu',数据)
解绑自定义事件this.$off('atguigu')
组件上也可以绑定原生DOM事件,需要使用native
修饰符。
注意:通过this.$refs.xxx.$on('atguigu',回调)
绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题!
全局事件总线是一种可以在任意组件间通信的方式,本质上就是一个对象。它必须满足以下条件:
- 所有的组件对象都必须能看见他
- 这个对象必须能够使用 o n 、 on、 on、emit和$off方法去绑定、触发和解绑事件
全局事件总线(GlobalEventBus):
一种组件间通信的方式,适用于任意组件间通信
安装全局事件总线:
new Vue({
...
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
},
...
})
接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身
export default {
methods(){
demo(data){...}
}
...
mounted() {
this.$bus.$on('xxx',this.demo)
}
}
提供数据:this.$bus.$emit('xxx',data)
最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件
<template>
<div class="school">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
</template>
<script>
import pubsub from 'pubsub-js'
export default {
name:'School',
props:['getSchoolName'],
data(){
return {
name:'尚硅谷',
address:'广州'
}
},
methods:{
demo(msgName,data){
console.log('hello消息收到了',data)
}
},
mounted(){
// console.log('School',this)
// this.$bus.$on('hello',(data)=>{
// console.log('School组件',data)
// })
this.pubId = pubsub.subscribe('hello',function(msgName,data){
console.log('有人发布了hello消息',msgName,data);
})
},
beforeDestroy(){
// this.$bus.$off('hello')
pubsub.unsubscribe(this.pubId)
},
}
</script>
<style scoped>
.school {
background-color: skyblue;
padding: 5px;
}
</style>
Student.vue
<template>
<div class="student">
<h2>学生姓名:{{name}}</h2>
<h2>学生年龄:{{age}}</h2>
<button @click="sendStudentName">吧学生名给School组件</button>
</div>
</template>
<script>
import pubsub from 'pubsub-js'
export default {
name:'Student',
data(){
return {
name:'余周周',
age:18
}
},
mounted(){
},
methods:{
sendStudentName(){
// this.$bus.$emit('hello',this.name)
pubsub.publish('hello',666)
}
}
}
</script>
<style scoped>
.student {
background-color: orange;
padding: 5px;
margin-top: 30px;
}
</style>
消息订阅与发布(pubsub):
消息订阅与发布是一种组件间通信的方式,适用于任意组件间通信
使用步骤:
安装pubsub:npm i pubsub-js
引入:import pubsub from 'pubsub-js'
接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身
export default {
methods(){
demo(data){...}
}
...
mounted() {
this.pid = pubsub.subscribe('xxx',this.demo)
}
}
提供数据:pubsub.publish('xxx',data)
最好在beforeDestroy钩子中,使用pubsub.unsubscribe(pid)
取消订阅
nextTick
== $nextTick(回调函数)可以将回调延迟到下次 DOM 更新循环之后执行==
$nextTick:
- 语法:
this.$nextTick(回调函数)
- 作用:在下一次 DOM 更新结束后执行其指定的回调
- 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏</button>
<transition name="hello" appear>
<h1 v-show="isShow">hello</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 1s;
}
.hello-leave-active {
animation: atguigu 1s reverse;
}
@keyframes atguigu {
from{
transform: translateX(-100px);
}
to{
transform: translateX(0px);
}
}
</style>
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏</button>
<transition name="hello" appear>
<h1 v-show="isShow">hello</h1>
</transition>
</div>
</template>
<script>
export default {
name:'Test',
data(){
return {
isShow:true
}
}
}
</script>
<style scoped>
h1 {
background-color: orange;
transition: 0.5s linear;
}
/*进入的起点*/
.hello-enter {
transform: translateX(-100%);
}
/*进入的终点*/
.hello-enter-to {
transform: translateX(0);
}
/*离开的起点*/
.hello-leave {
transform: translateX(0);
}
/*离开的终点*/
.hello-leave-to {
transform: translateX(-100%);
}
</style>
<template>
<div>
<button @click="isShow = !isShow">显示/隐藏</button>
<transition-group name="hello" appear>
<h1 v-show="!isShow" key="1">hello</h1>
<h1 v-show="isShow" key="2">hello111111</h1>
</transition-group>
</div>
</template>
<script>
export default {
name:'Test',
data(){
return {
isShow:true
}
}
}
</script>
<style scoped>
h1 {
background-color: orange;
transition: 0.5s linear;
}
/*进入的起点*/
.hello-enter {
transform: translateX(-100%);
}
/*进入的终点*/
.hello-enter-to {
transform: translateX(0);
}
/*离开的起点*/
.hello-leave {
transform: translateX(0);
}
/*离开的终点*/
.hello-leave-to {
transform: translateX(-100%);
}
</style>
作用:在插入、更新或移除 DOM元素时,在合适的时候给元素添加样式类名
(1)元素进入的样式:
v-enter:进入的起点
v-enter-active:进入过程中
v-enter-to:进入的终点
(2)元素离开的样式:
v-leave:离开的起点
v-leave-active:离开过程中
v-leave-to:离开的终点
2. 使用包裹要过度的元素,并配置name属性:
<transition name="hello">
<h1 v-show="isShow">你好啊!</h1>
</transition>
方法一:只能配置一个代理,不能控制灵活控制代理
App.vue
<template>
<div>
<button @click="getStudents">获取学生信息</button>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'App',
methods: {
getStudents(){
axios.get('http://localhost:8080/students').then(
response => {
console.log('请求成功了',response.data)
},
error => {
console.log('请求失败了',error.message)
}
)
}
}
}
</script>
vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
pages: {
index: {
// 入口
entry: 'src/main.js'
}
},
//关闭语法检查
lintOnSave:false,
// 开启代理服务器
devServer: {
proxy: 'http://localhost:5000'
}
})
<template>
<div>
<button @click="getStudents">获取学生信息</button>
<button @click="getCars">获取汽车信息</button>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'App',
methods: {
getStudents(){
axios.get('http://localhost:8080/atguigu/students').then(
response => {
console.log('请求成功了',response.data)
},
error => {
console.log('请求失败了',error.message)
}
)
},
getCars(){
axios.get('http://localhost:8080/demo/cars').then(
response => {
console.log('请求成功了',response.data)
},
error => {
console.log('请求失败了',error.message)
}
)
}
}
}
</script>
vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
pages: {
index: {
// 入口
entry: 'src/main.js'
}
},
//关闭语法检查
lintOnSave:false,
// 开启代理服务器(方式一)
/*devServer: {
proxy: 'http://localhost:5000'
}*/
// 开启代理服务器(方式二)
devServer: {
proxy: {
'/atguigu':{
target: 'http://localhost:5000',
pathRewrite: {'^/atguigu':''},
ws: true, //用于支持websocket
changeOrigin: true
},
'/demo':{
target: 'http://localhost:5001',
pathRewrite: {'^/demo':''},
ws: true, //用于支持websocket
changeOrigin: true
}
}
}
})
方法一:在vue.config.js中添加如下配置:
devServer:{
proxy:"http://localhost:5000"
}
说明:
方法二:
devServer: {
proxy: {
'/api1': { // 匹配所有以 '/api1'开头的请求路径
target: 'http://localhost:5000',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api1': ''}
},
'/api2': { // 匹配所有以 '/api2'开头的请求路径
target: 'http://localhost:5001',// 代理目标的基础路径
changeOrigin: true,
pathRewrite: {'^/api2': ''}
}
}
}
// changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
// changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
说明:
App.vue
<template>
<div class="container">
<Category title="美食" >
<img src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
</Category>
<Category title="游戏" >
<ul>
<li v-for="(g,index) in games" :key="index">{{g}}</li>
</ul>
</Category>
<Category title="电影">
<video controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
</Category>
</div>
</template>
<script>
import Category from './components/Category'
export default {
name:'App',
components:{Category},
data() {
return {
games:['植物大战僵尸','红色警戒','空洞骑士','王国']
}
},
}
</script>
<style scoped>
.container{
display: flex;
justify-content: space-around;
}
</style>
Category.vue
<template>
<div class="category">
<h3>{{title}}分类</h3>
<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
<slot>我是一些默认值,当使用者没有传递具体结构时,我会出现</slot>
</div>
</template>
<script>
export default {
name:'Category',
props:['title']
}
</script>
<style scoped>
.category{
background-color: skyblue;
width: 200px;
height: 300px;
}
h3{
text-align: center;
background-color: orange;
}
video{
width: 100%;
}
img{
width: 100%;
}
</style>
App.vue*
<template>
<div class="container">
<Category title="美食" >
<img slot="center" src="https://s3.ax1x.com/2021/01/16/srJlq0.jpg" alt="">
<a slot="footer" href="http://www.atguigu.com">更多美食</a>
</Category>
<Category title="游戏" >
<ul slot="center">
<li v-for="(g,index) in games" :key="index">{{g}}</li>
</ul>
<div class="foot" slot="footer">
<a href="http://www.atguigu.com">单机游戏</a>
<a href="http://www.atguigu.com">网络游戏</a>
</div>
</Category>
<Category title="电影">
<video slot="center" controls src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"></video>
<template v-slot:footer>
<div class="foot">
<a href="http://www.atguigu.com">经典</a>
<a href="http://www.atguigu.com">热门</a>
<a href="http://www.atguigu.com">推荐</a>
</div>
<h4>欢迎前来观影</h4>
</template>
</Category>
</div>
</template>
<script>
import Category from './components/Category'
export default {
name:'App',
components:{Category},
data() {
return {
games:['植物大战僵尸','红色警戒','空洞骑士','王国']
}
},
}
</script>
<style>
.container,.foot{
display: flex;
justify-content: space-around;
}
h4{
text-align: center;
}
</style>
Category.vue
<template>
<div class="category">
<h3>{{title}}分类</h3>
<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
<slot name="center">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot>
<slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现2</slot>
</div>
</template>
<script>
export default {
name:'Category',
props:['title']
}
</script>
<style scoped>
.category{
background-color: skyblue;
width: 200px;
height: 300px;
}
h3{
text-align: center;
background-color: orange;
}
video{
width: 100%;
}
img{
width: 100%;
}
</style>
App.vue
<template>
<div class="container">
<Category title="游戏" >
<template scope="jojo">
<ul>
<li v-for="(g,index) in jojo.games" :key="index">{{g}}</li>
</ul>
</template>
</Category>
<Category title="游戏" >
<template scope="jojo">
<ol>
<li v-for="(g,index) in jojo.games" :key="index">{{g}}</li>
</ol>
</template>
</Category>
<Category title="游戏" >
<template scope="jojo">
<h4 v-for="(g,index) in jojo.games" :key="index">{{g}}</h4>
</template>
</Category>
</div>
</template>
<script>
import Category from './components/Category'
export default {
name:'App',
components:{Category}
}
</script>
<style>
.container,.foot{
display: flex;
justify-content: space-around;
}
h4{
text-align: center;
}
</style>
Category.vue
<template>
<div class="category">
<h3>{{title}}分类</h3>
<!-- 定义一个插槽(挖个坑,等着组件的使用者进行填充) -->
<slot :games="games">我是一些默认值,当使用者没有传递具体结构时,我会出现1</slot>
</div>
</template>
<script>
export default {
name:'Category',
props:['title'],
data() {
return {
games:['植物大战僵尸','红色警戒','空洞骑士','王国']
}
},
}
</script>
<style scoped>
.category{
background-color: skyblue;
width: 200px;
height: 300px;
}
h3{
text-align: center;
background-color: orange;
}
video{
width: 100%;
}
img{
width: 100%;
}
</style>
插槽:
作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于==父组件 > 子组件
分类:默认插槽、具名插槽、作用域插槽
使用方式:
默认插槽:
父组件中:
<Category>
<div>html结构1</div>
</Category>
子组件中:
<template>
<div>
<slot>插槽默认内容...</slot>
</div>
</template>
具名插槽:
父组件中:
<Category>
<template slot="center">
<div>html结构1</div>
</template>
<template v-slot:footer>
<div>html结构2</div>
</template>
</Category>
子组件中:
<template>
<div>
<slot name="center">插槽默认内容...</slot>
<slot name="footer">插槽默认内容...</slot>
</div>
</template>
作用域插槽:
理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)
父组件中:
<Category>
<template scope="scopeData">
<!-- 生成的是ul列表 -->
<ul>
<li v-for="g in scopeData.games" :key="g">{{g}}</li>
</ul>
</template>
</Category>
<Category>
<template slot-scope="scopeData">
<!-- 生成的是h4标题 -->
<h4 v-for="g in scopeData.games" :key="g">{{g}}</h4>
</template>
</Category>
——
子组件中:
<template>
<div>
<slot :games="games"></slot>
</div>
</template>
<script>
export default {
name:'Category',
props:['title'],
//数据在子组件自身
data() {
return {
games:['红色警戒','穿越火线','劲舞团','超级玛丽']
}
},
}
</script>