赞
踩
Vue是一套构建用户界面的渐进式JavaScript框架
<!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>Document</title> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <div id="root"> <h1>Hello,{{name}}</h1> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 //创建Vue实例 new Vue({ el:'#root', //el用于指定当前Vue实例为那个容器服务,值通常为css选择器字符串 data:{ //data中用于存储数据,数据供el所指定的容器去使用,值暂时先写成一个对象 name:'尚硅谷' } }) </script> </body> </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>Document</title> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <div id="root"> <h1>插值语法</h1> <!-- 插值语法 {{name}} --> <h3>你好,{{name}}</h3> <hr> <h1>指令语法</h1> <!-- 指令语法 v-bind: 可以简写成:--> <a v-bind:href="url">点我去{{school.name}}学习</a> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 new Vue({ el:'#root', data:{ name:'jack', school:{ name:'尚硅谷', url:'http://www.atguigu.com' } } }) </script> </body> </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>Document</title> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <div id="root"> <!-- 普通写法 --> 单项数据绑定:<input type="text" v-bind:value="name"><br> 双项数据绑定:<input type="text" v-model:value="name"> <!-- 简写 --> 单项数据绑定:<input type="text" :value="name"><br> 双项数据绑定:<input type="text" v-model="name"> <!-- 如下代码是错误的,因为v-model只能应用在表单类元素(输入类元素) --> <!-- <h2 v-bind:x="name">你好啊</h2> --> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 new Vue({ el:'#root', data:{ name:'尚硅谷' } }) </script> </body> </html>
el有两种写法
data有两种写法
目前哪种写法都可以,以后学到组件时,data必须使用函数式,否则会报错
重要原则:由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了
<!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>Document</title> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <div id="root"> <h1>你好,{{name}}</h1> </div> </body> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 // const v = new Vue({ // // el:'#root', //第一种写法 // data:{ // name:'尚硅谷' // } // }) // v.$mount('#root') //第二种写法 new Vue({ el:'#root', //data第一种写法:对象式 // data:{ // name:'尚硅谷' // } //data第二种写法:函数式 data:function(){ return { name:'尚硅谷' } } }) </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>Document</title> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 let number = 18 let person = { name:'张三', sex:'男', } Object.defineProperty(person,'age',{ // value:18, // enumerable:true, //控制属性是否可以枚举,默认值是false // writable:true, //控制属性是否可以被修改,默认值是false // configurable:true, //控制属性是否可以被修改,默认值是false //当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值 get:function(){ return 'hello' }, set(value){ console.log('有人修改了age属性,且值时',value); number = value } }) </script> </body> </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>Document</title> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 let obj = {x:100} let obj2 = {y:200} Object.defineProperty(obj2,'x',{ get(){ return obj.x }, set(value){ obj.x = value } }) </script> </body> </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>Document</title> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <div id="root"> <h2>欢迎来到{{name}}学习</h2> <!-- <button v-on:click="showInfo">点我提示信息</button> --> <!-- 简写 --> <button @click="showInfo1">点我提示信息</button> <button @click="showInfo2(66,$event)">点我提示信息</button> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 new Vue({ el:'#root', data:{ name:'尚硅谷' }, methods:{ showInfo1(event){ // console.log(event.target); // console.log(this);//此处是this alert('同学你好1') }, showInfo2(number,event){ console.log(number); console.log(event); // console.log(event.target); // console.log(this);//此处是this alert('同学你好2') } } }) </script> </body> </html>
prevent:阻止默认事件(常用)
阻止了事件的默认行为,相当于调用了event.preventDefault
方法,提交事件将不再重新加载页面
<form @submit.prevent="onSubmit"></form>
stop:阻止事件冒泡(常用)
阻止了事件冒泡,相当于调用了event.stopPropagation
方法,单击事件将停止传递
<div @click="shout(2)">
<button @click.stop="shout(1)">ok</button>
</div>
//只输出1
once:事件只触发一次(常用)
绑定了事件以后只能触发一次,第二次就不会触发
<button @click.once="shout(1)">ok</button>
capture:使用事件的捕获模式
添加事件监听器时,使用 capture
捕获模式,例如:指向内部元素的事件,在被内部元素处理前,先被外部处理。使事件触发从包含这个元素的顶层开始往下触发
<div @click.capture="shout(1)">
obj1
<div @click.capture="shout(2)">
obj2
<div @click="shout(3)">
obj3
<div @click="shout(4)">
obj4
</div>
</div>
</div>
</div>
// 输出结构: 1 2 4 3
self:只有event.target
是当前操作的元素是才触发事件
仅当 event.target
是元素本身时才会触发事件处理器,例如:事件处理器不来自子元素
passive:事件的默认行为立即执行。无需等待事件回调之星完毕
在移动端,当我们在监听元素滚动事件的时候,会一直触发onscroll
事件会让我们的网页变卡,因此我们使用这个修饰符的时候,相当于给onscroll
事件整了一个.lazy
修饰符。
滚动事件的默认行为 (scrolling) 将立即发生而非等待 onScroll
完成,以防其中包含 event.preventDefault()
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>
Vue中常用的按键别名
Vue中未提供别名的按键,可以使用按键原始的key值去绑定,但注意要转为kebab-case(短横线命名)
系统修饰键(用法特殊):ctrl、alt、shift、meta
也可以使用keyCode去指定具体的按键(不推荐)
Vue.config.KeyCodes.自定义键名 = 键码,可以去自定义按键别名
<!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>Document</title> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <div id="root"> <h2>欢迎来到{{name}}学习</h2> <!-- 输入之后按下enter才会触发事件 --> <input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo"> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 new Vue({ el:'#root', data:{ name:'尚硅谷' }, methods:{ showInfo(e){ console.log(e.target.value) } } }) </script> </body> </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>Document</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>{{firstName}}-{{lastName}}</span> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 new Vue({ el:'#root', data:{ firstName:'张', lastName:'三' } }) </script> </body> </html>
methods实现
<!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>Document</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> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 new Vue({ el:'#root', data:{ firstName:'张', lastName:'三' }, methods:{ fullName(){ return this.firstName+'-'+this.lastName } } }) </script> </body> </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>Document</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> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 const vm = new Vue({ el:'#root', data:{ firstName:'张', lastName:'三', }, computed:{ fullName:{ // 当有人读取fullName时,get会被调用,且饭hi之就作为fullName的值 //get什么时候调用 1.初次读取时调用 2.所依赖的数据发生变化时 get(){ console.log('get被调用'); return this.firstName+'-'+this.lastName }, //set什么时候调用 当fullName被修改时 set(value){ const arr = value.split('-') this.firstName = arr[0] this.lastName = arr[1] } } } //简写 // fullName:function(){ // return this.firstName+'-'+this.lastName // } }) </script> </body> </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>Document</title> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <div id="root"> <h2>今天天气很{{info}}</h2> <button @click="changeWeather">切换天气</button> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 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什么时候调用 当isHost发生改变时 handler(newValue,oldValue){ console.log('isHot被修改',newValue,oldValue); } } }, }) // vm.$watch('isHot',{ // //初始化时让handler调用 // // immediate:true // //handler什么时候调用 当isHost发生改变时 // handler(newValue,oldValue){ // console.log('isHot被修改',newValue,oldValue); // } // }) </script> </body> </html>
当然我们可以在idHot
对象面添加一个属性:immediate
,当此属性布尔值为真的时候,handler
回调函数在初始化的时候就会调用一次。
备注:
<!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>Document</title> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <div id="root"> <h2>今天天气很{{info}}</h2> <button @click="changeWeather">切换天气</button> <hr> <h3>a的值是:{{numbers.a}}</h3> <button @click="numbers.a++">点我让a+1</button> <hr> <h3>b的值是:{{numbers.b}}</h3> <button @click="numbers.b++">点我让b+1</button> <button @click="numbers = {a:777,b:888}">彻底替换numbers</button> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 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什么时候调用 当isHost发生改变时 handler(newValue,oldValue){ console.log('isHot被修改',newValue,oldValue); } }, // 监视多级结果中某个属性的变化 'numbers.a':{ handler(){ console.log('a被改变了'); } }, //监视多级结构中所有属性变化 numbers:{ deep:true, handler(){ console.log('numbers改变了') } } }, }) </script> </body> </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>Document</title> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <div id="root"> <h2>今天天气很{{info}}</h2> <button @click="changeWeather">切换天气</button> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 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, // // deep:true, // //handler什么时候调用 当isHost发生改变时 // handler(newValue,oldValue){ // console.log('isHot被修改',newValue,oldValue); // } // } //简写 isHot(newValue,oldValue){ console.log('isHot被修改',newValue,oldValue); } }, }) vm.$watch('isHot',function(newValue,oldValue){ console.log('isHot被修改',newValue,oldValue); }) </script> </body> </html>
computed和watch之间的区别
重要原则:
写法:
写法:
<!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>Document</title> <style> .basic{ width: 300px; height: 100px; border: 1px solid black; } .happy{ border:4px solid red; background-color: rgba(255, 255, 0, 0.644); background: linear-gradient(30deg,yellow,pink,orange,yellow); } .sad{ border:4px solid cyan; background-color: rgba(12, 23, 0, 0.644); background: linear-gradient(30deg,rgb(178, 60, 5),rgb(168, 72, 88),rgb(155, 47, 146),rgb(114, 40, 40)); } .normal{ border:4px solid rgba(12, 107, 107, 0.608); background-color: rgba(12, 23, 0, 0.644); background: linear-gradient(30deg,rgb(178, 60, 5),rgb(121, 142, 38),rgb(157, 67, 150),rgb(239, 169, 169)); } .hello1{ background-color: orchid } .hello2{ font-size: 40px; } .hello3{ color:red } </style> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <div id="root"> <!-- 绑定class样式---字符串写法 适用于:样式的类名不确定,还要动态指定 --> <div class="basic" :class="mood" @click="changeMood">{{name}}</div><br><br><br> <!-- 绑定class样式---数组写法 适用于:要绑定的样式个数不确定、名字也不确定 --> <div class="basic" :class="classArr">{{name}}</div><br><br><br> <!-- 绑定class样式---对象写法 适用于:要绑定的样式个数确定、名字也确定,但要动态决定用不用 --> <div class="basic" :class="classObj">{{name}}</div><br><br><br> <!-- 绑定style样式---对象写法 --> <div class="basic" :style="styleObj">{{name}}</div><br><br><br> <div class="basic" :style="[styleObj,styleObj2]">{{name}}</div> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 const vm = new Vue({ el:'#root', data:{ name:'尚硅谷', mood:'normal', classArr:['hello1','hello2','hello3'], classObj:{ hello1:false, hello2:false }, styleObj:{ fontSize:'40px', color:'red', }, styleObj2:{ backgroundColor:'orange' } }, methods:{ changeMood(){ const arr = ['happy','sad','normal'] const num = Math.floor(Math.random()*3) this.mood = arr[num] } } }) </script> </body> </html>
1.v-if
写法:
2.v-show
写法:
备注:使用v-if时,元素可能无法获取到,而使用v-show一定可以获取到
<!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>Document</title> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <div id="root"> <h2>当前的n值是:{{n}}</h2> <button @click="n++">点我n+1</button> <!-- 使用v-show做条件渲染 --> <!-- <h2 v-show="false">欢迎来到{{name}}</h2> <h2 v-show="1 === 1">欢迎来到{{name}}</h2> --> <!-- 使用v-if做条件渲染 --> <!-- <h2 v-if="false">欢迎来到{{name}}</h2> <h2 v-if="1 === 1">欢迎来到{{name}}</h2> --> <div v-show="n === 1">Angular</div> <div v-show="n === 2">React</div> <div v-show="n === 3">Vue</div> <!-- v-else 和 v-else-if --> <!-- <div v-if="n === 1">Angular</div> <div v-else-if="n === 2">React</div> <div v-else-if="n === 3">Vue</div> --> <template v-if="n === 1"> <h2>你好</h2> <h2>尚硅谷</h2> <h2>北京</h2> </template> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 new Vue({ el:'#root', data:{ name:'尚硅谷', n:0 } }) </script> </body> </html>
v-for指令
v-for="(item,index) in xxx" :key="yyy"
<!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>Document</title> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <div id="root"> <!-- 遍历数组 --> <h2>人员列表</h2> <ul> <li v-for="p in persons" :key="p.id"> {{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> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 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> </body> </html>
watch实现
<!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>Document</title> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <div id="root"> <!-- 遍历数组 --> <h2>人员列表</h2> <input type="text" placeholder="请输入名字" v-model="keyWord"> <ul> <li v-for="p in filPersons" :key="p.id"> {{p.name}}-{{p.age}}---{{p.sex}} </li> </ul> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 new Vue({ el:'#root', data:{ keyWord:'', persons:[ {id:'001',name:'马冬梅',age:18,sex:'女'}, {id:'002',name:'周冬雨',age:19,sex:'女'}, {id:'003',name:'周杰伦',age:21,sex:'男'}, {id:'004',name:'温兆伦',age:20,sex:'男'} ], filPersons:[] }, watch:{ keyWord:{ immediate:true, handler(val){ this.filPersons = this.persons.filter((p)=>{ return p.name.indexOf(val) !== -1 }) } } } }) </script> </body> </html>
computed实现
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue初识</title> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <div id="root"> <!--遍历数组--> <h2>人员列表(遍历数组)</h2> <input type="text" placeholder="请输入名字" v-model="keyword"/> <ul> <li v-for="(p,index) in filterPersons" :key="p.id"> {{p.name}}--{{p.age}}---{{p.sex}} </li> </ul> </div> <script type="text/javascript"> Vue.config.productionTip = false //创建vue实例 new Vue({ el: "#root", data: { keyword: '', persons: [ {id: 1, name: "马冬梅", age: 18, sex: "女"}, {id: 2, name: "周冬雨", age: 19, sex: "女"}, {id: 3, name: "周杰伦", age: 20, sex: "男"}, {id: 4, name: "温兆伦", age: 21, sex: "男"}, ] }, computed: { filterPersons() { return this.persons.filter((p) => { return p.name.indexOf(this.keyword) !== -1 }) } } }) </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue初识</title> <script type="text/javascript" src="./js/vue.js"></script> </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 filterPersons" :key="p.id"> {{p.name}}--{{p.age}}---{{p.sex}} </li> </ul> </div> <script type="text/javascript"> Vue.config.productionTip = false //创建vue实例 new Vue({ el: "#root", data: { sortType:0, //0原顺序 1降序 2升序 keyword: '', persons: [ {id: 1, name: "马冬梅", age: 23, sex: "女"}, {id: 2, name: "周冬雨", age: 19, sex: "女"}, {id: 3, name: "周杰伦", age: 20, sex: "男"}, {id: 4, name: "温兆伦", age: 18, sex: "男"}, ] }, computed: { filterPersons() { const arr = this.persons.filter((p) => { return p.name.indexOf(this.keyword) !== -1 }) //判断是否需要排序 if(this.sortType){ arr.sort((p1,p2)=>{ return this.sortType == 1 ? p2.age-p1.age : p1.age-p2.age }) } return arr } } }) </script> </body> </html>
Vue监视数据的原理:
vue会监视data中所有层次的数据
如何检测对象中的数据
通过setter实现监视,且要在new Vue时传入要监测的数据
如何监测数组中的数据
通过包裹数组更新元素的方法实现,本质就是做了两件事
在Vue修改数组中的某个元素一定要用如下的方法
特别注意:Vue.set()和vm.$set()不能给vm或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>Document</title> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <div id="root"> <h1>学生信息</h1><br> <button @click="student.age++">年龄+1岁</button><br> <button @click="addSex">添加性别属性:默认值:男</button><br> <button @click="student.sex = '未知'">修改性别属性</button><br> <button @click="addFriend">在列表首位添加一个朋友</button><br> <button @click="updateFirstFriend">修改第一个朋友的名字:张三</button><br> <button @click="addHobby">添加一个爱好</button><br> <button @click="updateHobby">修改第一个爱好为:开车</button><br> <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.hobby" :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> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 const vm = new Vue({ el:'#root', data:{ student:{ name:'tom', age:18, hobby:['抽烟','喝酒','烫头'], friends:[ {name:'jerry',age:29}, {name:'tony',age:30} ] } }, methods:{ addSex(){ // Vue.set(this.student,'sex','男') this.$set(this.student,'sex','男') }, addFriend(){ this.student.friends.unshift({name:"peng",age:32}) }, updateFirstFriend(){ this.student.friends[0].name = '张三' }, addHobby(){ this.student.hobby.push('学习') }, updateHobby(){ // this.student.hobby.splice(0,1,'开车') // Vue.set(this.student.hobby,0,'开车') this.$set(this.student.hobby,0,'开车') } } }) </script> </body> </html>
若:<input type="text"/>
,则v-model收集的是value值,用户输入的就是value值
若:<input type="radio"/>
,则v-model收集的是value值,且要给标签配置value值
若:<input type="checkbox"/>
备注:
v-model的三个修饰符:
定义:对要显示的数据进行特定格式化后再显示(适用于简单逻辑的处理)
语法:
备注:
<div>{{name}}</div>
<div v-text="name"></div>
<div id="root"> <div>{{name}}</div> <div v-html="str"></div> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 new Vue({ el:'#root', data:{ name:'尚硅谷', str:`<h3>你好啊</h3>` } })
<!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>Document</title> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <div id="root"> <h2 v-once>初始化n值:{{n}}</h2> <h2>当前的n值是:{{n}}</h2> <button @click="n++">点我n+1</button> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 new Vue({ el:'#root', data:{ n:1 } }) </script> </body> </html>
<div id="root">
<h2 v-pre>Vue其实很简单</h2>
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
定义语法:
局部指令
new Vue({
directives:{指令名:配置对象}
})
或
new Vue({
directives{指令名:配置对象}
})
全局指令
Vue.directives(指令名,配置对象)或Vue.directive(指令名,回调函数)
配置对象中常用的3个回调函数
备注:
<!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>Document</title> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <!-- 需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值方法10倍--> <!-- 需求2:定义一个v-fbing指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点--> <div id="root"> <h2>当前的n值是:<span v-text="n"></span></h2> <h2>放大10倍后n值是:<span v-big="n"></span></h2> <button @click="n++">点我n+1</button><hr> <input type="text" v-fbind:value="n"> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 new Vue({ el:'#root', data:{ n:1 }, directives:{ //big函数何时会被调用 1.指令与元素成功绑定时 2.指令所在的模板被重新解析时 big(element,binding){ 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 element.focus() } } } }) </script> </body> </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>Document</title> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <div id="root"> <h2 :style="{opacity}">欢迎学习Vue</h2> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 new Vue({ el:'#root', data:{ opacity:1 }, methods:{ }, //Vue完成模板解析并把真实DOM元素放入页面后(挂载完毕)调用 mounted(){ setInterval(()=>{ this.opacity -= 0.01 if(this.opacity<=0) this.opacity=1 },16) } }) //通过外部定时器实现(不推荐) // setInterval(()=>{ // vm.opacity -= 0.01 // if(vm.opacity<=0){ // vm.opacity=1 // } // },16) </script> </body> </html>
常用的生命周期钩子:
关于销毁Vue实例:
Vue中使用组件的三大步骤:
定义组件
注册组件
使用组件
如何定义一个组件
使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但也有点区别;
区别如下:
备注:使用template可以配置组件结构
如何注册组件
编写组件标签
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <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> </div> </body> <script type="text/javascript"> Vue.config.productionTip = false //第一步:创建school组件 const school = Vue.extend({ //组件定义时,一定不要写el配置项,因为最终所有的组件都要被一个vm管理,由vm决定服务于哪个容器。 template:` <div class="demo"> <h2>学校名称:{{schoolName}}</h2> <h2>学校地址:{{address}}</h2> <button @click="showname">点我提示学校名</button> </div> `, data(){ return { schoolName:'尚硅谷', address:'北京昌平' } }, methods:{ showname(){ alert('尚硅谷') } } }) //第一步:创建student组件 const student = Vue.extend({ template:` <div> <h2>学生姓名:{{studentName}}</h2> <h2>学生年龄:{{age}}</h2> </div> `, data(){ return { studentName:'JOJO', age:20 } } }) //创建vm new Vue({ el:'#root', data:{ msg:'你好,JOJO!' }, //第二步:注册组件(局部注册) components:{ school, student } }) </script> </html>
关于组件名:
一个单词组成:
第一种写法(首字母小写):school
第二种写法(首字母大写):School
多个单词组成
第一种写法(kebab-case命名):my-schoole
第二种写法(CamelCase命名):MySchool(需要Vue脚手架)
备注
关于组件标签:
一个简写方式:
const school = Vue.extend(options)可简写为:const school = options
<!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>Document</title> <script type="text/javascript" src="./js/vue.js"></script> </head> <body> <div id="root"> <h1>{{msg}}</h1> <school></school> </div> <script type="text/javascript"> Vue.config.productionTip = false //阻止vue在启动时生成生产提示 //定义组件 const school = Vue.extend({ template:` <div> <h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> </div> `, data(){ return { name:'尚硅谷', address:'北京' } } }) new Vue({ el:'#root', data:{ msg:'欢迎学习Vue' }, components:{ school:school } }) </script> </body> </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"> </div> </body> <script type="text/javascript"> Vue.config.productionTip = false //定义student组件 const student = Vue.extend({ template:` <div> <h2>学生名称:{{name}}</h2> <h2>学生年龄:{{age}}</h2> </div> `, data(){ return { name:'JOJO', age:20 } } }) //定义school组件 const school = Vue.extend({ template:` <div> <h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> <student></student> </div> `, components:{ student }, data(){ return { name:'尚硅谷', address:'北京' } } }) //定义hello组件 const hello = Vue.extend({ template:` <h1>{{msg}}</h1> `, data(){ return { msg:"欢迎学习尚硅谷Vue教程!" } } }) //定义app组件 const app = Vue.extend({ template:` <div> <hello></hello> <school></school> </div> `, components:{ school, hello } }) //创建vm new Vue({ template:` <app></app> `, el:'#root', components:{ app } }) </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"> <school></school> </div> </body> <script type="text/javascript"> Vue.config.productionTip = false Vue.prototype.x = 99 const school = Vue.extend({ name:'school', template:` <div> <h2>学校名称:{{name}}</h2> <h2>学校地址:{{address}}</h2> <button @click="showX">点我输出x</button> </div> `, data(){ return { name:'尚硅谷', address:'北京' } }, methods: { showX(){ console.log(this.x) } }, }) const vm = new Vue({ el:'#root', data:{ msg:'你好' }, components:{school} }) </script> </html>
School.vue
<template> <!--组件的结构--> <div class="demo"> <h2>学校名称:{{ schoolName }}</h2> <h2>学校地址:{{ address }}</h2> <button @click="showName">点我提示学校名</button> </div> </template> <script> //组件交互代码(数据、方法等) export default { name:'School', data(){ return { schoolName:'尚硅谷', address:'北京' } }, methods:{ showName(){ alert(this.showName) } } } </script> <style> /* 组建的样式 */ .demo { background-color: red; } </style>
Student.vue
<template> <!--组件的结构--> <div class="demo"> <h2>学生姓名:{{ name }}</h2> <h2>学校年龄:{{ age }}</h2> </div> </template> <script> //组件交互代码(数据、方法等) export default { name:'Student', data(){ return { name:'彭于晏', age:32 } } } </script> <style> /* 组建的样式 */ .demo { background-color: red; } </style>
App.vue
<template> <div> <School></School> <Student></Student> </div> </template> <script> //引入组件 import School from './School.vue' import Student from './Student.vue' export default { name:'App', components:{ School, Student } } </script> <style> </style>
main.js
import App from './App.vue'
new Vue({
el:'#root',
template:`<App></App>`,
components:{App},
})
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="root"> </div> <script type="text/javascript" src="../js/vue.js"></script> <script type="text/javascript" src="./main.js"></script> </body> </html>
.文件目录 ├── node_modules ├── public │ ├── favicon.ico: 页签图标 │ └── index.html: 主页面 ├── src │ ├── assets: 存放静态资源 │ │ └── logo.png │ │── component: 存放组件 │ │ └── HelloWorld.vue │ │── App.vue: 汇总所有组件 │ └── main.js: 入口文件 ├── .gitignore: git版本管制忽略的配置 ├── babel.config.js: babel的配置文件 ├── package.json: 应用包配置文件 ├── README.md: 应用描述文件 └── package-lock.json: 包版本控制文件
<template> <div> <h1 v-text="msg" ref="title"></h1> <button ref="btn" @click="showDOM">点我输出上方的DOM元素</button> <School id="sch"/> </div> </template> <script> //引入School组件 import School from './components/School' export default { name:'App', data(){ return{ msg:'欢迎学习vue!' } }, components:{ School }, methods:{ showDOM(){ console.log(this.$refs.title);//真实DOM console.log(this.$refs.sch);//School组建的实例对象(vc) console.log(this.$refs.btn);//真实DOM } } } </script>
功能:让组件接受外部传过来的数据
传递数据
接收数据
第一种方式(只接受)
props:['name']
第二种方式(限制类型)
props:{name:String}
第三种方式(限制类型、限制必要性、指定默认值)
//接受的同时对数据进行类型限制+默认值的指定 + 必要限制
props:{
name:{
type:String,
required:true //名字是必要的
},
age:{
type:Number,
default:99
},
sex:{
type:String,
required:true
}
}
备注:props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props到data中一份,然后去修改data中的数据。
功能:可以把多个组件共有的配置提取成一个混入对象
使用方式:
第一步定义混合,例如
{
data(){......},
methods:{......}
}
第二步使用混合,例如
mixin.js
export const hunhe = {
methods:{
showName(){
alert(this.name)
}
}
}
<template> <div> <h2 @click="showName">学生姓名:{{ name }}</h2> <h2>学生性别:{{ sex }}</h2> </div> </template> <script> //引入hunhe import {hunhe} from '../mixin' export default { name:'Student', data(){ return { name:'张三', sex:'男' } }, mixins:[hunhe] } </script> <style> </style>
功能:用于增强Vue
本质:包含install方法的一个对象,install的第一个参数Vue,第二个以后的参数是插件使用者传递的数据
定义插件:
对象.install = function(Vue,option)
提娜佳全局过滤器
Vue.filter(...)
添加全局指令
Vue.directive(...)
配置全局混入
Vue.mixin(...)
添加实例方法
Vue.prototype.$myMethod = function(){...}
Vue.prototype.$myProperty = xxx
使用插件:Vue.user()
作用:让样式在局部生效,防止冲突
写法:<style scoped>
App.vue
<template> <div id="root"> <div class="todo-container"> <div class="todo-wrap"> <MyHeader :addTodo="addTodo"/> <MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/> <MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/> </div> </div> </div> </template> <script> import MyHeader from './components/MyHeader.vue' import MyFooter from './components/MyFooter.vue' import MyList from './components/MyList.vue' export default { name: 'App', components: { MyHeader,MyFooter,MyList }, data(){ return { todos:[ {id:'001',title:'吃饭',done:true}, {id:'002',title:'喝酒',done:true}, {id:'003',title:'唱歌',done:false}, {id:'004',title:'开车',done:true} ] } }, methods:{ //添加todo addTodo(todoObj){ this.todos.unshift(todoObj) }, //勾选或取消勾选 checkTodo(id){ this.todos.forEach((todo)=>{ if(todo.id === id) todo.done = !todo.done }) }, //删除todo deleteTodo(id){ this.todos = this.todos.filter((todo)=>{ return todo.id !== id }) }, //全选或全不选 checkAllTodo(done){ this.todos.forEach((todo)=>{ todo.done = done }) }, //清除所有完成todo clearAllTodo(){ this.todos = this.todos.filter((todo)=>{ return !todo.done }) } } } </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>
MyHeader.vue
<template> <div class="todo-header"> <input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/> </div> </template> <script> import {nanoid} from 'nanoid' export default { name:'MyHeader', props:['addTodo'], data(){ return { title:'' } }, methods:{ add(e){ if(!this.title.trim()) return //将用户输入包装成为todo对象 const todoObj = {id:nanoid(),title:e.target.value,done:false} //通知APP组件添加对象 this.addTodo(todoObj) //清空输入 this.title = '' } }, } </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>
MyList.vue
<template> <ul class="todo-main"> <MyItem v-for="todoObj in todos" :key="todoObj.id" :todo="todoObj" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/> </ul> </template> <script> import MyItem from './MyItem.vue' export default { name:'MyList', components:{MyItem}, props:['todos','checkTodo','deleteTodo'] } </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>
MyItem.vue
<template> <li > <label> <input type="checkbox" :checked="todo.done" @change="handleCheck(todo.id)"/> <span>{{todo.title}}</span> </label> <button class="btn btn-danger" @click="handleDelete(todo.id)">删除</button> </li> </template> <script> export default { name:'MyItem', //声明接受todo对象 props:['todo','checkTodo','deleteTodo'], methods:{ handleCheck(id){ //通知App组件将对应的todo对象的done值取反 this.checkTodo(id) }, handleDelete(id){ if(confirm('确定删除吗?')){ this.deleteTodo(id) } } } } </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; } li:hover { background-color: #ddd; } li:hover button{ display: block; } </style>
MyFooter.vue
<template> <div class="todo-footer" v-show="total"> <label> <input type="checkbox" :checked="isAll" @change="checkAll"/> </label> <span> <span>已完成{{doneTotal}}</span> / 全部{{total}} </span> <button class="btn btn-danger" @click="clearAll">清除已完成任务</button> </div> </template> <script> export default { name:'MyFooter', data(){ return { } }, props:['todos','checkAllTodo','clearAllTodo'], computed:{ total(){ return this.todos.length }, doneTotal(){ return this.todos.reduce((pre,current)=>{ return pre+(current.done?1:0) },0) }, isAll:{ get(){ return this.doneTotal === this.total && this.total>0 }, set(value){ this.checkAllTodo(value) } } }, methods:{ checkAll(e){ this.checkAllTodo(e.target.checked) }, clearAll(){ this.clearAllTodo() } } } </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>
<!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>Document</title> </head> <body> <h2>localStorage</h2> <button onclick="saveData()">点我保存一个数据</button> <button onclick="readData()">点我读取一个数据</button> <button onclick="deleteData()">点我删除一个数据</button> <button onclick="deleteAllData()">点我清空一个数据</button> <script> function saveData(){ let p = {name:'张三',age:20} localStorage.setItem('msg','hello') localStorage.setItem('boy','彭于晏') localStorage.setItem('age',43) localStorage.setItem('person',JSON.stringify(p)) } function readData(){ console.log(localStorage.getItem('msg')) console.log(localStorage.getItem('age')) const result = localStorage.getItem('person') console.log(JSON.parse(result)) } function deleteData(){ localStorage.removeItem('msg') localStorage.removeItem('boy') } function deleteAllData(){ localStorage.clear() } </script> </body> </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>Document</title> </head> <body> <h2>sessionStorage</h2> <button onclick="saveData()">点我保存一个数据</button> <button onclick="readData()">点我读取一个数据</button> <button onclick="deleteData()">点我删除一个数据</button> <button onclick="deleteAllData()">点我清空一个数据</button> <script> function saveData(){ let p = {name:'张三',age:20} sessionStorage.setItem('msg','hello') sessionStorage.setItem('boy','彭于晏') sessionStorage.setItem('age',43) sessionStorage.setItem('person',JSON.stringify(p)) } function readData(){ console.log(sessionStorage.getItem('msg')) console.log(sessionStorage.getItem('age')) const result = sessionStorage.getItem('person') console.log(JSON.parse(result)) } function deleteData(){ sessionStorage.removeItem('msg') sessionStorage.removeItem('boy') } function deleteAllData(){ sessionStorage.clear() } </script> </body> </html>
一种组件间通信的方式,适用于:子组件===>父组件
使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件
绑定自定义事件:
第一种方式,在父组件中<Demo @atguigu='test'
或<Demo v-on:atguigu='test'
第二种方式,在父组件中:
<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指向会出问题
App.vue
<template> <div class="app"> <h1>{{ msg }},学生姓名:{{ studentName }}</h1> <!-- 通过父组件给子组件传递函数类型的props实现:子给父传递数据 --> <School :getSchoolName=" getSchoolName"/> <!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第一种写法,使用v-on或@) --> <Student v-on:atguigu="getStudentName" @demo="m1"/> <!-- 通过父组件给子组件绑定一个自定义事件实现:子给父传递数据(第二种写法,使用ref) --> <Student ref="student" @click.native="show"/> </div> </template> <script> import Student from './components/Student.vue' import School from './components/School.vue' export default { name: 'App', components: { Student,School }, data(){ return { msg:'你好啊', studentName:'' } }, methods:{ getSchoolName(name){ console.log('App收到了学校名:',name); }, getStudentName(name,...params){ console.log('App收到了学生名:',name.params); }, m1(){ console.log('demo事件被触发了:'); } }, mounted(){ this.$refs.student.$on('atguigu',this.getStudentName) } } </script> <style> .app { background-color: gray; padding: 5px; } </style>
School.vue
<template> <div class="school"> <h2>学校名称:{{ name }}</h2> <h2>学校地址:{{ address }}</h2> <button @click="sendSchoolName">把学校名给App</button> </div> </template> <script> export default { name:'School', data(){ return { name:'尚硅谷', address:'北京' } }, props:[' getSchoolName'], methods:{ sendSchoolName(){ this.getSchoolName(this.name) } } } </script> <style> .school { padding: 5px; background-color: aqua; } </style>
Student.vue
<template> `` <div class="student"> <h2>学生姓名:{{ name }}</h2> <h2>学校性别:{{ age }}</h2> <button @click="sendStudentName">把学生名给App</button> <button @click="unbind">解绑atguigu事件</button> <button @click="death">销毁当前Student组件的实例</button> </div> </template> <script> export default { name:'Student', data(){ return { name:'张三', address:'男' } }, methods:{ sendStudentName(){ //触发Student实例身上的atguigu事件 this.$emit('atguigu',this.name) this.$emit('demo') }, unbind(){ this.$off('atguigu')//解绑一个自定义事件 // this.off(['atguigu','demo'])//解绑多个自定义事件 this.$off() //解绑所有自定义事件 }, death(){ this.$destroy()//销毁当前Student组件的实例,销毁后所有Student实例的自定义事件全不奏效 } } } </script> <style scoped> .student { padding: 5px; margin-top: 30px; background-color: pink; } </style>
一种组件间通信的方式,适用于任意组件间通信
安装全局事件总线:
new Vue({
render: h => h(App),
beforeCreate(){
this.prototype.$bus = this //安装全局事件总线
}
}).$mount('#app')
使用事件总线:
接收数据:A组件想要接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身
methods(){
demo(data){......}
}
mounted(){
this.$bus.$on('xxx',this.demo)
}
提供数据:this.$bus.$emit('xxx',数据)
最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件
beforeDestroy(){
this.$bus.$off('hello')
}
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:'张三', address:'男' } }, methods:{ sendStudentName(){ // this.$bus.$emit('hello',this.name) pubsub.publish('hello',666) } } } </script> <style scoped> .student { padding: 5px; margin-top: 30px; background-color: pink; } </style>
School.vue
<template> <div class="school"> <h2>学校名称:{{ name }}</h2> <h2>学校地址:{{ address }}</h2> </div> </template> <script> import pubsub from 'pubsub-js' export default { name:'School', data(){ return { name:'尚硅谷', address:'北京' } }, mounted(){ // this.$bus.$on('hello',(data)=>{ // console.log('我是School组件,我收到了数据',data); // }) this.pubId = pubsub.subscribe('hello',function(msgName,data){ console.log('有人发布了hello消息,hello消息的回调执行了',data); }) }, beforeDestroy(){ // this.$bus.$off('hello') pubsub.unsubscribe(this.pubOd) } } </script> <style> .school { padding: 5px; background-color: aqua; } </style>
this.$nextTick(回调函数)
作用:在插入、更新或移除DOM元素时,在合适的时候给元素添加样式类名
图示:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y45dbEcf-1686448066084)(…/%E5%9B%BE%E7%89%87/image-20230608230944042.png)]
写法:
准备好样式:
使用<transition>
包裹要过渡的元素,并配置name属性
<Transition name='hello'>
<h1 v-show="isShow">你好啊</h1>
</Transition>
备注:若有多个元素需要过渡,则需要使用:<transition-group>
,且每个元素都要指定key值
在vue.config.js中添加如下配置:
//开启代理服务器(方式一)
// devServer: {
// proxy: 'http://localhost:5000'
// },
说明:
编写vue.config.js配置具体代理规则:
//开启代理服务器(方式二) devServer: { proxy: { '/atguigu': { //匹配所有以'/atguigu'开头的请求路径 target: 'http://localhost:5000',//代理目标的基础路径 pathRewrite:{'^/atguigu':''}, ws:true,//用于支持websocket // ws: true, // changeOrigin: false }, '/demo': { target: 'http://localhost:5001', pathRewrite:{'^/demo':''}, ws:true,//用于支持websocket // ws: true, // changeOrigin: false }, } }
说明:
App.vue
<template> <div class="container"> <Search/> <List/> </div> </template> <script> import axios from 'axios' import Search from './components/Search.vue' import List from './components/List.vue' export default { name: 'App', components:{ Search,List } } </script> <style> </style>
Search.vue
<template> <section class="jumbotron"> <h3 class="jumbotron-heading">Search Github Users</h3> <div> <input type="text" placeholder="enter the name you search" v-model="keyWord"/> <button @click="searchUsers">Search</button> </div> </section> </template> <script> import axios from 'axios' export default { name:'Search', data(){ return { keyWord:'' } }, methods:{ searchUsers(){ this.$bus.$emit('updateListData',{isFirst:false,isLoading:true,errMsg:'',users:[]}) axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then( response => { //请求成功后更新List数据 console.log('请求成功'); this.$bus.$emit('updateListData',{isLoading:false,errMsg:'',users:response.data.items}) }, error => { //请求失败后 this.$bus.$emit('updateListData',{isLoading:false,errMsg:error.message,users:[]}) } ) } } } </script> <style> </style>
List.vue
<template> <div class="row"> <!-- 展示用户列表 --> <div class="card" v-for="user in info.users" :key="user.login" v-show="info.users.length"> <a :href="user.html_url" target="_blank"> <img :src="user.avatar_url" style='width: 100px'/> </a> <p class="card-text">{{user.login}}</p> </div> <!-- 展示欢迎词 --> <h1 v-show="info.isFirst">欢迎使用!</h1> <!-- 展示加载中 --> <h1 v-show="info.isLoading">加载中....</h1> <!-- 展示错误信息 --> <h1 v-show="info.errMsg">{{ info.errMsg }}</h1> </div> </template> <script> export default { name:'List', data(){ return { info:{ isFirst:true, isLoading:false, errMsg:'', users:[ ] } } }, mounted(){ this.$bus.$on('updateListData',(dataObj)=>{ this.info = {...this.info,...dataObj} }) } } </script> <style scoped> .album { min-height: 50rem; /* Can be removed; just added for demo purposes */ padding-top: 3rem; padding-bottom: 3rem; background-color: #f7f7f7; } .card { float: left; width: 33.333%; padding: .75rem; margin-bottom: 2rem; border: 1px solid #efefef; text-align: center; } .card > img { margin-bottom: .75rem; border-radius: 100px; } .card-text { font-size: 85%; } </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>
作用域插槽
父组件中: <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>
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 scoped> .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">我是一些默认值,当使用者没有传递具体结构时,我会出现</slot> <slot name="footer">我是一些默认值,当使用者没有传递具体结构时,我会出现</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>
概念:专门在Vue中实现集中式状态(数据)管理的一个Vue插件。对Vue应用中多个组件的共享状态进行集中式的管理(读/写),也是组件间通信的方式,且适用于任意组件间通信
何时使用
创建文件:src/store/index.js
//该文件用于创建Vuex中最为核心的store //引入vue核心库 import Vue from 'vue' //引入Vuex import Vuex from 'vuex' Vue.use(Vuex) //准备actions——用于响应组件中的动作 const actions = {} //准备Mutations——用于操纵数据(state) const mutations = {} //准备state——用于存储函数 const state = {} //创建store并暴露 export default new Vuex.Store({ actions, mutations, state, })
在main.js中创建vm时传入store配置项
......
//引入store
import store from './store'
......
//创建vm
new Vue({
el:'#app',
render:h=>h(App),
store
})
初始化数据、配置actions、配置mutations,操作文件index.js
//该文件用于创建Vuex中最为核心的store //引入Vuex import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) //准备actions——用于响应组件中的动作 const actions = { jia(context,value){ context.commit('JIA',value) }, jian(context,value){ context.commit('JIAN',value) }, jiaOdd(context,value){ if(context.state.sum%2){ context.commit('JIA',value) } }, jiaWait(context,value){ setTimeout(()=>{ context.commit('JIA',value) },500) } } //准备Mutations——用于操纵数据(state) const mutations = { JIA(state,value){ state.sum += value }, JIAN(state,value){ state.sum -= value } } //准备state——用于存储函数 const state = { sum:0,//当前和 } //创建store export default new Vuex.Store({ actions, mutations, state, }) //导出/暴露store
组件中读取vuex中的数据:$store.state.sum
组件中修改vuex中的数据:$store.dispatch('action中的方法名',数据)
或$store.commit('mutations中的方法名',数据)
概念:当state中的数据需要经过加工后再使用时,可以使用getters加工
在index.js中追加getter配置
//该文件用于创建Vuex中最为核心的store //引入Vuex import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) //准备actions——用于响应组件中的动作 const actions = { jia(context,value){ context.commit('JIA',value) }, jian(context,value){ context.commit('JIAN',value) }, jiaOdd(context,value){ if(context.state.sum%2){ context.commit('JIA',value) } }, jiaWait(context,value){ setTimeout(()=>{ context.commit('JIA',value) },500) } } //准备Mutations——用于操纵数据(state) const mutations = { JIA(state,value){ state.sum += value }, JIAN(state,value){ state.sum -= value } } //准备state——用于存储函数 const state = { sum:0,//当前和 } //准备getters——用于将state中的数据进行加工 const getters = { bigSum(state){ return state.sum*10 } } //创建store export default new Vuex.Store({ actions, mutations, state, getters }) //导出/暴露store
组件中读取数据:$store.getter.bigSum
mapState方法:用于帮助我们映射state中的数据为计算属性
computed:{ // sum(){ // return $store.state.sum // }, // bigSum(){ // return $store.getters.bigSum // }, // school(){ // return $store.state.school // }, // subject(){ // return $store.state.subject // }, //借助mapState生成计算属性,从state中读取数据(对象写法) ...mapState({sum:'sum',school:'school',subject:'subject'}), //借助mapState生成计算属性,从state中读取数据(数组写法) //...mapState(['sum','school','subject']), }
mapGetters方法:用于帮助我们映射getters中的数据为计算属性
computed:{
// bigSum(){
// return $store.getters.bigSum
// },
//借助mapGetters生成计算属性,从getters中读取数据(对象写法)
...mapGetters({bigSum:'bigSum'}),
//借助mapGetters生成计算属性,从getters中读取数据(数组写法)
...mapGetters({bigSum:'bigSum'})
}
mapActions方法:用于帮助我们生成actions对话的方法,即包含:$store.dispatch(xxx)
函数
methods:{
//靠mapActions生成:incrementOdd、incrementWait(对象形式)
...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
//靠mapActions生成:incrementOdd、incrementWait(数组形式)
...mapActions(['jiaOdd','jiaWait'])
}
mapMutations方法:用于帮助我们生成与mutations对话的方法,即包含$store.commit(xxx)
函数
methods:{
//靠mapActions生成:increment、decrement(对象形式)
...mapMutations({increment:'JIA',decrement:'JIAN'}),
//靠mapMutations生成:JIA、JIAN(对象形式)
...mapMutations(['JIA','JIAN']),
}
备注:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象
index.js
//该文件用于创建Vuex中最为核心的store //引入Vuex import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) //准备actions——用于响应组件中的动作 const actions = { jia(context,value){ context.commit('JIA',value) }, jian(context,value){ context.commit('JIAN',value) }, jiaOdd(context,value){ if(context.state.sum%2){ context.commit('JIA',value) } }, jiaWait(context,value){ setTimeout(()=>{ context.commit('JIA',value) },500) } } //准备Mutations——用于操纵数据(state) const mutations = { JIA(state,value){ state.sum += value }, JIAN(state,value){ state.sum -= value } } //准备state——用于存储函数 const state = { sum:0,//当前和 school:'尚硅谷', subject:'前端' } //准备getters——用于将state中的数据进行加工 const getters = { bigSum(state){ return state.sum*10 } } //创建store export default new Vuex.Store({ actions, mutations, state, getters }) //导出/暴露store
Count.vue
<template> <div> <h1>当前求和为:{{ sum }}</h1> <h3>当前求和放大10倍后为:{{ bigSum }}</h3> <h3>我在{{ school }},学习{{ subject }}</h3> <select v-model.number="n"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button @click="increment(n)">+</button> <button @click="decrement(n)">-</button> <button @click="incrementOdd(n)">当前求和为奇数再加</button> <button @click="incrementWait(n)">等一等再加</button> </div> </template> <script> import {mapState,mapGetters,mapActions,mapMutations} from 'vuex' export default { name:'Count', data(){ return { n:1,//用户选择数字 } }, methods:{ // increment(){ // this.$store.commit('JIA',this.n) // }, // decrement(){ // this.$store.commit('JIAN',this.n) // }, //借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法) ...mapMutations({increment:'JIA',decrement:'JIAN'}), //借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法) // ...mapMutations(['JIA','JIAN']), // incrementOdd(){ // this.$store.dispatch('jiaOdd',this.n) // }, // incrementWait(){ // this.$store.dispatch('jiaWait',this.n) // }, ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'}) }, computed:{ // sum(){ // return $store.state.sum // }, // bigSum(){ // return $store.getters.bigSum // }, // school(){ // return $store.state.school // }, // subject(){ // return $store.state.subject // }, //借助mapState生成计算属性,从state中读取数据(对象写法) ...mapState({sum:'sum',school:'school',subject:'subject'}), //借助mapState生成计算属性,从state中读取数据(数组写法) //...mapState(['sum','school','subject']), //借助mapGetters生成计算属性,从getters中读取数据(对象写法) ...mapGetters({bigSum:'bigSum'}), //借助mapGetters生成计算属性,从getters中读取数据(数组写法) ...mapGetters({bigSum:'bigSum'}) } } </script> <style lang="css"> button { margin-left: 5px; } </style>
index.js
//该文件用于创建Vuex中最为核心的store //引入Vuex import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) //准备actions——用于响应组件中的动作 const actions = { jia(context,value){ context.commit('JIA',value) }, jian(context,value){ context.commit('JIAN',value) }, jiaOdd(context,value){ if(context.state.sum%2){ context.commit('JIA',value) } }, jiaWait(context,value){ setTimeout(()=>{ context.commit('JIA',value) },500) } } //准备Mutations——用于操纵数据(state) const mutations = { JIA(state,value){ state.sum += value }, JIAN(state,value){ state.sum -= value }, ADD_PERSON(state,value){ state.personList.unshift(value) } } //准备state——用于存储函数 const state = { sum:0,//当前和 school:'尚硅谷', subject:'前端', personList:[ {id:'001',name:'张三'} ] } //准备getters——用于将state中的数据进行加工 const getters = { bigSum(state){ return state.sum*10 } } //创建store export default new Vuex.Store({ actions, mutations, state, getters }) //导出/暴露store
Count.vue
<template> <div> <h1>当前求和为:{{ sum }}</h1> <h3>当前求和放大10倍后为:{{ bigSum }}</h3> <h3>我在{{ school }},学习{{ subject }}</h3> <h3 style="color: red;">下方组件的总人数是:{{ personList.length }}</h3> <select v-model.number="n"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button @click="increment(n)">+</button> <button @click="decrement(n)">-</button> <button @click="incrementOdd(n)">当前求和为奇数再加</button> <button @click="incrementWait(n)">等一等再加</button> </div> </template> <script> import {mapState,mapGetters,mapActions,mapMutations} from 'vuex' export default { name:'Count', data(){ return { n:1,//用户选择数字 } }, methods:{ //借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法) ...mapMutations({increment:'JIA',decrement:'JIAN'}), //借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法) // ...mapMutations(['JIA','JIAN']), ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'}) }, computed:{ //借助mapState生成计算属性,从state中读取数据(对象写法) // ...mapState({sum:'sum',school:'school',subject:'subject'}), //借助mapState生成计算属性,从state中读取数据(数组写法) ...mapState(['sum','school','subject','personList']), //借助mapGetters生成计算属性,从getters中读取数据(数组写法) ...mapGetters({bigSum:'bigSum'}) } } </script> <style lang="css"> button { margin-left: 5px; } </style>
Person.vue
<template> <div> <h1>人员列表</h1> <h3 style="color: red;">上方组件的求和为:{{ sum }}</h3> <input type="text" placeholder="请输入名字" v-model="name"> <button @click="add">添加</button> <ul> <li v-for="p in personList" :key="p.id">{{ p.name }}</li> </ul> </div> </template> <script> import { mapState } from 'vuex'; import {nanoid} from 'nanoid' export default { name:'Person', data(){ return { name:'' } }, computed:{ // personList(){ // return this.$store.state.personList // } ...mapState(['personList']), sum(){ return this.$store.state.sum } }, methods:{ add(){ const obj = {id:nanoid,name:this.name} this.$store.commit('ADD_PERSON',obj) this.name = '' } } } </script>
目的:让代码更好维护,让多种分类更加明确
修改store.js
const countAbout = { namespaced:true,//开启命名空间 state:{x:1}, mutations: { ... }, actions: { ... }, getters: { bigSum(state){ return state.sum * 10 } } } const personAbout = { namespaced:true,//开启命名空间 state:{ ... }, mutations: { ... }, actions: { ... } } const store = new Vuex.Store({ modules: { countAbout, personAbout } })
开启命名空间后,组件中读取state数据
//方式一:自己直接读取
this.$store.state.personAbout.list
//方式二:借助mapState读取:
...mapState('countAbout',['sum','school','subject']),
开启命名空间后,组件读取getters数据
//方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName']
//方式二:借助mapGetters读取:
...mapGetters('countAbout',['bigSum'])
开启命名空间后,组件中调用dispatch
//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions:
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
开启命名空间后,组件中调用commit
//方式一:自己直接commit
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二:借助mapMutations:
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
src/store/index.js
//引入Vue核心库 import Vue from 'vue' //引入Vuex import Vuex from 'vuex' //引入count import countOptions from './count' //引入person import personOptions from './person' //应用Vuex插件 Vue.use(Vuex) //创建并暴露store export default new Vuex.Store({ modules:{ countAbout:countOptions, personAbout:personOptions, } })
src/store/count.js
export default{ namespaced:true, actions:{ addOdd(context,value){ console.log("actions中的addOdd被调用了") if(context.state.sum % 2){ context.commit('ADD',value) } }, addWait(context,value){ console.log("actions中的addWait被调用了") setTimeout(()=>{ context.commit('ADD',value) },500) } }, mutations:{ ADD(state,value){ state.sum += value }, SUBTRACT(state,value){ state.sum -= value } }, state:{ sum:0, //当前的和 name:'JOJO', school:'尚硅谷', }, getters:{ bigSum(state){ return state.sum * 10 } } }
src/store/person.js
import axios from "axios" import { nanoid } from "nanoid" export default{ namespaced:true, actions:{ addPersonWang(context,value){ if(value.name.indexOf('王') === 0){ context.commit('ADD_PERSON',value) }else{ alert('添加的人必须姓王!') } }, addPersonServer(context){ axios.get('http://api.uixsj.cn/hitokoto/get?type=social').then( response => { context.commit('ADD_PERSON',{id:nanoid(),name:response.data}) }, error => { alert(error.message) } ) } }, mutations:{ ADD_PERSON(state,value){ console.log('mutations中的ADD_PERSON被调用了') state.personList.unshift(value) } }, state:{ personList:[ {id:'001',name:'JOJO'} ] }, getters:{ firstPersonName(state){ return state.personList[0].name } } }
count.vue
<template> <div> <h1>当前求和为:{{sum}}</h1> <h3>当前求和的10倍为:{{bigSum}}</h3> <h3>我是{{name}},我在{{school}}学习</h3> <h3 style="color:red">Person组件的总人数是:{{personList.length}}</h3> <select v-model.number="n"> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button @click="increment(n)">+</button> <button @click="decrement(n)">-</button> <button @click="incrementOdd(n)">当前求和为奇数再加</button> <button @click="incrementWait(n)">等一等再加</button> </div> </template> <script> import {mapState,mapGetters,mapMutations,mapActions} from 'vuex' export default { name:'Count', data() { return { n:1, //用户选择的数字 } }, methods: { ...mapMutations('countAbout',{increment:'ADD',decrement:'SUBTRACT'}), ...mapActions('countAbout',{incrementOdd:'addOdd',incrementWait:'addWait'}) }, computed:{ ...mapState('countAbout',['sum','school','name']), ...mapGetters('countAbout',['bigSum']), ...mapState('personAbout',['personList']) } } </script> <style> button{ margin-left: 5px; } </style>
person.vue
<template> <div> <h1>人员列表</h1> <h3 style="color:red">Count组件求和为:{{sum}}</h3> <h3>列表中第一个人的名字是:{{firstPersonName}}</h3> <input type="text" placeholder="请输入名字" v-model="name"> <button @click="add">添加</button> <button @click="addWang">添加一个姓王的人</button> <button @click="addPerson">随机添加一个人</button> <ul> <li v-for="p in personList" :key="p.id">{{p.name}}</li> </ul> </div> </template> <script> import {nanoid} from 'nanoid' export default { name:'Person', data() { return { name:'' } }, computed:{ personList(){ return this.$store.state.personAbout.personList }, sum(){ return this.$store.state.countAbout.sum }, firstPersonName(){ return this.$store.getters['personAbout/firstPersonName'] } }, methods: { add(){ const personObj = {id:nanoid(),name:this.name} this.$store.commit('personAbout/ADD_PERSON',personObj) this.name = '' }, addWang(){ const personObj = {id:nanoid(),name:this.name} this.$store.dispatch('personAbout/addPersonWang',personObj) this.name = '' }, addPerson(){ this.$store.dispatch('personAbout/addPersonServer') } }, } </script>
vue的一个插件库,专门用来实现SPA应用
安装vue-router,命令:npm i vue-router
应用插件:Vue.user(VueRouter)
编写router配置项
// 该文件专门用于创建整个应用的路由器 import VueRouter from 'vue-router' //引入组件 import About from '../components/About' import Home from '../components/Home' //创建一个路由器并暴露 export default new VueRouter({ routes:[ { path:'/about', component:About }, { path:'/home', component:Home } ] })
实现切换(active-class可配置高亮样式)
<router-link class="list-group-item" active-class="active" to="/about">About</router-link>
<router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
指定展示位置
<router-view></router-view>
$route
属性,里面存储着自己的路由信息$router
属性获取到配置路由规则,使用children配置项
// 该文件专门用于创建整个应用的路由器 import VueRouter from 'vue-router' //引入组件 import About from '../pages/About' import Home from '../pages/Home' import News from '../pages/News' import Message from '../pages/Message' //创建一个路由器并暴露 export default new VueRouter({ routes:[ { path:'/about', component:About }, { path:'/home', component:Home, children:[ { path:'news', component:News }, { path:'message', component:Message } ] } ] })
跳转(要写完整路径)
<router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>
传递参数
<!-- 跳转路由并携带query参数,to的字符串写法 -->
<!-- <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{ m.title }}</router-link> -->
<!-- 跳转路由并携带query参数,to的对象写法 -->
<router-link :to="{
path:'/home/message/detail',
query:{
id:m.id,
title:m.title
}
}">
{{ m.title }}
</router-link>
接受参数
$route.query.id
$route.query.title
作用:可以简化路由的跳转
如何使用
给路由命名
{ path:'/demo', component:Demo, children:[ { path:'test', component:Test, children:[ { name:'hello' //给路由命名 path:'welcome', component:Hello, } ] } ] }
简化跳转
<!--简化前,需要写完整的路径 --> <router-link to="/demo/test/welcome">跳转</router-link> <!--简化后,直接通过名字跳转 --> <router-link :to="{name:'hello'}">跳转</router-link> <!--简化写法配合传递参数 --> <router-link :to="{ name:'hello', query:{ id:666, title:'你好' } }" >跳转</router-link>
配置路由,声明接受params参数
{ path:'/home', component:Home, children:[ { path:'news', component:News }, { component:Message, children:[ { name:'xiangqing', path:'detail/:id/:title', //使用占位符声明接收params参数 component:Detail } ] } ] }
传递参数
<!-- 跳转并携带params参数,to的字符串写法 -->
<router-link :to="/home/message/detail/666/你好">跳转</router-link>
<!-- 跳转并携带params参数,to的对象写法 -->
<router-link
:to="{
name:'xiangqing',
params:{
id:666,
title:'你好'
}
}"
>跳转</router-link>
特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置
接收参数
$route.params.id
$route.params.title
作用:让路由组件更方便的收到参数
{ name:'xiangqing', path:'detail/:id', component:Detail, //第一种写法:props值为对象,该对象中所有的key-value的组合最终会通过props传给Detail组件 //props:{a:900} //第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件 //props:true //第三种写法:props值为函数,该函数返回的对象种每一组key-value都会通过props传给Detail组件 props(route){ return { id:route.query.id, title:route.query.title } } }
<router-link replace ......></router-link>
作用:不借助<router-link>
实现路由跳转,让路由跳转更加灵活
具体编码:
this.$router.push({ name:'xiangqing', params:{ id:xxx, title:xxx } }) this.$router.replace({ name:'xiangqing', params:{ id:xxx, title:xxx } }) this.$router.forward() //前进 this.$router.back() //后退 this.$router.go() //可前进也可后退
作用:让步展示的路由组件保持挂载,不被销毁
具体编码:
<keep-alive include="News">
<router-view></router-view>
</keep-alive>
<!-- 缓存多个路由组件 -->
<keep-alive include="['News','Message']">
<router-view></router-view>
</keep-alive>
activated
路由组件被激活时触发deactivated
路由组件失活时触发作用:对路由进行权限控制
分类:全局守卫、独享守卫、组件内守卫
全局守卫:
//全局前置守卫:初始化时执行、每次路由切换前执行 router.beforeEach((to,from,next)=>{ console.log('beforeEach',to,from) if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制 if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则 next() //放行 }else{ alert('暂无权限查看') } }else{ next() //放行 } }) //全局后置守卫:初始化时执行、每次路由切换后执行 router.afterEach((to,from) => { console.log('afterEach',to,from) if(to.meta.title){ document.title = to.meta.title //修改网页的title }else{ document.title = 'vue_test' } })
独享守卫
beforeEnter(to,from,next){
console.log('beforeEnter',to,from)
if(localStorage.getItem('school') === 'atguigu'){
next()
}else{
alert('暂无权限查看')
}
}
组件内守卫
//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {...},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {...},
对于一个url来说,什么是hash值?——#及其后面的内容就是hash值
hash值不会包含在HTTP请求中,即:hash值不会带给服务器
hash模式:
history模式:
class=“active” to=“/home/news”>News
## 6.路由传参 1. 传递参数 ```vue <!-- 跳转路由并携带query参数,to的字符串写法 --> <!-- <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{ m.title }}</router-link> --> <!-- 跳转路由并携带query参数,to的对象写法 --> <router-link :to="{ path:'/home/message/detail', query:{ id:m.id, title:m.title } }"> {{ m.title }} </router-link>
接受参数
$route.query.id
$route.query.title
作用:可以简化路由的跳转
如何使用
给路由命名
{ path:'/demo', component:Demo, children:[ { path:'test', component:Test, children:[ { name:'hello' //给路由命名 path:'welcome', component:Hello, } ] } ] }
简化跳转
<!--简化前,需要写完整的路径 --> <router-link to="/demo/test/welcome">跳转</router-link> <!--简化后,直接通过名字跳转 --> <router-link :to="{name:'hello'}">跳转</router-link> <!--简化写法配合传递参数 --> <router-link :to="{ name:'hello', query:{ id:666, title:'你好' } }" >跳转</router-link>
配置路由,声明接受params参数
{ path:'/home', component:Home, children:[ { path:'news', component:News }, { component:Message, children:[ { name:'xiangqing', path:'detail/:id/:title', //使用占位符声明接收params参数 component:Detail } ] } ] }
传递参数
<!-- 跳转并携带params参数,to的字符串写法 -->
<router-link :to="/home/message/detail/666/你好">跳转</router-link>
<!-- 跳转并携带params参数,to的对象写法 -->
<router-link
:to="{
name:'xiangqing',
params:{
id:666,
title:'你好'
}
}"
>跳转</router-link>
特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置
接收参数
$route.params.id
$route.params.title
作用:让路由组件更方便的收到参数
{ name:'xiangqing', path:'detail/:id', component:Detail, //第一种写法:props值为对象,该对象中所有的key-value的组合最终会通过props传给Detail组件 //props:{a:900} //第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件 //props:true //第三种写法:props值为函数,该函数返回的对象种每一组key-value都会通过props传给Detail组件 props(route){ return { id:route.query.id, title:route.query.title } } }
<router-link replace ......></router-link>
作用:不借助<router-link>
实现路由跳转,让路由跳转更加灵活
具体编码:
this.$router.push({ name:'xiangqing', params:{ id:xxx, title:xxx } }) this.$router.replace({ name:'xiangqing', params:{ id:xxx, title:xxx } }) this.$router.forward() //前进 this.$router.back() //后退 this.$router.go() //可前进也可后退
作用:让步展示的路由组件保持挂载,不被销毁
具体编码:
<keep-alive include="News">
<router-view></router-view>
</keep-alive>
<!-- 缓存多个路由组件 -->
<keep-alive include="['News','Message']">
<router-view></router-view>
</keep-alive>
activated
路由组件被激活时触发deactivated
路由组件失活时触发作用:对路由进行权限控制
分类:全局守卫、独享守卫、组件内守卫
全局守卫:
//全局前置守卫:初始化时执行、每次路由切换前执行 router.beforeEach((to,from,next)=>{ console.log('beforeEach',to,from) if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制 if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则 next() //放行 }else{ alert('暂无权限查看') } }else{ next() //放行 } }) //全局后置守卫:初始化时执行、每次路由切换后执行 router.afterEach((to,from) => { console.log('afterEach',to,from) if(to.meta.title){ document.title = to.meta.title //修改网页的title }else{ document.title = 'vue_test' } })
独享守卫
beforeEnter(to,from,next){
console.log('beforeEnter',to,from)
if(localStorage.getItem('school') === 'atguigu'){
next()
}else{
alert('暂无权限查看')
}
}
组件内守卫
//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {...},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {...},
对于一个url来说,什么是hash值?——#及其后面的内容就是hash值
hash值不会包含在HTTP请求中,即:hash值不会带给服务器
hash模式:
history模式:
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。