vite:前端构建工具
相比vue-cli基于webpack搭建的项目,vite搭建的项目按需编译运行速度极速,能够显著提升前端开发效率
安装:
npm create vite@latest 项目名称 -- --template vue
下载依赖
npm install
启动项目
npm run dev
项目安装vue-router
npm install vue-router@4 -s
在vite.config.js文件配置
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
resolve: {
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json', '.vue'],
alias: {
'@':'/src'
// '@': Path.resolve(_dirname,'/src') 用于引用路径时@代替src
}
}
})
面试题:vue3中定义响应式数据ref,reactive的区别
ref可定义的任意类型数据,获取时需要.value获取,一般接口返回的数据用ref定义
reactive只能定义对象或数组,定义复杂多层次数据
面试题:vue2和vue3多数据拦截有什么不同点
vue2=>Object.defineProperty ,对于对象类型的数据,需要for in循环遍历+递归对象来获取一项
vue3=>new Proxy ,对于对象类型的数据,不循环+递归可以获取每一项,性能提升
setup语法糖插件
解决import { ref,reactive,…}每个文件都引入,比较繁琐的问题
安装
npm install unplugin-auto-import -D
配置vite.comfig.is文件
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
//引入自动导入插件
import AutoImport from 'unplugin-auto-import/vite'
export default defineConfig({
plugins: [
vue(),
AutoImport({
imports: ['vue','vue-router'],
dts: 'src/auto-imports.d.ts'
})
]
})
toRefs=>解构响应式数据
let obj = reactive({
name:'luishu',
id: 2
})
let {name,id} = toRefs(obj);
computed计算属性
//默认写法绑定v-model,不能实现双向数据绑定,有警告
const srcAdd = computed(() => {
return count.value + 1
})
//get,set写法,绑定v-model,可以实现双向数据绑定
const add = computed({
get() {
return count.value + 1
},
set(value) {
count.value = value
}
})
watch监听
//监听一个
watch(add, (newValue, oldValue) => {
console.log(newValue, oldValue)
})
// 监听多个数组包起来
watch([count, srt], (newValue, oldValue) => {
console.log(newValue, oldValue)
})
//一进入就监听,加immediate:true
watch([count, srt], (newValue, oldValue) => {
console.log(newValue, oldValue)
},{immediate:true})
//监听对象的某个属性, 层级深的属性并且深度监听
watch(() => obj.children, (newValue, oldValue) => {
console.log(newValue, oldValue)
},{immediate:true,deep:true})
//vue3中useRouter=>this.$router,useRoute=>this.$route
//监听router
let router = useRouter()
watch(() => router.currentRoute.value,(newValue)=>{
console.log(newValue)
},{ immediate: true })
//立即执行监听函数,默认监听全部
watchEffect(() => {
console.log('home')
})
面试题:watch与watchEffect的区别?
watchEffect 适合用于自动追踪依赖并执行副作用,不需要手动指定要观察的状态。
watch 提供了更多的控制选项,适合需要显式指定观察状态源和访问状态变化前后值的场景。
生命周期
vue2 | vue3 |
---|---|
beforeCreate | setup() |
created | setup() |
beforeMount | onBeforeMount(请问接口) |
mounted | onMounted (获取DOM) |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestroy | onBeforeUnmount |
destroyed | onUnmounted |
import { onBeforeMount, onMounted } from 'vue'
onBeforeMount(() => {
console.log('接口请求')
})
onMounted(() => {
console.log('获取DOM')
})
组件传值
父传子 defineProps
//父组件
<template>
<div class="about">
<h1>This is an about page</h1>
<HelloWorld :msg="msg" />
</div>
</template>
//js
import { ref } from 'vue'
import HelloWorld from '../components/HelloWorld.vue'
let msg = ref('Welcome to Your Vue.js')
//子组件
<template>
<h1>{{ msg }}</h1>
</template>
//js
import { defineProps } from 'vue'
defineProps({
msg: String,
})
子传父 defineEmits
//子组件
<button @click="emitChange">emit</button>
<script setup >
import { ref,defineEmits} from 'vue'
const count = ref(20)
const emit = defineEmits(['fu'])//js写法
const emit = defineEmits<{
(e: 'change', id: number) : void
}>()//ts写法
const emitChange = (id: number) => {
emit('change', count.value)
}
//父组件
<HelloWorld :msg="msg" @change="change"/>
//js
import HelloWorld from '../components/HelloWorld.vue'
const change = (e) => {
console.log(e)
}
</script>
新增内置组件teleport:是一个内置组件,它可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去。
<button @click="open = true">Open Modal</button>
//to:来指定传送的目标,可以是一个 CSS 选择器字符串,也可以是一个 DOM 元素对象(body,#idname,。classname)
<Teleport to="body">
<div v-if="open" class="modal">
<p>Hello from the modal!</p>
<button @click="open = false">Close</button>
</div>
</Teleport>
动态组件
<template>
<div class="card">
<ul>
<li
v-for="(item,index) in componentsList"
:key="index"
@click="emitChange(item)"
>
// <component :is="item.com"></component>
</li>
</ul>
</div>
// 动态组件的关键component :is="动态获取组件"
<keep-alive>
<component :is="DataTransferItemList.com"></component>
</keep-alive>
</template>
<script setup lang="ts">
import HelloWorld from "../components/HelloWorld.vue";
import Herder from "../components/Herder.vue";
import Footer from "../components/Footer.vue";
import { ref,reactive, markRaw } from "vue";
const count = ref(0);
const componentsList = reactive([
//reactive定义的是响应式数据,markRaw是去除控制台警告引入的组件不需要是响应式数据
{
name: "HelloWorld",
id:123,
com: markRaw(HelloWorld),
},
{
name: "Herder",
id:123,
com: markRaw(Herder),
},
{
name: "Footer",
id:123,
com: markRaw(Footer),
}
]);
const DataTransferItemList = reactive({
com: componentsList[0].com,
});
const emitChange = (item: any) => {
DataTransferItemList.com = item.com;
}
</script>
异步组件=》提高性能
需要下载插件vueues
官网:https://vueuse.org/core/useIntersectionObserver/
使用场景一:按需加载组件,当用户访问到组件时在加载此组件
安装:
npm install @vueuse/core
组件应用:
<template>
<Herder></Herder>
<Footer></Footer>
<div ref="target">
<HelloWorld v-if="targetIsVisible"></HelloWorld>
</div>
</template>
<script setup>
import { ref,defineAsyncComponent} from 'vue';
import Herder from '../components/Herder.vue'
import Footer from '../components/Footer.vue'
//defineAsyncComponent 定义异步组件
const HelloWorld = defineAsyncComponent(() => import('../components/HelloWorld.vue'))
// import HelloWorld from '../components/HelloWorld.vue'
//引入vueuse插件
import { useIntersectionObserver } from '@vueuse/core'
const target = ref(null)
const targetIsVisible = ref(false)
const { stop } = useIntersectionObserver(
target,
([{ isIntersecting }], observerElement) => {
console.log(isIntersecting)//false
targetIsVisible.value = isIntersecting
},
)
</script>
使用场景二:分包处理
新增内置组件Suspense:用来在组件树中协调对异步依赖的处理。它让我们可以在组件树上层等待下层的多个嵌套异步依赖项解析完成,并可以在等待时渲染一个加载状态。
打包完成后,异步组件有单独的js文件,从主体js分包出来的
<template>
<Suspense>
<template #default>
<Herder></Herder>
</template>
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>
<Footer></Footer>
<div ref="target">
<Suspense v-if="targetIsVisible">
<template #default>
<HelloWorld msg="Welcome to Your Vue.js App"></HelloWorld>
</template>
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>
</div>
</template>
<script setup>
import { defineAsyncComponent} from 'vue';
import Footer from '../components/Footer.vue'
//defineAsyncComponent 定义异步组件
const Herder = defineAsyncComponent(() => import('../components/Herder.vue'))
const HelloWorld = defineAsyncComponent(() => import('../components/HelloWorld.vue'))
依赖注入provide()和inject()
//父组件
provide('state',state)
//子组件
<template>
<div class="heder">
<button @click="onClick">修改provide数据</button>
</div>
</template>
<script setup>
import { ref,inject } from "vue"
let count = inject('state')
console.log(count)
//修改provide依赖的数据
const onClick = () => {
count.value = 20 + 1
}
</script>
pinia和vuex的区别
- pinia没有mutations,只有State,getters,actions
- pinia分模块不需要modules,vuex分模块需要modules
- pinia体积更小,性格更好
- pinia可以直接修改state数据,需要用storeToRefs(store)包裹后可以修改store数据
- 自动化代码拆分
- TypeScript更好的支持
官网:https://pinia.vuejs.org/
安装:
npm ininstall pinia
//main.js文件引入pinia
import { createPinia } from 'pinia'
createApp(App).use(router).use(createPinia()).mount('#app')
//文件src/store/index.js
import { defineStore } from "pinia";
export const useStore = defineStore("store", {
state: () => ({
count1: 0,
count2: 10,
count3: 100
}),
getters: {
//有缓存机制
doubleCount: (state) => state.count * 2,
},
actions: {
//actions支持同步和异步
increment() {
this.count++;
},
},
});
//组件应用pinia
template>
<div>
<span>{{ count1 }}</span>
<p>{{ count2 }}</p>
<span>{{ count3 }}</span>
<button @click="storeChang">修改Store</button>
</div>
</template>
<script setup>
import { useStore } from '@/store/index.js'
//storeToRefs解构
import { storeToRefs } from 'pinia'
const store = useStore();
const {count1,count2,count3} = storeToRefs(store);
const storeChang = () => {
//单个修改
store.count1 = 100;
store.count2 = 200;
store.count3 = 300;
//批量修改
store.$patch(state=>{
state.count1 ++,
state.count2 = 200
})
}
pinia的持久化存储
插件:pinia-plugin-persist
安装
npm install pinia-plugin-persist -save
//src/store/index.js
import { createPinia } from "pinia";
import piniaPersist from 'pinia-plugin-persist'
const store = createPinia()
store.use(piniaPersist)
export default store
//src/store/模块name.js
import { defineStore } from "pinia";
export const useStore = defineStore("store", {
state: () => ({
count1: 0,
count2: 10,
count3: 100
}),
getters: {
doubleCount: (state) => state.count * 2,
},
actions: {
increment() {
this.count++;
},
},
// 持久化配置
persist: {
enabled: true,
stratgies: [
{
key: "user",
storage: localStorage,
paths: ["count1","count2"],
}
],
storage: localStorage,
paths: ["count1", "count2"],
},
});