前端实习周报2

66 阅读6分钟

本周主要完成了三个任务

一、患者基础信息的查询

1.前后端接口联调

(1)患者基础信息解密
(2)患者基础信息修改

2.translate翻译

3.弹窗组件的封装

子组件(弹框组件):

<template>
  <a-modal
    v-model:visible="localVisible"
    :title="null"
    :confirm-loading="loading"
    width="600px"
    :style="{ top: '200px' }"
    @cancel="onCancel"
  >
    <div class="modal-header">
      <div class="modal-title">
        <span>{{ translate('Patient Basic Information') }}</span>
        <a-button
          v-if="hasAuth(['patient-details-edit', 'button', 'patient-details'])"
          type="link"
          @click="toggleEdit"
          style="background-color: white; padding: 4px 15px"
        >
          <EditOutlined />
        </a-button>
      </div>
    </div>

    <a-form layout="horizontal" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
      <a-form-item :label="translate('patient')" required>
        <a-input
          v-model:value="patientInfo.name"
          :disabled="!editMode"
          :maxlength="50"
          :placeholder="editMode ? translate('pleaseEnter') : ''"
          allow-clear
        />
      </a-form-item>
      <a-form-item :label="translate('Gender')" required>
        <a-select
          v-model:value="patientInfo.gender"
          :disabled="!editMode"
          style="width: 100%"
          :placeholder="editMode ? translate('pleaseSelect') : ''"
        >
          <a-select-option :value="1">{{ translate('Male') }}</a-select-option>
          <a-select-option :value="2">{{ translate('Female') }}</a-select-option>
        </a-select>
      </a-form-item>
      <a-form-item :label="translate('birthday')" required>
        <a-date-picker
          v-model:value="patientInfo.birthday"
          :disabled="!editMode"
          format="MM-DD-YYYY"
          valueFormat="YYYY-MM-DD"
          :disabled-date="disabledDate"
          style="width: 100%"
          :placeholder="editMode ? translate('pleaseSelect') : ''"
        />
      </a-form-item>
    </a-form>

    <template #footer>
      <a-button v-if="editMode" @click="handleCancel">
        {{ translate('cancel') }}
      </a-button>
      <a-button v-if="editMode" type="primary" :loading="loading" @click="savePatientInfo">
        {{ translate('save') }}
      </a-button>
    </template>
  </a-modal>
</template>

<script lang="ts">
import { defineComponent, ref, reactive, watch } from 'vue'
import { EditOutlined } from '@ant-design/icons-vue'
import { useLang } from '@/hooks'
import { hasAuth } from '@/directives/auth'
import { message } from 'ant-design-vue'
import { editBasicInfo } from '@/api/case'
import dayjs, { Dayjs } from 'dayjs'

export default defineComponent({
  components: {
    EditOutlined
  },
  props: {
    visible: Boolean,
    patientData: Object
  },
  emits: ['update:visible', 'saved'],
  setup(props, { emit }) {
    const { translate } = useLang('cases', 'model')
    const loading = ref(false)
    const editMode = ref(false)

    const localVisible = ref(props.visible)

    //监听props.visible
    watch(
      () => props.visible,
      (newValue) => {
        localVisible.value = newValue
      }
    )
    const patientInfo = reactive({
      name: '',
      gender: null as number | null,
      birthday: '',
      pds_code: '',
      patient_code: ''
    })
    const originalPatientInfo = ref({
      name: '',
      gender: null as number | null,
      birthday: '',
      pds_code: '',
      patient_code: ''
    })

    watch(
      () => props.patientData,
      (newVal) => {
        if (newVal) {
          if (newVal.gender === 'male') {
            newVal.gender = 1
          } else if (newVal.gender === 'female') {
            newVal.gender = 2
          }

          patientInfo.name = newVal.name || ''
          patientInfo.gender = newVal.gender || ''
          patientInfo.birthday = newVal.birthday || null
          patientInfo.pds_code = newVal.pds_code || ''
          patientInfo.patient_code = newVal.patient_code || ''

          Object.assign(originalPatientInfo.value, patientInfo)
        }
      },
      { immediate: true, deep: true }
    )

    const toggleEdit = () => {
      editMode.value = true
    }

    const savePatientInfo = async () => {
      if (!patientInfo.name || !patientInfo.gender || !patientInfo.birthday) {
        message.error(translate('fillAllRequiredFields'))
        return
      }
      loading.value = true
      try {
        const dataToSave = {
          pds_code: patientInfo.pds_code,
          name: patientInfo.name,
          gender: patientInfo.gender,
          birthday: patientInfo.birthday,
          patient_code: patientInfo.patient_code
        }

        const response = await editBasicInfo(dataToSave)

        if (response.code === 0) {
          message.success(translate('operationSuccess'))
          editMode.value = false
          emit('saved', patientInfo)
          emit('update:visible', false)
        } else {
          message.error(translate('operationFailed'))
        }
      } catch (error) {
        console.error('Failed to save patient info:', error)
        message.error(translate('operationFailed'))
      } finally {
        loading.value = false
      }
    }

    const handleCancel = () => {
      Object.assign(patientInfo, originalPatientInfo.value)
      // 切换回只读模式
      editMode.value = false
    }
    const disabledDate = (current: Dayjs) => {
      // 设置最早可选日期,120年前
      const earliestDate = dayjs().subtract(120, 'years')

      // 获取今天的日期
      const today = dayjs().endOf('day')

      // 禁用未来的日期和早于120年前的日期
      return current > today || current < earliestDate
    }
    const onCancel = () => {
      handleCancel()
      emit('update:visible', false)
    }

    return {
      loading,
      editMode,
      patientInfo,
      localVisible,
      translate,
      hasAuth,
      toggleEdit,
      handleCancel,
      savePatientInfo,
      disabledDate,
      onCancel
    }
  }
})
</script>

<style scoped>
.modal-header {
  margin: -24px -24px 24px;
}

.modal-title {
  display: flex;
  align-items: center;
  padding: 16px 24px;
  border-bottom: 1px solid #f0f0f0;
}

.modal-title span {
  font-size: 16px;
  font-weight: 500;
  color: rgba(0, 0, 0, 0.85);
}
</style>

父组件使用子组件方式:

    <PatientInfoModal
      v-model:visible="patientModal.visible"
      :patient-data="patientModal"
      @saved="handlePatientInfoSaved"
    />

注意:子组件的关闭显示的变量由父组件控制,父组件传递对应的visible给子组件,子组件使用watch监听对应的props.visible,当点击子组件的叉号希望关闭弹窗时,子组件触发对应的父组件update:visible

为什么不需要 definePropsdefineEmits
在代码中,组件是使用 defineComponent 定义的,这是 Vue 3 的选项式 API。在这种情况下,props 和 emits 是在组件选项中定义的。

4.开发过程中的注意事项

(1)将组件放到对应的文件夹下,先从index.ts中import导入,统一export导出后,然后再统一的导入对应的文件夹下的index.ts;
(2)git分支: 包括一个自己开发的feature分支,合并到develop分支,develop分支不要合并到任何分支(develop分支只出不进);通过Jenkins发布到dev环境测试,没问题后,将feature分支合并到release分支,后端提测。

二、vite学习

1.vite、webpack、rollup区别

工具核心优势适用场景缺点
Vite开发速度极快,开箱即用现代框架项目、快速原型开发生产依赖 Rollup,生态较新
Webpack功能全面,生态强大复杂应用、企业级项目配置复杂,性能较低
Rollup高效的 Tree-shaking,适合库打包JS 库/工具开发不适合复杂应用构建

2.vite不会完全替代webpack的意义:

vite是基于es modules的,侧重点不一样,webpack更多的关注兼容性,而vite关注浏览器的开发体验; webpack支持多种模块化,他一开始必须要统一模块化代码,所以意味着他需要将所有的依赖全部读一遍。二者的侧重点不一样。

3.传统的构建工具(如 Webpack、Rollup)在默认情况下会将所有依赖的模块打包成一个或多个 bundle 文件

传统的打包: image.png

Vite 通过在一开始将应用中的模块区分为 依赖 和 源码 两类,改进了开发服务器启动时间。

  • 依赖 大多为在开发时不会变动的纯 JavaScript。一些较大的依赖(例如有上百个模块的组件库)处理的代价也很高。依赖也通常会存在多种模块化格式(例如 ESM 或者 CommonJS)。

    Vite 将会使用 esbuild 预构建依赖。esbuild 使用 Go 编写,并且比以 JavaScript 编写的打包器预构建依赖快 10-100 倍。

  • 源码 通常包含一些并非直接是 JavaScript 的文件,需要转换(例如 JSX,CSS 或者 Vue/Svelte 组件),时常会被编辑。同时,并不是所有的源码都需要同时被加载(例如基于路由拆分的代码模块)。

    Vite 以 原生 ESM 方式提供源码。这实际上是让浏览器接管了打包程序的部分工作:Vite 只需要在浏览器请求源码时进行转换并按需提供源码。根据情景动态导入代码,即只在当前屏幕上实际使用时才会被处理。

image.png

4.传统的打包工具(如 WebpackRollup)和 Vite 在构建方式、开发体验、生产优化等方面有显著区别。以下是它们的核心差异对比:


(1) 开发环境对比

传统打包工具(Webpack/Rollup)

 全量打包

  • 启动时分析所有依赖,生成完整的 bundle.js(即使只改一行代码)。
  • 冷启动慢,尤其大型项目可能需要几十秒甚至更久。

HMR(热更新)

  • 修改代码后,Webpack 重新构建变动的模块,但仍有延迟。

开发体验瓶颈

  • 项目越大,启动和热更新越慢。

Vite

基于原生 ESM(ES Modules)

  • 不打包,浏览器直接加载 ES Modules,按需编译当前页面所需文件。
  • 冷启动极快(毫秒级),与项目规模无关。

按需编译

  • 仅编译当前访问的页面文件,动态加载依赖(如路由懒加载)。

HMR 超快

  • 利用浏览器缓存和原生 ESM,热更新几乎无延迟。

兼容性要求

  • 依赖现代浏览器支持 ESM,旧项目可能需要调整代码。

(2)生产环境对比

传统打包工具(Webpack/Rollup)

全量打包优化

  • 使用 Babel 降级(ES5)、Tree-shaking代码压缩(Terser)。
  • 生成少量 chunk 文件,减少 HTTP 请求。

成熟稳定

  • 适合复杂项目(如 SSR、微前端)。

配置复杂

  • 需要手动优化 Loader、Plugin、Code Splitting。

Vite

默认使用 Rollup 打包

  • 生产构建和传统工具类似,但配置更简单(继承开发环境优化)。
  • 仍然支持 Tree-shaking代码压缩

更智能的预构建

  • 依赖(node_modules)会预编译为 ESM,提升缓存利用率。

生态较新

  • 某些 Webpack 插件可能需要替代方案。

三、vue3源码整体架构(vue三大核心模块)

1.响应式

2.编译器

3.渲染

(1)渲染阶段:返回虚拟节点

(2)挂载阶段:使用虚拟DOM节点并调用DOM API创建网页

(3)补丁阶段:虚拟DOM节点与新DOM节点比较,只更新网页变化部分