# Vue3.0beta尝鲜

从去年开始Vue3.0一直在预热,到现在Vue3.0beta已经发布有一短时间了,网上也说了好多Vue3的一些新的特征。尤大大也说:Vue从底层开始重构,可以说重新出发。

前一段时间也出了中文版的composition-api,如果多想了解的,可以去看下。

废话不多说,赶紧动手玩下吧。

  1. 怎么使用Vue3.0beta

    1. Vue/cli版本要4.0以上,现在基本上都是4.0以上了,如果还没有升级到4.0以上的,首先要升级下

    关于旧版本
    Vue CLI 的包名称由 vue-cli 改成了 @vue/cli。 如果你已经全局安装了旧版本的 vue-cli (1.x 或 2.x),你需要先通过 npm uninstall vue-cli -g 或 yarn global remove vue-cli 卸载它。

    安装:

    npm install -g @vue/cli
    # OR
    yarn global add @vue/cli
    
    1
    2
    3

    你还可以用这个命令来检查其版本是否正确:

    vue --version
    
    1
    1. 创建新的项目并安装vue-next
    vue create vue3-demo
    
    1

    步骤我就不多说了,详情

    创建好项目,我们安装vue-next

    vue add vue-next
    
    1

    安装成功后我们可以看下package.json,如果Vue的版本出现beta,那么恭喜呢,你成功了,接下来你就可以愉快的玩Vue3了。

    "dependencies": {
     "core-js": "^3.6.5",
     "register-service-worker": "^1.7.1",
     "vue": "^3.0.0-beta.15",
     "vue-router": "^4.0.0-alpha.14",
     "vuex": "^4.0.0-alpha.4"
    },
    
    1
    2
    3
    4
    5
    6
    7
  2. 安装的另一种方式vite (推荐使用)

据尤大大说:vite只是一个demo,为了Vue3.0开发的工具,刚开始就是玩玩,谁知道一玩就控制不住了,所有就有了vite。

Vite是一个自以为是的Web开发人员构建工具,可在开发期间通过本机ES模块导入为您的代码提供服务,并将其与Rollup捆绑在一起进行生产。

  • 闪电般快速的冷服务器启动
  • 即时热模块更换(HMR)
  • 真正的按需编译

在Beta中,可能很快就会发布1.0。

如果你使用vite,你会发现编译的速度很快,可以说秒开。为什么这么快呢?vite的渲染方式和webpack渲染方式不同,vite可以根据现代的浏览器module直接渲染.vue文件,而webpage需要先编译在渲染,时间可想而知,那个很快!

先看个图:

vite

连webpage的大佬都要哭了,开玩笑了,不过vite确实很快。

我们对比下vite和webpage的渲染方式

vite渲染方式: vite-temp

webpage渲染方式: webpage-temp

vite是一款真正的开箱即用的工具,下面跟着我体验一下吧。

  1. 创建项目
# NPM
$ npm init vite-app <project-name>
$ cd <project-name>
$ npm install
$ npm run dev
1
2
3
4
5
# YARN
$ yarn create vite-app <project-name>
$ cd <project-name>
$ yarn
$ yarn dev
1
2
3
4
5

创建的速度也是很快3.79s vite-create

首先看下Vue3的语法:也可以去看下Vue3.0手册

export default {
  name: 'Home',
  setup() {
    // 定义一个ref响应式对象
    const count = ref(0)
    // 如果要定义多个可以使用reactive
    const state = reactive({
      size: 36,
      color: 'red'
    })
    // 定义一个方法
    const increment = () => {
      count.value++
    }

    return {
      count,
      increment,
      ...toRefs(state)
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

可以看出Vue3语法有个很大的改变,Vue3更多的放在逻辑层,可以说按需加载我们要渲染和要用的的函数,这样我们的浏览器的开销就小了许多,渲染的速度就快了许多。Vue3用Proxy监听数据的变化。
对比Vue2使用Object.defineProperty监听对象改变,所有的生命周期都暴露给this,这样的开销,不论我们改变那个变量或者函数,都会触发对象的改变。这样我们在data中定义的我们我们不用的变量,也是多余的开销,从而影响我们的渲染速度。

  1. 实战

    1. data
     #Vue2
     export default {
       data() {
         return {
           message: 'vue2'
         }
       },
     }
    
    1
    2
    3
    4
    5
    6
    7
    8

    setup() 函数返回的 property 将会被暴露给 this

    Vue3如果要定义响应对象,要用ref或者reactive,如果不用你定义的变量不是响应式的,这个还是要注意点。
    然后通过return返回我们要用的函数和要渲染的变量,如果不返回我们在HTML中是渲染不到的,这个就是我们说的按需渲染的。
    如果想了解的更多,可以看下这篇深入理解 Vue3 Reactivity API,这里就不多做介绍,这里我们就教大家怎么使用。

     import { ref, reactive, toRefs } from 'vue'
     export default {
       setup() {
         // msg 这样定义不是响应式的
         const msg = 'Vue3'
         // 定义一个ref响应式对象
         const count = ref(0)
         // 如果要定义多个可以使用reactive
         const state = reactive({
           size: 36,
           color: 'red'
         }
         // 如果我们要获取count的值 要用.value,在html渲染中可以省略.value   {{count}}
         console.log(count.value)
    
         return {
           count,
           ...toRefs(state)
         }
       }
     }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    1. 生命周期

    与 2.x 版本生命周期相对应的组合式 API

    • beforeCreate → 使用 setup()
    • created → 使用 setup()
    • beforeMount → onBeforeMount
    • mounted → onMounted
    • beforeUpdate → onBeforeUpdate
    • updated → onUpdated
    • beforeDestroy → onBeforeUnmount
    • destroyed → onUnmounted
    • errorCaptured → onErrorCaptured
    import { reactive, toRefs, computed, onMounted, onBeforeMount, onBeforeUpdate, 
    onBeforeUnmount, onUpdated, onUnmounted, onErrorCaptured } from 'vue'
    export default {
      setup() {
        // 定义一个ref响应式对象
        const count = ref(0)
    
        // beforeMount
        onBeforeMount(() => {
          console.log("onBeforeMount");
        })
        // mounted
        onMounted(() => {
          console.log("onMounted");
        })
        // beforeUpdate
        onBeforeUpdate(() => {
          console.log("onBeforeUpdate");
        })
        // updated
        onUpdated(() => {
          console.log("onUpdated");
        })
        // beforeDestroy
        onBeforeUnmount(() => {
          console.log("onBeforeUnmount");
        })
        // destory
        onUnmounted(() => {
          console.log("onUnmounted");
        })
        // errorCaptured
        onErrorCaptured(() => {
          console.log("onErrorCaptured");
        })
    
        return {
          count,
        }
      }
    }
    
    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
    1. mehods
    # Vue2
    
    export default {
      ...
      methods:{
        increment(){
          this.count++;
        }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #Vue3
    
    export default {
      setup() {
        // 定义一个方法
        const increment = () => {
          count.value++
        }
        
        // 记得要return 
        return { increment}
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    1. watch

    watchEffect 传入一个函数并立即执行,如果函数里面使用了上面定义好的响应式对象,当对象发生变化时,会再次触发这个函数

    import { reactive, toRefs, watch } from 'vue'
    export default {
      setup() {
        const loadPge = (path) => {
          router.push({ path })
        }
        const state = reactive({
          count: 1
        })
        const add = () => {
          state.count++
        }
    
        watchEffect(() => {
          state.number = state.count + '----'
          console.log(`effect 触发了!${state.count}`)
        })
    
    
        // 定义监听器
        const watcher = watch(state, (val, oldVal) => {
          console.log('watch', val, oldVal)
        })
        return { ...toRefs(state), add }
      }
    }
    
    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
    1. 组件

    组件通信
    和vue2的通信大同小异,新建ComDemo.vue,setup函数接受一个 props 和 context 上下文
    context: { attrs, emit, slots}

    children子组件

    // ComDemo.vue
    
    <template>
      <div>{{name}}</div>
    </template>
    
    <script>
    import { inject } from 'vue'
    export default {
      props: { name: { type: String, default: '' } },
      setup(props, context) {
        console.log(props, context)
        const str = '子组件的属性str'
        const talk = () => {
          console.log('我被父组件触发了')
        }
        context.emit('talk', '我是子组件 我触发你了')
        
        // 接收provide值
        const injectmsg = inject('injectmsg')
        console.log('injectmsg : ', injectmsg)
        return { str, talk }
      }
    }
    </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

    parent父组件

    // ParentDemo.vue 
    
    <template>
      <div>
        <ComDemo :name="name" @talk="talk" ref="comdemo" />
      </div>
    </template>
    
    <script>
    import ComDemo from '../components/ComDemo.vue'
    import { provide, reactive, toRefs, ref, onMounted } from 'vue'
    export default {
      components: { ComDemo },
      setup() {
        const state = reactive({
          name: '我是父组件传值'
        })
    
        // 多级传值
        provide('injectmsg', 'provide talk')
    
        const talk = () => {
          console.log('父组件')
        }
    
        const comdemo = ref(null)
    
        onMounted(() => {
          // 得到子组件的值
          console.log(comdemo.value.str)
          // 触发子组件事件
          // comdemo.value.talk()
        })
        return { ...toRefs(state), talk, comdemo }
      }
    }
    </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
    1. router

    比之前的的封装了一个函数useRouter,返回之前和Vue2一样的路由方法。

    <script>
    import { useRouter } from 'vue-router'
    export default {
      setup() {
        console.log(useRouter())
        const router = useRouter()
        const loadPge = (path) => {
          router.push({ path })
        }
        return { loadPge }
      }
    }
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    1. vuex

    vuex一样的封装了一个函数useStore,通过computed返回vuex的state和getter。

    // 方法1:
    
    import { computed } from 'vue'
    import { useStore } from 'vuex'
    export default {
     setup () {
       const store = useStore()
       return {
         count: computed(() => store.state.count), // state
         evenOrOdd: computed(() => store.getters.evenOrOdd), //getters
         increment: () => store.commit('increment'), // mutations
         decrement: () => store.dispatch('decrement'), // actions
       }
     }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 方法2:
    
    import { mapState, mapActions } from 'vuex'
    export default {
       computed: mapState({
         count: state => state.count
       }),
       methods: {
         ...mapMutations({
           decret: 'decret'
         }),
         ...mapActions({
           decrement: 'decrement'
         })
       },
       created () {
         this.$store.dispatch('products/getAllProducts')
       }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

其他的一直在总结中,不足之处,还请大家不吝指教!!!