一、创建简单vue实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>hello world</title>
<!-- 引入vue.js文件创建vue实例 -->
<script src='./vue.js'></script>
</head>
<body>
<div id="app">{{content}}</div>
<!-- <div>{{content}}</div> -->
<script>
// var dom = document.getElementById('app');
// dom.innerHTML = "Hello World!"
// setTimeout(function(){
// dom.innerHtml='bye world'
// },2000)
var app = new Vue({
el: '#app',
data: {
content: 'hello world!'
}
})
setTimeout(function () {
app.$data.content = 'bye world!'
}, 2000)
</script>
</body>
</html>
二、实现todoList
1.通过简单vue实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>todo list</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="app">
<input type='text' v-model="inputValue" />
<button v-on:click="handleBtnClick">提交</button>
<ul>
<!-- <li>第一课的内容</li>
<li>第二课的内容</li> -->
<li v-for="item in list">{{item}}</li>
</ul>
</div>
<script>
var app = new Vue({
el: '#app',
data: {
list: [],
inputValue:''
},
methods: {
handleBtnClick: function () {
// alert('click')
this.list.push(this.inputValue);
this.inputValue=''
}
}
})
</script>
</body>
</html>
2.通过jquery
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>todoList Jquery</title>
<script src='./jquery.js'></script>
</head>
<body>
<div>
<input id="input" type='text' />
<button id="btn">提交</button>
<ul id="list">
</ul>
</div>
<script>
function Page() {
}
$.extend(Page.prototype, {
init: function () {
this.bindEvents()
},
bindEvents: function () {
var btn = $('#btn');
// proxy方法 会改变handleBtnClick方法的this指向,使其一直是指向这个page实例
btn.on('click', $.proxy(this.handleBtnClick, this))
},
handleBtnClick: function () {
// alert('123')
var inputElem = $("#input");
// 获取Input框的内容
var inputValue = inputElem.val();
// 将其添加到ul里面
var ulElem = $("#list");
ulElem.append('<li>' + inputValue + '</li>');
// input框内容再置空
inputElem.val('');
}
})
var page = new Page();
page.init();
</script>
</body>
</html>
三、全局组件、局部组件、简单父传子、子传父
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>todo list</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="app">
<input type='text' v-model="inputValue" />
<button v-on:click="handleBtnClick">提交</button>
<ul>
<li v-for="item in list">{{item}}</li>
<!-- <todo-item v-bind:content="item" v-for="item in list">
</todo-item> -->
</ul>
</div>
<script>
// 全局组件
// Vue.component("TodoItem",{
// template:"<li>todo item</li>"
// })
// Vue.component("TodoItem",{
// props:['content'],
// template:"<li>{{content}}</li>"
// })
// 局部组件
// var TodoItem = {
// props: ['content'],
// template: "<li>{{content}}</li>"
// }
var app = new Vue({
el: '#app',
components: { TodoItem: TodoItem },
data: {
list: [],
inputValue: ''
},
methods: {
handleBtnClick: function () {
// alert('click')
this.list.push(this.inputValue);
this.inputValue = ''
}
}
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>todo list</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="root">
<input type='text' v-model="inputValue" />
<button v-on:click="handleBtnClick">提交</button>
<ul>
<todo-item
v-bind:content="item"
v-bind:key="index"
v-for="(item,index) in list"
v-on:delete="handleItemDelete(index)">
</todo-item>
</ul>
</div>
<script>
// 局部组件
var TodoItem = {
props: ['content','index'],
template: "<li v-on:click='handleItemClick'>{{content}}</li>",
methods: {
handleItemClick: function () {
this.$emit("delete",this.index);
}
}
}
var app = new Vue({
el: '#root',
components: { TodoItem: TodoItem },
data: {
list: [],
inputValue: ''
},
methods: {
handleBtnClick: function () {
// alert('click')
this.list.push(this.inputValue);
this.inputValue = ''
},
handleItemDelete: function (index) {
// alert('delete')
this.list.splice(index,1)
}
}
})
</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">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>hello world</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="root" @click="handleCLick">{{message}}
<item></item>
</div>
<script>
// 全局组件Vue.component(),不用在父组件当中components中再注册
Vue.component("item",{
template:"<p>hello item</p>"
})
// 根实例
var vm = new Vue({
el: "#root",
data: {
message: "hello world!"
},
methods: {
handleCLick: function () {
alert('hello world!');
}
}
// props computed watch
})
</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">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>生命周期</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="app">111111</div>
<script>
// 生命周期函数就是vue实例在某个"时间点"会"自动执行"的函数
// 注意生命周期钩子没有定义在methods里面
// 下面一共8个生命周期钩子,其实vue源码当中是11个,点击vue官网的学习->API
// activated() deactivated() errorCaptured() 在后面的学习当中会给演示说明
var vm = new Vue({
el: "#app",
template: "<div>{{test}}</div>",
data: {
test: "hello world!"
},
beforeCreate: function () {
console.log("beforeCreate");
},
created: function () {
console.log("created")
},
// 接着会询问是否有el选项
// 然后询问有无template选项
// 有template,就把模板内容直接渲染
// 无template,就把el接管的dom标签的全部内容当做模板template来进行渲染
// 在渲染之前,也就是在模板和数据相结合之前的一瞬间,会执行beforeMount函数
beforeMount: function () {
console.log(this.$el);
console.log("beforeMount");
// alert("beforeMount");
},
// beforeMount执行后,模板和数据结合后生成的vue实例当中的dom元素会挂载(显示)到页面之上
// vue实例挂载之后,会执行mounted()
mounted: function () {
//上面的和这行的$el反映出mounted执行后页面才渲染完毕
// 这里的$el才打印出模板template中的内容
console.log(this.$el);
console.log("mounted");
},
// 下面有两个beforeDestroy()和destroyed()
// 但是到页面刷新后发现并没有执行,那么它们什么时候会被执行呢?
// 答:只有当vm.$destroy()方法被调用执行之后才会触发这两个生命周期钩子
// 组件即将被销毁时 beforeDestroy
beforeDestroy: function () {
console.log("beforeDestroy");
},
// 组件被完全销毁后 destroyed
destroyed: function () {
console.log("destroyed");
},
// 下面有两个beforeUpdate()和updated()
// 但是到页面刷新后发现并没有执行,那么它们什么时候会被执行呢?
// 答:只有当数据发生改变之后才会触发这两个生命周期钩子
// 打开控制台 vm.test="zhangsan" 或 vm.$data.$test="zhangsan"
// 数据发生改变但还没重新渲染之前 beforeUpdate
beforeUpdate: function () {
console.log("beforeUpdate");
},
// 重新渲染之后 updated
updated: function () {
console.log("updated");
},
})
</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">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>模板语法</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="app">
<div>{{name+" Lee"}}</div>
<div v-text="name+' Lee'"></div>
<div v-html="name+' Lee'"></div>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
name: "Dell"
}
})
</script>
</body>
</html>
七、计算属性computed、方法method、侦听器watch
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>计算属性方法侦听器</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="app">
<!--冗余的 插值表达式 -->
<!-- {{firstName+" "+lastName}} -->
<!--1 计算属性computed -->
<!-- {{fullName}} -->
<!--2 方法methods -->
<!-- {{fullName()}} -->
<!--3 侦听器watch -->
{{fullName}} {{age}}
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
firstName: "Dell",
lastName: "Lee",
// 下面这样会数据冗余
fullName: "Dell Lee",
age: 28
},
// computed watch methods 若三者都可实现某种功能,推荐使用computed
// 侦听器(有缓存,只要侦听的变量所依赖的不变化,就不执行,用缓存)
// 即watch与computed很类似都有缓存机制,但是推荐computed
// watch: {
// firstName: function () {
// console.log("计算了一次firstName");
// this.fullName = this.firstName + " " + this.lastName;
// },
// lastName: function () {
// console.log("计算了一次lastName");
// this.fullName = this.firstName + " " + this.lastName;
// }
// },
// 方法(无缓存特点,只要页面渲染一次,方法就会重新执行一次)
// 测试:vm.lastname="liu" 会打印“计算了一次”
// 测试:vm.age=27 也会打印“计算了一次”
// methods: {
// fullName: function () {
// console.log("计算了一次");
// return this.firstName + " " + this.lastName;
// }
// }
// 计算属性(内置缓存的特点:若计算属性依赖的值没有发生变化,就不会执行)
// 测试:vm.age=27 不会打印“计算了一次”
// 测试:vm.lastname="liu" 会打印“计算了一次”
// computed: {
// fullName: function () {
// console.log("计算了一次");
// return this.firstName + " " + this.lastName;
// }
// }
})
</script>
</body>
</html>
八、计算属性setter-getter
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>计算属性-setter+getter</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="app">
{{fullName}}
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
firstName: "Dell",
lastName: "Lee"
},
computed: {
// fullName: function () {
// return this.firstName + " " + this.lastName;
// }
fullName: {
get: function () {
return this.firstName + " " + this.lastName;
},
set: function (value) {
// console.log(value);
var arr = value.split(" ");
// 因为firstName和lastName重新赋值了,所以执行set()后会引起上面的get()的执行
this.firstName = arr[0];
this.lastName = arr[1];
}
}
}
})
</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">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>样式绑定</title>
<script src='./vue.js'></script>
<style>
.activated {
color: red;
}
</style>
</head>
<body>
<div id="app">
<!-- class 对象-->
<!-- 样式和数据的一个绑定,我们把这个叫做class的对象绑定 -->
<!-- :class="{activated:isActivated}" -->
<!-- class 数组-->
<!-- 可绑定多个样式名 -->
<!-- :class="[activated,activatedOne]" -->
<!-- style 对象-->
<!-- :style="styleObj" -->
<!-- style 数组 除此之外其他代码同上面style 对象形式-->
<!-- :style="[styleObj]" -->
<!-- style 数组(可以放多个对象 -->
<!-- :style="[styleObj,{fontSize:'20px'}]" -->
<div :style="[styleObj, {fontSize:'20px'}]" @click="handleDivClick">
hello world
</div>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
// isActivated:false
// activated: '',
// activatedOne:"activated-one",
styleObj: {
color: "black"
}
},
methods: {
handleDivClick: function () {
// class :class="{activated:isActivated}"
// this.isActivated = !this.isActivated;
// class :class="[activated]"
// this.activated = this.activated === "activated" ? "" : "activated";
// style
this.styleObj.color = this.styleObj.color === "black" ? "red" : "black";
}
}
})
</script>
</body>
</html>
十、vue条件渲染 v-if v-show
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>条件渲染</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="app">
<!-- v-if和v-show -->
<!-- v-if 直接不挂载,不存在页面dom之上 -->
<!-- v-show 是不显示 display:none -->
<!-- v-show的性能较高,因为不会频繁移除dom -->
<!-- <div v-if="show" data-test="v-if">{{message}}</div>
<div v-if="show" data-test="v-if">{{message}}</div> -->
<!-- v-if和v-else -->
<!-- v-else不必绑定变量,但这两个指令所在的标签必须紧贴使用,中间不可放其他标签,比如放一个<span></span> -->
<!-- 有你没我,有我没你,dom树上只挂载其中一个 -->
<!-- <div v-if="show">{{message}}</div>
<div v-else>bye world</div> -->
<!-- v-if和v-else-if和v-else -->
<!-- 注意这三者也是有紧贴着使用 -->
<!-- <div v-if="show==='a'">This is A</div>
<div v-else-if="show==='b'">This is B</div>
<div v-else>This is Others</div> -->
<!-- 简单例子 -->
<!-- 问题:vue的虚拟dom机制会尽量复用页面上的dom -->
<!-- 比如下面如果<input/>,在切换用户名和邮箱名的时候,Input框会复用,即内容不会清空 -->
<!-- 解决方案,给<input key="" /> 这样vue会知道是唯一元素,就不会复用它-->
<!-- <div v-if="show">
用户名:<input key="userName"/>
</div>
<div v-else>
邮箱名:<input key="emailName"/>
</div> -->
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
show:false,
// show: 'a',
message: "hello world!"
}
})
</script>
</body>
</html>
十一、vue列表渲染 v-for
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>列表渲染</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="app">
<!-- 数组的循环 -->
<!-- <div v-for="item in list"></div> -->
<!-- 更推荐用of这个语法 -->
<!-- 为了提高循环性能,推荐给每一个循环项加上唯一的key值-->
<!-- 但是key不推荐用这里的index,推荐使用后端返回的id标识,即item.id -->
<!-- <div v-for="(item,index) of list"
:key="item.id">
{{item.text}}---{{index}}
</div> -->
<!-- template 占位符 在sources中不显示这个标签,即并不会渲染到页面之上 -->
<!-- 需要一个新的标签包裹使其只循环一次但是又不想用div-->
<!-- <template v-for="(item,index) of list">
<div>
{{item.text}}---{{index}}
</div>
<span>
{{item.text}}---{{index}}
</span>
</template> -->
<!-- 对象的循环 -->
<!-- vm.userInfo.name="Dell Lee" 操作已有的key值 数据变 页面变-->
<!-- vm.userInfo.address="Beijing" 操作没有的key值 数据变 页面不变-->
<!-- 如何解决这种问题,想添加新的Key值? -->
<!-- 答:和上面数组一样的方法,改变对象的引用。 vm.userInfo={新对象新的Key和value值} -->
<!-- 但其实还有set方法,见下一节内容 -->
<div v-for="(item,key,index) of userInfo">
{{item}}---{{key}}---{{index}}
</div>
</div>
<script>
var vm = new Vue({
el: "#app",
// vm.list.push({id:"26419",text:"try"}) 成功 数据变 页面变
// vm.list[4]={id:"26419",text:"try"} 失败 数据变 页面不变
// 注意:我们不能通过下标方式来操作数组,只能通过vue提供的七种数组变异方法来操作数组
// 数组尾部添加删除push pop
// 数组头部添加删除shift unshift
// 截取splice 排序sort 取反reverse
// 除了上面七种方法,可以有别的方法吗?
// 答:改变数组的引用地址,即直接给List赋值 vm.list=[新数组]
// 还可以用set方法,见下一节内容
// data: {
// list: [{
// id:"1628640",
// text:"hello"
// },{
// id:"6924155",
// text:"Dell"
// },{
// id:"0174746",
// text:"Lee"
// }]
// }
// 对象
data:{
userInfo:{
name:"Dell",
age:28,
gender:"male",
salary:"secret"
}
}
})
</script>
</body>
</html>
十二、vue-set
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vue-set</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="app">
<!-- 对象 -->
<!-- <div v-for="(item,key,index) of userInfo">
{{item}}---{{key}}---{{index}}
</div> -->
<!-- 数组 -->
<div v-for="(item,index) of userInfo">
{{item}}---{{index}}
</div>
</div>
<script>
// 对象
// set即是全局方法,也是实例方法
// 全局方法
// Vue.set(vm.userInfo,"address","Beijing")
// 实例方法
// vm.$set(vm.userInfo,"address","Beijing")
// 数组
// 全局方法 Vue.set(vm.userInfo,2,10)
// 实例方法 vm.$set(vm.userInfo,3,100)
// 改数组(数据变,页面变)
// (1) 七种变异方法 (2) 改变引用地址 (3)set
// 改对象(数据变,页面变)
// (1) 改变引用地址 (2)set
var vm = new Vue({
el: "#app",
data: {
// userInfo: {
// name: "Dell",
// age: 28,
// gender: "male",
// salary: "secret"
// }
userInfo:[1,2,3,4]
}
})
</script>
</body>
</html>
十三、事件绑定 v-on、@click
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>事件绑定</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="app">
<!-- @click="handleClick()"这样写会触发点击事件,但是获取不到事件对象e,可写成@click="handleClick($event)" -->
<!--写法1 <button @click="handleClick($event)">Button</button> -->
<!--写法2 <button @click="handleClick">Button</button> -->
<!--写法1加括号,可以传参 @click="handleClick($event,1,2,3)" -->
<!-- <button @click="handleClick($event,1,2,3)">Button</button> -->
<!-- "/abc" 为提交form表单后的跳转地址 -->
<!-- 为阻止这个默认跳转的行为,可在handleClick()函数内写上e.preventDefault(); -->
<!-- 当然也可这样 <form action="/abc" @click.prevent> 便可实现完全一致的功能-->
<!-- 这是vue提供的修饰符,上面也可写成 @click.prevent="handleClick" -->
<!-- <form action="/abc" @click.prevent>
<input type="submit" />
</form> -->
<!-- 这样的基础事件修饰符还有很多,如下 -->
<!-- 普通修饰符 -->
<!-- @click.prevent 阻止默认行为 e.preventDefault()-->
<!-- @click.stop 阻止事件冒泡 e.stopPropagation()-->
<!-- @click.self 只有e.target=e.currentTarget的时候才会执行。即只有触发元素和绑定元素一致时才会触发事件执行-->
<!-- @click.once 只执行一次事件 -->
<!-- @click.capture 事件冒泡——>事件捕获规则,当然是父和子元素都加上.capture -->
<!-- @click.stop 点击子元素时,若父元素也监听同样事件,将 不会冒泡到父元素身上-->
<!-- @click.self 只有点击标签自身的时候才会触发点击事件。点击其子元素时不触发。点击父元素也不触发。-->
<!-- @click.capture 按道理是按事件冒泡,由子到父的顺序,先触发子的事件再触发父的事件。 -->
<!-- 若加上capture的话,就事件捕获,即由父到子的顺序,先触发父的事件再触发子的事件 -->
<!-- <div @click.capture="handle">love
<div @click.capture="handleClick">hello world</div>
</div> -->
<!-- 按键修饰符 -->
<!-- @keydown.enter 只有点击enter键时候才会执行后面的函数 -->
<!-- @keydown.esc @keydown.tab @keydown.delete 等等 -->
<!-- <input @keydown.enter="handleKeyDown" /> -->
<!-- 系统修饰符 -->
<!-- 和按键修饰符差不多,有四个 -->
<!-- @keydown.ctrl -->
<!-- @keydown.alt -->
<!-- @keydown.shift -->
<!-- @keydown.meta 有些特殊键盘有这个键-->
<!-- 即要想执行后面的函数,关按键是不够的,还需要按下对应的系统键 -->
<!-- <input @keydown.shift="handleKeyDown" /> -->
<!-- 鼠标修饰符 -->
<!-- @click.right 监听鼠标右键的点击事件 -->
<!-- @click.left 监听鼠标左键的点击事件 -->
<!-- @click.middle 监听鼠标中键的点击事件 -->
<!-- <div @click.right="handleMouseClick">click</div> -->
</div>
<script>
var vm = new Vue({
el: "#app",
methods: {
handleClick: function (e) {
console.log("儿子");
},
handle: function (e) {
console.log("爸爸");
},
handleKeyDown:function(e){
console.log(e.target.value)
},
handleMouseClick:function(e){
console.log("right")
}
}
})
</script>
</body>
</html>
十四、表单绑定v-model
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>表单绑定</title>
<script src='./vue.js'></script>
</head>
<body>
<!-- 表单绑定主要是指 v-model -->
<!-- v-model不仅可以用于 <input>标签还可以 -->
<div id="app">
<!-- <input v-model="value" type="text"> -->
<!-- <textarea v-model="value"></textarea> -->
<!-- <input v-model="value" type="checkbox"> -->
<!-- <input v-model="value" type="radio" value="test"> -->
<!-- select v-model的value值优先选择option里的value(1,2,3),没有value才选择innerHtml(A,B,C) -->
<!-- <select v-model="value">
<option disabled>----请选择-----</option> 这个是为了更好的用户体验
<option value="1">A</option>
<option value="2">B</option>
<option value="3">C</option>
</select> -->
<!-- {{value}} -->
<!-- 表单修饰符 -->
<!-- <input type="text" v-model.lazy="value" /> -->
<!-- <input type="text" v-model.number="value" /> -->
<!-- <input type="text" v-model.trim="value" /> -->
<!-- v-model.number 不加时,123也会判断为string类型,加了之后会把能转换为number类型的都转换为number类型再输出 -->
<!-- v-model.lazy 当输入框失焦的时候才输出 -->
<!-- v-model.trim 去首尾空格修饰符后才输出 -->
{{value}}
</div>
<script>
var vm = new Vue({
el: "#app",
data:{
value:""
},
watch:{
value:function(){
console.log(typeof this.value)
}
}
})
</script>
</body>
</html>
十五、组件细节点
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>组件细节点</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="root">
<!-- 知识点1 用is解决h5标签上的一些小bug-->
<!-- 因为tbody里面只能接<tr> -->
<!-- 因为ul里面只能接<li> -->
<!-- 因为ol里面只能接<li> -->
<!-- 因为select里面只能接<option> -->
<!-- 故以上的都用 is属性来接所设置的子组件名字 -->
<!-- <table>
<tbody>
<tr is="row"></tr>
<tr is="row"></tr>
<tr is="row"></tr>
</tbody>
</table>
<table>
<ul>
<li is="row"></li>
<li is="row"></li>
<li is="row"></li>
</ul>
</table>
<table>
<ol>
<li is="row"></li>
<li is="row"></li>
<li is="row"></li>
</ol>
</table>
<select>
<option is="row"></option>
<option is="row"></option>
<option is="row"></option>
</select> -->
<!-- 知识点2 子组件当中定义data,data必须是一个返回对象的函数。只有在根组件实例中,data才可以是一个对象-->
<!-- <table>
<tbody>
<tr is="row"></tr>
<tr is="row"></tr>
<tr is="row"></tr>
</tbody>
</table> -->
<!-- 知识点3 ref -->
<!-- ref 获取dom或实例引用的语法 -->
<!-- ref在dom标签上,则this.$refs.name 获取的是这个dom元素 -->
<!-- ref在组件上,则this.$refs.name 获取的是这个组件的引用,即这个vue子组件实例的引用 -->
<!-- ref在组件的知识点见知识点4 -->
<!-- <div
ref='hello'
@click="handleClick"
>
hello world
</div> -->
<!-- 知识点4 -->
<!-- 实现一个计算器求和的功能 -->
<!-- ref在组件上,在父组件用this.$refs.name 获取的是这个子组件的引用,即这个vue子组件实例的引用 -->
<counter ref="one" @change="handleChange"></counter>
<counter ref="two" @change="handleChange"></counter>
<div>{{total}}</div>
</div>
<script>
// 知识点1
// Vue.component('row', {
// template: '<tr><td>this is a row</td></tr>'
// })
// 知识点2 data
// Vue.component('row', {
// 下面这样错误,只有根组件的data才可以是对象形式
// data:{
// content:"this is a row"
// },
// 子组件的data应该是function形式 这个函数返回一个对象
// 这样设置的目的是每一个子组件<row>是独享一套数据,而不是和其他<row>共享同一套数据,这样就不会出现不同子组件之间数据互相影响的情况
// data:function(){
// return {
// content:"this is a row"
// }
// },
// template: '<tr><td>{{content}}</td></tr>'
// })
// 知识点3 ref
// vue不建议我们直接操作dom,而是希望通过操作数据来实现对dom的操作
// 但必要情况下,我们需要直接操作dom,这就需要用到ref
// var vm = new Vue({
// el: '#root',
// methods:{
// handleClick:function(){
// alert('click')
// this.$refs 指的是整个vue实例中所有的引用
// this.$refs.hello 指的是ref名为hello的dom元素
// console.log(this.$refs.hello);
// console.log(this.$refs.hello.innerHTML);
// }
// }
// })
// 知识点4
Vue.component('counter',{
template:'<div @click="handleClick">{{number}}</div>',
data:function(){
return {
number:0
}
},
methods:{
handleClick:function(){
this.number++;
this.$emit('change')
}
}
})
// 注意computed不能监听refs的变化,只能放在methods当中
var vm=new Vue({
el:'#root',
data:{
total:0
},
methods:{
handleChange:function(){
// 为了父组件能够对两个子组件的数据进行求和,这时候需要用到ref方法
// console.log(this.$refs.one.number);
this.total=this.$refs.one.number+this.$refs.two.number
}
}
})
</script>
</body>
</html>
十六、父传子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>父传子</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="root">
<!-- 父传子 props 单项数据流-->
<!-- :count="0" 0是数字 "0"是js表达式 -->
<!-- count="0" 0是字符串 -->
<counter :count="1"></counter>
<counter :count="2"></counter>
</div>
<script>
var counter = {
props: ['count'],
data: function () {
return {
number: this.count
}
},
template: "<div @click='handleClick'>{{number}}</div>",
methods: {
handleClick: function () {
// 下面这样直接操作父组件传过来的数据,虽然可以操作成功,但是控制台会报警告说不要这样操作
// 单向数据流:子组件只能接收使用父组件传过来的数据,而不可以进行操作
// 因为万一传过来的不是值数据而是引用型数据的话,子组件再修改数据,会引起混乱
this.number++;
}
}
}
var app = new Vue({
el: '#root',
// 注意局部组件才需要注册
components: {
counter: counter
}
})
</script>
</body>
</html>
十七、子传父
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>子传父</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="root">
<!-- 子监听父的 -->
<!-- 监听事件 @加上事件名 例如 @inc= -->
<counter :count="3" @inc="handleInc"></counter>
<counter :count="2" @inc="handleInc"></counter>
<div>{{total}}</div>
</div>
<script>
var counter = {
props: ['count'],
data: function () {
return {
number: this.count
}
},
template: "<div @click='handleClick'>{{number}}</div>",
methods: {
handleClick: function () {
this.number = this.number + 2;
// 子传父,通过触发事件并且携带参数的方式
// 子传父 发送事件名,后面为带过去的数据
this.$emit('inc', 2)
}
}
}
var app = new Vue({
el: '#root',
data: {
total: 5
},
components: {
counter: counter
},
methods: {
handleInc: function (step) {
this.total += step;
}
}
})
</script>
</body>
</html>
十八、组件参数校验和非props
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>组件参数校验和非props</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="root">
<child content="hello world"></child>
<child content="hell"></child>
<!-- 下面为Number,不会通过String类型的校验 控制台会报错 -->
<!-- <child :content="123"></child> -->
</div>
<script>
// 组件参数校验
// 指的是子组件对父组件传递过来的content进行一些校验,比如必须要是字符串,如果是别的我不要
// 要进行参数校验的话,就不能是props:['content']这种数组格式,应该写成对象,如下
// props特性:父传子,子要接(一个属性),即子在props内进行了声明 父子组件有一个对应关系
// 非props特性:父传子,子不接(一个属性),即子没有在props内进行声明
// props特性的特点:
// (1)传递的这个属性不会出现在最终渲染的标签上
// (2)子可以通过插值表达式或者this.的方式获取父传过来的属性值
// 非props特性的特点:
// (1)传递的这个属性 会展示在子组件最外面的dom标签的html之中,即是attribute而不是property
// (2)子无法获取父组件的这个内容,没法用
Vue.component('child', {
// 校验1
// props: {
// content: Number
// },
// 校验2
// props: {
// content: [Object, String]
// },
// 校验3
// props: {
// content: {
// type: Number
// }
// },
// 校验4
// props: {
// content: {
// type: String,
// required:false, //true指的是这个值是必传的,就会报错。false则是可传可不传
// default:'default value' //required:false 时如果没传则会用这个默认值
// 更复杂的校验项
// validator: function (value) {
// 要求传过来的值的长度要大于5,否则报错
// return (value.length > 5)
// }
// }
// },
template: "<div>{{content}}</div>"
})
var app = new Vue({
el: '#root'
})
</script>
</body>
</html>
十九、给组件绑定原生事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>给组件绑定原生事件</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="root">
<!-- @click.native -->
<!-- 加了native之后,此时监听的是原生事件,而不是组件的自定义事件 -->
<!-- 给组件绑定原生事件:在后面加上 .native修饰符即可 -->
<child @click.native="handleClick"></child>
</div>
<script>
Vue.component('child', {
template: '<div>Child</div>',
// template: '<div @click="handleClick">Child</div>',
// methods: {
// handleClick: function () {
// // alert('child click');
// this.$emit('click');
// }
// }
})
var app = new Vue({
el: '#root',
methods: {
handleClick: function () {
alert('click')
}
}
})
</script>
</body>
</html>
二十、非父子组件传值(Bus/总线/发布订阅模式/观察者模式)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>非父子组件传值(Bus/总线/发布订阅模式/观察者模式)</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="root">
<child content="Dell"></child>
<child content="Lee"></child>
</div>
<script>
Vue.prototype.bus = new Vue();
// 这样可以使后面创建的每个组件或者每个vue实例都具有bus这个属性
// 并且这个vue属性将指向同一个vue实例
// 这个vue实例存在的意义
Vue.component('child', {
data: function () {
return {
selfContent: this.content
}
},
props: {
content: String
},
template: "<div @click='handleClick'>{{selfContent}}</div>",
methods: {
handleClick: function () {
// console.log(this.content);
this.bus.$emit('change', this.selfContent);
}
},
// this.bus是一个vue实例,所以具有emit和on方法
mounted: function () {
this.bus.$on('change', (msg) => {
// alert(msg)
this.selfContent = msg;
})
}
})
var app = new Vue({
el: '#root'
})
</script>
</body>
</html>
二十一、vue插槽(slot)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vue插槽(slot)</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="root">
<!-- 旧的方案 props -->
<!-- <child content="<p>Dell</p>"></child> -->
<!-- 新的方案 slot -->
<!-- <child><p>Dell</p></child>
<child></child> -->
<!-- 具名插槽 slot -->
<body-content>
<div class="header" slot="header">header</div>
<div class="footer" slot="footer">footer</div>
</body-content>
</div>
<script>
// 插槽的使用场景
// 需求:div标签里面不仅要展示p标签,还需要展示一个标签,但是这个标签不是由子组件决定的,是由父组件传递过来的
// 1.按以前的解决方法:props
// 缺点1:
// 这样会在p标签外多一层div标签
// <div v-html="this.content"></div>
// 这个模板占位符不管用
// <template v-html="this.content"></template>
// 缺点2:
// 一旦父组件传递过来的内容变多了,就会变得阅读性困难
// 单插槽
// 2.这时候我们需要用新的解决方案:插槽 slot
// 即如何在父子组件之间优雅的传递dom
// 在子组件当中直接用<slot></slot> 即可把父组件传递的展示出来
// 在子组件当中直接用<slot>默认内容</slot>,若父组件不传递内容过来便展示默认内容,若传递则展示父组件的
// 多插槽 具名插槽
// Vue.component('child',{
// props:['content'],
// template:`<div>
// <p>hello</p>
// <slot>默认内容</slot>
// </div>`
// })
// 具名插槽
Vue.component('body-content',{
props:['content'],
template:`<div>
<slot name="header"></slot>
<p>hello</p>
<slot name="footer"></slot>
</div>`
})
var app = new Vue({
el: '#root'
})
</script>
</body>
</html>
二十二、作用域插槽
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>作用域插槽</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="root">
<child>
<template slot-scope="props1">
<li>{{props1.item}}--hello</li>
</template>
</child>
</div>
<script>
Vue.component('child', {
data: function () {
return {
list: [1, 2, 3, 4]
}
},
template: `<div>
<ul>
<slot v-for="item of list"
:item=item>
</slot>
</ul>
</div>`
})
var app = new Vue({
el: '#root'
})
</script>
</body>
</html>
二十三、动态组件 v-once
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>动态组件</title>
<script src='./vue.js'></script>
</head>
<body>
<div id="root">
<!-- <component></component>就是vue里的动态组件 -->
<!-- 动态组件定义:根据is值的变化动态的加载不同的组件 -->
<component :is="type"></component>
<!-- 原始组件 -->
<child-one v-if="type==='child-one'"></child-one>
<child-two v-if="type==='child-two'"></child-two>
<button @click="handleClick">change</button>
<!-- v-once 内部内容只渲染一次,即使内部内容依赖数据发生变化也不重新渲染 -->
</div>
<script>
Vue.component('child-one', {
template: "<div>child-one</div>"
})
Vue.component('child-two', {
template: "<div>child-two</div>"
})
var app = new Vue({
el: '#root',
data: {
type: "child-one"
},
methods: {
handleClick: function () {
this.type = (this.type === 'child-one' ? 'child-two' : 'child-one')
}
}
})
</script>
</body>
</html>