Vue3.0的安装和启动
与其直接安装Vue3,不如克隆Vue -new -Webpack -preview项目,它将为我们提供包括Vue3在内的最小的Webpack设置。
$ git clone [https://github.com/vuejs/vue-next-webpack-preview.git](https://github.com/vuejs/vue-next-webpack-preview.git) vue3-experiment
$ cd vue3-experiment
$ npm i
一旦克隆并安装了NPM模块,我们所需要做的就是删除样板文件并创建一个新的main.js文件,这样就可以从头创建Vue 3应用程序了。
$ rm -rf src/*
$ touch src/main.js
packages目录中包含了vue3.0所有功能:
`├── packages
│ ├── compiler-core <span style="color:#999988">*\# 所有平台的编译器*</span>
│ ├── compiler-dom <span style="color:#999988">*\# 针对浏览器而写的编译器*</span>
│ ├── reactivity <span style="color:#999988">*\# 数据响应式系统*</span>
│ ├── runtime-core <span style="color:#999988">*\# 虚拟 DOM 渲染器 ,Vue 组件和 Vue 的各种API*</span>
│ ├── runtime-dom <span style="color:#999988">*\# 针对浏览器的 runtime。其功能包括处理原生 DOM API、DOM 事件和 DOM 属性等。*</span>
│ ├── runtime-test <span style="color:#999988">*\# 专门为测试写的runtime*</span>
│ ├── server-renderer <span style="color:#999988">*\# 用于SSR*</span>
│ ├── shared <span style="color:#999988">*\# 帮助方法*</span>
│ ├── template-explorer
**│ └── vue** <span style="color:#999988">***\# 构建vue runtime \+ compiler***</span>`
compiler
compiler-core主要功能是暴露编译相关的API以及baseCompile方法
compiler-dom基于compiler-core封装针对浏览器的compiler (对浏览器标签进行处理)
runtime
runtime-core 虚拟 DOM 渲染器、Vue 组件和 Vue 的各种API
runtime-test将DOM结构格式化成对象,方便测试
runtime-dom 基于runtime-core编写的浏览器的runtime (增加了节点的增删改查,样式处理等),返回render、createApp方法
reactivity
单独的数据响应式系统,核心方法reactive、effect、 ref、computed
Composition API
除了渲染函数API和作用域插槽语法之外的所有内容都将保持不变,或者通过兼容性构建让其与2.x保持兼容。
在这里可以在2.x 中引入@vue/composition-api,使用vue 3.0新特性。
初始化项目
1.安装vue-cli3
npm install -g @vue/cli
2.创建项目
vue create vue3
3.在项目中安装composition-api
npm install @vue/composition-api --save
4.在使用任何 @vue/composition-api 提供的能力前,必须先通过 Vue.use( ) 进行安装
import Vue from 'vue'
import VueCompositionApi from '@vue/composition-api'
Vue.use(VueCompositionApi)
setup()
Vue3 引入一个新的组件选项,setup(),它会在一个组件实例被创建时,初始化了props之后调用,会接收到初始的props作为参数。
`<span style="color:#333333">**export**</span> <span style="color:#333333">**default**</span> {
props: {
name: String
},
setup(props) {
console.log(props.name)
}
}`
传进来的props是响应式的,当后续props发生变动时它也会被框架内部同步更新,但对于用户代码来说,它时不可修改的。
同时,setup() 执行时机相当于2.x 生命周期beforeCreate之后,且在created之前:
export default {
beforeCreate() {
console.log('beforeCreate')
},
setup() {
console.log('setup')
},
created() {
console.log('created')
}
}
// 打印结果
// beforeCreate
// setup
// created
在setup()中this不再是vue实例对象了,而是undefined,可以理解为此时机实例还未创建。在setup()第二个参数是上下文参数,提供了一些2.x this上有用的属性。
export default {
setup(props, context) {
console.log('this: ', this)
console.log('context: ', context)
}
}
// 打印结果
// this: undefined
// context: {
// attrs: Object
// emit: f()
// isServer: false
// listeners: Object
// parent: VueComponent
// refs: Object
// root: Vue
// slots: {}
// ssrContext: undefined
// }
类似data() , setup()可以返回一个对象,这个对象上的属性会暴露给模板的渲染上下文:
<template>
<div>{{ name }}</div>
</template>
<script>
export default {
setup() {
return {
name: 'zs'
}
}
}
</script>
reactive()
等价于vue 2.x中的Vue.observable()函数, vue 3.x中提供了reactive()函数,用来创建响应式的数据对象。
引用数据直接改变不会让模板响应更新渲染:
<template>
<div>count: {{state.count}}</div>
</template>
<script>
export default {
setup() {
const state = { count: 0 }
setTimeout(() => {
state.count++
})
return { state }
}
}
// 一秒后页面没有变化
</script>
reactive创建的响应式数据对象,在对象属性发生变化时,模板是可以响应更新渲染的:
<template>
<div>count: {{state.count}}</div>
</template>
<script>
import { reactive } from '@vue/composition-api'
export default {
setup() {
const state = reactive({ count: 0 })
setTimeout(() => {
state.count++
}, 1000)
return { state }
}
}
// 一秒后页面数字从0变成1
</script>
ref()
在 Javascript 中,原始类型(如 String,Number)只有值,没有引用。如果在一个函数中返回一个字符串变量,接收到这个字符串的代码只会获得一个值,是无法追踪原始变量后续的变化的。
<template>
<div>count: {{state.count}}</div>
</template>
<script>
import { ref } from '@vue/composition-api'
export default {
setup() {
const count = 0
setTimeout(() => {
count++
}, 1000)
return { count }
}
}
// 页面没有变化
</script>
包装对象 ref() 的意义就在于提供一个让我们能够在函数之间以引用的方式传递任意类型值的容器。这有点像 React Hooks 中的 useRef —— 但不同的是 Vue 的包装对象同时还是响应式的数据源。有了这样的容器,我们就可以在封装了逻辑的组合函数中将状态以引用的方式传回给组件。组件负责展示(追踪依赖),组合函数负责管理状态(触发更新)。
ref() 返回的是一个 value reference (包装对象)。一个包装对象只有一个属性:.value ,该属性指向内部被包装的值。包装对象的值可以被直接修改。
<script>
import { ref } from '@vue/composition-api'
export default {
setup() {
const count = ref(0)
console.log('count.value: ', count.value)
count.value++ // 直接修改包装对象的值
console.log('count.value: ', count.value)
}
}
// 打印结果:
// count.value: 0
// count.value: 1
</script>
当包装对象被暴露给模版渲染上下文,或是被嵌套在另一个响应式对象中的时候,它会被自动展开 (unwrap) 为内部的值:
<template>
<div>ref count: {{count}}</div>
</template>
<script>
import { ref } from '@vue/composition-api'
export default {
setup() {
const count = ref(0)
console.log('count.value: ', count.value)
return {
count // 包装对象 value 属性自动展开
}
}
}
</script>
也可以用 ref() 包装对象作为 reactive() 创建的对象的属性值,同样属性值 ref() 包装对象也会模版上下文被展开:
<template>
<div>reactive ref count: {{state.count}}</div>
</template>
<script>
import { reactive, ref } from '@vue/composition-api'
export default {
setup() {
const count = ref(0)
const state = reactive({count})
return {
state // 包装对象 value 属性自动展开
}
}
}
</script>
在 Vue 2.x 中用实例上的 $refs 属性获取模版元素中 ref 属性标记 DOM 或组件信息,在这里用 ref() 包装对象也可以用来引用页面元素和组件;
<template>
<div><p ref="text">Hello</p></div>
</template>
<script>
import { ref } from '@vue/composition-api'
export default {
setup() {
const text = ref(null)
setTimeout(() => {
console.log('text: ', text.value.innerHTML)
}, 1000)
return {
text
}
}
}
// 打印结果:
// text: Hello
</script>