2.0.0
项目说明
该项目为web单页面应用,核心功能为显示时间。
技术栈使用 pnpm、Vite、Vue3、typescript、tailwind、github/api
功能需求
- 显示时间
- 显示随机壁纸
- 每日热词录入
非功能需求
- 移动端自适应
- 查看日前时间详细属性
页面对比
新项目预览
旧项目预览
旧页面
新页面
开发记录
Vscode Extension
- Vue 3 Pack
- Vue Extension Pack
- Tailwind CSS Extension Pack
alias设置
'@'默认生效、其他路径需要同时配置vite和typescript才生效,配置完最好重启ide
1.配置vite.config文件
{
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'#': path.resolve(__dirname, './src/views'),
'$': path.resolve(__dirname, './src/components'),
},
}
}
2.配置tsconfig.json文件
{
"compilerOptions": {
"paths": {
"/@/*": ["src/*"],
"/#/*": ["src/views/*"],
"/$/*": ["src/components/*"]
}
},
}
script setup
<script setup lang="ts">
import { reactive, watchEffect, computed, ref } from 'vue';
import gitPut from '$/gitPut.vue'; // 组件引入即可直接使用,不需要componet注册
// 以下变量都可以在template中使用
const menu = [...routes]; //非动态绑定
const menuShow = ref(false); //动态绑定,set/get时需要使用menuShow.value
const state = reactive({ //动态绑定。类似store/state,维护一组参数
imgUrl: '',
now: Date.now(),
twDate: computed(() => new TwDate(state.now)),
/*使用computed替代filter*/
weekMix: computed(() => state.twDate.getWeekEn() + '|' + state.twDate.getWeekZh()),
seasonMix: computed(() => state.twDate.getSeasonEn() + '|' + state.twDate.getSeasonZh()),
})
// watchEffect, 区别于watch只监听一个变量
watchEffect(() => {
// 可以定义所有变量监听,此处只监听了state.now
state.localStr = new TwDate(state.now).toLocaleString();
});
<script/>
Vue3 Hooks
最大区别就是取消了destroyed 改为 unMounted
创建hook.ts
log工具
import {
onBeforeMount,
onMounted,
// onBeforeUpdate,
// onUpdated,
onBeforeUnmount,
onUnmounted,
// onDeactivated,
// onActivated,
// onRenderTracked,
// onRenderTriggered
} from 'vue';
const HOOK_LIST: Map<string, any> = new Map([
['BeforeMount', onBeforeMount],
['Mounted', onMounted],
// ['BeforeUpdate', onBeforeUpdate],
// ['Updated', onUpdated],
['BeforeUnmount', onBeforeUnmount],
['Unmounted', onUnmounted],
// ['Deactivated', onDeactivated],
// ['Activated', onActivated]
// ['RenderTracked', onRenderTracked],
// ['RenderTriggered', onRenderTriggered]
]);
/**
* @description Hook 日志与回调
* @param cb
*/
export default (
cb:
| {
afterBeforeMount: () => void;
afterMounted: () => void;
afterUnmounted: () => void;
afterBeforeUnmount: () => void;
}
| any
) => {
HOOK_LIST.forEach((hook, key) => {
hook(() => {
console.log(`#Hook////${key}`);
let afterCb = cb?.[`after${key}`];
afterCb && afterCb();
});
});
};
使用hook.ts
<script setup lang="ts">
import hook from '@/common/hook';
import { reactive } from 'vue';
const state = reactive({
now: Date.now()
})
// timeInterval
let timer = setInterval(() => {
state.now = Date.now();
}, 800);
// test hook
hook({
afterUnmounted: () => {
clearInterval(timer);
timer = 0;
},
});
Class Date Extend
import dateEnum from './../enums/dateEnum';
export default class TwDate extends Date {
// 返回当天是当年的第几天
getDayInYear = () => {
return (
(Date.now() - new TwDate(new TwDate().getFullYear(), 0, 1, 0, 0, 0).getTime()) /
(24 * 3600 * 1000)
);
};
// 返回当前时刻是当天的第几毫秒
getMillSecInDay = () => {
const y = new TwDate().getFullYear();
const m = new TwDate().getMonth();
const d = new TwDate().getDate();
return Date.now() - new TwDate(y, m, d, 0, 0, 0).getTime();
};
// 返回当天/当年占比
getRatioYear = () => {
return this.getDayInYear() / 365;
};
// 返回当天/当月占比
getRatioMonth = () => {
const y = new TwDate().getFullYear();
const m = new TwDate().getMonth();
return new TwDate().getDate() / new TwDate(y, m + 1, 0).getDate();
};
// 返回当天/当周占比
getRatioWeek = () => {
return (new TwDate().getDay() || 7) / 7;
};
// 返回当前时刻/当天占比
getRatioDay = () => {
return this.getMillSecInDay() / (24 * 3600 * 1000);
};
// 返回文本:周-En
getWeekEn = () => {
return dateEnum.WEEK_EN[new TwDate().getDay()];
};
// 返回文本:周-Zh
getWeekZh = () => {
return dateEnum.WEEK_ZH[new TwDate().getDay()];
};
// 返回文本:季节-En
getSeasonEn = () => {
return dateEnum.SEASON_EN[Math.floor(new TwDate().getMonth() / 3)];
};
// 返回文本:季节-Zh
getSeasonZh = () => {
return dateEnum.SEASON_ZH[Math.floor(new TwDate().getMonth() / 3)];
};
}
随机图片地址
reference
unsplash:免费的高清摄影素材网站
const api = 'https://unsplash.it';
const randomId = Math.random() * 10;
const width = 1920;
const height = 1080;
let url = `${api}/${width}/${height}?random=${parseInt(randomId.toString(), 10)}`;
P.S.
也可用利用api获取Bing壁纸,但因为要处理跨域放弃了。如果是独立部署的话可以考虑。
// const bingUrl = 'https://cn.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&mkt=zh-CN';
tailwind使用
因为目前使用的是quasar框架,其中也有一些原子类的样式,所以思维上还是能理解。
但不习惯api的话起手比较吃力,有一定门槛,对与团队推行还是有一定难度。
个人感觉作为项目落地的话未来可能会有使用上的瓶颈,作为个人开发的话我目前还是比较中意的,有种中庸之美。
github/api
每日热词需求由 github/issue
实现,即输入热词回车后将参数由github/api
传递给github。
写入操作需要token验证,可以在github/开发者设置里创建。
因为token不可显性显示在代码中,所以对token采用非对称加密,每次请求要输入密钥,解码后获取token。
P.S.如果token直接暴露在代码中,该token会自动删除。
1.创建token,输入note即可,checkbox勾选repo即可
2.在github/repo/issue里创建新label,主要用于区分。
3.配置用户、项目 package.json
{
"config": {
"owner": "Mulander-J",
"repo": "timeWaster",
"label": "heart"
}
}
4.使用api
import axios from 'axios';
import pkg from './../../package.json';
const { owner, repo, label } = pkg.config;
const url = `https://api.github.com/repos/${owner}/${repo}`;
export default class Api {
static getIssueList = (since?: string): Promise<any> => {
since = since ? '&since=' + since : '';
return axios.get(`${url}/issues?filter=created&labels=${label + since}`);
};
static addIssue = (body: string, token: string): any => {
return axios.post(`${url}/issues?access_token=${token}`, {
labels: [label],
title: 'TW/' + new Date().toLocaleDateString(),
body,
});
};
}
打包及部署
打包前要根据仓库地址修改根目录属性
// [guide](https://vitejs.dev/config/)
export default defineConfig({
base: '/timeWaster'
});
使用github-page部署,这里用到了gh-pages
插件,两行命令直接部署。
{
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview",
"predeploy": "vite build",
"deploy": "gh-pages -d dist"
},
"devDependencies": {
"gh-pages": "^3.1.0"
},
}
结语
本意是紧跟潮流学点新东西,利用闲暇时间查资料等了一周,实际开发用了两天左右。
对于vue3没涉及具体业务还不清楚,对于vite的速度真的很喜欢。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。