在2020年的9月19日,万众期待的Vue3终于发布了正式版,命名为“One Piece”。
它也带来了很多新的特性:

  • 更好的性能
  • 更小的包体积
  • 更好的TypeScript集成
  • 更优秀的API设计

vue3变化

源码层面

  • 源码通过monorepo的形式来管理源代码:
    • Mono:单个
    • Repo:repository仓库
    • 主要是将许多项目的代码存储在同一个repository中
    • 这样做的目的是多个包本身相互独立,可以有自己的功能逻辑、单元测试等,同时又在同一个仓库下方便管理
    • 而且模块划分的更加清晰,可维护性、可扩展性更强
  • 源码使用TypeScript来进行重写:
    • 在Vue2.x的时候,Vue使用Flow来进行类型检测
    • 在Vue3.x的时候,Vue的源码全部使用TypeScript来进行重构,并且Vue本身对TypeScript支持也更好

性能层面

  • 使用Proxy进行数据劫持
    • 在Vue2.x的时候,Vue2是使用Object.defineProperty来劫持数据的getter和setter方法的
    • 这种方式一致存在一个缺陷就是当给对象添加或者删除属性时,是无法劫持和监听的
    • 所以在Vue2.x的时候,不得不提供一些特殊的API,比如$set或$delete,事实上都是一些hack方法,也增加了开发者学习新的API的成本
    • 而在Vue3.x开始,Vue使用Proxy来实现数据的劫持
  • 删除了一些不必要的API
    • 移除了实例上的$on, $off 和 $once
    • 移除了一些特性:如filter、内联模板等
  • 包括编译方面的优化
    • 生成Block Tree、Slot编译优化、diff算法优化

新的API

  • 由Options API 到 Composition API
    • 在Vue2.x的时候,我们会通过Options API来描述组件对象
    • Options API包括data、props、methods、computed、生命周期等等这些选项;
    • 存在比较大的问题是多个逻辑可能是在不同的地方
      • 比如created中会使用某一个method来修改data的数据,代码的内聚性非常差
    • Composition API可以将相关联的代码放到同一处进行处理,而不需要在多个Options之间寻找;
  • Hooks函数增加代码的复用性
    • 在Vue2.x的时候,我们通常通过mixins在多个组件之间共享逻辑
    • 但是有一个很大的缺陷就是 mixins也是由一大堆的Options组成的,并且多个mixins会存在命名冲突的问题
    • 在Vue3.x中,我们可以通过Hook函数,来将一部分独立的逻辑抽取出去,并且它们还可以做到是响应式的

如何使用

  • Vue的本质,就是一个JavaScript的库
  • 那么安装和使用Vue的方式
    • 方式一:在页面中通过CDN的方式来引入
    • 方式二:下载Vue的JavaScript文件,并且自己手动引入
    • 方式三:通过npm包管理工具安装使用它
    • 方式四:直接通过Vue CLI创建项目,并且使用它

vue3的基础demo

  1. <div id="app"></div>
  2. <script src="https://unpkg.com/vue@next"></script>
  3. <script>
  4. // vue的相关代码
  5. const obj = {
  6. template: "<h2>hello cos..</h2>",
  7. };
  8. const app = Vue.createApp(obj);
  9. // 将app挂载到id为app的元素上
  10. app.mount("#app");
  11. </script>
  12. // 也可以将这一部分内容写成链式调用的形式
  13. Vue.createApp({
  14. template: `<h2>hello cos..</h2>`,
  15. }).mount("#app");

上面这种引入方式涉及到CDN这个概念,大家应该都经常提到CDN, 但又解释不清楚具体是怎么回事,了解一下:

什么是CDN呢?

维基百科:

CDN称之为内容分发网络(Content Delivery Network或Content Distribution Network,缩写:CDN)

  • 它是指通过相互连接的网络系统,利用最靠近每个用户的服务器;
  • 更快、更可靠地将音乐、图片、视频、应用程序及其他文件发送给用户;
  • 来提供高性能、可扩展性及低成本的网络内容传递;

常用的CDN服务器可以大致分为两种:

  • 自己的CDN服务器:需要购买自己的CDN服务器,目前阿里、腾讯、亚马逊、Google等都可以购买CDN服务器;
  • 开源的CDN服务器:国际上使用比较多的是unpkg、JSDelivr、cdnjs;比如vue就使用了unpkg;

自己的话:

CDN其实就是在不同的城市都有服务器,当用户需要数据的时候我们通过距离用户最近的服务器给用户返回数据。
这里面用户访问的被称作边缘节点,如果边缘节点中没有对应的数据就会到其父节点中查找,如果父节点也没有,则到源站中查找。查找到的数据先存储到父节点,然后到边缘节点,后续如果有人再通过该边缘节点访问相同的数据就可以直接访问到了;

声明式和命令式

  • 原生开发和Vue开发的模式和特点是完全不同的,涉及到两种不同的编程范式:
    • 命令式编程和声明式编程;
    • 命令式编程关注的是 “how to do”,声明式编程关注的是 “what to do”,由框架(机器)完成 “how”的过程;
  • 在原生的实现过程中,我们是如何操作的呢?
    • 我们每完成一个操作,都需要通过JavaScript编写一条代码,来给浏览器一个指令;
    • 这样的编写代码的过程,我们称之为命令式编程;
    • 在早期的原生JavaScript和jQuery开发的过程中,我们都是通过这种命令式的方式在编写代码的;
  • 在Vue的实现过程中,我们是如何操作的呢?
    • 我们在createApp传入的对象中声明需要的内容,模板template、数据data、方法methods;
    • 这样的编写代码的过程,我们称之为是声明式编程;
    • 目前Vue、React、Angular的编程模式,我们称之为声明式编程;

MVVM模型

  • MVC和MVVM都是一种软件的体系结构
    • MVC是Model – View –Controller的简称,是在前期被使用非常广泛的架构模式,比如iOS、前端;
    • MVVM是Model-View-ViewModel的简称,是目前非常流行的架构模式;
  • 通常情况下,我们也经常称Vue是一个MVVM的框架。
  • Vue官方其实有说明,Vue虽然并没有完全遵守MVVM的模型,但是整个设计是受到它的启发的。

image.png

计数器案例

原生编写

  1. <button class="decrement">-</button>
  2. <span class="counter">0</span>
  3. <button class="increment">+</button>
  4. <script>
  5. const counterEl = document.querySelector(".counter");
  6. const decrementEl = document.querySelector(".decrement");
  7. const incrementEl = document.querySelector(".increment");
  8. let counter = 100;
  9. counterEl.innerHTML = counter;
  10. incrementEl.addEventListener("click", () => {
  11. counter += 1;
  12. counterEl.innerHTML = counter;
  13. });
  14. decrementEl.addEventListener("click", () => {
  15. counter -= 1;
  16. counterEl.innerHTML = counter;
  17. });
  18. </script>

vue3尝鲜

注:
a.vue3里面模板根元素可以是多个了。
b.以后vue3中会有新的写法,也不这样去写,我这里主要是做演示。

  1. Vue.createApp({
  2. template: `
  3. <div>
  4. <h2>{{message}}</h2>
  5. <button @click="decrement">-</button>
  6. <span>{{counter}}</span>
  7. <button @click="increment">+</button>
  8. </div>
  9. `,
  10. data() {
  11. return {
  12. message: "计数器。",
  13. counter: 100,
  14. };
  15. },
  16. methods: {
  17. increment() {
  18. this.counter++;
  19. },
  20. decrement() {
  21. this.counter--;
  22. },
  23. },
  24. }).mount("#app");

template属性

  • 在使用createApp的时候传入了一个对象,接下来我们详细解析一下之前传入的属性分别代表什么含义。
  • template属性:表示的是Vue需要帮助我们渲染的模板信息:
    • 目前我们看到它里面有很多的HTML标签,这些标签会替换掉我们挂载到的元素(比如id为app的div)的innerHTML;
    • 模板中有一些奇怪的语法,比如 {{}},比如 @click,这些都是模板特有的语法;
    • 但是这个模板的写法有点过于别扭了,并且IDE很有可能没有任何提示,阻碍我们编程的效率。
  • Vue提供了两种方式:

    • 方式一:使用script标签,并且标记它的类型为 x-template;

      • 这种写法有语法提示,但是却没有语法高亮;
        1. <div id="app"></div>
        2. <script src="../js//vue.global.js"></script>
        3. <script type="x-template" id="cos-app">
        4. <div>
        5. <h2>{{message}}</h2>
        6. <button @click="decrement">-</button>
        7. <span>{{counter}}</span>
        8. <button @click="increment">+</button>
        9. </div>
        10. </script>
        11. <script>
        12. Vue.createApp({
        13. template: "#cos-app",
        14. data() {
        15. return {
        16. message: "计数器。",
        17. counter: 100,
        18. };
        19. },
        20. methods: {
        21. increment() {
        22. this.counter++;
        23. },
        24. decrement() {
        25. this.counter--;
        26. },
        27. },
        28. }).mount("#app");
        29. </script>
    • 方式二:使用任意标签(通常使用template标签,因为不会被浏览器渲染),设置id;

      • template元素是一种用于保存客户端内容的机制,该内容再加载页面时不会被呈现,但随后可以在运行时使用JavaScript实例化;
      • 这种写法有语法提示,也有语法高亮
      • 这个时候,在createApp的对象中,我们需要传入的template以 # 开头:
        • 如果字符串是以 # 开始,那么它将被用作 querySelector,并且使用匹配元素的 innerHTML 作为模板字符串;
          1. <div id="app"></div>
          2. <template id="cos-app">
          3. <div>
          4. <h2>{{message}}</h2>
          5. <button @click="decrement">-</button>
          6. <span>{{counter}}</span>
          7. <button @click="increment">+</button>
          8. </div>
          9. </template>
          10. <script src="../js//vue.global.js"></script>
          11. <script>
          12. Vue.createApp({
          13. template: "#cos-app",
          14. data() {
          15. return {
          16. message: "计数器。",
          17. counter: 100,
          18. };
          19. },
          20. methods: {
          21. increment() {
          22. this.counter++;
          23. },
          24. decrement() {
          25. this.counter--;
          26. },
          27. },
          28. }).mount("#app");
          29. </script>

data属性

  • data属性是传入一个函数,并且该函数需要返回一个对象:
    • 在Vue2.x的时候,也可以传入一个对象(虽然官方推荐是一个函数);
    • 在Vue3.x的时候,必须传入一个函数,否则就会直接在浏览器中报错;
  • data中返回的对象会被Vue的响应式系统劫持,之后对该对象的修改或者访问都会在劫持中被处理:
    • 所以我们在template中通过 {{counter}} 访问counter,可以从对象中获取到数据;
    • 所以我们修改counter的值时,template中的 {{counter}}也会发生改变;

methods属性

methods属性是一个对象,通常我们会在这个对象中定义很多的方法:

  • 这些方法可以被绑定到 template 模板中;
  • 在该方法中,我们可以使用this关键字来直接访问到data中返回的对象的属性;

模拟面试题

一、为什么不能使用箭头函数?
image.png

  • 在methods中要使用data返回对象中的数据:
    • 那么这个this是必须有值的,并且应该可以通过this获取到data返回对象中的数据
  • 那么我们这个this能不能是window呢?
    • 不可以是window,因为window中我们无法获取到data返回对象中的数据
    • 但是如果我们使用箭头函数,那么这个this就会是window了
  • 为什么是window呢?
    • 这里涉及到箭头函数使用this的查找规则,它会在自己的上层作用于中来查找this
    • 最终刚好找到的是script作用于中的this,所以就是window
  • this到底是如何查找和绑定的呢?

二、不使用箭头函数的情况下,this到底指向的是什么?
事实上Vue的源码当中就是对methods中的所有函数进行了遍历,并且通过bind绑定了this:
image.png

vue源码调试

  • 如果想要学习Vue的源码,比如看createApp的实现过程,应该怎么办呢?
  • 第一步:在GitHub上搜索 vue-next,下载源代码;
    • 这里推荐通过 git clone 的方式下载;
  • 第二步:安装Vue源码项目相关的依赖;
    • 执行 yarn install
  • 第三步:对项目执行打包操作
    • 执行yarn dev(执行前修改脚本)

image.png

  • 第四步:通过 packages/vue/dist/vue.global.js 调试代码