vite+vue3学习记录

准备工作

  • 创建项目 pnpm create vite

    √ Project name: … vite-vue3-demo //项目名称

    √ Select a framework: » Vue //前端框架

    √ Select a variant: » JavaScript

  • 运行

    pnpm i

    pnpm run dev

  • 安装依赖

1
2
3
4
5
pnpm install vue-router@4
pnpm install ant-design-vue
pnpm install axios
pnpm install sass --save-dev
pnpm install @ant-design/icons-vue

响应式

官方api

reactive

创建一个响应式对象,仅对对象类型有效(对象、数组和 Map、Set 这样的集合类型),而对 string、number 和 boolean 这样的 原始类型 无效。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// javascript
import { reactive } from 'vue';
//定义响应式数据
const state = reactive({ count: 0,data: [5, 6, 7, 8]})
//1、 响应式
state.count++
//2、解构后无法保持响应式
let { count } = state
//无法保持响应式
count++;

//3、可以追加属性
state.newProp = { 'msg': '你好' }

//4、属性可替换赋值
state.data = { 'msg': '我变了' }

//5、【错】不能整个重新赋值
// state={ count: 100 }
1
<p> {{ state }}</p>

ref

创建任意类型的响应式数据

示例

1
2
3
4
5
6
7
8
9
10
11
import { ref } from 'vue'
//定义响应式数据
const count = ref(0)
const objectRef = ref({ count: 0 })
//1、响应式访问需要.value
count.value++;
objectRef.value.count++;
//2、可追加属性
objectRef.value.data=[1,2,3,4]
//3、可替换赋值并保持响应式
objectRef.value={ msg: '惊呆了、我整个被替换了' }
1
2
3
<!-- 模板里不需要.value-->
<p>{{count}}</p>
<p>{{objectRef.count}}</p>

$ref 响应式语法糖

官方api
结合了ref和reactive的优点,该功能处于实验性阶段
需要在vite.config.js里配置reactivityTransform: true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path, { resolve } from 'path'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue({
reactivityTransform: true//响应式语法糖
})
],
resolve: {
alias: {
'@': resolve(__dirname, 'src')
}
},
})

示例

1
2
3
4
5
6
7
//1、响应式 基础类型 和对象类型都不需要.value
num++;
$refObj.count++;
//2、可以追加属性
$refObj.newProp = { 'msg': '你好' }
//3、可替换赋值并保持响应式
$refObj = { msg: '惊呆了、我整个被替换了' }
1
2
<p> $refnum1 -->{{ num }}</p>
<p> $refObj -->{{ $refObj }}</p>

props

官方api

  • 父组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<template>
<div>
<h2>prop传参 示例</h2>
<button @click="change">change</button>
<p>num--><input v-model="num" type="number" /> </p>
<p>str-->{{ str }}</p>
<p>arr-->{{ arr }}</p>
<p>obj-->{{ obj }}</p>
<hr>
<propsChild :arr="arr" :num="num" :str="str" :obj="obj" ></propsChild>
</div>
</template>

<script setup>
import propsChild from './propsChild.vue';

let num=$ref(0);
let str=$ref("你好");
let arr=$ref(['苹果','火龙果'])
let obj=$ref({name:'张三',age:18})

const change=()=>{
num++;
str+="!";
arr.push("未知水果"+arr.length);
obj.age++;
obj.sex='boy';
}

</script>

  • 子组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<template>
<div style="background-color: aqua;">
<h2>子组件</h2>
<button @click="change">change</button>
<p>num-->{{ num }}</p>
<p>str-->{{ str }}</p>
<p>arr-->{{ arr }}</p>
<p>obj-->{{ obj }}</p>
</div>
</template>

<script setup>
import { defineProps } from 'vue';
let props=defineProps({
num:Number,
str:String,
arr:{
type:Array,
default(){
return []
}
},
obj:{
type:Object,
default(){
return {}
}
},
})

const change=()=>{
//1、基础类型不能修改值,控制台会提示target is readonly.
props.num++;
props.str+="~";
//2、对象、数组类型可以修改、但vue不建议这么做
props.arr.push("未知水果"+props.arr.length);
props.obj.age++;
props.obj.sex='boy';
}
</script>

当对象或数组作为 props 被传入时,虽然子组件无法更改 props 绑定,但仍然可以更改对象或数组内部的值。这是因为 JavaScript 的对象和数组是按引用传递,而对 Vue 来说,禁止这样的改动虽然可能,但有很大的性能损耗,比较得不偿失。
这种更改的主要缺陷是它允许了子组件以某种不明显的方式影响父组件的状态,可能会使数据流在将来变得更难以理解。在最佳实践中,你应该尽可能避免这样的更改,除非父子组件在设计上本来就需要紧密耦合。在大多数场景下,子组件应该抛出一个事件来通知父组件做出改变。

emit

官方api

父子通信的一种方式,事件触发

  • 示例

子组件 emitChild.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
<div style="background-color:aqua;">
<button @click="click1"> emit</button>
<input :value="props.modelValue" @input="emit('update:modelValue', $event.target.value)" />
<input :value="props.age" type="number" @input="emit('update:age', $event.target.value)" />
</div>
</template>
<script setup>
import { defineEmits, defineProps,defineExpose } from 'vue';
//1、定义事件
const emit = defineEmits(["changeParent", "update:modelValue", "update:age"])
//2. 配合v-model使用
const props = defineProps(['modelValue', 'age'])
const click1 = () => {
//触发事件
emit('changeParent', new Date().toLocaleString())
}
const name="nihao";
const method1=()=>{
console.log("你用了我的方法");
return "我返回给你什么就是什么"
}
//3 暴露属性方法
defineExpose({name,method1})
</script>

父组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<template>
<div>
<h2>emit</h2>
<button @click="getEmitChild">获取子组件暴露属性方法</button>
<input type="text" v-model="name">
<input v-model="age" type="number" />
<p>{{ loginDate }}</p>
<!-- 事件回调@事件名 -->
<emitChild ref="emitChild1" v-model="name" v-model:age="age" @change-parent="setLoginDate"></emitChild>
</div>
</template>

<script setup>
import emitChild from './emitChild.vue';
let name = $ref("张三");
let loginDate = $ref("2010-01-10 14:31:14")
let age=$ref(12);
const setLoginDate = (date) => {
loginDate = date;
}
const emitChild1=$ref();
const getEmitChild=()=>{
let method1Return=emitChild1.method1();
let name=emitChild1.name;
console.log("emitChild1 -> method1Return",method1Return);
console.log("emitChild1 -> name",name);
}
</script>

slot

官方api

  • 没有name的插槽为默认插槽,默认name为default
  • 可用v-slot:slot1 也可用 #slot1

    定义

1
2
3
4
<slot name="slot1">
<!-- 默认内容 -->
<p>具名插槽1 </p>
</slot>

使用

1
2
3
<template #slot1>
<p> 覆盖具名1插槽</p>
</template>
  • 示例

子组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<template>
<p>组件1</p>
<!-- 默认插槽 -->
<slot>
<button @click="click1" >默认插槽</button>
</slot>
<slot name="slot1">
<!-- 默认内容 -->
<p>具名插槽1 </p>
</slot>
<slot name="slot2">
</slot>
</template>
<script setup>
const click1 = () => { alert(1) };
</script>

父组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<div>
<slotChild>
<template #default>
<p> 覆盖默认插槽</p>
</template>
<!-- 可用v-slot:slot1 也可用 #slot1 -->
<template #slot1>
<p> 覆盖具名1插槽</p>
</template>
<template #slot2>
<p> 覆盖具名2插槽</p>
</template>
</slotChild>
</div>
</template>

<script setup>
import slotChild from './slotChild.vue';
</script>

数据注入

官方api

父组件向后代组件传递数据

  • 一般>=3级使用,两级可以使用props
  • $ref语法糖定义的数据在后代组件修改数据后貌似无法响应式

    父组件提供数据

1
2
3
4
<script setup>
import { provide } from 'vue'
provide('注入名', 注入值)
</script>

后代组件注入使用

1
2
import { inject } from 'vue'
const message = inject('注入名',默认值)

示例

  • 父组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
str1-><input v-model="str1" type="text" />
str2-><input v-model="str2" type="text" />
obj.count-><input v-model="obj.count" type="text" />
<hr>
<inject2></inject2>
</template>
<script setup>
import { provide, ref } from 'vue'
import inject2 from './inject2.vue';
const str1 = ref('你好');
const str2 = $ref('str2');
const obj = $ref({ count: 1 });
const change = () => {
str1.value += "~";
str2 += "!"
}
//注入
provide("vals", { str1, str2, obj, change })
</script>
  • 后代组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
<div>
<p>inject3</p>
<p>obj-->{{obj}}</p>
<p>str1--><input type="text" v-model="str1"></p>
<!-- $ref 定义的基础数据注入后没办法响应式? -->
<p>str2--><input type="text" v-model="str2"></p>
<p>obj.count--><input type="text" v-model="obj.count"></p>
<button @click="change"> change</button>
</div>
</template>

<script setup>
import { inject } from 'vue'
// inject('属性名',默认值)
const {str1,str2,obj,change}=inject('vals',{})
console.log('str1',str1);
console.log('str2',str2);
</script>

计算属性

官方api

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div>
<input type="text" v-model="firstName">
<input type="text" v-model="lastName">
<p>fullName--{{ fullName }}</p>
</div>
</template>
<script setup>
import { computed } from 'vue';
let firstName = $ref("zhang")
let lastName = $ref('shan')
const fullName = computed(() => {
return `${firstName} . ${lastName}`;
})
</script>

watch

官方api
监听属性改变

1
2
3
4
5
6
watch(prop,callBack, {
immediate: Boolean, //在侦听器创建时立即触发回调
deep: Boolean //深度监听
flush:Boolean //调整回调的刷新时机'pre'(dom渲染之前) | 'post'(dom渲染之后) | 'sync'
})

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<template>
<div>
<button @click="change">change</button>
<button @click="change2">change2</button>
<input type="text" v-model="obj.d.stu.age">
</div>
</template>
<script setup>
import { watch, ref, reactive } from 'vue';
let num = ref(0);
let obj = ref({ count: 1, d: { stu: { age: 12 } } })
let arr = ref([0, 1, 2])
let $arr = $ref([0, 1, 2])
let $num = $ref(100);
//监听$ref
watch(() => $num, (newVal, oldVal) => {
console.log("$num change ", JSON.stringify(newVal))
})
//监听数组
watch(() => $arr, (newVal, oldVal) => {
console.log("$refArr change ", JSON.stringify(newVal))
}, { deep: true })
watch(num, (newVal, oldVal) => {
console.log("num change ", newVal)
})
watch(obj, (newVal, oldVal) => {
console.log("obj change1 ", JSON.stringify(newVal))
}, { deep: true })
//监听数组
watch(arr, (newVal, oldVal) => {
console.log("arr change ", JSON.stringify(newVal))
}, { deep: true })

//监听对象属性 整个被替换才会触发obj.d.stu={name:'zhangs'}
watch(() => obj.value.d.stu, (newVal) => {
console.log("obj.d.stu change1 ", JSON.stringify(newVal))
})
//属性值发生改变也会触发deep:true
watch(() => obj.value.d.stu, (newVal) => {
console.log("obj.d.stu change2 ", JSON.stringify(newVal))
}, { deep: true })
const change = () => {
num.value++;
$num++
obj.value.count++
arr.value.push(1);
$arr.push('!');

}
const change2 = () => {
$arr = ["nihao"]
arr.value = ['A', 'B', 'C'];
obj.value.d.stu = { name: 'zhangs' }
}
</script>

生命周期

官方api
官方api

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<template>
<div>
<!-- 触发onUpdated -->
<input type="text" v-model="name">
</div>
</template>
<script setup>
import { onMounted, onUpdated, onUnmounted, onBeforeMount, onBeforeUpdate, onBeforeUnmount, onActivated, onDeactivated } from 'vue'
let name = $ref('zs')
onMounted(() => {
console.log('----onMounted----')
})
onUnmounted(() => {
console.log('----onUnmounted----')
})
onUpdated(() => {
console.log('----onUpdated----')
})
onBeforeMount(() => {
console.log('----onBeforeMount----')
})
onBeforeUpdate(() => {
console.log('----onBeforeUpdate----')
})
onBeforeUnmount(() => {
console.log('----onBeforeUnmount----')
})
onActivated(() => {
console.log('----onActivated----')
})
onDeactivated(() => {
console.log('----onDeactivated----')
})
</script>

iconfont

使用阿里巴巴矢量图标库 创建简单的图标组件

  • 1、资源管理-我的项目-生成symol 链接
  • 2、在index.html引入 (可以将js下载下来)
1
<script src="//at.alicdn.com/t/c/font_3760320_azguqahs9pt.js"></script>
  • 3、 编写icon-font 组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<template>
<svg :width="props.size" :height="props.size" :style="style" :class="`icon-font ${props.class}`">
<use :xlink:href="'#' + props.name"></use>
</svg>
</template>
<script setup>
import { defineProps,defineEmits } from 'vue';
defineEmits("onclick")
const props = defineProps({
name: { type: String },
color: { type: String },
class: { type: String },
size: {
type: String,
default: '1.5em'
},
})
const style = {
};
if (props.color) {
style.color = props.color;
}
</script>
<style>
.icon-font {
fill: currentColor;
overflow: hidden;
vertical-align: middle;
}
</style>
  • 4、在main.js里全局注册组件
1
2
3
import IconFont from "@/components/iconFont.vue";
const app=createApp(App)
app.component("IconFont",IconFont);
  • 5、使用组件
1
<icon-font name="icon-search" size="32px" />

hooks

官方api开源vueuse库

逻辑复用、逻辑和页面分离

例子

  • useTodoList.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { ref } from 'vue';
import { message } from 'ant-design-vue';
const useTodoList = function () {
const task = ref("");
const todoList = ref(["吃饭", "睡觉"]);
const addTodoList = () => {
if (!task.value) {
message.error('请输入任务名称')
return;
}
if (todoList.value.includes(task.value)) {
message.error('任务名称重复')
return;
}
todoList.value.push(task.value);
task.value = "";
}
return {
task,
todoList,
addTodoList
}
}
export default useTodoList
  • 使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div>
<label for="task">任务名称:</label>
<input type="text" v-model="task" id="task" />
<button class="my-btn" @click="addTodoList()">添加 </button>
<ul>
<li v-for="item in todoList">{{ item }}</li>
</ul>
</div>
</template>

<script setup>
import useTodoList from '@/hooks/useTodoList'
let { task, todoList, addTodoList } = useTodoList();
</script>

未完待续