1.1.Vue.js安装
- 方式一: 直接CDN引入
- 方式二: 下载和引入
- 开发环境
- 生产环境
- 方式三: NPM安装
- 后续通过webpack和CL的使用,我们使用方式
Vue的基础语法
1.2.第一个Vue程序,体验Vue的响应式
创建Vue对象的时候,传入了一些options:{}
- {}中包含了el属性:该属性决定了这个Vue对象挂载到哪一个元素上
- {}中包含了data属性: 该属性中通常会存储一些数据
- 这些数据可以是直接定义出来,也可以是来自网络,从服务器加载
<body>
<div id="app">
Hello{{message}}
<h2>{{name}}</h2>
</div>
<div>{{message}}</div>
<script src="../js/vue.js"></script>
<script>
// es6不用var 有缺陷,没有变量作用域
// let(变量)/ const(常量)
// new Vue 在vue源码里已经定义了一个类,要使用Vue这个对象,只需要从vue源码拿到类似
// function Vue(){
//
// } 这样的一个方法,然后就可通过new Vue这个关键字创建Vue这个实例即可
// 构造函数的调用
// 编程范式(方式): 声明式编程
const app = new Vue({ // 传入的参数是对象类型
// 把id="app"的元素放到vue实例里的el里进行管理
el: '#app', // 用于挂载要用的元素
data:{// 定义一些数据
message: 'Vuejs',
name:'tiga'
}
})
</script>
</body>
1.3.Vue列表显示
- HTML中使用v-for指令
- 响应式
- 可追加元素
<body>
<div id="app">
<ul>
<!-- for循环:对movies进行遍历,取出的元素放到item变量里 -->
<li v-for= "item in movies">{{item}}</li> <!--这些东西都已交给Vue管理了,Vue发现了有v-for,会帮你对某些东西进行遍历,v-for之所以有意义,是因为Vue实例赋予了它意义-->
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app', // 挂载某一要管理的元素的属性
data: {
message: '你好啊',
movies: ['少年派','盗梦空间','星际穿越'] // 存多个值用数组
}
}) // new一个Vue对象(在源码内定义的),并且传入对象类型
app.movies.push('海王') // 往数组中追加元素
</script>
</body>
<body>
<div id="app">
<li v-for = "(item,index) in items">
{{item.message}}--{{index}}
</li>
</div>
<script src="../js/vue.js"></script>
<script>
var vm = new Vue({
el: '#app',
data: {
items: [
{message: 'tiga1'},
{message: 'tiga2'},
{message: 'taga3'},
]
}
});
</script>
</body>
- v-for绑定和非绑定key的区别
![](https://img.haomeiwen.com/i24940810/0085f20516518a03.png)
- HTML中使用 v-if,v-else-if,v-else指令
<div id="app">
<!-- 语法: v-if="布尔值",是否要显示出来 -->
<!-- v-if操作dom树,v-show相当于display -->
<h1 v-if="isShow ">
<div>abc</div>
<div>abc</div>
<div>abc</div>
{{message}}
</h1>
<!-- v-else的使用 -->
<h1 v-else>isShow为false时,显示我</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: '你好鸭',
isShow: true
}
});
</script>
<body>
<div id="app">
<h1 v-if = "type === 'a'">a</h1>
<h1 v-else-if = "type === 'b'">b</h1>
<h1 v-else-if = "type === 'c'">c</h1>
<h1 v-else>d</h1>
</div>
<script src="../js/vue.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
type: 'a'
}
});
</script>
</body>
<div id="app">
<h1 v-if="score >= 90">优秀</h1>
<h1 v-else-if="score >= 80">良好</h1>
<h1 v-else-if="score >= 60">及格</h1>
<h1 v-else>不及格</h1>
<h1>{{result}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: '你好鸭',
score: 80
},
// 指令判断非常复杂的话用计算属性
computed: {
result(){
let showMessage = '',
if (this.score >= 90) {
showMessage = '优秀'
}else if (this.score >= 80) {
showMessage = '良好'
}
return showMessage;
}
}
});
</script>
- 登录切换的小案例
<div id="app">
<span v-if="isUser">
<!-- for的作用是将表单和input的id绑定,使得表单也可以发生响应点击事件 -->
<!-- 在输入框输入值后切换类型没有清空,因为Vue底层的虚拟dom(类似于java虚拟机)进行了复用,就像同一个人换了不同的一件衣服,因为没有value虚拟dom渲染,所以保留了 -->
<!-- 加key保证节点的唯一性,key作为一个标识来决定该标签在其他地方能不能进行复用 -->
<!-- 如果新旧标签的key值相同,就认为可以进行复用,key不一样的时候就不会复用,到时就会在虚拟动漫创建新的标签 -->
<label for="username">用户账号</label>
<input type="text" id="username" placeholder="用户账号" key="username">
</span>
<span v-else>
<label for="email">用户邮箱</label>
<input type="text" id="email" placeholder="用户邮箱" key="email">
</span>
<button @click="isUser = !isUser">切换类型</button>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
isUser: true
}
});
</script>
- HTML中使用v-bind指令
<body>
</head>
<!-- view层 模板 -->
<div id="app"> <!-- 2.绑定一个元素 -->
{{message}} <!-- 4.从模板里取出来 -->
<span v-bind:title = "message">
鼠标悬停几秒钟查看此处动态绑定提示信息!
</span>
</div>
<!-- 1.导入Vue.js -->
<script src="../js/vue.js"></script>
<script>
var vm = new Vue({ // 2.new一个Vue对象
el: "#app", // 3.放数据
// model: 数据
data:{
message: "HelloWorld"
}
});
</script>
</body>
- v-bind的基本使用
<div id="app">
<!-- 错误的写法: 这里不可以使用mustache语法 -->
<!-- <img src="{{imgURL}}" alt=""> -->
<!-- 正确使用v-bind指令 -->
<!-- <img v-bind:src="imgURL" alt=""> -->
<a v-bind:href="aHref"></a>
<!-- v-bind的语法糖写法 -->
<img :src="imgURL" alt="">
<a :href="aHref"></a>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: '你好',
// 设定一个从服务器获取的地址
imgURL: 'https://ccc-x.jd.com/dsp/nc?ext=aHR0cHM6Ly9pdGVtLmpkLmNvbS8xMDAwMTU3ODY5NDYuaHRtbA&log=o46NYM6UXiyqFTAocnwRJb5M1_CylLaeOW6-iFvzaKiMTUIuHy3gU3FvYgMLPC76SsYGp4c5EmR7Y8FSv3K7u2R_FYdaYVM_x3LBUf3RI-lDYHPdQpH_YJe-uhJNvip52av_V2JQNBTcmT8v-WBYHfWlo4_V-jW7sv1J-VCbeo1rLBuH6k-SD3zzTA9xSD69jucRxkw6uca1M56sztZP3_edGa71zb-D9MbZejW54lqKfyJ8LubiXKj1TTAiLt8YxFHgENKPpYssnl-KNr0A6XdpGc-2693JcpD2a5eEFk8On5iiImDtfjhH-mbDXPOKvPPnCbuc4xKeND9aISKqKRwTv_LCZ_S8G3uhd7ckb76G9oyXaLOFy_ZsXDimojuqZZgKT5c-r4uLaqAVvIZAWcxl4Nf3Q5rvYpgKVxlcotfEjW2JwzK0quUoD7xEb9OT6kF3WoGQijX5oPoihPW2o6EgHi6hF_eVPx10_gIwXfmZYIQGtHJ7sQNjZnvtTRn8AQxB2qy9rfYt9HHhw3XlgipKlg7N2rX8zkOqEbYIphittmqZpydVCXM-YBfST117WKw6UhwHac_RkCflQWBu_HDkK7L88c86Xn7xObn9gMknxjkNPh41IrCQNKCNG5ALOGRUU_SnXXHDzDY2AHpWGefdjTuUmsYKsXGdw0d35UFseuH-FJO6TeZOnYBtzOhRaW7d9RGYdyWNDIT5XuBE5BEmj6iWS-GbZ4Pa2ZT_laToWyA00yABzbSneed0r4Dy69-uDkG2MUd1XEWX451WByNzCNyXFLb8pIHc1hkiLw8iEf8WTmUAff-sKqIOpubG60qgUGOCQM7iuCD3rV76RfRtlUt2nII12BGL1a_lmVzKLLqcqay7xG1YGkLmloCKWgiRLmZw_OKD4O5Ro_FBTqncF8WyOMlcV5YjGGhR7qOu8MD9oO6_NasoIOBCNAEsTNgg2JIH3TdbAH694YHkbSwjEwAIhSNdeTzCQ1FXW_PAF9V3DhX-tRfCyyZCuTr0&v=404',
aHref: 'http://www.baidu.com'
}
});
</script>
- v-bind动态绑定class(对象语法)
<div id="app">
<!-- class除了可以绑定字符串,还可以绑定对象;对象的格式: {key: value,...} -->
<!-- <h1 v-bind:class="key1(键): value1(值), key2: value2">{{message}}</h1> -->
<!-- <h1 v-bind:class="{类名1,boolean, 类名2,boolean}">{{message}}</h1> -->
<h1 class="title" v-bind:class="{active: isActive,line: isLine}">{{message}}</h1>
<button v-on:click="btnClick">按钮</button>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: '你好鸭',
isActive: true,
isLine: true
},
methods: {
btnClick: function (){
this.isActive = !isActive
}
}
});
</script>
- v-bind动态绑定class(数组语法)
<div id="app">
<!-- 数组语法: class="[]",方括号里加''内容代表字符串,不加''内容代表变量 -->
<h1 class="title" v-bind:class="[active,line]">{{message}}</h1>
<h1 class="title" v-bind:class="getClasses()">{{message}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: '你好鸭',
active: 'aaa',
line: 'bbb'
},
methods: {
getClasses: function (){
return [this.active, this.line]
}
}
});
</script>
- v-bind绑定style
![](https://img.haomeiwen.com/i24940810/14c52d0ea81ececc.png)
- 绑定方式一:对象语法
<div id="app">
<!-- 属性名命名方法: fontSize,font-size, 50px必须要加'',否则是当做变量去解析的 -->
<!-- :style="{key(属性名): value(属性值)}" -->
<!-- finalSize被当成一个变量使用 -->
<!-- <h1 v-bind:style="{fontSize: finalSize}">{{message}}</h1> -->
<!-- 把finalSize + 'px'当作表达式,进行连接,把基本数据类型和字符串类型连接会进行隐式转换成字符串然后连接,达到想要的效果 -->
<h1 v-bind:style="{fontSize: finalSize + 'px',color: finalColor}">{{message}}</h1>
<h1 v-bind:style="getStyles()">{{message}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: 'nihaoya',
// finalSize: '100px'
finalSize: 100,
finalColor: 'red',
},
methods:{
getStyles: function(){
return {fontSize: this.finalSize + 'px', backgroundColor: this.finalColor}
}
}
});
</script>
- 绑定方式二: 数组语法
<div id="app">
<h1 :style="[baseStyle,baseStyle1]">{{message}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: "nihao",
baseStyle: {fontSize: '100px'},
baseStyle1: {backgroundColor: 'red'},
}
});
</script>
- v-for和v-bind结合使用
<div id="app">
<ul>
<!-- v-for语法: v-for="循环变量 in 循环列表" 遍历{{循环变量}}-->
<!-- 把model层获取到的currentIndex和view层的每一个li的index来对比决定要不要加active,当发生点击的时候再把点击的index赋值给最新的currentIndex -->
<li
v-for="(item,index) in movies"
v-bind:class="{active: currentIndex === index}"
v-on:click="liClick(index)">
{{index}}{{item}}
</li>
<!-- <li v-bind:class="{active: 0 === currentIndex}"></li>
<li v-bind:class="{active: 1 === currentIndex}"></li>
<li v-bind:class="{active: 2 === currentIndex}"></li>
<li v-bind:class="{active: 3 === currentIndex}"></li> -->
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
movies: ["海王", "海尔兄弟", "火影", "进击的巨人"],
currentIndex: null, // 记录准备变成红色的状态
},
methods: {
liClick(index) {
this.currentIndex = index;
},
},
});
</script>
- Vue绑定事件
- v-on: 绑定点击事件指令
<body>
<div id="app">
<!-- v-on:绑定事件指令,使用v-on绑定了click事件,并指定了名为sayHi的方法 -->
<button v-on:click="sayHi">click me</button>
</div>
<script src="../js/vue.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
message: "vue.js"
},
methods: { // 方法必须定义在Vue实例的 methods 对象中
sayHi: function(event){
// `this`在方法里指向当前Vue实例
alert(this.message);
}
}
});
</script>
</body>
- v-on的参数传递问题
<div id="app">
<!-- 1.事件调用的方法不带参数 -->
<button @click="btn1Click">1</button>
<button @click="btn1Click()">1</button>
<!-- 2.在事件定义时,写方法时省略了小括号,但是方法本身是需要一个参数的,
Vue会默认将浏览器生成的event事件对象作为参数传入到方法-->
<!-- 当传入不带引号的纯数字,会自动解析成数字,不会报错 -->
<!-- <button @click="btn2Click(123)">2</button> -->
<!-- 这样写调用方法时不带形参,定义方法时也不带形参,但输出带形参时系统自动把event对象以参数形式传入到方法 -->
<button @click="btn2Click">3</button>
<!-- 3.方法定义时,当需要event对象,同时有需要其他参数时,
在调用方式,如何手动的获取到浏览器参数的event对象: $event
当传入不带引号的形参时,系统默认为传入的是一个变量,传入带引号的形参,系统认为传入的是字符串
当传入单个参数时,系统不会自动给第二个传入event对象,第二个参数会被定义为undefined
当传入两个参数时,可以通过$event拿到浏览器产生的event对象作为参数传到方法里 -->
<button @click="btn3Click('abc', $event)">4</button>
</div>
<script src="../js/vue.js">2</script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: '你好',
},
methods: {
btn1Click(){
console.log('btn1Click');
},
btn2Click(event){
console.log('----', event);
},
btn3Click(abc, event){
console.log("+++", abc, event);
}
},
});
// 如果函数有传参,但没有传入,那函数形参被认为是变量被定义为为undefined
/* function abc(name){
console.log(name);
}
abc(); */
</script>
- v-on的修饰符使用:
![](https://img.haomeiwen.com/i24940810/e70df301d050a6f1.png)
<div id="app">
<!-- (常用)1. .stop修饰符的使用: 阻止事件冒泡,只进行当前被修饰标签的onclick响应,完成响应后立即stop,不再进行其他响应 -->
<div @click="divClick">
aaaa
<button @click.stop="btn2Click">1</button>
</div>
<br>
<!-- (常用)2. .prevent修饰符的使用: 阻止默认事件,可用来阻止当表单填写错误的链接跳转 -->
<form action="baidu">
<!-- input会收集填写form表单的数据提交到服务器 -->
<input type="submit" value="提交" @click.prevent="submitClick">
</form>
<!-- (特殊情况使用)3. keyup:监听某个键盘按键的事件 -->
<!-- keyup.enter: 只监听回车键弹起的点击事件 -->
<input type="text" @keyup.enter="keyupClick">
<!-- 4. .native:监听组件的点击事件,必须要在click命令后加上.native才能监听,否则无法监听 -->
<!-- <cpn @click.native="cpnClick"> </cpn> -->
<!-- 5. .once修饰符的使用: 只触发一次回调 -->
<button @click.once="btn2Click">2</button>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: '你好鸭'
},
methods: {
divClick(){
console.log('divclick');
},
btn2Click(){
console.log('btnclick');
},
submitClick(){
console.log('submitClick');
},
keyupClick(){
// 输出键盘输入的值
console.log(event.target.value);
},
btn2Click(){
console.log('btn2Click');
}
},
});
</script>
案例: 计数器
- 新的属性: methods, 该属性用于在Vue对象中定义方法.
- 新的指令: v-on:click或@click(语法糖),该指令用于监听某个元素的点击事件,并且需要指定当发生点击时,执行的方法(方法通常是methods中定义的方法)
- 语法糖: 简写,旨在使内容更容易阅读,但不引入任何新内容的语法
<body>
<!-- view层 -->
<div id="app">
<h2>当前计数: {{counter}}</h2>
<!-- v-on:监听事件 -->
<!-- <button v-on:click="counter++">+</button>
<button v-on:click="counter--">-</button> -->
<button v-on:click="add">+</button>
<button v-on:click="sub">-</button>
</div>
<script src="../js/vue.js"></script>
<script>
// model层
// 对象里有做一层代理 proxy
const obj = {
counter: 0
}
// viewmodel层
const app = new Vue({
el: '#app', // 指定挂载的元素
data: { // 定义数据
// counter: 0
obj
},
methods: { // 定义一个方法,里面对应的也是对象,可在对象里定义多个函数
add: function(){// 定义一个方法,是函数类型
console.log('add被执行');
this.counter++; // this指向当前Vue实例,理解为:当前对象
},
sub: function(){
console.log('sub被执行');
this.counter--;
}
}
})
</script>
</body>
![](https://img.haomeiwen.com/i24940810/2434789a79606547.png)
![](https://img.haomeiwen.com/i24940810/59e82ce132f769df.png)
![](https://img.haomeiwen.com/i24940810/7397cdc8d4b446b7.png)
- Vue的options选项
开发中什么时候称之为方法,什么时候称之为函数? - 写在全局里的叫函数
例如function abc(){}
- 写在类里面的叫方法,因为方法都是和某个实例对象挂钩的
例如function Person(){ 方法...}
说白了,函数无主,方法有主
创建Vue实例传入的options
![](https://img.haomeiwen.com/i24940810/6c669c48a87984da.png)
- Vue的生命周期
生命周期: 事物从诞生到消亡的整个过程
Vue的生命周期:new Vue()
创建一个Vue的对象, 他的对象就诞生了,一旦创建出来,它里面就会做一系列事情
new Vue() 开始 Vue就开始了生命周期,在生命里做了一系列操作
![](https://img.haomeiwen.com/i24940810/b8c2797efa177111.png)
- Mustache语法:
![](https://img.haomeiwen.com/i24940810/fcd0f30c9efd52fd.png)
<div id="app">
<h2>{{message}}</h2>
<h2>{{message}},李银河</h2>
<!-- mustache语法中,不仅仅可以直接写变量,也可以写简单的表达式(获得一个结果的算式) -->
<h2>{{firstname + lastname}}</h2>
<h2>{{firstname + " "+ lastname}}</h2>
<h2>{{firstname}} {{lastname}}</h2>
<h2>{{counter * 2}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: "你好",
firstname: "kobe",
lastname: "bryant",
counter: 100
}
});
</script>
- v-once指令: 只希望标签内容只展示一次
![](https://img.haomeiwen.com/i24940810/391f7d4caf2852d5.png)
<div id="app">
<h2>{{message}}</h2>
<!-- v-once: 阻止页面响应式指令 -->
<h2 v-once>{{message}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: '你好'
}
});
</script>
- v-html指令: 将字符串按照html格式解析
![](https://img.haomeiwen.com/i24940810/8c79b27dbe19b53d.png)
<div id="app">
<!-- v-html指令: 将字符串按照html格式解析,跟innerHTML一样,有HTML标签的时候才用v-html -->
<h2 v-html="url"></h2>
</div>
<script src="../js/vue.js"></script>
<script>
// vue,默认展示纯字符串
const vm = new Vue({
el: '#app',
data: {
message: '你好',
// 从服务器返回数据,可以展示到界面上 有HTML标签
url: '<a href="http://www.baidu.com">百度一下</a>'
}
});
</script>
- v-text指令: 把文本直接展示出来
![](https://img.haomeiwen.com/i24940810/0f4bc56301c7e83e.png)
<div id="app">
<h1>{{message}},李银河</h1>
<!-- 如果想把文本直接展示出来的话用v-text指令,但一般不这样使用,因为不够灵活,v-text会覆盖文本 -->
<h1 v-text="message">,李银河</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: '你好鸭'
}
});
</script>
- v-pre指令: 把标签里的内容原封不动的显示出来,而不做任何解析
<div id="app">
<!-- 页面上可以直接展示出来的是因为: {{message}}是mustache语法,而下面的vm(Vue实例)会帮助我们解析这个语法,然后把message变量替换到这个位置 -->
<h1>{{message}}</h1>
<!-- v-pre指令: 把标签里的内容原封不动的显示出来,而不做任何解析 -->
<h1 v-pre>{{message}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: '你好啊'
}
});
</script>
- v-cloak指令:
<title>Document</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<!-- 浏览器解析html代码的时候是从上往下解析 -->
<!-- v-cloak相当于是个属性,一旦vue给html标签做了解析,他会自动把v-cloak删除掉 -->
<div id="app" v-cloak>
{{message}}
</div>
<script src="../js/vue.js"></script>
<script>
// 在vue解析前,div中有一个属性v-cloak
// 在vue解析后,div中没有一个属性v-cloak
setTimeout(function (){
const vm = new Vue({
el: '#app',
data: {
message: '你好鸭'
}
});
},1000)
</script>
- Vue双向绑定
<body>
<div id="app">
<!-- 通过v-model指令让表单里的控件的value值和data中的数据做双向绑定 -->
<label for="male">
<input type="radio" id="male" value="男" v-model="sex">男
</label>
<label for="female">
<input type="radio" id="female" value="女" v-model="sex">女
</label>
<p>选择的性别是:{{sex}}</p>
</div>
<div id="app2">
<span>下拉框:</span>
<select name="" id="" v-model="selected">
<option value="" disabled>--请选择--</option> <!-- 此项不能被选 -->
<option>a</option>
<option>b</option>
<option>c</option>
<option>d</option>
</select>
</div>
<script src="./js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: '你好鸭',
sex: '男'
}
});
const vm2 = new Vue({
el: '#app2',
data: {
message: '',
selected: ''
}
});
</script>
</body>
- 第一个Vue组件(自定义标签)
什么是组件
组件是可复用的Vue实例,说白了就是一组可以重复使用的模板,跟JSTL的自定义标签,他不是像其他的组件能用,可理解为他就是一个标签,Thymeleaf的th:fragment
等框架有着异曲同工之妙.通常一个应用会议一棵嵌套的组件树的形式来组织:
![](https://img.haomeiwen.com/i24940810/8eb2f29598b1dd79.png)
例如,你可能有页头,侧边栏,内容区等组件,每个组件又包含了其他的像导航栏链接,博文之类的组件
<body>
<div id="app">
<!-- qin作为形参,item(变量名称自拟)作为实参,v-bind将实参与形参绑定 -->
<tiga v-for="item in items" v-bind:qin="item"></tiga>
</div>
<script src="../js/vue.js"></script>
<script>
// 定义一个Vue组件component
Vue.component('tiga',{ // 组件名,(自定义标签名)
props: ['qin'], // props是来定义一个形参,用来接收items中的变量,template用来取形参中的变量值
template: '<li>{{qin}}</li>' // 组件模板,相当于自定义标签,qin相当于形参,作用是(接收 )传递数据
});
const vm = new Vue({
el: '#app',
data: {
items: ['Java','Linux','front-end']
}
});
</script>
</body>
computed: 计算属性的使用
- 什么是计算属性?
在模板中可以直接通过差值语法对数据进行转化后显示或者需要将多个数据结合起来显示
<div id="app">
<!-- 三种展示方式 -->
<h1>{{firstName + ' ' + lastName}}</h1>
<h1>{{firstName}} {{lastName}}</h1>
<h1>{{getFullName()}}</h1>
<h1>{{fullName}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
firstName: 'Lebrown',
lastName: 'James'
},
// computed: 计算属性(),名词形式,本质就是一个属性,
computed: {
// 把计算属性当成属性来用就行,调用不需要加()
fullName: function(){
return this.firstName + ' ' + this.lastName
}
},
methods: {
getFullName (){
return this.firstName + ' ' + this.lastName
}
}
});
</script>
- 计算属性的复杂操作
<div id="app">总价为: {{totalPrice}}</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
books: [
{id: 101, name: '语文', price: 151},
{id: 102, name: '数学', price: 51},
{id: 103, name: '英语', price: 11},
{id: 104, name: '物理', price: 15},
]
},
// 计算属性和methods的区别: 计算属性会进行缓存在多次调用时候只会调用一次,methods调用几次就会在这执行几次,没有缓存性能更低
// 在 ES6里用let比var好,因为let有块级作用域
// 计算属性中的应用场景: 通过计算属性将价格汇总再放到view层展示
//
computed: {
totalPrice: function(){
// ES5语法
let result = 0
for(let i = 0; i < this.books.length; i++){
result += this.books[i].price
}
return result
// ES6语法
for(let i in books){
this.books[i]
}
for(let book of books){
this.book
}
}
}
});
</script>
- 计算属性的setter和getter
<div id="app">{{fullName}}</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
firstName: 'Kobe',
lastName: 'Bryant'
},
// 计算属性可以定义函数属性,也可以定义字符串属性
computed: {
// 计算属性完整是有set和get方法但一般不写set方法,即为只读属性
fullName: {
set: function(newValue){
// 一般情况下不需要写set方法,不希望别人随便给计算属性设置值,
console.log('--------', newValue);
const names = newValue.split(' '); // 用split函数将字符串以空格为界限拆分成子字符串数组
this.firstName = names[0]; // 将第一个子字符串数组赋给firstName
this.lastName = names[1];
},
get: function(){
return this.firstName + ' ' + this.lastName
}
},
// 更简便写法: 其实是这个计算属性只有get方法
// 所以在使用这个属性的时候不需要加(),因为使用的时候就是单独在使用这个属性,当使用的时候就是在调get,所以直接使用属性就可以了
// fullName: function(){
// return this.firstName + ' ' + this.lastName
// }
}
});
</script>
- 计算属性和methods的对比
Vue内部对计算属性做了一层缓存,会观察计算属性中的属性有无发生变化,如果每次调用,属性都没发生变化的话,就直接把原来的结果返回出去,而不是重新计算.如果是methods,每调用一次函数就会重新计算,所以computed性能相对较高
类似把数据做一层变化展示到界面上,一般使用计算属性,只在执行某些特殊东西的时候才用methods
- 块级作用域-let和var
当变量需要改变时用let
当标志符不需要改变时(常量)用const
let有块级作用域,而var没有
<button>1</button>
<button>2</button>
<button>3</button>
<script>
// ES5之前因为if和for都没有块级作用域的概念,所以在很多时候,必须借助于(闭包)function的作用域来解决外面变量的问题
// ES6中,加入let,他是有if和for的块级作用域的
// var 全局变量 let 局部变量 const 常量
// 1.变量作用域: 变量在什么范围内是可用的
/* {
var name = "why";
var func;
console.log(name);
} */
// 2.没有块级作用域引起的问题: if的块级 别处可以随意修改变量
/* var func;
if (true) {
var abc = "why";
func = function () {
console.log(abc);
};
// func();
}
abc = "kobe";
func(); */ // 在函数用之前,变量abc已经被改成Kobe了
// console.log(name);
// 3.没有块级作用域引起的问题: for的块级
// var btns = document.getElementsByTagName('button');
// for(var i = 0; i < btns.length; i++){
// addEventListener是Google的 DOM2事件,相当于onclick
// btns[i].addEventListener('click', function(){
// 先遍历才触发点击事件,先执行同步任务,才执行异步任务
// 其实在点击之前,又进行了好几次循环,输出的i已经被for循环改掉了
// console.log("第" + i + "个按钮被点击");
//})
// 为什么闭包可以解决问题: 函数是一个作用域
// 函数有自己的作用,所以函数的形参不受外部for循环的i影响
// 在js中 if,for块级都没有作用域的概念,函数有
// 因为for属于立即执行函数,立即执行就已经把各个i传入函数内部,而函数又是有作用域的,所以会先从自身找形参,找不到才到父级
// 2.情况二: ES5中使用闭包
// 匿名闭包本质上就是函数
// (function(i){ // 这里相当于声明闭包的匿名函数
// btns[i].addEventListener('click',function(){
// console.log("第" + (i + 1) + "个按钮");
// })
// })(i) // 这个括号相当于执行匿名函数
// }
// ES5中 for循环没有作用域,往后遍历的i会覆盖点击事件function前面遍历的i
// ES6中的 let
const btns = document.getElementsByTagName('button');
for(let i = 0; i < btns.length; i++){
btns[i].addEventListener('click',function(){
console.log("第" + i + "个按钮");
})
}
</script>
- 块级作用域 -- const
const 用来声明常量值,无法被改变,改变就报错,也具备块级作用域
建议: 在ES6开发中,优先使用const,只有要修改某个标识符的是有才用let,这样更加规范
<script>
// 注意一:const修饰常量,永远只能赋值一次,并且不能修改,永恒的
// 内存中,const常量指向的字符串相当于也是对象,const声明的常量存放的是地址,通过地址找到对象
const name = "why";
// name = "ff";
// 注意二: 使用const定义标识符,必须进行赋值
// const name; 报错
// 注意三: 常量的含义是指向的对象(存放的地址)不能修改,但可以改变对象内部的属性
const obj = {
name: 'why',
age: 18,
}
console.log(obj);
obj.name = 'tiga';
obj.age = 20;
console.log(obj);
</script>
- ES6对象字面量的增强写法
<script>
// const obj = new Object(); // 创建对象,开发中一般不这么写
// 大括号叫对象的字面量
/* const obj = {
name: 'why',
age: 18,
// 对象里放函数
run: function(){
console.log('在奔跑');
}
} */
//1.属性的增强写法(简写)
const name = "taga";
const age = 18;
const height = 1.88;
// ES5写法
/* const obj1 = {
name: name,
age: age,
height: height
}
console.log(obj1); */
// ES6写法: 键值对key和value值相同时可简写
const obj2 = {
name,
age,
height,
};
console.log(obj2);
// 2.函数的简写
const obj = {
run: function () {},
eat: function () {},
};
</script>
- 响应式的数组方法
<div id="app">
<ul>
<!-- 需要有插入操作的时候,增加key属性 -->
<!-- 有key内存里实际就是链表,所以插入操作效率高了 -->
<!-- key和数组元素对应就能确定唯一性 -->
<!-- 以后在用v-for时候,尽量绑定一个key,但绑定key的时候一定要保证他是唯一性的,如果不是唯一性则没有太大意义,这样就能给Vue原理的内部达到性能的利用 -->
<!-- Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染.这时可以用key来管理可复用的元素 -->
<li v-for="item in letters" :key="item">{{item}}</li>
</ul>
<button @click="btnClick">按钮</button>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
letters: ['A', 'B', 'C', 'D', 'E']
},
methods: {
btnClick(){
// 必须改变data里的数据本身,只改变数据的属性值是不会响应的
// 1.push方法: 数组最后添加元素
// this.letters.push('aaa')
// 通过索引值修改数组中的元素,不能做到响应式.vue内部没有通过改变数值进行监听
// this.letters[0] = 'bbbb';
// 可以通过splice方法插入
// this.letters.splice(0,1,'bbbbb');
// 或者通过Vue方法修改
// set(有修改的对象,索引值,修改后的值)
Vue.set(this.letters,0,'bbbbbbbb')
// 2.从数组最后一个元素弹出栈
// this.letters.pop();
// 3.删除数组中第一个元素
// this.letters.shift();
// 4.数组最前面添加元素.可添加多个值
//this.letters.unshift('aa','bbb','cc');
// 5.splice:删除元素/console/替换元素
// 格式: splice(第一个从哪个开始,删几个,插入元素)
// 删除元素: 第二个参数传入你要删除的几个元素(如果没有传,就删除后面所有元素)
// this.letters.splice(2);
// 替换元素: 第二个参数,表示要替换几个元素,后面是用于替换前面的元素
// this.letters.splice(1,3,'m','n','x');
// 插入元素: 第二个参数,传入0,并且后面跟上要插入的元素
// this.letters.splice(1,0,'m','n','x');
// 6.sort(),元素排序
// this.letters.sort();
// 7.reverse(): 元素反转
// this.letters.reverse();
}
}
});
// 可变参数/rest参数
/* function sum(...num){
console.log(num);
return num += num;
}
sum(10,2,6,3,8,3) */
</script>
- 购物车案例
<div id="app">
<table>
<thead>
<tr>
<td></td>
<td>书籍名称</td>
<td>出版日期</td>
<td>价格</td>
<td>购买数量</td>
<td>操作</td>
</tr> <!-- 一行 -->
</thead>
<tbody>
<!-- item是数组books中的一个一个对象,如果输出{{item}}相当于直接把对象当做字符串放在标签里,他会调用对象的toString方法,最终显示出字符串 -->
<!-- 我们不是想显示字符串,而是想显示对象中的其他东西 -->
<tr v-for="(item,index) in books">
<!-- 可以再嵌套个v-for,因为每个对象还有多个属性,而属性是一个一个排布的,对象也可以遍历 -->
<!-- 但是如果要对数据具体操作,建议不要遍历对象 -->
<!-- <td v-for="value in item">{{value}}</td> -->
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.date}}</td>
<!-- 直接调用声明好的方法 -->
<!-- <td>{{getFinalPrice(item.price)}}</td> toFixed():js中给数字保留小数 -->
<!-- 可以通过过滤器对当前的价钱进行过滤 -->
<!-- 过滤器格式: 过滤属性 | 过滤器 -->
<!-- 用过滤器做价格显示比较合适 -->
<td>{{item.price | showPrice}}</td>
<td>
<!-- 添加点击监听事件 -->
<!-- v-bind是绑定方法或对象的,加上v-bind就可以直接用Vue实例的是属性和方法 -->
<button @click="decrement(index)" v-bind:disabled="item.count <= 1">-</button>
{{item.count}}
<button @click="increment(index)">+</button>
</td>
<td><button @click="removeHandle(index)">移除</button></td>
</tr>
</tbody> <!-- tbody里的数据不写死 -->
</table>
<div v-if="books.length">
<h1>总价格: {{totalPrice | showPrice}}</h1>
</div>
<h1 v-else>购物车为空</h1>
</div>
<script src="../js/vue.js"></script>
<script src="./main.js"></script>
table{
border: 1px solid #000;
border-collapse: collapse;
border-spacing: 0;
}
th,td{
padding: 8px,16px;
border: 1px solid #000;
text-align: left;
}
th{
background-color: gray;
color: #5c6b77;
font-weight: 600;
}
const vm = new Vue({
el: '#app',
data: {
books: [// 在数组里多追加一个对象就可以了,保存数据一定是放在这里
{
id: 1,
name: '<<算法导论>>',
date: '2006-9',
price: 85.00,
count: 1
},
{
id: 2,
name: '<<Unix编程艺术>>',
date: '2006-2',
price: 59.00,
count: 1
},
{
id: 3,
name: '<<编程珠玑>>',
date: '2008-9',
price: 39.00,
count: 1
},
{
id: 4,
name: '<<代码大全>>',
date: '2001-9',
price: 128.00,
count: 1
}
],
},
methods: {
/* getFinalPrice(price){
// 因为传入的是每行item对象的price,所以下面可以不用写item.
return " ¥ " + price.toFixed(2);
} */
// 为了能区分改哪个count,必须取哪个index的对象,通过index可以取那本书改他的count
increment(index){
// 因为这里修改的是对象里的属性,所以这里是响应式的
this.books[index].count++;
},
decrement(index){
this.books[index].count--;
},
removeHandle(index){
this.books.splice(index,1);
}
},
// 计算属性,直接起名词形式,计算属性本质上就是属性,虽然看起来像函数
computed: {
totalPrice(){
// 三种方式遍历数组获得总价格
/* // 1.普通for循环
let totalPrice = 0;
for(let i = 0; i < this.books.length; i++){
totalPrice += this.books[i].price * this.books[i].count;
}
return totalPrice; */
// 2.for(let i in this.books) // for(let 索引值 in 数组)
// toFixed方法不能处理undefined这个数据类型,只能处理数组类型
// 如果没有return 浏览器会报错,因为当前计算属性没有返回值,相当于返回undefined,用这个属性相当于用undefined,因此不能用toFixed
/* let totalPrice = 0;
for(let i in this.books){
totalPrice += this.books[i].price * this.books[i].count;
}
return totalPrice; */
// 3.for(let i of this.books) // 可以直接拿到数组中的对象
let totalPrice = 0;
for(let item of this.books){
totalPrice = item.price * item.count;
}
return totalPrice;
// 用reduce方法:
/* return this.books.reduce(function(preValue, book){ // book参数相当for循环的item
return preValue + book.price * book.count;
},0) // 初始值必须加0,如果不加 第一个初始值默认为books对象 */
}
},
filters: {
// 过滤器一般是一个函数
// 会自动将前面过滤的item.price作为参数传入形参中,所以函数是有参数的
showPrice(price){
return "¥" + price.toFixed(2);
}
}
});
- 高阶函数的使用
// 编程范式: 命令式编程/声明式编程
// 编程范式: 面向对象(第一公民: 对象)/函数式(第一公民:函数)编程
// fiiter(过滤器)/map(映射)/reduce 高阶函数
// filter中的回调函数有一个要求: 必须返回一个boolean值
// 当返回true时,回调函数内部会自动将这次回调的n加入到内部新创建的数组中,而这个返回值会作为大的filter()函数的返回值,这里只需要用新变量做接收就行了
// 当返回false时,函数内部会过滤掉这次的n,不再使用他
const nums = [10,20,111,222,444,40,50]
// 对数组里的数据进行过滤,高阶函数: 函数本身需要的参数也是函数
// 这里会遍历7次
// 1.filter函数的使用: 整体遍历后过滤
// 10,20,40,50
let newNums = nums.filter(function(n){ // 形参传的是回调函数,会在每一次遍历出来一个数字时候就会执行一次回调函数,并且会把每一次数组遍历的数字传入回调函数中
return n < 100
})
// console.log(newNums);
// 2.map函数的使用,如果想对数组所有的元素进行某一次的变化的话,map会将数组中的所有元素整体遍历后进行操作
let new2Nums = newNums.map(function(n){
return n * 2
})
console.log(new2Nums)
// 3.reduce函数的使用:
// reduce作用对数组中所有的内容进行遍历然后进行汇总
// reduce(回调函数(前一个返回值,初始值),初始值)
let total = new2Nums.reduce(function(preValue,n){
return preValue + n;
},0)
console.log(total);
// 对上面三种函数进行综合
let total1 = nums.filter(function(n){
return n < 100;
}).map(function(n){
return n * 2;
}).reduce(function(preValue,n){
return preValue + n;
},0);
console.log(total1);
// 箭头函数写法:
let total2 = nums.filter(n => n < 100).map(n => n * 2).reduce((pre,n) => pre + n);
console.log(total2);
- v-model的原理:
![](https://img.haomeiwen.com/i24940810/c6f3871af865a1d4.png)
- v-model结合radio类型
<div id="app">
<label for="male">
<!-- 没有v-model的时候加name值才能作为互斥选项,因为在往服务器提交数据的时候,他是以name作为key值提交到服务器的,当name一样的时候,他肯定只能提交一个,所以他们两个是互斥的 -->
<!-- v-model可以把radio选择的value值绑定到sex变量里 -->
<!-- 一旦v-model绑定了同一个sex变量,name属性可以没有,这时候也可以让他们互斥 -->
<!-- 可以理解为v-model将绑定变量名作为控件的name属性值来进行分组 -->
<input type="radio" id="male" value="男" v-model="sex">男
</label>
<label for="female">
<input type="radio" id="female" value="女" v-model="sex">女
</label>
<h1>选择的性别是: {{sex}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: '你好鸭',
sex: "男" // 可加默认值,上面的value值就默认被选中
}
});
</script>
- v-model结合checkbox类型使用
<div id="app">
<!-- label标签的好处是点击文字也能选中输入框 -->
<!-- checkbox单选框 -->
<!-- 绑定布尔类型 -->
<!-- <label for="agree">
<input type="checkbox" id="agree" v-model="isAgree">同意协议
</label>
<h1>你选择的是 {{isAgree}}</h1>
<button :disabled="!isAgree">下一步</button> -->
<!-- 2.checkbox多选框 -->
<!-- 绑定数组类型 -->
<input type="checkbox" value="篮球" v-model="hobbies">篮球
<input type="checkbox" value="足球" v-model="hobbies">足球
<input type="checkbox" value="乒乓球" v-model="hobbies">乒乓球
<input type="checkbox" value="羽毛球" v-model="hobbies">羽毛球
<h1>你选择是: {{hobbies}}</h1>
<!-- 值绑定 -->
<!-- label的for属性是结合input的id属性使用的,当点击label的时候,会自动聚焦的input的输入框中 -->
<!-- 也可以不写:for和:id,因为每次循环,一个label对应一个input -->
<!-- for就是指要和哪一个input对应起来,for和id也是对应的 -->
<label v-for="item in originHobbies" :for="item">
<!-- v-model绑定hobbies判断是否选中 -->
<input type="checkbox" :value="item" :id="item" v-model="hobbies">{{item}}
</label>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: 'nihaoya', // 会自动将message信息绑定到input里面的v-model作为value
isAgree: false,
hobbies: [], // 定义一个便来你用来收集用户爱好
originHobbies: ['篮球','足球','乒乓球','羽毛球','台球','高尔夫球']
}
});
</script>
- v-model结合select类型使用
<div id="app">
<!-- 1.选择一个 -->
<!-- 向服务器提交的key就是name属性 -->
<!-- 把option对应的value的值绑定到fruit里 -->
<!-- 多个下拉框,id不能重复 -->
<select name="abc" v-model="fruit">
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="葡萄">葡萄</option>
<option value="榴莲">榴莲</option>
</select>
<h1>你选择的是: {{fruit}}</h1> <!-- 字符串类型 -->
<!-- 2.选择多个 -->
<!-- multiple: 按住Ctrl键可以多选 -->
<!-- 把选中的多个数据保存到数组中,最终可以把数组传给服务器,让服务器保存 -->
<select name="" v-model="fruits" multiple>
<option value="苹果">苹果</option>
<option value="香蕉">香蕉</option>
<option value="葡萄">葡萄</option>
<option value="榴莲">榴莲</option>
</select>
<h1>你选择的是: {{fruits}}</h1> <!-- 数组类型 -->
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: 'nihaoya', // 会自动将message信息绑定到input里面的v-model作为value
fruit: '香蕉',
fruits: []
}
});
</script>
- input中的值绑定
<!-- 值绑定 -->
<!-- label的for属性是结合input的id属性使用的,当点击label的时候,会自动聚焦的input的输入框中 -->
<!-- 也可以不写:for和:id,因为每次循环,一个label对应一个input -->
<!-- for就是指要和哪一个input对应起来,for和id也是对应的 -->
<label v-for="item in originHobbies" :for="item">
<!-- v-model绑定hobbies判断是否选中 -->
<input type="checkbox" :value="item" :id="item" v-model="hobbies">{{item}}
</label>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: 'nihaoya', // 会自动将message信息绑定到input里面的v-model作为value
isAgree: false,
hobbies: [], // 定义一个数组用来收集用户爱好
originHobbies: ['篮球','足球','乒乓球','羽毛球','台球','高尔夫球']
}
});
</script>
- v-model修饰符的使用
<div id="app">
<!-- 1.修饰符:lazy: 懒加载,输入框失去光标或回车的时候后才绑定数据 -->
<input type="text" v-model.lazy="message">
<h1>{{message}}</h1>
<!-- 2.修饰符: number: 将字符串型转换成数字型 -->
<!-- 输入框必须为数字类型才能输出,一般用于输手机号 -->
<!-- 默认情况系v-model给变量赋的值被认为是String类型 -->
<input type="number" v-model.number="age">
<!-- typeof: 输出类型 -->
<h1>{{age}}-{{typeof age}}</h1>
<!-- 3.修饰符: trim: 删除首尾的空格 -->
<input type="text" v-model.trim ="name">
<h1>你的名字是: {{name}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
message: 'nihaoya', // 会自动将message信息绑定到input里面的v-model作为value
age: 0,
name: '',
}
});
</script>
组件化
-
组件化的基本使用过程
-
全局组件和局部组件
-
父组件和子组件的区分
-
注册组件的语法糖写法
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
// 说白了,其实就是把之前的组件构造器换成对象,无论是注册全局组件还是局部组件。
// 1.全局组件注册的语法糖
// 1.创建组件构造器
// const cpn1 = new Vue.extend()
// 2.注册组件(全局)
// components本质上内部还是调了extend函数
Vue.component('cpn1',{
template: `
<div>
<h2>我是标题1</h2>
<p>我是内容,哈哈哈哈哈哈哈哈哈</p>
</div>
`
})
const vm = new Vue({
el: '#app',
data: {
message: 'nihaoya',
},
// 注册局部组件的语法糖
components: {
'cpn2': {
template: `
<div>
<h2>我是标题2</h2>
<p>我是内容,嘿嘿嘿</p>
</div>
`
}
}
});
</script>
![](https://img.haomeiwen.com/i24940810/aedfe9c9b93cf2a8.png)
网友评论