Vue3
""
Vue3介绍
1. vue3与vue2区别
2. vue3官网
创建Vue3项目
1.使用 vite 创建
$ npm init vite@latest
之后,输入项目名称、选择框架、选择语言。
创建完成
完成之后,需要运行一下命令来安装包
$ npm install
2.使用Vue脚手架创建
$ npm init vue@latest
之后,输入项目名称、选择若干个插件或依赖是否需要
创建完成
完成之后,也需要运行一下命令来安装所有包
$ npm install
目录、插件
认识使用vite创建的项目的目录
- 首先就是和v2一样的目录
vite-env-d.ts
:让ts认识.vue后缀index.html
:vite项目的入口文件tsconfig.json
:ts配置文件vite.config.ts
:vite配置文件
在.vue文件中:
<template>
只能写一组<script setup>
只能写一组,不带setup可以多组<style>
可以写多组
插件
Vetur
:写vue2时,使用
Volar
:写vue3时,使用,使用时,关闭Vetur
TypeScript Vue Plugin(Volar)
:vue3的ts插件
vue语法
vue3文件格式
<template>
<div>
{{ msg }}
<button @click="addMsg">点我加1</button>
</div>
</template>
<script setup lang="ts">
const msg:number = 1
const addMsg = ()=>{
msg += 1;
}
</script>
<style scoped>
</style>
语法大部分同vue2一样
v-bind
v-bind有些特殊,可以在style中使用,可以使用script中的变量值
<script setup lang='ts'>
let color = "red"
</script>
<style scoped lang='less'>
.box{
background-color: v-bind(color);
}
</style>
ref 系列
只有被ref修饰的才是响应式对象,就和v2中写在data选项中一样
Vue3的响应式不是Object.defineProperty()
实现的,是通过Proxy
实现的。
使用ref或reactive包裹的对象,变成响应式时,是变成了Proxy对象
ref
基本用法
<script setup lang="ts">
import { ref } from 'vue' //必须先引入ref
let p = ref({ name: 'zs' })
const addMsg = () => {
p.value.name = "lisi"
}
</script>
isRef
<script setup lang="ts">
import { ref,isRef } from 'vue' //依旧先引入
let p1 = ref({ name: 'zs' })
let p2 = {name:'王五'}
const fun = () => {
p1.value.name = "lisi"
console.log(isRef(p1)); //是ref包裹的对象,返回true
console.log(isRef(p2)); //返回false
}
</script>
shallowRef
浅层气ref,对象中的属性变化时,不会对页面刷新响应,只有对象变化了,才会响应页面。
<script setup lang="ts">
import { shallowRef } from 'vue' //先引入
let p1 = shallowRef({ name: 'zs' }) //使用shallowRef
const fun = () => {
p1.value.name = "lisi" //修改属性时,页面不改变
p1.value = { //修改整个对象,页面改变
name:'lisi'
}
}
</script>
💡:ref
和shallowRef
不可一个代码块写,ref会影响shallowRef,使得shallowRef也会更新属性变化时的页面。原理是因为ref的底层调用了triggerRef
<script setup lang="ts">
import { shallowRef } from 'vue'
let p1 = shallowRef({ name: 'zs' }) //使用shallowRef
let p2 = ref({ name: 'zs' }) //使用ref
const fun = () => {
p2.value.name = "lisi" //ref起作用了
p1.value.name = "lisi" //shallowRef被影响了,改变视图
}
</script>
💡:小技巧
可以在Chrome浏览器的调试窗口的设置 找到启用自定义格式设置工具 这样输出Ref的对象时,会直接显示该对象的value属性,省去其他没有用处的内部属性
在标签身上加ref属性,可以直接获取到该dom元素
<template> <!--在标签上添加ref属性,值随意--> <button ref="btn" @click="fun">点我加1</button> </template> <script setup lang="ts"> import { ref,isRef,shallowRef } from 'vue' //定义变量时,必须等于标签上ref的值 //给ref添加泛型,可以让编码工具提示想赢代码 let btn = ref<HTMLElement>() const fun = () => { //btn是真实dom。btn.value是dom元素。 console.log(btn.value?.innerHTML); } </script>
reactive 系列
reactive
基础
reactive和ref一样,但是ref可以包裹所有类型,reactive只能包裹引用类型,如Array,Object,Map,Set
<template>
<div>
<input v-model="obj.name"/>
{{ obj.name }}
</div>
</template>
<script setup lang="ts">
import { ref,reactive } from 'vue'
const obj = reactive({name:'zs'})
</script>
💡:不能直接给reactive创建的对象赋值。如果把reactive对象赋值,就会使reactive失效。
数组可以使用.push()
追加数据
<template>
<div>
<input v-model="obj.name"/>
{{ obj.name }}
</div>
</template>
<script setup lang="ts">
import { ref,reactive } from 'vue'
const obj = reactive({name:'zs'})
let obj1 = {name:"zs"};
obj = obj1; //就是把reactive的对象直接覆盖了,响应式就没了
</script>
readonly
让对象只读,不能修改
<script setup lang="ts">
import { ref,reactive,readonly } from 'vue'
const obj = reactive({name:'zs'})
let read = readonly(obj)
read.name = "lisi" //报错,因为read只读
</script>
shallowReactive
同shallowRef一样,修改深层属性时,数据会改变,但是页面不会改变
to
toRef
提取出来响应式对象
中的某个属性,对于非响应式对象
毫无用处。
使用这个方式提取出来的属性在发生变化时,源数据也会变化。
源数据变化时,提取的属性值,也会发生变化。
toRef(源对象,源对象中的键)
<script setup lang="ts">
import { toRef,reactive } from 'vue'
//使用reactive定义数据
let obj = reactive({
num:1
})
//使用toRef来同步obj的某个属性
let numRef = toRef(obj,'num')
// numRef改变
numRef.value++;
console.log(obj); //obj.num 的值是 2
// obj改变
obj.num = 3;
console.log(numRef);
</script>
toRefs
<script setup lang="ts">
import { toRef,reactive,toRefs } from 'vue'
let obj = reactive({
num:1,
msg:2
})
//使用结构赋值,和toRefs,可以把一个对象中的所有属性变为ref对象
let {num,msg} = toRefs(obj)
console.log(num,msg);
</script>
toRaw
让一个响应式对象,变为非响应式对象
toRaw()
可以返回由 reactive()
、readonly()
、shallowReactive()
或者 shallowReadonly()
创建的代理对应的原始对象。
<script setup lang="ts">
import { toRef,reactive,toRefs } from 'vue'
let obj = reactive({
num:1,
msg:2
})
toRaw(obj) //变成一个普通对象
</script>
computed
计算属性
使用方法1:回调函数
<script setup lang="ts">
import { ref,computed } from 'vue'
let firstName = ref('张')
let lastName = ref('三')
let fullName = computed(()=>{
return firstName.value+lastName.value;
})
</script>
使用方法2:配置对象
<script setup lang="ts">
import { ref,computed } from 'vue'
let firstName = ref('张')
let lastName = ref('三')
let fullName = computed({
get(){
return firstName.value+lastName.value;
},set(){
firstName.value+lastName.value;
}
})
</script>
watch
侦听方法
watch(属性,回调函数,配置项)
配置项有:
deep:true 深度侦听
immediate:true 立即执行一次
flush:"pre" pre组件更新之前调用,sync同步执行,post组件更新之后执行
侦听一个普通类型
<template>
<div>
<input type="text" v-model="msg"/>
</div>
</template>
<script setup lang="ts">
import { ref,computed,watch } from 'vue'
let msg = ref('erickiku')
//msg发生改变时,触发回调函数
watch(msg,(newV,oldV)=>{
console.log(newV,oldV);
});
</script>
侦听多个普通类型
<script setup lang="ts">
import { ref,computed,watch } from 'vue'
let msg = ref('erickiku')
let msg2 = ref('erickiku')
//这时,newV和oldV分别是一个数组,对应这两个属性
watch([msg,msg2],(newV,oldV)=>{
console.log(newV,oldV);
});
</script>
侦听对象中的属性
使用回调函数形式,返回属性的值,来进行侦听
<script setup lang="ts">
import { ref,computed,watch,reactive } from 'vue'
let msg = reactive({name:'zs'})
//
watch(()=>msg.name,(newV,oldV)=>{
console.log(newV,oldV);
},{deep:true,immediate:true}); //可以在第三个参数开启深度侦听和立即执行一次
</script>
watchEffect
不需要指定侦听的属性,在回调函数中,只要出现的属性变化了,就会执行回调
<script setup lang="ts">
import { ref,computed,watch,reactive,watchEffect } from 'vue'
let msg = ref('msg') //修改时会触发侦听
let msg1 = ref('msg2') //修改不会触发侦听
watchEffect(()=>{
console.log(msg);
})
</script>
💡:watchEffect的回调中,第一个参数可以有,可以是一个函数,函数名随意,在触发侦听的回调代码之前,先运行这个函数
watchEffect((before)=>{
console.log(msg);
before(()=>{
console.log("before"); //即是是在输出msg之后,侦听调用时也在最前
})
})
停止侦听:
watchEffect会返回一个停止函数
<script setup lang="ts">
import { ref,computed,watch,reactive,watchEffect } from 'vue'
let msg = ref('msg') //修改时会触发侦听
let msg1 = ref('msg2') //修改不会触发侦听
let stop = watchEffect(()=>{
console.log(msg);
})
stop() //调用stop方法,侦听就失效了
</script>
组件与生命周期
vue3的组件写好之后,在其他地方使用import引用之后,就可以直接使用,不需要使用components注册
setup
最先触发onBeforeMount(()=>{})
:创建之前 这个时候获取元素是undefinedonMounted(()=>{})
:创建 可以读取到dom元素onBeforeUpdate(()=>{})
:更新之前 获取dom元素时,获取的是更新之前的domonUpdated(()=>{})
:更新 获取到更新之后的domonBeforeUnmount(()=>{})
:销毁之前onUnmounted(()=>{})
:销毁onRenderTracked(()=>{e})
onRenderTriggered(()=>{e})
布局
使用less,并且清除默认样式,reset.css
一个布局代码
layout.vue
<template>
<div class="layout">
<Menu></Menu>
<div class="layout-right">
<Header></Header>
<Content></Content>
</div>
</div>
</template>
<script setup lang='ts'>
import Content from './Content/index.vue'
import Header from './Header/index.vue'
import Menu from './Menu/index.vue'
</script>
<style scoped lang='less'>
.layout{
display: flex;
height: 100%;
overflow: hidden;
&-right{
flex: 1;
display: flex;
flex-direction: column;
}
}
</style>
Header.vue
<template>
<div class="header">Header</div>
</template>
<script setup lang='ts'>
</script>
<style scoped lang='less'>
.header{
height: 60px;
border-bottom: 1px solid #ccc;
}
</style>
Menu.vue
<template>
<div class="menu">
Menu
</div>
</template>
<script setup lang='ts'>
</script>
<style scoped lang='less'>
.menu {
width: 200px;
border-right: 1px solid #ccc;
}
</style>
Content.vue
<template>
<div class="content">
<div class="content-items" v-for="item in 100" :key="item">
{{ item }}
</div>
</div>
</template>
<script setup lang='ts'>
</script>
<style scoped lang='less'>
.content{
flex:1;
margin: 20px;
border: 1px solid #ccc;
overflow: auto;
&-items{
padding: 20px;
border: 1px solid #ccc;
}
}
</style>
Flex 布局语法教程
编程技术
分类网页布局(layout)是CSS的一个重点应用。
布局的传统解决方案,基于盒状模型,依赖 display属性 + position属性 + float属性。它对于那些特殊布局非常不方便,比如,垂直居中就不容易实现。
2009年,W3C提出了一种新的方案—-Flex布局,可以简便、完整、响应式地实现各种页面布局。目前,它已经得到了所有浏览器的支持,这意味着,现在就能很安全地使用这项功能。
Flex布局将成为未来布局的首选方案。本文介绍Flex布局的语法。
以下内容主要参考了下面两篇文章:A Complete Guide to Flexbox 和 A Visual Guide to CSS3 Flexbox Properties。
一、Flex布局是什么?
Flex是Flexible Box的缩写,意为”弹性布局”,用来为盒状模型提供最大的灵活性。
任何一个容器都可以指定为Flex布局。
.box{
display: flex;
}
行内元素也可以使用Flex布局。
.box{
display: inline-flex;
}
Webkit内核的浏览器,必须加上-webkit前缀。
.box{
display: -webkit-flex; /* Safari */
display: flex;
}
注意,设为Flex布局以后,子元素的float、clear和vertical-align属性将失效。
二、基本概念
采用Flex布局的元素,称为Flex容器(flex container),简称”容器”。它的所有子元素自动成为容器成员,称为Flex项目(flex item),简称”项目”。
容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫做main start,结束位置叫做main end;交叉轴的开始位置叫做cross start,结束位置叫做cross end。
项目默认沿主轴排列。单个项目占据的主轴空间叫做main size,占据的交叉轴空间叫做cross size。
三、容器的属性
以下6个属性设置在容器上。
- flex-direction
- flex-wrap
- flex-flow
- justify-content
- align-items
- align-content
3.1 flex-direction属性
flex-direction属性决定主轴的方向(即项目的排列方向)。
.box {
flex-direction: row | row-reverse | column | column-reverse;
}
它可能有4个值。
- row(默认值):主轴为水平方向,起点在左端。
- row-reverse:主轴为水平方向,起点在右端。
- column:主轴为垂直方向,起点在上沿。
- column-reverse:主轴为垂直方向,起点在下沿。
3.2 flex-wrap属性
默认情况下,项目都排在一条线(又称”轴线”)上。flex-wrap属性定义,如果一条轴线排不下,如何换行。
.box{
flex-wrap: nowrap | wrap | wrap-reverse;
}
它可能取三个值。
(1)nowrap(默认):不换行。
(2)wrap:换行,第一行在上方。
(3)wrap-reverse:换行,第一行在下方。
3.3 flex-flow
flex-flow属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。
.box {
flex-flow: <flex-direction> <flex-wrap>;
}
3.4 justify-content属性
justify-content属性定义了项目在主轴上的对齐方式。
.box {
justify-content: flex-start | flex-end | center | space-between | space-around;
}
它可能取5个值,具体对齐方式与轴的方向有关。下面假设主轴为从左到右。
- flex-start(默认值):左对齐
- flex-end:右对齐
- center: 居中
- space-between:两端对齐,项目之间的间隔都相等。
- space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。
3.5 align-items属性
align-items属性定义项目在交叉轴上如何对齐。
.box {
align-items: flex-start | flex-end | center | baseline | stretch;
}
它可能取5个值。具体的对齐方式与交叉轴的方向有关,下面假设交叉轴从上到下。
- flex-start:交叉轴的起点对齐。
- flex-end:交叉轴的终点对齐。
- center:交叉轴的中点对齐。
- baseline: 项目的第一行文字的基线对齐。
- stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。
3.6 align-content属性
align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。
.box {
align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}
该属性可能取6个值。
- flex-start:与交叉轴的起点对齐。
- flex-end:与交叉轴的终点对齐。
- center:与交叉轴的中点对齐。
- space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。
- space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。
- stretch(默认值):轴线占满整个交叉轴。
四、项目的属性
以下6个属性设置在项目上。
- order
- flex-grow
- flex-shrink
- flex-basis
- flex
- align-self
4.1 order属性
order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。
.item {
order: <integer>;
}
4.2 flex-grow属性
flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。
.item {
flex-grow: <number>; /* default 0 */
}
如果所有项目的flex-grow属性都为1,则它们将等分剩余空间(如果有的话)。如果一个项目的flex-grow属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍。
4.3 flex-shrink属性
flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。
.item {
flex-shrink: <number>; /* default 1 */
}
如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。
负值对该属性无效。
4.4 flex-basis属性
flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。
.item {
flex-basis: <length> | auto; /* default auto */
}
它可以设为跟width或height属性一样的值(比如350px),则项目将占据固定空间。
4.5 flex属性
flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。
.item {
flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
}
该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。
建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。
4.6 align-self属性
align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。
.item {
align-self: auto | flex-start | flex-end | center | baseline | stretch;
}
该属性可能取6个值,除了auto,其他都与align-items属性完全一致。
父子组件传参
父子组件传参使用define***
系列
父->子
defineProps
父组件
在子组件标签上使用v-bind的简写:来定义要传递的属性名,值就是要传递的值
<template>
<Hello :msg1="name"></Hello>
</template>
<script setup lang="ts">
import Hello from './components/HelloWorld.vue'
const name = 'foo';
</script>
子组件
接收方式1:
使用defineProps({})
接收,配置项中属性名对应标签上的属性名,type是指定类型,default是指定默认值,有默认值后可以不传递,没有默认值就必须传递该属性
接收的参数可以直接在模板中使用,但是如果要在script中使用则需要使用一个变量接收,然后用变量.属性
才能获取
<template>
<div class="content">
{{ msg1 }}
</div>
</template>
<script setup lang="ts">
let props = defineProps({
msg1:{
type:String,
default:""
}
})
console.log(props.msg1);
</script>
接收方式2:
使用TS
专属方式接收:withDefaults(参数1,参数2)
该方法接收两个参数,第一个参数是defineProps方法
,第二个参数是配置项,用于配置接收属性的默认值
在defineProps中使用泛型,defineProps<{属性名:类型}>()
来接收属性
在配置项中定义默认值,而且需要一个回调函数返回属性的默认值
同样,可以直接在模板使用,在script中使用需要变量接收
<script setup lang="ts">
let props = withDefaults(defineProps<{
msg: number[]
}>(), {
msg: () => [1, 2]
})
console.log(props.msg);
</script>
子->父
defineEmits
父组件
定义一个自定义事件,如on-send
,绑定父组件中的一个方法,如getMsg
,然后让子组件去触发on-send
,返回的数据作为父组件绑定的方法的形参
<template>
<Hello @on-send="getMsg"></Hello>
</template>
<script setup lang="ts">
import Hello from './components/HelloWorld.vue'
let getMsg = (val: any)=>{
console.log(val);
}
</script>
子组件
需要使用defineEmits([])
来定义事件,事件名 对应 父组件中给子组件标签定义的自定义事件名,可以有多个,返回一个方法,如emit
使用该方法来触发某个事件名,第二个参数携带数据,会被父组件的形参所接收
<template>
<div class="content">
<button @click="send">点击</button>
</div>
</template>
<script setup lang="ts">
let emit = defineEmits(['on-send'])
let send = ()=>{
emit('on-send','erickiku');
}
</script>
第二种方式,使用TS
专属方式接收:
依旧是泛型中定义类型{(e:"事件名",形参名:形参类型):void}
,多个事件名中间不用逗号连续,不是对象,是一种类型,如:
<script setup lang="ts">
let emit = defineEmits<{
(e:"on-send",val:string):void
(e:"on-receive",val:string):void
}>()
let send = ()=>{
emit('on-send','erickiku');
}
</script>
给父组件暴露属性或方法
defineExpose({})
子组件
defineExpose({
name: 'bar',
fun: () => {
console.log('fun');
}
})
父组件
先在子组件标签上定义ref和值,使用ref获取标签
let i = ref<InstanceType<typeof HelloWorld>>();
以上代码是可以获取到对应标签的实例类型的
使用i.value?.暴露的属性名
来获取
但是在setup里直接获取子组件暴露的属性或方法是不能获取的到的,因为setup执行的时候,dom还没有渲染,所以应该在合适的生命周期中获取
<template>
<HelloWorld ref="i"></HelloWorld>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import HelloWorld from './components/HelloWorld.vue'
const i = ref<InstanceType<typeof HelloWorld>>();
console.log(i.value?.name);
i.value?.fun()
</script>
瀑布流
使用父子组件传参
局部/全局组件
局部注册和全局注册和vue2一样
递归组件:
可以在组件内直接使用该组件文件名的标签来递归
index.vue
<indev></indev> //直接使用自己文件名的标签
?
可选链操作符:?.
,如果说undefined.length
就会报错,因为undefiend没有length属性,但是可以undefined?.length
,没有length就会直接返回undefined
??
:当双问号前面是undefined或null
时,返回后面的值,如果不是就返回前面的值,不包括0和false
null ?? 123 >> 123
false ?? 123 >> false
动态组件
使用<component>
标签来动态的展示组件,也可以使用路由实现
<!-- currentTab 改变时组件也改变 -->
<component :is="tabs[currentTab]"></component>
<template>
<button @click="switchItem(1)">切换组件1</button>
<button @click="switchItem(2)">切换组件2</button>
<component :is="com"></component>
</template>
<script setup lang="ts">
import { ref,shallowRef } from 'vue'
import Dongtai from './components/Dongtai.vue';
import Dongtai2 from './components/Dongtai2.vue';
const com = shallowRef(Dongtai)
const switchItem = (v:number)=>{
if (v==1) {
com.value = Dongtai
}else{
com.value = Dongtai2
}
}
</script>
使用变量定义组件,然后在component标签中的:is
的值填写变量。之后只要切换变量对应的组件,即可改变component标签展示的组件,实现动态切换组件
插槽
异步组件
当组件中使用了顶层await
时,整个组件就是一个异步组件
引入异步组件也必须使用defineAsyncComponent
,使用必须前先引入
子组件-异步组件
使用了顶层await,此时就是一个异步组件,不需要async
<script setup lang='ts'>
import axios from 'axios'
let data: any;
await axios.get("/api/queryAllPost").then(res => {
data = res.data
})
</script>
其他引用子组件的父组件
<template>
<Suspense>
<Await></Await>
<template #fallback>
Loading...
</template>
</Suspense>
</template>
<script setup lang="ts">
import { defineAsyncComponent } from 'vue'
const Await = defineAsyncComponent(()=>import("./components/Await.vue"))
</script>
必须使用defineAsyncComponent(()=>import(""))
方式引入,可以结合Vue3的Suspense
标签使用。
标签下直接放要显示的组件,插槽中使用#fallback
来显示异步组件未完成时展示的组件或内容
Teleport
传送组件,可以把组件传送到任意位置
<template>
<div class="out">
<Teleport :disabled="false" to="body">
<A></A>
</Teleport>
</div>
</template>
把A组件传送到body标签下,不再属于out的div标签
:disables
可以通过js来控制是否禁用传送,为true则是禁用,不再传送
transition动画
provide依赖注入
用于提供可以被后代组件注入的值。
父组件
<script setup lang="ts">
import { ref,provide } from 'vue'
import Provide from './components/Provide.vue';
provide("msg",ref<string>('message'));
</script>
子组件及其所有后代组件
<script setup lang='ts'>
import { ref,inject } from 'vue';
let msg = inject('msg')
</script>
Mitt发布订阅库
自动引入插件
插件名:unplugin-auto-import,可以自动引入需要的依赖,安装之后引入插件时如果使用的是vite,则:
import AutoImport from 'unplugin-auto-import/vite'
v-model
v-model在组件上使用时,可以在组件内接收v-model绑定的变量值
父组件
<CustomInput v-model="searchText" />
子组件
<!-- CustomInput.vue -->
<template>
<input
:value="modelValue"
@input="change"
/>
</template>
<script setup>
defineProps(['modelValue'])
let emits = defineEmits(['update:modelValue'])
let change = ()=>{
emits("update:modelValue",'change')
}
</script>
子组件也可以通过defineProps
来获取到父组件双向绑定的值,接收时,第一个必须是modelValue
,更多的v-model则根据定义的名称。而通过适当的方法也可以修改这个变量的值,从而达到双向绑定
如果要在同一个组件上绑定多个v-model,则需要给第二个以上的vmodel定义名字:v-model:textV="value"
,在子组件中注册事件时,不再是update:modelValue
,而是update:textV
globalProperties
全局变量或方法
在main.js中定义全局可用的变量或方法
app.config.globalProperties.msg = 'hello'
在组件模板中可以直接使用,
在js中:
mounted() {
console.log(this.msg) // 'hello'
}
样式穿透
当想要修改UI库或者自定义组件中的样式时,会不生效,需要样式穿透
如,eleUI的某个组件的样式是el-input__inner
,但是直接
.el-input__inner:{}
是不生效的,因为scoped的原因,所以需要加上:deep
:deep(.el-input__inner){color:red}
宏任务与微任务
unocss原子化css
原子化css:减少了css体积,提高了css复用,减少起名的复杂度,但是增加了 记忆成本
安装
$ npm i -D unocss
桌面程序Electron
先创建一个vite+vue3+ts的项目
环境变量
主要作用就是让开发者区分不同的运行环境,来兼容开发和生产
例如:npm run dev就是开发环境,npm run build就是生产环境
查看环境方法:
此方法是vite方法
console.log(import.meta.env); //输出一个对象,含有环境相关的属性
vuecli方法是:
log(process.env)
自定义环境变量:
开发环境和生产环境都需要配置文件
新建文件:.env
+.任意名字
。如:.env.development
一般开发环境文件名:.env.development
生产环境文件名:.env.production
自定义全局环境变量时,ENV 这一变量必须写
.env.development
ENV = 'development'
VITE_BASE_URL='/api'
.env.production
ENV = 'production'
VITE_BASE_URL='/api'
定义好两个文件之后,使用npm run dev即可自动读取对应的development文件,打包之后使用会自动读取production文件
Vue3性能优化
1.浏览器开发者工具的Lighthouse
选项卡,可以给项目跑分
面试
异步组件打包优化问题
如果组件是异步组件,但是使用的使用是直接import from 方式引入的,那么打包时,会全部打包到一个js文件中,程序运行时会被异步组件阻挡,会有相当长的延迟时间
所以,必须使用defineAsyncComponent
方式去引入异步组件,打包时,会另外打成一个单独的js文件,只有使用到时,才会去加载