vue冷知识点(一)


前言


提示:本文基于vue3

一、DOM传送门

Vue.js提供了一个内置组件,将其插槽内容渲染到另一个DOM,成为该DOM的一部分。
// Teleport 的使用
<script setup>
const msg = "Hello World"
</script>
<template>
  <Teleport to="body">
   <span>{{ msg }}</span>
  </Teleport>
</template>

二、优化性能的指令

Vue.js 提供了一个指令,以便只渲染一次元素和组件,并且跳过以后的更新。
// v-once指令
<script setup>
import { ref } from "vue"
const count = ref(0)
setInterval(() => {
  count.value++
}, 1000)
</script>
<template>
  <span  v-once>Make it never change: {{ count }}</span>
</template>

三、动态CSS

Vue单文件组件
// css中使用v-bind
<script setup>
import { ref } from "vue"
const theme = ref("red")
const colors = ["blue", "yellow", "red", "green"]
setInterval(() => {
  theme.value = colors[Math.floor(Math.random() * 4)]
}, 1000)
</script>
<template>
  <p>hello</p>
</template>
<style scoped>
p {
  color: v-bind(theme )
}
</style>

四、全局CSS

有些时候,我们想在具有CSS作用域的Vue单文件组件设置全局CSS样式
// css中:global的使用
template>
  <p>Hello Vue.js</p>
</template>

<style scoped>

p {
  font-size:20px;
  color:red;
  text-align: center;
  line-height: 50px;
}

:global(body) {
  width: 100vw;
  height: 100vh;
  background-color: burlywood;
}
</style>

五、Prop验证

验证Button组件的Prop类型 ,使它只接收: primary | ghost | dashed | link | text | default ,且默认值为default
<script setup>
defineProps({
  type: {
    type: String,
    default: 'default',
    validator(value){
      return ['primary', 'ghost', 'dashed' ,'link' ,'text','default'].includes(value)
    }
  },
})
</script>
<template>
  <button>Button</button>
</template>

六、函数式组件

尝试实现一个函数式组件
// h函数来实现
<template>
    <div>
        <h2>函数组件</h2>
        <list-component
            :list="list"
            :active-index="activeIndex"
            @toggle="toggle"
        />
    </div>
</template>
<script setup lang="ts">
import {ref, h} from "vue"
const ListComponent = (props, context)=>{
    const {list} = props
    const activeIndex = props['active-index']
    const {emit} = context
    const lis = list.map((item:{name:string}, index:number)=>{
        return h('li',{
                style:{
                    color: activeIndex===index?'red':''
                },
                onClick:()=>{
                    emit('toggle', index)
                }
        },item.name)
    })
    return h('ul',lis)
}
const list = [{
    name:"JSON"
},{
    name:"Doe"
},
{
    name:"smith"
}]
const activeIndex = ref(0)
function toggle (index:number){
    activeIndex.value = index
}
</script>

七、渲染函数[h()]

使用h渲染函数来实现一个组件。请注意: 你应该确保参数被正确传递、事件被正常触发和插槽内容正常渲染
<script setup lang="ts">
import {FunctionalComponent, h} from 'vue'
const MyButton: FunctionalComponent<{}, {customClick(): void}> = (props, context)=> {
  console.log(props, context)
  return h('button', {
    ...context.attrs,
    onClick: () => context.emit("customClick")
  }, context.slots)
}
const onClick = () => {
  console.log('onClick')
}
</script>
<template>
  <MyButton type="submit" class="om" :disabled="false" @custom-click="onClick">
    my button
  </MyButton>
</template>

八、树组件

需要实现一个树组件
<script setup lang="ts">
interface TreeData {
  key: string
  title: string
  children: TreeData[]
}
defineProps<{data: TreeData[]}>()
</script>
<template>
  <ul>
    <template v-for="item in data" :key="item.key">
      <li >{{ item.title }}</li>
      <ul v-if="item.children && Array(item.children)">
         <TreeComponent :data="item.children" />
      </ul>
    </template>
  </ul>
</template>

九、切换器

尝试编写可组合函数
import { ref } from 'vue';
function useToggle(state: boolean): [boolean, () => void] {
  const status = ref(state);
  const toggle = () => (status.value = !status.value);
  return [status, toggle];
}
const [state, toggle] = useToggle(false);

十、计数器

实现一个计数器
<script setup lang="ts">
import { ref } from 'vue';
interface UseCounterOptions {
  min?: number;
  max?: number;
}
function useCounter(initialValue = 0, options: UseCounterOptions = {}) {
  const { min, max } = options;
  const count = ref(initialValue);
  const inc = () => max > count.value && count.value++;
  const dec = () => min < count.value && count.value--;
  const reset = () => (count.value = initialValue);
  return { count, inc, dec, reset };
}
const { count, inc, dec, reset } = useCounter(0, { min: 0, max: 10 });
</script>
<template>
  <p>Count: {{ count }}</p>
  <button @click="inc">inc</button>
  <button @click="dec">dec</button>
  <button @click="reset">reset</button>
</template>

十一、实现本地存储函数

我们经常需要使用localStorageAPI,一个好用的可组合函数封装将帮助我们更好地使用它
<script setup lang='ts'>
import { ref, watchEffect } from "vue"
function useLocalStorage(key: string, initialValue: any) {
  const value = ref(window.localStorage.getItem(key) || initialValue)
  watchEffect(() => {
    window.localStorage.setItem(key, value.value)
  })
  return value
}
const counter = useLocalStorage("counter", 0)
console.log(counter.value)
const update = () => counter.value++
</script>
<template>
  <p>Counter: {{ counter }}</p>
  <button @click="update">
    Update
  </button>
</template>

十二、鼠标坐标

使用Vue.js时,我们应该关注可复用性,可组合函数是一个很好的方式
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue';
function useEventListener(
  target: Window,
  event: string,
  callback: (e: any) => void
) {
  onMounted(() => {
    target.addEventListener(event, callback);
  });
  onUnmounted(() => {
    target.removeEventListener(event, callback);
  });
}
function useMouse() {
  const x = ref(0);
  const y = ref(0);
  useEventListener(window, 'mousemove', (event) => {
    x.value = event.pagex || document.body.scrollLeft + event.clientX;
    y.value = event.pagey || document.body.scrollTop + event.clientY;
  });
  return {
    x,
    y,
  };
}
const { x, y } = useMouse();
</script>
<template>Mouse position is at: {{ x }}, {{ y }}</template>

十三、生命周期钩子

使用 组合式 API: 生命周期钩子 来完成
<script setup lang="ts">
import { onMounted, onBeforeUnmount, inject } from "vue"
const timer = inject("timer")
const count = inject("count")
onMounted(() => {
  timer.value = window.setInterval(() => {
    count.value++
  }, 1000)
})
onBeforeUnmount(()=>{
  window.clearInterval(timer.value)
})
</script>
<template>
  <div>
    <p>
      Child Component: {{ count }}
    </p>
  </div>
</template>

十四、ref 全家桶

使用 响应式 API: ref 来完成
<template>
  <div>
    <p>
      <span @click="update(count - 1)">-</span>
      {{ count }}
      <span @click="update(count + 1)">+</span>
    </p>
  </div>
</template>
<script setup lang="ts">
import { ref, reactive, isRef, unref, toRef } from "vue"
import type { Ref } from "vue"
const initial = ref(10)
const count = ref(0)
// 挑战 1: 更新 ref
function update(value) {
  // 实现...
  count.value = value
}
/**
 * 挑战 2: 检查`count`是否为一个 ref 对象
 * 确保以下输出为1
*/
console.log(
  // impl ? 1 : 0
  isRef(count) ? 1 : 0
)
/**
 * 挑战 3: 如果参数是一个 ref,则返回内部值,否则返回参数本身
 * 确保以下输出为true
*/
function initialCount(value: number | Ref<number>) {

  // 确保以下输出为true
  console.log(unref(value) === 10)
}
initialCount(initial)
/**
 * 挑战 4:
 * 为源响应式对象上的某个 `property` 新创建一个 `ref`。
 * 然后,`ref` 可以被传递,它会保持对其源`property`的响应式连接。
 * 确保以下输出为true
*/
const state = reactive({
  foo: 1,
  bar: 2,
})
const fooRef = toRef(state, 'foo') // 修改这里的实现...
// 修改引用将更新原引用
fooRef.value++
console.log(state.foo === 2)
// 修改原引用也会更新`ref`
state.foo++
console.log(fooRef.value === 3)
</script>

十五、响应性丟失

在 JavaScript 中,我们经常解构/扩展对象。在Vue.js中,我们同样解构/扩展“响应式”对象,但它会失去响应性。如何保证解构/扩展不丢失响应性 ?
// toRefs 让所有属性具备响应式。 就可以解构
<script setup lang="ts">
import { reactive,toRefs } from "vue"
function useCount() {
  const state = reactive({
    count: 0,
  })
  function update(value: number) {
    state.count = value
  }
  return {
    state: toRefs(state),
    update,
  }
}
const { state: { count }, update } = useCount()
</script>
<template>
  <div>
    <p>
      <span @click="update(count-1)">-</span>
      {{ count }}
      <span @click="update(count+1)">+</span>
    </p>
  </div>
</template>
// 另一种方式,对一个属性toRef
<script setup lang="ts">
import { reactive,toRef } from "vue"
function useCount() {
  const state = reactive({
    count: 0,
  })
  function update(value: number) {
    state.count = value
  }
  return {
    state,
    update,
  }
}
const { state, update } = useCount()
const countRef = toRef(state,'count')
</script>
<template>
  <div>
    <p>
      <span @click="update(countRef-1)">-</span>
      {{ countRef }}
      <span @click="update(countRef+1)">+</span>
    </p>
  </div>
</template>
<style>
  body {
    user-select: none;
  }
</style>

十六、可写的计算属性

创建一个可写的计算属性
<template>
  <div>
    <p>{{ count }}</p>
    <p>{{ plusOne }}</p>
  </div>
</template>
<script setup lang="ts">
import { ref, computed } from "vue"
const count = ref(1)
/**
 * 确保 `plusOne` 可以被写入。
 * 最终我们得到的结果应该是 `plusOne` 等于 3 和 `count` 等于 2。
*/
const plusOne = computed({
  get() {
    return count.value + 1
  },
  set(newValue) {
    return count.value++
  }
})
plusOne.value++
</script>

十七、watch 全家桶

将使用 响应式 API: watch 来完成
// watch 是可以取消的
// watch的配置项含义
//{
//'immediate': true,
//'deep': true,
//'flush': post
//}
<script setup lang="ts">
import { ref, watch,nextTick } from "vue"
const count = ref(0)
/**
 * 挑战 1: Watch 一次
 * 确保副作用函数只执行一次
*/
const onceWatch = watch(count, () => {
  console.log("Only triggered once")
  // 执行本身可以取消监听
  onceWatch()
})
count.value = 1
setTimeout(() => count.value = 2)

/**
 * 挑战 2: Watch 对象
 * 确保副作用函数被正确触发
*/
const state = ref({
  count: 0,
})
// 深度监听 或者 watch(()=> state.value.count,...)
watch(state, () => {
  console.log("The state.count updated")
},{deep: true})

state.value.count = 2

/**
 * 挑战 3: 副作用函数刷新时机
 * 确保正确访问到更新后的`eleRef`值
*/

const eleRef = ref()
const age = ref(2)
watch(age, () => {
  console.log(eleRef.value)
},{flush: 'post'})
 // another solution:  use nextTick to solve it
 // watch(age, async () => {
 //   await nextTick()
 //   console.log(eleRef.value)
 // })
age.value = 18
</script>
<template>
  <div>
    <p>
      {{ count }}
    </p>
    <p ref="eleRef">
      {{ age }}
    </p>
  </div>
</template>

十八、浅层 ref

将使用 响应式 API: shallowRef 来完成
<script setup lang="ts">
import { shallowRef, watch } from "vue"
const state = shallowRef({ count: 1 })
// Does NOT trigger
watch(state, () => {
  console.log("State.count Updated")
}, { deep: true })
/**
 * 请注意,虽然在视图上能够看到count已经被修改了,但是并不会触发Wath,这是执行同步代码后,直接先于watch进入	到 effect 去了,导致在视图上起来已经赋值成功了
 然后如果在异步代码中执行一下的代码,并不会触发视图的更新
 所以要考虑同步和异步的问题
*/
  // 不会更新
  //Promise.resolve().then(() => {
  //	state.value.count = 2; // 不会被触发更改
	//});

// 同步代码会更新
// state.value.count = 2
// 由于shallowRef只对value层进行响应代理,不会进行深度deep响应,所以应该采用重新赋值的方式,触发watch
  state.value = {
    count: 2
  }
</script>
<template>
  <div>
    <p>
      {{ state.count }}
    </p>
  </div>
</template>

十九、依赖注入

组合式 API: 依赖注入 来完成它
<script setup lang="ts">
  import { inject } from 'vue'
  const count = inject('count')
</script>
<template>
  {{ count }}
</template>
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值