第六章 Vue-route
只有配置路由规则的时候是routes 其他都是router
1.route的简介
1.它是Vue的一个插件库,专门用来实现SPA应用的
spa (single page web application)单页面应用
2.SPA应用的理解
它是一个单页面应用
他只有一个index页面
在页面中点击导航链接不会刷新,只会局部更新
通过AJAX请求获取数据
3.什么是路由
路由就是一组映射关系 key - value的
key为路径 value可以是组件或者方法
后端路由就是匹配方法
前端路由就是匹配组件
2.路由的基本使用
1.因为路由是一个插件 ,需要安装使用
npm i vue-router@3
2.引入并且使用Vue的路由器
那么在vm上会多出来一个配置 router:
// 映入Vue的路由器
import vueRouter from 'vue-router'
// 使用路由器插件
Vue.use(vueRouter)
3.在src/router—index.js 配置路由器router
// 引入vue-router 它得到的是一个构造函数
import vueRouter from 'vue-router'
import About from '../components/About'
import Home from '../components/Home'
// 暴露配置的路由器 到vm中
export default new vueRouter({
// 在这个构造函数中配置一个 routes
routes: [ // 配置一个一个对象 中包含 path路由key 和 component 路由value
{
path:'/about',
component:About
},
{
path:'/home',
component:Home
}
]
})
4.在main.js的vm中 引入index.js 配置 router
import router from './router'
new Vue({
el:'#app',
render: h =>h(App), // 会覆盖 index上面的 <div id="app"></div>
router // 配置 router
})
5.在需要路由的地方 用 <router-link 标签 to 到路径 这个标签会自动转化为a标签 实现路由key的切换
<!-- router-link这个标签 会自动转化为a标签 -->
<router-link class="list-group-item" active-class="active" to="/about">About</router-link>
<router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
6.在要使用到路由变化组件的地方 使用 <router-view 来站位
<router-view></router-view>
3.Vue-router的几个注意点
1.一般会多写一个文件夹放路由组件,而一般组件会放在components上
2.切换掉的路由默认是被销毁了
3.被router管理的组件上,会多出一个$route属性和 $ router属性
route属性是一个组件自己的路由信息,而router是全部路由的信息
4.vue-router的多级路由 使用children[]来实现任意多级嵌套
1.需要在一个路由对象下使用children[{},{}]继续配置子路由 但是不用加/了
routes: [ // 配置一个一个对象 中包含 path路由key 和 component 路由value
{
path:'/about',
component:About
},
{
path:'/home',
component:Home,
// 多级路由 可以使用children配置一个对象数组
children:[
{
path:'news',
component:News
},
{
path:'message',
component:Message
}
]
}
]
2.在router-link的时候 to的地址写全。还是用router-view站位
<ul class="nav nav-tabs">
<li>
<!-- <a class="list-group-item" href="./home-news.html">News</a> -->
<!-- 使用router-link 代替a标签 -->
<router-link class="list-group-item" active-class="active" to="/home/news">News</router-link>
</li>
<li>
<!-- <a class="list-group-item active" href="./home-message.html">Message</a> -->
<router-link class="list-group-item" active-class="active" to="/home/message">Message</router-link>
</li>
</ul>
<router-view></router-view>
5.路由之间query传递参数,参数放在$route.query上(一)
1.在to路径上传递参数
<ul>
<li v-for="m in messages" :key="m.id">
<!-- 使用query类型来传递参数 就是路径后面?传参 -->
<router-link to="/home/message/list?name=战三&age=20">{{ m.title }}</router-link>
<!-- 如果要使用变量传递参数 就要用 :绑定为js表达式 在用``中的${使用变量}-->
<router-link :to="`/home/message/list?id=${m.id}&title=${m.title}`">{{ m.title }}</router-link>
<!-- 使用对象传递参数:to -->
<router-link :to="{
path:'/home/message/list',
query:{
id:m.id,
title:m.title
}
}">{{ m.title }}</router-link>
</li>
2.参数被传递vc.$route.query上
6.路由命名简化复杂路径
1.在routes中 路由配置多一个 name配置项 配置名字
children:[
{
name:'liebiao',
path:'list',
component:List
}
]
2.在使用to的时候 要成对象形式 :to{name:‘name’}
<router-link :to="{
// path:'/home/message/list',
name:'liebiao', // 使用name来代替路径
query:{
id:m.id,
title:m.title
}
}">{{ m.title }}</router-link>
7.路由之间传递参数params,to对象的形式必须是name
1.在routes中配置的时候,需要在path后面接收参数 /a/:id/:title
children:[
{
name:'liebiao',
path:'list/:id/:title', // 使用params传递参数 这里站位符指定参数名
component:List
}
]
2.在to传递参数的时候 直接写在路径上那么可以直接传递值 /a/0/fa
<!-- 如果要使用变量传递参数 就要用 :绑定为js表达式 在用``中的${使用变量}
params 传递参数 直接 /值/值-->
<router-link :to="`/home/message/list/${m.id}/${m.title}`">{{ m.title }}</router-link>
如果是to对象 那么它必须是name 和使用params对象传递参数
<router-link :to="{
// path:'/home/message/list',
name:'liebiao', // 使用name来代替路径
params:{ // 使用params 那么不能是path 必须是name
id:m.id,
title:m.title
}
}">{{ m.title }}</router-link>
3.使用$route.params.参数名 来访问参数
8.路由的props配置 简化组件参数的接收
在routes 中新增一个props配置项 他有三种写法
- 对象写法{} ,他的keyvalue都会以props的形式传递给组件
children:[
{
name:'liebiao',
path:'list/:id/:title', // 使用params传递参数 这里站位符指定参数名
component:List,
props:{id:1,title:'你好'} //对象写法{} ,他的key-value都会以props的形式传递给组件
}
]
2.props:true // 布尔值 为true的时候 那么这个params参数 会议props的形式传递给组件
<router-link :to="{
// path:'/home/message/list',
name:'liebiao', // 使用name来代替路径
params:{ // 使用params 那么不能是path 必须是name
id:m.id,
title:m.title
}
}">{{ m.title }}</router-link>
3.函数的形式,他会收到一个route对象 返回一个对象 这个对象会以props的形式传递给组件
props($route){
return {
id:$route.params.id,
title:$route.params.title
}
}
注意使用params传递参数的时候 在path上要写:/id站位参数
9.浏览器历史记录模式 push replace(替换)
1.浏览器是以栈的方式去存储 url资源页面
2.默认的router-link 是以push的形式跳转页面 ,也就是说往浏览器历史记录中压栈,然后指针指向栈顶元素,可以回退
3.在route-link中开启replace模式 那么他就会替换栈顶元素 ,而不是压栈
<router-link :replace="true"
10.编程式路由导航 $router.push()/replace()
1.push方法和replace方法
methods:{
pushShow(m){
// 编程式的路由导航 由$router原型对象上的东西 push 、replace
this.$router.push({
// 这个对象中的配置和to中的一样
name:'liebiao',
// query:{},
params:{
id:m.id,
title:m.title
}
})
},
replaceShow(m){
console.log(this.$router)
// replace是替换栈顶页面
this.$router.replace({
// path:'',
name:'liebiao',
params:{
id:m.id,
title:m.title
}
})
}
}
2.back方法前一个页面 forward 前进一个页面 to正数前进几个 负数后退几个
methods:{
qianjin(){
this.$router.forward() // 后退页面
},
houtui(){
this.$router.back() // 前进页面
},
tiao(){
this.$router.go(3) // 正数 往前跳几个 负数 往后跳几个
}
}
11.缓存路由组件,让他们切换时不被销毁
1.可以利用 《keep-alive include=‘组件名’》
<!-- 这里包含两个组件 ,一个news 和 一个message 要想在切换的时候不被销毁 那么就要加上 keep-alive -->
<keep-alive include="News">
<!-- 默认不写include 的话 那么就是包裹全部 全部都缓存 -->
<router-view></router-view>
</keep-alive>
2.多个的话 :keep-alive="[‘news’,‘message’]
12.两个新的生命周期,路由组件专属activated、deactivated
1.activated钩子 ,在组件被激活的时候被调动
activated(){
// 路由组件被激活的时候被调用
// 路由组件被激活了
console.log('路由组件激活')
this.dd = setInterval(()=>{
this.options -= 0.01;
if(this.options <= 0) this.options = 1
},16)
},
2.deactivated钩子 ,组件在被失活的时候被调用
deactivated(){
// 路由组失活的时候被调用
console.log('路由组件失活')
clearInterval(this.dd)
}
3.nextTick()这个钩子是 下一次更新dom元素后被调用
```java
// 获取焦点 这个nexttick 在下一次更新完成DOM元素后被调用
this.$nextTick(function(){
this.$refs.editFocus.focus()
})
13.全局前置路由守卫(拦截器)
to 和 from 都是一个一个route
- beforeEach((to,from,next)=>{
}
)
const rou = new VueRouter({})
// 给路由器配置全局前置路由守卫 它是初始化的时候会执行一次 还有,每次路由切换的时候会执行
// to 是到哪去的路由详情 from是来自哪里 next 放行 如果没有被放行那么路径也不会发生改变
rou.beforeEach((to,from,next)=>{
console.log(to,from)
if(to.name !== 'xinwen'){
next() // 放行
}
})
export default rou
14.路由中配置 元数据 meta:{}
meta:{title:'关于'} // 这个是在路由中配置自己的属性 通过 $route.meta.xxx拿取
15.全局后置路由守卫 afterEach(to,from)
// 全局后置路由守卫 初始化的时候被调用, 路由切换之后被调用
rou.afterEach((to,from)=>{
console.log(to,from)
document.title = to.meta.title
})
16.独享路由守卫 在routes中配置 beforeEnter()
独享路由守卫只有前置没有后置
如果配置了全局路由守卫 也配置了独享路由守卫 那么 先执行全局
// 多级路由 可以使用children配置一个对象数组
children:[
{
name:'xinwen',
path:'news',
component:News,
meta:{title:'新闻'},
beforeEnter(to,from,next){
console.log("独享的",to,from)
if(to.name !== 'xinwen'){
next() // 放行
}else{
alert("你不能访问")
}
}
},
17.组件内路由守卫
1.在定义组件的时候在对象中传入 beforeRouteEnter((to,from,next){
// 这个是 通过路由规则 进入组件之前触发
})
export default {
name:'About',
mounted(){
console.log(this)
},
// 直接在组件内配置路由守卫
beforeRouteEnter (to, from, next) {
console.log("我是组件内路由守卫 在通过路由规则 进入这个组件之前被调用")
next() // 放行
},
beforeRouteLeave (to, from, next) {
console.log("我是组件内路由守卫, 在通过路由规则离开之前被调用")
next() // 放行
}
}
2.beforeRouteLeave 通过路由规则离开组件之前触发
18.路由器的两种工作模式history、hash
1.首先默认采用的是hash的模式,在这种模式下url#及后面的路径不会随着http请求携带给服务器
2.history模式,这种模式下是没有#号,它会把全部的url携带给服务器
配置:
在new VueRouter({
配置
mode:‘history/hash’,
routes:[{}]
})
3.使用 npm run build,会把项目构建,把Vue文件给转化为html、js、css文件,然后把它放在服务器上运行
第七章:Vue UI组件库
1. 移动端常用 UI 组件库
- Vant https://youzan.github.io/vant
- Cube UI https://didi.github.io/cube-ui
- Mint UI http://mint-ui.github.io
2. PC 端常用 UI 组件库
- Element UI https://element.eleme.cn
- IView UI https://www.iviewui.com
3.elementUI 的基本使用
1.安装插件
npm i element-ui -S
2.完整引入 在main.js中引入
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
3.参照elementUI的组件文档选取组件
完整引入太大了 可以用到什么标签就引入什么 按需引入
1.首先,安装 babel-plugin-component:
npm install babel-plugin-component -D
2.将babel.config.js 中的配置 合并为
{
"presets": [["@babel/preset-env", { "modules": false }]],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
3.在 main.js 中写入以下内容:
import { Button, Select } from 'element-ui';
全局注册组件 组件名
Vue.component(Button.name, Button);
Vue.component(Select.name, Select);
/* 或写为
* Vue.use(Button)
* Vue.use(Select)
*/
第八章 Vue3 组件中的模板结构可以没有根标签
构建工具vite 它是先启动服务,再按需加载资源,(快速冷启动)
而传统的是先全部加载完成资源 在启动服务
1.分析工程结构
1.在main.js中
// 引入一个名为createApp的工厂函数 创建对象不用new
import { createApp } from 'vue'
createApp(App).mount('#app')
createApp(App) 他会返回一个App实例对象,它是vm的精简版
mount()是挂载某个组件 而 unmount()是取消挂载
2.安装Vue3的开发者工具看视频P140
3.初识setup (所有composition API的表演舞台)
let 表示一个可变的变量 const表示一个不可变的
1.在Vue3中所有的data,methods,computed等都是放在setup中
2.setup的返回值可以是对象也可以是渲染函数
对象:那么在模板中可以直接使用对象中的东西
setup(){
let name = 'zhansgan'; // let 表示一个可变的变量 const表示一个不可变的
let age = 20;
let sex = '男';
function helloVue3(){
// ``号 配合${变量}取值
alert(`欢迎学习vue3${name}${age}${sex}`)
}
// 2.setup的返回值可以是对象也可以是渲染函数
// 对象:那么在模板中可以直接使用对象中的东西
// 渲染函数:
return {
name:name,
age,
sex,
helloVue3
}
// return ()=>{return h('h1','欢迎学习·')}
}
渲染函数:
import {h} from 'vue'
setup(){
// 数据 函数 计算属性 watch等
return ()=>{return h('h1','欢迎学习·')}
}
3.注意Vue2的data,methods,computed等中可以访问setup返回的东西
而setup中不能访问Vue2中的东西(尽量不要混写)
如果混写重名了 那么以Vue3的为准
4.ref函数 处理基本数据类型的函数(响应式)
原来的在标签中的ref属性的话 它是id的替代者,它主要用来给元素打标识,通过this.$refs.名字得到
1.引入ref函数
import {ref} from 'vue'
2.用ref函数包裹基本数据类型
let name = ref('zhansgan'); // let 表示一个可变的变量 const表示一个不可变的
let age = ref(20);
let sex = ref('男');
那么ref的返回值就是一个refimpl 引用实例对象 ,要是想要修改一个值Vue监测到那么就要通过这个对象的value来修改
// 1.在Vue3中所有的data,methods,computed等都是放在setup中
setup(){// 所有compositionAPI的表演舞台
let name = ref('zhansgan'); // let 表示一个可变的变量 const表示一个不可变的
let age = ref(20);
let sex = ref('男');
function update(){
// Vue监测到那么就要通过这个对象的value来修改
console.log(name)
name.value = '张三';
age.value= 33;
sex.value = '女';
}
3.在模板中还是可以通过属性名直接访问,因为Vue3直接帮我们.value了
<h1>姓名{{ name }}</h1>
<h2>年龄{{ age }}</h2>
<h2>性别{{ sex }}</h2>
5.ref函数 处理对象类型的数据
这个对象是 refimpl 引用实例
let obj = ref({
// 用ref包裹住一个对象
name:'私立',
age:'20'
})
这个对象.value这个value是 proxy来包裹住的
console.log(obj.value) //
obj.value.age=30
使用的时候还是不用加value
<!-- 访问的时候也不用加value -->
<h1>{{ obj.age }}</h1>
基本数据类型的响应是靠defineproperty 的setget方法来实现的
而对象数据类型的响应靠的是 Vue3.0中的一个函数reactive (这个函数中用到了proxy)
6.reactive函数 对象/数据的响应式数据定义 不用value
定义响应式的数据是深层次的
1.const proxy = reactive(源对象) 返回代理对象,不用点value直接修改
let obj = reactive({
name:'私立',
age:'20'
})
// Vue3中操作数组直接操作元素也可以是响应式的
let arr = reactive(['1','2','3'])
console.log(obj) // 这个obj就是proxy === ref的 obj.value
obj.age=333 // 数据修改不用value
arr[0] = '8'
7.回顾Vue2的数据监测原理
1.对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)
2.数组类型:它是通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
缺点:
新增属性、删除属性, 界面不会更新。
解决:
1.通过this.$set(哪个对象,属性/数组下标,修改的值)
this. $delete(哪个对象,属性/数据下标)
缺点2:
直接通过下标修改数组, 界面不会自动更新。
解决:
通过this. $set(哪个对象,属性/数组下标,修改的值)
或者用Vue包裹住的方法来操作数据
8.Vue3的响应式原理(proxy构造函数)
使用proxy(person,// 第一个参数是代理哪个对象 ,{})
- get(target,propName) 当有人想要获取 p中的属性时调用
target 是代理对象的源对象 propName是 操作的属性名 - set(target,propName,value) set方法是 有人修改p的属性 或者是给p新增属性的时候会调用
- deleteProperty(target,propName) 有人 删除p中的一个属性的时候会调用
const p= new Proxy(person,{// 第一个参数是代理哪个对象
// 当有人想要获取 p中的属性时调用
get(target,propName){
// target 是代理对象的源对象 propName是 操作的属性名
console.log("get中的",target,propName)
return target[propName]
},
// set方法是 有人修改p的属性 或者是给p新增属性的时候会调用
set(target,propName,value){
// target 是代理对象的源对象 propName是 操作的属性名 value是修改值
console.log('我监测到了 我要去刷新页面了')
target[propName] = value;
},
// deleteproperty方法是 有人 删除p中的一个属性的时候会调用
deleteProperty(target,propName){
return delete target[propName]
}
})
Reflect反射 来操作属性
const p= new Proxy(person,{// 第一个参数是代理哪个对象
// 当有人想要获取 p中的属性时调用
get(target,propName){
console.log("get中的",target,propName)
return Reflect.get(target,propName) // 得到这个对象中的这个方法
},
set(target,propName,value){
console.log('我监测到了 我要去刷新页面了')
Reflect.set(target,propName,value) //修改这个对象的这个属性的值
},
deleteProperty(target,propName){
return Reflect.deleteProperty(target,propName) // 删除这个对象中的这个属性
}
// 另外这个Reflect中也有defineproperty这个方法 和object中的一样
//Reflect.defineProperty()
})
9.Vue3 setup的两个参数
setup的执行是在beforeCreate钩子函数之前的 并且 setup中的this是未定义的
首先$attrs 是没有用props接收的参数就会放在这里
$slots 这个是组件的插槽
props:['msg','x'],
emits:['hello'],
setup(props,context){
// console.log(props.x) // 这个参数是 props 传递的参数
console.log(context.attrs) //context上下文 他有attrs 就是没有用props接收的参数都在attrs中
console.log(context.emit) // 这个是用来触发事件的
console.log(context.slots) // 这个是这个组件传递的插槽
<template>
<!-- Vue3中可以没有 根标签 -->
<div>
<h1>姓名{{ obj.name }}</h1>
<h2>年龄{{ obj.age }}</h2>
<slot name="s"></slot>
<button @click="dian">点我触发hello事件</button>
</div>
</template>
<script>
// 引入reactive函数
import {reactive} from 'vue'
export default {
name: 'Demo',
props:['msg','x'], // 组件内部要接收 声明接收
emits:['hello'],
setup(props,context){
// console.log(props.x) // 这个参数是 使用props声明接收了的属性
console.log(context.attrs) //context上下文 他有attrs 就是没有用props接收的参数都在attrs中
console.log(context.emit) // 这个是用来触发事件的
console.log(context.slots) // 收到插槽的内容
let obj = reactive({
name:'私立',
age:'20'
})
function dian(){
context.emit('hello',999)
}
return {
obj,
dian
}
// return ()=>{return h('h1','欢迎学习·')}
}
}
</script>
<style>
</style>
10.Vue3的computed 组合pai
import {reactive,computed} from 'vue'
// 定义属性也是通过composition组合api来实现的 这个是简写形式
// obj.fullName = computed(()=>{
// return obj.firstName + '-' + obj.lastName
// })
计算属性名 = computed({
get(){
return obj.firstName + '-' + obj.lastName
},
set(value){
let a = value.split('-')
obj.firstName = a[0]
obj.lastName = a[1]
}
})
11.Vue3的watch
注意 watch只能监视 ref属性、reactive对象、数组监视对象默认就有深度监视
1.监视ref基本数据类型 的一个数据 参数一 监视的属性 参数二 回调函数 参数三 配置参数
watch(sum , (newvalue,oldvalue)=>{
console.log('sum改变咯',newvalue,oldvalue)
},{immediate:true})
2. 监视ref多个数据用数组
watch([sum,name],(new1,old1)=>{
console.log("sum或者name改变咯",new1,old1)
},{immediate:true})
3.监视reactive 对象 的问题
问题一: newvalue和oldvalue都是 新的数据
问题而: deep深度监视 配置失效
watch(obj,(new1,old1)=>{
console.log('reactive监视的对象改变了',new1,old1)
},{deep:false})
4. 监视对象中的一个属性的话 那么要写成函数的返回值 一个ref
注意 :: watch只能监视 ref、reactive对象、数组
watch(()=>{return obj.sex},(new1,old1)=>{
console.log("我只要监视对象中的一个属性",new1,old1)
})
5.监视对象中的多个 属性 通过返回一个数组的形式
watch(()=>{return [obj.sex,obj.age]},(new1,old1)=>{
console.log("监视对象中的多个属性",new1,old1)
})
或者写成多个函数
watch([()=>{return obj.sex},()=>{return obj.age}],(new1,old1)=>{
console.log("监视对象中的多个属性",new1,old1)
})
// 如果watch监视的是一个ref包裹住的对象 那么 可以使用.value 或者开启深度监视
// 如果不点value 那么 他监视的是refimpl 当修改的是对象中的属性的时候 使用Proxy包裹住的 要开启深度监视
watch(obj.value,(new1,old1)=>{
},{deep:true})
12.watchEffect函数 组合api
watchEffect函数 监视的是这个回调函数中所用到的数据(发生改变就会执行) 初始化就会执行一次
有点像computed 所依赖的属性发生改变就用到 但是它注重的值返回值
watchEffect注重的是过程 回调函数的过程
// watchEffect函数 监视的是这个回调函数中所用到的数据 初始化就会执行一次
// 不管有多深都可以
watchEffect(()=>{
const a = sum.value
console.log("我先执行一下")
})
13.Vue2生命周期和Vue3的生命周期对比
Vue2生命周期
此图片来自
此图片来自
Vue2 他不会先检查这个 el是否有就会执行掉 beforeCreate,和Created钩子,再会检查
而Vue3 会CreateApp(App).mount(“#app”)先执行 ,后面就不会判断有没有el了
Vue3的最后阶段变为了 beforeUnMount写在之前 和 unMount卸载之前
但是写在setup里面,就要先引入组合API 才能使用 而 BeforeCreate和Create都变为了setup 其他的钩子函数前面都加上on
beforeCreate() {
console.log("----我beforeCreate 数据还没有 数据监测也没有")
},
created(){
console.log("----我created 数据代理数据监测都有了")
},
beforeMount(){
console.log("-----我 beforeMount还没有完成挂载 是未经编译的DOM结构" )
},
mounted(){
console.log("-----我mounted 已经挂载完成")
},
beforeUpdate(){
console.log("-----我 beforeUpdate数据更新到页面之前 数据已经改变但是 页面没有修改")
},
updated(){
console.log("-----我updated数据和页面都修改了")
},
beforeUnmount(){
console.log("---- 我beforeUnmount是卸载之前要关闭一些东西 自定义事件啊 消息的订阅预发布啊")
},
unmounted(){
console.log("-----我unmounted是卸载完成")
}
而在setup中写的话
import {onMounted} from 'vue'
setup(){
onMounted(()=>{
console.log("-----onMounted 已经挂载完成")
})
}
14.Vue3中的hook函数 它是对setup中的组合API进行封装
hook本质上是一个函数,它是对setup的组合API进行了封装,类似于vue2.x中的mixin
hook的优势: 复用代码, 让setup中的逻辑更清楚易懂。
1.在src文件目录下新建 hooks文件下,下面放封装的js文件,一般以useXXX.js
// 这个函数的功能就是 给一个窗口绑定点击事件 并且返回鼠标点击的坐标
import {reactive,onMounted,onBeforeUnmount} from 'vue'
// 封装组合API
export default function(){
let point = reactive({
x:0,
y:0
})
function getXY(e){
console.log(e.pageX,e.pageY)
point.x = e.pageX
point.y = e.pageY
}
//在这个组件挂载的时候就给绑定事件
onMounted(()=>{
// 这个是给一个窗口绑定事件 后面是事件函数
window.addEventListener('click',getXY)
})
// 组件被卸载时解绑事件
onBeforeUnmount(()=>{
// 这个是移除事件 后面也是事件函数
window.removeEventListener('click',getXY)
})
return point
}
2.把要封装的 compositionAPI 做成一个函数返回数据,并且暴露
3.在要使用到这个函数的地方引入 ,并且接收到返回值
// 引入hook函数
import usePoint1 from '../hooks/usePoint'
const point = usePoint1() // 调用函数的到返回值 返回出去使用
15.Vue3中的toRef&toRefs
toref主要是生成一个refimp对象,它是对象中的某一个属性,value以代理的方式联动
// toRef函数 代理对象 代理对象中的一个属性
// 第一个参数是代理哪个对象 第二个参数是 对象中的哪一个属性
const sex=toRef(obj,'sex')
torefs的话是生成一个对象的第一层属性的代理Proxy
toRefs函数 处理对象的所有第一层属性
const a=toRefs(obj)
...toRefs(obj), // ... 是把这个对象的属性摊开放在另一个对象中
应用:想要把一个对象的某些属性提供给外部使用,而不会丢失响应式,数据也是一致
第九章 Vue3的其他compositionAPI
1.shallowReactive和ShallowRef (Shallow浅层的)
shallowReactive:它只会监测对象的第一层属性的变化
shallowRef:它不会监测对象的属性变化(数组也不会),因为Value没有用proxy,它适用于对象属性值不变,整个对象替换的
// shallowReactive:它只会监测对象的第一层属性的变化 后面的层数就没有用proxy了
let obj = shallowReactive({
sex:'男',
age:20,
job:{
slary:20
}
})
// shallowRef他只会监测基本数据类型 而对象类型不会用Proxy
let x =shallowRef({y:0})
2.shallowReadonly(浅只读第一层)和Readonly(深只读)
let x = ref(0)
// shallowReactive:它只会监测对象的第一层属性的变化 后面的层数就没有用proxy了
let obj = reactive({
sex:'男',
age:20,
job:{
slary:20
}
})
// readonly这个包裹一个数据 那么这个数据就是只可读的 不能被修改
x = readonly(x)
// shallowReadonly 包裹一个数据 如果这个数据是对象的话 那么只会有第一层只可读 后面的层是响应式的
obj = shallowReadonly(obj)
3.toRaw(响-原)和markRaw(标记为原始)组合API
toRaw:把reactive的数据变为原始数据, Vue3不会监测响应
const yuanshi = toRaw(obj)
// toRaw 把reactive的数据变为原始数据 Vue3不会监测响应
// 但是他的改变实际上这个obj也会改变 只要以后这个obj改变的话 那么就会响应之前的修改
const yuanshi = toRaw(obj)
markRaw:把这个对象标记为一个原始对象 ,那么这个对象不会在是响应式的 基本数据类型的话就改变
markRaw(car)
function addCar(){
// 这样往reactive的对象 那么这个对象也是响应式的
let car = {name:'奔驰',price:40}
// markRaw标记为对象
markRaw(car)
console.log(car) // 被markRaw修饰的话 那么就是Object的 不是proxy的
obj.car = car
}
toRaw:
作用:将一个由reactive生成的响应式对象转为普通对象。
使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新。
markRaw:
作用:标记一个对象,使其永远不会再成为响应式对象。
应用场景:
有些值不应被设置为响应式的,例如复杂的第三方类库等。
当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能。
4.customRef 定做的Ref
customRef是可以自定义一个ref,并对其依赖项跟踪和更新触发进行显式控制。
案例
setup(){
let times
// 2.创建函数的实现 并且再里面使用customRef进行 自定义控制
// customRef 自定义一个ref 并对其依赖项追踪track和数据更新触发trigger进行显示控制
function myRef(value){
// 3.customRef的使用 ,需要传递一个函数 并且返回一个对象,里面会包括getset方法
// get 在模板中读取get返回值的是时候被调用 set修改value的时候调用
return customRef((track,trigger)=>{
// track追踪依赖数据的改变get trigger触发模板重新解析
return {
get(){
console.log("get方法调用返回value",value)
track() // 追踪value的改变
return value;
},
set(newvalue){
console.log("set方法被调用新值是",newvalue)
// 因为是读取返回的get中的value ,所以要修改value 不过要触发模板的重新解析去读取value
clearTimeout(times) // 清除上一个定时器 只留下最后一个 防抖
times=setTimeout(()=>{ //过500毫秒去触发更新
value = newvalue
trigger()//触发模板的重新解析去读取value
},500)
}
}
})
}
// 1.先自定义一个函数,传递需要的参数
let keyward = myRef('hello')
5.provide与inject用于父与后代组件之间传递数据
1.在父组件中使用provide(“参数名”,数据)来传递
setup(){
let car = reactive({name:'奔驰',price:'40W'})
provide("car",car) //在父组件中 提供参数名 数据
return {
...toRefs(car) // 对象摊开
}
}
2.在任意的后代组件中使用inject(“参数名”)来获取数据
const car= inject('car')
return {
car
}
6.常用的响应式数据的判断
isRef:判断一个值是否是Ref对象
console.log(isRef(obj))
isReactive:检查一个对象是不是由reactive创建的响应式代理
console.log(isReactive(sum))
isReadonly: 检查一个对象是不是readonly创建的只读代理
console.log(isReadonly(readobj))
isProxy: 检查一个对象是不是由reactive或者readonly创建的代理
console.log(isProxy(obj))
readonly修饰响应对象,还是proxy的
7.Composition组合PAI的优势
首先Vue2采用的OptionsAPI 它,新增或者修改一个需求,就需要分别在data,methods,computed里修改 。比较杂乱
那么Vue3的compositionAPI可以让我们可以更加优雅的组织我们的代码,函数。让相关功能的代码更加有序的组织在一起。(配合hook函数)
8.Fragment片段组件 内置组件直接用
Vue2组件必须要有根标签
而Vue3不需要有根标签,而是通过把组件标签打入一个虚拟的Fragment内部
9.Teleport(瞬间移动)内置组件直接用
Teleport 是一种能够将我们的组件html结构移动到指定位置的技术。
<Teleport to="body">
<!-- to 写要传送的位置 可以直接写标签名 也可以通过标签选择器选择 #id
如果是 用v-show控制的话,那么会预编译在to的位置 desplay none -->
<div v-if="isshow">
<h1>对话内容</h1>
<!-- 现在是弹在son组件里面 我想通过TelePort传送到 任意组件标签内 -->
<h1>对话内容</h1>
<h1>对话内容</h1>
<button @click="isshow = false">点我关闭</button>
</div>
</Teleport>
10.suspense(悬疑)组件和defineAsyncComponent异步引入组件
// 除了这种静态引入组件,还有异步引入组件得方式
// 静态引入会等这个组件引入完 再显示
// import chilend from './components/chilend.vue'
import {defineAsyncComponent} from 'vue'
const child = defineAsyncComponent(()=>{
// 异步引入 不需要等待 返回一个组件
return import('./components/chilend.vue')
})
<Suspense>
<!-- Suspense 是一个组件,他里面有两个插槽 一个default插槽是默认显示的
一个fallback是default 有错误时显示的 比如加载满 -->
<template v-slot:default>
<child></child>
</template>
<template v-slot:fallback>
<h1>加载中。。。。</h1>
</template>
</Suspense>
11.Vue3的一些变化
1.全局API的转移
Vue2使用Vue来定义
而Vue使用createApp返回的对象定义
组件的data一定要是函数,为了避免组价复用的时候数据错乱
2.移除keyCode作为 v-on 的修饰符,同时也不再支持config.keyCodes
3.移除v-on.native修饰符
在子组件中用emits声明的就是自定义事件 然就是原生事件
export default {
emits: ['close']
}
4.移除过滤器(filter)