# vue3.0

Vue3.0 在北京时间2020年9月19 日凌晨,发布了 3.0 版本,代号:*One Piece* 

## 1.新特性

Vue 3 中一些需要关注的新功能包括:

- [组合式 API](https://v3.cn.vuejs.org/guide/composition-api-introduction.html)
- [Teleport 将组件内容传递到父组件以外的任何区域](https://v3.cn.vuejs.org/guide/teleport.html)
- [允许存在多个节点](https://v3.cn.vuejs.org/guide/migration/fragments.html)
- [触发组件选项 可以驼峰传递事件 发送事件前需要声明 可以对事件进行验证](https://v3.cn.vuejs.org/guide/component-custom-events.html)
- createRenderer可以跨平台渲染
- 单文件组件组合式 API 语法糖<script setup>
- [单文件组件状态驱动的 CSS 变量 (` style中的 `v-bind`)](https://v3.cn.vuejs.org/api/sfc-style.html#状态驱动的动态-css)
- [SFC <style scoped> 现在可以包含全局规则或只针对插槽内容的规则
- [Suspense](https://v3.cn.vuejs.org/guide/migration/suspense.html) 实验性
- 通过proxy进行数据劫持 vue2则是Object.defineProperty()
- 自定义指令的钩子函数有 7个 
- vue3中移除了过滤器
- vue3移除了eventBus

## 2.vue3和vue2有什么区别?(背诵)

1. 底层数据劫持的api发生了变化 在vue2里面使用的是Object.defineProperty,缺点是不能够劫持数组,vue3里面使用的是ES6的proxy,reflect可以完美响应数组变化,解决了vue2不能劫持数组的问题
2. 使用层面上来说,在vue3里面有'应用的概念',使用新的api createApp 用于解决配置项污染的问题
3. 一些新的特性,增加了组合式的api,组合式(composition)的api可以解决vue2中`选项式`代码量多的时候,焦点丢失,上下反复横跳的问题,是vue3里面非常非常大的革新
4. 可以有多个节点了,vue2里面只能有一个根节点,vue3里面可以有多个节点
5. 除了对props可以校验以外,vue3里面的事件也可以校验了
6. 新增了setup钩子,新增语法糖 script setup 编写逻辑更加简单方便,直接省略了选项
7. 移除了过滤器 移除了eventBus
8. style中也可以使用v-bind绑定数据了....

## 3.准备

安装最新版的调试工具

`vetur`不支持vue3 暂时禁用

替换成

`Volar` Vue Language Features

或者

修改 `vetur`的配置 取消`vetur>validation>template`的勾选

## 4.vue3.0体验 ✌🏻

### 3.1.一个船新 API

> `createApp`

Vue 2 没有“app”的概念,我们定义的应用只是通过 `new Vue()` 创建的根 Vue 实例。从同一个 Vue 构造函数创建的每个根实例**共享相同的全局配置**,因此局配置很容易意外地污染其他测试用例.可以调用 `createApp` 返回一个*应用实例*,一个 Vue 3 中的新概念,解决全局污染`配置`的问题

```js
import { createApp } from 'vue'

const app = createApp({})
```

### 3,2. vue2到vue3配置的变化

这块内容是对vue配置的迁移,可以回头过来再看

| 2.x 全局 API               | 3.x 实例 API (`app`)                                         |
| -------------------------- | ------------------------------------------------------------ |
| Vue.config                 | app.config                                                   |
| Vue.config.productionTip   | *移除* ([见下方](https://v3.cn.vuejs.org/guide/migration/global-api.html#config-productiontip-移除)) |
| Vue.config.ignoredElements | app.config.compilerOptions.isCustomElement ([见下方](https://v3.cn.vuejs.org/guide/migration/global-api.html#config-ignoredelements-替换为-config-iscustomelement)) |
| Vue.component              | app.component                                                |
| Vue.directive              | app.directive                                                |
| Vue.mixin                  | app.mixin                                                    |
| Vue.use                    | app.use ([见下方](https://v3.cn.vuejs.org/guide/migration/global-api.html#插件开发者须知)) |
| Vue.prototype              | app.config.globalProperties ([见下方](https://v3.cn.vuejs.org/guide/migration/global-api.html#vue-prototype-替换为-config-globalproperties)) |
| Vue.extend                 | *移除* ([见下方](https://v3.cn.vuejs.org/guide/migration/global-api.html#vue-extend-移除)) |

### 3.3.开始一个vue3项目

我们依旧采用最原始的html方式来体验一下vue3

在html中采用cdm的方式引入vue3

```html
<script src="https://unpkg.com/vue@next"></script>
```

```html
<div id="counter">
  name: {{ name }}
</div>
```

```js
const Counter = {
  data() {
    return {
      name: '李雷'
    }
  }
}

Vue.createApp(Counter).mount('#counter')
```

到目前为止,我们依旧觉得,和我们原来的vue2是差别不大的,但其实已经有变化了,现在在vue3.0中,必须明确的声明`data属性必须是一个函数`,另外就是不在需要去实例化Vue这个操作了,而是引入了一3个叫做`createApp`的方法,创建了一个子实例

## 5.过滤器

从 Vue 3.0 开始,过滤器已移除,且不再支持。取而代之的是,我们建议用方法调用或计算属性来替换它们。

## 6.组件

### 5.1.组件的定义

我们的一个vue3项目就已经跑起来啦,但是有一个感觉就是,就这? 多数人心中肯定有疑惑,就这??????和vue2的区别也不大嘛?的确,vue3本质上保留了vue2的编写特性,别着急,下面我们再来看看组件的定义

```html
 <div id="app">
        <h1>{{name}}</h1>
        <test></test>
 </div>

<template id="test">
    <fieldset>
        <legend>子组件</legend>
        <h1>{{msg}}</h1>
    </fieldset>
</template>
```

发现不同了吗?现在在`vue3.0中已经开始支持多节点了`,再也不同担心vue2.0单节点烦人的警告⚠️啦

```js
let app = Vue.createApp({
        data() {
            return {
                name: "李雷"
            }
        },
        mounted() {
            console.log(this.a)
            console.log(this)
        }
    })
    app.component("test", {
        template: "#test",
        data() {
            return {
                msg: "子组件"
            }
        }
    })
//注意这里的调用顺序
    app.mount("#app")
```

好了 定义完了,这里要注意的一个问题就是`app.mount`这个方法一定要在`组件注册完毕之后`调用

### 5.2.组件之间的传参

- 父子传参

- 子父传参

发现一个问题就是,和原来的vue2.0也没啥区别,感觉学了个寂寞,还有一个问题就是,兄弟传参哪儿去了?

#### bus总线

很遗憾的一个问题就是在vue3中`$on`,`$off` 和 `$once` 实例方法已被移除,组件实例不再实现事件触发接口。

处理方案可以被替换为使用外部的、实现了事件触发器接口的库,例如 [mitt](https://github.com/developit/mitt) 或 [tiny-emitter](https://github.com/scottcorgan/tiny-emitter)。

```js
// eventBus.js
import emitter from 'tiny-emitter/instance'

export default {
  $on: (...args) => emitter.on(...args),
  $once: (...args) => emitter.once(...args),
  $off: (...args) => emitter.off(...args),
  $emit: (...args) => emitter.emit(...args),
}
```

### 5.3.异步组件

#### 5.4.1.传统HTML基本使用

import(/*webpackChunkName:''*/'xxxx'')

在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了实现这个效果,Vue 有一个 `defineAsyncComponent` 方法:

```js
const { createApp, defineAsyncComponent } = Vue

const app = createApp({})

const AsyncComp = defineAsyncComponent(
  () =>
    new Promise((resolve, reject) => {
      resolve({
        template: '<div>I am async!</div>'
      })
    })
)

app.component('async-example', AsyncComp)
```

#### 2.使用模块化

如你所见,此方法接受一个返回 `Promise` 的工厂函数。从服务器检索组件定义后,应调用 Promise 的 `resolve` 回调。你也可以调用 `reject(reason)`,来表示加载失败。

你也可以在工厂函数中返回一个 `Promise`,把 webpack 2 及以上版本和 ES2015 语法相结合后,我们就可以这样使用动态地导入:

```js
import { defineAsyncComponent } from 'vue'

const AsyncComp = defineAsyncComponent(() =>
  import('./components/AsyncComponent.vue')
)

app.component('async-component', AsyncComp)
```

当[在局部注册组件](https://v3.cn.vuejs.org/guide/component-registration.html#局部注册)时,你也可以使用 `defineAsyncComponent`:

```js
import { createApp, defineAsyncComponent } from 'vue'

createApp({
  // ...
  components: {
    AsyncComponent: defineAsyncComponent(() =>
      import('./components/AsyncComponent.vue')
    )
  }
})
```

#### 3.与 Suspense 一起使用

异步组件在默认情况下是*可挂起*的。这意味着如果它在父链中有一个 ``,它将被视为该 `` 的异步依赖。在这种情况下,加载状态将由 `` 控制,组件自身的加载、错误、延迟和超时选项都将被忽略。

通过在其选项中指定 `suspensible: false`,异步组件可以退出 `Suspense` 控制,并始终控制自己的加载状态。

在正确渲染组件之前进行一些异步请求是很常见的事。组件通常会在本地处理这种逻辑,绝大多数情况下这是非常完美的做法。

该 `` 组件提供了另一个方案,允许将等待过程提升到组件树中处理,而不是在单个组件中。

一个常见的[异步组件](https://v3.cn.vuejs.org/guide/component-dynamic-async.html#异步组件)用例:

```vue
<template>
   <div id="app">
          {{msg}}
          <Suspense>
              <template #fallback>
                  <h1>正在加载...</h1>
              </template>
              <template #default>
                  <async-com></async-com>
              </template>
          </Suspense>

      </div>
</template>

<script>
    const { createApp, defineAsyncComponent } = Vue;

    let com = defineAsyncComponent(() => new Promise(resolve => {
        setTimeout(() => {
            resolve({ template: '<h1 >异步组件</h1>' })
        }, 2000)
    }))

    let app = createApp({
        data() {
            return {
                msg: "vue3"
            }
        },
        components: {
            asyncCom: com
        }
    })


    app.mount('#app')
</script>
```

`` 组件有两个插槽。它们都只接收一个直接子节点。`default` 插槽里的节点会尽可能展示出来。如果不能,则展示 `fallback` 插槽里的节点。

## 7.使用vue-cli开启一个vue3项目

1. 升级最新vue-cli

升级到5

```bash
npm i @vue/cli -g
```

2. 创建一个项目

```bash
vue create 项目
```

3.兼容vue-cli3.0

## 8.指令

vue3的指令几乎和vue2.0保持是一致的,但是增加了以下新特性

### 1.组件上可以绑定多个model了

- `组件可以绑定多个model`
- `v-model` prop 和事件默认名称已更改:
  - We can prop:`value` -> `modelValue`;
  - 事件:`input` -> `update:modelValue`
- 可以自定义 `v-model` 修饰符

v-model在元素上的写法没有变化,但是在组件中的写法有了新特性,写法就是在指令后面跟上指令参数,指令参数就是绑定的数据名称

**下面是一个例子**

```html
<body>
    <div id="app">
        <h1>{{name}}</h1>
        <h1>{{name2}}</h1>
        <child v-model:msg="name" v-model:msg2="name2"></child>
    </div>
</body>

</html>
<template id="child">
    <fieldset>
        <legend>子组件</legend>
        <input type="text" :value="msg" @input="trans">
        <input type="text" :value="msg2" @input="trans2">
    </fieldset>
</template>
<script>
    let app = Vue.createApp({
        data() {
            return {
                name: "李雷",
                name2:"韩梅梅"
            }
        }
    })
    app.component("child", {
        props:['msg','msg2'],
        template: "#child",
        methods:{
            trans(e){
                //update后面的参数要和model绑定的一直
                this.$emit("update:msg",e.target.value)
                console.log(this.msg2)
            },
            trans2(e){
                this.$emit("update:msg2",e.target.value)
                console.log(this.msg2)
            }
        }
    })
    app.mount("#app")
</script>
```

> 对于所有不带参数的 `v-model`,请确保分别将 prop 和 event 命名更改为 `modelValue` 和 `update:modelValue`

### 2.指令优先级的变化

Vue.js 中使用最多的两个指令就是 `v-if` 和 `v-for`,因此开发者们可能会想要同时使用它们。虽然不建议这样做,但有时确实是必须的2.x 版本中在一个元素上同时使用 `v-if` 和 `v-for` 时,`v-for` 会优先作用。

3.x 版本中 `v-if` 总是优先于 `v-for` 生效

```vue
 <ul>
            <!-- vue3 v-if和v-for同时使用的时候,可以把v-for迁移到template标签上 -->
            <template v-for="item of arr" :key="item.id">
                <li v-if="!item.isComplete">{{ item.todo }}</li>
            </template>
        </ul>
```

### 3.v-on.native修饰符

`v-on` 的 `.native` 修饰符已被移除。

- 2.x 语法

默认情况下,传递给带有 `v-on` 的组件的事件监听器只能通过 `this.$emit` 触发。要将原生 DOM 监听器添加到子组件的根元素中,可以使用 `.native` 修饰符:

```html
<my-component
  v-on:close="handleComponentEvent"
  v-on:click.native="handleNativeClickEvent"
/>
```

- 3.x 语法

`v-on` 的 `.native` 修饰符已被移除。同时,[新增的 `emits` 选项](https://v3.cn.vuejs.org/guide/migration/emits-option.html)允许子组件定义真正会被触发的事件。

因此,对于子组件中*未*被定义为组件触发的所有事件监听器,Vue 现在将把它们作为原生事件监听器添加到子组件的根元素中 (除非在子组件的选项中设置了 `inheritAttrs: false`)。

```html
<my-component
  v-on:close="handleComponentEvent"
  v-on:click="handleNativeClickEvent"
/>
```

```vue
MyComponent.vue
<script>
  export default {
    emits: ['close']
  }
</script>
```

### 4.自定义指令

自定义指令基本和vue2.0是保持一致的,但是有不同的地方就是,指令钩子函数增加到了7个,下面是一个基本用法

```js
const app = Vue.createApp({})
// 注册一个全局自定义指令 `v-focus`
app.directive('focus', {
  // 当被绑定的元素挂载到 DOM 中时……
  mounted(el) {
    // 聚焦元素
    el.focus()
  }
})
```

局部注册的方式是在app中

```js
directives: {
  focus: {
    // 指令的定义
    mounted(el) {
      el.focus()
    }
  }
}
```

不同的是vue3.0的指令钩子函数增加到了7个分别是

- `created`:在绑定元素的 attribute 或事件监听器被应用之前调用
- `beforeMount`:当指令第一次绑定到元素并且在挂载父组件之前调用。
- `mounted`:在绑定元素的父组件被挂载后调用。
- `beforeUpdate`:在更新包含组件的 VNode 之前调用。

提示

- `updated`:在包含组件的 VNode **及其子组件的 VNode** 更新后调用。
- `beforeUnmount`:在卸载绑定元素的父组件之前调用
- `unmounted`:当指令与元素解除绑定且父组件已卸载时,只调用一次。

### 7.小结

1. `vue3.0中已经开始支持多节点了`
2. 在vue3中`data属性必须是一个函数`
3. `vue3里面有app的概念,可以防止染全局配置`
4. `属性的劫持,使用了proxy`
5. `移除了过滤器`
6. `移除了eventBus`
7. `vue3.0中组件上可以绑定多个v-model了 而且参数和事件有变化`
8. `在vue3.0中 v-if的优先级要高于v-for`
9. `vue3.0中移除了native修饰符`

## 9.使用构建工具vite

[Vite](https://cn.vitejs.dev/) 是一个 web 开发构建工具,由于其原生 ES 模块导入方式,可以实现闪电般的冷服务器启动。

通过在终端中运行以下命令,可以使用 Vite 快速构建 Vue 项目。

使用 npm:

```bash
# npm 6.x
$ npm init vite@latest <project-name> --template vue

# npm 7+,需要加上额外的双短横线
$ npm init vite@latest <project-name> -- --template vue

$ cd <project-name>
$ npm install
$ npm run dev
```

或者 yarn:

```bash
$ yarn create vite <project-name> --template vue
$ cd <project-name>
$ yarn
$ yarn dev
```

## 模块化样式

### 1.选择器支持插槽了

 在单文件组件范围的样式中提供更一致的自定义CSS扩展。

```html
<style scoped>
/* 深选择器 */
::v-deep(.foo) {}
/* 简写 */
:deep(.foo) {}

/* 针对槽内容 */
::v-slotted(.foo) {}
/* 简写 */
:slotted(.foo) {}

/* 一个全局规则 */
::v-global(.foo) {}
/* 简写 */
:global(.foo) {}
</style>
```

### 2.深度选择器的改进

- `>>>` 和/deep/` 支持被弃用

- `::v-deep` 组合被弃用:

  ```css
  /* qiyong */
  ::v-deep .bar {}
  ```

  使用这种方式来替代上面的写法

  ```css
  ::v-deep(.bar) {}
  ```

  上述将编译为

  ```css
  [v-data-xxxxxxx] .bar {}
  ```

## ref

在 Vue 2 中,在 `v-for` 中使用的 `ref` attribute 会用 ref 数组填充相应的 `$refs` property。当存在嵌套的 `v-for` 时,这种行为会变得不明确且效率低下。

在 Vue 3 中,此类用法将不再自动创建 `$ref` 数组。要从单个绑定获取多个 ref,请将 `ref` 绑定到一个更灵活的函数上 (这是一个新特性):

```html
<div v-for="item in list" :ref="setItemRef"></div>
```

### 结合选项式 API:

```333 js
export default {
  data() {
    return {
      itemRefs: []
    }
  },
  methods: {
    setItemRef(el) {
      if (el) {
        this.itemRefs.push(el)
      }
    }
  },
  beforeUpdate() {
    this.itemRefs = []
  },
  updated() {
    console.log(this.itemRefs)
  }
}
```

### 结合组合式 API:

```js
import { onBeforeUpdate, onUpdated } from 'vue'

export default {
  setup() {
    let itemRefs = []
    const setItemRef = el => {
      if (el) {
        itemRefs.push(el)
      }
    }
    onBeforeUpdate(() => {
      itemRefs = []
    })
    onUpdated(() => {
      console.log(itemRefs)
    })
    return {
      setItemRef
    }
  }
}
```



> 注意:

- `itemRefs` 不必是数组:它也可以是一个对象,其 ref 可以通过迭代的 key 被设置。
- 如有需要,`itemRef` 也可以是响应式的,且可以被侦听。

## emits选项**新增**

### 概述

Vue 3 现在提供一个 `emits` 选项,和现有的 `props` 选项类似。这个选项可以用来定义一个组件可以向其父组件触发的事件。

### 2.x 的行为

在 Vue 2 中,你可以定义一个组件可接收的 prop,但是你无法声明它可以触发哪些事件:

```vue
<template>
  <div>
    <p>{{ text }}</p>
    <button v-on:click="$emit('accepted')">OK</button>
  </div>
</template>
<script>
  export default {
    props: ['text']
  }
</script>
```

### 3.x 的行为

和 prop 类似,现在可以通过 `emits` 选项来定义组件可触发的事件:

```vue
<template>
  <div>
    <p>{{ text }}</p>
    <button v-on:click="$emit('accepted')">OK</button>
  </div>
</template>
<script>
  export default {
    props: ['text'],
    emits: ['accepted']
  }
</script>
```

该选项也可以接收一个对象,该对象允许开发者定义传入事件参数的验证器,和 `props` 定义里的验证器类似。

### 迁移策略

强烈建议使用 `emits` 记录每个组件所触发的所有事件。

这尤为重要,因为我们[移除了 `.native` 修饰符](https://v3.cn.vuejs.org/guide/migration/v-on-native-modifier-removed.html)。任何未在 `emits` 中声明的事件监听器都会被算入组件的 `$attrs`,并将默认绑定到组件的根节点上。

### [#](https://v3.cn.vuejs.org/guide/migration/emits-option.html#示例)示例

对于向其父组件透传原生事件的组件来说,这会导致有两个事件被触发:

```vue
<template>
  <button v-on:click="$emit('click', $event)">OK</button>
</template>
<script>
export default {
  emits: [] // 不声明事件
}
</script>
```

当一个父级组件拥有 `click` 事件的监听器时:

```html
<my-button v-on:click="handleClick"></my-button>
```

该事件现在会被触发*两次*:

- 一次来自 `$emit()`。
- 另一次来自应用在根元素上的原生事件监听器。

现在你有两个选项:

1. 正确地声明 `click` 事件。当你真的在 `` 的事件处理器上加入了一些逻辑时,这会很有用。
2. 移除透传的事件,因为现在父组件可以很容易地监听原生事件,而不需要添加 `.native`。适用于你只想透传这个事件。

### emit验证

Emit也可以像props那样进行验证了

```vue
const app = createApp({})

// 数组语法
app.component('todo-item', {
  emits: ['check'],
  created() {
    this.$emit('check')
  }
})

// 对象语法
app.component('reply-form', {
  emits: {
    // 没有验证函数
    click: null,

    // 带有验证函数
    submit: payload => {
      if (payload.email && payload.password) {
        return true
      } else {
        console.warn(`Invalid submit event payload!`)
        return false
      }
    }
  }
})
```

 

原文地址:http://www.cnblogs.com/ht955/p/16869577.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性