vue3快速入门
title: vue3快速入门
author: mzk
authorURL: http://www.yusuf.asia
文章目录
vue安装
npm init vue@latest
来安装vue基础项目
D:\module\vue\vue3学习之路>npm init vue@latest
Vue.js - The Progressive JavaScript Framework
√ Project name: ... vue-base 不能有大写,设置名字
√ Add TypeScript? ... No / Yes
√ Add JSX Support? ... No / Yes
√ Add Vue Router for Single Page Application development? ... No / Yes
√ Add Pinia for state management? ... No / Yes
√ Add Vitest for Unit Testing? ... No / Yes
√ Add an End-to-End Testing Solution? » No
√ Add ESLint for code quality? ... No / Yes
Scaffolding project in D:\module\vue\vue3学习之路\vue-base...
Done. Now run:
cd vue-base
npm install
npm run dev
安装与运行
npm install
npm run dev
创建完成目录结构
.vscode Vscode工具的配置文件夹
node_modules Vue项目的运行依赖文件夹
public 资源文件夹(浏览器图标)
src 源码文件夹
.gitignore git忽略文件
index.html 入口HTML文件
package.json 信息描述文件
README.md 注释文件
vite.config.js Vue配置文件
Vue使用一种基于HTML的模板语法,使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上。所有的Vue模板都是语法层面合法的HTML,可以被符合规范的浏览器和HTML解析器解析。
文本插值
最基本的数据绑定形式是文本插值,他使用的是mustache语法,(即双大括号)
<script>
export default {
data(){
return {
info:"神奇的语法"
}
}
}
</script>
<template>
<h1> 模板语法</h1>
<p>
{{ info }}
</p>
</template>
使用JavaScript表达式
每个绑定仅支持单一表达式,也就是一段能够被求值的JavaScript代码。一个简单的判断方法是是否可以合法地写在return 后面。一定要有返回值
<script>
export default {
data(){
return {
info:"神奇的语法",
ok:true,
num:10
}
}
}
</script>
<template>
<h1> 模板语法</h1>
<p> {{ info }}</p>
<p>{{ ok ? 'yes':'no' }}</p>
<p>{{ num + 1 }}</p>
</template>
原始HTML
双大括号将会将数据插值为纯文本,而不是HTML。若想插入HTML,你需要使用v-html指令
<script>
export default {
data(){
return {
mzk:"<a href='https:www.baidu.com'>百度一下</a>"
}
}
}
</script>
<template>
<h1> 模板语法</h1>
<p v-html="mzk"></p>
</template>
属性绑定
双大括号不能在HTML attributes 中使用。想要响应式地绑定一个attribute,应该使用指令v-bind
<script>
export default {
data(){
return {
msg :"active"
}
}
}
</script>
<template>
<div v-bind:class="msg"></div>
</template>
1v-bind
指令指示Vue 将元素的 id
attribute 与组件的 dynamicld 属性保持一致。如果绑定的但是 null 或者 undefined,那么该attribute将会从渲染的元素上移除
简写
因为v-bind 非常常用,我们提供了特定的简写语法
<div :class="msg"></div>
布尔型 Attribute
布尔型 attribute依据 true / false值来决定attribute 是否应该存在于该元素上,disabled 就是最常见的例子之一
<script>
export default {
data(){
return {
isButtonDisabled:false,
}
}
}
</script>
<template>
<button :disabled="isButtonDisabled">button</button>
</template>
动态绑定多个值
如果你有像这样的一个包含多个attribute的JavaScript对象
<script>
export default {
data(){
return {
objectOfAttrs:{
id :"appid",
class :"appclass"
}
}
}
}
</script>
<template>
<h1> 模板语法</h1>
<div v-bind="objectOfAttrs"></div>
</template>
条件渲染
v-if
v-else
v-else-if
<template>
<h3 v-if="flag">turn</h3>
<h3 v-else></h3>
<div v-if="tyep === 'A' ">A</div>
<div v-else-if="tyep ==='B'">B</div>
</template>
<script>
export default {
data() {
return{
flag:true,
tyep :'B'
}
}
}
</script>
v-show
另一个可以用来按条件显示一个元素的指令是v-show。其用法基本一样
<div v-show="flag">hahahaha</div>
v-if VS v-show
- v-if 是“真实的”按条件渲染,因为它确保了在切换时,条件区块事件监听器和子组件都会被销毁与重建。
- v-if 也是惰性的:如果在初次渲染时条件值为false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。
- 相比之下,v-show 简单许多,元素无论初始条件如何,始终会被渲染,只有CSS display属性会被切换。
- 总的来说,v-if有更高的切换开销,而v-show有更高的初始渲染开销。因此,如果需要频繁切换,则使用较好v-show;如果在运行时绑定条件很少改变,则v-if会更合适
列表渲染
我们可以使用v-for指令基于一个数组来渲染一个列表。v-for 指令的值是使用item in items 形式的特殊语法,其中items 是源数据的数组,而item 是迭代项的别名
<template>
<h2>hhhh</h2>
<p v-for="i in personList" :key="i.name">
{{ i.age }}
{{ i.name }}
</p>
</template>
<script>
export default {
data() {
return{
// flag:true,
// tyep :'B',
personList:[
{name:'mzk',age:'18'},
{name:'mzk',age:'18'},
{name:'mzk',age:'18'},
{name:'mzk',age:'18'},
{name:'mzk',age:'18'}
]
}
}
}
</script>
v-for 也支持使用可选的第二个参数表示当前项的位置索引
<p v-for="(item,i) in personList" :key="item.name">
{{ i.age }} </p>
你也可以使用of作为分隔符来替代in,这更接近JavaScript的迭代器语法
<div v-for="item of items"></div>
与对象v-for你也可以使用来遍历一个对象的所有属性
<template>
<div>
<p v-for="(value) in obj" :key="value.name">{{ value }}</p>
</div>
</template>
<script>
export default {
data() {
return{
obj:{
name:'mzk',
age:'19',
address:'changsha'
}
}
}
}
</script>
通过key管理状态
Vue默认按照“就地更新”的策略来更新通过 v-for 渲染的元素列表。当数据项的顺序改变时,Vue不会随之移动DOM元素的顺序,而是就地更新每个元素,确保它们在原本指定的索引位置上渲染。
为了给Vue一个提示,以便它可以跟踪每个节点的标识,从而重用和重新排序现有的元素,你需要为每个元素对应的块提供一个唯一的 key attribute:
温馨提示
在这里是一个通过 v-bind 绑定的特殊attribute
key推荐在任何可行的时候为v-for 提供一个key attribute
绑定的值期望是一个基础类型的值,例如字符串或number类型key
事件处理
我们可以使用v-on指令(简写为)来监听DOM事件,并在事件触发时执行对应的JavaScript。用法:或on:click=“methodName”@click=“handler”
事件处理器的值可以是
- 内联事件处理器:事件被触发时执行的内联JavaScript 语句(与 onclick 类似)
- 方法事件处理器:一个指向组件上定义的方法的属性名或是路径
内联事件处理器
内联事件处理器通常用于简单场景
<template>
<p>事件处理器</p>
<button @click="count++">add + 1</button>
<p>{{ count }}</p>
</template>
<script>
export default{
data (){
return {
count:0
}
}
}
</script>
方法事件处理器
<template>
<p>事件处理器</p>
<button @click="addcount">add + 1</button>
<p>{{ count }}</p>
</template>
<script>
export default{
data (){
return {
count:0
}
},
//所有的方法放这里
methods:{
addcount(){
this.count+=1
}
}
}
</script>
事件类型总结:
当然,以下是对JavaScript中常见事件类型的总结:
鼠标事件(Mouse Events)
click
:点击事件dblclick
:双击事件mouseover
:鼠标移入元素事件mouseout
:鼠标移出元素事件mousemove
:鼠标移动事件mousedown
:鼠标按下事件mouseup
:鼠标松开事件
键盘事件(Keyboard Events)
keydown
:键盘按下事件keyup
:键盘松开事件keypress
:按键事件
表单事件(Form Events)
submit
:表单提交事件input
:输入事件change
:值变化事件focus
:获得焦点事件blur
:失去焦点事件
窗口事件(Window Events)
load
:页面加载完成事件resize
:窗口大小调整事件scroll
:滚动事件unload
:页面关闭事件
触摸事件(Touch Events)
touchstart
:触摸开始事件touchmove
:触摸移动事件touchend
:触摸结束事件touchcancel
:触摸取消事件
事件处理相关
addEventListener
:添加事件监听器removeEventListener
:移除事件监听器preventDefault
:阻止默认行为stopPropagation
:停止事件冒泡
这些只是JavaScript中常见的事件类型,根据需要选择适当的事件来实现交互和响应逻辑。还有其他类型的事件,如拖放事件、媒体事件等,可以根据具体情况进一步探索。
事件参数
事件参数可以获取event对象和通过事件传递数据
获取event 对象
<template>
<p>事件处理器</p>
<button @click="addcount">add + 1</button>
<p>{{ count }}</p>
</template>
<script>
export default{
data (){
return {
count:0
}
},
//所有的方法放这里
methods:{
addcount(event){
//event 对象 。 vue 中的enent 对象就是原生js的对象
event.target.innerHTML = "Add" + this.count
this.count+=1
}
}
}
</script>
传递参数
<template>
<h2>事件传参</h2>
<p @click="getNameHandler(i)" v-for="(i,index) in word" :key="index">{{ i }}</p>
</template>
<script>
export default{
data (){
return {
word:['zhanagsan','lisi','lili','mzk']
}
},
//所有的方法放这里
methods:{
getNameHandler(name,event){
//此时此刻event 在所用形参的最后添加
console.log(name)
}
}
}
</script>
事件修饰符
event.preventDefault()或event.stopPropagation()是很常见的。尽管我们可以直接在方法内调用,但在处理事件时调用如果方法能更专注于数据逻辑而不用去处理DOM事件的细节会更好
为解决这一问题,Vue为v-on提供了事件修饰符,常用有以下几个:
- .stop
- .prevent
- .once
- .enter
具体参考
地址:https://cn.vuejs.org/guide/essentials/event-handling.html#event-modifiers
阻止默认事件
<template>
<h2>事件修饰符</h2>
<!--@click.prevent 就是阻止默认事件调用 -->
<a @click.prevent="clickHandle" href="www.yusuf.asia">mzk个人网站</a>
<div @click="clickDiv">
<p @click="p">测试冒泡</p>
</div>
</template>
<script>
export default {
data (){
return {
}
},
methods:{
clickHandle(e){
// e.preventDefault();//阻止调用默认事件
console.log('点击了')
},
clickDiv(){
console.log("DIV")
},
p(e){
e.stopPropagation();//阻止子元素冒泡的父元素
console.log("PP")
}
}
}
</script>
数组变化的侦测
变更方法
Vue能够侦听响应式数组的变更方法,并在它们被调用时触发相关的更新。这些变更方法包括:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
<template>
<h2>数组侦听变化</h2>
<button @click="addListHandle">添加数据</button>
<div>
<p v-for="(value,index) in word" :key="index">{{ value }}</p>
</div>
<button @click="concatHandle">合并数组</button>
<h3>数组1</h3>
<p v-for="(item,index) in num1" :key="index">{{ item }}</p>
<h3>数据2</h3>
<p v-for="(item,index) in num2" :key="index">{{ item }}</p>
</template>
<script>
export default{
data(){
return{
word:['lili','zhangsna','lisi','mzk'],
num1 :[1,2,3,4,5],
num2 :[6,7,8,9,10]
}
},
methods:{
addListHandle(){
// 引起ui自动更新
// this.word.push('wdj'),
// 不会引起ui自动更新
this.word = this.word.concat(['putlist'])
},
concatHandle(){
this.num1=this.num1.concat(this.num2)
}
}
}
</script>
替换一个数组
变更方法,顾名思义,就是会对调用它们的原数组进行变更。相对地,也有一些不可变(immutable)方法,例如fiter(),concat()和slice(),这些都不会更改原数组,而总是返回一个新数组。当遇到的是非变更方法时,我们需要将旧的数组替换为新的
代码参考上面
计算属性
模板中的表达式虽然方便,但也只能用来做简单的操作。如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。因此我们推荐使用计算属性来描述依赖响应式状态的复杂逻辑
应用计算属性
<template>
<h2>计算属性</h2>
{{ show.name }}
{{ show.content }}
<p>{{ mzkcotent }}</p>
</template>
<script>
export default {
data(){
return {
show:{
name:"mzk",
content:['aa','bb','cc']
}
}
},
//计算属性
computed:{
mzkcotent(){
return this.show.content.length > 0 ? 'yes' :'no'
}
}
}
</script>
计算属性缓存vs方法
你可能注意到我们在表达式中像这样调用一个函数也会获得和计算属性相同的结果
重点区别:
计算属性:**计算属性值会基于其响应式依赖被缓存。**一个计算属性仅会在其响应式依赖更新时才重新计算
方法:方法调用总是会在重渲染发生时再次执行函数
class绑定
数据绑定的一个常见需求场景是操纵元素的CSS class列表,因为class是attribute,我们可以和其他attribute一样使用v-bind将它们和动态的字符串绑定。但是,在处理比较复杂的绑定时,通过拼接生成字符串是麻烦且易出错的。因此,Vue专门为class的v-bind用法提供了特殊的功能增强。除了字符串外,表达式的值也可以是对象或数组
绑定对象-多个绑定
<template>
<button @click="forActive">切换样式绑定</button>
<h1 :class="{'active':isActive,'text-danger':isDanger}">class 样式绑定222</h1>
<h1 :class="classObject">class 样式绑定33</h1>
</template>
<script>
export default {
data (){
return {
myclass:'demo',
isActive:true,
isDangen:true,
classObject:{
'active':true,
'text-danger': true
}
}
},
methods:{
forActive(){
this.isActive = !this.isActive
}
}
}
</script>
<style>
.active{
font-size: 50px;
}
.text-danger{
color:red;
}
</style>
如果你也想在数组中有条件地渲染某个class,你可以使用三元表达式:
<div :class="[isActive ? 'active':']">isActive</div>2
数组和对象
<div :class="[{ 'active':isActive }, errorClass]"></div>
温馨提示
数组和对象嵌套过程中,只能是数组嵌套对象,不能反其道而行
sytle绑定
数据绑定的一个常见需求场景是操纵元素的CSS style列表,因为 style是attribute,我们可以和其他attribute 一样使用 v-bind将它们和动态的字符串绑定。但是,在处理比较复杂的绑定时,通过拼接生成字符串是麻烦且易出错的。因此,Vue专门为style的v-bind 用法提供了特殊的功能增强。除了字符串外,表达式的值也可以是对象或数组
绑定对象
<template>
<h2 :style="{color:activeColor,fontSize:activeSize}">style 绑定</h2>
</template>
<script>
export default {
data (){
return {
activeColor:'red',
activeSize:'100px',
//也可以这么来写
styleObject:{
color:'red',
fontSize :'200px'
}
}
}
}
</script>
还有绑定数组,不建议用
侦听器
侦听数据的变化
选项在每次响应式属性发生变化时触发一个函数我们可以使用watch
<template>
<h2>侦听器</h2>
<p>{{ message }}</p>
<button @click="update">改变数据</button>
</template>
<script >
export default {
data(){
return{
message:"Hello"
}
},
methods:{
update(){
this.message = '早'
}
},
watch:{
//newvalue 就是改变之后的数据
//lodValue 改变之前的数据
//函数名必须和侦听的对象名保持一致
message(newValue,lodValue){
console.log('新数据',newValue)
console.log(lodValue)
}
}
}
</script>
表单输入绑定
在前端处理表单时,我们常常需要将表单输入框的内容同步给JavaScript 中相应的变量。手动连接值绑定和更改事件监听器可能会很麻烦,v-model指令帮我们简化了这一步骤
v-model 双向绑定
修饰符
也提供了修饰符:.number、v-model.lazy、.trim
.lazy 默认情况下,会在每次 input 事件后更新数据。你可以添加修饰符来改为在每次事件后更llazychangev-model新数据
<template>
<form>
<input type="text" v-model.lazy="message">
</form>
<p>{{ message }}</p>
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
</template>
<script >
export default {
data() {
return {
message :'',
checked:false
}
},
}
</script>
模板应用
虽然Vue的声明性渲染模型为你抽象了大部分对DOM的直接操作,但在某些情况下,我们仍然需要直接访问底层DOM元素。要实现这一点,我们可以使用特殊的 ref
attribute
挂载结束后引用都会被暴露在之上this.$refs
<template>
<div ref="container" class="container">容器</div>
<button @click="getElementHandle">获取元素</button>
</template>
<script>
/**
* 内容改变:{{ 模板语法 }}
* 属性改变:v-bind:指令
* 事件改变 v-on @click
* 如果没有特别的需求,不要操作DOM
*/
export default{
data() {
return {
}
},
methods:{
getElementHandle(){
// 这个时候获取的就是<div ref="container" class="container">容器</div> 本身
console.log(this.$refs.container.innerHTML = '哈哈哈')
}
}
}
</script>
组件组成
组件最大的优势就是可复用性
当使用构建步骤时,我们一般会将Vue组件定义在一个单独的.wue 文件中,这被叫做单文件组件(简称 SFC)
组件组成结构
<template>
<div></div>
</template>
<script>
export default {}
</script>
<style>
</style>
组件的引用
<template>
<!-- 第三步显示组件 -->
<myComponent/>
</template>
<script >
//第一步-引入组件
import myComponent from './components/componentsDemo.vue'
export default{
//第二部 注入组件
components:{
myComponent:myComponent
}
}
</script>
<style>
</style>
在Vue单文件组件中,样式中的scoped的作用是:
<style scoped>
<!--scoped:让当前样式只在当前组件中生效-->
</style>
组件的嵌套关系
组件允许我们将UI划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。在实际应用中,组件常常被组织成层层嵌套的树状结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FOgcMvUt-1687959737095)(C:\Users\kangz\AppData\Roaming\Typora\typora-user-images\image-20230621130216909.png)]
这和我们嵌套HTML元素的方式类似,Vue实现了自己的组件模型,使我们可以在每个组件内封装自定义内容与逻辑
创建组件及应用关系
Header
<template>
<h3>Hander</h3>
</template>
<style scoped>
h3{
width: 100%;
height: 100px;
border: 5px solid #999;
text-align: center;
line-height: 100px;
box-sizing: border-box;
}
</style>
Main
<template>
<div class="main">
<h3>Main</h3>
<Article/>
<Article/>
</div>
</template>
<script>
import Article from './Article.vue'
export default {
components:{
Article
}
}
</script>
<style scoped>
.main{
float: left;
width: 70%;
height:600px;
border:5px solid #999;
box-sizing: border-box;
/* border-top: 0px; */
}
</style>
Aside
<template>
<div class="aside">
<h3>Aside</h3>
<Item />
<Item />
<Item />
</div>
</template>
<script>
import Item from './Item.vue'
export default {
components:{
Item
}
}
</script>
<style scoped>
.aside{
float: right;
width: 30%;
height: 600px;
border: 5px solid #999;
box-sizing: border-box;
/* border-left: 0; */
}
</style>
Article
<template>
<h3>Article</h3>
</template>
<style scoped>
h3{
width: 80%;
margin: 0 auto;
text-align: center;
line-height: 100px;
box-sizing: border-box;
margin-top: 50px;
background: #999;
}
</style>
Item
<template>
<h3>Item</h3>
</template>
<style scoped>
h3{
width: 80%;
margin: 0 auto;
text-align: center;
line-height: 100px;
box-sizing: border-box;
margin-top: 10px;
background: #999;
}
</style>
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3LrfadCU-1687959737096)(image-20230621134232192.png)]
组件的注册方式
一个Vue组件在使用前需要先被“注册”,这样Vue才能在渲染模板时找到其对应的实现。组件注册有两种方式:全局注册和局部注册
全局注册 main.js
// import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import Hander from './pages/Hander.vue'
const app = createApp(App)
//必须在这中间写组件的注册
app.component("hander",Hander)//第一个是名字,第二个是组件
app.mount('#app')
局部注册
全局注册虽然很方便,但有以下几个问题:
-
全局注册,但并没有被使用的组件无法在生产打包时被自动移除(也叫“tree-shaking”)。如果你全局注册了一个组件,即使它并没有被实际使用,它仍然会出现在打包后的JS文件中
-
全局注册在大型项目中使项目的依赖关系变得不那么明确。在父组件中使用子组件时,不太容易定位子组件的实现。和使用过多的全局变量一样,这可能会影响应用长期的可维护性
局部注册需要使用components
选项
组件传递数据_Props
组件与组件之间不是完全独立的,而是有交集的,那就是组件与组件之间是可以传递数据的传递数据的解决方案就是props
<template>
<h3>Parent</h3>
<Child title="Parent数据" />
</template>
<script>
import Child from './Child.vue'
export default {
data(){
return {
}
},
components:{
Child
}
}
</script>
Child.vue
<template>
<h3>Child</h3>
<p>{{ title }}</p>
</template>
<script>
export default {
data(){
return {
}
},
props:["title"]
}
</script>
动态数据
<template>
<h3>Parent</h3>
<Child :title="message" />
</template>
<script>
import Child from './Child.vue'
export default {
data(){
return {
message :"parent111胡辣汤"
}
},
components:{
Child
}
}
</script>
注意事项
props
传递数据,只能从父级传递到子级,不能反其道而行之
传递组件多种数据类型
通过props
传递数据,不仅可以传递字符串类型的数据,还可以是其他类型,例如:数字、对象、数组等但实际上任何类型的值都可以作为props
的值被传递
任何类型都能传递
组件传递Props校验
vue 组件可以更细致的声明对传入的Props的校验要求
校验 默认值 必选项
A
<template>
<h3>AAA</h3>
<BBB :title="info" :names="names"/>
</template>
<script>
import BBB from './ComponentB.vue'
export default {
data(){
return {
info:'123123123',
names:[1,2,3,4,5]
}
},
components:{
BBB
}
}
</script>
B
<template>
<h3>BBBB</h3>
<p>{{ title }}</p>
<p v-for="(item,index) in names" :key="index">{{ item }}</p>
</template>
<script>
export default {
data(){
return {
}
},
props:{
title:{
type:[String,Number],
default:'默认值',//设置一个默认值
required:true//设置必选项
},
//数字和字符串可以直接default,但是如果是数组和对象,必须通过工厂函数返回默认值
names:{
tpyt:Array,
default(){
return [9,9,9,9,9]
}
}
}
}
</script>
警告
props是只读的!
组件事件
在组件的模板表达式中,可以直接使用 $emit
方法
触发自定义事件触发自定义事件的目的是组件之间传递数据
<template>
<h3>组件事件</h3>
<Ch @someEvent="getHandle" />
<p5>{{ msg }}</p5>
</template>
<script>
import Ch from './Ch.vue'
export default {
data (){
return {
msg:''
}
},
components:{
Ch
},
methods:{
getHandle(data){
console.log('触发了',data);
this.msg = data;
}
}
}
</script>
<template>
<h3>Ch</h3>
<button @click="clickEventHandle">传递数据</button>
</template>
<script>
export default {
methods:{
clickEventHandle(){
console.log('hhh')
//自定义 事件
this.$emit("someEvent","ch数据文本")
}
}
}
</script>
组件之间传递数据的方案:
父传子:props
子传父:自定义事件(this.$emit)
组件事件配合v-model使用
如果是用户输入,我们希望在获取数据的同时发送数据配合v-model来使用
父
<template>
<h1>Mian</h1>
<br/>
<Search @searchEvent="getsearch"/>
<b>{{ info }}</b>
</template>
<script>
import Search from './Search.vue'
export default{
data() {
return {
info:''
}
},
components:{
Search
},
methods:{
getsearch(data){
this.info = data
}
}
}
</script>
子
<template>
<h1>Search</h1>
搜索<input type="text" v-model="search"/>
</template>
<script>
export default{
data() {
return {
search:""
}
},
watch:{
search(newvalue,oldValue){
this.$emit("searchEvent",newvalue)
}
}
}
</script>
组件数据传递
我们之前讲解过了组件之间的数据传递,props
和自定义事件两种方式
-
props
:父传子 -
自定义事件:子传父
除了上述的方案,props
也可以实现子传父
<template>
<h2>aaa</h2>
<B title="标题" :onEvent="dataFn" />
<p>父元素{{ info }}</p>
</template>
<script>
import B from './B.vue'
export default{
data() {
return {
info:''
}
},
components:{
B
},
methods:{
dataFn(data){
console.log(data)
this.info =data
}
}
}
</script>
<template>
<h2>bbbb</h2>
<p>{{ title }}</p>
<p>{{ onEvent("今天天气真好") }}</p>
</template>
<script>
export default{
data() {
return {
}
},
props:{
title:String,
onEvent:Function
}
}
</script>
透传 Attributes
“透传attribute”指的是传递给一个组件,却没有被该组件声明为props或emits的attribute或者
v-on
事件监听器。最常见的例子就是class
、style
和id
当一个组件以单个元素为根作渲染时,透传的attribute会自动被添加到根元素上
禁用Attributes继承
<script>
export default {
inheritAttrs:false
</script>
插槽Slots
我们已经了解到组件能够接收任意类型的JavaScript值作为但组件要如何接收模板内容呢?在某些场props,景中,我们可能想要为子组件传递一些模板片段,让子组件在它们的组件中渲染这些片段
<slot>
元素是一个插槽出口(slot outlet),标示了父元素提供的插槽内容(slot content)将在哪里被渲染
在父vue组件中
<slots>
<h4>插槽标题</h4>
<p>插槽内容</p>
</slots>
///子
<template>
<h2>插槽基础</h2>
<slot></slot>
</template>
<script>
</script>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o2qm806g-1687959737097)(image-20230621161720608.png)]
插槽出口关键字slot
渲染作用域
插槽内容可以访问到父组件的数据作用域,因为插槽内容本身是在父组件模板中定义的
<template>
<SlotTow>
<h1>{{ mas }}</h1>
</SlotTow>
</template>
<script >
import SlotTow from './components/SlotsTow.vue'
export default{
//第二部 注入组件
components: {
slots,SlotTow
},
data() {
return {
mas:"插槽内容"
}
},
}
</script>
<style>
</style>
默认内容
在外部没有提供任何内容的情况下,可以为插槽指定默认内容
<template>
<h3>Slots</h3>
<slot>默认值</slot>
</template>
具名插槽
<SlotTow>
<template v-slot:header>
<h1>{{ mas }}</h1>
</template>
<template v-slot:main>
<h1>第二个内容</h1>
</template>
</SlotTow>
---------------------------------------------------------------
<template>
<h3>Slots</h3>
<slot name="header">默认值</slot>
<p>分隔---------------------</p>
<slot name="main">默认值</slot>
</template>
v-slot
有对应的简写#,因此<template v-slot:header>
可以简写为<template #header>。其意思就是
“将这部分模板片段传入子组件的 header 插槽中”
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dmQH61QS-1687959737097)(image-20230621163922201.png)]
在某些场景下插槽的内容可能想要同时使用父组件域内和子组件域内的数据。要做到这一点,我们需要一种方法来让子组件在渲染时将一部分数据提供给插槽
我们也确实有办法这么做!可以像对组件传递props那样,向一个插槽的出口上传递attributes
<SlotAction v-slot="slotProps">
<template >
<p>{{ mas}} - {{ slotProps.info }}</p>
</template>
</SlotAction>
-----------------------------------------------------------------------------
<template>
<h3>Slots</h3>
<slot name="header" :info="info">默认值</slot>
</template>
<script>
export default{
data() {
return {
info :'子组件数据!!'
}
},
}
</script>
具名插槽传递数据
<template>
<h3>Slots</h3>
<slot name="header" :info="info">默认值</slot>
<slot name="main" :job="jobName"></slot>
</template>
<script>
export default{
data() {
return {
info :'子组件数据!!',
jobName:'大数据'
}
},
}
</script>
<SlotAction>
<template #header="slotProps">
<p>{{ mas}} - {{ slotProps.info }}</p>
</template>
<template #main="slotProps">
<p>{{ slotProps.job }}</p>
</template>
</SlotAction>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zmOWdZt2-1687959737098)(image-20230621165606005.png)]
组件的生命周期
每个Vue组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到DOM,以及在数据改变时更新DOM。在此过程中,它也会运行被称为生命周期钩子的函数,让开发者有机会在特定阶段运行自己的代码
生命周期示意图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TgIwKwea-1687959737098)(vue生命周期图示.png)]
<template>
<h1>组件的生命周期</h1>
</template>
<script>
/** 生命周期函数
* 创建期: beforeCreate created
* 挂载期: beforeMounte mounted
* 更新期: beforeUpdate updated
* 销毁期: beforeUnmount Unmountd
*
*/
export default{
beforeCreate(){
console.log('组件创建之前')
},
created() {
console.log('组件创建之后')
},
beforeMount() {
console.log('组件渲染之前')
},
mounted(){
console.log('组件渲染之后')
},
}
</script>
<style scoped>
</style>
生命周期的引用
组件的生命周期会随着我们对vue
的了解越多,也会越来越重要,这里我们先讲两个常用的应用常见:
- 通过ref获取元素DOM结构
- 模拟网络请求渲染数据
通过ref获取元素DOM结构
<template>
<h2>组件生命周期函数应用</h2>
<p ref="name">马赞康</p>
</template>
<script>
export default{
beforeMount(){
console.log(this.$refs.name)
},
mounted(){
console.log(this.$refs.name)
}
}
</script>
模拟网络请求渲染数据
<template>
<h2>组件生命周期函数应用</h2>
<p ref="name">马赞康</p>
<ul>
<li v-for="(item,index) in banner" :key="index">
<p> {{ item.title }}</p>
<p> {{ item.content }}</p>
</li>
</ul>
</template>
<script>
export default{
data() {
return {
banner:[]
}
},
mounted(){
//模拟网络请求
this.banner = [
{
"title":"我在爱尔兰",
"content":"爱尔兰"
},
{
"title":"我在东京",
"content":"东京"
}
];
console.log(this.$refs.name)
},
beforeMount(){
console.log(this.$refs.name)
},
}
</script>
动态组件
有些场景会需要在两个组件间来回切换,不如Tab页面
A,B两个组件
<template>
<!-- <userTest/> -->
<!-- <A />
<B /> -->
<component :is="tabComponment"></component>
<button @click="changeHandle">切换组件</button>
</template>
<script>
import userTest from './components/userComponents.vue'
import A from './components/ComponentAA.vue'
import B from './components/ComponentBB.vue'
export default{
components:{
userTest,
A,
B
},
data() {
return {
tabComponment:"A"
}
},
methods:{
changeHandle(){
this.tabComponment = this.tabComponment == "A" ? "B" : "A"
}
}
}
</script>
组件保持存活
当使用<component :is="...">
来在多个组件间作切换时,被切换掉的组件会被卸载。我们可以通过<keep-alive>
组件强制被切换掉的组件仍然保持“存活”的状态
组件被卸载,组件保持存活
<template>
<!-- <userTest/> -->
<!-- <A />
<B /> -->
<keep-alive>
<component :is="tabComponment"></component>
</keep-alive>
<button @click="changeHandle">切换组件</button>
</template>
<script>
import userTest from './components/userComponents.vue'
import A from './components/ComponentAA.vue'
import B from './components/ComponentBB.vue'
export default{
components:{
userTest,
A,
B
},
data() {
return {
tabComponment:"A"
}
},
methods:{
changeHandle(){
this.tabComponment = this.tabComponment == "A" ? "B" : "A"
}
}
}
</script>
<template>
<h3>AAA</h3>
<p>{{ message }}</p>
<button @click="updateHandle">切换更新数据</button>
</template>
<script>
export default{
data() {
return {
message:"老数据"
}
},
beforeUnmount(){
console.log('组件卸载之前')
},
unmounted(){
console.log("组件卸载之后")
},
methods:{
updateHandle(){
this.message = "新数据"
}
}
}
</script>
<template>
<h3>BBB</h3>
</template>
<script>
</script>
异步组件
在大型项目中,我们可能需要拆分应用为更小的块,并仅在需要时再从服务器加载相关组件。Vue提供了defineAsyncComponent
方法来实现此功能
<template>
<!-- <userTest/> -->
<!-- <A />
<B /> -->
<keep-alive>
<component :is="tabComponment"></component>
</keep-alive>
<button @click="changeHandle">切换组件</button>
</template>
<script>
import userTest from './components/userComponents.vue'
import A from './components/ComponentAA.vue'
// import B from './components/ComponentBB.vue'
import { defineAsyncComponent } from 'vue'
//异步加载组件
const B = defineAsyncComponent(()=>
import('./components/ComponentBB.vue')
)
export default{
components:{
userTest,
A,
B
},
data() {
return {
tabComponment:"A"
}
},
methods:{
changeHandle(){
this.tabComponment = this.tabComponment == "A" ? "B" : "A"
}
}
}
</script>
依赖注入
通常情况下,当我们需要从父组件向子组件传递数据时,会使用props
。想象一下这样的结构:有一些多层级嵌套的组件,形成了一颗巨大的组件树,而某个深层的子组件需要一个较远的祖先组件中的部分数据。在这种情况下,如果仅使用props 则必须将其沿着组件链逐级传递下去,这会非常麻烦
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yn3q5MDt-1687959737099)(image-20230621230053326.png)]
这一问题被称为"prop 逐级透传”
provide
和 inject
可以帮助我们解决这一问题。一个父组件相对于其所有的后代组件,会作为依赖提供者。任何后代的组件树,无论层级有多深,都可以注入由父组件提供给整条链路的依赖。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U45JQ03F-1687959737099)(image-20230621231022132.png)]
<template>
<h1>老祖宗</h1>
<Parent/>
</template>
<script >
import Parent from './zhuru/parent.vue'
export default{
components:{
Parent
},
// provide:{
// message:"{{}}"
// },
data() {
return {
message:"我是爷爷"
}
},
provide(){
return { message:this.message}
}
}
</script>
<template>
<h1>parent</h1>
<Child/>
</template>
<script>
import Child from './child.vue'
export default{
components:{
Child
}
}
</script>
<template>
<h2>Child</h2>
<p>{{ fullMassage}}</p>
</template>
<script>
export default{
inject:["message"],
data(){
return{
fullMassage :this.message
}
}
}
</script>
温馨提示
provide
和inject
由上到下的传递,不能反其道而行之
除了在一个组件中提供依赖,我们还可以在整个应用层面提供依赖,main.js
// import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.provide("goladData","在整个应用层面提供依赖")
app.mount("#app")
vue应用
应用实例
每个Vue应用都是通过createApp函数创建一个新的应用实例
根组件
我们传入 createApp的对象实际上是一个组件,每个应用都需要一个“根组件”,其他组件将作为其子组件。
// import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
//app就是vue的实例对象
//在一个vue项目中,只有一个vue实例对象
const app = createApp(App)
//app就是根组件对象
app.mount("#app")
挂载应用
应用实例必须在调用了.mount()
方法后才会渲染出来。该方法接收一个“容器”参数,可以是一个实际的DOM元素或是一个CSS选择器字符串
app.mount("#app") 这个就是把一个app挂载到
注意:
浏览器可执行文件:
HTML
CSS
JavaScript
Image
构建工具:Webpack vite
vue文件只是构建,都要编译成浏览器的可实现文件才能运行
公共资源
assets 文件夹的作用就是存放公共资源,例如:图片、公共CSS或者字体图标等在src目录下的
着组件链逐级传递下去,这会非常麻烦
[外链图片转存中…(img-yn3q5MDt-1687959737099)]
这一问题被称为"prop 逐级透传”
provide
和 inject
可以帮助我们解决这一问题。一个父组件相对于其所有的后代组件,会作为依赖提供者。任何后代的组件树,无论层级有多深,都可以注入由父组件提供给整条链路的依赖。
[外链图片转存中…(img-U45JQ03F-1687959737099)]
<template>
<h1>老祖宗</h1>
<Parent/>
</template>
<script >
import Parent from './zhuru/parent.vue'
export default{
components:{
Parent
},
// provide:{
// message:"{{}}"
// },
data() {
return {
message:"我是爷爷"
}
},
provide(){
return { message:this.message}
}
}
</script>
<template>
<h1>parent</h1>
<Child/>
</template>
<script>
import Child from './child.vue'
export default{
components:{
Child
}
}
</script>
<template>
<h2>Child</h2>
<p>{{ fullMassage}}</p>
</template>
<script>
export default{
inject:["message"],
data(){
return{
fullMassage :this.message
}
}
}
</script>
温馨提示
provide
和inject
由上到下的传递,不能反其道而行之
除了在一个组件中提供依赖,我们还可以在整个应用层面提供依赖,main.js
// import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.provide("goladData","在整个应用层面提供依赖")
app.mount("#app")
vue应用
应用实例
每个Vue应用都是通过createApp函数创建一个新的应用实例
根组件
我们传入 createApp的对象实际上是一个组件,每个应用都需要一个“根组件”,其他组件将作为其子组件。
// import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
//app就是vue的实例对象
//在一个vue项目中,只有一个vue实例对象
const app = createApp(App)
//app就是根组件对象
app.mount("#app")
挂载应用
应用实例必须在调用了.mount()
方法后才会渲染出来。该方法接收一个“容器”参数,可以是一个实际的DOM元素或是一个CSS选择器字符串
app.mount("#app") 这个就是把一个app挂载到
注意:
浏览器可执行文件:
HTML
CSS
JavaScript
Image
构建工具:Webpack vite
vue文件只是构建,都要编译成浏览器的可实现文件才能运行
公共资源
assets 文件夹的作用就是存放公共资源,例如:图片、公共CSS或者字体图标等在src目录下的