使用Vue3重构vue2项目(下)

简介: 使用Vue3重构vue2项目(下)

如果需要在vue的原型上挂载东西,就不能使用以前的原型挂载方法,需要使用新方法config.globalProperties,详细用法请查阅官方文档。


我的项目中用到了一个websocket的插件,他需要在vuex中往Vue原型上挂载方法,下面是我的做法。


  • main.ts中的createApp方法导出。


import { createApp } from "vue";
const app = createApp(App);
export default app;


  • store/index.ts中导入main.ts,然后调用方法挂载即可。


mutations: {
    // 连接打开
    SOCKET_ONOPEN(state, event) {
      main.config.globalProperties.$socket = event.currentTarget;
      state.socket.isConnected = true;
      // 连接成功时启动定时发送心跳消息,避免被服务器断开连接
      state.socket.heartBeatTimer = setInterval(() => {
        const message = "心跳消息";
        state.socket.isConnected &&
          main.config.globalProperties.$socket.sendObj({
            code: 200,
            msg: message
          });
      }, state.socket.heartBeatInterval);
    }
  }


适配axios


axios在封装成插件时与之前的差别对比如下:


  • 暴露install方法由原来的Plugin.install改为了install
  • 增加了ts的类型声明
  • Object.defineProperties舍弃了,现在直接使用app.config.globalProperties挂载即可


适配完成的代码如下:


import { App } from "vue";
import axiosObj, { AxiosInstance, AxiosRequestConfig } from "axios";
import store from "../store/index";
const defaultConfig = {
  // baseURL在此处省略配置,考虑到项目可能由多人协作完成开发,域名也各不相同,此处通过对api的抽离,域名单独配置在base.js中
  // 请求超时时间
  timeout: 60 * 1000,
  // 跨域请求时是否需要凭证
  // withCredentials: true, // Check cross-site Access-Control
  heards: {
    get: {
      "Content-Type": "application/x-www-form-urlencoded;charset=utf-8"
      // 将普适性的请求头作为基础配置。当需要特殊请求头时,将特殊请求头作为参数传入,覆盖基础配置
    },
    post: {
      "Content-Type": "application/json;charset=utf-8"
      // 将普适性的请求头作为基础配置。当需要特殊请求头时,将特殊请求头作为参数传入,覆盖基础配置
    }
  }
};
/**
 * 请求失败后的错误统一处理,当然还有更多状态码判断,根据自己业务需求去扩展即可
 * @param status 请求失败的状态码
 * @param msg 错误信息
 */
const errorHandle = (status: number, msg: string) => {
  // 状态码判断
  switch (status) {
    // 401: 未登录状态,跳转登录页
    case 401:
      // 跳转登录页
      break;
    // 403 token过期
    case 403:
      // 如果不需要自动刷新token,可以在这里移除本地存储中的token,跳转登录页
      break;
    // 404请求不存在
    case 404:
      // 提示资源不存在
      break;
    default:
      console.log(msg);
  }
};
export default {
  // 暴露安装方法
  install(app: App, config: AxiosRequestConfig = defaultConfig) {
    let _axios: AxiosInstance;
    // 创建实例
    _axios = axiosObj.create(config);
    // 请求拦截器
    _axios.interceptors.request.use(
      function(config) {
        // 从vuex里获取token
        const token = store.state.token;
        // 如果token存在就在请求头里添加
        token && (config.headers.token = token);
        return config;
      },
      function(error) {
        // Do something with request error
        error.data = {};
        error.data.msg = "服务器异常";
        return Promise.reject(error);
      }
    );
    // 响应拦截器
    _axios.interceptors.response.use(
      function(response) {
        // 清除本地存储中的token,如果需要刷新token,在这里通过旧的token跟服务器换新token,将新的token设置的vuex中
        if (response.data.code === 401) {
          localStorage.removeItem("token");
          // 页面刷新
          parent.location.reload();
        }
        // 只返回response中的data数据
        return response.data;
      },
      function(error) {
        if (error) {
          // 请求已发出,但不在2xx范围内
          errorHandle(error.status, error.data.msg);
          return Promise.reject(error);
        } else {
          // 断网
          return Promise.reject(error);
        }
      }
    );
    // 将axios挂载到vue的全局属性中
    app.config.globalProperties.$axios = _axios;
  }
};


然后将其在main.js中use,就可以在代码中通过this.$axios.xx来使用了。


不过上述将axios挂载到vue上是多此一举的,因为我已经将api进行了抽离,在每个单独的api文件中都是通过导入我们封装好的axios的配置文件,然后用导入进来的axios实例来进行的接口封装。(ps: 之前由于自己太菜没注意到这个,傻傻的将其封装成了插件😂)


那么,不需要将其封装成插件的话,那它就属于对axios进行配置封装了,我们将它放在config目录下,将上述代码稍作修改即可,修改好的代码地址:config/axios.ts。


最后在main.ts中将api挂载到全局属性。


import { createApp } from "vue";
import api from "./api/index";
const app = createApp(App);
app.config.globalProperties.$api = api;


随后就就可以在业务代码中通过this.$api.xx按模块来调用我们抛出来的接口了。


shims-vue.d.ts类型声明文件


shims-vue.d.ts是一个Typescript的声明文件,当项目启用ts后,有些文件是我们自己封装的,类型较为复杂,ts不能推导出其具体类型,此时就需要我们进行手动声明。

例如上面我们挂载到原型上的$api,它导出了一个类文件,此时类型就较为复杂了,ts没法推导出其类型,我们在使用时就会报错。


640.png

                            image-20201010100416381


要解决这个错误,我们就需要在shims-vue.d.ts中声明api的的类型


// 声明全局属性类型
declare module "@vue/runtime-core" {
  interface ComponentCustomProperties<T> {
    $api: T;
  }
}


注意:在shims-vue.d.ts文件中,类型声明超过1个时,组件内需要import包就不能在其内部进行,需要将其写在最外层,否则会报错。


640.png

                                image-20201010101906448



适配入口文件


由于启用了typescript,入口文件由main.js变成了main.ts,文件中的写法与之前相比其不同点如下:


  • 初始化挂载vue由原先的new Vue(App)改为了按需导入写法的createApp(App)
  • 使用插件时,也由原先的Vue.use()改成了,createApp(App).use()

在我的项目中引用了几个插件,需要在入口文件中做一些初始化的操作,插件还是2.x版本,没有ts的类型声明文件,因此导入时ts没法推导出它的类型,就得用// @ts-ignore让ts忽略它。


完整的入口文件地址:main.ts


适配组件


基础设施完善后,接下来我们来适配组件,我们先来试试把2.x项目的所有组件搬过来看看,能不能直接启动。


结果可想而知,无法运行。因为我用了2.x的插件,vue3.0有关插件的封装,一些写法变了。我项目中总共引用了2个插件v-viewervue-native-websocketv-viewer这个插件无解,他底层使用用到的2.x语法太多了,所以我选择放弃这个插件。vue-native-websocket这个插件就是使用的Vue.prototype.xx写法被舍弃了,用新的写法Vue.config.globalProperties.xx将其替换即可。


640.png

                                    image-20201009174402912


替换完成后,重新编译即可,随后启动项目,如下所示,错误解决,项目成功启动。


640.png

                                    image-20201009175415170


正如上图中所看到的,控制台有黄色警告,因为我们组件的代码还是使用的vue2.x的语法,我们要重新整理组件中的方法从而适配vue3.0


注意:组件script标签声明lang="ts"后,就必须按照Vue官方文档所说使用defineComponent全局方法来定义组件。


组件优化


接下来,我们从login.vue组件开始重构,看看都做了哪些优化。


  1. 创建type文件夹,文件夹内创建ComponentDataType.ts,将组件中用到的类型指定放在其中。
  2. 创建enum文件夹,将组件中用到的枚举放在其中。


我们先来看看第一点,将组件内用到的类型进行统一管理,我们以登录组件为例,我们需要为data返回的对象指定其每个属性的类型,因此我们ComponentDataType.ts中创建一个名为loginDataType的类型,其代码如下。


export type loginDataType<T> = {
  loginUndo: T; // 禁止登录时的图标
  loginBtnNormal: T; // 登录时的按钮图标
  loginBtnHover: T; // 鼠标悬浮时的登录图标
  loginBtnDown: T; // 鼠标按下时的登录图标
  userName: string; // 用户名
  password: string; // 密码
  confirmPassword: string; // 注册时的确认登录密码
  isLoginStatus: number; // 登录状态:0.未登录 1.登录中 2.注册
  loginStatusEnum: Object; // 登录状态枚举
  isDefaultAvatar: boolean; // 头像是否为默认头像
  avatarSrc: T; // 头像地址
  loadText: string; // 加载层的文字
};


声明好类型后,就可以在组件中使用了,代码如下:


import { loginDataType } from "@/type/ComponentDataType";
export default defineComponent({
  data<T>(): loginDataType<T> {
    return {
      loginUndo: require("../assets/img/login/icon-enter-undo@2x.png"),
      loginBtnNormal: require("../assets/img/login/icon-enter-undo@2x.png"),
      loginBtnHover: require("../assets/img/login/icon-enter-hover@2x.png"),
      loginBtnDown: require("../assets/img/login/icon-enter-down@2x.png"),
      userName: "",
      password: "",
      confirmPassword: "",
      isLoginStatus: 0,
      loginStatusEnum: loginStatusEnum,
      isDefaultAvatar: true,
      avatarSrc: require("../assets/img/login/LoginWindow_BigDefaultHeadImage@2x.png"),
      loadText: "上传中"
    };
  }
})


上述代码完整地址:


  • type/ComponentDataType.ts
  • login.vue


再然后,我们看看第二点,使用enum来优化组件内部的条件判断,例如上面data中的isLoginStatus就有3种状态,我们要根据这三种状态来做不同的事情,如果直接用数字来代表三种状态直接赋值数字,后期维护时将是一件很痛苦的事情,如果用enum来定义的话,根据语意一眼就能看出它的状态是什么。


我们在enum文件夹中创建ComponentEnum.ts文件,组件内用到的所有枚举都会在此文件内定义,接下来在组件内创建loginStatusEnum,代码如下:


export enum loginStatusEnum {
  NOT_LOGGED_IN = 0, // 未登录
  LOGGING_IN = 1, // 登录中
  REGISTERED = 2 // 注册
}


声明好后,我们就可以在组件中使用了,代码如下:


import { loginStatusEnum } from "@/enum/ComponentEnum";
export default defineComponent({
  methods: {
    stateSwitching: function(status) {
      case "条件1":
       this.isLoginStatus = loginStatusEnum.LOGGING_IN;
       break;
      case "条件2":
       this.isLoginStatus = loginStatusEnum.NOT_LOGGED_IN;
       break;
    }
  }
})


上述代码完整地址:


  • enum/ComponentEnum.ts
  • login.vue


this指向


在适配组件过程中,方法内部的this不能很好的识别,无奈就用了很笨的方法解决。

如下所示:


const _img = new Image();
_img.src = base64;
_img.onload = function() {
    const _canvas = document.createElement("canvas");
    const w = this.width / scale;
    const h = this.height / scale;
    _canvas.setAttribute("width", w + "");
    _canvas.setAttribute("height", h + "");
    _canvas.getContext("2d")?.drawImage(this, 0, 0, w, h);
    const base64 = _canvas.toDataURL("image/jpeg");
}


onload方法内部的this应该是指向_img的,但是ts并不这么认为,报错如下所示。


640.png

                                               image-20201013171520088


this对象中不包含width属性,解决方案就是讲this换成_img,问题解决。


640.png

                                    image-20201013171712449


Dom对象类型定义


当操作dom对象时,层级过时ts就无法推断出具体类型了,如下所示:


sendMessage: function(event: KeyboardEvent) {
      if (event.key === "Enter") {
        // 阻止编辑框默认生成div事件
        event.preventDefault();
        let msgText = "";
        // 获取输入框下的所有子元素
        const allNodes = event.target.childNodes;
        for (const item of allNodes) {
          // 判断当前元素是否为img元素
          if (item.nodeName === "IMG") {
            if (item.alt === "") {
              // 是图片
              let base64Img = item.src;
              // 删除base64图片的前缀
              base64Img = base64Img.replace(/^data:image\/\w+;base64,/, "");
              //随机文件名
              const fileName = new Date().getTime() + "chatImg" + ".jpeg";
              //将base64转换成file
              const imgFile = this.convertBase64UrlToImgFile(
                base64Img,
                fileName,
                "image/jpeg"
              );
            }
          }
        }
      }
}


上面为一个发送消息的函数的部分代码,消息框中包含图片和文字,要对图片进行单独处理,我们需要要从target中拿到所有节点childNodes,然后遍历每个节点获取其类型,childNodes的类型为NodeList,那么他的每一个元素就是Node类型,如果当前遍历到的元素的nodeName属性是IMG时,它就是一个图片,我们就获取它的alt属性进一步判断,再获取src属性。


然而,ts会报错altsrc属性不存在,报错如下:


640.png

                                       image-20201013172815950


此时,我们就需要把item断言成HTMLImageElement类型。


640.png

                                    image-20201019110053258


复杂类型定义


在适配组件过程中,遇到一个比较复杂的数据类型定义,数据如下:


data(){
    return {
      friendsList: [
        {
          groupName: "我",
          totalPeople: 2,
          onlineUsers: 2,
          friendsData: [
            {
              username: "神奇的程序员",
              avatarSrc:
                "https://www.kaisir.cn/uploads/1ece3749801d4d45933ba8b31403c685touxiang.jpeg",
              signature: "今天的努力只为未来",
              onlineStatus: true,
              userId: "c04618bab36146e3a9d3b411e7f9eb8f"
            },
            {
              username: "admin",
              avatarSrc:
                "https://www.kaisir.cn/uploads/40ba319f75964c25a7370e3909d347c5admin.jpg",
              signature: "",
              onlineStatus: true,
              userId: "32ee06c8380e479b9cd4097e170a6193"
            }
          ]
        },
        {
          groupName: "我的朋友",
          totalPeople: 0,
          onlineUsers: 0,
          friendsData: []
        },
        {
          groupName: "我的家人",
          totalPeople: 0,
          onlineUsers: 0,
          friendsData: []
        },
        {
          groupName: "我的同事",
          totalPeople: 0,
          onlineUsers: 0,
          friendsData: []
        }
      ]
    };
  },


一开始我是这样定义的。

640.png

                                    image-20201014214430066


嵌套到一起,自认为没问题,放进代码后,报错长度不匹配,这样写知识给第一个对象定义了类型。


640.png

                            image-20201014214529652


经过一番求助后,他们说应该分开写,不能这样嵌套定义,正确写法如下:


  • 类型分开定义


// 联系人面板Data属性定义
export type contactListDataType<V> = {
  friendsList: Array<V>;
};
// 联系人列表类型定义
export type friendsListType<V> = {
  groupName: string; // 分组名称
  totalPeople: number; // 总人数
  onlineUsers: number; // 在线人数
  friendsData: Array<V>; // 好友列表
};
// 联系人类型定义
export type friendsDataType = {
  username: string; // 昵称
  avatarSrc: string; // 头像地址
  signature: string; // 个性签名
  onlineStatus: boolean; // 在线状态
  userId: string; // 用户id
};


  • 组件中使用


import {
  contactListDataType,
  friendsListType,
  friendsDataType
} from "@/type/ComponentDataType";
data(): contactListDataType<friendsListType<friendsDataType>> {
    return {
      friendsList: [
        {
          groupName: "我",
          totalPeople: 2,
          onlineUsers: 2,
          friendsData: [
            {
              username: "神奇的程序员",
              avatarSrc:
                "https://www.kaisir.cn/uploads/1ece3749801d4d45933ba8b31403c685touxiang.jpeg",
              signature: "今天的努力只为未来",
              onlineStatus: true,
              userId: "c04618bab36146e3a9d3b411e7f9eb8f"
            },
            {
              username: "admin",
              avatarSrc:
                "https://www.kaisir.cn/uploads/40ba319f75964c25a7370e3909d347c5admin.jpg",
              signature: "",
              onlineStatus: true,
              userId: "32ee06c8380e479b9cd4097e170a6193"
            }
          ]
        },
        {
          groupName: "我的朋友",
          totalPeople: 0,
          onlineUsers: 0,
          friendsData: []
        },
        {
          groupName: "我的家人",
          totalPeople: 0,
          onlineUsers: 0,
          friendsData: []
        },
        {
          groupName: "我的同事",
          totalPeople: 0,
          onlineUsers: 0,
          friendsData: []
        }
      ]
    };
  }


深刻的理解到了typescript泛型的使用,经验++😄


tag属性被移除


我们在使用router-link时,它默认会渲染成a标签,如果想让他渲染成其它自定义标签,可以通过tag属性来修改,如下所示:


<router-link :to="{ name: 'list' }" tag="div">


然而,在vue-router的新版本中,官方将event和tag属性移除了,因此我们就不能这么使用了,当然官方文档中也给了解决方案使用v-solt来作为替代方案,上述代码中我们希望将其渲染成div,用v-solt的写法如下所示:


<router-link :to="{ name: 'list' }" custom v-slot="{ navigate }">
    <div
      @click="navigate"
      @keypress.enter="navigate"
      role="link"
    >
  </div>
</router-link>


有关这一块的更多讲解,请移步官方文档:removal-of-event-and-tag-props-in-router-link


组件无法外链文件


当我把页面当组件进行引入声明时,发现vue3不支持将逻辑代码外链,像下面这样,通过src外链。


<script lang="ts" src="../assets/ts/message-display.ts"></script>


在组件中引用。


<template>
   <message-display message-status="0" list-id="1892144211" />
</template>
<script>
import messageDisplay from "@/components/message-display.vue";
export default defineComponent({
  name: "msg-list",
  components: {
    messageDisplay
  },
})
</script>


然后,他就报错了,类型无法推断。


640.png

                    image-20201018224619607


尝试了很多方法,最后发现是不能通过src外链的问题,于是我把ts文件中的代码写在vue模版中报错就没了。


必须使用as进行断言


当我把代码搬到vue模版中后,它报了一些很奇怪的错误,如下所示imgContent变量可能存在多个类型,ts无法推断出具体类型,此时就需要我们自己进行断言给他指定类型,我用了尖括号的写法,他报错了,webstorm可能对vue3的适配不是很好,他的报错很奇怪,如下所示


image.png


                            image-20201018225114933  


一开始,我看到这个错误我是一脸懵逼的,一个朋友告诉我用排除法,注释下距离它最近的代码,看看是否会报错,于是找到了问题根源,就是上面的类型断言的锅,将它修改后,问题解决。


image.png

                       image-20201018225618020


问题是解决了,但是我很是想不通为何一定要用as,尖括号跟他是同等的才对,于是我翻了官方文档。


image.png

                        image-20201018225919664


正如官方文档所说,启用jsx后就只能使用as语法了。可能vue3的模版语法默认是启用jsx的吧。


ref数组不会自动创建数组


在vue2中,在v-for里使用ref属性时会用ref数组填充相应的$refs属性,如下所示为好友列表的部分代码,它通过循环friendsList,将groupArrowbuddyList放进ref数组中。


<template>
            <div class="group-panel">
                <div class="title-panel">
                    <p class="title">好友</p>
                </div>
                <div class="row-panel" v-for="(item,index) in friendsList" :key="index">
                    <div class="main-content" @click="groupingStatus(index)">
                        <div class="icon-panel">
                            <img ref="groupArrow" src="../assets/img/list/tchat_his_arrow_right@2x.png" alt="左箭头"/>
                        </div>
                        <div class="name-panel">
                            <p>{{item.groupName}}</p>
                        </div>
                        <div class="quantity-panel">
                            <p>{{item.onlineUsers}}/{{item.totalPeople}}</p>
                        </div>
                    </div>
                    <!--好友列表-->
                    <div class="buddy-panel" ref="buddyList" style="display:none">
                        <div class="item-panel" v-for="(list,index) in item.friendsData" :key="index" tabindex="0">
                            <div class="main-panel" @click="getBuddyInfo(list.userId)">
                                <div class="head-img-panel">
                                    <img :src="list.avatarSrc" alt="用户头像">
                                </div>
                                <div class="nickname-panel">
                                    <!--昵称-->
                                    <div class="name-panel">
                                        {{list.username}}
                                    </div>
                                    <!--签名-->
                                    <div class="signature-panel">
                                        [{{list.onlineStatus?"在线":"离线"}}]{{list.signature}}
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
</template>


我们通过$refs可以访问到相应的节点,如下所示。


import lodash from 'lodash';
export default {
   name: "contact-list",
   methods:{
        // 分组状态切换
        groupingStatus:function (index) {
            if(lodash.isEmpty(this.$route.params.userId)===false){
                this.$router.push({name: "list"}).then();
            }
            // 获取transform的值
            let transformVal = this.$refs.groupArrow[index].style.transform;
            if(lodash.isEmpty(transformVal)===false){
                // 截取rotate的值
                transformVal = transformVal.substring(7,9);
                // 判断是否展开
                if (parseInt(transformVal)===90){
                    this.$refs.groupArrow[index].style.transform = "rotate(0deg)";
                    this.$refs.buddyList[index].style.display = "none";
                }else{
                    this.$refs.groupArrow[index].style.transform = "rotate(90deg)";
                    this.$refs.buddyList[index].style.display = "block";
                }
            }else{
                // 第一次点击添加transform属性,旋转90度
                this.$refs.groupArrow[index].style.transform = "rotate(90deg)";
                this.$refs.buddyList[index].style.display = "block";
            }
        },
        // 获取列表好友信息
        getBuddyInfo:function (userId) {
            // 判断当前路由params与当前点击项的userId是否相等
            if(!lodash.isEqual(this.$route.params.userId,userId)){
                this.$router.push({name: "dataPanel", params: {userId: userId}}).then();
            }
        }
    }
}


上述写法在vue2没问题,但是在vue3中你得到的结果是报错,官方认为这种行为会变得不明确且效率低下,采用了新的语法来解决这个问题,通过ref来绑定一个函数去处理,如下所示。


<template>
    <!---其它代码省略--->
    <img :ref="setGroupArrow" src="../assets/img/list/tchat_his_arrow_right@2x.png" alt="左箭头" />
    <!---其它代码省略--->
          <div class="buddy-panel" :ref="setGroupList" style="display:none">
        </div>
</template>
<script lang="ts">
import _ from "lodash";
import { defineComponent } from "vue";
import {
  contactListDataType,
  friendsListType,
  friendsDataType
} from "@/type/ComponentDataType";
export default defineComponent({
  name: "contact-list",
  data(): contactListDataType<friendsListType<friendsDataType>> {
    return {  
        groupArrow: [],
        groupList: []
    }
  },
   // 设置分组箭头Dom
   setGroupArrow: function(el: Element) {
      this.groupArrow.push(el);
   },
   // 设置分组列表dom
   setGroupList: function(el: Element) {
      this.groupList.push(el);
   },
    // 列表状态切换
    groupingStatus: function(index: number) {
      if (!_.isEmpty(this.$route.params.userId)) {
        this.$router.push({ name: "list" }).then();
      }
      // 获取transform的值
      let transformVal = this.groupArrow[index].style.transform;
      if (!_.isEmpty(transformVal)) {
        // 截取rotate的值
        transformVal = transformVal.substring(7, 9);
        // 判断分组列表是否展开
        if (parseInt(transformVal) === 90) {
          this.groupArrow[index].style.transform = "rotate(0deg)";
          this.groupList[index].style.display = "none";
        } else {
          this.groupArrow[index].style.transform = "rotate(90deg)";
          this.groupList[index].style.display = "block";
        }
      } else {
        // 第一次点击添加transform属性,旋转90度
        this.groupArrow[index].style.transform = "rotate(90deg)";
        this.groupList[index].style.display = "block";
      }
    }
)}


完整代码请移步:contact-list.vue


ref更多描述请移步官方文档: v-for 中的 Ref 数组


项目地址


至此,项目已经可以正常启动了,重构工作也结束了,接下来要解决的问题就是vue-native-websocket这个插件无法在vue3中工作的问题了。一开始我以为把它在原型行挂载的写法改动下就可以了,然而是我想的太简单了,改动后编辑器是不报错了,但是在运行时会报很多错。无奈只好先把与服务端交互这部分代码移除掉了。


接下来我会尝试重构vue-native-websocket这个插件,让其支持vue3。


最后放上本文重构好的项目代码地址:chat-system


写在最后


  • 公众号无法外链,文中链接可点击下方阅读原文进行查看。
相关文章
|
2天前
|
缓存 JavaScript UED
Vue3中v-model在处理自定义组件双向数据绑定时有哪些注意事项?
在使用`v-model`处理自定义组件双向数据绑定时,要仔细考虑各种因素,确保数据的准确传递和更新,同时提供良好的用户体验和代码可维护性。通过合理的设计和注意事项的遵循,能够更好地发挥`v-model`的优势,实现高效的双向数据绑定效果。
105 64
|
14天前
|
JavaScript 前端开发
如何在 Vue 项目中配置 Tree Shaking?
通过以上针对 Webpack 或 Rollup 的配置方法,就可以在 Vue 项目中有效地启用 Tree Shaking,从而优化项目的打包体积,提高项目的性能和加载速度。在实际配置过程中,需要根据项目的具体情况和需求,对配置进行适当的调整和优化。
|
2天前
|
JavaScript 前端开发 API
Vue 3 中 v-model 与 Vue 2 中 v-model 的区别是什么?
总的来说,Vue 3 中的 `v-model` 在灵活性、与组合式 API 的结合、对自定义组件的支持等方面都有了明显的提升和改进,使其更适应现代前端开发的需求和趋势。但需要注意的是,在迁移过程中可能需要对一些代码进行调整和适配。
|
24天前
|
JavaScript 前端开发 开发者
Vue 3中的Proxy
【10月更文挑战第23天】Vue 3中的`Proxy`为响应式系统带来了更强大、更灵活的功能,解决了Vue 2中响应式系统的一些局限性,同时在性能方面也有一定的提升,为开发者提供了更好的开发体验和性能保障。
51 7
|
24天前
|
JavaScript 数据管理 Java
在 Vue 3 中使用 Proxy 实现数据双向绑定的性能如何?
【10月更文挑战第23天】Vue 3中使用Proxy实现数据双向绑定在多个方面都带来了性能的提升,从更高效的响应式追踪、更好的初始化性能、对数组操作的优化到更优的内存管理等,使得Vue 3在处理复杂的应用场景和大量数据时能够更加高效和稳定地运行。
40 1
|
24天前
|
JavaScript 开发者
在 Vue 3 中使用 Proxy 实现数据的双向绑定
【10月更文挑战第23天】Vue 3利用 `Proxy` 实现了数据的双向绑定,无论是使用内置的指令如 `v-model`,还是通过自定义事件或自定义指令,都能够方便地实现数据与视图之间的双向交互,满足不同场景下的开发需求。
44 1
|
7月前
|
JavaScript API
【vue实战项目】通用管理系统:api封装、404页
【vue实战项目】通用管理系统:api封装、404页
77 3
|
7月前
|
人工智能 JavaScript 前端开发
毕设项目-基于Springboot和Vue实现蛋糕商城系统(三)
毕设项目-基于Springboot和Vue实现蛋糕商城系统
|
7月前
|
JavaScript Java 关系型数据库
毕设项目-基于Springboot和Vue实现蛋糕商城系统(一)
毕设项目-基于Springboot和Vue实现蛋糕商城系统
176 0
|
7月前
|
JavaScript 前端开发 API
Vue3+Vite+TypeScript常用项目模块详解
现在无论gitee还是github,越来越多的前端开源项目采用Vue3+Vite+TypeScript+Pinia+Elementplus+axios+Sass(css预编译语言等),其中还有各种项目配置比如eslint 校验代码工具配置等等,而我们想要进行前端项目的二次开发,就必须了解会使用这些东西,所以作者写了这篇文章进行简单的介绍。
145 0
Vue3+Vite+TypeScript常用项目模块详解
下一篇
无影云桌面