一、VUE环境搭建
1.1、下载开发版本的Vue
开发版本:https://cn.vuejs.org/js/vue.js
生产版本:https://cn.vuejs.org/js/vue.min.js
1、下载完毕后引入
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
1.2、安装开发者工具
谷歌应用商店直接下载:Vue.js devtools
https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd
安装完毕后查看:
1.3、关闭vue在启动时生成的生产环境提示
<script type="text/javascript">
Vue.config.productionTip = false // 阻止vue在启动时生成的生产环境提示
</script>
在浏览器上检查
二、写一个 hello world
<body>
<!--准备一个容器-->
<div id="root">
<h1>hello: {{name}},{{age}}</h1>
<!-- {{}} 模板选择器,读取data里面的数据 -->
</div>
<script type="text/javascript">
// 阻止vue在启动时生成的生产环境提示
Vue.config.productionTip = false
// 创建Vue实例
const x = new Vue({
el: "#root", // 绑定id="root"的容器,值通常为css选择器字符串 (CSS里面的ID选择器)
data:{ // data里面存储的数据,给'el'指定的容器使用
name: "wdl",
age:18
}
})
</script>
</body>
# 执行结果:hello: wdl,18
2.1、总结下这个简单的代码
1、想让Vue工作,就必须创建一个vue实例,并且传入一个配置对象(el和data)
2、root容器的代码依然符合html规范,只不过混入了一些特殊的Vue语法
3、root容器里的代码被称为(Vue模板)
三、延伸一下
3.1、在div容器内{{}}里面除了写vue的data里面的,还可以写js表达式
<body>
<!-- 容器 -->
<div id="root">
<!-- 这里面写js表达式(a,a+b,demo(1)这种) 和 js代码(if(){},for(){}这种) -->
<h1>1,{{name}},{{addr.toUpperCase()}},{{1 + 1}}</h1>
<!--
addr.toUpperCase() 字符串大写
addr.toLowerCase() 字符串小写
-->
</div>
<script type="text/javascript">
Vue.config.productionTip = false // 阻止vue在启动时生成的生产环境提示
// 创建Vue实例
new Vue({
el: '#root', // el 指定当前实例为哪个容器服务,值通常为CSS里面的选择器
data: { // 用于存储数据,给el指定的容器'#root'使用。
name: 'sudada',
addr: 'shanghai',
},
})
</script>
</body>
# 执行结果:1,sudada,SHANGHAI,2
3.2、总结下
1、Vue实例和容器是一一对应的。
2、真是开发中只有一个Vue实例,并且会配合着组件一起使用。
3、一旦data中的数据发生改变,那么模板中用到改数据的地方也会自动更新。
4、{{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性
四、模板语法
4.1、插值语法和指令语法(v-xxx)
<body>
<div id="root">
<h1>插值语法</h1>
<!--
插值语法:一般用于解析标签体内容
-->
<h3>hello,{{name}}</h3>
<h1>指令语法</h1>
<!--
指令语法:一般用于解析标签(格式为:v-xxx)
-->
<h1>奥术{{name}}大师</h1>
<a :href="url">百度</a>
<a :href="school.url">点我去学校{{school.name}}学习</a>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue ({
el:'#root',
data:{
name:'jack',
url:'http://www.baidu.com',
school:{
name:"sudada",
url:'http://www.sougou.com'
}
}
})
</script>
4.2、总结插值语法和指令语法
1、插值语法:一般用于解析"标签体"内容(<h1>xxx</h1>,这个标签了里面的"xxx"就是标签体)
写法:{{xxxx}},xxxx是js表达式,且可以直接读取到data中的所有属性
2、指令语法:格式为 "v-xxx" 一般用于解析"标签" (包含标签属性,标签内容,绑定事件等)(<a :href="school.url" x="hello">,这里面的":href"和x="hello"就是标签属性 )
写法:v-bind:href="xxx" 简写为 :href="xxx","xxx"会被当做js表达式执行,可以直接读取到data中的所有属性。
五、数据绑定
5.1、单向、双向数据绑定
<body>
<div id="root">
<h1>{{name}}</h1>
单向数据绑定: <input type="text" :value="name"><br>
双向数据绑定: <input type="text" v-model="name"><br>
<!--错误代码示例,如下,因为v-model只能应用在"表单"类元素(输入类元素,如上)-->
<h2 v-model:x="name">hello</h2>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:"#root",
data:{
name:"wdl",
input_value:"xxx"
}
})
</script>
5.2、单向、双向数据绑定总结
单向数据绑定(只针对表单input输入类,input输入的值就是value):v-bind:value="name",里面的'value'会去读"name"的值,当修改'value'的值时,"name"的值不会改变。只能读不能改
双向数据绑定(只针对表单input输入类,input输入的值就是value):v-model:value="name",里面的'value'会去读"name"的值,当修改'value'的值时,"name"的值也会跟着改变。可读可改写。
v-model:value 可以简写为:v-model,因为v-model默认收集的就是value值。
数据绑定简写:
单向数据绑定: <input type="text" :value="name"><br>
双向数据绑定: <input type="text" v-model="name"><br>
六、el和data的两种写法
<body>
<div id="root">
<h1>hello,{{name}}</h1>
</div>
<script type="text/javascript">
// 一、el 的2种写法(2种方式皆可正常使用)
// el 方法:把vue实例对象绑定(挂载)到容器(定义时就指定绑定哪个容器 )
new Vue({
el:"#root",
data:{
name:"111",
}
})
// $mount 方法:把vue实例对象,绑定(挂载)到容器(最后在指定绑定哪个容器)
const v = new Vue({
data:{
name:"222",
}
})
console.log(v)
v.$mount("#root")
// 二、data 的2种写法
new Vue({
el: "#root",
// data的第一种写法:对象式
data: {
name: "sudada",
},
// data的第二种写法:函数式(不能写成"=>"函数。data函数必须要返回一个数据,这个返回的数据,就是容器内用到的数据。比如容器内用的是name,那么这里就要return一个name)
data(){ // 普通函数,原生写法:data:function
console.log(this) // 此处的this是vue实例对象(这里的this就等于"const v = new Vue"的v)
return{
name: "szq"
}
}
})
</script>
七、MVVM模型
7.1、MVVM模型图解
MVVM模型:
1、M:模型(Model):data中的数据
2、V: 视图(View):页面模板(DOM)
3、VM:视图模型(ViewModel):Vue实例,简称vm
观察发现:
1、data对象中的所有属性(值),最后都出现在了vm身上。(可以通过vm.xxx拿到data对象里面xxx的值)
2、vm身上的所有属性以及Vue原型上所有属性,在Vue模板中都可以直接使用。(vm 这个变量名就表示了Vue实例对象)
7.2、用法说明
<body>
<!-- View 模型 -->
<div id="root">
<h1>学校:{{name}}</h1>
<h1>地址:{{addr}}</h1>
<h1>1:{{$options}}</h1>
<!-- $options:vm身上的属性,这里直接可以调用(仅做测试,目的是证明"模板"里面可以直接调用vm的所有属性) -->
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
// ViewModel模型,简称vm(vm 这个变量名就表示了Vue实例对象)
const vm = new Vue({
el:"#root",
// Model 模型
data:{
name:"sudada",
addr:"shanghai",
}
})
console.log(vm)
console.log(vm.name)
</script>
八、Vue中的数据代理
8.1、原理与实现
1、Vue中的数据代理:是通过vm对象中属性的操作(读/写)
2、Vue中数据代理的好处:更加方便的操作data中的数据
3、基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm的属性,都指定一个"getter/setter"。
在"getter/setter"内部去操作(读/写)data中对应的属性。
4、实践证明:
console.log(vm.name):获取的是data里面name属性的值,实际上调用的是Vue的"get name"方法实现的。
console.log(vm.name="szq"):修改的是data里面name属性的值,实际上调用的是Vue的"set name"方法实现的。
5、vm如何获取data对应的值,使用vm._data,返回的值是是一个对象。(vm._data == options.data == data)
8.2、数据代理图示
九、事件处理
9.1、绑定事件(鼠标点击/单击时触发)
1、绑定事件v-on:click 简写为 @click
2、绑定事件触发的函数要写到methods里面,不建议写到data里面
<body>
<div id="root">
<!--
v-on:click 简写为 @click
<button v-on:click="showinfo1">点我提示1</button> 注释:当"button"元素被click(点击)时,执行一个回调函数"showinfo1"
-->
<button @click="showinfo1">点我提示1(不传参)</button>
<!--
点击时传入一个参数:showinfo2(666)
点击时传入一个参数:showinfo2($event,666) 保留event,需要通过"$"占位符实现,这样可以在函数内部使用event.xxx的一些功能
-->
<button @click="showinfo2($event,666)">点我提示2(传参)</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:'#root',
data:{
name:'xxx',
address:'sh',
},
// 通过methods配置项,把事件的回调函数放到这里面。
methods:{
// showinfo1 函数可以传递对象,默认对象式event
// event:事件对象(点击按钮后,默认提供)
// 可以通过event.target拿到目标("button"按钮)
// 函数内的打印this, 拿到的就是vm(Vue实例对象)
// 所有被vue管理的函数最好都写成普通函数,尽量不写箭头函数
showinfo1(event){
console.log(event.target)
console.log(this)
alert('点我提示1')
},
showinfo2(event,number){
console.log(event.target)
console.log(number)
alert('点我提示2')
},
}
})
</script>
9.2.1、绑定事件总结
1、使用v-on:xxx 或者 @xxx 绑定事件,其中"xxx"是事件名
2、事件的回调需要配置在methods对象中,最终会在vm上
3、methods配置的函数,不需要用箭头函数,否则this就不是vm了
4、methods配置的函数,都是被Vue所管理的函数,this的指向是vm或者组件实例对象
5、@click="showInfo1" 和 @click="showInfo2($event,66)"效果一样,但是后者可以传参。
9.2、事件的修饰符
事件修饰符可以连着写:@click.prevent.stop 先阻止默认事件,再阻止事件冒泡
1、@click.prevent 阻止默认事件(常用)
2、@click.stop 阻止事件冒泡(常用)
3、@click.once 事件只触发一次(常用)
4、@click.capture 使用事件的捕捉模式
5、@click.self 只有event.target是当前操作的元素时才触发时间
6、@click.passive 事件的默认行为立即执行,无需等待事件回调执行完毕
9.2.1、事件处理是先捕获,在冒泡。
捕获阶段:由外往内
冒泡阶段:由内往外
9.2.2、代码示例
<body>
<style>
/* 通配符,所有元素之间都有20px的间距*/
*{
margin-top:20px;
}
.demo1{
width: 115px;
height: 110px;
float: left;
margin: 7px;
background-color: red;
}
.box1{
padding: 5px;
background-color: aqua;
}
.box2{
padding: 5px;
background-color: rosybrown;
}
</style>
<div id="root">
<!-- 常用
@click.prevent(事件修饰符): 阻止默认行为,也就是点击后,只有弹窗,没有a标签的URL跳转
-->
<a href="https://www.baidu.com" @click.prevent="showinfo">点我跳转</a>
<!-- 常用
@click.stop:阻止事件冒泡,也就是点击后只触发一次alert提示。否则会有多次alert提示
-->
<div class="demo1" @click="showinfo">
<!-- 这里加@click.stop之后,里面的button被点击时就不会触发"alert" -->
<button @click.stop="showinfo">点我提示2</button>
</div>
<!-- 常用
@click.once:事件只触发一次alert提示,下次点击就不会在触发alert提示了。
-->
<button @click.once="showinfo">点我提示3</button>
<!-- 不常用 @click.capture:使用事件的捕捉模式-->
<div class="box1" @click.capture="showmsg(1)">
div1
<div class="box2" @click="showmsg(2)">
div2
</div>
</div>
<!-- 不常用 @click.self:只有event.target是当前操作的元素时才触发时间-->
<div class="demo1" @click.self="showinfo">
<button @click="showinfo">点我提示4</button>
</div>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:"#root",
data:{
name:"sudada",
addr:"shanghai",
},
methods:{
showinfo(e){
// e.preventDefault(); // 阻止默认行为,需要在函数内接收参数"e"
// e.stopPropagation(); // 阻止事件冒泡,需要在函数内接收参数"e"
alert("同学你好")
},
showmsg(msg){
console.log(msg)
}
}
})
</script>
9.3、键盘事件
9.3.1、总结键盘事件
keyup:任意键盘"按下去然后松手"就会触发
keydown:任意键盘"按下去"就会触发
1)、Vue常见的按键别名:
回车键:@keyup.enter
删除键:@keyup.delete(退格键和delete键都能触发)
退出键:@keyup.esc
空格键:@keyup.space
换行键:@keyup.tab(这个键比较特殊,需要使用:@keydown.tab,使用@keyup.tab会和"tab键"本身的功能有冲突)
上键:@keyup.up
下键:@keyup.down
左键:@keyup.left
右键:@keyup.right
2)、系统修饰键(特殊用法):@keyup.ctrl,@keyup.alt,@keyup.shift,@keyup.meta
配合@keydown使用:正常触发
配合@keyup使用:按下修饰键的同时,在按下其他键,随后释放其他键,时间才会触发。
9.3.2、键盘事件示例
<body>
<div id="root">
<h1>hello,{{name}}</h1>
<!--
keyup:任意键盘"按下去然后松手"就会触发
keydown:任意键盘"按下去"就会触发
Vue常见的按键别名:
回车键:@keyup.enter
删除键:@keyup.delete(退格键和delete键都能触发)
退出键:@keyup.esc
空格键:@keyup.space
换行键:@keyup.tab(这个键比较特殊,需要使用:@keydown.tab,使用@keyup.tab会和"tab键"本身的功能有冲突)
上键:@keyup.up
下键:@keyup.down
左键:@keyup.left
右键:@keyup.right
系统修饰键(特殊用法):@keyup.ctrl,@keyup.alt,@keyup.shift,@keyup.meta
配合@keydown使用:正常触发
配合@keyup使用:按下修饰键的同时,在按下其他键,随后释放其他键,时间才会触发。
-->
<!--
@keyup.enter:在input框内输入内容,只有按下"回车"键后,才能拿到input框内所有的内容。
如果写成@keyup="showinfo",那么输入一行就拿到一行,拿到的内容就不完整(有多余的)
-->
<input type="text" @keydown.tab="showinfo">
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:"#root",
data:{
name:"szq"
},
methods:{
showinfo(e){
// e 就是event,默认传递的事件对象
// e.target.value 拿到的就是input框内输入的值
console.log(e.target.value)
}
}
})
</script>
十、计算属性
什么是属性 Property ?
data(对象) 里面定义的值,都是属性,都可以使用 {{ 属性 }} 直接解析到 属性的值。
data:{
firstname:"苏",
lastname:"三",
},
什么是方法 method ?
fullname 这个函数就是一个方法,可以使用 {{ fullname() }} 直接拿到 方法的返回值。
methods:{
fullname(){
return this.firstname + "-" + this.lastname
}
}
10.1、什么是计算属性?
计算属性是单独定义的属性(格式和data一样),是一个全新的配置项 "computed"。
计算属性总结:先拿到现有的属性(a,b),然后做一次加工(a+b),最后生成一个全新的属性(a+b=c,c这个属性就是计算属性)
1、定义:要用的属性c不存在,要通过"已有的属性a,b","计算a+b=c"得来的。
2、原理:底层借助了Object.defineproperty方法提供getter和setter。
3、get函数什么时候执行?(计算属性什么时候执行?)
初次读取时会执行一次;
当计算属性内"任何一个被依赖的数据"发生"改变"时,计算属性会被再次执行。
4、优势:与methods实现相比,内部有"缓存机制(可以复用)",效率更高,调试方便。
5、备注:
计算属性最终会出现在vm上,直接读取使用即可:this.xxx。
如果计算属性要被修改,那必须写"set函数"去响应修改,且"set函数"要对原属性a或者b的值做修改,修改的值就是"set函数"传入的值。
10.2、代码示例
<body>
<div id="root">
姓:<input type="text" v-model="firstname"> <br>
名:<input type="text" v-model="lastname"> <br>
全名: <span>{{fullname}}</span>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:"#root",
data:{
firstname:"su",
lastname:"dada",
},
// 计算属性 computed
computed:{
// 把计算过程配置成一个对象
fullname:{
// 读fullname时,执行get函数,并返回值
// get什么时候调用?
// 1.初次读取fullname时
// 2.所依赖的数据(this.firstname或者this.lastname)发生变化时。
get(){
console.log('get被调用')
return this.firstname + "-" + this.lastname
},
// 当fullname被修改时,那么执行set函数
// value 就是被修改后收到的值,比如把 fullname="张-三"修改为 fullname="李-四",那么"value"的值就是"李-四"
set(value){
console.log('set被调用', value)
// 把收到的value的值,做拆分
const arr = value.split('-')
// 姓就是数组的第一个值
this.firstname = arr[0]
// 名就是数组的第二个值
this.lastname = arr[1]
}
}
}
})
</script>
10.3、计算属性简写(只有在不写set的情况下才能使用简写方式,如果有set的话,不能简写。)
<body>
<div id="root">
姓:<input type="text" v-model="firstname"> <br>
名:<input type="text" v-model="lastname"> <br>
全名: <span>{{fullname}}</span>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:"#root",
data:{
firstname:"su",
lastname:"dada",
},
// 计算属性 computed
computed:{
// 这里的函数"fullname",就是计算属性的名称,这个函数和"getter函数"写法一致。
fullname(){
console.log("get发生了调用")
return this.firstname + '-' + this.lastname
}
}
})
</script>
十一、监视属性
11.1、监视属性watch总结
1、当"被监视"的属性发生变化时,"回调函数handler"自动调用,进行相关操作
2、"被监视"的属性必须存在,才能进行监视
3、监视属性的2种写法:
1. new Vue 时传入watch配置
2. 通过 vm.$watch 监视
11.2、监视属性,代码示例
<body>
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeweather">切换天气</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:"#root",
data:{
isHot: true,
},
computed:{
info(){
return this.isHot ? "炎热" : "凉爽"
}
},
methods:{
changeweather(){
this.isHot = !this.isHot
}
},
// 监视方法一(2选1):
watch:{
// 使用watch方法,监测一个数据,这里是"isHot"
isHot:{
// 初始化时让handler调用一下。
immediate:true,
// 当 isHot 发生改变时,handler被调用。
// oldValue 修改前的值
// newValue 修改后的值
handler(newValue,oldValue){
console.log("isHot被修改了",newValue,oldValue)
},
}
}
})
// 监视方法二(2选1):
// vm.$watch("isHot",{
// immediate:true,
// handler(newValue,oldValue){
// console.log("isHot被修改了",newValue,oldValue)
// },
// })
</script>
11.3、深度监视(举例说明)
1.Vue中的watch默认不监测"对象内部值"的改变 (详见:11.3.1)
2.配置的"deep:true"可以监测"对象内部值"改变 (详见:11.3.2)
3.备注:
1.Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以。
2.使用watch时可以根据数据的具体结构,决定是否采用深度监视(deep:true)。
11.3.1、Vue中的watch默认不监测"对象内部值"的改变
例子1:当numbers属性值是numbers:{a=1,b=2},numbers.a发生改变时,watch是不监测的,handler函数不会执行。
<body>
<div id="root">
<h3>a的值是:{{numbers.a}}</h3>
<button @click=numbers.a++>点我a++</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:"#root",
data:{
numbers:{
a:1
}
},
watch:{
// 使用watch方法,监测numbers属性
numbers:{
handler(newValue,oldValue){
console.log("numbers被修改了",newValue,oldValue)
}
}
}
})
</script>
例子2:当numbers属性值是numbers:true,此时当numbers的值发生改变(numbers=false)时,watch是监测的,handler函数会执行。
<body>
<div id="root">
<h3>a的值是:{{numbers}}</h3>
<button @click=numbers=!numbers>点我修改a</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:"#root",
data:{
numbers:true
},
watch:{
// 使用watch方法,监测numbers属性
numbers:{
handler(newValue,oldValue){
console.log("numbers被修改了",newValue,oldValue)
}
}
}
})
</script>
11.3.2、配置"deep:true"时,watch可以监测"对象内部值"改变
当numbers属性值是numbers:{a=1,b=2},numbers.a发生改变时,watch是监测的,handler函数会执行。
<body>
<div id="root">
<h3>a的值是:{{numbers.a}}</h3>
<button @click=numbers.a++>点我a++</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:"#root",
data:{
numbers: {
a:1,
b:2
}
},
watch:{
// 使用watch方法,监测numbers属性
numbers:{
deep:true,
handler(newValue,oldValue){
console.log("numbers被修改了",newValue,oldValue)
}
}
}
})
</script>
11.3.3、watch监视多级结构中"某个属性"的变化
当numbers属性值是numbers:{a=1,b=2},numbers.a发生改变时,或者numbers.b发生改变时,watch是能监测到的,handler函数会执行。
<body>
<div id="root">
<h3>a的值是:{{numbers.a}}</h3>
<button @click=numbers.a++>点我a++</button>
<hr/>
<h3>b的值是:{{numbers.b}}</h3>
<button @click=numbers.b++>点我b++</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:"#root",
data:{
numbers: {
a:1,
b:2
}
},
watch:{
// 监视多级结构中某个属性的变化,这里监视的是"numbers.a"
"numbers.a":{
handler(newValue,oldValue){
console.log("numbers.a被修改了",newValue,oldValue)
},
},
// 监视多级结构中某个属性的变化,这里监视的是"numbers.b"
"numbers.b":{
handler(newValue,oldValue){
console.log("numbers.b被修改了",newValue,oldValue)
},
},
}
})
</script>
11.4、监视属性的简写(当对象里面只有 handler 时,就可以使用简写了。就不能配置 "immediate:true" 和 "deep:true")
<body>
<div id="root">
<h3>a的值是:{{numbers}}</h3>
<button @click=numbers=!numbers>点我修改a</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:"#root",
data:{
numbers: true
},
watch:{
// 简写:当对象里面只有 handler 时,就可以使用简写了。就不能配置 "immediate:true" 和 "deep:true"
numbers(newValue,oldValue){
console.log("numbers被修改了",newValue,oldValue)
}
}
})
// 简写:就不能配置 "immediate:true" 和 "deep:true"
// vm.$watch("isHot",function (newValue,oldValue){
// console.log("isHot被修改了",newValue,oldValue)
// })
</script>
11.5、计算属性和监视属性的区分
computed和watch的区别:
1.computed能完成的功能,watch都可以完成,
2.watch能完成的功能,computed不一定能完成,比如watch可以进行异步操作。
2个重要的小原则:
1.所有被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或者 实例对象
2.所有不被Vue管理的函数(定时器的回调函数,ajax的回调函数等),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象。
十二、绑定样式(不变的样式写死,变化的样式使用":class"动态绑定)
12.1、绑定样式写法说明
1.class 样式(常用)
写法::class="xxx",xxx可以是字符串,对象,数组
字符串写法,适用于:样式的类名不确定,需要动态指定。
数组写法,适用于:要绑定的样式个数不确定,名字也不确定。
对象写法,适用于:要绑定的样式个数确定,名字也确定,但要动态决定是否使用。
2.style 样式(不常用)
:style="{fontSize: xxx}" 其中xxx是动态值。
:style="xxx" 其中xxx是样式对象。
12.2、绑定样式例子
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="../js/vue.js"></script>
<style>
.normal{
color: red;
font-size: 15px;
margin-left: 25px;
}
.happy {
color: yellow;
font-size: 10px;
text-decoration: line-through;
margin-left: 10px;
}
.nohappy{
color: green;
font-size: 10px;
text-decoration: line-through;
margin-left: 10px;
}
</style>
</head>
<body>
<div id="root">
<!-- 绑定class样式,字符串写法,适用于:样式的类名不确定,需要动态指定。-->
<div class=normal :class=mood @click=changemood>{{name}}</div> <br/>
<!-- 绑定class样式,数组写法,适用于:要绑定的样式个数不确定,名字也不确定-->
<div class=normal :class=classarr @click=changemood>{{name}}</div> <br/>
<!-- 绑定class样式,对象写法,适用于:要绑定的样式个数确定,名字也确定,但要动态决定是否使用-->
<div class=normal :class=classobj> {{name}} </div> <br/>
<!-- 绑定style样式,对象写法-->
<div class=normal :style=styleobj> {{name}} </div> <br/>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:"#root",
data:{
name:"sudada",
// 字符串写法
mood:"normal",
// 数组写法
classarr:["nohappy","happy"],
// 对象写法
classobj:{
normal:false, // 为false默认不使用, 为true时使用
happy:true
},
// 绑定style样式,对象写法
styleobj:{
fontSize:"40px", // fontSize 为固定写法,是font-size的意思(显示字体大小的格式)
color:"yellow", // 字体颜色(单个字段)
}
},
methods:{
// 字符串写法例子
changemood(){
this.mood = "happy"
},
}
})
</script>
十三、列表渲染(符合某些条件,做对应的渲染)
13.1、v-show使用
写法:v-show="表达式"
适用于:切换频率较高的场景
特点:不展示的dom元素未被移出,仅仅是是使用样式隐藏掉。
<body>
<div id="root">
<!-- v-show="false/true",为true时展示<div>,为false时不展示<div>
v-show条件渲染:页面的显示与隐藏(结构还在)-->
<div v-show="false">hello,{{name}}</div>
<h2>当前的n值是{{n}}</h2>
<button @click="n++">点我n+1</button>
<!-- // 频率高的用v-show-->
<div v-show="n===1">v-show显示111</div>
<div v-show="n===2">v-show显示222</div>
<div v-show="n===3">v-show显示333</div>
</div>
</body>
13.2、v-if使用
适用于:切换频率较低的场景
特点:不展示dom元素,直接删除
注意:v-if可以和v-else-if,v-else 一起使用,但要求结构不能被"打断"
<body>
<div id="root">
<!-- // v-if="false(0)/true(1)",为true(1)时展示<div>,为false(0)时不展示<div>
v-if条件渲染:页面的显示与隐藏(结构都不存在)-->
<div v-if="false">hello,{{name}}</div>
<!-- // 频率底的用v-if,还有v-else-if和v-else(后面不需要接条件)。
这种写法不允许打断(也就是要连续,中间不写其他的任何)-->
<div v-if="n===1">v-if显示111</div>
<div v-else-if="n===2">v-if显示222</div>
<div v-else-if="n===3">v-if显示333</div>
<div v-else>v-else哈哈哈</div>
<!-- // template 只能和v-if配合使用,不能配合v-show(加了v-show的条件不会生效)-->
<template v-if="n===1">
<h2>111</h2>
<h2>222</h2>
<h2>333</h2>
</template>
</div>
</body>
十四、列表渲染
14.1、v-for循环遍历
<body>
<div id="root">
<h2>人员列表</h2>
<ul>
<!-- for循环遍历"数组"类型:(p,index) in persons,
其中:"p"就是for循环"persons(数组类型)"的一行行数据,
"index"就是"persons"的索引值(0,1,2,xxx),
:key="index",绑定索引值 -->
<li v-for="(p,index) in persons" :key="index">
{{p.name}}-{{p.age}}
</li>
</ul>
<ul>
<!-- for循环遍历"对象"类型:v-for="(v,k) in car",
其中"v"就是对象的value,
k"就是对象的key,
:key="k",绑定对象的key -->
<li v-for="(v,k) in car" :key="k">
{{v}}--{{k}}
</ul>
<ul>
<!-- for循环遍历"字符串"类型:v-for="(s,index) in str",
其中"s"就是字符串的一个个字符,
"index"就是字符串的索引,
:key="index",绑定索引值 -->
<li v-for="(s,index) in str" :key="index">
{{s}}--{{index}}
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:"#root",
data:{
persons:[
{id:'001',name:'zhangsan',age:19},
{id:'002',name:'lisi',age:29},
{id:'003',name:'wangwu',age:39},
],
car:{
name:"adA8",
price:"80W",
color:"black",
},
str:"hello"
}
})
</script>
14.1.1、v-for循环遍历,":key"的作用与原理
1、虚拟DOM中key的作用
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据"新数据"生成"新的虚拟DOM",随后Vue进行"新的虚拟DOM"与"旧的虚拟DOM"的差异比较,比较规则如下:
2、对比规则
1.旧虚拟DOM中找到了与新虚拟DOM相同的key
若虚拟DOM中内容没变,直接使用之前的真实DOM。
若虚拟DOM中内容变了,则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
2.旧虚拟DOM中未找到与新虚拟DOM相同的key
创建新的真实DOM,随后渲染到页面
3、开发中如何选择:
1.最好使用每行数据的"唯一标识"作为key。
2.如果不需要对数据做"逆序添加"(this.persons.unshift(p)),"逆序删除"等"破坏顺序"的操作时,使用index作为Key没有问题。
<body>
<div id="root">
<h2>人员列表</h2>
<ul>
<!-- 前提:使用"this.persons.unshift(p)",在数组的"第一行"添加数据,才会出现如下情况,
如果使用"this.persons.push(p)",在数组的"最后一行"添加数据,那么:key="index"就没有影响 -->
<!-- 如果不写:key="xxx",那么默认就会把v-for循环"对象"的"index"当做 :key="index" 自动传入 -->
<li v-for="p in persons">
{{p.name}}-{{p.age}}
<input type="text">
</li>
<!-- 前提:使用"this.persons.unshift(p)",在数组的"第一行"添加数据,才会出现如下情况,
如果使用"this.persons.push(p)",在数组的"最后一行"添加数据,那么:key="index"就没有影响 -->
<!-- 如果:key="index"(传入的是对象的"索引值"),那么"虚拟dom的对比算法"之后,点击按钮生成一个新数据,就会出现错位的情况 -->
<li v-for="(p,index) in persons" :key="index">
{{p.name}}-{{p.age}}
<input type="text">
</li>
<!-- 使用"this.persons.unshift(p)" 或者 使用"this.persons.push(p)" 都不会有影响 -->
<!-- 如果:key="p.id"(传入的是对象的"唯一值"),那么"虚拟dom的对比算法"之后,点击按钮生成一个新数据,就不会出现错位的情况 -->
<li v-for="p in persons" :key="p.id">
{{p.name}}-{{p.age}}
<input type="text">
</li>
</ul>
<button @click.once="add">点击添加一个人员</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:"#root",
data:{
persons:[
{id:"001",name:"张三",age:18},
{id:"002",name:"李四",age:28},
{id:"003",name:"王五",age:38},
],
},
methods:{
add(){
const p = {id:'004',name:'老刘',age:49}
this.persons.unshift(p)
}
}
})
</script>
14.2、input框内的模糊搜索
14.2.1、计算属性实现
<body>
<div id="root">
<h2>人员列表</h2>
<input type="text" placeholder="请输入名字" v-model="keword">
<!-- 计算属性实现 -->
<ul>
<li v-for="(p,index) in filpersons" :key="index">
{{p.name}}--{{p.age}}--{{p.sex}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:"#root",
data: {
keword:"",
persons: [
{id: "001", name: "马冬梅", age: 18, sex: "女"},
{id: "002", name: "周冬雨", age: 28, sex: "女"},
{id: "003", name: "周杰伦", age: 38, sex: "男"},
{id: "004", name: "温兆伦", age: 48, sex: "男"},
],
},
// 计算属性实现
computed:{
filpersons(){
return this.persons.filter((p)=>{
// indexOf(val) == -1 说明没匹配到,等于其他"0,1,2,xxx"表示匹配数据的索引值。
// 这里依赖的数据是:"this.keword",也就是当"this.keword"发生改变,计算属性就会被执行。
return p.name.indexOf(this.keword) !== -1
})
},
}
})
</script>
14.2.2、监视属性实现
<body>
<div id="root">
<h2>人员列表</h2>
<input type="text" placeholder="请输入名字" v-model="keword">
<!-- 监视属性实现 -->
<ul>
<li v-for="(p,index) in filpersons" :key="index">
{{p.name}}--{{p.age}}--{{p.sex}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
// 监视属性实现
const vm = new Vue({
el:"#root",
data: {
keword:"",
persons: [
{id: "001", name: "马冬梅", age: 18, sex: "女"},
{id: "002", name: "周冬雨", age: 28, sex: "女"},
{id: "003", name: "周杰伦", age: 38, sex: "男"},
{id: "004", name: "温兆伦", age: 48, sex: "男"},
],
// 存放过滤后的数据
filpersons:[]
},
watch:{
// val 接收input框内输入的值
keword:{
// 初始化时让handler调用一下。
immediate:true,
handler(val){
// filter方法:"p"就是"persons"里面的一行行数据。
// 如果"val"的值为'空'时,使用"filter方法"就能拿到的就是"persons"里面所有的数据。
this.filpersons = this.persons.filter((p)=>{
// indexOf(val) == -1 说明没匹配到,等于其他"0,1,2,xxx"表示匹配数据的索引值。
return p.name.indexOf(val) !== -1
})
}
},
}
})
</script>
14.3、input框内数据排序(升降序)
<body>
<div id="root">
<ul>
<h2>人员搜索</h2>
<input type="text" placeholder="输入名称" v-model="keyWord">
<button @click="sortType=2">年龄升序</button>
<button @click="sortType=1">年龄降序</button>
<button @click="sortType=0">原顺序</button> <br/>
<li v-for="(p,index) in filepersons" :key="index">
{{p.name}}-{{p.age}}-{{p.sex}}-{{p.status}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
keyWord: "",
// 0原顺序,1降序,2升序
sortType:0,
persons: [
{id:"001",name:"马冬梅",age:48,sex:"女"},
{id:"002",name:"周冬雨",age:38,sex:"女"},
{id:"003",name:"周杰伦",age:18,sex:"男"},
{id:"004",name:"温兆伦",age:58,sex:"男"},
]
},
computed:{
filepersons(){
// 先过滤(拿到过滤后的数据arry)
const arry = this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1
})
// 然后对arry做判断是否需要排序(后排序)
if (this.sortType){
// this.sortType = 1 或者 2 时,按年龄排序(升序或者降序)
arry.sort((a,b)=>{
return this.sortType === 1 ? b.age-a.age : a.age-b.age
})
}
// 如果this.sortType=0,就返回原值或者过滤后的值(无排序)
// 如果this.sortType=1或者2,就返回原值或者过滤后的值(外加排序)
return arry
}
}
})
</script>
14.4、Vue监测数据的原理(通过Vue.set或者vm.$set)
14.4.1、例子,Vue.set:可以在"对象{}"内新增一个属性(这个属性之前是未定义的)
Vue.set:不能直接给data直接添加属性,只能给data里面的某一个对象,比如data.xxx添加属性。
<body>
<div id="root">
<h2>姓名{{student.name}}</h2>
<h2>年龄{{student.age}}</h2>
<h2 v-if="student.sex">性别{{student.sex}}</h2>
<button @click="addsex">点我添加性别属性</button>
<ul>
<h2>爱好</h2>
<li v-for="(h,index) in student.hoppy" :key="index">
{{h}}
</li>
<h2>朋友们</h2>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}}--{{f.age}}---{{f.sex}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:"#root",
data:{
name:"zzz",
addr:"shanghai",
student:{
name:"sudada",
age:18,
hoppy:["抽烟","喝酒","烫头"],
}
},
methods:{
addsex(){
// Vue.set:不能直接给data直接添加属性,只能给data里面的某一个对象,比如data.xxx添加属性。
// 给data内的某一个对象添加一个"属性",添加完毕后这个"属性"就可以被调用了。以下两种方法 2选一
// Vue.set(this.student,"sex","男") // 方法一 (给"student"对象添加一个"sex"属性,属性值是"男")
// Vue.set(this.student.hoppy,1,"打游戏") // 方法一 (给"student"对象添加一个"hoppy"属性,属性值是"打游戏")
vm.$set(this.student,"sex","男") // 方法二 (给"student"对象添加一个"sex"属性,属性值是"男")
}
}
})
</script>
14.4.2、直接对数组内的某个索引做"赋值替换",Vue是不生效的,例子如下:
直接对数组内的某个索引做"赋值替换",Vue是不生效的:
如果要修改数组内的某个索引对应的值(对象)的属性,Vue是生效的:
如果使用官方推荐的,数组的7种修改方法:(push末尾行追加,pop末尾行删除,unshift首行追加,shift首行删除,splice数组内某个元素替换,sort排序,reverse反转数组)修改,Vue是生效的。
<body>
<div id="root">
<h2>姓名:{{name}}</h2>
<h2>朋友</h2>
<button @click="addfriend">添加一个朋友</button>
<ul>
<li v-for="(f,index) in friends" :key="index">
{{f}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:"#root",
data:{
name:"szq",
friends:[
{name:"szq",age:28,sex:"男"},
]
},
methods:{
addfriend(){
// 直接对数组内的某个索引做"赋值替换",Vue是不生效的
// this.friends[0]={name:"sudada",age:18,sex:"女"}
// 如果要修改数组内的某个索引对应的值(对象)的属性,则可以生效
// this.friends[0].name="sudada"
// 如果使用官方推荐的7种方法(push末尾行追加,pop末尾行删除,unshift首行追加,shift首行删除,splice数组内某个元素替换,sort排序,reverse反转数组)修改,Vue是生效的
this.friends.unshift({name:"wss",age:18,sex:"女"})
}
}
})
</script>
14.4.3、总结Vue监视数据的原理
1、vue会监视data中所有层次的数据
2、如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
1.对象中后追加的属性,Vue默认不做响应式处理
2.如需给后添加 的属性做响应式,请使用如下API(2选1)
Vue.set(target, propertyName, value)
vm.$set(target, propertyName, value)
3、如何监测数组中的数据?
通过包裹数组更新元素的方式实现,本质就是做了两件事
1.调用原生对应的方法对数组进行更新
2.重新解析模板,进而更新页面
4、在Vue修改"数组"中的某个元素的办法
1.使用:push末尾行追加,pop末尾行删除,unshift首行追加,shift首行删除,splice数组内某个元素替换,sort排序,reverse反转数组
2.Vue.set 或者 vm.$set
5、特别注意:
Vue.set 和 vm.$set 不能给vm或者vm的"根数据对象"(也就是data) 添加属性。
6、代码示例
<body>
<div id="root">
<h1>学生信息</h1>
<button @click="student.age++">点我年龄加一</button> <br>
<button @click="addsex">添加一个性别</button> <br>
<button @click="student.sex='未知'">修改性别</button> <br>
<button @click="addfriend">添加朋友</button> <br>
<button @click="updatefirstfriendname">修改第一个朋友的名字</button> <br>
<button @click="addhoppy">添加一个爱好</button> <br>
<button @click="changehoppy">修改第一个爱好,开车</button> <br>
<button @click="nosmoke">过滤掉抽烟的爱好</button> <br>
<h2>姓名:{{student.name}}</h2>
<h2>年龄:{{student.age}}</h2>
<h2 v-if="student.sex">性别:{{student.sex}}</h2>
<h2>爱好</h2>
<ul>
<li v-for="(h,index) in student.hoppy" :key="index">
{{h}}
</li>
<h3>朋友们</h3>
<li v-for="(f,index) in student.friends">
{{f.name}}--{{f.age}}---{{f.sex}}
</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:"#root",
data:{
student:{
name:"sudada",
age:18,
hoppy:["抽烟","喝酒","烫头"],
friends:[
{name:"yyy",age:18,sex:"女"},
]
}
},
methods:{
addsex(){
vm.$set(this.student,'sex','男')
},
addfriend(){
this.student.friends.unshift({name:"szq",age:28,sex:"男"})
},
updatefirstfriendname(){
this.student.friends[0].name="张三"
},
addhoppy(){
this.student.hoppy.push("打游戏")
},
changehoppy(){
// 方法1:hoppy.splice(0,1,'开车') 把数组hoppy的第0个索引删掉,然后插入1个新的索引,值为"开车"。
// this.student.hoppy.splice(0,1,'开车')
// 方法2:把数组hoppy的第0个索引值改为'开车'
Vue.set(this.student.hoppy,0,'开车')
},
nosmoke(){
this.student.hoppy = this.student.hoppy.filter((h)=>{
return h !== '抽烟'
})
}
}
})
</script>
十五、收集form表单中的数据
15.1、例子
<body>
<div id="root">
<!-- 表达提交时(阻止默认行为"跳转页面"),触发的点击事件"demo" -->
<form @submit.prevent="demo">
<!-- v-model.trem:去除input框内首位输入的"空格" -->
账号:<input type="text" v-model.trim="account"> <br>
密码:<input type="password" v-model="password"><br>
<!-- type="number":input框输入的值,只能是整数类型 -->
<!-- v-model.number:Vue把收集到的值,转换为整数类型 -->
<!-- type="number" 和 v-model.number,一般同时使用 -->
年龄:<input type="number" v-model.number="age"><br>
性别<br>
<!-- name="sex":选项框只能勾选一个 -->
<!-- value="male|female":因为使用了"v-model",而"v-model"需要绑定value,所以就单独写一个value -->
男:<input type="radio" name="sex" v-model="sex" value="male">
女:<input type="radio" name="sex" v-model="sex" value="female"><br>
爱好<br>
<!-- value="chouyan|hejiu|tangtou":同样因为使用了"v-model",而"v-model"需要绑定value,所以就单独写一个value -->
<!-- 当type="checkbox"时(多组的勾选框),v-model="hobby"中的"hobby"需要为数组类型 -->
抽烟:<input type="checkbox" v-model="hobby" value="chouyan" >
喝酒:<input type="checkbox" v-model="hobby" value="hejiu">
烫头:<input type="checkbox" v-model="hobby" value="tangtou"><br>
所属校区:
<select v-model="city">
<option value="">请选择校区</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="shenzhen">深圳</option>
<option value="wuhan">武汉</option>
</select><br>
其他信息:
<!-- v-model.lazy:等待input输入完之后,鼠标点击别处时,Vue再收集数据 -->
<textarea v-model.lazy="other"></textarea><br>
<!-- v-model="agree":这里只需要拿到一个"布尔值"即可(勾选是true,不勾选是false),不需要拿里面输入的内容 -->
<input type="checkbox" v-model="agree"> 阅读并接受用户协议:<a href="http://www.baidu.com">《用户协议》</a><br>
<button>提交</button>
</form>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:"#root",
data:{
account:'',
password:'',
age:'',
sex:'male',
hobby:[],
city:'shanghai',
other:'',
agree:false,
},
methods:{
demo(){
console.log(this._data)
}
}
})
</script>
15.2、收集form表单数据总结:
如果:input type="text/password/number" 则v-model收集的是value的值,用户输入的就是value的值。
如果:input type="radio" 则v-model收集的是value的值,且要给标签配置value值。
如果:input type="checkbox"
1.没有配置input的value属性,那么收集的就是checked (勾选就是true 或 未勾选是false,是布尔值)
2.配置input的value属性:
2.1.v-model的初始值是非数组类型,那么收集的就是checked (勾选就是true 或 未勾选是false,是布尔值)
2.2.v-model的初始值是数组类型,那么收集的就是value组成的数组(是一个数组)
v-model的三个修饰符
1.v-model.lazy:失去焦点后在收集数据(不是事实采集数据,是输入完毕后,点击下一行时,在采集)
2.v-model.number:输入字符串转为有效地数字
3.v-model.trim:输入的字符串,首尾空格过滤(中间的空格不过滤)
十六、过滤器
16.1、过滤器的定义:对要显示的数据进行特定格式化后在显示(适用于一些简单逻辑的处理)
16.2、过滤器语法:
1.注册过滤器:Vue.filter(name,callback) 或者 new Vue(filters:{})
2.使用过滤器:{{xxx | 过滤器名}} 或 v-bind:属性 = "xxx | 过滤器名"
16.3、过滤器使用备注:
1.过滤器也可以接收额外参数,多个过滤器也可以串联
2.并没有改变原本的数据,是产生新的对应的数据。
16.4、例子
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript" src="../js/dayjs.min.js"></script>
</head>
<body>
<div id="root">
<h2>格式化后的时间</h2>
<!-- 计算属性实现 -->
<h3>现在是{{fmtTime}}</h3>
<!-- methods实现 -->
<h3>现在是{{getfmtTime()}}</h3>
<!-- 过滤器实现(本质就是函数),其中timeFormater是(过滤器)函数,time作为函数的参数传递给timeFormater -->
<h3>现在是{{time | timeFormater}}</h3>
<!-- 过滤器传参(默认第一个参数(value)是time,第二个参数(str)是'YYYY_MM_DD') -->
<h3>现在是{{time | timeFormater('YYYY_MM_DD')}}</h3>
<!-- 过滤器传参(timeFormater默认第一个参数(value)是time,第二个参数(str)是'YYYY_MM_DD',然后把timeFormater函数执行后的返回值,作为参数传递给mySlice) -->
<!-- "time"传递"timeFormater","timeFormater"传递给"mySlice",向下传递的过程 -->
<h3>现在是{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
// 全局过滤器,这里的mySlice可以参考下面的局部过滤器写法对比即可。
// Vue.filter('mySlice',function (value){
// return value.slice(0,4)
// })
new Vue({
el:"#root",
data:{
time:1621561377603,
},
computed:{
fmtTime(){
return dayjs(this.time).format("YYYY年-MM月-DD日 HH:mm:ss")
}
},
methods:{
getfmtTime(){
return dayjs(this.time).format("YYYY年-MM月-DD日 HH:mm:ss")
}
},
// 局部过滤器,关键字 "filters"
filters:{
// 函数接受的参数value,其实就是time。
// 参数str就是传递过来的'YYYY_MM_DD'
// str='YYYY年-MM月-DD日 HH:mm:ss',参数str的默认值
timeFormater(value,str='YYYY年-MM月-DD日 HH:mm:ss'){
return dayjs(value).format(str)
},
mySlice(value){
console.log(value)
// value = 2021_05_21,取前4位
return value.slice(0,4)
},
}
})
</script>
十七、内置指令
17.1、v-test 指令使用
v-bind:单向绑定解析表达式,可简写为 :xxx
例子:age="29"拿到的就是字符串类型"29",:age="29"拿到的就是经过js解析后的值,是一个整数类型29
v-model:双向数据绑定
v-for:便利数组/对象/字符串
v-on:绑定事件监听,可以简写为 @
v-if:条件渲染(动态控制节点是否存在)
v-else:条件渲染(动态控制节点是否存在)
v-show:条件渲染(动态控制节点是否展示)
v-test:
1.作用:向其所在的节点中"渲染"文本内容
2.与插值语法的区别,v-text会替换掉标签中的内容,{{xx}}插值语法则不会。
3.v-test例子
<body>
<div id="root">
<!-- 插值语法 -->
<div>名称,{{name}}</div>
<div>地址,{{name}}</div>
<!-- v-text拿到"name"的值之后,会替换掉div标签里面的内容,也就是"name"替换掉"名称," -->
<div v-text="name">名称,</div>
<div v-text="addr">地址,</div>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:"#root",
data:{
name:"sudada",
addr:"shanghai",
}
})
</script>
17.2、v-html 指令使用
1.作用:向指定节点中渲染包含 "html结构" 的内容
2.与插值语法的区别
1.v-html会替换掉标签中所有的内容,{{xx}}插值语法则不会
2.v-html可以识别html结构。
3.严重注意:v-html有安全性问题!
1.在网站上动态渲染任意html是非常危险的,容易导致XSS攻击、
2.一定要在可信任的内容上使用v-html,永远不要再用户提交的内容上。
<body>
<div id="root">
<h1>hello,{{name}}</h1>
<h1 v-html="str">王大陆</h1>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
name: "sudada",
str: '<h3>你好</h3>',
}
})
</script>
17.3、v-cloak 指令使用
1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性
2.使用css配合v-cloak 可以解决网速慢时,页面展示出{{xxx}}的问题。
17.4、v-once 指令使用
1.v-once所在节点在初次动态渲染之后,就为静态内容了
2.之后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
<body>
<div id="root">
<!-- v-once只渲染一次,之后就不在动态渲染了。-->
<h1 v-once>初始化的N值是{{n}}</h1>
<h2>当前的n值是{{n}}</h2>
<button @click="n++">点我N+1</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:"#root",
data:{
name:"sudada",
n:1,
}
})
</script>
17.5、v-pre 指令使用
1.加上v-pre指令之后,该行就变成"所见即所得",也就是vue不解析该行代码了。
2.跳过其所在节点的编译过程
3.可利用它跳过:没有使用指令语法,没有使用插值语法的节点,会加快编译。
<body>
<div id="root">
<h1 v-pre>v-pre指令的使用</h1>
<h2 v-pre>当前的n值是{{n}}</h2>
<button v-pre @click="n++">点我N+1</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:"#root",
data:{
name:"sudada",
n:1,
}
})
</script>
十八、自定义指令
18.1、函数式(例子:定义个v-big指令,和v-text指令类似,但会把绑定的数值放大10倍)
<body>
<div id="root">
<h1>hello, {{name}}</h1>
<h2>当前的值是:<span v-text="n"></span></h2>
<h2>放大10倍后的n值是 <span v-big="n"></span> </h2>
<button @click="n++">点我N+1</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:"#root",
data:{
name:"sudada",
n:1
},
// 定义一个自定义指令(局部指令),这个指令本质就是一个函数,但是不能写返回值,也就是不写return
directives:{
// big函数何时会被调用?
// 1.指令与元素成功绑定时。
// 2.指令所在的模板被重新解析时。
big(element,binding){
console.log(element) // "element"是真实的DOM元素(div里面v-big是是放在span里面的,那么这里的DOM就是span)
console.log(binding) // "binding"是一个对象,里面有个value,这个value就是v-big="n"里面"n"的值
// element.innerText:原生dom结构,元素内的文本内容。
element.innerText = binding.value * 10
}
}
})
</script>
18.2、对象式(例子:定义个v-fbind指令,和v-bind指令类似,但可以让其所绑定的input元素默认获取焦点)
<body>
<div id="root">
<h1>hello, {{name}}</h1>
<h2>当前的值是:<span v-text="n"></span></h2>
<h2>放大10倍后的n值是 <span v-big="n"></span> </h2>
<button @click="n++">点我N+1</button>
<br>
<input type="text" v-fbind:value="n">
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:"#root",
data:{
name:"sudada",
n:1
},
// 局部指令
directives:{
// big函数式
// 1.指令与元素成功绑定时。
// 2.指令所在的模板被重新解析时。
big(element,binding){
element.innerText = binding.value * 10
},
// fbind对象式
// 和上面big函数式的区别:"big函数式"只执行了"bind函数"和"update函数",没有执行"inserted函数"。
fbind:{
// 指令与元素成功绑定时,执行bind函数
bind(element,binding){
console.log("bind")
element.value=binding.value
},
// 指令所在元素被插入页面时,执行inserted函数
inserted(element,binding){
console.log("inserted")
element.focus()
},
// 指令所在的模板被重新解析时,执行update函数
update(element,binding){
console.log("update")
element.value=binding.value
},
}
}
})
</script>
18.3、自定义指令 总结
18.3.1、定义语法
1.1、局部指令
new Vue({
directives:{指令名:配置对象}
}) 或者
new Vue({
directives(){指令名:回调函数}
})
1.2、全局指令
Vue.directive(指令名:配置对象) 或者 Vue.directive(指令名:回调函数)
18.3.2、配置对象中常用的3个回调:
1.bind:指令与元素成功绑定时调用。
2.inserted:指令所在元素被插入页面时调用。
3.update:指令所在模版结构被重新解析时调用。
18.3.3、
1.指令定时不加"v-",但是使用时要加"v-"。
2.指令名如果是多个单词,要是有kebab-case命名方式,不能使用camelCase(驼峰体)。
十九、Vue生命周期函数
19.1、什么是周期回调函数
1.又名:生命周期回调函数,生命周期函数,生命周期钩子
2.是什么:vue在关键时刻帮我们调用的一些特殊名称的函数
3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
4.生命周期函数中的this指向的是 vm 或 vue组件实例对象。
19.2、总结生命周期
19.2.1、常用的生命周期钩子:
1.mounted:常用来发送ajax请求,启动定时器,绑定自定义事件,订阅消息等(初始化操作)
2.beforeDestroy:常用来清除定时器,解绑自定义事件,取消订阅消息等(收尾工作)
19.2.2、关于销毁vue实例
1.销毁后借助vue开发工具看不到任何信息。
2.销毁后自定义事件会失效,但原生DOM事件依然有效。
19.3、生命周期图示
二十、组件的使用
20.1、什么是组件?
组件是实现应用中"局部"功能"代码(html,css,js)"和"资源(各种文件)"的"集合"。
组件式编程的方式,如下图:
什么是模块?
模块一般就是一个js文件
为什么使用组件?
复用编码,简化项目编码,提高运行效率。
20.2、非单文件组件(一个文件中有多个组件)缺点:样式CSS不能跟着组件走
20.2.1、组件的基本使用方法(三大步骤)
1、定义组件
使用Vue.extend(options)创建,其中options和new Vue(options)时传入的那个options几乎一样,但区别如下:
1.el不写,原因:最终所有的组件都要经过一个vm管理,由vm中的el决定服务哪个容器
2.data必须写成函数,原因:避免组件被复用时,数据存在引用关系
备注:使用template可以配置组件结构
2、注册组件(分局部注册和全局注册)
1.局部注册:在vm里面传入components选项
2.全局注册:使用Vue.component("组件名",组件值)
3、使用组件(写"组件"标签即可实现组件的使用与复用)
<组件名></组件名>
4、案例
<body>
<div id="root">
<h2>{{msg}}</h2>
<!-- 第三步:使用组件(直接写"组件"标签即可实现组件的使用与复用) -->
<school></school>
<school></school>
<hr>
<student></student>
<student></student>
<hr>
<!-- 第3步:使用全局组件(直接写"组件"标签即可实现组件的使用与复用) -->
<hello></hello>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
// 第一步:创建school组件:Vue.extend
// 组件定义式不需要写"el"配置项
// data必须要写函数式,这样每次拿到的值都是一样的,如果写成对象式的话,会在内存里面有引用关系,会修改对象内的值。
const school = Vue.extend({
// 非单文件组件使用"template"这种方式
template:`
<div>
<h2>学校名称:{{schoolname}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
`,
data(){
return {
schoolname:"学校",
address:"上海",
}
}
})
// 第一步:创建student组件
const student = Vue.extend({
// 非单文件组件使用"template"这种方式
template:`
<div>
<h2>学生地址:{{name}}</h2>
<h2>学生名称:{{age}}</h2>
</div>
`,
data(){
return {
name:"王大陆",
age:18
}
}
})
// 第1步:创建hello组件(这个组件用来注册全局组件)
const hello = Vue.extend({
// 非单文件组件使用"template"这种方式
template:`
<div>
<h2>你好啊~{{name}}</h2>
</div>
`,
data(){
return {
name:"牛牛牛",
}
}
})
// 第2步:注册全局组件
Vue.component('hello',hello)
const vm = new Vue({
el:"#root",
data:{
msg:"hello"
},
// 第二步:注册组件(局部注册)
// 使用关键字:components,对象格式,里面的"key"就是使用时的"组件名"
components:{
school:school,
student:student
}
})
</script>
20.2.2、组件的注意点
1.关于组件名称:
一个单词组成的"组件名":
写法1:school(首字母小写)
写法2:School(首字母大写)
多个单词组成的"组件名":
写法1:'my-school'(必须要有引号)
写法2:MySchool (需要脚手架支持,否则报错)
备注:
组件名不能和HTML已有的元素名称重复,比如:h2,H2都不行
可以使用name配置项,指定组件在浏览器Vue开发者工具中呈现的名称。
2.关于组件标签
写法1:<school></school> 常用
写法2:<school/> 不用脚手架时,<school/>会导致后续组件不能渲染。
3.组件的简写方式:
const school = Vue.extend({options}) 可简写为:const school = {options}
4.案例(包含上述1,2,3)
<body>
<div id="root">
<h1>hello,{{name}}</h1>
<school></school> <!-- 写法1,推荐 -->
<!-- <school/> 写法2:单闭合 -->
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
// 组件定义简写:不需要 Vue.extend()
const school = {
// name:定义组件时,给组件起的别名(这个别名也就是浏览器Vue开发者工具拿到的组件名)
// 具体在代码里面使用时还是要写'components'注册的组件名
name:'sudada',
template:`
<div>
<h2>学校名称:{{schoolName}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
`,
data(){
return {
"schoolName": "上海大学",
"address":"shanghai",
}
}
}
const vm = new Vue({
el:"#root",
data:{
name:"szq",
addr:"shanghai",
},
components:{
school:school
}
})
</script>
5.还有个特殊的点(可以使用name配置项,指定组件在浏览器Vue开发者工具中呈现的名称。)
在定义组件时,定义一个name属性,那么这个属性就能被开发者工具识别到。
20.2.3、组建的嵌套
简单理解组件的嵌套:外面的就是父组件,里面的就是子组件。
组件嵌套的例子:
<body>
<div id="root">
<app></app>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
// 组件定义:student组件(子组件)
const student = Vue.extend({
template:`
<div>
<h2>学生名称:{{studentName}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
`,
data(){
return {
"studentName": "苏兆强",
"age":18,
}
}
})
// 组件定义:school组件(父组件)
const school = Vue.extend({
template:`
<div>
<h2>学校名称:{{schoolName}}</h2>
<h2>学校地址:{{address}}</h2>
<student></student>
</div>
`,
data(){
return {
"schoolName": "上海大学",
"address":"shanghai",
}
},
// school组件内,注册子组件student(局部注册)
components:{
student:student,
}
})
// 组件定义:hello组件
const hello = Vue.extend({
template:`
<div>
<h2>hello:{{name}}</h2>
</div>
`,
data(){
return {
"name": "sudada",
}
}
})
// 组件定义:app组件 (主组件)
const app = Vue.extend({
template:`
<div>
<hello></hello>
<school></school>
</div>
`,
// app组件内,注册子组件school和hello(局部注册)
components:{
school:school,
hello:hello,
}
})
// 注册组件(局部)
const vm = new Vue({
el:"#root",
components:{
app:app,
}
})
</script>
20.2.4、VueComponent分析
VueComponent说明:
前言:vc 是源码vue.extend里面的VueComponent函数执行后返回的对象:
Vue.extend = function (extendOptions) {
var Sub = function VueComponent (options) {
this._init(options);
};
return Sub
1.school 组件本质是一个名为VueComponent"构造函数",且不是程序员定义的,是Vue.extend生成的。
2.我们只需要写<school></school>,vue解析时会帮我们创建school组件的实例对象,即Vue帮我们执行的:new VueComponent(options)。
3.特别注意:每次调用vue.extend,返回的都是一个全新的VueComponent。
4.关于this指向:
1.在"组件"配置中:
data函数、methods函数、watch函数、computed函数 他们的this均是 【VueComponent实例对象,简称vc,也就是组件实例对象】
2.在New Vue({options}) 配置中:
data函数、methods函数、watch函数、computed函数 他们的this均是 【Vue实例对象,简称vm】
3.vm下的$children可以看到对应的vc,也就可以理解为vm管理vc。如果vc内部还有子组件的话,那就去vc下的$children可以看到对应的子vc。
5.vm 和 vc 的区别
vm有el属性,vc没有,同时vc也没法定义el。其他的属性都一样。
6.children 例子:vm里面注册了school组件(school组件里面注册了student组件)
<body>
<div id="root">
<school></school>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const student = Vue.extend({
template:`
<div>
<h2>学生名称:{{studentName}}</h2>
<h2>学生年龄:{{age}}</h2>
</div>
`,
data(){
return {
"studentName": "王大陆",
"age":18,
}
}
})
const school = Vue.extend({
template:`
<div>
<h2>学校名称:{{schoolName}}</h2>
<h2>学校地址:{{address}}</h2>
<student></student>
</div>
`,
data(){
return {
"schoolName": "上海大学",
"address":"shanghai",
}
},
components:{
student:student
}
})
const vm = new Vue({
el:"#root",
data:{
name:"szq"
},
components:{
school:school
}
})
</script>
children 例子展示:
20.2.5、一个重要的内置关系(VueComponent.prototype.__proto__ = Vue.prototype)
为什么要有这个关系:让(组件实例对象vc)可以访问到"Vue原型"上的属性和方法。
举例说明:
<body>
<div id="root">
<school></school>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
// // 定义一个构造函数
// function demo(){
// this.a=10
// this.b=20
// }
// // 创建一个d实例对象
// const d = new demo()
//
// console.log(demo.prototype) //显示原型属性(函数才有这个属性)
// console.log(d.__proto__) //隐式原型属性(对象才有这个属性)
// 定义Vue的原型属性,在里面新增一个:x=99
Vue.prototype.x = 99
// 组件定义 (组件实例对象vc)
const school = Vue.extend({
template:`
<div>
<h2>学校名称:{{schoolName}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="showX">点我查看x</button>
</div>
`,
data(){
return {
"schoolName": "上海大学",
"address":"shanghai",
}
},
methods:{
showX(){
// 在这里通过(组件实例对象vc)获取Vue的原型属性: x=99
// 这里的this就是(组件实例对象vc)
console.log(this.x)
}
}
})
const vm = new Vue({
el:"#root",
data:{
name:"sudada",
addr:"shanghai",
},
components:{
school:school
},
})
</script>
20.3、单文件组件.vue(一个文件中只有一个组件)
"非单文件组件" 格式
<script>
// 非单文件组件写法
const school = Vue.extend({
data(){
return {
"schoolName": "上海大学",
"address":"shanghai",
}
},
methods:{
showName(){
alert(this.schoolName)
}
}
})
</script>
"单文件组件" 默认暴露的简写方式
<!-- 组件的结构(HTML) -->
<template>
<div class="demo">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
<button @click="showName">点我显示学校名称</button>
</div>
</template>
<!-- 组件交互的代码(JS) -->
<script>
// 单文件组件的简写方式:暴露方式3("默认暴露"),简写方式
export default {
name: "School", // name配置项,指定组件在浏览器Vue开发者工具中呈现的名称
data(){
return {
"name": "上海大学",
"address":"shanghai",
}
},
methods:{
showName(){
alert(this.schoolName)
}
}
}
</script>
<!-- 组件的样式(CSS) -->
<style>
.demo{
background-color: orange;
}
</style>
"单文件组件"的其他暴露方式
<script>
// "分别暴露",主要是给import引用使用 (暴露方式1) import {xxx} from './xxx'
export const school = Vue.extend({
data(){
return {
"schoolName": "上海大学",
"address":"shanghai",
}
},
methods:{
showName(){
alert(this.schoolName)
}
}
})
</script>
<script>
const school = Vue.extend({
data(){
return {
"schoolName": "上海大学",
"address":"shanghai",
}
},
methods:{
showName(){
alert(this.schoolName)
}
}
})
export {school} // "统一暴露"(代码就是写在底部),主要是给import引用使用(暴露方式2)import {xxx} from './xxx'
</script>
<script>
const school = Vue.extend({
data(){
return {
"schoolName": "上海大学",
"address":"shanghai",
}
},
methods:{
showName(){
alert(this.schoolName)
}
}
})
export default school // "默认暴露"(代码就是写在底部),主要是给import引用使用(暴露方式3,推荐) import xxx from './xxx'
</script>
X、函数调用表达式
<div id="root">
addr.toUpperCase() 字符串大写
addr.toLowerCase() 字符串小写
</div>
# if 判断
if(e.target.value !== 13) return
# 定时器,延时1000毫秒后执行
setTimeout(()=>{
console.log("xxx")
},1000)
# 三元表达式(如果a=3。返回100,否则返回200)
a === 3 ? 100 : 200
# 在数组的开头添加一个值
persons=[]
const p = {id:'004',name:'laoliu',age:49}
persons.unshift(p)
this.persons.push(p) #在数组的末尾添加一个值
#filter方法(过滤数组内某个值)
persons: [
{id: "001", name: "马冬梅", age: 18, sex: "女"},
{id: "002", name: "周冬雨", age: 28, sex: "女"},
{id: "003", name: "周杰伦", age: 38, sex: "男"},
{id: "004", name: "温兆伦", age: 48, sex: "男"},
],
this.persons.filter((p)=>{
// indexOf(value) == -1 说明没匹配到,indexOf(value)等于其他"0,1,2,xxx"表示匹配数据的索引值(也就是匹配到了)。
return p.name.indexOf(value) !== -1
# sort排序
let arr = [1,3,6,2,5,4]
arr.sort((a,b)=>{
return a-b
})
console.log(arr)
// return a-b:升序 [1, 2, 3, 4, 5, 6]
// return a-b:降序 [6, 5, 4, 3, 2, 1]