TypeScript与Vue组合开发记录点(三)
pinia
pinia与vuex相似,是Vue的存储库,允许跨组件/页面共享状态,适用于单页面应用。
安装脚手架vite
// 安装vite
npm init vite@latest
安装状态管理库pinia
npm install pinia
配置pinia
因为vue单页面应用,那么整个应用使用状态管理,就需要在main.js文件里配置。
// main.js
import { createApp } from 'vue'
import App from './App.vue'
// 导入构造函数
import { createPinia } from 'pinia'
// 实例化 Pinia
const pinia = createPinia()
// 创建Vue应用实例app
const app = createApp(App)
// 应用以插件形式挂载Pinia实例
app.use(pinia)
app.mount('#app')
定义状态仓库
在工程文件夹下新建store文件夹,内部有index.js文件用来定义状态
// src/store/index.js
// 引入仓库定义函数
import { defineStore } from 'pinia'
// 传入2个参数,定义仓库并导出
// 第一个参数唯一不可重复,字符串类型,作为仓库ID以区分仓库
// 第二个参数,以对象形式配置仓库的state,getters,actions
// 配置 state getters actions
export const mainStore = defineStore('main', {
// state 类似组件的data选项,函数形式返回对象
state: () => {
return {
msg: 'hello world!',
counter: 0
}
},
getters: {},
actions: {}
})
使用pinia状态变化
通过$path
方法,批量修改数据的变更。$path
传入一个对象,对象的属性就是各种状态。
<template>
<button @click="handleClick">修改状态数据</button>
<p>{{msg}}</p>
<!-- 或者使用解构之后的 -->
<p>{{counter}}</p>
</template>
<script setup>
// 导入状态仓库
import { mainStore } from "../store/index.js";
// 使普通数据变响应式的函数
import { storeToRefs } from "pinia";
// 实例化仓库
const store = mainStore();
// 解构并使数据具有响应式
const { msg,counter } = storeToRefs(store);
// 点击 + 1;修改字符串
function handleClick() {
store.$patch({
msg: "pinia good!",
counter: counter.value + 1,
});
}
</script>
数据持久化
pinia支持扩展插件实现数据持久化,该插件为pinia-plugin-persist
。使用Pinia持久化插件,在开发的过程中,像用户信息(名字,头像,token)需要pinia中存储且需要本地存储
export const useUserStore = defineStore({
state () {
return {
count: 0,
num: 100,
list: [1, 2, 3, 4 ]
}
},
persist: {
enabled: true, // 开启缓存 默认会存储在本地localstorage
storage: sessionStorage, // 缓存使用方式
paths:[] // 需要缓存键
}
})
缓存结果
SCSS样式
样式Scss就是Sass的升级版,其语法完全兼容 CSS3,css有一个特别不常用的特性,即@import规则,它允许在一个css文件中导入其他css文件。并且@import规则引入时,该行引入代码一定要先于其他任何css规则,除了@charset。
// import.css文件
.hd{
color: blue;
}
// index.html文件
<style type="text/css">
@import "import.css";
.hd{
color: orange;
}
</style>
...
<p class="hd">我是什么颜色</p>
这时,文本p内的字体就变成了import.css内定义的蓝色。
Props传递组件数据方法
举例,在App.vue中使用了school自定义的组件,同时,需要传递参数。
// App.vue代码
<template>
<div>
<!-- 在school标签内加参数,表示该参数需要传递 -->
<School name="李四" address="女"></School>
<hr>
</div>
</template>
<script>
//引入School组件
import School from "./components/School.vue";
//注册组件
export default {
name: "App",
components: { School },
};
</script>
<style>
</style>
然后在自定义组件school内要对需传递的参数进行确认
// School.vue代码
<template>
<div>
<h2>学校的名称:{{ name }}</h2>
<h2>学校的地址:{{ address }}</h2>
<h2>个人简介:{{msg}}</h2>
</div>
</template>
<script>
export default {
School: "School",
data() {
return {
msg:'我是一个程序员'
};
},
//使用props确认传过来的参数,第一种写法,数组的形式
props:['name','address']
};
</script>
在子组件中确认参数的形式仅以数组的形式进行确认有时会出现bug,所以还有第二种写法,规定传递的参数的数据类型。写法如下:
//school.vue的代码
<template>
<div>
<h2>学校的名称:{{ name }}</h2>
<h2>学校的地址:{{ address }}</h2>
<h2>个人简介:{{ msg }}</h2>
<h2>成立的年份:{{ year + 1 }}</h2>
</div>
</template>
<script>
export default {
School: "School",
data() {
return {
msg: "我是一个程序员",
};
},
//使用props确认传过来的参数,第一种写法,数组的形式
// props: ["name", "address","year"],
//使用props确认传过来的参数,第二种写法,确认了参数,然后对其参数的类型进行限制
props: {
name: String,
address: String,
year: Number,
},
};
</script>
样式的Scoped属性
当
样式深度选择器
样式深度选择器::v-deep
与/deep/
,可以用来修改组件中子组件的样式,对于某些组件的样式使用了scoped属性后,又想去修改嵌套的子组件的样式,就可以用到样式穿透,即深度选择器。
// 在某个组件的样式代码中修改子组件<el-button>样式
<style lang="scss" scoped>
// ::v-deep写法
.el-button::v-deep{
span{
color: '#f00'
}
}
// /deep/写法
/deep/.el-button{
span{
color: '#f00'
}
}
</style>
超出内容滚动条
Overflow:auto给div盒子内容超出盒子高度时加滚动条,需要指定div盒子高度
空数据显示内容
ElementUI当表格Table组件内无数据时,使用empty-text="暂无数据"显示
路由守卫中的next()
next()可以通俗的理解为放行的意思。括号内参数为空。
beforeEach((to, from, next) => {
to // 要去的路由
from // 当前路由
next() // 放行的意思
}
next() 是放行,但是如果next()里有参数的话,next()就像被重载一样,就有了不同的功能,比如next(’/login’) 、 next(to)
等等,表示中断当前导航,并执行新的导航。
// 这里的意义并不是执行去login的路由页面,而是中断当前导航,开始执行login导航
beforeEach((to, from, next) => {
next('/login')
}
因为没有next()进行放行,上述代码会陷入循环嵌套
// 陷入循环嵌套
beforeEach((to, from, next) => {
beforeEach(('/logon', from, next) => {
beforeEach(('/logon', from, next) => {
beforeEach(('/logon', from, next) => {
beforeEac... // 一直循环下去...... , 因为我们没有使用 next() 放行
}
}
}
}
所以next(’/login’)不是说直接去/login路由,而是中断这一次路由守卫的操作,又进入一次路由守卫。加入一些判断条件后,就会改变这个死循环了。
// 第一次如果导航访问home,会执行next('/login'),然后就会进入新的路由守卫,
// 此时路径改变了,没有了home,那么就执行else中的next()放行。
beforeEach((to, from, next) => {
if(to.path === '/home') {
next('/logon')
} else {
// 如果要去的地方不是 /home , 就放行
next()
}
}
js中的next()
Js多个异步请求,使用next()按顺序执行,next()表示继续下一步
全局前置守卫
router.beforeEach 注册一个全局前置守卫:当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于等待中。
calc()函数
css用来动态计算宽度的函数。需要注意的是,运算符前后都需要保留一个空格,例如:width: calc(100% - 10px);
#div1 {
position: absolute;
left: 50px;
width: calc(100% - 100px);
border: 1px solid black;
background-color: yellow;
padding: 5px;
text-align: center;
}
作用域插槽
作用域插槽,父组件决定了插槽处的样式,子组件决定插槽处的数据,slot-scope是vue旧版本写法,新写法是v-slot。
nextTick()
nextTick()等待下一次 DOM 更新刷新的工具方法。Vue 中更改响应式状态时,最终的 DOM 更新并不是同步生效的,而是由 Vue 将它们缓存到“next tick”以确保每个组件无论发生多少状态改变,都仅执行一次更新。$nextTick()
是绑定在实例上的nextTick()函数,组件传递给 this.$nextTick() 的回调函数会带上 this 上下文,其绑定了当前组件实例。
// 某组件内使用ref绑定名称,使用了v-if=showComp设定是否显示
<MyComp ref="mycomp" v-if="showComp"></MyComp>
我们假定showComp默认值是false,在另一个函数里尝试获取当前的showComp值
// 一个函数里给showComp填了true
this.showComp=true;
// 直接使用this.$refs.mycomp,会获取到false
// 因为刚才showComp还是false,刚刚才设置成true,dom还没来得及加载。
// 这个时候我们就需要使用nextTick函数。
// 使用nextTick会获取到true
this.$nextTick(()=>{
this.$refs.mycomp.refresh();
});
横线 hr /标签
<hr />
是网页编辑里的一个标签,其表现形式为一条横线。
Popup组件
Popup是组件库mand-mobile(滴滴金融组件库)的一个点击弹出层
This.$emit()
This.$emit()
是子组件向父组件传值,子组件this.$emit(‘title’,data)
,父组件@title=”title
”,就接收到了子组件中的data值。
注意:@title
是v-on:title
的简写。
CSS样式注意点
-
justify-content: left; /* 一个挨一个在对齐容器得左边缘 */
-
font-weight: bold /将字体设置为粗体/
v-bind和v-on
在父子组件传递数据时有用处:
1.父组件给子组件传递值
// 父组件给子组件v-bind绑定一个属性,作为传参的依据
<div>
<child :aaa="aa" :bbb="bb" @ccc="cc" />
</div>
data() {
return {
aa: 666,
bb: "999",
}
}
methods() {
cc: function() {
console.log("cc")
}
}
// 子组件使用props接收确认参数,并规定参数类型
props: {
aaa: [String, Number],
bbb: {
type: String,
default: 'qwe'
required: true
},
ccc: {
type: Function
}
}
2.子组件给父组件传递值
//子组件:使用$emit向父组件传递一个事件名和所带的数据
<button @click="toReturn">返回</button>
methods: {
toReturn() {
this.$emit("childValue", "子组件向父组件传值", true);
}
}
//父组件:通过v-on事件名接收子组件传递过来的参数
<div v-on:childValue="getChildValue"></div>
methods: {
getChildValue: function(data, data2) {
console.log(data, data2);
}
}
v-bind
v-bind指令用于设置HTML属性:v-bind:href 缩写为 :href
// v-bind完整语法
<a v-bind:href="url"></a>
// 简写
<a :href="url"></a>
// v-bind完整语法
<child v-bind:data="data" />
// 简写
<child :data="data" />
v-on
v-on 指令用于绑定HTML事件 :v-on:click 缩写为 @click
// v-on完整语法
<div v-on:click="clickFunc">按钮</div >
// 缩写
<div @click="clickFunc">按钮</div>
// v-on完整语法
<child v-on:returnFunc="toReturnFunc" / >
// 缩写
<child @returnFunc="toReturnFunc" />