vueVite

本文讲述使用Vite搭建Vue3项目的结构及其作用

项目结构及其作用

  1. 根目录:
    1. package.json:这个文件包含了项目的依赖项、脚本等信息,是整个项目的元数据。
    2. vue.config.js:这是用于配置 Vite 的主要文件。在这里,你可以定义环境变量、设置插件、调整打包选项等。这是一个非常重要的配置文件,它会影响到你的开发体验和生产部署过程
  2. public:包含了一些静态资源,比如图标、favicon.ico、robots.txt 等。这些文件会被直接复制到构建目录中,不会经过 Vite 的处理。
    1. 在组件中导入public中的静态资源时不需要完整的路径,直接/文件名即可比如import qrImg from '/qr.png';导入public中的二维码图片
  3. src 文件夹:在 Vite 中最核心的一个文件夹,因为它存放着所有的源代码: index.htm-main.js-index.js、App.vue-其他view、components组件-静态资源(图片、cdn等等)
    1. assets 文件夹:用来存储静态资源,如图片、样式表等,这些资源通常不会经常变化,可以直接复制到服务器上
    2. components 文件夹:存放的是你写好的复用组件,每个子文件夹代表一类相关的组件,比如说有一个 base 或者 app 组件,然后再根据具体功能划分出不同的模块(例如:button、table 等)。在vue项目中可以将其中的文件称为vue文件:封装和组织组件的模板、逻辑和样式,使得组件的开发和管理更加高效和模块化(往往在components下创造组件,在view下的vue文件中引用组件),提高了代码的可维护性和复用性:其基础结构如下:
      1. <template></template>:内含html代码,用于组成页面结构

        1. 浏览器中不会存在<template>标签的原因是,Vue会在编译过程中将<template>中的内容转换为JavaScript渲染函数。在这个过程中,Vue会解析<template>的内容并生成虚拟DOM,而这个虚拟DOM最终会被渲染到实际的DOM中。
        2. 编译过程:
          1. 解析模板:Vue使用vue-template-compiler库将<template>块中的HTML字符串解析为抽象语法树(AST),这表示了模板的结构。
          2. 生成渲染函数:AST会被转换为JavaScript代码,这些代码负责创建虚拟DOM节点。每当组件需要渲染时,Vue会调用这些渲染函数,而不是直接使用<template>中的内容。
          3. 更新DOM:当数据变化时,Vue会根据虚拟DOM的变化来更新实际的DOM。
      2. 调用组件

        1. 引入组件有两种方式
          1. 直接在要使用的页面中导入
          2. 在main.js中导入,然后app.component(”组件名称”,组件)注入全局这样就可以在全局使用了
        2. 在组件中调用的组件名称也有两种方式
          在 Vue.js 中,当您在 main.js 中全局注册组件时,例如:
        1
        2
        3
        4
        5
        6
        7
        8
        // main.js
        import { createApp } from 'vue';
        import App from './App.vue';
        import PanelHead from './components/PanelHead.vue';

        const app = createApp(App);
        app.component('PanelHead', PanelHead);
        app.mount('#app');

        尽管您注册的组件名称是 'PanelHead',但在模板中可以使用 <panel-head></panel-head><PanelHead></PanelHead>的形式来引用,

        原因如下:

        1. 组件名的大小写不敏感解析:

          Vue 在解析模板时,会将自定义组件标签名中的连字符形式(kebab-case)和大驼峰形式(PascalCase)都识别为同一个组件。例如:

          • <PanelHead></PanelHead>
          • <panel-head></panel-head>

          上述两种写法都会被解析为注册的 'PanelHead' 组件。

        2. 组件名的规范化:

          根据 Vue 的组件名解析规则,注册时的组件名会被标准化处理,模板中的标签名也会被转换为相应的格式,以进行匹配。

          • 注册组件时使用 PascalCase(大驼峰命名)的名称。
          • 在模板中,可以使用 PascalCase 或 kebab-case(短横线命名)形式的组件标签。
        3. HTML 中的大小写不敏感性:

          • 在浏览器解析 HTML 时,标签名是大小写不敏感的。
          • 为了遵循 HTML 规范,Vue 推荐在模板中使用 kebab-case 的组件名。

        总结:

        • 注册组件: 使用 PascalCase 命名,例如 'PanelHead'
        • 在模板中引用: 可以使用 <PanelHead></PanelHead><panel-head></panel-head>,Vue 都能够正确解析。
        • 推荐使用: 在模板中使用 kebab-case 形式,即 <panel-head></panel-head>,以符合 HTML 规范和提高可读性。
      3. 组合式API与选项式API的对比(本项目中使用组合式API)

        1. 组合式API:Vue3提供了丰富的组合式API,帮助开发者管理组件的行为,核心功能如下:

          1. <script setup> 是在单文件组件中使用组合式 API 的编译时语法糖。主要特点:
            1. 顶层变量/函数自动暴露给模板:意思是说<script setup> 中定义的变量和函数会自动暴露给当前组件的模板使用;这种暴露仅限于当前组件内部的 template 部分;

              1. 无需 return 和 export 语句
              2. 引入的组件自动注册
                1
                2
                3
                4
                5
                6
                7
                8
                9
                10
                11
                12
                13
                14
                15
                16
                17
                18
                19
                20
                21
                22
                23
                24
                25
                26
                27
                28
                29
                30
                31
                32
                33
                34
                35
                36
                37
                38
                39
                40
                41
                42
                43
                44
                45
                46
                47
                48
                49
                50
                51
                52
                53

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

                const count = ref(0)
                const increment = () => count.value++
                </script>

                <template>
                <div>
                <p>{{ count }}</p>
                <button @click="increment">+1</button>
                <ChildComponent />
                </div>
                </template>

                ```
                2. 组件实例的访问:父组件通过 ref 访问子组件时,访问的是子组件的实例;出于安全考虑,Vue 3 默认关闭了组件实例的属性访问需要通过 defineExpose 明确声明哪些属性/方法可以被父组件访问;
                ```js
                <!-- 子组件 Child.vue -->
                <script setup>
                import { ref } from 'vue'

                const count = ref(0) // ✅ 自动暴露给当前模板使用
                const increment = () => count.value++

                defineExpose({ count }) // ✅ 显式暴露给父组件访问
                </script>

                <template>
                <div>
                {{ count }} <!-- ✅ 可以直接访问 count -->
                <button @click="increment">+1</button> <!-- ✅ 可以直接访问 increment -->
                </div>
                </template>

                <!-- 父组件 Parent.vue -->
                <script setup>
                import { ref } from 'vue'
                import Child from './Child.vue'

                const childRef = ref(null)

                // 访问子组件实例的属性
                console.log(childRef.value.count) // ✅ 可以访问(因为被 defineExpose 暴露)
                childRef.value.increment // ❌ 不能访问(未被暴露)
                </script>

                <template>
                <Child ref="childRef" />
                </template>

            2. 这种设计的目的是:

                   提高代码的可维护性
                   增强组件的封装性
                   避免父组件随意访问和修改子组件的内部状态
              
          2. 响应式系统:数据变更自动触发视图更新
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            import { ref, reactive } from 'vue'

            // ref 基本类型响应式
            const count = ref(0)
            count.value++ // 修改值会触发视图更新

            // reactive 对象响应式
            const state = reactive({
            name: '张三',
            age: 25
            })
            state.age++ // 修改值会触发视图更新
          3. 生命周期钩子(选项式 API:Vue 通过组件选项自动处理逻辑,无需手动导入响应式函数和生命周期钩子。组合式 API:需要手动导入所需的响应式函数和生命周期钩子,从而提供更灵活和模块化的代码结构,他们俩周期钩子也有差别;比如组合式没有beforecreate,把setup当created用,其它就当改了个名)

          1731056688357

          alt text

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          30
          31
          32
          33
          34
          35
          36
          37
          38
          39
          40
          41
          42
           <template>
          <div>{{ count }}</div>
          <button @click="onClick">
          增加 1
          </button>
          </template>

          <script setup>
          import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue';

          const count = ref(1);

          const onClick = () => {
          count.value += 1;
          };


          onBeforeMount(() => {
          alert("组件渲染之前调用的时beforeMount函数");
          });

          onMounted(() => {
          alert("组件渲染完成调用的时mounted函数");
          });

          onBeforeUpdate(() => {
          alert("组件更新之前调用的时beforeUpdate函数");
          });

          onUpdated(() => {
          alert("组件更新之后调用的时updated函数");
          });

          onBeforeUnmount(() => {
          alert("组件卸载之前调用的时beforeUnmount函数");
          });

          onUnmounted(() => {
          alert("组件卸载之后调用的时unmounted函数");
          });
          </script>


          4. 响应式监听
          1. watch的两个参数
          1. watch 的第一个参数需要是一个响应式数据(可以是多个),或者是一个返回值的函数。(比如返回一个对象obj的属性() => obj.age)
          2. 箭头函数(newValue,oldValue) => {}
          1. oldValue:监听的值在变化之前的值
          2. newValue:监听的值在变化之后的值
          2. watch的触发条件
          1. 要让 watch 监听器生效,需要在运行时修改被监听的响应式数据。直接在代码中修改变量的初始值(即在组件加载前设置的值)并不会触发 watch 的回调函数。watch 的作用是监听响应式数据的变化,当数据在运行时发生改变时(例如用户点击按钮触发事件函数),watch 才会检测到这种变化并执行回调函数。
          3. watch的两个属性:
          1. immediate(获取初始化):当设置为 true 时,监听器会在绑定后立即触发回调函数。这意味着在初始化阶段,即使被监听的值没有发生变化,回调函数也会被调用一次。作用如下
          1. 在组件加载时,需要根据初始值执行一些操作,例如根据初始参数请求数据。而不必等待被监听的值发生变化。
          2. 在组件创建时,需要将某些响应式数据同步到其他地方
          3. 在初始化组件的时候oldValue是undefined,newValue是当前值(初始值)
          2. deep:true
          1. Vue3 的 reactive 会自动对对象进行深层代理(Proxy),所以:当直接修改 监听对象的内置嵌套对象时,两个监听器都会触发
          2. deep: true 主要用于以下场景:
          1. 当你使用 reactive 对象作为 watch 的源,且需要在对象被整个替换时触发监听
          2. 当监听的是一个返回非响应式对象的 getter 函数时
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          const deep = reactive({
          name: 'JJ',
          age: 19,
          info: {
          address: 'Beijing',
          contacts: {
          phone: '123456',
          email: 'test@example.com'
          }
          }
          })
          watch(deep, () => {
          console.log('obj changed')
          })//vue3自带不需要deep

          // 如果是这种情况,则需要 deep: true:当改变的是 deep.info时,returnNonReactiveObj会被整个替换
          const returnNonReactiveObj = () => ({
          info: deep.info
          })
          watch(returnNonReactiveObj, (newValue, oldValue) => {
          console.log('non-reactive obj changed:', newValue)
          }, { deep: true })

          </script>


          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          <script setup>
          import { ref, reactive, watch } from 'vue'

          const counter1 = ref(0)
          const counter2 = ref(0)
          // 监听多个
          watch([counter1, counter2], (newValue, oldValue) => {
          console.log('The new counter1 value is: ' + counter1.value)
          console.log('The new counter2 value is: ' + counter2.value)
          })

          const obj = reactive({
          name: 'JJ',
          age: 18
          })
          //监听初始化值
          watch(obj, (newValue, oldValue) => {
          console.log('The new obj value is: ' + obj.value)
          }, {
          immediate: true
          })
          // watch监听单个属性
          watch(() => obj.name, (newValue, oldValue) => {
          console.log('The new obj value is: ' + obj.value)
          }, {
          immediate: true
          })

          </script>
          1. 计算属性:更加灵活,可以在定义响应式变量时声明;computed 可以用于任何类型的数据处理,不仅限于数值计算。

            1. 核心优势
              • 可处理任何数据类型
              • 自动跟踪依赖关系
              • 具有缓存机制
              • 提高代码可读性和维护性
            2. 以下是一些常见用例
              1. 字符串处理

                1
                2
                3
                // 字符串转换
                const upperCase = computed(() => name.value.toUpperCase())
                const fullName = computed(() => `${firstName.value} ${lastName.value}`)
              2. 数组处理

                1
                2
                3
                4
                // 数组过滤
                const activeTodos = computed(() => todos.value.filter(todo => !todo.completed))
                // 数组排序
                const sortedList = computed(() => items.value.sort((a, b) => a.id - b.id))
              3. 对象处理

                1
                2
                3
                4
                5
                6
                // 对象转换
                const userInfo = computed(() => ({
                fullName: `${user.value.firstName} ${user.value.lastName}`,
                age: user.value.age,
                isAdult: user.value.age >= 18
                }))
              4. 复杂逻辑

                1
                2
                3
                4
                5
                6
                // 购物车计算
                const cartTotal = computed(() => {
                return cart.value.reduce((total, item) => {
                return total + (item.price * item.quantity)
                }, 0)
                })
              5. 字符倒序

                1
                2
                3
                4
                5
                6
                7
                8
                9
                10

                const value = ref('this is a value')

                // 注意这里
                const reversedValue = computed(() => {
                // 使用 ref 需要 .value
                return value.value
                .split('').reverse().join('');
                })

          2. 条件渲染和列表渲染

            1. v-for的参数

              1. 如下代码中的v-for=”(item, index) in props.menuData” 中:这个 index 是 Vue 提供的数组索引值,是 v-for 指令内置提供的第二个参数表示当前遍历项在数组中的索引值(从0开始)是可选参数,如果不需要索引值,可以省略
                1
                2
                3
                4
                5
                6
                7
                8
                9
                10
                !-- 假设 props.menuData 是这样的数组 -->
                [
                { meta: { id: 1, name: '菜单1' } }, // index = 0
                { meta: { id: 2, name: '菜单2' } }, // index = 1
                { meta: { id: 3, name: '菜单3' } } // index = 2
                ]
                v-for="(item, index) in array"
                // item: 当前项
                // index: 当前项的索引

            2. v-if 中 index 和 key

              1. index=”${index}-${item.meta.id}

                1. 这是 Element Plus 菜单组件特有的属性
                2. 用于标识每个菜单项的唯一路径
                3. 帮助菜单组件追踪当前选中的菜单项
                4. 通常用于控制菜单的激活状态
              2. key 属性:

                1
                :key="`${index}-${item.meta.id}`"
              • 这是 Vue 框架要求的特殊属性
              • 用于给 v-for 循环中的每个元素一个唯一标识
              • 帮助 Vue 在虚拟 DOM 更新时准确识别节点
              • 提高渲染性能和准确性

              它们的主要区别:

              • index 是功能性的唯一标识,用于菜单组件的功能实现
              • key 是结构性的唯一标识,用于 Vue 框架的 DOM 更新优化
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          30
          31
          32
          33
          34
          35
          36
          37
          38
          39
          40
          41
          42
          43
          44
          45
          46
          47
          48
          49
          50
          51
          52
          53
          54
          55
          56
          57
          58
          59
          60
          61
          62
          63
          <!-- 项目中的菜单组件 -->
          <template>
          <template v-for="(item, index) in props.menuData">
          <!-- 无子菜单 -->
          <el-menu-item
          v-if="!item.children || item.children.length == 0"
          :indx="`${props.index}-${item.meta.id}`"
          :key="`${props.index}-${item.meta.id}`"
          >
          <!-- 结合路由信息以及动态元素实现图标渲染 -->
          <el-icon size="20">
          <component :is="item.meta.icon"></component>
          </el-icon>
          <!-- 菜单标题 -->
          <span>{{item.meta.name}}</span>
          </el-menu-item>
          <!-- 有子菜单:分两个部分:标题+递归调用子菜单 -->
          <el-sub-menu
          v-else
          :index="`${props.index}-${item.meta.id}`"
          >
          <template #title>
          <!-- 标题区域的内容: #title是 Element Plus 的 el-sub-menu 组件预定义的插槽名称 -->
          <el-icon size="20">
          <component :is="item.meta.icon"></component>
          </el-icon>
          <span>{{item.meta.name}}</span>
          </template>
          <!-- 递归调用此组件实现子菜单 -->
          <!-- index:1 1-2 1-3 实现每个菜单项的唯一标识 -->
          <SubMenu
          :index="`${props.index}-${item.meta.id}`"
          :menuData="item.children"
          />
          </el-sub-menu>
          </template>
          </template>

          ```

          ```js
          <template>
          <!-- 条件渲染 -->
          <div v-if="show">显示内容</div>
          <div v-else>其他内容</div>

          <!-- 列表渲染 -->
          <ul>
          <li v-for="item in list" :key="item.id">
          {{ item.name }}
          </li>
          </ul>
          </template>

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

          const show = ref(true)
          const list = ref([
          { id: 1, name: '项目1' },
          { id: 2, name: '项目2' }
          ])
          </script>
          1. v-if和v-show的区别

            • v-ifv-show 都是 Vue.js 中用于条件渲染的指令,但它们有不同的作用和使用场景。

            • 总结:

              • 使用 v-if 时,元素在条件为假时不会存在于 DOM 中。
              • 使用 v-show 时,元素始终存在于 DOM 中,只是通过 CSS 控制显示或隐藏。
            • v-if

              • 作用:根据表达式的真假值来有条件地渲染元素。
              • 特点:元素及其绑定的事件和子组件在条件为假时不会被渲染或销毁。
              • 性能:初始渲染时有更高的开销,因为需要添加或删除 DOM 元素。
            • v-show

              • 作用:根据表达式的真假值来切换元素的显示状态。
              • 特点:元素始终会被渲染并保留在 DOM 中,只是通过 CSS 的 display 属性来控制显示或隐藏。
              • 性能:初始渲染开销较小,但频繁切换显示状态时性能更好。
            • 使用场景

              • **v-if**:适用于在运行时条件很少改变的场景,因为它会在条件变化时进行 DOM 的添加和删除。
              • **v-show**:适用于需要频繁切换显示状态的场景,因为它只会切换 display 属性。
            • 示例

              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              14
              15
              16
              17
              18
              19
              20
              21
              22
              <template>
              <!-- v-if 示例 -->
              <div v-if="isShow">
              这个元素会根据 isShow 的值来添加或删除
              </div>

              <!-- v-show 示例 -->
              <div v-show="isVisible">
              这个元素会根据 isVisible 的值来显示或隐藏
              </div>
              </template>

              <script>
              export default {
              data() {
              return {
              isShow: true,
              isVisible: true
              };
              }
              };
              </script>
          2. 事件处理与双向绑定

            1. v-model 的用途:获取表单中的输入数据赋值给变量;v-model 是 Vue.js 中用于创建双向数据绑定的指令,主要用于表单输入元素与应用状态之间的同步。主要功能如下
              1. 双向绑定

                • 自动将用户输入的值同步到组件的状态(如 dataref)。
                • 同时,当状态发生变化时,更新输入元素的显示值。
              2. 简化代码

                • 替代手动编写 :value@input 事件处理器,实现更简洁的双向绑定。
            2. 常见用法
            • 文本输入

              1
              <input v-model="变量名" />
            • 复选框

            1
            <input type="checkbox" v-model="变量名" />
            • 选择框
            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21
            22
            23
            24
            25
            26
            27
                  <select v-model="变量名">
            <option value="A">A</option>
            <option value="B">B</option>
            </select>

            ```js
            <template>
            <input v-model="username" />
            <button @click="handleClick">点击</button>
            </template>

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

            const username = ref('')
            const handleClick = () => {
            console.log(username.value)
            }
            </script>
            /*
            - **`v-model="username"`**:
            - 将输入框的值与 `username` 变量绑定。
            - 用户在输入框中输入内容时,`username` 会自动更新。
            - 如果在代码中修改 `username` 的值,输入框的显示内容也会相应更新。

            */

        2. 对比一下选项式API:在 Vue 2 和 Vue 3 的选项式 API 中的<script>中需要 return 和 export 的原因:(这些在组合式API中都不用考虑)

          1. export default 的目的:
            1. 将组件配置对象导出,使其可以被其他组件引入使用
            2. 这是 ES6 模块系统的要求
            3. Vue 通过这个导出的对象来创建组件实例
          2. return 的目的:
            1. 在 data() 函数中返回数据对象,使其成为响应式数据
            2. 每个组件实例都需要独立的数据副本,避免数据共享
              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              14
              15
              16
              17
              18
              19
              20
              21
              22
              23
              24
              25
              26
              27
              28
              29
              30
              31
              <script>
              // 选项式 API
              export default {
              name: 'MyComponent',

              // data 必须是函数并返回对象,确保每个组件实例有独立的数据副本
              data() {
              return {
              count: 0,
              message: 'Hello'
              }
              },

              methods: {
              increment() {
              this.count++
              }
              },

              computed: {
              doubleCount() {
              return this.count * 2
              }
              }
              }
              </script>
              // ❌ 错误示例
              data: {
              count: 0
              }

              1
              2
              3
              4
              5
              6
              7
              8
              9
              10
              11
              12
              13
              14
              15
              16
              17
              18
              19
              20
              21
              22
              23
              24
              25
              26
              27
              28
              29
              30
              31
              32
              33
              34
              //组合式API中:组合式API基础语法
              <script setup>
              import { ref, reactive } from 'vue'

              // 方式1:使用 ref() 定义基础类型
              const count = ref(0)
              const message = ref('Hello')

              // 方式2:使用 reactive() 定义对象类型
              const state = reactive({
              count: 0,
              message: 'Hello'
              })

              // 使用时:(在template之外)
              // ref 需要 .value
              console.log(count.value)
              console.log(message.value)

              // reactive 直接使用
              console.log(state.count)
              console.log(state.message)
              </script>

              <template>
              <!-- 在模板中使用 ref 不需要 .value -->
              <div>{{ count }}</div>
              <div>{{ message }}</div>

              <!-- reactive 对象的使用 -->
              <div>{{ state.count }}</div>
              <div>{{ state.message }}</div>
              </template>

        3. 选项式与组合式API的区别(除了上述之外):

          1. 选项式 API:Vue 通过组件选项自动处理逻辑,无需手动导入响应式函数和生命周期钩子等等。

          2. 组合式 API:需要手动导入所需的响应式函数和生命周期钩子等等,从而提供更灵活和模块化的代码结构。(除了编译器宏比如defineProps,编译器宏:由 Vue 的编译器在编译时处理的特殊语法糖。它并非普通的 JavaScript 函数,因此不需要通过 import 从 ‘vue’ 中导入。自动可用:在<script setup>块内,defineProps 会被自动识别并处理。)

            1
            2
            3
            4
            5
            6
            7
            8
            9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21
            22
            23
            24
            25
            26
            27
            28
            29
            30
            31
            32
            33
            //拿子传父举例
            // 选项式 API
            <template>
            <button @click="sendMessage">Send Message</button>
            </template>
            <script>
            export default {
            methods: {
            sendMessage() {
            this.$emit('message-event', 'Hello from child');//自动处理this.$emit
            }
            }
            };
            </script>

            // 组合式 API
            <template>
            <ChildComponent @message-event="handleMessageEvent" />
            <p>{{ messageFromChild }}</p>
            </template>

            <script setup>

            import { ref } from 'vue';

            import ChildComponent from './ChildComponent.vue'
            // 必须遵循js的规范,任何变量使用前必须申明
            let messageFromChild = ref('');

            const handleMessageEvent = (message) => {
            messageFromChild.value = message;
            };
            </script>
          3. 选项式 API 和 组合式 API 的主要区别补充:

            1. 组织代码的方式

              • 选项式 API:通过选项(如 datamethodscomputed 等)组织代码,适合简单组件。
              • 组合式 API:通过组合函数(如 setup)组织代码,更适合复杂逻辑和代码复用。
            2. 逻辑复用

              • 选项式 API:依赖混入(mixins)和高阶组件(HOCs),可能导致命名冲突和难以追踪。
              • 组合式 API:使用组合函数(composables)实现逻辑复用,结构更清晰,避免命名冲突。
            3. 类型推导和 TypeScript 支持

              • 选项式 API:TypeScript 支持有限,类型推导较复杂。
              • 组合式 API:与 TypeScript 集成更好,提供更精准的类型推导。
            4. 代码组织与可维护性

              • 选项式 API:按选项分割,功能分散,难以管理大型组件。
              • 组合式 API:按功能分割,相关逻辑集中,提升可维护性。
            5. 生命周期钩子

              • 选项式 API:通过选项直接定义生命周期钩子(如 mountedcreated)。
              • 组合式 API:在 setup 内使用函数(如 onMountedonCreated)定义生命周期钩子。
            6. 响应式系统

              • 选项式 API:使用 data 对象进行响应式管理,自动处理响应式属性。
              • 组合式 API:使用 refreactive 等函数显式创建响应式数据,更灵活。
            7. 学习曲线

            • 选项式 API:更直观,适合 Vue 新手。
            • 组合式 API:需要理解响应式原理和组合函数,学习曲线稍陡。
            1. 调试和测试
            • 选项式 API:由于逻辑分散,单独测试某一功能较困难。
            • 组合式 API:逻辑集中,单元测试和调试更容易。
          4. 最后总结一下两者的异同:

            1. 同:无论是选项式还是组合式API,都得遵循js的规范,比如任何变量使用前必须申明,否则会报错;调用其他组件时都需要导入相应文件;
            2. 异:
              1. 选项式需要export default导出组件对象,并在其中使用components申明导入的组件、data()函数return 返回数据变量/对象(定义申明)以实现相应,而组合式直接ref/reactive即可;
              2. 组合式中需要导入响应式函数和生命周期钩子等等,而选项式不需要;(除了编译器宏比如defineProps之外)
            3. 在这个项目中我主要使用组合式,往后就不管选项式了
      4. <style scoped></style>:用于定义组件的样式。开发者可以使用CSS或预处理器(如Sass、Less、Tailwind CSS,问perplexity)编写样式:

        1. <style> 标签内编写的 CSS 样式默认是全局作用域,会影响到其他路由页面。要使样式仅作用于当前组件,需要在 <style> 标签上添加 scoped 属性:
          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          <style lang="less" scoped>
          /* 在这里添加样式 */
          .logo-lg {
          font-size: 20px;
          text-align: center;
          height: 50px;
          line-height: 50px;
          color: style
          }
          </style>

    3. 根组件App.vue:包含了整个应用程序的布局结构
      1. 页面渲染的入口点。所有其他组件都会嵌套在App.vue中
      2. 通常会与Vue Router的组件<RouterView />结合使用,处理页面之间的导航和路由,实现不同页面的切换和展示;如果使用了路由,但是App.vue中没有<RouterView />,则路由无法正常工作;经典内容如下:
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15


        <template>

        <RouterView />

        </template>

        <script setup>

        </script>
        <style scoped>

        </style>

    4. Vue应用的入口文件main.js:初始化Vue实例并将其挂载到DOM中,导出了整个应用程序所需的全局状态和方法。一般来说,在这里初始化一些全局变量或函数,以便于后续操作,其基础操作如下:
      1. 导入router实例import router from './router';(访问router/index.js文件)
      2. 创建app实例const app = createApp(App),这里的App是根组件App.vue;
      3. 挂载router实例app.use(router)
      4. 挂载app实例到指定ID的div元素上:app.mount('#app')
        1. 这里的#app是index.html中的一个div元素的id,Vue会将app实例挂载到这个元素上,从而渲染整个应用
        2. 你也可以将app实例挂载到其他元素上,但要在index.html中添加相应的元素以及id属性
        3. 默认使用#app的原因
          1. 约定俗成:使用id=”app”是Vue文档和许多示例中的常见做法,主要是为了简化学习和开发过程。开发者可以快速识别出这是Vue应用的挂载点。
          2. 避免冲突:在大型应用中,使用特定的id可以帮助避免与其他JavaScript库或框架的冲突,确保Vue应用的DOM元素明确且独特。
    5. router文件夹:
      1. 功能:路由管理
        1. 导入组件,定义路由规则,控制页面间的跳转逻辑
        2. 定义 URL 路径与组件的对应关系
        3. 管理路由参数传递
        4. router/index.js负责初始化和配置Vue应用; 主要功能如下
          1. 定义路由routes
          2. 创造并导出router实例
        5. 注意:不能重复导入同一个组件,否则会导致组件重复注册,从而引发错误
      2. 典型结构
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        // router/index.js
        import { createRouter, createWebHistory } from 'vue-router'
        import Home from '../views/Home.vue'
        import About from '../views/About.vue'
        const routes = [
        {
        path: '/',
        component: () => import('@/views/Home.vue')
        },
        {
        path: '/about',
        component: () => import('@/views/About.vue')
        }
        ]

        const router = createRouter({
        history: createWebHistory(),
        routes
        })
        export default router

    6. View文件夹:
      1. 视图功能:负责页面渲染
        1. 存放页面级别的组件
        2. 对应具体的路由页面
        3. 组合和管理其他小组件
      2. 与components的区别
        1. views:负责页面级别的组件,对应具体路由逻辑的页面(也就是说路由怎么设置的,view下就要有相应的页面组件),组合和管理其他小组件(可嵌入components中或者view中子组件)
        2. components:负责通用的小组件,可以在多个页面中复用,不涉及具体的路由逻辑
      3. 举例 Main.vue
        1. 作用:主要负责页面的整体布局和结构,包含了整个页面的主要内容
        2. 典型结构
          1
          2
          3
          4
          5
          6
          7
          8
          <!-- views/Main.vue -->
          <template>
          <div class="main">
          <Header />
          <Aside />
          <MainContent />
          </div>
          </template>
 1.  services :存放业务逻辑层面的代码,包括多个组件共用的服务端接口调用函数等
 2.  utils:存放通用工具函数,不同的地方不重复书写相同的代码片段

执行顺序

  • 当启动应用程序时,Vite 会按照如下顺序加载和处理这些文件:
    1
    2
    3
    4
    5
    6
    读取 package.json:获取项目依赖项和脚本信息。
    解析 vue.config.js:配置Vite的行为,包括环境变量管理、插件注册等。
    载入 src/App.vue:作为应用程序的根组件,负责渲染页面结构。
    执行 src/main.js:初始化应用程序,全局状态和方法的创建。
    递归查找并加载各个组件:从根组件开始,逐级向下寻找并注入子组件,形成完整的DOM树结构。
    优化与缓存处理:利用Vite提供的内置优化机制,对JS/CSS进行压缩混淆,以及对第三方库进行tree-shaking以减少无效代码。

vueVite
https://tolsz.me/2024/10/16/vueVite/
作者
wbj_Lsz
发布于
2024年10月16日
许可协议