简介
基本内容
- 渐进式框架
- 核心思想:数据驱动
- 双向数据绑定
如何理解渐进式
Vue框架本身是分层的设计:
- 核心,视图渲染
- 组件化
- 单页面路由
- 状态管理
- 构建系统
一开始不必使用上框架的全部知识,根据需要自行选择即可。
双向数据绑定:
概念理解:数据的变化会引起页面的变化,反之亦然。主要使用与表单元素
指令
v-model
将数据与表单相关元素(input、select、textarea)绑定起来。实现双向数据绑定。
<body>
<!--
v-model 其实是个语法糖(写起来方便,让我们甜甜的)
<input type="text" v-model="msg" />
=>
<input type="text" v-bind:value="msg" v-on:input="msg = $event.target.value" />
$event 是事件对象
$event.target 就是触发这个事件的元素的DOM对象 input
$event.target.value input 的 value
<input type="text" v-model.lazy="msg" />
=>
<input type="text" v-bind:value="msg" v-on:change="msg = $event.target.value" />
-->
<div id="app">
<input
type="text"
v-bind:value="msg"
v-on:input="msg = $event.target.value"
/>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script>
<script>
var vm = new Vue({
el: "#app",
data: {
msg: "Hello",
},
});
</script>
</body>
<div id="app">
<textarea :value="message" @input="message = $event.target.value"></textarea>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
message: 'sss'
}
},
})
</script>
v-html、v-text
v-html: 会解析html元素 v-text: 不会解析html元素
v-show、v-if
v-show: 根据表达式的真假值,切换元素的display CSS属性 v-if: 通过表达式的值来控制元素是否渲染 v-show和v-if的效果基本一样
v-show 与 v-if 的区别:
-
1.
v-if是真正的条件渲染, 默认条件为false时元素不渲染,默认条件为true时元素渲染。条件变为false时元素会销毁,条件变为true时元素会重新创建。 而v-show只是简单的去控制元素的css的display属性,DOM一直存在。 -
- v-show 有更高的初始渲染开销(因为不管默认条件为真还是假,都会渲染出来)
v-if 有更高的切换开销(
切换时做销毁与重建的操作) 如果需要频繁切换显示隐藏使用v-show更好,其余情况使用v-if就好。
- v-show 有更高的初始渲染开销(因为不管默认条件为真还是假,都会渲染出来)
v-if 有更高的切换开销(
-
- v-if 有 v-else v-else-if 去配套使用。 v-show 没有,它是孤零零的一个人
-
- v-if 能配合
template元素使用,而 v-show 不能。
- v-if 能配合
问题:为什么 v-show 不能与 template 去使用呢? 答案:template是不会渲染出来,v-show 是要去控制元素的css属性的。
v-cloak
vue 页面闪烁问题 现象描述:有时会在页面上出现插值表达式的语句,例如 {{ msg }} 。然后过一小会就没了
问题解释:vue 解析页面需要时间
解决方案:
一、不使用 插值表达式语法 。改用 v-text 。
二、推荐 使用 v-cloak 这个指令
- 将 v-cloak 写在挂载点元素上,它没有参数也没有表达式
- 设置 一个全局 css 属性选择器样式。样式代码如下
[v-cloak] {
display: none
}
v-cloak 的特性:在vue实例化并且解析完成之后会自动删除掉这个属性
v-for
v-for 指令:主要就是用于做循环 一、循环数组
v-for="item in xxx"
item 循环的每一项
in 固定写法
xxx 要循环的数据
v-for="(item, index) in xxx"
item 循环的每一项
index 循环的下标
in 固定写法
xxx 要循环的数据
二、循环对象
v-for="item in xxx"
item 循环的xxx对象的value值
in 固定的写法
xxx 要循环的数据(对象)
v-for="(value, key, index) in xxx"
value value值
key key值
index 下标值
in 固定的写法
xxx 要循环的数据(对象)
三、循环字符串
v-for="item in '张三'"
四、循环数字
v-for="item in 10" => v-for="item in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
数组更新检测
Vue 是数据驱动的,如果某个数据的类型是数组类型,在一些数组的操作下是可以更新页面的,还有一些数组的操作不会引起页面的更新.
下列的数组方法是可以引起页面更新的(变异方法):
官网说明
- push() 尾部添加
- pop() 尾部删除
- shift() 头部删除
- unshift() 头部添加
- splice() 高级方法,删除、添加、替换
- sort() 排序方法
- reverse() 颠倒顺序 还有一些数组的操作可以引起页面更新。直接替换原数组 vm.fruits = ['Hello', 'World']
!!下面两种方式修改数组是不会引起页面的更新的:
- 直接通过下标的方式去修改数组: vm.fruits[1] = '我的天'
- 直接通过修改数组的length属性的方式:vm.fruits.length = num
?作业:去百度找一找这种修改不引起页面更新的原因是什么?
解决上面两个情况的方案
- 方案一
- 使用
Vue.set(target, index, value)静态方法 - 使用
实例对象的 $set()实例方法vm.$set(target, index, value)
- 使用
Vue.set() 与 vm.$set() 语法和作用都是一样的。随你选择使用哪种方式
Vue.set(target, index, value)
/*
target 要修改的数据源
index 要修改的下标
value 要修改成什么样
*/
- 方案二
直接换成 splice() 方法来操作即可
vm.fruits.splice(1)
对象更新检测的问题
直接给对象
新增一个属性是不会引起页面更新的。
解决方法使用 Vue.set() 或者 vm.$set() 即可
Vue.set(target, key, value)
/**
target 要修改的数据源
key key
value value
*/
动态class的几种写法
一、v-bind:class="'hello'"
直接使用字符串 hello 做为类名
二、v-bind:class="hello"
使用 hello 这个数据的值作为类名
三、对象 v-bind:class="{ key1: value1, key2: value2 }"
根据 value1 这个数据是否为真值,来控制是否要加上 key1 这个类名
根据 value2 这个数据是否为真值,来控制是否要加上 key2 这个类名
四、数组 v-bind:class="[ value1, 'value2', value3, { key: value } ]"
使用 value1 这个数据的值来作为其中一个类名
使用 value2 这个字符串直接作为其中一个类名
使用 value3 这个数据的值赖作为其中一个类名
根据 value 这个数据的值是否为真值,来控制是否要加上 key 这个类名
<div id="app">
<p :class="hello">我真好看</p>
<p :class="{ key1: true, key2: false }">我真的好看</p>
<p :class="{ key1: key1Value, key2: key2Value }">我的人</p>
<!-- box1, value2, box3, key -->
<p :class="[ value1, 'value2', value3, { key: value } ]">我爱学习</p>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
hello: "box",
key1Value: true,
key2Value: false,
value1: "box1",
value2: "box2",
value3: "box3",
value: true,
},
});
</script>
动态style的几种写法
一、v-bind:style="color: red"
这种是有问题的 二、
对象v-bind:style="{ key1: value1, key2: value2, key3: 'value3' }" key1、key2、key3 都是css的属性名
value1 的值作为 key1 这个css属性名的值
value2 的值作为 key2 这个css属性名的值
value3 这个字符串会作为 key3 这个css属性名的值
css属性名如果是短横线写法,需要转换成驼峰写法: font-size => fontSize
三、数组 v-bind:style="[value1, value2, { color: myColor }]"
使用 value1 这个数据来控制
value1: {} √value1: "fontSize: '20px'" ×
使用 value2 这个数据来控制
使用 myColor 这个数据来控制 color 属性的值
<div id="app">
<!-- <p :style="color: red">p1</p> -->
<p :style="{ color: myColor, fontSize: mySize, fontWeight: 'bold' }">
p2
</p>
<p :style="[ value1, value2, { color: myColor } ]">p3</p>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
myColor: "red",
mySize: "20px",
value1: {
fontSize: "20px",
},
value2: {
fontWeight: "bold",
},
},
});
</script>
watch
watch 配置选项:监听数据的变化。当数据发生变化的时候,可以去做一些额外的操作。
语法
watch: {
key1: value1,
key2: value2
}
/**
key1、key2 是要监听的数据表达式
value1、value2 是处理函数。string | function | object | Array
处理函数会自动接收到两个参数分别是:newVal, oldVal
*/
var vm = new Vue({
el: "#app",
data: {
a: 1,
obj: {
name: "张三",
age: 18,
},
},
watch: {
// 1. 普通函数的写法
// a: function (newVal, oldVal) {
// console.log("newVal: ", newVal);
// console.log("oldVal: ", oldVal);
// },
// 2. 对象函数的简写方式
// a(newVal, oldVal) {
// console.log("newVal: ", newVal);
// console.log("oldVal: ", oldVal);
// },
// 3. 注意!!!不能使用箭头函数!!! methods 选项中也不要使用箭头函数
// 箭头函数绑定了 父级作用域 的上下文,所以 this 将不会按期望指向Vue实例
// a: (newVal, oldVal) => {
// console.log("newVal: ", newVal);
// console.log("oldVal: ", oldVal);
// console.log(this);
// },
// 4. string 写法。hello 是指向的 methods 中 hello 函数
// a: "hello",
// 5. object 写法
// a: {
// // 一定要提供 handler 属性,属性值就是处理函数
// handler(newVal, oldVal) {
// console.log("newVal: ", newVal);
// console.log("oldVal: ", oldVal);
// console.log(this);
// },
// },
// 6. 为啥要有 object 写法,可以做更多监听配置,比如深度监听、默认触发监听函数一次
// 深度监听: 对象中某个属性值发生变化默认是监听不到的
// obj: {
// handler(newVal, oldVal) {
// console.log("newVal: ", newVal);
// console.log("oldVal: ", oldVal);
// },
// deep: true, // 深度监听
// immediate: true, // 默认触发一次监听
// },
// 7. 数组的写法 [string, function, { handler: function }, ...]
// a: [
// "hello",
// function () {
// console.log("f2");
// },
// {
// handler: function () {
// console.log("f3");
// },
// },
// ],
// **key 的表达式写法** 直接去监听某个对象数据中的某个属性 obj.name
"obj.name": function (newVal, oldVal) {
console.log(newVal);
console.log(oldVal);
},
},
methods: {
hello(newVal, oldVal) {
console.log("hello");
// console.log("newVal: ", newVal);
// console.log("oldVal: ", oldVal);
// console.log(this);
},
},
});
注意!!!不能使用箭头函数!!! methods 选项中也不要使用箭头函数。因为箭头函数绑定了 父级作用域 的上下文,所以 this 将不会按期望指向Vue实例
<div id="app">
<input type="text" v-model="inputValue">
</div>
<script>
new Vue({
el: '#app',
data() {
return {
inputValue: ''
}
},
watch: {
// inputValue: (newValue) => {
// console.log(this) // window
// },
inputValue: function (newValue) {
console.log(this) // Vue
}
}
})
</script>
computed
computed 配置选项 (计算属性,衍生属性,派生属性):基于其余的数据来计算出一份新的数据
语法
computed: {
key1: value1,
key2: value2
}
/**
key1 、key2 就是计算属性。
value1、value2 是对应的计算属性的处理函数(计算方式)。是一个函数,必须有返回值。返回值就是这个计算属性的值
*/
function | object
示例一:
computed: {
fullName: function () {
return xxxx
}
}
示例二:
computed: {
fullName: {
// 获取这个计算属性的值的函数
get: function () {
return xxxx
},
// 修改这个计算属性时触发的函数
// value 要修改的值
set: function (value) {
}
}
}
计算属性的特点
-
- 计算属性也可以像
data中的数据一样去使用
- 计算属性也可以像
-
- 计算属性
不允许手动去修改它的值,它的值是与他的依赖项有关的。只有依赖项发生变化,他的值才会发生变化
- 计算属性
-
- 计算属性
有缓存。默认计算出来之后,如果依赖项不发生变化,后续使用将一直使用的是缓存的数据
- 计算属性
-
- 计算属性可以设置 setter 。但是就算有 setter 它也改变不了计算属性的值,真正能
改变它的一直都是它的依赖项。
- 计算属性可以设置 setter 。但是就算有 setter 它也改变不了计算属性的值,真正能
<div id="app">
<p>firstName: {{ firstName }}</p>
<p>lastName: {{ lastName }}</p>
<p>fullName: {{ fullName }}</p>
<p>fullName: {{ fullName }}</p>
<p>fullName: {{ fullName }}</p>
<p>fullName: {{ fullName }}</p>
<p>fullName: {{ fullName }}</p>
<p>fullName: {{ fullName }}</p>
</div>
<script>
var vm = new Vue({
el: "#app",
data: {
firstName: "张",
lastName: "三",
},
computed: {
// 一、普通函数的形式
// fullName: function () {
// console.log("开始计算 fullName");
// // 返回一个值
// // fullName 的依赖项就是 firstName 与 lastName
// // 任何一个依赖项发生变化,这个计算属性就会得到变化
// return this.firstName + " " + this.lastName;
// },
// 二、对象的形式。 与 一 的功能一样
// fullName: {
// get() {
// console.log("开始计算 fullName");
// return this.firstName + " " + this.lastName;
// },
// },
// 三、对象的形式并且允许修改
fullName: {
get() {
console.log("开始计算 fullName");
return this.firstName + " " + this.lastName;
},
set(value) {
// 对 fullName 做赋值的时候,会触发 set 函数
// vm.fullName = '李 四'
console.log(value);
// 间接的去改变它的依赖项,从而实现看上去 fullName 变了的效果
let tmp = value.split(" ");
this.firstName = tmp[0];
this.lastName = tmp[1];
},
},
},
});
</script>
动态参数
约束
- 避免使用大写字符(驼峰也不可以)
<div id="app">
<button v-bind:[attribute]="value">{{ message }}</button>
<button v-bind:[attributeName]="value">{{ message }}</button>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
message: '提交',
value: 'submit',
attribute: 'type',
attributeName: 'type'
}
},
})
</script>
在 DOM 中使用模板时 (直接在一个 HTML 文件里撰写模板),还需要避免使用大写字符来命名键名,因为浏览器会把 attribute 名全部强制转为小写:
- 动态参数的值的约束:只能是字符串类型或null,null表示移除绑定
<div id="app">
<button v-bind:[attribute]="value">{{ message }}</button>
</div>
<script>
new Vue({
el: '#app',
data() {
return {
message: '提交',
value: 'submit',
attribute: true,
attributeName: 'type'
}
},
})
</script>
- 表达式的约束
动态参数表达式有一些语法约束,因为某些字符,如空格和引号,放在 HTML attribute 名里是无效的
组件基础
data必须是函数的原因
官网原文:一个组件的
data选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:
组件可以多次复用使用,如果data不使用函数,那么当其中一个组件更改data,其他组件的数据也会被修改
组件注册
全局组件
Vue.component('my-com', {
template: `
<div>
<div>123</div>
</div>
`
})
new Vue({
el: '#app',
data() {
return {
message: 'sss'
}
}
})
局部组件
Vue.component('my-com', {
template: `
<div>
<div>123</div>
<com1></com1>
</div>
`,
// 局部组件
components: {
com1: {
template: `<div>com1<div>`
}
}
})
new Vue({
el: '#app',
data() {
return {
message: 'sss'
}
}
})
组件名
<!--
在 template 选项中写模板内容,非常不方便,没有代码提示,也没有语法高亮。
这时可以将 模板内容,放置到一个 script 标签中,要注意
1. 给这个script的type属性设置为 text/x-template
2. 给这个script设置一个id
然后 组件的 template 选项,可以直接去使用 #xxId 即可
-->
<script id="appTmp" type="text/x-template">
<div id="app">
<com1></com1>
<hello-world></hello-world>
<helloWorld></helloWorld>
</div>
</script>
<script>
Vue.component("com1", {
template: `
<div>
<div>我是一个 com1 com1 </div>
<p>com1</p>
</div>
`,
});
Vue.component("helloWorld", {
template: `
<div>我是一个 Helo world 组件</div>
`,
});
var vm = new Vue({
el: "#app",
template: "#appTmp",
});
</script>
prop
props 选项
将组件看成是一个函数,props 就是这个函数接收到的参数集合。
prop 就是 props 中的具体一个参数
prop大小写
HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名
prop类型
字符串数组
props: ['title', 'likes',....]
对象形式
可以给每个prop指定值的类型、默认值
props: {
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证) propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: { type: String, required: true },
// 带有默认值的数字
propD: { type: Number, default: 100 },
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return { message: 'hello' }
}
}
传值的几种类型写法
单向数据流
prop是
不允许修改,应为要遵循单向数据流这个规定
非 prop 的 attribute (非 prop 的特性)
在调用组件时,设置的属性(attribute)如果这个组件中没有定义相应的prop。那么这个属性就叫做 非prop的attribute
Vue.component('hello', {
props: {
name: String,
age: Number
},
template: `<div>hello</div>`
})
<hello name="张三" age="18" id="box" title="我的天" class="box1" style="color: red"></hello>
/*
这里 hello 组件调用时。设置的属性有:name、age、id、title、class、style
hello 组件中将 name、age 是规定为 prop 数据的。
那么 这里 id 和 title 这两个属性就叫做非prop的特性
注意:class 与 style 不是非prop的特性。他们两个是非常非常特殊的一个属性
*/
非prop的attribute 的特点
-
- 最主要的一个特点就是会继承到组件的
根元素上
- 最主要的一个特点就是会继承到组件的
-
- 并且是一种
替换的操作
- 并且是一种
class与style是非常特殊的
-
- 他们有 非prop的attribute 的继承的特点
-
- 但是他们
不是替换的操作,而是拼接操作
- 但是他们
用处:调用别人的组件的时候,这个组件的源代码我不能修改,这时如果要修改样式,就可以在调用时设置自己的 class 或 style
<div id="app">
<hello
name="张三"
:age="18"
id="box"
title="我的天"
class="box1"
style="color: red;"
></hello>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script>
<script>
Vue.component("hello", {
props: {
name: String,
age: Number,
},
template: `<div id="hello" title="我的地" class="box2" style="color: green">hello</div>`,
});
var vm = new Vue({
el: "#app",
});
</script>
$attr、$props实例属性
$attrs 实例属性
$attrs 可以获取到组件接收到的非prop的特性。是一个对象 {key1: value1, key2: value2}
$props 实例属性
$props 可以获取到组件接收到的 prop 数据,是一个对象 {key1: value1, key2: value2}
<div id="app">
<hello
id="box"
title="hello"
name="zhagnsan"
:age="18"
class="box"
style="color: red;"
></hello>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script>
<script>
Vue.component("hello", {
props: {
name: String,
age: Number,
},
template: `<div>hello</div>`,
created() {
console.log(this.$attrs);
console.log(this.$props);
},
});
var vm = new Vue({
el: "#app",
});
</script>
inheritAttrs
不希望组件的根元素继承 attribute,可以在组件的选项中设置
inheritAttrs: false
<div id="app">
<base-input label="姓名" placeholder="请输入您的姓名"></base-input>
</div>
<script>
Vue.component('baseInput', {
props: {
label: {
type: String,
default: ''
}
},
template: `
<div>
<span>{{label}}</span>
<input v-bind="$attrs"></input>
</div>
`
})
new Vue({
el: '#app',
data() {
return {
message: 'sss'
}
},
})
</script>
Vue.component('baseInput', {
inheritAttrs: false,
props: {
label: {
type: String,
default: ''
}
},
template: `
<div>
<span>{{label}}</span>
<input v-bind="$attrs"></input>
</div>
`
})
new Vue({
el: '#app',
data() {
return {
message: 'sss'
}
},
})
inheritAttrs: false
基于 $attrs 和 inheritAttrs 来实现一些基础组件
思考实现一个 input 组件
1. label 可以配置
2. type 可以配置
3. placeholder 可以配置
4. readonly 可以配置
5. disabled 可以配置
发现,原生的 input 框是有很多的属性可以去控制,比如:type、placeholder、readonly、disabled
现在,将这些属性都设置为 prop 数据来进行控制的话,有点费劲。
能不能解决这个费劲的问题:希望能够像下面这种去调用。但是不需要在组件中做这些prop的定义
<base-input label="用户名" placeholder="请输入用户名"></base-input>
<base-input
label="密码"
type="password"
placeholder="请输入密码"
></base-input>
<base-input label="地址" placeholder="请输入地址" disabled></base-input>
这时,就可以使用 $attrs 与 inheritAttrs 来实现
做一个组件的时候需要考虑的事情:(!!!!)
-
- 组件要实现什么功能
-
- 组件中那些地方是应该使用数据来控制的,这个数据用什么数据来控制呢
-
- data 数据? 组件自身要对数据做修改的时候
-
- props 数据? 需要通过父组件去传递下来的时候
-
- computed 数据? 这个数据可以基于 现有的 data 、props、computed 做计算得来的时候
<div id="app">
<base-input label="用户名" placeholder="请输入用户名"></base-input>
<base-input
label="密码"
type="password"
placeholder="请输入密码"
></base-input>
<base-input label="地址" placeholder="请输入地址" disabled></base-input>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script>
<script>
Vue.component("baseInput", {
inheritAttrs: false,
props: {
label: {
type: String,
required: true,
},
},
// 不使用prop数据,而是使用非prop特性数据
// template: `
// <div>
// <label>
// <span>{{ label }}</span>
// <input
// :type="$attrs.type"
// :placeholder="$attrs.placeholder"
// :disabled="$attrs.disabled"
// :readonly="$attrs.readonly" />
// </label>
// </div>
// `,
// 一个一个的去绑定 $attrs 也挺费劲。这时可以使用 v-bind 直接绑定一个对象
// v-bind="{key1: value1, key2: value2}" => v-bind:key1="value1" v-bind:key2="value2"
template: `
<div>
<label>
<span>{{ label }}</span>
<input
v-bind="$attrs" />
</label>
</div>
`,
created() {
console.log(this.$attrs);
},
});
// Vue.component("baseInput", {
// props: {
// label: {
// type: String,
// required: true,
// },
// type: {
// type: String,
// default: "text",
// },
// placeholder: {
// type: String,
// default: "请输入",
// },
// readonly: {
// type: Boolean,
// default: false,
// },
// disabled: {
// type: Boolean,
// default: false,
// },
// },
// template: `
// <div>
// <label>
// <span>{{ label }}</span>
// <input :type="type" :placeholder="placeholder" :readonly="readonly" :disabled="disabled" />
// </label>
// </div>
// `,
// });
var vm = new Vue({
el: "#app",
});
</script>
v-bind="{key1: value1, key2: value2}" => v-bind:key1="value1" v-bind:key2="value2"