# 快速掌握Vue3 (opens new window)

# setup 函数

setup() 函数是 vue3 中,专门为组件提供的新属性。它为我们使用 vue3 的 Composition API 新特性提供了统一的入口. 它取代了 2.x中的beforeCreate 和Created钩子。

setup函数会在以下方法前执行

  • Components
  • Props
  • Data
  • Methods
  • Computed Properties
  • Lifecycle methods

setup接受两个参数,props和 context,需要返回数据才能在模板中使用

  • props: 用来接收 props 数据
  • context 用来定义上下文
  • 返回值: return {}, 返回响应式数据, 模版中需要使用的函数
代码示例
<template>
  <h1>{{name}}</h1>
</template>
<script>
import { ref,  watch } from 'vue'
export default {
  setup(props, ctx) {
    const name = ref('hello')
    setTimeout(() => {
      name.value = 'world'
    }, 2000)

    watch(name,(nv) => {
      console.log(nv)
    })
    return {
      name
    }
  }
}
</script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# reactive 函数

reactive() 函数接收一个普通对象,返回一个响应式的数据对象。在setup函数创立完之后,需要返回整个对象,不能解构。 在模板中使用的时候会自动解开。

代码示例
<template>
  <h1>{{state.text}}</h1>
  <h1>{{text}}</h1>
</template>

<script>
import { ref,  watch, reactive } from 'vue'
export default {
  setup(props, ctx) {
    const state = reactive({
      text: 'hello world'
    })
    setTimeout(() => {
      state.text = "It's responsive"
    }, 1000)
    return {
      state,
      ...state
    }
  }
}
</script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# ref() 函数

对数据进行包装,然后返回一个响应式对象。要在js内读取返回的值需要执行 xxx.value的方式,然而在模板中使用可以不用写value。

代码示例
<template>
  <h1>{{text}}</h1>
</template>

<script>
import { ref } from 'vue'
export default {
  setup(props, ctx) {
    const text = ref('hello world')
    setTimeout(() => {
      text.value = "It's responsive"
    }, 1000)
    return {
      text
    }
  }
}
</script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# isRef() 函数

isRef() 用来判断某个值是否为 ref() 创建出来的对象

# toRefs() 函数

toRefs() 函数可以将 reactive() 创建出来的响应式对象,转换为普通的对象. 在setup函数解构也不会丢失响应式。对比reactive

DETAILS
<template>
  <h1>{{text}}</h1>
</template>
<script>
import { reactive, toRefs } from 'vue'
export default {
  setup(props, ctx) {
    const state = reactive({
      text: 'hello world'
    })
    const refState = toRefs(state)
    setTimeout(() => {
      refState.text.value = "It's responsive"
    }, 1000)
    return {
      ...refState
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# computed()

该函数用来创造计算属性,和过去一样,它返回的值是一个ref对象。 里面可以传方法,或者一个对象,对象中包含set()、get()方法

DETAILS
<template>
  <h1>{{fullName}}</h1>
</template>

<script>
import { computed } from 'vue'
export default {
  setup(props, ctx) {
    const state = reactive({
      first: 'hello ',
      last: 'world'
    })

    const fullName = computed(() => {
      return  state.first + state.last
    })

    setTimeout(() => {
      state.first = "hello, ...."
    }, 1000)
    return {
      fullName,
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# watch() 函数

watch 函数用来侦听特定的数据源,并在回调函数中执行副作用。默认情况是懒执行的,侦听的源数据变更时才执行回调。

  • reactive定义的数据,watch的时候需要写方法,并返回对应属性的值
  • ref定义的数据可以直接watch
代码片段

<template>
  <h1>{{state.first + state.last}}</h1>
  <h1>{{count}}</h1>
  <button @click="change">state.first change</button>
  <button @click="count++">count++</button>
</template>

<script>
import { watch,reactive,ref } from 'vue'
export default {
  setup(props, ctx) {
    const state = reactive({
      first: 'hello ',
      last: 'world'
    })
    function change() {
      state.first = Math.random()
    }
    // 不能!!不能!!不能!!
    watch(state.first, (nv) => {
      console.log('state.first 数据是', state.first)
    })
    // 必须!! 必须!!必须!!

    watch(() => state.first, (nv) => {
      console.log('state.first 数据是', state.first)
    })
    // 或者
    const count = ref(0)
    watch(count, (nv) => {
      console.log('count 数据是', count.value)
    })
    watch([() => state.first,count], ([nfirst,ncount],[ofirst, ocount]) => {
      console.log('集中观测===========')
      console.log('nfirst:>> ', nfirst);
      console.log('ocount :>> ', ocount);
       console.log('ofirst :>> ', ofirst);
      console.log('ncount :>> ', ncount);

    })
    return {
      state,
      count,
      change
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

# watchEffect() 函数

# LifeCycle Hooks(新的生命后期)

新的生命周期钩子需要从vue中按需引入,这些钩子只能在setup函数中使用

vue2 vue3
beforeCreate和created setup
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeDestroy onBeforeUnmount
destroyed onUnmounted
activated onActivated
deactivated onDeactivated
errorCaptured onErrorCaptured
- onRenderTracked
- onRenderTriggred

# Template refs

可以在模板中设置ref属性的值获取真实的DOM,

DETAILS
<template>
  <h1 ref="h1RefDom">hello</h1>
</template>
<script>
import { watch,reactive,ref, onMounted } from 'vue'
export default {
  setup(props, ctx) {
    const h1RefDom = ref(null)
    console.log('hRefDom :>> ', h1RefDom.value);
    onMounted(() => {
      // 获取到真实DOM
      console.log('hRefDom :>> ', h1RefDom.value.innerHTML);
    })
    return {
      h1RefDom
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# vue 的全局配置

vue2.x中通过 Vue.prototype.$xxx =xxx

vue3.x中通过 app.config.globalProperties.$xxx 来配置

  • errorHandler,指定一个处理函数,来处理组件渲染方法执行期间以及侦听器抛出的未捕获错误。这个处理函数被调用时,可获取错误信息和应用实例。
  • warnHandler,为 Vue 的运行时警告指定一个自定义处理函数。注意这只会在开发环境下生效,在生产环境下它会被忽略。
  • globalProperties, 添加可以在应用程序内的任何组件实例中访问的全局 property。属性名冲突时,组件的 property 将具有优先权。
  • isCustomElement
  • optionMergeStrategies,为自定义选项定义合并策略。
  • performance:设置为 true 以在浏览器开发工具的 performance/timeline 面板中启用对组件初始化、编译、渲染和更新的性能追踪

# Sharing State - 共享状态

DETAILS
usePromise.js
import { ref } from "vue";

export default function usePromise(fn) {
  const results = ref(null);
  // is PENDING
  const loading = ref(false);
  const error = ref(null);

  const createPromise = async (...args) => {
    loading.value = true;
    error.value = null;
    results.value = null;
    try {
      results.value = await fn(...args);
    } catch (err) {
      error.value = err;
    } finally {
      loading.value = false;
    }
  };
  return { results, loading, error, createPromise };
}

App.vue
import { ref, watch } from "vue";
import usePromise from "./usePromise";
export default {
  setup() {
    const searchInput = ref("");
    function getEventCount() {
      return new Promise((resolve) => {
        setTimeout(() => resolve(3), 1000);
      });
    }

    const getEvents = usePromise((searchInput) => getEventCount());

    watch(searchInput, () => {
      if (searchInput.value !== "") {
        getEvents.createPromise(searchInput);
      } else {
        getEvents.results.value = null;
      }
    });

    return { searchInput, ...getEvents };
  },
};


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

# Suspense 组件

DETAILS
HelloWorld.vue

<template>
  <h1>{{ msg }} {{count}}</h1>
</template>
<script>
import { ref } from 'vue'
async function getResult() {
  return new Promise((resolve => {
    setTimeout(() => {
      resolve(Math.random()) 
    }, 1000)
  }))
}
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  },
  async setup(props) {
    const count = ref(0);
    count.value = await getResult()
    return {
      count
    }
  }
}
</script>

App.vue
<template>
  <div>
    <div v-if="error">Uh oh .. {{ error }}</div>
    <Suspense>
      <template #default>
        <div>
          <HelloWorld :msg='"dsfs"'/>
        </div>
      </template>
      <template #fallback> Loading.... </template>
    </Suspense>
  </div>
</template>

<script>
import { ref, onErrorCaptured, defineAsyncComponent } from "vue";

import HelloWorld from "./components/HelloWorld.vue";
export default {
  components: {
    HelloWorld,
  },

  setup() {
    const error = ref(null);
    onErrorCaptured((e) => {
      error.value = e;
      // 阻止错误继续冒泡
      return true;
    });
    return { error };
  },
};
</script>


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

# Teleport

Teleport (opens new window)

DETAILS
App.vue

<template>
 <div style="position: relative;">
    <h3>Tooltips with Vue 3 Teleport</h3>
    <div>
      <modal-button></modal-button>
    </div>
  </div>
</template>

<script>
import ModalButton from './components/ModalButton'
export default {
  components :{
    ModalButton
  }
}
</script>


components/ModalButton.vue
<template>
<teleport to="body">
  <button @click="modalOpen = true">
      Open full screen modal!
  </button>
  <div v-if="modalOpen" class="modal">
    <div>
      I'm a modal! 
      <button @click="modalOpen = false">
        Close
      </button>
    </div>
  </div>
  </teleport>
</template>
<script>
import { ref } from 'vue';
export default {
  setup() {
    const modalOpen = ref(false)
    return {
      modalOpen
    }
  }
}
</script>
<style>
.modal{
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.2)
}
</style>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

# 案例

# 生态

生态 地址
官网 https://v3.cn.vuejs.org/
源码 https://github.com/vuejs/vue-next
vite构建器 https://github.com/vitejs/vite
脚手架 https://cli.vuejs.org/
vue-router-next https://github.com/vuejs/vue-router-next
vuex4.0 https://github.com/vuejs/vuex/tree/4.0
vant2.x https://vant-contrib.gitee.io/vant/next/#/zh-CN/
Ant Design of Vue 2.x https://2x.antdv.com/docs/vue/introduce-cn/
element-plus https://element-plus.org/#/zh-CN
模板解析器 https://vue-next-template-explorer.netlify.app/
awesome-vite 官方集合 https://github.com/vitejs/awesome-vite

# 好文推荐

  1. 快速掌握Vue3 (opens new window)
  2. Vue3官方教程 视屏 (opens new window)
  3. Vue3官方教程 笔记 (opens new window)
  4. Vue 3 Deep Dive with Evan You (opens new window)