昨日回顾

# 1 props
	-不需要在data中定义了,他就在当前组件对象身上了 this 
    -props:[]
    -props:{name:String}
    -props:{name:{require:,default:...}}
    
# 2 组件间通信
	-1 父子:父传子 ,自定义属性
    -2 子传父,自定义事件
    -3 ref属性
    -4 localStorage,sessionStorage,cookies
    -5 vuex 
    
# 3 插件 --> 使用第三方插件(vue-router,vuex,elementui)
	-导入
    -Vue.use(elementui)----> install(){}
    
    -自定义插件:
    	-xx.js 中  export default{
            install(vue){
                1 自定义指令
                2 往vue中放全局  Vue.prototype.$axios=axios
                3 执行minxin
                4 全局组件
            }
        }

# vuex:状态管理器,vue存数据的地方,跨组件间通信
	-vue2使用版本是vuex3
    -vue3使用版本是vuex4
    -npm install vuex@3
	-三个状态:
    	state:真正存数据的
        mutations:修改state的地址 state.变量名,修改即可
        actions:判断,跟后端交互,调用mutations的地方  context.commit
        
    -在组件中使用
    	-模板中:  {{$store.state.变量名}}
        -js中:
        	-直接用:this.$store.state.变量名
            -调用mutations:this.$store.commit('mutations')
            -正常套路:触发actions,this.$store.dispatch()
    
    
# vue-router

	-vue-router:3版本对应vue2,4版本对应vue3
    
	-单页面应用:index.html,以后都是页面组件间的切换,借助于vue-router
    
    -使用步骤:
    	-1 基本使用,router/index.js---->routes,加对象 {},加入路径后,访问这个路径就显示指定的组件
    	-2 跳转到不同的页面
    		-js控制:this.$router.push('路径') ,可以写个对象 
        	-标签控制:<router-link to属性=路径>,也可以写个对象
    	-3 跳转到别的页面传参数
    		-?name=lqz&age=19       取:this.$route.query.name
    		-/books/1/              index.js 中配置路由,取:this.$route.params.id
    	
    -路由嵌套:routes内部对象中写: children ---》/goods/list
    
    -路由跳转:
    	-html中跳转:router-link  to属性 可以之间写字符串路径,数据绑定对象{}
        -js跳转:this.$router.push('goods')
        
    -跳转携带数据:
    	-get地址中参数:this.$route.query.参数名
        -路由中分割出来的参数:this.$route.params.【router/index.js的routes使用:绑定的】
    
    -路由守卫:
    	-进入该路径之前,进行一些逻辑判断,允许进还是不允许
        -权限的控制
        	-router.beforeEach()
            -router.afterEach()
        -全局路由守卫
        	-前置守卫
            -后置守卫
    
    	-前置守卫
            router.beforeEach((to, from, next) => {
                // 1 取出cookie中token
                // 2 判断如果不是login页面,from对象,就是路由对象,判断一下,如果是,next()
                // 3 如果不是,判断token的值是否为空,如果为空,弹窗,跳转到login
                // router.push({name: 'login', params: {id: 99}})
                // router.push('/login/88')
                next({name:'login',params:{id:33}})
            })
            
    - localStorage,sessionStorage,cookie
    	-cookie:借助于第三方  vue-cookies 

今日内容

1 vue3介绍

# 1 vue项目的版本,新项目使用vue3,有部分老项目使用vue2

# vue3 的变化
1.性能的提升
    打包大小减少41%
    初次渲染快55%, 更新渲染快133%
    内存减少54%
2.源码的升级
    使用Proxy代替defineProperty实现响应式
    重写虚拟DOM的实现和Tree-Shaking
    - Tree-Shaking:摇晃树 --> 掉树叶
		意思就是将代码中不用的部分给删掉了
3.拥抱TypeScript
    Vue3可以更好的支持TypeScript
4.新的特性
    Composition API(组合API) 最新的特性
        setup配置
        ref与reactive
        watch与watchEffect
        provide与inject
    新的内置组件
        Fragment
        Teleport
        Suspense
    其他改变
        新的生命周期钩子 换了名字
        data 选项应始终被声明为一个函数
        移除keyCode支持作为 v-on 的修饰符

5 组合式API和配置项API
	vue2 :配置项API
         new Vue({
             el:'#app',
             data:{}
         })
        
    vue3: 组合式API
    setup(){
        let name='lqz'
        let add=()=>{ 
        }
        return {name, add}
    }

组合式api:

image-20230221221406308

2 创建vue3项目的两种方式

# vue-cli:创建vue2和vue3   
	-跟之前一样
# vite:创建vue3,创建最新
	npm init vue@latest
    # Pinai
# vite创建另一种方式:创建vue3.0.4版本
    npm init vite-app <project-name>
    ## 进入工程目录
    cd <project-name>
    ## 安装依赖
    npm install
    ## 运行
    npm run dev
    
# 启动的两种方式

# 以后再页面中的this,已经不是vue2中的vc对象了,是一个代理对象

vue-cli创建Vue3项目

# 创建跟之前vue2 一样,只是选择vue版本的时候,选择vue3

# 创建完成,使用pycharm打开,并运行

# eslint是什么?
类似python的PEP8编码规范。而且不符合规范时,项目无法编译。

# history模式

补充说明,history模式:

image-20230221221957678

history风格的router在这里:

image-20230221222007897

npm run serve本质执行的是package.json中的如下:

image-20230221222346876

可以改名字:

image-20230221222425754

main.js

image-20230222095854914

create(app)创建一个Vue对象。
Vue3建议不使用配置项api,推荐使用组合式api。

Vite创建Vue3项目

# 新建的前端构建工具,最大的优势就是速度快
	https://cn.vuejs.org/guide/scaling-up/tooling.html#project-scaffolding
           
# 使用步骤:
	- 安装 :npm init vue@latest 
    	- 按需选择,vueRouter
    - cd到项目中执行cnpm install 把依赖装好
    - 运行:npm run dev
     
    - vueRouter:跟之前一样
    - Pinia:用来替换Vuex的,新一代的状态管理器
    - 链式调用

# 在Vite创建的项目里使用Vuex:
	1.在Vite项目新建目录和文件 store/index.js
    2.将Vue3项目中的 store/index.js文件的代码复制到Vite创建的store/index.js中。
    3.在Vite项目的main.js注册插件
    import vuex from './stores/index'
	const app = createApp(App)
	app.use(vuex)
	
    -index.js:
        import { createStore } from 'vuex'

        export default createStore({
            state: {
            },
            getters: {
            },
            mutations: {
            },
            actions: {
            },
            modules: {
            }
        })

    

# 为什么这么快
	-创建项目快----》不安装第三方依赖
    -执行项目,热更新----》按需编译
   

Vite的优点:特点速度快

image-20230222100238907

创建项目:

image-20230221222705209

添加vue rounter:

image-20230221222904654

使用pinia(vuex5)做状态管理:

image-20230221222916355

继续按照图片配置:

image-20230221222950492

不懂的就选择NO。

Vue3目录结构

main.js:

image-20230222100850167

Vite项目没有下第三方依赖:还需要下依赖

项目运行命令也发送了变化:

npm run dev

image-20230222101215954

Vite的优点

image-20230222101455887

访问哪个页面,就编译哪个页面,也就是只有用的时候才会进行编译。而不是每次都编译整个项目的所有页面。

3 setup函数

# vue 新增的setup配置项函数,
	-在里面可以定义变量
    -定义函数
    -必须return 变量和函数,在模板中才能使用

Vue3创建实例和Vue2的区别

# vue2的创建vue实例和vue3创建vue实例的区别

	-new Vew()---->是Vue的实例,里面有$store,$refs...
    -createApp(App)--->是个对象,对象里有东西,没有$store,$refs...,以后有用,都是导入使用

    
# 以后vue3 的<template>,里面不需要写一个div标签了


# 以后都写在setup函数中,定义变量,定义函数,一定要return,在template中才能使用

# 失去了响应式

Vue3:

image-20230222102319673

Vue2:

image-20230222102430432

查看Vue3 app对象:

image-20230222102511098

查看Vue2 Vue实例:

总结:Vue2会创建Vue的实例,里面有$store...Vue3创建的就是一个对象,不再是Vue的实例了。总的来说,Vue3就是为了不使用this。以后使用都是导入使用。

还有区别,Vue3的template不需要只写在一个div里面了。

使用组合式api,核心就是setup函数。以后都在setup函数中写组合式api。

示例:

在setup申明变量:

image-20230222103053779

image-20230222103043756

需要return返回出去一个对象:

image-20230222103206495

点击按钮年龄加一:
怎么使用setup中的数据,直接用即可,无需使用this。修改setup中定义的数据,页面并不会变化,因为没有做数据和页面的双向绑定,失去了响应式。

<template>
  <h2>{{ name }}</h2>
  <h3>{{ age }}</h3>
  <button @click="handleClick">点我看美女</button>
  <br>
  <button @click="handleAdd">点我,age+1</button>
</template>

<script>


export default {
  name: 'App',
  setup() {   // setup中没有this了
    // 以后所有的变量定义函数编写,都写在这个函数中
    // 定义变量 如果这么写,变量渲染没问题,但是没有响应式,页面变了,变量不会变
    let age = 19
    let name = 'lqz'
    // 定义函数
    function handleClick() {
      alert('美女~~~')
    }
    let handleAdd = () => {
      console.log('我要开始加了')
      age=age+1
      console.log(age)
    }
    // 函数必须有返回值
    return {
      age, name, handleClick, handleAdd
    }

  }
}
</script>


4 ref和reactive

# 导入使用:import {ref, reactive} from 'vue'

# 有响应式
	-以后定义变量,如果想有响应式就用 ref 包裹起来,再修改变量,需要用 变量名.value 修改
    
# 配置项api和组合式api可以混写,不建议
	-在前在data中定义的变量
    -在setup中定义的变量
    
    -总结:
    	在setup中定义的变量和函数,在之前配置项api中可以直接使用this.变量,函数调用即可
    	但是在原来配置项中定义的变量,函数,在setup中无法使用
        
# 不同类型做响应式
	ref通常用来包裹,数字,字符串类型,以后使用  xx.value
    reactive用来包裹数组和对象类型    以后使用  直接用即可
    
# 总结  
ref定义的数据:
操作数据需要.value,读取数据时模板中直接读取不需要.value。
reactive定义的数据:
操作数据与读取数据:均不需要.value

# Vue2和Vue3的响应式区别
响应式会降低速度,直接渲染较快。通过ref来自定义哪些变量需要响应式,哪些变量无响应式。而Vue2是监听所有变量,比较消耗资源。

ref

想要实现数据和页面的双向绑定,需要使用ref.

先导入ref:

image-20230222103729738

这里age变成了ref对象:

image-20230222103805284

真正的值在value中。

如何实现响应式:

image-20230222103933338

实现名字后面加?:

image-20230222104155539

配置项api和组合式api可以一起写,但是不建议:

image-20230222110248283

在setup中定义的变量和函数,在之前配置项api可以直接使用this获取这些变量和函数。但是这个this不是原来的this了 。

reactive

申明对象时:

image-20230222110656073

使用ref包一下。
image-20230222110731062

需要使用value来修改值:

ref对象通过点value可以获取到proxy代理对象。

使用reactive:

image-20230222110856155

可以直接修改:

image-20230222111008566

如果使用插值语法渲染,直接将变量名放在template就行了。

<template>
  <h2>{{ name }}</h2>
  <h3>{{ age }}</h3>
  <br>
  <button @click="handleAdd">点我,age+1</button>
  <br>
  <button @click="handleChangeName">点我name变化</button>
</template>

<script>

import {ref, reactive} from 'vue'

export default {
  name: 'App',
  setup() {
    let age = ref(19) // age 已经不是数字了,是RefImpl的对象
    let name = ref('lqz')
    let handleAdd = () => {
      console.log('我要开始加了,age是', age)
      age.value = age.value + 1
      console.log('我要开始加了,age是', age.value)
    }
    function handleChangeName(){
      name.value=name.value+'?'
      console.log(name)
    }

    return {
      age, name, handleClick, handleAdd,handleChangeName
    }

  }
}
</script>


5 计算和监听属性

5.1 计算属性

# computed 的配置项中的写法,不建议用了
	computed:{
        fullName(){
          return this.firstName+this.lastName
        }
      },
    
 # vue3 新写法
	let person = reactive({
      firstName: '',
      lastName: '',
    })
   let fullName = computed(() => {
    return person.firstName + person.lastName
    })

# 计算属性取值和修改值
    let fullName = computed({
      get() {
        return person.firstName + person.lastName
      },
      set(value) {
        person.firstName = value.slice(0, 1)
        person.lastName = value.slice(1)
      },
    })

准备:

image-20230222111350713

image-20230222111426838

使用计算属性:

之前的写法:(不建议使用,还是可以用)
image-20230222111510170

现在的写法:

需要导入computed函数。

image-20230222111705339

换一种写法:

image-20230222111817137

image-20230222111747864

计算属性反向改值

image-20230222112356691

修改计算属性的值,反向修改变量。

image-20230222112031836

获取属性触发get,修改属性触发set。

修改计算属性时,反向修改变量:

image-20230222112258107

value是计算属性经过修改之后的值。

export default {
  name: 'App',
  // computed:{
  //   fullName:function (){}
  // },
  setup() {
    // 1 计算属性案例1
    // let firstName = ref('刘')
    // let lastName = ref('清政')
    // // 定义计算属性
    // let fullName = computed(() => {
    //   return firstName.value + lastName.value
    // })

    // 2 计算属性案例2
    let person = reactive({
      firstName: '刘',
      lastName: '清政',
    })
    // 定义计算属性
    person.fullName = computed({
      get() {
        return person.firstName + '-' + person.lastName
      },
      set(value) {
        console.log(value)
        const nameArr = value.split('-')
        person.firstName = nameArr[0]
        person.lastName = nameArr[1]

      }
    })
    return {
      person
    }


  }
}

5.2 监听属性

# 组合式api写法,只要name发生变化,就会触发匿名函数执行
   let name = ref('lqz')
   watch(name, (newName, oldName) => {
      console.log('name变了')
      console.log('老的', oldName)
      console.log('新的', newName)
    })

# watchEffect 用法,只要age变化,就会执行,age不变化,就不会执行
    watchEffect(() => {
      console.log(age.value)
    })

watch也需要导入。
只要变量变化,触发匿名函数执行:

image-20230222113052121

image-20230222113013436

查看前端:

image-20230222113104569

watchEffect

image-20230222113339971

只要里面使用到的变量发送了变化,这个匿名函数就会执行。

代码:

import {computed, watch, ref, reactive, watchEffect} from 'vue'

export default {
  name: 'App',
  setup() {
    // 1 计算属性案例1
    let name = ref('lqz')

    // 定义监听属性
    watch(name, (newValue, old) => {
      console.log('name变了')
      // console.log(old)
      // console.log(newValue)
    })
    // vue3 多的watchEffect,只要函数中使用的变量发生变化,它就会触发
    watchEffect(() => {
      // 只要该函数中使用的变量发生变化,它就会触发
      // let a = name.value + '?'
      console.log('watchEffect配置的回调执行了')
    })
    return {
      name
    }


  }
}

6 生命周期

# Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
-     vue2                         vue3       
    beforeCreate  	 ===>	    beforeCreate
    created       	 ===>	    created
    beforeMount 	 ===>	    beforeMount
    mounted	         ===>		mounted
    beforeUpdate	 ===>	    beforeUpdate
    updated 		 ===>       updated
    beforeDestroy 	 ===>		beforeUnmount
    destroyed 		 ===>	    unmounted
    
    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
        
# 以前写在created中的代码,现在直接写在setup开始即可
 let show = ref(false)
 axios.get().then(res=>{
      show.value=res.data.show
 })

setup在beforeCreate之前执行。

准备:在组件中写生命周期钩子函数

image-20230222114243236

导入:

image-20230222114808331

查看:

image-20230222114547578

不推荐配置项写法,将生命周期函数统统写在setup中:

image-20230222114844042

页面一加载,向后端发送请求:

image-20230222115132173

直接在setup中间写。

7 hooks

# 什么是hook?—— 
本质是一个函数,把setup函数中使用的Composition API进行了封装。
类似于vue2.x中的mixin。
自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂

7.1 hook/usePoint.js

import {onMounted, onUnmounted, reactive} from "vue";

export default function () {
    let p = reactive({
        x: 0,
        y: 0
    })

    function getPoint(event) {
        console.log(event)
        p.x = event.pageX
        p.y = event.pageY
    }

    // 声明周期钩子的onMounted,当页面挂载就会执行
    onMounted(() => {
        // 给数鼠标点击增加监听,当点击鼠标,就会执行这个函数
        window.addEventListener('click', getPoint)
    })
    // 组件被销毁时,把功能去掉
    onUnmounted(() => {
        window.removeEventListener('click', getPoint)
    })

    return p
}

7.2 在想使用的组件中引入使用即可

<template>
  <h2>x坐标是:{{ p.x }},y坐标是:{{ p.y }}</h2>
</template>

<script>
import {reactive, onMounted, onUnmounted} from 'vue'
import usePoint from '../hook/uesPoint.js'

export default {
  name: "Point",
  setup() {
    let p = usePoint()
    return {p}
  }
}

</script>

<style scoped>

</style>

8 toRefs

# 以后setup的返回值可以直接使用
  setup() {
    let data = reactive({
      name: 'lqz',
      age: 19,
      gender: '男'
    })
    return {...toRefs(data)}
  }
# 以后在模板中直接用  {{name}}
export default {
  name: 'App',

  setup() {
    let data = reactive({
      name: 'lqz',
      age: 19,
      isShow: true
    })

    function handleShow() {
      console.log('ssdaf')
      data.isShow = !data.isShow
      data.age++
    }

    return {
      ...toRefs(data),
      handleShow
      // data
    }

  }
}

image-20230222120559864

使用torefs:

image-20230222120701382

在前端无需使用data.name,这种方式使用插值:

image-20230222120641880

...toRefs(data)解构赋值,相当于;

image-20230222120812761

只使用...:

image-20230222121044766

9 script setup的作用和lang=ts

<script setup>
import {ref} from 'vue'
let name = ref('lqz')
let handleClick = () => {
  alert('美女')
}
</script>

# 以后 这个script中的代码直接放到 setup函数中,不用return了

#lang=ts
里面代码使用ts写,而ts完全兼容js,继续写js代码没问题

Vue3高版本,可以在script标签中直接定义变量,会自动放入setup函数中:

image-20230222121520536

导入组件后无需注册直接使用:

image-20230222121531078

以后都不需要导入导出组件了。

支持ts代码;

image-20230222121650693

查看elementui写的Vue3:

image-20230222121908712

父传子需要使用defineProps接受:

image-20230222122202845

10 后台管理模板

# vue-admin-template-master
	-跑起来
	-package.json 第7行加入
    "dev": "set NODE_OPTIONS=--openssl-legacy-provider & vue-cli-service serve",
    -网址:github.com/PanJiaChen/Vue-admin-template

# java版的若依
带权限控制的后台管理模块 ---前端---> vue-element-admin

# python :
django-vue-admin ---前端---> D2Admin  

# python flask-vue-admin

# go:gin-vue-admin

# element前端 + Vue rbac后台管理
https://gitee.com/liuqingzheng/vue_admin
https://gitee.com/liuqingzheng/rbac_manager

练习

#  创建vue3项目,在setup中写看美女案例
#  练习监听属性和计算顺序
#  从后端接口拿数据展示在页面上
-------------------------------------
# app

随机图片

<template>
  <h1 class="text-center">随机美女图片</h1>
  <div class="text-center">
    <img @click="" :src="default_img" alt="" style="width: 300px">

  </div>
  <br>
  <div class="text-center">
    <button @click="startFunc" class="btn btn-success ">点我开始随机</button> <!-- 当没有给函数传参时,会把点击事件传进来 -->
    <button @click="stopFunc" class="btn btn-success ">点我停止随机</button>
  </div>

</template>

<script setup>
import {ref, reactive} from 'vue'

let imgList = reactive(['https://img2.woyaogexing.com/2022/10/23/af963193e9fb67ee!400x400.jpg',
  'https://img2.woyaogexing.com/2022/10/22/95afdd5cd39d556d!400x400.jpg',
  'https://img2.woyaogexing.com/2022/10/21/f06c65142fe50c19!400x400.jpg',
  'https://img2.woyaogexing.com/2022/10/20/c0135f7e74050a74!400x400.jpg',])
let default_img = ref('https://img2.woyaogexing.com/2022/10/20/c0135f7e74050a74!400x400.jpg')
let timeTool = null

function stopFunc() {
  clearInterval(timeTool)
  timeTool = null
}

function startFunc() {
  if (timeTool === null) {  // 第二次点击的时候timeTool不为null,而是存放一个定时器。
    timeTool = setInterval(() => {
      let i = Math.floor(Math.random() * imgList.length)
      default_img.value = imgList[i]
    }, 20)
  }
}


</script>

<style scoped>

</style>

监听属性和计算属性

<template>
  <input type="text" v-model="userInput">
  <input type="text" v-model="cuteName">
</template>

<script setup>
import {watch, ref, computed} from "vue";

let userInput = ref('')

let cuteName = computed({
  get() {  // 调用计算属性时会触发
    if (userInput.value.length === 0) {
      return ''
    } else {
      return '= = cute--->' + userInput.value
    }
  },  // 修改、设置计算属性时会触发,value是修改之后的值
  set(value) {
    userInput.value = ''
  },
})

watch(userInput, (newName, oldName) => { // 每次修改userInput都会触发该函数
      if (newName.length >= 8) {
        alert('= = 你输入的名字好长')
      }
    }
)

</script>

<style scoped>

</style>

与后端交互

<template>
  <h2>serverView</h2>
  <h2>{{ show }}</h2>
</template>

<script setup>
import axios from 'axios'
import {ref} from "vue";

let show = ref('')
axios.get('http://127.0.0.1:8000/api/v1/movies/').then(res => {
  show.value = res.data
})
</script>

<style scoped>

</style>