Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
对于制作原型或学习,你可以这样使用最新版本:
对于生产环境,我们推荐链接到一个明确的版本号和构建文件,以避免新版本造成的不可预期的破坏:
如果你使用原生 ES Modules,这里也有一个兼容 ES Module 的构建文件:
你可以在 cdn.jsdelivr.net/npm/vue 浏览 NPM 包的源代码。
Vue 也可以在 unpkg 和 cdnjs 上获取 (cdnjs 的版本更新可能略滞后)。
请确认了解不同构建版本并在你发布的站点中使用生产环境版本,把 vue.js
换成 vue.min.js
。这是一个更小的构建,可以带来比开发环境下更快的速度体验。
el:
类型:string | HTMLElement
作用:决定之后Vue实例会管理哪一个DOM
data:
类型:Object | Function (组件当中data必须是一个函数)
作用:Vue实例对应的数据对象。
methods:
类型:{ [key: string]: Function }
作用:定义属于Vue的一些方法,可以在其他地方调用,也可以在指令中使用。
computed
计算属性,实现data中一些数据的整理运算,详细看3.4小节
filters
过滤器,书写与methods类似,使用时在变量后面加 ‘|’ ,Vue会把前面的值传入到后面的过滤器中
Total Price: {{totalPrice | showTwoDecimal}} 元
// 实现价格保留2位小数
const app = new Vue({
data:{
totalPrice:58
},
filters:{
showTwoDecimal(price){
return '¥' + price.toFixed(2)
}
}
})
mustache(双大括号)
简单的表达式操作(拼接、倍乘、除法、减法)
<h2>{{firstName + lastName}}h2>
<h2>{{firstName + ' ' + lastName}}h2>
<h2>{{firstName}} {{lastName}}h2>
<h2>{{counter * 2}}h2>
<script src="../js/vue.js">script>
data: {
firstName: 'kobe',
lastName: 'bryant',
counter: 100
}
v-once
只渲染第一次后,不受data中的数据影响
<h2 v-once>{{message}}h2>
v-html
可以对data中的数据,在携带有标签时进行html渲染,不会单独的显示字符串内容。
<h2 v-html="baidu">{{baidu}}h2>
data:{
baidu:'<a href="www.baidu.com">百度a>'
}
v-text
只会显示便是指定的值后面的值会进行覆盖
, 李银河!
v-pre
不进行vue的渲染,原封不动的显示标签中字符串内容
{{message}}
c-cloak(cloak:斗篷,解析前斗篷存在,解析后斗篷摘掉了)
在vue解析前,div中v-cloak属性会存在
在vue解析后,div中v-cloak属性会被删除
# 开始时没有解析vue前,网页文本内容为空(display:none)
# 1秒后解析了vue,v-cloak属性去掉了,故style样式就不生效了。网页出现内容
<style>
[v-cloak] {
display: none;
}
style>
<h2>{{message}}h2>
<script>
// 在vue解析之前, div中有一个属性v-cloak
// 在vue解析之后, div中没有一个属性v-cloak
setTimeout(function () {
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
}
})
}, 1000)
script>
v-bind
动态绑定标签的属性(基本使用)
data:{
imgUrl:"www.baidu.com"
}
# 语法糖写法
data:{
imgUrl:"www.baidu.com"
}
动态绑定class(对象语法)
# class 调用的是一个对象
<h2 class="title" v-bind:class="{active: isActive, line: isLine}">{{message}}h2>
<button v-on:click="btnClick">按钮button>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
isActive: true,
isLine: true
},
methods: {
btnClick: function () {
this.isActive = !this.isActive
},
// 此处为函数调用
getClasses: function () {
return {active: this.isActive, line: this.isLine}
}
}
})
script>
动态绑定class(函数语法)
# class 调用的是一个函数
{{message}}
methods: {
getClasses: function () {
return {active: this.isActive, line: this.isLine}
}
}
动态绑定class(数组语法)-- 使用较少
{{message}}
{{message}}
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
active: 'aaaaaa',
line: 'bbbbbbb'
},
methods: {
getClasses: function () {
return [this.active, this.line]
}
}
})
动态绑定style
<h2 :style="{fontSize: finalSize + 'px', backgroundColor: finalColor}">{{message}}h2> // 对象调用style
<h2 :style="getStyles()">{{message}}h2> // 函数调用style
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
finalSize: 100,
finalColor: 'red',
},
methods: {
// 函数调用style
getStyles: function () {
return {fontSize: this.finalSize + 'px', backgroundColor: this.finalColor}
}
}
})
script>
v-on
事件监听,比如点击,拖拽,键盘事件等
情况一:如果该方法不需要额外参数,那么方法后的()可以不添加。
但是注意:如果方法本身中有一个参数,那么会默认将原生事件event参数传递进去
情况二:如果需要同时传入某个参数,同时需要event时,可以通过$event传入事件。
作用:绑定事件监听器
缩写:@ (语法糖)
预期:Function | Inline Statement | Object
参数:event
基础使用
<button @click="increment">+button>
<button @click="decrement">-button>
methods: {
increment() {
this.counter++
},
decrement() {
this.counter--
}
}
带参数使用
<div id="app">
<button @click="btn1Click()">按钮1button>
<button @click="btn1Click">按钮1button>
<button @click="btn2Click">按钮2button>
<button @click="btn3Click(abc, $event)">按钮3button>
div>
<script src="../js/vue.js">script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
abc: 123
},
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
修饰符
@click.stop
阻止冒泡
@click.prevent
把一些浏览器自动处理时修改为手动处理(例如form表单)
@click.enter
监听某个键盘的键帽
@click.once
只监听一次
<div id="app">
<div @click="divClick">
aaaaaaa
<button @click.stop="btnClick">按钮button>
div>
<br>
<form action="baidu">
<input type="submit" value="提交" @click.prevent="submitClick">
form>
<input type="text" @keyup.enter="keyUp">
<button @click.once="btn2Click">按钮2button>
div>
<script src="../js/vue.js">script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
methods: {
btnClick() {
console.log("btnClick");
},
divClick() {
console.log("divClick");
},
submitClick() {
console.log('submitClick');
},
keyUp() {
console.log('keyUp');
},
btn2Click() {
console.log('btn2Click');
}
}
})
script>
v-show
v-show
与 v-if
的区别
` v-show` 当条件为false时, v-show只是给我们的元素添加一个行内样式: display: none
`v-if `当条件为false时, 包含v-if指令的元素, 根本就不会存在dom中
<div id="app">
<h2 v-if="isShow" id="aaa">{{message}}h2>
<h2 v-show="isShow" id="bbb">{{message}}h2>
div>
data: {
message: '你好啊',
isShow: true
}
v-if / v-else / v-else-if
v-if
判断是否显示,传入布尔值
data: {
isShow: true
}
v-if / v-else
<h2 v-if="isShow">
{{message}}
h2>
<h1 v-else>isShow为false时, 显示我h1>
data: {
message: 'isShow为true时, 显示我',
isShow: true
}
v-if / v-else-if / v-else
在第一个if
条件失败,会执行了第二个else-if
,所有条件判断完了就会执行else
<div id="app">
// 方式一 通过操作使用 v-if / v-else-if / v-else 可读写不高
<h2 v-if="score>=90">优秀h2>
<h2 v-else-if="score>=80">良好h2>
<h2 v-else-if="score>=60">及格h2>
<h2 v-else>不及格h2>
// 方式二(推荐)采用了计算属性来显示文字内容
<h1>{{result}}h1>
div>
<script>
const app = new Vue({
el: '#app',
data: {
score: 99
},
// 方式二采用了计算属性来显示文字内容
computed: {
result() {
let showMessage = '';
if (this.score >= 90) {
showMessage = '优秀'
} else if (this.score >= 80) {
showMessage = '良好'
}
// ...
return showMessage
}
}
})
script>
v-for
遍历数组
<ul>
<li v-for="item in names">{{item}}li>
ul>
<li v-for="(item, index) in names">
{{index+1}}.{{item}}
li>
遍历对象
<ul>
<li v-for="item in info">{{item}}li>
ul>
<ul>
<li v-for="(value, key) in info">{{value}}-{{key}}li>
ul>
<ul>
<li v-for="(value, key, index) in info">{{value}}-{{key}}- {{index}}li>
ul>
data: {
info: {
name: 'why',
age: 18,
height: 1.88
}
}
v-for 添加 key属性
在没有添加key时,如果在中间插入元素,则dom会把下标的值修改为插入的值,后续的下标的值依次往后移一个位置。例如在C处添加F, 则DOM会把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率?
Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。
总结:key的作用主要是为了高效的更新虚拟DOM
基本使用
使用在input
或textare
等可以表单类型的标签中
v-model
指令来实现表单元素和数据的双向绑定
<div id="app">
<input type="text" v-model="message">
{{message}}
div>
data:{
message:''
}
v-model 结合radio类型
不使用vue实现单选按钮
使用vue实现单选按钮
<div id="app">
<label for="male">
<input type="radio" id="male" value="男" v-model="sex" checked>男
label>
<label for="female">
<input type="radio" id="female" value="女" v-model="sex">女
label>
<h2>您选择的性别是: {{sex}}h2>
div>
data: {
sex: '男'
}
v-model 结合checkbox类型
html式checkbox框
Vue式checkbox实现单选框
<label for="agree">-->
<input type="checkbox" id="agree" v-model="isAgree">同意协议
label>
<h2>您选择的是: {{isAgree}}h2>
// 如果isAgree为true,那么disable就会生效,下一步将无法选择,注意前面同意为true,下一步这里取反了
<button :disabled="!isAgree">下一步button>
data:{
isAgree: false, // 单选框
}
Vue式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">羽毛球
<h2>您的爱好是: {{hobbies}}h2>
// 方式二(推荐)
<label v-for="item in originHobbies" :for="item">
<input type="checkbox" :value="item" :id="item" v-model="hobbies">{{item}}
label>
div>
data: {
hobbies: [], // 多选框,
originHobbies: ['篮球', '足球', '乒乓球', '羽毛球', '台球', '高尔夫球']
}
})
v-model 结合select类型
选择一个
<select name="abc" v-model="fruit">
<option value="苹果">苹果option>
<option value="香蕉">香蕉option>
<option value="榴莲">榴莲option>
<option value="葡萄">葡萄option>
select>
<h2>您选择的水果是: {{fruit}}h2>
data:{
fruit:''
}
选择多个
<select name="abc" v-model="fruits" multiple>
<option value="苹果">苹果option>
<option value="香蕉">香蕉option>
<option value="榴莲">榴莲option>
<option value="葡萄">葡萄option>
select>
<h2>您选择的水果是: {{fruits}}h2>
data:{
fruits:[]
}
v-model 原理
v-model
其实是一个语法糖,它的背后本质上是包含两个操作:
v-bind
绑定一个value
属性
v-on
指令给当前元素绑定input
事件
流程解析
不适用v-model
实现数据双向绑定
<input type="text" v-model="message">
<input type="text" :value="message" @input="valueChange">
data:{
message:''
},
methods:{
// 这里传参的event,在标签如果不写的话,默认会把该input事件传入进来
valueChange(event){
this.message = this.event.target.value
}
}
简洁写法实现v-model效果
<input type="text" :value="message" @input="message = $event.target.value">
data:{
message:''
},
v-model
修饰符
.lazy
v-model
默认是在input事件中同步输入框的数据的。
也就是说,一旦有数据发生改变对应的data中的数据就会自动发生改变
lazy
修饰符可以让数据在失去焦点或者回车时才会更新
{{message}}
.number
默认情况下,在输入框中无论我们输入的是字母还是数字,都会被当做字符串类型进行处理。
但是如果我们希望处理的是数字类型,那么最好直接将内容当做数字处理。
number修饰符可以让在输入框中输入的内容自动转成数字类型
{{age}}-{{typeof age}}
tirm
如果输入的内容首尾有很多空格,通常我们希望将其去除
trim修饰符可以过滤内容左右两边的空格
您输入的名字:{{name}}
因为Vue是响应式的,所以当数据发生变化时,Vue会自动检测数据变化,视图会发生对应的更新。Vue中包含了一组观察数组编译的方法,使用它们改变数组也会触发视图的更新。
/ 1.push方法
// this.letters.push('aaa')
// this.letters.push('aaaa', 'bbbb', 'cccc')
// 2.pop(): 删除数组中的最后一个元素
// this.letters.pop();
// 3.shift(): 删除数组中的第一个元素
// this.letters.shift();
// 4.unshift(): 在数组最前面添加元素
// this.letters.unshift()
// this.letters.unshift('aaa', 'bbb', 'ccc')
// 5.splice作用: 删除元素/插入元素/替换元素
// 删除元素: 第二个参数传入你要删除几个元素(如果没有传,就删除后面所有的元素)
// 替换元素: 第二个参数, 表示我们要替换几个元素, 后面是用于替换前面的元素
// 插入元素: 第二个参数, 传入0, 并且后面跟上要插入的元素
// splice(start)
// splice(start):
this.letters.splice(1, 3, 'm', 'n', 'l', 'x')
// this.letters.splice(1, 0, 'x', 'y', 'z')
// 5.sort()
// this.letters.sort()
// 6.reverse()
// this.letters.reverse()
// 注意: 通过索引值修改数组中的元素不会使Vue的值更新
// this.letters[0] = 'bbbbbb';
// this.letters.splice(0, 1, 'bbbbbb')
// set(要修改的对象, 索引值, 修改后的值)
// Vue.set(this.letters, 0, 'bbbbbb')
基本使用
下面代码实现了四种显示英文姓和名的写法,使用计算属性来书写使得代码可读性更好。
<div id="app">
// 字符串拼接方式
<h2>{{firstName + ' ' + lastName}}h2>
// 模板变量组合方式
<h2>{{firstName}} {{lastName}}h2>
// 使用 methods方法
<h2>{{getFullName()}}h2>
// 使用computed计算属性
<h2>{{fullName}}h2>
div>
<script>
const app = new Vue({
el: '#app',
data: {
firstName: 'Lebron',
lastName: 'James'
},
// computed: 计算属性()
computed: {
fullName: function () {
return this.firstName + ' ' + this.lastName
}
},
methods: {
getFullName() {
return this.firstName + ' ' + this.lastName
}
}
})
script>
computed 实现多本书籍价格统计
总价格: {{totalPrice}}
总价格: {{getTotalPrice()}}
计算属性深入理解
computed 包含 set 函数和get函数
set方法主要对数据是否变化进行监听,如果数据发生变化则会触发set方法
get方法主要用于数据值的返回,并被Vue接收渲染
注意:计算属性一般没有set方法,只有属性
# 基本结构
computed: {
fullName: {
set: function(*arg) {
pass
},
get: function () {
pass
}
}
由于set方法一般不会进行书写,所以computed进行了简写
# fullname属性取代了get方法
computed: {
fullName: function () {
console.log('fullName');
return this.firstName + ' ' + this.lastName
}
}
methods 与 computed 的对比
method 在每次调用时都会去进行一次,没有缓存
computed 则是对值进行检测,如果值没有发生改变,则每次调用都只会加载一次,computed 数据会存储在缓存中。
{{firstName}} {{lastName}}
{{getFullName()}}
{{getFullName()}}
{{getFullName()}}
{{getFullName()}}
{{fullName}}
{{fullName}}
{{fullName}}
{{fullName}}
...
✒️ 这是原始的书写方式,后面会有更加便捷的写法(语法糖)
创建组件构造器
Vue.extend()
- 调用Vue.extend()创建的是一个组件构造器。
- 通常在创建组件构造器时,传入template代表我们自定义组件的模板。
- 该模板就是在使用到组件的地方,要显示的HTML代码。
- 事实上,这种写法在Vue2.x的文档中几乎已经看不到了,它会直接使用下面我们会讲到的语法糖,但是在很多资料还是会提到这种方式,而且这种方式是学习后面方式的基础。
注册组件
Vue.component()
- 调用Vue.component()是将刚才的组件构造器注册为一个组件,并且给它起一个组件的标签名称。
所以需要传递两个参数:
1、注册组件的标签名
2、组件构造器
使用组件
组件必须挂载在某个Vue实例下,否则它不会生效。
我们来看下面我使用了三次
一个完整的组件demo
<div id="app">
// 3.使用组件
<my-cpn></my-cpn>
</div>
// 1. 创建组件
const cpn = Vue.extend({ // 注意extend没有s
template:` // 注意template没有s
这是组件的标题
这是组件内容
`
})
// 2. 注册组件
Vue.component('my-cpn',cpn)
全局组件
当我们通过调用**Vue.component()**注册组件时,组件的注册是全局的,这意味着该组件可以在任意Vue示例下使用。
当我们使用Vue 创建多个实例时,全局组件可以在任何一个实例中使用
// my-cpn 可以在app1和app2中使用
Vue.component('my-cpn',cpn)
const app1 = new Vue({ ... })
const app1 = new Vue({ ... })
局部组件
如果我们注册的组件是挂载在某个实例中, 那么就是一个局部组件
<div id="app">
<cpn2_tag></cpn2_tag>
</div>
const cpn2 = Vue.extend({
template:`
这是局部组件的标题
这是局部组件内容
`
})
const app = new Vue({
el: '#app',
data: {},
methods: {},
components:{ // 此处有s
// cpn2_tag 表示标签名
cpn2_tag:cpn2
}
});
// 1.创建第一个组件构造器(子组件)
const cpnC1 = Vue.extend({
template: `
我是标题1
我是内容, 哈哈哈哈
`
})
// 2.创建第二个组件构造器(父组件)
const cpnC2 = Vue.extend({
template: `
我是标题2
我是内容, 呵呵呵呵
`,
components: {
cpn1: cpnC1
}
})
// root组件
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
components: {
cpn2: cpnC2
}
})
方式一:全局组件注册的语法糖
这种方式书写的代码过于臃肿,不够简洁美观
// 全局注册组件
Vue.component('cpn1', {
template: `
<div>
<h2>我是标题1h2>
<p>我是内容, 哈哈哈哈p>
div>
`
})
// 第二个参数的对象,其实就是调用Vue.extend返回的对象被component接收了
// 局部注册组件语法糖
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
components: {
'cpn2': {
template: `
<div>
<h2>我是标题2h2>
<p>我是内容, 呵呵呵p>
div>
`
}
}
})
⭐️方式二:组件模板分离(推荐)
分工更加明确,可读写更高。
// 1. 书写模板标签,指定id用以被template获取
<template id="cpn">
<div>
<h2>我是标题h2>
<p>我是内容,呵呵呵p>
div>
template>
// 2. 注册组件,
Vue.component('cpn', {
template: '#cpn' // 指定template标签的id
})
使用data()
函数来获取数据,注意组件中使用变量来获取数据时,根实例的数据是不能引用的。
组件对象也有一个data属性(也可以有methods等属性)
只是这个data属性必须是一个函数
格式:data(){ return {} } -> 函数再返回一个对象,在对象中定义数据
<template id="cpn">
<div>
<h2>{{title}}h2>
<p>我是内容,呵呵呵p>
div>
template>
Vue.component('cpn', {
template: '#cpn',
data() {
return {
title: 'abc'
}
}
})
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
title: '我是标题' // 不会引用该变量
}
})
为什么data用函数来书写,而不是直接用对象来书写
仔细对比发现,在组件中data是通过函数返回对象,并在对象中定义数据
而在根实例中data是直接使用对象来定义数据的。
为什么会这么定义呢?
解析
组件同一对象调用伪代码
const obj = {
name: 'why',
age: 18
}
Vue.component('cpn', {
template: '#cpn',
data() {
return obj // 以后每一个生成组件,数据都是引用同一个地址
},
组件不同对象调用
const obj =
Vue.component('cpn', {
template: '#cpn',
data() {
return { // 以后每一个生成组件,数据都是引用不同的地址
name: 'why',
age: 18
}
},
封装组件demo
<template id="cpn">
<div>
<h2>当前计数: {{counter}}h2>
<button @click="increment">+button>
<button @click="decrement">-button>
div>
template>
<script>
Vue.component('cpn', {
template: '#cpn',
data() {
return {
counter: 0
}
},
methods: {
increment() {
this.counter++
},
decrement() {
this.counter--
}
}
})
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
}
})
script>
在开发中,往往一些数据确实需要从上层传递到下层,
父组件向子组件传递 props
注意驼峰命名,在脚手架中可以使用,在原生Vue中需要使用`-`来区分来,或者全部小写
方式一
:字符串数组,数组中的字符串就是传递时的名称
// cmovies 要不全小写命名,要不写成c-movies
<cpn v-bind:cmovies="movies">cpn>
// cpn> 上面代码就等同这种格式
const cpn = {
template: '#cpn',
props: ['cmovies', 'cmessage'],
}
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
movies: ['海王', '海贼王', '海尔兄弟']
},
components: {
cpn
}
})
- :star:`方式二`:对象,对象可以设置传递时的类型,也可以设置默认值等。
const cpn = {
template: '#cpn',
props: {
// 1.类型限制
// cmovies: Array,
// cmessage: String,
// 2.提供一些默认值, 以及必传值
cmessage: {
type: String,
default: 'aaaaaaaa',
required: true
},
// 类型是对象或者数组时, 默认值必须是一个函数
cmovies: {
type: Array,
default() {
return []
}
}
}
}
const app = new Vue({
el: '#app',
data: {
message: '你好啊',
movies: ['海王', '海贼王', '海尔兄弟']
},
components: {
cpn
}
})
props 数据验证
如上代码,当需要对**props**进行类型等验证时,就需要对象写法了。
子组件向父组件传递 $emit()
使用自定义事件来完成子组件向父组件传递
在子组件中,通过$emit()来触发事件。
格式:.$emit('EventName',data)
(事件名称,数据)
在父组件中,通过v-on来监听子组件事件。
<div id="app">
// cat-click就是子组件定义事件的名称,catClick是父组件实现的方法,可以简单的理解为cat-click就是一个点击事件,后者是实现的方法,
// 注意在父子传递的过程中,事件名称不可以使用【驼峰命名】
<cpn @cat-click="catClick">cpn>
div>
<template id="cpn">
<div>
<ul>
<li v-for="item in categories">
// 子组件调用了catClick()方法
<button @click="catClick(item)">{{item.name}}button>
li>
ul>
<h6>
{{desc}}
h6>
div>
template>
const cpn = { // 这里被根实例的components调用了,就变成了子组件
template:'#cpn',
data(){
return{
categories:[
{
'id':0001,
'name':'数码',
},
{
'id':0002,
'name':'图书',
},
{
'id':0003,
'name':'家电',
},
{
'id':0004,
'name':'服饰',
},
],
desc:'每天24开始更新数据'
}
},
methods:{
catClick(item){
// (自定义的事件方法名,传递的数据)
this.$emit('cat-click',item) // 发射事件:自定义事件
}
}
}
const app = new Vue({
el: '#app',
data: {},
methods: {},
components:{
cpn // 增强写法就等于 cpn:cpn -》(标签名:对象)
},
methods:{
catClick(item){
// 此处接收的item就是子组件中的item数据
console.log(item)
}
}
});
父子组件访问方式
父组件→子组件,使用$children
或$refs
$children
使用
const app = new Vue({
el: '#app',
data: {
message: '你好啊'
},
methods: {
btnClick() {
// 1.$children
console.log(this.$children);
for (let c of this.$children) {
console.log(c.name);
}
}
},
components: { // 子组件
cpn: {
template: '#cpn',
data() {
return {
name: ['Jack','Rion','Lucy']
}
},
⭐️$refs
使用
<div id="app">
<cpn ref="aaa">cpn> // 绑定一个特定的ID
<button @click="btnClick">按钮button>
div>
methods: {
btnClick() {
// 2.$refs => 对象类型, 默认是一个空的对象 ref='bbb'
console.log(this.$refs.aaa.name) // 使用$refs.ID来获取组件中的数据
}
},
components: {
cpn: {
template: '#cpn',
data() {
return {
name: ['Jack','Rion','Lucy']
}
},
子组件→父组件,使用$parent
或$root
注意:
1.尽管在Vue开发中,我们允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做
2.子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。
3.如果我们将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。
4.另外,更不好做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于我的调试和维护。
data() {
return {
name: '我是cpn组件的name'
}
},
components: {
cpn: {
template: '#cpn',
methods: {
btnClick() {
// 1.访问父组件$parent
console.log(this.$parent);
console.log(this.$parent.name);
// 2.访问根组件$root
console.log(this.$root);
console.log(this.$root.message);
}
插槽(slot)是什么?
组件的插槽有什么好处
栗子:移动网站中的导航栏。
移动开发中,几乎每个页面都有导航栏。显示内容也不一致
导航栏我们必然会封装成一个插件,比如nav-bar组件。
一旦有了这个组件,我们就可以在多个页面中复用了。
基本使用
1.插槽的基本使用
2.插槽的默认值 button
3.如果有多个值, 同时放入到组件进行替换时, 一起作为替换元素
<div id="app">
<cpn><button>左侧button>button>cpn> //此处内容会替换slot标签的内容
<cpn>cpn> // 若为空则会显示slot标签中的内容
<cpn> // 组件中的全部内容一起替换slot标签中的内容
<span>第一段内容span>
<span>第一段内容span>
<span>第一段内容span>
cpn>
div>
<template id="cpn">
<div>
<h2>我是组件,这是共同部分h2>
<slot><h4>这时插槽中的默认值h4>slot>
div>
template>
slot
元素一个name
属性即可// slot属性用来指定加载到模板中哪个slot标签,没有被指定name的slot标签就不会加载
<div id="app">
<cpn><span slot="center">标题span>cpn>
<cpn><button slot="left">返回button>cpn>
div>
<template id="cpn">
<div>
<slot name="left"><span>左边span>slot>
<slot name="center"><span>中间span>slot>
<slot name="right"><span>右边span>slot>
div>
template>
父组件模板的所有东西都会在父级作用域内编译;
子组件模板的所有东西都会在子级作用域内编译。
当我们在使用插槽时,想要同一组数据不同方式展示,如何将数据传递给slot中呢
我们通过获取到slotProps属性
通过slotProps.data
就可以获取到刚才我们传入的data了
slot
标签中可以指定数据对象:DateName="pLanguages"
slot-scope="SlotName"
来获取数据SlotName.DateName
来使用数据<div id="app">
<cpn>cpn> // 默认显示方式,即 ul>li 的方式来显示
<cpn>
<template slot-scope="slot"> // template 名字是可以修改的,slot-scope获取了 模板slot标签的属性,即获取了数据 slot.data = pLanguages
<span v-for="item in slot.data"> {{item}} | span>
template>
cpn>
<cpn>
<template slot-scope="slot">
<span>{{slot.data.join(' * ')}}span>
template>
cpn>
div>
<template id="cpn">
<div>
<slot :data="pLanguages"> //:data data名字可以随意修改,后续调用通过改名字获取数据
<ul>
<li v-for="item in pLanguages">{{item}}li>
ul>
slot>
div>
template>
// 子组件中定义了pLanguages数组
components: {
cpn: {
template: '#cpn',
data() {
return {
pLanguages: ['JavaScript', 'C++', 'Java', 'C#', 'Python', 'Go', 'Swift']
}
}
}
}
网页效果
CLI
是Command-Line Interface
,翻译为命令行界面, 但是俗称脚手架,使用 vue-cli 可以快速搭建Vue开发环境以及对应的webpack配置。
配置下载源
// 如出现下载缓慢请配置 npm 淘宝镜像
npm config set registry https://registry.npm.taobao.org
npm 使用
在使用npm之前,需要先下载node安装包。
npm(node package Manager)
node 环境配置
npm init
安装包
// npm安装
npm install vue
npm install --save
npm install --save-dev
npm install -g
npm search // 搜索
npm update // 更新
npm list -g --depth 0 // 查看全局包,–depth 0:列表搜索深度0级 不加这参数 会默认所有
npm ls vue/cli -g // 查询单独一个包文件是否安装
npm install -g cnpm --registry
npm uninstall -g 包名称 // 卸载
npm run build调用流程
npm run dev 运行流程
安装Vue CLI
npm install -g @vue/cli
创建 vue 项目
vue crate projectname
启动 vue 项目
npm run serve
一个是带有#/
一个不带
hash
URL的hash也就是锚点(#), 本质上是改变window.location的href属性
我们可以通过直接赋值location.hash来改变href, 但是页面不发生刷新
history
history接口是HTML5新增的, 它有五种模式改变URL而不刷新页面.
安装
npm install vue-router --save
vue-router的简单使用
第一步: 创建路由组件
第二步: 配置路由映射: 组件和路径映射关系
第三步: 使用路由: 通过和
实例
创建router实例
//src/router/index.js
import VueRouter from "vue-router";
import Vue from "vue";
import Home from '../components/home'
import Order from '../components/order'
// 第一步: 创建路由组件
Vue.use(VueRouter)
// 第二步: 配置路由映射: 组件和路径映射关系
const routes = [
{
path:"/home",
component:Home
},
{
path:"/order",
component:Order
},
]
const router = new VueRouter({
routes,
mode:"history"
})
// 第三步:将router对象传入到Vue实例
export default router
```
挂在到main.js
中
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App),
}).$mount(’#app’)
```
实现component中的组件
// home.vue
Home
这是Home页面
// order.vue
Home
这是Home页面
to属性
实现组件之间的切换,即各组件之间的跳转
tag属性
tag可以指定之后渲染成什么组件, 比如上面的代码会被渲染成一个
Home
replace属性
replace不会留下history记录, 所以指定replace的情况下, 后退键返回不能返回到上一个页面中
Home
active-class
当对应的路由匹配成功时, 会自动给当前元素设置一个router-link-active的class, 设置active-class可以修改默认的名称.
标签通过点击事件来实现路由跳转,在实现的methods中,调用$router
, $router
这是一个系统自带的对象,属于全局的vuerouter实例。
使用push方法,则网页可以进行前进和后退
使用repalce方法,则不允许网页进行前进或者后退
methods:{
clickHome(){
// return this.$router.push('/home')
return this.$router.replace('/home')
},
clickOrder(){
// return this.$router.push('/order')
return this.$router.replace('/order')
}
}
设置接收参数
// router/index.js
{
path: '/user/:id',
component: User,
}
组件解析id
我是用户界面
{{userId}}
视图传值
用户
其他方式使用懒加载
方式一: 结合Vue的异步组件和Webpack的代码分析.
const Home = resolve => { require.ensure(['../components/Home.vue'], () => { resolve(require('../components/Home.vue')) })};
方式二: AMD写法
const About = resolve => require(['../components/About.vue'], resolve);
方式三(⭐): 在ES6中, 我们可以有更加简单的写法来组织Vue异步组件和Webpack的代码分割.
const Home = () => import('../components/Home.vue')
实现嵌套路由有两个步骤
// 1. 创建对应的HomeNews.vue 和 HomeMessage.vue 组件
{
path: '/home',
component: Home,
meta: {
title: '首页'
},
// 2. 在home页面创建对应的子组件(children)
children: [
// { 设置子组件的默认显示
// path: '',
// redirect: 'news'
// },
{
path: 'news',
component: HomeNews
},
{
path: 'message',
component: HomeMessage
}
]
},
// 3. 路由视图书写
关于
关于
在网页URL后面传递query,如:http://www.baidu.com/?type=1&name=rion
获取参数通过$route
对象获取的
在使用了 vue-router 的应用中,路由对象会被注入每个组件中,赋值为 this.$route
,并且当路由切换时,路由对象会被更新。
router-link 传递
// App.vue
档案
// profile.vue
我是Profile组件
{{$route.query.name}}
{{$route.query.age}}
{{$route.query.height}}
methods方法传递
// App.vue
methods: {
profileClick() {
this.$router.push({
path: '/profile',
query: {
name: 'kobe',
age: 19,
height: 1.87
}
})
}
}
// profile.vue
我是Profile组件
{{$route.query.name}}
{{$route.query.age}}
{{$route.query.height}}
router & route 的区别
$router
r为VueRouter
实例,想要导航到不同URL,则使用$router.push
方法$route
为当前router
跳转对象里面可以获取name、path、query、params
等vue-router提供的导航守卫主要用来监听监听路由的进入和离开的.
vue-router提供了beforeEach和afterEach的钩子函数, 它们会在路由即将改变前和改变后触发.
可以在路由发生改变时执行一些操作,比如修改网页的title等
beforeEach
格式:beforeEach(to,from,next) => {}
to: 即将要进入的目标的路由对象.
from: 当前导航即将要离开的路由对象.
next: 调用该方法后, 才能进入下一个钩子
首先, 我们可以在钩子当中定义一些标题, 可以利用meta来定义
其次, 利用导航守卫,修改我们的标题.
// 子组件中获取元数据的title的方式
document.title = to.matched[0].meta.title
```
**补充**
```
如果是后置钩子, 也就是afterEach, 不需要主动调用next()函数.
补充二: 上面我们使用的导航守卫, 被称之为全局守卫.
路由独享的守卫.
组件内的守卫.
```
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
它们有两个非常重要的属性:
include - 字符串或正则表达,只有匹配的组件会被缓存
exclude - 字符串或正则表达式,任何匹配的组件都不会被缓存.
router-view 也是一个组件,如果直接被包在 keep-alive 里面,所有路径匹配到的视图组件都会被缓存:
实现切换标签保持组件的alive状态
注意,以下都是基于keep-alive状态下才能实现
定义path路径
使用生命周期create函数,在创建组件是保存path的路径
在切换组件时,使用组件守卫来保持path路径,实现保存保持