Skip to content

创建项目

创建项目

使用vite创建

npm create vite demo

使用vite最新版创建

npm init vite@latest

使用vue3最新版创建(更全)

npm init vue@latest

cd demo

npm i

npm run dev

安装vue-router

image-20220929212710531

快速开始

控制台输入:npm i vue-router@4 -S

创建文件

js
//创建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;
js
//main.ts导入
import router from "./router/index";

createApp(App).use(router).mount("#app");
基本使用
html
<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>
路由传参
html
<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>
路由取参
html
<script setup lang='ts'>
import { useRoute } from "vue-router";
const route = useRoute()

// query取参
// route.query

// params取参
// route.params
    
    
</script>
路由重定向
js
const routes: Array<RouteRecordRaw> = [
  {
    path: "/",
    redirect: "/reg" //重定向
    name: 'home',
  },  {
    path: "/",
    redirect: (to) => { //重定向
      return {
        path: "/reg",
        query: to.query,
      };
    },
    name: 'home',
  },
];
前置前置守卫
js
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);
})
简单的权限案例
js
const whileList = ["/"];
router.beforeEach((to, from, next) => {
  let token = localStorage.getItem("token");
  //白名单 有值 或者登陆过存储了token信息可以跳转 否则就去登录页面
  if (whileList.includes(to.path) || token) {
    next();
  } else {
    next({ path: "/" });
  }
});
后置前置守卫
js
router.afterEach((to, from) => {});

配置自动导入

按需引入插件

npm i -D unplugin-auto-import

js
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

js
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

添加配置文件:

js
// 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)(\/|\\)/,
        })
      ]
    }
  },
})

引入时会爆红,可以添加一个声明文件

js
// 新建 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

直接使用:

html
<div class="i-ic-baseline-3k"></div>

presetAttributify 美化属性

装了美化属性后可以不用卸载class里,例如:

html
<div class="flex red m-1"><p>p</p></div>
等价于
<div flex red m="1"><p>p</p></div>

presetUno 工具类

可以直接使用类似Tailwind类名

响应式系统API

ref()

javascript
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.name

reactive()

javascript
const state = reactive({
    n1:0,
    n2:0
}); // state.n1 : 0

computed 计算

html
<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(即使未触发也调用一次)

侦听简单数据类型

javascript
//侦听1个
watch(msg,
  (newVal, oldVal) => {
    console.log("new:" + newVal, "  old: " + oldVal);
  },{
    deep:true,//开启深度监听
    immediate:true,//默认第一次调用一次watch
  })

//侦听多个 传数组即可
watch([msg1,msg2],(newVal, oldVal) => {})

监听对象中某一个值,监听单一项

javascript
watch(()=>data.name,//监听对象单一项需要使用函数返回
  (newVal, oldVal) => {
    console.log("new:" + newVal, "old: " + oldVal);
  },{
    deep:true,//深度监听
    immediate:true,//默认第一次调用一次watch
  })

watchEffect 高级侦听器

watchEffect 高级侦听器用到什么直接写就行,会对写的数据进行侦听

watchEffect 侦听器是非惰性的,使用时默认调用一次

javascript
//简单使用
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 接收

接收来自父组件的参数

js
////////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 发送

向父组件发送事件

js
<!-- 子组件 -->
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 导出子组件数据

导出子组件数据,父组件可以直接拿到子组件导出的数据

子组件:

html
<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>

父组件:

html
<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传递参数

发布组件

javascript
import { ref, reactive,provide } from 'vue'

const num = 0
const btn = () =>{
  provide("update:num",num)
}

注入组件

javascript
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

js
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)

全局函数和变量

如果声明完后爆红,重启编辑器即可

js
//声明类型
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组件包裹的元素 离开结束时的样式

html
<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}" 进入时,离开时

html
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"离开过渡打断效果被打断,还没完成就被结束了
html
    @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

html
<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

进入页面时执行动画

html
正常使用配置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

跟上面用法一样

html
<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过渡

image-20221026230531700

html
<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过渡等等

html
<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

js
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添加

js
    "jsx":"preserve",
    "jsxFactory": "h",
    "jsxFragmentFactory": "Fragment"