当前位置: 首页 > news >正文

第十三步:vue

Vue

1、上手

1、安装

  • 使用命令:npm create vue@latest
  • vue文件后缀为.vue
  • const app = createApp(App):初始化根组件
  • app.mount("#app"):挂载根组件到页面

2、文件

  • script标签:编写js
  • template标签:编写html
  • style标签:编写css

2、样式

1、样式设置

  • style标签内,编写样式
  • template标签内,元素节点通过class定义类名

2、样式隔离

style标签通过scoped,能够进行组件之间的样式隔离

3、样式穿透

  • vue2中使用::v-deep前缀修饰

    ::v-deep .box {}
    
  • vue3中使用:deep(.box)进行包裹

    :deep(.box) {}
    

4、使用less

  • 安装less

  • 修改标签:<style scoped lang="less"></style>

3、渲染

1、基础渲染

  • 定义常量:const num = 10
  • 显示常量:<div>{{ num }}</div>
  • 计算显示:<div>{{ num - 2 }}</div>
    • 能够在数据显示中,进行js逻辑执行
  • 设置动态属性:
    • 定义属性:const idName = "box"
    • 绑定属性:<div v-bind:id="idName">vue</div>
    • 简写:<div :id="idName">vue</div>
  • 设置多个属性:
    • 定义属性:const attr = { id: "box" }
    • 绑定属性:<div v-bind="attr"></div>
  • 渲染HTML:
    • 定义:const dom = "<span>vue</span>"
    • 渲染:<div v-html="dom"></div>
    • 注意:此时div不能添加内容
  • 类名绑定:
    • 单类名:<div :class="className">vue</div>
    • 多类名:<div :class="[class1, class2]">vue</vue>
    • 可控类名:<div :class="{ active: isActive }">vue</div>

2、条件渲染

  • 定义变量:const show = false
  • 判断显示:<div v-show="show">隐藏</div>
  • 判断显示:<div v-if="show">消失</div>
  • 区别:
    • v-show:节点会进行构建、布局和渲染,但是会设置display为none进行隐藏
    • v-if:节点不参与构建、页面没有该节点
    • v-else:配合v-if使用,v-ifv-elsev-else-if

3、列表渲染

  • 定义数组:const list = [1, 2, 3]
  • 列表遍历:<div v-for="(item, index) in list" :key="index">{{item}}</div>
  • 属性key:提供给vue,方便节点插入删除,提高性能
  • 空数组时隐藏
  • 优先度小于v-showv-if
  • 不建议和v-if一起使用

4、事件响应

  • 语法:v-on:事件类型="绑定函数名"

  • 简写:@:事件类型="绑定函数名"

  • 绑定事件函数:<button @click="fn">按钮</button>

  • 事件传值:

    • 调用传值:<button @click="fn('value')">按钮</button>
    • 事件对象:
      • <button @click="fn($event)">按钮</button>
      • <button @click="(e) => fn(e)">按钮</button>
  • 事件修饰:

    • @click.stop:阻止事件冒泡
    • @click.prevent:阻止默认事件
    • @click.self:事件只作用与本身
    • @click.once:事件只执行一次
    • @click.capture:事件在捕获阶段执行
    • @scroll.passive:先执行滚动,然后再执行监听函数,避免滚动卡顿
    • 链式:
      • @click.stop.prevent:阻止冒泡、阻止默认
      • 需要注意链式调用顺序
  • 按键修饰:

    • @keyup.enter:仅enter按键弹起时触发
    • @keyup.tab
    • @keyup.delete
    • @keyup.esc
    • @keyup.space
    • @keyup.up
    • @keyup.down
    • @keyup.left
    • @keyup.right
    • @keyup.ctrl
    • @keyup.alt
    • @keyup.shift
    • @keyup.meta:菜单键
    • 链式:@keyup.alt.enter:符合按键alt + enter
    • 点击组合:@click.ctrl:点击 + ctrl
  • 鼠标修饰

    • @click.exact:没有任何按键时触发
    • @click.left:鼠标左键触发
    • @click.right:鼠标右键触发
    • @click.middle:鼠标辅助键触发

5、动态数据

1、定义
  • vue3:使用ref定义数据。const count = ref(0)
    • 返回值:初始化的变量
    • 参数:初始化的值
    • 元素内可以直接使用
    • 元素外需要通过count.value访问
  • vue3:使用reactive定义数据:const data = reactive({ count: 1 })
    • 返回值:初始化的复杂变量
    • 参数:需要代理的对象,数组等。无法代理普通类型数据
    • 元素内外可以直接使用
2、绑定和显示

和其他常量一样,直接进行使用

4、修改
  • 节点内:<button @click="count++">{{ count }}</button>
  • 节点外:function fn() { count.value++ }
5、双向绑定
  • v-model:输入框数据双向绑定

    • 使用:<input type="text" v-model="text" />

    • 相当于:

      <inputtype="text":value="text"@input="event => text = event.target.value"
      />
      
  • 修饰:

    • v-model.lazy:相当于绑定@change
    • v-model.number:只输入数字
    • v-trim:去掉两端空格
6、计算属性
  • const num = computed(() => count):获取计算属性
    • 只有当count响应变量变化时,才会重新计算num数据。
    • 能够对数据进行缓存,如computed(() => Date.now())
7、数据监听
  • watch(data, callback):数据监听

    • data:监听的数据
    • callback:监听函数
      • 参数一:可选,变化后的值
      • 参数二:可选,变化前的值

    • data:监听多个数据,[data1, data2]

    • callback:监听函数

      • 参数一:可选,变化后的值,[data1, data2]
      • 参数二:可选,变化前的值,[data1, data2]
  • watchEffect(callback):数据监听

    • 组件中,多个响应数据发生变化,组件会等待所有数据变化完后,统一刷新一次
    • 触发条件:
      • 响应数据发送变化
      • callback函数中,只用使用到的数据才会被监听。
      • 组件挂载完成后,也会被触发
8、强行刷新
  • 由于统一刷新机制,某个响应变量需要提前响应,需要强行刷新
  • nextTick():强行刷新函数

6、插槽

slot组件:定义插槽,就是子元素渲染位置

1、渲染内容
<template><h3>Box</h3><slot />
</template>
2、默认内容
<template><h3>Box</h3><slot>这里是插槽默认内容</slot>
</template>
3、具名插槽
  • <slot name="header"></slot>:定义插槽名称
  • <template v-slot:name>插入内容</template>
4、动态插槽名
  • <template v-slot:[动态响应变量]>插入内容</template>
5、插槽传值
  • 普通插槽

    • 传值:<slot :text="传值"></slot>

    • 读取:

      <template v-slot="slotProps"><p>插槽 {{ slotProps.text }}</p></template>
      
  • 具名插槽:

    • 传值:<slot name="header" :text="传值"></slot>

    • 读取:

      <template v-slot:header="slotProps"><p>插槽 {{ slotProps.text }}</p>
      </template>
      

7、动态组件

  • 使用component组件动态加载不同组件
  • 使用:<component :is="组件名" />

4、组件传值

1、父传子

  • 传值

    • 通过属性之间传值
    • 通过v-bind传递动态属性变量,或者方法
  • 读取

    • 子组件通过:const props = defineProps(["属性名"]) 读取属性、方法

    • props只读,无法修改

    • defineProps:

      • 参数为数组:读取属性名
      • 参数为对象:设置props的数据类型,必填、默认值
        • 属性名称:值可以为数据类型,可以为对象
          • type:数据类型
          • required:是否必填
          • default:默认值
          • validator:自定义校验
      const props = defineProps({propA: Number,propB: [Number, String],propC: Function,propD: {type: String,required: true,default: "必填"},propF: {validator(value, props) {return ["success", "error"].includes(value);}}
      })
      

  • 绑定事件:<Box @submit="callback" />

  • 触发事件:

    • <button @click="$emit('submit')">按钮</button>

    • 使用const emit = defineEmits(['事件名'])接受事件

      • emit('事件名'):调用事件
    • 传值:

      • $emit("事件名", arg1, arg2)
      • emit("时间吗", arg1, arg2)

  • 单个v-model

    • 绑定v-model:<Box v-model="text" />
    • 获取model:const model = defineModel([optons])
      • options:可选
        • required:是否必填
  • 具名v-model

    • 绑定v-model:<Box v-model:title="text" />
    • 获取model:const title = defineModel('title'[, options])
      • 参数一:名称
      • options:可选,同上
  • 多个v-model

    • 绑定多个v-model

      <Box v-model:title="title" v-model:text="text" />
      
    • 获取model

      const title = defineModel("title")
      const text = defineModel("text")
      

  • 读取attrs
    • 使用$attrs直接进行读取
    • 使用:const attrs = useAttrs() 获取
  • 读取slots
    • 使用$slots直接进行读取
    • 使用:const solts = useSlots() 获取

2、父读子

  • 子组件通过defineExpose({ 变量, 方法 }),暴漏自身变量和方法
  • 父组件通过const child = useTemplateRef(refName)获取绑定ref的子组件
  • 父组件通过ref属性绑定refName:<Box ref="refName" />
  • 父组件读取:child.value.名称
  • 父组件调用:child.value.名称()

  • 如果ref绑定的是html标签元素,直接通过 refName.名称 进行读取和调用

3、兄弟传值

  • 共享父组件响应变量传值
  • 使用pinia状态管理传值

4、上下文

  • 单个数据
    • 父组件提供:provide(key, value)
    • 子组件注入:const value = inject(key)
  • 多个数据
    • 父组件提供:provide(key, value, setValue)
    • 子组件注入:const { value, setValue } = inject(key)
  • provide可以多次调用,inject也可以多次调用

5、生命周期

  • onBeforeMount(fn):加载前
  • onMounted(fn):加载后
  • onBeforeUpdate(fn):数据变化前
  • onUpdated(fn):数据变化后
  • onBeforeUnmount(fn):卸载前
  • onUnmounted(fn):卸载后
  • onErrorCaptured(fn):子组件出现错误钩子函数
  • onActivated(fn):
    • 首次挂载时调用
    • 每次从缓存中重新插入
  • onDeactivated(fn):
    • 卸载时调用
    • 从缓存中卸载时调用
  • onServerPrefetch(fn):异步组件在服务器上渲染之前触发

1、加载前

  • script标签内,直接执行的代码就是组件加载前执s行
  • onBeforeMount(callback):加载前执行

2、加载后

  • onMounted(callback):加载后执行

3、变化后

  • watch:监听数据变化完成
  • watchEffect:监听数据变化完成

4、卸载前

  • onBeforeUnmount(callback):卸载前执行

5、进出动画

1、条件渲染
  • 使用v-ifv-show等控制组件进入进出时,渲染动画

  • 使用组件Transition完成动画

    <Transition><div v-if="show">内容</div>
    </Transition>
    
  • 基于css过度

    • Transition默认会给包裹子节点添加6个类名
      • .v-enter-from:渲染前className,定义元素进入页面前的样式
      • .v-enter-active:渲染中className,定义元素进入页面时的CSS动画
      • .v-enter-to:渲染后className,定义元素进入页面后的样式
      • .v-leave-from:卸载前className,定义元素退出页面前的样式
      • .v-leave-active:卸载时className,定义元素退出页面时的CSS动画
      • .v-leave-to:卸载后className,定义元素退出页面后的样式
    • Transition设置name属性:<Transition name="fade">
      • .v-enter-from就需要改为.fade-enter-from
  • 自定义css

    • Transition可以自定义类名
      • enter-from-class:重定义.v-enter-from类名
      • enter-active-class:重定义.v-enter-active类名
      • enter-to-class:重定义.v-enter-to类名
      • leave-from-class:重定义.v-leave-from:类名
      • leave-active-class:重定义.v-leave-active类名
      • leave-to-class:重定义.v-leave-to类名
  • 动画钩子函数

    • Transition每个动画阶段都有钩子事件函数

      • @before-enter:进入页面前触发事件
      • @enter:进入页面时触发事件
      • @after-enter:进入页面后触发事件
      • @enter-cancelled:进入动画完成后,触发事件
      • @before-leave:退出页面前触发事件
      • @leave:退出页面时触发事件
      • @after-leave:退出页面后触发事件
      • @leave-cancelled:退出动画完成后,触发事件
    • Transition组件在使用钩子函数时,建议设置css属性为false

    • 事件绑定函数,参数一为当前元素指向

    • @enter@leave还有参数二,为动画完成后回调函数。告诉vue动画以及完成

      <template><div><h1>app</h1><button @click="toggle">按钮</button><Transitionname="fade"@enter="activeEnter"@leave="leaveEnter"><div v-if="show">内容</div></Transition></div>
      </template><script setup>
      import { ref } from 'vue'
      import { gsap } from 'gsap'const show = ref(false)
      function toggle() {show = !show
      }function activeEnter(el, done) {gsap.fromTo(el,{ x: -100, opacity: 0 },{ x: 0, opacity: 1, duration: 1 }).then(done)
      }function leaveEnter(el, done) {gsap.fromTo(el,{ x: 0, opacity: 1 },{ x: 100, opacity: 0, duration: 1 }).then(done)
      }
      </script>
      
  • 渲染时动画

    • 不依靠v-ifv-show控制,而是组件mount时进行动画
    • Transition组件添加appear属性
  • 替换动画

    • 当使用v-ifv-else进行组件替换时,需要设置model
    • Transition组件添加设置model="out-in"
  • 组件替换

    • 当使用component 组件进行动态组件替换时
    • Transition组件添加设置model="out-in"
2、列表渲染
  • 使用组件TransitionGroup完成v-for的动画渲染

  • 组件TransitionGroupTransition拥有基本相同的props

    • 多一个.v-move的类名,用于元素的过度
    • 多一个tag属性,用于创建一个元素标签
    .list-move, /* 对移动中的元素应用的过渡 */
    .list-enter-active,
    .list-leave-active {transition: all 0.5s ease;
    }.list-enter-from,
    .list-leave-to {opacity: 0;transform: translateX(30px);
    }/* 确保将离开的元素从布局流中删除以便能够正确地计算移动的动画。 */
    .list-leave-active {position: absolute;
    }
    

6、Pinia

1、使用

  • 入口文件main.js,添加使用中间件

    import { createPinia } from 'pinia'// ...
    app.use(createPinia())
    
  • 定义数据

    • 创建文件:src/stores/counter

    • 使用响应式定义数据

      import { ref, computed } from 'vue'
      import { defineStore } from 'pinia'export const useCounterStore = defineStore('counter', () => {const count = ref(0)count doubleCount = computed(() => count.value * 2)function add() {count.value++}return { count, doubleCount, add }
      })
      
  • createPinie:函数,返回一个中间件

  • defineStore(key, callback):函数

    1. key:唯一标识
    2. callback:使用响应式创建数据和方法

2、数据使用

  • 引入Store:import { useCounterSotre } from "@/stores/counter"

  • 使用:

    <template><div>{{ counter.count }}</div><button @click="counter.add">按钮</button>
    </template><script setup>
    import { useCounterStore } from "@/stores/counter"
    const counter = useCounterStore()
    </script>
    
  • 注意:

    • 不要对counter进行结构,否则无法进行数据响应变化
    • 原因:结构后相当于重新赋值,所以结构后的值不会发生任何变化

3、数据监听

  • store内监听

    • 直接使用watchwatchEffect进行数据监听即可
    • 然后使用sessionStorage或者loactStorage完成数据持久化
  • 组件内监听

    • 也使用watchwatchEffect进行数据监听,需要深层次监听

      watch(() => store.count,() => {// 数据变化时触发}
      )
      

4、异步修改

  • 直接在方法里,进行数据异步修改即可

5、模块化

  • 直接修改defineStore的key值,实现不过store不同唯一标识

6、外部使用

  • 直接在外部js文件中引入store,然后使用store方法即可

7、路由

1、路由使用

  • const router = createRouter(options):创建路由配置

  • app.use(router):中间件的方式使用router

  • RouterView:渲染路由页面组件

  • options配置项

    • history:定义路由模式

      • createWebHistory(import.meta.env.BASE_URL):定义history模式
      • createWebHashHistory(import.meta.env.BASE_URL):定义hash模式
    • routers:定义路由列表

      • router-item配置
        • path:路由地址
        • name:路由别名
        • meta:原信息
        • component:组件
        • redirect:路由重定向
        • children:子路由列表
    • 404

      routes: [{ path: '/:catchAll(.*)', component: Error }
      ]
      

2、路由跳转

  • const router = useRouter():获取router操作
    • router.back():返回上一个路由
    • router.go(number):返回数值步路由
      • number可以为负
        • 1,向前1步。
        • -1,向后1步。
        • 0,刷新路由
    • router.push(path):路由跳转
      • 参数为字符串时,直接进行路由跳转
      • 参数为对象时
        • path:路由路径
        • query:路由query传递参数

3、子路由

  • 定义子路由

    const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes: [{ path: "/", redirect: "/home" },{path: "/home",component: Home,children: [{ path: "/home/one", component: HomeOne },{ path: "/home/two", component: HomeTwo },]}]
    })
    
  • 使用子路由

    <template><h1>home</h1><RouterView />
    </template>
    

5、路由传值

  • 路由传值,也就是get请求传参。
    • params:url/:id,需要对路由路径进行修改
    • query:url?a=1&b=2
    • hash:url#123
  • 传值参数
    • 获取路由操作:const router = useRouter()
    • params传递:router.push("url/123456")
    • query传递:
      • router.push("url?a=10&b=10")
      • router.push({ path: "url", query: { a: 10, b: 10 } })
    • hash传值:router.push("url#123456")
  • 读取参数
    • 获取路由读取:const route = useRoute()
    • 读取param:const param = route.param
    • 读取query:const query = route.query
    • 读取hash:const hash = route.hash
    • 读取meta:const meta = route.meta
    • 读取完整路径:const fullPath = route.fullPath

6、路由懒加载

const router = createRouter({history: createWebHistory(import.meta.env.BASE_URL),routes: [{path: "/home",component: () => import("@/view/Home.vue")}]
})

7、跳转守卫

  • 跳转前拦截:router.beforeEach((to, from ,next) => {})
  • 跳转后拦截:router.afterEach((to, from) => {})

8、路由进度条

  • 插件:nprogress
  • 引入:
    • 引入组件:import NProgress from 'nprogress';
    • 引入样式:import 'nprogress/nprogress.css';
  • 使用:
    • 开启:NProgress.start()
    • 结束:NProgress.done()
    • 设置进度:
      • NProgress.set(0.4):0~1之间
      • NProgress.inc():设置随机进度
  • 配置:NProgress.config(options)
    • minimum:进度条的最小百分比,默认0.08
    • easing:动画动作 [ease、linear]
    • speed:动画速度,默认200
    • showSpinner:是否显示旋转加速器,默认true
    • parent:进度条父容器,默认body

9、进出动画

  • 首先创建store,存储路由变化状态,默认为false
  • 路由跳转前,修改状态为true
    • 注意:必须在router.beforeEach回调内使用useStore,否则报错
    • 原因:在外面使用,路由组件还未进行挂载,pinia也为进行挂载
  • 路由跳转后,修改状态为false
  • 在layout组件,也就是使用RouterView组件内,监听状态
  • 如果状态为true,控制RouterView外壳元素进行gsap离开动画
  • 如果状态为false,控制RouterView外壳元素进行gsao进入动画

10、路由封装

  • 自动读取views文件内的index.vue
  • 自动构建路由
import { createRouter, createWebHistory } from 'vue-router'const history = createWebHistory(import.meta.env.BASE_URL)
const routes = [{ path: '/', redirect: '/home' }]
const globModel = import.meta.glob(`@/views/**/index.vue`)Object.entries(globModel).forEach(([file, model]) => {const segments = file.split('/')let current = {}let path = ''for (let i = 3; i < segments.length; i++) {const segment = segments[i]if (segment === 'index.vue') {current.component = model} else {let list = routesif (i !== 3) {if (!current.children) current.children = []list = current.children}path += '/' + segmentconst child = list.find((child) => child.path === path)if (child) current = childelse {current = { path }list.push(current)}}}
})routes.push({path: '/:catchAll(.*)',component: () => import('@/views/ErrorView.vue'),
})export default createRouter({ history, routes })

8、指令

1、v-html

正常渲染的数据都是字符串,v-html能把html字符串渲染成html节点

2、v-bind

  • 动态绑定属性:<div v-bind:id="idName">vue</div>
  • 简写:<div :id="idName">vue</div>
  • 绑定多个属性:<div v-bind="attr"></div>

3、v-show

  • 控制隐藏:<div v-show="show">vue</div>
  • 通过变量,控制display样式,进行显示或隐藏

4、v-if

  • 控制渲染:<div v-if="show">vue</div>
  • 通过变量,控制节点是否渲染,进行显示或隐藏

5、v-for

  • 列表渲染:<div v-for="(item, index) in list" :key="index">{{ item }}</div>
  • 进行遍历,可以结构出下标
  • 需要绑定key属性,让vue能够更好进行节点计算,提高性能
  • 空数组时隐藏
  • 优先度小于v-ifv-show
  • 不建议和v-if一起使用
  • 配套使用:v-ifv-elsev-else-if

6、v-on

  • 绑定事件

7、v-model

  • 双向绑定
  • 相当于:v-bind:value + v-on:input

8、v-slot

  • 使用具名插槽:<template v-slot:插槽名称>内容</template>
  • 简写:<template #插槽名称>内容</template>

9、v-pre

  • 所有 Vue 模板语法都会被保留并按原样渲染
  • 如:<span v-pre>{{ 123 }}</span>
  • 渲染成:<span>{{ 123 }}</span>

10、自定义

  • app.directive(name, callback):自定义指令

    • app:createApp创建的根节点

    • name:指令名称,使用为v-名称

    • callback:

      • 函数时

        • 参数一:使用的元素

        • 参数二:赋值的参数

        • 案例:

          <div v-color="color"></div>app.dircetive('color', (el, binding) => {el.style.color = binding.value
          })
          
      • 对象时:

        • created(el, binding, vnode):创建节点后

        • beforeMount(el, binding, vnode):挂载前

        • mounted(el, binding, vnode):挂载后

        • beforeUpdate(el, binding, vnode, prevVnode):更新前

        • update(el, binding, vnode, prevVnode):更新后

        • beforeUnmount(el, binding, vnode):卸载前

        • unmounted(el, binding, vnode):卸载后

        • el:使用的元素

        • binding:赋值的参数

        • vnode:绑定元素的底层vnode

        • prevVNode,代表之前的渲染中指令所绑定元素的 VNode

        • 案例:

          <div v-color="color"></div>app.dircetive('color', {mounted(el, binding) {el.style.color = binding.value}
          })
          

9、组件

  • template:空标签组件

  • computed:组件加载空标签

  • Transition:过度动画标签

  • TransitionGroup:列表过度动画标签

  • KeepAlive:

    • 缓存组件

      <KeepAlive><component :is="activeComponent" />
      </KeepAlive>
      
    • 排除:include

      <KeepAlive :include="['a', 'b']"><component :is="view" />
      </KeepAlive>
      
    • 最大缓存数:max

      <KeepAlive :max="10"><component :is="activeComponent" />
      </KeepAlive>
      
    • 生命周期

      • onActivated:挂载时,从缓存中重新挂载时,触发
      • onDeactivated:卸载时,从缓存中卸载时,调用
  • Teleport:选择挂载节点

    <Teleport to="body"><div>该节点将添加挂载到body上</div>
    </Teleport>
    
  • 组件全局注册

    • vue3中,局部组件之间引入即可使用
    • vue3中,全局组件注册:app.component(组件名, 组件)

10、API

  • app.use():使用插件

  • app.mount():挂载节点

  • app.directive(name, options):自定义指令

  • app.component(name, component):注册全局组件

  • h:渲染函数,创建虚拟dom

    • 参数一:标签、或者组件名称
    • 参数二:标签的属性,事件
    • 参数三:子节点
  • ref:定义并初始化响应数据

  • nextTick:响应数据变化后,强行刷新组件函数

  • reactive:定义并初始化复杂类型数据

  • readonly:设置并返回响应数据只读

    import {ref, readonly} from "vue"
    const count = ref(0)
    const copyCount = readonly(count)
    
  • computed:定义并通过函数处理获取数据

  • watch:对数据进行监听

  • watchEffect:对使用数据进行监听

  • defineProps:读取props

  • defineEmits:读取组件绑定事件

  • defineModel:读取v-model

  • defineOptions:设置组件

    • inheritAttrs:是否Attributes继承
  • defineExpose:抛给ref变量,方法,函数

  • provide:提供上下文

  • inject:注册上下文

11、变量

  • $attrs:读取组件被设置的属性
  • $event:读取事件绑定元素本身的事件对象
  • $slot:读取组件的所有slot
    • 具名插槽:$slot.名称
    • 非具名:$slot.default
  • $emit("事件名称"):触发组件绑定的事件

12、函数与插件

1、函数

  • 在pinia中,就能看到vue的api是可以单独在函数中使用

  • 所以vue的script标签内部的重复逻辑可以提取到外部

  • 组合式函数约定驼峰命名,并以use开头

  • import { ref } from 'vue'function useCount(init) {const count = ref(init)functoin add() {count++}return { count, add }
    }export default useCount
    
  • 使用:const { count, addCount } = useCount()

2、插件

  • 插件本质是一个函数

  • 定义一个插件:

    function addDir(app) {app.directive('color', (el, binding) => {el.style.color = binding.value})
    }
    
  • 使用插件:app.use(addDir)

13、请求

// src/utils/api.js 封装class Api {constructor(baseurl, timeOut) {this.baseurl = baseurl;this.timeOut = timeOut || 10000;}async #request(url, method = "GET", data, json = false, fileName) {const path = this.baseurl + url;const controller = new AbortController();const config = { method, signal: controller.signal };const timeoutPromise = new Promise((_, reject) =>setTimeout(() => {controller.abort();reject(new Error("请求超时"));}, this.timeOut));if (data) {config.body = json ? JSON.stringify(data) : data;if (json) config.headers = { "Content-Type": "application/json" };}try {const res = await Promise.race([fetch(path, config), timeoutPromise]);if (!res.ok) throw new Error(res.statusText);// 进行接口响应后拦截逻辑 - 可通过响应头获取登录状态等const contentType = res.headers.get("content-type").split(";")[0].trim();if (!contentType) throw new Error("Unknown content type");// 处理文件下载if (fileName) {const resData = await res.arrayBuffer();this.#downloadFile(resData, contentType, fileName);return { success: true };}// 返回请求结果return contentType === "application/json"? await res.json(): await res.text();} catch (error) {throw new Error(`请求失败: ${error.message}`);}}#downloadFile(res, contentType, fileName) {const blob = new Blob([res], { type: contentType });const url = URL.createObjectURL(blob);const a = document.createElement("a");a.href = url;a.download = fileName;a.click();URL.revokeObjectURL(url);a.remove();}get(url, query, param) {let path = url;if (query) path += "?" + new URLSearchParams(query).toString();if (param) path += "/" + param;return this.#request(path);}post(url, data) {return this.#request(url, "POST", data, true);}postByFormData(url, data) {let formData = new FormData();for (const key in data) {formData.append(key, data[key]);}return this.#request(url, "POST", formData);}download(url, fileName = "file") {this.#request(url, "GET", null, false, fileName);}upload(url, file, key = "file") {const formData = new FormData();formData.append(key, file);return this.#request(url, "POST", formData);}uploads(url, files, key = "files") {const formData = new FormData();for (const file of files) {formData.append(key, file);}return this.#request(url, "POST", formData);}
}let baseurl = import.meta.env.MODE === "production" ? "" : "/api";
let api = new Api(baseurl);
export default api;

14、服务代理

// vite.config.js
// ...const default defineConfig({// ...server: {proxy: {'/api': {target: 'http://localhost:3000',changeOrigin: true,rewrite: (path) => path.replace(/^\/api/, '')},'/bpi': {target: 'http://localhost:3001',changeOrigin: true,rewrite: (path) => path.replace(/^\/bpi/, '')}}}
})

15、环境变量

  • 读取环境变量:import.meta.env
  • 环境判断
    • import.meta.env.MODE === development:开发环境
    • import.ment.env.MODE === production:生产环境
  • 设置变量
    • 创建.env文件
    • 创建变量:VITE_NAME=hello
    • 变量前缀需为:VITE_
  • env文件
    • .env文件:所有环境都能访问
    • .env.development:开发环境才能访问
    • .env.production:生产环境才能访问

16、别名配置

  • resolve-alias:配置别名

    // vite.config.js
    import { fileURLToPath, URL } from 'node:url'
    import { defineConfig } from 'vite'export default defineConfig({resolve: {alias: {"@": fileURLToPath(new URL('./src', import.meta.url))}}
    })
    

17、静态资源

  • 样式使用
    • public:background: url("/public/images/bg.jpg")
    • src/assets:background: url("./assets/images/bg.jpg")
  • src使用
    • public:<img alt="#" src="/public/images/bg.jpg" />
    • src/assets:<img alt="#" src="./assets/images/bg.jpg" />
  • 打包:
    • public:文件会被复制
    • src/assets:文件会被编译,压缩(需要配置),文件名会发生变化

18、校验规则

  • 脚手架创建项目时,能够选择eslint

  • 对eslint微调

    • eslint.config.js中,添加对象

    • // eslint.config.js
      // ...
      export default defineConfig([// ...{rules: {'no-console': 'off'}}
      ])
      
  • 对prettier微调

    • 在.prottierrc.json内直接修改

19、渲染函数

  • 导入渲染函数:import { h } from 'vue'

  • 创建dom

    import { h } from 'vue'
    const vnode = h('div', { class: 'box' }, 'hello world vue3')
    
  • 配置attr

    • 配置id:h('div', { id: 'box' })
    • 配置class:h('div', { class: 'box' })
    • 配置style:h('div', { style: { color: 'red' } })
  • 事件绑定

    • import { h } from 'vue'
      const handleClick = () => alert('hello world')
      const vnode = h('div', { onClick: handleClick })
      
    • 绑定其他事件:使用jsx的形式绑定,如onInput

    • v-model:

      import { h, ref } from 'vue'
      const text = ref('')
      const vnode = h('input',{type: 'text',value: text.value,onInput: (e) => {text.value = e.target.value}}
      )
      
  • 子节点

    • 单个子节点:h('div', vnode)
    • 多个子节点:h('div', [vnode1, vnode2])
  • 组件

    • 创建组件节点

      import Home from "@/views/Home.vue"
      import { h } from 'vue'
      const vnode = h('Home', { class: 'home' })
      

相关文章:

  • 【PVR】《Adaptive Palm Vein Recognition Method》
  • React Testing Library
  • Java学习手册:开发 Web 网站要知道的知识
  • T检验、F检验及样本容量计算学习总结
  • 2025第16届蓝桥杯省赛之研究生组D题最大数字求解
  • 学习spark总结
  • 常见锁策略
  • 关系型数据库PostgreSQL vs MySQL 深度对比:专业术语+白话解析+实战案例
  • Customizing Materials Management with SAP ERP Operations
  • AI日报 - 2025年04月28日
  • (26)VTK C++开发示例 ---将点坐标写入PLY文件
  • Java多线程实现顺序执行
  • 界面打印和重定向同时实现
  • CodeGeeX 免费的国产AI编程助手
  • HikariCP 6.3.0 完整配置与 Keepalive 优化指南
  • SAP-pp 怎么通过底表的手段查找BOM的全部ECN变更历史
  • 【实战篇】数字化打印——打印格式设计器的功能说明
  • (25)VTK C++开发示例 --- 将点坐标写入.xyz文件
  • 复盘笔记1
  • JavaScript性能优化实战:从瓶颈定位到极致提速
  • 五一期间上海景观照明开启重大活动模式,外滩不展演光影秀
  • 为何未来的福利国家必须绿色且公平
  • 人民日报社论:做新时代挺膺担当的奋斗者
  • 独家丨申万宏源研究所将迎来新所长:首席策略分析师王胜升任
  • 多家媒体及网红走进云南曲靖沾益:感受珠江源头
  • 大学2025丨专访北邮校长徐坤:工科教育要真正回归工程本质