`
Model View ViewModel
view
(视图层)在我们前端开发中,通常就是DOM层,主要是给用户展示各种信息
Model
(数据层) 数据可能使我们固定死的数据,更多的是来自我们服务器,从网络上请求下来的数据,在我们计数器案例中,就是后面抽取出来的obj,当然,里面的数据可能没有那么简单、
VueModel
(视图模型层)视图模型层是View和Model沟通的桥梁,一方面它实现了DataBinding,也就是数据绑定,将Model的改变实时的反应到View中,另一方面它实现了DOM Listener,也就是监听DOM,当DOM发生一些事件(点击,滚动,touch)时,可以监听到,并在需要的情况下改变对应的Data
1.导入开发版本的vue.js
2.创建vue实例对象,设置el属性和data属性
3.使用简介的模板语法把数据渲染到页面上
<body>
<div id="app">
{{ message }}
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
var app = new Vue({
el: "#app",
data: {
message:"hello world 你好,小黑!"
}
})
script>
body>
vue实例的作用范围 是什么呢?
是否可以使用其他的选择器?
是否可以设置其他的DOM元素?
0.el是用来设置vue实例挂载(管理)
1.vue会管理el选项命中的元素极其内部的后代元素
2.可以使用其他的选择器,但是建议使用id选择器
3.可以使用其他的双标签,不能使用html ,body
补充 挂载点可以传入字符串或者 DOM对象
1.vue中用到的数据定义在data中
2.data中可以写复杂类型的数据
3.渲染复杂类型数据时,遵守js的语法即可
补充 data中可以传入object 或者 是一个函数(组件中data必须是一个函数)
通过vue使用常见的网页效果
学习vue指令,以案例巩固知识点
vue指令指的是,以v-开头的一组特殊语法
v-once:解析一次,当vue实例中的数据发生变化,页面重新渲染时,该元素的内容不再更新,维持第一次解析渲染的结果
v-pre: 当需要吧 {{ message }} 原封不动 的解析渲染到页面时候,加上这个指令,该指令会让Vue不再对 mustache 语法进行解析 让她直接渲染出来
把 mustache 没有渲染的时候 遮罩起来,避免胡子被用户看到
1.v-text指令的作用是:设置标签的内容(textContent)
2.默认写法替换全部内容,使用==插值表达式 {{ }}==可以替换指定内容
3.内部支持写表达式
设置标签的文本值,使用v-text方法设置文本会覆盖标签中原本的元素。不想全部替换的话请使用插值表达式
<body>
<div id="app">
<h2 v-text="message + '!'">h2>
<h5>{{ message + "!" }} h5>
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
// 创建Vue实例,得到ViewModel
var vm = new Vue({
el:'#app',
data:{
message:"hello world"
},
methods:{}
})
script>
v-text字符串拼接直接 + ‘字符串’ 即可 ,差值表达式需要在 双括号内 + “字符串”
1.v-html指令的作用是:设置元素的innerHTML
2.内容中有html结构会被解析为标签
3.v-text指令无论内容是什么,只会解析为文本
4.解析文本使用v-text,需要解析html结构使用v-html
1.v-on指令的作用是:为元素绑定事件
2.事件名不需要写on
3.指令可以简写为@
4.绑定的方法定义在methods属性中
5方法内部通过this关键字可以访问定义在data中数据
补充:
1.事件绑定的方法写成函数调用的形式,可以传入自定义参数
2.定义方法时需要定义形参来接收传入的实参
3.事件的后面跟上 .修饰符可以对事件进行限制
4…enter可以限制触发按键为回车
5.事件修饰符有多种
事件修饰符如:.stop
.prevent
.capture
.self
.once
1.当v-on绑定的方法不传参,或者省略括号,而定义方法中传一个参的话,则该参数默认为 事件对象 event
2.当v-on绑定的方法不传参,而定义方法中传参的话,且超过一个以上参数,则第一个参数默认为 event事件对象,其他参数为undefind
3.如果在事件绑定中,不想省略 event事件对象的参数的话 则 使用 $event 传参。
<div id="app">
<h2 v-text="message">h2>
<input type="button" value="点我" v-on:mouseleave=fn v-on:mouseenter=fn>
<input type="button" value="进来" v-on:mouseenter=fn>
<input type="button" value="加数据" v-on:click=fn2>
div>
var vm = new Vue({
el:'#app',
data:{
message:"hello world"
},
methods:{
fn: function(){
console.log(this.message);
},
fn2:function () {
this.message += "ok"
}
}
})
1.创建vue示例时:el挂载点,data数据,methods方法
2.v-on指令的作用是绑定事件,简写为@
3.方法中通过this,关键字获取data中的数据
4.v-text指令的作用是:设置元素的文本值,简写{{}}
5.v-html指令的作用是:设置元素的innerHTML
1.v-show指令的作用是:根据真假切换元素的显示状态
2.原理是修改元素的display,实现隐藏
3.指令后面的内容,最终都会解析为布尔
4.值为true元素显示,值为false元素隐藏
5.数据改变之后,对应元素的显示状态会同步更新
1…v-if指令的作用是:根据 表达式的真假切换元素的显示状态
2.本质是通过曹总dom元素来切换显示状态
3.表达式的值为true,元素存在于dom树中,为false,从dom树中移除
4.频繁的切换v-show,反之使用v-if,前者的切换消耗小
1.v-bind指令的作用是:为元素绑定属性
2.完整写法是 v-bind:属性,名
3.简写的话可以直接省略v-bind,只保留:属性名
4.需要动态的增删class建议使用对象的方式,当然三元表达式也可以,注意变量与常量
<div id="app">
<img v-bind:src="imgSrc" alt="" :title="imgTitle+'!!!!!!'">
<br>
<img :src="imgSrc" alt="" :title="imgTitle+'!!!!!!'" :class="{active:imgBorder}" @click="isBorder">
<br>
<img :src="imgSrc" alt="" :title="imgTitle+'!!!!!!'" :class="imgBorder?'active':''" @click="isBorder">
div>
var vm = new Vue({
el:'#app',
data:{
imgSrc:"http://www.itheima.com/images/logo.png",
imgTitle:"黑马程序员",
imgBorder:false
},
methods:{
isBorder:function(){
this.imgBorder = !this.imgBorder
}
}
})
1.列表数据使用数组保存
2.v-bind指令可以设置元素属性,比如src
3.v-show和v-if都可以切换元素的显示状态,频繁切换使用v-show
<style>
*{margin: 0;padding: 0;}
.bgc{
position: relative;
margin: 100px auto;
width: 500px;
background-color: #ccc;
}
.bgc img{
width: 100%;
}
a{
position: absolute;
width: 40px;
height: 80px;
line-height: 80px;
color: rgba(0, 0, 0, 0.452);
background-color: rgb(255, 0, 0,.4);
text-decoration: none;
font-size: 50px;
}
a:first-child{
top: 50%;
transform: translateY(-50%);
}
a:last-child{
right: 0;
top: 50%;
transform: translateY(-50%);
}
style>
head>
<body>
<div id="mask">
<div class="bgc">
<a href="javascript:;" @click="prev" v-show="index>0" ><a>
<img :src="imgArr[index]" alt="">
<a href="javascript:;" @click="next" v-show="index < imgArr.length-1" >>a>
div>
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
var app = new Vue({
el:"#mask",
data:{
imgArr:["img/1.jpg","img/2.jpg","img/3.jpg","img/4.jpg","img/5.jpg"],
index : 1
},
methods:{
// 改变index 使得图片改变
prev:function(){
this.index--
},
next:function(){
this.index++
}
}
})
1.v-for指令的作用是:根据数据生成列表结构
2.数组经常和 v-for 一起使用
3.语法 : (item,index)in 数据
4.item 和 index 可以结合其他指令一起使用
5.数组长度的更新会同步到页面上,是响应式的
<div id="app">
<input type="button" value="添加" @click="addPeople">
<input type="button" value="删除" @click="removePeople">
<ul>
<li v-for="item in vegetables">
{{ item }}
li>
ul>
<h2 v-for="(item,index) in peoples">
{{ index+1 }} --- {{ item.name }}=>年龄:{{ item.age }} 性别:{{ item.gender }}
h2>
div>
var vm = new Vue({
el:'#app',
data:{
vegetables: ["西红柿","丝瓜","番茄","猪肉","黄瓜"],
peoples:[{name:"孙悟空",age:18,gender:"男"},{name:"猪八戒",age:22,gender:"男"},{name:"白骨精",age:16,gender:"女"}]
},
methods:{
addPeople:function(){
this.peoples.push({name:"二郎神",age:26,gender:"不明"})
},
removePeople:function(){
this.peoples.shift()
}
}
})
1.v-model指令的作用是便捷的设置和获取表单元素的值
2.绑定数据会和表单元素的值相关联
3.绑定的数据 <---- ----->表单元素的值
<html lang="cn">
<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>Documenttitle>
<style>
*{margin: 0; padding: 0;}
body{background-color: rgb(240, 236, 236);}
ul{list-style: none;}
a{text-decoration: none;}
#wrap{
position: relative;
margin: 50px auto;
width: 800px;
background-color: rgb(240, 236, 236);
}
#wrap .header{
width: 100%;
text-align: center;
margin-bottom: 20px;
}
h2{
font-family: "微软雅黑";
font-size:50px;
font-weight: lighter;
color: #8C3F37;
}
#wrap .content{
position: relative;
z-index: 99;
margin: 0 auto;
width: 500px;
background-color: #eee;
box-shadow: 3px 3px 30px 1px rgba(0, 0, 0, 0.6);
}
#wrap .content input{
box-sizing: border-box;
padding-left: 8px;
width: 100%;
height: 60px;
background-color: rgba(233, 232, 232, 0.6);
border: none;
border-bottom: 1px solid #ccc;
font-size: 23px;
color: rgb(126, 126, 126);
outline: none;
outline-color: rgba(255, 255, 255, 0);
}
#wrap .content ul li{
box-sizing: border-box;
padding-left: 8px;
width: 100%;
height: 60px;
line-height: 60px;
background-color: rgba(233, 232, 232, 0.6);
border: none;
border-bottom: 1px solid #ccc;
font-size: 23px;
color: rgb(126, 126, 126);
outline: none;
outline-color: rgba(255, 255, 255, 0);
}
#wrap .content ul li span,#wrap .content ul li h4{
float: left;
margin-right: 20px;
font-weight: 100;
}
#wrap .content ul li a{
float: right;
padding-right: 10px;
font-size: 30px;
color: rgb(126, 126, 126);
}
#wrap .bottom{
display: flex;
justify-content:space-between;
width: 100%;
height: 30px;
background-color: rgba(233, 232, 232, 0.6);
border: none;
border-bottom: 1px solid #ccc;
}
#wrap .bottom span{
padding: 0 15px;
font-size: 15px;
color: rgb(78, 77, 77);
line-height: 30px;
background-color: rgba(233, 232, 232, 0.6);
cursor:pointer
}
style>
head>
<body>
<div id="app">
<section id="wrap">
<div class="header">
<h2>小王记事本h2>
div>
<div class="content">
<input type="text" autofocus="true" required="required" placeholder="输入事件" autocomplete="off" v-model="message" @keydown.enter="addNote" >
<ul>
<li v-for="(item,index) in notepad" @mouseenter="show(index)" @mouseleave="hide(index)">
<span>{{ index+1 }}.span>
<h4>{{ item.message }}h4>
<a href="javascript:;" @click="removeNote(index)" v-show="item.flag">×a>
li>
ul>
<div class="bottom">
<span v-show="notepad != ''">{{ notepad.length }} items leftspan>
<span v-show="notepad != ''" @click="empty">Clearspan>
div>
div>
section>
div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script>
// 创建Vue实例,得到ViewModel
var vm = new Vue({
el:'#app',
data:{
message:"",
notepad:[],
val:"",
},
methods:{
addNote:function(){
if(!this.message){
return
}
var obj = {message:this.message,flag:false}
this.notepad.push(obj)
this.message = ""
},
removeNote:function (index) {
this.notepad.splice(index,1)
},
empty: function () {
this.notepad.splice(0,this.notepad.length)
},
show:function(index){
this.notepad[index].flag = true
},
hide:function(index){
this.notepad[index].flag = false
}
}
})
script>
body>
html>
官网地址 : https://github.com/axios/axios
1.axios必须先导包,才可以使用
2.使用get或post即可发送相应的请求
3.then方法中的回调函数会在请求成功或者失败时触发
4.通过回调函数的形参可以获取响应内容或者错误信息
<script src="https://unpkg.com/axios/dist/axios.min.js">script>
简单用法:
axios.get(地址?key=value&key2=values).then(function(response){},function(err){}) //get请求
axios.post(地址,{key:value&key2:values}).then(function(response){},function(err){}) //post请求
两个测试接口:
1.笑话接口(get): https://autumnfish.cn/api/joke/list 参数名num 参数说明:笑话条数,类型Number
2.用户注册接口(post):https://autumnfish.cn/api/user/reg 参数名: username 参数说明:用户名 备注:不能为空
本身没有依赖关系,导入先后无没有关系。
1.axios回调函数中的this已经改变,无法访问到data中的数据
2.把this保存起来,回调函数中直接使用保存的this即可
3.和本地应用的最大区别就是改变了数据来源
接口:http://wthrcdn.etouch.cn/weather_mini GET 请求参数:city
<html lang="cn">
<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>Documenttitle>
<link rel="stylesheet" href="./css/mini.css">
head>
<body>
<div id="app" class="weather-wrap">
<div class="header"><img src="./img/weaher-logo.png" alt="">div>
<div class="content">
<input type="text" class="city-input" v-model="city" @keydown.enter="searchWeather">
<input type="button" value="搜索" class="city-search" @click="searchWeather">
div>
<div class="hot-city">
<span @click="hotCity('北京')">北京span>
<span @click="hotCity('上海')">上海span>
<span @click="hotCity('杭州')">杭州span>
<span @click="hotCity('宣城')">宣城span>
div>
<ul class="weather-list">
<li v-for="item in weather">
<p v-text="item.type">p>
<div>
<span v-text="item.low">span>
<span v-text="item.high">span>
<p v-text="item.date">p>
div>
li>
ul>
div>
<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js">script>
<script src="./js/min.js">script>
body>
html>
*{margin: 0;padding: 0;}
body{
background-color: #000;
}
ul{
list-style: none;
}
.weather-wrap{
margin: 150px auto;
width: 1080px;
height: 800px;
text-align: center;
}
.weather-wrap .content{
margin: 0 auto;
width: 584px;
overflow: hidden;;
}
.weather-wrap .city-input{
float: left;
width: 500px;
height: 30px;
font-size: 18px;
background-color: #000;
/* border:none; */
color: #fff;
}
.weather-wrap .city-search{
float: left;
width: 80px;
height: 34px;
background-color: #fff;
border: none;
font-size: 17px;
}
.weather-wrap .hot-city{
margin: 0 auto;
width: 584px;
text-align: left;
}
.weather-wrap .hot-city span{
color: ivory;
font-size: 12px;
cursor: pointer;
}
.weather-wrap .weather-list {
display: flex;
justify-content: space-around;
margin: 90px auto;
width: 900px;
}
/* .weather-wrap .weather-list li:nth-child(2n){
border-left: 1px solid rgba(255, 255, 255, .4);
border-right: 1px solid rgba(255, 255, 255, .4);
} */
.weather-wrap .weather-list li p:first-child{
color: orange;
font-size: 30px;
font-weight: 500;
}
.weather-wrap .weather-list li span{
font-size: 12px;
color: orange;
}
.weather-wrap .weather-list li p:last-child{
font-size: 12px;
color: #ccc;
}
window.onload = function () {
// 创建Vue实例,得到ViewModel
var vm = new Vue({
el:'#app',
data:{
city:"",
weather:[]
},
methods:{
searchWeather:function () {
// http://wthrcdn.etouch.cn/weather_mini
axios.get('http://wthrcdn.etouch.cn/weather_mini?city=' +this.city)
.then((respones)=>{
this.weather = respones.data.data==undefined?[{date:"",high:"",low:"",type:"没有查询到该城市的天气"}]:respones.data.data.forecast;
},(err)=>{
console.log(err);
})
},
hotCity: function (c) {
this.city = c
this.searchWeather()
}
}
})
}
功能:
1.歌曲搜索
2.歌曲播放
3.歌曲封面
4.歌曲评论
5.播放动画
6.mv播放
接口:https://autumnfish.cn/search 请求方法 get 请求参数 keywords 响应内容:歌曲搜索结果
歌曲url地址获取接口 : https://autumnfish.cn/song/url 请求方法 get 请求参数 id 响应内容:歌曲url
歌曲url地址获取接口 : https://autumnfish.cn/song/detail 请求方法 get 请求参数 ids 响应内容:歌曲详情
热门评论接口 https://autumnfish.cn/comment/hot?type=0 请求方法 get 请求参数 id (歌曲id,type固定为0) 响应内容:歌曲热门评论
mv接口地址: https://autumnfish.cn/mv/url 请求方法 get 请求参数 id (mvid,为0说明没有mv) 响应内容:mv地址
作用域:
ES5之前因为if和for都没有块级作用域的概念,所以在很多时候,我们都必须借助于function的作用域来解决应用外面变量的问题
ES6中,加入了let ,let 它是有 if 和 for 的块级作用域的
1.变量作用域:变量在什么范围内是可用
{
var name = 'why';
console.log(name);
}
console.log(name);
2.没有块级作用域引起的问题:if 的块级
var func;
if (true) {
var name = 'why';
func = function () {
console.log(name);
}
// func()
}
name = 'pike'
func() // 我们并不希望这里调用的时候,前面if作用域中的name 被修改
3.没有块级作用域引起的问题:for 的块级;
原来的做法是用函数的闭包,因为函数是一个作用域。ES5中函数也是一个作用域
var btns = document.getElementsByTagName('button');
for (var i=0; i<btn.length; i++){
btns[i].addEventListener('click',function(){
console.log('第'+ i + '个按钮被点击')
})
}
// 由于 var 声明在for中没有块级作用域,因此 此处一旦触发了点击事件,打印的永远是遍历最后一次的 i 的 值
{btns[i].addEventListener('click',function(){console.log('第'+ i + '个按钮被点击')
}) i=0}
{btns[i].addEventListener('click',function(){console.log('第'+ i + '个按钮被点击')
}) i=1}
{btns[i].addEventListener('click',function(){console.log('第'+ i + '个按钮被点击')
}) i=2}
... //这样的遍历操作 由于 i 是var声明的 因此for中的i 是同一个,循环结束后 i 是最后一次的赋值
//可以用函数闭包的方式解决
for (var i=0; i<btn.length; i++){
(function(i){
btns[i].addEventListener('click',function(){
console.log('第'+ i + '个按钮被点击')
})
})(i)
}
// 这样每次回调拿的 都是 这个函数作用域中的i 值 ,该值不会被其他作用域改变
es6 使用 let 声明,此时 该声明的变量 有 块级作用域
var btns = document.getElementsByTagName('button');
for (let i=0; i<btn.length; i++){
btns[i].addEventListener('click',function(){
console.log('第'+ i + '个按钮被点击')
})
}
1.注意一: 一旦给const修饰的标识符被赋值之后,不能修改
2.注意二: 在使用const 定义的标识符,必须进行赋值
3.注意三:常量的含义是指向的对象不能修改,但是可以改变对象的内部属性
const obj = { }
1.属性的增强写法
const name = 'why';
const age = 18;
const height = 1.88;
// ES5的写法
const obj = {
name: name,
age: age,
height: height
};
// ES6 的写法
const obj = {
name,
age,
height
};
2.函数的增强写法
// ES5写法
const obj = {
run: function () { console.log('跑'); },
eat: function () { console.log('吃'); }
}
// ES6写法
const obj = {
run(){ console.log('跑'); },
eat(){ console.log('吃'); }
}
1.动态绑定style
<h2 :style="{ backgroundColor: 'pink', color: 'yellow'}" > 哈哈哈</h2>
<h2 v-cloak :style="getStyle()" >{{ message }}</h2>
computed:{
fullname: function(){
return this.firstName + this.lastName
}
},
元素中调用的时候不需要加 () ,虽然定义的是一个方法,但是由于是计算属性, Vue内部了一系列处理,使用的时候直接当属性使用即可
内部原理,有set 和 get两个函数,当对这个属性进行赋值时候就会出发set函数,一般不做赋值处理,因此 就可以使用语法糖 直接简写
computed: {
fullName:{
set: function(newValue){
const names = newValue.split(' ');
this.firstName = names[0];
this.lastName = names[1]
// 当对这个计算属性 fullName 赋值时候就会出发set函数 app.fullName = 'Huang mengxiang'
},
get: function(){
return this.firstName + ' ' + this.lastName
}
}
},
**注意:!**这类计算属性值的操作尽量使用 computed 而不是使用 methods 定义方法,原因是 计算属性的性能比定义方法更高,Vue 内部会对数据监控,一旦计算属性发生变化才会调用这个计算属性的函数,否则内部会进行缓存,直接使用。
<div id="app">
<h2 v-if="score>90">优秀h2>
<h2 v-else-if="score>80">良好h2>
<h2 v-else-if="score>60">及格h2>
<h2 v-else="score<60">不及格h2>
<h1>{{ result }}h1>
div>
const app = new Vue({
el: "#app",
data: {
score: 92
},
computed: {
result() {
let grade = '';
if (this.score > 90) { grade = '优秀'} else if (this.score > 80) {grade = '良好'} else if (this.score > 60{
grade = '及格'} else {grade = '不及格'}
return grade;
}
},
methods: {}
})
以上是响应式的数组函数,在函数调用更改数据后,页面也会得到响应式的 渲染
注意 通过索引值 修改数组中的元素不是响应式的
如 this.letters[0] = ‘bbbb’ 此时数据 letters中的第0个数据被改为’bbbb’ 但是页面并没有响应式渲染。 这种方式 Vue中没有进行监听,因此不是响应式的
filters: {
showPrice(price) {
return '¥' + parseFloat(price).toFixed(2)
}
}
<td>{{ item.price | showPrice}}td>
默认过滤器中的方法会将 | 前面的参数传入,最终return 回一个处理好的数据 ,此时 元素上也会显示处理好的数据
for(let i=0; i<arr.length; i++){
}
for(let i in arr){
// i 为 arr中每一项索引
}
for(let item of arr){
// item 为 arr中每一项
}
1.编程范式:
2.编程范式:
区分方式看第一公民
filter / map / reduce
1.filter
中的回调函数有一个要求:必须返回一个boolean值,true:当返回true时,函数内部会自动将这次回调的n加入到新的数组中,false:当返回false时,则会过滤到这次的n
2.map
中的回调函数 对原数组进行一个映射,可以将每个 n 进行一系列操作,然后 return 返回,此时新数组就会将这个return 值 加入
3.reduce
传递两个参数,第一个参数为回调函数,第二个参数为初始值。 回调函数中传递两个参数,一个是上次的计算值,第一次的时候直接等于初始值,第二个参数是回调每次拿到的数组中的遍历对象。 在回调中返回 第一个参数跟 拿到的遍历对象之间的操作。 该函数会直接返回最终 计算的值
const nums = [123, 222, 1, 6, 66, 88, 145, 69, 77, 61]
let result = nums.filter(n => n < 90).map(n => n + 33).reduce((pre, n) => pre + n, 0)
console.log(result);
原理:其实就是一个语法糖,他的背后本质上是包含两个操作:
也就是说下面的代码: 等同于下面的代码
<input type="text" v-model="message">
// 等同于
<input type="text" :value="message" @input="message=$event.target.value">
和 checkbox 一样,select也分单选和多选
单选:只能选中一个值
多选:可以选中多个值
单选
<div id="app">
<select v-model="fruits" >
<option value="苹果">苹果option>
<option value="橘子">橘子option>
<option value="香蕉">香蕉option>
<option value="梨子">梨子option>
<option value="榴莲">榴莲option>
select>
<h2>水果是:{{ fruits }}h2>
div>
<script src="./lib/vue-2.4.0.js">script>
<script>
const app = new Vue({
el: "#app",
data: {
fruits: ''
},
methods: {}
})
script>
多选
<div id="app">
<select v-model="fruits" multiple>
<option value="苹果">苹果option>
<option value="橘子">橘子option>
<option value="香蕉">香蕉option>
<option value="梨子">梨子option>
<option value="榴莲">榴莲option>
select>
<h2>水果是:{{ fruits }}h2>
div>
<script src="./lib/vue-2.4.0.js">script>
<script>
const app = new Vue({
el: "#app",
data: {
fruits: []
},
methods: {}
})
script>
<div id="app">
<label for="man">
<input type="radio" name="sex" id="man" v-model="sex" value="男">男
label>
<label for="woman">
<input type="radio" name="sex" id="woman" v-model="sex" value="女">女
label>
<h2>性别是: {{ sex }} h2>
div>
<script src="./lib/vue-2.4.0.js">script>
<script>
const app = new Vue({
el: "#app",
data: {
sex: ''
},
methods: {}
})
script>
<div id="app">
<h2>你喜欢的运动h2>
<input type="checkbox" name="" id="" v-model="spots" value="篮球">篮球
<input type="checkbox" name="" id="" v-model="spots" value="羽毛球">羽毛球
<input type="checkbox" name="" id="" v-model="spots" value="电竞">电竞
<input type="checkbox" name="" id="" v-model="spots" value="睡觉">睡觉
<input type="checkbox" name="" id="" v-model="spots" value="乒乓球">乒乓球
<input type="checkbox" name="" id="" v-model="spots" value="足球">足球
<h2>你喜欢的运动有:{{ spots }} h2>
div>
<script src="./lib/vue-2.4.0.js">script>
<script>
const app = new Vue({
el: "#app",
data: {
spots: []
},
methods: {}
})
script>
v-model 是多项选择的话 绑定数据请使用数组, 单项选择使用字符串
分三步:
// 1. 创建组建
const cpnConstructor = Vue.extend({
template: `
- 你愁啥
- 瞅你咋地
- 不咋地
- 在瞅个试试
`
})
// 2.注册组件
Vue.component('my-cpn', cpnConstructor)
<my-cpn>my-cpn>
1.全局组件注册是在全局作用域中
2.局部组件注册是在局部作用域中(Vue实例中)
<script>
const cpnConstructor = Vue.extend({
template:`
- 你愁啥
- 瞅你咋地
- 不咋地
- 在瞅个试试
`
})
const app = new Vue({
el: "#app",
data: {
sex: ''
},
methods: {},
components:{
myCpn: cpnConstructor
}
})
script>
此处注意,js中使用的 驼峰命名的 标签 myCpn
当在html中当标签使用的时候,标签名 应将大写字母改为小写,且 在该字母前面加上 杠
1.创建好组件构造器
2.当在全局中注册则为全局组件,在局部注册则为局部组件,局部组件只能在局部使用,当在其他地方使用的时候,会报错,显示找不到该组件
3.父子组件是相对的,组件在另一个组件中注册的时候,该组件则为另一个组件的子组件。
4.通过 Vue.extend() 传递一个对象可以实例化一个 组件构造器,对象中的参数 template 属性定义组件模板,后面还有一个 components属性 ,该属性跟Vue实例中的components属性用法一致,当使用该属性为一个组件注册后,则称为被注册组件的父组件
5.子组件只能在父组件中使用,而且是在 父组件的 模板 tempalte中使用
<div id="app">
<cpn2>cpn2>
div>
<script>
const cpnC1 = Vue.extend({
template: `- 你愁啥111
- 瞅你咋地11
- 不咋地11
- 在瞅个试试11
`
})
const cpnC2 = Vue.extend({
template: `
- 你愁啥222
- 瞅你咋地222
- 不咋地22
- 在瞅个试试22
`,
components: {
cpn1: cpnC1
}
})
const app = new Vue({
el: "#app",
data: {},
methods: {},
components: {
cpn2: cpnC2
}
})
// 1.全局注册组件语法糖方式
Vue.component('my-cpn', {
template: '你好啊
世界
'
})
// 2.局部注册组件的语法糖方式
const app = new Vue({
el: "#app",
data: {},
methods: {},
components: {
myCpn2: {
template: `Hello
World
`
}
}
})
关键点: 原本的 组件构造器 Vue.extend( template: 模板) 直接改为了{template:模板}
原本传 组件构造器的 地方直接改为 {template:模板},将注册跟创建构造器合在了一起,由Vue内部自行处理, 其实Vue内部也是调用了 Vue.extend( template: 模板) 构造器,然后再将构造器传到注册中
<script type="text/x-template" id="cpn">
<div>
<h2>hello world</h2>
<ul>
<li>你说啥</li>
<li>什么</li>
<li>啊啊</li>
</ul>
</div>
script>
<template id="cpn2">
<div>
<h2>hello worldh2>
<ul>
<li>????li>
<li>!!!li>
<li>eeli>
ul>
div>
template>
注意!!!!组件不能访问Vue实例数据
组件中 的数据是保存在哪里?
Vue 组件应该有自己保存数据的地方,那个地方就是注册组件的位置,除l template属性 components 属性 还有 data属性,该属性用于保存组件的数据
该属性 data 的值是一个函数,必须有返回值, return 一个对象
components: {
cpn: {
template: '#cpn2',
data(){
return {
title:'abc'
}
}
}
}
1.组件本身是会被多次使用的,如果data不是一个函数, 而是一个对象,则每次给组件的data数据都是同一个内存地址上的数据,也就是说所有该组件使用的是同一个data数据,这就会导致当一个组件的data数据更改后,其他组件都会因为这个改变而产生连锁的改变。这样会导致很多问题
如下:直观感受为什么Vue设计的时候 组件data是一个函数
function func(){
return {name:'jack',age:18}
}
let obj1 = func()
let obj2 = func()
let obj3 = func()
// 此时改变其中一个对象的属性
obj1.name = 'mike'
// obj2 obj3 并不会受到影响
// 因为每次调用func()的时候 ,函数会开辟一块内存,存储创建的对象,因此三次函数调用返回的对象,其实是不同的内存地址
//反观 如下 则会影响
let obj = {name:'aaa',age:11}
function func2(){
return obj
}
let obj11 = func2()
let obj22 = func2()
let obj33 = func2()
obj11.name = 'bbb'
// 影响的原因是 obj指向了一块存有 {name:'aaa',age:11} 的内存地址, 函数func2 返回值 就是这个地址,每次都一样
// 所以 这三个对象指向的内存地址都一样,因此一个改变,则所有指向该地址的对象都会被改变
子组件是不能引用父组件或者Vue实例的数据
但是在开发中,往往一些数据确实需要从上层传递到下层
如何进行父子组件之间的通信呢
注意!!! js代码给props 名称命名为 驼峰的话,则 v-bind绑定的时候 需要将大写改为小写,并在前面加 杠 -
在组件中,使用选项props来声明需要从父级接收到的数据
props的值有两种方式:
<div id="app">
<cpn :cmovies="movies" :cmessage="message">cpn>
div>
<template id="cpn">
<div>
<ul>
<li v-for="item in cmovies">{{ item }}li>
ul>
<h2>{{ cmessage }}h2>
div>
template>
<script src="./lib/vue-2.4.0.js">script>
<script>
const cpn = {
template: '#cpn',
props: ['cmovies', 'cmessage']
}
const app = new Vue({
el: "#app",
data: {
message: "helloWorld",
movies: ['西游记', '三国演义', '水浒传', '红楼梦']
},
methods: {},
components: {
cpn
}
})
script>
注意 props传递的名称用 v-bind 绑定在组件的标签上 不是绑定在模板上!!!!!!!!!!!
Vue.components('my-cpn',{
props:{
// 基础的类型检查 `null`匹配任何类型
propsA: Number,
propB: [String,Number],
propC:{
String,
required:true
},
// 带有默认值的数字
propD: {
type: Number,
default:100
},
propE: {
type: Object,
default:function(){
return {message:'hello'}
}
},
//自定义验证函数
propF: {
validator: function(value){
// 这个值必须匹配下列字符串中的一个
return ['string','success','warning','danger'].indexOf(value) !== -1
}
}
}
})
// 自定义类型
function Person(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
Vue.component('blog-post',{
props: {
author: Person
}
})
props 用于父组件向子组件传递数据,还有一种比较常见的是子组件传递数据或事件到父组件中
此时我们就需要借助自定义事件来完成传递了
自定义事件的流程:
$emit()
来出发事件v-on
来监听子组件事件1.创建子组件 , 父组件
2.子组件注册在父组件内部
3.子组件定义一个方法,将要传递的数据作为参数放入这个方法,然后通过 this.$emit('自定义的事件名',传递的参数)
这个方法将 传递的参数发射出去。 然后父组件通过 事件绑定的方式 v-on 绑定这个发射的事件,事件触发的函数定义时候接收一个参数 , 此时传递的参数就是 $event ,父组件不传递参数的话,默认会将 这个参数传递过去, 从而完成了 父组件访问 子组件内部的数据
<body>
<div id="app">
<cpn1>cpn1>
div>
<template id="cpn1">
<div>
<div>
<h2>hello worldh2>
<cpn2 @itemclick="show($event)">cpn2>
<h2>{{ message }}h2>
div>
div>
template>
<template id="cpn2">
<div>
<button v-for="item in categories" @click="btnClick(item)">{{ item.name }}button>
div>
template>
<script src="./lib/vue-2.4.0.js">script>
<script>
// 定义子组件构造器(虽然是一个对象,但是注册时候,vue内部会调用Vue.extend())
const cpn2 = {
template: '#cpn2',
data() {
return {
categories: [
{id: 'aaa',name: '可乐'},
{id: 'bbb',name: 'pingguo'},
{id: 'ccc', name: '向iao' },
{id: 'ddd', name: 'iaohuas' },
{id: 'eee', name: '苹果' }]
}
},
methods: {
btnClick(item) {
this.$emit('itemclick', item)
}
}
}
// 注册父组件
Vue.component('cpn1', {
template: '#cpn1',
data() {
return {
message: []
}
},
components: {
cpn2
},
methods: {
show(item) {
this.message.push(item.name)
}
}
})
const app = new Vue({
el: "#app",
data: {},
methods: {}
})
script>
body>
不论是子传父 ,还是父传子,都是在子组件构造器中定义,父传子,子组件中定义props , 子传父 ,在子组件中调用 this.$emit()方法,然后在 父组件区域内的子组件上进行配置
$children $refs
有时候我们 需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问根组件
先来看下$children的访问
const app = new Vue({
el: "#app",
data: {},
methods: {
callOn() {
// console.log(this.$children);
// this.$children[0].showDate()
console.log(this.$refs);
this.$refs.ccc.showDate()
}
},
components: {
cpn: {
template: '#cpn',
data() {
return {
message: ['海王', '海泽日晚', '海贼王', '海尔兄弟']
}
},
methods: {
showDate() {
console.log(this.message);
}
}
}
}
})
$refs
使用
1.直接在父组件的 方法中调用 this.$refs 得到的是一个空数组,要想获取子组件信息, 需要在 父组件区域的子组件标签上 加上属性 ref
2**.ref这个属性的属性值就是得到这个子组件的 属性名**。 到时候直接调用 this.$refs.属性名 即可访问子组件
$parent
在子组件定义方法,通过 this.$parent 拿到父组件,该组件在哪个父组件中注册,并使用了注册时候的方法,则获取的就是对应的父组件,只有一个父组件
访问根组件
$root
1.slot
标签即可, 在使用子组件的时候,组件内部填充的所有内容,元素就会被匹配到这个插槽上
可以给一个默认值,该默认值 在使用这个组件 没有给插槽内容的时候会自动使用2.具名插槽 slot
插槽,此时子组件被使用时候,内部插入内容就会匹配到这个插槽,因为二者都没有名字,因此能够匹配 ,当
当定义模板使用插槽 给插槽一个名字的时候,那么 直接内部使用标签填充则不会匹配到这个插槽, 只有当 使用 slot=“center” 才能匹配到这个插槽 如 我匹配到了
注意 : 2.6版本起 该具名插槽方式已被废弃
新的具名插槽方式(**重点!!!!!!!!**2.6版本更新后用法)
插槽,此时子组件被使用时候,内部插入内容就会匹配到这个插槽,因为二者都没有名字,因此能够匹配 ,当
当定义模板使用插槽 给插槽一个名字的时候,那么 直接内部使用标签填充则不会匹配到这个插槽。此时在父页面使用
来锁定这个name 为 center 的这个插槽。如果
没有name属性的话 ,其实Vue会给它一个 默认的 name 为 default , 因此匿名插槽也可以通过
来获取这个插槽Vue 的编译作用域:首先看模板是谁的,那么这个范围内的变量就从谁那里找, 因此子组件模板的作用域就是这个子组件,父组件的模板作用域就是这个父组件,Vue实例的作用域就是这个实例
作用域插槽是slot 一个比较难理解的点
1.和具名插槽一样,绑定了插槽之后,由于要获取子组件的数据,或者方法,因此 v-slot:子组件的name值
=‘自定义一个名字(该名字用来接收子组件传递的数据)’
2.那么子组件如何将数据传递出去呢?? 直接 :data = “传递的数据” 此时Vue内部会将 父页面自定义的名字与这个 :data 数据绑定起来,此时父页面的 自定义名字 . data
就是 这个 :data 我们就可以通过这个方式 将数据传到父 页面 ,供父页面使用
<body>
<div id="app">
<cpn>cpn>
<cpn>
<template v-slot:data="slotProps">
<h2>{{slotProps.user}}h2>
template>
cpn>
div>
<template id="cpn">
<div>
<slot name="data" :user="pLanguages">
<ul>
<li v-for="item in pLanguages">{{ item }}li>
ul>
slot>
div>
template>
<script src="./lib/vue.js">script>
<script>
const app = new Vue({
el: "#app",
data: {},
methods: {},
components: {
'cpn': {
template: '#cpn',
methods: {
show() {
return console.log(123);
}
},
data() {
return {
pLanguages: ['JavaScript', 'C', 'C++', 'Java', 'C#', 'Pathon'],
user: {
lastName: '123',
firstName: 'wyj'
}
}
}
}
}
})
script>
body>