Vue CLI

Vue CLI 概述

Vue CLI 是一个官方提供的基于 Vue.js 进行快速开发的脚手架工具,用于快速搭建基于 Vue.js 的前端项目。

Vue CLI 文档:https://cli.vuejs.org/

Vue CLI 的特点:

  • 内置了 webpack 打包工具,可以快速构建项目的基本结构和配置。
  • 支持自定义配置,包括 webpack 配置、开发环境和生产环境配置、环境变量等。
  • 支持插件机制,可以集成其他框架和库,提高开发效率。
  • 支持命令行工具,可以快速生成组件、页面、路由等。

使用 Vue CLI,需要全局安装 @vue/cli。


示例:全局安装 @vue/cli

终端执行:

‍# 安装vue-cli
npm install @vue/cli -g

# 查看vue-cli版本信息
vue -V

示例效果:


使用 Vue CLI 创建项目

Vue CLI 引入了图形用户界面(GUI)来创建和管理项目。通过 Vue CLI GUI,可以快速创建 Vue CLI 项目,快速地为项目安装一些插件和依赖。


示例:使用 GUI 创建项目

终端执行:

# 运行GUI
vue ui

示例效果:

在浏览器中打开 GUI:

在 GUI 中创建项目,手动配置项目,注意选择插件和 Vue.js 版本:

项目创建完成后,可以在 GUI 中对项目和插件进行管理:

使用 IDEA 打开创建好的 Vue 脚手架项目:

打开 package.json,执行 serve 指令,运行 Vue 脚手架项目:

示例效果:


Vue CLI 项目结构

使用 Vue CLI 创建的项目:

文件/文件夹结构:

名称描述
node_modules通过 npm 下载的项目中使用的依赖包
public包含 index.html 文件,是项目的入口页面
该文件夹可以存放静态资源,静态资源不会被 webpack 压缩
src包含项目的源代码
src/assets存放静态资源,如图片、样式表等
该文件夹存放的静态资源会被 webpack 压缩
src/components存放 Vue 普通组件
src/App.vue所有组件的根组件
src/main.js项目的入口 JavaScript 文件
全局的配置和初始化设置在这里执行
package.jsonnpm 配置文件
vue.config.jsVue CLI 配置文件,可以配置 Vue CLI 选项
如 webpack 配置、开发服务器设置等

src/main.js:项目的入口 JavaScript 文件。

// 导入相关内容
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

// 实例化Vue实例
// 渲染APP实例,并挂载到index.html的#app上
new Vue({
    render: h => h(App)
}).$mount('#app')

App.vue:所有组件的根组件。

<template>
    <div id="app">
        <img alt="Vue logo" src="./assets/logo.png">
        <!-- 调用组件 -->
        <HelloWorld msg="Welcome to Your Vue.js App"/>
    </div>
</template>

<script>
// 导入组件
import HelloWorld from './components/HelloWorld.vue'

export default {
    name: 'App',
    // 注册局部组件
    components: {
        HelloWorld
    }
}
</script>

<style>
#app {
    font-family: Avenir, Helvetica, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-align: center;
    color: #2c3e50;
    margin-top: 60px;
}
</style>

*.vue 文件:Vue 单文件组件。每一个 Vue 组件都包含 script、template、style。

<script>
// export default对外抛出组件,方便其他位置调用
export default {
    // 组件配置
}
</script>

<template>
    <!-- 组件模板,有且只能有一个根标签 -->
</template>

<style scoped>
<!--
    组件的CSS样式
    scoped:样式只针对当前组件有效
-->
</style>

package.json:npm 配置文件。

{
    "name": "vuecli-demo",
    "version": "0.1.0",
    "private": true,
    "scripts": {
        "serve": "vue-cli-service serve",
        "build": "vue-cli-service build"
    },
    "dependencies": {
        "core-js": "^3.8.3",
        "vue": "^2.6.14"
    },
    "devDependencies": {
        "@vue/cli-plugin-babel": "~5.0.0",
        "@vue/cli-service": "~5.0.0",
        "vue-template-compiler": "^2.6.14"
    },
    "browserslist": [
        "> 1%",
        "last 2 versions",
        "not dead"
    ]
}

vue.config.js:Vue CLI 配置文件。配置文档:https://cli.vuejs.org/zh/config/

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
    transpileDependencies: true,
    devServer: {        // 配置开发环境
        port: 8080,     // 端口号
    }
})

Vue 组件的属性和方法

data()

data() 函数用于定义组件中的数据,函数内返回一个数据对象。

  • 在组件模板中通过插值表达式 {{属性名 xxx}} 访问数据。
  • 在 JavaScript 中可以通过 this.$data.xxx 的方式访问数据。
  • Vue 实例代理了 data 对象,可以通过 this.xxx 的方式访问数据。

示例:data()

根组件(src/App.vue):

<template>
    <div>
        <h3>title:{{title}}</h3>
        <h3>user.name:{{user.name}}</h3>
    </div>
</template>

<script>
export default {
    name: 'App',
    data() {
        // 定义组件数据,返回数据对象
        return {
            title: 'Hello Vue CLI!',
            user: {
                name: 'duozai',
                age: 26
            }
        }
    }
}
</script>

示例效果:


methods

methods 属性用于定义组件中的方法。

  • 定义在 methods 属性中的方法可以作为页面中的事件处理方法使用,如按钮点击事件等。
  • 在定义的方法中,this 指向 Vue 实例本身。

示例:methods

根组件(src/App.vue):

<template>
    <div>
        <!-- ... -->
        <h3>user.age:{{user.age}}</h3>
        <button @click="updateAge">user.age++</button>
    </div>
</template>

<script>
export default {
    // ...
    methods: {
        updateAge() {
            this.user.age++
        }
    }
}
</script>

示例效果:


computed

computed 属性用于定义组件中的计算属性。

当有一些数据需要随着其他数据变动而变动时,可以使用 computed 计算属性。

  • 计算属性结果会被缓存起来。
  • 计算属性依赖的属性发生变化时,会重新计算计算属性的值。
  • 在组件模板中中直接通过插值表达式 {{属性名 xxx}} 的方式显示计算属性的数据。
  • 计算属性适合于简单的数据转换和同步计算。

示例:computed

根组件(src/App.vue):

<template>
    <div>
        <!-- ... -->
        <h3>user.age:{{user.age}}</h3>
        <h3>user.tag:{{userTag}}</h3>
        <button @click="updateAge">user.age++</button>
    </div>
</template>

<script>
export default {
    // ...
    computed: {
        userTag() {
            if(this.user.age <= 30) {
                return "青年"
            } else if(this.user.age <= 50) {
                return "中年"
            } else if(this.user.age > 50) {
                return "老年"
            }
        }
    }
}
</script>

示例效果:


watch

watch 属性用于定义组件中的状态监听。

watch 状态监听可以监听当前组件中的数据变化,调用当前数据所绑定的事件处理方法。

  • 状态监听是执行代码响应数据变化的,不返回值。
  • 状态监听不进行缓存,每当监听的数据变化时总是会执行。
  • 状态监听适合执行数据变化时的异步操作或较为复杂的数据处理。

示例:watch

根组件(src/App.vue):

<template>
    <div>
        <h3>count:{{count}}</h3>
        <button @click="updateCount">count++</button>
    </div>
</template>

<script>
export default {
    data() {
        return {
            count: 0
        }
    },
    methods: {
        updateCount() {
            this.count++
        }
    },
    watch: {
        count(newVal, oldVal) {
            console.log(newVal, oldVal)
        }
    }
}
</script>

示例效果:


生命周期钩子函数

添加自己的代码的机会。

组件生命周期对应的钩子函数:

名称描述
beforeCreate组件创建前执行,此时无法访问组件中的数据和方法
created组件创建后执行,此时可以访问组件中的数据和方法
一般在这个生命周期中发送异步请求
beforeMount组件挂载成功前执行,此时组件 DOM 未编译
mounted组件挂载成功后执行,此时组件 DOM 已编译
beforeUpdate组件更新之前执行,此时数据是新的,页面是旧的
updated组件更新之后执行,此时数据和页面都是新的
beforeDestory组件销毁之前执行
destoryed组件销毁之后执行

Vue 内置指令

v-model

v-model 指令用于实现双向数据绑定,常用于表单元素上。

双向数据绑定:页面中的数据改变时 data 中的数据会改变,data 中的数据改变时页面中的数据也会改变,双向数据绑定是数据驱动视图的结果。

v-model 的基本用法:

<input v-model="xxx">

示例:v-model

根组件(src/App.vue):

<template>
    <div>
        <div>userName:<input v-model="userName"></div>
    <div>userName:{{userName}}</div>
    </div>
</template>

<script>
export default {
    name: 'App',
    data() {
        return {
            userName: 'duozai'
        }
    }
}
</script>

示例效果:


v-bind/:xxx

v-bind 指令用于实现单向数据绑定,常用于绑定 HTML 元素属性。

单向数据绑定:页面中的数据改变时 data 中的数据不会改变,data 中的数据改变时页面中数据会改变。

v-bind 的基本用法:

<div v-bind:元素属性="xxx"></div>
<div :元素属性="xxx"></div>

示例:v-bind

根组件(src/App.vue):

<template>
    <div>
        <!-- ... -->
        <div :id="userName">userName:{{userName}}</div>
    </div>
</template>

<script>
export default {
    // ...
}
</script>

示例效果:


v-on/@xxx

v-on 指令是事件监听指令,直接与标签的事件类型(如 click、change 事件等)配合使用,可以在触发事件时运行一些 JavaScript 代码,或绑定事件处理方法。

v-on 的基本用法:

<button v-on:事件类型="xxx"></button>
<button @事件类型="xxx"></button>

示例:v-on

根组件(src/App.vue):

<template>
    <div>
        <!-- ... -->
        <button @click="btnClick">click</button>
    </div>
</template>

<script>
export default {
    // ...
    methods: {
        btnClick() {
            alert('button click!')
        }
    }
}
</script>

示例效果:


v-text 和 v-html

v-text 指令用于在元素内部插入文本内容。

v-html 指令用于在元素内部插入 HTML 标签内容。

v-text 和 v-html 的基本用法:

<p v-text="xxx"></p>

<p v-html="xxx"></p>

示例:v-text 和 v-html

根组件(src/App.vue):

<template>
    <div>
        <!-- ... -->
        <div v-text="userName"></div>
        <div v-html="userName"></div>
    </div>
</template>

<script>
export default {
    name: 'App',
    data() {
        return {
            userName: '<h3>duozai</h3>'
        }
    },
    // ...
}
</script>

示例效果:


v-for

v-for 指令用于实现页面列表渲染,常用来循环数组。

v-for 的基本用法:

<div v-for="(item, index) in list" :key="id"></div>

v-for 指令的基本参数:

名称描述
list要遍历的数据列表/数组
item遍历后的每一个元素的别名
index当前元素的索引值
key使用 v-for 时,需要为每项提供一个唯一的 key 属性
其主要用于 Vue 的 diff 算法,提高遍历效率
key 的值只能是字符串或数字类型,且必须具有唯一性
建议把数据项的主键 id 属性的值作为 key 的值

示例:v-for

根组件(src/App.vue):

<template>
    <div>
        <div v-for="(item, index) in userList" :key="item.id">
            {{index}} => {{item.name}}
        </div>
    </div>
</template>

<script>
export default {
    name: 'App',
    data() {
        return {
            userList: [
                { id: 1, name: '张三' },
                { id: 2, name: '李四' },
                { id: 3, name: '王五' },
                { id: 4, name: '赵六' },
                { id: 5, name: '陈七' }
            ]
        }
    }
}
</script>

示例效果:


v-if 和 v-show

v-if 指令用于控制元素显示隐藏,其会对元素进行删除和重新创建,性能较低。

v-show 指令用于控制元素显示隐藏,其本质是操作元素的 display 属性,不会对元素进行删除和重新创建,性能较高。

v-if 和 v-show 的基本用法:

<p v-if="条件"><p>
<p v-else></p>

<p v-show="条件"></p>

示例:v-if 和 v-show

根组件(src/App.vue):

<template>
    <div>
        <div>
            <h3 v-if="isHome">Home Page</h3>
            <h3 v-if="!isHome">Login Page</h3>
        </div>
        <div>
            <h3 v-show="isHome">Home Page</h3>
            <h3 v-show="!isHome">Login Page</h3>
        </div>
        <button @click="isHome=!isHome">toggle</button>
    </div>
</template>

<script>
export default {
    name: 'App',
    data() {
        return {
            isHome: true
        }
    }
}
</script>

示例效果:


Vue 组件系统

组件注册

在 Vue CLI 中,可以将普通组件放在 src/components 文件夹下,并使用局部注册或全局注册的方式注册组件。

局部注册的组件,仅在注册它的父组件及其子组件中可用,其他未注册的组件无法直接使用。


示例:局部注册组件

MyComponent 组件(src/components/MyComponent.vue):

<template>
    <div>
        <h3>hello,这是MyComponent组件</h3>
    </div>
</template>

根组件(src/App.vue):

<script>
// 导入MyComponent组件
// import 对象名称 from 路径
// 对象名称可以自定义
import MyComponent from "@/components/MyComponent.vue"

export default {
    name: 'App',
    // 局部注册组件
    components: {
        // 组件名称: 组件对象
        // 组件名称=组件对象时,可以缩写成MyComponent
        MyComponent: MyComponent
    }
}
</script>

<template>
    <div>
        <!-- 调用MyComponent组件 -->
        <myComponent></myComponent>
    </div>
</template>

示例效果:


全局注册的组件,在整个 Vue 应用的所有组件(包括子组件、孙组件等嵌套组件)中都可以直接使用,无需再次导入或注册。


示例:全局注册组件

项目入口文件(src/main.js):

// 导入MyComponent组件
// import 对象名称 from 路径
// 对象名称可以自定义
import MyComponent from "@/components/MyComponent.vue"

// 全局注册组件
// 参数1:组件名称
// 参数2:组件对象
Vue.component('MyComponent', MyComponent)

根组件(src/App.vue):

<template>
    <div>
        <!-- 调用MyComponent组件 -->
        <myComponent></myComponent>
    </div>
</template>

示例效果:


组件插槽

Vue 中的组件中使用 template 模板定义 HTML 结构,为了方便使用 template 公共模板结构,Vue 提出了插槽(Slots)的概念,插槽就是定义在组件内部的 template 模板。


示例:组件插槽

根组件(src/App.vue):

<template>
    <div>
        <myComponent>
            <!-- 调用组件时传递插槽模板 -->
            <span>多仔</span>
        </myComponent>
    </div>
</template>

MyComponent 组件(src/components/MyComponent.vue):

<template>
    <div>
        <!-- <slot> 相当于插槽模板的占位符 -->
        <h3>hello,<slot></slot></h3>
    </div>
</template>

示例效果:


当调用组件时配置了多个插槽,可以为插槽命名。


示例:具名插槽

根组件(src/App.vue):

<template>
    <div>
        <myComponent>
            <!--
                每一个插槽模板都是一个<template>标签
                使用v-slot指令来指定插槽名称
            -->
            <template v-slot:name>
                <span>多仔</span>
            </template>
            <template v-slot:age>
                <span>26</span>
            </template>
        </myComponent>

    <myComponent>
            <template v-slot:name>
                <span>少仔</span>
            </template>
            <template v-slot:age>
                <span>18</span>
            </template>
        </myComponent>
    </div>
</template>

MyComponent 组件(src/components/MyComponent.vue):

<template>
    <div>
        <!--
            <slot> 相当于插槽模板的占位符
            使用name属性指定插槽名称
        -->
        <h3>hello,<slot name="name"></slot></h3>
        <h3>我的年龄是<slot name="age"></slot></h3>
    </div>
</template>

示例效果:


组件通信

组件实例内部具有自己的独立作用域,不能直接被外部访问。

组件之间的数据传递需要借助一些工具来实现,且组件之间的数据传递是单向的。

父组件向子组件传递数据的基本思想:

  • 父组件中给子组件绑定属性。
  • 子组件内部通过 props 选项接收数据。

示例:父组件向子组件传递数据

根组件(src/App.vue):

<template>
    <div>
        <!-- 以属性的方式传递数据 -->
        <myComponent name="多仔" age="26"></myComponent>
        <myComponent name="少仔" age="18"></myComponent>
    </div>
</template>

子组件(src/components/MyComponent.vue):

<script>
export default {
    name: "MyComponent",
    // 使用props接收数据
    props: ['name', 'age'],
}
</script>

<template>
    <div>
        <h3>我的姓名是{{name}}</h3>
        <h3>我的年龄是{{age}}</h3>
    </div>
</template>

示例效果:


子组件向父组件传递数据的基本思想:

  • 父组件中给子组件标签通过 @ 绑定自定义事件。
  • 子组件内部通过 emit 方法触发事件。

示例:子组件向父组件传递数据

子组件(src/components/MyComponent.vue):

<script>
export default {
    name: "MyComponent",
    data() {
        return {
            msg: 'Hello World'
        }
    },
    methods: {
        sendMessage() {
            // 通过this.$emit触发事件,并传递参数
            // 参数1:父组件调用子组件时绑定的事件名称
            // 参数2:要传递的参数
            this.$emit('receive', this.msg)
        }
    }
}
</script>

<template>
    <div>
        请输入消息:<input v-model="msg">
        <button @click="sendMessage">发送</button>
    </div>
</template>

根组件(src/App.vue):

<template>
    <div>
        <!--
            父组件在调用子组件时绑定一个自定义事件,触发接收方法
            事件名称和子组件emit触发的事件名称一致
            this.$emit('receive', this.msg)
        -->
        <myComponent @receive="receive"></myComponent>
        <div>子组件传递给父组件的数据:{{msg}}</div>
    </div>
</template>

<script>
export default {
    name: 'App',
    data() {
        return {
            msg: ''
        }
    },
    methods: {
        // 接收子组件传递给父组件数据的方法
        // 参数1:子组件传递给父组件的数据
        receive(msg) {
            // 将子组件传递的数据保存给父组件
            this.msg = msg
        }
    }
}
</script>

示例效果: