<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
        <title>901web前端博客</title>
        <link>https://901web.com</link>
        <description>pybyongbo的博客</description>
        <atom:link href="https://901web.com/rss.html" rel="self" />
        <language>zh-cn</language>
        <lastBuildDate>Fri, 03 Apr 2026 22:19:38 GMT</lastBuildDate>
        <item>
            <title>Vue3中Element Plus日期范围选择组件二次封装,方便数据提交</title>
            <link>https://901web.com/post/vue3-element-plus-date-picker-field-submit.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#%E6%A0%B8%E5%BF%83%E7%9B%AE%E6%A0%87"><strong>核心目标</strong></a></li>
<li><a href="#%E5%B0%81%E8%A3%85%E6%80%9D%E8%B7%AF"><strong>封装思路</strong></a><ul>
<li><a href="#%E8%BF%99%E4%B8%AA%E7%BB%84%E4%BB%B6%E5%81%9A%E4%BA%86%E4%BB%80%E4%B9%88"><strong>这个组件做了什么？</strong></a></li>
<li><a href="#toc-f58">父组件使用</a></li>
</ul>
</li>
</ul>
</div><p>在做后台管理系统的时候,搜索条件经常会有日期范围选择.ElementPlus日期范围组件选择的日期值返回的是一个数组.</p>
<p>但是提交的时候,后台有时候是分成两个字段进行接收.</p>
<blockquote>
<p>封装一个通用的 <strong>日期范围组件</strong>（基于 Element Plus 的 <el-date-picker type="daterange">），</p>
</blockquote>
<blockquote>
<p>让前端使用时简单（用数组表示），</p>
</blockquote>
<blockquote>
<p>但提交给后端时，能自动映射为两个独立字段：startTime 和 endTime。</p>
</blockquote>
<h2><a id="toc-dad" class="anchor" href="#toc-dad"></a><strong>核心目标</strong></h2>
<ol>
<li><p><strong>组件内部</strong> 使用 v-model 绑定一个日期数组（[开始时间, 结束时间]）；</p>
</li>
<li><p><strong>对外暴露</strong> 时，可以自动：</p>
</li>
</ol>
<ul>
<li>发出完整的数组；</li>
<li>或者同时发出 { startTime, endTime } 两个字段；</li>
</ul>
<ol start="3">
<li>支持在父组件中自定义字段名（例如后端是 begin_date / end_date）。</li>
</ol>
<h2><a id="toc-693" class="anchor" href="#toc-693"></a><strong>封装思路</strong></h2>
<p>我们可以封装一个组件，比如叫 <strong>DateRangePicker.vue</strong></p>
<pre><code class="hljs lang-undefined">// DateRangePicker.vue 组件
&lt;template&gt;
  &lt;el-date-picker
    v-model="innerValue"
    type="daterange"
    range-separator="至"
    start-placeholder="开始日期"
    end-placeholder="结束日期"
    value-format="YYYY-MM-DD"
    @change="handleChange"
    style="width: 100%"
  /&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { computed, ref, watch } from "vue";

const props = defineProps({
  modelValue: {
    type: Array,
    default: () =&gt; [],
  },
  startKey: {
    type: String,
    default: "startTime",
  },
  endKey: {
    type: String,
    default: "endTime",
  },
});

const emits = defineEmits(["update:modelValue", "update:fields", "change"]);

// 内部绑定值
const innerValue = ref(props.modelValue);

// 监听外部更新
watch(
  () =&gt; props.modelValue,
  (val) =&gt; {
    innerValue.value = val;
  }
);

// 当值变化时，发出两个不同字段
const handleChange = (val) =&gt; {
  emits("update:modelValue", val);

  if (Array.isArray(val) &amp;&amp; val.length === 2) {
    emits("update:fields", {
      [props.startKey]: val[0],
      [props.endKey]: val[1],
    });
  } else {
    emits("update:fields", {
      [props.startKey]: "",
      [props.endKey]: "",
    });
  }

  emits("change", val);
};
&lt;/script&gt;
</code></pre>
<h3><a id="toc-3a2" class="anchor" href="#toc-3a2"></a><strong>这个组件做了什么？</strong></h3>
<table>
<thead>
<tr>
<th><strong>功能</strong></th>
<th><strong>说明</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>v-model 数组绑定</td>
<td>让父组件用 [start, end] 来操作</td>
</tr>
<tr>
<td>自动转换成对象</td>
<td>通过 @update:fields 发出 { startTime, endTime }</td>
</tr>
<tr>
<td>字段名可配置</td>
<td>父组件可用 startKey=&quot;beginDate&quot; endKey=&quot;endDate&quot; 来适配后端字段</td>
</tr>
</tbody>
</table>
<h3><a id="toc-f58" class="anchor" href="#toc-f58"></a>父组件使用</h3>
<pre><code class="hljs lang-undefined">&lt;template&gt;
  &lt;div style="width: 300px; margin: 20px;"&gt;
    &lt;DateRangePicker
      v-model="range"
      @update:fields="handleDateFields"
      startKey="beginDate"
      endKey="endDate"
    /&gt;

    &lt;div style="margin-top: 20px;"&gt;
      &lt;p&gt;当前日期数组: {{ range }}&lt;/p&gt;
      &lt;p&gt;提交给后端的对象: {{ dateFields }}&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref } from "vue";
import DateRangePicker from "./DateRangePicker.vue";

const range = ref(["2025-01-01", "2025-01-15"]);
const dateFields = ref({ beginDate: "", endDate: "" });

const handleDateFields = (val) =&gt; {
  dateFields.value = val;
};
&lt;/script&gt;
</code></pre>

            ]]></description>
            <pubDate>Sun, 28 Dec 2025 13:21:32 GMT</pubDate>
            <guid>https://901web.com/post/vue3-element-plus-date-picker-field-submit.html</guid>
        </item>
        <item>
            <title>对Vue框架中一些相关概念的理解和总结</title>
            <link>https://901web.com/post/vue-understanding-and-summarizing.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-39a">vue 模版为什么必须是单根的?</a></li>
<li><a href="#toc-658">对vue中模版语法理解</a></li>
<li><a href="#toc-668">对vue3中定义响应式数据(ref和reactive)的理解</a></li>
<li><a href="#toc-479">vue computed的回调什么时候执行?</a></li>
<li><a href="#toc-ac9">响应式系统的核心是什么?</a></li>
<li><a href="#toc-f08">静态资源的动态访问(Vite打包构建项目)</a></li>
</ul>
</div><p>最近公司的项目在用Vue3做相关的开发,之前也接触过Vue2的版本,现在重新温故了一下vue相关的知识点,对一些相关概念的理解做下自己的总结</p>
<!--more-->
<h3><a id="toc-39a" class="anchor" href="#toc-39a"></a>vue 模版为什么必须是单根的?</h3>
<p>注意:这是因为虚拟节点树必须是单根的,所以模版必须是单根的.</p>
<h3><a id="toc-658" class="anchor" href="#toc-658"></a>对vue中模版语法理解</h3>
<ol>
<li>模版语法</li>
<li>template -&gt; HTML字符串 -&gt; Vue特性 -&gt; 表达式/属性/指令</li>
<li>Vue的模版都是基于HTML</li>
<li>模版中直接写HTML都是能够被HTML解析器解析的</li>
<li>Vue -&gt; 表达式/自定义属性/指令</li>
<li>Vue有一套自己的模版编译系统</li>
<li>开发者写的template -&gt; 分析 HTML 字符串 -&gt; AST语法树 -&gt; 表达式/自定义属性/指令 -&gt; 虚拟DOM树 -&gt; 解析真实DOM -&gt;  render</li>
</ol>
<h3><a id="toc-668" class="anchor" href="#toc-668"></a>对vue3中定义响应式数据(ref和reactive)的理解</h3>
<p>如果你直接对整个reactive定义的对象进行赋值改变，确实不会触发视图的更新。这是因为reactive函数只会将对象的属性转换为响应式，而不会将整个对象转换为响应式。 </p>
<p>如果你想要实现整个对象的动态变化并触发视图更新，可以使用ref函数将整个对象转换为响应式。然后，你可以通过修改value属性来改变整个对象。</p>
<p>在Vue 3中，使用ref函数将一个值转换为响应式对象时，会返回一个包装对象，该对象具有一个<code>.value</code>属性来访问原始值。</p>
<p>这是因为Vue 3中引入了新的响应式系统，将响应式对象的访问和修改分离开来，以提高性能。</p>
<h3><a id="toc-479" class="anchor" href="#toc-479"></a>vue computed的回调什么时候执行?</h3>
<p>必须满足以下两个条件:</p>
<ol>
<li>读取到计算属性</li>
<li>当前计算属性是脏数据 (isDirty) <ul>
<li>2-1 初始化后 isDirty = true</li>
<li>2-2  读取后 isDirty = false</li>
<li>2-3 计算属性依赖的数据变化 (isDirty = true)</li>
</ul>
</li>
</ol>
<h3><a id="toc-ac9" class="anchor" href="#toc-ac9"></a>响应式系统的核心是什么?</h3>
<p>就是: 函数和数据的关联,当数据发生改变,函数重新执行.</p>
<h3><a id="toc-f08" class="anchor" href="#toc-f08"></a>静态资源的动态访问(Vite打包构建项目)</h3>
<p>依赖分析: (编译时态),并没有运行代码,无法知道变量的值是多少.</p>
<p>自动依赖发现</p>
<ol>
<li>多媒体元素的静态链接</li>
<li>样式中的静态链接</li>
<li>动态导入语句中的静态或半静态链接</li>
<li>URL构造器中的静态或半静态链接</li>
</ol>

            ]]></description>
            <pubDate>Sun, 06 Jul 2025 03:49:50 GMT</pubDate>
            <guid>https://901web.com/post/vue-understanding-and-summarizing.html</guid>
        </item>
        <item>
            <title>Element plus form 校验规则提出去啦.如何进行值的关联校验?</title>
            <link>https://901web.com/post/element-plus-form-relation-validate.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-d42">1. 校验规则抽离</a></li>
<li><a href="#toc-162">2. 实现值的关联校验</a></li>
<li><a href="#toc-38b">3. 在组件中使用</a></li>
<li><a href="#toc-763">4. 总结</a></li>
</ul>
</div><p>在 Element Plus 中，表单校验规则可以单独抽离到一个对象中进行统一管理。如果需要实现“值的关联校验”（如密码和确认密码一致、开始时间小于结束时间等），可以通过自定义校验函数实现。</p>
<!--more-->
<h3><a id="toc-d42" class="anchor" href="#toc-d42"></a>1. 校验规则抽离</h3>
<pre><code class="hljs lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> formRules = {
  <span class="hljs-attr">username</span>: [
    { <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">message</span>: <span class="hljs-string">'请输入用户名'</span>, <span class="hljs-attr">trigger</span>: <span class="hljs-string">'blur'</span> }
  ],
  <span class="hljs-attr">password</span>: [
    { <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">message</span>: <span class="hljs-string">'请输入密码'</span>, <span class="hljs-attr">trigger</span>: <span class="hljs-string">'blur'</span> }
  ],
  <span class="hljs-attr">confirmPassword</span>: [
    { <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">message</span>: <span class="hljs-string">'请再次输入密码'</span>, <span class="hljs-attr">trigger</span>: <span class="hljs-string">'blur'</span> },
    <span class="hljs-comment">// 关联校验在下面</span>
  ]
};
</code></pre>
<h3><a id="toc-162" class="anchor" href="#toc-162"></a>2. 实现值的关联校验</h3>
<p>以“密码”和“确认密码”一致为例：</p>
<pre><code class="hljs lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> formRules = <span class="hljs-function">(<span class="hljs-params">form</span>) =&gt;</span> ({
  <span class="hljs-attr">password</span>: [
    { <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">message</span>: <span class="hljs-string">'请输入密码'</span>, <span class="hljs-attr">trigger</span>: <span class="hljs-string">'blur'</span> }
  ],
  <span class="hljs-attr">confirmPassword</span>: [
    { <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">message</span>: <span class="hljs-string">'请再次输入密码'</span>, <span class="hljs-attr">trigger</span>: <span class="hljs-string">'blur'</span> },
    {
      <span class="hljs-attr">validator</span>: <span class="hljs-function">(<span class="hljs-params">rule, value, callback</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (value !== form.password) {
          callback(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'两次输入的密码不一致'</span>));
        } <span class="hljs-keyword">else</span> {
          callback();
        }
      },
      <span class="hljs-attr">trigger</span>: <span class="hljs-string">'blur'</span>
    }
  ]
});
</code></pre>
<h3><a id="toc-38b" class="anchor" href="#toc-38b"></a>3. 在组件中使用</h3>
<pre><code class="hljs lang-undefined">&lt;script setup&gt;
import { reactive, ref } from 'vue';
import { formRules } from '@/rules/formRules.js';

const form = reactive({
  username: '',
  password: '',
  confirmPassword: ''
});

const rules = formRules(form);
&lt;/script&gt;

&lt;template&gt;
  &lt;el-form :model="form" :rules="rules" ref="formRef"&gt;
    &lt;el-form-item label="用户名" prop="username"&gt;
      &lt;el-input v-model="form.username" /&gt;
    &lt;/el-form-item&gt;
    &lt;el-form-item label="密码" prop="password"&gt;
      &lt;el-input v-model="form.password" type="password" /&gt;
    &lt;/el-form-item&gt;
    &lt;el-form-item label="确认密码" prop="confirmPassword"&gt;
      &lt;el-input v-model="form.confirmPassword" type="password" /&gt;
    &lt;/el-form-item&gt;
    &lt;el-button @click="submit"&gt;提交&lt;/el-button&gt;
  &lt;/el-form&gt;
&lt;/template&gt;
</code></pre>
<p><img src="https://images.901web.com/blog901web/form-validate.png" alt="表单校验"></p>
<h3><a id="toc-763" class="anchor" href="#toc-763"></a>4. 总结</h3>
<ul>
<li>校验规则可以单独抽离成模块。</li>
<li>关联校验通过自定义 <code>validator</code>，并传入整个表单对象实现。</li>
<li>只要在 <code>validator</code> 里能访问到其他字段的值，就能实现各种关联校验。</li>
</ul>
<p>如需更多复杂校验，可继续扩展 <code>validator</code> 函数逻辑。</p>

            ]]></description>
            <pubDate>Sun, 06 Jul 2025 03:27:34 GMT</pubDate>
            <guid>https://901web.com/post/element-plus-form-relation-validate.html</guid>
        </item>
        <item>
            <title>分页打印 el-table时,如何为每一页增加表头显示(vue2/vue3)</title>
            <link>https://901web.com/post/el-table-print-thead.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-bd3">分页打印 el-table时,如何为每一页增加表头(vue2/vue3)</a><ul>
<li><a href="#toc-e9b">Vue2主要代码:</a></li>
<li><a href="#toc-0aa">vue3 主要代码:</a></li>
</ul>
</li>
</ul>
</div><h3><a id="toc-bd3" class="anchor" href="#toc-bd3"></a>分页打印 el-table时,如何为每一页增加表头(vue2/vue3)</h3>
<p>在做公司项目需求的时候,遇到了一个打印表格的问题,产品项每一页都显示表头内容.项目用的是vue+element ui相关的技术栈</p>
<p>记录一下实现过程中遇到的问题.(实现思路都是相同的,主要是写法优点不同),在实现的过程中还是会有各种问题,打印预览第一页不显示,显示不全或者被截断的情况.</p>
<!--more-->
<p>如果使用的是Vue2版本的话,UI组件时Element UI. el-table组件的表格的表头和内容是分开的.主要是为了实现表头固定效果.我们可以复制一份表格的表头,插入到下面的内容中.然后打印的时候,影藏掉原先表格的表头.</p>
<h4><a id="toc-e9b" class="anchor" href="#toc-e9b"></a>Vue2主要代码:</h4>
<p>html代码部分</p>
<pre><code class="hljs lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">el-table</span> <span class="hljs-attr">:data</span>=<span class="hljs-string">"tableData"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"mytable"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">el-table-column</span> <span class="hljs-attr">prop</span>=<span class="hljs-string">"date"</span> <span class="hljs-attr">label</span>=<span class="hljs-string">"日期"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"180"</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">el-table-column</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">el-table-column</span> <span class="hljs-attr">prop</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">label</span>=<span class="hljs-string">"姓名"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"180"</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">el-table-column</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">el-table-column</span> <span class="hljs-attr">prop</span>=<span class="hljs-string">"address"</span> <span class="hljs-attr">label</span>=<span class="hljs-string">"地址"</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">el-table-column</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">el-table</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>javascript代码部分</p>
<pre><code class="hljs lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  data() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">tableData</span>: [{
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-02'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1518 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-04'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1517 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-01'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1519 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-03'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1516 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-02'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1518 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-04'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1517 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-01'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1519 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-03'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1516 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-02'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1518 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-04'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1517 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-01'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1519 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-03'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1516 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-02'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1518 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-04'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1517 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-01'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1519 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-03'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1516 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-02'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1518 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-04'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1517 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-01'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1519 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-03'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1516 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-02'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1518 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-04'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1517 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-01'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1519 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-03'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1516 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-02'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1518 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-04'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1517 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-01'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1519 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-03'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1516 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-02'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1518 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-04'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1517 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-01'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1519 弄'</span>
      }, {
        <span class="hljs-attr">date</span>: <span class="hljs-string">'2016-05-03'</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">'王小虎'</span>,
        <span class="hljs-attr">address</span>: <span class="hljs-string">'上海市普陀区金沙江路 1516 弄'</span>
      }]
    }
  },
  <span class="hljs-attr">mounted</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// 等待DOM加载完成后再执行</span>
    <span class="hljs-keyword">this</span>.$nextTick(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
      <span class="hljs-keyword">let</span> thead = <span class="hljs-keyword">this</span>.$refs.mytable.$el.querySelector(<span class="hljs-string">'.el-table__header-wrapper thead'</span>);
      <span class="hljs-keyword">let</span> theadNew = thead.cloneNode(<span class="hljs-literal">true</span>);
      <span class="hljs-keyword">this</span>.$refs.mytable.$el.querySelector(<span class="hljs-string">'.el-table__body-wrapper table'</span>).appendChild(theadNew);
    });
  },
}
</code></pre>
<p>样式代码,@media print 媒体查询打印的时候样式.</p>
<pre><code class="hljs lang-css"><span class="hljs-selector-class">.el-table</span>&gt;&gt;&gt;<span class="hljs-selector-class">.el-table__body-wrapper</span> <span class="hljs-selector-tag">table</span> <span class="hljs-selector-tag">thead</span> {
  <span class="hljs-attribute">display</span>: none;
}

<span class="hljs-keyword">@media</span> print {
  <span class="hljs-selector-class">.el-table</span>&gt;&gt;&gt;<span class="hljs-selector-class">.el-table__header-wrapper</span> {
    <span class="hljs-attribute">display</span>: none;
  }

  <span class="hljs-selector-class">.el-table</span>&gt;&gt;&gt;<span class="hljs-selector-class">.el-table__body-wrapper</span> <span class="hljs-selector-tag">table</span> <span class="hljs-selector-tag">thead</span> {
    <span class="hljs-attribute">display</span>: table-header-group;
  }
}
</code></pre>
<p><img src="https://901web.com/static/upload/20250706/06cbea73-d1a4-472d-a360-cb2a169c0edb.png" alt="vue2打印截图"></p>
<h4><a id="toc-0aa" class="anchor" href="#toc-0aa"></a>vue3 主要代码:</h4>
<p>html代码部分</p>
<pre><code class="hljs lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"no-print"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">el-button</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"handlePrint"</span>&gt;</span>打印表格<span class="hljs-tag">&lt;/<span class="hljs-name">el-button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">el-table</span>
      <span class="hljs-attr">ref</span>=<span class="hljs-string">"elTableRef"</span>
      <span class="hljs-attr">:data</span>=<span class="hljs-string">"tableData"</span>
      <span class="hljs-attr">style</span>=<span class="hljs-string">"width: 100%; margin-top: 20px"</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">el-table-column</span> <span class="hljs-attr">prop</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">label</span>=<span class="hljs-string">"姓名"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"180"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">el-table-column</span> <span class="hljs-attr">prop</span>=<span class="hljs-string">"age"</span> <span class="hljs-attr">label</span>=<span class="hljs-string">"年龄"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"100"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">el-table-column</span> <span class="hljs-attr">prop</span>=<span class="hljs-string">"address"</span> <span class="hljs-attr">label</span>=<span class="hljs-string">"地址"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"150"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">el-table-column</span> <span class="hljs-attr">prop</span>=<span class="hljs-string">"job"</span> <span class="hljs-attr">label</span>=<span class="hljs-string">"职业"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"150"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">el-table-column</span> <span class="hljs-attr">prop</span>=<span class="hljs-string">"salary"</span> <span class="hljs-attr">label</span>=<span class="hljs-string">"工资"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"150"</span> /&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">el-table</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>javascript代码</p>
<pre><code class="hljs lang-javascript"><span class="hljs-comment">// setup语法</span>
<span class="hljs-keyword">import</span> { ref, nextTick,onMounted } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>;
<span class="hljs-keyword">const</span> elTableRef = ref();
<span class="hljs-keyword">const</span> tableData = <span class="hljs-built_in">Array</span>.from({ <span class="hljs-attr">length</span>: <span class="hljs-number">100</span> }, (_, i) =&gt; ({
  <span class="hljs-attr">name</span>: <span class="hljs-string">`张三 <span class="hljs-subst">${i + <span class="hljs-number">1</span>}</span>`</span>,
  <span class="hljs-attr">age</span>: <span class="hljs-number">20</span> + (i % <span class="hljs-number">10</span>),
  <span class="hljs-attr">address</span>: <span class="hljs-string">`中国某地 <span class="hljs-subst">${i + <span class="hljs-number">1</span>}</span>`</span>,
  <span class="hljs-attr">job</span>: <span class="hljs-string">`职位 <span class="hljs-subst">${i + <span class="hljs-number">1</span>}</span>`</span>,
  <span class="hljs-attr">salary</span>: <span class="hljs-string">'¥'</span>+(<span class="hljs-number">3000</span> + i * <span class="hljs-number">100</span>).toFixed(<span class="hljs-number">2</span>)
}));


onMounted(<span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-comment">// console.log(elTableRef.value);</span>
   <span class="hljs-keyword">await</span> nextTick();

   setTimeout(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> tableEl = elTableRef.value?.$el;
  <span class="hljs-keyword">if</span> (!tableEl) <span class="hljs-keyword">return</span>;


  <span class="hljs-keyword">const</span> headerTable = tableEl.querySelector(<span class="hljs-string">'.el-table__header-wrapper table'</span>);
  <span class="hljs-keyword">const</span> bodyTable = tableEl.querySelector(<span class="hljs-string">'.el-table__body-wrapper table'</span>);


  <span class="hljs-comment">// 先清理旧的 thead（防止重复插入）</span>
  <span class="hljs-keyword">const</span> existingClonedThead = bodyTable.querySelector(<span class="hljs-string">'thead.clone-thead'</span>);
  <span class="hljs-keyword">if</span> (existingClonedThead) {
    existingClonedThead.remove();
  }

  <span class="hljs-keyword">if</span> (headerTable &amp;&amp; bodyTable) {
    <span class="hljs-keyword">const</span> thead = headerTable.querySelector(<span class="hljs-string">'thead'</span>);
    <span class="hljs-keyword">if</span> (thead) {
      <span class="hljs-keyword">const</span> clonedThead = thead.cloneNode(<span class="hljs-literal">true</span>);
      <span class="hljs-built_in">console</span>.log(<span class="hljs-number">555</span>, clonedThead);
      clonedThead.classList.add(<span class="hljs-string">'clone-thead'</span>); <span class="hljs-comment">// 标记用于清理</span>
      bodyTable.insertBefore(clonedThead, bodyTable.firstChild);
    }
  }
   }, <span class="hljs-number">100</span>);
});

<span class="hljs-keyword">const</span> handlePrint = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">await</span> nextTick();
  <span class="hljs-keyword">const</span> tableEl = elTableRef.value?.$el;
  <span class="hljs-keyword">if</span> (!tableEl) <span class="hljs-keyword">return</span>;
  <span class="hljs-keyword">const</span> headerTable = tableEl.querySelector(<span class="hljs-string">'.el-table__header-wrapper table'</span>);
  <span class="hljs-keyword">const</span> bodyTable = tableEl.querySelector(<span class="hljs-string">'.el-table__body-wrapper table'</span>);
  <span class="hljs-comment">// 先清理旧的 thead（防止重复插入）</span>
  <span class="hljs-keyword">const</span> existingClonedThead = bodyTable.querySelector(<span class="hljs-string">'thead.clone-thead'</span>);
  <span class="hljs-keyword">if</span> (existingClonedThead) {
    existingClonedThead.remove();
  }
  <span class="hljs-keyword">if</span> (headerTable &amp;&amp; bodyTable) {
    <span class="hljs-keyword">const</span> thead = headerTable.querySelector(<span class="hljs-string">'thead'</span>);
    <span class="hljs-keyword">if</span> (thead) {
      <span class="hljs-keyword">const</span> clonedThead = thead.cloneNode(<span class="hljs-literal">true</span>);
      clonedThead.classList.add(<span class="hljs-string">'clone-thead'</span>); <span class="hljs-comment">// 标记用于清理</span>
      bodyTable.insertBefore(clonedThead, bodyTable.firstChild);
    }
  }

  <span class="hljs-built_in">window</span>.print();

  <span class="hljs-comment">// 打印后可选清除</span>
  setTimeout(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> cloned = bodyTable.querySelector(<span class="hljs-string">'thead.clone-thead'</span>);
    <span class="hljs-keyword">if</span> (cloned) cloned.remove();
  }, <span class="hljs-number">500</span>);
};
</code></pre>
<p>样式部分:</p>
<pre><code class="hljs lang-undefined">&lt;style&gt;
.clone-thead{
  display: none;
}
@media print {
  tr {
    break-inside: avoid; 
    page-break-inside: avoid;
  }
  .el-scrollbar__view {
    display: block !important;
  }
  .el-table__inner-wrapper {
    height: auto;
  }
  .el-scrollbar {
    height: auto !important;
  }
}
  @media print {
    :deep(.el-table__header-wrapper table thead) {
      display: none!important;
    }
    .clone-thead {
      display: table-header-group;
    }
  }

  @media print {
  .el-table__header-wrapper {
    display: none !important;
  }
  thead {
    display: table-header-group !important;
  }
  tfoot {
    display: table-footer-group !important;
  }
  .no-print {
    display: none !important;
  }
  tr {
    page-break-inside: avoid;
  }
  .el-table__body-wrapper table,
  .el-table__body-wrapper table thead.clone-thead,
  .el-table__body-wrapper table tbody {
    border-collapse: collapse !important;
    border-spacing: 0 !important;
    table-layout: fixed !important;
    width: 100% !important;
  }
}
&lt;/style&gt;
</code></pre>
<p><img src="https://901web.com/static/upload/20250706/38c553fc-88d2-480e-89ca-7c1ec9fcdaca.png" alt="vue3打印截图"></p>

            ]]></description>
            <pubDate>Sun, 06 Jul 2025 02:12:51 GMT</pubDate>
            <guid>https://901web.com/post/el-table-print-thead.html</guid>
        </item>
        <item>
            <title>Vue3+Vite 动态引入图片打包后生产环境不显示记录</title>
            <link>https://901web.com/post/vue3-vite-dynamic-image-path.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-115">Vue3+vite 项目中动态引入图片(非 public 目录)</a><ul>
<li><a href="#toc-8f4">开发环境截图</a></li>
<li><a href="#toc-2d1">glob 导入 动态引入图片</a></li>
<li><a href="#toc-f64">生产环境截图</a></li>
<li><a href="#toc-930">逻辑部分 JS 代码(setup 语法糖)</a></li>
</ul>
</li>
<li><a href="#toc-a00">使用 new URL() 的方法进行动态导入</a></li>
</ul>
</div><h3><a id="toc-115" class="anchor" href="#toc-115"></a>Vue3+vite 项目中动态引入图片(非 public 目录)</h3>
<p>今天在做项目的时候，使用动态路由配置,发现在 <code>vue3+vite</code> 项目中，动态引入图片，在开发环境中的时候,图片可以正常显示,但是打包构建后,部署生产找不到图片的,经过一番搜索,找到了解决方法,如下：</p>
<!--more-->
<p>首先模版中的代码:</p>
<pre><code class="hljs lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"eggs-con"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span>
      <span class="hljs-attr">v-for</span>=<span class="hljs-string">"(dataEgg, index) in dataEggs"</span>
      <span class="hljs-attr">:key</span>=<span class="hljs-string">"dataEgg.id"</span>
      <span class="hljs-attr">:to</span>=<span class="hljs-string">"{ name: 'eggs', params: { eggType: dataEgg.type } }"</span>
    &gt;</span>
      {{ dataEgg.name }}
      <span class="hljs-tag">&lt;<span class="hljs-name">el-divider</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"index !== dataEggs.length - 1"</span> <span class="hljs-attr">direction</span>=<span class="hljs-string">"vertical"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!dataEgg"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>查无此蛋<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-else</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{{ dataEgg.name }}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{ dataEgg.description }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{{ dataEgg.flavour }}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

      <span class="hljs-comment">&lt;!-- 开发环境可以正常的显示,打包后无法正常预览 --&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"`../../src/assets/images/${dataEgg.image}`"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>import.meta.glob (动态导入)<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"imageSrc"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"imageSrc"</span> /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">br</span> /&gt;</span>
      <span class="hljs-comment">&lt;!-- &lt;img :src="picture.default" alt=""&gt; --&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>使用计算属性方式:(new URL)<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">:src</span>=<span class="hljs-string">"imageSrcCom"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>之前用 vue2 版本的时候,使用的是<code>require</code>来引入图片,打包构建工具是<code>webpack</code>,但是现在使用的是<code>vite</code>构建,所以需要使用<code>import.meta.glob</code>来引入图片,并且需要使用<code>new URL</code>来获取图片的路径. <code>vite</code> 和 <code>webpack</code> 的核心差别还是挺大的，在 <code>vite</code> 中并没有这样的包。</p>
<h4><a id="toc-8f4" class="anchor" href="#toc-8f4"></a>开发环境截图</h4>
<p><img src="https://images.901web.com/blog901web/dev-imgpath.jpg" alt="开发环境截图"></p>
<h4><a id="toc-2d1" class="anchor" href="#toc-2d1"></a>glob 导入 动态引入图片</h4>
<p>Vite 支持使用特殊的 <code>import.meta.glob</code> 函数从文件系统导入多个模块（该方式为<strong>异步加载模块形式</strong>），该函数接收一个匹配模块文件的通配符，返回一个对象，其中键是文件路径，值是可以导入相应模块的函数。</p>
<p>其中,data.json 文件中的内容如下:</p>
<pre><code class="hljs lang-json">[
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"鸡蛋"</span>,
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"chicken-egg"</span>,
    <span class="hljs-attr">"image"</span>: <span class="hljs-string">"chicken-egg.jpeg"</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"又名鸡卵、鸡子，是母鸡所产的卵"</span>,
    <span class="hljs-attr">"flavour"</span>: <span class="hljs-string">"味甘，性平，无毒（煮熟后）"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">2</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"鸭蛋"</span>,
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"duck-egg"</span>,
    <span class="hljs-attr">"image"</span>: <span class="hljs-string">"duck-egg.jpeg"</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"又名鸭子、鸭卵、太平、鸭春、青皮等，为鸭科动物家鸭的卵，受精卵可孵化成小鸭"</span>,
    <span class="hljs-attr">"flavour"</span>: <span class="hljs-string">"性涼、味甘"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">3</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"鹅蛋"</span>,
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"goose-egg"</span>,
    <span class="hljs-attr">"image"</span>: <span class="hljs-string">"goose-egg.jpeg"</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"家禽鹅生下的卵"</span>,
    <span class="hljs-attr">"flavour"</span>: <span class="hljs-string">"有些油"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">4</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"鹌鹑蛋"</span>,
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"quail-egg"</span>,
    <span class="hljs-attr">"image"</span>: <span class="hljs-string">"quail-egg.jpeg"</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"鵪鶉所產的卵，蛋殼表面帶有棕褐色斑點"</span>,
    <span class="hljs-attr">"flavour"</span>: <span class="hljs-string">"味甘、性平"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">5</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"笨蛋"</span>,
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"dumb-egg"</span>,
    <span class="hljs-attr">"image"</span>: <span class="hljs-string">"dumb-egg.jpeg"</span>,
    <span class="hljs-attr">"description"</span>: <span class="hljs-string">"我才不是笨蛋"</span>,
    <span class="hljs-attr">"flavour"</span>: <span class="hljs-string">"没吃过"</span>
  }
]
</code></pre>
<h4><a id="toc-f64" class="anchor" href="#toc-f64"></a>生产环境截图</h4>
<p><img src="https://images.901web.com/blog901web/pro-imgpath.jpg" alt="生产环境截图"></p>
<h4><a id="toc-930" class="anchor" href="#toc-930"></a>逻辑部分 JS 代码(setup 语法糖)</h4>
<pre><code class="hljs lang-javascript"><span class="hljs-keyword">import</span> { ref, computed, watch, watchEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>;
<span class="hljs-keyword">import</span> dataEggs <span class="hljs-keyword">from</span> <span class="hljs-string">'../src/data.json'</span>;

<span class="hljs-keyword">const</span> imageSrc = ref(<span class="hljs-string">''</span>);

<span class="hljs-keyword">const</span> eggType = computed(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> route.params.eggType);
<span class="hljs-keyword">const</span> dataEgg = computed(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span>
  dataEggs.find(<span class="hljs-function">(<span class="hljs-params">egg</span>) =&gt;</span> egg.type === eggType.value)
);

<span class="hljs-comment">// 方式1: // 使用 import.meta.glob 动态导入图片 不配置  { eager: true }</span>
<span class="hljs-keyword">const</span> images = <span class="hljs-keyword">import</span>.meta.glob(<span class="hljs-string">'../src/assets/images/*.jpeg'</span>);

watchEffect(<span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">if</span> (dataEgg.value &amp;&amp; dataEgg.value.image) {
    <span class="hljs-keyword">const</span> imagePath = <span class="hljs-string">`../src/assets/images/<span class="hljs-subst">${dataEgg.value.image}</span>`</span>;
    <span class="hljs-keyword">const</span> imageModule = images[imagePath];
    <span class="hljs-keyword">if</span> (imageModule) {
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> img = <span class="hljs-keyword">await</span> imageModule();
        imageSrc.value = img.default;
      } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Image not found:'</span>, dataEgg.value.image, error);
        imageSrc.value = <span class="hljs-string">''</span>;
      }
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Image not found:'</span>, dataEgg.value.image);
      imageSrc.value = <span class="hljs-string">''</span>;
    }
  } <span class="hljs-keyword">else</span> {
    imageSrc.value = <span class="hljs-string">''</span>;
  }
});
</code></pre>
<p>以上这种方式匹配到的文件默认是<code>懒加载</code>的, 通过<code>动态导入</code>实现,并会在<code>构建时分离为独立的 chunk 文件</code>。如果你倾向于直接引入（同步加载使用）所有的模块，你可以传入 <code>{ eager: true }</code> 作为第二个参数`.</p>
<pre><code class="hljs lang-javascript"><span class="hljs-comment">// ...</span>
<span class="hljs-comment">// 省略导入相关文件</span>

<span class="hljs-comment">// 方式1: // 使用 import.meta.glob 动态导入图片 配置 { eager: true }</span>
<span class="hljs-keyword">const</span> images = <span class="hljs-keyword">import</span>.meta.glob(<span class="hljs-string">'../src/assets/images/*.jpeg'</span>, { <span class="hljs-attr">eager</span>: <span class="hljs-literal">true</span> });

watchEffect(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
  <span class="hljs-keyword">if</span> (dataEgg.value &amp;&amp; dataEgg.value.image) {
    <span class="hljs-keyword">const</span> imagePath = <span class="hljs-string">`../src/assets/images/<span class="hljs-subst">${dataEgg.value.image}</span>`</span>;
    <span class="hljs-keyword">const</span> imageModule = images[imagePath];
    <span class="hljs-keyword">if</span> (imageModule) {
      <span class="hljs-comment">// console.log('imageModule.default', imageModule.default);</span>
      imageSrc.value = imageModule.default;
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Image not found:'</span>, dataEgg.value.image);
      imageSrc.value = <span class="hljs-string">''</span>;
    }
  } <span class="hljs-keyword">else</span> {
    imageSrc.value = <span class="hljs-string">''</span>;
  }
});
</code></pre>
<h3><a id="toc-a00" class="anchor" href="#toc-a00"></a>使用 new URL() 的方法进行动态导入</h3>
<p>在 Vue 中，如果你需要使用动态导入图片，你可以使用 <code>new URL()</code> 方法来获取图片的路径。</p>
<pre><code class="hljs lang-javascript"><span class="hljs-comment">// ...</span>
<span class="hljs-comment">// 省略导入相关文件</span>

<span class="hljs-keyword">const</span> imageSrcCom = computed(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
  <span class="hljs-keyword">if</span> (dataEgg.value &amp;&amp; dataEgg.value.image) {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> URL(
        <span class="hljs-string">`../src/assets/images/<span class="hljs-subst">${dataEgg.value.image}</span>`</span>,
        <span class="hljs-keyword">import</span>.meta.url
      ).href;
    } <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Image not found:'</span>, e);
      <span class="hljs-keyword">return</span> <span class="hljs-string">''</span>;
    }
  }
  <span class="hljs-keyword">return</span> <span class="hljs-string">''</span>;
});
</code></pre>
<p>在 <code>new URL()</code> 方法中，<code>import.meta.url</code> 是一个特殊的变量，它表示当前模块的 URL。 在 <code>vue3+vite</code>的项目中,如果需要动态的导入图片,可以使用上面介绍的方法,防止在开发环境中好好的,打包构建后,部署到生产环境,图片无法正常预览...</p>

            ]]></description>
            <pubDate>Thu, 27 Mar 2025 07:39:50 GMT</pubDate>
            <guid>https://901web.com/post/vue3-vite-dynamic-image-path.html</guid>
        </item>
        <item>
            <title>Vue3 中 reactive()中定义的对象再次赋值,页面不会自动更新</title>
            <link>https://901web.com/post/vue3-dingyiduixiang-zaicifuzhi-bugengxin.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-6c1">Vue3 中 reactive()中定义的对象再次赋值,页面不会自动更新</a><ul>
<li><a href="#toc-16d">reactive 定义的数据不能直接赋值</a></li>
<li><a href="#toc-957">解决方法</a></li>
</ul>
</li>
</ul>
</div><h3><a id="toc-6c1" class="anchor" href="#toc-6c1"></a>Vue3 中 reactive()中定义的对象再次赋值,页面不会自动更新</h3>
<p>在 vue3 里，ref 和 reacitve 都可以定义响应式数据，但是两者有所不同。
在使用 reactive 定义复杂结构的响应式数据时，如果你要对其赋值，会丢失其响应性。然后赋值是我们经常进行的操作，那么该怎么解决呢？</p>
<!--more-->
<h4><a id="toc-16d" class="anchor" href="#toc-16d"></a>reactive 定义的数据不能直接赋值</h4>
<pre><code class="hljs lang-js"><span class="hljs-comment">// 定义的表单响应式数据</span>
<span class="hljs-keyword">let</span> ruleForm = reactive({
  <span class="hljs-attr">name</span>: <span class="hljs-string">''</span>,
  <span class="hljs-attr">gender</span>: <span class="hljs-string">''</span>,
  <span class="hljs-attr">age</span>: <span class="hljs-number">20</span>,
  <span class="hljs-attr">salary</span>: <span class="hljs-string">''</span>,
  <span class="hljs-attr">createtime</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getTime(),
});

<span class="hljs-comment">// 请求接口成功后表单需要默认初始值</span>

<span class="hljs-comment">// 需要这样的赋值</span>
<span class="hljs-keyword">const</span> getStudentDetailInfo = <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> {
  indexModel.getStudentDetail(id).then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> { code, msg } = res;
    <span class="hljs-comment">// 这样赋值没效果</span>
    <span class="hljs-keyword">if</span> (code === <span class="hljs-number">200</span>) {
      ruleForm = { ...res };
    }
    <span class="hljs-comment">// 下面这样赋值可以,但是如果有很多字段,需要写很多</span>
    <span class="hljs-comment">// ruleForm.name = res.name;</span>
    <span class="hljs-comment">// ruleForm.gender = res.gender;</span>
    <span class="hljs-comment">// ruleForm.age = res.age;</span>
    <span class="hljs-comment">// ruleForm.salary = res.salary;</span>
  });
};
</code></pre>
<h4><a id="toc-957" class="anchor" href="#toc-957"></a>解决方法</h4>
<ul>
<li>方法 1: 改为<strong>ref</strong>定义响应式数据</li>
</ul>
<pre><code class="hljs lang-js"><span class="hljs-keyword">let</span> ruleForm = ref({
  <span class="hljs-attr">name</span>: <span class="hljs-string">''</span>,
  <span class="hljs-attr">gender</span>: <span class="hljs-string">''</span>,
  <span class="hljs-attr">age</span>: <span class="hljs-number">20</span>,
  <span class="hljs-attr">salary</span>: <span class="hljs-string">''</span>,
  <span class="hljs-attr">createtime</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getTime(),
});

ruleForm.value = { ...res };
</code></pre>
<p>2.如果是数组的话,可以使用 push 新增数据</p>
<pre><code class="hljs lang-js"><span class="hljs-keyword">const</span> arr = reactive([]);
arr.push(...[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]);
</code></pre>
<ol start="3">
<li>再封装一层数据(推荐!)即定义属性名,在后期赋值的时候,对属性名进行赋值</li>
</ol>
<pre><code class="hljs lang-js"><span class="hljs-keyword">const</span> state = reactive({
  <span class="hljs-attr">arr</span>: [],
  <span class="hljs-attr">ruleForm</span>: {},
});

state.arr = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
state.ruleForm = { ...res };
</code></pre>
<p>但是这样的话在 html 模板里，使用数据就得 state.arr
所以我们可以用解构将它 return 出来（script setup 里面 不需要 return 解构出来即可）</p>
<p>但是 reactive 解构出来的数据会丢失响应性
所以再用 toRefs()方法为它们添加响应性</p>
<p>最终为:</p>
<pre><code class="hljs lang-js"><span class="hljs-keyword">const</span> state = reactive({
  <span class="hljs-attr">arr</span>: [],
  <span class="hljs-attr">ruleForm</span>: {},
});
state.arr = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
state.ruleForm = { ...res };
<span class="hljs-keyword">return</span> { ...toRefs(state) };

<span class="hljs-comment">//如果是在 script setup 里面 不需要return 解构出来即可</span>

<span class="hljs-keyword">const</span> { ruleForm, arr } = toRefs(state);
</code></pre>

            ]]></description>
            <pubDate>Mon, 24 Mar 2025 03:13:22 GMT</pubDate>
            <guid>https://901web.com/post/vue3-dingyiduixiang-zaicifuzhi-bugengxin.html</guid>
        </item>
        <item>
            <title>Vue 3 script setup 到底做了什么?</title>
            <link>https://901web.com/post/vue3-setup-yu-fa.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-8bc">Vue 3 script setup 语法糖到底做了什么?setup语法糖和setup函数写法到底有啥区别</a></li>
<li><a href="#toc-12e">setup 语法为什么要这么做?</a></li>
</ul>
</div><h3><a id="toc-8bc" class="anchor" href="#toc-8bc"></a>Vue 3 script setup 语法糖到底做了什么?setup语法糖和setup函数写法到底有啥区别</h3>
<p>script setup 它是一个语法糖,相当于包含在 setup 函数里面. 返回的值会自动注入到当前作用域中.</p>
<p>script setup 是一个语法糖，它相当于在 setup 函数中包含的代码。它允许你在一个组件中同时定义数据和逻辑，并通过一个简单的语法来声明组件的属性和事件。</p>
<!--more-->
<p>要了解清楚这两种写法的差异,就要看他们的编译结果.因为最终运行的一定不是这个单文件组件,而是单文件组件编译出来的结果.</p>
<p>需要用到<code>vite-plugin-inspect</code>插件.进行配置.查看编译结果.</p>
<p><img src="https://901web.com/static/upload/20250323/528dc774-e149-4c82-9c41-d76c8bd9f923.png" alt="alt=&quot;编译结果&quot;"></p>
<p>setup 语法糖编译结果会自动调用 expose() 方法,而 setup 函数的编译结果不会调用 expose() 方法.</p>
<ul>
<li>setup 函数写法:</li>
</ul>
<p>expose: [], 暴露这个组件实例中的哪些成员.</p>
<p>如果做了这个配置,就不会暴露其他乱七八糟的成员了.只会暴露你配置的东西.</p>
<ul>
<li>setup 语法糖</li>
</ul>
<p>使用 setup 语法糖的话,它会自动给你加上.这就导致了用这种模式产生的组件,它的组件实例里面,实际上是没有暴露任何东西的.</p>
<p>setup 语法糖编译结果会自动调用 expose() 方法,而 setup 函数的编译结果不会调用 expose() 方法.</p>
<h3><a id="toc-12e" class="anchor" href="#toc-12e"></a>setup 语法为什么要这么做?</h3>
<p>组件实例里面有这些东西不好吗?
为什么这个语法要多此一举,去阻止它暴露这些东西呢?</p>
<hr>
<p>是因为暴露这些东西实际上不是一件好事.因为这就提供了一种可能,你在这个组件之外,你可以通过 ref 给这个组件加个 ref,然后去拿到这个组件的实例.<code>this.$refs.xxx</code>,通过这个实例去访问这些组件内部的成员.然后执行某种操作,这样就打破了单向数据流的规则.</p>
<p>这个是组件内部的数据,你就给外部提供了一种改动它的可能性. 这种做法式有很大的隐患的.单向数据流一旦被打破,代码就离屎山越来越近啦.</p>
<p>所以为了解决这个问题,vue3 就加入了整个<code>expose</code>.而在 setup 语法糖里面,会自动调用<code>expose</code>方法,防止你向外暴露太多东西. 如果你一定要暴露的话,就自行调用这个方法.</p>
<p>setup 语法糖里面,暴露某些东西,可以使用 <code>defineExpose</code> 方法. 编译的时候,会转换为 <code>expose</code> 方法.</p>
<p>这也就是为什么 <code>defineExpose</code>,<code>defineProps</code>,<code>defineEmits</code> 使用不需要导入,因为它们不参与运行的.它们只是用来在编译阶段做语法糖的.</p>
<p>运行环境里面压根就没有这些函数,它只是在编译环境里面,给你做了一个对应.</p>
<p>所以,以后在写 Vue3 的时候,尽量使用 setup 语法糖.因为它会隐式的帮你去做这件事情.</p>
<p>如果说你没有手动的声明就表示那么它默认情况下,不会向外界暴露任何东西,就保护了单向数据流.这是一个强约束. 强约束永远是好于规范的.</p>

            ]]></description>
            <pubDate>Sun, 23 Mar 2025 08:29:39 GMT</pubDate>
            <guid>https://901web.com/post/vue3-setup-yu-fa.html</guid>
        </item>
        <item>
            <title>Chrome高级调试技巧总结记录</title>
            <link>https://901web.com/post/chrom-debugging-skill.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-e90">Chrome高级调试技巧总结</a></li>
</ul>
</div><h3><a id="toc-e90" class="anchor" href="#toc-e90"></a>Chrome高级调试技巧总结</h3>
<ul>
<li>一键重新发起请求</li>
</ul>
<blockquote>
<p>在与后端接口联调或排查线上bug时候,我们需要重新发起一次请求非常实用.在network中找到对应的请求,然后右键鼠标,选择 <code>Replay XHR</code>即可重新发起一次请求</p>
</blockquote>
<ul>
<li>在控制台快速发起请求</li>
</ul>
<blockquote>
<p>在联调或者修改bug的时候,我们需要修改入参,然后重新发起请求; 比如后端在找bug,然后让你把某个字段先写死一个固定值.又比如在做导出功能的时候,一直出问题,这时我们判断应该是请求头设置有问题.可能需要不断的调整代码中的请求头,然后执行代码重新发送请求.其实可以在控制台中快速发起请求. 步骤如下</p>
</blockquote>
<p>(1).打开network,找到需要修改参数的请求.</p>
<p>(2).右击选择 <code>Copy as fetch</code></p>
<p>(3).粘贴到console中,修改请求参数,按回车</p>
<p>(4).在network中查看响应结果</p>
<p>​    </p>
<ul>
<li>复制Javascript复杂对象</li>
</ul>
<blockquote>
<p>比如我们在代码中使用console.log打印复杂对象的时候,它是格式化的,不便于复制给其他人看.</p>
<p>var obj = {</p>
<p>​    name:&#39;张三&#39;,</p>
<p>​    age:25,</p>
<p>​    gender:&#39;male&#39;,</p>
<p>​    edu:&#39;本科&#39;</p>
<p>}</p>
<p>console.log(obj)</p>
<p>为了拷贝:</p>
<p>console.log(JSON.stringify(obj))</p>
<p>现在实现 copy(obj)</p>
</blockquote>
<ul>
<li>截取一张全屏的网页</li>
</ul>
<blockquote>
<p>对网页截屏的需求我们应该一直都用,一屏还好,系统自带的截屏或者微信截图等都可以,但是要超出一屏的内容咋办?</p>
<p>chrome其实已经给我们准备好啦.</p>
</blockquote>
<p>(1)打开要截图的网页</p>
<p>(2)cmd+shift+P 执行Command命令</p>
<p>(3)输入Capture full size screenshot按下回车即可</p>
<ul>
<li>条件断点妙用 (Add conditional breakpoint)</li>
<li>$i 直接控制台安装npm包</li>
</ul>
<blockquote>
<p>有时候,我们想使用比如dayjs 或者 lodash的某个API,但又不想去官网查,如果可以在控制台直接试出来就好.就可以安装这个插件.</p>
<p>$i(&#39;dayjs&#39;)</p>
<p>$i(&#39;lodash&#39;)</p>
<p>然后就可以尝试使用其API</p>
</blockquote>
<ul>
<li>调试一些ToolTips弹出框样式</li>
</ul>
<p><img src="https://901web.com/static/upload/20241128/upload_2eeeccba97f7219548f0fcfe41e6b964.png" alt="image.png"></p>
<p>打开控制台,按住<code>common+shift+p</code>,然后选择<code>disable javascript</code>即可.</p>
<p>如果需要启用Javascript,则再次打开,选择<code>enable javascript</code>即可.</p>

            ]]></description>
            <pubDate>Thu, 28 Nov 2024 09:46:31 GMT</pubDate>
            <guid>https://901web.com/post/chrom-debugging-skill.html</guid>
        </item>
        <item>
            <title>当面试被问到什么是Promise?该如何回答~</title>
            <link>https://901web.com/post/how_to_answer_what_is_promise.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-d2d">什么是Promise?</a></li>
</ul>
</div><h3><a id="toc-d2d" class="anchor" href="#toc-d2d"></a>什么是Promise?</h3>
<hr>
<p>当面试时问到什么是Promise? 应该怎么回答:</p>
<p>首先说一下Promise A+规范 , 当有一个符合Promise A+规范的 then 方法的对象.就是一个Promise.而 ES6 出现之后, 它带来了一个构造函数,通过这个构造函数,可以创建一个 满足Promise A+ 规范的Promise对象. 这就是Promise.</p>
<p>异步问题同步化的解决方案. Promise执行是同步的 (executor 同步)？ p.then 是异步的</p>
<!--more-->
<p>当说到Promise,它有两层含义:</p>
<ul>
<li><p>Promise A+规范 (出现的时间点比较早,在ES6之前) 民间标准 (社区搞出来的) (2015年之前)  主要处理回调地狱和异步处理不统一的问题</p>
</li>
<li><p>符合PromiseA+规范的 (例如:)</p>
</li>
</ul>
<p>一个对象包含一个then方法,是一个Promise</p>
<pre><code class="hljs lang-js">  { <span class="hljs-attr">then</span>:<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{} }
</code></pre>
<p>  这个也是一个Promise</p>
<pre><code class="hljs lang-js">  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">A</span>(<span class="hljs-params"></span>)</span>{}
  A.then = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{}
</code></pre>
<p>直到ES6出来,搞了一个构造函数 <code>Promise</code>. 整个构造函数和 Promise A+ 规范有什么关联呢?</p>
<p>当你通过构造函数去创建一个对象,创建的那个对象它是满足 Promise A+规范的. ES6吸纳了 Promise A+ 规范. </p>
<p>它创建的Promise 满足Promise A+ 规范,而且还增加了很多其他的东西.</p>
<p>比方说 它给里面增加了一个<code>catch</code> 方法, <code>finally</code>, 增加了一些静态方法: <code>Promise.all</code>,<code>Promise.race</code>,<code>Promise.resolve</code> 等等. </p>
<p>这些并不是Promise A+ 规范中的规定. 而是ES6 中Promise新增的.</p>
<p>而且在Promise A+ 规范里面,它有一个说法.就是你只要满足Promise A+ 规范,你就是一个Promise, 而你只要是一个Promise.你们之间就可以互操作. (这个就比较有意思啦~~)</p>
<p>比如说我们用过的一个库<code>jQuery</code>, 中有一个写法:</p>
<pre><code class="hljs lang-js"><span class="hljs-comment">// 返回的是一个Promise,但是并不是ES6通过构造函数创建的Promise</span>
<span class="hljs-comment">// 而是它自己写的一套满足Promise A+规范的Promise,但是你感觉不到.仍然可以使用await关键字.可以使用.then方法</span>
<span class="hljs-comment">// 而且这个then 方法还可以跟我们这个ES6 的Promise进行互操作.</span>
<span class="hljs-keyword">await</span> $.ajax().then()
</code></pre>
<p>感觉上它好像就是ES6的Promise.但实际上它不是的.
不是它为什么能够进行互操作呢? 
是因为 无论是 ES6 的Promise,还是你自己写的Promise,或者是第三方库里面自己实现的Promise.它们都满足 Promise A+ 规范. 只要满足这个规范.它们就可以互操作.</p>

            ]]></description>
            <pubDate>Tue, 17 Sep 2024 03:26:16 GMT</pubDate>
            <guid>https://901web.com/post/how_to_answer_what_is_promise.html</guid>
        </item>
        <item>
            <title>Promise A+ 规范和解读(Promise的前世今生)</title>
            <link>https://901web.com/post/what_is_Promise_in_javascript.html</link>
            <description><![CDATA[
            <div class="toc"><ul>
<li><a href="#toc-9d0">规范的产生背景</a></li>
<li><a href="#toc-fbe">规范翻译</a></li>
<li><a href="#promise">Promise</a></li>
<li><a href="#toc-84c">then方法</a></li>
<li><a href="#toc-163">任务完成处理流程</a></li>
<li><a href="#toc-c1d">解读</a></li>
</ul>
</div><h3><a id="toc-9d0" class="anchor" href="#toc-9d0"></a>规范的产生背景</h3>
<hr>
<p>该规范产生于2012年左右，它是 Promise A+ 规范的升级版</p>
<p>可以看出在出现时间上，Promise A+ 是早于 ES6 的, 民间标准 (社区搞出来的)</p>
<p>在那个时候，为了处理异步场景，JS中充斥着各种回调函数：</p>
<!--more-->
<pre><code class="hljs lang-js"><span class="hljs-comment">// 以回调模式处理异步</span>

button.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">e</span>)</span>{
  <span class="hljs-comment">// 回调函数</span>
})

setTimeout(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{
  <span class="hljs-comment">// 回调函数</span>
}, <span class="hljs-number">1000</span>)
</code></pre>
<p>过去，回调函数也不是唯一的处理异步的方式，有些异步场景也可以把函数保存到对象的属性中，以便将来调用：</p>
<pre><code class="hljs lang-js"><span class="hljs-comment">// 保存函数的属性</span>
xhr.onreadstatechange = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{

}

button.onclick = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{

}
</code></pre>
<p>可以看出,过去处理异步的方式是<strong>不统一</strong>的! 在那个混乱的异步处理年代,Promise A+ 横空出世. 它<strong>不是一个技术,而是一套规范</strong></p>
<p>它希望所有的前端开发者都遵循这套规范.使所有的开发者书写的异步代码达到统一</p>
<blockquote>
<p>后来,ES6在制作Promise标准时,就参考了该规范.</p>
</blockquote>
<h3><a id="toc-fbe" class="anchor" href="#toc-fbe"></a>规范翻译</h3>
<hr>
<p>一个 Promise 对象应该能够表达一个异步操作，异步操作无论成功还是失败，总会有一个结果，这个结果可以通过它的<code>then</code>方法来进行交互，具体的交互方式是，注册一个回调函数到<code>then</code>方法中</p>
<p>该规范详细的描述了<code>then</code>方法的具体规格</p>
<h3><a id="promise" class="anchor" href="#promise"></a>Promise</h3>
<p>promise 用于表示一个异步任务,它应该是一个带有<code>then</code> 方法的对象.它有三种状态: <code>pending挂起</code>, <code>fulfilled完成</code>,<code>rejected失败</code></p>
<p>任何时刻,promise一定处于三种状态之一.</p>
<blockquote>
<p>ES6中的 Promise完成状态是 resolved,单词不同,含义一样.</p>
</blockquote>
<ol>
<li><p>当 promise 处于 <code>pending</code> 状态时：</p>
<p> 它可以在任何合适的时候把状态转变为<code>fulfilled</code>或<code>rejected</code></p>
</li>
<li><p>当 promise 处于 <code>fulfilled</code> 状态时：</p>
<p> 它无法再次更改到其他状态</p>
<p> 它必须拥有一个值，表示任务完成时的数据，该数据可以是任何 JS 数据（甚至是 undefined），并且这个值一旦确定下来，不可更改</p>
</li>
<li><p>当 promise 处于 <code>rejected</code> 状态时：</p>
<p> 它无法再次更改到其他状态</p>
<p> 它必须拥有一个值，表示任务失败的原因，该值可以是任何 JS 数据（甚至是 undefined），并且这个值一旦确定下来，不可更改</p>
</li>
</ol>
<h3><a id="toc-84c" class="anchor" href="#toc-84c"></a>then方法</h3>
<hr>
<p>promise 应该提供一个 <code>then</code> 方法，通过这个方法，我们可以访问到任务完成的值 或 任务失败的原因</p>
<p><code>then</code>方法可以接收两个参数：</p>
<pre><code class="hljs lang-undefined">promise.then(onFulfilled, onRejected)
</code></pre>
<ol>
<li><p><code>onFulfilled</code> 和 <code>onRejected</code> 都是可选参数：</p>
<p> 如果 <code>onFulfilled</code> 不是一个函数，它必须被忽略</p>
<p> 如果 <code>onRejected</code> 不是一个函数，它必须被忽略</p>
</li>
<li><p>如果 <code>onFulfilled</code> 是一个函数：</p>
<p> 它应该在 <code>promise</code> 到达 <code>fulfilled</code> 状态时被调用，调用该函数时，应该把任务完成时的值作为第一个参数传递进去</p>
<p> 该函数只能被调用一次</p>
</li>
<li><p>如果 <code>onRejected</code> 是一个函数：</p>
<p> 它应该在 <code>promise</code> 到达 <code>rejected</code> 状态时被调用，调用该函数时，应该把任务失败的原因作为第一个参数传递进去</p>
<p> 该函数只能被调用一次</p>
</li>
<li><p><code>onFulfilled</code> 和 <code>onRejected</code>必须要等到当前执行栈清空后才能被调用，换句话说，它们是异步执行的</p>
</li>
<li><p>可以多次对同一个<code>promise</code>调用<code>then</code>方法，从而注册多个<code>onFulfilled</code>或<code>onRejected</code></p>
<p> 当任务完成 或 任务失败时，将按照注册的顺序，依次调用注册的 <code>onFulfilled</code> 或 <code>onRejected</code></p>
</li>
<li><p><code>then</code>方法必须再次返回一个<code>promise</code></p>
</li>
</ol>
<pre><code class="hljs lang-undefined">promise2 = promise1.then(onFulfilled, onRejected);
</code></pre>
<p>在执行<code>onFulfilled</code> 或 <code>onRejected</code> 时，如果执行的过程中报了一个错误，则会导致 promise2 进入 <code>rejected</code> 状态，并且和状态相关的失败原因就是抛出的错误</p>
<p>在执行<code>onFulfilled</code> 或 <code>onRejected</code> 时，如果执行的过程中没有发生错误，并且返回值是 <code>x</code>（<code>x</code> 可以是任何数据，包括<code>undefined</code>），则会进入 <strong>任务完成处理流程</strong> <code>[[Resolve]](promise2, x)</code>，该流程在下一个小节解释</p>
<p>如果<code>onFulfilled</code>不是一个函数，同时<code>promise1</code>已经<code>fulfilled</code>，<code>promise2</code>也会自动变成<code>fulfilled</code>，完成的相关数据和<code>promise1</code>一致</p>
<p>如果<code>onRejected</code>不是一个函数，同时<code>promise1</code>已经<code>rejected</code>，<code>promise2</code>也会自动变成<code>rejected</code>，失败的原因和<code>promise1</code>一致</p>
<h3><a id="toc-163" class="anchor" href="#toc-163"></a>任务完成处理流程</h3>
<hr>
<p>这个流程是一个概念上的操作规范，而不是真实的代码</p>
<p>Promise A+ 期望实现该规范的代码也是要实现这个处理流程</p>
<p>为了便于说明，Promise A+ 把这个操作规范记作<code>[[Resolve]](promise, x)</code>，它应该按照以下的流程进行：</p>
<ol>
<li>如果<code>promise</code>和<code>x</code>是同一个对象，则把<code>promise</code>设置为<code>rejected</code>状态，失败原因是一个<code>TypeError</code></li>
<li>如果<code>x</code>是一个<code>promise</code>：<ol>
<li>如果<code>x</code>处于<code>pending</code>，<code>promise</code>也必须处于<code>pending</code>，直到<code>x</code>完成或失败</li>
<li>如果<code>x</code>完成，<code>promise</code>也会完成，数据和<code>x</code>完成的数据一致</li>
<li>如果<code>x</code>失败，<code>promise</code>也会失败，原因和<code>x</code>失败原因一致</li>
</ol>
</li>
<li>如果<code>x</code>是一个对象或函数：<ol>
<li>读取<code>x.then</code>，如果读取发生异常，则直接让<code>promise</code>失败，失败原因就是抛出的错误，然后结束处理即可</li>
<li>如果<code>x.then</code>是一个<code>function</code><ol>
<li>调用<code>x.then</code>方法：<code>x.then(onFulfilled, onRejected)</code></li>
<li>当<code>onFulfilled</code>执行时，如果得到的数据是<code>y</code>，则执行<code>[[Resolve]](promise, y)</code></li>
<li>当<code>onRejected</code>执行时，如果得到的失败原因是<code>error</code>，则让<code>promise</code>变成失败状态，原因是<code>error</code></li>
</ol>
</li>
<li>如果<code>x.then</code>不是一个<code>function</code>， 把<code>promise</code>变成<code>fulfilled</code>状态，数据为<code>x</code></li>
<li>如果调用<code>x.then</code>的时候发生错误，则把<code>promise</code>变成失败状态，失败原因为抛出的错误</li>
</ol>
</li>
<li>如果<code>x</code>不是一个对象或函数，把<code>promise</code>变成<code>fulfilled</code>状态，数据为<code>x</code></li>
</ol>
<h3><a id="toc-c1d" class="anchor" href="#toc-c1d"></a>解读</h3>
<hr>
<ol>
<li><p><strong>ES6 的 Promise 和 Promise A+ 是一个什么样的关系？</strong></p>
<p>ES6 的 Promise 遵循的是 Promise A+ 的规范
这也就意味着 ES6 的 Promise 完全可以和其他遵循了 Promise A+ 规范的 Promise 进行互操作
比如，ES6 的 Promise 可以和 JQuery 的Promise进行互操作</p>
</li>
</ol>
<pre><code class="hljs lang-js">$.ajax(...).then(<span class="hljs-function"><span class="hljs-params">resp</span>=&gt;</span>{
    <span class="hljs-comment">// 这是JQuery的Promise的then函数</span>
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function"><span class="hljs-params">resolve</span>=&gt;</span>{
      <span class="hljs-comment">// 这是ES6的Promise     </span>
  })
})
</code></pre>
<ol start="2">
<li><strong>关于Promise的catch函数</strong></li>
</ol>
<p>Promise A+ 规范中并没有规定需要提供catch，但ES6的Promise提供了这一函数，它是为了让开发者使用起来更加方便</p>
<p>这并不代表ES6的Promise打破了这一规范，因为绝大部分规范都是只规定了<em>你至少有什么东西</em>，并不关心<em>你多了什么东西</em></p>
<ol start="3">
<li><strong>Promise必须通过构造函数<code>new Promise</code>得到吗？</strong></li>
</ol>
<p>Promise A+没有这样规定，它可以通过任何途径得到，只不过ES6的Promise是通过构造函数得到的</p>
<p>Promise A+规范，要求Promise必须是一个对象或是一个函数，该对象或函数提供了满足要求的<code>then</code>方法即可</p>
<p>比如，下面的代码得到的都是满足规范的Promise</p>
<pre><code class="hljs lang-js"><span class="hljs-keyword">const</span> promise1 = {
  then(onFufilled, onRejected){
    <span class="hljs-comment">// 满足规范的then函数</span>
  }
}

<span class="hljs-keyword">const</span> promise2 = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">a, b</span>)</span>{<span class="hljs-keyword">return</span> a+b}
promise2.then = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">onFufilled, onRejected</span>)</span>{
  <span class="hljs-comment">// 满足规范的then函数</span>
}
</code></pre>
<ol start="4">
<li><strong>关于Promise的状态</strong></li>
</ol>
<p>Promise A+ 并没有要求Promise必须提供什么属性来得到状态，这些都是Promise的内部信息，你可以对外提供，也可以不提供</p>
<p>ES6的Promise提供了两个属性<code>[[PromiseStatus]]</code>和<code>[[PromiseValue]]</code>，分别表示Promise的状态和状态的相关数据（成功的数据或失败的原因），但这两个属性均无法在外部访问</p>
<p>另外，Promise A+ 也没有对状态的名称做出强制要求，在规范文档中，Promise A+ 使用了<code>pending</code>、<code>fufilled</code>、<code>rejected</code>，但这些名称仅仅是为了说明规范而产生的，在具体的实现中，你可以自行规定任何的名称来表示三种状态</p>
<p>ES6的Promise就使用了<code>pending</code>、<code>resolved</code>、<code>rejected</code>来表示三种状态，这并没有打破Promise A+规范</p>

            ]]></description>
            <pubDate>Tue, 17 Sep 2024 02:46:47 GMT</pubDate>
            <guid>https://901web.com/post/what_is_Promise_in_javascript.html</guid>
        </item>
    </channel>
</rss>
