# Vue 快速入门、基础组件、单文件组件、Vue 脚手架

TIP

从本节内容开始,正式进入 Vue 全家桶 从入门到实践的完整系统学习了,这也是我们当下迈向企业项目开发必备核心技能之一。

  • 学习 Vue 需要具备的基础知识有哪些
  • 为什么要学习 Vue 框架
  • Vue 需要学习哪些内容
  • Vue 简介
  • 创建一个 Vue 应用
  • Vue 组件基础
  • 单文件组件开发(SFC)
  • Vue 脚手架创建项目

Vue 官网文档:https://cn.vuejs.org/ (opens new window)

# 一、学习 Vue 前需要具备的基础知识

TIP

在学习 Vue 课程,你需要具备以下扎实的基础知识

  • HTML/HTML5 + CSS/CSS3 + Sass(Sass 了解更好,不了解也没关系)
  • 熟练掌握 JavaScript (基础知识、数组 API、对象、原型、原型链 ... 等)
  • ES6(基础语法、Promise、模块化 ... 等)
  • HTTP 与 Axios
  • Node 的包管理工具 npm
  • WebPack 基础知识

如果你具备了以上基础,那就可以正式的跟着我来学习 Vue 了 !

# 二、为什么需要 Vue 框架

TIP

在学习 Vue 前,我们必须要搞懂一个问题,那就是,为什么需要学习 Vue 框架 ?

难道没有 Vue 框架,仅凭我们前面学习过的 HTML5+CSS3+JS+HTTP 协议+Ajax,不能与后端开发配合,开发一个完整 Web 应用吗 ?

答案显然是可以的,那为什么我们还需要学习 Vue 框架呢 ?要解决这个疑问,我们就得先从 Web 前端技术的发展历程讲起。

# 1、Web 前端技术的发展

TIP

关于 Web 前端技术的发展,我们从 Web 应用开发的三个阶段来展开学习:

  • 前后端不分离
  • 前后端分离
  • 前端工程化

# 1.1、前后端不分离

TIP

早期(1995 年)以前,Web 应用主要是静态网页的浏览,技术主要以 HTML+CSS 为主。在 1995 年,网景公司的工程师 Brendan Eich(布兰登·艾奇) 用 10 天时间设计了 JavaScript,这个时候 JS 诞生了,随后的网页中开始诞生了动画特效。但是,Web 的应用主要还是静态网页的浏览,只是比之前多了些特效罢了。

再之后,随着后端技术的越来越成熟,用户的需求越来越复杂,Web 应用也变得越来越复杂,不再局限于只是显示静态的网页。更多的应用需要根据用户的请求动态生成页面信息,复杂一点的还需要从数据库中查询数据,经过一定的运算,生成一个 HTML 页面返回给用户。

在 2004 年以前,还没有前端工程师这一岗位,前端工作主要交由美工来负责。美工利用 HTML+CSS+JS 来编写静态网页,然后交由后端开发人员,后端开发人员利用服务端技术从服务器的数据库中把数据查出来,然后绑定到 HTML 页面中,实现根据用户的请求动态生成 HTML 页面信息。这个时候,浏览器拿到的是服务端渲染好的 HTML 页面,然后展示在浏览器端。

# 1.2、前后端不分离,带来的问题

TIP

  • 由于返回的 HTML 页面是后端渲染的,所以每一次的数据交互都需要刷新一次浏览器(全局刷新),频繁的刷新页面非常影响用户的体验。
  • 其次,站在开发成本的角度,前后端不分离,造成后端需要等前端的 HTML 页面开发好了后,才能开始后续的开发工作。同时一个人负责后续所有工作,开发时间相对较长。
  • 还因为前后端不分离,前后端的代码揉在一起,可维护性较差。每一次的改动,前后端代码都需要同步更改。

前后端不分离,造成问题

  • HTML 页面由服务端渲染
  • 每一次数据的交互都要全局刷新
  • 前后端代码揉在一起,开发维护成本高

# 1.3、前后端分离

TIP

在 2004,Google 发布了 Gmail(一款电子邮件应用),其利用的就是 Ajax 技术,用户可以在不刷新页面的情况下进行复杂的交互,之后,Ajax 逐渐成为网页开发的技术标准,也不断地被应用于各种网站。

2005 年, XMLHttpRequest 对象发请求的技术正式取名为 AJAX

Ajax 这个技术让我们可以异步的获取数据并且刷新页面,从此前端不再受限于后端的模板,这也宣告了 Web2.0 时代正式到来。至此,前端工程师也正式作为一个独立工种出现。

在之后的一段时间里,前端技术的发展主要集中在如何简化页面的开发和如何实现富页面。前端的工作主要在于利用 HTML+CSS+Jquery+JS+BootStrap 等开发网页,同时利用 Ajax 向后端发送请求,拿到后端返回的(XML 或 JSON)等格式的数据做渲染,在渲染时,要做大量的 DOM 操作。

<!-- 发送Ajax请求,把请求过来的JSON数据渲染成DOM,插入到页面 -->

<div id="app"></div>
<script>
/**
 * @param method 请求方式 get  post
 * @param url 请求地址
 * @param callback 回调函数,请求成功后调用
 */
function $ajax(method, url, callback) {
  // 1、创建xhr对象
  const xhr = new XMLHttpRequest();
  // 4、通过监听readystatechange()事件,来处理服务器响应
  xhr.onreadystatechange = function () {
    if (xhr.readyState !== 4) return;
    // readyState 等于 4,表示完成,并已经接收到全部响应数据
    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
      // 请求被发送后,从服务器端返回文本。
      callback(xhr.responseText);
    }
  };
  // 2、调用open()方法,准备发送
  xhr.open(method, url);
  // 3.调用send()方法,正式发送请求
  xhr.send();
}

$ajax(
  "get",
  "https://www.fastmock.site/mock/6ec78e345df340241e1f5043f0167833/icode/menu",
  (res) => {
    // 拿到返回的数据,再始渲染
    const data = JSON.parse(res).data;
    let html = "<ul>";
    data.forEach((item) => {
      html += "<li>" + item.title + "</li>";
    });
    html += "</ul>";
    // 最后将内容添加到页面
    document.getElementById("app").innerHTML = html;
  }
);
</script>

以上代码,最终渲染后结果如下:

<div id="app">
  <ul>
    <li>人气 TOP</li>
    <li>爆款套餐</li>
    <li>咖啡</li>
    <li>奶茶</li>
    <li>甜品小点</li>
  </ul>
</div>

注:

在这期间,前后端是可以实现完全分离,后端只需要专注于业务逻辑的处理,返回(XML 或 JSON)格式的数据。前端利用 Ajax 向后端发送请求,然后把请求过来的数据(XML 或 JSON)渲染到页面中,前端只需要专注于页面的开发和数据渲染就好。

同时前后端可以并行开发,不再需要像之前那样串行开发了,开发的时间成本降低了。同时前后端分离,维护成本降低,当布局需要变动,内容不变时,只需要变动前端布局就好,后端就可以不动等。

# 1.4、前后端分离,带来的新问题

TIP

前端在实现数据渲染时,就需要大量的操作 DOM,而操作 DOM 是一件非常繁琐的事情,需要耗费大量的时间,造成前端在数据渲染的时候,更多时间用来处理 DOM 操作,而非逻辑处理。所以那个时候 JQuery 非常流行,因为他可以帮助我们简化 DOM 的操作,但依然还是需要大量操作 DOM,并没有帮前端开发人员,从操作 DOM 中解放出来。

因为数据都是在浏览器端通过 Ajax 请求获取的,然后渲染在页面中。所以百度蜘蛛在爬取页面内容时,没有什么有用的实质性内容可获取,页面只有一些简单的 HTML 构结。所以对 SEO 优化非常不友好。如果开发的项目不用考虑 SEO 优化,可以采用前后端分离;如果需要考虑 SEO 优化,还是的选择早期前后端不分离形式来开发,服务端负责渲染数据。

前后端分离的优点

  • 利用 Ajax 异步请求获取数据,实现了局部刷新,不需要在每次请求更新内容时,刷新整个浏览器
  • 前端只专注于页面和数据渲染,后端只专注于业务逻辑处理,前后端可并行开发,缩短了开发时间

前后端分离带来的问题

  • 前端开发在数据渲染时,需要大量的 DOM 操作,非常繁琐和耗时。
  • SEO 优化非常不友好,因为数据都是在浏览器端通过 Ajax 请求获取的,所以百度蜘蛛在爬取页面内容时,没有什么实质性内容可获取。

# 1.5、前端工程化

TIP

在 2009 年 AngularJS 和 Node.js 的诞生,也宣告前端工程化时代的到来。

  • AngularJS 的诞生,引领了前端 MVVM 模式的潮流。
  • Node.js 的诞生,让前端有了入侵后端的能力,也加速了前端工程化的诞生。
  • 2013 年 12 月 7 日发布了 Vue.js 的 0.6.0 版本,也就是 Vue 框架诞生了。

Vue、Angular、React 这类前端框架,可以帮助我们解决在前端数据渲染时,不需要再关注于 DOM 的操作,而只需要关注于业务逻辑的处理。

Vue 等前端框架让前端工程师从大量的 DOM 操作中解放出来,但依然对 SEO 不友好,因为他本质也是通过 Ajax 请求,向后台拿到数据,在前端渲染。

不过,现在 Vue 提供了@vue/server-renderer插件,可以在服务器端解析 Vue 的组件,直接把渲染结果返回给浏览器。这样 SEO 不友好的问题也就解决了。

注:

前端工程化时代,前端开发人员会借助前端相关框架(Vue,React,Angular)

  • 实现在开发过程中不再需要关注于 DOM 的操作
  • 同时也可以借助相关的插件,解决 SEO 不友好的问题

# 2、前端框架介绍

TIP

为更好的理解框架是什么 ?以及框架帮我们做了什么 ?

接下来我们通过生活中的例子对比来解析。

# 2.1、什么是框架

TIP

如果,你想要有一个自己的房子,按正常流程,你得自己从 0 开始一点一点的修建。就好比在老家建房子,从建房子开始到最后房子装修,至少得一年吧!这样太费时了。如果有人把房子的框架都搭好了,(比如:开房商提供的毛坯房),你只需要按自己喜欢的风格来装修就好,这样是不是 2-3 个月就能入住了,大大的缩减了建房子的时间。

上面例子中毛坯房相比装修好的房子,毛坯房就是房子的框架,有了毛坯房,就不需要再从 0 开始搭建房子了,直接进入装修环节就好,这样省时有省力。

# 2.2、框架帮我们做了什么

TIP

从上面的例子中,我们知道,如果直接选用毛坯房,那我们就不需要再从 0 开始修建房子了,直接装修就好。所以框架帮我们把原来属于我们自己要做的那一部分工作给做掉了,我们只需要在这个基础之上,做其它的后续工作就可以。

同理,前端框架就是帮开发人员把原本属于开发人员的一部分工作给做掉了,开发人员只需要在这个基础之上,做其它工作。

# 2.3、前端流行框架有那些

TIP

目前前端流行的框架主要有 Vue、React、Angular。这些框架的目标都是为了帮助开发者高效地开发 Web 应用,只不过走的路线略显不同

  • React 注重数据不可变、虚拟 DOM 和运行时。
  • Angular 则在抽象这个维度又走向一个极致,生来就是为了复杂项目
  • Vue 相比前两种框架要简单多了,简单到大部分前端开发者都能学得会。Vue 在每个维度之间,做了非常好的权衡和取舍,算是一个非常中庸且优雅的框架,兼顾响应式、虚拟 DOM、运行时和编译优化

# 2.4、Vue 框架帮我们做了什么

TIP

作为前端开发人员,如果你想配合后端,开发出一个完整的 Web 网站或应用等。

在没有应用 Vue 框架前,你需要利用 HTML+CSS+JS 来开发 HTML 网页,同时利用 Ajax 请求向后端获取 JSON 数据,然后利用 JS 操作 DOM 将数据渲染到 HTML 中。

如果有了 Vue 框架,只需要利用 HTML+CSS+JS 来开发 HTML 网页,同时利用 Ajax 请求向后端获取 JSON 数据。不再需要利用 JS 操作 DOM 将数据渲染到 HTML 中,因为 Vue 框架帮你做了这部分工作。利用 Vue 框架将数据与 HTML 结合在一起,相比直接用 JS 操作 DOM 将数据渲染到 HTML 中要简单多。

Vue 框架本质就是帮我们把 JS 操作 DOM 的这一套逻辑业务代码写好了,我们只需在这个基础做后续开发。

下面是一个简单的例子,帮助直观感受传统方式与框架模式下开发,框架模式更高效

传统方式:

Ajax 发请求获取数据,再利用 JS 将返回的 JSON 数据渲染成 DOM,插入页成

<!--html模块-->
<ul class="menu"></ul>

<!--引入axios-->
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.5/axios.min.js"></script>

<!--JS 操作DOM实现数据与HTML绑定-->
<script>
  // 请求地址
  const url = "https://www.fastmock.site/mock/6ec78e345df340241e1f5043f0167833";
  // 发送ajax请求获取数据
  axios.get(url + "/icode/menu").then((res) => {
    // 获取响应成功的数据
    const data = res.data.data;
    // 将数据与HTML绑定,最后将DOM插入页面
    let html = "";
    for (let i = 0; i < data.length; i++) {
      html += `<li>${data[i].title}</li>`;
    }
    const $menu = document.querySelector(".menu");
    $menu.innerHTML = html;
  });
</script>

VUE 框架:

Ajax 发请求获取 JSON 数据,利用 Vue 框架将 JSON 数据与 HTML 绑定,最终渲染成 DOM,插入页面。

<!--引入vue框架-->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<!--引入axios-->
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.3.5/axios.min.js"></script>

<!--html模块-->
<ul class="menu">
  <li v-for="{title} in data">{{title}}</li>
</ul>

<!--借助Vue来实现数据与HTMl绑定(渲染)-->
<script>
  const { createApp } = Vue;
  createApp({
    data() {
      return {
        data: [],
      };
    },
    created() {
      // 请求地址
      const url =
        "https://www.fastmock.site/mock/6ec78e345df340241e1f5043f0167833";
      // 发送ajax请求获取数据
      axios.get(url + "/icode/menu").then((res) => {
        // 获取响应成功的数据
        this.data = res.data.data;
      });
    },
  }).mount(".menu");
</script>

# 3、MVVM 模式

TIP

前端框架几乎都是采用典型的 MVVM 设计模型,那 Vue 也不例外。MVVM 是 Model-View-ViewModel 的简写。

MVVM 模式 说明
Model 模型 用于封装应用的业务逻辑和数据。在 Vue 框架中书写在<script>标签中的代码
View 视图 用于封装 UI 和 UI 逻辑,也就是我们常写的 HTML 与 CSS 相关的代码。就好比上面的 HTML 模块
ViewModel 视图模型 他主要是将 Model 与 View 结合到一起,封装了将他们结合到一起的底层逻辑
Vue 框架负责的就是 VM 的工作,通过 Vue 可以将视图和模型相关联,省去了开发人员手动操作 DOM 的工作。

MVVM 的工作模式

f05b502e6b65b0e40b9bb4e9f58ff10

注:

  • 我们可以把上面的 VM 看作 Vue 框架,那 Vue 负责 VM(视图模型)的工作,通过 Vue 可以将视图和模型相关联。
  • 当 Model 模型(JS 数据)发生变化时,会通知 ViewModel,ViewModel 会控制视图自动更新
  • 当 View 视图(如:表单数据)发生变化时,也会通知 ViewModel,ViewModel 会控制 Model 自动更新数据。

以上工作模式,也是 Vue 框架的一个核心特点:响应式

# 4、总结

TIP

我们了解了 web 前端技术发展的三个阶段及每个阶段所存在的问题

前后端不分离(存在问题)

  • HTML 页面由服务端渲染
  • 每一次数据的交互都要全局刷新
  • 前后端代码揉在一起,开发维护成本高

前后端分离 - 优点

  • Ajax 异步获取数据,实现局部刷新,不需要在每次请求更新内容时,刷新整个浏览器
  • 前端只专注于页面和数据渲染,后端只专注于业务逻辑处理,前后端可并行开发,缩短了开发时间

前后端分离 - 存在问题

  • 前端开发在数据渲染时,需要大量的 DOM 操作,非常繁琐和耗时。
  • SEO 优化非常不友好,因为数据都是在浏览器端通过 Ajax 请求获取的,所以百度蜘蛛在爬取页面内容时,没有什么实质性内容可获取。

前端工程化 - 优点

  • 实现在开发过程中不再需要关注于 DOM 的操作
  • 同时也可以借助相关的插件,解决 SEO 不友好的问题

能实现前端工程化的主要核心就是借助前端框架,如:Vue、React、Angular 等框架

Vue 框架与 MVVM 模式

借助 Vue 框架开发项目,前端开发人员不再需要关注 DOM 的操作,这部分工作 Vue 框架帮忙做掉了。

Vue 框架采用经典的 MVVM 模式,实现了数据和视图的双向绑定,当视图发生变化时会驱动数据的变化,当数据变化时会驱动视图的变化。

# 三、Vue 简介

TIP

深入浅出什么是 Vue 及它的三大特点:

  • 声明式
  • 组件化
  • 渐进式

以及 Vue 作者简介 与 Vue 发展历程

# 1、什么是 Vue

TIP

Vue 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。无论是简单还是复杂的界面,Vue 都可以胜任。

用户界面

是指系统和用户之间进行交互和信息交换的媒介。比如:电脑屏幕,手机等电子设备屏幕。这里你可以理解为,客户端界面要呈现的内容,可以用 Vue 来构建。

# 1.1、声明式与命令式

TIP

要了解什么是声明式,最好与命令式结合在一起来学习,这样能帮助我们更好的理解声明式。

  • 命令式:更关注过程
  • 声明式:更关注结果

我们先用一个生活中的例子,来帮助我们理解。比如,你想要你男朋友帮你倒杯水

如果用命令式:

你要对他说:“先到餐桌 -> 拿起我的水杯 -> 转身 -> 向前走 -> 把杯子放在饮水机下 -> 按下开关 -> 端水 -> 走到我面前”,然后才能喝到水。

如果用声明式:

你只需要对他说:“我口渴了 -> 想喝水”,他就会把水端到你面前,你就能喝到水了。

通过上面例子,我相信你能理解,什么叫更关注过程和更关注结果。

总结

  • 命令式:需要你把每一步的要如何做告诉他,才能达到你想的结果
  • 声明式:你只需要告诉他你想要什么就好,他就会帮你实现。

# 1.2、命令式框架与声明式框架

TIP

视图层框架通常分为命令式和声明式,JQuery 就是典型的命令式框架,Vue 就是典型的声明式框架

接下来,我们通过一个具体的需求,来演示命令式与声明式框架的不同。

案例演示

当我们点击".box"元素时,向里面的.box元素添加内容为“Hello"

  • 命令式框架(JQuery 框架)
<!--引入Jquery框架(插件)-->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<div id="app">
  <div class="box"></div>
</div>

<!--JQuery代码-->
<script>
  $(".box").on("click", function () {
    $(this).text("Hello");
  });
</script>

<!--
	上面代码,相当于在执行以下几个步骤
        1、获取.box元素
        2、给.box元素绑定click事件
        3、当click事件触发后,为.box元素添加内容为"hello"
-->

注:

通过上面 JQuery 代码可以看到,代码描述的是实现最终效果要经过的具体步骤,更关注“做事的过程”,当然这更符合我们的逻辑,只是代码写起来比较麻烦。

如果大家对 JQuery 不了解,可以参考下面给出的原生 JS 代码实例。我们用原生 JS 来实现以上效果,显然也是命令式的。(不过 JS 不是框架,是一门脚本语言,以下方式为命令式编程)

// 1、获取.box元素
const box = document.querySelector(".box");
// 2、.box绑定click事件
box.onclick = function () {
  // 3、将box的内容更新为Hello
  this.innerText = "Hello";
};
  • 声明式框架 (采用 Vue)
<!---引入Vue框架-->
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

<!--声明式-->
<div id="app">
  <div @click="show" class="box">{{message}}</div>
</div>

<!--Vue代码-->
<script>
  const { createApp } = Vue;
  createApp({
    data() {
      return {
        message: "",
      };
    },
    methods: {
      show() {
        this.message = "Hello";
      },
    },
  }).mount("#app");
</script>

注:

以上代码,重点关注 HTML 部分代码,这就是 Vue 帮我们实现以上功能的方式。可以看到,我们提供的是一个“结果”。就好比告诉 Vue.js:“嘿,Vue.js,我要在.box中显示以下 JS 中message里的内容,当我点击.box这个元素时,你调用 show 方法,帮我更新下.box中的内容”。

至于具体的实现过程,我们不需要关注,Vue.js 会帮我们完成。换句话说,Vue.js 帮我们封装了整个 JS 操作 DOM 的过程,因此,我们可以猜想,Vue.js 的内部实现一定是命令式的,而暴露给用户的却是声明式的。

总结:

声明式框架的好处,就是让开发人员无需直接操作 DOM,只需关注业务逻辑,提高开发效率

# 1.3、组件化

TIP

组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。

在实际应用中,组件常常被组织成层层嵌套的树状结构

components.7fbb3771

注:

上图左侧的网页,由一个根组件 Root 开始,在根组件 Root 中包含 Header、Main、Aside 子组件,在 Main 和 Aside 组件中,又可以放很多的子组件。

我们再来看下面这个网页结构,用红色框出来的部分,都可以抽离出去作成一个单独的组件,重而达到复用。

16818821754465

在 Vue 中:

组件本质就是一组 DOM 的封装,用来显示具体功能的 UI 界面。

一个完整的 Vue 组件包含 HTML、CSS、JS 三部分。当我们需要在多个页面实现相同的功能的 UI 时,就可以把这部分功能的(CSS、HTML、JS)抽离出去,做成一个单独的组件,然后实现复用。

注:区分模块化与组件化

  • 模块化强调的是把一个大的 JS 文件拆分成许多具有独立功能的小的 JS 文件,从而实现代码复用
  • 组件化强调的把一个大的 UI 界面,拆分成许多具有独立功能的小 UI,从而实现 UI 界面的复用

# 1.4、渐进式

TIP

在 Vue 的官网,开篇就提到,Vue 是一个 Javascript 渐进式框架,那什么是渐进式呢?

所谓渐进式,就是把框架进行分层设计,每层都是可选的,不同层可以灵活地替换为其他的方案。

Vue 渐进式框架分层结构下如图:

image-20230420131320242

TIP

Vue 最核心的部分就是他的声明式渲染,向外是组件系统,在这个基础上再加入路由系统和状态管理,最外层是构建系统。

组合 说明
组合一 声明式渲染 当我们开发的页面很简单,可以把 Vue.js 作为一个 JS 库来使用,只使用他的声明式渲染来帮我们轻松地操作 DOM,实现数据变化的自动视图渲染。
组合二 声明式渲染+组件系统 当我们开发的页面较复杂,想要把界面元素组件化,就可以在组合一的基础上加入 Vue 的组件系统,将界面的部分功能开发成一个组件,采用分而治之的策略,而且还可以实现功能的复用。
组合三 声明式渲染+组件系统+路由系统 如果想要将前端做成单页面应用程序,可以在组合二的基础上加入 Vue 的路由系统 Vue Router,实现单页面应用。
组合四 声明式渲染+组件系统+路由系统+状态管理 如果我们的项目较复杂,应用中有很多数据需要在多个组件间共享,就可以在组合二或三的基础上引入 Vuex 或 Pinia 统一对状态(数据)进行管理
组合五 声明式渲染+组件系统+路由系统+状态管理+构建系统 如果你不想手动的从 0 开始搭建一个 Vue 项目,可以引入 Vue 的构建系统,他可以轻松地帮你搭建一个脚手架项目。然后在这个基础上开发你的 Web 应用。

注:

在实际开发中,我们的项目都不会太小,所以我们通常会选择组合五来开发我们的项目。

# 2、Vue 作者与发展史

TIP

以下是 Vue 作者尤雨溪 github 上的一张截图。大家有兴趣可以关注下作者,可以了解 Vue 的最新动态。

16818862555172

Vue 发展历程简介

最开始尤雨溪在 Google 公司工作,在工作中用到 Angular 框架,他觉得 Angular 框架太重了,然后慢慢就萌芽出一个想法,想自己开发出一个轻量级的框架出来。最开始他开发的这个框架叫 Seed,后来更名为 Vue,版本 0.6.0

  • 2013 年 12 月 7 日发布了 Vue 的 0.6.0 版本,也就是 Vue 框架诞生了。
  • 2014 年 Vue 正式对外发布,版本号 0.8.0
  • 2015 年 10 月 27 日,正式发布 Vue1.0.0 Evangellon(新世纪福音战士)
  • 2016 年 10 月 1 日,正式发布 Vue2.0.0 Ghost in the Shell (攻壳机动队)
  • 2020 年 9 月 18 日,正式发布 Vue3.0.0 One Piece(海赋王)

题外话

Vue 之所以能火起来,除了它本身确实好用之外,还有一个重要的原因就是Taylor Otwell 的推荐。Taylor Otwel是 PHP Web 开发框架 Laravel 之父,他在圈里面非常有名气,而且粉丝量特别庞大。

在 2014 年的某一天,他在 Twitter 上发表动态,说自己正在学习 Vue.js,觉得还不错。他这一说,就相当于给 Vue 在程序员圈子里打了个广告,所以更多的人知道了 Vue,并学习他,慢慢的就火起来了。

# 3、总结

TIP

Vue 框架的三大特点:

  • Vue 框架为典型的声明式框架,借助 Vue 框架我们不再需要手动操作 DOM
  • Vue 支持组件化开发,可以将 UI 抽离成单独的组件,从页实现 UI 的复用
  • Vue 是一个渐进式框架,我们可以根据项目的需要来选择使用 Vue 的五大核心功能:声明式渲染、组件系统、路由系统、状态管理、构建系统

# 四、创建一个 Vue 应用

TIP

在项目中使用 Vue,方式有如下三种:

  • 通过 CDN 来使用 Vue
  • 通过原生的 ES 模块使用 Vue
  • 通过 Vue 脚手架来使用 Vue

在实际的开发中,主要用到的是第三种方式:通过 Vue 脚手架来使用 Vue,前两种方式在实际的开发中几乎不用。

对于初学者,建议先借助第一种方式来学习 Vue,有一定理解后,再使用第三种方式 即:Vue 脚手架 的方式深入浅出,效果更好。

# 1、通过 CDN 来使用 Vue

TIP

借助 script 标签直接通过 CDN 来使用 Vue

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

以上代码就相当添加了全局变量 Vue,我们可以直接利用解构赋值,来解构 Vue 对象中的的createApp方法,这个方法用来创建一个应用实例。

const { createApp } = Vue;

createApp方法在创建应用实例时,需要传入一个对象作为参数,这个被传入的对象我们称为:根组件 ,每个应用都需要一个 “根组件” ,其他组件将作为其子组件。

组件被编译后,生成一组 DOM 元素,最后被添加到页面中,所以组件本质就是一组 DOM 元素的封装

const app = createApp({
  // 这个对像我们称其为根组件,不过此时组件中没有任何数据
});

# 1.1、template 属性

TIP

  • 根组件对象身上有一个template属性(可选),用于声明组件的字符串模板。
  • template属性中的html字符串最终会被 Vue 编译成真实的 HTML 元素),添加到页面中。
const app = createApp({
  template: "<div>Hello Vue</div>",
});

上面的根组件最终被渲染成如下html代码

<div>Hello Vue</div>

如何将渲染后的html代码,添加到页面对应的容器(HTML 元素)中 ?这就需要用到mount方法

# 1.2、mount 方法

TIP

  • 调用createApp()方法创建的应用实例 app 身上的mount()方法,来实现挂载。
  • 挂载:是指将生成的HTML元素,添加到真实的页面中去。
  • mount()方法需要传一个的css 选择器字符串作为参数,用来告诉 Vue,把 template 模块中渲染后的内容添加到页面的那个 HTML 元素(容器)内。
  • mount()方法的返回值为根组件实例对象
const app = createApp({
  template: "<div>Hello Vue</div>",
});
// root为根组件实例对象
const root = app.mount("#app");
// mount的方法,就相当于把template中的内容赋值给到#app元素的innerHTML属性。

完整代码示例

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app"></div>
<script>
  const { createApp } = Vue;
  // app 应用实例
  const app = createApp({
    template: "<div>Hello Vue</div>",
  });
  // root为根组件实例对象
  const root = app.mount("#app");
</script>

以上代码,最终在浏览器显示的 HTML 结构如下

<div id="app">
  <div>Hello Vue</div>
</div>

# 2、data 属性

TIP

  • 在根组件对象上,还可以添加一个data函数,用于声明组件初始响应式状态的函数。
  • data 的值是一个函数,该函数应当返回一个普通 JavaScript 对象,Vue 会将它转换为响应式对象
  • 根组件实例创建后,可以通过 root.$data 访问该响应式对象,同时组件实例也代理了该数据对象上所有的属性,因此 root.message 等价于 root.$data.message

演示代码

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app"></div>
<script>
  const { createApp } = Vue;
  // app 为创建的应用实例,指代上面的this
  const app = createApp({
    data() {
      return {
        message: "Hello Vue",
      };
    },
    template: "<div>Hello Vue</div>",
  });
  const root = app.mount("#app");
</script>

在浏览器中打开当前页面,然后在控制台输入app,查看输出结果如下:

image-20230606233906900

接下,我们在控制台输入对应代码,输出结果如下:

image-20230606234049217

以上输出结果分析

  • 因为root.$data代理了 data 函数返回的那个对象,所以可以通过root.$data.message访问到 message 属性。
  • 因为 root 对象代理了 data 函数返回的那个对象的所有属性,所以可以通过root.message访问到 message 属性。
  • 当执行root.message="更改Hello vue",本质上是通过代理更改 data 函数返回的那个对象身上的 message 属性。所以通过root.$data.message再次访问 message 属性时,得到结果也为"更改Hello vue"

# 2.1、模板中访问 data 属性

TIP

我们可以在template模板中,使用插值语法{{}}等方式访问 data 函数返回的对象身上的属性。

具体写法如下

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app"></div>
<script>
  const { createApp } = Vue;
  const app = createApp({
    data() {
      return {
        message: "Hello Vue",
      };
    },
    template: "<div>{{message}}</div>",
  });
  const root = app.mount("#app");
</script>

以上代码,最终在浏览器显示的 HTML 结构如下

<div id="app">
  <div>Hello Vue</div>
</div>

# 2.2、data 的响应性

TIP

上面提到:data函数返回一个普通 JavaScript 对象,Vue 会将它转换为响应式对象

响应式对象特点

  • 当我们更改该对象属性的值时,页面 DOM 中展示的数据会更新为更改后的值。
  • 当我们更改了页面中的数据时,JS 对象身上的属性值也会更新为更改后的值。

代码演示

  • 以下代码中 input 输入框的值需要采用v-model指令来实现双向绑定
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app"></div>
<script>
  const { createApp } = Vue;
  const app = createApp({
    data() {
      return {
        message: "Hello Vue",
      };
    },
    // v-model指令  双向数据绑定
    template: "<input v-model='message'> ",
  });
  const root = app.mount("#app");
</script>

为了让大家能更好的感受到data的响应性,我们结合 Vue 开发用到的调试工具devtools来帮助查看整个响应过程。关于devtools的安装,我们稍后再讲。

GIF2023-4-1917-21-33

注:

观察以上截图,可以看到,当我们更改了message属性值时,输入框中的值也同步更新了。当我们更改了输入框中的值时,message属性的值也同步更新了。

响应性是 Vue 的一个核心特点,关于其内部实现原理,我们放在后面再来学习。

# 3、Vue 调试工具 Devtools

TIP

安装 Devtools 调试工具有两种方案

①、Google 应用商店安装

  • 点击 Google 浏览器的右上角 ...
  • 点击扩展程序
  • 访问 Google 应用商店
  • 在搜索框搜索需要的插件,即可安装

②、下载 Devtools 插件,然后再安装

  • 点击 Google 浏览器的右上角 ...
  • 点击扩展程序
  • 点击管理扩展程序,打开浏览器右上角开发者模式
  • 把下载好的vue.jsdevtools.crx拖到扩展程序页面,弹出是否安装,点击确认即可

image-20230607000005931

# 4、template 属性

TIP

在前面我们提到,组件对象身上的template属性是可选的。也就是说,也可以没有template这个属性。

如果没有template属性,组件会以mount()方法传入的参数(CSS 选择器)选择的元素的innerHTML作为渲染的模板。

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<!--html模板-->
<div id="app">
  <div>{{message}}</div>
</div>

<script>
  const { createApp } = Vue;
  const app = createApp({
    data() {
      return {
        message: "Hello Vue",
      };
    },
  });
  app.mount("#app");
</script>

以上代码中,并没有template属性,所以 Vue 最终会把#app的 innerHTML 作为渲染的模板,最终编译后效果如下:

<div id="app">
  <div>Hello Vue</div>
</div>

注:

以上方式仅做了解,在实际项目中几乎不用,因为后期开发主要采用的是组件化开发。

# 5、template 注意事项

TIP

如果组件没有template属性,组件会以mount()方法传入的参数(CSS 选择器)选择的元素的innerHTML作为渲染的模板。

但选择的元素本身(被挂载的容器)不会被视为应用的一部分,不参与渲染

  • 把上面代码中的 html 模板更改为如下
<!-- :title="message" 相当于给元素的自定义属性title赋值-->
<div id="app" :title="message">
  <div :title="message">{{message}}</div>
</div>
  • 以上代码渲染后,生成的 HTML 结构如下
<div id="app" :title="message">
  <div title="Hello Vue">Hello Vue</div>
</div>

注:

观察渲染后的结果,发现#app元素身上的:title="message"并没有被渲染,而是原样输出。

但其子元素上的title被渲染成功,因为被挂载的容器本身是不参与渲染的。

# 7、模块化开发

TIP

以下index.html页面中代码,大部分是固定写法,只有根组件中的代码需要经常变动。

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

<div id="app"></div>
<script>
  const { createApp } = Vue;
  const app = createApp({
    data() {
      return {
        message: "Hello Vue",
      };
    },
    template: "<div>{{message}}</div>",
  }).mount("#app");
</script>

对以上代码进行如下拆分:

  • index.html页面中<script>标签中的代码抽离出来,放到一个单独的main.js文件中,然后在index.html页面,通过模块化的方式引入
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

<div id="app"></div>
<!-- 注意,需要添加type="module" -->
<script type="module" src="./main.js"></script>
  • main.js中的根组件抽离出去,放在一个单独的app.js文件中,然后对外默认导出,最后在main.js中通过模块化的方式导入。
// main.js
const { createApp } = Vue;
import App from "./app.js";
const app = createApp(App);
app.mount("#app");
  • 以下是App.js文件中代码,定义根组件
export default {
  data() {
    return {
      message: "Hello Vue",
    };
  },
  template: "<div>{{message}}</div>",
};

通过 VSCode 打开index.html页面,程序正常运行,输出了我们想的结果。

# 8、通过原生的 ES 模块使用 Vue

TIP

我们也可以使用的生 ES 模块化的方式来使用 Vue,不过导航的 JS 文件与前面的不一样,要注意区分。

<script type="module">
  import { createApp } from "https://unpkg.com/vue@3/dist/vue.esm-browser.js";
</script>

我们把上面案例中index.html页面中通过<script>引入 Vue 的代码删除,修改main.js中代码如下:

// 新增
import { createApp } from "https://unpkg.com/vue@3/dist/vue.esm-browser.js";

// 删除
// const { createApp } = Vue;

import App from "./app.js";
const app = createApp(App);
app.mount("#app");

# 9、总结

TIP

  • 在 Vue 中,组件是一个包含特定选项(属性)的 JS 对象,组件最终被编译成 DOM 元素插入到页面。
  • 我们可以通过 Vue 的createApp()方法,来创建一个应用实例,传入的参数为一个根组件,最终通过调用应用实例的mount方法实现挂载。
  • mount()方法的参数为CSS 选择器DOM 元素
  • 组件身上的 data 属性返回的对象,最终被组件实例 root.$data所代理。同时组件实例root也代理了该数据对象上所有的属性
  • data 函数返回的对象最终被 Vue 转换为一个响应式对象,当数据发生变化时,视图也会相应变化,当视图发生变化时,数据也会相应变化。
  • 我们可以在template模板中使用{{}}插值语法访问data()函数返回对象身上的属性
  • 如果组件没有template属性,则最终会将mount()参数所对应的 DOM 元素的innerHTML内容作为渲染的内容。

# 五、Vue 组件基础

TIP

  • 组件是用一个包含特定选项(属性)的 JS 对象来表示,比如:有datatemplate等更多属性。
  • 组件最终会被渲染成一个 DOM 元素插入到页面中。
  • Vue 组件又分为根组件和子组件,一个应用一般是由一个根组件与多个子组件组成。

接下来,我们重点学习:

  • 如何创建根组件
  • 如何创建子组件
  • 如何注册全局子组件
  • 如何注册局部子组件
  • 组件的复用性
  • 全局组件 VS 局部组件
  • 组件化应用实例

# 1、如何创建根组件

TIP

  • 一个页面可以有多个根组件,但正常情况下,一个单页面应用只有一个根组件,其它组件作为根组件的子组件,然后嵌套组合成一个完整的应用。
  • 根组件需要通过createApp()方法来渲染,最终调用mount()方法,将渲染后的 DOM 添加到页面中对应容器。

以下代码创建了 2 个根组件,这 2 个根组件最终都被渲染成 DOM,插入到页面

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

<div id="app1"></div>
<div id="app2"></div>
<script>
  const { createApp } = Vue;
  // 创建应用实例1
  createApp({
    // 根组件
    data() {
      return {
        message: "Hello app1",
      };
    },
    template: "<div>{{message}}</div>",
  }).mount("#app1");

  // 创建应用实列2
  createApp({
    // 根组件
    data() {
      return {
        message: "Hello app2",
      };
    },
    template: "<div>{{message}}</div>",
  }).mount("#app2");
</script>

以上代码,渲染后效果如下:

<div id="app1" data-v-app=""><div>Hello app1</div></div>
<div id="app2" data-v-app=""><div>Hello app2</div></div>

# 2、如何创建子组件

TIP

子组件本身也是组件,所以也是用一个 JS 对象表示,其写法与根组件的写法一模一样。

# 2.1、创建一个子组件

以下代码,就相当于创建了一个子组件,子组件的名字为MyComponent

// 子组件
const MyComponent = {
  data() {
    return {
      message: "Hello Vue!!",
    };
  },
  template: "<div>{{message}}</div>",
};

# 2.2、使用子组件

TIP

  • 子组件不能单独使用,需要被嵌套在根组件或其它子组件中使用。
  • 多个子组件层层嵌套组合在一起,配合根组件最终实现一个完整的应用。
  • 子组件在使用前需要先被 “注册” ,这样 Vue 才能在渲染模板时找到对应子组件并渲染成 DOM。

# 3、组件注册

TIP

组件注册有两种方式

  • 全局注册
  • 局部注册

# 4、注册全局组件

TIP

我们可以使用 Vue 应用实例的app.component() 方法来注册一个全局组件,该组件可以在当前 Vue 应用中全局可用。

/*
	name :  为注册成功的全局组件的组件名
	component : 为注册为全局组件的子组件(
*/
component(name: string, component: Component): this
  • 以下定义了一个全局组件global-component
// 定义子组件
const MyComponent = {
  template: "<div>Hello Vue</div>",
};
// 将子组件MyComponent注册为全局组件,全局组件名为 "global-component"
app.component("global-component", MyComponent);
  • 全局组件在整个应用中全局可用,我们可以直接在其它组件的template模板中使用
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app"></div>

<script>
  const { createApp } = Vue;
  // 创建应用实例
  const app = createApp({
    // 使用全局组件
    template: `<global-component />`,
  });

  // 定义子组件
  const MyComponent = {
    template: "<div>Hello Vue</div>",
  };

  // 将子组件注册为全局组件,在整个应用中全局可用,全局组件名自定义
  app.component("global-component", MyComponent);

  // 挂载
  app.mount("#app");
</script>

以上代码,最终渲染后的 HTML 代码如下

<div id="app" data-v-app=""><div>Hello Vue!!</div></div>

总结:

把组件注册为全局组件并使用分为以下三步

  • 定义子组件 const MyComponent={ /* */ }
  • 调用app.component(name,component)方法,将组件注册为全局组件
  • 在其它组件的template模板中使用全局组件

# 4.1、app.componet() 特殊用法

app.component()方法还可以接受一个参数

component(name: string): Component | undefined
  • 参数name是一个被注册过的组件的名字,最终返回该名字对应的组件。
  • 这种方式可以帮助我们通过注册过的组件名来获取对应的组件。
// Component 就是上面代码中注册的"global-component"全局组件
const Componet = app.component("global-component");
console.log(Componet === MyComponent); // true

# 4.2、全局组件全局可用

TIP

全局组件在当前应用中全局可用,既可以嵌套在根组件中使用,也可以嵌套在任意的子组件中使用

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app"></div>

<script>
  const { createApp } = Vue;
  // 创建应用
  const app = createApp({
    // 使用全局组件
    template: "<my-list/>",
  });

  // 注册全局 my-list组件,并在组件中调用my-item组件
  app.component("my-list", {
    template: `<div class="list">
                    my-list组件中调用my-item组件
                    <my-item/>
                  </div>
                `,
  });
  // 注册全局 my-item组件
  app.component("my-item", {
    template: `<div class="item">my-item组件内容</div>`,
  });
  // 挂载
  app.mount("#app");
</script>

以上代码,编译后效果如下:

<div id="app" data-v-app="">
  <div class="list">
    my-list组件中调用my-item组件
    <div class="item">my-item组件内容</div>
  </div>
</div>

# 4.3、app.component() 链式调用

TIP

app.component() 方法返回值为 app 应用实例,所以该方法支持链式调用

上面代码,可以简写成如下

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app"></div>

<script>
  const { createApp } = Vue;
  // 创建应用
  const app = createApp({
    // 使用全局组件
    template: "<my-list/>",
  });

  // 注册全局 my-list组件,并在组件中调用my-item组件
  app
    .component("my-list", {
      template: `<div class="list">my-list组件中调用my-item组件 <my-item/></div>`,
    })
    .component("my-item", {
      template: `<div class="item">my-item组件内容</div>`,
    });
  // 挂载
  app.mount("#app");
</script>

# 5、注册局部组件

TIP

  • 局部组件只能在使用它的父组件中显示导入才能使用,并且只能在导入的父组件中使用
  • 在父组件中显示导入需要使用的子组件,需要使用components属性
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app"></div>

<script>
  const { createApp } = Vue;
  // 创建子组件 ComponentA
  const ComponentA = {
    data() {
      return {
        message: "ComponentA",
      };
    },
    template: "<div>{{message}}</div>",
  };
  // 创建子组件 ComponentB
  const ComponentB = {
    components: {
      ComponentA,
    },
    data() {
      return {
        message: "ComponentB",
      };
    },
    // 在 ComponentB组件中,使用ComponentA组件
    template: `
                         <div>{{message}}</div>
                         <div><ComponentA /></div>
                    `,
  };

  // 创建应用
  const app = createApp({
    // 在父组件中显示导入要使用的子组件
    components: {
      ComponentA: ComponentA,
      ComponentB, // ComponetB为 ComponentB : ComponentB 的简写
    },
    // 在根组件中使用子组件
    template: "<ComponentA /> <ComponentB />",
  });

  // 挂载
  app.mount("#app");
</script>

注意:

需要使用的子组件,需写在const app = createApp({})代码之前,否则会出现找不到组件的错误

以上代码最终渲染效果如下:

<div id="app" data-v-app="">
  <div>ComponentA</div>
  <div>ComponentB</div>
  <div>
    <div>ComponentA</div>
  </div>
</div>

# 5.1、抽离子组件

TIP

以上代码,子组件全写在一个页面看起来代码混乱,可以把

  • 子组件抽离出去,放在单独的 JS 文件中,通过export default对来默认导出
  • 需要使用该子组件的父组件中,通过import导入对应子组件
  • index.html页面,项目入口
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app"></div>

<!--注意要加入 type="module"-->
<script type="module">
  const { createApp } = Vue;
  // 导入子组件
  import ComponentA from "./src/components/ComponentA.js";
  import ComponentB from "./src/components/ComponentB.js";

  // 创建应用
  const app = createApp({
    // 在父组件中显示导入要使用的子组件
    components: {
      ComponentA: ComponentA,
      ComponentB, // 相当于 ComponentB: ComponentB的简写
    },
    // 在根组件中使用子组件
    template: "<ComponentA /> <ComponentB />",
  });
  // 挂载
  app.mount("#app");
</script>
  • ComponentA.js 文件,对外默认暴露子组件 ComponentA
// 创建子组件
export default {
  data() {
    return {
      message: "ComponentA",
    };
  },
  template: "<div>{{message}}</div>",
};
  • ComponentB.js 文件,对外默认暴露子组件 ComponentB
const ComponentB = {
  components: {
    ComponentA,
  },
  data() {
    return {
      message: "ComponentB",
    };
  },
  // 在 ComponentB组件中,使用ComponentA组件
  template: `
                         <div>{{message}}</div>
                         <div><ComponentA /></div>
                    `,
};

# 6、组件的复用性

TIP

全局或局部 组件最大的优势就是可以复用,所以我们可以在一个页面重复使用同一个组件多次,他们的数据互不干扰

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app"></div>

<script>
  const { createApp } = Vue;
  // 创建应用
  const app = createApp({
    // 使用同一个子组件三次
    template: "<Count/><Count/><Count/>",
  });

  // 创建子组件
  const Count = {
    data() {
      return {
        num: 1,
      };
    },
    // @click为button绑定了点击事件,点击后num就会+1
    template: "<button @click='num++'>{{num}}</button>",
  };

  // 注册子组件Count
  app.component("Count", Count);

  // 挂载
  app.mount("#app");
</script>

以上代码渲染后效果如下:

GIF2023-5-920-24-56

data 为什么被设置成一个函数,而不是一个对象

之所以每个子组件的数据不会产生相互干扰,是因为每个子组件使用的数据都是通过data()函数返回的一个新的响应式对象,所以在操作数据时,每个组件操作的是与他对应的响应式对象。

# 7、组件命名

TIP

  • 组件名如果为单个单词,首字母推建大写,这样在定义时可以与变量区分,在使用时可以与原生的 html 区分。如:组件名为Counter ,在模板中使用写法<Counter/>
  • 组件名如果为多个单词组成,推荐采用 PascalCase 格式,每个单字首字母大写(如:MyComponent ) 但在模板中使用时,可以采用以下三种写法
  • <MyComponent /><MyComponent></MyComponent><my-component></my-component>

温馨提示:

如果你想区分全局组件与局部组件,全局组件命采用多个单词,并使用-短横分隔(如:my-component)。

在模板中使用时,采用<my-component></my-component><my-component/>简写

一般引用的第三方组件库,都是全局组件,组件名采用多个单词,并用-短横线分隔

# 8、全局组件 VS 局部组件

TIP

全局注册虽然很方便,但有以下几个问题:

  • ①、全局注册,但并没有被使用的组件无法在生产打包时被自动移除(也叫“tree-shaking”)。如果你全局注册了一个组件,即使它并没有被实际使用,它仍然会出现在打包后的 JS 文件中。

  • ②、全局注册,在大型项目中使项目的依赖关系变得不那么明确。在父组件中使用子组件时,不太容易定位子组件的实现。和使用过多的全局变量一样,这可能会影响应用长期的可维护性。

相比之下,局部注册的组件需要在使用它的父组件中显式导入,并且只能在该父组件中使用。它的优点是使组件之间的依赖关系更加明确,并且对 tree-shaking 更加友好。

详细查阅,Vue 官方文档 - 全局注册 - 局部注册 (opens new window)

# 9、组件化应用

TIP

利用组件化来实现以下布局

components.7fbb3771

# 9.1、搭建 Vue 项目

TIP

首先我们把所有代码写在一个页面中,然后再利用组件化的思想来进行拆分。

<style>
  html,
  body {
    margin: 0;
    padding: 0;
    font-size: 30px;
    text-align: center;
    background-color: #ddd;
  }
  .header-content {
    height: 100px;
    background-color: plum;
  }
  .clearfix::after {
    content: "";
    display: block;
    clear: both;
  }
  .main {
    width: 70%;
    float: left;
  }
  .main-content {
    width: 100%;
    display: inline-block;
  }
  .main-content .article1,
  .main-content .article2 {
    height: 310px;
    margin: 20px;
    background-color: skyblue;
  }
  .main-content .article2 {
    background-color: khaki;
  }
  .aside {
    width: 30%;
    float: right;
  }
  .aside .item {
    margin: 20px;
    height: 200px;
  }
  .aside .item1 {
    background-color: orange;
  }
  .aside .item2 {
    background-color: skyblue;
  }
  .aside .item3 {
    background-color: tomato;
  }
  .box {
    width: 100px;
    height: 100px;

    background-color: black;
  }
</style>
<body>
  <!--app应用 相当于根组件Root-->
  <div id="app">
    <!--头部,相当于子组件Header-->
    <div class="header">{{header}}</div>
    <!--content start-->
    <div class="content clearfix">
      <!--main start-->
      <!--main,相当于子组件Main-->
      <div class="main">
        <div class="article1">{{article1}}</div>
        <div class="article2">{{article2}}</div>
      </div>
      <!--aside start-->
      <!--aside,相当于子组件Aside-->
      <div class="aside">
        <div class="item item1">{{item1}}</div>
        <div class="item item2">{{item2}}</div>
        <div class="item item3">{{item3}}</div>
      </div>
    </div>
    <!--content end-->
  </div>

  <script type="module">
    import { createApp } from "https://unpkg.com/vue@3/dist/vue.esm-browser.js";
    const app = createApp({
      data() {
        return {
          header: "网站头部",
          article1: "文章版块一",
          article2: "文章版块二",
          item1: "侧边栏一",
          item2: "侧边栏一",
          item3: "侧边栏一",
        };
      },
    }).mount("#app");
  </script>
</body>

以上代码显示的最终效果如下:

image-20230424161345805

# 9.2、模块化开发

TIP

index.html页面,拆分成index.html、main.js、app.js三个文件,并把html标签放到app.js文件中

  • index.html
<style>
  html,
  body {
    margin: 0;
    padding: 0;
    font-size: 30px;
    text-align: center;
    background-color: #ddd;
  }
  .header {
    height: 100px;
    background-color: plum;
  }
  .clearfix::after {
    content: "";
    display: block;
    clear: both;
  }
  .main {
    width: 70%;
    float: left;
  }
  .main .article1,
  .main .article2 {
    height: 310px;
    margin: 20px;
    background-color: skyblue;
  }
  .main.article2 {
    background-color: khaki;
  }
  .aside {
    width: 30%;
    float: right;
  }
  .aside .item {
    margin: 20px;
    height: 200px;
  }
  .aside .item1 {
    background-color: orange;
  }
  .aside .item2 {
    background-color: skyblue;
  }
  .aside .item3 {
    background-color: tomato;
  }
</style>

<body>
  <!--app应用-->
  <div id="app"></div>
  <script type="module" src="./main.js"></script>
</body>
  • main.js
import { createApp } from "https://unpkg.com/vue@3/dist/vue.esm-browser.js";
import App from "./app.js";
const app = createApp(App).mount("#app");
  • app.js 根组件,相当于前面图中的 Root 组件
export default {
  data() {
    return {
      header: "网站头部",
      article1: "文章版块一",
      article2: "文章版块二",
      item1: "侧边栏一",
      item2: "侧边栏一",
      item3: "侧边栏一",
    };
  },
  template: `
        <div class="header">
            {{header}}
        </div>
        
        <!--content start-->
        <div class="content clearfix">

            <div class="main">
                <div class="article1">{{article1}}</div>
                <div class="article2">{{article2}}</div>
            </div>
            
            <div class="aside">
                <div class="item item1">{{item1}}</div>
                <div class="item item2">{{item2}}</div>
                <div class="item item3">{{item3}}</div>
            </div>
            
        </div>
         <!--content end-->
    `,
};

# 9.3、组件拆分

TIP

接下来,我们利用组件化思想,对app.js文件中的内容进行插分,拆成 Header,Main、Aside 三个子组件。

一个完整的组件由三部分组成:HTML、CSS、JS,我可以考虑先把 HTML 和 JS 先拆出去,CSS 随后再说。

项目结构调整为如下:

vue
├─ app.js
├─ components
│  ├─ aside.js
│  ├─ header.js
│  └─ main.js
├─ index.html
└─ main.js

每个文件中对应的内容如下:

每个组件中有自己的逻辑和模板,唯独没有 CSS,这是一个非常严重的问题,后续会解决

  • components/header.js
export default {
  data() {
    return {
      header: "网站头部",
    };
  },
  template: ` <div class="header">{{header}}</div>  `,
};
  • components/main.js
export default {
  data() {
    return {
      article1: "文章版块一",
      article2: "文章版块二",
    };
  },
  template: `
            <div class="main">
                <div class="article1">{{article1}}</div>
                <div class="article2">{{article2}}</div>
            </div>
        `,
};
  • components/aside.js
export default {
  data() {
    return {
      item1: "侧边栏一",
      item2: "侧边栏一",
      item3: "侧边栏一",
    };
  },
  template: `
    <div class="aside">
        <div class="item item1">{{item1}}</div>
        <div class="item item2">{{item2}}</div>
        <div class="item item3">{{item3}}</div>
    </div>
        `,
};

进过拆分后,app.js文件内容如下:

export default {
  data() {
    return {
      // 数据全部拆出去了
    };
  },
  template: `
       	<!--拆到Header组件中去了-->

        <!--content start-->
        <div class="content clearfix">

          	<!--拆到Main组件中去了-->

          	<!--拆到Aside组件中去了-->

        </div>
    `,
};

# 9.4、根组件中使用子组件

TIP

接下来,我们需要在app.js(相当于 Root 根组件)中来使用子组件。使用子组件以下三步:

  • 导入组件,组件名一般要求首字母大写,或驼峰名命
  • 在根组件的components选项中注册子组件
  • template模板中使用组件

app.js文件内容修改成如下

// 导入组件
import Header from "./components/header.js";
import Main from "./components/main.js";
import Aside from "./components/aside.js";

export default {
  data() {
    return {};
  },
  // 注册组件
  components: {
    Header, // 相当于 Header:Header
    Main,
    Aside,
  },
  // 模板中使用组件
  template: `
     	<!--Header组件-->
        <Header></Header>
        <div class="content clearfix">
         	<!--Main组件-->
            <Main></Main>
            <!--Aside组件-->
            <Aside></Aside>
        </div>
    `,
};

最后,程序按预期渲染,得到了想要的效果。

# 9.5、非单文件组件产生问题

TIP

相对于接下来要讲的单文件组件化开发,我们把上面组件写一个单独 JS 文件的方式称为非单文件组件开发,显然以上方式存在以下两个严重的问题:

  • 不支持 CSS,CSS 样式没有拆分出去,本质上是没有完全实现组件化的,因为一个完整的组件需要HTML,CSS,JS三部分
  • 在 JS 中通过template字符串模板来书写 HTML 非常不方便,代码显得很丑陋,缺乏语法高亮和代码提示。

非单文件组件在实际开发中并几乎不用,这里只是借用他来了解组件,并引出单文件组件。

Vue 也要考虑到了这个问题,为我们提供单文件组件(SFC- Single-File Component)开发,我们在下一个小节就会讲到。

# 10、总结

TIP

理解什么是组件

  • Vue 中组件是用一个 JS 对象来表示,组件本质就是一组 DOM 的封装
  • 组件最大的优点就是可以使用复用。

区分根组件与子组件

  • 一个页面可以有多个根组件,但一般只有一个根组件。根组件要使用createApp()方法来渲染
  • 子组件需要被嵌套在其它子组件或根组件中使用,一个完整的应用就是有多个子组件与根组件嵌套组合而成。

如何注册全局组件与局部组件

  • 通过app.component()方法来组册一个全局组件,全局组件在当前应用中全局可用
  • 直接定义的组件即为局部组件,局部组件需要在使用他的父组件的components选项中注册,然后才可以使用,并且仅能在注册的父组件中使用

组件的命名

  • 组件名如果为单个英文字母,首字母推荐大写
  • 组件名如果为多个英文单词,建议命名采用 PascalCase 格式
  • 全局组件命名,可以采用-短横线来分隔多个单词

# 六、单文件组件开发(SFC)

TIP

我们一般会将 Vue 组件定义在一个单独的 .vue结尾的文件中,这被叫做单文件组件 (英文 Single-File Component,简称 SFC)。

他是一种特殊的文件格式,使我们能够将一个 Vue 组件的模板、逻辑与样式封装在单个文件中。

# 1、定义一个单文件组件

TIP

下面是一个单文件组件的示例

  • 组件的逻辑,即 JS 代码写在<script>标签中,通过export default对外暴露组件选项对象
  • 组件的模板,即 HTML 模板代码,写在<template>标签中
  • 组件的样式,即 CSS 样式,写在<style>标签中
<!--App.vue文件内容-->

<!--逻辑-->
<script>
export default {
  data() {
    return {
      message: "Hello World!",
    };
  },
};
</script>

<!--模板-->
<template>
  <div class="box">{{ message }}</div>
</template>

<!--样式-->
<style>
.box {
  width: 100px;
  height: 100px;
  background-color: skyblue;
}
</style>

注意事项

  • 每个 *.vue 文件最多可以包含一个顶层 <template>
  • 每个 *.vue 文件最多可以包含一个 <script> 块。(使用<script setup>的情况除外)
  • 每个 *.vue 文件可以包含多个 <style> 标签

# 2、使用单文件组件

TIP

单文件组件本质上还是一个组件,只是改变了组件的书写方式。

非单文件组件是以一个包含 Vue 特定选项的 JavaScript 对象来定义,而单文件组件是将 Vue 组件定义在一个以.vue结尾的的文件中。

所以单文件组件的使用方式和非单文件组件的使用方式完全一样。

  • 单文件组件作为应用的根组件,同样需要通过createApp()方法来渲染,最终调用mount()方法,将渲染后的 DOM 添加到页面中对应容器。
// 导入根组件
import App from "./App.vue";
// 创建应用实例
const app = createApp(App);
  • 单文件组件注册为全局组件使用,同样需要调用app.components()方法先注册,然后就可以在当前 Vue 应用中全局使用
import Count from "./src/components/Count.vue";
// 注册为全局组件
app.component("Count", Count);
  • 单文件组件注册为局部组件使用,需要在父组件的components选项中注册,然后就可以在当前父组件中使用。
<script>
  // 导入Hello 组件
  import Hello from "./src/components/Hello.vue";
  export default {
    // 注册组件
    components: {
      Hello,
    },
  };
</script>

<template>
  <!--使用组件-->
  <Hello />
</template>

# 3、单文件组件应用 - 1

以下是一个单文件组件的应用示例,项目的结构如下:

vueTest
├─ App.vue  // 根组件
├─ index.html  // 项目首页,入口文件
├─ main.js  // 创建Vue应用实例的入口JS
└─ src
   └─ components  // 组件目录
      ├─ Count.vue  // Count组件
      └─ Hello.vue  // Hello组件

每个文件的内容如下:

  • index.html 项目主页,入口文件
<div id="app"></div>
<script type="module" src="./main.js"></script>
  • main.js创建 Vue 应用实例的入口 JS
import { createApp } from "vue";
// 导入根组件
import App from "./App.vue";
// 创建应用实例
const app = createApp(App);
// 注册全局组件
import Count from "./src/components/Count.vue";
app.component("Count", Count);
// 挂载
app.mount("#app");
  • App.vue 根组件
<script>
  // 导入组件
  import Hello from "./src/components/Hello.vue";
  export default {
    // 注册组件
    components: {
      Hello,
    },
  };
</script>

<template>
  <!--使用组件,以下Count组件为全局组件-->
  <Count />
  <Count />
  <Hello />
</template>
  • Count.vue Count 组件
<script>
  export default {
    data() {
      return {
        num: 0,
      };
    },
  };
</script>
<template>
  <div>num的值:{{ num }}</div>
  <button @click="num++">点击num+1</button>
</template>
  • Hello.vue Hello 组件
<script>
  export default {
    data() {
      return {
        message: "Hello Vue",
      };
    },
  };
</script>

<template>
  <div>{{ message }}</div>
</template>

注:

以上 Vue 项目是没有正常访问,因为单文件组件是将 Vue 组件定义在一个以.vue结尾的文件,而浏览器是没有办法识别.vue结尾的文件。

所以我们需要借助构建工具来完成项目的构建,这里我们借助Vite构建工具。

# 4、利用 Vite 打包 Vue 项目

TIP

温馨提示

  • 如果你对 Vite 不了解,也没有关系,你可以按我提供的步骤来操作即可。
  • 如果你想对 Vite 有一定的了解,推荐阅读下一章节《Vite 前端构建工具》
  • Vite 官网地址:https://cn.vitejs.dev/ (opens new window)
  • 在当前项目根目录下,执行npm init -y初始化项目的package.json文件
  • 执行以下命令安装 Vite 前端构建工具
npm i vite -D
  • 构建工具打包时,默认是不识别.vue文件,所以需要安装@vitejs/plugin-vue插件来对.vue文件解析
npm i -D @vitejs/plugin-vue

安装以上插件,会自动帮我们安装好 vue3.0 以上的包,不需要再手动执行npm i vue@3.2.47命令安装 Vue 包。

  • 在项目的根目录下新建vite.config.js文件,此文件为 Vite 的配置文件,我们需要配置 Vite 的插件,告诉 Vite 在构建打包时,启用插件来识别.vue的文件。

vite.config.js文件内容如下

// 导入vue插件包
import vue from "@vitejs/plugin-vue";
// defineConfig方法,让配置文件有智能提示功能
import { defineConfig } from "vite";
export default defineConfig({
  // 配置vue插件。
  plugins: [vue()],
});
  • 最后,我们可以执行以下命令,启动开发服务
npx vite # 启动开发服务

命令执行后,会在终端显示以下信息

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h to show help

点击上面的网址,就可以查看到开发的项目,如下:

GIF2023-5-1013-59-10

# 5、Volar 插件

TIP

Vue Language Features (Volar) 插件是 Vscode 开发 vue3 必备插件,针对.vue文件提供语法高亮提示,非常好用。

按以下步骤安装即可:

image-20230424200542086

# 6、单文件组件应用 - 2

TIP

我们把最开始利用 Vue 开发的以下应用,改写成 Vue 单文件组件化开发。

image-20230424181810634

# 6.1、项目结构调整

原来项目的结构调整后如下

vue
├─ App.vue  // 根组件
├─ components  // 子组件目录
│  ├─ Aside.vue  // Aside 组件
│  ├─ Header.vue // Header 组件
│  └─ Main.vue // Main 组件
├─ index.html  // 项目主页,入口文件
├─ main.js  // 创建Vue应用实例的入口JS
├─ package-lock.json
├─ package.json
└─ vite.config.js  // Vite配置文件

具体每个文件的内容如下

  • index.html 项目入口文件
<style>
  html,
  body {
    margin: 0;
    padding: 0;
    font-size: 30px;
    text-align: center;
    background-color: #ddd;
  }
</style>
<!--app应用-->
<div id="app"></div>
<script type="module" src="./main.js"></script>
  • main.js 文件
// import { createApp } from "https://unpkg.com/vue@3/dist/vue.esm-browser.js";

// 以上方式使用Vue不支持Vue单文件组件化开发,所以要改成以下方式,以下方式要采用构建工作打包,并执行npm i vue@3.2.47 来安装vue包
import { createApp } from "vue";
import App from "./App.vue";
const app = createApp(App).mount("#app");
  • App.vue 根组件
<script>
// 导入局部子组件
import Header from "./components/header.vue";
import Main from "./components/main.vue";
import Aside from "./components/aside.vue";
export default {
  data() {
    return {};
  },
  // 注册子组件
  components: {
    Header,
    Main,
    Aside,
  },
};
</script>

<template>
  <!--使用组件-->
  <Header></Header>
  <div class="content clearfix">
    <Main></Main>
    <Aside></Aside>
  </div>
</template>

<style>
.clearfix::after {
  content: "";
  display: block;
  clear: both;
}
</style>
  • Header.vue Header 子组件
<script>
export default {
  data() {
    return {
      header: "网站头部",
    };
  },
};
</script>

<template>
  <div class="header">{{ header }}</div>
</template>

<style>
.header {
  height: 100px;
  background-color: plum;
}
</style>
  • Main.vue Main 子组件
<script>
export default {
  data() {
    return {
      article1: "文章版块一",
      article2: "文章版块二",
    };
  },
};
</script>

<template>
  <div class="main">
    <div class="article1">{{ article1 }}</div>
    <div class="article2">{{ article2 }}</div>
  </div>
</template>

<style>
.main {
  width: 70%;
  float: left;
}
.main .article1,
.main .article2 {
  height: 310px;
  margin: 20px;
  background-color: skyblue;
}
.main.article2 {
  background-color: khaki;
}
</style>
  • Aside.vue Aside 子组件
<script>
export default {
  data() {
    return {
      item1: "侧边栏一",
      item2: "侧边栏一",
      item3: "侧边栏一",
    };
  },
};
</script>

<template>
  <div class="aside">
    <div class="item item1">{{ item1 }}</div>
    <div class="item item2">{{ item2 }}</div>
    <div class="item item3">{{ item3 }}</div>
  </div>
</template>

<style>
.aside {
  width: 30%;
  float: right;
}
.aside .item {
  margin: 20px;
  height: 200px;
}
.aside .item1 {
  background-color: orange;
}
.aside .item2 {
  background-color: skyblue;
}
.aside .item3 {
  background-color: tomato;
}
</style>

注:

以上采用单文件组件发开发,项目是没有办法跑起来的,因为.vue结尾的文件浏览器是不能识别的。

所以需要借助构建工具来编译.vue文件,对项目进行打包构建。

# 6.2、Vite 构建项目

TIP

  • 在当前项目根目录下,执行npm init -y初始化项目的package.json文件
  • 执行npm i vite -D命令安装 Vite 前端构建工具
  • 执行npm i -D @vitejs/plugin-vue命令安装@vitejs/plugin-vue插件来对.vue文件解析
  • 在项目根目录下创建Vite配置文件vite.config.js,同时配置 Vite 的插件,告诉 Vite 在构建打包时,启用插件来识别.vue的文件。

vite.config.js文件内容如下

// 导入vue插件包
import vue from "@vitejs/plugin-vue";
// defineConfig方法,让配置文件有智能提示功能
import { defineConfig } from "vite";
export default defineConfig({
  // 配置vue插件。
  plugins: [vue()],
});
  • 最后,我们可以执行以下 vite 的命令,启动开发服务
npx vite # 启动开发服务

命令执行后,会在终端显示以下信息

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h to show help

点击上面的网址,就可以查看到开发的项目,如下:

image-20230424181810634

  • 如果想要对生产环境打包,并预览项目,可以执行以下两个命令
npx vite build # 生产环境打包
npx vite preview # 预览生产环境打包后的项目

# 7、总结

TIP

单文件组件

  • 单文件组件本质上是一个特殊的文件格式,使我们能够将一个 Vue 组件的模板、逻辑与样式封装在单个文件中。
  • 单文件组件开发必需配合打包工具才能使用,因为浏览器本身不能识别.vue的文件。
  • Vite 打包工具,需要安装@vitejs/plugin-vue插件,并在配置文件中配置好该插件,在打包时才能正确识别.vue文件。

组件的拆分

  • 了解并尝试把一个大的项目拆分成多个组件,你可以自己尝试把上面项目的AsideMain组件再进一步的拆分成多个子组件。

单文件组件的优点

Volar 插件的安装

当我们使用单文件组件来编写 Vue 代码时,默认是没有高亮的语法提示的,所以需要我们在VSCode中安装Volar插件,这样在.vue文件中编写代码时,就有了高亮的语法提示。

# 七、Vue 脚手架创建 Vue 项目

TIP

通过 Vue 脚手架来使用 Vue,是我们需要重点掌握的,在实际开发中,这是我们首选的开发方式,同时他支持 Vue 的单文件组件(SFC)化开发。

这种方式,需要我们先安装node,同时了解 npm 包管理工具的使用。因为前面我们学习过 node 和 npm,所以这里就不再做相关介绍。

在学习使用 Vue 脚手架创建 Vue 项目前,建议先学习下一章要讲到的 Vite 构建工具。

# 1、安装 Vue

TIP

官方安装教程地址:https://cn.vuejs.org/guide/quick-start.html (opens new window),建议大家以后在安装 Vue 时,以官方教程为主。

因为技术在更新时,相关的命令也有可能更新

执行以下命令,进行安装

npm init vue@latest

这一指令将会安装并执行 create-vue (opens new window),它是 Vue 官方的项目脚手架工具。会帮我们把开发项目时每次都需要做的一些重复的繁琐的工作给做掉,从而提高我们的开发效率。

你将会看到一些诸如 TypeScript 和测试支持之类的可选功能提示:

✔ Project name: … <your-project-name>
✔ Add TypeScript? … No / Yes        # 是否使用 TypeScript
✔ Add JSX Support? … No / Yes		# 是否使用 JSX
✔ Add Vue Router for Single Page Application development? … No / Yes   # 是否安装 Vue Router开发一个单页面应用
✔ Add Pinia for state management? … No / Yes    # 是否添加Pinia组件来进行状态管理
✔ Add Vitest for Unit testing? … No / Yes      # 是否添加Vitest来进行单元测试
✔ Add Cypress for both Unit and End-to-End testing? … No / Yes # 是否添加Cypress来进行单元测试和端到端测试
✔ Add ESLint for code quality? … No / Yes  		# 是否添加ESLint来进行代码质量检查
✔ Add Prettier for code formatting? … No / Yes   # 为代码格式添加 Prettier  (格式化代码插件)

Scaffolding project in ./<your-project-name>...
Done.

在这里,我们在执行npm init vue@latest命令后,全部选择No就好。因为我们刚开始学习,对于安装的第三方依赖是什么以及有什么用,都不了解。等后我们学完 Vue 后,大家就明白这些第三方依赖是什么,根据需要选择安装就好。

安装好后,会出现以下内容。

cd vue-project   # 进入到项目目录 vue-project
npm install   # 安装package.json中的开发和生产依赖
npm run dev  # 在开发环境下运行vue项目

项目创建后,并没有给我们安装相应的依赖包,所以需要我们通过执行cd vue-project命令进到项目根目录,然后执行npm install安装package.json文件中提供的开发和生产依赖包。

package.json文件内容如下,帮我们配置好了scripts命令脚本、开发与生产依赖需要的包。

{
  "name": "vue-project",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "vue": "^3.2.47"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^4.0.0",
    "vite": "^4.1.4"
  }
}

最后,我们再执行npm run dev命令,会出现如下内容

➜  Local:   http://localhost:5173/
➜  Network: use --host to expose
➜  press h to show help

我们在浏览器中打开上面 Local字段后面对应的http://localhost:5173/地址,就可以看创建好的 Vue 项目。预览效果如下,说明 Vue 启动成功。

image-20230419184333433

# 2、项目目录结构

TIP

通过npm init vue@latest命令创建好的项目的目录结构

如下:

image-20230423193114618

以下表格中序号对应上图中标出来的序号,我们针对标出来的文件或文件夹做相关说明

文件编号 文件或文件夹名 说明
1 .vscode 针对当前项目,.vscode目录存放当前项目相关的 VSCode 配置文件的目录
2 extensions.json 推荐当前项目使用的 VSCode 插件
3 public public 下的资源会在项目生产环境打包时,被完整复制到目标目录的根目录下。如果你有以下资源,应该放在 public 目录下:
不会被源码引用(例如 robots.txt
必须保持原有文件名(没有经过 hash)
... 或者你压根不想引入该资源,只是想得到其 URL
4 src 源码资源目录,开发阶段的源码都是放在这个目录下
5 assets 静态资源目录,默认存放图片、CSS、JS 等
6 components 公共组件目录,用来存放 Vue 子组件
7 .vue文件 vue 的单文件组件(简单 SFC)
8 App.vue Vue 的根组件
9 main.js 创建 Vue 应用实例的入口 JS
10 .gitignore 在用 git 提交项目时,需要过滤掉的一些文件
11 index.html 项目的主页(也是打包的入口文件)
12 README.md 项目的说明文档
13 vite.config.js Vite 前端构建工具的配置文件。

# 3、vite.config.js 配置文件

针对vite.config.js配置文件中相关内容,给大家做个简单的介绍

// 导入node:url模块,这个模块是用来处理路径的
import { fileURLToPath, URL } from "node:url";

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve: {
    // 配置路径别名
    alias: {
      "@": fileURLToPath(new URL("./src", import.meta.url)),
    },
  },
});
  • import.meta.url

    • import.meta是一个给 JavaScript 模块暴露特定上下文的元数据属性的对象。它包含了这个模块的信息,比如说这个模块的 URL。
    • import.meta
    • import.meta.url为当前模块的完整 url 地址,如C:\Users\EDY\Desktop\vue\vue-project\vite.config.js
  • new URL() 创建路径

new URL(input[, base])
  • input<string>要解析的绝对/相对的URL,如果input是相对路径,则需要base。如果是绝对路径,则忽略base
  • base<string>|<URL>如果input不是绝对路径,则为要解析的基本URL
new URL('./src', import.meta.url)
// 得到一个URL对象,对象的结构如下

URL {
  href: 'file:///C:/Users/EDY/Desktop/vue/vue-project/src',
  origin: 'null',
  protocol: 'file:',
  username: '',
  password: '',
  host: '',
  hostname: '',
  port: '',
  pathname: '/C:/Users/EDY/Desktop/vue/vue-project/src',
  search: '',
  searchParams: URLSearchParams {},
  hash: ''
}
  • fileURLToPath 文件url 转换为 本地文件路径,其参数可以是 url 也可以是 url 对象。
fileURLToPath(new URL("./src", import.meta.url));
// 最后输出的结果为:
// C:\Users\EDY\Desktop\vue\vue-project\src

# 4、项目核心代码分析

index.html文件

作为项目的入口文件,里面通过 ES 模块化的方式加载了main.js,同时提供了 Vue 项目根组件容器#app,即:后续所有组件(本质是 DOM)都会被添加到#app容器内。

<div id="app"></div>
<script type="module" src="/src/main.js"></script>

main.js 文件

// 导入vue的 createApp方法
import { createApp } from "vue";
// 导入 Vue的根组件
import App from "./App.vue";
// 导入main.css,最终main.css中内容,会被添加到index.html页面的<style>标签中
import "./assets/main.css";
// 创建应用实例,将应用实例挂载到#app 容器中
createApp(App).mount("#app");

App.vue 文件 (最终被转换成 DOM)添加到#app容器中

<!--组件逻辑,以下采用的是Vue3支持的组合式写法-->
<script setup>
// 导入子组件
import HelloWorld from "./components/HelloWorld.vue";
import TheWelcome from "./components/TheWelcome.vue";
</script>

<!--组件模板-->
<template>
  <header>
    <img
      alt="Vue logo"
      class="logo"
      src="./assets/logo.svg"
      width="125"
      height="125"
    />

    <div class="wrapper">
      <!--添加子组件  本质是DOM-->
      <HelloWorld msg="You did it!" />
    </div>
  </header>

  <main>
    <!--添加子组件 本质是DOM-->
    <TheWelcome />
  </main>
</template>

<style scoped>
/* 组件的css样式,内容省略*/
</style>
上次更新时间: 6/11/2023, 1:44:02 AM

大厂最新技术学习分享群

大厂最新技术学习分享群

微信扫一扫进群,获取资料

X