当创建新的vue项目时,遇到无法加载文件 E:\node\node_global\vue.ps1,因为在此系统上禁止运行脚本的错误,以下为解决办法。
vue中文文档
菜鸟教程:Vue.js
该笔记只是我看视频和参考以上资料的vue学习基础笔记,还没有包含vue脚手架的学习(重点),所以想快速上手项目的话还是随便看看这个再重点看vue cli的学习吧。
一、Vue框架的简介
1、从MVC模型到MVVM模型
前端MVC、MVVM的简单实现(转载)
MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。(一种设计模式,前后端都有)
M->model->模型->数据
V->View->视图->用户所见界面
C->Control->控制器->事件交互->如何根据视图与用户交互后改变数据(比如表单提交和点击监听)
MVVM模型如图下所示。
2、Vue的特点
在Vue中一个核心的概念就是:数据驱动,避免手动操作DOM元素。这样的话,可以让前端程序员可以更多的时间关注数据业务逻辑,而不是关注DOM如何渲染了。(能够帮助我们减少不必要的DOM操作(虚拟DOM);提高渲染效率;双向数据绑定)
双向数据绑定:指的是vue实例中的data与其渲染的DOM元素的内容保持一致,无论谁被改变,另一方会相应的更新为相同的数据。这是通过设置属性访问器实现的。如下图所示,可以直接在console中改变数据。
双向绑定原理(转载)
Vue数据绑定以及双向绑定原理分析(转载)
浅谈Vue的单向绑定和双向绑定(转载)
什么是虚拟DOM:在js的内存里构建类似于DOM的对象,去拼装数据,拼装完整后,把数据整体解析,一次性插入html里去,这就形成了虚拟DOM。
一个简单的vue例子:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="/npm/vue"></script></head><body><!--VIEW--><div id="app">{{title}}<h1>{{msg}}</h1></div><script type="text/javascript">console.log(Vue)//实例化VUE对象let app=new Vue({el:'#app',//el是元素的意思//Modeldata:{title:'hello Vue!',msg:'前端VUE'}})</script></body></html>
二、Vue基本操作
vue中template的作用及使用(转载)
1、条件渲染
v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="/npm/vue"></script><style type="text/css">#pane {width:200px;height: 200px;background-color: aquamarine;}</style></head><body><!--条件渲染--><!--VIEW--><div id="app"><h1>用户名:{{username}}</h1><h3 v-if="isVIP">用户类型:VIP</h3><!--注意v-if和v-else中间不能有其他元素--><h3 v-else>用户类型:普通用户</h3><hr><h1>用户允许登录时间</h1><h3 v-if="age>18">允许24小时登录</h3><h3 v-else-if="age>14">允许登录8小时</h3><h3 v-else>允许登录4小时</h3><hr><div v-show="isShow" id="pane">HelloVue</div><button @click="showBtn">切换显示内容</button></div><script type="text/javascript">let app=new Vue({el:"#app",data:{username:"小明",isVIP:true,age:24,isShow:true},methods:{showBtn:function(e){//触发事件econsole.log(e);app.isShow=!app.isShow;}}})/*v-if:不显示时,第一次直接不渲染,如果内容已经显示将其内容改为不显示,内容直接从DOM去除。只是渲染一次内容用v-if.v-show:不显示时,就会改为display:none,但是会渲染在DOM上。反复需要切换内容,使用v-show。*/</script></body></html>
一个小例子(点击按钮切换标题):
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="/npm/vue"></script></head><body><!--tab切换--><!--VIEW--><div id="app"><h3 v-if="tab==1">首页</h3><h3 v-else-if="tab==2">新闻页</h3><h3 v-else>个人中心</h3><!--<h3 v-show="tab==1">首页</h3><h3 v-show="tab==2">新闻页</h3><h3 v-show="tab==3">个人中心</h3>--><button @click="tabChange" data-id="1">首页</button><button @click="tabChange" data-id="2">新闻</button><button @click="tabChange" data-id="3"> 个人中心</button></div><script type="text/javascript">let app=new Vue({el:"#app",data:{tab:1},methods:{tabChange:function(e){//console.log(e);let tabid=e.target.dataset.id;//console.log(this);//app.tab=tabid跟this.tab=tabid等同this.tab=tabid;}}})</script></body></html>
2、列表渲染
我们可以用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。
key的作用:Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。加上key属性后表示“这两个元素是完全独立的,不要复用它们”,从而会重新渲染。
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="/npm/vue"></script></head><body><div id="app"><h3>明星列表</h3><ul><li v-for="item in stars">{{item}}</li></ul><h3>学生列表</h3><ul><li v-for="item in student"><h4>{{item.studentName}}</h4><p>年龄:{{item.age}}--------学校:{{item.school}}</p></li></ul><h3>学生列表(带索引值)</h3><ul><li v-for="item,key in student"><h4>索引值:{{key}}----{{item.studentName}}</h4><p>年龄:{{item.age}}--------学校:{{item.school}}</p></li></ul><h3>循环对象</h3><ul><li v-for="item,key in student[0]"><!--item,key顺序不能改-->key:{{key}}-----value:{{item}}</li></ul><img :src="student[0].imheader"><h3>条件+循环渲染(将偶数年龄的学生渲染出来):先循环再判断</h3><ol><li v-for="item,index in student" v-if="item.age%2==0" :key="index"><!--如果“”中是变量则key需要加冒号,如果是字符串数字等则不需要--><h4>索引值:{{index}}----{{item.studentName}}</h4><p>年龄:{{item.age}}--------学校:{{item.school}}</p></li></ol></div><script type="text/javascript">var app=new Vue({el:"#app",data:{stars:['周杰伦','陈奕迅','陈信宏','林俊杰'],student:[{studentName:"贺朝",age:17,school:"清华大学",imheader:"/images/mix/searchicon.png"},{studentName:"谢俞",age:18,school:"北京大学"},{studentName:"蒋丞",age:19,school:"中国人民大学"},{studentName:"顾飞",age:20,school:"浙江大学"}]}})</script></body></html>
3、模板语法
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="/npm/vue"></script><style type="text/css">#login{background-color: aquamarine;}#register{background-color: pink;}</style></head><body><div id="app"><h1>{{msg}}</h1><!--一次性插入,不再修改--><h1 v-once>{{msg}}</h1><!--插入html内容--><h1>{{htmlTxt}}</h1><h1 v-html="htmlTxt"></h1><!--修改属性内容--><!--绑定动态属性,全写--><div v-bind:id="idname"><h1>登录</h1></div><!--绑定动态属性,省略--><div :id="idname"><h1>登录</h1></div><!--模板语言的表达式应用--><div>{{firstname+lastname}}</div><!--三元运算符--><div>{{isVIP?"欢迎VIP用户回来!":"普通用户请充值"}}</div><!--事件的绑定--><button v-on:click="changeBtn">改变背景</button><button @click="changeBtn">改变背景</button></div><script>var app=new Vue({el:"#app",data:{msg:"hello Vue",htmlTxt:'<span>hello</span>',idname:"login",firstname:"张",lastname:"三",isVIP:true},methods:{changeBtn:function(){document.body.style.background="skyblue";}}})</script></body></html>
4、计算属性和侦听器
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。这时就用到了计算属性,它可以简化模板逻辑,又可以将计算的结果进行缓存,不用重新渲染,提高了性能。
计算属性与方法的区别
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="/npm/vue"></script></head><body><div id="app"><!--一般情况下写法--><h1>{{firstname+lastname}}</h1><!--计算属性--><h1>{{fullname}}</h1><!--一般情况下单词逆序--><h1>{{word.split("").reverse().join("")}}</h1><!--计算属性--><h1>{{reverseword}}</h1><!--计算属性,显示年龄为偶数的学生信息--><ul><li v-for="item,index in evenstudent"><h3>{{item.studentName}}</h3><h4>{{item.age}}-----{{item.school}}</h4></li></ul></div><script type="text/javascript">var app=new Vue({el:"#app",data:{firstname:"张",lastname:"三",word:"english",student:[{studentName:"贺朝",age:17,school:"清华大学",imheader:"/images/mix/searchicon.png"},{studentName:"谢俞",age:18,school:"北京大学"},{studentName:"蒋丞",age:19,school:"中国人民大学"},{studentName:"顾飞",age:20,school:"浙江大学"}]},computed:{fullname:function(){//会将计算的结果进行缓存,只要this.firstname和lastname变量的内容不改变,就不会重新计算return this.firstname+this.lastname;},reverseword:function(){return this.word.split("").reverse().join("");},evenstudent:function(){let result=this.student.filter(function(item){return item.age%2==0});return result;}}})</script></body></html>
待补充侦听器和set,get方法
5、Class 与 Style 绑定
绑定 HTML Class绑定内联样式
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="/npm/vue"></script><style>.page{background-color: orange;width: 200px;height: 200px;display: none;}.active{display: block;}</style></head><body><div id="app"><!--通过对象方式决定是否存在某个类--><div class="page" :class="{active:isTrue}"></div><!--直接放置对象--><div class="page" :class="styleObj"></div><!--放置数组--><div class="page" :class="styleAr"></div><!--放置字符串--><div class="page" :class="styleSt"></div><!--数组和对象混合使用--><div class="page" :class="styleArrObj"></div><!--css内联样式变量拼接--><div style="width: 100px;height: 100px;background-color: skyblue;" :style="{border:boderWidth+'px solid red',padding:paddingWidth+'px'}"></div><!--css内联样式放置对象--><div :style="styleObjcss"></div><!--css数组的方式拼接--><div :style="styleObjArrcss"></div></div><script>var app=new Vue({el:"#app",data:{isTrue:true,styleObj:{active:true,laochen:true,"col-lg-6":true},//可直接用数组的方式进行添加与删除比如app.styleAr.push("...")/popstyleAr:['col-xs-12','red-bg'],styleSt:"abc def qwer",styleArrObj:['abc',{active:true}],boderWidth:50,paddingWidth:10,styleObjcss:{width:"100px",height:"100px",padding:"50px",'background-color':'skyblue'},styleObjArrcss:[{width:"100px",height:"100px",padding:"50px",'background-color':'skyblue'},{border:"10px solid red"}]}})</script></body></html>
一个小例子(侧边栏的显示):
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="/npm/vue"></script><style>*{margin: 0;padding: 0;box-sizing: border-box;}.page{width: 100vw;height: 100vh;background-color: skyblue;position: fixed;left: 0;top: 0;}.r1Menu{width: 50vw;height: 100vw;background-color: slateblue;position: fixed;top: 0;left: 0;transform: translateX(100vw);transition: transform 0.5s;}.r2Menu{width: 50vw;height: 100vw;background-color:palegoldenrod;position: fixed;top: 0;left: 0;transform: translateX(100vw);transition: transform 0.5s;}.active{transform: translateX(50vw);}</style></head><!--菜单栏与侧边栏的切换--><body><div id="app"><div class="page">首页<button @click="changeMenu" type="button">切换侧边栏</button></div><div class="r1Menu" :class="{active:isShow}" >侧边栏1</div><div class="r2Menu" :style="{transform:'translateX('+rmenuWidth+'vw)'}">侧边栏2</div></div><script>var app=new Vue({el:"#app",data:{isShow:false,rmenuWidth:100},methods:{changeMenu:function(){this.isShow=!this.isShow;if(this.rmenuWidth==100){this.rmenuWidth=70;}else{this.rmenuWidth=100;}}}})</script></body></html>
6、事件处理
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="/npm/vue"></script></head><body><div id="app"><h1>点击次数:{{count}}</h1><!--某个事件中可以放方法名也可以放表达式--><!--可以使用表达式完成事件操作--><button @click="count+=1" type="button">点击1</button><!--获取事件对象--><button @click="clickEvent" type="button">点击2</button><!--有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法--><ul><li v-for="item,index in stars" @click="clickEvent(index,item,$event)">索引值-----{{index}}----名字:{{item}}</li></ul><!--事件修饰符--><!--stop修饰符:阻止冒泡事件向上传递(因为点击按钮会出发clickChild、clickParent两个事件,加上stop后只触发clickChild)--><div class="btnParent" @click="clickParent"><button @click.stop="clickChild">按钮</button></div><form action="" method="POST"><!--绑定输入框回车事件--><input type="text" @keydown.enter.f1="searchWeather" name="username" v-model="city"/><!--prevent修饰符:阻止默认事件--><input @click.prevent="searchWeather" type="submit" value="提交"/></form><!--once修饰符表示只触发一次--><div id="weather"><h1>{{tmp}}</h1><h1>{{brief}}</h1></div></div><script>//自定义按键修饰符,这里定义了f1Vue.config.keyCodes.f1 = 112var app=new Vue({el:"#app",data:{count:0,stars:['周杰伦','陈奕迅','林俊杰','阿信'],city:"广州",tmp:"",brief:""},methods:{clickEvent:function(index,value,event){console.log(event);console.log(this);console.log(index);console.log(value);this.count+=1;},clickParent:function(){console.log("clickParent");},clickChild:function(){console.log("clickChild");},searchWeather:async function(){console.log("searchWeather");console.log(this.city);let httpUrl=`https://free-/s6/weather/now?location=${this.city}&key=6731996e93b2412e8778a99d53b07a89`;let res=await fetch(httpUrl);let result=await res.json();console.log(result);let now=result.HeWeather6[0].now;this.tmp=now.tmp;this.brief=now.cond_txt;}}})</script></body></html>
七、表单输入绑定
我们可以用 v-model 指令在表单<input>
、<textarea>
及<select>
元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="/npm/vue/dist/vue.js"></script></head><body><div id="app"><h1>单行文本输入框</h1><p>{{username}}</p><!--.lazy表示等文本框输入完后双向绑定数据才进行改变,.trim表示自动忽略空格--><input type="text" name="username" v-model.lazy.trim="username"/><h1>多行文本输入框</h1><p>{{username}}</p><textarea v-model="username"></textarea><h2>复选框</h2><span v-for="item in fruits">{{item}}<input type="checkbox" v-model="checkFruits" :value="item"/></span><h2>{{checkFruits}}</h2><h2>单选框</h2><span v-for="item in fruits">{{item}}<input type="radio" v-model="radioFruits" :value="item"/></span><h2>{{radioFruits}}</h2><h2>选项框:选择你居住的城市</h2><select v-model="chooseCity"><option v-for="item in city" :value="item">{{item}}</option></select><p>{{chooseCity}}</p><h2>选项框(多选):选择你喜欢的城市</h2><select v-model="moreCity" multiple="multiple"><!--多选时需要按住ctrl键--><option v-for="item in city" :value="item">{{item}}</option></select><p>{{moreCity}}</p><h1>将字符串变为数字</h1><input type="text" name="age" v-model.number="age" /></div><script>let app=new Vue({el:"#app",data:{username:"xiaoming",fruits:['apple','banana','orange'],checkFruits:[],radioFruits:[],city:['shanghai','guangzhou','hangzhou'],chooseCity:"",moreCity:[],age:16}})</script></body></html>
八、过渡动画
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="/npm/vue/dist/vue.js"></script><style>.content{width: 200px;height: 200px;background-color: skyblue;}.fade-enter-active, .fade-leave-active {transition: opacity .5s;}.fade-enter, .fade-leave-to {opacity: 0;}/*v-enter-active 和 v-leave-active 可以控制进入/离开过渡的过程时间,延迟和曲线函数*/.slide-enter-active, .slide-leave-active {transition: all 5s;}/*定义进入过渡的开始状态和离开过渡的开始状态*/.slide-enter, .slide-leave-to {transform:translateX(500px);opacity: 0;}</style></head><!--过渡动画--><body><div id="app"><transition name="slide"><div v-if="isShow" class="content"></div></transition><button @click="changeEvent">切换内容</button></div><script>let app=new Vue({el:"#app",data:{isShow:true},methods:{changeEvent:function(){this.isShow=!this.isShow;}}})</script></body></html>
滑动侧边栏菜单的vue实现:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="/npm/vue"></script><style>*{margin: 0;padding: 0;box-sizing: border-box;}.page{width: 100vw;height: 100vh;background-color: skyblue;position: fixed;left: 0;top: 0;}.r1Menu{width: 50vw;height: 100vw;background-color: slateblue;position: fixed;top: 0;left: 0;transform: translateX(50vw);}/*自动追加类名*/.slideRight-enter-active,.slideRight-leave-active{transition:all 5s;}.slideRight-enter,.slideRight-leave-to{transform: translateX(100vw);}</style></head><!--菜单栏与侧边栏的切换--><body><div id="app"><div class="page">首页<button @click="changeMenu" type="button">切换侧边栏</button></div><transition name="slideRight"><div class="r1Menu" v-if="isShow" >侧边栏1</div></transition><script>var app=new Vue({el:"#app",data:{isShow:false,},methods:{changeMenu:function(){this.isShow=!this.isShow;}}})</script></body></html>
九、Vue实例的生命周期与组件生命周期
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="/npm/vue"></script></head><body><div id="app"><h1>{{msg}}</h1><h1 :class="className">类名绑定</h1></div><script>let app=new Vue({el:"#app",data:{msg:"helloVue",className:"rubg"},beforeCreate(){console.log('beforeCreate')console.log(this)console.log(this.msg)console.log(this.clickEvent)//此时数据data和事件方法methods还未绑定到app对象上},created(){//此时数据data和事件方法methods绑定到应用对象app上console.log('created')},beforeMount(){//渲染之前,根据数据生成的DOM对象是获取不到的console.log('beforeMount')let dom=document.querySelector(".rubg")console.log(dom)},mounted(){//渲染之后,可以获取数据生成的DOM对象console.log('mounted')let dom=document.querySelector(".rubg")console.log(dom)},methods:{clickEvent:function(){}},beforeUpdate(){//数据更改,但内容未更改之前console.log('beforeUpdate')},updated(){//内容已更新完成console.log('updated')},beforeDestroy(){//应用销毁之前console.log('beforeDestroy')},destroyed(){//应用销毁之后console.log('destroy')}})</script></body></html>
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="/npm/vue"></script></head><body><div id="app"><hello-com v-if="isShow"></hello-com><!--v-show当isShow为false时,不会销毁组件,而是让display:none--><hello-com v-show="isShow"></hello-com></div><script>ponent('hello-com',{template:'<div><h1>{{name}}</h1><button @click="changeData">修改数据</button></div>',data:function(){return {name:"hello ty"}},methods:{changeData:function(){this.name="hello jay"}},beforeCreate(){console.log('beforeCreate')//此时数据data和事件方法methods还未绑定到app对象上},created(){//此时数据data和事件方法methods绑定到应用对象app上console.log('created')},beforeMount(){//渲染之前,根据数据生成的DOM对象是获取不到的console.log('beforeMount')},mounted(){//渲染之后,可以获取数据生成的DOM对象console.log('mounted')},beforeUpdate(){//数据更改,但内容未更改之前console.log('beforeUpdate')},updated(){//内容已更新完成console.log('updated')},beforeDestroy(){//应用销毁之前console.log('beforeDestroy')},destroyed(){//应用销毁之后console.log('destroy')}})let app=new Vue({el:"#app",data:{isShow:true},})</script></body></html>
十、组件基础
1、全局组件or局部组件
全局组件(先注册组件再实例化)
<!--全局变量--><div id="app"><runoob></runoob></div><script>// 注册ponent('runoob', {template: '<h1>自定义组件!</h1>'})// 创建根实例new Vue({el: '#app'})</script>
局部组件:这样组件只能在这个实例中使用
<div id="app"><runoob></runoob></div><script>var Child = {template: '<h1>自定义组件!</h1>'}// 创建根实例new Vue({el: '#app',components: {// <runoob> 将只在父模板可用'runoob': Child}})</script>
2、组件间的传值
从父组件传到子组件(传数据):利用 props和v-bind。
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="/npm/vue"></script></head><!--组件之间的传值:从父组件传到子组件--><body><div id="app"><ul><!--从父组件传值到子组件--><!--静态属性--><school school-name="清华北大"></school><!--动态属性--><school v-bind:school-name="'上海师大'"></school><!--动态属性--><school v-bind:school-name="schoolList[0]"></school><!--循环传值组件--><school v-for="item,index in schoolList" :key="'abc'+index" :index='index' :school-name="item"></school></ul></div><script>//学校组件ponent("school",{props:['schoolName','index'],template:`<li><h3>{{index}}学校名称:{{schoolName}}</h3></li>`,})//根组件let app=new Vue({el:"#app",data:{schoolList:['qinghua','beida','hangdian','xiamen']}})</script></body></html>
从子组件传到父组件(传数据):注意每个组件都有各自的触发方法在methods中,通过在子组件触发方法中写this.$emit(绑定的父组件触发事件名,参数)进行传输数据。
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="/npm/vue"></script></head><!--组件之间的传值:从子组件传到父组件--><body><div id="app"><ul><!--循环传值组件--><school v-for="item,index in schoolList" @childparent='changeEvent' :key="'abc'+index" :index='index' :schoolname="item"></school></ul><h2>选中的学校是:{{chooseSchool}}</h2></div><script>//学校组件ponent("school",{props:['schoolname','index'],template:`<li><h3>{{index}}学校名称:{{schoolname}}</h3><button @click="chooseEvent(schoolname)">选择学校</button></li>`,methods:{chooseEvent:function (schoolname) {console.log(schoolname)//想要将子元素的数据传递给父元素,需要自定义触发事件实现数据传值//触发一个事件名称叫做child-parent的事件this.$emit('childparent',schoolname)}}})//根组件let app=new Vue({el:"#app",data:{schoolList:['qinghua','beida','hangdian','xiamen'],chooseSchool:""},methods:{changeEvent:function (data) {console.log("触发学校选择事件"),this.chooseSchool=data}}})</script></body></html>
运行结果如下:
将父元素方法传值给子元素
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="/npm/vue"></script></head><!--组件之间的传值:将父元素方法传值给子元素--><body><div id="app"><ul><!--从父组件传值到子组件--><!--循环传值组件--><!--因为因为父元素的方法可以直接修改父元素的数据所以将父元素的方法传递给子元素然后由子元素进行调用,从而修改父元素的数据--><school v-for="item,index in schoolList" :action='changeEvent' :key="'abc'+index" :index='index' :schoolname="item"></school></ul><h2>选中的学校是:{{chooseSchool}}</h2></div><script>//学校组件ponent("school",{props:['schoolname','index','action'],template:`<li><h3>{{index}}学校名称:{{schoolname}}</h3><button @click="chooseEvent(schoolname)">选择学校</button></li>`,methods:{chooseEvent:function (schoolname) {console.log(schoolname)//console.log(this.action)this.action(schoolname)}}})//根组件let app=new Vue({el:"#app",data:{schoolList:['qinghua','beida','hangdian','xiamen'],chooseSchool:""},methods:{changeEvent:function (data) {//console.log("触发学校选择事件")this.chooseSchool=data}}})</script></body></html>
直接通过$parent
属性找到父元素的vue对象(不建议用,因为通常我们是想让组件之间是低耦合的状态)
当在子组件的方法中console.log(this)
中后会得到下面的输出,可以看到在$parent
属性下有根组件的所有内容。所以我们可以利用该属性找到父元素的vue对象。$root
是最外层的根组件属性。$children
是子属性。
//子组件ponent("school",{props:['schoolname','index','action'],template:`<li><h3>{{index}}学校名称:{{schoolname}}</h3><!--2、在视图直接调用父元素方法--><button @click="$parent.changeEvent(schoolname)">选择学校</button><!--3、在视图直接修改父元素中的值--><button @click="$parent.chooseSchool=schoolname">选择学校</button></li>`,methods:{chooseEvent:function (schoolname) {//console.log(schoolname)//console.log(this)//1、组件可以通过$parent属性找到父元素的vue对象//this.$parent.changeEvent(schoolname)}}})//根组件let app=new Vue({el:"#app",data:{schoolList:['qinghua','beida','hangdian','xiamen'],chooseSchool:""},methods:{changeEvent:function (data) {//console.log("触发学校选择事件")//console.log(this)this.chooseSchool=data}}})
3、一点补充(补充前面出现的属性)
prop 是子组件用来接受父组件传递过来的数据的一个自定义属性。
父组件的数据需要通过 props 把数据传给子组件,子组件需要显式地用 props 选项声明 “prop”:
动态 Prop
<div id="app"><div><input v-model="parentMsg"><br><child v-bind:message="parentMsg"></child></div></div><script>// 注册ponent('child', {// 声明 propsprops: ['message'],// 同样也可以在 vm 实例中像 "this.message" 这样使用template: '<span>{{ message }}</span>'})// 创建根实例new Vue({el: '#app',data: {parentMsg: '父组件内容'}})</script>
使用 v-bind 指令将 todo 传到每一个重复的组件中
<div id="app"><ol><todo-item v-for="item in sites" v-bind:todo="item"></todo-item></ol></div><script>ponent('todo-item', {props: ['todo'],template: '<li>{{ todo.text }}</li>'})new Vue({el: '#app',data: {sites: [{text: 'Runoob' },{text: 'Google' },{text: 'Taobao' }]}})</script>
prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。
Vue.js 组件 - 自定义事件
父组件是使用 props 传递数据给子组件,但如果子组件要把数据传递回去,就需要使用自定义事件!
我们可以使用 v-on 绑定自定义事件, 每个 Vue 实例都实现了事件接口(Events interface),即:
–使用 $on(eventName) 监听事件
– 使用 $emit(eventName) 触发事件
另外,父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。
一个小例子(计数器)
<!DOCTYPE html><html><head><meta charset="utf-8"><title>Vue 测试实例 - 菜鸟教程()</title><script src="/vue/2.2.2/vue.min.js"></script></head><body><div id="app"><div id="counter-event-example">// 6. 页面上更新total的值<p>{{ total }}</p> // 4. 这里的自定义事件再次触发局域方法incrementTotal<button-counter v-on:increment="incrementTotal"></button-counter><button-counter v-on:increment="incrementTotal"></button-counter></div></div><script>// 1. 注册全局组件ponent('button-counter', {// 2. button绑定点击事件incrementHandler template: '<button v-on:click="incrementHandler">{{ counter }}</button>',data: function () {//data 必须是一个函数return {counter: 0}},methods: {// 3. 点击事件触发后,再次触发自定义事件incrementincrementHandler: function () {this.counter += 1this.$emit('increment')}},})new Vue({el: '#counter-event-example',data: {total: 0},methods: {// 5. 局域方法执行了total+1incrementTotal: function () {this.total += 1}}})</script></body></html>
Vue组件中的data是函数的原因
为什么Vue组件中的data是一个函数原理(转载)
4、 v-model与插槽
v-model
<!--v-model的作用相当于下面语句的作用--><input type="text" v-model="username"/><input type="text" @input="username=$event.target.value" :value="username"/>
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="/npm/vue"></script></head><!--原始的数据绑定<body><input type="text" id="username"/><h1></h1><script>let inputDom=document.querySelector("#username")inputDom.οninput=function(){document.querySelector("h1").innerHTML=inputDom.value}</script></body>--><!--v-model--><body><div id="app"><!--这里的input事件是在模板里emit后定义的名字为input,这里的三个input都相互等价--><input-com :username1="username" @input="changeEvent"></input-com><input-com :username1="username" @input="username1=$event"></input-com><input-com v-model="username"></input-com><h3>{{username}}</h3></div><script>ponent('input-com',{props:['username1'],template:`<input type="text" @input="$emit('input',$event.target.value)" :value="username1"/>`,data:function(){return{}}})let app=new Vue({el:"#app",data:{username:""},methods:{changeEvent:function(data){this.username=data}}})</script></body></html>
插槽
用于向一个组件内直接传递内容。
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="/npm/vue"></script></head><body><div id="app"><alert-com :html='content'></alert-com><!--slot里面的内容变量只跟父元素有关--><alert-com1 ><p>注意安全,{{content}}</p></alert-com1></div><script>ponent('alert-com',{props:['html'],template:`<div class="alert"><h1>温馨提示</h1><div class="content">{{html}}</div></div>`})ponent('alert-com1',{template:`<div class="alert"><h1>温馨提示</h1><div class="content"><slot></slot></div></div>`,data:function(){return{}}})let app=new Vue({el:"#app",data:{content:'天干物燥,小心火烛'}})</script></body></html>
5、动态组件
利用component标签中的is属性动态选择组件。
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="/npm/vue"></script></head><body><div id="app"><div id="content"><!--动态组件:利用component标签中的is属性选择组件--><component :is="com"></component></div><button @click="chooseContent(1)">首页</button><button @click="chooseContent(2)">列表</button><button @click="chooseContent(3)">新闻</button><button @click="chooseContent(4)">个人</button></div><script>let com1=ponent('index-com',{template:'<h1>首页内容</h1>'})let com2=ponent('list-com',{template:'<h1>列表内容</h1>'})let com3=ponent('news-com',{template:'<h1>新闻内容</h1>'})let com4=ponent('me-com',{template:'<h1>个人内容</h1>'})let app=new Vue({el:"#app",data:{com:com1},methods:{chooseContent:function(id){console.log(id)console.log(this)//通过获取id,选择注册好的组件=this.$ponent['com'+id]}},component:{com1,com2,com3,com4}})</script></body></html>
一个非常有用的网站:vant官网