app开发定制公司Vue3+TS+Vite 入门指南

app开发定制公司最近尝试上手 Vue3+TS+Vite,对比起 Vue2 app开发定制公司有些不适应,app开发定制公司但还是真香~

app开发定制公司上手前先说下 Vue3 app开发定制公司的一些变化吧~

Vue3 的变化

Vue3 app开发定制公司带来的变化主要有以下几个方面:

  • 使用层面

    • 对比起 Vue2 app开发定制公司启动速度快很多,新项目从 1s app开发定制公司升级到不到 500ms
    • vite.config.ts app开发定制公司配置文件修改后无需重app开发定制公司启服务就能更新
  • 代码层面

    • app开发定制公司函数式编程,app开发定制公司方便组合逻辑,如mixinapp开发定制公司容易命名冲突,app开发定制公司数据来源不清晰
    • 新增 refreative API定义变量
    • 更好的 ts 支持
    • app开发定制公司组件文件中 template app开发定制公司模板内无需用根节点标app开发定制公司签包着组件元素
  • 底层设计

    • app开发定制公司双向数据绑定从 defineProperty for in app开发定制公司循环变量改成 proxydefineProperty 是改变原对象属性标签;而 proxy 未改变原对象,而是产生新的代理对象,js 引擎更喜欢稳定的对象
    • 重新定义 vdom 对比思路:
      • 区分动静态 dom,只对比动态数据 dom,用block 标记动态标签内部的静态标签
      • 使用最长递增子序列算法,找到所有不需要移动的元素
    • compile 编译优化,把大量计算放在 node 层,最后浏览器只需执行最少的代码

底层设计层面的改变决定了 vue3 比 更快

下面介绍上手步骤~ ()

创建项目

使用 vite 命令创建初始项目

# npm 6.xnpm create vite@latest my-vue-app --template vue# npm 7+, extra double-dash is needed:npm create vite@latest my-vue-app -- --template vuecd my-vue-appnpm installnpm run dev
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Vite 配置

功能一致的配置大多跟 vue-cli 配置大同小异,不过多赘述

resolve

resolve.alias:当使用文件系统路径的别名时,请始终使用绝对路径。相对路径的别名值会原封不动地被使用,因此无法被正常解析。

/* vite.config.ts */resolve: {   //文件系统路径的别名, 绝对路径   alias: {     "@": path.resolve(__dirname, "src"),    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

sass配置

安装sass依赖和配置 vite.config.ts 预定义全局变量

npm i sass -D
  • 1
/* vite.config.ts */css: {    preprocessorOptions: {      scss: {        additionalData: '@import "./src/assets/scss/var.scss";'      }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

开启服务配置

开启 http 服务

/* vite.config.ts */server:{    host: 'dev.moon.cn',    port: 3000}
  • 1
  • 2
  • 3
  • 4
  • 5

开启 https 服务

/* vite.config.ts */let httpsConfig = {  key: fs.readFileSync("C:/Users/ca/wps.cn/_wildcard.wps.cn+3-key.pem"),  cert: fs.readFileSync("C:/Users/ca/wps.cn/_wildcard.wps.cn+3.pem")};server:{    https: httpsConfig,    host: 'dev.moon.cn',    port: 443,    open: true}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

预构建依赖优化

默认情况下, 会抓取你的 index.html 来检测需要预构建的依赖项。如果指定了 build.rollupOptions.input,Vite 将转而去抓取这些入口点。

optimizeDeps.include

默认情况下,不在 node_modules 中的,链接的包不会被预构建。使用此选项可强制预构建链接的包。

/* vite.config.ts */optimizeDeps: {  include: ['axios'],},
  • 1
  • 2
  • 3
  • 4

optimizeDeps.exclude

在预构建中强制排除的依赖项。

eslint 配置

vue3tseslint 配置需另外自行配置,除了需配置 eslint 规则外还需调整 vite 的相关配置,感兴趣的话可以看看我另一篇(内附配置解析),或者直接看,这里不做赘述。

TypeScript

TypeScript 是添加了类型系统的 JavaScript,适用于任何规模的项目,在编译阶段进行类型检查。

基础知识可直接看,英文比较好的小伙伴可以直接看,这里不做赘述,这里分享一些值得说的地方

类型/接口/泛型

  • 类型:类型(type)不是定义一个新类型,而是一个类型别名,使类型更具体化

  • 接口:接口(interface)则是描述一个对象的形状,对值所具有的结构进行类型检查。接口的作用类似于抽象类,不同点在于接口中的所有方法和属性都是没有实值的,换句话说接口中的所有方法都是抽象方法。接口主要负责定义一个类的结构,接口可以去限制一个对象的接口,对象只有包含接口中定义的所有属性和方法时才能匹配接口。同时,可以让一个类去实现接口,实现接口时类中要保护接口中的所有属性。

  • 泛型:支持多种数据结构,有函数泛型,类泛型,接口泛型等。

你可能想问什么时候用类型,什么时候用接口?Typescript团队的建议是

可以使用接口就尽量使用接口,因为接口更灵活,更容易处理

很多时候 interface 和 type 是相同的,但有一个明显区别在于 interface 可以重复定义,类型注解会累加,而 type 重复定义会报错

类型声明

类型声明(Type Declaration) 或者类型定义(Type Definition) 文件是一个以.d.ts作为文件后缀名的TypeScript文件。文件中只包含与类型相关的代码,不包含逻辑代码,它们的作用旨在为开发者提供类型信息,所以它们只在开发阶段起作用。

typescript编译后会将类型信息移除,类型信息仅在开发阶段起作用。

全局类型/变量声明

先在项目 src 目录中新建 global.d.ts 文件

全局类型声明

项目的根目录有 tsconfig.json 可以配置 TypeScript,include 属性包含了需要校验 ts 的文件。ts 默认会将 xx.d.ts 类型文件中的类型注册成全局的,下面举个栗子:

// global.d.tstype T1 = number
  • 1
  • 2
// 组件内 <script lang="ts">     let num1: T1 = 1</script>
  • 1
  • 2
  • 3
  • 4

全局变量声明

有三种方式声明全局变量,挂载在浏览器的 window 对象中

  • 使用 window

global.d.ts文件

// 若想不带window使用userId,但需重复声明declare let userId: string interface Window {  userId: string}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

注:不声明且不带window使用开发模式不会报错,但打包时会报错

组件文件

window.userId = '1'console.log(userId)
  • 1
  • 2
  • 使用 global 配合 windowvar,需加 export,否则会打包报错
// global.d.tsexport {}declare global {  interface Window {    // 使用foo全局变量时得带window,否则打包会报错    foo: string  }  var age: number}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
// 组件内globalThis.age = 18window.foo = '1'console.log(age, window.foo)
  • 1
  • 2
  • 3
  • 4

注:加上export后其他声明会失效,其他声明可另起 *.d.ts 文件定义

  • 使用var
// global.d.tsdeclare var age: number
  • 1
  • 2
// 组件内globalThis.age = 18console.log(age)
  • 1
  • 2
  • 3

每种方式各有利弊,自行选择

第三方库声明

第三方库需有类型声明,可自动生成或者自己写,有兴趣可。

Vue3 + TS

vue3新增了composition api的写法,更接近react的写法,下面介绍ts下的vue3写法和生命周期

setup 语法糖

一个组件选项,在组件被创建之前props 被解析之后执行。它是组合式 API 的入口。

<script setup> 是在单文件组件 (SFC) 中使用 的编译时语法糖。相比于普通的 <script> 语法,它具有更多优势:

  • 更少的样板内容,更简洁的代码。
  • 能够使用纯 Typescript 声明 props 和抛出事件。
  • 更好的运行时性能 (其模板会被编译成与其同一作用域的渲染函数,没有任何的中间代理)。
  • 更好的 IDE 类型推断性能 (减少语言服务器从代码中抽离类型的工作)。

使用这个语法,需要将 setup attribute 添加到 <script> 代码块上:

<script setup lang="ts"></script>
  • 1
  • 2

里面的代码会被编译成组件 setup() 函数的内容。这意味着与普通的 <script> 只在组件被首次引入的时候执行一次不同,<script setup> 中的代码会在每次组件实例被创建的时候执行

setup 函数在生命周期方面,它是在  钩子之前调用的。

生命周期

选项式 API 的生命周期选项和组合式 API 之间的映射

  • beforeCreate -> 使用 setup()
  • created -> 使用 setup()
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeUnmount -> onBeforeUnmount
  • unmounted -> onUnmounted
  • errorCaptured -> onErrorCaptured
  • renderTracked -> onRenderTracked
  • renderTriggered -> onRenderTriggered
  • activated -> onActivated
  • deactivated -> onDeactivated

TIP: 因为 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。

响应式 ref

接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象仅有一个 .value property,指向该内部值。和从 setup() 函数中返回值一样,ref 值在模板中使用的时候会自动解包。

可以在调用 ref 时传递一个泛型参数以覆盖默认推断

import { ref } from "vue";let str = ref<string>("test");
  • 1
  • 2
  • 3

还可以指定复杂类型

const foo = ref<string | number>('foo') // foo 的类型:Ref<string | number>foo.value = 123 // ok!
  • 1
  • 2
  • 3

props/emit

  • 仅限类型的 props/emit 声明
defineProps<{ title: string }>();const emit = defineEmits<{  (e: 'change', id: number): void  (e: 'update', value: string): void}>()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • props 设置默认值

    有两种方法设置默认值

    • 使用运行时声明

      运行时声明 的方式只能设置参数类型、默认值、是否必传、自定义验证。报错为控制台warn警告。
      若想设置[ 编辑器报错、编辑器语法提示 ]则需要使用类型声明的方式。

      const props = defineProps({  modelValue: { type: Boolean, default: false },  title: { type: String, default: '弹窗提示' },  msg: { type: String, default: '弹窗信息' }})
      • 1
      • 2
      • 3
      • 4
      • 5
    • 使用类型声明时的默认 props 值

      仅限类型的 defineProps 声明的不足之处在于,它不能给 props 定义默认值。需配合 withDefaults 编译器宏解决:

interface Props {  title?: string;  msg?: string;}withDefaults(defineProps<Props>(), {  title: "提示",  msg: "是否跳转到app?",});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

defineProps、withDefaults 是只在 <script setup> 中才能使用的编译器宏。他不需要导入且会随着 <script setup> 处理过程一同被编译掉。

v-model 双向绑定

vue2 中的 v-model 的使用是通过传递 value 属性和接收 input 事件实现,vue3 则换成了 modelValue 属性,接收的方法是update:modelValue

以下弹窗例子以Page.vue为父组件,Dialog.vue为子组件,关键代码如下:

/* Page.vue */<template>  <Dialog v-model="dialogVisible"></Dialog>  <div class="bottom-btn" @click="onTap">点击按钮</div></template><script setup lang="ts">import { ref } from "vue";import Dialog from "./Dialog.vue";let dialogVisible = ref<boolean>(false);function onTap() {  dialogVisible.value = true;}<script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
/* Dialog.vue */<template>  <div class="dialog" v-show="modelValue">      <span class="dialog-content-btn" @click="onConfirm">确定</span>  </div></template><script setup lang="ts">import { ref } from "vue";interface Props {  modelValue?: boolean;}let props = withDefaults(defineProps<Props>(), {  modelValue: false // v-model绑定的属性值});// 传递的方法const emit = defineEmits<{  (e: "update:modelValue", visible: boolean): boolean;}>();function onConfirm() {    emit("update:modelValue", false);}<script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

遇到的问题

做好所有配置后,主要遇到以下两个问题

vite 打包报错/告警

“@charset” must be the first rule in the file }@charset “UTF-8”;

告警如图:

原因:使用了scss类库 ,scss编译的时候,因为被编译的文件里可能有中文导致

解决:在vite.config.js里面,加一个sass的配置,把charset关掉就行了

vite.config.js 中的配置

export default defineConfig({  css: {    preprocessorOptions: {      scss: {        charset: false      }    }  }})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

去除 Typescript 全局变量的 eslint 报错

1. 使用 var 定义全局变量

var 相关声明下会带下划线,并报错

Unexpected var, use let or const instead.

解决:在 .eslintrc 配置文件中增加规则

rules: {    // 全局变量允许使用 var    'no-var': 'off',}
  • 1
  • 2
  • 3
  • 4

2. 使用 global 定义全局变量

global 相关声明下会带下划线,并报错

Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.

解决:在 global.d.ts 声明文件中添加一行代码

export {}
  • 1

注:新增后会导致该文件中的其他变量/类型等声明失效,其他声明可另起 *.d.ts 文件定义

Vite 为什么更快

Vite 主要通过以下几个方面进行优化:

  • 启动应用时按需提供代码
  • 浏览器缓存(协商缓存和强缓存)进行代码更新
  • 使用 esbuild 预构建依赖和加快构建速度

启动时间和更新时间

启动时间

以往的打包工具当冷启动开发服务器时,基于打包器的方式启动必须优先抓取并构建你的整个应用,然后才能提供服务。而且存在性能瓶颈——使用 JavaScript 开发的工具通常需要很长时间(甚至是几分钟!)才能启动开发服务器,即使使用 HMR,文件修改后的效果也需要几秒钟才能在浏览器中反映出来。

Vite 通过在一开始将应用中的模块区分为 依赖 和 源码 两类,并只在浏览器请求源码时进行转换并按需提供源码,改进了开发服务器启动时间。而且 使用的语言是go,比以 JavaScript 编写的打包器预构建依赖快 10-100 倍。

更新时间

在 Vite 中,HMR 是在原生 ESM 上执行的。当编辑一个文件时,Vite 只需要精确地使已编辑的模块与其最近的 HMR 边界之间的链失活(大多数时候只是模块本身),使得无论应用大小如何,HMR 始终能保持快速更新。

Vite 同时利用 HTTP 头来加速整个页面的重新加载(再次让浏览器为我们做更多事情):源码模块的请求会根据 304 Not Modified 进行协商缓存,而依赖模块请求则会通过 Cache-Control: max-age=31536000,immutable 进行强缓存,因此一旦被缓存它们将不需要再次请求。

预构建依赖的前因后果

Vite 预构建依赖原因有二:

  • CommonJS 和 UMD 兼容性: 开发阶段中,Vite 的开发服务器将所有代码视为原生 ES 模块。因此,Vite 必须先将作为 CommonJS 或 UMD 发布的依赖项转换为 ESM。

  • 性能: Vite 通过预构建依赖将有许多内部模块的 ESM 依赖关系转换为单个模块,从而减少浏览器的请求数量,提升页面加载性能。

    如,当执行 import { debounce } from 'lodash-es' 时,浏览器同时发出 600 多个 HTTP 请求;通过预构建 lodash-es 成为一个模块,就只需要一个 HTTP 请求。

自动依赖搜寻

如果没有找到相应的缓存,Vite 将抓取你的源码,并自动寻找引入的依赖项(即 “bare import”,表示期望从 node_modules 解析),并将这些依赖项作为预构建包的入口点。

在服务器已经启动之后,如果遇到一个新的依赖关系导入,而这个依赖关系还没有在缓存中,Vite 将重新运行依赖构建进程并重新加载页面。

对于 仓库中的某个依赖成为另一个包的依赖,Vite 会自动侦测没有从 node_modules 解析的依赖项,并将链接的依赖视为源码。它不会尝试打包被链接的依赖,而是会分析被链接依赖的依赖列表。

缓存

文件系统缓存

Vite 会将预构建的依赖缓存到 node_modules/.vite。它根据几个源来决定是否需要重新运行预构建步骤:

  • package.json 中的 dependencies 列表
  • 包管理器的 lockfile,例如 package-lock.jsonyarn.lock,或者 pnpm-lock.yaml
  • 可能在 vite.config.js 相关字段中配置过的

只有在上述其中一项发生更改时,才需要重新运行预构建。

如果要强制 Vite 重新构建依赖,你可以用 --force 命令行选项启动开发服务器,或者手动删除 node_modules/.vite 目录。

浏览器缓存

解析后的依赖请求会以 HTTP 头 max-age=31536000,immutable 强缓存,以提高在开发时的页面重载性能。一旦被缓存,这些请求将永远不会再到达开发服务器。如果安装了不同的版本(这反映在包管理器的 lockfile 中),则附加的版本 query 会自动使它们失效。如果你想通过本地编辑来调试依赖项,你可以:

  1. 通过浏览器调试工具的 Network 选项卡暂时禁用缓存;
  2. 重启 Vite dev server,并添加 --force 命令以重新构建依赖;
  3. 重新载入页面。

为何不用 ESBuild 打包?

虽然 esbuild 快得惊人,且是一个在构建库方面比较出色的工具,但一些针对构建 应用 的重要功能仍然还在持续开发中 —— 特别是代码分割和 CSS 处理方面。就目前来说,Rollup 在应用打包方面更加成熟和灵活。

最后

最后附上,如对前端自动化部署有兴趣,可继续看在本文 vue3 基础上搭建的

相关文章

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发