Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

通过10个实例小练习,快速入门熟练 Vue3.0 核心新特性 #63

Open
biaochenxuying opened this issue May 10, 2020 · 0 comments
Assignees
Labels
vue.js vue 相关知识点

Comments

@biaochenxuying
Copy link
Owner

biaochenxuying commented May 10, 2020

Vue3.0 发 beta 版都有一段时间了,正式版也不远了,所以真的要学习一下 Vue3.0 的语法了。

GitHub 博客地址: https://github.com/biaochenxuying/blog

环境搭建

$ git pull https://github.com/vuejs/vue-next.git
$ cd vue-next && yarn

下载完成之后打开代码, 开启 sourceMap :

  • tsconfig.json 把 sourceMap 字段修改为 true: "sourceMap": true

  • rollup.config.js 在 rollup.config.js 中,手动键入: output.sourcemap = true

  • 生成 vue 全局的文件:yarn dev

  • 在根目录创建一个 demo 目录用于存放示例代码,并在 demo 目录下创建 html 文件,引入构建后的 vue 文件


api 的使用都是很简单的,下文的内容,看例子代码就能懂了的,所以下面的例子不会做过多解释。

reactive

reactive: 创建响应式数据对象

setup 函数是个新的入口函数,相当于 vue2.x 中 beforeCreate 和 created,在 beforeCreate 之后 created 之前执行。

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello Vue3.0</title>
    <style>
        body,
        #app {
            text-align: center;
            padding: 30px;
        }
    </style>
    <script src="../../packages/vue/dist/vue.global.js"></script>
</head>
<body>
    <h3>reactive</h3>
    <div id='app'></div>
</body>
<script>
    const { createApp, reactive } = Vue
    const App = {
        template: `
            <button @click='click'>reverse</button> 
            <div style="margin-top: 20px">{{ state.message }}</div>
        `,
        setup() {
            console.log('setup ');

            const state = reactive({
                message: 'Hello Vue3!!'
            })

            click = () => {
                state.message = state.message.split('').reverse().join('')
            }

            return {
                state,
                click
            }
        }
    }
    createApp(App).mount('#app')
</script>
</html>

ref & isRef

ref : 创建一个响应式的数据对象
isRef : 检查值是否是 ref 的引用对象。

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello Vue3.0</title>
    <style>
        body,
        #app {
            text-align: center;
            padding: 30px;
        }
    </style>
    <script src="../../packages/vue/dist/vue.global.js"></script>
</head>
<body>
    <h3>ref & isRef</h3>
    <div id='app'></div>
</body>
<script>
    const { createApp, reactive, ref, isRef } = Vue
    const App = {
        template: `
            <button @click='click'>count++</button> 
            <div style="margin-top: 20px">{{ count }}</div>
        `,
        setup() {
            const count = ref(0);
            console.log("count.value:", count.value)  // 0

            count.value++
            console.log("count.value:", count.value)  // 1

            // 判断某值是否是响应式类型
            console.log('count is ref:', isRef(count))

            click = () => {
                count.value++;
                console.log("click count.value:", count.value) 
            }

            return {
                count,
                click,
            }
        }
    }
    createApp(App).mount('#app')
</script>
</html>

Template Refs

使用 Composition API 时,反应性引用和模板引用的概念是统一的。

为了获得对模板中元素或组件实例的引用,我们可以像往常一样声明 ref 并从 setup() 返回。

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello Vue3.0</title>
    <style>
        body,
        #app {
            text-align: center;
            padding: 30px;
        }
    </style>
    <script src="../../packages/vue/dist/vue.global.js"></script>
</head>
<body>
    <h3>Template Refs</h3>
    <div id='app'></div>
</body>
<script>
    const { createApp, reactive, ref, isRef, toRefs, onMounted, onBeforeUpdate } = Vue
    const App = {
        template: `
            <button @click='click'>count++</button> 
            <div ref="count" style="margin-top: 20px">{{ count }}</div>
        `,
        setup() {
            const count = ref(null);

            onMounted(() => {
                // the DOM element will be assigned to the ref after initial render
                console.log(count.value) // <div/>
            })

            click = () => {
                count.value++;
                console.log("click count.value:", count.value) 
            }

            return {
                count,
                click
            }
        }
    }
    
    createApp(App).mount('#app')
</script>
</html>


<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello Vue3.0</title>
    <style>
        body,
        #app {
            text-align: center;
            padding: 30px;
        }
    </style>
    <script src="../../packages/vue/dist/vue.global.js"></script>
</head>
<body>
    <h3>Template Refs</h3>
    <div id='app'></div>
</body>
<script>
    const { createApp, reactive, ref, isRef, toRefs, onMounted, onBeforeUpdate } = Vue
    const App = {
        template: `
            <div v-for="(item, i) in list" :ref="el => { divs[i] = el }">
                {{ item }}
            </div>
        `,
        setup() {
            const list = reactive([1, 2, 3])
            const divs = ref([])

            // make sure to reset the refs before each update
            onBeforeUpdate(() => {
                divs.value = []
            })

            onMounted(() => {
                // the DOM element will be assigned to the ref after initial render
                console.log(divs.value) // [<div/>]
            })

            return {
                list,
                divs
            }
        }
    }
    
    createApp(App).mount('#app')
</script>
</html>

toRefs

toRefs : 将响应式数据对象转换为单一响应式对象

将一个 reactive 代理对象打平,转换为 ref 代理对象,使得对象的属性可以直接在 template 上使用。

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello Vue3.0</title>
    <style>
        body,
        #app {
            text-align: center;
            padding: 30px;
        }
    </style>
    <script src="../../packages/vue/dist/vue.global.js"></script>
</head>
<body>
    <h3>toRefs</h3>
    <div id='app'></div>
</body>
<script>
    const { createApp, reactive, ref, isRef, toRefs } = Vue
    const App = {
        // template: `
        //     <button @click='click'>reverse</button> 
        //     <div style="margin-top: 20px">{{ state.message }}</div>
        // `,
        // setup() {
        //     const state = reactive({
        //         message: 'Hello Vue3.0!!'
        //     })

        //     click = () => {
        //         state.message = state.message.split('').reverse().join('')
        //         console.log('state.message: ', state.message)
        //     }

        //     return {
        //         state,
        //         click
        //     }
        // }

        template: `
            <button @click='click'>count++</button> 
            <div style="margin-top: 20px">{{ message }}</div>
        `,
        setup() {
            const state = reactive({
                message: 'Hello Vue3.0!!'
            })

            click = () => {
                state.message = state.message.split('').reverse().join('')
                console.log('state.message: ', state.message)
            }

            return {
                click,
                ...toRefs(state)
            }
        }
    }
    createApp(App).mount('#app')
</script>
</html>

computed

computed : 创建计算属性

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello Vue3.0</title>
    <style>
        body,
        #app {
            text-align: center;
            padding: 30px;
        }
    </style>
    <script src="../../packages/vue/dist/vue.global.js"></script>
</head>
<body>
    <h3>computed</h3>
    <div id='app'></div>
</body>
<script>
    const { createApp, reactive, ref, computed } = Vue
    const App = {
        template: `
            <button @click='handleClick'>count++</button> 
            <div style="margin-top: 20px">{{ count }}</div>
        `,
        setup() {
            const refData = ref(0);

            const count = computed(()=>{
                return refData.value; 
            })

            const handleClick = () =>{
                refData.value += 1 // 要修改 count 的依赖项 refData
            }

            console.log("refData:" , refData)

            return {
                count,
                handleClick
            }
        }
    }
    createApp(App).mount('#app')
</script>
</html>


<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello Vue3.0</title>
    <style>
        body,
        #app {
            text-align: center;
            padding: 30px;
        }
    </style>
    <script src="../../packages/vue/dist/vue.global.js"></script>
</head>
<body>
    <h3>computed</h3>
    <div id='app'></div>
</body>
<script>
    const { createApp, reactive, ref, computed } = Vue
    const App = {
        template: `
            <button @click='handleClick'>count++</button> 
            <div style="margin-top: 20px">{{ count }}</div>
        `,
        setup() {
            const refData = ref(0);

            const count = computed({
                get(){
                    return refData.value;
                },
                set(value){
                    console.log("value:", value)
                    refData.value = value; 
                }
            })

            const handleClick = () =>{
                count.value += 1 // 直接修改 count
            }

            console.log(refData)

            return {
                count, 
                handleClick
            }
        }
    }
    createApp(App).mount('#app')
</script>
</html>

watch & watchEffect

watch : 创建 watch 监听

watchEffect : 如果响应性的属性有变更,就会触发这个函数

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello Vue3.0</title>
    <style>
        body,
        #app {
            text-align: center;
            padding: 30px;
        }
    </style>
    <script src="../../packages/vue/dist/vue.global.js"></script>
</head>
<body>
    <h3>watch && watchEffect</h3>
    <div id='app'></div>
</body>
<script>
    const { createApp, reactive, ref, watch, watchEffect } = Vue
    const App = {
        template: `
            <div class="container">
                <button style="margin-left: 10px" @click="handleClick()">按钮</button>
                <button style="margin-left: 10px" @click="handleStop">停止 watch</button>
                <button style="margin-left: 10px" @click="handleStopWatchEffect">停止 watchEffect</button>
                <div style="margin-top: 20px">{{ refData }}</div>
            </div>`
        ,
        setup() {
            let refData = ref(0);

            const handleClick = () =>{
                refData.value += 1
            }

            const stop = watch(refData, (val, oldVal) => {
                console.log('watch ', refData.value)
            })

            const stopWatchEffect = watchEffect(() => {
                console.log('watchEffect ', refData.value)
            })

            const handleStop = () =>{
                stop()
            }

            const handleStopWatchEffect = () =>{
                stopWatchEffect()
            }

            return {
                refData, 
                handleClick,
                handleStop,
                handleStopWatchEffect
            }
        }
    }
    createApp(App).mount('#app')
</script>
</html>

v-model

v-model:就是双向绑定

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello Vue3.0</title>
    <style>
        body,
        #app {
            text-align: center;
            padding: 30px;
        }
    </style>
    <script src="../../packages/vue/dist/vue.global.js"></script>
</head>
<body>
    <h3>v-model</h3>
    <div id='app'></div>
</body>
<script>
    const { createApp, reactive, watchEffect } = Vue
    const App = {
        template: `<button @click='click'>reverse</button> 
                    <div></div>
                    <input v-model="state.message" style="margin-top: 20px" />
                    <div style="margin-top: 20px">{{ state.message }}</div>`,
        setup() {
            const state = reactive({
                message:'Hello Vue 3!!'
            })

            watchEffect(() => {
                console.log('state change ', state.message)
            })

            click = () => {
                state.message = state.message.split('').reverse().join('')
            }

            return {
                state,
                click
            }
        }
    }
    createApp(App).mount('#app')
</script>
</html>

readonly

使用 readonly 函数,可以把 普通 object 对象reactive 对象ref 对象 返回一个只读对象。

返回的 readonly 对象,一旦修改就会在 consolewarning 警告。

程序还是会照常运行,不会报错。

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello Vue3.0</title>
    <style>
        body,
        #app {
            text-align: center;
            padding: 30px;
        }
    </style>
    <script src="../../packages/vue/dist/vue.global.js"></script>
</head>
<body>
    <h3>readonly</h3>
    <div id='app'></div>
</body>
<script>
    const { createApp, reactive, readonly, watchEffect } = Vue
    const App = {
        template: `
            <button @click='click'>reverse</button> 
            <button @click='clickReadonly' style="margin-left: 20px">readonly++</button> 
            <div style="margin-top: 20px">{{ original.count }}</div>
        `,
        setup() {
            const original = reactive({ count: 0 })

            const copy = readonly(original)

            watchEffect(() => {
                // works for reactivity tracking
                console.log(copy.count)
            })

            click = () => {
                // mutating original will trigger watchers relying on the copy
                original.count++
            }

            clickReadonly = () => {
                // mutating the copy will fail and result in a warning
                copy.count++ // warning!
            }

            return {
                original,
                click,
                clickReadonly
            }
        }
    }
    createApp(App).mount('#app')
</script>
</html>

provide & inject

provideinject 启用类似于 2.x provide / inject 选项的依赖项注入。

两者都只能在 setup() 当前活动实例期间调用。

import { provide, inject } from 'vue'

const ThemeSymbol = Symbol()

const Ancestor = {
  setup() {
    provide(ThemeSymbol, 'dark')
  }
}

const Descendent = {
  setup() {
    const theme = inject(ThemeSymbol, 'light' /* optional default value */)
    return {
      theme
    }
  }
}

inject 接受可选的默认值作为第二个参数。

如果未提供默认值,并且在 Provide 上下文中找不到该属性,则 inject 返回 undefined

Lifecycle Hooks

Vue2 与 Vue3 的生命周期勾子对比:

Vue2 Vue3
beforeCreate setup(替代)
created setup(替代)
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeDestroy onBeforeUnmount
destroyed onUnmounted
errorCaptured onErrorCaptured
onRenderTracked
onRenderTriggered

除了 2.x 生命周期等效项之外,Composition API 还提供了以下调试挂钩:

  • onRenderTracked
  • onRenderTriggered

这两个钩子都收到一个 DebuggerEvent,类似于观察者的 onTrackonTrigger 选项:

export default {
  onRenderTriggered(e) {
    debugger
    // inspect which dependency is causing the component to re-render
  }
}

例子:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hello Vue3.0</title>
    <style>
        body,
        #app {
            text-align: center;
            padding: 30px;
        }
    </style>
    <script src="../../packages/vue/dist/vue.global.js"></script>
</head>
<body>
    <h3>Lifecycle Hooks</h3>
    <div id='app'></div>
</body>
<script>
    const { createApp, reactive, onMounted, onUpdated, onUnmounted } = Vue
    const App = {
        template: `
            <div class="container">
                <button @click='click'>reverse</button>
                <div style="margin-top: 20px">{{ state.message }}</div>
            </div>`
        ,
        setup() {
            console.log('setup!')

            const state = reactive({
                message: 'Hello Vue3!!'
            })

            click = () => {
                state.message = state.message.split('').reverse().join('')
            }

            onMounted(() => {
                console.log('mounted!')
            })
            onUpdated(() => {
                console.log('updated!')
            })
            onUnmounted(() => {
                console.log('unmounted!')
            })

            return {
                state,
                click
            }
        }
    }
    createApp(App).mount('#app')
</script>
</html>

最后

本文只列出了笔者觉得会用得非常多的 api,Vue3.0 里面还有不少新特性的,比如 customRefmarkRaw ,如果读者有兴趣可看 Vue Composition API 文档。

参考文章:

支持一下下👇

@biaochenxuying biaochenxuying added the vue.js vue 相关知识点 label May 10, 2020
@biaochenxuying biaochenxuying self-assigned this May 10, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
vue.js vue 相关知识点
Projects
None yet
Development

No branches or pull requests

1 participant