一、Vue 中应用和组件的基本概念
1. vue的导入
<script src="https://unpkg.com/vue@next"></script>
2. MVVM 设计模式
- MVVM 设计模式,M -> Model 数据, V -> View 视图, VM -> ViewModel 视图数据连接层。
3. 创建一个vue应用
- 全局API:createApp用法。
- 应用API:mount用法。
- 选项-Data:data用法。
- 选项-DOM:template用法。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> <script> // createApp 表示创建一个 Vue 应用, 存储到 app 变量中 // 传入的参数表示,这个应用最外层的组件,应该如何展示 const app = Vue.createApp({ data() { return { message: 'hello world' } }, template: "<div>{{message}}</div>" }); // vm 代表的就是 Vue 应用的根组件 // mount的作用是:用vue应用根组件的模板渲染结果去替换指定DOM元素的innerHTML内容 const vm = app.mount('#root'); </script> </body> </html>
二、理解Vue中的生命周期函数
- 生命周期函数:在某一时刻会自动执行的函数。
- 选项-生命周期钩子。
1. beforeCreate
- 在实例生成之前会自动执行的函数。
2. created
- 在实例生成之后会自动执行的函数。
- 意味着以下内容已被配置完毕:数据侦听、计算属性、方法、事件/侦听器的回调函数。
- 但是此时dom还没有被挂载。该阶段允许执行http请求操作。
补充
ajax请求可以在created以及mounted中。
但是,当请求在created中时,此时视图中的dom没有被渲染出来,拿不到真实的dom
而在mounted中,此时dom已经渲染出来了,所以可以直接操作dom。
- 注意:在服务端渲染不支持mounted,所以在服务端渲染的情况下统一使用created。
- 不考虑服务器端渲染,一般选在
mounted
周期内请求数据,因为这个周期开始时,当前组件已经被挂载到真实的元素上了。
3. beforeMount
- 在组件内容被渲染到页面之前自动执行的函数。
4. mounted
- 在组件内容被渲染到页面之后自动执行的函数。
5. beforeUpdate
- 当data中的数据发生变化时会自动执行的函数。
6. updated
- 当data中的数据发生变化,同时页面完成更新后,会执行的函数。
7. beforeUnmount
- 在卸载组件实例之前,自动执行的函数。
8. unmounted
- 在卸载组件实例之后,dom完全销毁,自动执行的函数。
- 应用API:unmount 卸载应用实例的根组件。
示例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="root"></div> <script> const app = Vue.createApp({ data() { return { message: 'hello' }; }, methods: { showMessage() { console.log(document.getElementById('root').innerHTML); } }, beforeCreate() { console.log('beforeCreate:实例化生成之前。'); }, created() { console.log('created:实例化生成之后'); }, beforeMount() { console.log('beforeMount:组件内容被渲染到页面之前。'); this.showMessage(); }, mounted() { console.log('mounted:组件内容被渲染到页面之后'); this.showMessage(); }, beforeUpdate() { console.log('beforeUpdate:当data中的数据发生变化时会自动执行的函数'); this.showMessage(); }, updated() { console.log('updated:当data中的数据发生变化,同时页面完成更新后,会执行的函数。'); this.showMessage(); }, beforeUnmount() { console.log('beforeUnmount:在卸载组件实例之前,自动执行的函数。'); this.showMessage(); }, unmounted() { console.log('unmounted:在卸载组件实例之后,dom完全销毁,自动执行的函数。'); this.showMessage(); }, template: "<div>{{message}}</div>" }); const vm = app.mount('#root'); </script> </body> </html>
三、常用模板语法
1. 插值表达式
- {{}} :插值表达式,里面可以写js的表达式。
2. v-html 指令
- v-html:更新元素的 innerHTML,内容按普通 HTML 插入。
- 和插值表达式一起使用时,v-html的内容会覆盖插值表达式的内容。
data(){ return { message: '<strong>hello</strong>' } }, template: "<div v-html='message'></div>"
3. v-bind 指令
- v-bind:缩写为“ : ”,用于动态地绑定一个或多个 attribute。
message: '666' template: "<div v-bind:title='message'>hello</div>"
- 在绑定
class
或style
attribute 时,支持其它类型的值,如数组或对象。- 在绑定 prop 时,prop 必须在子组件中声明。
- 没有参数时,可以绑定到一个包含键值对的对象。
<!-- 绑定 attribute --> <img v-bind:src="imageSrc" /> <!-- 动态 attribute 名 --> <button v-bind:[key]="value"></button> <!-- 内联字符串拼接 --> <img :src="'/path/to/images/' + fileName" /> <!-- class 绑定 --> <div :class="{ red: isRed }"></div> <div :class="[classA, classB]"></div> <div :class="[classA, { classB: isB, classC: isC }]"></div> <!-- style 绑定 --> <div :style="{ fontSize: size + 'px' }"></div> <div :style="[styleObjectA, styleObjectB]"></div> <!-- 绑定一个全是 attribute 的对象 --> <div v-bind="{ id: someProp, 'other-attr': otherProp }"></div> <!-- prop 绑定。"prop" 必须在 my-component 声明 --> <my-component :prop="someThing"></my-component> <!-- 将父组件的 props 一起传给子组件 --> <child-component v-bind="$props"></child-component>
4. v-once 指令
- v-once:只渲染元素和组件一次(使用时不需要表达式)。
message: '666' template: `<div v-once>{{message}}</div>`
- 注:改变message的值,DOM不会重新渲染。
5. v-if 指令
- v-if:根据表达式的真假值来有条件地渲染元素。
show: true template: `<div v-if='show'>hello</div>`
7. v-on 指令
- v-on:缩写@,用于绑定事件监听。
data() { return { message: 'hello' } }, methods: { handleClick() { this.message = '666666'; } }, template: `<div v-on:click='handleClick'>{{message}}</div>`
事件修饰符:
.prevent
- 调用event.preventDefault()
。template: `<a href='https://www.baidu.com' @[event].prevent='handleClick'></a>`
8. 动态属性
- 动态属性:使用 [] 包裹属性,用于动态改变属性名。
data() { return { message: 'hello', event: 'click', name: 'title' } }, methods: { handleClick() { this.message = '666666'; } }, template: `<div :[name]='message' v-on:[event]='handleClick'> {{message}} </div>`
- 改变 name
- 改变 event
四、数据、方法、计算属性和侦听器
1. data 选项
- data:类型 Function ,该函数返回组件实例的 data 对象。
- 实例创建之后,可以通过
vm.$data
访问原始数据对象。- 组件实例也代理了 data 对象上所有的 property,因此访问
vm.a
等价于访问vm.$data.a
。data() { return { a: 'hello', } }
2. methods 选项
- methods:类型 {} ,用于定义一些方法,如:事件触发时执行的函数。
data() { return { a: 'hello', } }, methods: { handleClick(str1){ this.a = str1; } }, template: `<div @click='handleClick(66666)'>{{a}}</div>`
- this 指向vue实例(前提:不是箭头函数)。
- 只要页面重新渲染,调用方法将总会再次执行函数。
data() { return { a: 'hello', } }, methods: { showTime(){ now = Date.now(); console.log(`now: ${now}`); return now; } }, template: `<div>{{a}}--{{showTime()}}</div>`
3. computed选项
- computed:类型 {} ,计算属性的结果会被缓存,只有当依赖的响应式 property 变化时才会重新计算。
- 定义时必须要有 return,否则使用时无法接收到属性的值。
- 使用时不需要加(),直接用属性名。
data() { return { a: 'hello', count: 1 } }, computed: { add() { return this.count += 1; } }, methods: { showTime() { now = Date.now(); console.log(`now: ${now}`); return now; } }, template: `<div>{{a}}--{{showTime()}}--{{add}}</div>`
4. watch选项
- watch:类型 {} ,侦听响应式 property的变化。
- 可以得到改变之后的值和改变之前的值。
- 使用
watch
选项允许我们执行异步操作 (访问一个 API),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。price: 5 watch: { price(current, prev){ setTimeout(()=>console.log(current, prev),1000); } }
5. computed、methods和watch的区别
5.1 computed 和 methods
- computed是计算属性,methods是方法,都可以实现对 data 中的数据加工后再输出。
- 不同的是 computed 计算属性是基于它们的依赖进行缓存的,计算属性 computed 只有在它的相关依赖发生改变时才会重新求值。
- 而对于methods,只要页面发生重新渲染,methods 调用总会执行该函数。
5.2 computed 和 watch
- 异同:
- 它们都是vue对监听器的实现,只不过computed主要用于对同步数据的处理。
- watch则主要用于观测某个值的变化去完成一段开销较大的复杂业务逻辑,可处理异步操作。
- 运用场景:
- 当我们需要进行数值计算时,并且依赖于其他数据时,应该使用computed。
- 当我们需要在数据变化时执行异步或者开销较大的操作时,应该使用watch。
四、样式绑定语法
1. 通过class改变样式
1.1 值为字符串
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="https://unpkg.com/vue@next"></script> <style> .red { color: red; } .green { color: green; } </style> </head> <body> <div id="root"></div> <script> const app = Vue.createApp({ data() { return { classString: 'red' } }, template: `<div :class='classString'>hello</div>` }); const vm = app.mount('#root'); </script> </body> </html>
1.2 值为对象
data() { return { classObject: { red: true, green: false } } }, template: `<div :class='classObject'>hello</div>`
1.3 值为数组
data() { return { classArray: ['red', 'green'] } }, template: `<div :class='classArray'>hello</div>`
1.4 包含子组件的情况
当子组件只有一个元素时:
const app = Vue.createApp({ data() { return { classString: 'red', classObject: { red: true, green: false }, classArray: ['red', 'green'] } }, template: ` <div :class='classArray'>hello</div> <demo class='red' /> ` }); app.component('demo', { template: ` <div>one</div> ` })
当子组件有多个组件时时,需要用到 $attrs:
data() { return { classString: 'red', classArray: ['red', 'green'] } }, template: ` <div :class='classArray'> hello <demo :class='classString'/> </div> ` }); app.component('demo', { template: ` <div :class='$attrs.class'>one</div> <div :class='$attrs.class'>two</div> <div class='green'>three</div> ` })
2. 通过行内样式 style 修改样式
- 值同样也有三种形式:字符串、对象、数组。
data() { return { styleString: 'color: blue', styleObject: { color: 'orange', background: 'gold' }, styleArray: ['color: blue', 'background: cyan'] } }, template: ` <div :style='styleString'>one</div> <div :style='styleObject'>two</div> <div :style='styleArray'>three</div> `
五、条件渲染
1. v-if 指令
- 表达式为真值渲染元素,为假值销毁元素。
const app = Vue.createApp({ data() { return { show: true } }, template: ` <div v-if='show'>hello</div> ` }); const vm = app.mount('#root');
2. v-show 指令
- 表达式为真值正常渲染元素,为假值时元素会添加 style="display: none;" 属性。
template: ` <div v-show='show'>hello</div> `
3. v-else 指令
不需要表达式
限制:前一兄弟元素必须有
v-if
或v-else-if
。用法:
为
v-if
或者v-else-if
添加“else 块”。data() { return { show: false } }, template: ` <div v-if='show'>if</div> <div v-else>else</div> `
4. v-else-if 指令
限制:前一兄弟元素必须有
v-if
或v-else-if
。用法:
表示
v-if
的“else if 块”。可以链式调用。data() { return { showOne: false, showTwo: true } }, template: ` <div v-if='showOne'>if</div> <div v-else-if='showTwo'>else-if</div> `
六、列表循环渲染
1. v-for 指令
- 可以使用v-for的类型:Array | Object | number | string | Iterable。
- 作用:基于源数据多次渲染元素或模板块。
1.1 数组的循环渲染
- 接受的数据依次为:值、索引。
const app = Vue.createApp({ data() { return { listArray: ['a', 'b', 'c'], } }, template: ` <div v-for='(item, index) in listArray'> {{index}}--{{item}} </div> ` }); const vm = app.mount('#root');
1.2 对象的循环渲染
- 接受的数据依次为:值、键名、索引。
data() { return { listObject: { name: 'hy', age: 18, sex: 'male' } } }, template: ` <div v-for='(item, key, index) in listObject'> {{index}}--{{key}}--{{item}} </div> `
1.3 number的循环渲染
- 接受的数据依次为:值、索引。
- 从1开始。
template: ` <div v-for='(item, index) in 3'> {{index}}--{{item}} </div> `
1.4 特殊属性 key
- key的作用就是更新组件时判断两个节点是否相同,相同就复用,不相同就删除旧的创建新的。
- 如果不添加key,组件默认都是就地复用,不会删除添加节点,只是改变列表项中的文本值。
- 建议尽可能在使用
v-for
时提供key
attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。- 不要使用对象或数组之类的非基本类型值作为
v-for
的 key。请用字符串或数值类型的值。template: ` <div v-for='(item, index) in 3' :key='index'> {{index}}--{{item}} </div> `
2. 数组、对象更新检测
2.1 使用数组的变更函数
push():插入最后一项。
pop():删除最后一项。
shift():删除第一项。
unshift():插入第一项。
2.2 直接替换数组
2.3 直接更新数组的内容
2.4 直接添加对象的内容
3. v-if 与 v-for 同时使用
- v-if 的优先级比 v-for 要高,不能将它们写在一起。
- 可以使用 <template></template> 进行实现。
- template标签,HTML5提供的新标签,一个父容器,页面加载时会隐藏。
data() { return { listObject: { name: 'hy', age: 18, sex: 'male' } } }, template: ` <template v-for='(item, key) in listObject' :key='key'> <div v-if="key !== 'sex'"> {{key}}--{{item}} </div> </template> `
七、事件绑定
1. 接受事件对象
1.1 不传参数时
data() { return {count: 0} }, methods: { handleClick(event){ console.log(event.target); this.count += 1; } }, template: ` <div> {{count}} <button @click='handleClick'>add</button> </div> `
1.2 传参数时
- 使用 $event 传递原生DOM事件对象
methods: { handleClick(num, event){ console.log(event.target); this.count += num; } }, template: ` <div> {{count}} <button @click='handleClick(2, $event)'>add</button> </div> `
2. 多事件处理器
- 事件处理程序中可以有多个方法,这些方法由逗号运算符分隔。
- 注意:在绑定事件时,函数名后面要加上括号()。
methods: { handleClick1(){ console.log(++this.count); }, handleClick2(){ console.log(--this.count); } }, template: ` <div> {{count}} <button @click='handleClick1(), handleClick2()'>add</button> </div> `
3. 事件修饰符
- 修饰符是由点开头的指令后缀来表示的。
.stop:阻止事件传播。
.prevent:阻止事件默认行为。
.capture:使用事件捕获模式。
.self:点击自己才触发。
.once:只触发一次。
.passive:常用于提升移动端性能。
<!-- 阻止单击事件继续传播 --> <a @click.stop="doThis"></a> <!-- 提交事件不再重载页面 --> <form @submit.prevent="onSubmit"></form> <!-- 添加事件监听器时使用事件捕获模式 --> <!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 --> <div @click.capture="doThis">...</div> <!-- 只当在 event.target 是当前元素自身时触发处理函数 --> <!-- 即事件不是从内部元素触发的 --> <div @click.self="doThat">...</div> <!-- 点击事件将只会触发一次 --> <a @click.once="doThis"></a> <!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 --> <!-- 而不会等待 `onScroll` 完成,提升scroll的性能 --> <div @scroll.passive="onScroll">...</div>
4. 按键修饰符
- 在监听键盘事件时,我们经常需要检查详细的按键。
- 常用的有:
enter
、tab
、delete
(捕获“删除”和“退格”键)、esc
、space
、up
、down
、left
、right。
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` --> <input @keyup.enter="submit" />
5. 其他修饰符
- 系统修饰键:
ctrl、alt、shift、meta。
鼠标按钮修饰符:left、right、middle。
- .exact 修饰符允许你控制由精确的系统修饰符组合触发的事件。
八、表单中双向绑定指令的使用
- v-model指令:在表单控件或者组件上创建双向绑定。
1. input(text)文本中的双向绑定
- 不用写value,绑定的数据默认就是value。
data() { return {message: 'hello'} }, template: ` <div> <input v-model='message'/> {{message}} </div> `
2. textarea 多行文本中的双向绑定
- 不用写value,绑定的数据默认就是value。
- 在文本区域插值不起作用,应该使用
v-model
来代替。<textarea v-model='message'/> // 正确写法 <textarea>{{message}}</textarea> // 错误写法
3. input(Checkbox)复选框中的双向绑定
- 单个复选框,绑定的值为布尔值。
- checkbox 默认的value是 “on”不会改变,checked 值会随着是否选中变为true或false。
data() { return {message: true} }, template: ` <div> <input type='checkbox' v-model='message' /> {{message}} </div> `
- 多个复选框,绑定到同一个数组(需要给复选框设置value值)。
- 同样value值设置之后不会发生改变,checked 值会随着是否选中变为true或false。
- 绑定的message数据会在选中时向数组末尾推入选中元素的value值,反之取消选中时会从数组末尾推出该元素的value值。
data() { return {message: []} }, template: ` <div> <input type='checkbox' v-model='message' value='xm'/> <input type='checkbox' v-model='message' value='hw'/> <input type='checkbox' v-model='message' value='ip'/> {{message}} </div> `
- 修改选中值和取消值,需要添加
true-value
和false-value
属性。- 这里的
true-value
和false-value
属性并不会影响输入控件的value
属性。- 这个设置在单个复选框模式中才有效。
data() { return { message: 'yes' } }, template: ` <div> <input type='checkbox' v-model='message' true-value='yes' false-value='no' value='666'/> {{message}} </div> `
4. input (Radio) 单选框中的双向绑定
- message初始给空字符串即可,如果给数组也会变成字符串。
data() { return {message: ''} }, template: ` <div> <input type='radio' v-model='message' value='xm'/> <input type='radio' v-model='message' value='hw'/> <input type='radio' v-model='message' value='ip'/> {{message}} </div> `
5. select 选择框中的双向绑定
- 注意是在 select 标签上添加 v-model 指令。
- 单选时
data() { return {message: ''} }, template: ` <div> <select v-model='message'> <option disabled value="">Please select one</option> <option>A</option> <option>B</option> <option>C</option> </select> {{message}} </div> `
- 多选时,在 select 标签上添加 multiple 属性,绑定到一个数组。
data() { return {message: []} }, template: ` <div> <select v-model='message' multiple> <option>A</option> <option>B</option> <option>C</option> <option>666666</option> </select> {{message}} </div> `
- 用
v-for
渲染的动态选项data() { return { message: [], // 字符串写法 // options:[ // 'A','B','C' // ] // 对象写法 options: [ {value:'A',text:'A'}, {value:'B',text:'B'}, {value:'C',text:'C'} ] } }, template: ` <div> <select v-model='message' multiple> <option v-for='item in options' :value=item.value>{{item.text}}</option> </select> {{message}} </div> `
6. 修饰符
6.1 .lazy
- 在默认情况下,
v-model
在每次input
事件触发后将输入框的值与数据进行同步。- 添加
lazy
修饰符,从而转为在change
事件之后进行同步(回车或失去焦点)。<input v-model.lazy='message'/>
6. 2 .number
number
修饰符可以将用户的输入值转为数值类型。template: ` <div> <input v-model.number='message' type="number"/> {{typeof message}} </div> `
6.3 .trim
- trim 修饰符会自动过滤用户输入的首尾空白字符。
data() { return { message: '' } }, template: ` <div> <input v-model.trim='message'/> {{message}} </div> `