目录
- Vue安装和配置
- 模板语法
- 前置工作
- 1.文本插值
- 2.属性插值
- 3.条件渲染
- 4.列表渲染
- 5.事件处理
- 6.数组变化侦测
- 7.计算属性
- 8.class绑定
- 9.style绑定
- 10.侦听器
- 11.表单输入绑定v-model
- 12.获取DOM操作
- 13.组件组成和引入
- 14.组件嵌套
- 15.组件的注册(全局注册)
- 16.组件传递数据props(父传子)
- 17.组件传递数据的类型
- 18.组件传递的参数校验
- 19.组件事件(子数据传父数据)
- 20.组件事件和v-model
- 21.子传父(函数传递)
- 透传属性attribute
- 插槽slot(父传子)
- 插槽2(子传父数据)
- 组件生命周期
- 组件生命周期的应用
- 动态组件(组件切换)
- 组件保持存活
- 异步组件
- 依赖注入
- vue应用
Vue安装和配置
1.进入想要的目录
2.输入命令
npm init vue@latest
成功显示:
之后依次输入下面三个命令:
cd vue-project
npm install
npm run dev
运行后出先:
在浏览器中输入会出现:
之后,给VScode安装vue的插件colar:
项目文件说明
在src下写代码。
运行项目
在项目目录输入:
cd vue-project
npm run dev
模板语法
前置工作
第一次使用,我们可以先把默认的数据删除,以方便书写代码。把src/components下的内容全面删除,再把app.vue中的内容全部删除,换成:
<template>
指的是html文本,<script>
指的是语句。
<template>
</template>
<script>
</script>
之后这个启动页面就变成全白了。
我们再把src/main.js中文件的下面这条删掉,这样就没有样式了。
import './assets/main.css'
1.文本插值
使用{{ }}
来进行文本插值。
代码:
<template>
<h3>模板语法</h3>
<p>{{ msg }}</p>
</template>
<script>
export default {
data(){
return{
msg:"你好"
}
}
}
</script>
显示:
合法和不合法的表达式
合法的表达式:
单一的数据,有结果的表达式 三步运算符可以使用{{ }}
来显示也可以表示。
msg
number+1
ok? 'Yes':'No' //ok是true返回Yes,false返回No
代码为:
<template>
<h3>模板语法</h3>
<p>{{ msg }}</p>
<p>{{ number+1 }}</p>
<p>{{ ok? 'Yes':'No' }}</p>
</template>
<script>
export default {
data(){
return{
msg:"你好",
number:10,
ok:true,
}
}
}
</script>
不合法的表达式
下面这类为不合法的表达式:
1.赋值语句,不是表达式。
2.换行的条件控制。
{{ var a =1 }} //不合法
{{ if(ok) { return message } }} //不合法
插入HTML
如果想插入HTML文本,而不是文本,就需要使用v-html
命令(再templete中)。
代码为:
<template>
<h3>模板语法</h3>
<p v-html="raw"></p>
</template>
<script>
export default {
data(){
return{
raw:"<a href='https://www.baidu.com/'>进入百度</a>"
}
}
}
</script>
结果像这样的:
2.属性插值
{{ }}
可以绑定值,但是绑定不了属性。比如我们想绑定id数据:
<div id=""> </div>
我们可以使用这样的方法,在属性前面加上v-bind:
来实现:
<div v-bind:id="idd"> </div>
也可以使用简写:
:id="值"
具体代码为:
<template>
<div v-bind:id="idd"> </div>
<h3>模板语法</h3>
</template>
<script>
export default {
data(){
return{
idd : 10
}
}
}
</script>
查看控制台,这个div被赋值为10:
如果绑定的值为undefined或者null,这个属性就会被移除。
绑定多个属性
直接把v-bind:
后面的值改为一个对象。这个对象中有多个值。
<template>
<div v-bind="idd"> </div>
<h3>模板语法</h3>
<p v-html="raw"></p>
</template>
<script>
export default {
data(){
return{
raw:"<a href='https://www.baidu.com/'>进入百度</a>",
idd : {
id:100,
name:"okk"
}
}
}
}
</script>
结果为:
3.条件渲染
类似if-else。在vue中有4中。
v-if 和 v-show
如果为真,渲染内容,为假,隐藏内容:
tem:
<div v-if="flag">如果为false,不仅不会渲染,也不会添加</div>
<div v-show="flag">不能实现if-else等,但是一定会渲染,因此频繁的切换可以使用v-show。
具体来说就是设置为display:none</div>
scr:
flag:false
<template>
<div v-bind="idd"> </div>
<h3>模板语法</h3>
<div v-if="flag">看不到我</div>
</template>
<script>
export default {
data(){
return{
raw:"<a href='https://www.baidu.com/'>进入百度</a>",
flag:false
}
}
}
</script>
这里为false不会显示内容。
v-else
需要和v-if
一起使用,如果v-if
的值为false,则显示v-else
。
tem:
<div v-else>我真帅!</div>
<template>
<div v-bind="idd"> </div>
<h3>模板语法</h3>
<div v-if="flag">看不到我</div>
<div v-else>我真帅!</div>
</template>
<script>
export default {
data(){
return{
raw:"<a href='https://www.baidu.com/'>进入百度</a>",
flag:false
}
}
}
</script>
只会显示:我真帅。
v-else-if
类似elif,使用方法如下:
tem:
<div v-if= "type === 'A'">A</div>
<div v-if="type === 'B'">B</div>
<div v-if="type === 'C'">C</div>
src:
type:"B"
结果为:B
4.列表渲染
v-for
指令用来渲染数组或者列表,,需要使用item in items
特殊语法才能使用,其中item为源数据的数组,items为子数据。(item of item这个语法也是一样的)
tem:
<p v-for="item in names">{{ item }}</p>
src:
names:["你好","我好",66]
<template>
<h3>模板语法</h3>
<p v-for="item in names">{{ item }}</p>
</template>
<script>
export default {
data(){
return{
names:["你好","我好",66]
}
}
}
</script>
结果为:
处理JSON请求
在前后端交互中,可以使用这种方法来传递JSON数据。注意了,想要实现图片,需要使用v-bind:
绑定属性
绑定JSON数组:
<div v-for="item in result">
遍历其中变量:
{{item.变量名}}
对属性进行操作:
v-bind:属性:"item.变量"
<template>
<h3>模板语法</h3>
<div v-for="item in result"> <!--设置列表-->
<p>{{ item.title }}</p> <!--遍历title属性-->
<img v-bind:src="item.avator" alt="">
</div>
</template>
<script>
export default {
data(){
return{
result:[
{
"id":220,
"title":"你好",
"avator":"图片路径1"
},
{
"id":221,
"title":"你好1",
"avator":"图片路径2"
}
]
}
}
}
</script>
结果为.可以自己添加图片测试:
处理JSON请求2
上面的请求放在数组,也可以不放在数组中,直接写在JSON格式里。其中value为值,key为键,index为索引
tem:
<p v-for="(value,key,index) in userInfo">{{ value }}-{{ key }}-{{ index }}</p>
src:
userInfo:{
name:"iwen",
age:20,
}
<template>
<h3>模板语法</h3>
<p v-for="(item,index) in names">{{ item }}-{{ index }}</p>
<div v-for="item in result"> <!--设置列表-->
<p>{{ item.title }}</p> <!--遍历title属性-->
<img v-bind:src="item.avator" alt="">
</div>
<p v-for="(value,key,index) in userInfo">{{ value }}-{{ key }}-{{ index }}</p>
</template>
<script>
export default {
data(){
return{
names:["你好","我好",66],
result:[
{
"id":220,
"title":"你好",
"avator":"图片路径1"
},
{
"id":221,
"title":"你好1",
"avator":"图片路径2"
}
],
userInfo:{
name:"JSON格式",
age:20,
}
}
}
}
</script>
显示索引
我们可以使用这种语法来显示索引值。
tem:
<p v-for="(item,index) in names">{{ item }}-{{ index }}</p>
scr:
names:["你好","我好",66],
整体代码为:
<template>
<h3>模板语法</h3>
<p v-for="(item,index) in names">{{ item }}-{{ index }}</p>
<div v-for="item in result"> <!--设置列表-->
<p>{{ item.title }}</p> <!--遍历title属性-->
<img v-bind:src="item.avator" alt="">
</div>
</template>
<script>
export default {
data(){
return{
names:["你好","我好",66],
result:[
{
"id":220,
"title":"你好",
"avator":"图片路径1"
},
{
"id":221,
"title":"你好1",
"avator":"图片路径2"
}
]
}
}
}
</script>
使用key进行更新
使用v-for
指令渲染的时,如果用户触发了某种条件,需要使用DOM更改元素的顺序,这时,v-for
会把之前的销毁并重新生成,这可能会降低性能。这时需要使用key
。注意,key
为属性,需要使用v-bind
进行绑定。
在实际的项目中,推荐使用id作为key的值(item.id),因为id一般唯一。
tem:
<p v-for="(item,index) in names" v-bind:key="index">{{ item }}</p>
src:
names:["你好","我好",6666]
<template>
<h3>模板语法</h3>
<p v-for="(item,index) in names" v-bind:key="index">{{ item }}</p>
</template>
<script>
export default {
data(){
return{
names:["你好","我好",6666]
}
}
}
</script>
5.事件处理
使用v-on
或者@
来监听DOM事件,并在事件触发时执行JS,用法为:@click="handler"
或on:click="methodName"
。
内链事件处理器
比如我们想实现 每按一次按钮,值都会+1 可以这样写。内链事件基本不会使用,一般都是使用方法事件处理。
tem:
<button v-on:click="count++">Add</button>
<p>{{ count }}</p>
src:
count:0,
<template>
<h3>模板语法</h3>
<button v-on:click="count++">Add</button>
<p>{{ count }}</p>
</template>
<script>
export default {
data(){
return{
count:0,
}
}
}
</script>
方法事件处理
所有的方法都应写在method中,和data同级。这里说明以下,想要获得return中的数据,需要使用this.
进行绑定。方法事件处理是最常见的。
tem:
<button @click="addCount">Add</button>
<p>{{ count }}</p>
scr:
methods:{
addCount(){
this.count++;
}
}
<template>
<h3>模板语法</h3>
<button @click="addCount">Add</button>
<p>{{ count }}</p>
</template>
<script>
export default {
data(){
return{
count:0,
}
},
methods:{
addCount(){
this.count++;
}
}
}
</script>
事件中的参数
获取event对象
methods:{
addCount(e){
console.log(e);
this.count++;
}
}
这个event对象可以使用target
,也就是说可以使用js(innerHTML之类的)改变内容。
tem:
<button @click="addCount">Add</button>
<p>{{ count }}</p>
src:
methods:{
addCount(e){
e.target.innerHTML = "Add"+ this.count
this.count++
}
<template>
<h3>模板语法</h3>
<button @click="addCount">Add</button>
<p>{{ count }}</p>
</template>
<script>
export default {
data(){
return{
count:0,
}
},
methods:{
addCount(e){
e.target.innerHTML = "Add"+ this.count
this.count++
}
}
}
</script>
传递参数
比如我们想实现点击页面上的那个数据,控制台就输出那个数据。就是把item作为参数传递给函数,之后再控制台打印。
tem:
<p @click="getName(item)" v-for="(item,index) of names" :key="index">{{ item }}</p>
scr:
data(){
return{
names:["11","22","33"],
}
},
methods:{
getName(name){
console.log(name);
}
}
如果这样做,就无法通过之前的方法获得event对象了。可以使用下面的方法:
向第二个参数除加一个 $event
<template>
<h3>模板语法</h3>
<p @click="getName(item,$event)" v-for="(item,index) of names" :key="index">{{ item }}</p>
</template>
<script>
export default {
data(){
return{
names:["11","22","33"],
}
},
methods:{
getName(name,e){
console.log(name);
console.log(e);
}
}
}
</script>
事件修饰符
在使用事件的时候,会有很多事件我们时常调用,因此,可以使用简化的事件修饰符。常见的事件修饰符为:
.stop
.prevent
.once
.enter
阻止默认事件
可以通过event阻止事件,比如我们想让一个a标签的href失效。
method:
e.preventDefault();
<template>
<h3>模板语法</h3>
<a @click="clickhanle" href="https://www.baidu.com/">进入百度</a>
</template>
<script>
export default {
data(){
return{
}
}
,
methods:{
clickhanle(e){
e.preventDefault();
console.log("点击了");
}
}
}
</script>
现在这个跳转就失效了
也可以直接在语句中添加:
<a @click.prevent="clickhanle" href="https://www.baidu.com/">进入百度</a>
父元素被同时触发
又叫阻止事件冒泡。
冒泡事件是:点击了子事件之后,父事件也会触发,但是我们不想让父事件触发。
<div @click="clickhanle1">
<p @click="clickhanle2">clickhanle1和clickhanle2都会被触发。</p>
</div>
可以在子事件中使用event.stopPropagation()
方法就可以防止父元素被触发。
<template>
<h3>模板语法</h3>
<a @click="clickhanle" href="https://www.baidu.com/">进入百度</a>
<div @click="clickhanle1">
<p @click="clickhanle2">冒泡</p>
</div>
</template>
<script>
export default {
data(){
return{
}
}
,
methods:{
clickhanle(e){
e.preventDefault();
console.log("点击了");
},
clickhanle1(e){
console.log("点击了11");
},
clickhanle2(e){
e.stopPropagation()
console.log("点击了22");
}
}
}
</script>
也可以使用.stop
方法:
<p @click.stop="clickhanle2">冒泡</p>
6.数组变化侦测
分为两种1.push方法对应的变更方法。 2.concat方法对应的替换方法。
push方法
push方法可以引起事件更新,比如下面就是按下按钮后增加元素。
<template>
<h3>模板语法</h3>
<button @click="addHandle">添加数据</button>
<ul>
<li v-for="(item,index) of names" :key="index">{{ item }}</li>
</ul>
</template>
<script>
export default {
data(){
return{
names:["111","222","333"],
}
}
,
methods:{
addHandle(){
this.names.push("4444")
}
}
}
</script>
concat方法
又叫合并数组,concat方法虽然也会更新数组,但是在页面中不显示,因为本质是是让数组变成了一个新的数组。(返回一个新的数组)
<template>
<h3>模板语法</h3>
<button @click="addHandle">添加数据</button>
<ul>
<li v-for="(item,index) of names" :key="index">{{ item }}</li>
</ul>
</template>
<script>
export default {
data(){
return{
names:["111","222","333"],
}
}
,
methods:{
addHandle(){
//this.names.push("4444")
console.log(this.names.concat(["55555"]));
}
}
}
</script>
如果想要更新,就把这个返回的数组替换并打印出来:
method:
this.names = this.names.concat(["55555"])
<template>
<h3>模板语法</h3>
<button @click="addHandle">添加数据</button>
<ul>
<li v-for="(item,index) of names" :key="index">{{ item }}</li>
</ul>
</template>
<script>
export default {
data(){
return{
names:["111","222","333"],
}
}
,
methods:{
addHandle(){
//this.names.push("4444")
this.names = this.names.concat(["55555"])
}
}
}
</script>
这样点击后就在页面中显示了。
7.计算属性
可以使用计算属性来替代在{{}}
中写表达式。比如这样。
<p>{{ it.content.length > 0 ? 'Yes' : 'No' }}</p> 在这里写太长了,<!--批量使用不方便。-->
在和data同级的目录下创建computed
作为计算属性,
computed:{
itContent(){
return this.it.content.length > 0 ? 'Yes' : 'No'
}
}
使用就是直接把计算属性放到表达式中:
tem:
<p>{{ itContent }}</p>
整体代码为:
<template>
<h3>模板语法</h3>
<p>{{ it.name }}</p>
<p>{{ itContent }}</p>
</template>
<script>
export default {
data(){
return{
it:{
name:"你好",
content:["我好","大家好"]
}
}
}
,
methods:{
},
computed:{
itContent(){
return this.it.content.length > 0 ? 'Yes' : 'No'
}
}
}
</script>
计算属性和函数的区别:
如果计算属性没有发生变动(就是一个方法,没有更改参数或表达式),则多次调用只是一次渲染。而函数是每次调用每次渲染。
就是优化了一点性能。
8.class绑定
class也是一种属性,可以使用v-bind
来绑定。但是class使用的太多,v-bind
在处理字符串拼接时比较复杂,所以出了一个专用的功能加强。参数中可以使用对象或者数组。
下面这种方法是给class绑定CSS语法。
tem:
<p :class="{'active':isActive}">我是绑定</p>
scr:
isActive:true //是否显示,可以自定义
hasError:true //是否显示,可以绑定很多个条件,一个为false就不显示
sty:
.active{
color:red;
font-size: 100px;
}
<template>
<h3>模板语法</h3>
<p :class="{'active':isActive,'text-danger':hasError}">我是绑定</p>
</template>
<script>
export default {
data(){
return{
isActive:true, //是否显示
hasError:true //是否显示,可以绑定很多个条件,一个为false就不显示
}
}
}
</script>
<style>
.active{
color:red;
font-size: 100px;
}
</style>
多个对象绑定
如果有很多个对象,可以使用更简单的方法进行绑定。
tem:
<p :class="classObject">我是绑定222</p>
scr:
classObject:{
'active':true,
'text-danger':true,
}
sty:
.active{
color:red;
font-size: 60px;
}
<template>
<h3>模板语法</h3>
<p :class="{'active':isActive,'text-danger':hasError}">我是绑定</p>
<p :class="classObject">我是绑定222</p>
</template>
<script>
export default {
data(){
return{
isActive:true, //是否显示
hasError:true, //是否显示
classObject:{
'active':true,
'text-danger':true,
}
}
}
}
</script>
<style>
.active{
color:red;
font-size: 60px;
}
</style>
绑定数组
使用方法如下:
tem:
<p :class="[arrActive,数组名]">我是绑定333</p>
scr:
arrActive:"active" //数组名 : style中的active样式
sty:
.active{
color:red;
font-size: 60px;
}
<template>
<h3>模板语法</h3>
<p :class="{'active':isActive,'text-danger':hasError}">我是绑定</p>
<p :class="classObject">我是绑定222</p>
<p :class="[arrActive]">我是绑定333</p>
</template>
<script>
export default {
data(){
return{
isActive:true, //是否显示
hasError:true, //是否显示
classObject:{
'active':true,
'text-danger':true,
},
arrActive:"active"
}
}
}
</script>
<style>
.active{
color:red;
font-size: 60px;
}
</style>
在数组中使用三元运算符:为true显示active,为false,不显示active。
tem:
<p :class="[isActive? 'active' : ' ']">我是绑定444</p>
scr:
isActive:true, //为true显示active样式
sty:
.active{
color:red;
font-size: 60px;
}
数组和对象一起使用
只能数组嵌套对象。这样会有多个对象。
class="[{'active':isActive},errorClass]"
9.style绑定
和class相同,vue有对style的加强。
一般的使用方法:
<p style="{color:'red'}">style绑定</p>
对象绑定
tem:
<p :style="{color:actC,fontSize:actC1+'px'}">style绑定</p>
src:
actC:"green",
actC1:60
<template>
<h3>模板语法</h3>
<p :style="{color:actC,fontSize:actC1+'px'}">style绑定</p>
</template>
<script>
export default {
data(){
return{
actC:"green",
actC1:60
}
}
}
</script>
<style>
</style>
也可以放在对象中:
tem:
<p :style="styleO">style绑定</p>
src:
styleO:{
color:"red",
fontSize:"30px"
}
<template>
<h3>模板语法</h3>
<p :style="{color:actC,fontSize:actC1+'px'}">style绑定</p>
<p :style="styleO">style绑定</p>
</template>
<script>
export default {
data(){
return{
actC:"green",
actC1:60,
styleO:{
color:"red",
fontSize:"30px"
}
}
}
}
</script>
<style>
</style>
也可以使用数组,和class一样。
10.侦听器
只能监听动态数据。比如这个例子,按下按钮后Hello切换为6666。
<template>
<h3>模板语法</h3>
<p>{{ message }}</p>
<button @click="update">修改数据</button>
</template>
<script>
export default {
data(){
return{
message:"hello"
}
},
methods:{
update(){
this.message = "6666"
}
}
}
</script>
<style>
</style>
监听器需要在data同级处创建一个watch
。它的函数名必须和data中属性名(监听的数据)一致。其中有两个参数newValue,oldValue
,分别为更新前的数据和更新后的数据(两个是自带的)。我们可以在数据变化时使用函数来处理这些变化(如果需要的话)
watch:{
message(newValue,oldValue){
//数据变化时发生的函数
console.log(newValue,oldValue);
}
}
11.表单输入绑定v-model
手动绑定监听器会比较麻烦(使用js比较麻烦),但是在vue中只需要使用v-model
指令就可以了。下面这个代码是当用户输入时,页面中会同时显示输入的数据。
比如百度的搜索框就是表单输入绑定。
tem:
<form action="">
<input type="text" v-model="message">
<p>{{ message }}</p>
</form>
scr:
message:""
<template>
<h3>模板语法</h3>
<form action="">
<input type="text" v-model="message">
<p>{{ message }}</p>
</form>
</template>
<script>
export default {
data(){
return{
message:""
}
},
methods:{
},
watch:{
}
}
</script>
<style>
</style>
其他的搜索框也可以实现
tem:
<form action="">
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
</form>
scr:
checked:false
一旦点击这个框,就会显示true。
v-model的修饰符
v-model.number
:只获取数字。
v-model.trim
:获取时去掉前后空格。
v-model.lazy
:输入完点击回车或确定才获取。
12.获取DOM操作
前面已经完成了大量的DOM元素,如果想要操作一些不常用的操作,可以使用ref
标签来获得元素,使用this.$refs
来操作元素。`
tem:
<div ref="container" class="container">
{{ content }}
</div>
<button @click="getE">获取元素</button>
methods
getE(){
console.log(this.$refs.container);
}
这样点击按钮就可以获得这个ref
值为为container
的元素。
因为获得了这个元素,所以你可以对它重新赋值,其他JS操作也可以:
mthods:
getE(){
console.log(this.$refs.container.innerHTML="hhhhh"); //修改内容
}
下面这个例子是获取用户的输入。
tem:
<input type="text" ref="username">
mthods:
console.log(this.$refs.username.value);
13.组件组成和引入
组件可以复用。一般在使用时,会把组件定义在一个单独的.vue
文件中。一个vue
组件包含tempate
,script
,style
三种,分别用来写html,js,css。
组件引入
之前都是使用默认的App.vue来写代码,如果我们想要自定义组件,可以在conponent
目录下新建.vue
文件。
如果想要显示组件,我们先在默认的App.vue
中引入。在scr下面引入:
//写在export default外面
import (引入变量) from "./components/文件名.vue"
import nihao from "./components/nihao.vue"
之后需要注入组件,在里面写一个components:{}把引入变量放入:
export default{
components:{
nihao
}
}
最后-显示组件,在tem中使用标签的形式显示组件:
tem:
<nihao />
scoped参数
在style
中加入scoped参数可以让当前样式只在当前组件中生效,默认在app.vue中的style
全局生效的。你可以在子组件中使用scoped
来让改组件使用自己的样式。
<style scoped>
14.组件嵌套
组件之间可以嵌套,组件一般在开发中被层层嵌套。比如想实现下面的效果(每个框都是独立的一个文件):
基本上每一个组件都可以实现嵌套。
1.创建文件Header.vue并在里面书写代码。
<template>
<h3>标题</h3>
</template>
<style scoped>
h3{
height: 100px;
width: 100%;
border: 1px solid ;
box-sizing: border-box;
text-align: center;
line-height: 100px;
}
</style>
2.创建Main文件并写入代码:
<template>
<div class="main">
<h3>主题内容</h3>
<Article />
<Article />
</div>
</template>
<script>
</script>
<style scoped>
.main{
float: left;
width: 70%;
border: 2px solid ;
height: 400px;
}
</style>
3.创建Aside文件并写入代码:
<template>
<div class="aside">
<h3>右边内容</h3>
</div>
</template>
<style scoped>
.aside{
float: right;
width: 29.4%;
border: 2px solid ;
height: 400px;
}
</style>
4.因为Header.vue/Main.vue/Aside.vue在App.vue中,所以需要在App.vue下引入:
<template>
<Header />
<Main />
<Aside />
</template>
<script>
import Header from "./pages/Header.vue"
import Main from "./pages/Main.vue"
import Aside from "./pages/Aside.vue"
export default{
components:{
Header,
Main,
Aside
}
}
</script>
<style>
</style>
5.实现文章创建Article文件,并被Main.vue引入:
<template>
<h3>文章</h3>
</template>
<style scoped>
h3{
width: 80%;
margin: 0 auto;
height: 100px;
margin-bottom: 10px;
text-align: center;
background: #999;
}
</style>
修改Main代码为:
<template>
<div class="main">
<h3>主题内容</h3>
<Article />
<Article />
</div>
</template>
<script>
import Article from "./Article.vue"
export default{
components:{
Article
}
}
</script>
<style scoped>
.main{
float: left;
width: 70%;
border: 2px solid ;
height: 400px;
}
</style>
15.组件的注册(全局注册)
就是组件的引入方式。一个vue组件在使用前需要先注册。注册有两种方式:全局注册和局部注册。之前使用的引入都是局部注册。全局注册就是注册后所有组件都可以引入,就是只是需要写<Header />
就可以引入了。
比如我们上面的例子,我们把Header这个组件相关的引入先删除。
全局注册需要在main.js
处实现。使用下面的代码替换:
import { createApp } from 'vue'
import App from './App.vue'
//引入组件
import Header from "./pages/Header.vue"
//构造对象
const app = createApp(App)
//注册组件,第一个参数是键(名字)
app.component("Header",Header)
//一定要放到最后
app.mount('#app')
全局注册后,页面显示:
全局注册会消耗更多的性能。
16.组件传递数据props(父传子)
无法修改父级传来的数据。
组件和组件之间是可以传递数据的。使用props
来传递参数。只能由父级传给子级。用法为:
<Child title="Parent数据" /> <!--在定义的组件处加一个 (参数值)="传递的数据"-->
子组件使用参数:
props:
props:["title"] //如果参数值为titile
tem:
{{ titile }}
在components目录下创建两个新的文件Parent.vue
和Child.vue
,在里面写入内容:
Parent,需要在里面定义Child组件:
<template>
<h3>Parent</h3>
<Child title="Parent数据" />
</template>
<script>
import Child from "./Child.vue"
export default {
data(){
},
components:{
Child
}
}
</script>
Child:
<template>
<h3>Child</h3>
<p>{{ title }}</p>
</template>
<script>
export default {
data(){
},
//必须以字符串形式存在
props:["title"]
}
</script>
在App.vue
中引入父组件:
<template>
<Parent />
</template>
<script>
import Parent from "./components/Parent.vue"
export default{
components:{
Parent,
}
}
</script>
<style>
</style>
动态获得数据
也很简单,但是需要注意,绑定属性需要使用v-bind(或:)
方法。
tem:
<Child :title="message" />
scr-data:
message:"Parent数据"
<template>
<h3>Parent</h3>
<Child :title="message" />
</template>
<script>
import Child from "./Child.vue"
export default {
data(){
message:"Parent数据"
},
components:{
Child
}
}
</script>
17.组件传递数据的类型
使用props
传递数据,任何类型都可以传递。
数字类型
tem:
<Child :title="message" :age="age" />
scr-data:
age:20
数组类型
tem:
<Child :title="message" :age="age" :name="names"/>
scr-data:
names:["你好","我好"]
对象类型
tem:
<Child :title="message" :age="age" :name="names" :userInfo="userInfo"/>
scr-data:
userInfo:{
name:"hhhh",
age:10
}
Parent
<template>
<h3>Parent</h3>
<Child :title="message" :age="age" :name="names" :userInfo="userInfo"/>
</template>
<script>
import Child from "./Child.vue"
export default {
data(){
return{
message:"Parent数据",
age:20,
names:[
"你好","我好"
],
userInfo:{
name:"hhhh",
age:10
}
}
},
components:{
Child
}
}
</script>
Child:
<template>
<h3>Child</h3>
<p>{{ title }}</p>
<p>{{ age }}</p>
<p>{{ name }}</p>
<ul>
<li v-for="(item,index) of names" :key="index">{{ item }}</li>
</ul>
<p>{{ userInfo.name }}</p>
<p>{{ userInfo.age }}</p>
</template>
<script>
export default {
data(){
},
//必须以字符串形式存在
props:["title","age","name","userInfo"]
}
</script>
18.组件传递的参数校验
无法修改父级传来的数据。
比如我们希望传递数字类型,但是发送了字符串类型。只用修改props
中的值就可以了。
props:{
title:{ //接受的参数
type:[String,Number,Array,Object] //希望接受字符串类型,数字类型,数组类型,对象类型。
}
}
如果你传递了其他数据,还是会显示,但是会出现一个报错信息。
默认值
就是子组件希望父组件传递的参数,比如子组件希望父组件传递title
和age
这两个参数。只能使用在字符串和数字中。
props:{
title:{
type:String
},
age:{
type:Number
}
}
但是如果父组件不传,则显示默认值。
props:{
title:{
type:String
},
age:{
type:Number,
default:0 //如果没有age,显示0
}
}
工厂函数(默认值)
数组和对象使用默认值需要使用工厂函数。
default(){return [" "]}
props:{
names:{
type:Array,
default(){
return ["空"]
}
}
}
这样,如果不传names这个数组,就显示空
必选项
不传就会报警告。
required:true
props:{
title:{
type:String,
required:true
},
age:{
type:Number,
default:0
},
names:{
type:Array,
default(){
return ["空"]
}
}
}
整体代码
需要创建两个文件componentA
和componentB
并在里面写入内容,在componentA
处引入componentB
,在app.vue
处引入componentA
componentA:
<template>
<h3>ComponentA</h3>
<componentB :title="title" />
</template>
<script>
import componentB from './componentB.vue';
export default {
data(){
return {
title:"标题",
names:["aa","bb"]
}
},
components:{
componentB
}
}
</script>
componentB:
<template>
<h3>ComponentB</h3>
<p>{{ title }}</p>
<p>{{ age }}</p>
<p v-for="(item,index) of names" :key="index">{{ item }}</p>
</template>
<script>
export default {
data(){
return {
}
},
props:{
title:{
type:String,
required:true
},
age:{
type:Number,
default:0
},
names:{
type:Array,
default(){
return ["空"]
}
}
}
}
</script>
App.vue:
<template>
<ComponentA />
</template>
<script>
//import Header from "./pages/Header.vue"
//import Main from "./pages/Main.vue"
//import Aside from "./pages/Aside.vue"
//import Parent from "./components/Parent.vue"
import ComponentA from "./components/componentA.vue"
export default{
components:{
//Header,
//Main,
//Aside
//Parent,
ComponentA,
}
}
</script>
<style>
</style>
19.组件事件(子数据传父数据)
在组件中,可以使用$emit
方法来触发自定义事件(事件),它可以实现子组件向父组件传递数据。又叫自定义事件。
子级:
tem:
<button @click="clickEvent">传递数据</button>
src:
this.$emit("someEvent","数据1")//向父级发送一个事件someEvent,第二个参数可以为数据
父级:
tem:
<cEvent @someEvent="getHandle" />
<p>{{ message }}</p>
src-data:
message:""
met:
getHandle(){
console.log("触发了" + data);
this.message = data //把data存入message中
}
这样子组件点击按钮后,父元素就会响应数据了。
具体代码为:
在component目录创建一个文件Event.vue
和cEvent.vue
cEvent.vue:
<template>
<h3>子组件事件</h3>
<button @click="clickEvent">传递数据</button>
</template>
<script>
export default {
methods:{
clickEvent(){
console.log("哈哈哈");
//自定义事件
this.$emit("someEvent","数据1")
}
}
}
</script>
Event.vue:
<template>
<h3>组件事件</h3>
<cEvent @someEvent="getHandle" />
<p>父元素:{{ message }}</p>
</template>
<script>
import cEvent from "./cEvent.vue"
export default {
data(){
return{
message:""
}
},
components:{
cEvent
},
methods:{
getHandle(data){
console.log("触发了" + data);
this.message = data; //把data存入message中
}
}
}
</script>
20.组件事件和v-model
比如用户在组件A中输入,同时结果出现在组件B。本身是使用侦听器。
1.创建组件search.vur
,用来实现搜索框:
<template>
<input type=" text" v-model="search"> <!-- 这里是使用v-model来绑定数据,获得输入的数据 -->
</template>
<script>
export default{
data(){
return {
search :""
}
},
//侦听器
watch:{
search(newValue,oldValue){
this.$emit("searchEvent",newValue) //把值作为一个事件传递给下一个组件
}
}
}
</script>
2.创建组件Main.vue
用来重复内容:
<template>
<h3>我是主页面</h3>
<p>搜索内容为:{{ search }}</p>
搜索:<search @searchEvent='getSerach' /> <!--绑定事件,创建函数处理-->
</template>
<script>
import search from './search.vue';
export default {
components:{
search
},
data(){
return {
search:""
}
},
methods:{
getSerach(data){
this.search = data; //把传来的数赋给search
}
}
}
</script>
21.子传父(函数传递)
之前说了父传子(props),子传父($emit)。但是props
其实也可以实现子传父,使用另一种方式-父组件传递函数给子组件,子组件实现这个函数之后又传给父元素。
传递函数:
tem:
<子组件名 :onEvent="函数名" />
<CB :onEvent="dataFn" />
1.创建父组件,在父组件中实现子组件:
<template>
<h3>组件A</h3>
<CB :onEvent="dataFn" />
<p>{{ message }}</p>
</template>
<script>
import CB from './CB.vue';
export default {
data(){
return{
message:""
}
},
components:{
CB,
},
methods:{
dataFn(data){
console.log(data);
this.message = data
}
}
}
</script>
2.创建子组件,实现函数(父组件定义,子组件执行,之后会传给父组件):
<template>
<h3>组件B</h3>
<p>{{ onEvent('子给父传递数据') }}</p>
</template>
<script>
export default {
data(){
return{
}
},
props:{ //接受父组件传递的这个函数
onEvent:Function
}
}
</script>
这样写又些复杂,容易混淆,可以用来理解传递函数的概念。
透传属性attribute
使用率比较低,可以只用来了解。
透传是指传递给一个组件,但是这个组件不是props
,emit
或者v-on
事件监听器。而是class,id,style渲染之类的。而且只能有一个元素。
比如在组件声明处加入一个class:
<Att class="attr-container" />
这个是代码,如果我们加了.attr-container
这个样式,那么我们的元素就会直接使用这个样式(不需要给它上js,css之类的,比如这个h3没有被申明样式,但是它任然是红色的)
<template>
<!--必须为唯一根元素,就是只能由一个元素-->
<h3>透传</h3>
</template>
<script>
</script>
<style>
.attr-container{
color:red
}
</style>
禁用attribute
在script
代码处加入:
export default {
inheritAttrs:false
}
没有什么用
插槽slot(父传子)
组件中传递数据使用props
,而传递模板
需要使用到slot
。我们可以使用模板来书写代码,而写法就不是之前申明处不是使用<组件名 />
这样了,而是<slot1></slot1>
这样的双标签。
同时,使用模板中的内容也是使用<slot1></slot1>
,
App.vue:
<template>
<slot1>
<h3>插槽2-你可以在这里写代码</h3>
<p>我是内容</p>
</slot1>
</template>
<script>
import slot1 from "./components/slot.vue"
export default{
components:{
slot1,
}
}
</script>
slot.vue:
<template>
<h3>插槽基础知识</h3>
<slot></slot>
</template>
<script>
</script>
slot动态数据显示
slot可以访问到父组件的数据作用域,因为其就是在父组件中定义的。比如我们想使用动态数据:
就像:
<h3>{{ message }}</h3>
这个message
我们需要在父组件中定义。就像下面这个例子:
<template>
<slot1>
<h3>{{ message }}</h3>
</slot1>
</template>
<script>
export default{
components:{
slot1,
},
data(){
return {
message:"插槽内容2"
}
}
}
</script>
默认内容
如果slot没有传递任何数据,默认值就会生效,插槽默认值的设置方法如下:
在子组件中:
<slot>默认值</slot>
<template>
<h3>插槽基础知识2</h3>
<slot>默认值</slot>
</template>
<script>
export default {
data(){
return{
}
}
}
</script>
具名插槽
如果我们只是需要一部分的插槽内容
,我们可以把插槽的一部分加上名字
,在使用时可以指定使用那些部分的内容。使用<template v-slot:名字>
来定义。
v-slot
拥有简写#
,就是<template #名字>
<template v-slot:header>
在父组件中定义:
<组件名>
<template v-slot:header>
<h3>{{ message }}</h3>
</template>
<template v-slot:main>
<p>内容</p>
</template>
</组件名1>
父组件的整体代码:
<template>
<slot1>
<template v-slot:header>
<h3>{{ message }}</h3>
</template>
<template v-slot:main>
<p>内容</p>
</template>
</slot1>
</template>
<script>
import slot1 from "./components/slot.vue"
export default{
components:{
slot1,
},
data(){
return {
message:"插槽内容2"
}
}
}
</script>
<style>
</style>
子组件中,我们只想要header部分的元素:
<template>
<h3>插槽基础知识2</h3>
<slot name="header">默认值</slot>
<hr>
</template>
<script>
export default {
data(){
return{
}
}
}
</script>
只能使用在template中
插槽2(子传父数据)
插槽一般只能从父组件中传递,但是如果同时想要子组件的数据,就需要先把数据传给父元素,父元素再把值传到对象中。
传递的方法:
<!--子元素 msg为自定义值,child为要传递的数据 -->
<slot :msg="child"></slot>
<!--父元素 slotp是一个对象,可以自定义名字,需要使用 对象.传来的值 这样生效-->
<!--这里的v-slot不能使用#代替-->
<slot2 v-slot="slotp">
<h3>{{ text }}-{{ slotp.msg }}</h3>
</slot2>
子组件的代码:
<template>
<h3>我是slot</h3>
<slot :msg="child"></slot>
</template>
<script>
export default {
data(){
return{
child:"我是子组件的数据"
}
}
}
</script>
父组件的代码:
<template>
<slot2 v-slot="slotp">
<h3>{{ text }}-{{ slotp.msg }}</h3>
</slot2>
</template>
<script>
import slot2 from "./components/slot11.vue"
export default{
components:{
slot2,
},
data(){
return {
message:"插槽内容2",
text:"测试文本"
}
}
}
</script>
具名插槽的数据传递
如果插槽有名字,也可以实现子传父。
子组件:
<template>
<h3>第一个命名为header,第二个命名为main</h3>
<slot name="haeder" :msg="child"></slot>
<slot name="main" :job="job"></slot>
</template>
<script>
export default {
data(){
return{
child:"我是子组件的数据",
job:"你好啊"
}
}
}
</script>
父组件:
<template>
<slot2 >
<template #header="slotp"> <!--使用名为header的slot,对象为slotp -->
<h3>{{ text }}-{{ slotp.msg }}</h3>
</template>
<template #main="slotp"> <!--使用名为main的slot -->
<p>{{ slotp.job }}</p>
</template>
</slot2>
</template>
<script>
import slot2 from "./components/slot11.vue"
export default{
components:{
slot2,
},
data(){
return {
message:"插槽内容2",
text:"测试文本"
}
}
}
</script>
组件生命周期
分为四个事件,每个周期有其特殊的生命周期函数。每个阶段有两个,一个是创建(更新)前,一个是创建(更新)阶段。
1.创建期:beforeCreate/created
2.挂载期:beforeMounte/mounted
3.更新期:beforeUpdate/updated
4.销毁期:beforeUnmount/unmounted
<template>
<h3>组件的生命周期</h3>
<p>{{ Message }}</p>
<button @click="updateHandle">更新数据</button>
</template>
<script>
export default{
data(){
return{
Message:"更新之前",
}
},
methods:{
updateHandle(){
this.Message = "更新之后"
}
},
beforeCreate(){
console.log("组件创建之前");
},
created(){
console.log("组件创建之后");
},
beforeMount(){
console.log("组件渲染之前");
},
mounted(){
console.log("组件渲染之后");
},
beforeUpdate(){
console.log("组件更新之前");
},
updated(){
console.log("组件更新之后");
},
beforeUnmount(){
console.log("组件销毁之前");
},
unmounted(){
console.log("组件销毁之后");
}
}
</script>
如果我们直接打开页面。会执行四个生命周期函数:
如果我们点击按钮更新页面,会执行两个生命周期函数:
组件销毁后面再做。
组件生命周期的应用
有两个最常见的应用:
1.通过``ref```获取元素的DOM结构。
2.模拟网络请求渲染数据。
1.只能在渲染期读取到数据:
<template>
<h3>组件生命周期</h3>
<p ref="name">我是数据</p>
</template>
<script>
export default {
beforeMount(){
//这个时期,你无法读取到这个数据
console.log(this.$refs.name); //undefined
},
mounted(){
//这个时期,可以读到
console.log(this.$refs.name);
}
}
</script>
2.比如接受后端传来的数据,只能在创建期后(更好是渲染期)执行:
如果在创建期执行,很可能会出现数据提前拿到,但是css没有加载完成。
<template>
<ul>
<li v-for="(item,index) of banner" :key="index">
<h3>{{ item.title }}</h3>
<p>{{ item.content }}</p>
</li>
</ul>
</template>
<script>
export default {
data(){
return{
banner:[]
}
},
created(){
//模拟网络请求-只能在创建期实现
this.banner = [{
"title":"伦敦",
"content":"是英国的首都"
},]
},
}
</script>
动态组件(组件切换)
有的时候需要两个组件来回切换,比如tap界面。需要注意的是,这里的赋值需要以字符串的形式赋值。使用<component :is="值"></component>
来实现,具体代码如下:
app.vue
<template>
<component :is="tapComponent"></component>
<button @click="changeHandle">切换组件</button>
</template>
<script>
import A from "./components/CAA.vue"
import B from "./components/CBB.vue"
export default{
data(){
return {
tapComponent:"A"
}
},
components:{
A,
B,
},
methods:{
changeHandle(){
//如果是A就切换为B,是B就切换为A
this.tapComponent = this.tapComponent == "A" ? "B" :"A"
}
}
}
</script>
a.vue:
<template>
<h3>我是A界面</h3>
</template>
<script>
export default{
}
</script>
b.vue:
<template>
<h3>我是B界面</h3>
</template>
组件保持存活
如果我们使用<component :is>
来进行组件的切换,被切换的组件就会被卸载,我们可以使用<keep-alive>
来让他保持存活。
比如,我们在A组件中更新了数据,又切换组件到B,这个组件A又回到了原始数据。如果我们.
需要保存组件A更新的数据,就需要使用<keep-alive>
这个标签包括<component :is>
。
App.vue:
<template>
<KeepAlive>
<component :is="tapComponent"></component>
</KeepAlive>
<button @click="changeHandle">切换组件</button>
</template>
<script>
import A from "./components/CAA.vue"
import B from "./components/CBB.vue"
export default{
data(){
return {
tapComponent:"A"
}
},
components:{
A,
B,
},
methods:{
changeHandle(){
this.tapComponent = this.tapComponent == "A" ? "B" :"A"
}
}
}
</script>
A:
<template>
<h3>我是A界面</h3>
<p>{{ message }}</p>
<button @click="updateHandle">更新数据</button>
</template>
<script>
export default{
data(){
return {
message:"老数据"
}
},
methods:{
updateHandle(){
this.message = "新数据"
}
}
}
</script>
B:
<template>
<h3>我是B界面</h3>
</template>
异步组件
需要使用上面的代码,只需要修改引入。
同步:按顺序执行。异步:功能同进行。在大型项目中,我们可能会许多的组件,如果是正常的执行,我们需要等待所有组件执行完毕,但是异步执行是用到那个执行那个,这会显著的提高性能。
比如上面的组件切换,我们切换后实际上组件B并没有执行,所以可以把它作为异步组件,等我们点击切换后再执行。
我们只需要修改引入的代码就可以了:
//import B from "./components/CBB.vue" 把这个改为下面的
//异步加载组件
import { defineAsyncComponent } from "vue"
const B = defineAsyncComponent(()=>
import("./components/componentB.vue")
)
<template>
<KeepAlive>
<component :is="tapComponent"></component>
</KeepAlive>
<button @click="changeHandle">切换组件</button>
</template>
<script>
import A from "./components/CAA.vue"
//import B from "./components/CBB.vue"
//异步加载组件
import { defineAsyncComponent } from "vue"
const B = defineAsyncComponent(()=>
import("./components/componentB.vue")
)
export default{
data(){
return {
tapComponent:"A"
}
},
components:{
A,
B,
},
methods:{
changeHandle(){
this.tapComponent = this.tapComponent == "A" ? "B" :"A"
}
}
}
</script>
这样我们点击切换页面的时候,可以发现,网络中又一次新的请求发布
依赖注入
如果有一个多层级嵌套的组件,形成了一颗巨大的组件树,而某个深层的组件需要较远组件中的部分数据,这种情况下如果使用props
传递数据就会十分麻烦。
比如我们像祖宗给孙子传递数据,一般是先传给父组件,再由父组件传给子组件,这样十分麻烦。我们可以使用provide
来传数据,使用inject
来接受数据。
1.创建祖先文件
<template>
<h3>我是祖先</h3>
<A />
</template>
<script>
import A from "./components/AAA.vue"
export default{
data(){
return {
message:"祖先的财产~~"
}
},
provide(){
return{
message:this.message
}
},
components:{
A,
}
}
</script>
2.创建父组件:
<template>
<h3>我是父组件</h3>
<B />
</template>
<script>
import B from "./BBB.vue"
export default {
components:{
B
}
}
</script>
3.创建子组件:
<template>
<h3>我是子组件</h3>
<p>{{ message }}</p>
</template>
<script>
export default {
inject:["message"]
}
</script>
子组件也可以把接受的数据先放到data中,就像这样:
<template>
<h3>我是子组件</h3>
<p>{{ fullMessage }}</p>
</template>
<script>
export default {
inject:["message"],
data(){
return {
fullMessage:this.message
}
}
}
</script>
只能从上到下传递数据
依赖化
我们也可以在整个应用层面提供依赖。就是在main.js页面中。可以使用类似这样的代码:
mian.js
import { createApp } from 'vue'
import App from './App.vue'
//引入组件
import Header from "./pages/Header.vue"
//构造对
const app = createApp(App)
//注册组件,第一个参数是键(名字)
app.component("Header",Header)
//这里
app.provide("golabData","我是全局数据")
//一定要放到最后
app.mount('#app')
任意一个子项目,比如上面的子组件:
<template>
<h3>我是子组件</h3>
<p>{{ fullMessage }}</p>
<p>{{ golabData }}</p>
</template>
<script>
export default {
inject:["message","golabData"],
data(){
return {
fullMessage:this.message
}
}
}
</script>
vue应用
实例
每个vue应用都是通过createApp
函数来创建一个新的应用实例。