2020年4月vue3.0发布beta版本,这就意味着正式版本3.0即将到来。发布的内容包括下面的内容:
vue: Beta
vue-router: Alpha
vuex: Alpha
vue-class-component: Alpha
vue-cli: Experimental support via vue-cli-plugin-vue-next
eslint-plugin-vue: Alpha
vue-test-utils: Alpha
vue-devtools: WIP
jsx: WIP
接下来我们就不多说其他的废话直接进入主题来初体验一下vue3.0。
1、新建一个项目,还是使用原理的vue-cli脚手架进行新建 vue create demo (这里demo是项目名称),输入命令后,会出现命令行交互窗口选择你需要安装的就行(不明白的可以直接网上搜索一下,我就不一一介绍咯)
2、如果项目新建成功咯之后,cd demo进入项目目录,直接运行npm run serve
3、目前创建 Vue 3.0 项目需要通过插件升级的方式来实现,vue-cli 还没有直接支持,我们进入项目目录,并输入以下指令:vue add vue-next
4、行上述指令后,会自动安装 vue-cli-plugin-vue-next 插件,该插件会完成以下操作:
- 安装 Vue 3.0 依赖
- 更新 Vue 3.0 webpack loader 配置,使其能够支持 .vue 文件构建(这点非常重要)
- 创建 Vue 3.0 的模板代码
- 自动将代码中的 Vue Router 和 Vuex 升级到 4.0 版本,如果未安装则不会升级
- 自动生成 Vue Router 和 Vuex 模板代码
接下来我们执行一下npm run serve命令,项目也是正常显示,
vue3.0版本的main.js代码如下
// 旧版
import App from './App'
import store from './store'
import router from './router'
new Vue({
// 这个就是路由
router,
// 这个是vuex
store,
// render渲染,$mount是代表index.html的id
// 即我把App.vue渲染到id为app的div里
render: h => h(App)
}).$mount('#app')
// 新版
import { createApp } from 'vue';
import App from './App.vue'
import router from './router'
import store from './store'
createApp(App).use(router).use(store).mount('#app')
接下来我们看一下src目录下router里面的index.js文件
代码如下:
import { createRouter, createWebHashHistory } from 'vue-router'
import Home from '../views/Home.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
初始化 Vue Router 的过程与 3.0 版本变化不大,只是之前采用构造函数的方式,这里改为使用 createRouter 来创建 Vue Router 实例,配置的方法基本一致,接下来我们直接就在About.vue里面修改代码吧,把里面的内容全部删除,添加如下代码(你也可以重新新建一个文件然后配置一下路由)。
状态和事件绑定
<template>
<div class="test">
<h1>count: {{count}}</h1>
<h1>statet.size:{statet.size}</h1>
<h2>reactive:::{{size}}----------color:{{color}}</h2>
</div>
</template>
<script>
import { ref, reactive, toRefs } from 'vue'
export default {
setup () {
// 定义一个ref响应式对象
const count = ref(0);
// 如果要定义多个可以使用reactive,返回的时候再return使用...toRefs(statet ),页面就可以直接调用statet里面的属性咯,例如下面的{{size}} {{color}}
const statet = reactive({
size: 36,
color: "red"
});
return {
count,
...toRefs(state)
}
}
}
</script>
Vue 3.0 中初始化状态通过 setup 方法,定义状态需要调用 ref 和reactive方法。2种方法的区别如下
1、是为了适应不同的写法的人群
有的人可能喜欢这种写法
const a = 1
const b = 2
也有的人可能喜欢这种写法
const field = {
a : 1,
b : 1
}
在这里不评价哪种合适,也没有什么准确的答案,两者的主要区别在于:一,我们使用了2个变量来存储值;而风格二则当作对象的属性存储。这两种风格的代码工作的都没问题,关键在于个人或团队的偏好:使用单独的变量还是使用对象封装,我觉得ref更偏向于第一类人,而reactive更偏向于第二类吧
2、ref只可以监听简单数据,reactive可以监听所有数据
ref这种写法简单,但也有弊端,经过尝试,我发现他只能监听一些如数字、字符串、布尔之类的简单数据而如果需要监听如上面代码一样的jsonArray我们在vue2种都会使用$set来进行变更,到了vue3我们终于可以愉快的使用reactive来实现了。
3、使用方式不一样
a、ref修改数据需要使用这样count.value=xxx的形式,而reactive只需要state.reactiveField=值这样来使用
b、第二点体现在template中引用时候为reactiveField,不需要state,也就是说我reactive对象里面字段是应该直接使用的
c、体现在reactive在return时候需要toRefs来转换成响应式对象
目前刚刚接触,我总结出的不同大概就这么些吧,我上面也提到了使用reactive需要用toRefs来包裹,那这个函数干嘛用的呢?
toRefs函数我是这么理解的 他能将reactive创建的响应式对象,转化成为普通的对象,并且这个对象上的每个节点,都是ref()类型的响应式数据。
接下来我们定义一个事件,用来更新 count 状态:
<template>
<div class="test">
<h1>count: {{count}}</h1>
<button @click="add">add</button>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup () {
const count = ref(0)
const add = () => {
count.value++
}
return {
count,
add
}
}
}
</script>
这里的 add 方法不再需要定义在 methods 中,但注意更新 count 值的时候不能直接使用 count++,而应使用 count.value++,更新代码后,点击按钮,count 的值就会更新了
计算属性和监听器
Vue 3.0 中计算属性和监听器的实现依赖 computed 和 watch 方法
<template>
<div class="test">
<h1>count: {{count}}</h1>
<div>count * 5 = {{newCount}}</div>
<button @click="add">add</button>
</div>
</template>
<script>
import { ref, computed, watch } from 'vue'
export default {
setup () {
const count = ref(0)
const add = () => {
count.value++
}
// watch
watch(() => count.value, val => {
console.log(`count is ${val}`)
})
// computed
const newCount= computed(() => count.value * 5)
return {
count,
newCount,
add
}
}
}
</script>
计算属性 computed 是一个方法,里面需要包含一个回调函数,当我们访问计算属性返回结果时,会自动获取回调函数的值
const newCount= computed(() => count.value * 5)
监听器 watch 同样是一个方法,它包含 2 个参数,2 个参数都是 function
watch(
() => count.value,
val => { // val代表更新后的新值,如果需要显示旧值,可以这样写(newVal, oldVal) = >{..............}
console.log(`count is ${val}`)
}
)
第一个参数是监听的值,count.value 表示当 count.value 发生变化就会触发监听器的回调函数,即第二个参数可以执行监听时候的回调
获取路由信息
Vue 3.0 中通过 getCurrentInstance 方法获取当前组件的实例,然后通过 ctx 属性获得当前上下文,ctx.$router 是 Vue Router 实例,里面包含了 currentRoute 可以获取到当前的路由信息
方法一:import { getCurrentInstance } from 'vue'
方法二:import { useRouter } from "vue-router";
setup () {
// 方法一
/*const { ctx } = getCurrentInstance()
console.log(ctx.$router.currentRoute.value)*/
// 方法二
const router = useRouter();
// console.log(router.currentRoute.value)
}
vueX集成
1、引用 Vuex 状态
首先找到项目目录store文件夹下的index.js(vue3.0版本的store和原来的Api几乎没有啥变化),我们在state里面新添加一个testA状态
import Vuex from 'vuex'
export default Vuex.createStore({
state: {
testA: 125
},
actions: {
},
modules: {
}
});
然后再about.vue页面里使用computed引用store里面的状态。
/* store使用*/
方法一:const testA = computed(() => ctx.$store.state.testA)
方法二:先引入vuex里面的useStore
import { useStore } from 'vuex'
然后再step里面定义变量接受
const store = useStore();
const testA = store.state.testA;
然后再return返回,页面里面就可以使用{{testA}}
<template>
<div class="test">
<h1>count: {{count}}</h1>
<h3>newCount:{{newCount}}</h3>
<button @click="add">Add</button>
<h2>vuex testA:{{testA}}</h2>
<button @click="update">Add-Store</button>
</div>
</template>
<script>
import { useStore } from 'vuex'
import { ref, computed, watch, getCurrentInstance } from 'vue'
export default {
setup () {
/*
定义变量 data
*/
const count = ref(0);
/*
watch 监听器 watch 同样是一个方法,它包含 2 个参数,2 个参数都是 function:
*/
watch(
() => count.value,
val => {
console.log(`count is ${val}`)
}
)
/*
computed
*/
const newCount = computed(() => count.value * 2)
/*
获取当前路由信息 Vue 3.0 中通过 getCurrentInstance 方法获取当前组件的实例,然后通过 ctx 属性获得当前上下文,ctx.$router 是 Vue Router 实例,里面包含了 currentRoute 可以获取到当前的路由信息
*/
const { ctx } = getCurrentInstance()
/* console.log(ctx.$router)
console.log('store:', ctx.$store)
console.log('state:', ctx.$store.state) */
/*
store使用
*/
// const testA = computed(() => ctx.$store.state.testA); // 同下面2行
const store = useStore(); // 使用useStore必须要inport { useStore} from 'vuex'
const testA = store.state.testA;
/*
定义方法 methods
*/
const add = () => {
count.value++
}
return {
count,
testA,
newCount,
// methods
add,
}
}
}
</script>
<style lang="scss" scoped>
.test {
color: red;
}
</style>
2、更新 Vuex 状态
更新 Vuex 状态仍然使用 commit 方法,这点和 Vuex 3.0 版本一样
import Vuex from 'vuex'
export default Vuex.createStore({
state: {
testA: 125
},
mutations: {
setTestA(state, value) {
state.testA += value;
}
},
actions: {
},
modules: {
}
});
页面使用commit方法调用
<template>
<div class="test">
<h1>count: {{count}}</h1>
<h3>newCount:{{newCount}}</h3>
<button @click="add">Add</button>
<h2>vuex testA:{{testA}}</h2>
<button @click="updateStore">updata-store</button>
</div>
</template>
<script>
import {useStore} from 'vuex'
import { ref, computed, watch, getCurrentInstance } from 'vue'
export default {
setup () {
/*
定义变量 data
*/
const count = ref(0);
const store = useStore();
/*
watch 监听器 watch 同样是一个方法,它包含 2 个参数,2 个参数都是 function:
*/
watch(
() => count.value,
val => {
console.log(`count is ${val}`)
}
)
/*
computed
*/
const newCount = computed(() => count.value * 2)
/*
获取当前路由信息 Vue 3.0 中通过 getCurrentInstance 方法获取当前组件的实例,然后通过 ctx 属性获得当前上下文,ctx.$router 是 Vue Router 实例,里面包含了 currentRoute 可以获取到当前的路由信息
*/
const { ctx } = getCurrentInstance()
/* console.log(ctx.$router)
console.log('store:', ctx.$store)
console.log('state:', ctx.$store.state) */
/*
store使用
*/
const testA = computed(() => ctx.$store.state.testA)
/*
定义方法 methods
*/
const add = () => {
count.value++
}
//
const updateStore = () => {
// mutations
// ctx.$store.commit('setTestA', 100)
store.commit("increment");
}
return {
count,
testA,
newCount,
// methods
add,
updateStore
}
}
}
</script>
<style lang="scss" scoped>
.test {
color: red;
}
</style>
页面点击updata-store按钮后,会触发 update 方法,此时会通过 ctx.$store.commit 调用 setTestA 方法,将 state里面的testA 值覆盖 ,另外一种方法是引入useStore 然后通过store.commit("setTestA")调用
vue3.0版本生命周期
// beforeCreate -> 使用 setup()
// created -> 使用 setup()
// beforeMount -> onBeforeMount
// mounted -> onMounted
// beforeUpdate -> onBeforeUpdate
// updated -> onUpdated
// beforeDestroy -> onBeforeUnmount
// destroyed -> onUnmounted
// errorCaptured -> onErrorCaptured
// 生命周期同样需要引入,写在setup里面,但不需要return
import { onMounted, onUnmounted } from "vue";
export default {
name: "Home",
setup() {
onMounted(() => {
console.log("onMounted");
});
}
};