vue3初尝试
前言目前vue3还处于beta版本,离正式版的发布还有一段时间.在此之前我们可以对vue3新特性CompositionAPI做一个demo练习,加深对其具体应用的场景的理解.相关文档API完整项目地址Demo需求上图为Demo最终展现结果.Demo总共有三张页面:首页,商品详情页和登录页.首页有"猜你喜欢"和"今日推荐"两个数据列表.点击列表标题会随机触发列表项的颤动.点...
前言
目前vue3还处于beta版本,离正式版的发布还有一段时间.在此之前我们可以对vue3新特性 composition api 做一个demo练习,加深对其具体应用场景的理解.
Demo需求
上图为Demo最终展现结果.Demo总共有三张页面:首页,商品详情页和登录页.
首页有"猜你喜欢"和"今日推荐"两个数据列表.点击列表标题会随机触发列表项的颤动.
点击列表项会获取到商品id进入到商品详情页面并展现出商品的详细信息.
点击头部的登录按钮会进入登录页面,用户登陆后会持久化保存登录状态.
首页开发
代码描述:
和vue2相比,vue3多了一个setup函数.它是整个组件执行的入口.以前我们在组件中编写data存储状态,编写computed计算属性,编写method函数,编写生命周期来做一些特定操作等等.如今这些行为我们全部可以放到setup函数中实现.
在setup函数中我们返回了一个对象,里面包含了三个数据likes,recommends和inquire函数.likes和recommends是通过调用useData函数(这是用户自定义的函数)返回的数据,inquire是通过调用useDetailPage函数返回的一个函数.最终通过setup导出才能被template模板渲染,而导出的函数能被模板上的事件触发.
我们现在追踪一下useData函数的具体实现.其中我们要着重关注两个新API:ref和onMounted. let likes = ref({});这句代码的含义是定义了一个响应式变量likes,它的初始值为空对象.onMounted顾名思义就是生命周期函数mounted,里面接受一个函数.当组件挂载完毕便会执行init函数请求服务器端的数据,最后将返回的结果赋值给likes和recommends,如此template模板上的"猜你喜欢"和"今日推荐"两个列表数据便渲染出来了.使用ref时需要注意它的赋值方式是调用.value来接受值.
在useDetailPage函数中,我们可以看到如何在vue3中使用路由.运行useRouter()获取router对象,再调用.push方法即可跳转,同理调用.back 方法可实现回退.
vue3它为什么要将数据状态,计算属性还有函数等等都放在setup函数中呢?这样代码不是显的很臃肿吗?
在setup函数中,我们可以定义许多功能耦合度很高的独立小单元函数,将数据状态和函数放在一起.比如下面的useData函数,里面盛放的数据就是likes和recommends,而如何获取likes和recommends的功能函数或者操作它们的相关方法都可以和数据定义放一起.这样就可以将组件中的逻辑进行更小密度的拆分,如果外界需要相关的数据和方法就可以导出供其调用.
首页源代码:
<template>
<div class="home">
<Header :has_back="false" />
<Product :items="likes" @inquire="inquire" />
<Product :items="recommends" @inquire="inquire" />
</div>
</template>
<script>
// @ is an alias to /src
import Header from "@/components/Header.vue";
import { ref, onMounted } from "vue";
import Product from "../components/Product";
import { useRouter } from "vue-router";
import { post } from "../util/tool";
export default {
components: {
Header,
Product
},
setup() {
const { likes, recommends } = useData();
const { inquire } = useDetailPage();
return {
likes,
recommends,
inquire
};
}
};
/**
* 进入详情页面相关操作
*/
function useDetailPage() {
const router = useRouter();
function inquire(data) {
const { id } = data;
router.push(`/detail/${id}`);
}
return {
inquire
};
}
/**
* 获取页面的数据
*/
function useData() {
let likes = ref({});
let recommends = ref({});
onMounted(() => {
init();
});
function init() {
return new Promise(resolve => {
post({
url: "/api/home"
}).then(result => {
likes.value = result.likes;
recommends.value = result.recommends;
});
});
}
return {
likes,
recommends
};
}
</script>
列表组件开发
我们现在来看一下首页当中的Product组件的实现源码.(下面源码中省略了css,完整代码请打开最上面的项目地址)
1.在vue3中父子组件是如何通信的呢?
在setup函数中含有两个参数props和context.其中props用来获取父组件传递过来的参数,而context.emit用于调用父组件的方法.
2.vue3中如何使用计算属性?
在computed函数中传递一个回调函数并返回某个值.
3.vue3中如何通过ref获取dom节点?
在下面的lightHanlder函数中首先定义一个响应式的数组doms,并导出到模板template中使用.我们可以看到模板中的这个用法:
:ref="ref=>doms[index]=ref",:ref右边接的是一个函数,函数的参数ref也就是相应的原生dom对象,将列表的原生dom对象都赋值到
doms数组中.lightHanlder函数主要是操作原生dom对象执行动画.
<template>
<div class="Product">
<p class="title" @click="lightOne">{{title}}</p>
<div
class="item"
v-for="(item,index) in list"
:key="item.id"
@click="inquire(item)"
:ref="ref=>doms[index]=ref"
>
<p class="item_top box">
<i class="lt">{{item.name}}</i>
<i class="gt">{{item.price}}</i>
</p>
<p class="item_bottom">{{item.desc}}</p>
</div>
</div>
</template>
<script>
import { computed, ref } from "vue";
export default {
props: {
items: Object
},
setup(props, context) {
const title = computed(() => {
return props.items ? props.items.title : "";
});
const list = computed(() => {
return props.items ? props.items.data : [];
});
function inquire(item) {
context.emit("inquire", item);
}
const { doms, lightOne } = lightHanlder();
return {
title,
list,
inquire,
doms,
lightOne
};
}
};
function lightHanlder() {
const doms = ref([]);
function lightOne() {
const len = doms.value.length;
if (len == 0) {
return false;
}
const index = parseInt(Math.random() * len);
doms.value[index].classList.add("animated");
setTimeout(() => {
if (doms.value[index]) {
doms.value[index].classList.remove("animated");
}
}, 1000);
}
return {
doms,
lightOne
};
}
</script>
详情页开发
1.获取路由参数id?
通过调用useRoute()方法返回route对象,在其params属性中可以获取到相关的路由参数.
2.reactive如何使用?
reactive和ref的功能类似都是定义响应式的状态,只不过reactive里面通常装一些对象数据.比如界面上有大量的表单数据时,我们就可以考虑使用reactive来定义表单数据的状态.另外reactive赋值时不需要像ref调用.value来赋值.
源代码
<template>
<div class="detail">
<Header title="详情页" />
<div class="item">
<p class="item_top box">
<i class="lt">id</i>
<i class="gt">{{detailDetail.form.id}}</i>
</p>
</div>
<div class="item">
<p class="item_top box">
<i class="lt">名称</i>
<i class="gt">{{detailDetail.form.price}}</i>
</p>
</div>
<div class="item">
<p class="item_top box">
<i class="lt">数量</i>
<i class="gt">{{detailDetail.form.count}}</i>
</p>
</div>
<div class="item">
<p class="item_top box">
<i class="lt">价格</i>
<i class="gt">{{detailDetail.form.price}}</i>
</p>
</div>
<div class="item high">
<p class="item_top box">
<i class="lt">照片</i>
<i class="gt">
<img :src="detailDetail.form.img" />
</i>
</p>
</div>
<div class="item high">
<p class="item_top box">
<i class="lt">描述</i>
<i class="gt">{{detailDetail.form.desc}}</i>
</p>
</div>
</div>
</template>
<script>
import Header from "../components/Header";
import { useRoute } from "vue-router";
import { reactive } from "vue";
import { post } from "../util/tool";
export default {
components: {
Header
},
setup() {
const { detailDetail } = useData();
return { detailDetail };
}
};
function useData() {
const route = useRoute();
const { id } = route.params;
const detailDetail = reactive({
form: {
count: "",
date: "",
desc: "",
id: "",
img: "",
name: "",
price: ""
}
});
requestData();
function requestData() {
post({
url: `/api/detail`,
data: {
id
}
}).then(res => {
detailDetail.form = res;
});
}
return { detailDetail };
}
</script>
登录页开发
登录前:
登录后:
vue3中如何使用vuex?
vuex相关的配置可以保持与vue2中一样,但是在页面中获取vuex的数据和调用vuex中的mutations或者actions有点区别.
我们可以看下面的httpMethods函数,通过调用useStore()函数获取到store仓库,通过调用store.commit方法来调用mutations中的相关函数来改变vuex中的数据.怎么获取vuex中数据呢?看下一小结.
源代码
<template>
<div class="login">
<Header :has_login="false" title="登录" />
<div class="form" v-if="!userInfo">
<p class="line">
<i class="label">用户名:</i>
<input
type="text"
placeholder="请输入用户名"
:value="user.user_name"
@input="setForm({user_name:$event.target.value})"
/>
</p>
<p class="line">
<i class="label">密码:</i>
<input
type="password"
placeholder="请输入密码"
:value="user.password"
@input="setForm({password:$event.target.value})"
/>
</p>
<p class="btn">
<a class="submmit" @click="login">登录</a>
</p>
</div>
<div v-else>
<div class="tag">{{userInfo.user_name}}殿下,欢迎您登录!</div>
<p class="btn">
<a class="submmit" @click="logout">退出登录</a>
</p>
</div>
</div>
</template>
<script>
import Header from "../components/Header";
import { useStore } from "vuex";
import { reactive } from "vue";
import { post, storeUser, clearUser } from "../util/tool";
import UserState from "../components/UserState";
export default {
components: {
Header
},
setup() {
const { user, setForm } = paramsHandler(); //表单参数
const { login, logout } = httpMethods(user); //登录操作的函数
const { userInfo } = UserState.setup(); //获取用户信息
return {
user,
login,
setForm,
userInfo,
logout
};
}
};
/**
* 登录操作
*/
function httpMethods(user) {
const store = useStore();
function login() {
const { user_name, password } = user;
if (user_name.trim() === "" || password.trim() === "") {
alert("账号密码不能为空");
return false;
}
post({
url: "/api/login",
data: {
user_name,
password
}
}).then(res => {
const user = {
user_name,
password: "",
user_id: res.user_id
};
store.commit("setUser", user);
storeUser(user);
});
}
function logout() {
store.commit("clearUser");
clearUser(); //删除localstorage存储的用户信息
}
return {
login,
logout
};
}
/**
* 表单的参数处理
*/
function paramsHandler() {
const user = reactive({
user_name: "",
password: ""
});
function setForm(data) {
const key = Object.keys(data)[0];
user[key] = data[key];
}
return {
user,
setForm
};
}
</script>
计算逻辑的复用
细心的同学会发现在登录页面中我们是通过调用UserState.setup()来获取当前用户登录的状态,在这里需要引起格外注意的是UserState是一个组件.
我们在A组件中引用B组件并调用B组件中setup方法来获取数据,这和我们之前vue2中组件的使用方法就不太一样了.在vue2中我们定义的组件包含template,script,style这三部分,而在vue3里面我们可以定义一个组件而这个组件只包含script部分,这样就意味我们可以脱离template模板只复用组件的计算逻辑.
这样的计算逻辑复用有什么好处呢?
比如我的很多页面都需要获取登录状态,按照之前的做法就是每个组件都与vuex进行连接获取登录状态.而现在呢我们可以在vuex中获取登录状态的逻辑封装到UserState组件中,其他页面想要获取登录状态只需要调用UserState.setup()即可得到登录信息,而不用再一个个去连接vuex了.
观察下面代码,在UserState中定义用户的登录状态信息userInfo,让了能让其监听到vuex的状态变化从而触发其他引用该状态的页面产生相应的变化,我们可以用watch也可以用computed让userInfo实时变化,最后将其导出.
UserState源码:
<script>
import { ref, watch } from "vue";
import { useStore } from "vuex";
export default {
setup() {
const store = useStore();
const userInfo = ref(store.state.user);
watch(
() => {
return store.state.user;
},
(newv, prev) => {
userInfo.value = newv;
}
);
return {
userInfo
};
}
};
</script>
更多推荐
所有评论(0)