体验一波Vue3.0

719 阅读5分钟

Vue3.0beta尝鲜

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

前一段时间也出了中文版的composition-api,如果多想了解的,可以去看下。Vue38月份要发正式版。

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

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

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

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

步骤我就不多说了,详情

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

vue add vue-next

安装成功后我们可以看下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"
},

2. 安装的另一种方式vite (推荐使用)

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

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

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

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

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

先看个图:

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

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

vite渲染方式:

webpage渲染方式:

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

3. 创建项目

# NPM
$ npm init vite-app <project-name>
$ cd <project-name>
$ npm install
$ npm run dev
# YARN
$ yarn create vite-app <project-name>
$ cd <project-name>
$ yarn
$ yarn dev

创建的速度也是很快3.79s

首先看下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)
    }
  }
}

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

4. 实战

1. data
 #Vue2
 export default {
   data() {
     return {
       message: 'vue2'
     }
   },
 }

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)
    }
  }
}
2. 生命周期

与 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,
  }
}
}
3. mehods
# Vue2

export default {
 ...
 methods:{
   increment(){
     this.count++;
   }
 }
}
#Vue3

<script>
export default {
  setup() {
    // 定义一个方法
    const increment = () => {
      count.value++
    }

    // 记得要return 
    return { increment }
  }
}
</script>
4. 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)
    })
    // 单个监听(立即执行)
    watch(() => state.count, (val, oldVal) => {
      console.log('count', val, oldVal)
    }, { immediate: true })
    
    return { ...toRefs(state), add }
  }
}
5. Mixins

在 Vue 2 中,mixin 是将部分组件逻辑抽象成可重用块的主要工具。但是,他们有几个问题:

mixin 很容易发生冲突:因为每个特性的属性都被合并到同一个组件中,所以为了避免 property 名冲突和调试,你仍然需要了解其他每个特性。

可重用性是有限的:我们不能向 mixin 传递任何参数来改变它的逻辑,这降低了它们在抽象逻辑方面的灵活性

Vue3.0不推荐使用Mixins,可以使用Composition API

// filters.js
export default function () {
  // 没有参数的处理
  const paramsType = (val) => {
    if (val !== undefined) {
      return val;
    }
    return '-';
  };
  // 金额类型
  const moneyType = (val) => {
    if (val) return val >= 0 ? `¥${val}` : `-¥${Math.abs(val)}`;
    return `¥${0}`;
  };
  return {
    paramsType, moneyType
  };
}

组件中使用:

// ComDemo.vue

<template>
  <div>{{ paramsType(name)  }}</div>
</template>

<script>
// 推荐使用use开头命名
import useFilters from './filters';
export default {
  props: { name: { type: String, default: '' } },
  setup(props, context) {
    const { paramsType } = useFilters();
    return { paramsType }
  }
}   
</script>
6. 组件

组件通信
和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>

parent父组件

// ParentDemo.vue 

<template>
  <div>
    <!-- 方式1 -->
    <ComDemo :name="name" @talk="talk" ref="comdemo" />
     <!-- 方式1 -->
     <!-- <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('父组件')
    }
    // 获取子组件
    // 方式1:
    const comdemo = ref(null)
    onMounted(() => {
      // 得到子组件的值
      console.log(comdemo.value.str)
      // 触发子组件事件
      // comdemo.value.talk()
    })
    // 方式2:
    // const comdemo = (el) => {
    //   console.log('el: ', el.offsetHeight);
    // };
    return { ...toRefs(state), talk, comdemo }
  }
}
</script>
7. 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>
8. vuex

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

// 方法1:

<script>
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
    }
  }}
</script>
// 方法2:

<script>
import { mapState, mapActions } from 'vuex'
export default {
  computed: mapState({
    count: state => state.count
  }),
  methods: {
    ...mapMutations({
      increment: 'increment'
    }),
    ...mapActions({
      decrement: 'decrement'
    })
  },
  created() {
    this.$store.dispatch('products/getAllProducts')
  }
}
</script>

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