博主头像
简单

tired

头图

Vue学习

Vue

1.创建应用

简单开发,导入vue.global.js在目录下,head中加入

<script src="vue.global.js"></script>

模块化开发,导入vue.esm-browser.js在目录下,在js中导入

<script type="module">
        import {createApp,reactive,ref} from './vue.esm-browser.js'
</script>

"渐进式"是指可以按需引入Vue.js的部分功能, 而不必全量引入整个框架

<div id="app">
    {{ msg }}

    <h2>{{ web.title }}</h2>
    <h3>{{ web.url }}</h3>
</div>
<div id="app"></div> 指定一个 id 为 app 的 div 元素
/*
{{ }} 插值表达式, 可以将 Vue 实例中定义的数据在视图中进行渲染
    如: Vue 实例中定义一个 msg 变量, 值为 "Hello world", 在模板中若使用插值表达式 {{ msg }} 则会被渲染成 "Hello world"

    响应式数据是指当数据发生变化时, 模板中依赖于该数据的部分会自动更新
*/
    //创建一个 Vue 应用程序
    Vue.createApp({
        //Composition API(组合式 API) 的 setup选项 用于设置响应式数据和方法等
        setup() {
            //Composition API 的 reactive()函数 用于创建响应式数据
            const web = Vue.reactive({ //Vue.reactive 创建一个响应式数据对象 web, 其中包含 title 和 url 属性
                title: "邓瑞编程",
                url: "dengruicode.com"
            })

            //返回数据
            return {
                msg: "success",
                web
            }
        }
    }).mount("#app") //将 Vue 应用程序挂载(mount) 到 app 元素上


//将 Vue 对象中的 createApp、reactive 属性赋值给 createApp、reactive 变量,解构后不用使用Vue.来调用
const { createApp, reactive } = Vue //解构赋值语法

createApp({
    setup() {
        const web = reactive({
            title: "邓瑞编程",
            url: "dengruicode.com"
        })

        return {
            msg: "success",
            web
        }
    }
}).mount("#app")

完整:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

</head>
<body>
    
    <div id="app">
        {{ msg }}

        <h2>{{ number }}</h2>
    </div>

    <script type="module">
        import {createApp,reactive,ref} from './vue.esm-browser.js'

        createApp({
            setup() {
                const number = ref(10)

                const web = reactive({
                    title:"wsy",
                    url:"wsy.com",
                    show: true
                })

                return{
                    msg:"success",
                    number,
                    web                    
                }

            }
        }).mount("#app")
    </script>

</body>
</html>

2.ref和reactive(响应式对象数据)

const number = ref(10) //ref用于存储单个基本类型的数据, 如:数字、字符串等,但是也可以用于数组,例如const number = ref([1,2,3])

number.value = 20 //使用ref创建的响应式对象, 需要通过.value属性来访问和修改其值

const web = reactive({ //用于存储复杂数据类型, 如:对象或数组等
    title: "邓瑞编程",
    url: "dengruicode.com"
})
web.url = "www.dengruicode.com" //使用reactive创建的响应式对象, 可以直接通过属性名来访问和修改值

注意,在模板中使用 ref 时,我们不需要附加 .value。为了方便起见,当在模板中使用时,ref 会自动解包 (有一些注意事项)。

import { ref } from 'vue'

const count = ref(0)

{{ count }}

与 reactive 对象不同的是,当 ref 作为响应式数组或原生集合类型 (如 Map) 中的元素被访问时,它不会被解包:

const books = reactive([ref('Vue 3 Guide')])
// 这里需要 .value
console.log(books[0].value)

const map = reactive(new Map([['count', ref(0)]]))
// 这里需要 .value
console.log(map.get('count').value)

3.v-on(监听事件,绑定方法)

img
img

v-on:事件="方法" 用来监听事件,也可以简写为 @事件="方法"

<!-- v-on:click 表示在 button 元素上监听 click 事件 -->
<button v-on:click="edit">修改</button> <br>

<!-- @click 简写形式 -->
<button @click="add(20, 30)">加法</button> <br>

<!-- 
enter space tab 按键修饰符
keyup是在用户松开按键时才触发
keydown是在用户按下按键时立即触发
-->
回车 <input type="text" @keyup.enter="add(40, 60)"> <br>
空格 <input type="text" @keyup.space="add(20, 30)"> <br>
Tab <input type="text" @keydown.tab="add(10, 20)"> <br>
w <input type="text" @keyup.w="add(5, 10)"> <br>

<!-- 组合快捷键 -->
Ctrl + Enter <input type="text" @keyup.ctrl.enter="add(40, 60)"> <br>
Ctrl + A <input type="text" @keyup.ctrl.a="add(20, 30)">
createApp({
    setup() {
        const web = reactive({
            title: "邓瑞编程",
            url: "dengruicode.com",
            user: 0
        })

        const edit = () => {
            web.url = "www.dengruicode.com"
            //msg = "邓瑞编程" //错误示例 不能直接改变msg的值,因为msg是一个普通变量, 不是响应式数据
        }

        const add = (a, b) => {
            web.user += a + b
        }

        const sub = (a, b) => {
            return a - b
        }

        return {
            msg: "success", //普通变量, 非响应式数据, 在模板中普通变量不会自动更新
            web, //响应式数据
            edit, //方法
            add,
            sub,
        }
    }
}).mount("#app")

4.v-show(显示隐藏)

当v-show="对象",这个对象为true时就渲染显示该部分,为false就不显示

<div id="app">
    <h3>{{ web.show }}</h3>
    <p v-show="web.show">邓瑞编程 dengruicode.com</p>

    <button @click="toggle">点击切换显示状态</button>
</div>
/*
v-show 通过 css display属性 来控制元素的显示或隐藏
v-show 适用于频繁切换元素的显示状态, 因为只改变 display 属性, 不需要重新渲染整个组件
*/

createApp({
    setup() {
        const web = reactive({
            show: true
        })

        const toggle = () => {
            web.show = !web.show
        }

        return {
            web,
            toggle
        }
    }
}).mount("#app")

5.v-if、v-else、v-else-if(条件渲染)

<div id="app">
    <h3>{{ web.show }}</h3>
    <p v-show="web.show">邓瑞编程</p>
    <p v-if="web.show">dengruicode.com</p>

    <button @click="toggle">点击切换显示状态</button>

    <p v-if="web.user < 1000">新网站</p>
    <p v-else-if="web.user >= 1000 && web.user < 10000">优秀网站</p>
    <p v-else-if="web.user >= 10000 && web.user < 100000">资深网站</p>
    <p v-else>超级网站</p>
</div>
/*
        v-if 用于对元素进行条件渲染. 当条件为 true 时, 渲染该元素, 为 false 时, 则不渲染
        v-if 适用于较少改变的场景, 因为频繁从 dom 中删除或添加元素, 会导致性能下降
    */
createApp({
    setup() {
        const web = reactive({
            show: true,
            user: 500
        })

        const toggle = () => {
            web.show = !web.show
        }

        return {
            web,
            toggle
        }
    }
}).mount("#app")

6.v-bind(动态绑定)

v-bind:value(或者src、class等等)="对象", 绑定数据对象后,当数据对象改变,绑定的内容也会进行改变渲染
单向绑定只是绑定对象数据,当修改对象后,不会对其他绑定了该对象的数据进行修改渲染

<div id="app">
    <!--静态值-->
    <h3>value="dengruicode.com"</h3>
    <input type="text" value="dengruicode.com">

    <!--单向绑定:value-->
    <h3>v-bind:value="web.url"</h3>
    <input type="text" v-bind:value="web.url">

    <h3>简写 :value="web.url"</h3>
    <input type="text" :value="web.url">

    <!-- :src -->
    <h3>src="windows.jpg"</h3>
    <img src="windows.jpg">

    <h3>:src="web.img"</h3>
    <img :src="web.img">

    <!-- :class -->
    <h3>class="textColor"</h3>
    <b class="textColor">邓瑞编程</b>

    <!-- :class="{ 类名: 条件表达式 }" 当条件表达式
    为真,<b class="textColor">dengruicode.com</b> 
    为假,<b>dengruicode.com</b>-->
    <h3>:class="{textColor:web.fontStatus}"</h3>
    <b :class="{textColor:web.fontStatus}">dengruicode.com</b>
</div>
createApp({
    setup() {
        const web = reactive({
            url: "www.dengruicode.com",
            img: "windows.jpg",
            fontStatus: true
        })

        return {
            web
        }
    }
}).mount("#app")

7.v-for(遍历数组对象)

v-for="参数" in 对象数据

<div id="app">
    <ul>
        <li v-for="(value, index) in data.number">
            index=> {{ index }} : value=> {{ value }}
        </li>
    </ul>

    <ul>
        <li v-for="value in data.user">
            value=> {{ value }}
        </li>
    </ul>

    <ul>
        <li v-for="(value, key) in data.user">
            key=> {{ key }} : value=> {{ value }}
        </li>
    </ul>

    <ul>
        <li v-for="(value, key, index) in data.user">
            index=> {{ index }} : key=> {{ key }} : value=> {{ value }}
        </li>
    </ul>

    <ul>
        <!-- <template> 标签可以用来包装多个元素或者多行代码, 不会在页面中渲染  -->
        <template v-for="(value, key, index) in data.user">
            <li v-if="index == 1">
                index=> {{ index }} : key=> {{ key }} : value=> {{ value }}
            </li>
        </template>
    </ul>

    <ul>
        <!-- :key="value.id" 为 每个 li 元素设置一个唯一的 key 值 -->
        <li v-for="(value, index) in data.teacher" :title="value.name" :key="value.id">
            index=> {{ index }} : value.id=>{{ value.id }} value.name=>{{ value.name }} value.web=>{{ value.web }}
        </li>
    </ul>
</div>
createApp({
    setup() {
        const data = reactive({
            number: ["十", "十一", "十二"], //数组
            user: { //对象
                name: "Luna",
                gender: "女"
            },
            teacher: [ //包含两个对象的数组
                { id: 100, name: "邓瑞", web: "dengruicode.com" },
                { id: 101, name: "David", web: "www.dengruicode.com" }
            ]
        })

        return {
            data
        }
    }
}).mount("#app")

8.v-model(双向绑定)

双向绑定既对当前使用了v-model的 对象数据进行修改,也会对其他绑定了该对象的部分也进行修改渲染

<div id="app">
    <h3>文本框 {{ data.text }}</h3>
    <h3>单选框 {{ data.radio }}</h3>
    <h3>复选框 {{ data.checkbox }}</h3>
    <h3>记住密码 {{ data.remember }}</h3>
    <h3>下拉框 {{ data.select }}</h3>

    <!-- 单向数据绑定 当数据发生改变时, 视图会自动更新. 但用户手动更改 input 的值, 数据不会自动更新 -->
    单向数据绑定 <input type="text" :value="data.text">

    <hr>
    <!-- 
        双向数据绑定 当数据发生改变时, 视图会自动更新. 当用户手动更改 input 的值, 数据也会自动更新
        对于 <input type="text">, v-model 绑定的是 input 元素的 value 属性
     -->
    双向数据绑定 <input type="text" v-model="data.text">

    <hr>
    <!-- 
        单选框
        对于 <input type="radio">, v-model 绑定的是 input 元素的选中状态
     -->
    <input type="radio" v-model="data.radio" value="1">写作
    <input type="radio" v-model="data.radio" value="2">画画

    <hr>
    <!-- 
        复选框
        对于 <input type="checkbox">, v-model 绑定的是 input 元素的选中状态
     -->
    <input type="checkbox" v-model="data.checkbox" value="a">写作
    <input type="checkbox" v-model="data.checkbox" value="b">画画
    <input type="checkbox" v-model="data.checkbox" value="c">运动

    <hr>
    <!-- 记住密码 -->
    <input type="checkbox" v-model="data.remember">记住密码

    <hr>
    <!-- 
        下拉框
        对于 <select>, v-model 绑定的是 select 元素的选中状态
     -->
    <select v-model="data.select">
        <option value="">请选择</option>
        <option value="A">写作</option>
        <option value="B">画画</option>
        <option value="C">运动</option>
    </select>
</div>
createApp({
    setup() {
        const data = reactive({
            text: "dengruicode.com", //文本框
            radio: "", //单选框
            checkbox: [], //复选框
            remember: false, //单个复选框-记住密码
            select: "" //下拉框
        })

        return {
            data
        }
    }
}).mount("#app")    

9.v-model修饰符

<div id="app">
    <h3>url: {{ web.url }}</h3>
    <h3>user: {{ web.user }}</h3>

    实时渲染 <input type="text" v-model="web.url"> <br>

    在失去焦点或按下回车键之后渲染 <input type="text" v-model.lazy="web.url"> <br>

    <!-- 输入 100人, web.user 的值仍为 100 -->
    输入框的值转换为数字类型 <input type="text" v-model.number="web.user"> <br>

    去除首尾空格 <input type="text" v-model.trim="web.url">
</div>
createApp({
    setup() {
        const web = reactive({
            url: "dengruicode.com",
            user: 10
        })

        return {
            web
        }
    }
}).mount("#app")

10.v-text、v-html(渲染数据)

<div id="app">
    <h3>{{ web.title }}</h3>

    <!-- v-text 将数据解析为纯文本格式 -->
    <h3 v-text="web.title"></h3>

    <!-- v-html 将数据解析为 html 格式 -->
    <h3 v-html="web.url"></h3>
</div>
createApp({
    setup() {
        const web = reactive({
            title: "邓瑞编程",
            url:"<i style='color:blue;'>www.dengruicode.com</i>"
        })

        return {
            web
        }
    }
}).mount("#app")

11.computed(计算属性)

computed的计算是有缓存的,当有执行过一次后,再次计算,会调用之前的缓存

<div id="app">
    <h3>add: {{ add() }}</h3>
    <h3>add: {{ add() }}</h3>

    <h3>sum: {{ sum }}</h3>
    <h3>sum: {{ sum }}</h3>

    x <input type="text" v-model.number="data.x"> <br>
    y <input type="text" v-model.number="data.y">
</div>
import { computed } from './vue.esm-browser.js'
createApp({
    setup() {
        const data = reactive({
            x: 10,
            y: 20
        })

        //方法-无缓存
        let add = () => {
            console.log("add") //打印两次
            return data.x + data.y
        }

        //计算属性-有缓存 [计算属性根据其依赖的响应式数据变化而重新计算]
        const sum = computed(() => {
            console.log("sum") //打印一次
            return data.x + data.y
        })

        return {
            data,
            sum,
            add
        }
    }
}).mount("#app")

12.监听器watch

<div id="app">
    爱好
    <select v-model="hobby">
        <option value="">请选择</option>
        <option value="1">写作</option>
        <option value="2">画画</option>
        <option value="3">运动</option>
    </select>

    <hr>

    年
    <select v-model="date.year">
        <option value="">请选择</option>
        <option value="2023">2023</option>
        <option value="2024">2024</option>
        <option value="2025">2025</option>
    </select>

    月
    <select v-model="date.month">
        <option value="">请选择</option>
        <option value="10">10</option>
        <option value="11">11</option>
        <option value="12">12</option>
    </select>
</div>
import { watch } from './vue.esm-browser.js'
createApp({
    setup() {
        const hobby = ref("") //爱好
        const date = reactive({ //日期
            year: "2023",
            month: "10"
        })

        //监听 hobby
        watch(hobby, (newValue, oldValue) => {
            console.log("oldValue", oldValue, "newValue", newValue)

            if (newValue == "2") {
                console.log("画画")
            }
        })

        //监听 date
        watch(date, (newValue, oldValue) => {
            /*
                    JS中对象和数组是通过引用传递的, 而不是通过值传递
                    当修改对象或数组的值时, 实际上修改的是对象或数组的引用, 而不是创建一个新的对象或数组
                    所以,如果修改了对象或数组的值,那么打印出来的结果则是修改后的值
                */
            console.log("oldValue", oldValue, "newValue", newValue)

            if (newValue.year == "2025") {
                console.log("2025")
            }

            if (newValue.month == "11") {
                console.log("11")
            }
        })

        //监听 date 中的某个属性 year
        watch(() => date.year, (newValue, oldValue) => {
            console.log("oldValue", oldValue, "newValue", newValue)

            if (date.year == "2024") {
                console.log("2024")
            }
        })

        return {
            hobby,
            date
        }
    }
}).mount("#app")

13.自动监听器watchEffect

<div id="app">
    爱好
    <select v-model="hobby">
        <option value="">请选择</option>
        <option value="1">写作</option>
        <option value="2">画画</option>
        <option value="3">运动</option>
    </select>

    <hr>

    年
    <select v-model="date.year">
        <option value="">请选择</option>
        <option value="2023">2023</option>
        <option value="2024">2024</option>
        <option value="2025">2025</option>
    </select>

    月
    <select v-model="date.month">
        <option value="">请选择</option>
        <option value="10">10</option>
        <option value="11">11</option>
        <option value="12">12</option>
    </select>
</div>
/*
        watch需要显式指定要监听的属性, 并且只有当监听的属性发生变化时才会执行
        若需要更精细地控制或需要获取到原值, 需要使用watch
    */
import { watchEffect } from './vue.esm-browser.js'

createApp({
    setup() {
        const hobby = ref("") //爱好
        const date = reactive({ //日期
            year: "2023",
            month: "10"
        })

        //自动监听
        watchEffect(() => {
            console.log("------ 监听开始")

            if (hobby.value == "2") {
                console.log("画画")
            }

            if (date.year == "2025") {
                console.log("2025")
            }

            if (date.month == "11") {
                console.log("11")
            }

            console.log("------ 监听结束")
        })

        return {
            hobby,
            date
        }
    }
}).mount("#app")

14.axios使用

导入:axios.min.js, 例如:

<script src="js/axios.min.js"></script>
//get请求
axios.get(`http://127.0.0.1/article/get/id/${id}`).then(response => {
    console.log("get.data:", response.data)

    if(response.data.status == "success"){
        if(response.data.data){
            data.list.push(response.data.data) //push 向数组末尾添加一个或多个元素
        }
    }
}).catch(error => {
    console.log("get.error:", error)
})

//post请求 [axios post的默认请求头是 application/json]
axios.post('http://127.0.0.1/article/postJson/search', param).then(response => {
    console.log("postJson.data:", response.data)

    if(response.data.status == "success"){
        for(let i=0; i<response.data.data.length; i++){
            data.list.push(response.data.data[i]) //push 向数组末尾添加一个或多个元素
        }
    }
}).catch(error => {
    console.log("postJson.error:", error)
})

15.使用Vite创建vue3项目

(1)npm create vite@latest

(2)选择y

(3)project name:要创建的项目名

(4)选择vue

(5) Done. Now run:

  • ​ cd demo
  • ​ npm install
  • ​ npm run dev

​ 删除文件

​ src\style.css

​ src\components\HelloWorld.vue

​ 删除代码

​ main.js

​ import './style.css'

16.使用vue(官方)创建vue3项目

下面把 create-vue 交互里每一个「Yes/No」选项拆开讲:

  • 选了 Yes 之后到底会 额外 给你什么文件 / 依赖 / 配置;
  • 这些配置在真实开发里到底解决什么问题。

你可以把它当成一份「勾选清单说明书」。

npm create vue@latest

──────────────────────────

  1. Project name
    • 作用:仅决定文件夹名,与 npm 包名一致(package.json 里的 "name" 字段)。
    • 额外产物:一个同名目录,无其他配置差异。

──────────────────────────

  1. Add TypeScript → Yes
    额外给你:
    • 文件后缀改成 .ts/.vue 里 <script setup lang="ts">
    • tsconfig.json、tsconfig.node.json(TypeScript 编译选项)
    • env.d.ts(让 TS 识别 *.vue、vite/client 类型)
    • 依赖:typescript、vue-tsc(build 时做类型检查)

开发收益:
静态类型检查、IDE 自动补全、重构更安心。

──────────────────────────

  1. JSX Support → Yes
    额外给你:
    • vite.config.ts 里自动加 jsx 插件:
    import vueJsx from '@vitejs/plugin-vue-jsx'
    plugins: [vue(), vueJsx()]

• 示例文件:src/components/HelloWorld.jsx / tsx
• 依赖:@vitejs/plugin-vue-jsx

开发收益:
可在 .jsx/.tsx 或 <script setup lang="tsx"> 里写 render 函数 / 高阶组件。

──────────────────────────

  1. Vue Router → Yes
    额外给你:
    • src/router/index.ts(路由表、history 模式)
    • src/views/ 目录 + HomeView.vue / AboutView.vue 示例
    • main.ts 里 app.use(router)
    • 依赖:vue-router@4

开发收益:
SPA 多页面切换、路由守卫、动态路由。

──────────────────────────

  1. Pinia → Yes
    额外给你:
    • src/stores/ 目录 + counter.ts 示例(defineStore)
    • main.ts 里 app.use(createPinia())
    • 依赖:pinia

开发收益:
全局状态管理,替代 Vuex,DevTools 支持热更新。

──────────────────────────

  1. Vitest → Yes(单元测试)
    额外给你:
    • vitest.config.ts(测试专用 Vite 配置)
    • src/components/__tests__/HelloWorld.spec.ts 示例
    • package.json script:"test:unit": "vitest"
    • 依赖:vitest、jsdom、@vue/test-utils

开发收益:
单文件组件也能跑 Jest-like 测试,速度比 Jest + vue-jest 快很多。

──────────────────────────

  1. Cypress → Yes(端到端测试)
    额外给你:
    • cypress/ 目录
    ├─ e2e/
    │ └─ example.cy.ts(访问首页断言标题存在)
    ├─ fixtures/
    ├─ support/
    • package.json script:"test:e2e": "cypress open"
    • 依赖:cypress

开发收益:
在真实浏览器里跑完整用户流程(登录、跳转、交互),防止集成缺陷。

──────────────────────────

  1. ESLint → Yes
    额外给你:
    • .eslintrc.cjs(extends: ["@vue/eslint-config-typescript/recommended", "@vue/eslint-config-prettier"])
    • package.json script:"lint": "eslint . --ext .vue,.js,.ts"
    • 依赖:eslint、@vue/eslint-config-* 系列

开发收益:
自动发现语法错误、风格违规;配合 IDE 红线提示 & 保存即修复。

──────────────────────────

  1. Prettier → Yes
    额外给你:
    • .prettierrc.json(printWidth、semi、singleQuote 等)
    • .vscode/extensions.json 推荐安装 Prettier 插件
    • 依赖:prettier

开发收益:
一键统一缩进、换行、引号风格,避免团队因格式争吵。

──────────────────────────

  1. DevTools / VS Code 建议
    create-vue 还会根据你的选择自动生成:
    • .vscode/extensions.json(推荐 Volar、ESLint、Prettier)
    • .vscode/settings.json(保存时自动格式化、ESLint 自动修复)

这些不算 Yes/No 选项,但会在你选了 ESLint/Prettier/TS 后自动出现。

──────────────────────────
一句话速记
TS / JSX 决定语言特性;Router / Pinia 决定架构;Vitest / Cypress 决定测试策略;ESLint / Prettier 决定代码质量。
全部选 Yes 你就拿到一个「可直接上线」的现代化 Vue3 工程模板;全部选 No 就是最小可运行示例。

17.区别

一句话区别
npm create vue@latest 是「官方 Vue 团队做的、只给 Vue3 用的『应用级』脚手架」;
npm create vite@latest 是「Vite 官方做的、面向所有框架的『通用』脚手架」。

下面把两条命令在 6 个维度拆开对比,你就能知道什么时候该用哪一个。

────────────────────────────

  1. 作者 & 维护者
    • create-vue:Vue 核心团队(Evan You、三咲智子等人)维护,发布节奏跟 Vue 版本走。
    • create-vite:Vite 核心团队(patak 等人)维护,发布节奏跟 Vite 版本走。

────────────────────────────

  1. 适用范围
    • create-vue:只生成 Vue3 项目。
    • create-vite:可选 Vanilla、Vue、React、Preact、Svelte、Solid、Lit、Qwik、Alpine……
    也就是说,用 create-vite 也可以选 Vue,但它不会给你 Pinia、Router、ESLint、Prettier 等「Vue 全家桶」预设。

────────────────────────────

  1. 交互式提问
    • create-vue:
    ‑ Project name
    ‑ TypeScript?
    ‑ JSX?
    ‑ Vue Router?
    ‑ Pinia?
    ‑ Vitest?
    ‑ Cypress?
    ‑ ESLint?
    ‑ Prettier?
    一口气帮你把“一个真实业务项目”需要的配置都问完。

​ • create-vite:

      ‑ Project name  
      ‑ Framework?  
      ‑ Variant?(如 vue-ts、vue、react-ts、react-swc-ts …)  
      只问框架和语言变体,不会问 Router、状态管理、测试、Lint。

────────────────────────────

  1. 生成的目录/文件差异
    • create-vue 会额外给你:
    src/router/、src/stores/、.eslintrc.cjs、.prettierrc.json、cypress/、vitest.config.ts、.vscode/extensions.json、README.md(带 VueLogo)。

​ • create-vite(选 vue-ts)只会给你最简的:

      src/main.ts、src/App.vue、src/style.css、vite.config.ts、index.html、tsconfig.json —— 没有 Router、没有         Pinia、没有测试、没有 ESLint。

────────────────────────────

  1. 默认依赖
    • create-vue:
    "dependencies": { vue, vue-router?, pinia? }
    "devDependencies": { vite, @vitejs/plugin-vue, eslint, prettier, vitest, cypress }

​ • create-vite:
​ "dependencies": { vue }

      "devDependencies": { vite, @vitejs/plugin-vue }

────────────────────────────

  1. 适用场景
    • 想一步到位得到「可直接写业务」的 Vue3 SPA → 用 npm create vue@latest。
    • 想从零开始、自己决定要不要 Router / Pinia / ESLint,或想搭非 Vue 项目 → 用 npm create vite@latest。
    • 想学习 Vite 配置、做最小复现 Demo → 用 create-vite 选 Vanilla 或对应框架最简模板最快。

────────────────────────────
一句话总结
create-vue = create-vite 的「Vue 全家桶特供版」。
前者给你「拎包入住」的 Vue3 应用,后者给你「毛坯房」让你自由装修。

  1. cd demo
    进入刚生成的项目目录(你之前把项目起名叫 demo)。
  2. npm install
    安装 package.json 里列出的所有依赖(vite、vue、router、pinia、eslint…)。这一步会把 node_modules 拉下来,项目才能真正跑起来。
  3. npm run format
    运行 Prettier 一键格式化所有代码(src、tests、config 等)。脚手架默认把 Prettier 规则配好了,执行后代码风格立刻统一。
  4. npm run dev
    启动 Vite 开发服务器,默认打开 http://localhost:5173,热更新实时预览页面。
  5. git init && git add -A && git commit -m "initial commit"(可选)
    在项目里初始化 Git 仓库 → 把所有文件加入暂存区 → 提交第一条提交记录。完成后你就可以正常用 git 做版本管理了。

18.组件

<script setup>
import ButtonCounter from './ButtonCounter.vue'
</script>

<template>
  <h1>Here is a child component!</h1>
  <ButtonCounter />
</template>

defineProps

defineProps是Vue3的编译时宏函数,用于接收父组件向子组件传递的属性(props) 注: 当使用Vue编译器编译包含defineProps的组件时,编译器会将这些宏替换为相应的运行时代码

例如:子组件

const props = defineProps(["propsName","propsUrl"])

父组件:

<!-- 父传子 - 方式1 -->
<Header propsName="邓瑞编程" propsUrl="dengruicode.com" />

子组件:

//接收方式2 - 对象
    /*
    const props = defineProps({
        user: Number,
        ip: String
    })
    */
    const props = defineProps({
        user: Number,
        ip: {
            type: String,
            required: true, //true表示必传属性,若未传则会提示警告信息
            default: 'localhost' //未传默认值
        }
    })

父组件:

//响应式数据
  const propsWeb = reactive({
    user: 10,
    ip: '127.0.0.1'
  })
<!-- 父传子 - 方式2 -->
<!-- <Footer v-bind="propsWeb" /> -->
<Footer :="propsWeb" />

defineEmits

defineEmits是Vue3的编译时宏函数,用于子组件向父组件发送自定义事件
例如:

子组件:

<script setup>
//子传父
    //定义一个名为 emits 的对象, 用于存储自定义事件
    const emits = defineEmits(["web","user"])
    //发送名为 web的自定义事件
    emits("web", {name:"邓瑞",url:"www.dengruicode.com"})
    
    //添加用户
    const userAdd = () => {
        //发送名为 user 的自定义事件
        emits("user", 10)
    }
</script>

<template>
    <h3>Header</h3>

    <button @click="userAdd">添加用户</button>
</template>

父组件:

<script setup>
//响应式数据
  const web = reactive({
    name: "邓瑞编程",
    url: 'dengruicode.com'
  })

  const user = ref(0)

  //子传父
  const emitsWeb = (data) => {
    console.log("emitsWeb:",data)
    web.url = data.url
  }

  const emitsUser = (data) => {
    console.log("emitsUser:",data)
    user.value += data
  }
</script>

<template>
  <!-- 子传父 -->
  <Header @web="emitsWeb" @user="emitsUser" />

  {{ web.url }} - {{ user }}
</template>

slot

插槽

<template>
  <div class="alert-box">
    <strong>Error!</strong>
    <br/>
    <slot />
  </div>
</template>
<AlertBox>
  Something bad happened.
</AlertBox>

使用 <slot> 作为一个占位符,父组件传递进来的内容就会渲染在这里

img1
img1

19.axios

下面给出一份「axios 在 2025 年 Vue 项目中的常用套路清单」——从安装、全局配置到各种业务场景,全部带可复制代码块。直接按场景 Ctrl + C / Ctrl + V 即可。

────────────────────

  1. 安装 & 全局挂载
    ────────────────────
npm i axios
Vue3 官方不再推荐把 axios 挂到 app.config.globalProperties,而是独立文件导出实例,方便 Tree-Shaking 和类型推导。

────────────────────

  1. 创建统一实例(src/utils/request.js)
    ────────────────────
import axios from 'axios'
import { ElMessage } from 'element-plus'   // UI 提示库示例

const request = axios.create({
  baseURL: import.meta.env.VITE_API_BASE,  // 读取 .env 里的域名
  timeout: 10000,                          // 10 秒超时
  headers: { 'Content-Type': 'application/json' }
})

// 请求拦截器:统一携带 token
request.interceptors.request.use(
  config => {
    const token = localStorage.getItem('token')
    if (token) config.headers.Authorization = `Bearer ${token}`
    return config
  },
  error => Promise.reject(error)
)

// 响应拦截器:统一错误提示 + 数据剥离
request.interceptors.response.use(
  response => response.data,               // 直接返回业务数据
  error => {
    const msg = error.response?.data?.message || '网络错误'
    ElMessage.error(msg)
    return Promise.reject(error)           // 继续抛出,让组件 catch
  }
)

export default request

────────────────────

  1. 在组件/Store 里四种常见调用
    ────────────────────
    ① 查询(GET)
import request from '@/utils/request'

// 无参数
const list = await request.get('/articles')

// 带查询参数 → ?page=1&size=10
const list2 = await request.get('/articles', { params: { page: 1, size: 10 } })

② 提交(POST)

await request.post('/login', { username, password })

③ 上传文件(POST + FormData)

const fd = new FormData()
fd.append('file', file.raw)
const { url } = await request.post('/upload', fd, {
  headers: { 'Content-Type': 'multipart/form-data' }
})

④ 并发请求(等待全部完成)

const [user, order] = await Promise.all([
  request.get('/user/1'),
  request.get('/order/100')
])

────────────────────

  1. 统一错误码处理(可选)
    ────────────────────
    在响应拦截器里加 switch:
switch (error.response?.status) {
  case 401:
    router.push('/login')
    break
  case 403:
    ElMessage.warning('无权限')
    break
  case 500:
    ElMessage.error('服务器异常')
    break
}

────────────────────

  1. TypeScript 类型提示(src/types/request.d.ts)
    ────────────────────
declare module '@/utils/request' {
  import { AxiosInstance } from 'axios'
  const request: AxiosInstance
  export default request
}

────────────────────

  1. 常见业务场景速查
    ────────────────────
场景一行代码示例
下载文件await request({ url: '/download', method: 'get', responseType: 'blob' })
带进度条上传onUploadProgress: e => setProgress(Math.round(e.loaded * 100 / e.total))
取消请求const source = axios.CancelToken.source(); source.cancel()
微前端子应用baseURL 设成 window.__POWERED_BY_QIANKUN__ ? '/child-api' : '/api'

────────────────────

  1. 一句话总结
  • axios 负责「发请求 + 拦截器 + 错误处理」。
  • 在 Vue 中写成「统一实例 + async/await」即可覆盖 99 % 场景。
Vue学习
https://yufornote.xyz/index.php/archives/9/
本文作者 yu
发布时间 2025-08-12
许可协议 CC BY-NC-SA 4.0
发表新评论