Vue3笔记——(尚硅谷张天禹Vue笔记)
Vue3
Vue3简介
1.Vue3简介
.2020年9月18日,Vue.js发布3.0版本,代号: One Piece(海贼王)。耗时2年多、2600+次提交、30+个RFC、600+次PR、99位贡献者
. github上的tags地址: https://github.com/vuejs/vue-next/releases/tag/v3.0.0
2.Vue3带来了什么
.性能的提升
.打包大小减少41%
.初次渲染快55%,更新渲染快133%。内存减少54%
3.拥抱TypeScript
. vue3可以更好的支持TypeScript
4.新的特性
1.Composition API(组合API)
. setup配置
. ref与reactive
. watch与watchEffect
. provide与inject
. ...
2.新的内置组件
. Fragment
. Teleport
. suspense
3.其他改变
. 新的生命周期钩子
. data选项应始终被声明为一个函数
. 移除keyCode支持作为v-on的修饰符
. ...
使用vue-cli创建工程
1.使用vue-cli 创建
官方文档: https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create
## 查看@vue/cli版本,确保@vue/cli版本在4.5.o以上
vue --version
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create vue_test
## 启动
cd vue_test
npm run serve
使用vite创建工程
⒉使用vite 创建
官方文档: https://v3.cn.vuejs.org/guide/installation.html#vite
vite官网: https://vitejs.cn
.什么是vite? ——相对于webpack,新一代前端构建工具。
.优势如下:
。开发环境中,无需打包操作,可快速的冷启动。
。轻量快速的热重载(HMR)。
。真正的按需编译,不再等待整个应用编译完成。
##创建工程
npm init vite-app <project-name>
npm init vite-app vue3_test_vite
##进入工程目录
cd <project-name>
##安装依赖
npm install
##运行
npm run dev
分析Vue3的工程结构
分析vue-cli所创建出的vue3工程
关闭语法检查:vue3自带的vue.config.js中的内容直接替换为vue2的写法也行,如下:
module.exports = {
lintOnSave:false,//关闭语法检查
}
App.vue中vue3中template标签中可以没有根标签括起来
main.js
// 引入的不再是Vue构造函数了,引入的是一个名为createApp的工厂函数
import { createApp } from 'vue'
//引入App组件
import App from './App.vue'// createApp(App).mount('#app')
// 创建应用实例对象--app(类似于之前Vue2中的vm,但app比vm更“轻”)
const app = createApp(App)app.mount('#app')setTimeout(() => { app.unmount('#app')
}, 1000);/* 类似vue2中的如下方式的创建vm挂载Vue到app挂载点,但main.js中不兼容vue2的写法,把vue2的main.js拿来替换不能用,因为无法引入Vue
const vm = new VueElement({render:h =>h(App)
})
vm.$mount('app') */
vue3的开发者工具
Vue3开发者工具,可在极简插件中下载,选那个vue图标右下角带圆角矩形的
极简插件_Chrome扩展插件商店_优质crx应用下载
别使用谷歌浏览器下载,会自动删除下载的文件
初识setup
是常用Composition API 组合式API
拉开序幕的setup
1.理解: vue3.0中一个新的配置项,值为一个函数。
2.setup是所有Composition API(组合API)“表演的舞台”。
3.组件中所用到的:数据、方法等等,均要配置在setup中。
4.setup函数的两种返回值:
1.若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用。(重点关注! )
2.若返回一个渲染函数:则可以自定义渲染内容。(了解)
5.注意点:
1.尽量不要与Vue2.x配置混用
. Vue2.x配置(data、methos、computed...)中可以访问到setup中的属性、方法。
·但在setup中不能访问到Vue2.x配置(data、methos、computed...)。
·如果有重名, setup优先。
2.setup不能是一个async函数,因为返回值不再是return的对象,而是promise,模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)
App.vue
<template><h1>一个人的信息</h1><h2>姓名:{{name}}</h2><h2>年龄:{{age}}</h2><h2>性别:{{sex}}</h2><h2>vue2和vue3的数据a冲突的时候的以vue3的setup为主{{a}}</h2><button @click="sayHello">说话(Vue3配置的sayHello方法)</button><br/><br/><button @click="sayWelcome">说话(Vue2配置的sayWelcome方法)</button><br/><br/><button @click="test1">测试一下在vue2中读取vue3中的数据和方法</button><br/><br/><button @click="test2">测试一下在vue3中读取vue2中的数据和方法</button>
</template><script>
import {h} from "vue"
export default {name: 'App',data() {return {sex:"男" ,a:100}},methods:{sayWelcome(){alert('欢迎来到尚硅谷学习'); },test1(){//在vue2中读取vue3是可以的console.log(this.sex)console.log(this.name)console.log(this.age)console.log(this.sayHello)}},setup(){ // setup是所有Composition API(组合API)“表演的舞台”。组件中所用到的:数据、方法等等,均要配置在setup中。//data直接往这里面写 ,变量用let,常量用constlet name = "张三";let age = 18;let a = 200;//方法也直接写functionfunction sayHello(){alert(`我叫${name},我${age}岁了,你好啊`)//使用``加${}传数据};function test2(){//在vue3中读取vue2,读取不到console.log(this.name)console.log(this.age)console.log(this.sex) // undefinedconsole.log(this.sayWelcome) // undefined}//setup的return若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用。(重点关注! )return{name,age,sayHello,test2,a,}// 若返回一个渲染函数:则可以自定义渲染内容。(了解) 会覆盖掉template中的所有内容// return()=>{ return h("h1","尚硅谷")}/* return()=> h("h1","尚硅谷") */}
}
</script>
ref函数处理基本数据类型
除了html中标签中ref属性,vue3多了一个ref函数。
ref加工后的数据就是一个对象RefImpl,reference implement引用实现,引用实现的对象简称引用对象。里面有个value属性,为实现数据响应式的,通过还是get和set做的响应式在__proto__原型对象中的。像vue2中的_data中的东西放在了vm上。
App.vue
<template><h1>一个人的信息</h1><!-- ref引用对象,在模板中不用name.value,模板自动会解析加上.value --><h2>姓名:{{name}}</h2><h2>年龄:{{age}}</h2><button @click="changeInfo">修改人的信息</button>
</template><script>
import {h} from "vue"
import {ref} from 'vue'
export default {name: 'App',setup(){/* let name = "张三";let age = 18; */// 把普通数据变成一个响应式的数据let name = ref("张三")let age = ref(18)function changeInfo(){/* name = '李四'age = 48 */// 由于name和age为ref函数得到的引用对象,通过get和set做的响应式,所以我们要通过.vauename.value = '李四'age.value = 48}return{name,age,changeInfo}}
}
</script>
ref函数处理对象类型的数据
对于ref函数处理的对象类型,没有直接变为RefIMpl应用对象,而是变为了Proxy代理对象。是内部求助的reactive函数,这个函数封装了proxy。
App.vue
<template><h1>一个人的信息</h1><!-- ref引用对象,在模板中不用name.value,模板自动会解析加上.value --><h2>姓名:{{name}}</h2><h2>年龄:{{age}}</h2><h3>手机品牌:{{phone.brand}}</h3><h3>手机颜色:{{phone.color}}</h3><button @click="changeInfo">修改人的信息</button>
</template><script>
import {h} from "vue"
import {ref} from 'vue'
export default {name: 'App',setup(){/* let name = "张三";let age = 18; */// 把普通数据变成一个响应式的数据let name = ref("张三")let age = ref(18)// ref函数处理对象类型let phone = ref({brand:"redmi", color:"red"})function changeInfo(){/* name = '李四'age = 48 */// 由于name和age为ref函数得到的引用对象,通过get和set做的响应式,所以我们要通过.vauename.value = '李四'age.value = 48phone.value.brand="xiaomi" //ref函数对象类型的属性修改数据phone.value.color="green"}return{name,age,changeInfo,phone}}
}
</script>
reactive函数
reactive函数
。作用:定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)
。语法: const 代理对象= reactive(源对象)接收一个对象(或数组),返回一个代理对象〈Proxy的实例对象,简称proxy对象)
。 reactive定义的响应式数据是"深层次的”。
。内部基于ES6的Proxy实现,通过代理对象操作源对象内部数据进行操作。
App.vue
<template><h1>一个人的信息</h1><!-- ref引用对象,在模板中不用name.value,模板自动会解析加上.value --><h2>姓名:{{name}}</h2><h2>年龄:{{age}}</h2><h3>手机品牌:{{phone.brand}}</h3><h3>手机颜色:{{phone.color}}</h3><h3>hobby:{{hobby}}</h3><h3>测试的数据d:{{a.b.c.d}}</h3><button @click="changeInfo">修改数据</button>
</template><script>
import {h} from "vue"
import {ref,reactive} from 'vue'
export default {name: 'App',setup(){// 把普通数据变成一个响应式的数据let name = ref("张三")let age = ref(18)// reactive将对象变为响应式的let phone = reactive({brand:"redmi", color:"red"})let a = reactive({b:{c:{d:666}}})let hobby = reactive(['看喜羊羊','看灰太狼','看光头强'])function changeInfo(){name.value = '李四'age.value = 48phone.brand="xiaomi" //active函数对象类型的属性修改数据phone.color="green"a.b.c.d=999hobby[0] = '看熊出没'}return{name,age,changeInfo,phone,a,hobby}}
}
</script>
Vue2的响应式原理的回顾
vue2.x的响应式
. 实现原理:
。对象类型:通过object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。
。数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
object.defineProperty(data, 'count ', {
get(){},
set(){}
})
. 存在问题:
。新增属性、删除属性,界面不会更新。
。直接通过下标修改数组,界面不会自动更新。
Vue2项目中的App.vue,来看vue2的响应式存在的问题
<template><div><h1>我是Vue2的写法</h1><h2 v-show="person.name">姓名:{{person.name}}</h2><h2>年龄:{{person.age}}</h2><h2>爱好:{{person.hobby}}</h2><h2 v-show="person.sex">性别:{{person.sex}}</h2><button @click="addSex">添加一个sex属性</button><button @click="deleteName">添加一个name属性</button><button @click="updateHobby">修改第一个爱好的名字</button></div>
</template>
<script>
import Vue from 'vue'
export default {name:"App",data() {return {person:{name:'张三',age:18,hobby:["看喜羊羊",'看灰太狼','看光头强']}}},methods:{addSex(){/* console.log(this.person.sex) //undefinedthis.person.sex='女'console.log(this.person.sex) //'女' 确实新增了sex属性了,但页面不更新,defineProperties监测不到 */this.$set(this.person,"sex",'女') //可通过这种和下面这种方式加上这个属性 // Vue.set(this.person,"sex",'女')},deleteName(){/* console.log(this.person.name) // '张三'delete this.person.nameconsole.log(this.person.name)// undefined 确实删除了name,但页面中监测不到,页面还是不变 *///this.$delete(this.person,"name")Vue.delete(this.person,"name")},updateHobby(){// this.person.hobby[0] = '看大大怪' // 这样修改确实能修改,但页面监测不到,页面上的数据不变//this.$set(this.person.hobby,0,"看大大怪")// 或使用数组的方法,如push shift splice等,这些方法为vue对普通方法的封装,可以监测到this.person.hobby.splice(0,1,'看小小怪')}}
}
</script>
Vue3的响应式原理Proxy
Vue3中不存在vue2中的添加或属性或修改数组元素的不是响应式的页面不变的问题
Vue3中的App.vue
<template><h1>一个人的信息</h1><!-- ref引用对象,在模板中不用name.value,模板自动会解析加上.value --><h2 v-show="person.name">姓名:{{person.name}}</h2><h2>年龄:{{person.age}}</h2><h2 v-show="person.sex">性别:{{person.sex}}</h2><h3>手机品牌:{{person.phone.brand}}</h3><h3>手机颜色:{{person.phone.color}}</h3><h3>hobby:{{person.hobby}}</h3><button @click="changeInfo">修改数据</button><button @click="addSex">添加一个sex属性</button><button @click="deleteName">删除一个name属性</button></template><script>
import {ref,reactive} from 'vue'
export default {name: 'App',setup(){let person = reactive({name:"张三",age:18,phone:{brand:"redmi", color:"red"},hobby:['看喜羊羊','看灰太狼','看光头强']})function changeInfo(){person.name = '李四'person.age = 48person.phone.brand="xiaomi" //active函数对象类型的属性修改数据person.phone.color="green"person.hobby[0] = '看熊出没' //直接通过数组下标修改元素也是可以响应式的,页面也会发生变化}function addSex(){person.sex = '男' // 添加一个不存在的属性,页面可以发生变化,做了响应式,reactive的原因。}function deleteName(){delete person.name}return{person,changeInfo,addSex,deleteName}}
}
</script>
Object.defineProperties、new Proxy()模拟vue2和vue3的响应式原理
响应式原理.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><script type="text/javascript">//源数据let person = {name : "张三",age : 18}//模拟vue2中实现响应式//#region/* let p = {}Object.defineProperties(p,"name",{configurable:true, //声明为可配置的,就允许被删除了get(){ //有人读取的时候调用return person.name},set(value){console.log("有人修改了name属性,发现了,要去更新界面!")person.name = value}})Object.defineProperties(p,"age",{get(){ //有人读取的时候调用return person.age},set(value){console.log("有人修改了age属性,发现了,要去更新界面!")person.age = value}})// 然后再f12控制台中,person.name = '李四' person.sex = '男' delete person.name 之类操作查看结果 *///#endregion//模拟vue3中实现响应式const p = new Proxy(person,{ //就会进行代理,person中的属性都会有get和set了,下面代码可以里面的api做响应式//有人读取p的某个属性式调用get(target,propName){console.log(`有人读取了p身上的${propName}属性`)return target[propName]},// 有人修改了p的某个属性,或给p追加某个属性时调用set(target,propName,value){console.log(`有人修改了p身上的${propName}属性,我要去更新页面了!`)target[propName] = value},//有人删除p的某个属性时调用deleteProperty(target,propName){console.log(`有人删除了p身上的${propName}属性,我要去更新页面了!`)return delete target[propName]}})// f12中person.name='李四'等操作即可</script>
</body>
</html>
Vue3响应式原理Reflect
Reflect反射的方式,容错性更好,我们修改上面的Vue3的模拟中的操作
Vue3的响应式.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><script type="text/javascript">//源数据let person = {name : "张三",age : 18}//模拟vue2中实现响应式//#region/* let p = {}Object.defineProperties(p,"name",{configurable:true, //声明为可配置的,就允许被删除了get(){ //有人读取的时候调用return person.name},set(value){console.log("有人修改了name属性,发现了,要去更新界面!")person.name = value}})Object.defineProperties(p,"age",{get(){ //有人读取的时候调用return person.age},set(value){console.log("有人修改了age属性,发现了,要去更新界面!")person.age = value}})// 然后再f12控制台中,person.name = '李四' person.sex = '男' delete person.name 之类操作查看结果 *///#endregion//模拟vue3中实现响应式//#regionconst p = new Proxy(person,{ //就会进行代理,person中的属性都会有get和set了,下面代码可以里面的api做响应式//有人读取p的某个属性式调用get(target,propName){console.log(`有人读取了p身上的${propName}属性`)// return target[propName]return Reflect.get(target,propName)},// 有人修改了p的某个属性,或给p追加某个属性时调用set(target,propName,value){console.log(`有人修改了p身上的${propName}属性,我要去更新页面了!`)// target[propName] = valueReflect.set(target,propName,value)},//有人删除p的某个属性时调用deleteProperty(target,propName){console.log(`有人删除了p身上的${propName}属性,我要去更新页面了!`)// return delete target[propName]return Reflect.deleteProperty(target,propName)}})//#endregion//#region/* //Reflect.definedProperty反射对象去操作let obj = {a:1,b:2}//除了obj.a = 666 还能Reflect.set(obj,'a',666)的方式修改a Reflect.deleteProperty(obj,'a')的方式删除a//通过Object.defineProperty去操作Object.defineProperty(obj,'c',{get(){return 3}})Object.defineProperty(obj,'c',{get(){return 4}})//如上Object.defineProperty对obj定义追加了c重复了,就会报错,这样容错性不太好,需要try-catch包裹起来//但使用如下Reflect.definedProperty反射对象去操作后,重复后就不会报错const x1 = Reflect.defineProperty(obj,'c',{get(){return 3}})const x2 = Reflect.defineProperty(obj,'c',{get(){return 4}})//可通过if-else判断if(x2){console.log("某某操作成功了")}else{console.log("某某操作失败了")} *///#endregion</script>
</body>
</html>
实现原理:
。通过Proxy(代理)︰拦截对象中任意属性的变化,包括:属性值的读写、属性的添加、属性的删除等。
。通过Reflect(反射)︰对源对象的属性进行操作。
。MDN文档中描述的Proxy与Reflect:
.Proxy: https:/ldeveloper.mozilla.org/zh-CN/docs/WeblJavaScript/Reference/Global_Objects/Proxy
.Reflect: https:/ldeveloper.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect
reactive和ref的对比
. 从定义数据角度对比:
。ref用来定义:基本类型数据。
。 reactive用来定义:对象(或数组)类型数据。
。备注: ref也可以用来定义对象(或数组)类型数据,它内部会自动通过reactive转为代理对象。
. 从原理角度对比:
。ref通过 object.definePropertv()的get与set来实现响应式(数据劫持)。
。reactive通过使用Proxy来实现响应式(数据劫持)﹐并通过Reflect操作源对象内部的数据。
. 从使用角度对比:
。ref定义的数据:操作数据需要.value,读取数据时模板中直接读取不需要.value 。
。reactive定义的数据:操作数据与读取数据:均不需要.value。
setup的两个注意点
setup的两个注意点
. setup执行的时机
。在beforeCreate之前执行一次,this是undefined。
. setup的参数
。props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
。context: 上下文对象
` attrs:值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性,相当于[this .$attrs 。
` slots: 收到的插槽内容,相当于this.$slots 。
` emit:分发自定义事件的函数,相当于this.$emit 。
vue2中的props传参,父组件写的参数,子组件props:[]没声明的参数,就会被$atts捡漏。vue2中slots父组件中写的额插槽的内容,会放在$slot中作为虚拟节点。
set第一个参数需要props声明,第二个参数用到的emit需要在上面声明给emits
vue3中使用具名插槽尽量用v-slot
App.vue
<template><Demo msg='你好' school='尚硅谷' @hello='showHelloMsg'><span>尚硅谷</span></Demo>
</template><script>
import Demo from "./components/Demo.vue"
export default {name: 'App',components:{Demo},setup(){function showHelloMsg(value){alert(`你好啊,你触发了hello事件,我收到的参数是:${value}`)}return {showHelloMsg}}
}
</script>
Demo.vue
<template><h1>一个人的信息</h1><h2>姓名:{{person.name}}</h2><h2>年龄:{{person.age}}</h2><button @click="test">测试触发一下Demo组件的Hello事件</button>
</template><script>
import { reactive } from 'vue'
export default {name:'Demo',props:['msg','school'],emits:['hello'],setup(props,context) {// console.log("-----setup-----",props)// console.log("---setup---",context)// console.log("---setup---",context.attrs)//相当于vue2中的$attrs// console.log("---setup---",context.emit) // 触发自定义事件的console.log("---setup---",context.slots) //插槽//数据let person = reactive({name:'张三',age:18})function test(){context.emit('hello',666)}return {person,test}}
}
</script><style></style>
Vue3computed计算属性
App.vue
<template><Demo/>
</template><script>
import Demo from "./components/Demo.vue"
export default {name: 'App',components:{Demo},
}
</script>
Demo.vue
<template><h1>一个人的信息</h1>姓:<input type="text" v-model="person.firstName"><br/>名:<input type="text" v-model="person.lastName"><br/><!-- <span>全名:{{fullName}}</span> --><span>全名:{{person.fullName}}</span><br/>全名:<input type="text" v-model="person.fullName">
</template><script>
import { reactive ,computed} from 'vue'
export default {name:'Demo',/* computed:{ // 在vue3中是可以像vue2中写计算属性的。但不推荐。fullName(){return this.person.firstName + '-' + this.person.lastName}}, */setup() {let person = reactive({firstName:'张',lastName:"三"})// 计算属性-简写(没有考虑计算属性被修改的情况)/* let fullName = computed(()=>{return person.firstName + "-" + person.lastName}) *//* person.fullName = computed(()=>{return person.firstName + "-" + person.lastName}) */// 计算属性的完整写法(考虑读和写)person.fullName = computed({get(){return person.firstName + '-' +person.lastName},set(value){const nameArr = value.split('-')person.firstName=nameArr[0],person.lastName=nameArr[1]}})return {person}}
}
</script><style></style>
watch监视ref定义的数据
Demo.vue
<template><h2>当前求和为:{{sum}}</h2><button @click="sum++">点我+1</button><hr><h2>当前的信息为:{{msg}}</h2><button @click="msg+='!'">修改信息</button>
</template><script>
import { ref, watch } from 'vue'
export default {name:'Demo',/* watch:{// vue3中也可使用vue2中的watch,不过不推荐sum(newValue,oldValue){//简写的方式,详细写法见vue2中的watchconsole.log('sum的值变化了',newValue,oldValue)}}, */setup() {let sum = ref(0)let msg = ref('你好啊')//情况1,监视ref所定义的一个响应式数据/* watch(sum,(newValue,oldValue)=>{console.log("sum变化了",newValue,oldValue)}) *///情况2,监视ref所定义的多个响应式数据 可写多个watch在vue3中/* watch(sum,(newValue,oldValue)=>{console.log("sum变化了",newValue,oldValue)})watch(msg,(newValue,oldValue)=>{console.log("msg变化了",newValue,oldValue)}) *//* watch([sum,msg],(newValue,oldValue)=>{ // newValue,oldValue分别为数组console.log("sum或msg变化了",newValue,oldValue)}) */watch(sum,(newValue,oldValue)=>{console.log("sum变化了",newValue,oldValue)},{immediate:true})//可加第三个参数为配置 但deep:true在vue3中有小问题return {sum,msg}}
}
</script><style></style>
App.vue中引用注册并渲染这个Demo.vue
watch监视reactive定义的数据
watch函数
。与Vue2.x中watch配置功能一致。
两个小“坑":
。监视reactive定义的响应式数据时: oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
。监视reactive定义的响应式数据中某个属性时: deep配置有效。
Demo.vue
<template><h2>姓名:{{person.name}}</h2><h2>年龄:{{person.age}}</h2><h2>薪水:{{person.job.j1.salary}}</h2><button @click="person.name+='~'">修改姓名</button><button @click="person.age++">增长年龄</button><button @click="person.job.j1.salary++">增长薪资</button>
</template><script>
import { reactive, ref, watch } from 'vue'
export default {name:'Demo',setup() {let person = reactive({name:'张三',age:18,job:{j1:{salary:20}}})//情况三:监视reactive所定义的一个响应式数据的全部属性,注意,此处无法正确的获取oldValue,这是vue3的小bugwatch(person,(newValue,oldValue)=>{ //vue3中默认就开启了深度监视了,所以salary修改也能显示,且关不掉这个深度监视console.log('person发生变化了',newValue,oldValue);})//情况四:监视reactive所定义的一个响应式数据中的某个属性watch(()=>person.age,(newValue,oldValue)=>{ //vue3中默认就开启了深度监视了,所以salary修改也能显示,且关不掉这个深度监视console.log('person的age发生变化了',newValue,oldValue);})//情况五:监视reactive所定义的一个响应式数据中的某些属性watch([()=>person.age,()=>person.name],(newValue,oldValue)=>{ //vue3中默认就开启了深度监视了,所以salary修改也能显示,且关不掉这个深度监视console.log('person的age或name发生变化了',newValue,oldValue);})//特殊情况 ()=>person.job 如果改job下的子属性,需要开启深度监视deep:truewatch(()=>person.job,(newValue,oldValue)=>{ //vue3中默认就开启了深度监视了,所以salary修改也能显示,且关不掉这个深度监视console.log('person的job发生变化了',newValue,oldValue);},{deep:true})return {person}}
}
</script><style></style>
App.vue中引用注册并渲染这个Demo.vue
watch时的value问题
ref()对基本类型,.value就是基本类型,但ref对对象类型,.value的是proxy代理对象。
所以watch为ref普通属性直接写,但watch为ref的对象时,修改person里面的属性,用person.value,还可在后面加上deep:true属性
watchEffect函数
watchEffect函数
. watch的套路是:既要指明监视的属性,也要指明监视的回调。
. watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
. watchEffect有点像computed:
。但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
。而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
Demo.vue
<template><h2>当前求和为:{{sum}}</h2><button @click="sum++">点我+1</button><hr><h2>当前的信息为:{{msg}}</h2><button @click="msg+='!'">修改信息</button><hr><h2>姓名:{{person.name}}</h2><h2>年龄:{{person.age}}</h2><h2>薪水:{{person.job.j1.salary}}</h2><button @click="person.name+='~'">修改姓名</button><button @click="person.age++">增长年龄</button><button @click="person.job.j1.salary++">增长薪资</button>
</template><script>
import { ref, reactive, watch, watchEffect } from 'vue'
export default {name:'Demo',setup() {let sum = ref(0)let msg = ref('你好啊')let person = reactive({name:'张三',age:18,job:{j1:{salary:20}}})/* watch(sum,(newValue,oldValue)=>{console.log("sum变化了",newValue,oldValue)},{immediate:true}) */watchEffect(()=>{//里面用到了哪些属性就监视谁,而且还默认第一次就加载const x1 = sum.valueconst x2 = person.job.j1.salaryconsole.log("watchEffect所指定的回调执行了");})return {sum,msg,person}}
}
</script><style></style>
App.vue中引用注册并渲染这个Demo.vue
Vue3生命周期钩子
vue2生命周期钩子前6个都一样,最后beforeDestroy和destroyed与vue3的beforeUnmount和unmounted不一样.vue2中即使没写el,也能执行beforeCreate和created这两步,但vue3得都有才能开始执行beforeCreate。vue3也能通过配置项的形式引用生命周期钩子,也可通过组合式api的形式去使用生命周期钩子
Vue3.0也提供了Composition API形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
。beforeCreate ===> setup()
。created =======> setup()
。beforeMount =======> onBeforeMount
。mounted =======> onMounted
。beforeUpdate ===> onBeforeUpdate
。updated =======> onUpdated
。beforeUnmount ==> onBeforeUnmount
。unmounted =====> onUnmounted
Demo.vue
<template><h2>当前求和为:{{sum}}</h2><button @click="sum++">点我+1</button>
</template><script>
import { ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'
export default {name:'Demo',setup() {let sum = ref(0)//通过组合式api的形式去使用生命周期钩子 优先级setup的高于配置项的onBeforeMount(()=>{console.log("---onBeforeMount---");})onMounted(()=>{console.log("---onMounted---");})onBeforeUpdate(()=>{console.log("---onBeforeUpdate---");})onUpdated(()=>{console.log("---onUpdated---");})onBeforeUnmount(()=>{console.log("---onBeforeUnmount---");})onUnmounted(()=>{console.log("---onUnmounted---");})return {sum,}},//#region // 通过配置项的形式引用生命周期钩子
/* beforeCreate() {console.log('beforeCreate')},created() {console.log('created')},beforeMount() {console.log('beforeMount')},mounted() {console.log('mounted')},beforeUpdate() {console.log('beforeUpdate')},updated() {console.log('updated')},beforeUnmount() {console.log("beforeUnmount")},unmounted() {console.log("unmounted")}, *///#endregion
}
</script><style></style>
App.vue
<template><button @click="isShowDemo = !isShowDemo">切换隐场显示</button><Demo v-if="isShowDemo"/>
</template><script>
import { ref } from 'vue'
import Demo from "./components/Demo.vue"
export default {name: 'App',components:{Demo},setup(){let isShowDemo = ref(true)return {isShowDemo}},
}
</script>
自定义hook
.什么是hook?——本质是一个函数,把setup函数中使用的Composition APlI进行了封装。
·类似于vue2.x中的mixin。
·自定义hook的优势:复用代码,让setup中的逻辑更清楚易懂。
本来在Demo.vue中写的鼠标打点的方法,但可能其他组件也需要使用改鼠标打点的功能,要么将这段代码复制粘贴,或者我们可以在src下建一个hooks文件夹,创建userPoint.js文件,然后在这里写那些代码,然后返回出去,然后Demo.vue中再引入
usePoint.js
import {reactive,onMounted, onBeforeUnmount} from 'vue'// function savePoint(){
export default function (){//实现鼠标打点的数据let point = reactive({x:0,y:0})//实现鼠标打点的方法function savePoint(event){point.x = event.pageXpoint.y = event.pageYconsole.log(event.pageX,event.pageY);}//实现鼠标打点相关的生命周期钩子onMounted(()=>{window.addEventListener("click",savePoint)})onBeforeUnmount(()=>{window.removeEventListener("click",savePoint)})return point
}// export default savePoint
Demo.vue
<template><h2>当前求和为:{{sum}}</h2><button @click="sum++">点我+1</button><hr><h2>当前点击时鼠标的坐标为:x:{{point.x}},y:{{point.y}}</h2>
</template><script>
import usePoint from '../hooks/usePoint'
import { ref,reactive,onMounted, onBeforeUnmount} from 'vue'
export default {name:'Demo',setup() {let sum = ref(0)/* let point = reactive({x:0,y:0})function savePoint(event){point.x = event.pageXpoint.y = event.pageYconsole.log(event.pageX,event.pageY);}onMounted(()=>{window.addEventListener("click",savePoint)})onBeforeUnmount(()=>{window.removeEventListener("click",savePoint)}) *///然后复用let point = usePoint()return {sum,point}},}
</script><style></style>
toRef与toRefs
toRef:将一个不是ref的变成ref
. 作用:创建一个ref对象,其value值指向另一个对象中的某个属性。
· 语法:const name = toRef(person , ' name ')
. 应用:要将响应式对象中的某个属性单独提供给外部使用时。
. 扩展: toRefs 与toRef功能一致,但可以批量创建多个ref对象,语法: toRefs(person)
Demo.vue
<template><h2>姓名:{{name}}</h2><h2>年龄:{{age}}</h2><h2>薪水:{{job.j1.salary}}</h2><button @click="name+='~'">修改姓名</button><button @click="person.age++">增长年龄</button><button @click="job.j1.salary++">增长薪资</button>
</template><script>
import {reactive, toRef, toRefs} from 'vue'
export default {name:'Demo',setup() {let person = reactive({name:'张三',age:18,job:{j1:{salary:20}}})/* const name1 = person.nameconsole.log('%%%%%%',name1);const name2 = toRef(person,'name') // 这里并不是又复制了一份,而是这里读取的指向的还是上面的let person里面的console.log('@@@@@',name2); */const x = toRefs(person) //将person中所有属性变成ref引用对象console.log(x);return {/* person,name:toRef(person,'name'),age:toRef(person,'age'),salary:toRef(person.job.j1,'salary'), *///name:ref(person.name) //用ref的话,之后修改的就不是上面的person中的name了,而是新的ref,所以不能这么写...toRefs(person)}}
}
</script><style></style>
App.vue中引入即可
其他组合式API
不常用
shallowReactive与shallowRef
App.vue中导入注册渲染Demo.vue. shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
. shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理。
. 什么时候使用?
。如果有一个对象数据,结构比较深,但变化时只是外层属性变化===> shallowReactive。
。如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换===>shallowRef。
Demo.vue
<template><h4>当前x的值是:{{x.y}}</h4><button @click="x.y++">点我x+1</button><h2>姓名:{{name}}</h2><h2>年龄:{{age}}</h2><h2>薪水:{{job.j1.salary}}</h2><button @click="name+='~'">修改姓名</button><button @click="person.age++">增长年龄</button><button @click="job.j1.salary++">增长薪资</button>
</template><script>
import {ref,reactive, toRef, toRefs,shallowReactive,shallowRef} from 'vue'
export default {name:'Demo',setup() {//let person = shallowReactive({ //浅层次的reactive,只有第一层可响应式,即下面的name和age有响应式,但salary没有响应式let person = reactive({name:'张三',age:18,job:{j1:{salary:20}}})// let x = shallowRef({ //shallowRef和ref区别为,当值为对象时,shallowRef只能修改一层,如下x的y属性就没有响应式let x = ref({y:0})return {...toRefs(person),x}}
}
</script><style></style>
readOnly与shallowReadOny
readonly:让一个响应式数据变为只读的(深只读)。
shallowReadonly:让一个响应式数据变为只读的(浅只读)
应用场景:不希望数据被修改时。
Demo.vue
<template><h4>当前求和为:{{sum}}</h4><button @click="sum++">点我+1</button><h2>姓名:{{name}}</h2><h2>年龄:{{age}}</h2><h2>薪水:{{job.j1.salary}}</h2><button @click="name+='~'">修改姓名</button><button @click="age++">增长年龄</button><button @click="job.j1.salary++">增长薪资</button>
</template><script>
import {ref,reactive, toRef, toRefs,shallowReactive,shallowRef, readonly, shallowReadonly} from 'vue'
export default {name:'Demo',setup() {let person = reactive({name:'张三',age:18,job:{j1:{salary:20}}})let sum = ref(0)// person = readonly(person) //不让任何人修改这个数据,就可以用readonly,并且用person接收覆盖了原来的personperson = shallowReadonly(person) //底有第一层年龄和姓名改不了,但薪资可改的// sum = readonly(sum)// sum = shallowReadonly(sum)return {...toRefs(person),sum}}
}
</script><style></style>
toRaw与markRaw
. toRaw:
。作用:将一个由reactive生成的响应式对象转为普通对象。
。使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新.
. markRaw":
。作用:标记一个对象,使其永远不会再成为响应式对象。
。应用场景:
1.有些值不应被设置为响应式的,例如复杂的第三方类库等。
2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
Demo.vue
<template><h4>当前求和为:{{sum}}</h4><button @click="sum++">点我+1</button><h2>姓名:{{name}}</h2><h2>年龄:{{age}}</h2><h2>薪水:{{job.j1.salary}}</h2><h2>手机:{{person.phone}}</h2><button @click="name+='~'">修改姓名</button><button @click="age++">增长年龄</button><button @click="job.j1.salary++">增长薪资</button><button @click="showRawPerson">输出最原始的person</button><button @click="addPhone">给人添加一个手机</button><button @click="person.phone.brand+='!'">换手机品牌</button><button @click="person.phone.color+='@'">换手机颜色</button>
</template><script>
import {markRaw,ref,reactive, toRef, toRefs,shallowReactive,shallowRef, toRaw} from 'vue'
export default {name:'Demo',setup() {let person = reactive({name:'张三',age:18,job:{j1:{salary:20}}})let sum = ref(0)function showRawPerson(){const p = toRaw(person) // 将将一个由reactive生成的响应式对象转为普通对象。ref的行不通。console.log(person);console.log(p);}function addPhone(){/* let phone = {brand:"小米",color:"red"}person.phone = phone //追加的内容为响应式的 */let phone = {brand:"小米",color:"red"}person.phone = markRaw(phone) //使用markRaw标记后这个数据永远不做响应式了,修改时数据确实改了,但页面不变化}return {...toRefs(person),sum,person,//把person也交出去,后期添加person属性可直接用person.的方式获取值showRawPerson,addPhone}}
}
</script><style></style>
App.vue中引用Demo.vue
customRef自定义ref
作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显式控制。
通过一个防抖的案例:即输入框中输入内容,显示的h4标签过1秒再显示。
App.vue
<template><input type="text" v-model="keyWord"><h3>{{keyWord}}</h3>
</template><script>
import { ref ,customRef} from 'vue'
export default {name: 'App', setup(){//自定义一个ref—名为:myReffunction myRef(value,delay){let timer;return customRef((track,trigger)=>{ //要返回一个对象return{get(){console.log(`有人从myRef这个容器中读取数据了,我把${value}给他了`);track() // 2 通知vue追踪value的变化,(提前和get商量一下,让他认为这个value是有用的)return value},set(newValue){console.log(`有人把myRef这个容器中数据改为了${newValue}`);clearTimeout(timer) //防抖//value = newValue// trigger() // 1 通知vue去重新解析模板timer = setTimeout(() => {value = newValuetrigger() // 1 通知vue去重新解析模板}, delay);}}})}// let keyWord = ref("hello")// 使用vue提供的reflet keyWord = myRef("hello",500)// 使用vue提供的refreturn {keyWord}},
}
</script>
provide与inject
。作用:实现祖与后代组件间通信
。套路:父组件有一个provide选项来提供数据,后代组件有一个inject选项来开始使用这些数据
App.vue
<template><div class="app"><h3>我是App组件(祖)</h3><Child/></div>
</template><script>
import { provide, reactive, toRefs } from 'vue'
import Child from "./components/Child.vue"
export default {name: 'App', components:{Child},setup(){let phone = reactive({brand:"小米",color:"red"})provide('phone',phone)return {...toRefs(phone)}}
}
</script>
<style>
.app{background-color: grey;padding: 10px;
}
</style>
Child.vue
<template><div class="child"><h3>我是Child组件(子),{{phone.brand}}--{{phone.color}}</h3><Sun/></div>
</template><script>
import { inject } from 'vue'
import Sun from './Sun.vue'
export default {name: 'Child', components:{Sun},setup(){let phone = inject('phone')return{phone}}
}
</script>
<style>
.child{background-color: skyblue;padding: 10px;
}
</style>
Sun.vue
<template><div class="sun"><h3>我是Sun组件(孙),{{phone.brand}}--{{phone.color}}</h3></div>
</template><script>
import { inject } from 'vue'
export default {name: 'Sun', setup(){let phone = inject('phone')return{phone}}
}
</script>
<style>
.sun{background-color: orange;padding: 10px;
}
</style>
响应式数据的判断
响应式数据的判断API
isRef:检查一个值是否为一个ref 对象
isReactive:检查一个对象是否是由reactive创建的响应式代理
isReadonly:检查一个对象是否是由readonly 创建的只读代理
isProxy:检查一个对象是否是由reactive或者readonly方法创建的代理
App.vue
<template><h3>我是App组件(祖)</h3>
</template><script>
import { isProxy, isReactive, isReadonly, isRef, provide, reactive, readonly, ref, toRefs } from 'vue'
export default {name: 'App', setup(){let phone = reactive({brand:"小米",color:"red"})let sum = ref(0)let phone2 = readonly(phone)console.log(isRef(sum));//trueconsole.log(isReactive(phone));//trueconsole.log(isReadonly(phone2));//trueconsole.log(isProxy(phone));//trueconsole.log(isProxy(phone2));//true readOnly处理后的也是proxyprovide('phone',phone)return {...toRefs(phone)}}
}
</script>
<style>
</style>
组合式API的优势
Composition API组合式API,Options API配置式API
Options API存在的问题:使用传统OptionsAPI中,新增或者修改一个需求,就需要分别在data,methods,computed里修改。
2.Composition API的优势:我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。还可借助hook函数发挥组合式API的威力。
新的组件
Fragment与Teleport
1.Fragment
。在Vue2中:组件必须有一个根标签
。在Vue3中:组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中。好处:减少标签层级,减小内存占用
2.Teleport。什么是Teleport?
Teleport是一种能够将我们的组件html结构移动到指定位置的技术。
案例:传统的方式写弹窗会将外几层的组件全都撑开。使用teleport指定to='html'或to='body'就可将这段代码的东西传送到html标签或body标签中,也可加遮罩层
App.vue
<template><div class="app"><h3>我是App组件</h3><Child/></div>
</template><script>
import Child from "./components/Child.vue"
export default {name: 'App', components:{Child}
}
</script>
<style>
.app{background-color: grey;padding: 10px;
}
</style>
Child.vue
<template><div class="child"><h3>我是Child组件</h3><Sun/></div>
</template><script>
import Sun from './Sun.vue'
export default {name: 'Child', components:{Sun},
}
</script>
<style>
.child{background-color: skyblue;padding: 10px;
}
</style>
Son.vue
<template><div class="sun"><h3>我是Sun组件</h3><Dialog/></div>
</template><script>
import Dialog from './Dialog.vue'
export default {name: 'Sun', components:{Dialog}
}
</script>
<style>
.sun{background-color: orange; padding: 10px;
}
</style>
Dialog.vue
<template><div><button @click="isShow = true">点我弹个窗</button>
<!-- <div v-if="isShow" class="dialog"><h3>我是一个弹窗</h3><h4>一些内容</h4><h4>一些内容</h4><h4>一些内容</h4><button @click="isShow = false">关闭弹窗</button></div> --><teleport to="body"><div v-if="isShow" class="mask"><div class="dialog"><h3>我是一个弹窗</h3><h4>一些内容</h4><h4>一些内容</h4><h4>一些内容</h4><button @click="isShow = false">关闭弹窗</button></div> </div></teleport></div>
</template><script>
import { ref } from 'vue'export default {name:"Dialog",setup(){let isShow = ref(false)return {isShow}}
}
</script><style>
.dialog{width: 300px;height: 300px;background-color: green;position: absolute;top:50%;left:50%;transform: translate(-50%,-50%);
}
.mask{/* 遮罩层样式,点被其遮住的元素的点不了 */position: absolute;top: 0 ;bottom: 0;left: 0;right: 0;background-color: rgba(0, 0, 0, 0.5);
}
</style>
Suspense组件
网速快的时候,静态引入和动态引入一样,但网速慢的时候,静态引入是App和Child组件一起出来(最后引入取决于组件套娃中最后出来的那个组件出来后才显示),动态引入是App和Child谁加载完谁出来。但动态引入当某个先出来展示,某个组件加载慢1秒中后才出来就会用户体验不好,页面有抖动,可以用Suspense将子标签Child标签包起来,然后再Child组件还未加载好的时候先用一个loading替换
App.vue
<template><div class="app"><h3>我是App组件</h3><!-- <Child/> --><Suspense><template v-slot:default><Child/></template><template v-slot:fallback><h3>稍等,加载中......</h3></template></Suspense></div>
</template><script>
// import Child from "./components/Child.vue" //静态引入
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child.vue')) //动态引入 异步引入
export default {name: 'App', components:{Child}
}
</script>
<style>
.app{background-color: grey;padding: 10px;
}
</style>
Child.vue
<template><div class="child"><h3>我是Child组件</h3>{{sum}}</div>
</template><script>
import { ref } from 'vue';
export default {name: 'Child', async setup(){ //因为App.vue中使用了Suspense,且引入Child组件时为异步引入,所以这里的setup可返回Promise对象let sum = ref(0)let p = new Promise((resolve,reject)=>{setTimeout(() => {resolve({sum})}, 3000);})return await p}
}
</script>
<style>
.child{background-color: skyblue;padding: 10px;
}
</style>
其他
全局API的转移
. Vue 2.x有许多全局API和配置。
。例如:注册全局组件、注册全局指令等。
//注册全局组件
Vue. component( ' MyButton ', {
data: () =>({
count: 0}),
template: '<button @click="count++">clicked {{ count }} times.</button>'
})
//注册全局指令
Vue.directive( 'focus ', {
inserted : el => el.focus()
}
. Vue3.0中对这些API做出了调整:
。将全局的API,即:Vue.xxx调整到应用实例(app ) 上
2.x全局APl (vue ) => 3.x 实例APl( app)
Vue.config.xxxX => app.config.xxXx
Vue.config.productionTip => 移除
Vue.component => app.component
Vue.directive => app.directive
Vue.mixin => app.mixin
Vue.use => app.use
Vue.prototype => app.config.globalProperties
2.其他改变
. data选项应始终被声明为一个函数。
。过度类名的更改:
o Vue2.x写法
.v-enter,
.v-leave-to {
opacity: 0;
}
.v-leave,
.v-enter-to {
opacity: 1;
}
o Vue3.x写法
.v-enter-from,
.v-leave-to {
opacity: 0;
}
.v-leave-from,
.v-enter-to {
opacity: 1;
}
移除keyCode作为v-on 的修饰符,同时也不再支持config.keyCodes
移除v-on.native修饰符
移除了过滤器filter
. . . . . .
相关文章:
Vue3笔记——(尚硅谷张天禹Vue笔记)
Vue3 Vue3简介 1.Vue3简介 .2020年9月18日,Vue.js发布3.0版本,代号: One Piece(海贼王)。耗时2年多、2600次提交、30个RFC、600次PR、99位贡献者 . github上的tags地址: https://github.com/vuejs/vue-next/releases/tag/v3.0.0 2.Vue3带来了什么 .性能…...
正则表达式一小时学完
闯关式学习Regex 正则表达式,我感觉挺不错的,记录一下。 遇到不会的题,可以评论交流。 真的很不错 链接 Regex Learn - Step by step, from zero to advanced....
上门服务系统|上门服务小程序如何提升生活质量?
上门服务其实就是本地生活服务的升级,上门服务包含很多行业可以做的。例如:厨师上门、上门家电维修、跑腿等等。如今各类本地化生活服务越来越受大家的喜爱。基于此市场愿景,我们来谈谈上门服务系统功能。 一、上门服务系统功能 1、预约服务…...
系统报错msvcp120.dll丢失的解决方法,常见的三种解决方法
今天为大家讲述关于系统报错msvcp120.dll丢失的解决方法。在这个信息爆炸的时代,我们每个人都可能遇到各种各样的问题,而这些问题往往需要我们去探索、去解决。今天,我将带领大家走进这个神秘的世界,一起寻找解决msvcp120.dll丢失…...
数据库备份工具有哪些
本文主要介绍下数据库备份工具。 数据库备份工具有很多种,以下是一些常见的数据库备份工具: mysqldump:MySQL官方提供的命令行备份工具,适用于MySQL和MariaDB数据库。它可以将数据库导出为SQL文件,方便进行备份和恢复…...
Sentinel流量控制与熔断降级
📝 学技术、更要掌握学习的方法,一起学习,让进步发生 👩🏻 作者:一只IT攻城狮 ,关注我,不迷路 。 💐学习建议:1、养成习惯,学习java的任何一个技术…...
The Connector 周刊#10:你真的知道什么是DevOps文化吗?
AI 探索 用 LLM 构建企业专属的用户助手:很好的 LLM 应用工程实践,主要介绍了 PingCAP 如何使用大型语言模型(Large Language Model,LLM)构建一个搭载企业专属知识库的智能客服机器人。除了采用行业内通行的基于知识库…...
leetcode438. 找到字符串中所有字母异位词(java)
滑动窗口 找到字符串中所有字母异位词滑动窗口数组优化 上期经典 找到字符串中所有字母异位词 难度 - 中等 Leetcode 438 - 找到字符串中所有字母异位词 给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出…...
【锐捷】OSPF 多区域配置
【实验名称】 配置 OSPF 多区域。 【实验目的】 配置 OSPF 多区域,理解 OSPF 层次型网络的特点。 【背景描述】 本实验拓扑图中有 3 台路由器,路由器在区域 0 和区域 1 中,路由器 B 在区域 0 和区域 30, 路由器 C 在区域 30。 【需…...
Linux常用命令_权限管理命令
文章目录 1. 权限管理命令: chmod2. 其他权限管理命令2.1 权限管理命令: chown2.2 权限管理命令: chgrp2.3 权限管理命令: umask 1. 权限管理命令: chmod {ugoa}中分别为:u-user、g-group、a-all;谁创建文件,谁是所有者;所属组为所…...
【黑马头条之热点文章kafkaStream】
本笔记内容为黑马头条项目的热点文章-实时计算部分 目录 一、实时流式计算 1、概念 2、应用场景 3、技术方案选型 二、Kafka Stream 1、概述 2、Kafka Streams的关键概念 3、KStream 4、Kafka Stream入门案例编写 5、SpringBoot集成Kafka Stream 三、app端热点文章…...
【SpringSecurity】三、访问授权
文章目录 1、配置用户权限2、针对URL授权3、针对方法的授权 1、配置用户权限 继续上一章,给在内存中创建两个用户配置权限。配置权限有两种方式: 配置roles配置authorities //哪个写在后面哪个起作用 //角色变成权限后会加一个ROLE_前缀,比…...
你对SPA单页面的理解,它的优缺点分别是什么?如何实现SPA应用呢?
一、什么是SPA SPA(single-page application),翻译过来就是单页应用SPA是一种网络应用程序或网站的模型,它通过动态重写当前页面来与用户交互,这种方法避免了页面之间切换打断用户体验在单页应用中,所有必…...
【LeetCode75】第三十七题 二叉树中的最长交错路径
目录 题目: 示例: 分析: 代码: 题目: 示例: 分析: 给我们一棵二叉树,问我们在这棵树里能找到的最长交错路径。最长交错路径就是在二叉树里一左一右一左一右这样走,最…...
百度Apollo学习心得:探索自动驾驶技术的前沿之旅
文章目录 前言一、理论学习与实践结合二、多方资源的整合利用三、团队合作与交流分享四、持续学习与创新思维总结 前言 百度Apollo是一项引领自动驾驶技术发展的开放平台,通过深度学习、感知与决策、定位与控制等关键技术,为开发者提供了丰富的工具和资…...
kafka原理之springboot 集成批量消费
前言 由于 Kafka 的写性能非常高,因此项目经常会碰到 Kafka 消息队列拥堵的情况。遇到这种情况,我们可以通过并发消费、批量消费的方法进行解决。 一、新建一个maven工程,添加kafka依赖 <dependency><groupId>org.springframe…...
【GeoDa实用技巧100例】024:geoda计算全局(局部)莫兰指数Moran‘s I,LISA聚类地图,显著性地图
严重声明:本文及专栏《GeoDa空间计量案例教程100例》为CSDN博客专家刘一哥GIS原创,原文及专栏地址为:https://blog.csdn.net/lucky51222/category_12373659.html,谢绝转载或爬取!!! 文章目录 一、计算全局(或局部)单变量莫兰指数I1. 加载实验数据2. 加载权重矩阵3. 创建…...
Java进阶(7)——手动实现LinkedList 内部node类的实现 增删改查的实现 toString方法 源码的初步理解
目录 引出从ArrayList到Linkedlist手动实现ArrayList从ArrayList到LinkedList 总体设计Node类Node的方法:根据index找node 增删改查的实现增加元素删除元素修改元素查询元素 toString方法完整代码List接口类LinkedList的实现测试类 总结 引出 1.linkedList的节点&am…...
CPU总线的理解
目录 CPU总线CPU总线是什么?CPU总线可以分为前端部分和后端部分吗? CPU总线 CPU总线是什么? CPU总线(Central Processing Unit Bus)是计算机硬件中的一个重要组成部分,它是连接CPU和其他硬件组件的通道。…...
Spring Boot 中的 AOP,到底是 JDK 动态代理还是 Cglib 动态代理
大家都知道,AOP 底层是动态代理,而 Java 中的动态代理有两种实现方式: 基于 JDK 的动态代理 基于 Cglib 的动态代理 这两者最大的区别在于基于 JDK 的动态代理需要被代理的对象有接口,而基于 Cglib 的动态代理并不需要被代理对…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
