vue有哪些好用的进阶知识

来让我们看看进阶知识吧,大致包含以下内容,内容并不完成,但是想在这里先分享一下。

目录

  1. 环境变量
  2. 跨域
  3. 常用API
  4. 常用hooks
  5. 插件
  6. web components
  7. nextTick
  8. 函数
  9. 全局
  10. 样式
  11. 内置组件
  12. 性能优化

环境变量

基本使用
import.meta.env // 获取当前环境变量信息
自定义环境变量

src/.env.development

VITE_HTTP = 开发环境接口地址

src/.env.production

VITE_HTTP = 生产环境接口地址

生效 package.json

"scripts": {
	"dev": "vite --mode development"
}

跨域

后端cors
"Access-Control-Allow-Origin": "地址"
前端Proxy

vite.config.ts

server: {
    proxy: {
        '/api': {
            target: "地址",
            rewrite: (path) => path.replace(正则) // 重写地址,可选
        }
    }
}

API

defineProps

vue3.3之后支持

  1. 泛型支持
<script generic="T">
defineProps<{
    name: T[]
}>
</script>
defineEmits
  1. ts写法
// 子组件
const emit = defineEmits<{
   (event: string, msg1: string, msg2: string): void              
}>()
const fun = () => {
	// (事件, 参数)
	emit('send', 'hello', 'heihei')
}

// 父组件
<Child @send="getMsg"></Child>

const getMsg = (msg1: string, msg2: string) => {
	console.log(msg1, msg2)
}
  1. 简化
const emit = defineEmits<{
      'send': [msg1: string, msg2: string]
}>
defineOptions
  1. 常用:定义组件名
defineOptions({
	name: "组件名"
})
defineSlots
  1. 常用于约束插槽
// 子组件
const title = "子组件插槽"
<div>
    <slot :title="title"></slot>
</div>

defineSlots<{
    default(props: {title: string}):void         
}>()
    
// 父组件 
<Child>
	<template #default="{title}">
    	{{title}}
    </template>
</Child>

Hooks

useAttrs

获取组件上传输的属性

// 父组件
<template>
	<ChildComponent title="传递title属性" id="传递id属性"/>
</template>

// 子组件
<script setup lang="ts">
import {useAttrs} from "vue"
    
const attrs = useAttrs()   
attrs.title // 传递title属性
attrs.id // 传递id属性
</script>
自定义hook

需求:自定义指令和hook监听dom的宽高变化

function useResize(el: HTMLElement, callback: Function) {
  // callback(entities: 被观察元素的数组, observer: 当前实例)
  let resizeObserver = new ResizeObserver((entries) => {
    // 获取第一个元素的contentRect,里面包含宽高属性
    callback(entries[0].contentRect)
  })
  resizeObserver.observe(el)
}

/**
 * 自定义指令
 */
const install = (app: App) => {
  app.directive('resize', {
    // el: 绑定的元素  binding.value: 指令的值
    mounted(el, binding) {
      useResize(el, binding.value)
    }
  })
}

useResize.install = install

export default useResize


// hooks使用
onMounted(() => {
    useResize(document.querySelector("#resize") as HTMLElement, (dom: any) => {
        console.log(dom)
    })
})

// 自定义指令
createApp(App).use(useResize)
<div v-resize="fun">
</div>

const fun = (dom: any) => {
    console.log(dom)
}

插件

自定义插件

需求:自定义全局加载消息插件

// 组件
defineExpose({
    show
    hide
})

// 插件
import type {App, VNode} from "vue";
import Loading from "./index.vue"
import {createVNode, render} from "vue";

export default {
  install(app: App) {
    // 将组件转为VNode,便可以访问组件上的dom内容
    const LoadingVnode: VNode = createVNode(Loading)
    // LoadingVnode上是component是空的,需要挂载一下(这里是全局的,挂载到body上)
    render(LoadingVnode, document.body)
    // 获取到组件上的方法(组件上的方法记得defineExpose暴露出来)
    app.config.globalProperties.$myLoading = {
      // LoadingVnode.component?.exposed可以获取到组件上暴露的方法
      show: LoadingVnode.component?.exposed?.show,
      hide: LoadingVnode.component?.exposed?.hide
    }
  }
}

Web Components

原生写法

这是什么东西呢,就是原生写组件,非常好玩,我们可以尝试写一个原生按钮组件,有一个很重要的点,那就是shadow DOM

index.html

<script src="./btn.js"></script>

<body>
  可以复用, 试试看
  <my-btn></my-btn>
  <my-btn></my-btn>
  <my-btn></my-btn>
</body>

btn.js(通过js操作dom)

class Btn extends HTMLElement {
  constructor() {
    super()

    const shadowDom = this.attachShadow({ mode: 'open' })

    this.p = this.h('p')

    this.p.innerText = "我的按钮"

    this.p.setAttribute('style', 'width:120px;height:40px;border:1px solid black;border-radius: 5px;')

    shadowDom.appendChild(this.p)
  }

  // 创建元素的h函数
  h (el) {
    return document.createElement(el)
  }
}

window.customElements.define('my-btn', Btn)

btn.js(通过模板操作)

这样用的好处是什么呢,样式是隔离的,不会影响其他元素,是不是很不错,有没有想封装一个自己的UI库呢

class Btn extends HTMLElement {
  constructor() {
    super()

    const shadowDom = this.attachShadow({ mode: 'open' })

    this.template = this.h('template')

    this.template.innerHTML = `
      <style>
        p{
          width:120px;
          height:40px;
          border:1px solid black;
          border-radius: 5px;
        }
      </style>
      <p class="p">
        我是按钮
      </p>
    `

    shadowDom.appendChild(this.template.content.cloneNode(true))
  }

  // 创建元素的h函数
  h (el) {
    return document.createElement(el)
  }
}

window.customElements.define('my-btn', Btn)
vue + 写法

配置vite.config.ts

plugins: [
    vue({
        template: {
            compilerOptions: {
                isCustomElement: (tag) => tag.includes("hang-")
            }
        }
    })
]

封装组件

组件名需以.ce.vue结尾

组件导入

import {defineCustomElement} from 'vue'

const Btn = defineCustomElement(封装的vue组件)

window.customElements.define('hang-btn', Btn)

nextTick

原理

解决的问题:vue中dom的更新是异步,数据的更新是同步的,有时数据更新,dom却没有更新。

使用

需求:有一个聊天对话框,当输入消息,点击发送,消息对话框增加一条消息,当增加到一定量,有滚动条(记得设置属性)时,我们继续添加消息,则应滚动到最下方增加用户体验。

// 1. 获取dom
const box = ref<HTMLDivElement>("box")
    
// 2. 设置距离(此时,会有一个错位)
box.value!.scrollTop = 99999
    
// 3. 方案一 nextTick + callback
nextTick(() => {
	box.value!.scrollTop = 99999
})
    
// 4. 方案二 nextTick + async await
async 操作dom的函数

await nextTick()
下面代码全为异步
box.value!.scrollTop = 99999

函数

h函数

优势:直接跳过模板编译

场景:封装一个组件

const Btn = () => {
	// h(节点,属性,内容)
	return h('button', {
		style: {},
		class: "",
		onClick: () => {console.log('click')}
	}, '按钮')
}

全局

全局函数

main.ts

// 定义$filter函数
app.config.globalProperties.$filter = {
  format<T>(str: T) {
    return `hang-${str}`
  }
}

// 使用
<template>
	<div>
        {{ $filter.format("yyds") }} // hang-yyds
    </div>
</template>
全局变量

main.ts

// 定义$env变量
app.config.globalProperties.$env = 'dev'

// 使用
<template>
	<div>
        {{ $env }} // dev
    </div>
</template>
报错处理
type Filter = {
    format<T>(str: T): string
}

declare module 'vue/runtime-core' {
    export interface ComponentCustomProperties {
        $filter: Filter,
        $env: string
    }
}
script获取值
import {getCurrentInstance} from "vue" // 获取当前组件实例

const instance = getCurrentInstance()
instance.proxy.$env
instance.proxy.$filter.format

样式

scoped

会给dom节点添加一个不重复属性来确定其唯一性

deep

定制化UI组件库样式

:deep(类名){
	样式
}
全局选择器
:global(类) {
	样式
}
动态样式
<template>
	<div class="box1">
        我是一个盒子
        <div :class="[hang.box2]">
         我是第二个盒子    
       	</div>
    </div>
</template>

<script setup lang="ts">
import {ref} from "vue"
    
const style = ref({
    color: "red",
    fontSize: "14px"
})
</script>

<style scoped module="hang">
    .box1 {
        color: v-bind(style.color);
        font-size: v-bind(style.fontSize);
    }
    .box2 {
        border: 1px solid #ccc;
    }
</style>

Suspense组件

骨架屏

用于加载异步组件,搭配骨架屏组件使用

<template>
  <Suspense>
    <template #default>需要显示的组件,由于需要发起请求,可能会耗时</template>
    <template #fallback>骨架屏组件,当请求还未完成时显示</template>
  </Suspense>
</template>

<script setup lang="ts">
import { defineAsyncComponent } from 'vue';
// 异步组件
const AsyncComp = defineAsyncComponent(() => import('@/components/AsyncComp.vue'));
</script>

keep-alive组件

缓存

用于缓存,可以用于缓存信息,比如说我们可以包裹一个form表单

<template>
  <KeepAlive>
    <form>
      休需要缓存的表单, 如用户输入的信息进行缓存,切换页面返回信息依然存在
    </form>
  </KeepAlive>
</template>
部分缓存

如果范围太大,我们只想缓存一部分呢

<KeepAlive :include="["组件"]">
<KeepAlive :exclude="["组件"]">
限制数量

如果想要限制缓存数量

<KeepAlive :max="10">
生命周期

使用之后新增了什么东西呢(两个生命周期)

<script setup lang="ts">
import { onMounted, onUnmounted, onActivated, onDeactivated } from 'vue';

onMounted(() => {
  console.log("我只发生一次, 可以把初始化请求给我");
}),

onActivated(() => {
  console.log("每次切换包裹的组件,我就执行一次");
})

onDeactivated(() => {
  console.log("keep-alive卸载");
})

onUnmounted(() => {
  console.log("我不再执行了");
}),
</script>

性能优化

分析打包后的体积

由于vue3项目默认打包基于rollup

npm install rollup-plugin-visualizer

引入插件vite.config.ts

import {visualizer} from "rollup-plugin-visualizer"

plugins: [visualizer({open: true})]

打包

npm run build
通过配置

vite.config.ts

build: {
    cssCodeSplit: true, // css分片
    sourcemap: false, // 不生成sourcemap
    minify: "", // esbuild打包速度最快 terser打包体积最小
    assetsInlineLimit: 4000,
}
PWA离线存储技术

让web网页无限接近于Native应用,也就是原生应用

启动打包后的项目服务
npm install http-server -g

dist目录下
http-server -p 端口号
  • 35
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值