Vue3学习文档
铺垫p136 vue3简介文档tag版本-v3.0.0-这个一个比较重要的里程碑提升-相比于vue21.性能提升(打包、渲染……)2.源码升级(使用Proxy代替defineProperty实现响应式、重写虚拟DOM的实现和Tree-Shaking)3.拥抱ts4.新的特性tree shaking概念摇树-实际上就是把不要的代码移除vue3新的特性-其他改变1.新的生命周期钩子2.data 选项应
铺垫
p136 vue3简介
文档
- tag版本-v3.0.0-这个一个比较重要的里程碑
- 提升-相比于vue2
1.性能提升(打包、渲染……)
2.源码升级(使用Proxy代替defineProperty实现响应式、重写虚拟DOM的实现和Tree-Shaking)
3.拥抱ts
4.新的特性
- tree shaking概念
摇树-实际上就是把不要的代码移除
- 其他改变
1.新的生命周期钩子
2.data 选项应始终被声明为一个函数
3.移除keyCode支持作为 v-on 的修饰符 (就是没有这种写法了 )
P137 使用vue-cli创建工程
正文
vue create xxx
创建项目命令行
{}
- vue3脚手架要求-
- 运行vue3项目,发现调试工具不亮
是因为该调试工具是针对vue2的
P138 使用vite创建工程
文档
- 啥是vite
官网介绍:下一代的前端构建工具
- 构建工具历史
grunt
、gulp
、webpack
->vite
- vite优势
1.开发环境中,无需打包操作,可快速的冷启动。
2.轻量快速的热重载(HMR)。
3.真正的按需编译,不再等待整个应用编译完成。
- vite快的原因
如图所示,就是webpack是要引起全部文件打包再编译到服务器,而vite是先指定服务器然后动态引入文件
P:这块是随便写的,知识点有缘再看
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tzzAfIYP-1652603476757)(Images/image-20220127105608085.png)]
- 使用方式-命令行
## 创建工程
npm init vite-app <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev
- 目前还没有大规模应用-可能会有坑、所以教程还是用传统的vuecli
P:原来这个vite相当于是一种脚手架?
P139 分析工程结构
核心code
1.main.js
//引入的不再是Vue构造函数了,引入的是一个名为createApp的工厂函数
import { createApp } from 'vue'
import App from './App.vue'
//import Vue form 'vue'-控制台报错没有该对象,说明vue3不兼容vue2
//创建App实例对象——app(比vm更“轻”)
const app = createApp(App)
app.mount('#app')//挂载
//createApp(App).mount('#app')-简写形式
//unmount使用
setTimeout(()=>{
app.unmount("#app")
},1000)
//vue2版本
const vm=new Vue({
render:h=>h(App)
})
vm.$mount("#app")
2.vue.config.js
module.exports = {
lintOnSave:false, //关闭语法检查
}
{}
- vue3组件无需设置根标签
P140 安装开发者工具
文档
-
商城下载调试工具
-
Vue3调试工具是测试版,没有vuex
P141 初始setup
setup是所有Composition API(组合API)“ 表演的舞台 ”
核心code
1.template
<template>
<h1>一个人的信息</h1>
<h2>姓名:{{name}}</h2>
<h2>性别:{{sex}}</h2>
<button @click="sayHello">说话(Vue3所配置的——sayHello)</button>
<button @click="sayWelcome">说话(Vue2所配置的——sayWelcome)</button>
<button @click="test1">测试一下在Vue2的配置中去读取Vue3中的数据、方法</button>
<button @click="test2">测试一下在Vue3的setup配置中去读取Vue2中的数据、方法</button>
</template>
2.vue2
//vue2的语法还是可以正常使用-以后可能会去除
data() {
return {
sex:'男',
}
},
methods: {
sayWelcome(){
alert('欢迎来到尚硅谷学习')
},
test1(){
console.log(this.name) //vue2中能拿到vue3的配置
console.log(this.sayHello)
}
},
3.vue3
// import {h} from 'vue'
export default {
setup(){
let name = '张三'
function sayHello(){
alert(`我叫${name},你好啊!`)
//注:这里的name不需要想vue2一样加“this”-都在setup函数下
}
function test2(){
console.log(name)
console.log(sayHello)
console.log(this.sex) //vue3拿不到vue2的配置
console.log(this.sayWelcome)
}
//return一个对象,上面的模板才能使用!!
return {
name,
sayHello,
test2,
}
//return一个函数(渲染函数)-需要引入h,函数回覆盖return对象
// return ()=> h('h1','尚硅谷')
}
}
{}
- vue2和vue3不用混用
因为vue3不能读到vue2参数,而且两者定义相同参数名会冲突-取vue3
- setup通常不能是一个async函数(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)
因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。
P:不要为了使用await,给整个setup()加上async
- Composition(组合式) API-是vue3中的很重要的概念
ref函数
P142 处理基本类型-
P143 处理对象类型-
文档
-
作用:定义一个响应式的数据
-
基本类型-接收数据
原理和vue2相同,都是用到了Object.defineProperty
中的get 和set 。
get set藏在原型对象中,并把value拿出-方便使用
- 对象类型-接收数据
对象地址用的是数据截持,但对于里面的属性用的是更高级的proxy 。
ref对象类型的数据:内部 “ 求助 ” 了Vue3.0中的一个新函数—— reactive
函数。
reactive里面用到了proxy
核心code
<h1>一个人的信息</h1>
<h2>姓名:{{name}}</h2> //和vue2相同不需要value-模板已经做了一层处理、用了反而报错
<h3>工作种类:{{job.type}}</h3> //通俗用法
<h3>工作薪水:{{job.salary}}</h3>
<button @click="changeInfo">修改人的信息</button>
import {ref} from 'vue' //引入ref
setup(){
let name = ref('张三') //使用ref让数据变响应式
let job = ref({ //ref创建对象类型数据
type:'前端工程师',
salary:'30K'
})
function changeInfo(){
name.value = '李四' //数据修改要加".vlaue"
console.log(job.value)
job.value.type = 'UI设计师' //对象类型修改不用套.value
job.value.salary = '60K'
}
return {
name,
job,
changeInfo
}
}
{}
-
reflmpl单词解释
-
let name = ref(‘张三’) 是啥
创建的不是简单类型,而是一个refImpl对象-引用实现的实例
简称:引用对象
P144 reactive函数
正文
-
定义一个对象类型的响应式数据(基本类型不要用它,要用
ref
函数) -
reactive语法:const 代理对象= reactive(源对象)
接收一个对象(或数组)-源对象,返回一个代理对象(Proxy的实例对象,简称proxy对象)
-
reactive定义的响应式数据是“深层次的”。
-
内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
核心code
<h1>一个人的信息</h1>
<h2>姓名:{{person.name}}</h2>
<h3>工作种类:{{person.job.type}}</h3>
<h3>工作薪水:{{person.job.salary}}</h3>
<h3>爱好:{{person.hobby}}</h3>
<button @click="changeInfo">修改人的信息</button>
import {reactive} from 'vue' //引入reactive
export default {
name: 'App',
setup(){
//let name=ref("张三") //如果是基本类型只能用ref来响应
let person = reactive({
//可以把基本类型写在一个!对象里-作属性,这样就可以用reactive来响应了
name:'张三',
job:{
type:'前端工程师',
salary:'30K',
},
hobby:['抽烟','喝酒','烫头']
})
function changeInfo(){
person.name = '李四'
person.job.type = 'UI设计师'
//之前用ref()时,修改写法:person.value.job.type,如果是reactive则不用
person.job.salary = '60K'
person.hobby[0] = '学习'
}
return {
person, //数据封装到一个对象里,return也能少写点
changeInfo
}
}
}
Vue3响应式原理
P145 回顾Vue2的响应式原理
核心code
<h2>{{person.name}}</h2>
<h2>{{person.age}}</h2>
<button @click="addSex">添加一个sex属性</button>
<button @click="deleteName">删除name属性</button>
return{
person:{
name:'张三',
age:18,
}
}
import Vue form 'vue'
methods:{
addSex(){
//this.person.sex='女'-这种普通方法修改,页面不会更新
this.$set(this.person,'sex','女')
Vue.set(this.person,'sex','女')//功能同上-引入Vue
}
deleteName(){
//delete this.person.name-这种普通方法删除,页面不会更新
this.$delete(this.person,'name','李四')
Vue.delete(this.person,'name','李四')
}
}
正文
-
实现原理:
对象类型:通过
Object.defineProperty()
对属性的读取、修改进行拦截(数据劫持)。数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
Object.defineProperty(data, 'count', {
get () {},
set () {}
})
-
存在问题:
新增属性、删除属性, 界面不会更新。
直接通过下标修改数组, 界面不会自动更新。
P146 Proxy
code
//传送中的源对象
let person = {
name:'张三',
age:18
}
2.Vue2
let p = {} //传说中的代理对象
Object.defineProperty(p,'name',{
configurable:true, //定义了这个配置defineProperty才能删除-return true
get(){
return person.name
},
set(value){
console.log('更新界面!')
person.name = value
}
})
Object.defineProperty(p,'age',{
get(){
return person.age
},
set(value){
console.log('更新界面!')
person.age = value
}
})
3.vue3
const p = new Proxy(person,{ //p是代理数据,person是源数据
//读取p的某个属性
get(target,propName){
return target[propName]//注:不能.propName因为propName是变量,只能用[]
},
//修改p的某个属性、或给p追加某个属性时
set(target,propName,value){
console.log(`更新界面!`)//-这里实际要写真正的更新代码
target[propName]=value
},
//删除p的某个属性时
deleteProperty(target,propName){
console.log(`更新界面!`)
return delete target[propName]//delete是带返回值的-是否成功删除
}
})
//#endregion
正文
const p = new Proxy(person,{})
上面这段如果第二个参数为空,就只能实现数据代理,并不能实现响应式
还需要在里面写内容来实现响应式的修改-get()、set()
other
- reacitve进步点
可以直接进行对象属性的删除修改,页面也会更新
- 可通过下标操作数组
P147 Reflect
核心代码
//传送中的源对象
let person = {
name:'张三',
age:18
}
1.上述例子使用上述例子使用Reflect代替
const p = new Proxy(person,{ //p是代理数据,person是源数据
// 拦截读取属性值
get(target,propName){
return Reflect.get(target,propName)
//用Reflect代替对象操作更有利于封装-不会报错
},
// 拦截设置属性值或添加新属性
set(target,propName,value){
console.log(`更新界面!`)//-这里实际要写真正的更新代码
Reflect.set(target,propName,value)
},
// 拦截删除属性
deleteProperty(target,propName){
console.log(`更新界面!`)
return Reflect.deleteProperty(target,propName)
}
})
//#endregion
2.使用Reflect的原因
let obj = {a:1,b:2}
//Object.defineProperty-应该是obj.c的全写
try { //如果使用defineProperty必须要这样接收报错,不然项目无法启动
Object.defineProperty(obj,'c',{
get(){
return 3
}
})
Object.defineProperty(obj,'c',{ //虽然这样写是错的-但也不好影响项目运行
get(){
return 4
}
})
} catch (error) {
console.log(error)
}
//Reflect.defineProperty
const x1 = Reflect.defineProperty(obj,'c',{ //Reflect有布尔返回值而不是报错
get(){
return 3
}
})
const x2 = Reflect.defineProperty(obj,'c',{
get(){
return 4
}
})
if(x2){ //比起上面的try,这样的判断更好做大的框架封装
console.log('操作成功了!')
}else{
console.log('操作失败了!')
}
正文
- 实现原理
通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
通过Reflect(反射): 对源对象的属性进行操作。
- Reflect(反射)
这个东西的window的内置对象,es6新语法
简单用法:Reflect.set(obj,'a',666)
others
- 为什么好好的
obj.a
不用,要用Reflect.set(obj,'a')
来拿属性值?
这个因为Reflect有我们看不到的优点,ECMA已经准备把Object的属性移植到window的Reflect属性上
P148 reactive对比ref
code
//可以在所有数据外面套个data,这样基本数据也可以使用reactive
let data =reactive({ //像vue2一样用data来包装所有数据
aa:'11'
person:{
name:"张三"
}
}),
funtion change(){
data.person.name="张三" //使用reactive就不需要用data.value.person.name这种麻烦写法了
//p:但是这样同时每个数据都要加个前缀-data,也挺麻烦的
}
return {
data //这里也可以少写数据
}
正文
- 从定义数据角度对比:
- ref用来定义:基本类型数据。
- reactive用来定义:对象(或数组)类型数据。
- 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过reactive转为代理对象。
- 从原理角度对比:
- ref通过Object.defineProperty()的
get
与set
来实现响应式(数据劫持)。 - reactive通过使用Proxy的
get
与set
来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
- ref通过Object.defineProperty()的
- 从使用角度对比:
- ref定义的数据:操作数据需要**.value**,读取数据时模板中直接读取不需要**.value**。
- reactive定义的数据:操作数据与读取数据:均不需要**.value**。
P149 setup的两个注意点(参数和时机)
code
1.App.vue-父组件
<Demo @hello="showHelloMsg" msg="你好啊" school="尚硅谷">
<template v-slot:qwe>
<!-- v-slot:qwe在子组件的content.slots有对应展示-->
<span>尚硅谷</span>
</template>
</Demo>
<script>
export default {
setup(){
function showHelloMsg(value){
alert(`你触发了hello事件,${value}!`)
}
return {
showHelloMsg
}
}
}
</script>
2.Demo.vue-子组件
<button @click="test">触发Hello事件</button>
<script>
export default {
props:['msg','school'],
emits:['hello'], //可以不加但会警告
setup(props,context){
// context.attrs-Vue2中的this.$attrs
// context.emit-this.$emit
// context.slots-this.$slots
console.log(context.slots)
//输出结果-Proxy{_:1,_vInternal:1,qwe:f}
function test(){
context.emit('hello',666)
}
return {
test
}
}
}
</script>
正文
- 文档
setup执行的时机
- 在beforeCreate之前执行一次,this是undefined。
setup的参数!!
- props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
- context:上下文对象
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
this.$attrs
。 - slots: 收到的插槽内容, 相当于
this.$slots
。 - emit: 分发自定义事件的函数, 相当于
this.$emit
。
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
other
- vue2中vc. a t t r s ∗ ∗ 和 ∗ ∗ attrs**和** attrs∗∗和∗∗slots
组件实例对象(vc)没有用props接收父组件属性,那么组件的**$attrs也会收到传递属性**,只不过不能进行限制
组件实例对象(vc)会用**$slots接收插槽对象**
计算属性
P150 computed计算属性
code
import {reactive,computed} from 'vue'
setup(){
let person = reactive({
firstName:'张',
lastName:'三'
})
person.fullName = computed(()=>{ //简写-属性只读不能修改
//P:为什么都是reactive对象里的属性了,还不能修改呢??
return person.firstName + '-' + person.lastName
})
person.fullName = computed({//完整-属性可读可写
//person.fullName-体现了reactive可增加响应式属性的好处,如果是之前这样加属性不会相应
get(){
return person.firstName + '-' + person.lastName
},
set(value){
const nameArr = value.split('-') //split-对字符串切片
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
return {
person //一般数据只输出一个
}
}
computed:{
fullName(){
return this.person.firstName+'-'+this.person.lastName
//在setup外部需要使用this
}
}
监听属性
P151 watch监听ref定义的数据
code
1.vue2
watch(){
sum:(newV,oldV){
console.log(newV,oldV)
}
sum:{
immediate:true,
handler(newV,oldV){
console.log(newV,oldV)
}
}
}
2.vue3
import {ref,reactive,watch} from 'vue'
setup(){
let sum = ref(0)
let msg = ref('你好啊')
let person =ref({
name:'张三'
})
//监视一个-ref
watch(sum,(newValue,oldValue)=>{
console.log('sum变了',newValue,oldValue) //1,0
},{immediate:true}) //第三个参数写配置
//监视多个-ref
watch([sum,msg],(newValue,oldValue)=>{
console.log('sum或msg变了',newValue,oldValue)//[1,'你好啊!'],[0,'你好啊']
})
//监视对象-ref
watch(person.value,(newValue,oldValue)=>{ //注:如果ref是对象要加.value
console.log('person变了',newValue,oldValue)//{name:'张三'},[name,'张三']-oldV监听不到
})
}
P152 watch监听reactive定义的数据
code
<button @click="sum++">点我+1</button> //简单方法可以直接写在模板里-sum++
2.vue3
import {reactive,watch} from 'vue'
setup(){
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
//对象的全部属性-reactive
watch(person,(newValue,oldValue)=>{
console.log('person变化了',newValue,oldValue)//!!无法正确获取oldValue
}) //强制开启了深度监视(deep配置无效)
//对象的单个属性(监听oldV)-reactive
watch(()=>person.name,(newValue,oldValue)=>{//需要写成函数形式
console.log('person的name变化了',newValue,oldValue)
}) //能获取到oldV且不用设置deep-因为这里本质监听的是基本数据
//对象的多个属性(监听oldV)-reactive
watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{//列表函数形式
console.log('person的name或age变化了',newValue,oldValue)
})
//对象的单个d属性(对象、监听oldV)-reactive
watch(()=>person.job,(newValue,oldValue)=>{
console.log('person的job变化了',newValue,oldValue)
},{deep:true}) //监视的reactive对象属性值还是对象-deep配置有效
}
P153 watch时value的问题
code
import {ref,watch} from 'vue'
let sum = ref(0)
let person =ref({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
watch(sum,(……)=>{
console.log('监听sum第一层属性')
})
watch(person.value,(……)=>{
console.log('监听person.value(Proxy)属性')
})
watch(person,(……)=>{
console.log('深度监听person所有属性')
},{deep:true})
正文
- watch ref 基本类型时不加value
watch(sum,(……)=>{}
-可监听到sum的属性更改,但watch(sum.value,(……)=>{}
会报错,因为.value会变成监听数值
- watch ref 对象注意点
由于监听ref只是监听ref对象的第一层属性,所以只能监听value值变化(整个Proxy),但是像value属性里面的二级属性(value.name:张三)就监听不到了
所以需要使用person.value
监听value(Proxy)
也可以使用深度监视ref-watch(person,(……)=>{},{deep:true})
来实现对ref下所有属性的监视
P154 watchEffect函数
code
import { ref, reactive, watchEffect } from "vue";
let sum = ref(0)
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
watchEffect(()=>{
//watchEffect回调中用到的数据发生变化,就重新执行回调;且默认为立即监听
const x1 = sum.value
const x2 = person.job.j1.salary
console.log('watchEffect配置的回调执行了')
})
文档
- 文档
watch的套路是:既要指明监视的属性,也要指明监视的回调。
watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
watchEffect有点像computed:
- 但computed 注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
- 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
生命周期
P155 Vue3生命周期
code
import {ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'
setup(){
console.log('---setup---') //对应create相关配置项-且比beforeCreate等优先级高
//通过组合式API的形式去使用生命周期钩子-按顺序展示
onBeforeMount(()=>{
console.log('---onBeforeMount---') //所有的onxx都比配置项xx优先级高
})
onMounted(()=>{
console.log('---onMounted---')
})
onBeforeUpdate(()=>{
console.log('---onBeforeUpdate---')
})
onUpdated(()=>{
console.log('---onUpdated---')
})
onBeforeUnmount(()=>{
console.log('---onBeforeUnmount---')
})
onUnmounted(()=>{
console.log('---onUnmounted---')
})
},
//通过配置项的形式使用生命周期钩子-按顺序展示
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---')
},
正文
- 生命周期图
- 文档
- Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:
beforeDestroy
改名为 beforeUnmount
destroyed
改名为 unmounted
- Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
beforeCreate
===>setup()
P:setup本身**就是create,**所以这里就没有对应钩子了
created
=======>setup()
beforeMount
===>onBeforeMount
mounted
=======>onMounted
beforeUpdate
===>onBeforeUpdate
updated
=======>onUpdated
beforeUnmount
==>onBeforeUnmount
unmounted
=====>onUnmounted
- 生命周期两种方式
- 配置项-
mounted() {}
- 组合API-
onMounted(() => {})
other
- 生命周期可以按之前的写法,不一定要写在setup中
P156 自定义hook
code
1.hooks-usePoint.js //像这样把组合api抽离成一个方法-便于复用
import {reactive,onMounted,onBeforeUnmount} from 'vue'
export default function (){ //默认暴露简写-单个内容、不用写方法名
let point = reactive({
x:0,
})
function savePoint(event){
point.x = event.pageX
}
onMounted(()=>{
window.addEventListener('click',savePoint) //window绑定事件-全屏点击都有效
})
onBeforeUnmount(()=>{ //如果绑定事件不需要了记得卸载-毕竟window是全局的
window.removeEventListener('click',savePoint)
})
return point
}
2.test.vue-components
//组件引用hooks
<h2>当前点击时鼠标的坐标为:x:{{point.x}},y:{{point.y}}</h2>
import usePoint from '../hooks/usePoint'
export default {
name:'Test',
setup(){
const point = usePoint()
return {point}
}
}
正文
- hoos文件创建例子-和components同级
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c0B3E6Y4-1652603476759)(Images/image-20220216104549472.png)]一般会加个use前缀
- 文档
什么是hook?—— 本质是一个函数,把setup函数中使用的Composition API进行了封装。
类似于vue2.x中的mixin。
自定义hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。
P157 toRef与toRefs
code
1.Demo.vue
<h4>{{person}}</h4>
<h2>姓名:{{name}}</h2>
<h2>薪资:{{job.j1.salary}}K</h2>
<button @click="name+='~'">修改姓名</button>
<button @click="job.j1.salary++">涨薪</button>
import {ref,reactive,toRef,toRefs} from 'vue'
setup(){
let person = reactive({
name:'张三',
job:{
j1:{
salary:20
}
}
})
//const name = toRefs(person,'name')-也可以这样用、一般是直接return写好
//const x = toRefs(person)
return {
person,
name:toRef(person,'name'),//单个属性输出、赋值的是ref对象有用
salary:toRef(person.job.j1,'salary'),
...toRefs(person)//多个属性输出-只输出对象子层属性
//<错误写法
// name:person.name-无效、因为这只是赋值了一个简单类型又不是ref对象
// name:ref(person.name)-无效、相当于new了个ref对象、和reactvie里的没有关系
//<
}
}
P:个人感觉toref需要配合reactive使用,毕竟ref不用再toref了,然后toref主要的作用是简写 多层的reactive对象,貌似没 别的用处
正文
- toRef实例结果-实际上是RefImpl响应式对象
- toRef实例结果
- 文档
作用:创建一个 ref 对象,其value值指向另一个对象中的某个属性。
语法:const name = toRef(person,'name')
应用: 要将响应式对象中的某个属性单独提供给外部使用时。
扩展:toRefs
与toRef
功能一致,但可以批量创建多个 ref 对象,语法:toRefs(person)
other
- toRef这种作用禹神形容成桥接
P158 shallow(浅)Reactive与shallowRef
code
let person = shallowReactive({ //只考虑第一层数据的响应式
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
let x = shallowRef(0)//这样写没有意义,shallowRef针对的是ref一个对象形式
let x = shallowRef({ //修改x.y不响应,修改整个响应
y:0
})
正文
- 文档
shallowReactive:只处理对象 最外层属性的响应式(浅响应式)。
shallowRef:只处理基本数据类型的响应式, 不进行 对象的响应式处理。
什么时候使用?
- 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。
- 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。
P159 readonly与shallowReadonly
-code 5:50控制台警告 readonly的意义和const普通区别 使用场景
code
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
person = readonly(person) //数据只读不能修改
person = shallowReadonly(person)//数据的第一层只读不能修改
正文
- 修改readonly定义数据控制台输出
- 为什么不用普通const代替readonly
使用const 声明一个普通对象的效果也是可读修改属性模板不响应,但是这个是治标不治本,因为本质上const声明的对象还是改变了,只是模板不响应罢了,但是readonly是让对象都不能修改,修改会警告。
- readonly的使用场景-当他人的组件 返回数据 只读不想让你修改时
P160 toRaw与markRaw
code
<button @click="addCar">给人添加一台车</button>
<button @click="person.car.name+='!'">换车名</button> //后加属性也是响应式的
let sum = ref(0)
let person = reactive({
name:'张三',
age:18,
job:{
j1:{
salary:20
}
}
})
function showRawPerson(){
//const s = toRaw(sum)-无效,toRaw只对reactive对象使用
const p = toRaw(person)
//p.age++ -不生效,因为p toRaw后只是一个普通对象
console.log(p)
}
function addCar(){ //添加一个属性且该属性为响应式的-因为Proxy牛逼
let car = {name:'奔驰',price:40}
person.car = markRaw(car) //让数据变为不可响应式-markRaw
//markRaw的使用场景-见正文文档
}
return {
...toRefs(person), //toRefs不作用于后面添加的属性-car
person,//模板用到后加属性(car),还需return person
}
正文
- 文档
toRaw:
- 作用:将一个由reactive生成的响应式对象转为普通对象。
- 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
markRaw:
- 作用:标记一个对象,使其永远不会再成为响应式对象。
- 应用场景:
- 有些值不应被设置为响应式的,例如复杂的第三方类库等。
- 当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
P161 customRef
code
<template>
<input type="text" v-model="keyword">
<h3>{{keyword}}</h3>
</template>
<script>
import {ref,customRef} from 'vue'
export default {
name:'Demo',
setup(){
// let keyword = ref('hello') //使用Vue准备好的内置ref
//自定义一个myRef
function myRef(value,delay){
let timer
//通过customRef去实现自定义
return customRef((track,trigger)=>{
return{
get(){ //写法和计算属性很像
track() //告诉Vue这个value值是需要被“追踪”的
return value
},
set(newValue){
clearTimeout(timer) //需要关闭定时器-不然输入队列内容太多会乱
timer = setTimeout(()=>{
value = newValue
//value是传参,为什么能再传给get()??可能和回调有关
trigger() //告诉Vue去更新界面
},delay) //!实现了节流
}
}
})
}
let keyword = myRef('hello',500) //使用程序员自定义的ref
return {
keyword
}
}
}
</script>
正文
- 文档
创建一个自定义的 ref,并对其依赖项跟踪和更新触发进行显式控制。
p:就是让ref使用更自由,可以写自定义逻辑,如节流等
P162 provide(提供)与inject(注入)
code
1.grandf.vue
<h3>{{car.name}}--{{car.price}}</h3>
import { toRefs,provide } from 'vue'
export default {
setup(){
let car = reactive({name:'奔驰',price:'40W'})
provide('car',car) //给自己的后代组件传递数据
return {...toRefs(car)} //toRefs貌似是把reactive“拆分”
}
}
2.son.vue
<h3>{{car.name}}--{{car.price}}</h3>//没用torefs的话只能正常拿
import {inject} from 'vue'
export default {
setup(){
let car = inject('car')
return {car}
}
}
other
- toRefs作用-把reactive数据 “拆分”
<h3>{{name}}--{{price}}</h3> //toRefs拆分后模板可以直接使用对象属性
import { toRefs } from 'vue'
export default {
setup(){
let car = reactive({name:'奔驰',price:'40W'})
return {...toRefs(car)}
}
}
P163 响应式数据的判断
code
let car = reactive({name:'奔驰',price:'40W'})
let sum = ref(0)
let car2 = readonly(car) //创建一个只读的reactive
console.log(isRef(sum)) //以下都为true
console.log(isReactive(car))
console.log(isReadonly(car2))
console.log(isProxy(car))
console.log(isProxy(sum)) //false
正文
- 文档
isRef: 检查一个值是否为一个 ref 对象
isReactive: 检查一个对象是否是由 reactive
创建的响应式代理
isReadonly: 检查一个对象是否是由 readonly
创建的只读代理
isProxy: 检查一个对象是否是由 reactive
或者 readonly
方法创建的代理
P164 CompositionAPI的优势
code
1.usePoint.js //组合api
import {reactive,onMounted,onBeforeUnmount} from 'vue'
//实现窗口window点击打印定位功能
export default function (){
let point = reactive({
x:0,
})
function savePoint(event){
point.x = event.pageX
console.log(event.pageX)
}
onMounted(()=>{
window.addEventListener('click',savePoint)
})
onBeforeUnmount(()=>{
window.removeEventListener('click',savePoint)
})
return point //必须return出去
}
2.Demo.vue //某个子组件
import usePoint from '../hooks/usePoint'
export default {
setup(){
let point = usePoint()
return {point}
}
}
正文
- 文档
1.Options API 存在的问题
使用传统OptionsAPI(配置项)中,新增或者修改一个需求,需要分别在data,methods,computed里修改
2.Composition API 的优势
我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序(集中)的组织在一起
P165 Fragment组件(片段)
正文
- 文档
在Vue2中: 组件必须有一个根标签
在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个**Fragment ** 虚拟元素中
好处: 减少标签层级, 减小内存占用
P166 Teleport组件(传送)
code
<button>点我弹个窗</button>
<teleport to="body"> <!-- 传送到body标签下-这样定位就有个稳定基线了 -->
<!-- <teleport to="body">-也可以指定id跳转、但是cli貌似一般只有一个id(app) -->
<div>……我是一个弹窗……</div>
</teleport>
正文
- 文档
什么是Teleport?—— Teleport
是一种能够将我们的组件html结构移动到指定位置的技术。
other
- 绝对定位居中-transform技巧
.dialog{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);/* 不清楚需不需要高宽确定 */
width: 300px;
height: 300px;
}
P167 Suspense组件(悬念)
code
1.App.vue
<Suspense> <!-- 使用Suspense实现loading加载 -->
<template v-slot:default>
<Child/>
</template>
<template v-slot:fallback>
<h3>稍等,加载中...</h3>
</template>
</Suspense>
// import Child from './components/Child'//静态引入
import {defineAsyncComponent} from 'vue'
const Child = defineAsyncComponent(()=>import('./components/Child')) //异步引入
//异步虽然组件能分开加载了,但往往还不过-需要再加一个loading组件
export default {
name:'App',
components:{Child},
}
2.Child.vue
async setup(){ //实际上setup是可以加async-前提是在Suspense里,不然不显示
let sum = ref(0)
let p = new Promise((resolve,reject)=>{ //简单Promise写法-还是要自己有缘实践下
setTimeout(()=>{
resolve({sum})
},3000)
})
return await p
}
正文
- 异步组件作用
多层嵌套组件要等最慢的组件的加载完才出现,如果组件加载时间要很长的话,白屏会给用户不好的体验
如果时异步组件的话,就先展示加载好的组件,对于没有加载好的组件还可以用**“加载中组件”提示**,大大提高用户的体验和耐心
- defineAsyncComponent(异步引入组件)和Suspense(悬疑)配合使用才有意义
P168 Vue3中其他的改变
-弹幕 script setup $
正文
- 文档
data选项应始终被声明为一个函数。
过度类名的更改:Vue2.md-P91
-
Vue2.x写法
.v-enter, .v-leave-to { opacity: 0; } .v-leave, .v-enter-to { opacity: 1; }
-
Vue3.x写法
.v-enter-from, .v-leave-to { opacity: 0; } .v-leave-from, .v-enter-to { opacity: 1; }
移除keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes
p:@keyup.13
移除v-on.native
修饰符 p:native在vue2中作用是让@click成为组件本身事件,而不是自定义事件
-
父组件中绑定事件
<my-component v-on:close="handleComponentEvent" v-on:click="handleNativeClickEvent" />
-
子组件中声明自定义事件
<script> export default { emits: ['close'] } </script>
移除过滤器(filter)
过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性去替换过滤器。
…
})
return await p
}
`正文`
- 异步组件作用
多层嵌套组件要等**最慢的组件**的**加载完**才出现,如果组件加载时间要很长的话,**白屏**会给用户**不好**的体验
如果时异步组件的话,就**先**展示**加载好**的组件,对于**没有加载**好的组件还可以用**“加载中组件”提示**,大大**提高**用户的体验和耐心
<img src="Images/image-20220216152553767.png" alt="image-20220216152553767" align='left' style="zoom:50%;" />
- **defineAsyncComponent**(异步引入**组件**)和**Suspense**(悬疑)**配合使用**才有意义
### P168 Vue3中其他的改变
-弹幕 script setup $
`正文`
- 文档
**data**选项应**始终**被声明为一个**函数**。
过度类名的更改:[Vue2.md-P91](..\..\..\学习\Vue\VueJs-B站(张天禹)\P40-P95(Vue2).md)
- **Vue2**.x写法
```css
.v-enter,
.v-leave-to {
opacity: 0;
}
.v-leave,
.v-enter-to {
opacity: 1;
}
-
Vue3.x写法
.v-enter-from, .v-leave-to { opacity: 0; } .v-leave-from, .v-enter-to { opacity: 1; }
移除keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes
p:@keyup.13
移除v-on.native
修饰符 p:native在vue2中作用是让@click成为组件本身事件,而不是自定义事件
-
父组件中绑定事件
<my-component v-on:close="handleComponentEvent" v-on:click="handleNativeClickEvent" />
-
子组件中声明自定义事件
<script> export default { emits: ['close'] } </script>
移除过滤器(filter)
过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性去替换过滤器。
…
更多推荐
所有评论(0)