Vue

流程

Vuecil脚手架安装

依次安装Node.js、Vue、Vuecli

安装脚手架

1
npm install -g @vue/cli

检查版本

1
vue --version

创建项目

1
vue create hello-world

图形化界面

1
vue ui

项目结构

image-20221216142020026

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
├── node_modules 
├── public
│ ├── favicon.ico: 页签图标
│ └── index.html: 主页面
├── src
│ ├── assets: 存放静态资源
│ │ └── logo.png
│ │── component: 存放组件
│ │ └── HelloWorld.vue
│ │── App.vue: 汇总所有组件
│ │── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件
├── README.md: 应用描述文件
├── package-lock.json:包版本控制文件

路由

Vue为只有一个index.html作为单页面,会用路由实现多页面的跳转,路由则将路径和页面进行映射

屏幕截图(53)

路由:router包下面写index.js文件并附上path和name(注意!!!path: ‘/‘也就代表了网页的第一个页面是什么)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'/* 1、引入AboutView.vue,一般不使用这种 */

Vue.use(VueRouter)

const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* 2、引入AboutView.vue,一般建议使用这种 */ '../views/AboutView.vue')
}
]

const router = new VueRouter({
routes
})

export default router

路由在组件中的使用:

1
2
3
4
5
<li><router-link :to="{ name: 'home' }">首页</router-link></li>
<li>
<router-link :to="{ name: 'bookclass' }"> 全部作品 </router-link>
</li>
<li><router-link :to="{ name: 'bookRank' }">排行榜</router-link></li>

当你点击 <router-link> 时, router.push 方法会在内部调用,所以说,点击<router-link :to="..."> 等同于调用 router.push(...)

1
2
3
4
5
6
7
8
// 字符串
router.push('home')

// 对象
this.$router.push({path: '/login?url=' + this.$route.path});

// 命名的路由
router.push({ name: 'user', params: { userId: 123 }})

路由在父组件App.vue的

1
2
3
4
5
6
7
8
9
10
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
name: 'App'
}
</script>

在views包下写主要页面,再在如Home.vue里引入components(当然组件自己也可以引用自己)

1
2
3
4
5
6
<template>
<div class="header">
<Top />
<Navbar />
</div>
</template>

引用组件时要记得注册组件,并引入文件位置

1
2
3
4
5
6
7
8
9
10
11
<script>
import Navbar from "@/components/common/Navbar";
import Top from "@/components/common/Top";
export default {
name: "Header",
components: {
Navbar,
Top,
},
};
</script>

Css的引入

在assets包(静态资源管理包)下建立css包并命名global.css

1
2
3
4
html, body, div {
margin: 0;
padding: 0;
}

随后在main.js中引入

1
import '@/assets/css/global.css';

axios

Axios 是一个基于 promise 网络请求库,这不是一种新技术,本质上还是对原生XMLHttpRequest的封装,用来实现Ajax(异步网络请求。Ajax能够让页面无刷新的请求数据,由来是浏览器页面在向服务器请求数据时,因为返回的是整个页面的数据,页面都会强制刷新一下,这对于用户来讲并不是很友好。并且我们只是需要修改页面的部分数据,但是从服务器端发送的却是整个页面的数据,十分消耗网络资源。而我们只是需要修改页面的部分数据,也希望不刷新页面,因此异步网络请求就应运而生。)

安装

1
npm install axios

最基础需要写比较长的配置来使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
<!-- 方法一 -->
axios({
method:"get",
url:"https://www.baidu.com",
data:user_id=7
}).then(res => {
console.log(res.data);
})
<!-- 方法二 -->
axios.get("https://www.baidu.com").then(res => {
this.obj= res.data[0];
console.log(res.data);
})
</script>

axios-1

axios-2

也可以全局注册后使用(这里不展开)

但是一般在请求数量增多时会需要来进行封装axios来简化开发

在utils下创建request.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import axios from 'axios'
import router from '@/router'
import { ElMessage } from 'element-plus'
import { getToken, removeToken, removeNickName, setToken } from '@/utils/auth'



axios.defaults.baseURL = process.env.VUE_APP_BASE_API_URL
axios.defaults.timeout = 10000
axios.defaults.withCredentials = true
axios.defaults.headers['X-Requested-With'] = 'XMLHttpRequest'
axios.defaults.headers.post['Content-Type'] = 'application/json'

axios.interceptors.request.use(config => {
config.headers['Authorization'] = getToken()
return config
}, error => {
console.log(error)
Promise.reject(error)
})

axios.interceptors.response.use(res => {
if (typeof res.data !== 'object') {
ElMessage.error('服务端异常!')
return Promise.reject(res)
}
if (res.data.code != "00000") {
if (res.data.message) {
ElMessage.error(res.data.message)
}
// 登录已过期
if (res.data.code == 'A0230') {
// 移除 token
removeToken();
removeNickName();
router.push({ path: '/login' })
}

return Promise.reject(res.data)
}

return res.data
}, error => {
ElMessage.error('网络异常!')
console.log(error)
Promise.reject(error)
})

export default axios

在main.js中注册

1
2
3
4
5
6
7
8
import axios from './util/axios';
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App);
app.use(store)
.use(router)
.mount('#app')
app.config.globalProperties.$axios = axios;

封装api在api包下

1
2
3
4
5
6
7
8
9
import request from '../utils/request'

export function listHomeBooks() {
return request.get('/front/home/books');
}
//动态传入用${bookId}
export function getBookById(bookId) {
return request.get(`/front/book/${bookId}`);
}

然后在vue的生命周期函数里调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<script>
<!--导入-->
import { reactive, toRefs, onMounted } from "vue";
import { useRouter, useRoute } from "vue-router";
import { listHomeBooks } from "@/api/home";
<!--注册组件-->
export default {
name: "home",
components: {
Header,
LatestNews,
FriendLink,
BookVisitRank,
BookNewestRank,
BookUpdateRank,
Footer,
},
setup() {
<!--route是一个跳转的路由对象,每一个路由都会有一个route对象,是一个局部的对象,可以获取对应的name,path,params,query等:-->
const route = useRoute();
<!--router是VueRouter的一个对象,通过Vue.use(VueRouter)和VueRouter构造函数得到一个router的实例对象,这个对象中是一个全局的对象,包含了所有的路由包含了许多关键的对象和属性。例如history对象$router.push({path:’/path’}); 本质是向history栈中添加一个路由,在我们看来是 切换路由,但本质是在添加一个history记录-->
const router = useRouter();
<!--reactive()函数接收一个普通对象,返回一个响应式的数据对象。访问: state.参数名-->
const state = reactive({
// 轮播图
sliderContent: [],
// 顶部栏
topBooks1: [],
//本周强推
weekcommend: [],
// 热门推荐
hotRecommend: [],
// 精品推荐
goodRecommend: [],
imgBaseUrl: process.env.VUE_APP_BASE_IMG_URL,
});
<!--生命周期函数-->
onMounted(async () => {
const loadingInstance = ElLoading.service({
target: "#topBooks2",
text: "加载中。。。",
});
<!--等待listHomeBooks()方法传来数据给data-->
const { data } = await listHomeBooks();

<!--可能book和v-for的item相似,属于别名,这段的意思就是从-->
await data.forEach((book) => {
if (book.type == 0) {
// 轮播图
state.sliderContent[state.sliderContent.length] = book;
}
if (book.type == 1) {
// 顶部栏
state.topBooks1[state.topBooks1.length] = book;
}
if (book.type == 2) {
//本周强推
state.weekcommend[state.weekcommend.length] = book;
}
if (book.type == 3) {
//热门推荐
state.hotRecommend[state.hotRecommend.length] = book;
}
if (book.type == 4) {
//精品推荐
state.goodRecommend[state.goodRecommend.length] = book;
}
});

<!--push就是在浏览器的历史记录中追加一个新的记录,你可以通过window.history看到这个记录。而replace则是将当前的路由记录替换掉,替换后你如果回退,会发现上一个路由地址已经消失了。此处是点击链接后触发的方法,将跳转到小说的详情页-->
const bookDetail = (bookId) => {
router.push({ path: `/book/${bookId}` });
};
<!--toRefs将一个响应式对象,转换为普通对象,并且将其中的属性转换为 Ref 对象,与reactive搭配使用,reactive将数据变成响应式数据,当数据发生变化时state也会自动更新-->
return {
...toRefs(state),
bookDetail,
};
}
};
</script>

Vue3与Vue2的不同

Vite脚手架的安装

1
2
3
4
5
6
7
8
## 创建工程
npm init vite-app vue3_test-vite
## 进入工程目录
cd vue3_test-vite
## 安装依赖
npm install
## 运行
npm run dev

分析文件目录

main.js

Vue2项目的main.js

1
2
3
4
5
6
7
8
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
render: h => h(App),
}).$mount('#app')

我们再来看看Vue3项目中的main.js

1
2
3
4
import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

分析一下

1
2
3
4
5
6
7
8
9
// 引入的不再是Vue构造函数了,引入的是一个名为createApp的工厂函数
import { createApp } from 'vue'
import App from './App.vue'

// 创建应用实例对象——app(类似于之前Vue2中的vm,但app比vm更“轻”)
const app = createApp(App)
console.log(app)
// 挂载
app.mount('#app')

App.vue

我们再来看看组件

template标签里可以没有根标签了

1
2
3
4
5
<template>
<!-- Vue3组件中的模板结构可以没有根标签 -->
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
</template>

setup函数

代替了Vue2的data()以及method

vue基础

ref

一、ref函数的引入

  import { ref } from ‘vue’

二、什么是 ref

​ 1、ref 和 reactive 一样都是实现响应式数据的方法

​ 2、由于 reactive 必须传递一个对象,所以导致我们再企业开发中,如果我们只想让某个变量实现响应式的时候非常麻烦,所以 Vue3 就提供了 ref 方法,实现对简单值的监听

三、ref 的本质

const input = ref(‘’)

  ref 底层的本质还是 reactive 系统会自动根据我们给 ref 传入的值将他转换成 ref(xx) —— reactive({value: xx})

四、ref 注意点

  1、在 VUE 的模板中使用 ref 的值不需要通过 value 获取 (Vue 会通过自动给 ref 的值加上 .value)

  2、在 js 中使用 ref 的值必须使用 .value 获取’

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<uni-popup ref="popup" type="dialog">
<!--mode="base"为对话框加两个按钮的形式-->
<uni-popup-dialog mode="base" title="确定要放弃吗?" content="本次计时将不会得到任何分数" :duration="2000" :before-close="true"
@close="close" @confirm="confirm"></uni-popup-dialog>
</uni-popup>
</template>
<script setup>

let popup = ref(null); //记着赋初值,本质是reactive({value:null})

const timeEnd = () => {
popup.value.open() //记得.value然后调用函数
}
const confirm = () => {
popup.value.close()
}

const close = () => {
popup.value.close()
}
</script>

v-for

在v-for中使用字符串

string:data中的源数据字符串,

str:data数据string字符串的每一个,

index:string字符串索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div id="app">
<!-- 这里表示被vue控制的区域 -->
<ul>
<li v-for="(str, index) in string">
{{ index }}---{{ str }}
</li>
</ul>
</div>
<script>
const vm = new Vue({
el: '#app', // 控制id为app的元素
data: {
// 存放所需要的数据
string: 'abcdefgh'
}
})
</script>

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<div class="sliderContent">
<dl class="scBigImg" id="carouseBig">
<dd v-for="(item, index) in sliderContent" :key="index" :class="{ on: index == 0 }">
<!-- 点击触发bookDetail函数,传入sliderContent.bookid,并在最后使用push来跳转到小说的详情页 -->
<a href="javascript:void(0)" @click="bookDetail(item.bookId)">
<img :src="`${imgBaseUrl}` + `${item.picUrl}`" :alt="item.bookName"
onerror="this.src='default.gif';this.onerror=null" />
</a>
</dd>
</dl>
</div>
</template>

其中

sliderContent:data中的源数据数组(会从数据库中获得),

item:data数据list数组的别名,

index:list数组索引,即为数据数字的多少

函数传入数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//targetNoTimeDelete(index)来传入数据index,方便下面接收
<view class="targetMenuDetailD" v-for="(item, index) in state.targetNoTime" :key="index"
@click="targetNoTimeDelete(index)">
<text>{{item.targetName}}</text>
<view class="targetMenuDetailDI">
<img src="@\static\coin.svg" style="width: 34rpx;height:34rpx;">
<text>X{{item.targetPoint}}</text>
</view>
<text>任意时间</text>
</view>
<script setup>
//记忆一下书写方式
const targetNoTimeDelete = (index) => {
uni.request({
url: 'http://localhost:8181/target/delete',
method: "POST",
data: {
targetName: state.targetWithTime[index].targetName,
ifPoints: 1,
},
success: (res) => {
console.log(res)
// 从targetWithTime数组中移除已删除的目标数据
state.targetWithTime.splice(index, 1);
user.data.point = res.data.data.targetPoint
}
})
}
</script>

route.params.categoryId的用法

route.params.url参数

url参数来自于:categoryId(要求与该变量完全一致,卡了半小时QAQ)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const routes = [
{
path: '/:categoryId',
name: 'bookcategory',
component: () => import('@/views/BookCategory')

},
]

const router = createRouter({
history: createWebHashHistory(),
routes
})

export default router

正式使用:

在onMounted生命周期函数里,就用变量来接,之后要记得放进带参数的变量里,还是要求id参数一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<script>
import { reactive, toRefs, onMounted } from "vue";
import { useRouter, useRoute } from "vue-router";
import { listHomeCategoryId } from "@/api/home";
export default {
name: "home",
setup() {
const route = useRoute();
const router = useRouter();
const state = reactive({
// 分类推荐
catagorycommendId: [],
});
onMounted(async () => {
const categoryId = route.params.categoryId-2;
dataCategoryId(categoryId);
});
const dataCategoryId = async (categoryId) => {
const { data } = await listHomeCategoryId(categoryId);
await data.forEach((book) => {
{
//历史推荐
state.catagorycommendId[state.catagorycommendId.length] = book;
}
});
}
return {
...toRefs(state),
dataCategoryId,
};
}
}
</script>

组件

组件是.vue的单文件组件,实现代码复用,以及理顺css以及js之间的关系,可以用script setup这样就可以在引入之后自动注册组件

选项式api

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<script>
//js对象
export default {
name: "targetMenu",
//实例对象的属性
data() {
//返回出去的才是响应式数据
return {
showFocusAfter: true,
showTargetAfter: false,
showTargetBefore: true,
showFocusBefore: false,
};
},
//实例对象的方法
methods() {
const menuItems = this.$route.meta.menuItems || []
if (menuItems.includes('target')) {
this.showFocusAfter = false;
this.showTargetBefore = false;
this.showTargetAfter = true;
this.showFocusBefore = true;
}
},
};
</script>

组合式api

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
<script>
//使用setup,可以直接写变量,但是只是普通变量,需要通过const state = reactive({hotRecommend: [], });来变成响应式变量,也可以用ref(),可以接收任何值,不止于此对象,把数据转化为{value:0}
import { reactive, toRefs, onMounted } from "vue";
import { useRouter, useRoute } from "vue-router";
import Header from '@/components/Header';
import Footer from '@/components/Footer';
import { listHomeBooks, listHomeCategory,listHomeCategory2,listHomeCategory3,listHomeCategory4 } from "@/api/home";
import { getBookById, } from "@/api/book";
import { SetUp } from '@element-plus/icons-vue';
export default {
name: "home",
components: {
Header,
Footer,
},
setup() {
const route = useRoute();
const router = useRouter();
const state = reactive({
// 热门推荐
hotRecommend: [],
// 精品推荐
goodRecommend: [],
// 分类推荐
catagorycommend: [],
catagorycommend2: [],
catagorycommend3: [],
catagorycommend4: [],
imgBaseUrl: process.env.VUE_APP_BASE_IMG_URL,
});
onMounted(async () => {
const { data } = await listHomeBooks();
await data.forEach((book) => {

if (book.type == 3) {
//热门推荐
state.hotRecommend[state.hotRecommend.length] = book;
}
if (book.type == 4) {
//精品推荐
state.goodRecommend[state.goodRecommend.length] = book;
}
});
dataCategory();
dataCategory2();
dataCategory3();
dataCategory4();
});
const bookDetail = (bookId) => {
router.push({ path: `/book/${bookId}` });
};
const dataCategory = async () => {
const { data } = await listHomeCategory();
await data.forEach((book) => {
if (book.categoryName == "历史军事") {
//历史推荐
state.catagorycommend[state.catagorycommend.length] = book;
}
});
}
const dataCategory2 = async () => {
const { data } = await listHomeCategory2();
await data.forEach((book) => {
if (book.categoryName == "科幻灵异") {
//历史推荐
state.catagorycommend2[state.catagorycommend2.length] = book;
}
});
}
const dataCategory3 = async () => {
const { data } = await listHomeCategory3();
await data.forEach((book) => {
if (book.categoryName == "都市言情") {
//历史推荐
state.catagorycommend3[state.catagorycommend3.length] = book;
}
});
}
const dataCategory4 = async () => {
const { data } = await listHomeCategory4();
await data.forEach((book) => {
if (book.categoryName == "玄幻奇幻") {
//历史推荐
state.catagorycommend4[state.catagorycommend4.length] = book;
}
});
}


return {
...toRefs(state),
bookDetail,
dataCategory,
};
}
}

</script>

v-bind

当需要在属性内使用动态数据的时候则需要使用v-bind,标签内则可以直接使用双大括号,可简写为:

1
2
3
4
5
6
7
<template>
<img v-bind:src="imgPath"/>
</template>
//简写
<template>
<img :src="imgPath"/>
</template>

style scoped

这样设置会成为局部样式

1
2
3
4
5
6
7
8
9
10
<style scoped lang="scss">
.menu {
width: 200px;
margin-left: 20px;
}

.menuButton {
margin-top: 50px;
}
</style>

style module

css 变成模块,通过:class=”$style.box1”使用

v-on

v-on:click简写为@click

v-model

双向绑定

此处还采用了pinia集中管理状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<view class="LoginInput">
<text>你的邮箱</text>
<input v-model="user.data.userEmail" name="txtEmail" type="text" id="txtPassword" placeholder="请输入邮箱"
class="LoginInputIn">
</view>

<view class="LoginP">
<text>登陆</text>
<text>将使用{{user.data.userEmail}}登陆</text>
</view>

<script setup>
import {
useUserStore
} from "../../store/user";
const user = useUserStore()
</script>

store

Router

引入

1
import {useRoute,useRouter} from 'vue-router';

使用

1
2
3
4
5
6
7
const route = useRoute();
const router = useRouter();
const HomeR = () => {
router.push({
path: `/Login`
});
}

Route

引入

1
import {useRoute,useRouter} from 'vue-router';

使用

1
2
3
const route = useRoute();
const router = useRouter();
const id = route.params.id;

params

props

在组合式api和setup语法糖中需要按下面的方式进行定义

1
2
3
4
5
6
7
8
<script setup>
const props = defineProps({
navbarTo: {
type: String,
required: true
}
})
</script>

组件传参

1、组件内定义props

1
2
3
4
5
6
7
8
<script setup>
const props = defineProps({
navbarTo: {
type: String,
required: true
}
})
</script>

2、在需要使用组件的部分进行数据双向绑定

1
<Navbar :navbarTo="'/menu/2'"></Navbar>

路由传参

1、定义参数:router-link :to=“params”

1
2
3
4
5
<a href="#">
router-link :to="props.navbarTo">
<img src="@\assets\Filter.png" alt="#">
</router-link>
</a>

2、接收参数

1
2
const route = useRoute();
const id = route.params.id;

v-if

1、在需要控制出现与否的标签上加上v-if(注意不需要双向绑定 :,但是要加上state.)

1
2
3
4
<FocusAfter v-if="state.showFocusAfter"></FocusAfter>
<TargetBefore v-if="state.showTargetBefore"></TargetBefore>
<FocusBefore v-if="state.showFocusBefore"></FocusBefore>
<TargetAfter v-if="state.showTargetAfter"></TargetAfter>

2、通过响应式的数据,更改标签出现与否

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const route = useRoute();
const state = reactive({
showFocusAfter: true,
showTargetBefore: true,
showFocusBefore: false,
showTargetAfter: false,
});

const id = route.params.id;
if (id == 2) {
state.showFocusAfter = false;
state.showTargetBefore = false;
state.showFocusBefore = true;
state.showTargetAfter = true;
}

依赖注入

用于父组件和子组件的数据传递(但是只能用于父组件和子组件)

父组件

1
2
3
4
import {provide} from "vue";
provide: {
message: '这是祖先组件提供的消息'
},

子组件

1
2
3
4
import {inject} from 'vue';
provide: {
message: '这是祖先组件提供的消息'
},

pinia

在面对多组件内传参的情况下,可以使用集中的状态管理pinia对应vue2的vuex

1、创建store文件夹下的js文件进行后续定义

2、定义状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import {
defineStore
} from "pinia"

export const useUserStore = defineStore('user', {
state: () => ({
data: {
userEmail: "",
userName: "",
userPassword: "",
userCode: ""
}
}),
actions: {}
})

3、在所需要该状态的组件内进行引用和使用

1
2
3
4
5
6
7
8
import {
useUserStore
} from "../../store/user";

const state = reactive({})
const user = useUserStore()

console.log(user.data.userEmail)

computed

1
2
3
4
5
6
7
8
9
10
11
12
//<view class="timerWork">
//<!-- 计时 -->
//<text>{{ formattedTime }}</text>
//</view>
// 获取格式化后的时间
const formattedTime = computed(() => {
state.hours = Math.floor(state.remainingTime / 3600);
state.minutes = Math.floor((state.remainingTime % 3600) / 60);
state.seconds = Math.floor(state.remainingTime % 60);

return `${formatTime(state.hours)}:${formatTime(state.minutes)}:${formatTime(state.seconds)}`;
});

js基础

时间类函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//今天的时间
let time = new Date()
let myDate = time.getDate()
let dayNum = time.getDay()
let myMonth = time.getMonth()

//初始化一个date对象
const selectedDate = new Date(time.getFullYear(), state.dayList[0].month, 1);
time = new Date(selectedDate); // 创建新的日期对象

//时间自增
for (let i = 0; i < 30; i++) {
time.setDate(time.getDate() + 1)
state.dayList.push({
'day': time.getDate(),
'month': time.getMonth(),
'week': weeks[time.getDay()],
'className': 'Num'
})
}

//设置时间
time.setDate(time.getDate() + 1)
time.setDate(state.dayList[index].day)
time.setMonth(state.dayList[index].month)
//时间差计算
let currentTime = new Date(target.data.currentTime)
let timeDiff = item.getTime() - currentTime.getTime()
let dayDiff = Math.floor(timeDiff / (1000 * 3600 * 24))

数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//定义
const state = reactive({
dayList: []
});
//遍历
target.data.deadlineDate.forEach((item) => {
let currentTime = new Date(target.data.currentTime)
let timeDiff = item.getTime() - currentTime.getTime()
let dayDiff = Math.ceil(timeDiff / (1000 * 3600 * 24))
});
//增加元素
state.dayList.push({
'day': time.getDate(),
'month': time.getMonth(),
'week': weeks[time.getDay()],
'className': 'Num'
})
//去除元素
const targetNoTimeDelete = (index) => {
// 从targetWithTime数组中移除已删除的目标数据
//第index个元素开始,删除1个
state.targetWithTime.splice(index, 1);
}

字符串拼接

1
2
3
4
5
6
7
8
9
//拼接为“20:02”
const hours = item.getHours();
const minutes = item.getMinutes();
const timeString = `${hours}:${minutes.toString().padStart(2, '0')}`

//拼接为“5.30”
const month = item.getMonth() + 1; // 当前月份
const date = item.getDate(); // 当前日期
dayDiff = `${month}.${date}`;

math函数

1
2
3
4
5
6
//Math.floor() 函数总是返回小于等于一个给定数字的最大整数。
let dayDiff = Math.floor(timeDiff / (1000 * 3600 * 24))
//Math.ceil() 函数总是四舍五入并返回大于等于给定数字的最小整数。
let dayDiff = Math.ceil(timeDiff / (1000 * 3600 * 24))
//Math.trunc() 方法会将数字的小数部分去掉,只保留整数部分。
let dayDiff = Math.trunc(timeDiff / (1000 * 3600 * 24))

substr

1
2
3
4
5
6
const str = "Hello, world!";
console.log(str.substr(7)); // 输出 "world!"
console.log(str.substr(-6)); // 输出 "world!"
console.log(str.substr(7, 5)); // 输出 "world"

//<text class="UserProgressDC">比起上{{state.UserTimeP.substr(-2)}}</text>

练习

数组刷新重复输入问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//关键在于在函数里给数组赋初值为空,这样每次刷新都会清空数组,方便放入下一组数据
onMounted(
uni.request({
url: 'http://localhost:8181/target/get',
method: "POST",
data: user.data.userEmail,
success: (res) => {
//数组赋初值为空
target.data.deadlineDate = []

for (let i = 0; i < res.data.data.length; i++) {
if (res.data.data[i].status == 0) {
state.targetNoTime.push({
'targetName': res.data.data[i].targetName,
'targetDescribe': res.data.data[i].targetDescribe,
'targetPoint': res.data.data[i].targetPoint,
'deadline': res.data.data[i].deadline,
})
} else if (res.data.data[i].status == 1) {

let deadline = new Date(res.data.data[i].deadline)
if (!target.data.deadlineDate.includes(deadline)) {
target.data.deadlineDate.push(deadline);
}
state.targetWithTime.push({
'targetName': res.data.data[i].targetName,
'targetDescribe': res.data.data[i].targetDescribe,
'targetPoint': res.data.data[i].targetPoint,
'deadline': res.data.data[i].deadline,
})
} else if (res.data.data[i].status == 2) {
state.targetCompleted.push({
'targetName': res.data.data[i].targetName,
'targetDescribe': res.data.data[i].targetDescribe,
'targetPoint': res.data.data[i].targetPoint,
'deadline': res.data.data[i].deadline,
})
} else if (res.data.data[i].status == 3) {
state.targetExpire.push({
'targetName': res.data.data[i].targetName,
'targetDescribe': res.data.data[i].targetDescribe,
'targetPoint': res.data.data[i].targetPoint,
'deadline': res.data.data[i].deadline,
})
}
}
}
})

)

后端交互完前端页面不更新问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//关键在于,响应式的数据没有更新,所以界面就没有更新
const targetNoTimeDelete = (index) => {
uni.request({
url: 'http://localhost:8181/target/delete',
method: "POST",
data: {
targetName: state.targetWithTime[index].targetName,
ifPoints: 1,
},
success: (res) => {
console.log(res)
// 从targetWithTime数组中移除已删除的目标数据
//这里的对state.targetWithTime和user.data.point的操作很关键
//因为数据库操作成功了,但是前端数据并没有发送改变
//这时候就需要对响应式数据进行相应的改变,来保证页面的改变
state.targetWithTime.splice(index, 1);
user.data.point = res.data.data.targetPoint
}
})
}

关于点击转换样式的练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//关键在于通过数组的方式,使得每个v-for生成的元素都有它独立的className:state.dayList[index].className
//这样就可以通过改变特定的className,来达成效果
<template>
<view class="tagetDay">
<scroll-view class="scroll-view" scroll-x="true" enable-flex="true">
<view class="Mon" v-for="(item, index) in state.dayList" :key="index" @click="classChange(index)">
<text>{{item.week}}</text>
<view :class="item.className">
<text class="Num">{{item.day}}</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup>
const state = reactive({
dayList: []
});

state.dayList.push({
'className': 'NumCenter'
})
for (let i = 0; i < 30; i++) {
time.setDate(time.getDate() + 1)
state.dayList.push({
'className': 'Num'
})
}

const classChange = (index) => {
state.dayList.forEach((item) => {
item.className = "Num"; // 先将所有日期的 className 属性设置为 Num
});
state.dayList[index].className = "NumCenter"; // 将点击的日期的 className 属性设置为 NumCenter

};
</script>

前端获取不到初值的原因

使用中经常出现,赋不上初值的问题,需要在刷新时才能获取到数据,在通过打印后发现,网络请求返回前,初值已经赋上,解决方法就是在需要该数据的前一个页面就要加上请求语句,获取到所有的数据(clock的bug QAQ)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<script setup>
const home = () => {
uni.request({
url: 'http://localhost:8181/user/home',
method: "POST",
data: user.data.userEmail,
success: (res) => {
if (res.data.code != null) {
uni.redirectTo({
url: '../../pages/Login/Login'
});
} else {
uni.redirectTo({
url: '../../pages/Sign/Sign'
});
}
}
})
//后续需要的tag数据
uni.request({
url: 'http://localhost:8181/tag/get',
method: "POST",
data: user.data.userEmail,
success: (res) => {
console.log(res)
tag.data.tagName = res.data.data[0].tagName
tag.data.tagDescribe = res.data.data[0].tagDescribe
tag.data.tagPoint = res.data.data[0].tagPoint
tag.data.tagHour = res.data.data[0].tagHour
tag.data.tagMinute = res.data.data[0].tagMinute
}
})
//此处的time.data.remainingTime其实还是零,原因就是上面所说的,所以在下一个页面有相同的语句,获取到time.data.remainingTime的初值,此处因为在需要数据的页面前有两个页面才能完成需求,不知道有没有其他办法来优化
time.data.remainingTime = (tag.data.tagHour * 3600) + (tag.data.tagMinute * 60) + tag.data.tagSecond
}
</script>