2.4 计算属性
从字符串反转中,我们发现
-
插值语法的初衷是用于简单运算。明显练习题中的写法,违背了插值语法的初衷。
-
methods方法可以。但是方法中如果封装的是性能开销比较大的逻辑代码,需要进行大量的运算,并且别的属性还依赖这个方法,那不可避免的是,这个方法可能还要执行很多次。 这种情况,对CPU的性能开销可想而知,有多巨大了。
而Vue引入了计算属性 来解决这个问题,它提供了缓存机制。如果重复调用计算属性,就使用缓存数据,提高性能。
完整语法格式:
computed:{
属性名:{
get(){
//提供get方法,1. 初始加载时调用 2.所依赖的值被改变时
}
set(value){
//提供set方法,1. 当对应的计算属性fullName的值主动修改时
}
}
}
简化语法格式: 只有确定 属性为只读时,才能使用简化写法。
computed:{
属性名(){
//....其实是get方法逻辑
}
}
<!--
计算属性:
-1. 定义: 要用的属性不存在,要通过已有属性计算得来
-2. 原理: 底层借助了Object.difineProperty方法提供的getter和setter
-3. get函数什么时候执行
(1) 初次读取时会执行一次
(2) 当依赖的数据发生变化时会再次被调用
-4. 优势: 与methods实现相比,内部有缓存机制(重复使用),效率更高,调试方便
-5. 备注:
(1) 计算属性最终会出现在vm上,直接读取即可。
(2) 如果计算属性要被修改,那必须使用set函数,且set函数中要对计算时所依赖的属性进行修改
-6. 与方法的比较
(1). 计算属性,在调用时不加(),原因:调用的是属性, 重复调用,走缓存
(2). methods方式,在调用时,必须添加(), 原因:调用的是函数,没有缓存机制
-->
2.5 侦听属性
2.5.1 介绍
侦听(监听) 就是对 内置对象的状态或者属性进行监听,如果发生了变化,就做出一些相应的处理操作。
在 Vue.js 中,监视属性是用于观察 Vue 实例中的数据变动的一种机制,当需要在数据变化时执行异步或开销较大的操作时,适合使用监听属性watch
。
- **语法1:**在Vue中添加watch属性
// 完整写法
watch: {
`被监听的属性名`:{
immediate: true, //Vue初始化时,就调用一次回调函数handler
deep:true, //开启深度监听
handler(newValue,oldValue){ //回调函数
//第一个参数,是被监听属性的新值
//第二个参数,是被监听属性的旧值
}
}
}
//简化写法
watch: {
被监听属性名(newValue,oldValue){
//第一个参数,是被监听属性的新值
//第二个参数,是被监听属性的旧值
}
}
}
- 语法2: 使用vm实例绑定watch方法
vm.$watch("被监听属性名", {
immediate: true,
deep: true,
handler(newValue, oldValue) {
//....
}
})
//简化写法
vm.$watch("被监听属性名",function(newValue,oldValue){
//...
})
-
当被监视的属性变化时,回调函数自动调用
-
监视的属性必须存在,才能被监视
2.5.2 案例演示
<div id="demo">
<input type="text" v-model="weather" placeholder="请输入天气"> <br>
<input type="text" v-model="hobbies.hobby1" placeholder="请输入爱好1"> <br>
<input type="text" v-model="hobbies.hobby2" placeholder="请输入爱好2"> <br>
</div>
2.5.3 计算属性与监视属性的区别
计算属性是依赖的值改变后重新计算结果更新DOM,会进行缓存。
属性监听的是属性值,当定义的值发生变化时,执行相对应的函数。
最主要的用途区别: 计算属性不能执行异步任务。计算属性一般不会用来向服务器请求或者执行异步任务,因为耗时可能会比较长,我们的计算属性要实时更新。所以这个异步任务就可以用监听属性来做。
总而言之:computed能实现的,watch都能实现,computed不能实现的,watch也能实现
2.6 列表练习题(计算属性和监听属性)
{id:1001,name:'马冬梅',age:30,gender:'男'},
{id:1002,name:'周冬雨',age:28,gender:'男'},
{id:1003,name:'周杰伦',age:32,gender:'女'},
{id:1004,name:'温兆伦',age:22,gender:'女'}
1)列表过滤
2)列表排序
2.7生命周期
2.8 练习题
2.7.1 样式属性练习
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<!-- 第一步: 引入vue文件 -->
<script type="text/javascript" src="../js/vue.js"></script>
<style>
.basic{
border: 1px red solid;
width: 400px;
height: 100px;
}
/* 下面的三个样式,使用其中一个 */
.r1{
border: 10px blue dotted;
background-color: gray;
}
.r2{
border: 10px orange dashed;
background-color: green;
}
.r3{
border: 10px red double;
background-color:burlywood;
}
/* 下面的三种样式,可能会使用其中1个,或者2个,或者三个,或者一个都不用 */
.c1{
font-size: 30px;
font-style: italic;
}
.c2{
border-radius: 20px;
}
.c3{
background: linear-gradient(to right, green,rgb(0, 225, 255));
}
</style>
</head>
<body>
<!-- 绑定样式:
- class样式
:class="xxx" xxx可以是字符串,对象,数组
-字符串写法适用于:类名不确定,要动态获取
-对象写法适用于:要绑定多个样式,个数确定,名字确定,但是不确定用不用
-数组写法适用于:要绑定多个样式,个数不确定,名字也不确定
- style样式
:style="{fontSize:xxx}" 其中xxx是动态值
:style="[a,b]" 其中a、b是样式对象
-->
<!-- 第二步: 准备容器 -->
<div id="demo">
<!-- 绑定class样式--字符串写法 ,适用于样式的类名不确定,需要动态指定 --> -->
<div class="basic" :class="classStyle">
欢迎来到{{name}}学习 <br><br>
<button @click="change1">点我改变样式</button>
</div> <br><br>
<!-- 绑定class样式:对象写法,适用于要绑定的样式个数确定,名字确定,但要动态决定用不用 -->
<div class="basic" :class="classObject">
欢迎来到{{name}}学习 <br><br>
</div> <br><br>
<!-- 绑定class样式:数组写法,适用于要绑定的样式个数不确定,名字也不确定 -->
<div class="basic" :class="classArr">
欢迎来到{{name}}学习 <br><br>
</div> <br><br>
<!-- 绑定style样式: 对象写法 -->
<div class="basic" :style="styleObject">
欢迎来到{{name}}学习 <br><br>
</div> <br><br>
<!-- 绑定style样式: 数组写法 -->
<div class="basic" :style="styleArr">
欢迎来到{{name}}学习 <br><br>
</div> <br><br>
</div>
</body>
<script>
// 第三步: 创建Vue实例
new Vue({
// 第四步: 设置挂载点, 以及配置数据
el:"#demo",
data:{
name:"水利电力学院",
classStyle:"",
// index:0,
classObject:{
c1:false,
c2:true,
c3:true
},
classArr:['c1','c2','c3'],
styleObject:{
fontSize:'40px',
backgroundColor:"red"
},
styleArr:[
{fontSize:'40px'},
{backgroundColor:"blue"}
]
},
methods: {
change1(){
var arr = ["r1","r2","r3"]
var index = Math.floor(Math.random()*3)
this.classStyle = arr[index]
}
},
})
</script>
</html>
2.9 自定义指令
<!-- 自定义指令定义语法:
- 局部指令:
写法1:
new Vue({
directives:{指令名:配置对象}
})
写法2:
new Vue({
directives:{指令名:回调函数}
})
2.全局指令:
Vue.directive(指令名,配置对象)
Vue.directive(指令名,回调函数)
- 配置对象中常用的3个回调函数:
bind(element,binding):指令与元素成功绑定时调用
inserted(element,binding):指令所在元素被插入页面时调用
update(element,binding):指令所在模板结构被重新解析时调用
- 备注:
指令定义时不加"v-",但使用时要加"v-"
指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名
new Vue({
//...
directives:{
'my-text'(element,binding){
element.innerText = binding.value * 10
}
}
//...
})
-->
三 组件化开发
3.1 传统方式和组件方式编写应用比较
组件:就是实现应用中局部功能代码和资源的整合
组件化:一个页面可以拆分成一个个组件,每个组件有着自己独立的结构、样式、行为。 以上图为例。
组件化的好处:便于维护,利于复用,从而提升开发效率。
组件分类:普通组件、根组件。
比如:下面这个页面,可以把所有的代码都写在一个页面中,但是这样显得代码比较混乱,难易维护。咱们可以按模块进行组件划分
3.2 非单文件组件的开发
非单文件组件开发,指的是在一个html文件中,包含n个组件。
3.2.1 组件开发的步骤
**第一步:**定义组件(创建组件)
//定义组件。 与new Vue({...}) 语法类似,但是不能配置el选项。 所有组件都由一个具体的VM实例来管理
const 组件变量 = Vue.extend({
name:'组件名称', //VueDevtools调试面板中显示的组件名
template: `组件模版内容`,
data() { // 必须写成data函数且有返回值,返回js对象格式 ....原因是避免组件被复用时,数据存在引用关系。
return {//....}
},
methods: {
},
//......
})
//简写方式
const 组件变量 = {...} //省略Vue.extend,底层默认调用它。
**第二步:**注册组件
1. 局部注册:new Vue的时候配置components选项
//创建Vue
new Vue({
//...
components: {
//配置各个组件
//组件名: 组件变量
}
})
2. 全局注册:
Vue.component('组件名',组件) //在定时Vue实例来之前注册
组件名的定义:
1. 一个单词时:
- 首字母大小写,都可以. 但是开发者工具中显示的都是大写的。 比如 School
2. 多个单词组成时:
- 可以使用kebab-case命名法: my-shool
- 可以使用CamelCase命名法:MySchool(脚手架里支持这种写法)
- 注意: 无论哪种写法,开发者工具中显示的都是大写的。 比如 MySchool
3. 组件里的name选项,是用来配置开发者工具中显示的名字的
4. 组件名不要使用html,css,js中的关键字
**第三步:**编写组件标签:
在html文件的相应位置,引入组件名
1. 双标记写法:<组件名></组件名>
2. 单标记写法:<组件名/> 该写法会导致后续组件不能被渲染, 在脚手架里使用,是没有问题的
3.2.2 简单案例演示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>非单文件组件的基本应用</title>
<script type="text/javascript" src="./myjs/vue.js"></script>
</head>
<body>
<div id="app">
<h1>学校信息</h1>
<xuexiao></xuexiao>
<h1>学生信息</h1>
<xuesheng></xuesheng>
</div>
</body>
<script>
const school = Vue.extend({
template: `<div>
<h2>学校名称:{{schoolName}}</h2>
<h2>学校地址:{{schoolAddress}}</h2>
<button @click="showInfo">点我弹出学校名称 </button>
</div>`,
data() {
return {
schoolName: "吉林大学",
schoolAddress: "南关区自由大路"
}
},
methods: {
showInfo: function () {
alert(this.schoolName)
}
},
})
const student = Vue.extend({
template: `<div>
<h2>学生姓名:{{studentName}}</h2>
<h2>学生年龄:{{studentAge}}</h2>
</div>`,
data() {
return {
studentName: "michael",
studentAge: 21
}
}
})
//全局注册: 需要在vue实例创建之前定义
Vue.component("xuexiao", school)
Vue.component('xuesheng', student)
//注册组件
new Vue({
el: "#app",
// components: { //局部注册
// xuexiao: school,
// xuesheng: student
// }
})
</script>
</html>
3.2.3 组件的嵌套
1)书写规则
-
子组件要先创建。父组件后创建。 原因:父组件里要使用子组件的名,js而且是从上向下解析的
-
在父组件的定义期间,来注册子组件。
-
在父组件的定义期间,在template中调用子组件
代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>非单文件组件的基本应用</title> <script type="text/javascript" src="./myjs/vue.js"></script> </head> <body> <div id="app"> <app></app> </div> </body> <script> const school = { template: `<ul><li> <span>学校名称:{{schoolName}}</span> <br> <span>学校地址:{{schoolAddress}}</span> </li> </ul>`, data() { return { schoolName: "吉林大学", schoolAddress: "南关区自由大路" } }, } const city = { template: `<ul> <li>{{city}} <school></school> </li> </ul>`, data() { return { city: "长春市", } }, components: { school } } const province = { template: `<ul><li>{{province}} <city></city> </li></ul>`, data() { return { province: "吉林省" } }, components: { city } } const app = { template: ` <province></province> `, components: { province } } //注册组件 new Vue({ el: "#app", components: { //局部注册 app } }) </script> </html>
3.2.4 VueComponent
**1)**VueComponent是组件的构造函数,该构造函数不需要我们程序员自己调用,Vue.extend会自动调用的,可以查看源码:
var Sub = function VueComponent(options) {
this._init(options);
};
**2)**当我们编写组件标签时,Vue在解析时,会帮我们创建组件的实例对象,即Vue帮我们执行下列操作:
new VueComponent(options)。
**3)**每次调用Vue.extend时,返回的都是一个全新的VueComponent构造器
Vue.extend = function (extendOptions) {
/* ...省略... */
var Sub = function VueComponent(options) {
this._init(options);
};
/* ...省略... */
return Sub;
};
**4)**this关键字
1. 组件配置中:
data函数、methods中的函数、watch中的函数、computed中的函数
它们的this均是【VueComponent的实例对象】
2. new Vue(options)配置中:
data函数、methods中的函数、watch中的函数、computed中的函数
它们的this均是【Vue实例对象】
**5)**VueComponent的实例对象,以后简称vc(也可称之为:组件实例对象)。Vue的实例对象,以后简称vm
3.2.5 VueComponent原型链
3.3 单文件组件的开发
单文件组件的意思,就是一个组件占用一个文件,并且该文件的扩展名是.vue
。该文件里包含了HTML,JS,CSS代码,每种代码都是单独一个模块。
3.3.1单文件组件开发的步骤
**第一步:**创建组件文件,比如School.vue。建议文件名首字母大写
**第二步:**编写组件模版,并向外暴露组件。
<!-- 1. template: 书写组件的HTML布局代码 -->
<template>
</template>
<!-- 2. script: 书写组件的交互代码 -->
<script>
</script>
<!-- 3. style: 书写组件的CSS代码 -->
<style>
</style>
向外暴露有三种方式:
1. 直接暴露: 在定义组件时,直接使用export来修饰
export const school = {
data(){
return {
studentName:"水利电力学院",
age:21
}
}
}
2. 统一暴露: 可以一次性暴露多个组件
export {school,.....}
3. 默认暴露:也是在定义组件时,进行暴露,格式如下:
export default {
name:"组件名", // 最好和文件名一致。
data(){
return {
studentName:"水利电力学院",
age:21
}
},
//....
}
**第三步:**定义所有组件的父组件App.vue, 引入并注册子组件,并暴露App组件
<template>
</template>
<script>
//引入School
import School from "./School.vue";
export default {
name:"App",
//注册子组件
components:{
School
}
}
</script>
<style>
</style>
**第四步:**定义程序入口文件main.js,引入相关组件,并定义Vue实例,配置相关选项
import App from "./App.vue"
new Vue({
el: "#demo",
components: {
App
}
})
**第五步:**定义主页面index.html。定义容器,使用组件,导入js文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="demo">
<App></App>
</div>
<script src="../../js/vue.js"></script>
<script src="./main.js"></script>
</body>
</html>
3.3.2 简单案例演示
School.vue
<!-- 1. template: 书写组件的HTML布局代码 --> <template> <div class="sc1"> <p>学校名称:{{schoolName}}</p> <p>学校地址:{{schoolAddress}}</p> </div> </template> <!-- 2. script: 书写组件的交互代码 --> <script> export default{ name:"School", data(){ return { schoolName:"吉林大学", schoolAddress:"高新区前进大街2699号" } } } </script> <!-- 3. style: 书写组件的CSS代码 --> <style> .sc{ background-color: #178167; font-style: italic; } </style>
City.vue
<!-- 1. template: 书写组件的HTML布局代码 --> <template> <ul>{{cityName}} <li style="list-style: decimal;"> <School/> </li> </ul> </template> <!-- 2. script: 书写组件的交互代码 --> <script> import School from './School.vue' export default{ name:"City", data(){ return { cityName:"长春市" } }, components: { School } } </script> <!-- 3. style: 书写组件的CSS代码 --> <style> </style>
App.vue
<template> <div> <City></City> </div> </template> <script> import City from './City.vue' export default { name:"App", components:{ City } } </script> <style> </style>
main.js
import App from './App.vue' new Vue({ el: "#app", components: { App } })
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"> <App></App> </div> <script src="../myjs/vue.js"></script> <script src="./main.js"></script> </body> </html>
单文件组织结构如下图:
案例写完了,但是运行时报错了。原因很简单, 浏览器不能直接支持es6的模块化语法...
此时,需要使用Vue的脚手架才可以。