vue1

75 阅读4分钟

1. v-bind

<el-popover placement="top-start" :width="400" trigger="click">
    <template #reference>
      <el-button style="margin-right: 10px">我要反馈</el-button>
    </template>
    <section>
        ……
    </section>
  • width 在这里是html的属性而不是css的属性,可以不带单位,默认为px
  • ":" 是v-bind:的简写,用作将JavaScript表达式的值绑定在HTML属性上,也就是说这里的400是表达式的求值结果
  • el-popover: 弹出框,placement->弹出框的位置,triggrt->触发方式

2. template

<template #reference>
  <el-button style="margin-right: 10px">我要反馈</el-button>
</template>

组件就像一个“可复用的盒子”,而插槽(slot)就是盒子里预留的空位,让外部传内容进来

默认插槽(default slot)

<div class="card">
  <slot></slot>
</div>
<MyCard>
  <p>巧克力馅</p>
</MyCard>

vue在渲染时就会将巧克力馅注入模具中:

<div class="card">
  <p>巧克力馅</p>
</div>

所以默认插槽不需要命名,不需要写#default、也不需要 <template>,是靠什么知道要将p放到slot里的?

vue编译器在“编译模板”时,会记录组件关系

vue模板编译器在编译阶段(不是运行时)会做两件事:

  • 它看到<MyCard>不是原生标签(不是div,span),就会认定它是一个组件
  • 它会把<MyCard>里面的内容里面的内容保存为一个插槽函数,在渲染<MyCard>组件时,把这个函数通过slot传进去

vue的具名插槽(named slot)

具名插槽就是给插槽起一个名字,这样父组件可以指定要把内容放到哪个位置,例如:

<el-popover placement="top-start" :width="400" trigger="click">
    <template #reference>
      <el-button style="margin-right: 10px">我要反馈</el-button>
    </template>
</el-popover>

这里没有slot的原因是,它被ElementUI封装在了<el-popover>组件内部:

<template>
  <div>
    <slot name="reference"></slot>
    <slot></slot>
  </div>
</template>
<template #reference>
   <el-button style="margin-right: 10px">我要反馈</el-button>
</template>

2. el-form表单

el-form

const ruleFormRef = ref()
const ruleForm = reactive({
    type: '',
    desc: '',
})
<el-form
  label-position="top"
  ref="ruleFormRef"
  :model="ruleForm"
  :rules="rules"
  label-width="auto"
  class="feedback-ruleForm"
  status-icon
>
  • label-position: 标签相对输入框的位置
  • ref: 为表单设置引用名
  • :model: 绑定表单对象,输入框的值会自动同步到"ruleForm"中
  • label-width="auto":标签宽度自动调整

el-form-item

<el-form-item label="反馈类型" prop="type">
  <el-radio-group v-model="ruleForm.type">
    <el-radio value="BUG" name="type">BUG</el-radio>
    <el-radio value="优化建议" name="type">优化建议</el-radio>
    <el-radio value="其他" name="type">其他</el-radio>
  </el-radio-group>
</el-form-item>

<el-form-item label="描述" prop="desc">
    <el-input v-model="ruleForm.desc"
        type="textarea"
        placeholder="请输入描述信息"
        :autosize="{ minRows: 4}"/>
</el-form-item>
  • el-form-item:表单中的一项,对应一个字段
  • prop:对应表单模型ruleForm中的字段名,用于绑定和验证

v-model:

  • 负责实现双向绑定,将用户输入的数据同步到组件 prop:
  • 用于与表单验证规则配合使用,是一个标识字段,让表单组件知道每个<el-from-item>对应哪个数据字段
  • prop="type" 让 Element Plus 知道这一项验证(例如必填、格式验证等)是作用于 ruleForm.type

验证规则

const rules = reactive({
  type: [
    {
      required: true,
      message: '请选择反馈类型',
      trigger: 'change',
    },
  ],
  desc: [
    {
      required: true,
      message: '请输入描述信息',
      trigger: 'blur',
    }
  ],
})

搭配:

<el-form :rules="rules">

用来告诉表单每个字段的验证条件,提示信息以及触发时间

规则:

{
  required: true,
  message: '请选择反馈类型',
  trigger: 'change',
}
  • required: true 表示这个字段是必填项
  • message: 验证失败时显示的提示文字
  • trigger: 'change' 什么时候触发验证,这里change表示当用户选择或者更改选项时进行验证,blur表示用户离开输入框时触发验证

el-button提交按钮

<el-button type="primary" @click="submitForm(ruleFormRef)">
  提交
</el-button>
  • @click="submitForm(ruleFormRef)": 点击时调用一个名为submitForm方法,并把ruleFormRef(表单引用)作为参数传入

为什么这里传入的是组件实例而不是数据对象 el-form有很多内置方法:

  • validate():触发表单校验
  • resetFields(): 重置所有字段
  • clearValidate():清除校验结果 这些方法是挂载在组件实例上,而不是在数据对象中

校验函数(异步)

const submitForm = async (formEl) => {
  if (!formEl) return
  await formEl.validate((valid, fields) => {
    if (valid) {
      console.log('submit!')
    } else {
      console.log('error submit!')
    }
  })
}

之所以是异步函数,因为validate会遍历表单的每个el-form-item,调用每个字段的validate方法,每个字段可能运行异步校验函数(比如调用后端接口),等所有字段的校验promise都完成后,返回最终结果

也就是说,整个过程需要异步等待所有校验完成

js异步原理

JavaScript本身运行在单线程环境中,同一时间只能执行一个任务,是怎么实现异步的?

异步的关键:事件循环

当你调用异步函数(比如 setTimeoutaxios.getformEl.validate())时,JavaScript不会停下来等待它完成,而是:

  1. 把这项任务交给浏览器或Node.js的底层线程去处理(比如网络请求、定时器等)
  2. JS主线程继续往下执行别的代码
  3. 当异步任务完成时,底层系统会把“回调函数”放进消息队列
  4. JS主线程空下来后,事件循环(Event Loop)会取出回调任务来执行
  • JS脚本在执行时是单线程的,但运行它的环境(比如浏览器或Node.js)是多线程系统

比如浏览器中:

模块作用是否独立线程
JS 引擎线程执行 JS 代码✅ 主线程(唯一)
GUI 渲染线程渲染页面✅ 独立线程
定时器线程管理 setTimeout / setInterval✅ 独立线程
网络线程处理 fetch / XHR 请求✅ 独立线程
事件触发线程处理用户事件、I/O 回调✅ 独立线程