vue3使用回顾/总结

 经验使你在第二次犯相同错误时及时发现。 —— 琼斯

一、基本使用

1. vue3.x组件开发のsetup和defineComponent

1.1 defineComponent
- 需要导入再使用。(使用ts需借助自动推导)
- defineComponent 返回传递给它的对象或有一个有合成类型的构造函数。用于类型推导,简化类型定义。
- 只要是vue本身API(比如setup或vue2.x的配置项),defineComponent 都可以自动推导。在编写组件中,只需维护自定义的数据类型,专注于业务。
- 参数:[ 具有组件选项的对象 | setup函数]
// 栗子
import { defineComponent } from 'vue'
// vue2.x 配置项
export default defineComponent({
  data() {
    return { count: 1 }
  },
  methods: {
    increment() {
      this.count++
    }
  }
})

// vue3
export default defineComponent({
  setup (props, context) {
    // ...
    
    return {
      // ...template 中用到的业务数据
    }
  }
})

// 参数为 setup函数 --- 备忘
const HelloWorld = defineComponent(function HelloWorld () {
    return { 
        //  ...template 中用到的业务数据
    }
})
1.2 setup函数
- 一个组件选项,创建组件前执行,作为入口点
  3.x中,整个组件相关的业务代码,都可放到 setup 里编写。因为setup后其他的生命周期才会被启用
- 参数(props,context)
  props :它是响应式的(只要不解构它,或者使用 toRef / toRefs 进行响应式解构),当传入新的 prop 时,它将被更新。 
context :一个普通的对象,它暴露三个组件的 property(attrs, slots, emit)

2. 生命周期

2.1 生命周期对比(Vue3.x compares with Vue2.x)

在这里插入图片描述

官方解释: “setup 是围绕 beforeCreate 和 created 生命周期钩子运行”

3.x中用setup的周期可以替代2.x版本中的beforeCreate和created的两个钩子
2.2 vue3.x的生命周期使用

3.x中,生命周期必须先导入再使用的,且必需放setup中执行

3. vue3新语法糖——setup script

3.1 使用方法
<script setup></script>
3.2 作用
  • 自动注册子组件
    (1) vue2.x语法
<template>
  <div>
    <h2>父组件!</h2>
    <Child />
  </div>
</template>
<script>
import Child from './Child.vue'
export default {
    component: {
        Child // 注册子组件后方可在template中使用
    }
}
</script>

(2)vue3语法

<template>
  <div>
    <h2>父组件!</h2>
    <Child />
  </div>
</template>
<script>
import { defineComponent, ref } from 'vue';
import Child from './Child.vue'
export default defineComponent({
 components: { 
     Child // 注册子组件后方可在template中使用
 }, 
 setup() { 
     return { 
     } 
 }
});
</script>

(3)setup script语法

<template>
  <div>
    <h2>父组件!-setup script</h2>
    
    <Child />
  </div>
</template>
<script setup>
import Child from './Child.vue'
// 省略了子组件注册的过程,import后可直接在template中使用
</script>
  • 属性和方法无需返回
    (1)vue2.x在template中使用属性和方法栗子-略
    (2)vue3.x语法
    如需在template中使用属性和方法,必须手动返回对应的属性和方法。这种composition API的编写规则,相对繁琐。如下栗子:
<template>
  <div>
    <h2 @click="addCount">购买数量 {{ count}} 件</h2>
  </div>
</template>

<script>
import { defineComponent, ref } from 'vue';
export default defineComponent({
    setup() {
        const count = ref(1)
        const addCount = () => {
          count.value++
        }
        return {
            count,
            addCount
        }
    }
});
</script>

(3)setup script语法

<template>
  <div>
    <h2 @click="addCount">购买数量 {{ count}} 件</h2>
  </div>
</template>

<script setup>
import { ref } from 'vue';
const count = ref(1)
const addCount = () => {
  count.value++
}
</script>
  • 支持props、emit和context
    (1)vue3.x语法
    前面setup函数中提及,setup函数包含props和context2个参数,通过这两个参数,达到数据通信及事件触发的目的。(参考官方文档:Setup | Vue.js
    (2)setup scrip语法
    没有setup()那怎么获取到props和context呢?
    setup script语法糖提供了三个新的API来供我们使用:defineProps、defineEmit和useContext。
    defineProps用来接收父组件传来的值props;
    defineEmit用来声明触发的事件表;
    useContext用来获取组件上下文context
// father
<template>
  <div class="radio-card">
    <radio-card name="规格" :options="standards" @select="changeOption"/>
  </div>
</template>
<script setup>
import { reactive } from 'vue'
import RadioCard from './RadioCard.vue'
const standards = reactive([])
const changeOption = (option) => {
    console.log(option)
}
</script>


//child
<template>
  <div class="radio-card">
    <header class="card-header">
      <div class="title"> {{ name }} </div>
    </header>
    <main class="card-body">
      <slot>
        <div class="radio-group">
          <template v-for="option in options">
            <van-button
              class="radio-item"
              @touchstart="onSelect(option)"
            >{{option.value}}</van-button>
          </template>
        </div>
      </slot>
    </main>
  </div>
</template>

<script setup>
// import { useContext, defineProps, defineEmits } from 'vue'; 待验证-useContext放最后import会报错?
import { defineProps, defineEmits } from 'vue';
const props = defineProps({
  name: {
    type: String,
    default: () => ''
  },
  options: {
    type: Array,
    default: () => []
  }  
});
const emit = defineEmits(['select'])
const onSelect = (option) => {
  emit('select', option)
}
</script>

总结:

  1. setup() 在创建组件之前执行,因此在其中不能使用this。也就不能在setup里用this取对应熟悉数据、方法、computed计算属性里的数据。
  2. setup可以借助props和context参数,读取属性props,attrs,slots,emit。
  3. props响应式,慎用解构;context可普通对象解构。eg.const { attrs, slots, emit } = context

二、问题回顾及总结

1. 终端警告提示

如下图:
在这里插入图片描述
(1) 编写自定义组件,import了“defineEmits”、“defineProps”、“defineExpose”,终端提示这些api是个编译宏,不再需要导入。
(2) 是因为“defineEmits”、“defineProps”、“defineExpose”是使用setup script语法是定义对应属性、事件等的API,在

2. vue2中sync修饰符的功能,在vue3中如何呈现?

2.1 sync修饰符回顾
  vue规则:props 是单向向下绑定的,子组件不能修改props接收过来的外部数据。
  1. 如果在子组件中修改 props ,Vue会向你发出一个警告。(无法通过修改子组件的props来更改父组件。)而若需要在子组件更新数据时通知父组件同步更新,需要结合$emit和v-on实现。
  2. 而sync修饰符的作用则是简化事件声明及监听的写法。

如下栗子,比较sync和正常修改数据通知外层的写法:

// 父组件
<template>
    <div> 数量: {{num}}</div>
    <!-- <ChildComponent :num="num" @increase="num = $event"/> -->
    <ChildComponent :num.sync="num" />
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
    component: {
        ChildComponent
    },
    data() {
        return {
            num: 1
        }
    }
}
</scrip>

//子组件
<template>
    <div @click="addNum"> 接收数量: {{num}}</div>
</template>
<script>
export default {
    props: ['num'],
    // data() {
    //    return {
    //        childNum: this.num
    //    }
    // },
    methods: {
        addNum() {
            // this. childNum++
            // this.$emit('increase', this. childNum)
            this.$emit('update:num', this.num + 1)
        }
    }
}
</scrip>
2.2 sync的语法糖功能在vue3中如何编写使用?

vue3中,通过 v-model:propName 实现自定义组件的间数据的双向绑定。看栗子:

// 子组件 ActionSheet.vue
<template>
  <div class="ds-action-sheet">
    <!-- v-model:show 是vant组件抛出的v-model的属性 -->
    <van-action-sheet
      v-model:show="visible"
      :title="title"
      :closeable="closeable"
      :close-on-click-overlay="closeOnClickOverlay"
      :actions="actions"
      @select="onSelect"
      @close="onClose"
      @click-overlay="onClickOverlay"
      @cancel="onCancel"
    >
      <slot></slot>
    </van-action-sheet>
  </div>
</template>
<script setup>
const props = defineProps({
  visible: {
    type: Boolean,
    default: () => false
  },
  // 是否在点击遮罩层后关闭
  closeOnClickOverlay: {
    type: Boolean,
    default: () => true
  },
  title: {
    type: String,
    default: () => ''
  },
  // 面板选项列表
  actions: {
    type: Array,
    default: () => []
  }
}const emit = defineEmits(['update:visible', 'select', 'close', 'click-overlay'])
const onSelect = () => {
  emit('select')
}
const onClose = () => {
  // 关键句,父组件则可通过 v-model:visible 同步子组件更新后的数据
  emit('update:visible')
  emit('close')
}
const onCancel = () => {
  // cancel事件,修复打包后在微信浏览器无法更新visible值问题
  emit('update:visible')
  emit('cancel')
}
const onClickOverlay = () => {
  emit('click-overlay')
  if(props.closeOnClickOverlay) {
      // click-overlay事件 修复打包后在微信浏览器无法更新visible值问题
      emit('update:visible')
  }  
}
</script>

// 父组件
<template>
  <van-button @click="showActionSheet">弹出弹层</van-button>
  <ds-action-sheet
    title="do a test"
    v-model:visible="mVisilbe"
  >
    <div>弹层内容</div>
  </ds-action-sheet>
</template>
<script setup>
import { ref } from 'vue';
import DsActionSheet from './ActionSheet.vue';
const mVisilbe = ref(false)
const showActionSheet = () => {
    mVisilbe.value = true
}
</script>

总结:

  1. 父组件通过 “v-model:绑定的属性名”传递数据属性,支持绑定多个属性;
  2. 子组件配置emits,通过 “update:属性名” 的格式定义更新事件

3. vant+vue3,二次封装的ActionSheet组件(见2.2的栗子)问题

问题:打包后在微信浏览器中点击弹层的关闭按钮或点击遮罩层(已配置点击遮罩层关闭弹层配置项),无法正常关闭弹层。(本地运行可正常关闭)
解决:在对应事件中,添加 emit(‘update:visible’) ,手动添加更新visible值。见上栗子中的47行、52行及onClickOverlay 函数中的条件判断处理

4. vue3不支持过滤器功能

在 3.x 中,处理通用文本格式的过滤器功能已删除,不再支持。建议用方法调用或计算属性替换它们。

<template>
  <div>
    <span>总价:</span>
    <span class="unit">¥</span>
    <span class="price">
    {{ amountSeparatorFormatter(totalPrice) }}
    </span>
  </div>
</template>

<script setup>
const amountSeparatorFormatter = (value, fixedNum = 2) => {
  if (!value) {
    return;
  }
  if (isNaN(value)) {
    return value;
  }
  let val = Number(value).toFixed(fixedNum).toString();
  val = val.replace(/\./, ',')
  while (/\d{4}/.test(val)) {
    val = val.replace(/(\d+)(\d{3}\,)/, '$1,$2')
  }
  return val.replace(/\,(\d*)$/, '.$1')
}
</script>
若应用中存在全局过滤器,可以定义全局属性在所有组件应用次全局属性。eg:
<template>
  <h1>使用全局属性</h1>
  <p>{{ $filters. currencyShow(money) }}</p>
</template>
// main.js
const app = createApp(App)

app.config.globalProperties.$filters = {
  currencyShow(value) {
    return '¥' + value
  }
}

参考文档: https://vue3.chengpeiquan.com/

转载请标明出处!

Logo

前往低代码交流专区

更多推荐