vue使用技术总结

vue3+TypeScript+Element Plus技术

技术概述

该技术用于web前端技术开发,也是现阶段较为流行的前端开发语言。学习该技术的原因是因为我在项目中担任前端开发的角色,需要在项目中使用,而且该技术相对比较好上门和入手。该技术的难点在于vue3还不够成熟,网上的资料还是相对较少,使找资料的效率会较低。

技术详述

以结对实战的为例,我是如下使用该技术

组件抽象

对项目的前端原型中的相同的部分抽象为一个个组件,使其便于复用,通过组件来进行页面的搭建
在这里插入图片描述

页面视图表现

将一个个页面抽象为一个个视图,通过组件组合成一个个视图
在这里插入图片描述

路由部署

对不同的页面,使用不同的路由访问,路由间传参使用props传

import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'app',
      component: HomeView
    },
    {
      path: '/home',
      name: 'home',
      component: HomeView
    },
    {
      path: '/more',
      name: 'more',
      component: () => import('../views/MoreView.vue')
    },
    {
      path: '/athletes',
      name: 'athletes',
      component: () => import('../views/AthletesView.vue')
    },
    {
      path: '/results',
      name: 'results',
      component: () => import('../views/ResultsView.vue')
    },
    {
      path: '/schedules',
      name: 'schedules',
      component: () => import('../views/SchedulesView.vue')
    },
    {
      path: '/medal_table',
      name: 'medal_table',
      component: () => import('../views/MedalTableView.vue')
    },
    {
      path: '/details/:name/:type',
      name: 'details',
      component: () => import('../views/DetailsView.vue'),
      props: true // 通过props传递参数
    }

  ]
})

export default router

App程序入口

在App.vue中使用router-view来显示各个页面

<script setup>

</script>

<template>
  <!-- <router-link to="/">10</router-link><br>
  <router-link to="/more">22</router-link> -->
  <router-view></router-view>
</template>

<style scoped>

</style>

程序运行入口

在main.js中部署后端的访问地址,路由的引入以及前端页面开始的入口

import { createApp, provide } from 'vue'
import App from './App.vue'
import router from './router'
import { library } from '@fortawesome/fontawesome-svg-core';
import { fas } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import ElementPlus from 'element-plus'

library.add(fas);

const backendRouter = {
    // 在这里定义全局变量
    appName: 'My App',
    url:'http://8.140.241.231:14000' /* 'http://localhost:14000' */
  };

const app = createApp(App)

app.provide('backendRouter', backendRouter)

app.use(router).use(ElementPlus, { size: 'small', zIndex: 3000 })
    .component('font-awesome-icon', FontAwesomeIcon).mount('#app')

访问后端接口

用axios和async进行异步访问后端

  watch(activeNames, () => { 
        /* console.log(activeNames.value) */
        if(activeNames.value !== "" ){
            /* console.log(activeNames.value) */
            // 发送GET请求
        axios.get(backendRouter.url +'/rank/detail?eventName=' + activeNames.value )
            .then(response => { 
                // response.data 就是上面的JSON数组Message: {{ msg }}
                datas.value = response.data.data;
                types.value = types.value.filter((t) => t === '')
                datas.value.forEach(element => {
                    types.value.push(element.phaseName);
                });
                phaseName.value = types.value[types.value.length - 1];
                datas.value.forEach(element => {
                    if(element.phaseName === phaseName.value){
                        detials.value = element.eventRanks;
                    }
                });
            })
        } 
    })

技术使用中遇到的问题和解决过程

element Plus的css样式不好修改

可以按F12进入开发者模式进行寻找和调整
在这里插入图片描述

可以输入表情的富文本框

由于网上的这些富文本框要么是vue2语法,要么就是用其他的语言,我学习对照了这些程序,用自己的代码进行描述

表情框选择组件
<template>
        <div class="emoji-panel-container">
            <div class="emoji-title" v-show="emojihistory.length !== 0">
              <span>最近使用</span>
              <span class="delete-history-btn" @click="removeHistory">删除历史</span>
            </div>
            <div class="emoji-history-container">
              <img v-for="i in emojihistory" :src="'/emoji/' + i"
                :key="i" class="emoji-item" @click="appendEmoji(i)">
            </div>

            <div class="emoji-title">
              全部表情
            </div>
            <div class="emoji-all-container">
              <img class="emoji-item" v-for="i in emoji" :src="'/emoji/' + i"
                :key="i" @click="appendEmoji(i)">
            </div>
        </div>
</template>
<script lang="ts" setup>
    import { ref, onMounted } from 'vue';

    const emojihistory = ref<string[]>([]);
    const emit = defineEmits(['emojiChick']);
    const emoji = ref([
    "0161251e1e8547b2a3af25089edfac36.png",...//就是你所有的表情包文件,由于表情包不多直接放在public中
    ]);
	
    // 往文本框中输入表情包图片地址
    function appendEmoji(i: string){
      let targetUrl = "/emoji/" + i;
      emit("emojiChick", targetUrl); // 请确保父组件中有对应的事件监听器
      saveHistory(i);
      restoreHistory();
    };
	
    // 移除历史记录
    function removeHistory(){
      window.localStorage.removeItem("emoji-history");
      restoreHistory();
    };
	
    // 保存历史记录
    function restoreHistory(){
      let record = window.localStorage.getItem("emoji-history");
      emojihistory.value = record ? record.split(",") : [];
    };

    // 保存表情包为历史记录
    function saveHistory(emojiIndex: string){
      let record = window.localStorage.getItem("emoji-history");
      let targetSaveStr = emojiIndex;
      let count = 1;
      if (record) {
        let splitArrays = record.split(",");
        for (let i = 0; i < splitArrays.length; i++) {
          let item = splitArrays[i];
          if (emojiIndex.toString() !== item) {
            targetSaveStr += ",";
            targetSaveStr += item;
            count++;
            if (count > 5) {
              break;
            }
          }
        }
      }
      window.localStorage.setItem("emoji-history", targetSaveStr);
    };

    onMounted(() => {
      restoreHistory();
    });
</script>

<style>
    /*均可自己设计*/
    .emoji-panel-container{
      width: 25vw;
      border: 0.1vh solid;
      border-radius: 0.5vh;
      box-shadow: 0.5vh 0.5vh 1vh rgba(29, 29, 29, 0.5); /* 水平偏移量 垂直偏移量 模糊半径 阴影颜色 */
      background-color: white;
    }

    .delete-history-btn{
      color: #0084FF;
      cursor: pointer;
    }

    .emoji-title{
      display: flex;
      justify-content: space-between;
    }

    .emoji-title {
      padding: 1vh;
    }

    /*滚动条效果*/
    .emoji-panel-container::-webkit-scrollbar{ 
      width: 1vw;
    }

    .emoji-panel-container::-webkit-scrollbar-track{
      background-color: #F9FAFB;
      -webkit-border-radius: 2em;
      -moz-border-radius: 2em;
      border-radius: 2em;
    }

    .emoji-panel-container::-webkit-scrollbar-thumb {
      background-color: #E5E6EB;
      -webkit-border-radius: 2em;
      -moz-border-radius: 2em;
      border-radius: 2em;
    }

    .emoji-panel-container {
      overflow-y: scroll;
      overflow-x: hidden;
      height: 20vh;
    }

    .emoji-item:hover{
      transform: scale(1.3);
      transition: transform .3s;
    }

    .emoji-item{
      width: 5vh;
      height: 5vh;
      transition: all .3s linear;
      padding: 1vh;
      cursor: pointer;
    }
</style>
富文本框组件
<template>
  <div class="rich-text-input">
      <!--用div的contenteditable属性将该标签设置为可编辑框-->
    <div ref="inputBox" class="input-box" :placeholder="hitContent" contenteditable="true" spellcheck="false"></div>
  </div>
</template>

<script setup lang="ts">
    import { ref, onMounted, watch} from 'vue';

    const props = defineProps({
      content: {
        type: String,
        required: true
      }
    });

    const emit = defineEmits(['update:content']);

    const hitContent = '请输入内容';
    const inputBox = ref<HTMLElement | null>(null);
	
    // 更新文本
    function updateContent(value: string){
      if (inputBox.value && value !== inputBox.value.innerHTML) {
        inputBox.value.innerHTML = value;
      }
    };
	
    // 获取输入框焦点
    const requestFocus = () => {
      if (inputBox.value) {
        inputBox.value.focus();
      }
    };

    defineExpose({
      requestFocus
    })

    onMounted(() => {
      updateContent(props.content);
      if (inputBox.value) {
          inputBox.value.addEventListener("input", () => {
              if(inputBox.value != null){
                  //console.log(inputBox.value.innerHTML);
                  emit('update:content', inputBox.value.innerHTML);
              }
          });
      }
    });
	
    // 当文本框内容发生变化(插入表情)时,获取输入框焦点
    watch(() => props.content, (newValue: string) => {
      updateContent(newValue);
      if (inputBox.value) {
        //console.log(inputBox.value.innerHTML);
        inputBox.value.focus();
      }
    });
</script>

<style>
    .rich-text-input{
      width: 100%;
      height: 40vh;
    }
    .input-box:focus{
      border: #0084FF solid 0.2vh;
    }

    .input-box:empty:before {
        content: attr(placeholder);
        position: flex;
        color: lightgray;
        background-color: transparent;
    }

    .input-box {
      font-size: 17px;
      width: 96%;
      border-radius: 0.5vh;
      height: 40vh;
      overflow-y: auto;
      display: block;
      outline: none;
      border: #EBEDF0 solid 0.2vh;
      padding: 1vh;
      margin: 1vh;
    }

</style>
组合一体

将富文本框和表情选择框组合一起,相互联系

<template>
  <div class="container">
    <div class="parent">
      <div class="input-box-container">
        <RichInput ref="richTextInput" v-model:content="Content" />
        <div class="emoji-container" v-if="isEmojiShow">
          <Emoji @emojiChick="onEmojiClick" />
        </div>
        <div class="input-action-part">
          <span class="emoji-trigger" ref="emojiTrigger" @click="controlEmojiPanelVisibility">
              <!--一个笑脸标志-->
            <svg t="1715419910457" class="emoji-icon" viewBox="0 0 1024 1024" version="1.1"
              xmlns="http://www.w3.org/2000/svg" p-id="14031"
              data-spm-anchor-id="a313x.search_index.0.i26.73883a81a68hSX" width="200" height="200">
              <path
                d="M536.953154 464.455014m-448.014129 0a448.014129 448.014129 0 1 0 896.028258 0 448.014129 448.014129 0 1 0-896.028258 0Z"
                fill="#FFE585" p-id="14032"></path>
              <path
                d="M547.508202 24.443486c-4.200646 0-8.421844 0.078094-12.593718 0.193181 223.698798 6.374953 403.064748 189.699047 403.064748 414.939177 0 229.268148-185.8601 415.132358-415.132358 415.132358-229.255817 0-415.132358-185.86421-415.132358-415.132358 0-36.104184 4.619889-71.143822 13.284236-104.5517-13.958312 43.305292-21.504678 89.483629-21.504679 137.433471 0 247.427106 200.599354 448.014129 448.014129 448.014129 247.431216 0 448.014129-200.587023 448.014129-448.014129 0-247.431216-200.582913-448.014129-448.014129-448.014129z"
                fill="#FF9900" opacity=".24" p-id="14033"></path>
              <path
                d="M437.757071 1024h197.290626a16.440885 16.440885 0 0 0 0-32.881771h-197.290626a16.440885 16.440885 0 0 0 0 32.881771z"
                fill="#ffffff" opacity=".29" p-id="14034" data-spm-anchor-id="a313x.search_index.0.i22.73883a81a68hSX"
                class=""></path>
              <path
                d="M708.480912 1024h57.543099a16.440885 16.440885 0 0 0 0-32.881771h-57.543099a16.440885 16.440885 0 0 0 0 32.881771zM359.112096 991.118229h-45.212435a16.440885 16.440885 0 0 0 0 32.881771h45.212435a16.440885 16.440885 0 0 0 0-32.881771z"
                fill="#ffffff" opacity=".17" p-id="14035" data-spm-anchor-id="a313x.search_index.0.i24.73883a81a68hSX"
                class=""></path>
              <path
                d="M125.931017 487.061232c0-234.948474 190.459438-425.407911 425.407912-425.407912 229.851799 0 417.092934 182.300648 425.107865 410.187762 0.18085-5.182989 0.300046-10.378309 0.300046-15.60651 0-247.427106-200.599354-448.014129-448.014129-448.014129-247.431216 0-448.014129 200.587023-448.014129 448.014129 0 205.954972 138.983025 379.435085 328.308042 431.819856-164.955514-58.558324-283.095607-215.955141-283.095607-400.993196z"
                fill="#FFEFB5" p-id="14036"></path>
              <path
                d="M244.531455 452.979276c-4.151324 144.252329 45.985157 270.925241 166.768122 304.933213 120.778855 34.007972 274.945038-35.121842 346.688951-160.331515L244.531455 452.979276z"
                fill="#C7A17B" p-id="14037"></path>
              <path
                d="M228.09468 452.506601c-4.841841 168.264242 61.982138 288.35258 178.749417 321.234351 129.956979 36.593301 290.465234-37.189283 365.406899-167.984748a16.449106 16.449106 0 0 0-9.815208-23.999582l-513.448853-144.601698a16.436775 16.436775 0 0 0-20.892255 15.351677z m515.62727 136.903253c-66.66368 116.348036-213.789054 184.836655-327.962783 152.678283-146.019724-41.114544-156.89537-215.544119-154.790937-288.636185a16.453216 16.453216 0 0 1-20.892255 15.351677l513.457073 144.605808a16.444996 16.444996 0 0 1-9.811098-23.999583z"
                fill="#6E6E96" p-id="14038"></path>
              <path
                d="M313.065286 97.954796C381.340173 55.385233 460.182439 32.881771 541.063375 32.881771c115.275268 0 223.649475 44.891838 305.163386 126.405748 81.51802 81.51391 126.409858 189.892227 126.409858 305.167495 0 237.969486-193.607867 431.573243-431.573244 431.573244-237.969486 0-431.573243-193.603757-431.573243-431.573244 0-51.965529 9.132912-102.767865 27.139792-151.005422a16.440885 16.440885 0 0 0-30.80611-11.49629C86.44001 353.877729 76.608361 408.551894 76.608361 464.455014c0 256.107893 208.347121 464.455014 464.455014 464.455015 256.103783 0 464.455014-208.347121 464.455015-464.455015 0-124.058811-48.315652-240.694563-136.044217-328.419017C781.753828 48.311542 665.122187 0 541.063375 0 454.025328 0 369.165697 24.225645 295.666719 70.054613a16.440885 16.440885 0 0 0 17.398567 27.900183z"
                fill="#6E6E96" p-id="14039"></path>
              <path
                d="M451.727714 273.078997m-36.991992 0a36.991992 36.991992 0 1 0 73.983984 0 36.991992 36.991992 0 1 0-73.983984 0Z"
                fill="#6E6E96" p-id="14040"></path>
              <path
                d="M711.016919 341.049728m-36.991993 0a36.991992 36.991992 0 1 0 73.983985 0 36.991992 36.991992 0 1 0-73.983985 0Z"
                fill="#6E6E96" p-id="14041"></path>
              <path
                d="M219.37279 277.008369c9.342533-28.442732 38.393578-63.326181 64.682553-77.646192l0.098646-0.061653c26.420503-14.274799 26.420503-37.579754 0-51.821671l-0.098646-0.086315C257.762257 133.13829 227.190431 101.0128 216.117495 76.039095c-11.052385-25.014807-29.174351-25.014807-40.255509 0-11.035944 25.006587-41.636542 57.115636-68.052935 71.349333l-0.123306 0.098645c-26.342409 14.254248-26.342409 37.542762 0 51.829892l0.123306 0.049322c26.416393 14.258358 55.393453 49.19524 64.662003 77.637972l6.658558 20.123644c9.301431 28.467393 24.32018 28.467393 33.679154 0l6.564024-20.119534z"
                fill="#CFD3FF" p-id="14042"></path>
              <path
                d="M234.991631 282.137925c8.101246-24.648998 34.16827-55.944223 56.930676-68.33654l0.300046-0.164409 0.291826-0.17674-0.542549 0.304157c18.080864-9.765886 28.446842-24.492809 28.438621-40.403476-0.00411-15.902446-10.374199-30.608819-28.450952-40.358264l3.058005 2.129095-1.393365-1.224846a48.891083 48.891083 0 0 0-1.730403-0.974123c-23.235081-12.593718-51.048949-41.702306-60.744962-63.560463-10.970181-24.821627-26.724659-28.533157-35.138282-28.537267s-24.172212 3.699199-35.183495 28.541377c-9.65902 21.882819-37.50577 50.970855-60.818946 63.535802l-1.331712 0.719289-1.183743 0.953571 2.371597-1.561884c-18.023321 9.753555-28.360527 24.455817-28.368747 40.341823-0.00822 15.894226 10.328986 30.617039 28.352307 40.399366l0.92891 0.501447 0.982342 0.37814-1.755064-0.817934c22.750075 12.277231 48.784217 43.539575 56.848472 68.266667l6.679109 20.193517c8.799984 26.938391 22.47469 32.618717 32.400875 32.626938s23.625552-5.647444 32.507741-32.655709l6.551693-20.119534z m-37.793485 9.860421c-2.947029 8.956172-5.478925 11.278447-5.507697 11.298999 1.878371-1.66464 6.666779-1.656419 8.54104 0.00822-0.028772-0.020551-2.548337-2.338716-5.466594-11.278447l-6.67911-20.185297c-10.604371-32.520071-42.446256-70.73691-72.471423-86.939403l-0.908359-0.493226-0.965902-0.37403 1.783836 0.834375c-7.706665-4.180095-11.155141-8.779433-11.155141-11.479849 0-2.696305 3.444366-7.279202 11.1387-11.438746l1.323491-0.715178 1.171413-0.945351-2.396259 1.574215c29.692239-16.001092 62.76308-50.781785 75.291035-79.187525 3.34983-7.562807 6.198214-9.145243 6.226986-9.157573a3.312838 3.312838 0 0 1-2.268843 0c0.028772 0.012331 2.868935 1.594766 6.218765 9.169903 12.585498 28.372858 45.590575 63.13711 75.143067 79.158754l-3.021012-2.112654 1.409806 1.237177c0.094535 0.086315 1.750954 0.978233 1.750954 0.978232 7.718996 4.159544 11.175692 8.738331 11.175692 11.426416 0 2.696305-3.456696 7.283312-11.188023 11.459297l-0.324707 0.172629-0.320598 0.18907 0.497337-0.279495c-29.963514 16.317579-61.780737 54.509756-72.442651 86.951733l-6.555803 20.127754z"
                fill="#6E6E96" p-id="14043"></path>
              <path
                d="M260.791491 594.53119s251.730508-113.355795 386.081313 118.867602c0 0 111.115724-89.820668 111.115724-115.809597L244.531455 452.979276s-29.030494 96.269605 16.260036 141.551914z"
                fill="#6E6E96" opacity=".25" p-id="14044"></path>
            </svg>
          </span>
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
  import { ref, onMounted, watch } from 'vue';
  import Emoji from './Emoji.vue'
  import RichInput from './RichInputBox.vue'

  const props = defineProps({
    inputContent: {
      type: String,
      required: true
    }
  });
  const emit = defineEmits(['update:inputContent']);

  const isEmojiShow = ref(false);
  const Content = ref('');
  const richTextInput = ref<InstanceType<typeof RichInput> | null>(null);
  const emojiTrigger = ref<HTMLElement | null>(null);
    
  // 当点击表情时,将表情加入输入框
  function onEmojiClick(emojiUrl: string) {
    if (richTextInput.value != null) {
      //console.log(emojiUrl);
      const inputBox = richTextInput.value;
      inputBox?.requestFocus();
      const imgTag = `<img src="${emojiUrl}" class="emoji">`;
      document.execCommand("insertHTML", false, imgTag);
    }
  };

  // 模拟发布按键指令
  function doPost() {
    console.log(props.inputContent);
  }

  // 控制表情面板的显示和隐藏
  function controlEmojiPanelVisibility() {
    console.log(isEmojiShow)
    isEmojiShow.value = !isEmojiShow.value;
  }

  onMounted(() => {
    if (emojiTrigger.value) {
      emojiTrigger.value.addEventListener("mousedown", (event) => {
        event.preventDefault();
      });
    }
    Content.value = props.inputContent;
  });

  // 监听content的属性编号
  watch(Content, ()=> {
    emit('update:inputContent', Content.value);
  });
  
</script>

<style>
  .container {
    background-color: white;
    width: 100%;
    min-height: 50vh;
  }

  .input-box-container {
    padding: 5px;
    margin-bottom: 20px;
    position: relative;
  }

  .emoji-trigger {
    cursor: pointer;
    color: #0084FF;
  }

  .emoji-icon {
    width: 2vw;
    height: 2vw;
    transition: transform 0.5s ease;
  }

  .emoji-icon:hover {
    transform: scale(1.3);
  }

  .emoji-container {
    position: absolute;
    top: 20vh;
    left: 2vw;
  }

  .content-post-btn:hover {
    opacity: .8;
  }

  .content-post-btn {
    background: #0084FF;
    color: white;
    border-radius: 4px;
    margin-right: 10px;
    cursor: pointer;
  }

  .input-action-part {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 5px 20px;
    margin-top: 10px;
    margin-bottom: 10px;
  }

  .input-action-part {
    display: flex;
    justify-content: space-between;
  }

  .parent {
    width: 100%;
    height: 40vh;
    background: #fff;
  }
</style>

资源管理器的面包屑组件

因为element Plus的面包屑组件只能和路由绑定,故而无法使用,然而每次要寻找路径时都需要遍历数结构,时间消耗巨大,十分缓慢,故而我采取以空间换时间,将所有的路径直接存储,然后通过id寻找节点,时间复杂度直接变为O(1).

    const defaultProps = {// 树结构
        id: 'id',
        children: 'children',
        label: 'name',
    }    
function setbread(date: any[], idPath: object[]) {
        date.forEach((item: any) => {// data为一个树结构
            //console.log(item);
            idPath.push({ id: item.id, name: item.name, children: item.children });
            //console.log([...idPath]);
            //console.log(idPath.length);
            paths.value.push({ id: item.id, name: item.name, path: [...idPath] });
            //console.log(paths.value);

            //console.log(item.children)
            if (item.children !== null) {
                setbread(item.children, [...idPath]);
            }
            idPath.pop();
        })
    }

总结

Vue 3携手TypeScript和Element Plus,为前端开发带来了高效与健康新生态。Vue 3通过Composition API增强代码组织灵活性,性能优化及强化TypeScript支持,提升了应用的开发效率与维护性。TypeScript注入静态类型检查,减少运行时错误,IDE智能提示让开发更流畅。Element Plus,作为成熟的UI组件库,提供丰富预设样式组件,支持按需加载与国际化,无缝融合Vue 3新特性,加速界面搭建。三者结合,不仅极大提升开发速度与代码质量,还保障了项目的可维护性和未来的扩展性,为开发者营造了现代化、高效且愉悦的开发体验。

参考文献、参考博客

vue官方文档:https://cn.vuejs.org/guide/introduction.html

Element Plus组件官方文档:https://element-plus.org/zh-CN/component/overview.html

axios官方文档:https://www.axios-http.cn/docs/req_config

TinyMCE中文文档中文手册 (ax-z.cn)

  • 17
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值