Vue-组合式API

vite构建工具

搭建vue3项目

  • npm create vite
  • cd dir-vite : 进入项目目录
  • npm install: 安装依赖
  • npm run dev: 启动项目
Local:   http://localhost:5173/
Network: use --host to expose
Vue DevTools: Open http://localhost:5173/__devtools__/ as a separate window
Vue DevTools: Press Alt+Shift+D in App to toggle the Vue DevTools

启动过程

  1. 启动服务
    当执行npm run dev时, 使用了vite工具启动了一个调试服务器(dev Server). 监听5173端口.
    1. 会将vite-vue作为网站的根目录
    2. 会将index.html作为网站的默认文件
    3. 当浏览器访问localhost:5173时, 调试服务器会将index.html返回给浏览器
  2. 加载main.js
    1. index.html中, 加载了main.js
    2. main.js导入App组件

单文件组件

一个.vue文件就是一个单文件组件, 包括

  • template: 模板, 用于编写HTML
  • script: 逻辑, 用于编写JS
  • style: 样式, 用于编写CSS
<template>
  <!-- 组件的模板部分 -->
  计数器:{{ count }}
  <button @click="handleClick">点击+1</button>
</template>

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

const count = ref(0)

function handleClick() {
  count.value++
}
</script>

<style>
/* 组件的样式 */
</style>

组合式API

setup语法糖

vue项目中, 通常使用setup语法简化书写

  1. 在setup语法中定义变量, 可以直接在模板中使用
  2. 在setup语法中定义函数, 可以直接在模板中使用
  3. 导入的组件对象, 可以直接在模板中使用

常用的组合式API

  • ref
  • computed
  • watch
  • onMounted

ref

响应性 API,用于创建一个响应式的数据源ref对象, ref 对象具有一个指向内部值的 .value 属性。

import { ref } from 'vue'
// 声明变量
const count = ref(0);
// 使用变量, 需要通过.value属性来访问
console.log(count.value); // 输出 0

// 在 setup 函数中返回 ref
export default {
  setup() {
    const count = ref(0);
    return { count };
  }
};
<template>
  <button @click="handleClick">显示/隐藏</button>
  <div v-show="flag">这是一个div</div>
</template>

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

// 声明变量
const flag = ref(true)

// 声明函数
function handleClick() {
  flag.value = !flag.value
}
</script>

computed

<template>
  姓: <input type="text" v-model="firstname" /> <br />
  名: <input type="text" v-model="lastname" /> <br />
  全名: {{ fullname }}
</template>

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

  const firstname = ref('')
  const lastname = ref('')

  const fullname = computed(() => {
    return firstname.value + lastname.value
  })
</script>

创建可读写的计算属性:

const doubleCount = computed({
  get: () => {
    return count.value * 2;
  },
  set: (newValue) => {
    count.value = newValue / 2;
  }
});

watch

<template>
  单价: 5 <br />
  数量: <input type="text" v-model.number="num" /> <br />
  总价: {{ total }}
</template>

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

const num = ref(0)
const total = ref(0)

watch(num, () => {
  total.value = num.value * 5
})
</script>

onMounted

生命周期钩子函数,用于在组件挂载(即元素被插入到 DOM 中)之后执行代码。这是执行副作用操作(如设置定时器、发起网络请求、直接操作 DOM 等)的理想时机。

<template>
  <ul>
    <li v-for="book in books" :key="book.id">{{ book.name }}</li>
  </ul>
</template>

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

const books = ref([])

onMounted(() => {
  // 在onMounted中给books赋值
  setTimeout(() => {
    books.value = [
      { id: 1, name: 'vue' },
      { id: 2, name: 'gis' },
      { id: 3, name: 'js' },
    ]
  }, 1000)
})
</script>

组件

简单使用

  1. 定义组件

components目录下, 创建组件文件TheCounter.vue

<template>
  计数器:{{ count }}
  <button @click="handleClick">点击+1</button>
</template>

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

const count = ref(0)
console.log(count)

function handleClick() {
  count.value++
}
</script>

<style>
/* 组件的样式 */
</style>
  1. 导入组件

App.vue中, 通过import语法导入组件对象

<script setup>
// 2. 导入组件对象
import TheCounter from './components/TheCounter.vue'
</script>
  1. 引用组件

App.vue的模板中, 通过组件名引用组件

<template>
  <!-- 3. 引用组件 -->
  <TheCounter></TheCounter>
</template>

父传子

父组件向子组件传递数据

  1. 在父组件中, 借助自定义属性
  2. 在子组件中, 通过defineProps接受

defineProps

  1. 只能在 <script setup> 的顶层使用
  2. type: StringNumberBooleanArrayObjectDate
  3. default : 默认值
  4. required: true,指定一个属性是必需的
  5. validator,用于自定义验证函数

静态绑定

<template>
  <TheBlog title="01-vue"></TheBlog>
  <TheBlog title="02-gis"></TheBlog>
  <TheBlog title="03-js"></TheBlog>
</template>

<script setup>
import TheBlog from './components/TheBlog.vue'
</script>
<template>
  <div>{{ title }}</div>
</template>

<script setup>
const props = defineProps({
  title: {
    type: String,
    required: true,
  },
})
</script>

动态绑定

<template>
  <TheBlog title="01-vue"></TheBlog>
  <TheBlog title="02-gis"></TheBlog>
  <!-- 将属性值和App组件中的一个状态动态绑定 -->
  <TheBlog :title="t"></TheBlog>
</template>

<script setup>
import TheBlog from './components/TheBlog.vue'
import { ref } from 'vue'
const t = ref('03-标题')
</script>

子传父

子组件向父组件传递数据

  1. 在子组件中, 触发自定义事件
  2. 在父组件中, 监听自定义事件, 在回调函数中通过参数获取

自定义事件

const emit = defineEmits(['eventName1', 'eventName2']);

<script setup>
const emit = defineEmits({
  eventName1: null, // 不进行类型检查
  eventName2: (payload) => {
    // 类型检查函数
    if (typeof payload !== 'string') {
      console.warn('eventName2 expects a string as the payload');
    }
  }
});
</script>
<template>
  <button @click="updateValue(newvalue)">更新值</button>
</template>

<script setup>
import { ref } from 'vue';
const newvalue = ref('新值')

const emit = defineEmits(['childclick']);
function updateValue(newValue) {
  emit('childclick', newValue);
}
</script>
<template>
  <ChildComponent @childclick="handleChildClick($event)" />
  <p>当前值: {{ modelValue }}</p>
</template>

<script setup>
import { ref } from 'vue';
import ChildComponent from './components/test.vue';

const modelValue = ref('初始值');
function handleChildClick(value) {
  modelValue.value = value
}
</script>

实例: 根据id关闭指定组件

<template>
  <div>
    {{ title }}
    <button @click="handleClick">关闭</button>
  </div>
</template>

<script setup>
const props = defineProps({
  title: {
    type: String,
    required: true,
  },
  id: {
    type: Number,
    required: true,
  },
})
const emits = defineEmits(['close'])
function handleClick() {
  emits('close', props.id)
}
</script>
<template>
  <!-- v-if和v-for不能应用于同一个元素 -->
  <TheBlog
    v-for="blog in showBlogs"
    :key="blog.id"
    :title="blog.title"
    :id="blog.id"
    @close="handleClose"
  ></TheBlog>
</template>

<script setup>
import TheBlog from './components/test.vue'
import { ref, computed } from 'vue'

const blogs = ref([
  { id: 1, title: '01-vue', isShow: true },
  { id: 2, title: '02-gis', isShow: true },
  { id: 3, title: '03-mapbox', isShow: true },
])

const showBlogs = computed(() => {
  return blogs.value.filter((item) => item.isShow)
})
function handleClose(id) {
  console.log(id)
  const blog = blogs.value.find((item) => item.id == id)
  blog.isShow = false
}
</script>

provide和inject

  • provide: 提供. 数据由祖先组件提供
  • inject: 注入. 由后代组件获取数据

app应用实例上挂载

const app = createApp(App)
// 通过app定义全局的对象: $map
app.provide('$map', { map: 'world' })

app对应的子组件中, 可以直接引用

import { inject } from 'vue'

const map = inject('$map')
console.log(map)

插槽

  1. 在组件中预留一个slot插槽
  2. 在组件渲染时, 将内容插入slot

普通插槽

在子组件定时时, 预留插槽

<template>
  <!-- 预留一个slot插槽 -->
  <slot></slot>
</template>

在渲染子组件时, 会使用组件内部的内容替换掉slot部分

<TheComputer>
  <div>
    <h3>名称: MacBookPro</h3>
    <p>型号: MBP 14 M1</p>
  </div>
</TheComputer>

具名插槽

在定义slot插槽时, 设置name属性

<template>
  <!-- 具名插槽 -->
  <slot name="cpu"></slot>
  <slot name="memery"></slot>
</template>

在渲染时, 通过v-slot指令指定插槽的名称

<template>
  <TheComputer>
    <template v-slot:cpu>
      <!-- 这里的内容插入到名字叫cpu的插槽中 -->
      CPU: M1 Pro
    </template>
    <template #memery> Memery: 16G </template>
  </TheComputer>
</template>

作用域插槽

将子组件的属性及其值传递给父组件,以便在父组件的插槽内容中使用

  1. 默认插槽
<template>
  <div>
    <slot :userprop="user"></slot>
  </div>
</template>

<script setup>
  const user = reactive({
    name: '张三',
    age: 30})
</script>
<template>
  <test>
    <template v-slot:default="slotProps">
      {{ slotProps.userprop.name }} - {{ slotProps.userprop.age }}
    </template>
  </test>
</template>
  1. 具名插槽
<template>
  <div>
    <slot name="body" msg="这是消息" :age="12"></slot>
  </div>
</template>
<template>
  <test>
    <template #body="data">
      这是插槽内部传递的数据: {{ data.msg  }} age: {{ data.age }}
    </template>
  </test>
</template>
posted @ 2024-11-27 22:53  Khru  阅读(10)  评论(0编辑  收藏  举报