01 Vue3介绍
1. Vue是什么
一套用于构建用户界面的渐进式JavaScript框架。
2. Vue的特点
- 采用组件化模式,提高代码复用率、且让代码更好维护。
- 声明式编码,让开发人员无需直接操作DOM,提高开发效率。
- 使用虚拟DOM+Diff算法,尽量复用DOM节点。
02 为什么用Vite
1. 什么是Vite
Vite :新一代前端构建工具。
2. vue-cli vue2.x | vue3.x
vue create demo
:基于的构建工具webpack。
webpack:更新缓慢。修改一个页面保存后会自动加载所有页面。
基于打包器启动时,重建整个包的效率很低。
3. 为什么用Vite
- 可以大大的提升我们的开发效率。
- 真正的按需编译,不再等待整个应用编译完成。(修改一个页面保存后只加载此页面)
03 基于Vite搭建Vue3项目
官方文档:v3.cn.vuejs.org/guide/insta…
vite官网:vitejs.cn
创建项目:npm init @vitejs/app <项目名称>
安装步骤:
启动项目:
第一步 安装Router:npm install vue-router@4 -S
第二步 创建目录和文件
- 在项目根目录,新建router目录
- 在router目录中新建index.js文件
第三步 填充router/index.js的内容
第四步 在main.js挂载
import { createApp } from 'vue'
import App from './App.vue'
import router from "./router";
createApp(App).use(router).mount('#app')
04 Vite解决@问题
1. 写法
- vue2:在template必须要有父元素。
- vue3:在Vite构建的vue3项目中,无需父元素。
2. Vite解决@问题
过去写Vue项目的时候,@代表src目录,例如引入文件import About from '@/views/About.vue'
那么在Vite构建的Vue3项目中需要先配置一下
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
const path = require('path');
export default defineConfig({
plugins: [vue()],
resolve: {
// 配置路径别名
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});
3. 引入vue文件要写.vue的扩展名,引入.js文件不用写扩展名。
05 Vue3代码写法风格
Vue3做了向下兼容,也支持Vue2的写法(有些不行)
Vue2.x写法==>叫做选项式API
Vue3的setup写法==>叫做组合式API
06 setup定义数据和插件安装
1. setup内部写法: 定义数据
- 死数据:不可以修改之类的,但是可以展示视图层
let str = '1'; - 响应式数据:ref
在使用的时候需要用value获取值:x.value - 响应式数据:reactive
在使用的时候直接获取对象值,不用ref一样不需要用x.value
reactive 只能写对象或者数组
2 Vue2 和 Vue3数据拦截不同的点
Vue2.x ==> Object.defineProperty
对象中的属性是对象,层级比较多时;获取不到底层的值,所以需要$set()方法
Vue3.x ==> new Proxy
3. setup语法糖插件安装
解决import { ref , reactive ... } 引入的问题
npm安装淘宝镜像
npm install -g cnpm --registry=http://registry.npmmirror.com
下载安装npm i unplugin-auto-import -D
//引入插件
import AutoImport from 'unplugin-auto-import/vite';
export default defineConfig({
// 引入插件
plugins: [
vue(),
AutoImport({
imports:['vue','vue-router']
})
],
});
07 toRefs和computed
1. toRefs() 解构响应式数据
let obj = reactive({
name:'张三',
age:20
})
let { name,age } = toRefs( obj );
获取name的值:name.value
2. computed计算属性
let changeStr = computed(()=>{ return str.value; })
计算属性默认是只读的。不允许修改。
表单输入框使用时,需要用到“可写”的属性。可以用下面的写法:
let changeStr = computed({
get(){
return str.value;
},
set( val ){
str.value = val;
}
})
08 watch
1. 监听某一个数据
watch( str , (newVal,oldVal)=>{
console.log( newVal,oldVal);
})
2. 同时监听多个数据「一起监听」
watch( [ str , num ] , (newVal,oldVal)=>{
console.log( newVal,oldVal);
})
3. 初始化监听
watch( num , (newVal,oldVal)=>{
console.log( newVal,oldVal);
},{
immediate:true
})
4. 监听对象
watch( obj , (newVal)=>{
console.log( newVal )
},{
immediate:true,
})
5. 监听对象某一个key,并且深度监听
watch( ()=>obj.m , (newVal,oldVal)=>{
console.log( newVal,oldVal )
},{
immediate:true,
deep:true
})
6. 立即执行监听函数
watchEffect(()=>{
console.log( str.value )
})
7. 监听路由
let router = useRouter();
watch( ()=>router.currentRoute.value,( newVal )=>{
console.log( newVal );
},{
immediate:true
})
09 生命周期钩子
1. 选项式 API
beforeCreate ...
2. setup 组合式API
注意:没有beforeCreate和created
其他生命周期要使用前面加"on" 例如:onMounted
onBeforeMount() // 请求接口
onMounted() // 获取DOM
onBeforeUpdate() // 修改前
onUpdated() //修改后
onBeforeUnmount() //销毁前
参考链接:v3.cn.vuejs.org/guide/compo…
10 路由
vue-router : router.vuejs.org/zh/api/
1. tag属性去除了
<router-link to='/about' tag='div'>跳转到关于我们</router-link>
2. 写法问题
let router = useRouter(); ===> this.$router
let route = useRoute(); ===> this.$route
3. 导航守卫
- 全局路由守卫
- beforeEach(to, from, next) 全局前置守卫,路由跳转前触发
- beforeResolve(to, from, next) 全局解析守卫 在所有组件内守卫和异步路由组件被解析之后触发
- afterEach(to, from) 全局后置守卫,路由跳转完成后触发
- 路由独享守卫
- beforeEnter(to,from,next) 路由对象单个路由配置 ,单个路由进入前触发
- 组件路由守卫
- beforeRouteEnter(to,from,next) 在组件生命周期beforeCreate阶段触发
- beforeRouteUpdadte(to,from,next) 当前路由改变时触发
- beforeRouteLeave(to,from,next) 导航离开该组件的对应路由时触发
11 组件-父传子
- 父
<template>
<div>
<List :msg='msg'></List>
</div>
</template>
<script setup>
import List from '../components/List.vue'
let msg = ref('这是父传过去的数据');
</script>
- 子
<template>
<div>
这是子组件 ==> {{ msg }}
</div>
</template>
<script setup>
defineProps({
msg:{
type:String,
default:'1111'
}
})
</script>
12 组件-子传父
- 子
<template>
<div>
这是子组件 ==> {{ num }}
<button @click='changeNum'>按钮</button>
</div>
</template>
<script setup lang='ts'>
let num = ref(200);
const emit = defineEmits<{
(e: 'fn', id: number): void
}>()
const changeNum = ()=>{
emit('fn',num)
}
</script>
- 父
<template>
<div>
<List @fn='changeHome'></List>
</div>
</template>
<script setup>
import List from '../components/List.vue'
const changeHome = (n)=>{
console.log( n.value );
}
</script>
13 v-model传值
父:
<List v-model:num='num'></List>
<script setup>
import List from '../components/List.vue'
let num = ref(1);
</script>
子:
const props = defineProps({
num:{
type:Number,
default:100
}
})
const emit = defineEmits(['update:num'])
const btn = ()=>{
emit('update:num',200);
}
14 组件-兄弟组件传值
1. 下载安装
npm install mitt -S
2. plugins/Bus.js
import mitt from 'mitt'; const emitter = mitt() export default emitter;
3. A组件
emitter.emit('fn',str);
4. B组件
emitter.on('fn',e=>{ s.value = e.value; })
15 插槽
1. 匿名插槽
父:
<A>
这是xxxxx数据
这是yyyyy数据
</A>
子:
<header>
<div>头部</div>
<slot></slot>
</header>
<footer>
<div>底部</div>
<slot></slot>
</footer>
2. 具名插槽
父:
<A>
<template v-slot:xxx>
这是xxxxx数据
</template>
<template v-slot:yyy>
这是yyyyy数据
</template>
</A>
简写:<template #xxx>
子:
<header>
<div>头部</div>
<slot name='xxx'></slot>
<slot name='yyy'></slot>
</header>
<footer>
<div>底部</div>
<slot name='xxx'></slot>
</footer>
3. 作用域插槽
父:
<template v-slot='{data}'>
{{ data.name }} --> {{ data.age }}
</template>
简写:<template #default='{data}'>
子:
<div v-for='item in list' :key='item.id'>
<slot :data='item'></slot>
</div>
4. 动态插槽:说了就是通过数据进行切换
父:
<template #[xxx]>
这是xxxxx数据
</template>
<script setup>
let xxx = ref('xxx');
</script>
16 Teleport 传送
<teleport to='#container'></teleport>
<teleport to='.main'></teleport>
<teleport to='body'></teleport>
必须传送到有这个dom的内容【顺序】
17 动态组件
<component :is="动态去切换组件"></component>
18 异步组件
提升性能
vueuse : vueuse.org/core/useint…
1. 使用场景1
组件按需引入:当用户访问到了组件再去加载该组件
<template>
<div ref='target'>
<C v-if='targetIsVisible'></C>
</div>
</template>
<script setup>
import { useIntersectionObserver } from '@vueuse/core'
const C = defineAsyncComponent(() =>
import('../components/C.vue')
)
const target = ref(null);
const targetIsVisible = ref(false);
const { stop } = useIntersectionObserver(
target,
([{ isIntersecting }]) => {
if( isIntersecting ) {
targetIsVisible.value = isIntersecting
}
},
)
</script>
2. 使用场景2
<Suspense>
<template #default>
<A></A>
</template>
<template #fallback>
加载中...
</template>
</Suspense>
<script setup>
const A = defineAsyncComponent(() =>
import('../components/A.vue')
)
</script>
3.打包分包处理
npm run build打包完成后,异步组件有单独的js文件,是从主体js分包出来的
A.c7d21c1a.js
C.91709cb2.js
19 Mixin
混入是什么?来分发 Vue 组件中的可复用功能。
1. setup写法 Vue3
mixin.js:
import { ref } from 'vue'
export default function(){
let num = ref(1);
let fav = ref(false);
let favBtn = ()=>{
num.value += 1;
fav.value = true;
setTimeout(()=>{
fav.value = false;
},2000)
}
return {
num,
fav,
favBtn
}
}
组件:
<template>
<div>
<h1>A组件</h1>
{{ num }}
<button @click='favBtn'>
{{ fav ? '收藏中...' : '收藏' }}
</button>
</div>
</template>
<script setup>
import mixin from '../mixins/mixin.js'
let { num , fav , favBtn } = mixin();
</script>
2. 选项式api写法 Vue2
mixin.js:
export const fav = {
data () {
return { num:10 }
},
methods:{
favBtn( params ){
this.num += params;
}
}
}
组件:
<template>
<div>
<h1>A组件</h1>
{{ num }}
<button @click='favBtn(1)'>按钮</button>
</div>
</template>
<script type="text/javascript">
import { fav } from '../mixins/mixin.js'
export default{
data () {
return {
str:'你好'
}
},
mixins:[fav]
}
</script>
20 Provide和Inject
Provide和Inject ==> 依赖注入
提供:
<script setup>
provide('changeNum', num );
</script>
注入:
<template>
<div>
<h1>B组件</h1>
{{ bNum }}
</div>
</template>
<script setup>
const bNum = inject('changeNum');
</script>
21 Vuex
- state:
let num = computed( ()=> store.state.num ); - getters:
let total = computed( ()=> store.getters.total ); - mutations:
store.commit('xxx') - actions:
store.dispatch( 'xxx' ) - modules:
和之前的版本使用一样 - Vuex持久化存储【插件】
-
npm i vuex-persistedstate -S
-
import persistedState from 'vuex-persistedstate'
export default createStore({
modules: {
user
},
plugins:[persistedState({
key:'xiaoluxian',
paths:['user']
})]
});
22 Pinia简单使用
1. Vuex和pinia的区别
参考网址: github.com/vuejs/rfcs/…
- pinia没有mutations,只有:state、getters、actions
- pinia分模块不需要modules(之前vuex分模块需要modules)
- pinia体积更小(性能更好)
- pinia可以直接修改state数据
2. pinia使用
官方网址:pinia.vuejs.org/
具体使用:xuexiluxian.cn/blog/detail…
3. pinia持久化存储
参考链接:xuexiluxian.cn/blog/detail…
23 Pinia分模块
24 Pinia持久化存储
25 设置代理
API参考链接:staging-cn.vuejs.org/api/#onmoun…
参考文章:xuexiluxian.cn/blog/detail…
- 设置代理
文件:vite.config.js
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']//自动导入vue和vue-router相关函数
})
],
server:{
proxy:{
'/api':'http://testapi.xuexiluxian.cn'
}
}
})
- axios二次封装
文件:新建utils/request.js
import axios from 'axios';
//1. 创建axios对象
const service = axios.create();
//2. 请求拦截器
service.interceptors.request.use(config => {
return config;
}, error => {
Promise.reject(error);
});
//3. 响应拦截器
service.interceptors.response.use(response => {
//判断code码
return response.data;
},error => {
return Promise.reject(error);
});
export default service;
- API解耦合
文件:api/new.js
import request from '../utils/request'
export function mostNew( data ){
return request({
url:'/api/course/mostNew',
method:"post",
data
})
}
26 项目搭建
使用的技术栈: vite + vue3 + pinia
做的项目:www.xuexiluxian.cn/
1. 创建项目
npm init @vitejs/app saas
2. 安装router
npm install vue-router@4 -S
- 在src目录新建router/index.js
import { createRouter, createWebHistory } from "vue-router";
import Home from "../views/Home.vue";
const routes = [
{
path: "/",
name: "Home",
component: Home,
},
{
path: "/about",
name: "About",
component: () =>
import(/* webpackChunkName: "about" */ "../views/About.vue"),
},
];
const router = createRouter({
history: createWebHistory(),
routes,
});
export default router;
3. unplugin-auto-import插件 和 @代表src目录
- 安装setup语法糖插件
npm i unplugin-auto-import -D
- 在vite.config.js中进行配置
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue';
//引入插件
import AutoImport from 'unplugin-auto-import/vite';
const path = require('path');
export default defineConfig({
plugins: [
vue(),
//配置插件
AutoImport({
imports:['vue','vue-router']
})
],
resolve: {
// 配置路径别名
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});
4. element-plus
网址:element-plus.org/zh-CN/guide…
- 下载
npm install element-plus --save
npm install -D unplugin-vue-components unplugin-auto-import
- 配置修改vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue';
//引入插件
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
const path = require('path');
export default defineConfig({
plugins: [
vue(),
//配置插件
AutoImport({
resolvers: [ElementPlusResolver()],
imports:['vue','vue-router']
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
resolve: {
// 配置路径别名
alias: {
'@': path.resolve(__dirname, './src'),
},
},
});