vue进阶笔记 --002

436 阅读14分钟

一、 Flex实现弹性布局

1.1 FlexFlexible Box的缩写,意为”弹性布局”,用来为盒状模型提供最大的灵活性。注意:任何一个容器都可以指定为Flex布局。

.box{ display: flex; }

行内元素也可以使用Flex布局。

.box{
  display: inline-flex;
}

Webkit内核的浏览器,必须加上-webkit前缀。

.box{
  display: -webkit-flex; /* Safari */
  display: flex;
}

设为Flex布局以后,子元素的floatclearvertical-align属性将失效。

1.2 容器属性

以下6个属性设置在容器上。

  • flex-direction
  • flex-wrap
  • flex-flow
  • justify-content
  • align-items
  • align-content

1.21 flex-direction 属性

flex-direction属性决定主轴的方向(即项目的排列方向)。

.box {
  flex-direction: row | row-reverse | column | column-reverse;
}
  • row(默认值):主轴为水平方向,起点在左端。
  • row-reverse:主轴为水平方向,起点在右端。
  • column:主轴为垂直方向,起点在上沿。
  • column-reverse:主轴为垂直方向,起点在下沿。

1.22 flex-wrap 属性

默认情况下,项目都排在一条线(又称”轴线”)上。flex-wrap属性定义,如果一条轴线排不下,如何换行。

.box{
  flex-wrap: nowrap | wrap | wrap-reverse;
}
  • nowrap(默认):不换行。
  • wrap:换行,第一行在上方。
  • wrap-reverse:换行,第一行在下方。

1.23 flex-flow 属性

flex-flow属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap

.box {
  flex-flow: <flex-direction> <flex-wrap>;
}

1.24 justify-content 属性

justify-content属性定义了项目在主轴上的对齐方式。

.box {
  justify-content: flex-start | flex-end | center | space-between | space-around;
}
  • flex-start(默认值):左对齐
  • flex-end:右对齐
  • center: 居中
  • space-between:两端对齐,项目之间的间隔都相等。
  • space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。

1.25 align-items 属性

align-items属性定义项目在交叉轴上如何对齐。

.box {
  align-items: flex-start | flex-end | center | baseline | stretch;
}
  • flex-start:交叉轴的起点对齐。
  • flex-end:交叉轴的终点对齐。
  • center:交叉轴的中点对齐。
  • baseline: 项目的第一行文字的基线对齐。
  • stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。

1.26 align-content 属性

align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。

.box {
  align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}
  • flex-start:与交叉轴的起点对齐。
  • flex-end:与交叉轴的终点对齐。
  • center:与交叉轴的中点对齐。
  • space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。
  • space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。
  • stretch(默认值):轴线占满整个交叉轴。

1.3 项目属性

以下6个属性设置在项目上。

  • order
  • flex-grow
  • flex-shrink
  • flex-basis
  • flex
  • align-self

1.31 order 属性

order属性定义项目的排列顺序。数值越小,排列越靠前,默认为0。

.item {
  order: <integer>;
}

1.32 flex-grow 属性

flex-grow属性定义项目的放大比例,默认为0,即如果存在剩余空间,也不放大。

.item {
  flex-grow: <number>; /* default 0 */
}

1.33 flex-shrink 属性

flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。

.item {
  flex-shrink: <number>; /* default 1 */
}

!注:负值对该属性无效。

 1.34 flex-basis 属性

flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。

.item {
  flex-basis: <length> | auto; /* default auto */
}

它可以设为跟widthheight属性一样的值(比如350px),则项目将占据固定空间。

1.35 flex 属性

flex属性是flex-growflex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。

.item {
  flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
}

该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)

建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。

1.36 align-self 属性

align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch

.item {
  align-self: auto | flex-start | flex-end | center | baseline | stretch;
}

该属性可能取6个值,除了auto,其他都与align-items属性完全一致。

二、Vuex 辅助函数

通过辅助函数mapGettersmapStatemapActionsmapMutations,把vuex.store中的属性映射到vue实例身上,这样在vue实例中就能访问vuex.store中的属性了,对于操作vuex.store就变得非常方便。

state辅助函数为mapState,actions辅助函数为mapActions,mutations辅助函数为mapMutations。(Vuex实例身上有mapState、mapActions、mapMutations属性,属性值都是函数)

2.1 modules属性: 模块

把公共的状态按照模块进行划分:

  • 1、每个模块都相当于一个小型的Vuex ;
  • 2、每个模块里面都会有state getters actions mutations ;
  • 3、切记在导出模块的时候加一个 namespaced:true 主要的作用是将每个模块都有独立命名空间;
  • 4、namespace:true在多人协作开发的时候,可能子模块和主模块中的函数名字会相同,这样在调用函数的时候,相同名字的函数都会被调用,就会发生问题。为了解决这个问题,导出模块的时候要加namespace:true.

image.png

2.2 命名空间

模块开启命名空间后,享有独自的命名空间。示例代码:

export default {
	namespaced: true,
	....
}

三、代码优化:

3.1 三元运算符

用三元运算符代替简单的if else

me = age < 18 ? '小哥哥' : '靓仔';

3.2 判断

当需要判断的情况不止一个时:ES6中的includes

if( [1,2,3,4,5].includes(type) ){
   //...
}

3.3 取值

ES6的解构赋值

const {a,b,c} = obj;

3.4 获取对象属性值

ES6提供了可选连操作符?.

const name = obj?.name;

3.5 反转字符串

将一个字符串进行翻转操作,返回翻转后的字符串

const reverse = str => str.split('').reverse().join('');
 
reverse('hello world');   // 'dlrow olleh'

3.6 生成随机字符串

生成一个随机的字符串,包含字母和数字

const randomString = () => Math.random().toString(36).slice(2);
//函数调用
randomString();

3.7 数组去重

const unique = (arr) => [...new Set(arr)];
 
console.log(unique([1, 2, 2, 2, 3, 4, 4, 5, 6, 6]));

3.8 合并数据

const a = [1,2,3];
const b = [1,5,6];
const c = [...new Set([...a,...b])];//[1,2,3,5,6]

3.9 判断数组是否为空

判断一个数组是否为空数组,它将返回一个布尔值

const notEmpty = arr => Array.isArray(arr) && arr.length > 0;
 
notEmpty([1, 2, 3]);  // true

3.10 交换两个变量

//旧写法
let a=1;
let b=2;
let temp;
temp=a
a=b
b=temp
 
//新写法
[a, b] = [b, a];

3.11 获取两个数之间的随机整数

const random = (min, max) => Math.floor(Math.random() * (max - min + 1) + min);
 
random(1, 100);

3.12 检查日期是否为工作日

传入日期,判断是否是工作日

const isWeekday = (date) => date.getDay() % 6 !== 0;
console.log(isWeekday(new Date(2021, 11, 11)));
// false 
console.log(isWeekday(new Date(2021, 11, 13)));
// true

3.13 滚动到页面顶部

const goToTop = () => window.scrollTo(0, 0);
goToTop();

3.14 浏览器是否支持触摸事件

通过判断浏览器是否有ontouchstart事件来判断是否支持触摸

const touchSupported = () => {
  ('ontouchstart' in window || window.DocumentTouch && document instanceof window.DocumentTouch);
}
console.log(touchSupported());

3.15 当前设备是否为苹果设备

前端经常要兼容andriod和ios

const isAppleDevice = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
console.log(isAppleDevice);
// Result: will return true if user is on an Apple device

3.16 复制内容到剪切板

使用 navigator.clipboard.writeText 来实现将文本复制到剪贴板

const copyToClipboard = (text) => navigator.clipboard.writeText(text);
 
copyToClipboard("hello alex");

3.17 检测是否是黑暗模式

用于检测当前的环境是否是黑暗模式,返回一个布尔值

const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
 
console.log(isDarkMode)

3.18 网站变成黑白

有时候网站在某种特定的情况下,需要使整个网站变成黑白的颜色

filter:grayscale(100%)

四、 前端Vue项目webpack打包部署后源码泄露解决

4.1 经Webpack打包部署到服务器后,访问并打开开发者模式,在Source下出现[name]路径,内部包含(webpack)buildin文件夹。(做漏洞分析时,会认为该内容涉及源码泄露)

(webpack/buildin)

4.2 打包时,通常通过配置 productionSourceMap:false 防止源码泄露问题,一般在对应的config文件中 build 选项进行配置。该配置会阻止生成 .map.js 文件,同时浏览器中不会出现 webpack:// 文件夹(该目录下包含了所有的前端页面源码)。

productionSourceMap是Vue的配置(并非Webpack配置) ,其作用为阻止 .map.js 文件的生成,降低包的大小,防止源码泄露。

4.3 是否由于本地代码中本身就有 .map.js 文件从而引起的该现象?

  • 解决方案:

检查项目中是否存在 .map.js 文件,并将其删除。
若该文件有被引用,则用其 .min.js 文件替换。(很小概率会出现代码中引入了 .map.js 文件)

五、 vue3的基本使用

5.1 reactive对比ref

  • 从定义数据角度对比:

    • ref用来定义: 基本数据类型
    • reactive用来定义: 对象(或数组)类型数据

备注: ref也可以用来定义对象(或数组)类型数据,它内部会自动通过reactive转为代理对象 从原理角度对比:

  • ref通过Object.defineProperty()的get和set来实现响应式(数据劫持)

reactive通过Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部的数据

  • 从使用角度对比:

    • ref定义数据:操作数据需要 .value ,读取数据时模板中直接读取不需要 .value
    • reactive 定义的数据: 操作数据和读取数据均不需要 .value

5.2 watch函数

  • 和computed一样
  • 需要引入api

5.21 watch有两个小坑:

1.监视reactive定义的响应式数据的时候:oldValue无法获取到正确的值,强制开启了深度监视(deep配置无效)

2.监视reactive定义的响应式数据中某个属性的时候:deep配置有效

 watch([sum,msg], (newValue,oldValue)=>{
    console.log('新的值',newValue); //['sum的newValue', 'msg的newValue']
    console.log('旧的值',oldValue); //['sum的oldValue', 'msg的oldValue']
},{immediate: true,deep:true})

示例:

<template>
  <h1>当前求和为: {{sum}}</h1>
  <button @click="sum++">点我+1</button>
  <hr>
  <h1>当前信息为: {{msg}}</h1>
  <button @click="msg+='!' ">修改信息</button>
  <hr>
  <h2>姓名: {{person.name}}</h2>
  <h2>年龄: {{person.age}}</h2>
  <button @click="person.name += '~' ">修改姓名</button> <button @click="person.age++">增长年龄</button>
</template>

<script>
    //使用setup的注意事项
    import { watch,ref,reactive } from 'vue'

    export default {
        name: 'test5',
        props: ['msg'],
        emits:['hello'],
        setup(){
            let sum  = ref(0)
            let msg = ref('你好啊')
            let person = reactive({
                name: '张三',
                age: 18,
                job:{
                    salary: '15k'
                },
            })
            //由于这里的this是指的是undefined,所以使用箭头函数
            //情况一:监视ref所定义的一个响应式数据
            // watch(sum, (newValue,oldValue)=>{
            //     console.log('新的值',newValue);
            //     console.log('旧的值',oldValue);
            // })

            //情况二:监视ref所定义的多个响应式数据
            watch([sum,msg], (newValue,oldValue)=>{
                console.log('新的值',newValue); //['sum的newValue', 'msg的newValue']
                console.log('旧的值',oldValue); //['sum的oldValue', 'msg的oldValue']
            },{immediate: true,deep:true}) //这里vue3的deep是有点小问题的,可以不用deep,(隐式强制deep)

            //情况三:监视reactive定义的所有响应式数据,
            //1.此处无法获取正确的oldValue(newValue与oldValue是一致值),且目前无法解决
            //2.强制开启了深度监视(deep配置无效)
            /**
            * 受到码友热心评论解释: 此处附上码友的解释供大家参考:
            * 1. 当你监听一个响应式对象的时候,这里的newVal和oldVal是一样的,因为他们是同一个对象【引用地址一样】,
            *    即使里面的属性值会发生变化,但主体对象引用地址不变。这不是一个bug。要想不一样除非这里把对象都换了
            * 
            * 2. 当你监听一个响应式对象的时候,vue3会隐式的创建一个深层监听,即对象里只要有变化就会被调用。
            *    这也解释了你说的deep配置无效,这里是强制的。
            */
            watch(person, (newValue,oldValue)=>{
                console.log('新的值',newValue); 
                console.log('旧的值',oldValue);
            })

            //情况四:监视reactive对象中某一个属性的值,
            //注意: 这里监视某一个属性的时候可以监听到oldValue
            watch(()=>person.name, (newValue,oldValue)=>{
                console.log('新的值',newValue);  
                console.log('旧的值',oldValue);
            })

            //情况五:监视reactive对象中某一些属性的值
            watch([()=>person.name,()=>person.age], (newValue,oldValue)=>{
                console.log('新的值',newValue);  
                console.log('旧的值',oldValue);
            })

            //特殊情况: 监视reactive响应式数据中深层次的对象,此时deep的配置奏效了
            watch(()=>person.job, (newValue,oldValue)=>{
                console.log('新的值',newValue);  
                console.log('旧的值',oldValue);
            },{deep:true}) //此时deep有用

            return {
                sum,
                msg,
                person,
            }
        },
        
    }
</script>

5.22 watchEffect函数

  • watch的套路是:既要指明监视的属性,也要指明监视的回调
  • watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性
  • watchEffect有点像computed:
    • 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值
    • watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值
let sum  = ref(0)
            
//用处: 如果是比较复杂的业务,发票报销等,那就不许需要去监听其他依赖,只要发生变化,立马重新回调
//注重逻辑过程,你发生改变了我就重新执行回调,不用就不执行,只执行一次
watchEffect(()=>{
    //这里面你用到了谁就监视谁,里面就发生回调
    const x1 = sum.value
    console.log('我调用了');
})

5.3 生命周期函数

/**
     * beforeCreate 和  created 这两个生命周期钩子就相当于 setup 所以,不需要这两个
     * 
     * beforeMount   ===>  onBeforeMount
     * mounted       ===>  onMounted
     * beforeUpdate  ===>  onBeforeUpdate
     * updated       ===>  onUpdated
     * beforeUnmount ===>  onBeforeUnmount
     * unmounted     ===>  onUnmounted
*/

5.4 自定义hook函数

  • 什么是hook函数: 本质是一个函数,把setup函数中使用的Composition API进行了封装
  • 类似于vue2.x中的 mixin
  • 自定义hook的优势: 复用代码,让setup中的逻辑更清楚易懂
  • 使用hook实现鼠标打点”:
  • 创建文件夹和usePoint.js文件

image.png

usePoint.js:

//usePoint.js
import {reactive,onMounted,onBeforeUnmount } from 'vue'
function savePoint(){
    //实现鼠标打点的数据
    let point = reactive({
        x: null,
        y: null
    })
    //实现鼠标点的方法
    const savePoint = (e)=>{
         point.x = e.pageX
         point.y = e.pageY
    } 
    //实现鼠标打点的生命周期钩子
    onMounted(()=>{
        window.addEventListener('click',savePoint)
    })
    onBeforeUnmount(()=>{
        window.removeEventListener('click',savePoint)
    })
    return point
}
export default savePoint

组件test.vue:

//组件test.vue

<template>
  <p>当前求和为: {{sum}} </p>
  <button @click="sum++">加一</button>
  <hr>
  <h2>当前点击时候的坐标: x: {{point.x}}  y:{{point.y}}</h2>

</template>

<script>
import { ref } from 'vue'
import usePoint from '../hooks/usePoint'
export default {
    name: 'test8',
    setup(props,context){
        let sum = ref(0)
        let point = usePoint()
        return {
            sum,
            point
        }
    }
}
</script>

5.5 toRaw与markRaw

  • toRaw
    • 作用:将一个由reactive生成的响应式对象转换为普通对象
    • 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新
  • markRaw:
    • 作用:标记一个对象,使其永远不会再成为响应式对象
    • 使用场景:

    有些值不应被设置成响应式的,例如复杂的第三方类库等

    当渲染具有不可变数据的大列表时候,跳过响应式转换可以提高性能

5.6 provide与inject

  • 作用:实现祖孙组件间的通信
  • 套路:父组件有一个provide选项提供数据,子组件有一个inject选项来开始使用这些数据
  • 具体写法:

父组件:

//父组件
<script setup>

import { ref,reactive,toRefs,provide } from 'vue';
import ChildVue from './components/Child.vue';

let car = reactive({
  name: '奔驰',
  price: '40w'
})
provide('car',car) //给自己的后代组件传递数据
const {name, price} = toRefs(car)
</script>
<template>
  <div class="app">
    <h3>我是父组件, {{name}}--{{price}}</h3>
    <ChildVue></ChildVue>
  </div>
</template>
<style>
.app{
  background-color: gray;
  padding: 10px;
  box-sizing: border-box;
}
</style>

子组件:

//子组件
<script setup>
import { ref } from '@vue/reactivity';
import SonVue from './Son.vue';
</script>

<template>
  <div class="app2">
    <h3>我是子组件</h3>
    <SonVue></SonVue>
  </div>
</template>

<style>
.app2{
  background-color: rgb(82, 150, 214);
  padding: 10px;
  box-sizing: border-box;
}
</style>

孙组件:

//孙组件
<script setup>

import { ref,inject } from 'vue';
let car = inject('car') //拿到父组件的数据
const {name, price} = car
</script>

<template>
  <div class="app3">
    <h3>我是孙组件</h3>
    <p>{{name}}-{{price}}</p>
  </div>
</template>
<style>
.app3{
  background-color: rgb(231, 184, 56);
  padding: 10px;
  box-sizing: border-box;
}
</style>

5.7 响应式数据的判断

  • isRef:检查一个值是否为ref对象
  • isReactivce:检查一个对象是否是由reactive创建的响应式代理
  • isReadonly:检查一个对象是否由readonly创建的只读代理
  • isProxy:检查一个对象是否由reactive或者readonly方法创建的代理

5.8 新的组件1 Transition

  • 会在一个元素或组件进入和离开 DOM 时应用动画
  • 它是一个内置组件,这意味着它在任意别的组件中都可以被使用,无需注册。它可以将进入和离开动画应用到通过默认插槽传递给它的元素或组件上。进入或离开可以由以下的条件之一触发:
    • 由 v-if 所触发的切换
    • 由 v-show 所触发的切换
    • 由特殊元素 切换的动态组件
<button @click="show = !show">切换</button>
<Transition>
  <p v-if="show">HelloWord</p>
</Transition>
//当一个 <Transition> 组件中的元素被插入或移除时,会发生下面这些事情
/**
1.Vue 会自动检测目标元素是否应用了 CSS 过渡或动画。如果是,则一些 CSS 过渡 class 会在适当的时机被添加和移除
2.如果有作为监听器的 JavaScript 钩子,这些钩子函数会在适当时机被调用
3.如果没有探测到 CSS 过渡或动画、也没有提供 JavaScript 钩子,那么 DOM 的插入、删除操作将在浏览器的下一个动画帧后执行
*/

//针对CSS 的过渡效果
/**
1.v-enter-from:进入动画的起始状态。在元素插入之前添加,在元素插入完成后的下一帧移除。
2.v-enter-active:进入动画的生效状态。应用于整个进入动画阶段。在元素被插入之前添加,在过渡或动画完成之后移除。这个 class 可以被用来定义进入动画的持续时间、延迟与速度曲线类型
3.v-enter-to:进入动画的结束状态。在元素插入完成后的下一帧被添加 (也就是 v-enter-from 被移除的同时),在过渡或动画完成之后移除。
4.v-leave-from:离开动画的起始状态。在离开过渡效果被触发时立即添加,在一帧后被移除
5.v-leave-active:离开动画的生效状态。应用于整个离开动画阶段。在离开过渡效果被触发时立即添加,在过渡或动画完成之后移除。这个 class 可以被用来定义离开动画的持续时间、延迟与速度曲线类型。
6.v-leave-to:离开动画的结束状态。在一个离开动画被触发后的下一帧被添加 (也就是 v-leave-from 被移除的同时),在过渡或动画完成之后移除。
*/
.v-enter-active,
.v-leave-active {
  transition: opacity 0.5s ease;
}

.v-enter-from,
.v-leave-to {
  opacity: 0;
}

5.9 新的组件2 Fragment

  • 在vue2中:组件必须有一个根标签
  • 在vue3中:组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中
  • 好处:减少标签层级,减少内存占用

6.1 新的组件3 Teleport

  • 什么是Teleport? —— Teleport是一种能够将我们组件html结构移动到指定位置的技术(开发的时候非常有用)
//弹窗实现
<script setup>
import { ref,inject } from 'vue';
let isShow = ref(false)
</script>

<template>
  <div>
      <button @click="isShow = true">点我弹窗</button>
      <teleport to="body"> //定位到body
        <div class="mask" v-if="isShow">
            <div class="dialog">
                <h4>我是一个弹窗</h4>
                <h5>内容</h5>
                <h5>内容</h5>
                <h5>内容</h5>
                <button @click="isShow = false">关闭</button>
            </div>
        </div>
      </teleport>
  </div>
</template>

<style>
.dialog{
    width: 300px;
    height: 300px;
    text-align: center;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%,-50%);
    background-color: blueviolet;
    margin: 0 auto;
}
.mask{
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    background-color: rgba(0, 0, 0, 0.5);
}
</style>

6.2 新的组件4 Suspense

  • 等待异步组件时渲染一些后备内容,获得更好的用户体验
<script setup>

import { defineAsyncComponent } from 'vue'; //引入异步组件
const ChildVue = defineAsyncComponent(()=> import('./components/Child.vue')) //这叫做动态引入
//这种引入叫做异步引入,如果app不出来的话,那么Child组件也不会引入进来,有一个先后顺序

// import ChildVue from './components/Child.vue'; //静态引入
// 得等,等所有的组件加载完成之后app才会一起出现

/**
 * Suspense这个标签,底层就内置了插槽,就可以解决异步引入有时候刷新先后出来慢的问题
 * v-slot:default 表示默认的输出组件
 * v-slot:fallback 表示如果页面加载的慢了,会优先展示这个内容,有点像刷新页面的时候数据回来的慢了,就加载一会儿
*/
</script>

<template>
  <div class="app">
    <h3>我是父组件</h3>
    <Suspense>
      <template v-slot:default>
        <ChildVue></ChildVue>
      </template>

      <template v-slot:fallback>
        <h3>稍等,加载中....</h3>
      </template>
    </Suspense>
  </div>
</template>

<style>
.app{
  background-color: gray;
  padding: 10px;
  box-sizing: border-box;
}
</style>
/**还有一种方法就是在子组件中,setup返回一个promise对象,这里之所以可以使用setup返回promise的原因
是: 我们引入的是异步组件且使用了<Suspense></Suspense>
*/

6.3  解决没有this + 各种api的方法

  • 在Vue2项目中可以使用this.$router.push等方法进行路由的跳转,但是在Vue3的setup函数里,并没有this这个概念,因此如何使用路由方法
// 在新的vue-router里面尤大加入了一些方法,比如这里代替this的useRouter,具体使用如下:
//引入路由函数
import { useRouter } from "vue-router";
//使用
setup() {
    //初始化路由
    const router = useRouter();
    router.push({
        path: "/"
    });
    
    return {};
}
  • 在vue2中可以通过this来访问到$refs,vue3中由于没有this所以获取不到了,但是官网中提供了方法来获取:

getCurrentInstance()或者ref(null)

<template>
  <h2 ref="root">姓名</h2>
</template>
<script>
    //使用setup的注意事项
    import { onMounted, ref } from 'vue'
    export default {
        name: 'test9',
        setup(){
            const root  = ref(null)
            onMounted(()=>{
                console.log(root.value);
            })
            return {
                root
            }
        },
    }
</script>

//第二种方法,也可以通过getCurrentInstance来获取
<template>
  <h2 ref="root">姓名</h2>
</template>

<script>
    //使用setup的注意事项
    import { onMounted, ref, getCurrentInstance } from 'vue'

    export default {
        name: 'test9',
        setup(){)
            const {proxy} = getCurrentInstance()
            onMounted(()=>{
                console.log(proxy.$refs.root);
            })
            return {
            }
        },
    }
</script>

七、获取浏览器scrollTop

var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;

八、 Vue操作对象的几种常用方法(isassignkeysvaluesentries)

Object.is()

  • 用来判断两个值是否为同一个值,返回一个布尔类型的值。

Object.assign()

  • 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象,并返回目标对象。

Object.keys()、Object.values()、Object.entries()

  • Object.keys() 返回对象所有属性
  • Object.values() 返回对象所有属性值
  • Object.entries() 返回多个数组,每个数组是 key–value

九、正则表达式

9.1 量词

量词含义如下:

  • {m, },至少出现m次。
  • {m, n},最少出现m次,最多出现n次(最多出现N次的写法为{0, n},而不是{, n})
  • ,匹配0或者1次
  • +,最少匹配1次,与{1, }等价
  • *,匹配任意次,与{0, }等价

9.2 贪婪匹配与非贪婪匹配

  • 贪婪匹配:所有的量词都会尽可能多的进行匹配,默认值。以/a+/'aaa'为例,匹配的结果是'aaa'
  • 非贪婪匹配:所有的两次都会尽可能少的匹配。以/a+?/'aaa'为例,匹配的结果是'a'

贪婪匹配后加上一个?,就表示是一个非贪婪匹配。

注:非贪婪匹配只会向后作用,不会向前作用。即/a+?bb/匹配'aabb'是'aabb',而不是'abb';而/aab+?/则是匹配'aab'。

9.3 分支逻辑 |

9.4 位置匹配

  • ^,匹配开头,多行模式下匹配行开头,即每行开头都会被匹配。

  • $,匹配结尾,多行模式下匹配行结尾,即每行结尾都会被匹配。

  • \b\w\W之间的位置(\w表示[0-9A-Za-z_],而\W就是\w的补集),包括开头结尾(即也包括\w^之间的位置,和\w$之间的位置)

  • \B,与\b相反,\w\w之间的位置,和\W\W之间的位置,包括开头结尾(相对的,即包括\W^之间的位置,和\W$之间的位置)

  • (?=p),正向肯定断言。p是一个子模式,匹配要在p这个模式之前的位置

  • (?!p),正向否定断言。与(?=p)相反,匹配不要在p这个模式之前的位置

const reg1 = /^ab/; //对于字符串'abab'来说,只会匹配到开头的'ab'
const reg2 = /ab$/; //对于字符串'abab'来说,只会匹配到结尾的'ab'
const reg3 = /\b/; //对于字符串'a b'来说,会匹配到'a'前面的位置、'a'' '之间的位置、' ''b'之间的位置、'b'后面的位置
const reg4 = /\B/; //对于字符串'aa bb[/来说,会匹配到'a'和'a'之间的位置、'b'和'b'之间的位置、'['后面的位置
const reg5 = /(?=a)/; //对于字符串'bac'来说,会匹配到'a'之前的位置
const reg6 = /(?!a)/; //对于字符串'bac'来说,会匹配到'b'之前的位置、'c'之前的位置以及'c'之后的位置

9.5 ES2018新特性

在ES2018中,增加了反向肯定断言反向否定断言。具体格式如下:

  • (?<=p),反向肯定断言。p是一个子模式,匹配要在p模式之后的位置
  • (?<!p),反向否定断言。与(?<=p)相反,匹配不要在p模式之后的位置
const reg1 = /(?<=a)b/ //对于字符串'abb'来说,会匹配到'a''b'之间的位置。
const reg2 = /(?<!a)b/ //对于字符串'abb'来说,会匹配到'b''b'之间的位置。

9.6 捕获组与非捕获组 括号()

如果我们在正则表达式中,我们需要获取特定的匹配内容,那么我们就要用到捕获组。捕获组通常使用(p),其中p是一个子模式,表示需要捕获的内容。具体使用示例如下:

const reg = /a(bc)d/;
let result = 'abcd'.match(reg); // 得到的result[1]就是第一个捕获组匹配的字符'bc'

但是,如果我们在一些需要保证优先级的地方使用了小括号,但是又不想成为捕获组来干扰匹配,我们应该怎么办呢?这个时候我们就需要非捕获组。我们只需要在括号最开始加上一个?:即可。具体使用示例如下:

const reg = /a(?:bc)d/;
let result = 'abcd'.match(reg); // 得到的result没有捕获组

全局匹配的时候,result[1]拿到的不一定是捕获组。获取捕获组建议使用RegExp.$1......RegExp.$n

let reg = /a(bc)d/g;  
let result = 'abcdabcd'.match(reg);  
console.log(result) // ["abcd", "abcd"]  
console.log(RegExp.$1) // bc

9.7 匹配特殊字符

正则表达式用-表达区间,但有时我们需要在正则表达式中使用符号-这时就该使用转义字符 \,完整 \-

9.8 单词边界——\b

单词边界:单词符号之间的边界;这里的单词可以指“中文字符,英文字符,数字”;符号可以是“中文符号,英文符号,空格,制表符,换行符”
\b 在所需单词两端的情况: \babc\b

  • \b在字符串前:\babc,只有在abc字符串前没有字符时才可选中

  • \b在字符串后:abc\b,只有在abc字符串后没有字符才可选中

9.9 非捕获分组:非捕获分组只匹配结果,但是不会捕获结果,也不会分配组号

语法:(?:表达式)

十、vue3 补充

10.1 Vue3使用watch监视属性的注意点

10.11 监视ref对象

监视的是ref对象的value属性而不是ref对象本身。

import { ref, watch } from 'vue';
const count = ref(0);
watch(count.value, (newValue, oldValue) => {
  console.log(`count changed from ${oldValue} to ${newValue}`);
});

10.12 监视reactive对象

监视整个对象而不是它的某个属性。

import { reactive, watch } from 'vue';
const state = reactive({
  count: 0,
  message: 'Hello World!'
});
watch(state, (newValue, oldValue) => {
  console.log('state changed', newValue, oldValue);
});

10.13 监视嵌套属性

当监视reactive对象的嵌套属性时,需要在watch函数的选项对象中指定deep选项为true,否则只会监听属性的赋值操作,而不会监听属性内部的变化。

import { reactive, watch } from 'vue';
const state = reactive({
  nested: {
    count: 0
  }
});
watch(
  () => state.nested,
  (newValue, oldValue) => {
    console.log('nested state changed', newValue, oldValue);
  },
  { deep: true }
);

10.14 避免循环引用:

如果reactive对象中包含循环引用,那么watch函数会陷入无限递归,导致程序崩溃。因此,在使用watch函数时需要注意避免循环引用的问题。

import { reactive, watch } from 'vue';
const state = reactive({
  a: 1,
  b: {
    c: 2
  }
});
// 此处会陷入无限递归,导致程序崩溃
state.b.d = state;
watch(
  state,
  (newValue, oldValue) => {
    console.log('state changed', newValue, oldValue);
  },
  { deep: true }
);

10.2 watchEffect函数

Vue3中,watchEffect函数可以用来监视响应式变量的变化并执行副作用函数。当响应式变量发生变化时,副作用函数会被重新执行。

import { reactive, watchEffect } from 'vue'
export default {
  setup() {
    const state = reactive({
      count: 0
    })
    watchEffect(() => {
      console.log('Count changed to:', state.count)
    })
    function increment() {
      state.count++
    }
    return {
      state,
      increment
    }
  }
}

模板中点击增加按钮时,state.count的值会发生变化,watchEffect函数会被重新执行并在控制台中打印新的值。这个过程是自动触发的,不需要显式地定义一个watcher函数。

10.4 Vue3新增

10.41 toReftoRefs

toReftoRefsVue3中的两个新函数,用于创建响应式对象。

  • toRef接收两个参数,第一个参数是响应式对象,第二个参数是该对象的属性名。它会返回一个新的ref对象,该对象指向原始对象的属性,并且任何对该属性的修改都会被响应式地更新到原始对象上。

  • toRefs则接收一个响应式对象,并将其转换为一个包含相同属性名的对象,每个属性都是ref对象。这样做的好处是可以在模板中方便地使用对象的属性而不需要使用.value

import { reactive, toRefs } from 'vue';
const person = reactive({
  name: 'Alice',
  age: 30
});
const personRefs = toRefs(person);
console.log(personRefs.name.value); // 'Alice'
personRefs.age.value++;
console.log(person.age); // 31

10.42 shallowReactive与shallowRef

shallowReactiveshallowRefVue3中的两个新函数,用于创建浅层响应式对象。

  • shallowReactive函数可以将一个普通对象转换成一个响应式对象。与reactive不同的是,shallowReactive只会将对象的第一层属性转换成响应式对象,而不会递归转换嵌套对象的属性。这个函数的作用是可以在需要响应式对象的时候避免不必要的性能开销。

  • shallowRef函数可以将一个普通的值转换成一个ref对象。与ref不同的是,shallowRef只会将值转换成一个ref对象,而不会递归地将值的属性转换成响应式对象。

import { shallowRef } from 'vue';
const person = {
  name: 'Alice',
  age: 30
};
const shallowPersonRef = shallowRef(person);
console.log(shallowPersonRef.value); // {name: "Alice", age: 30}
console.log(shallowPersonRef.value.name); // "Alice"

10.43 readonly与shallowReadonly

readonlyshallowReadonlyVue3中的两个新函数,用于创建只读对象。

  • readonly函数可以将一个普通对象转换成一个只读对象。与reactive不同的是,readonly会将对象的所有属性都转换成只读属性,这样就不能修改对象的属性值了。

  • shallowReadonly函数可以将一个普通对象转换成一个浅层只读对象。与shallowReactive类似,shallowReadonly只会将对象的第一层属性转换成只读属性,而不会递归转换嵌套对象的属性。

10.44 oRaw与markRaw

toRawmarkRawVue3中的两个函数,用于在响应式对象和普通对象之间转换,并标记一个对象为“原始”对象,即不会被转换成响应式对象。

  • toRaw函数可以将一个响应式对象转换成一个普通对象,返回的对象是这个响应式对象的原始对象,不再是响应式对象。
import { reactive, toRaw } from 'vue';
const person = reactive({
  name: 'Alice',
  age: 30
});
const rawPerson = toRaw(person);
console.log(rawPerson === person); // false
console.log(rawPerson.name); // 'Alice'
console.log(rawPerson.age); // 30
  • markRaw函数可以标记一个对象为“原始”对象,这个对象不会被转换成响应式对象。

10.45 provide与inject

provideinjectVue3新增的两个API,用于在父子组件之间传递数据,相比于props和事件,它们可以更方便地在组件树中的任意层次之间进行数据传递,而不需要一级一级地手动传递。

  • 使用provide函数在父组件中提供了一个值为Hello, World!的数据,key是一个Symbol类型的值:
import { provide } from 'vue';
export default {
  setup() {
    provide(Symbol('message'), 'Hello, World!');
    // or provide('message', 'Hello, World!'); 
    // It is recommended to use a Symbol as the key to avoid naming conflicts
  }
}
  • 使用inject函数在子组件中注入了父组件提供的值为Hello, World!的数据,key是一个Symbol类型的值。
import { inject } from 'vue';
export default {
  setup() {
    const message = inject(Symbol('message'));
    // or const message = inject('message');
    console.log(message); // 'Hello, World!'
  }
}

provideinject并不是响应式的,它们只是提供了一个方便的方式在组件之间传递数据。如果需要在子组件中监听父组件提供的数据变化,可以使用响应式数据来实现。

10.46 eleport

Teleport 是Vue3中新增的组件,它可以让你在组件树中的任何位置渲染元素,而不需要受到父组件的限制。这个组件通常用于实现弹出层或者模态框等组件,可以将它的内容渲染到指定的DOM节点上。

Teleport 的用法类似于普通的组件,只需要将需要渲染的内容放在 <Teleport> 标签中即可。其中,to 属性用于指定要渲染到的目标节点,可以是DOM节点或者Vue组件。例如:

<template>
  <button @click="showModal = true">Show Modal</button>
  <Teleport to="#modal">
    <div v-if="showModal" class="modal">
      <h2>Hello World</h2>
      <button @click="showModal = false">Close</button>
    </div>
  </Teleport>
</template>
<script>
import { ref } from 'vue';
export default {
  setup() {
    const showModal = ref(false);
    return {
      showModal,
    };
  },
};
</script>

10.47 Suspense

SuspenseVue3中的一个新特性,它可以让你在异步加载组件时,显示一些占位符或者loading状态,直到组件加载完成后再显示真正的内容。类似于React中的 Suspense 组件。

<template>
  <Suspense>
    <template #default>
      <HelloWorld />
    </template>
    <template #fallback>
      <div>Loading...</div>
    </template>
  </Suspense>
</template>
<script>
import { defineAsyncComponent } from 'vue';

const HelloWorld = defineAsyncComponent(() =>
  import('./components/HelloWorld.vue')
);
export default {
  components: {
    HelloWorld,
  },
};
</script>