vue3使用笔记

个人使用React6年多了,vue使用还是大于6年前,因为公司项目需要使用vue,所以做个笔记。个人感觉虽然vue3引入的组合式写法更简单了,但是对于快速开发或者可维护性来说,没有选项式写法好,这点和React是一样的,class写法更好维护、更易于阅读、更易于复用,这点vue不明显,react非常明显,useEffect如果多了、依赖多了,逻辑是非常难以维护的。

小结

  • 组件库PC用 element-plus,移动端用vant
  • 可以把逻辑写在多个script里
  • vue3可以多个根标签,不需要 Fragment
  • vite-plugin-vue-setup-extend 可以指定组件的名字,组件名字默认就是文件名
  • defineExpose定义可以暴露的方法给父组件
  • 如果页面代码比较多,样式代码可抽离为单独文件引入
    <style scoped lang="less">
    @import "./index.less";
    </style>
    

基本使用

<div
  class="static"
  :class="{ active: isActive, 'text-danger': hasError }"
></div>

<div :style="{ 'font-size': fontSize + 'px' }"></div>

<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>

全局方法

const app = createApp(App)
app.config.globalProperties.$myGlobalMethod = function () {
  return 'Hello World!'
}
this.$myGlobalMethod()

组件


<template>
 <div id="demo1">
   <div>{{title}}</div>
   <child />
 </div>
</template>
<script setup lang="ts">
import {defineProps} from "vue"
// 在vue3.2 中当我们导入子组件时,setup语法糖会自动去注册该组件,所以注册语句不需要写了
import child from "./children.vue"
// 接收父组件的props,可以传数组和对象
defineProps(["a"])

const data = defineProps({
  title: {
    type: String,
    default: ""
  }
})
// 拿到父组件的数据
console.log(data);
</script>

生命周期

<script lang="ts" setup name="Person">
  import { 
    ref, 
    onBeforeMount, 
    onMounted, 
    onBeforeUpdate, 
    onUpdated, 
    onBeforeUnmount, 
    onUnmounted 
  } from 'vue'

  // 数据
  let sum = ref(0)
  // 方法
  function changeSum() {
    sum.value += 1
  }
  console.log('setup')
  // 生命周期钩子
  onBeforeMount(()=>{
    console.log('挂载之前')
  })
  onMounted(()=>{
    console.log('挂载完毕')
  })
  onBeforeUpdate(()=>{
    console.log('更新之前')
  })
  onUpdated(()=>{
    console.log('更新完毕')
  })
  onBeforeUnmount(()=>{
    console.log('卸载之前')
  })
  onUnmounted(()=>{
    console.log('卸载完毕')
  })
</script>

事件

.prevent:.prevent用于阻止事件的默认行为,比如阻止表单提交时的页面刷新。
.stop:.stop用于停止事件冒泡,使得事件不向父级元素传播。
.once:.once表示事件只触发一次,之后就不再响应。
.capture:.capture指明事件应该在捕获阶段被触发,而不是在冒泡阶段。
.self:.self限定只有当事件在该元素本身(而不是子元素)触发时才执行。
.passive:.passive用于改善滚动性能,指明事件的默认行为不会影响到浏览器的滚动。

nextTick

在 Vue 中,当我们修改数据时,Vue 会自动更新视图。但是,由于 JavaScript 的事件循环机制,我们无法立即得知视图更新完成的时机。这时候,我们就需要使用 nextTick 来获取视图更新完成后的 DOM 状态。

自定义hook

import {ref,onMounted} from 'vue'

export default function(){
  let sum = ref(0)

  const increment = ()=>{
    sum.value += 1
  }
  const decrement = ()=>{
    sum.value -= 1
  }
  onMounted(()=>{
    increment()
  })

  //向外部暴露数据
  return {sum,increment,decrement}
}		

动态组件渲染

import tab1 from "./tab1.vue"
import tab2 from "./tab2.vue"

export const tabList = [
  {
    title: "销售项目进展情况",
    component: tab1,
  },
  {
    title: "订单与选型情况",
    component: tab2,
  }
]
    <van-collapse v-model="tab">
      <van-collapse-item
          v-for="(item, index) in tabList"
          :title="item.title"
          :key="index"
          :name="index"
      >
        <component :is="item.component"></component>
      </van-collapse-item>
    </van-collapse>


插槽

// 默认插槽使用
 <demo title="title1">
   <span>haha</span>
 </demo>
 // 组件 demo.vue
<template>
 <div>{{title}}</div>
  <slot>默认内容</slot>
</template>

// 具名插槽使用
<template>
  <div>
    <demo title="title1" v-slot:s1 >
        <span>content1</span>
    </demo>
    <demo title="title1">
    // 推荐这么用
      <template v-slot:s2 >
        <span>content2</span>
      </template>
      // 也可以这么写啊
     <template  #s2 >
        <span>content2</span>
      </template>
    </demo>
  </div>
</template>

// demo.vue
<template>
  <div>{{ title }}</div>
  <slot name="s1"></slot>
  <slot name="s2"></slot>
</template>

<template>
  <div>
    <demo title="title1">
      <template v-slot:name1="params" >
        <div v-for="a in params.data">{{ a }}</div>
      </template>
    </demo>
  </div>
</template>

// demo.vue 插槽可以传递数据
<template>
  <div>{{ title }}</div>
  <slot :data="data" name="name1"></slot>
</template>

<script setup>
import {defineProps} from "vue"
const data = [1,2,3]

defineProps(['title'])
</script>

数据的跨层级传递

<script setup>
import { provide, ref } from 'vue'

const location = ref('North Pole')

function updateLocation() {
  location.value = 'South Pole'
}

provide('location', {
  location,
  updateLocation
})
</script>
<!-- 在注入方组件 -->
<script setup>
import { inject } from 'vue'

const { location, updateLocation } = inject('location')
</script>

<template>
  <button @click="updateLocation">{{ location }}</button>
</template>


setup

  • setup里可以直接声明变量使用,而不用 ref
  • setup返回值,可以返回一个函数来渲染内容
  • setup写法和data、methods一起存在,data可以读取setup里的数据

ref

  • ref在组件上就是组件实例,用在dom上就是获取dom
<div id="hua" ref="hua">huahuadavids</div>
const hua = ref()
console.log(hua.value)
  • reactiveref的区别,前者在脚本中直接改变,不需要 .value,ref处理对象,其实也是reactive
  • reactive修改整个对象,会失去响应式,使用object.assign 就可以
  • toRefs用来解构reactive
const obj = reactive({name: "david", age: 22})
let {name,age } = toRefs(obj)

watch

  • watch是可以解除监视
const stopWatch = watch(name, (nv, ov) => {
  console.log(nv)
  if(nv === "stop"){
    stopWatch()
  }
})

  • watch一个 ref对象,整个对象都变了才行,如果想深度监视,第三个参数加true

const obj = ref({name: "david"})
watch(obj, (newValue, oldValue) => {
  console.log(newValue)
},  {
  // 深度监视
  deep: true, 
  // 立即执行
  immediate: true
})

  • watch 监视 reactive默认开启深度
  • watch 监视 getter函数,就是返回一个值的方法,最佳写法是写函数式,并且开启deep
const obj = reactive({name: "david", age: 8})
watch(() => obj.name, () => {
  console.log("name 改变")
})
  • watchEffect 和 react的一样,但是不需要指定依赖

通信

Vue3组件通信和Vue2的区别:
移出事件总线,使用mitt代替。
vuex换成了pinia。
把.sync优化到了v-model里面了。
l i s t e n e r s 所有的东西,合并到 listeners所有的东西,合并到 listeners所有的东西,合并到attrs中了。
$children被砍掉了。

props通信

缺点是只能一层一层的传递

// 父组件
<template>
  <Demo1 title="aaa" :getValue="getValue">
  </Demo1>
</template>
<script lang="ts" setup>
import Demo1 from "./Demo1.vue"
function getValue(a){
  console.log(a);
}
</script>

// 子组件
<template>
 <button @click="getValue('child')">send to parent</button>
</template>
<script setup lang="ts">
import {defineProps} from "vue"
const data = defineProps({
  getValue: {
    type: Function
  }
})
</script>

自定义事件通信

官方推荐肉串写法

//父组件
<template>
  <div>
    <h1>{{ title }}</h1>
    <Demo1 @change-route="clickEven"/>
  </div>
</template>
<script setup>
import Demo1 from "./Demo1.vue"
import {ref} from "vue"
const title = ref("parent-title")
const clickEven = (val) => {
  title.value = val
}
</script>

// 子组件
<template>
  <button @click=" emit('change-route', '1111')">2222</button>
</template>
<script setup>
import {defineEmits} from "vue";
const emit = defineEmits(['change-route'])
</script>

v-model

// 下边是等价的
<input v-model="a">
<input :value="a" @input="a = $event.target.value">

attr

$attr 用来获取父组件传了,但是子组件没有声明接受的数据。

// index1.vue
<template>
  <div id="index1">
    index1
    <div>a = {{a}}</div>
    <div>b = {{b}}</div>
    <index2 v-bind="{a,b,c}"></index2>
     <!--等价的 -->
<!--    <index2 :a="a"  :b="b" :c="c" ></index2>-->
  </div>
</template>
// index2.vue
<template>
  <div id="index2">
    index2
    <div>a = {{a}}</div>
    <div>b = {{b}}</div>
    <index3 v-bind="$attrs"></index3>
  </div>
</template>
// index3.vue
<template>
  <div id="index3">
    index3
    <br>
    a = {{a}}
  </div>
</template>

<script setup>
import {defineProps} from "vue"
defineProps(['a'])
</script>

$refs

获取所有子组件的实例对象,首先子组件得有ref才行。

vue-router

// 1. 简单使用
import {createRouter,createWebHistory} from 'vue-router'
import Home from '@/pages/Home.vue'
import News from '@/pages/News.vue'
import About from '@/pages/About.vue'

const router = createRouter({
	history:createWebHistory(),
	routes:[
		{
			path:'/home',
			component:Home
		},
		{
			path:'/about',
			component:About
		}
	]
})
export default router
import router from './router/index'
app.use(router)

app.mount('#app')

// 2. link
<router-link active-class="active" to="/home">主页</router-link>
<router-link active-class="active" :to="{path:'/home'}">Home</router-link>

// 3. api使用
import {useRouter, useRoute} from "vue-router";
const router = useRouter();
router.replace('/processControl');
const route = useRoute()
// 打印query参数
console.log(route.query)


Teleport

<teleport to='body' >
    <div class="modal" v-show="isShow">
      <h2>我是一个弹窗</h2>
      <p>我是弹窗中的一些内容</p>
      <button @click="isShow = false">关闭弹窗</button>
    </div>
</teleport>

Suspense

import { defineAsyncComponent,Suspense } from "vue";
const Child = defineAsyncComponent(()=>import('./Child.vue'))

<template>
    <div class="app">
        <h3>我是App组件</h3>
        <Suspense>
          <template v-slot:default>
            <Child/>
          </template>
          <template v-slot:fallback>
            <h3>加载中.......</h3>
          </template>
        </Suspense>
    </div>
</template>

tsx写法

<script lang="tsx">
import {defineComponent} from 'vue';

export default defineComponent({
  props: {
    params: {
      type: Object,
      default: () => {
      }
    }
  },
  data: () => {
    return {
      data: {
        name: 'data-name'
      }
    }
  },
  methods: {
    renderFooter() {
      return (
          <div>
            <van-button>确定</van-button>
            <van-button>取消</van-button>
          </div>
      )
    },
    setName(){
      this.data.name = "set-name"
    }
  },
  render() {
    const buttons = this.renderFooter()
    return (
        <div class="demo">
          <div>内容</div>
          {buttons}
          <div>
            {this.data.name}
          </div>
          <van-button onClick={this.setName}></van-button>
        </div>
    )
  }
})
</script>

<style scoped lang="less">
.demo {
  color: red;
}
</style>

pinia

// index.ts
import { createPinia } from 'pinia';
import { createApp } from 'vue';
import App from './App.vue';
// 创建 Pinia 实例
const pinia = createPinia();
// 创建 Vue 应用
const app = createApp(App);
// 使用 Pinia 实例
app.use(pinia);
app.mount('#app');

// stores/counter.js
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', {
  state: () => ({
    count: 0,
  }),
  actions: {
    increment() {
      this.count++;
    },
  },
});

// components/Counter.vue
import { useCounterStore } from '../stores/counter';

export default {
  setup() {
    const counter = useCounterStore();

    return {
      count: counter.count,
      increment: counter.increment,
    };
  },
};

参考链接

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值