随遇随记篇

vue 函数

unref() 获取原始值

ref 定义的属性 需要 .value 才能拿到值,unref 直接返回原始值;若属性不是ref 定义的,也是直接返回原始值;

TypeScript 复制代码
 /* @description: 是否必填
   */
  required?: boolean | Ref<boolean>

.....

let value = unref(required)

toRefs 为属性添加响应

具体场景:解构方式会使响应式丢失,通过toRefs 添加响应式

复制代码
````
/**
 * @description: 接收父组件参数
 * @param { RouteRecordRaw[] }  value  路由列表
 * @param { boolean } collapsed   是否展开-收起
 */
const props = defineProps({
  value: {
    type: Object as PropType<RouteRecordRaw[]>,
    required: true,
  },
  collapsed: {
    type: Boolean as PropType<boolean>,
    required: true,
    default: () => true,
  },
})


````` 解构会响应式丢失,使用toRefs 为属性添加响应式
// 父组件参数传递给子组件
const { value } = toRefs(props)

console.log(value.value)

useSlots()获取组件的插槽

TypeScript 复制代码
// 获取插槽的名称
const slots = useSlots()
let slotsList: string[] = []
for (const key in slots) {
  slotsList.push(key)
}

vue3中$attrs对象获取非props定义的所有属性

vue3中组件定义了props,但是父组件传了 非 props 定义的属性,可以使用 $attrs 获取

复制代码
``` 父组件传值调用
<table-list
      row-key="id"
      class="customerManagement-table"
      :columns="columns"
      size="small"
      :data-source="tableDataList"
      :pagination="pagination"
      :row-selection="{
        selectedRowKeys: selectedRowKey,
        onChange: onSelectChange,
      }"
      @change="pagination.change"
    />



``` 子组件定义的props
const props = defineProps({
  /**
   * @description: 接收父组件参数
   * @param { Array } columns 表头标题
   */
  columns: {
    type: Array as PropType<Array<ColumnType>>,
    required: true,
    default: () => [],
  },
  pagination: {
    type: [Object, Boolean] as PropType<Pagination | false>,
    default: false,
  }
})

``` 获取 父组件传过来的 data-source 属性,这属性,没有在props上定义
const attrs = useAttrs()
const data = attrs['data-source']

<!-- ParentComponent.vue -->
<template>
  <child-component custom-attr="value"></child-component>
</template>

<!-- ChildComponent.vue -->
<script>
export default {
  mounted() {
    console.log(this.$attrs); // { custom-attr: 'value' }
  }
};
</script>

v-model 为啥能传值给组件

当v-model 传值时,组件内部默认会接收一个value 的值;

vue3组件通信

Vue 组件实例之间通过 props、events、provide/inject、parent/children、attrs/listeners 等方式进行通信,具体取决于组件的关系和需求。这些机制允许组件在不同层级之间传递数据和响应用户交互。

emit 方式自动更新父组件的数据,不用方法触发

当传入

复制代码
 props: {
    importFile: {
      type: Object,
      default: () => {},
    },
    importLoading: {
      type: Boolean,
      default: false,
    },
  },

使用 emit的方式可以自动更新父组件的数据

复制代码
emits: ['update:importLoading', 'update:importFile']

通过emit update 的方式可以自动更新父组件的值
// 这里子组件会给父组件的 importLoading 传值 false, 父组件不需要特别接收处理
emit('update:importLoading', false)

````
// 子组件更新 父组件的 importFile 的值为 file,
emit('update:importFile', file)

<ImportModal
      v-if="importVisible"
      v-model="importVisible"
      v-model:import-loading="importLoading"
      v-model:import-file="importFile"
      template
      @import="handleImportFile"
      @template="handleTemplate"
    />

defineExpose 编译宏命令,暴露子组件方法或属性

vue3 中 当使用<script setup>语法糖时,组件默认不会自动暴露内部的任何属性或方法给外部,为了显示地暴露属性或方法,可以使用 defineExpose

defineProps 获取父组件传过来的值

defineEmits 子通过方法传给父

vite

import.meta.glob

这是vite 提供的特殊功能, 用于动态导入多个模块。

import.meta.glob('./**/*.ts', { eager: true }) 动态导入 当前的 所有 ts 文件,eager:true 是立即导入

复制代码
import { createApp } from 'vue'
import { ObjectExtends } from '@/model/common'

/**
 * @description 加载所有 Plugins (以_开头的文件名不被加载)
 * @param  { ReturnType<typeof createApp> } app 整个应用的实例
 */
export default function loadAllPlugins(app: ReturnType<typeof createApp>): void {
  const files: ObjectExtends = import.meta.glob('./**/*.ts', { eager: true })
  Object.keys(files).forEach((key: string) => {
    if (typeof files[key].default === 'function') {
      const fileName = key.replace('./', '') // Remove './' prefix
      if (!fileName.startsWith('_') && fileName !== 'index.ts') {
        files[key].default(app)
      }
    }
  })
}

css 预处理器

当引入antd 组件,但是需要修改主题风格,需要全局修改变量,例如在variables.less 中定义字体和颜色变量

复制代码
/**
 * antd
 */
@primary-color: #13c2c2; // 全局主色
@danger-color: #f96868; // 危险色
@link-color: #41a3fd; // 链接色
@success-color: #13c2c2; // 成功色
@warning-color: #ff972f; // 警告色
@error-color: #f96868; // 错误色
@font-size-base: 14px; // 主字号
@heading-color: #333333; // 标题色
@text-color: #333333; // 主文本色
@text-color-secondary: #999999; // 次文本色
@disabled-color: #cccccc; // 失效色
@border-radius-base: 2px; // 组件/浮层圆角
@border-color-base: #d9d9d9; // 边框色
@box-shadow-base: 0 2px 8px rgba(0, 0, 0, 0.15); // 浮层阴影
@table-header-bg: #f5f5f5; // 表格头部背景色
@divider-color: #f0f0f0; // 分割线
@component-background: #ffffff; // 底色/背景色
@table-selected-row-bg: #f5f7fa; // 表格选中
@table-selected-row-hover-bg: #f5f7fa; // 表格悬停状态
@border-color: #f0f0f0; // 边框颜色
@border-radius: 2px; // 边框圆角
@border-radius-panel: 4px; // 面板圆角
@success-border-color: #7cdddd; // 成功色边框色
@success-background-color: #e7f9f9; // 成功色背景色
@error-border-color: #fbabab; // 错误色边框色
@error-background-color: #feefef; // 错误色背景色
@warning-border-color: #fec78e; // 警告色边框色
@warning-background-color: #fef8ef; // 警告色背景色

/**
 * 层级
 */
@heder-z-index: 1070;
@sideBar-z-index: 1070;
@progress-z-index: 1085;
@tabs-z-index: 1060;
@tabs-content-z-index: 1065;

/**
 * 常用
 */
@white: #fff;
@gray-1: #9b9b9b;
@gray-2: #e7e7e7;
@gray-3: #3a3a3a;
@gray-4: #4a4a4a;
@gray-5: #cccccc;
@gray-6: #5e5e5e;
@gray-7: #666666;
@gray-8: #999999;
@gray-9: #333333;
@gray-10: #d9d9d9;

以下代码配置:

  1. variables.less 文件作为参考导入,使其变量可用于其他 Less 文件。

  2. 导入 index.less 文件,并将其编译为 CSS 构建过程的一部分。

  3. 启用 Less 中的 JavaScript 支持,允许更动态和灵活的 CSS 代码。

    css: {
    preprocessorOptions: {
    less: {
    // modifyVars:这个选项允许你在 Less 编译过程中修改变量;
    // @import (reference) "./src/assets/css/variables.less" 将一个 Less 文件作为参考导入,而不是直接编译它;
    modifyVars: {
    hack: true; @import (reference) "./src/assets/css/variables.less";,
    },
    // additionalData:这个选项允许你添加额外的数据到 Less 编译过程中;
    additionalData: @import "./src/assets/css/index.less"; ,
    javascriptEnabled: true,
    },
    },
    },

插件

postCss 转换css 代码工具
复制代码
module.exports = {
  plugins: [
    require('postcss-preset-env'),
    require('postcss-flexbugs-fixes'),
    require('tailwindcss'),
    require('autoprefixer'),
  ],
}

1、autoprefixer 自动添加兼容不同浏览器的前缀

2、cssnext 把css新特性转译成当前浏览器中可使用的语法

3、css 模块化,css类名在使用时会被转换成带有唯一标识的形式,避免名称冲突

4、postcss-preset-env‌:支持最新的 CSS 语法并自动转换

5、cssnano‌:压缩和优化 CSS

6、postcss-flexbugs-fixes 尝试解决flex 布局问题的插件

7、tailwindcss 与 post-flexbugs-fixes 结合使用,确保flexbox 布局获得最佳的兼容性

vite:preloadError

vite:preloadError 是 Vite 构建工具在预加载资源时遇到的错误。

预加载是 Vite 的一个特性,用于在应用启动时预加载资源,以提高应用的性能。预加载错误通常是由于资源文件不存在、资源文件格式不正确或资源文件无法被解析等原因引起的。

对错误的处理:

复制代码
<script type="text/javascript">
      //  设置cookie
      function setCookie(name, value, seconds) {
        const date = new Date()
        date.setTime(date.getTime() + seconds * 1000)
        const expires = 'expires=' + date.toUTCString()
        document.cookie = name + '=' + value + ';' + expires + ';path=/'
      }

      //  获取cookie
      function getCookie(name) {
        const nameEQ = name + '='
        const ca = document.cookie.split(';')
        for (let i = 0; i < ca.length; i++) {
          let c = ca[i]
          while (c.charAt(0) === ' ') c = c.substring(1, c.length)
          if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length)
        }
        return null
      }

      window.addEventListener('vite:preloadError', (event) => {
        // 检查 cookie 中是否存在标记
        const firstVisit = getCookie('viteFirstVisit')

        if (!firstVisit) {
          // 第一次进入页面,强制浏览器重新加载
          setCookie('viteFirstVisit', 'true', 30) // 设置 cookie 有效期为30秒
          window.location.reload()
        } else {
          alert(
            '请尝试清除浏览器缓存(Ctrl + Shift + Del)后重新加载页面,或联系我们的技术支持以获取帮助。'
          )
        }
      })
    </script>

nginx.conf.js

是一个用于配置 Nginx 的 JavaScript 文件,它通常在以下情况下被调用:

  1. Vite 开发模式:当你运行 vite 命令启动开发服务器时,Vite 会自动调用 nginx.conf.js 文件来配置 Nginx。

  2. Vite 构建模式:当你运行 vite build 命令构建应用时,Vite 会调用 nginx.conf.js 文件来配置 Nginx,以便在生产环境中使用。

  3. Nginx 启动时:如果你手动启动 Nginx 服务,Nginx 会自动调用 nginx.conf.js 文件来加载配置。

nginx.conf.js 文件中,你可以定义 Nginx 的配置,例如:

  • 设置端口号和监听地址

  • 配置静态资源服务

  • 配置代理服务器

  • 配置 SSL/TLS 加密

  • 等等

    const fs = require('fs')

    const env = process.env.VUE_APP_MODE || 'production'

    function parseApiJsonAndGenerateNginxConfig() {
    let apiJson
    try {
    apiJson = JSON.parse(fs.readFileSync('./api.json').toString('utf-8'))
    } catch (ex) {
    console.error('读取api.json错误', ex)

    复制代码
      return
    }
    
    if (!apiJson) {
      console.error('api.json为空')
      return
    }
    if (!apiJson[env]) {
      console.error(`读取api配置错误, env: ${env}`)
      return
    }
    
    const nginxContent = generateNginxConfig(apiJson, env)
    let filePath
    if (env === 'production') {
      filePath = './nginx.prod.conf'
    } else {
      filePath = './nginx.test.conf'
    }
    
    fs.writeFileSync(filePath, nginxContent)

    }

    /**

    • @description: 跨域配置
      */
      const crossDomainConfig = add_header 'Access-Control-Allow-Origin' '*' always; add_header 'Access-Control-Allow-Headers' '*' always; add_header 'Access-Control-Expose-Headers' '*' always;

    /**

    • @description: 请求预检配置
      */
      const requestOptionsConfig = `
      if (request_method = 'OPTIONS') { {crossDomainConfig}

      Tell client that this pre-flight info is valid for 20 days

      add_header 'Access-Control-Max-Age' 1728000;
      add_header 'Content-Type' 'text/plain; charset=utf-8';
      add_header 'Content-Length' 0;
      return 204;
      }
      `

    /**

    • @description: 请求方法配置
      */
      const requestMethodConfig = ${requestOptionsConfig} if ($request_method = 'POST') { ${crossDomainConfig} } if ($request_method = 'GET') { ${crossDomainConfig} }

    /**

    • 根据api代理配置生成 nginx 配置
    • @param {*} apiJson
    • @param {*} env
    • @returns
      */
      const generateNginxConfig = (apiJson, env) => {
      let tplList = Object.keys(apiJson[env])
      .map((k) => {
      return makeNginxProxy(k, apiJson[env][k])
      })
      .join('\n\n')
    复制代码
    return `server {
    
      # 端口
      listen 80;
    
      server_name localhost;
    
      root /app/dist;
    
      index index.html index.htm;
      include /etc/nginx/default.d/*.conf;
    
      # 上传文件大小限制
      client_max_body_size 200M;   
    
      # 静态资源缓存600秒
      location ~* .(css|js|png|jpg|jpeg|gif|gz|svg|mp4|ogg|ogv|webm|htc|xml|woff)$ {
        ${requestMethodConfig}
        add_header    Cache-Control  max-age=600;
      }
    
      # 静态资源
      location / {
        index index.html;
        try_files $uri $uri/ /index.html;
        add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
        ${requestOptionsConfig}
        ${crossDomainConfig}
      }
    
      # 代理配置
      ${tplList}
    
    }`

    }

    const makeNginxProxy = (apiPath, proxyHttpHost) => location /${apiPath}/ { ${requestMethodConfig} proxy_pass ${proxyHttpHost}/; proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }

    parseApiJsonAndGenerateNginxConfig()

vite.config.ts

1、alias 设置别名

在Node.js中,当你使用ES Modules时,import.meta.url提供了一个当前模块的绝对URL。通过结合使用new URL()构造函数和fileURLToPath()函数,可以动态地获取当前脚本所在目录下的特定文件的实际文件系统路径。这种方法特别适用于需要动态解析文件路径的场景,例如读取或修改文件内容‌。

复制代码
import { fileURLToPath, URL } from 'node:url'

resolve: {
      alias: {
        '@': fileURLToPath(new URL('./src', import.meta.url)),
        '~': fileURLToPath(new URL('./', import.meta.url)),
      },
    },

css 属性

选择器选中 placeholder

::placeholder 是一个伪元素

css 复制代码
input::placeholder {
  color: #666; /* 更改 placeholder 文本颜色 */
  font-size: 12px; /* 更改 placeholder 文本大小 */
}
input::-webkit-input-placeholder {
  color: #666; /* 更改 WebKit 浏览器中的 placeholder 文本颜色 */
}
input:-moz-placeholder {
  color: #666; /* 更改 Mozilla 浏览器中的 placeholder 文本颜色 */
}
input:-ms-input-placeholder {
  color: #666; /* 更改 Microsoft 浏览器中的 placeholder 文本颜色 */
}

更改特定 input 元素的 placeholder 文本样式

css 复制代码
input[type="text"]::placeholder {
  color: #666; /* 更改文本输入框 placeholder 文本颜色 */
}

字体颜色渐变

线性渐变 linear-gradient(),第一个参数是方向 to left | right | top | bottom

background-clip 剪切文字,扣字

css 复制代码
background: linear-gradient(to right, #13c2c2, #f96868);
background-clip: text;
-webkit-background-clip: text;
color: transparent;

CSS 多列布局属性解析:column-count 和 column-gap

column-count 属性用于设置元素的列数,而 column-gap 属性用于设置元素之间的间隔。

column-count 属性确定了元素要分割成的列数。它接受一个整数值,表示要分成的列数。值得注意的是,由于 column-count 只是设定了列数,并没有设定列宽,实际列宽会根据父元素的宽度和列数自动计算得出。

效果如下:

box-sizing 属性

border-box, 元素总宽度和总高度包括了元素的border 边框和padding 内边距,而不仅仅是 content 内容。

content-box,默认值,宽度和高度仅包含内容区域。

cursor css样式-鼠标指针

cursor: w-resize 水平双箭头

鼠标指针形状为水平双箭头,(表示可以左右拉动边界)

cursor: s-resize 垂直双箭头

鼠标指针形状为垂直双箭头,(表示可以向上或向下拉动边界)

resize 属性允许用户拖拉界面

resize: none | both | horizontal | vertical | inherit;

  • none: 不允许用户调整元素的大小

  • both: 允许用户调整元素的宽度和高度

  • horizontal: 允许用户调整元素的宽度

  • vertical: 允许用户调整元素的高度

  • inherit: 从父元素继承 resize 属性值

注意:resize 属性必须与 overflow 属性一起使用,且 overflow 的值不能为 visible

示例:

eval() 函数

是javascript 内置的函数,用于计算字符串表达式的值,即 把字符串转成代码 执行 返回 运算值。例如: eval("3+5"); // 值: 8;不接受return, 会抛出异常。

JSON 转换器

JSON.parse() 是将JSON代码转换成 Javascript 的解析器。JSON.stringfy() 将Javascript对象转换成 JSON字符串。

JS 刷新本页面: window.location.reload()

HTTP

HTTP中常见的 ContentType

HTTP协议是以 ASCll 码传输的,HTTP请求主要有三个部分:状态行、请求头、消息主体。消息主题没有规定必须使用什么编码方式,所以需要在请求头里面设置 content-type 编码方式,消息主体满足这里设定的编码方式即可。服务器端根据 请求头的 Content-Tyoe字段获知 请求中的消息主体使用何种编码方式,再对主体进行解析。

  1. application / x-www-form-urlencoded
    1. 这是表单提交 post 请求常用的 content-type, 提交的数据按照key1=val1&key2=val2&key3=val3&key4=val4 的方式编码,其中 key和val 都会进行 url 转码。
  2. multipart / form-data
    1.
  3. applicetion / json
  4. text / xml

responseType 响应类型

如果返回的数据是 文件流,要设置响应头格式为 arraybuffer

复制代码
/**
 * 接口返回一个导出的文件流,
 * 请求方法里面加上响应请求头类型 arraybuffer 
 */
export const customerApiExport = (
  data: PaginationReq<AdvancedQueryReq>
): Promise<AxiosResponse> => {
  return request.post(`${apiProxy}/sc/consumer-account/exportList`, data, {
    responseType: 'arraybuffer',
  })
}

文件流数据处理,使用new Blob() 存储二进制数据,

复制代码
 // 获取导出的文件流
  customerApiExport(tableDataReq.value)
    .then((res) => {
      const blob = new Blob([res.data], { type: res.headers['content-type'] })
      const url = window.URL.createObjectURL(blob)
      const a = document.createElement('a')
      a.href = url
      a.download = '客户信息'
      a.click()
      window.URL.revokeObjectURL(url)
    })
    .finally(() => {
      exportBntLoading.value = false
    })

responseType值的类型

|-------------|---------------------------------------------------------------------|
| 值 | 数据类型 |
| '' | DOMString(默认类型) |
| arraybuffer | ArrayBuffer对象 |
| blob | Blob对象 |
| document | Documnet对象 |
| json | JavaScript object, parsed from a JSON string returned by the server |
| text | DOMString |

http1 、http2、http3

http1.0

非持久性连接,每次请求都要重新建立连接;

浏览器客户端在同一时间针对同一域名下的请求有一定的数量限制,超出数目的请求会被堵塞;

数据是文本形式传输;

http1.1

引入了更多缓存控制策略

优化了宽带

新增状态响应码

头部压缩

服务端推送

支持使用 HTTPS 进行加密传输

http2.0 采用多路复用、二进制分帧传输

http3.0 基于 UDP 协议的 QUIC 协议

  1. 线头阻塞问题更加彻底

    1. 基于TCP的Http2,尽管在逻辑上相互独立,不会相互影响,但在实际中,数据是 一帧一帧发送和接收,但是一旦某一个流的数据有丢包,则同样会阻塞在它之后的传输流数据传输。而基于UDP的Http3.0就没有这个问题,能够让不同的流之间实现真正的独立。
  2. 网络切换时连接保持

    1. 在当前移动端的应用环境,用户网络会经常切换,基于TCP的协议,由于切换网络之后,IP会改变,所以说之前建立的连接会断开。而基于UDP的Http3.0,则可以内建与TCP中不同的连接标识方法,从而在网络完成切换后,恢复之前的服务器连接

other

如果一个字段 为 undefined,则不会上传到接口

复制代码
...(formData.value?.configInfo?.length
        ? { configInfo: formData.value.configInfo.join(',') }
        : {}),

const params: UpdateConfigReq = {
      id: formData.value.id,
      isNewSystem: formData.value.isNewSystem,
      configInfo: formData.value.configInfo?.join(',') || undefined,
    }

?? 和 || 的区别

用法相似:两者都是二元运算符,前后各有一个值,并根据前面的值决定返回哪个值

如:

  • a ?? b

  • a || b

?? 空值合并运算符

如果前面的变量是 null 或者 undefined ,则会执行 ?? 后面的代码。

仅在前面的值为 nullundefined 时返回后面的值。

|| 逻辑或运算符

如果前面的变量是 假值,则会执行 || 后面的代码

当前面的值为任何"假值"(如 false0NaN、空字符串 ""nullundefined)时返回后面的值。

相关推荐
东风破_17 分钟前
LLM 为什么记不住你?无状态、messages 和上下文管理
javascript·人工智能
梨子同志26 分钟前
qiankun
前端
梨子同志34 分钟前
Nginx
前端
梨子同志36 分钟前
NestJS
前端
梨子同志1 小时前
Next.js
前端
谭光志1 小时前
工具塞满上下文窗口怎么办?深度拆解 AI Agent Tool Search 按需加载实现原理
前端·后端·ai编程
Hilaku1 小时前
前端架构师的克制:什么时候该坚决对新技术说不?
前端·javascript·程序员
陈随易1 小时前
前端项目部署只要30秒
前端·后端·程序员
本末倒置1832 小时前
从 Vue CLI 迁移到 Rsbuild 后,JSX 突然全挂了?一次排查记录
前端
古夕2 小时前
多应用同域部署下 Favicon 异常问题排查与治理
前端·javascript