1.vant组件库 Rem 布局适配
安装postcss-pxtorem,用于将 px 单位转化为 rem 单位,安装lib-flexible 用于设置 rem 基准值。新建postcss.config.js文件,引入以下代码。并在main.js引入amfe-flexible
module.exports = {
plugins: {
'postcss-pxtorem': {
rootValue: 37.5,
propList: ['*'],
},
},
};
2.全局样式处理使得font-size最大不超过75px,屏幕最大宽度不超过750px。不能在index.html中控制font-size,因为index.html在app.vue之前加载。我们可以在utils中定义一个函数获取html的font-size大小和页面宽度,控制使其在规定范围。
3.在主页头部引入最开始默认的静态图片时,不能在setup中返回图片的相对路径然后在模板中动态绑定,因为webpack打包后就没有assets文件夹了,原图片地址变成另一个图片地址。在vue中动态绑定的图片不能使用相对地址,可以通过require引入相对路径,webpack打包的时候就不会打包了。还可以通过import动态导入,导入的就是打包后的地址。
import timg from "@/assets/images/timg.jpg"
4.首页头部封装成一个单独的组件,在utils中封装一个格式化时间的组件,home组件给head组件传递时间,如果没有传递时间则获取现在的时间,在头部组件中通过计算属性获取head中展示的时间并转化为满足的格式。再在模板中进行展示。
head中将时间转化为符合条件的格式
let timeNow = computed(() => {
let time = props.time || null;
let [month, day] = formatTime(time, "{1}-{2}").split("-");
let area = [
"一月",
"二月",
"三月",
"四月",
"五月",
"六月",
"七月",
"八月",
"九月",
"十月",
"十一月",
"十二月",
];
return {
month: area[month - 1],
day,
};
});
格式化时间的format函数:
export const formatTime = function formatTime(time, template) {
// 如果传入的时间不是一个字符串,就获取现在的时间
if (typeof time !== 'string') {
time = new Date().toLocaleString('zh-CN', { hour12: false });
}
// 如果传入的模板不是一个字符串 就使用下面的默认模板
if (typeof template !== 'string') {
template = '{0}年{1}月{2}日 {3}:{4}:{5}';
}
// 解析出年月日等信息
let arr = [];
// 日期可能存在的格式 '20211025' '2021/12/25' '2021-12-5 10:00:00'
if (/^\d{8}$/.test(time)) {
let [, $1, $2, $3] = /^(\d{4})(\d{2})(\d{2})$/.exec(time);
arr.push($1, $2, $3);
} else {
arr = time.match(/\d+/g);
}
// 把获取的数据替换模板
return template.replace(/\{(\d+)\}/g, (_, $1) => {
let item = arr[$1] || "00";
if (item.length < 2) item = "0" + item;
return item;
})
}
5.轮播图部分优化:
①轮播图图片懒加载
②从服务器拿的数据,以后不需要再改细节,我们可以通过Object.freeze(对象)做冻结,使数据对象不会成为响应式,提升性能。
Object.freeze() 可以冻结一个对象,冻结之后不能向这个对象添加新的属性,不能修改其已有属性的值,不能删除已有属性,以及不能修改该对象已有属性的可枚举性、可配置性、可写性。该方法返回被冻结的对象。
首页获取服务器数据时就用到了Object.freeze()
// 第一次加载首页获取数据
onBeforeMount(async () => {
let { date, stories, top_stories } = await api.queryNewsLatest();
// 修改数据
state.today = date;
state.bannerList = Object.freeze(top_stories);
state.newsList.push(Object.freeze({ date, stories }));
});
6.我发现当我从首页进入详情页后,如果再返回首页,页面又会回到最顶端,这显然不是最好的,不能给用户很好的体验感。所以我们应该使用keep-alive缓存home组件,使得我们从详情页返回主页的时候依然能停留在之前的位置。Vue3中keep-alive的用法与Vue2不同,具体用法如下:
<router-view v-slot="{ Component }">
<keep-alive include="Home">
<component :is="Component" />
</keep-alive>
</router-view>
7.详情页的主体部分展示的内容及样式是服务器直接返回回来的,我们向服务器发请求获取详情页的展示信息,当数据返回回来之后,我们在主体盒子中通过v-html插入展示的数据,通过动态创建并追加link标签添加样式。为了防止离开详情页后样式影响到别的组件,需要在组件销毁前移除link标签。
8.点击登录按钮后发生的那些事。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AjKaKaZq-1678344606750)(C:\Users\雨落倾城夏末凉\AppData\Roaming\Typora\typora-user-images\image-20230210151614601.png)]
9.有部分接口向服务器发请求时需要携带token,time,sign。这个操作在封装axios请求的请求头中进行。
axios.interceptors.request.use(config => {
// 针对于部分接口,我们携带令牌和签名信息
let apiList = ["/check_login", "/user_info", "/user_update", "/store", store_remove", "/store_list"]
let token = localStorage.getItem("token");
if (apiList.includes(config.url.replace("/api", "")) && token) {
let time = +new Date();
let sign = md5(`${token}@${time}@zhufeng`);
config.headers['authorzation'] = token;
config.headers['time'] = time;
config.headers['sign'] = sign;
}
return config;
});
10.登录页用到了van-form和轻提示等基本组件。登录页需要在失去焦点和点击发送验证码,点击登录按钮的时候验证表单。失去焦点的验证直接通过:rules绑定,点击发送验证码的表单验证需要自定义的校验,点击登录时触发表单的submit事件自动进行表单校验。登录成功以后,需要存储token,修改vuex中isLogin,info等信息并进行路由跳转。
11.在进行路由跳转之前,要通过全局路由导航判断跳转页面是否是个人中心,修改个人信息或收藏页。如果是这些页面就要判断是否登录,如果已登录直接跳转,如果未登录则就跳往登录页先登录,不确定是否已登录(比如已登录但是刷新页面isLogin就又为空)则派发请求查看是否登录再进行后续操作。
router.beforeEach(async (to, from, next) => {
let arr = ['/person', '/updateperson', '/store'];
if (arr.includes(to.path)) {
// 检验是否已登录
let isLogin = store.state.isLogin;
// 已登录
if (isLogin) {
next();
}
// 未登录
if (isLogin == false) {
next('/login');
showToast("小主,请您先登录");
}
// 不确定是否已登录
if (isLogin === null) {
try {
let { code, data } = await api.checkLogin();
if (code !== 0) {
next('/login');
showToast("小主,请您先登录");
return;
}
store.commit('changeInfo', data);
store.commit('changeIsLogin', true)
next();
} catch (error) { }
return;
}
} else {
next();
}
})
12.如果未登录的情况下是从详情页通过点击收藏按钮进入登录页的,则登录成功之后会返回详情页,我们在详情页进行跳转的时候需要带上一个query参数,参数名为from,参数值为detail。从其他页面跳往登录页,成功之后就跳往个人中心。在登录成功进行路由跳转时需要进行判断,代码如下:
if (route.query.from) {
router.replace(`/${route.query.from}`);
} else {
router.replace("/person");
}
13.首页头部展示的图片并不是默认的,需要在组件挂载之前查看仓库中islogin和info是否为null,如果是则发请求获取这两个值。通过计算属性计算展示的图片,根据isLogin和info决定展示的图片是默认的还是服务器返回的。如果info值存在,则返回服务器返回的图片,否则展示默认的图片。
let pic = computed(() => {
let { isLogin, info } = store.state;
if (isLogin) return info.pic || timg;
return timg;
});
onBeforeMount(() => {
let { isLogin, info } = store.state;
if (isLogin == null) store.dispatch("changeIsLoginAsync");
if (info === null) store.dispatch("changeInfoAsync");
});
14.个人中心页,通过计算属性读取仓库中的info并在页面展示,当点击头像会进入个人中心,点击我的收藏跳转到收藏页,点击退出登录,清除token和vuex中存储的数据并进行路由跳转。退出登录代码如下:
// 退出登录
let logout = () => {
showToast("小主,您已经安全退出~");
localStorage.removeItem("token");
store.commit("changeStoreList", null);
store.commit("changeIsLogin", null);
store.commit("changeInfo", null);
router.replace("/login");
};