天才第一步首先你得安装Vue3
方案一:
克隆源代码 https://github.com/vuejs/vue-next
找到package.json
不难看出在scripts
中各种启动命令项,同时也发现到作者使用rollup
进行构建项目。 下载依赖npm install
后,执行npm run dev
编译不出意外会生成一个目录packages/vue/dist/vue.global.js
文件,这就是vue3核心文件。 启动项目npm run serve
不难发现启动一个端口为localhost:5000的页面。再跟目录中建立一个文件夹test
里面创建两个文件分别:index.html
、index.js
index.html
Vue.js
index.js
const {reactive, computed} = Vue;
const App = {
template: `
{{state.message}}, double is: {{state.double}}
`,
setup() {
const state = reactive({
message: 10,
double: computed(() => state.message * 3)
})
function increment() {
state.message++
}
return {
state,
increment
}
}
}
Vue.createApp(App).mount("#app");
进入html页面中不难发现成功运行一个vue3
小demo
方案二:
npm install -g @vue/cli
// 如果安装失败可以切换至淘宝镜像
cnpm install -g @vue/cli
vue -V
// @vue/cli 4.3.1
vue create vue-next
cd vue-next-test
vue add vue-next
npm run serve
安装并且启动完成我们来看看
Vue2
和Vue3
中的生命周期钩子非常相似–我们仍然可以访问相同的钩子,并且我们仍然希望将它们用于相同的用例。
但是,随着Composition API的引入,我们访问这些钩子的方式已经改变。
到本文结束时,您将了解在Vue3中使用生命周期钩子的新方法,并开始编写更好的代码。
我们走吧!
Composition API : https://learnvue.co/2019/12/a-first-look-at-vue3-a-vue-composition-api-tutorial/
如果您还不了解,则Vue3 Composition API
带有setup()
方法。此方法封装了我们的大多数组件代码,并处理了反应性,生命周期钩子等。
简而言之,Composition API
使我们能够更好地将代码组织为更多的模块化功能,而不是根据属性的类型(methods, computed, data)进行分离。
在旧的beforeCreate
钩子之后和创建的钩子之前立即调用setup
方法。因此,我们不再需要这两个钩子,我们可以简单地将本应有的代码放在setup()
方法中。
与Vue3中的大多数功能一样,生命周期钩子是我们必须导入到项目中的东西。这是为了帮助使项目尽可能轻巧。
我们导入生命周期钩子的方式是这样的。
import { onMounted, onUpdated, onUnmounted } from'vue'
除beforeCreate
和created
之外,我们可以在设置方法中访问9个旧的生命周期钩子
onBeforeMount –在安装开始之前调用
onMounted –在安装组件时调用
onBeforeUpdate –在反应性数据更改时以及重新呈现之前调用
onUpdated –重新渲染后调用
onBeforeUnmount –在销毁Vue实例之前调用
onUnmount –在实例被销毁后调用
onActivated –当激活kept-alive
组件时调用
onDeactivated –当kept-alive
组件被停用时调用
onErrorCaptured –从子组件捕获错误时调用
当我们导入它们并在我们的代码中访问它们时,它将看起来像这样。
import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onActivated, onDeactivated, onErrorCaptured } from'vue'
exportdefault {
setup() {
onBeforeMount(() => {
// ...
})
onMounted(() => {
// ...
})
onBeforeUpdate(() => {
// ...
})
onUpdated(() => {
// ...
})
onBeforeUnmount(() => {
// ...
})
onUnmounted(() => {
// ...
})
onActivated(() => {
// ...
})
onDeactivated(() => {
// ...
})
onErrorCaptured(() => {
// ...
})
}
}
方便的Vue2到Vue3生命周期映射直接来自Vue3 Composition API文档,我认为这是了解事物将如何变化以及如何使用它们的最有用的方法之一。
beforeCreate -> use setup()
created -> use setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
errorCaptured -> onErrorCaptured
我们还可以在Vue3中使用两个全新的钩子来进行调试。他们是:
onRenderTracked
onRenderTriggered
这两个事件都带有一个DebuggerEvent,它使我们能够知道是什么导致了Vue实例中的重新渲染。
exportdefault {
onRenderTriggered(e) {
debugger
// 检查哪个依赖项导致组件重新呈现
}
}
如果您已经有Vue的经验,那么这将是一个非常容易的切换。只需导入钩子并将它们包括在设置方法中即可。
我希望这可以帮助您更多地了解Vue3中的更改以及如何在项目中实现它们。
随着Vue3 Alpha
的发布,许多开发人员都在尝试新的变化——最大的变化是 Composition API
。
该setup
功能是新的组件选项。它充当在组件内部使用Composition API的入口点。
setup
创建组件实例时,在初始道具解析后立即调用。在生命周期方面,它在beforeCreate
挂载之前被调用。
如果setup
返回一个对象,则该对象的属性将合并到组件模板的渲染上下文中:
{{ count }} {{ object.foo }}
请注意,setup
在模板中访问时,从ref返回的引用将自动解包,因此不需要.value
模板。
setup
还可以返回一个render函数,该函数可以直接使用在同一作用域中声明的反应状态:
import { h, ref, reactive } from'vue'
exportdefault {
setup() {
const count = ref(0)
const object = reactive({ foo: 'bar' })
return() => h('div', [count.value, object.foo])
}
}
该函数将接收到的props作为其第一个参数:
exportdefault {
props: {
name: String
},
setup(props) {
console.log(props.name)
}
}
请注意,此props
对象是反应性的-即,当传入新的道具时会更新该对象,使用watchEffect
或可以观察到并做出反应watch
:
exportdefault {
props: {
name: String
},
setup(props) {
watchEffect(() => {
console.log(`name is: ` + props.name)
})
}
}
但是,请勿破坏props
对象,因为它会失去反应性:
exportdefault {
props: {
name: String
},
setup({ name }) {
watchEffect(() => {
console.log(`name is: ` + name)
})
}
}
props
在开发过程中,该对象对于用户区代码是不可变的(如果用户代码尝试对其进行更改,则会发出警告)。
第二个参数提供了一个上下文对象,该对象公开了先前this
在2.x API中公开的属性的选择性列表:
const MyComponent = {
setup(props, context) {
context.attrs
context.slots
context.emit
}
}
attrs
并且slots
是内部组件实例上对应值的代理。这可以确保即使在更新后它们也始终显示最新值,以便我们可以对它们进行结构分解,而不必担心访问陈旧的引用:
const MyComponent = {
setup(props, { attrs }) {
// 在以后的阶段可能会被调用的函数
function onClick() {
console.log(attrs.foo) // 保证是最新的参考
}
}
}
取得一个对象并返回原始对象的reactive
代理。这等于2.x的Vue.observable()
。
reactive
转换是“deep”的:它影响所有嵌套的属性。在基于ES2015代理的实现中,返回的代理不等于原始对象。建议仅与reactive
代理一起使用,并避免依赖原始对象。
function reactive(raw: T): T
接受一个内部值并返回一个 reactive
且可变的ref
对象。ref对象具有.value
指向内部值的单个属性
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
如果将一个对象分配为ref
的值,则该reactive
方法会使该对象具有高度的反应性。
当ref作为渲染上下文(从中返回的对象setup()
)的属性返回并在模板中进行访问时,它会自动展开为内部值。无需.value
在模板中附加:
{{ count }}
当ref被访问或作为反应对象的属性进行更改时,它会自动展开为内部值,因此其行为类似于普通属性:
const count = ref(0)
const state = reactive({
count
})
console.log(state.count) // 0
state.count = 1
console.log(count.value) // 1
请注意,如果将新的引用分配给链接到现有引用的属性,它将替换旧的引用:
const otherCount = ref(2)
state.count = otherCount
console.log(state.count) // 2
console.log(count.value) // 1
请注意,仅当ref嵌套嵌套在react时才发生Object
。从ref Array
或本机集合类型(如)访问ref时,不会执行解包Map
:
const arr = reactive([ref(0)])
// 这里需要value
console.log(arr[0].value)
const map = reactive(newMap([['foo', ref(0)]]))
// 这里需要value
console.log(map.get('foo').value)
interface Ref {
value: T
}
function ref(value: T): Ref
有时我们可能需要为ref的内部值指定复杂类型。我们可以通过在调用ref
以覆盖默认推断时传递泛型参数来简洁地实现此目的:
const foo = ref('foo') // foo's type: Ref
foo.value = 123// ok!
使用getter函数,并为getter返回的值返回一个不变的反应性ref对象。
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
plusOne.value++ // error
或者,它可以使用具有get
和set
功能的对象来创建可写的ref对象。
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: val => {
count.value = val - 1
}
})
plusOne.value = 1
console.log(count.value) // 0
// read-only
function computed(getter: () => T): Readonly>>
// writable
function computed(options: {
get: () => T
set: (value: T) => void
}): Ref
取得一个对象(反应性或普通)或ref,并返回一个只读代理到原始对象。只读代理很深:访问的任何嵌套属性也将是只读的。
const original = reactive({ count: 0 })
const copy = readonly(original)
watchEffect(() => {
// 用于反应性跟踪
console.log(copy.count)
})
// 对原版本进行变异将会引发观察者对拷贝的依赖
original.count++
// 更改副本将失败并导致警告
copy.count++ // warning!
在反应性地跟踪其依赖关系时立即运行一个函数,并在依赖关系发生变化时重新运行它。
const count = ref(0)
watchEffect(() =>console.log(count.value))
// -> logs 0
setTimeout(() => {
count.value++
// -> logs 1
}, 100)
当watchEffect
在一个组件的调用setup()
功能或生命周期挂钩,观察者链接到组件的生命周期,当组件卸载将自动停止。
在其他情况下,它返回停止句柄,可以调用该句柄以显式停止观察程序:
const stop = watchEffect(() => {
/* ... */
})
// later
stop()
有时,受监视的效果函数会执行异步副作用,当无效该副作用时,需要清理该异步副作用(即,在完成效果之前更改状态)。效果函数接收onInvalidate
可用于注册无效回调的函数。在以下情况下调用无效回调:
效果将重新运行
观察者停止(例如,如果watchEffect
在内部setup()
或生命周期挂钩中使用该组件,则该组件将被卸下)
watchEffect(onInvalidate => {
const token = performAsyncOperation(id.value)
onInvalidate(() => {
// id has changed or watcher is stopped.
// invalidate previously pending async operation
token.cancel()
})
})
我们正在通过传递的函数注册无效回调,而不是从回调(如React useEffect
)返回无效回调,因为返回值对于异步错误处理很重要。在执行数据提取时,效果函数通常是异步函数:
const data = ref(null)
watchEffect(async () => {
data.value = await fetchData(props.id)
})
异步函数隐式返回Promise,但是在Promise解析之前,必须立即注册清除函数。此外,Vue依靠返回的Promise来自动处理Promise链中的潜在错误。
Vue的反应系统缓冲无效的效果,并异步刷新它们,以避免在同一“标记”中发生许多状态突变时不必要的重复调用。在内部,组件的更新功能也是受监视的效果。当用户效果排队时,总是在所有组件更新效果之后调用它:
{{ count }}
在此示例中:
该计数将在初始运行时同步记录。
当count
发生突变时,回调将被调用**后,**组件已更新。
请注意,第一次运行是在安装组件之前执行的。因此,如果您希望以监视的效果访问DOM(或模板引用),请在安装的挂钩中进行操作:
onMounted(() => {
watchEffect(() => {
// access the DOM or template refs
})
})
如果需要同步运行观察者效果或在组件更新之前重新运行,我们可以将附加的options对象与flush
选项一起传递(默认值为'post'
):
// fire synchronously
watchEffect(
() => {
/* ... */
},
{
flush: 'sync'
}
)
// fire before component updates
watchEffect(
() => {
/* ... */
},
{
flush: 'pre'
}
)
该onTrack
和onTrigger
选项可以用来调试观察者的行为。
onTrack
当将反应性属性或ref跟踪为依赖项时将被调用
onTrigger
当监视者回调由依赖项的突变触发时将被调用
这两个回调都将接收到一个调试器事件,该事件包含有关所依赖项的信息。建议debugger
在这些回调中放置一条语句以交互方式检查依赖关系:
watchEffect(
() => {
/* side effect */
},
{
onTrigger(e) {
debugger
}
}
)
onTrack
并且onTrigger
仅适用于开发模式。
function watchEffect(
effect: (onInvalidate: InvalidateCbRegistrator) => void,
options?: WatchEffectOptions
): StopHandle
interface WatchEffectOptions {
flush?: 'pre' | 'post' | 'sync'
onTrack?: (event: DebuggerEvent) =>void
onTrigger?: (event: DebuggerEvent) =>void
}
interface DebuggerEvent {
effect: ReactiveEffect
target: any
type: OperationTypes
key: string | symbol | undefined
}
type InvalidateCbRegistrator = (invalidate: () => void) =>void
type StopHandle = () =>void