uni-app:创建组件

  1. 什么是组件
  2. 组件创建
  3. Vue文件的执行顺序
    1. 完整的执行顺序
    2. 实际执行顺序演示
    3. 为什么是这个顺序?
    4. 特殊情况:script setup 内的异步

什么是组件

组件就是每一个 .vue 文件,无论是哪个文件夹下的。

pages/shop/category.vue   ← 这是组件
pages/shop/goods.vue      ← 这是组件  
pages/shop/search.vue     ← 这是组件
pages/index/index.vue     ← 这是组件
pages/my/my.vue          ← 这是组件
components/shop-sku.vue   ← 这是组件
components/mod-nav-bar.vue ← 这是组件

组件的本质是”可复用的代码块”

<!-- 这是一个组件:按钮组件 -->
<!-- components/MyButton.vue -->
<template>
  <button class="my-btn" @click="handleClick">
    <slot></slot>  <!-- 这里可以放任何内容 -->
  </button>
</template>

<!-- 可以在任何地方复用这个组件 -->
<!-- pages/index.vue -->
<template>
  <view>
    <MyButton>登录</MyButton>
    <MyButton>注册</MyButton>
    <MyButton>提交订单</MyButton>
  </view>
</template>

<!-- pages/shop.vue -->
<template>
  <view>
    <MyButton>加入购物车</MyButton>
    <MyButton>立即购买</MyButton>
  </view>
</template>

在项目中,只要满足以下条件的都是组件:

  1. 文件扩展名是 .vue
  2. 可以被导入并在模板中使用
  3. 有自己独立的模板、脚本、样式

组件创建

组件创建 = .vue文件加载 + 实例化

组件创建的完整过程:

// 当这个代码执行时:
<template>
  <Category />  <!-- 使用Category组件 -->
</template>

// 背后发生的过程:

组件的”加载”过程分解

// 步骤1:文件被加载(解析)
// 把 category.vue 这个文件读入内存
// 拆分成 template、script、style 三部分

// 步骤2:实例化(创建实例)
// 在内存中创建一个组件对象
const categoryInstance = {
  // 有自己独立的数据
  currentClassId: ref(""),
  mainScrollTop: ref(0),
  
  // 有自己的方法
  onClassTab() {},
  showSkuPop() {},
  
  // 有自己的模板
  template: `<view>...</view>`
}

// 步骤3:执行代码
// 运行 <script setup> 中的代码
// 发送请求、初始化数据
console.log('组件被创建了!')
const { data } = useGoodsCategoryApi() // 发送请求

可以观察到的现象

<!-- category.vue -->
<script setup>
// 这一行会在组件创建时立即执行
console.log('🔥 Category组件开始创建', new Date().toLocaleTimeString())

// 请求立即发送
const { data: categoryList } = useGoodsCategoryApi()
console.log('📦 请求已发送')

// 你可以看到请求的时间
// 控制台输出:
// 🔥 Category组件开始创建 14:30:25
// 📦 请求已发送 14:30:25
// (网络请求) 14:30:26 请求返回
</script>

Vue文件的执行顺序

完整的执行顺序

<!-- 1. 首先:<script setup> 部分(立即执行) -->
<script setup>
console.log('1. script setup 开始执行')

import { ref, onMounted } from 'vue'
import { useCartStore } from '@/stores/cart'

// 这些代码立即执行
console.log('2. 导入语句执行')
const count = ref(0)
console.log('3. 响应式数据创建')

// 生命周期钩子只是注册,不会立即执行
onMounted(() => {
  console.log('6. onMounted 执行')
})

console.log('4. script setup 执行完毕')
</script>

<!-- 2. 然后:<template> 被编译成渲染函数 -->
<!-- template本身不"执行",它被编译成JavaScript渲染函数 -->

<!-- 3. 最后:<style> 被提取处理 -->
<!-- style是静态样式,不参与执行顺序 -->

<template>
  <view>
    <!-- 当组件渲染时,这里的内容被转换成DOM -->
    {{ console.log('5. 模板渲染过程中执行') }}
  </view>
</template>

<style lang="scss" scoped>
/* 样式被提取到单独的CSS,不参与JS执行顺序 */
</style>

实际执行顺序演示

<script setup>
// 第1组:立即执行
console.log('📦 [1] script setup 开始')

// 导入语句会提升,但执行顺序按书写顺序
import { ref, onMounted } from 'vue'
console.log('📦 [2] 导入完成')

// 响应式数据创建
const message = ref('hello')
console.log('📦 [3] 响应式数据创建')

// 普通函数定义
const handleClick = () => {
  console.log('点击事件')
}
console.log('📦 [4] 函数定义完成')

// 生命周期钩子(只注册,不执行)
onMounted(() => {
  console.log('📦 [7] onMounted 回调执行')
})

// 立即执行的逻辑
const initData = () => {
  console.log('📦 [5] initData 函数执行')
}
initData()

console.log('📦 [6] script setup 结束')
</script>

<template>
  <view>
    <!-- 模板中的表达式在渲染时执行 -->
    <text>{{ console.log('📦 [模板] 渲染过程中') }}</text>
    <button @click="handleClick">按钮</button>
  </view>
</template>

<!-- 控制台输出顺序: -->
📦 [1] script setup 开始
📦 [2] 导入完成
📦 [3] 响应式数据创建  
📦 [4] 函数定义完成
📦 [5] initData 函数执行
📦 [6] script setup 结束
📦 [模板] 渲染过程中
📦 [7] onMounted 回调执行

为什么是这个顺序?

// Vue组件编译后的伪代码
export default {
  setup() {
    // 1. <script setup> 的内容被提取到这里
    console.log('script setup 执行')
    
    // 注册生命周期
    onMounted(() => {
      // 3. 最后执行
    })
    
    return {
      // 返回给模板使用的数据和方法
    }
  },
  
  // 2. <template> 被编译成 render 函数
  render() {
    console.log('模板渲染')
    return h('view', ...)
  }
}

特殊情况:script setup 内的异步

<script setup>
console.log('1. 同步代码立即执行')

// 异步代码会晚于同步代码
setTimeout(() => {
  console.log('4. 异步回调执行')
}, 0)

// Promise 微任务
Promise.resolve().then(() => {
  console.log('3. 微任务执行')
})

console.log('2. 同步代码继续执行')
</script>

<template>
  <view>模板渲染</view>
</template>

// 输出顺序:
// 1. 同步代码立即执行
// 2. 同步代码继续执行
// 模板渲染
// 3. 微任务执行
// 4. 异步回调执行

×

喜欢就点赞,疼爱就打赏