一、Vue3介绍
关于vue项目的版本,新项目使用vue3,有部分老项目使用vue2。
vue3的变化
1.性能的提升
-打包大小减少41%
-初次渲染快55%, 更新渲染快133%
-内存减少54%
2.源码的升级
使用Proxy代替defineProperty实现响应式
重写虚拟DOM的实现和Tree-Shaking
3.拥抱TypeScript
Vue3可以更好的支持TypeScript
4.新的特性
Composition API(组合API)
setup配置
ref与reactive
watch与watchEffect
provide与inject
新的内置组件
Fragment
Teleport
Suspense
其他改变
新的生命周期钩子
data 选项应始终被声明为一个函数
移除keyCode支持作为 v-on 的修饰符
# vue2配置项与vue3组合式的区别
vue2 :配置项API
new Vue({
el:'#app',
data:{}
})
vue3: 组合式API
let name='kiki'
let add=()=>{
}
二、Vue3项目创建
1)使用vue-cli创建vue3项目
创建跟之前的vue2一样,只是在选择vue版本的时候选择vue3版本,同样用pycharm打开然后配置好配置项后运行即可。
vue create 名称 // 选择需要的配置 到版本选择3.X即可
npm run server // 运行服务器
vue3的项目简单解释
'main.js'
import { createApp } from 'vue' # 通过 createApp创建vue实例
import App from './App.vue' # 根组件
import router from './router' # vue-router
import store from './store' #vuex
var vue = createApp(App) # 创建vue实例
vue.use(store)#使用vuex
vue.use(router)# 使用vue-router
vue.mount('#app')# 挂在index.html中得div
'其他地方写起来跟之前写vue2完全一致,vue3是兼容的'
2)使用vite创建vue3项目
Vite 是一个轻量级的、速度极快的构建工具,对 Vue SFC 提供第一优先级支持。作者是尤雨溪,同时也是 Vue 的作者!是新建的前端构架工具,最大的优势就是速度快。
vite使用步骤
1. 安装---默认最新版
npm create vue@latest // 这个命令可以根据自己的需求选择功能(如vue-router)
npm create vite@latest //这个命令没有选择第三方插件
2. 在项目中执行npm install 命令 ,虽然vite创建非常快,但是它没有安装依赖,所以需要自己安装一下
没有这个node_modules依赖
3 运行npm run dev启动项目
注意点:
4. vueRouter跟之前一样
5. Pinia:用来替换Vuex的,新一代的状态管理器
三、Setup函数
简化代码和项目的简洁度,
vue
新增了setup
函数。
-
setup为Vue3.0中一个新的配置项,值为一个函数
-
setup是所有Composition API(组合API)编写的位置
-
组件中所用到的:数据、方法等等,均要配置在setup中
-
setup函数的返回值:返回一个对象,对象中的属性、方法, 在模板中均可以直接使用
-
注意:
尽量不要与Vue2.x配置混用- Vue2.x配置(data、methos、computed…)中可以访问到setup中的属性、方法。
但在setup中不能访问到Vue2.x配置(data、methos、computed…)。
如果有重名, setup优先。
- Vue2.x配置(data、methos、computed…)中可以访问到setup中的属性、方法。
任意一个页面组件看看效果
<template>
<div class="home">
<h1>Setup函数</h1>
<hr>
<p>名字:{{name}}</p>
<p>年龄:{{age}}</p>
<button @click="addAge">点击年龄+10</button>
</div>
</template>
<script>
export default {
name: 'HomeView',
setup(){
// setup中没有this了
// 以后所有的变量定义函数编写,都写在这个函数中
// 定义变量 如果这么写,变量渲染没问题,但是没有响应式,页面变了,变量不会变
// vue3中,默认是没有响应式的
//1.定义变量,跟正常js一样,但是推荐使用let,而不是使用var
let name = 'jack'
let age = 18
//2.定义函数
let addAge = () => {
age+=10
console.log(age) //可以发现变量是变了,但是没有响应式
}
//必须要有返回值,是个对象,返回的对象,可以在模版中使用
return {name,age,addAge}
}
}
</script>
vue2的创建vue实例和vue3创建vue实例的区别
'Vue2'
1.new Vew()--->是Vue的实例,里面有$store,$refs....
'Vue3'
1.createApp(App)--->是个对象,对象里有东西,没有$store,$refs....等对象,当需要使用直接导入使用
2.vue3的<template>,不需要写一个标签了
3.定义变量/函数,都写在setup函数中,都要return函数,在<template>才能使用
4.但是失去了响应式
总结:
1.在setup中定义的变量和函数,在之前配置项api中可以直接使用this.变量,函数调用即可
2.但是在原来配置项中定义的变量,函数,在setup中无法使用
四、ref和reactive
1.导入使用:import {ref, reactive} from 'vue'
2.基本数据类型(数字,字符串,布尔)如果要加响应式
使用ref包裹,在模板中直接读取定义量
js中通过对象.value取值
3.对象,数组引用使用reactive,ref可以包对象类型,但是用的时候必须.value。
操作数据与读取数据:均不需要.value
1)ref函数
- 作用: 定义一个响应式的数据
- 语法:
const xxx = ref(initValue)
- 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。
- JS中操作数据:
xxx.value
- 模板中读取数据: 不需要.value,直接:
<div>{{xxx}}</div>
- 备注:
- 接收的数据可以是:基本类型、也可以是对象类型。
- 基本类型的数据:响应式依然是靠
Object.defineProperty()
的ge
t与set
完成的 - 对象类型的数据:内部 求助 了Vue3.0中的一个新函数——
reactive函数
<template>
<div class="home">
<h1>Setup函数</h1>
<hr>
<h3>ref函数</h3>
<p>名字:{{name}}</p>
<p>年龄:{{age}}</p>
<button @click="addAge">点击年龄+10</button>
<button @click="handlerChange('吴彦祖')">点击修改名字</button>
</div>
</template>
<script>
import {ref,reactive} from "vue";
export default {
name: 'HomeView',
setup(){
let name = ref('jack')
let age = ref(18) //已经不是数字了,是RefImpl的对象
//2.定义函数
let addAge = () => {
age.value+=10
console.log(age)
}
let handlerChange = (data)=>{
name.value = data //属性.value就可以对属性进行操作
console.log(name)
}
'''
如果是拿子组件对象的属性和方法,需要让子组件的属性和方法暴露出来才可以拿到使用
在子组件中:defineExpose({传入属性、方法}) //这个是可选暴露的,vue2中父组件可以拿到所有
并且在当前组件中使用子组件的属性或方法,必须定义与子组件中定义的名字相同
'''
//必须要有返回值,是个对象,返回的对象,可以在模版中使用
return {name,age,addAge,handlerChange}
}
}
</script>
2)reactive函数
- 作用: 定义一个对象类型的响应式数据(
基本类型不要用它,要用ref函数
) - 语法:
const 代理对象= reactive(源对象)
接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象) - reactive定义的响应式数据是“深层次的”
- 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作
<template>
<div class="home">
<h1>Setup函数</h1>
<hr>
<h3>reactive函数</h3>
<p>用户名:{{userinfo.name}}</p>
<p>年龄:{{userinfo.age}}</p>
<p>爱好:{{userinfo.hobby[0]}},{{userinfo.hobby[1]}}</p>
<button @click="handlerClick">点击年龄+1</button>
</div>
</template>
<script>
import {ref,reactive} from "vue";
export default {
name: 'HomeView',
setup(){
let userinfo=reactive({'name':'oscar','age':23,'hobby':['jump','rap']})
let handlerClick = ()=>{
userinfo.age++
console.log(userinfo)
}
//必须要有返回值,是个对象,返回的对象,可以在模版中使用
return {userinfo,handlerClick}
}
}
</script>
3)reactive对比ref
从定义数据角度对比:
ref用来定义:基本类型数据
reactive用来定义:对象(或数组)类型数据
备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象
从原理角度对比:
ref通过Object.defineProperty()的get与set来实现响应式(数据劫持)。
reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
从使用角度对比:
ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value。
reactive定义的数据:操作数据与读取数据:均不需要.value。
4)setup的两个注意点
setup执行的时机
在beforeCreate之前执行一次,this是undefined。
setup的参数
props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
context:上下文对象
attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs。
slots: 收到的插槽内容, 相当于 this.$slots。
emit: 分发自定义事件的函数, 相当于 this.$emit。
五、计算属性与监听属性
1)计算属性(computed函数)
通过绑定ref和rective来编写计算(computed)
<script>
import {ref, reactive,computed} from "vue";
export default {
name: 'StatsView',
setup() {
let person = reactive({
firstname:'',
lastname:'',
})
//计算属性----》获取值 ,单拎出来写
// person.fullName = computed(()=>{
// return person.firstname + person.lastname
// })
//计算属性----》修改值 //组合使用
person.fullName = computed({
get(){ //获取 触发它的执行
return person.firstname + person.lastname
},
set(NewValue){ //修改 触发它,传入修改后的值
console.log(NewValue)
person.firstname = NewValue.substring(0,1)
person.lastname = NewValue.slice(1)
}
})
return {person,}
},
}
</script>
<template>
<div>
<h1>计算属性:computed</h1>
<hr>
<p>姓:<input type="text" v-model="person.firstname"></p>
<p>名:<input type="text" v-model="person.lastname"></p>
<p>全名:{{person.fullName}}</p>
<p>全名修改:<input type="text" v-model="person.fullName"></p>
</div>
</template>
2)监听属性(watch函数)
<script>
import {ref, reactive,computed,watch} from "vue";
export default {
name: 'StatsView',
setup() {
let count = ref(0)
let AddCount = ()=>{
count.value++
}
//监听基本类型
watch(count,(NewValue,OldValue)=>{
console.log(OldValue,NewValue)
})
//监听对象
let userinfo = reactive({
name:'jack',
age:22
})
let handlerName=()=>{
userinfo.name = '彭于晏'
}
watch(()=>userinfo.name,(NewValue,OldValue)=>{
console.log('名字变更了:'+userinfo.name)
})
//可以同时监听多个变量
/*
const sum = ref(100)
const msg = ref('很好')
watch([sum, msg], (newValue, oldValue) => {
console.log('sum或msg变化了', newValue, oldValue)
})
*/
return {person,count,AddCount,userinfo,handlerName}
},
}
</script>
<template>
<div>
<h1>监听属性:watch---基本类型</h1>
<p>数量:{{count}}</p>
<button @click="AddCount">点击数量+1</button>
<hr>
<h1>监听属性:watch---对象</h1>
<p>名字:{{userinfo.name}}</p>
<button @click="handlerName">点击变换名字</button>
<hr>
</div>
</template>
补充watchEffect函数
- watch的套路是:
既要指明监视的属性,也要指明监视的回调。
- watchEffect的套路是:
不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
- watchEffect有点像computed:
- 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
- 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
watchEffect(() => {
const x1 = sum.value
const x2 = person.age
console.log('watchEffect配置的回调执行了')
})
六、Vue3生命周期
- Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
- beforeDestroy改名为 beforeUnmount
- destroyed改名为 unmounted
- Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
- beforeCreate===>setup()
- created=======>setup()
- beforeMount ===>onBeforeMount
- mounted=======>onMounted
- beforeUpdate===>onBeforeUpdate
- updated =======>onUpdated
- beforeUnmount ==>onBeforeUnmount
- unmounted =====>onUnmounted
注意:
vue3中的配置项方式的生命周期还是跟vue2的一样,上面的是vue3的组合式方式的
使用
'在想要使用的组件中导入想使用的某些或某个即可'
import {onBeforeMount, onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from "vue";
'注意,在vue3中beforecreate和created都被setup替代,因为setup里面就是直接写变量,没写的时候就相当于没赋值'
'例子'
setup(){
onMounted(()=>{
console.log('挂载完毕')
})
onUpdated(()=>{
console.log('更新完毕')
})
}
'例如想在组件加载完成前就设置一个定时器'
setup(){
'直接在setup中写就行了,vue2中还需要写this,在setup中无需写了直接用'
let t = null
setInterval(()=>{},3000)
onBeforeUnmount(()=>{
clearInterval(t)
t=null
})
}
七、toRef与toRefs
- toRef的作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。
- 语法:
const name =toRef(person,'name')
- 应用: 要将响应式对象中的某个属性单独提供给外部使用时。
- 扩展:
toRefs
与toRef
功能一致,但可以批量创建多个ref 对象,语法:toRefs(person)
补充es解构赋值
//解构赋值
let person = {name:'jack',age:18}
// let {name,age} = person //普通解构赋值 相当于name=person.name
// console.log(name,age)
let {name:a,age:b} = person //重命名解构赋值 a=person.name
console.log(a,b)
<script>
import {ref,reactive,toRef,toRefs} from "vue";
export default {
name:'TorefView.',
setup(){
let data = reactive({
name:'jack',
age:22,
hobby:'rap'
})
console.log({...toRefs(data)}) //解压赋值
let count = ref(11)
return {...toRefs(data),count} //解压赋值,后续写影响
}
}
</script>
<template>
<h1>toRef函数</h1>
<hr>
<p>名字:{{name}}</p>
<p>年龄:{{age}}</p>
<p>爱好:{{hobby}}</p>
</template>
<script setup lang="js">
import {ref,reactive,toRef,toRefs} from "vue";
import axios from 'axios'
//toRefs
const person = reactive({name:'jack',age:18}) //设置响应式
// let res = toRefs(person) //把里面所有的普通变量变成响应式 name=ref('jack')
// let res1 = person //单纯的解构
// console.log(res)
// console.log(res1)
let {name,age} = toRefs(person) //这样就相当于解构赋值变成响应式的 name=ref('jack')
console.log(name)
function addAge(){
age.value++
console.log(age.value)
}
function addName(){
name.value = 'oscar'
console.log(name.value)
}
</script>
<template>
<h1>toRefs</h1>
<p>名字:{{name}}</p>
<p>年龄:{{age}}</p>
<button @click="addAge">点击年龄+1</button>
<button @click="addName">修改名字</button>
</template>
八、axios
axios基础运用
1.安装
npm install axios -S
2在组件中使用.
导入:import axios from 'axios'
然后直接在setup中直接使用
小案例
<script setup lang="js">
import {ref,reactive} from "vue";
import axios from 'axios'
1.方式一:
// 加载电影数据 //设置响应式
let filmList = reactive({result:[]})
//发送ajax
axios.get('http://localhost:8000/api/v1/movies/').then(res=>{
console.log(res.data.data.films)
//因为响应式影响filmList,如果直接赋值,这样响应式就没有了,
// filmList = res.data.data.films //这样响应式就没有
//所以在里面设置一个对象,让它来接收,这样响应式就还在,深层次响应式
filmList.result = res.data.data.films
})
2.方式二:
//使用async和await定义,async异步
//异步请求
const filmList = reactive({result:[]})
async function load(){
//await必须写在被async修饰的函数中
let response = await axios.get('http://localhost:8000/api/v1/movies/')
filmList.result=response.data.data.films
}
</script>
<template>
<div v-for="film in filmList.result">名字:{{film.name}}</div>
</template>
axios拦截器
1.什么是axios拦截器、为什么要使用axios拦截器?
在vue项目中,我们通常使用axios与后台进行数据交互,axios是一款基于promise封装的库,可以运行在浏览器端和node环境中。
2.axios特性
- 1、拦截请求和响应
- 2、取消请求
- 3、转换json
- 4、客户端防御XSRF等。
3.使用拦截器的原因
若出现请求数多的情况下,我们将会用到 axios 的一个API:拦截器。
页面发送http请求,很多情况我们要对请求和其响应进行特定的处理,
如果每个请求都附带后端返回的token,我们需要在拿到response之前loading动画的展示等。
4.拦截器的分类
拦截器分为
请求(request)拦截器
和响应(response)拦截器
。
5.拦截器的使用
在请求或响应被 then 或 catch 处理前拦截它们。
1.请求拦截器
// 添加请求拦截器
axios.interceptors.request.use(
function (config) {
do......
// 在发送请求之前进行操作
return config;
},
function (error) {
do......
// 对请求错误进行操作
return Promise.reject(error);
}
);
举例:
// http request 拦截器
axios.interceptors.request.use(
config => {
if (store.state.token) {
// 判断是否存在 token, 如果存在的话, 则每个 http header 都加上 token
config.headers.Authorization = `token ${store.state.token}`;
}
return config;
},
err => {
return Promise.reject(err);
});
2.响应拦截器
// 添加响应拦截器
axios.interceptors.response.use(
function (response) {
// 对响应数据进行操作
return response;
},
function (error) {
// 对响应错误进行操作
return Promise.reject(error);
}
);
举例:
// http response 拦截器
axios.interceptors.response.use(
response => {
return response;
},
error => {
if (error.response) {
switch (error.response.status) {
case 401:
// 返回 401 清除 token 信息并跳转到登录页面
store.commit(types.LOGOUT);
router.replace({
path: 'login',
query: {redirect: router.currentRoute.fullPath}
})
}
}
return Promise.reject(error.response.data)
// 返回接口返回的错误信息
});
说明:
如果我们使用中需要统一处理所有 http 请求和响应, 就需要使用 axios 拦截器。
通过配置 http response inteceptor, 如果后端接口返回 401 Unauthorized(说明该用户未授权), 用户需重新登录。
3.移除拦截器:
const myInterceptor = axios.interceptors.request.use(function () {
do ......
//具体的操作
});
axios.interceptors.request.eject(myInterceptor);
为自定义 axios 实例添加拦截器:
const instance = axios.create();
instance.interceptors.request.use(function () {
do ......
//具体的操作
});
九、补充
1.在vue3中使用子组件方式
-在vue3配置项方式中导入后需要配置components才可以在模版中使用,和vue2写法一样
<script>
import HelloWorld from "@/components/HelloWorld.vue";
export default {
components: {HelloWorld},
setup(){
}
}
</script>
<template>
<HelloWorld msg="hello world"></HelloWorld>
</template>
-在vue3组合式方式中导入后,直接可以在模版中使用了,但是需要导入到后缀.vue才行
<script setup>
import HelloWorld from "@/components/HelloWorld.vue";
</script>
<template>
<HelloWorld msg="hello world"></HelloWorld>
</template>
2.vue3中路由跳转方式:
-在vue3配置项方式中导入后然后定义对象才行,然后配置事件return出去即可
<script>
import {useRoute,useRouter} from "vue-router";
export default {
setup(){
const router = useRouter() //就是以前this.$router
const route = useRoute() //就是以前this.$route
function Tiao(){
router.push('/')
}
return {Tiao}
},
}
</script>
-在vue3组合式方式中和配置项几乎一样,仅仅只是配置事件后无需return而已
-携带数据跳转也是一样的操作
3.路由嵌套、路由守卫也是和vue2一样
4.vue3上的状态管理器更推荐使用pinia,当然也可以使用vuex
-在vue3上使用vuex的话和vue2差不多
-在vue3上使用pinia,在组件中导入和定义一个store对象
import {useCounterStore} from '@/stores/counter'
//可以在组件中的任意位置访问 `store` 变量 ✨
const store = useCounterStore()
//console.log(store.count)
// let handleAdd = () => {
// store.count++
// }
5.vue3上使用element-plus,不是element-ui了,具体使用看官方文档即可
最后vue3小细节
//在script标签中写了setup 以后这里面的东西就相当于写在setup中了
//lang=js意味着在setup中只写js,如果是lang=ts意味着在setup中只写typescript
//原来的setup中必须要返回出来,现在无需返回了,可以直接在template中直接使用了