Appearance
1.什么是Vite
Vite 下一代前端开发与构建工具,用来构建前端工程,特点:很快!很快!很快!!!
vue-cli 也是一种基于webpack 的构建工具,相比 Vite 他比较慢 ,所以慢慢的用的人不多了 ,所以 我们来学习Vite吧 ,大大提高效率
| 💡 极速的服务启动 | ⚡️ 轻量快速的热重载 | 🛠️ 丰富的功能 |
|---|---|---|
| 使用原生 ESM 文件,无需打包! | 无论应用程序大小如何,都始终极快的模块热重载(HMR) | 对 TypeScript、JSX、CSS 等支持开箱即用。 |
| 📦 优化的构建 | 🔩 通用的插件 | 🔑 完全类型化的API |
| 可选 “多页应用” 或 “库” 模式的预配置 Rollup 构建 | 在开发和构建之间共享 Rollup-superset 插件接口。 | 灵活的 API 和完整 TypeScript 类型。 |
2.基于Vite搭建Vue3项目
方式1
shell
npm init vite@latest projectname -- --template vue参数说明:
| init | vite@latest | projectname | --template vue |
|---|---|---|---|
| 初始化项目 | 使用Vite的最新版本 | 项目名称 | 项目类型是vue |

方式2【推荐】
这种方式 会让你选择 前端框架 , js 还是 ts
shell
npm create vite
3.安装 router
shell
npm install vue-router@4 -S在 src 下面创建 router 文件夹 , router文件夹下创建 index.js
js
import {
createRouter,
createWebHistory
} from 'vue-router'
import Home from "../views/Home.vue"
const routes = [{
path:"/",
name:"Home",
component:Home
},{
path:"/about",
name:"About",
component:()=>import("../views/About.vue")
}]
const router = createRouter({
history:createWebHistory(),
routes
})
export default router在main.js 进行配置
js
import { createApp } from 'vue'
import App from './App.vue'
import router from "./router"
createApp(App).use(router).mount('#app')修改App.vue
vue
<script setup>
</script>
<template>
<!-- 路由视口 -->
<router-view></router-view>
</template>
<style scoped>
</style>细节1:vite构件的工程 的 vue 文件的 template 中 不用再定义唯一的 根元素了,下面的写法 没问题
vue
<template>
<div>
Home
</div>
<h1>123</h1>
</template>细节2:vite 中 引入 vue文件时 ,需要 写.vue 后缀 , import Home from "../views/Home.vue" Home 后面的.vue不能省
细节3: 导入 js 文件时 , .js 后缀可以省
4.配置src的别名
修改vite.config.js
js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from "path"
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve:{
alias:{
"@":path.resolve(__dirname,"src")
}
},
})5.vue3 代码风格
Vue3对Vue2代码风格做了兼容 但是 不是完全兼容 优化的地方还是比较多的, 比如 setup 语法糖, vue2 的 js 部分的写法 层次比较分明,叫选项式API , vue3的写法 是 组合式 API,一大堆东西 写在 这里面,看不到 vue2 的 data,methods 等 这些元素
响应式方式1 ref 需要 .value
vue
<template>
<div>
Home
{{ str }}
<button @click="fun1">按钮</button>
</div>
</template>
<script setup>
import {ref} from 'vue';
// let str = "hello";
let str = ref("hello");
function fun1(){
str.value = "world"
}
</script>响应式方式2 reactive 只能修饰 对象 或 数组
vue
<template>
<div>
Home
{{ str }}
<button @click="fun1">按钮</button>
</div>
</template>
<script setup>
import {ref,reactive} from 'vue';
// let str = "hello";
let str = reactive({a:"hello"});
function fun1(){
str.a = "world"
}
</script>6.setup 语法糖插件
解决 import {ref,reactive,....} from 'vue' 引入 过长的问题
安装插件
shell
npm i unplugin-auto-import -D在vite.config.js 中 导入
shell
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import path from "path"
export default defineConfig({
plugins: [
vue(),
AutoImport({
imports:['vue','vue-router']
})
],
resolve:{
alias:{
"@":path.resolve(__dirname,"src")
}
},
})配置好插件后 ,就不用在 vue 页面中 手动导入了 可以去掉这句话
shell
// import {ref,reactive} from 'vue';7.toRefs
vue
<template>
<div>
<!-- {{ obj.name }}
{{ obj.age }} -->
{{ name }}
{{ age }}
<button @click="fun1">按钮</button>
</div>
</template>
<script setup>
import { reactive } from "vue";
let obj = reactive({
name:'zs',
age:19
});
// toRefs 结构响应式数据
let {name,age} = toRefs(obj);
function fun1(){
// obj.name = "ls";
name.value = "ls"
}
</script>8.computed
vue
<template>
<div>
vue3的计算属性:
{{ changeStr }}
</div>
</template>
<script setup>
// import {ref,computed} from 'vue' 因为 咱们安装了插件 所以 这行代码可以注释掉
let str = ref('这是数据')
//计算属性
let changeStr = computed(()=>{
return str.value + 2
})
</script>
<style scoped>
/* //@import url();引入公共css类 */
</style>9.watch
vue
<template>
<div>
<input type="text" v-model="str">
<input type="text" v-model="num">
{{ obj }}
<button @click="fun1">按钮</button>
</div>
</template>
<script setup>
import { reactive, watch, watchEffect } from "vue";
let str = ref("这是一个数据")
let num = ref(100)
let obj = reactive({
a:1,
b:2,
m:{
c:3
}
})
// 监听某一个属性
watch(str,(newVal,oldVal)=>{
console.log(newVal,oldVal);
})
// 同时监听多个属性
watch([str,num],(newVal,oldVal)=>{
console.log(newVal,oldVal);
})
// 页面一初始化 就监听 上来就监听
watch([str,num],(newVal,oldVal)=>{
console.log(newVal,oldVal);
},{
immediate:true
})
const fun1 = ()=>{
obj.a="a"
// obj.m.c = 888
}
// 监听一个对象
watch(obj,(newVal,oldVal)=>{
console.log(newVal)
})
// 只监听一个对象的一个属性
watch(()=>obj.a,(newVal,oldVal)=>{
console.log(newVal,oldVal)
})
// 可以监听所有 相当于 立即执行
watchEffect(()=>{
console.log("11111"+str.value)
console.log("22222"+obj.a)
})
</script>监听路由
vue
// import { useRouter,useRoute } from "vue-router"; 插件中有 所以不需要再导入
// 监听路由
let router = useRouter();
watch(()=>router.currentRoute.value,(newVal)=>{
console.log(newVal)
},{
immediate:true
})10.钩子函数

vue
<template>
<div>
{{ str }}
<button @click="fun">修改</button>
<router-link to="/">跳转</router-link>
</div>
</template>
<script setup>
import { onBeforeUnmount, onUnmounted } from "vue"
// import {ref, onBeforeMount, onMounted, onUpdated } from "vue";
let str = ref("hello")
const fun = ()=>{
str.value = "world"
}
onBeforeMount(()=>{
console.log("页面挂载之前,可以在这里请求接口")
})
onMounted(()=>{
console.log("页面挂载完毕")
})
onBeforeUpdate(() => {
console.log("修改前")
})
onUpdated(()=>{
console.log("修改后")
})
onBeforeUnmount(()=>{
console.log("销毁前")
})
onUnmounted(()=>{
console.log("销毁后")
})
</script>11.路由
vue2用的router版本是3.x vue3 用的router版本是4.x
vue
<template>
<div>
<button @click="goAbout">跳转</button>
</div>
</template>
<script setup>
// import { useRouter,useRoute } from "vue-router"; 有插件
// 等同于 vue2 的 this.$router
let router = useRouter();
// 等同于 vue2 的 this.$route
let route = useRoute();
const goAbout=()=>{
router.push('/about')
}
</script>导航守卫
全局路由守卫
shell
beforeEach(to, from, next) 全局前置守卫,
路由跳转前触发 beforeResolve(to, from, next) 全局解析守卫 在所有组件内守卫和异步路由组件被解析之后触发
afterEach(to, from) 全局后置守卫,路由跳转完成后触发路由独享守卫
shell
beforeEnter(to,from,next) 路由对象单个路由配置 ,单个路由进入前触发组件路由守卫
shell
beforeRouteEnter(to,from,next) 在组件生命周期beforeCreate阶段触发
beforeRouteUpdadte(to,from,next) 当前路由改变时触发
beforeRouteLeave(to,from,next) 导航离开该组件的对应路由时触发参数
shell
to: 即将要进入的目标路由对象
from: 即将要离开的路由对象
next(Function):是否可以进入某个具体路由,或者是某个具体路由的路径demo

12.组件
父组件 调用 子组件 并传递数据
vue
<template>
<div>
父组件
<zizujian :msg="msg"></zizujian>
</div>
</template>
<script setup>
import Zizujian from '../components/zizujian.vue'
let msg = ref("这是父组件传递过去的数据")
</script>vue
<template>
<div>
子组件
{{ msg }}
</div>
</template>
<script setup>
// import {defineProps} from 'vue' 有插件
defineProps({
msg:{
type:String,
default:"默认值"
}
})
</script>子组件传递给父组件
子组件写法 这个是 ts的写法
vue
<template>
<div>
子组件
{{ msg }}
<button @click="method1">调用父组件的函数</button>
</div>
</template>
<script setup lang="ts">
import {ref,defineProps} from 'vue' //有插件
defineProps({
msg:{
type:String,
default:"默认值"
}
})
let num = ref(200);
const emit = defineEmits<{
(e: 'fn',id:number): void
}>()
const method1 = ()=>{
emit('fn',num)
}
</script>子组件写法 这个是 js的写法
vue
<template>
<div>
子组件
{{ msg }}
<button @click="method1">调用父组件的函数</button>
</div>
</template>
<script setup >
import {ref,defineProps} from 'vue' //有插件
defineProps({
msg:{
type:String,
default:"默认值"
}
})
let num = ref(200);
const emit = defineEmits(['fn']);
const method1 = ()=>{
emit('fn',num)
}
</script>父组件写法
vue
<template>
<div>
父组件
<zizujian :msg="msg" @fn="method1"></zizujian>
</div>
</template>
<script setup>
import Zizujian from '../components/zizujian.vue'
let msg = ref("这是父组件传递过去的数据")
const method1=(n)=>{
console.log("子组件传过来的参数"+n.value)
}
</script>13.父子组件使用v-model
父组件
vue
<zizujian2 v-model:num="num"></zizujian2>
let num = ref(99)子组件
vue
<template>
<div>
子组件2
{{ num }}
<button @click="change">修改</button>
</div>
</template>
<script setup>
const props = defineProps({
num:{
type:Number,
default: 100
}
})
const emit = defineEmits(['update:num'])
const change=()=>{
emit('update:num',199)
}
</script>14.兄弟组件数据传递
A组件传递数据给B组件
shell
npm install mitt -S创建@/plugins/Bus.js
js
import mitt from "mitt";
const emitter = mitt();
export default emitter;A组件
vue
<template>
<div>
<h1>A组件</h1>
<button @click="fun">按钮</button>
</div>
</template>
<script setup>
import emitter from '../plugins/Bus'
let str = ref("这是A组件的数据")
const fun = ()=>{
emitter.emit('fn',str)
}
</script>B组件
vue
<template>
<div>
<h1>B组件</h1>
{{ s }}
</div>
</template>
<script setup>
import { onBeforeMount } from 'vue';
import emitter from '../plugins/Bus'
let s = ref('');
onBeforeMount(()=>{
emitter.on('fn',e=>{
console.log("A组件传递过来的数据:"+e.value)
s.value = e.value
})
})
</script>15.插槽 slot
具名插槽
调用方 zujian.vue
vue
<C>
<template v-slot:xxx>
这是沙发
</template>
<!-- v-slot 可以简写为# -->
<template #yyy>
这是床
</template>
</C>被调用组件C
vue
<template>
<div>
<header>
<slot name="xxx"></slot>
</header>
<h1>插槽</h1>
<footer>
<slot name="yyy"></slot>
</footer>
</div>
</template>
<script setup>
</script>作用域插槽
调用方 zujian.vue
vue
<template>
<div>
父组件
<br>
子组件1 <zizujian :msg="msg" @fn="method1"></zizujian>
<br>
子组件2
<zizujian2 v-model:num="num"></zizujian2>
<br>
<A></A>
<B></B>
<br>
<C>
<template v-slot:xxx>
这是沙发
</template>
<!-- v-slot 可以简写为# -->
<template #yyy>
这是床
</template>
<!-- <template v-slot="{data}"> v-slot可以简写为 #default -->
<template #default="{data}">
{{ data.name }} -- {{ data.age }}
</template>
</C>
</div>
</template>
<script setup>
import Zizujian from '../components/zizujian.vue'
import Zizujian2 from '../components/zizujian2.vue'
import A from '../components/A.vue'
import B from '../components/B.vue'
import C from '../components/C.vue'
let msg = ref("这是父组件传递过去的数据")
const method1=(n)=>{
console.log("子组件传过来的参数"+n.value)
}
let num = ref(99)
</script>被调用方
vue
<template>
<div>
<h1>具名插槽</h1>
<header>
<slot name="xxx"></slot>
</header>
<footer>
<slot name="yyy"></slot>
</footer>
<h1>作用域插槽</h1>
<section>
<div v-for="item in list" :key="item.id">
<slot :data="item"></slot>
</div>
</section>
</div>
</template>
<script setup>
let list = ref([
{id:1,name:'zs',age:18},
{id:2,name:'ls',age:19},
{id:3,name:'ww',age:21},
{id:4,name:'zl',age:20},
])
</script>动态插槽 说白了 就是通过变量 动态传入 插槽名字
调用方
vue
<template #[zzz]>
动态插槽
</template>
<script setup>
let zzz = ref('yyy')
</script>16.Taleport
传送 把某一个 布局 或者 盒子 传送到另外一个地方
比如 D组件 传递 内容 到 teleport.vue
teleport.vue
vue
<template>
<div>
<div id="main">
这是main
</div>
<div class="container">
这是container
</div>
</div>
<D></D>
</template>
<script setup>
import D from '../components/D.vue'
</script>D.vue
vue
<template>
<div>
<h1>D组件</h1>
<!-- 传送到id 为 main 的地方 -->
<teleport to="#main">
<div>这是传送</div>
</teleport>
</div>
</template>
<script setup>
</script>可以用 id 或 类别选择器 等 方式 指定传送位置
vue
<div id="main">
这是main
</div>
<div class="container">
这是container
</div>
<teleport to="#main">传送内容</teleport>
<teleport to=".container">传送内容</teleport>注意 这里 的书写位置 有讲究, 传送的目的地 得 先于 teleport 元素 而存在 比如 下面的写法 就有问题
vue
这里 要传送到 id 为 main 的元素 ,结果 id为main 的 元素 还没加载 会报警告
<teleport to="#main">传送内容</teleport>
<teleport to=".container">传送内容</teleport>
<div id="main">
这是main
</div>
<div class="container">
这是container
</div>17.动态组件
dongtaizujian.vue
vue
<template>
<div>
动态组件
<ul>
<li v-for="item in tabList" :key="item.id" @click="change(item.id)">
{{ item.name }}</li>
</ul>
<keep-alive>
<component :is="currentComponent.com"></component>
</keep-alive>
</div>
</template>
<script setup>
import { reactive } from 'vue'
import E from '../components/E.vue'
import F from '../components/F.vue'
import G from '../components/G.vue'
let tabList= reactive([
{name:'E准备面试题',id:1,com:markRaw(E)},
{name:'F准备简历',id:2,com:markRaw(F)},
{name:'G准备项目',id:3,com:markRaw(G)},
])
let currentComponent = reactive({
com:E
})
const change= (id)=>{
currentComponent.com = tabList[id-1].com
}
</script>18.异步组件
yibuzujian.vue
vue
<template>
<div>
异步组件
<E></E>
<F></F>
<div ref="target">
<G v-if="targetIsVisible"></G>
</div>
</div>
</template>
<script setup>
// import { defineAsyncComponent } from 'vue' 有插件
import {useIntersectionObserver} from '@vueuse/core'
import E from '../components/E.vue'
import F from '../components/F.vue'
// import G from '../components/G.vue'
const G = defineAsyncComponent(()=>import('../components/G.vue'))
const target = ref(null);
const targetIsVisible = ref(false);
const { stop } = useIntersectionObserver(
target,
([{isIntersecting}])=>{
if(isIntersecting){
targetIsVisible.value = isIntersecting
}
}
)
</script>安装一个插件
shell
npm i @vueuse/core -S在页面 yibuzujian.vue 配置
vue
import {useIntersectionObserver} from '@vueuse/core'
const G = defineAsyncComponent(()=>import('../components/G.vue'))
const target = ref(null);
const targetIsVisible = ref(false);
const { stop } = useIntersectionObserver(
target,
([{isIntersecting}])=>{
if(isIntersecting){
targetIsVisible.value = isIntersecting
}
}
)观察效果
拉动页面,拉动到有图片的组件时 才加载那个图片地址
![]() | ![]() |
|---|


