!!!!!注意 示例很多
较于Vue2的比较
- 更好的逻辑复用 与 代码组织 (composition组合式api)
- Vue3采用组合式Api(Composition API,代码组织更方便了,利于维护)。
- 官方对组合式Api的解释:通过组合式 API,我们可以使用导入的 API 函数来描述组件逻辑。在单文件组件中,组合式 API 通常会与
<script setup>搭配使用。这个setupattribute 是一个标识,告诉 Vue 需要在编译时进行一些处理,让我们可以更简洁地使用组合式 API。比如,<script setup>中的导入和顶层变量/函数都能够在模板中直接使用。
<script setup>
import { ref, onMounted } from 'vue'
// 响应式状态
const count = ref(0)
// 用来修改状态、触发更新的函数
function increment() {
count.value++
}
// 生命周期钩子
onMounted(() => {
console.log(`The initial count is ${count.value}.`)
})
</script>
<template>
<button @click="increment">Count is: {{ count }}</button>
</template>
- 更好的类型推导 (typescript支持),ts大名鼎鼎,就不需要过多介绍了。
Vue3新特性
- 数据响应式与Vue2不同,采用proxy代理进行数据劫持,解决了: 例如数组的更新检测等bug, 大大优化了响应式监听的性能(原来检测对象属性的变化, 需要一个个对属性递归监听) proxy 可以直接对整个对象劫持
- 虚拟DOM - 新算法 (更快 更小)
- 提供了composition api, 可以更好的逻辑复用
- 模板可以有多个根元素
- 源码用 typescript 重写, 有更好的类型推导 (类型检测更为严格, 更稳定)
Vite相关知识(官方解读)
Vite(法语意为 "快速的",发音 /vit/,发音同 "veet")是一种新型前端构建工具,能够显著提升前端开发体验。它主要由两部分组成:
- 一个开发服务器,它基于 原生 ES 模块 提供了 丰富的内建功能,如速度快到惊人的 模块热更新(HMR)。
- 一套构建指令,它使用 Rollup 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。
Vite 意在提供开箱即用的配置,同时它的 插件 API 和 JavaScript API 带来了高度的可扩展性,并有完整的类型支持。
个人理解:vite是一个与脚手架vue-cli类似的工具,其为鱿鱼团队开发的,开发距今日期较短,在package.json配置里面进行的配置是
"scripts": {
"dev": "vite", // 开启服务器
"build": "vite build", // 打包
"preview": "vite preview" //预览
},
好处就是很快,非常快。原因是用原生esm模块化无需打包,还是按需引入。 但是不建议在项目中使用,因为需要配置的东西有很多。 项目中最好还是用脚手架和 webpack进行打包等工作。(关于如何搭建一个vite项目,看官网开始 | Vite (vitejs.net))
Vue3的组合式API
这里以一个简单的数字进行简介:
<template>
<button @click="add">{{ count }}</button> //一个按钮实现点击对count加一的操作
</template>
<script>
// option 式api
// setup一开始就会调用,有点类似与选择式API的create函数
export default {
setup() {
let count = 1; // 声明count
function add() { //声明button的操作
count++;
console.log(count);
}
return { //这种写法必须写return 把数据方法和数据暴露出去
count,
add,
};
},
};
</script>
// 下面这种方法是对上面setup进行简写 不用再写return和setup,也就是上面代码块的语法糖
<!-- <script setup>
// option 式api
let count = 1;
function add() {
count++;
console.log(count);
}
</script> -->
上面代码有个致命的问题,就是数据不是响应式的,试图无法与数据同步发生变化。此时需要一个新的api:ref对数据进行和操作进行包裹,代码如下:(需要注意的是,此时script代码块里数据和操作需要通过value属性来进行操作,但是渲染到试图不用写value属性)
<template>
<button @click="add">{{ count }}</button>
</template>
<!-- <script>
// option 式api
import {ref} from 'vue';
export default {
setup() {
let count = ref(1);
function add() {
count.value++;
console.log(count.value);
}
return {
count,
add,
};
},
};
</script> -->
<script setup>
// option 式api
import {ref} from 'vue'; // 引入新的api ref 对数据进行包裹
let count = ref(1);
function add() {
count.value++;
console.log(count.value);
}
</script>
还有一个api reactive,是用来实现对象的响应式的,实现代码如下 :
<template>
<button @click="add">{{ count }}</button>
</template>
<script setup>
import {reactive} from 'vue' // 引入api
let count =reactive ({ // 包裹对象
name:'zs',
age:18
})
function add (){
count.age++ // 实现操作
}
</script>
不过尽量reactive尽量不要使用了,原因很简单,ref即可以用于简单数据类型,也可以用于复杂数据类型,且性能更好,(ref底层做了优化) 将上面的代码可以改写成下面代码:
<template>
<button @click="add">{{ count }}</button>
</template>
<script setup>
import {ref} from 'vue';
let count =ref ({ // 通过ref对对象进行包裹实现响应式
name:'zs',
age:18
})
function add (){
count.value.age++ // ref在使用时必须使用value
}
</script>
这里可以了解一下hook就是把多个组件中一样的逻辑提取出来例如data、methods。了解一下第三方的库 vueuse (VueUse | VueUse)。
有两个api是onmounted和onunmounted类似于vue2中的mounted挂载后和beforedestroy销毁前 ,示例如下:
<template>
<p>{{obj}}</p>
</template>
<script setup>
// 一般以下情况导致内存泄漏:定时器、全局变量、闭包、vue组件中监听的dom时间再组件销毁时候没有被移除
import {ref,onMounted,onUnmounted} from 'vue'
const obj = ref({
x:0,
y:0
})
let fn = function (e){
obj.value.x=e.pageX
obj.value.y=e.pageY
}
// 相当于vue2中的mounted
onMounted(()=>{
document.documentElement.addEventListener('mousemove',fn) // 鼠标移动事件
})
// 相当于之前的beforeDestroy
onUnmounted(()=>{
document.documentElement.removeEventListener('mousemove',fn)
})
</script>
计算属性computed函数,与vue2类似,代码实例如下 计算属性两种写法:一种就是计算属性里面写钩子函数,写产生变化的数据。另一种就是get和set的写法,get返回获取到的值,set返回本身变化对其他产生变换的值。(注意,不要再计算属性里面异步操作或者改变dom,想要进行这些操作,要卸载侦听器watch里面) :
<template>
<div>今年的年纪 <input type="text" v-model="age" /></div>
<div>明年的年龄 {{ nextAge }}</div>
<div>后年的年龄 <input type="text" v-model="nextAge2" /></div>
<div></div>
</template>
<script setup>
// 计算属性两种写法
import { computed, ref } from "vue";
const age = ref(20);
const nextAge = computed(() => { // 第一种写法 简单写法,是只读的
return +age.value + 1;
});
const nextAge2 = computed({ // 第二种写法 也是完整写法,是双向的
get() {
return +age.value + 2;
},
set(value) {
age.value = value - 2;
},
});
</script>
监听器watch函数 watch监视, 接收三个参数:
- 参数1: 监视的数据源
- 参数2: 回调函数
- 参数3: 额外的配置
实例代码如下:
<input type="text" v-model="name">
<p :style="{ color : bool ?'green':'red'}">{{bool?'合法':'违法'}}</p>
</template>
<script setup>
import { watch, ref } from "vue";
let name = ref('')
let bool = ref(true)
watch(
name, //数据源
(val) => { // 回调函数
if (val.length < 3 || val.length > 8) {
bool.value = false;
} else {
bool.value = true;
}
},
{ 额外配置
deep: true,
immediate: true,
}
);
</script>
钩子函数: 常用的就四个 setup(ajax) onMounted(操作dom) onUpdated(拿到数据更新之后最新的DOM) onBeforeUnmount(移除定时器等)
组件的复用及传值
组件的局部调用,不再像vue2需要先进行注册再去使用,vue3里引用后直接使用。
<template>
<MyCeshi></MyCeshi>
</template>
<script setup>
import MyCeshi from './components/MyCeshi.vue'
</script>
组件间传值:
- 父向子传值:子组件通过defineProps来接收,
<template>
{{car}}
</template>
<script setup>
//父组件给子组件传值 只需要再函数调用里加上一句 :car = '要传的数据'
const props = defineProps({ // 通过defineProps来接收,不用引入啥的
car:{
type:String,
required: true
}
})
console.log(props.car);
</script>
- 子向父传值:通过defineEmits发送
// 父组件 通过@myclick="fn"声明事件
<template>
<MyCeshi @myclick="fn"></MyCeshi>
</template>
<script setup>
import MyCeshi from "./components/MyCeshi.vue";
function fn(val) {
console.log('子向父传值'+val);
}
</script>
// 子组件 通过defineEmits和emit来进行操作 有两种写法
<template>
<button @click="$emit('myclick',200)">子向父传值</button>
<button @click="handleClick">另一种子向父传值方法</button>
</template>
<script setup>
const emit = defineEmits(['myclick']) //写法1
function handleClick(){ //写法2
emit('myclick',300)
}
</script>
- 兄弟组件传值:这个与vue2不同,不能通过eventbus来传值。vue3通过mitt第三方插件可实现组件间通信。 兄弟组件及一个mitt.js文件可以实现
//mitt.js文件
import mitt from 'mitt' // 下载好mitt后引入
export default mitt() // 暴露mitt()方法
//两兄弟组件
//组件1
<template>
<button @click="btn">组件间传值</button>
</template>
<script setup>
import mitt from '../mitt'
function btn() {
mitt.emit('my-event',200) 通过emit来发送数据
}
</script>
// 组件2
<script setup>
import mitt from '../mitt'
mitt.on('my-event',(num)=>{ // 通过on来接收数据
console.log(num);
})
</script>
- 隔代传值 provide inject
- 任意两个组件 不再用vuex而是使用pinia(菠萝)
通过ref获取元素
<template>
<p ref="pRef">123645</p>
</template>
<script setup>
import { onMounted, ref } from "vue";
const pRef = ref(null);
onMounted(() => {
console.log(pRef.value); // 就获取到了元素
});
</script>
过滤器
再vue里,filter过滤器被废弃,取而代之的直接使用函数形式就可以了:
<template>
<p>{{ format(money, "元") }}</p> // 使用过滤器函数
</template>
<script setup>
import { ref } from "vue";
const money = ref(600)
function format(money, param) { // 声明过滤器函数
return money + param;
}
</script>