vue3.0
vue2和vue3的区别
1vue2 和vue3双向数据绑定原理不同
2.0的响应式基于Object.defineProperty中的set和get方法实现兼容主流浏览器和ie9以上的ie浏览器,能够监听数据对象的变化,但是监听不到对象属性的增删、数组元素和长度的变化,同时会在vue初始化的时候把所有的Observer(观察者)都建立好,才能观察到数据对象属性的变化。
3.0的响应式采用了ES2015的Proxy来代替Object.defineProperty,可以做到监听对象属性的增删和数组元素和长度的修改,同时还实现了惰性的监听(懒观察)(不会在初始化的时候创建所有的Observer,而是会在用到的时候才去监听)但是,虽然主流的浏览器都支持Proxy,ie系列却还是不兼容,所以针对ie11,vue3.0决定做单独的适配,暴露出来的api一样,但是底层实现还是Object.defineProperty
2Vue3 对ts的支持更好
3.vue2组件属性方式Options API(选项式api)变成CompositionAPI(组合式api)函数式风格。
**选项式api:**vue文件中data,methods,computed,watch
中定义属性和方法,共同处理页面逻辑
优点:上手容易
缺点:项目小还好,清晰明了,但是项目大了后,一个methods
中可能包含很多个方法,往往分不清哪个方法对应着哪个功能,而且当你想要新增一个功能的时候你可能需要在 data,methods,computed,watch
中都要写一些东西,但是这个时候每个选项里面的内容很多你需要上下来回的翻滚,特别影响效率。
**组合式api:**把原来创建响应式数据 计算属性等内容变成了一个个api 更好的逻辑复用 更灵活的代码组织
vue3 项目的两种创建方式
1.vue/cli
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3yGAUsKg-1687790777274)(C:\Users\孙小二\AppData\Roaming\Typora\typora-user-images\image-20230605214755254.png)]
2.vite
Vue3 新特性函数 ---- setup 启动器
1.什么是setup
1.setup函数是处于 生命周期函数 beforeCreate 和 Created 两个钩子函数之间的函数 也就说在 setup函数中是无法 使用 data 和 methods 中的数据和方法的
2、setup函数是 Composition API(组合API)的入口
3、在setup函数中定义的变量和方法最后都是需要 return 出去的 不然无法再模板中使用
注意:vue3和vue2不同的地方就是不必写 data、methods、等代码块了所有的东西都可以在 setup 中返回
2.setup的返回值以及参数
1.setup的两种返回值和参数
返回对象、对象中的属性,方法都可以直接在模板中使用
返回渲染函数,可以自定义渲染内容(render函数)
注意:渲染函数优先级比较高,他会替换掉模板中的其他内容
h()函数和createVNode()函数的使用
h()函数和createVNode()函数都是创建dom节点,他们的作用是一样的,但是在VUE3中createVNode()函数的功能比h()函数要多且做了性能优化,渲染节点的速度也更快。
语法:
h(标签, {属性},内容)
setup`选项里直接`return ()=> h()` 或者多个`return ()=> [h(),h()]
<template>
<div>
</div>
</template>
<script>
// 1.要使用先引用
import {h} from "vue"
export default {
setup() {
return () => h('div',{class:"demodiv"}, ['Hello,Vue3'])
}
}
</script>
<style>
</style>
h(标签, {属性},[可以继续嵌套h()]),createVNode同h
<script>
import { createVNode } from "vue";
export default {
setup() {
return () => [
createVNode("div", { class: "demodiv" }, [
"ggg",
createVNode("p", { class: "demo" }, ["搞笑女"]),
]),
createVNode("h3", { class: "demoa" }, ["搞笑男"]),
];
},
};
</script>
setup的两个参数(props,context)
props是一个对象,包含父组件传递给子组件的所有数据。
父组件
<template>
<div>
我是父组件
<Zc title="我是正向传值的数据"/>
</div>
</template>
<script>
import { defineComponent } from 'vue'
import Zc from "../components/ZiCom.vue"
//defineComponent最重要的是:在TypeScript下,给予了组件 正确的参数类型推断
export default defineComponent({
components:{
Zc
},
})
</script>
子组件
<template>
<div>
我是子组件--{{title}}
</div>
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
props:["title"],//子组件设置props
setup(props) {//setup第一个参数是props
console.log(props)//如果想在setup中读取props数据必须设置第一个形参
return {
}
},
})
</script>
为什么要使用setup的第一个形参props来接收父组件传递过来的数据?
之前在vue2中我们接受父组件传递过来的数据 都时使用props:[xxxxxxx,xxxxxx]这种定义方式
在vue3中只是给我们提供了更多的获取方式 ,给我们提供这个第一个形参就是接收父组件传递过来的数据 只是为了更加方便我们在setup中使用父组件的传递数据 仅此而已 而不是必须要使用的
context是setup的第二个参数里面包含了以下三个属性 attrs emit slots
attrs
attrs 获取当前标签上面的所有属性的对象** (还是接受父组件传递过来的数据 )
注意 attrs是接收props没有声明的属性
**注意 **如果子组件用props已经声明 就不能使用attrs 否则会返回undefind
父组件
<template>
<div>
我是父组件
<Zc title="我是正向传值的数据"/>
</div>
</template>
<script>
import { defineComponent } from 'vue'
import Zc from "../components/ZiCom.vue"
export default defineComponent({
components:{
Zc
},
})
</script>
子组件
<template>
<div>
我是子组件--{{title}}
</div>
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
// props:["title"],//如果使用attrs接收就不能声明props否则接收不到
setup(props,context) {//setup第2个参数是context
console.log(context.attrs)
return {
}
},
})
</script>
3.什么时候用props、context来接受数据
1.如果你接收来的数据想在页面展示的时候你使用props来接收(使用props接受的时候 必须定义props这个属性)
2.如果父组件传递过来的数据你不想在页面展示 只是想使用这个数据进行一些逻辑处理 那么你可以使用context.attrs来进行接受(如果想使用 不能定义props)
4.总结setup
setup是vue3中的CompositionAPI的启动器 他是会在beforecreate 与created之间去执行的 setup有2个形参 2种返回值
2个形参分别是 props和context 其中props就是接收父组件传递过来的属性 context其中包含的内容就比叫多了 他有三个属性 attrs emit slots
attrs获取当前标签上面的所有属性的对象 但是不能和props属性连用 如果连用了 那么会返回什么都没有
3.ref的使用
在vue2.0中 ref就是对dom进行找到与获取的
但是在vue3.0中他有了更多的功能
ref:的主要作用是定义数据和进行dom操作
ref :它接收一个参数作为值,然后返回一个响应式对象(简单来说就是可以创建基本数据类型的响应式变量)
要是想改变 ref 的值 必须改变它里面有个属性 value
ref创建基本类型数据
<template>
<div>
我是一个组件-{{ text }}
<button @click="fun()">点我修改</button>
</div>
</template>
// <script>
// export default {
// // 旧方式
// setup() {
// let text = ref("你好我是数据");
// let fun = () => {
// text.value = "我变了";
// };
// return {
// text,
// fun,
// };
// },
// };
// </script>
<script setup>
import { ref } from "vue";
let text = ref("你好我是数据");
let fun = () => {
text.value = "我变了";
};
</script>
ref操作dom元素
<template>
<div>
<!-- 3.绑定 -->
<h1 ref="demoh">我是一个dom</h1>
<button @click="fun()">点我获取</button>
</div>
</template>
<script setup>
//1.引用
import { ref } from "vue";
// 2.创建
let demoh = ref(null);
let fun = () => {
// 4使用 这个.value是ref获取数据的时候必要的
console.log(demoh.value)
};
</script>
ref总结
在vue3中 ref可以创建基本数据类型的响应式变量 ref在创建出基本数据类型之外 想要修改的话 必须.value来进行修改
ref 如果传递null 就变成了页面dom操作的一员 在查找dom的时候 不要忘了也要.value 才能获取到dom数据
4.reactive创建引用类型
<template>
<div>
<h1>{{obj.name}}</h1>
<button @click="fun()">修改</button>
</div>
</template>
<script setup>
import { reactive } from "vue";
let obj = reactive({
name:"xixi",
age:18
});
let fun = () => {
obj.name="haha"
};
</script>
5.emit自定义事件
emit事件分发 用于子传父 如果子组件的数据想传递给父组件 就是用emit(逆向传值)
子组件
<template>
<div>
我是子组件
<button @click="zifun">点我抛出事件</button>
</div>
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
setup(props,context) {
// 子组件通过context的emit方法进行数据的抛出
let zifun=()=>{
context.emit("zipao",'我是子组件的数据')
}
return {//不要忘了return
zifun
}
},
})
</script>
父组件
<template>
<div>
我是父组件
<!-- 接收子组件的数据 注意不加()不加()-->
<Zc @zipao="fufun"/>
</div>
</template>
<script>
import { defineComponent } from 'vue'
import Zc from "../components/ZiCom.vue"
export default defineComponent({
components:{
Zc
},
setup() {
let fufun=(val)=>{
console.log("子组件的数据",val)
}
return {
fufun
}
},
})
</script>
计算属性computed
计算属性computed是用对数据进行逻辑处理操作,实现数据包装。计算属性通常依赖于当前vue对象中的普通属性
当依赖的依赖的普通属性发生变化的时候,计算属性也会发生变化。
基本使用
<template>
<div>
<input type="text" v-model="data">
<h1>默认的:{{data}}</h1>
<h1>变大写的:{{newdata}}</h1>
</div>
</template>
<script setup>
import {ref,computed} from "vue"
let data=ref("abcdefg");
// 计算属性
let newdata=computed(()=>{
return data.value.toUpperCase()
})
</script>
watch监听
监听响应式数据 当监听的数据改变了 watch就会触发 执行一些操作
1.监听单个数据
<template>
<div>
<input type="text" v-model="data" />
<h1>{{ data }}</h1>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
let data = ref("abcdefg");
// 第一个参数是你要监听的数据
// 第二个参数是数据改变之后触发的函数
watch(data, (newVal, oldVal) => {
console.log("newVal", newVal);
console.log("oldVal", oldVal);
});
</script>
<style>
</style>
2.监听多个数据
将需要监听的数据添加到数组
<template>
<div>
<input type="text" v-model="data" />
<h1>{{ data }}</h1>
<input type="text" v-model="datab" />
<h1>{{ datab }}</h1>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
let data = ref("第1个数据");
let datab = ref("第2个数据");
// 第一个参数是你要监听的数据多个数据使用数组来表示
// 第二个参数是数据改变之后触发的函数
watch([data,datab], (newVal, oldVal) => {
console.log("newVal", newVal);
console.log("oldVal", oldVal);
});
</script>
<style>
</style>
3.watch监听对象
在vue2中watch默认是无法监听到对象中的属性 必须手动开启deep深度监听
在vue3中默认开启deep深度监听(但是注意如果使用默认的watch语法当 监听值为响应式对象时,oldValue值将出现异常,此时与newValue相同)
监听对象属性正确写法
在监听对象的时候我们可以把第一个参数设置成函数并且返回你要监听的数据 这样new和oldval正常了
<template>
<div>
<input type="text" v-model="obj.name" />
<h1>{{ obj.name }}</h1>
</div>
</template>
<script setup>
import { reactive, watch } from "vue";
let obj=reactive({
name:"xixi",
age:18
})
// 在监听对象的时候我们可以把第一个参数设置成函数并且返回你要监听的数据
watch(()=>{return obj.name}, (newVal, oldVal) => {
console.log("newVal", newVal);
console.log("oldVal", oldVal);
});
</script>
<style>
</style>
监听多个对象属性
监听多个对象属性的时候使用数组来进行设置
<template>
<div>
<input type="text" v-model="obj.name" />
<input type="text" v-model="obj.age" />
<h1>{{ obj.name }}</h1>
<h1>{{ obj.age }}</h1>
</div>
</template>
<script setup>
import { reactive, watch } from "vue";
let obj=reactive({
name:"xixi",
age:18
})
// 监听多个对象属性的时候使用数组来进行设置
watch([()=>{return obj.name},()=>{return obj.age}], (newVal, oldVal) => {
console.log("newVal", newVal);
console.log("oldVal", oldVal);
});
</script>
手动开启deep深度监听
当监听响应式对象的属性为复杂数据类型时,需要开启deep深度监听
比如下面得数据 我们监听obj下的user vue3也没有办法了
let obj = reactive({
user: {
name: "xixi",
age: 18,
},
});
<template>
<div>
<input type="text" v-model="obj.user.name" />
<input type="text" v-model="obj.user.age" />
<h1>{{ obj.user.name }}</h1>
<h1>{{ obj.user.age }}</h1>
</div>
</template>
<script setup>
import { reactive, watch } from "vue";
let obj = reactive({
user: {
name: "xixi",
age: 18,
},
});
// 监听多个对象属性的时候使用数组来进行设置
watch(
() => {return obj.user;},
(newVal, oldVal) => {
console.log("newVal", newVal);
console.log("oldVal", oldVal);
},
// 第三个参数就是设置deep
{
deep:true
}
);
</script>
<style>
</style>
但是newval和oldval还是有问题 但是数据修改函数可以触发
4.watch总结
vue3中的watch作用和vue2是一样的都是对数据进行监听当数据改变的时候watch就会知道从而执行一个异步操作
在vue3中组合式api的watch他在监听基本类型和复杂类型的时候语法有所不同
在监听基本类型的时候watch的第一个参数就是你要监听的内容 第二个参数就是监听内容改变之后触发的函数 如果又多个监听项需要使用数组来容纳多个监听内容同
在监听复杂类型的时候 vue3watch默认是开启deep的 如果直接监听复杂数据 可能会出现 数据改变了 但是监听函数中的newval和oldval相同的问题 解决方式就是在监听的时候把第一个参数变成一个函数return 我们要监听的内容 即可解决
但是在监听数据的时候 如果监听的数据的属性也是复杂类型那么默认就不行了 我们必须在watch上设置第三个参数 是一个对象 写入deep为true开启深度监听
生命周期
<template>
<div>
</div>
</template>
<script setup>
import {
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
} from "vue";
// 其他的生命周期
onBeforeMount(() => {
console.log("App ===> 相当于 vue2.x 中 beforeMount");
});
onMounted(() => {
console.log("App ===> 相当于 vue2.x 中 mounted");
});
// 注意,onBeforeUpdate 和 onUpdated 里面不要修改值
onBeforeUpdate(() => {
console.log("App ===> 相当于 vue2.x 中 beforeUpdate");
});
onUpdated(() => {
console.log("App ===> 相当于 vue2.x 中 updated");
});
onBeforeUnmount(() => {
console.log("App ===> 相当于 vue2.x 中 beforeDestroy");
});
onUnmounted(() => {
console.log("App ===> 相当于 vue2.x 中 destroyed");
});
</script>
<style>
</style>
过滤器
vue中 在vue1x中过滤器有内置过滤器和自定义过滤器 但是到了2x中取消了内置过滤器 在3x中啥都没有了
vue3取消了vue2中的自定义过滤器,但是变相一下,在vue3中可以把数据当成函数的实参传递给一个函数 这样一来就可以使用一个函数来模拟原来的vue2过滤器
比如我们要过滤下 一个数据
<template>
<div>
<h1>正常展示:{{data}}</h1>
<!-- 创建一个函数模拟过滤器 -->
<h1>只显示年:{{setdata(data)}}</h1>
</div>
</template>
<script setup>
import {ref} from "vue"
let data=ref("2022-10-1")
let setdata=(val)=>{
return val.substr(0,4)
}
</script>
<style>
</style>
插槽–slot
就是给组件设置了一个开口(组件默认是一个完整地独立地个体 外部内容不能插入 如果你想插入数据可以直接使用props)槽口/插槽是吧一个dom元素传入到组件中 传递了dom就可以提高组件的复用性
1.默认插槽
定义slot接受
<template>
<div>
<h1>我是子组件</h1>
<slot></slot>
</div>
</template>
<script setup>
</script>
<style>
</style>
插入的时候需要使用#default来设置
<template>
<div>
<h1>我是父组件</h1>
<ZiView>
<template #default>
<h1>我是内容</h1>
<h1>我是内容</h1>
<h1>我是内容</h1>
</template>
</ZiView>
</div>
</template>
<script setup>
import ZiView from "../components/ZiView.vue"
</script>
<style>
</style>
2.具名插槽
使用name起名
<template>
<div>
<h1>我是子组件</h1>
<slot name="main"></slot>
</div>
</template>
<script setup>
</script>
<style>
</style>
使用#slot的名字 代替原来2.0的slot属性
<template>
<div>
<h1>我是父组件</h1>
<ZiView>
<!-- 使用#slot的名字 代替原来2.0的slot属性 -->
<template #main>
<h1>我是内容</h1>
<h1>我是内容</h1>
<h1>我是内容</h1>
</template>
</ZiView>
</div>
</template>
<script setup>
import ZiView from "../components/ZiView.vue"
</script>
<style>
</style>
3.作用域插槽
关于插槽就是无非就是在子组件中挖个坑,坑里面放什么东西由父组件决定。而作用域插槽就是父组件可以直接拿到子组件的值
定义子组件
<template>
<div>
<h1>我是子组件</h1>
<!-- 向父组件 传递数据 以data接受 -->
<slot :data="item"></slot>
</div>
</template>
<script setup>
import {ref} from "vue"
let item=ref("我是数据")
</script>
<style>
</style>
父组件
<template>
<div>
<h1>我是父组件</h1>
<ZiView>
<!-- 使用#slot的名字 代替原来2.0的slot属性 -->
<template #default="{ data }"> {{ data}} </template>
</ZiView>
</div>
</template>
<script setup>
import ZiView from "../components/ZiView.vue"
</script>
<style>
</style>
组件
全局组件—component
1.在main.js中引用所需要的组件
2.使用component调用
3.在任意组件可以直接使用
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
// 1.引用
import SunView from "./components/SunView.vue";
// 2.使用component调用
createApp(App).component('SunView',SunView).use(store).use(router).mount('#app')
局部组件
<template>
<div>
<!--2使用-->
<ZiView/>
</div>
</template>
<script setup>
// 1.要使用先引用
import ZiView from "@/components/ZiView.vue"
</script>
<style>
</style>
组件传值
1.正向传值–defineProps
旧语法
子组件接收正向传值的语法同vue2x
<script>
import { defineComponent } from 'vue'
export default defineComponent({
// 旧语法和vue2x一样使用props
props:["title","num"],
setup() {
},
})
</script>
新语法
子组件
<template>
<div>
我是一个子组件--{{text}}
</div>
</template>
<script setup>
import {defineProps} from "vue"
// 定义接受参数
defineProps({
text:{
type:String
}
})
</script>
父组件
<template>
<div>
<ZiView text="我是正向传值"/>
</div>
</template>
<script setup>
// 1.要使用先引用
import ZiView from "@/components/ZiView.vue"
</script>
总结
vue中组件传值正传—我们可以使用选项式api props来进行传递 props也可以设置验证写法 在vue3中 正向传值的方式 首先可以使用setup的第一个形参props来进行接收 同时我们也可以使用 setup的第二个形参context其中的atters属性来接收 但是两种方式有点区别 1.如果你接收来的数据想在页面展示的时候你使用props来接收(使用props接受的时候 必须定义props这个属性)
2.如果父组件传递过来的数据你不想在页面展示 只是想使用这个数据进行一些逻辑处理 那么你可以使用context.attrs来进行接受(如果想使用 不能定义props)
2.逆向传值
逆向传值–defineEmits
在vue2x中逆向传值使用this.$emit()自定义事件来完成
子组件–使用emits抛出自定义事件
<template>
<div>
我是一个子组件
<button @click="fun()">点我逆向传值</button>
</div>
</template>
<script setup>
import {defineEmits} from "vue"
const emits = defineEmits(['xiaoming'])
let fun=()=>{
emits('xiaoming',"我是子组件数据")
}
</script>
<style>
</style>
父组件–接受自定义事件
<template>
<div>
<ZiView @xiaoming="fufun"/>
</div>
</template>
<script setup>
// 1.要使用先引用
import ZiView from "@/components/ZiView.vue"
let fufun=(val)=>{
console.log("父组件",val)
}
</script>
<style>
</style>
逆向传值–ref—defineExpose
在vue3中如果你直接给子组件什么绑定一个ref那么你是什么都拿不到的 必须要使用下面的方式暴漏
子组件—千万不要忘了 ref方式传值 需要在子组件使用defineExpose(子组件暴露自己的属性)
<template>
<div>
<h1>我是子组件--{{ obj.name }}</h1>
</div>
</template>
<script setup>
import { reactive ,defineExpose} from "vue";
let obj = reactive({
name: "xixi",
age: 18,
});
// 千万不要忘了 ref方式传值 需要在子组件使用defineExpose(暴露自己的属性) 暴露自己的属性
defineExpose({ obj })
</script>
父组件
<template>
<div>
<!-- 2.绑定到组件之上 -->
<ZiView ref="com"/>
<button @click="fun()">点我得到子组件的内容</button>
</div>
</template>
<script setup>
import ZiView from "@/components/ZiView.vue"
import {ref} from "vue"
// 1.创建ref
let com=ref(null)
let fun=()=>{
console.log(com.value.obj)
}
</script>
总结
在vue中如果想进行逆向传值 可以使用$emit自定义事件来完成 但是到了v3中我们就需要使用组合式api的defineemits来完成
同时我们也可以使用ref的方式来完成逆向传值 但是在组合式api中 我们需要在子组件中使用defineExports来暴漏自己的属性才能正常获取
当然也可以使用setup启动器的context形参中的emit属性来进行逆向传值
3.跨组件provide/inject
provide和inject是Vue中提供的一对API,该API可以实现父组件向子组件传递数据,无论层级有多深,都可以通过这对API实现
祖先组件–使用provide传递
<template>
<div>
<h1>爷组件</h1>
<ZiView ref="com"/>
</div>
</template>
<script setup>
import ZiView from "@/components/ZiView.vue"
import {ref,provide} from "vue"
let yeText=ref("我是爷爷组件的数据")
// 向子组件提供数据
provide('list', yeText.value)
</script>
<style>
</style>
后代组件–inject使用数据
<template>
<div>
我是孙组件--{{yeText}}
</div>
</template>
<script setup>
import { inject } from 'vue'
// 接受父组件提供的数据
const yeText = inject('list')
</script>
<style>
</style>
4.跨组件传值–vuex 4x
toRef与toRefs(修改对象的值)
修改对象值的四种方式:
1.直接对象.属性=属性值
2.ref(直接使用解构赋值,不能改变数据;因为vue是响应式数据,直接解构出来的变量不是响应式数据,需要借助toref)
3.toRef
4.toRefs
<template>
<div>
<h1>{{ name }}---------{{ age }}</h1>
<button @click="fun">修改</button>
</div>
</template>
<script setup>
import { reactive, toRef, toRefs, ref } from "vue";
let obj = reactive({ name: "hh", age: 20 });
//利用ref改变对象的值
// let name = ref(obj.name);
// let age = ref(obj.age);
// let fun = () => {
// //注意要使用.value取值
// name.value = "小白";
// age.value = 19;
// };
//利用toRef
// let name = toRef(obj, "name");
// let age = toRef(obj, "age");
// let fun = () => {
// name.value = "uuuu";
// age.value = 322;
// };
//利用toRefs
// let { name, age } = toRefs(obj);
// let fun = () => {
// name.value = "uuuu";
// age.value = 322;
// };
</script>
vue3新组件
1.Fragment
在vue2中组件必须有一个根组件包裹多行内容 (这个根组件仅仅是包裹的作用 会在页面生成多余的冗余标签)
在vue3中 组件是可以不需要写根标签的 因为vue3会创建一个虚拟的Fragment根标签自动帮助我们包裹
优点:减少了不必要的标签渲染 减少过多的内存损耗
2.Teleport
Teleport 是什么 (TP传送)
Teleport 可以把我们组件中的dom内容 移动到当前项目的任意dom节点中
使用Teleport 其中to属性就是传送到那个dom上
<template>
<div>
<div class="demoa">
<h1>我是demoa</h1>
</div>
<div class="demob">
<h1>我是demob</h1>
<button @click="bool = !bool">点我显示隐藏</button>
<teleport to="body">
<div v-if="bool">
<p>我是占位的</p>
<p>我是占位的</p>
<p>我是占位的</p>
<p>我是占位的</p>
<p>我是占位的</p>
<p>我是占位的</p>
<p>我是占位的</p>
</div>
</teleport>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
let bool = ref(true);
</script>
<style scoped>
.demoa {
background-color: pink;
}
.demob {
background-color: goldenrod;
}
</style>
使用场景 : 比如在某个组件中我们需要有一个弹框 弹出在页面中 那么就可以使用teleport 把这个组件内部的dom挂载到组件之外的地方 方便我们设置层级
vite
1、搭建第一个Vite项目
兼容性注意
Vite 需要 Node.js 版本 >= 12.0.0。
pnpm create vite
原来咱们学习v3的时候 使用的是vue/cli脚手架(实质上是帮助我们封装好了webpack来帮助我们构建与编译项目)
Vite 是 vue 的作者尤雨溪在开发 vue3.0的时候开发的一个 web 开发构建工具。
Vite 和 Webpack 区别
Vite 优势:
1.vite 开发服务器启动速度比 webpack 快
webpack 会先打包,然后启动开发服务器,请求服务器时直接给予打包结果。
vite 在启动开发服务器时不需要打包,也就意味着不需要分析模块的依赖、不需要编译,因此启动速度非常快。当浏览器请求某个模块时,再根据需要对模块内容进行编译。这种按需动态编译的方式,极大的缩减了编译时间,项目越复杂、模块越多,vite的优势越明显。
2.vite 热更新比 webpack 快
当改动了一个模块后,vite仅需让浏览器重新请求该模块即可,不像webpack那样需要把该模块的相关依赖模块全部编译一次,效率更高。
3.vite 构建速度快,比 webpack 的 nodejs,快 10-100 倍。
Vite 劣势:
1.生态不及webpack,加载器、插件不够丰富
2.项目的开发浏览器要支持 ES Module,而且不能识别 CommonJS 语法
vite+vue-router4
1.创建文件夹用来容纳 页面views与路由规则router
2. 新建路由页面
3.配置路由规则–与下载路由
pnpm install --save vue-router
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/home',
name: 'home',
component: () => import('../views/HomeView.vue')
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
4 设置路由出口
在app.vue中设置 router-view
5 注入路由
在main.js中引用使用路由
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
vuex4
1.下载
pnpm install --save vuex
2,创建对应容纳文件夹 store
3.编写vuex相关内容
在store/index.js中创建
import { createStore } from 'vuex'
export default createStore({
state: {
},
getters: {
},
mutations: {
},
actions: {
},
modules: {
}
})
4.在项目中配置vuex
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'
import store from './store'
createApp(App).use(store).use(router).mount('#app')
Vue Router4路由
Vue3支持最新版本由于Vue 3 引入了createApp API,该API更改了将插件添加到Vue实例的方式。 因此,以前版本的Vue Router将与Vue3不兼容。Vue Router 4 引入了createRouter API,该API创建了一个可以在Vue3中安装 router 实例。
1.路由配置
同传统路由
2.路由模式
History模式
History选项在Vue Router 4中,这些模式已被抽象到模块中,可以将其导入并分配给新的history
选项。
import { createRouter, createWebHistory } from "vue-router";
export default createRouter({
history: createWebHistory(),//定义history模式url不带#
routes: [],
});
hash模式
import { createRouter, createWebHashHistory } from "vue-router";
export default createRouter({
history: createWebHashHistory(),//定义hash模式
routes: [],
});
3.路由导航
声明式
同传统路由—router-link
编程式–useRouter()
使用useRouter() 来替代 this.$router
<template>
<div>
<button @click="fun">dianwo</button>
</div>
</template>
<script setup>
import { useRouter} from "vue-router";
// useRouter赋值给一个变量
let router = useRouter();
let fun=()=>{
router.push("/")
}
</script>
4.动态路由匹配–useRoute()
params
1.路由规则配置接受参数
2.发送数据–同之前
3.接受数据
原来在vue-router3中 使用的是this. r o u t e . p a r m a s . x x x x 现在使用 u s e R o u t e 代替了 t h i s . route.parmas.xxxx 现在使用useRoute代替了this. route.parmas.xxxx现在使用useRoute代替了this.route
<template>
<div class="home">
</div>
</template>
<script lang="ts" setup>
import {onMounted } from 'vue';
import {useRoute} from "vue-router";
let route=useRoute()
onMounted(()=>{
// 接收params
console.log(route.params.xiaoming)
})
</script>
query
1.发送
2.接受
<template>
<div class="home">
</div>
</template>
<script lang="ts" setup>
import {onMounted } from 'vue';
import {useRoute} from "vue-router";
let route=useRoute()
onMounted(()=>{
// 接收query
console.log(route.query.xiaoming)
})
</script>
5.路由守卫
pinia的使用
pinia是一个用于vue的状态管理库,类似于vuex,是vue的另一种状态管理工具
Pinia由很多小块组成。在pinia中,每个store都是单独存在,一同进行状态管理。
很多人也将pinia称为vuex5,因为pinia将作为vue3推荐的状态管理库,而vuex将不再迭代。
1.安装(仅限于vue3)
- 通过你喜欢的包管理器安装
pnpm install pinia yarn add pinia npm install --save pinia
2.在main.js中创建并挂载pinia实例
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
// 引用
import {createPinia } from 'pinia'
// 创建Pinia实例
const Pinia = createPinia()
// 挂载pinia
createApp(App).use(Pinia).mount('#app')
3.在src目录下新建一个stroe文件夹,在文件夹中新建一个index.ts
4.通过import将下载好的pinia引入到index.ts中并导出 并且使用defineStore创建store对象
import { defineStore } from 'pinia'
// 第一个参数是应用程序中 store 的唯一 id
export let useStore = defineStore('main', {
})
1.state
state 是 store 的核心部分。 我们通常从定义应用程序的状态开始。 在 Pinia 中,状态被定义为返回初始状态的函数。 Pinia 在服务器端和客户端都可以工作。
import { defineStore } from 'pinia'
export let useStore = defineStore('main', {
state: () => {//创建状态
return {
text: "我是pinia的一个状态变量"
}
}
})
组件内使用
先引入js,再调用pinia实例,最后直接通过打点的方式使用state里的变量
<template>
<div>
{{ store.text }}
</div>
</template>
<script setup>
import { useStore } from "../store/index.js";
let store = useStore();
</script>
2.pinia–修改–action
修改方式1
在js中创建更改方法
actions: {
upDate() {
this.text = "我的确变了"
}
}
在组件中通过点击事件调用方法
<template>
<div>
<h1>
{{ store.text }}
</h1>
<button @click="updata()">点我修改内容</button>
</div>
</template>
<script setup>
import { useStore } from "../store/index.js";
let store = useStore();
let updata = () => {
store.upDate();
};
</script>
修改方式2–$patch
$patch是pinia的内置方法 通过该方法可以直接修改state的数据
1.方式1
在组件内调用$patch 便可直接修改数据
let update=()=>{
store.$patch({
//你要修改state的数据: 你要修改的value值
text:"我是$patch直接修改"
})
}
2.方式2
let update=()=>{
store.$patch((state)=>{
console.log(state)
state.text="你又被改了"
})
}
3.pinia–修改常见问题
利用解构法获得state里的数据时,数据会丢失响应式;需要配合toRefs进行解构
4.pinia–重置数据–$reset
顾名思义就是让数据恢复成state的初始值
<template>
<div>
我是测试pinia的组件--{{store.text}}
<button @click="reset()">点我恢复初始值</button>
</div>
</template>
<script lang="ts" setup>
import {useStore} from "../store/index"
let store=useStore()
let reset=()=>{
// $reset
store.$reset()
}
</script>
5.pinia–监听数据修改–$subscribe
$subscribe 监听store数据修改 当数据改变了 那么subscribe也会触发
<template>
<div>
我是测试pinia的组件--{{store.text}}
<button @click="update()">点我修改</button>
</div>
</template>
<script lang="ts" setup>
import {useStore} from "../store/index"
let store=useStore()
let update=()=>{
store.updatetext()
}
// 监听store数据修改
let sub=store.$subscribe((params,state)=>{
console.log("params",params);
console.log("state",state);
})
</script>
6.pinia–处理异步操作–action
在pinia中acition不但能处理同步操作 同样也可以处理异步操作
使用方式和之前一致
<template>
<div>
我是测试pinia的组件--{{store.text}}
<button @click="demoAxios()">点我获取异步数据</button>
</div>
</template>
<script lang="ts" setup>
import {useStore} from "../store/index"
let store=useStore()
let demoAxios=()=>{
store.link()
}
</script>
<style>
</style>
import { defineStore } from 'pinia'
import $http from "axios"
export const useStore = defineStore('main', {
state:()=>{
return {
text:"我是pinia的一个状态变量"
}
},
actions:{
link().then((ok)=>{
console.log(ok)
})
async link(){
try {
let data= await $http({
url:"http://localhost:8888/userlist/demoget",
method:"get"
})
console.log(data.data.data.name)
this.text=data.data.data.name
} catch (error) {
console.log("失败")
}
}
}
})
7.pinia–getters
pinia的计算属性 他和计算属性最大的区别是 getters处理的数据可以在很多个地方来使用
import { defineStore } from 'pinia'
export const useStore = defineStore('mainb', {
state:()=>{
return {
text:"abcdefg"
}
},
actions:{
},
getters:{
newtext(){
return this.text.toUpperCase()
}
}
})
8.pinia–模块化
pinia中定义模块 只需要定义多个store即可 因为pinia没有单一数据源这个概念 在其中可以定义多个store对象
注意:defineStore里第一个参数,可以用来区分模块
import { defineStore } from 'pinia'
export let useStore = defineStore('main', {
state: () => {//创建状态
return {
text: "我是pinia的一个状态变量"
}
},
})
export let useStorea = defineStore('xiaohong', {
state: () => {//创建状态
return {
text: "我"
}
}
})
使用
<template>
<div>
<h1>
{{ store.text }}
</h1>
<p>
{{ storea.text }}
</p>
</div>
</template>
<script setup>
import { useStore } from "../store/index.js";
import { useStorea } from "../store/index.js";
let store = useStore();
let storea = useStorea();
</script>
与vuex有什么不同
与 Vuex 相比,Pinia 提供了一个更简单的 API,具有更少的规范,提供了 Composition-API 风格的 API,最重要的是,在与 TypeScript 一起使用时具有可靠的类型推断支持。
1.mutations 不再存在。
2.无需创建自定义复杂包装器来支持 TypeScript,所有内容都是类型化的,并且 API 的设计方式尽可能利用 TS 类型推断。
3.不再需要注入、导入函数、调用函数、享受自动完成功能!
4.无需动态添加 Store,默认情况下它们都是动态的
re(‘main’, {
state:()=>{
return {
text:“我是pinia的一个状态变量”
}
},
actions:{
link().then((ok)=>{
console.log(ok)
})
async link(){
try {
let data= await $http({
url:“http://localhost:8888/userlist/demoget”,
method:“get”
})
console.log(data.data.data.name)
this.text=data.data.data.name
} catch (error) {
console.log("失败")
}
}
}
})
### 7.pinia--getters
pinia的计算属性 他和计算属性最大的区别是 getters处理的数据可以在很多个地方来使用
````js
import { defineStore } from 'pinia'
export const useStore = defineStore('mainb', {
state:()=>{
return {
text:"abcdefg"
}
},
actions:{
},
getters:{
newtext(){
return this.text.toUpperCase()
}
}
})
8.pinia–模块化
pinia中定义模块 只需要定义多个store即可 因为pinia没有单一数据源这个概念 在其中可以定义多个store对象
注意:defineStore里第一个参数,可以用来区分模块
import { defineStore } from 'pinia'
export let useStore = defineStore('main', {
state: () => {//创建状态
return {
text: "我是pinia的一个状态变量"
}
},
})
export let useStorea = defineStore('xiaohong', {
state: () => {//创建状态
return {
text: "我"
}
}
})
使用
<template>
<div>
<h1>
{{ store.text }}
</h1>
<p>
{{ storea.text }}
</p>
</div>
</template>
<script setup>
import { useStore } from "../store/index.js";
import { useStorea } from "../store/index.js";
let store = useStore();
let storea = useStorea();
</script>
与vuex有什么不同
与 Vuex 相比,Pinia 提供了一个更简单的 API,具有更少的规范,提供了 Composition-API 风格的 API,最重要的是,在与 TypeScript 一起使用时具有可靠的类型推断支持。
1.mutations 不再存在。
2.无需创建自定义复杂包装器来支持 TypeScript,所有内容都是类型化的,并且 API 的设计方式尽可能利用 TS 类型推断。
3.不再需要注入、导入函数、调用函数、享受自动完成功能!
4.无需动态添加 Store,默认情况下它们都是动态的
5.不再有 modules 的嵌套结构