vue3
vue2和vue3的区别
1、v-if 和v-for
- 同一元素上使用的
v-if
和v-for
优先级已更改,但不推荐同时使用v-if
和v-for
。
2、组件事件
- 组件事件需要在
emits
选项中声明
3、生命周期
destroyed
生命周期选项被重命名为unmounted
beforeDestroy
生命周期选项被重命名为beforeUnmount
4、自定义指令
- 自定义指令的API已更改为与组件生命周期一致
5、新增组件
- 新增了三个组件:
Fragment
支持多个根节点、Suspense
可以在组件渲染之前的等待时间显示指定内容、Teleport
可以让子组件能够在视觉上跳出父组件(如父组件overflow:hidden)
6、新增指令
- 新增指令
v-memo
,可以缓存 html 模板,比如 v-for 列表不会变化的就缓存,简单说就是用内存换时间。
7、proxy响应式系统
- 用
Proxy
代替 Object.defineProperty 重构了响应式系统,可以监听到数组下标变化,及对象新增属性,因为监听的不是对象属性,而是对象本身,还可拦截 apply、has 等13种方法
8、重构DOM和diff算法
- 重构了虚拟 DOM,在编译时会将事件缓存、将 slot 编译为 lazy 函数、保存静态节点直接复用(静态提升)、以及添加静态标记、Diff 算法使用 最长递增子序列 优化了对比流程,使得虚拟 DOM 生成速度提升
200%
9、在CSS绑定JS变量
- 支持在
<style></style>
里使用v-bind
,给 CSS 绑定 JS 变量(color: v-bind(str)
)
10、新增 Composition API
- 新增
Composition API
可以更好的逻辑复用和代码组织,同一功能的代码不至于像以前一样太分散,虽然 Vue2 中可以用 minxin 来实现复用代码,但也存在问题,比如方法或属性名会冲突,代码来源也不清楚等
11、移除set和delete
- 全局函数
set
和delete
以及实例方法$set
和$delete
移除。基于代理的变化检测已经不再需要它们了
12、vue3使用Ts写,不兼容IE11
- 毕竟 Vue3 是用
TS
写的,所以对TS
的支持度更好 - Vue3 不兼容
IE11
13、移除实例方法
$on
,$off
和$once
实例方法已被移除,组件实例不再实现事件触发接口。
组合式API
setup
//方式一:
<script lang="ts">
//optipn API
//引入
import { defineComponent, onBeforeMount, onMounted } from 'vue'
//导出
export default defineComponent({
setup(){
let a = 1
const test = ()=>{
console.log('test方法');
}
onBeforeMount(()=>{
console.log('挂载前');
})
onMounted(()=>{
console.log('挂载后');
})
return {
a,
test
}
}
})
</script>
//方式二: 写在script标签上
<script lang="ts" setup>
// 组合式Api
import { onBeforeMount, onMounted, ref ,reactive} from 'vue'
// reactive就可以不用.value了
let a = reactive({
name:"张三"
});
const test = () => {
console.log('test方法');
a.name = a.name+1
}
// 类型可以不屑,会自动推导
let num = ref<number>(1);
const add1 = (): void => {
num.value++; // 注意通过ref声明的变量,所以js要修改对应的值是要通过.value访问才可以,template模板不需要通过.value访问
};
onBeforeMount(() => {
console.log('挂载前');
})
onMounted(() => {
console.log('挂载后');
})
let num1 = ref<number>(0)
let num2 = ref<number>(0)
let count = ref<number | string>("")
const addNum=()=>{
count.value = num1.value * num2.value
}
</script>
生命周期
下表包含如何在 Option API 和 setup() 内部调用生命周期钩子
Option API | setup |
---|---|
beforeCreate | - |
created | - |
beforeMount | OnBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
activated | onActivated |
deactivated | onDeactivated |
因为
setup
是在beforeCreate
和created
生命周期钩子前运行的,所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在setup
函数中编写。
响应式数据
ref
声明响应式数据,用于声明基本数据类型
//用法:
//引入:
import { ref } from "vue";
//声明变量
let num = ref<number>(1);
//调用或者修改值变化
const add1 = (): void => {
num.value++; // 注意通过ref声明的变量,所以js要修改对应的值是要通过.value访问才可以,template模板不需要通过.value访问
};
//或者可以简写,省略数据类型
const add1=()=>{
num.value++;
}
- ref可以传递基础数据类型和引用数据类型。如果是基础数据类型,那么这个基础数据值保存在返回的响应式数据的
.value
上;如果是对象,响应式数据在.value
上。 - ref本质是将一个数据变成一个对象,这个对象具有响应式特点
reactive
reactive声明响应式数据,用于声明引用数据类型
//用法:
//引入:
import { reactive } from "vue";
//声明变量
let state = reactive<ObjItf>(obj);
//或者
let state = reactive({
name:"张三"
});
//调用或者修改值变化
const test = () => {
console.log('test方法');
a.name = a.name+1
}
//或者
const add2 = (): void => {
state.count++; // 通过reactive声明的遍历,不需要通过.value访问值
};
- reactive可以传递基础数据类型和引用数据类型,基础数据类型不会被包装成响应式数据
- reactive返回的响应式数据本质是Proxy对象,对象里面每一层都会被包装成Proxy对象
- reactive返回的响应式数据和原始的数据会相互影响
toRef
和toRefs
toRef
和toRefs
整两个方法,它们不创造响应式,而是延续响应式。创造响应式一般由ref和reactive来解决,而toRef和toRefs则把对象的数据进行分解和扩散,其这个对象针对的是响应式对象(reactive
)而非普通对象。
// toRefs解构响应式数据
let { count } = toRefs<ObjItf>(state);
const add3 = (): void => {
count.value++; // 通过toRefs结构的值和ref声明的变量一样,需要通过.value访问其值
};
toRef
可以用来为源响应式对象上的某个 property 新创建一个 ref
。然后,ref 可以被传递,它会保持对其源 property 的响应式连接。
const state = reactive({
foo: 1,
bar: 2
})
const fooRef = toRef(state, 'foo')
fooRef.value++
console.log(state.foo) // 2
state.foo++
console.log(fooRef.value) // 3
//当你要将 prop 的 ref 传递给复合函数时,toRef 很有用:
export default {
setup(props) {
useSomeFeature(toRef(props, 'foo'))
}
}
//即使源 property 不存在,toRef 也会返回一个可用的 ref。这使得它在使用可选 prop 时特别有用,可选 prop 并不会被 toRefs 处理。
Proxy-vue3响应式原理
https://es6.ruanyifeng.com/#docs/proxy
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
Proxy-vue3响应式原理
<script>
let obj = {b:'999'}
let proxy = new Proxy(obj,{
get:function(target,propKey){
console.log(target,propKey);//{x: 123} 'x'
return target[propKey]
},
set:function(target,propKey,value){
console.log(target,propKey,value);//{} 'x' 123
target[propKey] = value
}
})
proxy.x = 123
console.log(proxy.x,obj.x);//123 123
proxy.b = "你好世界"
console.log(proxy.b,obj.b);//123 123
</script>
提交代码风格:https://www.yuque.com/docs/share/e85b95af-9cab-46fb-abb1-a759f5872426?#
事件监听
Watch
语法:watch(监听源|[多个], (val, oldVal) => {}, {immediate?: false, deep: false})
watch写法上支持一个或者多个监听源,这些监听源必须只能是getter/effect
函数,ref数据,reactive对象,或者是数组类型
<script lang="ts" setup>
// 组合式Api
import {reactive, ref,toRefs, watch} from 'vue'
let num = ref(1)
let num1 = 1
let obj = reactive({m:1})
let { m } = toRefs(obj)
setTimeout(() => {
num.value =3;
num1 = 10;
obj.m = 999;
}, 1000);
// 监听num
watch(num,(newVal,oldVal)=>{
console.log(newVal,oldVal);
// 3 1
})
// 监听num1,如果是普通数据(不是响应数据,这个时候是监听不到的)
watch(()=>num1,(newVal,oldVal)=>{
console.log(newVal,oldVal);
})
// 监听对象obj.属性
watch(()=>obj.m,(newVal,oldVal)=>{
console.log(newVal,oldVal);
// 999 1
})
watch(m,(newVal,oldVal)=>{
console.log(newVal,oldVal);
// 999 1
})
// 监听多个值变化
watch([num,m],(newVal,oldVal)=>{
console.log(newVal,oldVal);
// (2) [3, 999] (2) [1, 1]
})
</script>
watchEffect
它立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。
<script lang="ts" setup>
// 组合式Api
import {watchEffect,ref} from 'vue'
let num = ref(1)
let num1 = 1
setTimeout(() => {
num.value++;
num1 ++;
}, 1000);
watchEffect(()=>{
// 一开始就会执行一次回调函数,打印出1,1
// num1不是响应式数据,能打印出2,是因为num.value是响应式数据,响应式数据变化,触发回调函数
console.log(num1);//2
console.log(num.value);//2
})
</script>
watch 和 watchEffect的区别
- 两者都可以监听
data
属性变化; watch
需要明确监听哪个属性;- 而
watchEffect
会根据其中的属性,自动监听其变化。
计算属性computed
<script lang="ts" setup>
import {watchEffect,ref, computed} from 'vue'
let num = ref(1)
setTimeout(() => {
num.value++;
}, 1000);
// 计算属性
let newNum = computed(()=>{
return 'ref:num值=' + num.value
})
console.log(newNum);
console.log(num);
</script>
组件
组件注册
局部注册(或者组件内注册)
在使用组件的的XX.vue的文件中,写入
//引入组件
import MyCom from './components/Mycom.vue'
//直接使用
<template>
<!-- 局部组件 -->
<MyCom></MyCom>
</template>
全局组件
在main.ts文件中,引入
import MyCom1 from './components/Mycom.vue'
//导出
createApp(App).component('my-com',MyCom1).mount('#app')
在使用组件的XXX.vue文件中直接使用
<template>
<!-- 全局组件 -->
<my-com></my-com>
</template>
Lodash
Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库。
https://www.lodashjs.com/
引入:
第三方库
js-Cookies
https://github.com/js-cookie/js-cookie
查看是否有对应的Ts类型声明的库
https://www.typescriptlang.org/dt/search?search=
路由
路由前置导航守卫
动态生成路由
使用addRoute
在router/index.ts文件中
两种使用方法:
addRoute(parentName: string | symbol, route: RouteRecordRaw): () => void
Parameters
Parameter | Type | Description |
---|---|---|
parentName | string | symbol | Parent Route Record where route should be appended at |
route | RouteRecordRaw | Route Record to add |
addRoute(route: RouteRecordRaw): () => void
插件
时间格式
moment JavaScript 日期处理类库
http://momentjs.cn/
//具体用法:moment(传入要处理的数据).format(数据要处理成的格式)
moment(scope.row.createTime).format('YYYY-MM-DD hh:mm:ss')
手写时间格式处理
// 格式化时间
const formateData = (time: string | undefined) => {
if (!time) return "";
const data = new Date(time)
let year = addZero(data.getFullYear())
let month = addZero(data.getMonth()+1)
let day = addZero(data.getDate())
let hour = addZero(data.getHours())
let min = addZero(data.getMinutes())
let sec = addZero(data.getSeconds())
return `${year}-${month}-${day} ${hour}:${min}:${sec}`
}
const addZero = (num: number) => {
return num > 9 ? num : '0' + num
}
//然后直接调用方法
formateData(传入实参)
发送请求—request
在src目录下,新建request文件夹下,里面包含两个文件api.ts和request.ts或者是js文件
api.ts 放具体的请求路径……
request.ts 放请求设置,请求拦截器……
发送在URL上带参数的请求
//例如:http://120.24.64.5:8088/mall-admin/admin/list?pageNum=1&pageSize=10&keyword=
//后面带?key=value的
//用params
export const getAdminlistApi = (data:AdminListData):ProminseRes<AdminListRes>=>request.get('/admin/list',{params:data})
//或者写法:ES6写法,key和value相同,可以省略,例如{params:data} ===》{params}
export const getAdminlistApi = (params:AdminListData):ProminseRes<AdminListRes>=>request.get('/admin/list',{params})
api.ts或者api.js文件
//引入reques文件
import request from './request'
// import qs from 'qs'
//类型设置
interface AdminLoginData {
password: string
username: string
}
type ProminseRes<T> = Promise<ManageResult<T>>
interface ManageResult<T = {}> {
code: number;
data: T
message: string
}
//登录返回token
interface AdminLoginRes {
token: string;
tokenHead: string;
}
// 当前用户信息
interface AdminInfoRes{
menus:[],
username:String
}
// 用户列表
interface AdminListRes{
list:[],
total:Number,
totalPage:Number
}
interface AdminListData {
keyword:string
pageNum: Number
pageSize: Number
}
//export导出
// 登录功能
export const LoginApi = (data: AdminLoginData):ProminseRes<AdminLoginRes> => request.post('/admin/login', data)
// 获取用户信息
export const getAdminInfoApi = ():ProminseRes<AdminInfoRes>=>request.get('/admin/info')
// 根据用户名或姓名分页获取用户列表
// export const getAdminlistApi = (data:AdminListData):ProminseRes<AdminListRes>=>request.get(`/admin/list?pageNum=${data.pageNum}&pageSize=${data.pageSize}`)
export const getAdminlistApi = (data:AdminListData):ProminseRes<AdminListRes>=>request.get('/admin/list',{params:data})
request.ts或者request.js文件
import axios from "axios";
import Cookies from 'js-cookie'
let instance = axios.create({
baseURL: "http://120.24.64.5:8088/mall-admin",
//响应时间
timeout: 5000
});
instance.interceptors.request.use(function (config) {
let token =Cookies.get('token')
if(token){
config.headers = config.headers || {}
config.headers.Authorization = token
}
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// 添加响应拦截器
instance.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response.data;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
export default instance