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>
其他的一直在总结中,不足之处,还请大家不吝指教!!!