目录
1、遇到的坑:用reactive定义数组,对数组赋值或者用concat方法都无法获取到响应数组
1、async await 要求:执行完第一个事件后,才开始执行第二个事件
2022.08.16
1、Vuex内容
commit:同步操作,数据提交至mutations,可用于读取用户信息写到缓存里
vue2:
组件中使用this.$store.commit('loginStatus',1)
或者使用 mapMutations 辅助函数
vue3:
组件中使用store.commit('home/SET_DETAIL', item)
在vuex文件中,使用commit('SET_DETAIL', data)
dispatch:含有异步操作,数据提交至actions,可用于向后台提交数据
vue2:this.$store.dispatch('isLogin',true)
vue3:store.dispatch('user/getInfo',参数 可要可不要)
读取 Vuex 的内容经常使用 computed 属性进行读取,因为 Vuex 的数据是响应式的
const bannerList = computed(() => store.state.home.bannerList)
使用常量替代 Mutation 事件类型
mutations: {
// 可以使用 ES2015 风格的计算属性命名功能
// 来使用一个常量作为函数名
['SET_NOTICE'](state: IBaseState, payload: INotice) {
// 修改 state
state.notice = payload
}
}
2、vue3组件间传值
以setup语法糖为例,子组件必须使用 defineProps
和 defineEmits
API 来声明 props
和 emits。
props
// ts写法
const props = defineProps<{
foo: string
bar?: number
}>()
const props = defineProps({
foo: String,
closeText: {
type: String,
default: '取消'
}
})
emits
<script setup lang="ts">
import { defineEmits } from 'vue'
const emit = defineEmits(["onClose"])
function clickButton(){
emit("onClose",参数 可有可无)
}
</script>
// ts写法
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
const click = () => {
emit('change', 1)
emit('update', 'abc')
}
3、typescript 接口interface
接口可以用来描述对象的形状
interface Speakable {
speak(): void;
readonly lng: string; //readonly表示只读属性 后续不可以更改
name?: string; //?表示可选属性
}
let speakman: Speakable = {
// speak() {}, //少属性会报错
name: "hello",
lng: "en",
age: 111, //多属性也会报错
};
2022.08.17
1、vue3 + vite 多环境打包 及axios的封装
- 开发环境、测试环境、生产环境
开发环境:npm run dev
测试环境:npm run build:test
生产环境:npm run build
- 配置环境变量
在项目根目录下新建三个配置文件
.env.development文件:
注意,后面不用加分号,注释也不能加在url后面,常量名也必须已VITE_开头。
NODE_ENV = development
VITE_APP_ENV = develop
VITE_APP_BASE_API = 'https://'
.env.production文件:
NODE_ENV = production
VITE_APP_ENV = release
VITE_APP_BASE_API = 'https://'
.env.test文件:
NODE_ENV = tset
VITE_APP_ENV = develop
VITE_APP_BASE_API = 'https://'
- 使用全局变量
vue-cli引用:
process.env.变量名
vite引用:
import.meta.env.变量名
- 配置打包命令:
"dev": "vite",
"build": "vue-tsc && vite build --mode production",
"build:test": "vue-tsc --noEmit --skipLibCheck && vite build --mode development",
- 创建axios实例,在配置axios时使用全局baseURL
const baseURL = import.meta.env.VITE_BASE_URL as string; //根据环境获得不同的代理模式
const service = axios.create({
baseURL
})
- 接口api
- 所有请求放在vuex的actions中
2、给背景加上毛玻璃效果
opacity: 0.75;
backdrop-filter: blur(10px) contrast(100%);
-webkit-backdrop-filter: blur(10px) contrast(100%); //解决ios端兼容性问题
3、stylus预编译器 .styl文件
不再需要花括号{},冒号: ,分号;
且可以嵌套
4、vuex 4 + ts
actions有两个参数。第一个参数{ commit, state }
= context
参数,vuex
的 d.ts
提供有类型 ActionContext
,用法如下:
import { ActionContext } from 'vuex'
const actions = {
setBanner (context: ActionContext<IHomeState, IStore>, payload: number) {
const res = await getBanner()
context.commit('SET_BANNER', res.data?.list)
}
//或者
async setBanner({ commit }: ActionContext<IHomeState, IStore>, payload: number) {
const res = await getBanner()
commit('SET_BANNER', res.data?.list)
}
}
5、vant组件库中的slot插槽怎么使用?
.vue文件中,使用<template #slot> </template>
6、vue单页项目判断有没有上一页
判断一个页面有没有上一页,没有就关闭页面,有的话就返回上一页
// 全局后置钩子函数
router.afterEach((to, from) => {
// 当页面为打开的第一个页面时,url添加进sessionStorage
if (!window.sessionStorage.firstUrl && to.path !== '/') {
window.sessionStorage.firstUrl = window.location.href
}
})
7、动态绑定class
<div :class="{ 'isActive' : active }"></div>
<div :class="{ 'isActive' : active === 1 }"></div>
<div :class="[ isActive ? 'active' : '' ]"></div>
2022.08.18
1、请求后端数据以及渲染前端页面过程
第一步,先在actions中发送请求,获取数据;
第二步,通过mutations函数将数据保存在vuex中;
第三步,获取vuex中的数据,渲染页面。
2、验证码倒计时60秒
<template>
<div class="count-down" :style="{ color: color }" @click="handleClick">
重新发送 {{ count ? `${count}s` : '' }}
</div>
</template>
<script setup lang="ts">
import { onBeforeUnmount, ref } from 'vue'
const count = ref(60)
const timer = ref<number>(0)
const emit = defineEmits(['onClick'])
defineProps({
color: {
type: String,
default: 'rgba(95, 143, 245, 1)'
}
})
const init = () => {
timer.value = setInterval(() => {
if (count.value > 0) {
count.value--
} else {
clearInterval(timer.value)
timer.value = 0
}
}, 1000) as unknown as number
}
init()
const handleClick = () => {
// 如果倒计时还在动,就不能点击重新倒计时
if (count.value > 0) {
return
}
count.value = 60
init()
emit('onClick')
}
// 在组件销毁之前,清除定时器
onBeforeUnmount(() => {
clearInterval(timer.value)
})
</script>
<style lang="stylus" scoped>
.count-down {
display: inline-block
color: rgba(95, 143, 245, 1);
}
</style>
2022.08.19
1、vue3 语法糖<script setup>
优点:
- 引入组件后可直接使用,无需注册
- 无需再return变量的值和函数
- defineProps,defineEmits,defineExpose,withDefaults这几个编译器宏,不需要手动引入
2、ts中数组的类型注解
const levelList = ref<{ name: string; icon: string }[]>([])
const levelList = ref<object[]>([])
3、css3 盒模型
盒模型有两种:标准盒模型、IE(替代)盒模型。
盒模型由“ content + padding + border + margin ”构成,大小由“ content + padding + border ”决定。 但是盒子内容宽/高(width / height)的计算范围根据盒子模型的不同而有所不同。
- 标准盒模型:只包含content
- IE(替代)盒模型:包含content + padding + border
可以通过 box-sizing 来改变元素的盒模型。
- 标准盒模型:box-sizing:content-box;
- IE(替代)盒模型:box-sizing:border-box;
4、vue3 祖孙组件通信:provide、inject
祖组件:
setup(){
let car = reactive({name:'奔驰',price:'40万'})
provide('car',car) //可以用来传递响影数据
}
孙组件:
setup(){
const car = inject('car')
return {car}
}
5、Pinia 全新的Vue状态管理库
抛弃传统的 Mutations
,只有 state, getters
和 actions。
1.安装Pinia:npm install pinia
2. 在main.ts中挂载pinia
import { createPinia } from 'pinia'
const pinia = createPinia()
app.use(pinia)
3. 创建Store
// src/store/index.ts
import { defineStore } from 'pinia'
export const mainStore = defineStore('main',{
state:() => {
return{
msg:'Hello Pinia!'
}
},
getters:{},
actions:{}
})
4. 使用Store
<template>
<div>{{ store.msg }}</div>
</template>
<script setup lang='ts'>
import { mainStore } from '../store/index'
const store = mainStore()
</script>
5. 解构Store:storeToRefs (ES6传统方式解构store,数据不会响应式更新)
<template>
<div>{{ count }}</div>
</template>
<script setup lang='ts'>
import { storeToRefs } from 'pinia'
const { conut } = storeToRefs(store)
</script>
6. 修改多条数据 $patch
$patch(对象)
$patch(函数)
7. 通过actions修改
actions:{
changeState(){
this.count++
this.msg = 'Pinia还挺简单!'
}
}
在组件中调用actions中的方法
const clickMe = () => {
store.changeState()
}
8. getters
Pinia 中的 getter 和 Vue 中的计算属性几乎一样
getter 中的值有缓存特性,如果值没有改变,多次使用也只会调用一次
2022.08.23
1、避免大量if...else...
示例1:
if(x===a){
res=A
}else if(x===b){
res=B
}else if(x===c){
res=C
}else if(x===d){
//...
}
改写成map的写法
let mapRes={
a:A,
b:B,
c:C,
//...
}
res=mapRes[x]
示例2:
const isMammal = (creature) => {
if (creature === "human") {
return true;
} else if (creature === "dog") {
return true;
} else if (creature === "cat") {
return true;
}
// ...
return false;
}
改写成数组
const isMammal = (creature) => {
const mammals = ["human", "dog", "cat", /* ... */];
return mammals.includes(creature);
}
2、将多个条件封装
if (
person.getAge() > 30 &&
person.getName() === "simon" &&
person.getOrigin() === "sweden"
) {
// ...
}
改写成
const isSimon =
person.getAge() > 30 &&
person.getName() === "simon" &&
person.getOrigin() === "sweden";
if (isSimon) {
// ...
}
转载于:写出干净的 JavaScript 5 个小技巧 - 掘金
3、css3实现图片边框动画
第二种样式css边框动画
div{
display: flex;
width: 258px
height: 258px
justify-content: center;
align-items: center;
padding: 11px;
background: url(@/assets/1.png) no-repeat
background-size: 100% 100%;
box-sizing: border-box
position relative
overflow hidden
border-radius: 20px
&::before{
content: ''
background-image: conic-gradient(rgba(255,255,255,0.3) 10deg, transparent 45deg, rgba(255,255,255,0.3) 90deg, transparent 135deg, rgba(255,255,255,0.3) 180deg, transparent 225deg, rgba(255,255,255,0.3) 270deg, transparent 315deg, rgba(255,255,255,0.3))
width 160%
height: 160%
position: absolute
animation: rotate 6s linear infinite
}
@keyframes rotate {
0%{
transform: rotate(0deg)
}
100%{
transform: rotate(360deg)
}
}
2022.08.25
1、遇到的坑:访问从后端获取的链接里面的数据,进行页面展示
actions:
mutations:
state:
在ts中获取vuex中的数据:
在页面上展示:
2、解决vuex刷新页面后state数据丢失
安装vuex-persist插件
npm install --save vuex-persist
在store文件下的index.ts中引入
import VuexPersistence from 'vuex-persist'
创建对象并配置
const vuexLocal = new VuexPersistence({
storage: window.localStorage
})
在vuex文件中引入
state:{},
mutations:{},
actions:{},
plugins:[vuexLocal.plugin]
2022.08.26
1、遇到的坑:用reactive定义数组,对数组赋值或者用concat方法都无法获取到响应数组
// 方法一:对已经获取的数组进行遍历,逐项push进变量里
let list = reactive([])
for(let i = 0; i < arr.length; i++){
list.push(arr[i])
}
// 方法二:采用扩展运算符
let list = reactive([])
list.push(...arr)
// 方法三:嵌套一层对象在外层
let obj = reactive({
list: []
})
obj.list = arr
// 方法四:使用ref去定义
let list = ref([])
list.value = arr
2、vue v-for之后怎么修改最后一项的样式
动态添加class名设置样式,:class="{'special-style':index===textList.length - 1}"
<div class="interest-item-context">
<van-row
class="interest-item-text"
v-for="(item, index) in textList"
:key="index"
>
<van-col
span="6"
:class="{ 'special-style': index === textList.length - 1 }"
>{{ item.key }}:</van-col
>
<van-col span="18">{{ item.value }}</van-col>
</van-row>
</div>
.special-style{
color: red;
}
2022.08.30
1、async await 要求:执行完第一个事件后,才开始执行第二个事件
2、api,发送path参数和body参数给后端
// api文件
export const updateAddressApi = (data: { id: string; data: IAddressApi }) => {
return service({
url: `/address/${data.id}`,
method: 'PUT',
data: data.data
})
}
// vuex:actions
async updateAddress(
{ commit }: ActionContext<ISettingsState, IStore>,
payload: { id: string; data: IAddressApi }
) {
try {
const res = await updateAddressApi(payload)
if (res.code === 200) {
// commit('SET_ADDRESS_DETAIL', res.data || {})
return Promise.resolve(res)
}
return Promise.reject(res)
} catch (error) {
return Promise.reject(error)
}
}
// .vue文件
const data = { id: tempEditId.value, data: IAddress }
const res = await store.dispatch('settings/updateAddress', data)
if (res.code === 200) {
showDialog.value = false
getData()
} else {
Toast.fail(res.message)
}
api文件:
actions中:
mutations中:
state中:
页面上:
2022.08.31
1、使用Day.js插件格式化时间
Day.js官网:安装 | Day.js中文网 (fenxianglu.cn)
下载dayjs
npm install dayjs --save
导入到组件中
import dayjs from 'dayjs'
格式化时间
dayjs(time).format('YYYY-MM-DD HH:mm:ss')
2、vue3实现骨架屏
安装
npm install -S @x-ui-vue3/skeleton
main.js文件
import { createApp } from 'vue'
import Skeleton from '@x-ui-vue3/skeleton'
import App from './App.vue'
createApp(App).use(Skeleton).mount('#app')
页面中使用
<template>
<label for="loading">点击切换</label>
<input v-model="loading" id="loading" type="checkbox" />
<br /><br />
<div v-skeleton="loading">
<span v-skeleton-item>超文本标记语言是一种用于创建网页的标准标记语言。</span>
<br /><br />
<span v-skeleton-item>www.runoob.com</span>
<br /><br />
<span v-skeleton-item>Good good study, day day up!</span>
</div>
</template>
<script setup>
import { ref } from 'vue'
const loading = ref(false)
</script>
转载于:@x-ui-vue3/skeleton - npm (npmjs.com)
3、两个盒子,左边固定宽,右边自适应
<div class="content">
<div class="left"></div>
<div class="right"></div>
</div>
方法一:float和BFC实现 (BFC: 使得内部元素不受外界因素影响)
.content {
height: 800px;
padding: 20px;
}
.left {
width: 200px;
height: 100%;
float: left;
}
.right {
height: 100%;
overflow: hidden;
}
方法二:absolute定位和margin值实现
.content {
height: 800px;
padding: 20px;
}
.left {
width: 200px;
height: 100%;
position: absolute;
}
.right {
height: 100%;
margin-left: 200px;
}
方法三:flex布局
.content {
height: 800px;
padding: 20px;
display: flex;
}
.left {
width: 200px;
height: 100%;
}
.right {
height: 100%;
flex: 1;
}
方法四:calc(100%-固定内容的宽度) 用calc函数动态计算数值
.content {
height: 800px;
padding: 20px;
}
.left {
width: 200px;
height: 100%;
float: left;
}
.right {
height: 100%;
float: left;
width: calc(100% - 200px);
}
方法五:使用table和table-cell实现
.content {
width: 100%;
height: 800px;
display: table;
}
.left {
width: 200px;
height: 100%;
display: table-cell;
}
.right {
height: 100%;
display: table-cell;
}
转载于:两个盒子,左边固定宽,右边自适应,你能想到几种方法? - 掘金 (juejin.cn)
4、flex:1;表示什么?
flex属性是 flex-grow,flex-shrink,flex-basis 的简写,默认值是flex:0 1 auto;
flex:1; 等同于 flex:1 1 0%; 适合等分布局。
flex-grow属性都为1,则它们将扩大,等分剩余空间。
flex-shrink属性都为1,当空间不足时,都将等比例缩小。
flex-basis属性为项目本身的大小,默认值是auto。