Vue 3 详解

一、开发环境搭建:基石奠定

  1. Node 环境安装
    • Vue 3 的开发离不开 Node.js,它为项目提供了运行时环境与丰富的包管理能力。
  2. 开发编辑工具
    • Visual Studio Code(Vscode)是当下热门且功能强大的前端开发工具。
  3. 浏览器环境
    • 谷歌浏览器(Google Chrome)凭借出色的调试功能与对前端技术的良好支持,成为 Vue 开发的首选浏览器。

二、初窥 Vue 3:创建首个应用

  1. 通过 CDN 使用 Vue
    • 在 HTML 文件中引入 Vue 3 的 CDN 链接,如:
html 复制代码
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app">{{ message }}</div>
<script type="module">
  import { createApp, ref } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
  createApp({
    setup() {
      const message = ref('Hello Vue!')
      return {
        message
      }
    }
  }).mount('#app')
</script>
  • 这里先引入 Vue 全局脚本,然后利用 createApp 函数创建 Vue 应用实例,在 setup 函数中定义响应式数据 message,最后通过 .mount('#app') 将应用挂载到指定 DOM 节点(即 idappdiv)上,页面便能展示出 Hello Vue!
  • 还可以创建交互组件,如:
html 复制代码
<div id="app">
  <button @click="count++">{{ count }}</button>
</div>
<script type="module">
import { createApp, ref } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'
createApp({
    setup() {
      const count = ref(0)
      return {
        count
      }
    }
  }).mount('#app')
</script>
  • 点击按钮,count 值会自动更新并在页面显示,展示了 Vue 的响应式特性。
  1. 通过 Vite 或者官方的命令行创建 Vue 3 项目
    • 使用 Vite:
      • 首先确保已安装 Node.js,在终端执行 npm init vite@latest my-vue3-app -- --template vuemy-vue3-app 为项目名称,可自定义),这会创建一个基于 Vue 3 和 Vite 的初始项目结构。
      • 进入项目目录 cd my-vue3-app,执行 npm install 安装依赖,然后 npm run dev 启动开发服务器,即可在浏览器访问 http://localhost:3000 查看初始页面,后续可在此基础上进行组件开发、路由配置等操作。
    • 使用官方命令行工具(需先全局安装 @vue/clinpm install -g @vue/cli):
      • 执行 vue create my-vue3-projectmy-vue3-project 为自定义项目名),按提示选择 Vue 3 相关预设,如默认配置或手动选择功能插件(如路由、状态管理等),完成项目创建。同样进入项目目录后 npm run serve 启动开发服务器,开启开发流程。

三、核心知识剖析:语法与特性

  1. 模板语法

    • 文本插值 :使用双大括号 {``{}},如 <span>Message: {``{ msg }}</span>msg 为 Vue 实例中的响应式数据,能实时将数据渲染到页面。
    • 插入 HTML
      • 插值语法插入 HTML 字符串时,会以文本形式显示,如 <p>插值语法插入html: {``{ <span style="color: red">drogen</span> }}</p>,只会显示 HTML 代码文本。
      • 若要正确渲染 HTML,需使用 v-html 指令,如 <p>v-html指令: <span v-html="rawHtml"></span></p>,其中 rawHtml 应为包含合法 HTML 标签的字符串变量,要注意避免 XSS 攻击,谨慎使用。
  2. Attribute 绑定

    • 使用 v-bind 指令,可简写为 :,如 <div v-bind:id="cxy"></div><div :id="cxy"></div>cxy 为 Vue 实例中的数据,能动态绑定元素属性。常用于按钮禁用状态绑定 <button :disabled="isDisabled"> 按钮 </button>,或链接跳转地址绑定 <a :href="https://chenxuyuan.net"> drogen官网 </a>
  3. 响应式原理

    • ref() :通过 ref 函数创建响应式数据,如 const count = ref(0),在 JavaScript 中访问其值需使用 .value,即 count.value,但在模板中使用时无需加 .value,Vue 会自动处理。当 count.value 发生变化,使用该数据的 DOM 区域会自动更新。
    • DOM 更新时机 :Vue 采用异步更新策略,修改响应式状态时,DOM 不会立即更新,而是在"next tick"更新周期中缓冲所有状态修改,确保每个组件只更新一次。若需等待 DOM 更新完成后执行额外代码,可使用 nextTick 全局 API,如:
    javascript 复制代码
    import { nextTick } from 'vue'
    async function increment() {
      count.value++
      await nextTick()
      // 现在 DOM 已经更新了
    }
    • reactive() :用于创建响应式对象,如 const state = reactive({ count: 0 }),但它有一定局限性:
      • 只能用于对象类型(对象、数组和如 Map、Set 这样的集合类型),不能持有如 string、number 或 boolean 这样的原始类型。
      • 不能替换整个对象,否则响应性连接将丢失,如 state = reactive({ count: 1 }) 会导致原 state 的响应性失效。
      • 对解构操作不友好,解构后的数据与原响应式对象断开连接,如 const { count } = state,修改 count 不会影响原 state.count,若要保持响应性,需传入整个对象。

四、进阶特性:提升开发效能

  1. 计算属性

    • 无计算属性:在模板中直接进行复杂逻辑运算,如:
    html 复制代码
    <script setup>
    import { ref } from "vue";
    const list = ref({
      books: [
        "语文",
        "数学",
        "英语",
      ],
    });
    </script>
    <template>
      <p>是否拥有书籍:</p>
      <span>{{ list.books.length > 0? "Yes" : "No" }}</span>
    </template>
    • 但这样会使模板逻辑复杂,不易维护。
    • 有计算属性 :使用 computed 函数创建计算属性,如:
    html 复制代码
    <script setup>
    import { ref, computed } from "vue";
    const list = ref({
      books: [
        "语文",
        "数学",
        "英语",
      ],
    });
    // 一个计算属性 ref
    const booksLength = computed(() => {
      return list.value.books.length > 0? "Yes" : "No";
    });
    </script>
    <template>
      <p>拥有书籍的个数:</p>
      <span>{{ booksLength }}</span>
    </template>
    • 计算属性会基于其依赖的数据自动缓存结果,当依赖数据不变时,不会重复计算,提升性能,且在模板中使用如同普通数据,简洁清晰。
    • 可写计算属性
    html 复制代码
    <script setup>
    import { ref, computed } from "vue";
    const firstName = ref("老");
    const lastName = ref("王");
    const fullName = computed({
      // getter
      get() {
        return firstName.value + " " + lastName.value;
      },
      // setter
      set(newValue) {
        // 注意:我们这里使用的是解构赋值语法
        [firstName.value, lastName.value] = newValue.split(" ");
      },
    });
    // fullName.value = "范 冰冰";
    </script>
    <template>
      <p>Has published books:</p>
      <span>{{ fullName }}</span>
    </template>
    • 通过定义 getset 方法,既能像普通属性读取,又能在赋值时执行自定义逻辑,实现数据双向关联。
  2. 类与样式绑定

    • 类绑定
      • 绑定 HTML class :使用 :class 指令,可绑定对象或计算属性,如:
    html 复制代码
    <script setup>
    import { ref, computed } from "vue";
    const isActive = ref(true);
    const isTitle = ref(true);
    const computedClass = computed(() => ({ active: isActive, title: isTitle }));
    </script>
    <template>
      <div :class="computedClass">drogen</div>
    </template>
    • 绑定数组:可传递数组元素作为类名,如:
    html 复制代码
    <script setup>
    import { ref } from "vue";
    const active = ref("active");
    const title = ref("title");
    const isActive = ref(true);
    </script>
    <template>
      <div :class="[isActive? active : '', title]">drogen</div>
    </template>
    • 绑定内联样式 :使用 :style 指令,可绑定对象,对象属性为 CSS 样式名与对应值,如:
    html 复制代码
    <script setup>
    import { ref } from "vue";
    const color = ref("red");
    const fontSize = ref(40);
    const styleObject = ref({
      color: "red",
      fontSize: "40px",
      });
    </script>
    <template>
      <div :style="styleObject">drogen</div>
    </template>
  3. 条件渲染与列表渲染

    • 条件渲染
      • v-if+v-else:根据条件决定元素是否渲染,如:
    html 复制代码
    <script setup>
    import { ref } from "vue";
    const title = ref(true);
    </script>
    <template>
      <h1 v-if="title">drogen</h1>
      <h2 v-else>chenxuyuan.net</h2>
    </template>
    • v-else-if:实现多条件分支,如:
    html 复制代码
    <script setup>
    import { ref } from "vue";
    const type = ref(1);
    </script>
    <template>
      <h1 v-if="type === 1">drogen</h1>
      <h2 v-else-if="type === 2">html</h2>
      <h2 v-else-if="type === 3">css</h2>
      <h2 v-else-if="type === 4">js</h2>
      <h2 v-else>chenxuyuan.net</h2>
    </template>
    复制代码
      - `v-show`:元素始终渲染,通过 `display` 属性控制显示隐藏,如:
    html 复制代码
    <script setup>
    import { ref } from "vue";
    const title = ref(true);
    </script>
    <template>
      <h1 v-show="title">drogen</h1>
    </template>
    • 列表渲染
      • v-for:遍历数组或对象,如:
    html 复制代码
    <script setup>
    import { ref } from "vue";
    const courseList = ref(["html", "css", "js"]);
    </script>
    <template>
      <ul>
        <li v-for="item in courseList">{{ item }}</li>
      </ul>
    </template>
    • 也可使用 of 关键字,如 <li v-for="item of courseList">{``{ item }}</li>,效果相同。
    • 嵌套遍历:
    html 复制代码
    <script setup>
    import { ref } from "vue";
    const courseList = ref([
      { name: "html", list: ["h1", "h2", "h3"] },
      { name: "css", list: ["color", "fontSize", "display"] },
      { name: "js", list: ["var", "const", "let"] },
    ]);
    </script>
    <template>
      <ul>
        <li v-for="item in courseList">
          {{ item.name }}
          <div v-for="childItem in item.list">{{ childItem }}</div>
        </li>
      </ul>
    </template>
    • 注意:不推荐 v-ifv-for 同时用在同一标签,因为 v-if 优先级高于 v-for,会导致 v-if 条件无法访问 v-for 作用域内变量别名,如:
    html 复制代码
    <script setup>
    import { ref } from "vue";
    const courseObject = ref({ front: "js", back: "java" });
    </script>
    <template>
      <ul>
        <li v-for="(value, key, index) in courseObject" v-if="!value">
          {{ value }} - {{ key }} -{{ index }}
        </li>
      </ul>
    </template>
  4. 事件处理与表单输入绑定

    • 事件处理
      • 监听事件:使用 v-on:click 或简写 @click,如:
    html 复制代码
    <script setup>
    import { ref } from "vue";
    const count = ref(0);
    const addClick = () => {
      count.value++;
    };
    </script>
    <template>
      <div @click="addClick()">计数器:{{ count }}</div>
    </template>
    • 事件修饰符:如 .stop 阻止冒泡、.prevent 阻止默认行为、.self 仅当事件目标是自身时触发、.capture 使用捕获模式、.once 只触发一次、.passive 提升移动端滚动性能;按键修饰符如 .enter.tab 等用于特定按键监听,系统按键修饰符 .ctrl.alt 等可组合使用。
    • 表单输入绑定
      • 未使用 Vue 时,需手动处理双向数据绑定,如:
    html 复制代码
    <input
      :value="text"
      @input="event => text = event.target.value"
    >
    • 使用 v-model 指令简化,如:
    html 复制代码
    <input v-model="text">
    • 不同表单元素用法:
      • 文本
    html 复制代码
    <p>Message is: {{ message }}</p>
    <input v-model="message" placeholder="edit me" />
    复制代码
    - **复选框**:
    html 复制代码
    <input type="checkbox" id="checkbox" v-model="checked" />
    <label for="checkbox">{{ checked }}</label>
    复制代码
        - **单选按钮**:
    html 复制代码
    <div>Picked: {{ picked }}</div>
    <input type="radio" id="one" value="One" v-model="picked" />
    <label for="one">One</label>
    <input type="radio" id="two" value="Two" v-model="picked" />
    <label for="two">Two</label>
    复制代码
    - **选择器**:
    html 复制代码
    <div>Selected: {{ selected }}</div>
    <select v-model="selected">
      <option disabled value="">Please select one</option>
      <option>A</option>
      <option>B</option>
      <option>C</option>
    </select>
  5. 侦听器

    • 使用 watch 侦听器:
    html 复制代码
    <script setup>
    import { ref, watch } from 'vue';
    
    const question = ref('');
    const answer = ref('答案');
    const loading = ref(false);
    
    // 可以直接侦听一个 ref
    watch(question, (newQuestion) => {
      if (newQuestion.includes('?')) {
        loading.value = true;
        answer.value = 'Thinking...';
        setTimeout(() => {
          answer.value = '是的';
        }, 1000);
      }
    });
    </script>
    
    <template>
      <p>
        提问问题
        <input v-model="question" :disabled="loading" />
      </p>
      <p>{{ answer }}</p>
    </template>
    • 这里侦听 question 数据变化,当输入包含 ? 时,更新 loadinganswer 状态模拟异步获取答案过程。
    • 侦听数据源类型
      • 可侦听 ref(包括计算属性)、响应式对象、getter 函数、多个数据源组成的数组。注意不能直接侦听响应式对象的属性值,需通过 getter 函数,如:
    javascript 复制代码
    const obj = ref({ count: 0 });
    // 错误,因为 watch() 得到的参数是一个 number
    watch(obj.value, (count) => {
      console.log(`count is: ${count}`);
    });
    // 提供一个 getter 函数
    watch(
      () => obj.value.count,
      (count) => {
        console.log(`count is: ${count}`);
      }
    );
    • 深层侦听器
      • 侦听响应式对象:
    javascript 复制代码
    const obj = ref({ count: 0 });
    watch(obj.value, (newValue, oldValue) => {
      // 在嵌套的属性变更时触发
      // 注意:`newValue` 此处和 `oldValue` 是相等的
      // 因为它们是同一个对象!
    });
    obj.count++;
    • 侦听返回响应式对象的 getter 函数(开销大,慎用):
    javascript 复制代码
    watch(
      () => state.someObject,
      (newValue, oldValue) => {
        // 注意:`newValue` 此处和 `oldValue` 是相等的
        // *除非* state.someObject 被整个替换了
      },
      { deep: true }
    );
    • 即时回调侦听器
    javascript 复制代码
    watch(
      source,
      (newValue, oldValue) => {
        // 立即执行,且当 `source` 改变时再次执行
      },
      { immediate: true }
    );
    • 一次性侦听器
    javascript 复制代码
    watch(
      source,
      (newValue, oldValue) => {
        // 当 `source` 变化时,仅触发一次
      },
      { once: true }
    );
    • watchEffect():会在副作用发生期间追踪依赖,自动追踪所有能访问到的响应式属性,代码简洁但响应性依赖关系有时不明确,如:
    javascript 复制代码
    import { ref } from 'vue';
    const count = ref(0);
    WatchEffect(() => {
      console.log(`当前 count 的值为 ${count.value}`);
    });
  6. 模板引用

    • 模板引用 ref
    html 复制代码
    <script setup>
    import { ref, onMounted } from 'vue';
    
    // 声明一个 ref 来存放该元素的引用
    // 必须和模板里的 ref 同名
    const input = ref(null);
    
    onMounted(() => {
      input.value.focus();
    });
    </script>
    
    <template>
      <input ref="input" />
    </template>
    • 这里获取输入框元素引用,在组件挂载后使其获取焦点。
    • 函数的模板引用
    html 复制代码
    <input :ref="(el) => { /* 将 el 赋值给一个数据属性或 ref 变量 */ }">
    • v-for 模板引用
    html 复制代码
    <script setup>
    import { ref, onMounted } from 'vue';
    
    const list = ref([
      /*... */
    ]);
    
    const itemRefs = ref([]);
    
    onMounted(() => console.log(itemRefs.value));
    </script>
    
    <template>
      <ul>
        <li v-for="item in list" ref="itemRefs">
          {{ item }}
        </li>
      </ul>
    </template>
    • 组件上的 ref
    html 复制代码
    <script setup>
    import { ref, onMounted } from 'vue';
    import Child from './Child.vue';
    
    const child = ref(null);
    
    onMounted(() => {
      // child.value 是 <Child /> 组件的实例
    });
    </script>
    
    <template>
      <Child ref="child" />
    </template>

五、生命周期:组件的生命轨迹

  1. 认识 Vue 3 当中的生命周期函数

    html 复制代码
    <script setup>
    import { ref, onMounted } from 'vue';
      
    const data = ref('');
      
    // 挂载时机生命周期函数;
    onMounted(() => {
      setTimeout(() => {
        data.value = 'drogen';
      }, 100);
    });
    </script>
      
    <template>
      <div>接口 100ms 后请求数据:</div>
      <span>{{ data }}</span>
    </template>
    • 这里利用 onMounted 在组件挂载完成后,通过定时器模拟异步请求数据并更新 data 展示。
    • onMounted:常用于执行定时器、绑定事件、订阅消息等操作,此时组件已挂载到 DOM,可访问真实 DOM 元素。
    • onUnMounted:组件卸载之后,用于清除定时器、取消订阅消息等,防止内存泄漏。
  2. 概览 Vue 3 的所有生命周期函数流程

    • 组件生命周期图示清晰展示各阶段,从初始化开始,历经挂载、更新、卸载等环节,各阶段有对应的生命周期函数被调用。
  3. 详解生命周期的初始化流程:组件加载前进行基础数据初始化等操作,为后续挂载做准备。

  4. 详解生命周期的挂载流程

    • onBeforeMount:在组件即将挂载到 DOM 之前被调用,模板已编译完成但未渲染,可在此预处理数据。
    • onMounted:组件挂载到 DOM 后立即调用,常用于 DOM 操作。
  5. 详解生命周期的更新 + 销毁流程

    • onBeforeUpdate:组件数据更新前调用,可保存状态等。
    • onUpdated:数据更新并重新渲染完成后调用,可检查更新后的 DOM。
    • onBeforeUnmount:组件即将卸载前调用,清理资源。
    • onUnmounted:组件从 DOM 中卸载后调用,标志生命周期结束。

六、组件开发:构建 Vue 应用基石

  1. Vue 3 单页面组件开发基础 - 组件定义 + 使用

    • 组件定义
    html 复制代码
    <script setup>
    import { ref } from 'vue';
    
    const count = ref(0);
    </script>
    
    <template>
      <button @click="count++">You clicked me {{ count }} times.</button>
    </template>
    • 定义一个简单的按钮点击计数组件。
    • 使用组件
    html 复制代码
    <script setup>
    import ButtonCounter from './ButtonCounter.vue';
    </script>
    
    <template>
      <h1>Here is a child component!</h1>
      <ButtonCounter />
    </template>
    • 在父组件中导入并使用子组件,可多次重用。
    • 闭合写法 :使用自定义标签名调用组件,如 <button-counter></button-counter>,需在父组件正确注册。
    • 传递 props
    html 复制代码
    <script setup>
    defineProps(['title']);
    </script>
    
    <template>
      <h4>{{ title }}</h4>
    </template>
    
    - 在子组件通过 `defineProps` 接收父组件传入属性,父组件使用时:
    
    <ChildComponent title="drogen" />
  2. Vue 3 单页面组件开发基础 - 监听事件

    • 父组件传递函数给子组件:
      • $emit + defineEmits
        • 子组件:
    html 复制代码
    defineEmits(["childClick"]);
    @click="$emit('childClick')"
    复制代码
        - 父组件:
         ```html
         @childClick="()=>{}"
         ```	
    • 传递参数

      • 子组件:
      html 复制代码
      defineEmits(["childClick"]);
      @click="$emit('childClick','chenxuyuan.net')"
      • 父组件:
      html 复制代码
      @childClick="(n)=>{console.log(n)}"
    • 还可在子组件通过 emit 函数触发:

      html 复制代码
      <script setup>
      import { ref } from 'vue';
      const emit = defineEmits(["childClick"]);
      
      onMounted(() => {
        emit("childClick", "声明式触发事件");
      });
      </script>
  3. Vue 3 单页面组件开发基础 - 插槽

    • defineExpose
      • 子组件:
    html 复制代码
    <script setup>
    const cxy = ref(0);
    defineExpose({ cxy });
    </script>
    • 父组件:
    html 复制代码
    <script setup>
    const childRef = ref('');
    <child-component ref='childRef' />
    <span>{{ childRef.cxy }}<span/>
    </script>
    • 插槽 slot
      • 子组件:
    html 复制代码
    <template>
      <slot></slot>
    </template>
    • 父组件:
    html 复制代码
    <child-component>
      drogen
    </<child-component>

七、组件进阶:优化与拓展组件功能

  1. Vue 3 单页面组件开发进阶 - 组件注册 + 命名

    • 组件注册
      • 全局注册
    javascript 复制代码
    // main.js
    import { createApp } from 'vue';
    import App from './App.vue';
    import ChildComponent from './ChildComponent.vue';
    
    const app = createApp(App);
    app.component('ChildComponent', ChildComponent);
    app.mount('#app');
    • 全局注册虽方便,但存在问题:不必要的文件体积,未使用组件无法 tree - shaking;难维护,依赖关系不明确。
    • 局部注册 :在使用组件的父组件中通过 import 导入并在 components 选项注册,更利于大型项目维护。
    • 组件名格式 :推荐大驼峰(ChildComponent)或连字符格式(child-component),遵循统一规范利于代码可读性与维护性。
  2. Vue 3 单页面组件开发进阶 - props

    • props 声明
    javascript 复制代码
    <script setup>
    const props = defineProps(['cxy']);
    
    console.log(props.cxy);
    </script>
    <script setup>
    defineProps({
      cxy: String,
      name: String
    });
    </script>
    • 通过 defineProps 声明接收父组件传入属性,指定类型利于开发时错误排查。
    • props 名字格式:建议小驼峰命名,符合 JavaScript 变量命名习惯。
    • 静态和动态
      • 静态:<ChildComponent title="drogen" />
      • 动态:<ChildComponent :title="cxy" />,通过 : 绑定动态数据。
    • 单项数据流
      • prop 是只读的,不能直接修改,如:
    javascript 复制代码
    const props = defineProps(['cxy']);
    // ❌ 警告!prop 是只读的!
    props.cxy = 'chenxuyuan.net';
    • 更改用法:
      • 用于初始值:
    javascript 复制代码
    const props = defineProps(['cxy']);
    // 计数器只是将 props.cxy 作为初始值
    // 像下面这样做就使 prop 和后续更新无关了
    const counter = ref(props.cxy);
    复制代码
    - 进一步处理/转换:
    javascript 复制代码
    const props = defineProps(['cxy']);
    // 该 prop 变更时计算属性也会自动更新
    const newcxy = computed(() => props.cxy + '老王');
  3. Vue 3 单页面组件开发进阶 - defineModel

    • 实现父子组件数据双向绑定的新语法糖:
      • 子组件:
    html 复制代码
    <script setup>
    const model = defineModel();
    </script>
    
    <template>
      <h2>子组件</h2>
      <input v-model="model" />
    </template>
    • 父组件:
    html 复制代码
    <script setup>
    import ChildComponent from "./ChildComponent.vue";
    import { ref } from "vue";
    
    const cxy = ref("drogen");
    </script>
    
    <template>
      <h1>父组件</h1>
      <span>{{ cxy }}</span>
      <ChildComponent v-model="cxy"></ChildComponent>
    </script>
    • 还可多属性双向绑定:

      • 父组件:
      html 复制代码
      <ChildComponent v-model:cxy="cxy" v-model:aa="aa"></ChildComponent>
      • 子组件:
      html 复制代码
      const cxy = defineModel("cxy");
      const aa = defineModel("aa");
      
      <input type="text" v-model="cxy" />
      <input type="text" v-model="aa" />
  4. Vue 3 单页面组件开发进阶 - 依赖注入

    • 依赖注入 :用于跨组件层级传递数据,无需层层传递 props

    • provide + inject

      • 供给方组件:

        html 复制代码
        <script setup>
        import { provide } from 'vue';
        
        provide(/* 注入名 */ 'message', /* 值 */ 'hello!');
        </script>
      • 注入方组件:

        html 复制代码
        <script setup>
        import { inject } from 'vue';
        
        const message = inject('message');
        </script>
      • 和响应式数据配合:

        • 供给方组件:
        html 复制代码
        <script setup>
        import { provide, ref } from 'vue';
        
        const cxy = ref('drogen');
        
        function updatecxy() {
          cxy.value = 'chenxuyuan.net';
        }
        
        provide('cxy', {
          cxy,
          updatecxy
        });
        </script>
        • 注入方组件:
        html 复制代码
        <script setup>
        import { inject } from 'vue';
        
        const { cxy, updatecxy } = inject('cxy');
        </script>
        
        <template>
          <button @click="updatecxy">{{ cxy }}</button>
        </template>
    • 注意:注入的数据默认只读,防止意外修改导致数据混乱。

  5. Vue 3 单页面组件开发进阶 - KeepAlive

    • 是内置组件,用于在多个组件间动态切换时缓存被移除的组件实例:
    html 复制代码
    <script setup>
    import ChildComponent1 from "./ChildComponent1.vue";
    import ChildComponent2 from "./ChildComponent2.vue";
    import { ref } from "vue";
    const cxy = ref(true);
    </script>
    <template>
      <h1>父组件</h1>
      <button @click="() => (cxy =!cxy)">切换组件</button>
      <KeepAlive><ChildComponent1 v-if="cxy" /></KeepAlive>
      <ChildComponent2 v-if="!cxy" />
    </template>
    • 如切换回缓存组件,其状态(如计数器值)保持不变,提升用户体验。

八、路由应用:构建单页面应用导航

  1. Vue 3 单页面实现规模化 Vue Router 基础 - 入门使用

    • 安装 :执行 pnpm install vue-router@4 安装 Vue Router 4。
    • 使用
    javascript 复制代码
    import { createRouter, createWebHashHistory } from 'vue-router';
    
    import ChildComponent1 from './ChildComponent1.vue';
    import ChildComponent2 from './ChildComponent2.vue';
    const routes = [
      { path: '/one', component: ChildComponent1 },
      { path: '/two', component: ChildComponent2 },
    ];
    const router = createRouter({
      history: createWebHashHistory(),
      routes,
    });
    
    export default router;
    html 复制代码
    <script setup>
    import { ref } from 'vue';
    
    const cxy = ref(true);
    </script>
    <template>
      <h1>父组件</h1>
      <p>
        <!-- 通过传递 `to` 来指定链接 -->
        <router-link to="/one">去组件1</router-link> <br />
        <router-link to="/two">去组件2</router-link>
      </p>
      <div>
        <!-- 路由出口 -->
        <router-view></router-view>
      </div>
    </template>
    • 这里创建路由实例,定义路由路径与对应组件,在父组件通过 <router-link> 实现导航链接,<router-view> 展示当前路由匹配组件内容。
  2. Vue 3 单页面实现规模化 Vue Router 基础 - 嵌套路由 + 编程式导航

    • 嵌套路由
    javascript 复制代码
    import { createRouter, createWebHashHistory } from 'vue-router';
    import ChildComponent1 from './ChildComponent1.vue';
    import ChildComponent2 from './ChildComponent2.vue';
    import ChildComponent3 from './ChildComponent3.vue';
    
    const routes = [
      {
        path: '/one',
        component: ChildComponent1,
      },
      {
        path: '/two',
    'two',
        component: ChildComponent2,
        children: [
          {
            path: 'aa',
            component: ChildComponent3,
          },
        ],
      },
    ];
    
    const router = createRouter({
      history: createWebHashHistory(),
      routes,
    });
    
    export default router;
    • 上述代码定义了嵌套路由,当访问 /two/aa 时,会在 ChildComponent2<router-view> 子路由出口中展示 ChildComponent3 组件,实现更复杂的页面布局与导航结构。
    • 编程式导航
    html 复制代码
    <script setup>
    import router from "./router.js";
    const routerChange = (n) => {
      router.push(n);
    };
    </script>
    <template>
      <h1>父组件</h1>
      <p>
        <button @click="routerChange('/one')">打开组件1</button> <br />
        <button @click="routerChange('/two')">打开组件2</button><br />
        <button @click="routerChange('/two/aa')">打开组件3</button>
      </p>
      <div>
        <router-view></router-view>
      </div>
    </template>
    • 通过 router.push 方法实现编程式导航,可在 JavaScript 代码中根据业务逻辑灵活切换路由,比如点击按钮时触发相应路由跳转。
  3. Vue 3 单页面实现规模化 Vue Router 基础 - 命名 + 重定向

    javascript 复制代码
    import { createRouter, createWebHashHistory } from 'vue-router';
    import ChildComponent1 from './ChildComponent1.vue';
    import ChildComponent2 from './ChildComponent2.vue';
    import ChildComponent3 from './ChildComponent3.vue';
    
    const routes = [
      {
        path: '/',
        redirect: '/one',
      },
      {
        path: '/one',
        name: 'one',
        component: ChildComponent1,
      },
      {
        path: '/two',
        name: 'two',
        component: ChildComponent2,
        children: [
          {
            path: 'three',
            name: 'three',
            component: ChildComponent3,
          },
        ],
      },
    ];
    
    const router = createRouter({
      history: createWebHashHistory(),
      routes,
    });
    
    export default router;
    • 为路由命名方便后续编程式导航引用,如 router.push({ name: 'one' }) 。设置重定向可在用户访问根路径 "/" 时自动跳转到指定路由,优化用户初始访问体验。
  4. Vue 3 单页面实现规模化 Vue Router 基础 - 路由传参

    • query
    html 复制代码
    <router-link
      :to="{ path: '/one', query: { cxy: 'drogen', state: 'chenxuyuan' } }"
      >打开组件1</router-link
    >
    • 通过 query 参数传递数据,在目标组件可通过 $route.query 获取,如 this.$route.query.cxy ,常用于携带一些非关键、临时性的搜索条件等信息,参数会显示在 URL 中。
    • params
    html 复制代码
    <router-link :to="{ name: 'one', params: { cxy: 'drogen' } }"
      >打开组件1</router-link
    >
    path: "/one/:cxy"
    • 先在路由定义中声明参数占位符 :cxy ,然后通过 params 传递,在目标组件通过 $route.params 获取,参数不会显示在 URL 中,适合传递一些敏感或对 URL 美观有要求的数据,如用户 ID 等。
    • 编程式导航传参
    javascript 复制代码
    const routerChange = (n, obj) => {
      router.push({ path: n, query: obj });
    };
    • 以编程方式跳转路由并传参,灵活实现业务逻辑中的数据传递需求。
  5. Vue 3 单页面实现规模化 Vue Router 进阶 - 历史记录模式 + 导航守卫

    • 不同的历史记录模式
      • 哈希模式:createWebHashHistory() ,此模式 URL 带有 # 符号,如 http://localhost:3000/#/one ,兼容性好,服务器无需特殊配置,是推荐的入门模式。
      • html5 模式:createWebHistory() ,使用 HTML5 History API,URL 更简洁美观,如 http://localhost:3000/one ,但需要服务器配合进行 URL 重写,防止刷新页面出现 404 错误。
    • 导航守卫
    javascript 复制代码
    router.beforeEach((to, from, next) => {
      if (to.meta.isAuth) {
        if (localStorage.getItem("token") === "1") {
          next();
        } else {
          alert("请先登录");
        }
      } else {
        next();
      }
    });
    • 在路由跳转前进行权限验证等操作,to 是即将进入的目标路由,from 是当前离开的路由,next 用于控制导航流程,决定是否继续跳转、重定向或中断导航,保障应用安全性与业务逻辑正确性。
  6. Vue 3 单页面实现规模化 Vue Router 进阶 - 路由懒加载

    javascript 复制代码
    component: () => import("./ChildComponent1.vue"),
    • 采用路由懒加载技术,将组件的加载延迟到路由被访问时,而非一次性加载所有组件,优化初始加载性能,尤其在大型项目中有大量组件时,能显著减少初始包体积,加快页面首次加载速度,提升用户体验。
相关推荐
Pedantic15 小时前
SwiftUI 手势层级(Gesture Hierarchy)详解
前端
飘尘16 小时前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
一颗烂土豆16 小时前
Meshopt 压缩深度解析,为什么它比 Draco 更快
前端·javascript·webgl
浏览器工程师17 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
雨季mo浅忆17 小时前
VSCode自动格式化三要素
前端
爱勇宝18 小时前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员
kyriewen18 小时前
同事每天催我 Code Review,我写了个脚本让 AI 替我 review PR——现在他反过来催 AI 了
前端·javascript·ai编程
user205855615181320 小时前
Windows 项目安装时报 `node-sass` 错误,如何快速处理
前端
LiaCode20 小时前
Redis 在生产项目的使用
前端·后端