创建项目
创建项目
使用vite创建
npm create vite demo
使用vite最新版创建
npm init vite@latest
使用vue3最新版创建(更全)
npm init vue@latest
cd demo
npm i
npm run dev
安装vue-router

快速开始
控制台输入:npm i vue-router@4 -S
创建文件
//创建router/index.ts文件
import { createRouter, RouteRecordRaw, createWebHashHistory } from "vue-router";
const routes: Array<RouteRecordRaw> = [
{
path: "/",
name: 'home',
alias: ["home","homee","homeeees"], // 路由别名
component: () => import("../components/login.vue"),
},
{
path: "/reg",
name: 'regester',
component: () => import("../components/reg.vue"),
},{
path: "/reg/:id",
name: 'regId',
component: () => import("../components/reg.vue"),
},
];
const router = createRouter({
//createWebHashHistory() hash模式 和 createWebHistory() history模式
history: createWebHashHistory(),
//每次进入页面时初始化滚轴位置
scrollBehavior: (to, from, savePosition) => {
return savePosition ? savePosition : {top: 0}
},
routes,
});
export default router;//main.ts导入
import router from "./router/index";
createApp(App).use(router).mount("#app");基本使用
<template>
<div>
<RouterLink replace to="/">没有历史记录</RouterLink>
<RouterLink :to="{name:'home'}">Login</RouterLink>
<RouterLink to="/reg">reg</RouterLink>
<a href="/#/reg">reg 会刷新页面</a>
<RouterView></RouterView>
</div>
</template>
<script setup lang='ts'>
import { ref, reactive } from 'vue'
import { useRouter } from "vue-router";
const router = useRouter()
const btn = (url: string) => {
router.replace('/') //没油历史记录
router.push('/')
router.push({path: '/'})
router.push({name: 'home'})
router.go(2) //前进两步
router.back() //后退
}
</script>路由传参
<script setup lang='ts'>
import { ref, reactive } from 'vue'
import { useRouter } from "vue-router";
const router = useRouter()
const item = { name: "wsname", age: 16, id: 16 }
// query传参
const query = () => router.push({ path: '/reg', query: item })
const query = () => router.push({ name: 'regester', query: item })
// params传参vue-router在4.1.4不再支持
const query = () => router.push({ path: '/reg', params: item })
</script>路由取参
<script setup lang='ts'>
import { useRoute } from "vue-router";
const route = useRoute()
// query取参
// route.query
// params取参
// route.params
</script>路由重定向
const routes: Array<RouteRecordRaw> = [
{
path: "/",
redirect: "/reg" //重定向
name: 'home',
}, {
path: "/",
redirect: (to) => { //重定向
return {
path: "/reg",
query: to.query,
};
},
name: 'home',
},
];前置前置守卫
const router = createRouter({
//createWebHashHistory() hash模式 和 createWebHistory() history模式
history: createWebHashHistory(),
routes,
});
/**
* to :Route 即将要进入的目标路由对象
* from:Route 当前正要离开的路由
* next(): 进行管道中的下一个狗子,如果全部执行完了,则导航的状态就是 confirmed(确认的)
* next(false):中断当前的导航,如果浏览器URL改变了,会重置到 from
* next("/") 或 next({path:'/'}) 跳转到一个不同的地址,当前导航被中断,进行新的一个导航
*/
router.beforeEach((to,from,next)=>{
console.log(to,from);
})简单的权限案例
const whileList = ["/"];
router.beforeEach((to, from, next) => {
let token = localStorage.getItem("token");
//白名单 有值 或者登陆过存储了token信息可以跳转 否则就去登录页面
if (whileList.includes(to.path) || token) {
next();
} else {
next({ path: "/" });
}
});后置前置守卫
router.afterEach((to, from) => {});配置自动导入
按需引入插件
npm i -D unplugin-auto-import
import { defineConfig } from "vite";
import AutoImport from 'unplugin-auto-import/vite'
export default defineConfig({
plugins: [
AutoImport({ imports: ['vue', 'vue-router'] }),
]
//.........
})按需引入组件
npm i -D unplugin-vue-components
import { defineConfig } from "vite";
import Components from 'unplugin-vue-components/vite' // 按需加载自定义组件
import { ElementPlusResolver, AntDesignVueResolver} from 'unplugin-vue-components/resolvers'
export default defineConfig {
plugins: [
Components({
dts: true,
dirs: ['src/components'], // 按需加载的文件夹
resolvers: [
ElementPlusResolver(), // Antd 按需加载
AntDesignVueResolver() // ElementPlus按需加载
]
})
],
}开发移动端项目
安装:npm i postcss-px-to-viewport -D
这个插件帮我们把px转成vw或者vh
添加配置文件:
// vite.config.ts
// 引入
import pxtoViewPort from 'postcss-px-to-viewport'
export default defineConfig({
plugins: [vue(), vueJsx()],
css:{
postcss:{
plugins:[
pxtoViewPort({
unitToConvert: "px", // 需要转换的单位
viewportWidth: 750, // UI设计稿的宽度
unitPrecision: 6, // 转换后的精度,小数点后的位数
propList: ['*'], // 指定的css属性单位,*代表所有的css属性单位转换
viewportUnit: 'vw',// 指定需要转换成的视窗单位,默认vw
fontViewportUnit: 'vw', // 指定字体需要转换成的视窗单位 默认vw
selectorBlackList: ['ignore-'], // 指定不转换为视窗单位的类名
minPixelValue: 1, // 默认1 小于等于1px不转换
mediaQuery: true, // 是否在媒体查询的css代码中也进行转换,默认false
replace: true, // 是否转换后直接更换属性值
landscape: false, // 是否处理横屏情况
exclude: /(\/|\\)(node_modules)(\/|\\)/,
})
]
}
},
})引入时会爆红,可以添加一个声明文件
// 新建 postcss-px-to-viewport.d.ts
declare module "postcss-px-to-viewport" {
type Options = {
unitToConvert: "px" | "rem" | "cm" | "em";
viewportWidth: number;
viewportHeight: number;
unitPrecision: number;
viewportUnit: string;
fontViewportUnit: string;
selectorBlackList: string[];
propList: string[];
minPixelValue: number;
mediaQuery: boolean;
replace: boolean;
landscape: boolean;
landscapeUnit: string;
landscapeWidth: number;
exclude:RegExp
};
export default function (options: Partial<Options>): any;
}tsconfig.config.json文件 include内添加"postcss-px-to-viewport.d.*"
presetIcons 图标库
例如 https://icones.js.org/collection/ic ,使用ic 执行cmd npm i -D @iconify-json/ic
直接使用:
<div class="i-ic-baseline-3k"></div>presetAttributify 美化属性
装了美化属性后可以不用卸载class里,例如:
<div class="flex red m-1"><p>p</p></div>
等价于
<div flex red m="1"><p>p</p></div>presetUno 工具类
可以直接使用类似Tailwind类名
响应式系统API
ref()
const n1 = ref(0) // msg.value
const msg = ref('Hello') // msg.value
//ref传对象类型数据会在内部调用reactive()方法,取值时仍需要 .value
const obj = ref({name:'myName'}) //--> reactive({name:'myName'}) >> msg.value.namereactive()
const state = reactive({
n1:0,
n2:0
}); // state.n1 : 0computed 计算
<script setup lang='ts'>
import { ref, computed, reactive } from 'vue'
type Shop = {
name: string,
num: number,
price: number
}
const data = reactive<Array<Shop>>([
{
name: "苹果",
num: 1,
price: 3.99
}, {
name: "番茄",
num: 4,
price: 13.99
}
])
//求和
let total = computed(()=>{
return data.reduce((p,n,index,y)=>{
return p + (n.num*n.price)
},0)
})
</script>
<template>
<div>
<table>
<tbody>
<tr v-for="(item, index) in data">
<td>{{ item.name }}</td>
<td><button @click="item.num--">-</button>{{ item.num }}<button @click="item.num++">+</button></td>
<td>{{ item.price }}</td>
<td><button @click="data.splice(index, 1)">删除</button></td>
</tr>
</tbody>
<tfoot>
<td></td>
<td></td>
<td></td>
<td>总价:{{total}}</td>
</tfoot>
</table>
</div>
</template>
<style lang='scss' scoped>
</style>watch 侦听器
侦听ref()创建的数据需要 **添加deep:true来进行深度监听 **对象内的数据
而reactive()创建的数据 **默认进行深度监听 **
watch侦听器是惰性的 需要添加immediate:true来默认第一次调用一次watch(即使未触发也调用一次)
侦听简单数据类型
//侦听1个
watch(msg,
(newVal, oldVal) => {
console.log("new:" + newVal, " old: " + oldVal);
},{
deep:true,//开启深度监听
immediate:true,//默认第一次调用一次watch
})
//侦听多个 传数组即可
watch([msg1,msg2],(newVal, oldVal) => {})监听对象中某一个值,监听单一项
watch(()=>data.name,//监听对象单一项需要使用函数返回
(newVal, oldVal) => {
console.log("new:" + newVal, "old: " + oldVal);
},{
deep:true,//深度监听
immediate:true,//默认第一次调用一次watch
})watchEffect 高级侦听器
watchEffect 高级侦听器用到什么直接写就行,会对写的数据进行侦听
watchEffect 侦听器是非惰性的,使用时默认调用一次
//简单使用
watchEffect(() => {
console.log("更新了:"+ data.nav.bar);
console.log("更新了:"+ data.nav.list);
})
//调用前回调
watchEffect((oninvalid) => {
console.log("更新了:"+ data.nav.bar);
console.log("更新了:"+ data.nav.list);
//watchEffect 会优先执行 oninvalid 回调函数,可以进行数组删除等操作
oninvalid(()=>{
console.log("before");
})
})
const watch = watchEffect((oninvalid) => {
console.log("更新了:"+ data.nav.bar);
console.log("更新了:"+ data.nav.list);
//watchEffect 会优先执行 oninvalid 回调函数,可以进行数组删除等操作
oninvalid(()=>{
console.log("before");
})
},{
flush:"post",
onTrigger(e){//开发时进行调试
debugger
}
})
// 调用 watch() 可以停止侦听
// flush: "post|pre|sync" post组件更新后执行 pre组件更新前执行 sync强制同步触发组件传参
defineProps 接收
接收来自父组件的参数
////////setup()使用
setup(props,{emit}){
const state = reactive({
n1:0,
n2:0,
result: computed(()=>n1 + n2)
});
const add = () =>{
emit("Num:add",state.result)
}
}
////////////////////
//setup语法糖使用
type Props = {
title:string,
data:Array<Number>
}
const prop = defineProps<Props>()
//使用withDefaults()设置默认值
type Props = {
title?: string,
data?: Array<Number>
}
const prop = withDefaults(defineProps<Props>(), {
title: '我是默认title值',
data: () => [0, 0, 0]
})defineEmits 发送
向父组件发送事件
<!-- 子组件 -->
const emit = defineEmits(['on-click']) //可以定多个
const clickTap = () =>{
emit('on-click',prop.data) //向父组件发送一个 `on-click` 事件,并携带参数
}
<!-- 子组件 -->
<!-- 父组件 -->
const menuClick = (list:Array<Number>) =>{
list.push(Math.random())
}
// 接收自定义事件并调用方法
// <Menu @on-click="menuClick"></Menu>
//
<!-- 父组件 -->defineExpose 导出子组件数据
导出子组件数据,父组件可以直接拿到子组件导出的数据
子组件:
<script setup lang='ts'>
import { ref, reactive } from 'vue'
const list = reactive<number[]>([1,2,3])
const flag = ref(true)
//使用defineExpose导出数据
defineExpose({
list,flag
})
</script>父组件:
<script setup lang='ts'>
import { ref, reactive } from 'vue'
const menus = ref(null)
const menuClick = () => console.log(menus.value); // list,flag
</script>
<template>
<Menu ref='menus' @click="menuClick"></Menu>
</template>依赖注入 Provide/Inject
当有多层组件直接传参时,使用Provide传递参数
发布组件
import { ref, reactive,provide } from 'vue'
const num = 0
const btn = () =>{
provide("update:num",num)
}注入组件
import { ref, reactive,inject } from 'vue'
import type {Ref} from 'vue'
const num = inject<Ref<number>>("update:num")
//被注入的组件也可以修改传过来的参数,修改时其他组件也会一同修改
const change = () => {
num!.value = 30
}Mitt库($bus)
npm install mitt -S
import mitt from 'mitt'
const Mit = mitt(const Mit = mitt()
//TypeScript注册
//由于必要拓展ComponentCustomProperties类型才能获得类型提升
declare module 'vue' {
export interface ComponentCustomProperties{
$Bus: typeof Mit
}
}
app.config.globalProperties.$bus = Mit)全局函数和变量
如果声明完后爆红,重启编辑器即可
//声明类型
type Filter = {
format: <T>(str:T) => string
}
//定义声明文件
declare module '@vue/runtime-core' {
export interface ComponentCustomProperties{
$filters: Filter
}
}
//定义全局的 过滤器
app.config.globalProperties.$filters = {
format <T>(str:T):string{
return `臻 · ${str}`
}
}
// 直接使用 `{{$filters.format("苦bee")}}`动画 transition
.fade-enter-from:用来定义被fade组件包裹的元素 进入开始时的样式
.fade-enter-active:用来定义被fade组件包裹的元素 进入过渡时的样式 常用
.fade-enter-to:用来定义被fade组件包裹的元素 进入结束时的样式
.fade-leave-from:用来定义被fade组件包裹的元素 离开开始时的样式
.fade-leave-active:用来定义被fade组件包裹的元素 离开过渡时的样式 常用
.fade-leave-to:用来定义被fade组件包裹的元素 离开结束时的样式
<script setup lang='ts'>
import { ref, reactive} from 'vue'
const flag = ref(true)
let change = () =>flag.value = !flag.value
</script>
<template>
<div class="content">
<button @click="change">swich</button>
<transition name="fade"> <div class="box" v-if="flag"> www </div> </transition>
</div>
</template>animate.css
npm install animate.css -S
animate4+使用时需要加前缀
如:
duration="2000"定义动画执行时间,可以单独定义
duration="{enter:50,leave:500}" 进入时,离开时
import 'animate.css'
<transition
:duration="2000"
enterActiveClass="animate__animated animate__backInDown"
leaveActiveClass="animate__animated animate__backOutLeft"
>
<div class="box" v-if="flag"> www </div>
</transition>transition 生命周期
| 指令 | 对应阶段 | 备注 |
|---|---|---|
| @beforeEnter="beforeEnter" | 对应enter-from | 进入之前 |
| @enter="enter" | 对应enter-active | 进入过渡曲线 |
| @afterEnter="afterEnter" | 对应enter-to | 进入完成 |
| @enterCancelled="enterCancel" | 显示过渡打断 | 效果被打断,还没完成就被结束了 |
| @beforeLeave="beforeLeave" | 对应leave-from | 离开之前 |
| @leave="leave" | 对应leave-active | 离开过渡曲线 |
| @afterLeave="afterLeave" | 对应leave-to | 离开完成 |
| @leaveCancelled="leaveCancel" | 离开过渡打断 | 效果被打断,还没完成就被结束了 |
@beforeEnter="EnterFrom"
@enter="EnterActive"
@afterEnter="EnterTo"
@enterCancelled="enterCancel"
@beforeLeave="LeaveFrom"
@leave="LeaveActive"
@afterLeave="LeaveTo"
@leaveCancelled="leaveCancel"
const EnterActive = (el:Element,done:Function)=>{
done() //继续操作
}
const LeaveActive = (el:Element,done:Function)=>{
done() //继续操作
}gsap.js 动画库
npm install gsap -S
<script setup lang='ts'>
import { ref } from 'vue'
import 'animate.css'
import gsap from 'gsap'
const flag = ref(true)
let change = () => flag.value = !flag.value
//开始前
const EnterFrom = (el: Element) => {
gsap.set(el,{
width:0,
height:0
})
}
//进入时
const EnterActive = (el: Element, done: gsap.Callback) => {
gsap.to(el,{
width:200,
height:200,
onComplete:done
})
}
//离开时
const LeaveActive = (el: Element, done: gsap.Callback) => {
gsap.to(el,{
width:0,
height:0,
onComplete:done
})
}
</script>
<template>
<div class="content">
<button @click="change">swich</button>
<transition @beforeEnter="EnterFrom" @enter="EnterActive" @leave="LeaveActive">
<div class="box" v-if="flag"> www </div>
</transition>
</div>
</template>
<style lang='less' scoped>
.content {
flex: 1;
border: 1px solid #ccc;
margin: 20px;
}
.box {
width: 100px;
height: 100px;
background-color: skyblue;
}
</style>appear
进入页面时执行动画
正常使用配置appearActiveClass即可
<transition
appear
appearActiveClass="animate__animated animate__fadeInTopLeft">
<div class="box" v-if="flag"> www </div>
</transition>
全部配置
<transition
appear
appearFromClass="animate__animated animate__fadeInTopLeft"
appearActiveClass="animate__animated animate__fadeInTopLeft"
appearToClass="animate__animated animate__fadeInTopLeft"
>
<div class="box" v-if="flag"> www </div>
</transition>transition-group
跟上面用法一样
<script setup lang='ts'>
import { reactive, ref } from 'vue'
import 'animate.css'
const flag = ref(true)
let change = () => flag.value = !flag.value
const list = reactive([1,2,3,4,5,5])
</script>
<template>
<div class="content">
<button @click="change">swich</button>
<transition-group
tag="div"
appear
appearActiveClass="animate__animated animate__fadeIn"
>
<div class="box" v-for="item in list" :key="item"> www </div>
</transition-group>
</div>
</template>酷炫平面移动过渡
安装 npm install lodash -S
安装声明文件 npm install @types/lodash -D
使用lodash帮我们把数组打乱
moveClass过渡

<script setup lang='ts'>
import { ref, reactive } from 'vue'
import _ from 'lodash'
const list = ref(Array.apply(null,{length:81} as number[]).map((_,index)=>{
return{
id:index,
number:(index%9)+1
}
}))
const randen = () =>list.value = _.shuffle(list.value)
</script>
<template>
<div>
<button @click="randen">切换</button>
<div class="group">
<TransitionGroup moveClass="mmm">
<div class="box" v-for="item in list" :key="item.id">
{{item.number}}
</div>
</TransitionGroup>
</div>
</div>
</template>
<style lang='less' scoped>
.group{
display: flex;
flex-wrap: wrap;
width: 400px;
.box{
width: 30px;
height: 30px;
padding: 3px;
margin: 3px;
}
}
.mmm{
transition: all .3s;
}
</style>状态过渡
比如 数值过渡、背景颜色渐变过渡、svg过渡等等
<script setup lang='ts'>
import { ref, reactive,watch } from 'vue'
import gsap from 'gsap'
const num = reactive({
current: 0,
tweenedNumber: 0
})
watch(() => num.current,(n,o)=>{
gsap.to(num,{
duration:1,
tweenedNumber:n
})
})
</script>
<template>
<div>
<input v-model="num.current" type="number" step="20">
<div> {{ num.tweenedNumber.toFixed(0) }} </div>
</div>
</template>TSX
安装包
npm install @vitejs/plugin-vue-jsx
配置vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(),vueJsx()]
})配置 tsconfig.json
compilerOptionst添加
"jsx":"preserve",
"jsxFactory": "h",
"jsxFragmentFactory": "Fragment"