赞
踩
- # 创建项目
- npm create vite@latest my-vue-app -- --template vue
- # 安装依赖
- npm i
- # 运行
- npm run dev
文本插值
最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 (即双大括号):
<span>Message: {{ msg }}</span>
双大括号标签会被替换为相应组件实例中 msg
属性的值。同时每次 msg
属性更改时它也会同步更新。
原始 HTML
双大括号会将数据解释为纯文本,而不是 HTML。若想插入 HTML,你需要使用 v-html 指令:
- <template>
- {{ num }}
- <span>Message: {{ msg }}</span>
- <p>Using text interpolation: {{ html }}</p>
- <p>Using v-html directive: <span v-html="html"></span></p>
- </template>
- <script setup>
- let num = 123
- let msg = "(*´▽`)ノノ"
- let html = '<span style="color: red">This should be red.</span>'
- </script>
Attribute 绑定
双大括号不能在 HTML attributes 中使用。想要响应式地绑定一个 attribute,应该使用 v-bind 指令
-
- <div v-bind:id="dynamicId"></div>
-
- <input v-bind:value="dynamicId">
- let dynamicId=1
v-bind
指令指示 Vue 将元素的 id
attribute 与组件的 dynamicId
属性保持一致。如果绑定的值是 null
或者 undefined
,那么该 attribute 将会从渲染的元素上移除。
简写
因为 v-bind
非常常用,我们提供了特定的简写语法:
-
- <div :id="dynamicId"></div>
-
- <input v-bind:value="dynamicId"><br>
- <input :value="dynamicId">
开头为 :
的 attribute 可能和一般的 HTML attribute 看起来不太一样,但它的确是合法的 attribute 名称字符,并且所有支持 Vue 的浏览器都能正确解析它。此外,他们不会出现在最终渲染的 DOM 中。简写语法是可选的,但相信在你了解了它更多的用处后,你应该会更喜欢它。
接下来的指引中,我们都将在示例中使用简写语法,因为这是在实际开发中更常见的用法。
布尔型 Attribute
布尔型 attribute 依据 true / false 值来决定 attribute 是否应该存在于该元素上。disabled 就是最常见的例子之一。
v-bind
在这种场景下的行为略有不同:
-
- <button :disabled="isButtonDisabled">Button</button>
-
- <button :disabled="!isButtonDisabled">Button</button>
当 isButtonDisabled
为真值或一个空字符串 (即 <button disabled="">
) 时,元素会包含这个 disabled
attribute。而当其为其他假值时 attribute 将被忽略。
动态绑定多个值
如果你有像这样的一个包含多个 attribute 的 JavaScript 对象:
-
- const objectOfAttrs = {
- id: 'container',
- class: 'wrapper'
- }
- 通过不带参数的 v-bind,你可以将它们绑定到单个元素上:
-
-
-
- <div v-bind="objectOfAttrs"></div>
-
- <div v-bind="objectOfAttrs">
- 233
- </div>
至此,我们仅在模板中绑定了一些简单的属性名。但是 Vue 实际上在所有的数据绑定中都支持完整的 JavaScript 表达式:
-
- {{ number + 1 }}
-
- {{ ok ? 'YES' : 'NO' }}
-
- {{ message.split('').reverse().join('') }}
-
- <div :id="`list-${id}`"></div>
-
- let number = 1
- let ok = 1
- let message = '如何快速进行多任务切换:多任 务切换的关键在 于能不能在一 段 时间内集 中火力, 把一件事做好,再去做下一件事。如果已经定下具 体的目标,一切的工具都只是辅助 ,必要时可以全都 舍弃掉。 '
- let id = 1
这些表达式都会被作为 JavaScript ,以当前组件实例为作用域解析执行。
在 Vue 模板内,JavaScript 表达式可以被使用在如下场景上:
在文本插值中 (双大括号)
在任何 Vue 指令 (以 v-
开头的特殊 attribute) attribute 的值中
仅支持表达式
每个绑定仅支持单一表达式,也就是一段能够被求值的 JavaScript 代码。一个简单的判断方法是是否可以合法地写在 return
后面。
因此,下面的例子都是无效的:
-
- <!-- 这是一个语句,而非表达式 -->
- {{ var a = 1 }}
-
- <!-- 条件控制也不支持,请使用三元表达式 -->
- {{ if (ok) { return message } }}
调用函数
可以在绑定的表达式中使用一个组件暴露的方法:
-
- <time :title="toTitleDate(date)" :datetime="date">
- {{ formatDate(date) }}
- </time>
TIP
绑定在表达式中的方法在组件每次更新时都会被重新调用,因此不应该产生任何副作用,比如改变数据或触发异步操作。
受限的全局访问
模板中的表达式将被沙盒化,仅能够访问到有限的全局对象列表。该列表中会暴露常用的内置全局对象,比如 Math
和 Date
。
没有显式包含在列表中的全局对象将不能在模板内表达式中访问,例如用户附加在 window
上的属性。然而,你也可以自行在 app.config.globalProperties 上显式地添加它们,供所有的 Vue 表达式使用。
指令 Directives
指令是带有 v-
前缀的特殊 attribute。Vue 提供了许多内置指令,包括上面我们所介绍的 v-bind
和 v-html
。
指令 attribute 的期望值为一个 JavaScript 表达式 (除了少数几个例外,即之后要讨论到的 v-for
、v-on
和 v-slot
)。一个指令的任务是在其表达式的值变化时响应式地更新 DOM。以 v-if 为例:
<p v-if="seen">Now you see me</p>
这里,v-if
指令会基于表达式 seen
的值的真假来移除/插入该 <p>
元素。
参数 Arguments
某些指令会需要一个“参数”,在指令名后通过一个冒号隔开做标识。例如用 v-bind
指令来响应式地更新一个 HTML attribute:
-
- <a v-bind:href="url"> ... </a>
-
- <!-- 简写 -->
- <a :href="url"> ... </a>
- 这里 href 就是一个参数,它告诉 v-bind 指令将表达式 url 的值绑定到元素的 href attribute 上。在简写中,参数前的一切 (例如 v-bind:) 都会被缩略为一个 : 字符。
-
- 另一个例子是 v-on 指令,它将监听 DOM 事件:
-
-
- <a v-on:click="doSomething"> ... </a>
-
- <!-- 简写 -->
- <a @click="doSomething"> ... </a>
这里的参数是要监听的事件名称:click
。v-on
有一个相应的缩写,即 @
字符。我们之后也会讨论关于事件处理的更多细节。
动态参数
同样在指令参数上也可以使用一个 JavaScript 表达式,需要包含在一对方括号内:
-
- <!--
- 注意,参数表达式有一些约束,
- 参见下面“动态参数值的限制”与“动态参数语法的限制”章节的解释
- -->
- <a v-bind:[attributeName]="url"> ... </a>
-
- <!-- 简写 -->
- <a :[attributeName]="url"> ... </a>
这里的 attributeName
会作为一个 JavaScript 表达式被动态执行,计算得到的值会被用作最终的参数。举例来说,如果你的组件实例有一个数据属性 attributeName
,其值为 "href"
,那么这个绑定就等价于 v-bind:href
。
相似地,你还可以将一个函数绑定到动态的事件名称上:
-
- <a v-on:[eventName]="doSomething"> ... </a>
-
- <!-- 简写 -->
- <a @[eventName]="doSomething">
在此示例中,当 eventName
的值是 "focus"
时,v-on:[eventName]
就等价于 v-on:focus
。
动态参数值的限制
动态参数中表达式的值应当是一个字符串,或者是 null
。特殊值 null
意为显式移除该绑定。其他非字符串的值会触发警告。
动态参数语法的限制
动态参数表达式因为某些字符的缘故有一些语法限制,比如空格和引号,在 HTML attribute 名称中都是不合法的。例如下面的示例:
- <!-- 这会触发一个编译器警告 -->
- <a :['foo' + bar]="value"> ... </a>
如果你需要传入一个复杂的动态参数,我们推荐使用计算属性替换复杂的表达式,也是 Vue 最基础的概念之一,我们很快就会讲到。
当使用 DOM 内嵌模板 (直接写在 HTML 文件里的模板) 时,我们需要避免在名称中使用大写字母,因为浏览器会强制将其转换为小写:
<a :[someAttr]="value"> ... </a>
上面的例子将会在 DOM 内嵌模板中被转换为 :[someattr]
。如果你的组件拥有 “someAttr” 属性而非 “someattr”,这段代码将不会工作。单文件组件内的模板不受此限制。
修饰符 Modifiers
修饰符是以点开头的特殊后缀,表明指令需要以一些特殊的方式被绑定。例如 .prevent
修饰符会告知 v-on
指令对触发的事件调用 event.preventDefault()
:
<form @submit.prevent="onSubmit">...</form>
之后在讲到 v-on 和 v-model 的功能时,你将会看到其他修饰符的例子。
最后,在这里你可以直观地看到完整的指令语法:
- <template>
- <h3>{{ text }}</h3>
- <h3>{{ user }}</h3>
-
- <!--
- 指令 v-
- v-model 数据双向绑定
- v-if 判断表达式的值,true则显示,false则隐藏 -- 控制dom元素的创建和销毁,应避免频繁切换状态
- v-show 和v-if区别 -- 始终会被渲染并保留在dom中,只是css被隐藏了 "display: none;" 一次性的
- v-for 循环
- v-bind 绑定属性或对象
- v-on 注册事件
- -->
- <input v-model="user.age" />
-
- <span v-if="user.age == 18">成年人:{{ user.age }}</span>
- <span v-else-if="user.age < 18">未成年</span>
- <span v-else>长大了...</span>
- <span v-show="user.age >= 18">
- 和v-if区别:始终会被渲染并保留在dom中,只是css被隐藏了 "display: none;"
- </span>
-
- <!-- <button v-bind:disabled="true">v-bind</button> -->
- <button :disabled="true">v-bind</button>
-
- <h6
- v-for="(friends,index) in user.friends"
- :key="index"
- style="color: rgb(70, 238, 146)"
- >
- {{ friends.name }} - {{ friends.age }} - {{ index }}
- </h6>
-
- <!-- <button v-on:click="addFriend">添加好友</button> -->
- <button @click="addFriend">添加好友</button>
-
-
- <button @click="deleteFriend">删除</button>
-
-
- <p :class="{ active: isActive }">Class 与 Style 绑定</p>
- <div v-for="i in 5">{{i}}</div>
- </template>
-
- <script setup>
- import { ref, reactive } from "vue";
-
- const text = ref("HelloWorld");
- const isActive = ref(true);
-
- const user = reactive({
- name: "小郑",
- age: 18,
- friends: [
- {
- name: "小张",
- age: 18,
- },
- {
- name: "小李",
- age: 20,
- },
- {
- name: "张三",
- age: 201,
- },
- ],
- });
-
- function addFriend() {
- user.friends.push({
- name: "哈基米",
- age: 18,
- });
- }
-
- function deleteFriend() {
- user.friends.pop()
- }
- </script>
-
- <style scoped>
- .active {
- color: rgb(134, 17, 250);
- }
- </style>

ref 和 reactive 是 Vue 3 中用于管理数据的两个方法。
ref 用于创建一个可响应的指针。指针可以指向任何值,包括原始值、对象或数组。当指针指向的值发生变化时,Vue 会自动更新指针的值。
reactive 用于创建一个响应式的对象。对象的所有属性都将成为响应式变量。当对象的属性发生变化时,Vue 会自动更新该属性的值。
name
、age
和 friends
。当这些属性发生变化时,user 对象的值也会发生变化。使用场景
ref 通常用于以下场景:
reactive 通常用于以下场景:
导入其他vue组件
- <template>
- <HelloWorld />
- </template>
- <script setup>
- import HelloWorld from "./components/HelloWorld.vue";
- </script>
- <template>
- <h3>{{ count }}</h3>
- <h3>{{ data }}</h3>
- <hr />
- <h3>{{ getStr }}</h3>
- <h3>{{ getStr2 }}</h3>
- <input type="text" v-model="getStr2" />
- <hr />
- <h3>name:{{ name }}</h3>
- <button @click="changeData">修改</button>
- </template>
-
- <script setup>
- import { ref, reactive, computed, watch, watchEffect, toRefs } from "vue";
-
- // ref:定义基本数据类型的响应式数据
- var count = ref(0);
-
- // reactive:定义“数组/对象/map”复杂数据类型的深层响应式数据,shallowReactive:浅层响应式(只保留对这个对象顶层次访问的响应性)
- const data = reactive({
- name: "小王",
- age: 18,
- girlfriends: [{ name: "小张" }],
- });
-
- // toRefs:解构响应式,没有的话,无法修改name值,修改的时候使用 name.value 修改
- const { name } = toRefs(
- reactive({
- name: "小郑",
- })
- );
-
- // computed:计算属性
- const str = ref("hello");
- const getStr = computed(() => {
- console.log("getStr计算属性执行了...");
- return str.value;
- });
- // 如果要修改计算属性值,上面的方式会报错 Write operation failed: computed value is readonly
- // 使用下面的方式
- const getStr2 = computed({
- get() {
- console.log("getStr2计算属性执行了...");
- return str.value;
- },
- set(val) {
- str.value = val;
- },
- });
-
- // watch:监听器
- watch(
- // count, // ref
- // () => geoObj.value.lng, // ref 对象中的某一个属性值
- data, // reactive
- // () => data.age, // reactive 对象中的某一个属性值
- // [count, data], // 监听多个数据
- // () => props.list, // 监听defineProps中的数据
- // () => proxy.$router.currentRoute.value, // 监听路由变化
- (newValue, oldValue) => {
- console.log("监听器执行了... ", newValue, oldValue);
- },
- {
- immediate: true, // 初始化执行一次
- // deep: true, // 深度监听 -- eg: 监听数组里面的数据变更
- }
- );
-
- // watchEffect:副作用函数,里面涉及到的属性有变更就会被触发执行
- watchEffect(() => {
- console.log("watchEffect执行了... ", data.age);
- });
-
- function changeData() {
- count.value++;
-
- data.age++;
- data.girlfriends = [{ name: "小甜" }, { name: "小李" }];
- data.girlfriends.push({ name: "哈基米" });
-
- name.value = "小郑变了"+count.value;
-
- str.value += "1";
- }
- </script>
-
-
- <style scoped>
- .active {
- color: rgb(134, 17, 250);
- }
- </style>

toRefs: toRefs是一个方法,它可以把一个响应式对象转换成普通对象,同时保留它的响应性。
比如有一个响应式对象:
const state = reactive({ foo: 1, bar: 2 })
如果我们解构它:
const { foo, bar } = state
那么foo和bar就失去了响应性。
但是如果我们使用toRefs:
- const state = reactive({ foo: 1, bar: 2 });
- const { foo, bar } = toRefs(state);
这样foo和bar仍然是响应式的!
所以toRefs的作用就是让解构后的变量也保持响应式。
computed: computed是一个方法,它可以创建一个计算属性。计算属性会根据它的依赖进行缓存和更新。监听一个值
比如:
- const count = ref(1)
-
- const plusOne = computed(() => count.value + 1)
-
- console.log(plusOne.value) // 2
-
- count.value++
-
- console.log(plusOne.value) // 3
这里plusOne会跟踪count的变化并实时更新自己的值。
所以computed的作用就是创建一个有缓存的属性,避免重复计算。
相同效果 import { ref, reactive, computed, watch, watchEffect, toRefs } from "vue"; const state = reactive({ foo: 1, bar: 2 }); function changeData() { state.foo++ state.bar++ } const state = reactive({ foo: 1, bar: 2 }); const { foo, bar } = toRefs(state); function changeData() { foo.value++ bar.value++ }
unplugin-auto-import插件
解决 import { ref , reactive ..... } from 'vue'
大量引入的问题
配置后可以不用引入,直接使用
npm install -g cnpm --registry=https://registry.npm.taobao.org cnpm i -D unplugin-auto-import vite.config.js import { defineConfig } from "vite"; import vue from "@vitejs/plugin-vue"; import AutoImport from "unplugin-auto-import/vite"; // https://vitejs.dev/config/ export default defineConfig({ plugins: [ vue(), AutoImport({ imports: ["vue", "vue-router"], }), ], });
一、配置
法一
vue3.4版本之后废除
- vite.config.js
-
- import { defineConfig } from 'vite'
- import vue from '@vitejs/plugin-vue'
-
- export default defineConfig({
- plugins: [
- vue({
- reactivityTransform: true, // 启用响应式语法糖 $ref $computed $toRef ...
- })
- ]
- })
法二
https://vue-macros.sxzz.moe/zh-CN/features/reactivity-transform.html
tips: store(pinia版) 中使用 $ref 无法正常持久化数据!!!
- cnpm i -D @vue-macros/reactivity-transform
- vite.config.js
-
- import { defineConfig } from 'vite'
- import vue from '@vitejs/plugin-vue'
- import ReactivityTransform from '@vue-macros/reactivity-transform/vite';
-
- export default defineConfig({
- plugins: [
- vue(),
- ReactivityTransform(), // 启用响应式语法糖 $ref ...
- ]
- })
- 解决ESLint警告: '$ref' is not defined.
-
-
- .eslintrc.cjs
-
- module.exports = {
- globals: { $ref: 'readonly', $computed: 'readonly', $shallowRef: 'readonly', $customRef: 'readonly', $toRef: 'readonly' },
- };

二、测试
原本 .value 响应式
- <template>
- <h1>{{ count }}</h1>
- <button @click="handleClick">click</button>
- </template>
-
- <script setup>
- let count = ref(0);
-
- function handleClick() {
- count.value++;
- }
- </script>
- 现在 $ref 去除 .value
-
- <template>
- <h1>{{ count }}</h1>
- <button @click="handleClick">click</button>
- </template>
-
- <script setup>
- let count = $ref(0);
-
- function handleClick() {
- count++;
- }
- </script>

三、注意事项
$ref 在以下情况无法直接使用
store pinia
watch 监听器
通信方式:
一、props & emit
:list
传值defineProps
接收父组件值,defineEmits
调用父组件方法传值到父组件中 eg: proxy.$emit('handle-succ', data);- 父组件 https://cn.vuejs.org/api/sfc-script-setup.html#defineprops-defineemits
- <template>
- <h3>{{ count }}</h3>
-
- <p>子组件:</p>
- <hello :list=list2 @add="handleAdd"/>
- </template>
-
- <script setup>
- import {ref} from "vue";
- import hello from "./components/hello.vue";
-
- var list2 = [{name: '小郑'}, {name: '张三'}]
- const count = ref(0);
-
- function handleAdd(data) {
- console.log(data);
- count.value++;
- }
- </script>
-
- <style scoped></style>
- 子组件
- <template>
- <h3>{{ list }}</h3>
- <button @click="handleSubmit">提交数据</button>
- </template>
-
- <script setup>
- // 父传子
- const props = defineProps({
- list: {
- type: Array,
- required: false,
- default: () => [],
- },
- });
-
- // 子传父
- const emits = defineEmits(["add"]);
-
- function handleSubmit() {
- emits("add", "child");
- }
-
- // 或直接通过 proxy.$emit("add", "child");
- </script>
-
- <style scoped></style>

二、v-model 父子组件双向绑定
v-model
props
接收父组件值modelValue
,proxy.$emit("update:modelValue", 666);
传值tips: 如果父组件是
v-model:num
,那么子组件的modelValue
变更为num
uniapp中可通过 props 来获取页面参数 (tips:子组件内无法通过这种方式获取到路径参数!) eg: /pages/index/index?code=xxx
==> const props = defineProps({ code: { type: String, required: false } });
- 父组件
- <template>
- <h3>父组件:{{ data }}</h3>
- <HelloWorld ref="helloRef" v-model="data" />
- </template>
-
- <script setup>
- import HelloWorld from "./components/HelloWorld.vue";
- const data = ref(0);
- </script>
-
- <style scoped></style>
- 子组件
- <template>
- <h3>子组件:{{ modelValue }}</h3>
- <button @click="changeData">click</button>
- </template>
-
- <script setup>
- const { proxy } = getCurrentInstance();
- const props = defineProps({
- modelValue: {
- type: Number,
- required: false,
- default: () => 0,
- },
- });
- function changeData() {
- proxy.$emit("update:modelValue", 666);
- }
- </script>
-
- <style scoped></style>
- 三、provide/inject:跨代传值
- 父组件
- import { provide } from 'vue'
- provide('msg', xxx)
- 子子组件
- import { inject } from 'vue'
- const msg = inject('msg')
- const msg = inject('msg', 'hello') // 没值的话使用默认值hello

如果父组件想要调用子组件的方法
- 子组件为选项式api 可以在父组件中使用 `proxy.$refs.helloRef.changeData();` 调用
- 子组件为组合式api 需在子组件中使用 `defineExpose` 暴露需要调用的方法
父组件
- ```
- <template>
- <HelloWorld ref="helloRef" />
- <button @click="handleClick">click</button>
- <br />
- <button @click="$refs.helloRef.changeData()">调用子组件方法</button>
- </template>
-
- <script setup>
- const { proxy } = getCurrentInstance();
- import HelloWorld from "./components/HelloWorld.vue";
-
- function handleClick() {
- proxy.$refs.helloRef.changeData();
- }
- </script>
-
- <style scoped></style>
- ```
-
- 子组件
-
- ```
- <template>
- <h3>count:{{ count }}</h3>
- </template>
-
- <script setup>
- const count = ref(0);
-
- function changeData() {
- count.value++;
- }
-
- // 暴露方法
- defineExpose({
- changeData,
- });
- </script>
-
- <style scoped></style>
- ```

- ### 父组件
-
- ```
- <template>
- <HelloWorld @ok="handleOk" />
- </template>
- <script setup>
- import { ref } from "vue";
- import HelloWorld from "./components/HelloWorld.vue";
-
- function handleOk(data) {
- console.log(data);
- }
- </script>
- <style scoped></style>
- ```
-
- ### 子组件
-
- 法一:
-
- ```
- <script setup>
- const { proxy } = getCurrentInstance();
-
- function changeData() {
- proxy.$emit('ok', 'hello');
- }
- </script>
- ```
-
- 法二:
-
- ```
- <script setup>
- const { proxy } = getCurrentInstance();
- const emits = defineEmits(["ok"]);
-
- function changeData() {
- emits("ok", "hello");
- }
- </script>
- ```

这是一个使用 Vue 3 的组件插槽(slots)的示例。
<template>
标签内定义了一个名为 HelloWorld
的组件。
这个组件有以下几种类型的插槽:
- 具名插槽(Named Slots)
-
- <slot name="left">
-
- <slot name="right">
-
- 使用方法:
-
- <template v-slot:left>...</template>
- <template #right>...</template>
- 作用域插槽(Scoped Slots)
-
- <slot :data="item">
-
- 可以将组件内的数据(item)传递给插槽。
-
- 使用方法:
-
- <template #default="{ data }">...</template>
- 动态插槽(Dynamic Slots)
-
- <slot name="dynamic">
-
- 插槽的名称可以动态绑定。
-
- 使用方法:
-
- <template #\[xx\]>...</template>
- 这里的 xx 可以是一个变量。

主要功能是组件内通过不同类型的插槽向组件外暴露数据或标记点,以便组件外插入自定义内容。
这提高了组件的灵活性和可复用性。
Vue 的插槽(slot)允许组件内注入自定义内容。它的工作原理可以简单概括为:
- 组件内部使用 <slot> 标签作为插槽容器并定义名称
-
- <div class="component">
- <slot name="header">默认内容</slot>
-
- <div class="content">...</div>
-
- <slot name="footer"></slot>
- </div>
- 组件外部使用特殊的 template 语法插入自定义内容到插槽中
-
- <MyComponent>
- <template v-slot:header>
- <!-- 自定义header -->
- </template>
-
- <template v-slot:footer>
- <!-- 自定义footer -->
- </template>
- </MyComponent>
- Vue 渲染组件时:
-
- 如果有插入内容,则替换 <slot> 对应位置为自定义内容
-
- 如果没有插入内容,则保留 <slot> 的默认内容

所以,插槽允许用户决定组件内部的某些部分应显示什么内容,提高了组件的灵活性和可复用性。
同时为了模块化,插槽内容和组件其他部分相互独立。这就是插槽的工作原理。
- 作用域插槽(Scoped Slots)
-
- 在组件中,可以为插槽传递数据:
-
- <slot :user="user">
- {{ user.lastName }}
- </slot>
- 组件使用方可以接收传递的数据并自定义插槽内容:
-
- <MyComponent>
- <template #default="slotProps">
- {{ slotProps.user.firstName }}
- </template>
- </MyComponent>
- 这样就实现了从组件内部将数据传递给插槽。
-
- 动态插槽(Dynamic Slots)
-
- 插槽名可以是动态绑定的:
-
- <slot :name="slotName">...</slot>
- 使用模板引用作为插槽名:
-
- <MyComponent>
- <template #[slotName]>
- ...自定义内容
- </template>
- </MyComponent>

好的,举两个例子说明 Vue 插槽的高级用法:
slotName 可以是一个变量,这样插槽名就可以动态变化了。
这种方式非常灵活,允许根据条件自定义不同的插槽内容。
所以这两种机制都大大提高了插槽的可扩展性。
-
- 父组件:
- <template>
- <HelloWorld>
- <!-- <template v-slot:left>具名插槽-left</template>-->
- <template v-slot:left></template>
- <template #right>具名插槽-right</template>
- <template #default="{ data }">作用域插槽:{{ data }}</template>
- <template #[xx]>动态插槽</template>
- </HelloWorld>
- </template>
- <script setup>
- import HelloWorld from "./components/HelloWorld.vue";
- let xx = ref("dynamic"); // 这里可以随时变 dynamic/left => xx
- </script>
- <style scoped></style>
-
- 子组件:
- <template>
- <h3>hello</h3>
- <slot name="left"><h1>具名插槽-left</h1></slot>
- <br />
- <slot name="right"></slot>
- <div v-for="item in list" :key="item.id">
- <slot :data="item"></slot>
- <!-- 作用域插槽可回调值给父组件使用 <template #right-show="{ isShow }"></template> -->
- <!-- <slot name="right-show" :is-show="isShowRightMenu" /> -->
- </div>
- <slot name="dynamic"></slot>
- </template>
- <script setup>
- let list = ref([
- { id: 1, name: "小张" },
- { id: 2, name: "小李" },
- ]);
- </script>
- <style scoped></style>

- <template>
- <h3>hello</h3>
-
- <button id="count" @click="count++">{{ count }}</button>
-
- </template>
-
- <script setup>
- console.log(111);
-
- onMounted(() => {
- console.log(222);
- });
-
- const count = ref(0)
-
- onUpdated(() => {
- // 文本内容应该与当前的 `count.value` 一致
- // console.log(document.getElementById('count').textContent)
- console.log(count.value)
- })
- </script>
- <style scoped></style>

1. setup() ``` // 组件初始化时执行,通常用于定义数据、方法等 setup(){ // 代码逻辑 } ``` 2. onBeforeMount() ``` // 在组件渲染到页面之前执行 onBeforeMount(){ // 获取渲染前的 DOM 状态 } ``` 3. onMounted() ``` // 组件渲染 complet 后执行 // 可以获取到 DOM 元素 onMounted(){ // 已经可以通过 ref 获取到 DOM } ``` 4. onBeforeUpdate() ``` // 数据更新前执行 onBeforeUpdate(){ // 可以在这里获取更新前的状态 } ``` 5. onUpdated() ``` // 数据更新后执行(在组件的任意 DOM 更新后被调用) onUpdated(){ // 组件重新渲染完成 } ``` 这个钩子会在组件的任意 DOM 更新后被调用,这些更新可能是由不同的状态变更导致的,因为多个状态变更可以在同一个渲染周期中批量执行(考虑到性能因素)。如果你需要在某个特定的状态更改后访问更新后的 DOM,请使用 nextTick() 作为替代。 不要在 updated 钩子中更改组件的状态,这可能会导致无限的更新循环! 6. onBeforeUnmount() ``` // 组件销毁前执行 onBeforeUnmount(){ // 清除定时器、事件监听等 } ``` 这些钩子函数可以让我们在不同阶段执行自定义逻辑,非常实用。
https://cn.vuejs.org/guide/built-ins/teleport.html
可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去。
- <template>
- <div class="body"></div>
-
- <button @click="open = open^1">Open Modal</button>
-
- <Teleport to=".body">
- <div v-if="open" class="modal">
- <p>Hello from the modal!</p>
- <button @click="open = false">Close</button>
- </div>
- </Teleport>
- </template>
- <script setup>
- let open = ref(false);
- </script>
- <style scoped>
- .body {
- width: 200px;
- height: 200px;
- background-color: #0ee757;
- }
-
- .modal {
- background-color: #1789ca;
- }
- </style>

有些场景会需要在两个组件间来回切换,比如 Tab 界面
- <template>
- <div class="home">
- <ul>
- <li v-for="(item, index) in tabList" :key="index" @click="change(index,item)">
- {{ item.name }}
- </li>
- </ul>
- <component :is="currentComponent.com"></component>
- </div>
- </template>
-
- <script setup>
- // import { reactive } from "vue";
- import A from "./components/A.vue";
- import B from "./components/B.vue";
- let tabList = reactive([
- { name: "显示组件A", com: markRaw(A) },
- { name: "显示组件B", com: markRaw(B) },
- ]);
-
- let currentComponent = reactive({
- com: A,
- });
-
- const change = (index,item) => {
- console.log('index',index,'item',item.name)
- currentComponent.com = tabList[index].com;
- };
- </script>
-
- <template><h1>A组件</h1></template>
-
- <template><h2>B组件</h2><button >按钮B</button></template>
-
- 这段代码实现了一个 tab 切换不同组件的功能。
-
- 主要逻辑是:
-
- 1. 定义了一个 tabList 数组,里面是不同的 tab 项,每个项包含 name 和 com 两个属性。
- - name:tab 的名称
- - com:对应的组件,使用 markRaw 包装过可以直接当组件使用
- 2. 定义一个当前组件 currentComponent,里面有一个 com 属性,默认设置为组件 A。
- 3. 使用 v-for 渲染 tab 列表,当点击不同的 tab 时,调用 change 方法。
- 4. change 方法中,根据点击的 tab 索引,设置 currentComponent.com 为对应的组件,这样就切换了当前的组件。
- 5. 使用 component 标签,动态绑定 currentComponent.com,所以就可以渲染不同的组件了。
- 这样通过 tab 切换,动态渲染不同的组件,实现了组件的动态切换。
- 举个例子,默认情况下渲染 A 组件,当我们点击“显示组件 B”这个 tab 时,就会切换到 B 组件。
-
-
-
- 所以这个例子实现了一个通过 tab 动态切换组件的功能。
- 在 `<component>` 组件中,`:is` 是用于指定动态组件的属性。
- `:is="currentComponent.com"` 表示动态绑定 currentComponent.com 中指定的组件,并渲染该组件。
-
- 一些关键点:
-
- - `<component>` 是 Vue 提供的一个内置组件,用于动态渲染组件
- - `:is` 特殊 attribute,可以用来指定要动态渲染的组件
- - 其值 `currentComponent.com` 需要是一个组件对象,这里通过引入并 `markRaw` 包装了组件满足要求
- - 当 `currentComponent.com` 更新时,`:is` 也会动态更新,实现组件切换
-
- 所以简单说,`:is` 指定了 `<component>` 需要渲染的目标组件。其动态性允许我们实现组件的动态切换。
-
- 这样通过 `currentComponent.com` 和 `:is` 的配合,我们就可以灵活地切换不同的组件了。

- <div class="container">
- <table>
- <thead>
- <tr>
- <th>标题</th>
- <th>描述</th>
- </tr>
- </thead>
- <tbody>
- <tr v-for="(item, index) in items" :key="index">
- <td>{{ item.title }}</td>
- <td>{{ item.description }}</td>
- </tr>
- </tbody>
- </table>
- </div>
- 这两个代码示例的主要区别在于使用了不同的 HTTP 客户端来获取数据:
-
- 1. 使用 axios 获取数据:
-
- ```
- const response = await axios.get('http://127.0.0.1:8000/api/data');
- items.value = response.data;
- ```
-
- axios 是一个流行的 HTTP 客户端,它会自动为我们转换响应数据到 JSON。所以我们可以直接通过 `response.data` 来访问 JSON 数据。
-
- 2. 使用原生 fetch 获取数据:
-
- ```
- const response = await fetch('http://127.0.0.1:8000/api/data');
- const data = await response.json();
- items.value = data;
- ```
-
- fetch 是浏览器原生提供的 API。它获取到的响应体是一个流数据,我们需要通过调用 `response.json()` 方法来自己把它转换成 JSON 对象。然后才能保存到 `data` 变量中,最后赋值给 `items.value`。
-
- 总结主要区别:
-
- - axios 会自动转换响应数据,fetch 需要手动转换
- - axios 是第三方库,fetch 是原生 API
- - 语法上 axios 更简单直观,fetch 需要额外转换步骤
-
- 所以对于大多数用例来说,使用 axios 会更加便捷。但 fetch 作为原生 API 也有其优势,比如体积更小,兼容性更好。
-

vue-element-admin是一个后台前端解决方案,它基于vue和element-ui实现。它使用了最新的前端技术栈,内置了 i18 国际化解决方案,动态路由,权限验证,提炼了典型的业务模型,提供了丰富的功能组件,它可以帮助你快速搭建企业级中后台产品原型。
一上来就看到那么多文件夹确实头疼,咱先不管别的,主要先了解标注部分文件
- ├── build # 构建相关
- ├── config # 配置相关
- ├── mock # 项目mock 模拟数据
- ├── plop-templates
- ├── public # 静态资源
- │ ├── favicon.ico
- │ └── index.html
- ├── src # 源代码
- │ ├── api # 所有请求
- │ ├── assets # 主题 字体等静态资源
- │ ├── components # 全局公用组件
- │ ├── directive # 全局指令
- │ ├── filters # 全局 filter
- │ ├── icons # 项目所有 svg icons
- │ ├── lang # 国际化 language
- │ ├── layout # 布局相关
- │ ├── mock # 项目mock 模拟数据
- │ ├── router # 路由
- │ ├── store # 全局 store管理
- │ ├── styles # 全局样式
- │ ├── utils # 全局公用方法
- │ ├── vendor # 公用vendor
- │ ├── views # view
- │ ├── App.vue # 入口页面
- │ ├── main.js # 入口 加载组件 初始化等
- │ └── permission.js # 权限管理
- ├── tests
- ├── static # 第三方不打包资源
- │ └── Tinymce # 富文本
- ├── .babelrc # babel-loader 配置
- ├── eslintrc.js # eslint 配置项
- ├── .gitignore # git 忽略项
- ├── favicon.ico # favicon图标
- ├── index.html # html模板
- ├── .env.xxx
- ├── .eslintrc.js
- ├── .travis.yml
- ├── vue.config.js # vue-cli 配置
- └── package.json

- # 克隆项目
- git clone https://github.com/PanJiaChen/vue-element-admin.git
-
- # 进入项目目录
- cd vue-element-admin
-
- # 建议不要用 cnpm 安装 会有各种诡异的bug 可以通过如下操作解决 npm 下载速度慢的问题
- npm install --registry=https://registry.npm.taobao.org
-
- # 安装依赖
- npm install
-
- # 本地开发 启动项目
- npm run dev
新建一下request.js文件,修改如下
- import request from '@/utils/request_new'
-
- export function system_status() {
- return request({
- url: '/api/system_status',
- method: 'get'
- })
- }
-
-
-
- import axios from 'axios'
- import { MessageBox, Message } from 'element-ui'
- import store from '@/store'
- import { getToken } from '@/utils/auth'
-
-
- // create an axios instance
- const service = axios.create({
- baseURL:'http://127.0.0.1:8003', // url = base url + request url
- // withCredentials: true, // send cookies when cross-domain requests
- timeout: 5000, // request timeout
-
- })
-
- // request interceptor
- service.interceptors.request.use(
- config => {
- // do something before request is sent
-
- if (store.getters.token) {
- // let each request carry token
- // ['X-Token'] is a custom headers key
- // please modify it according to the actual situation
- config.headers['X-Token'] = getToken()
- }
- return config
- },
- error => {
- // do something with request error
- console.log(error) // for debug
- return Promise.reject(error)
- }
- )
-
- // response interceptor
- service.interceptors.response.use(
- /**
- * If you want to get http information such as headers or status
- * Please return response => response
- */
-
- /**
- * Determine the request status by custom code
- * Here is just an example
- * You can also judge the status by HTTP Status Code
- */
- response => {
- const res = response.data
-
- return res
-
- },
- error => {
- console.log('err' + error) // for debug
- Message({
- message: error.message,
- type: 'error',
- duration: 5 * 1000
- })
- return Promise.reject(error)
- }
- )
-
- export default service

参考资料:手摸手,带你用vue撸后台 系列一(基础篇) - 掘金
vue-element-admin 跨域配置 - CSDN文库
vue配置多个服务端请求地址(使用vue-admin-template举例说明)_vue-admin-template改变请求地址-CSDN博客
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。