参考文章 :https://juejin.cn/post/7079785777692934174

一、搭建开发环境

vite 官网地址 https://vitejs.cn/ vue3 官网地址 https://v3.cn.vuejs.org/ ElementPlus https://element-plus.gitee.io/zh-CN/ TypeScript https://www.tslang.cn/docs/home.html TypeScript https://www.typescriptlang.org/zh/

1、安装vite

Vite 需要 Node.js 版本 >= 12.0.0

  1. # 全局安装vite
  2. npm install vite -g
  3. # 验证是否安装成功
  4. vite -v
  5. -> vite/2.9.14 win32-x64 node-v16.13.2

2、安装pnpm

pnpm 与 npm 一样,是一个包管理器,但是比npm更加快速

  1. # 全局安装 pnpm
  2. npm i pnpm -g
  3. # 验证是否安装成功
  4. pnpm -v
  5. -> 7.5.1

3、创建Vue3+TS模板

  1. #快速创建
  2. # npm 7+, 需要额外的双横线:
  3. npm init vite@latest my-vue-app -- --template vue-ts
  4. # pnpm
  5. pnpm create vite my-vue-app -- --template vue-ts
  6. #按需创建
  7. # npm 7+, 需要额外的双横线:
  8. npm init vite@latest my-vue-app
  9. # pnpm
  10. pnpm create vite my-vue-app

4、安装ElementPlus

  1. # NPM
  2. $ npm install element-plus --save
  3. # pnpm
  4. $ pnpm install element-plus
  5. #如果需要EP组件按需导入,并且是自动导入 需要安装下面两款插件
  6. pnpm install -D unplugin-vue-components unplugin-auto-import

5、VSCode配置

  1. 安装vscode的Volar插件
  2. 禁用Vetur组件
  3. 设置中注释vetur相关配置

    6、vite.config.ts

    ```javascript // 使用path库,如果报错,安装 @types/node ( npm i @types/node -D ) import { resolve } from ‘path’; import { defineConfig } from ‘vite’; import vue from ‘@vitejs/plugin-vue’; // 根据 ElementPlus 管网,配置自动导入 import AutoImport from ‘unplugin-auto-import/vite’; import Components from ‘unplugin-vue-components/vite’; import { ElementPlusResolver } from ‘unplugin-vue-components/resolvers’;

// 获取目录路径 const pathSrc = resolve(__dirname, ‘src’);

// https://vitejs.dev/config/ export default defineConfig({ resolve: { // 配置别名 alias: { ‘@/‘: ${pathSrc}/, }, }, plugins: [ vue(), // 根据 ElementPlus 管网,配置自动导入 AutoImport({ resolvers: [ElementPlusResolver()], }), Components({ resolvers: [ElementPlusResolver()], }), ], // 配置服务相关信息 / server: { port: 9955, host: “0.0.0.0”, proxy: { “/model-management”: { // 开发环境 // target: “http://172.16.6.136:7080“, // 开发自测环境 target: “http://172.16.6.22:7080“, // 测试环境 // target: “http://172.16.6.22:7088“, // 测试环境 // target: “http://172.16.6.240:7080“, changeOrigin: true } } }, / }); vite.config.ts

  1. <a name="uqltT"></a>
  2. ## 7、tsconfig.json
  3. ```javascript
  4. {
  5. "compilerOptions": {
  6. // 设置根目录为当前目录
  7. "baseUrl": ".",
  8. "target": "esnext",
  9. "useDefineForClassFields": true,
  10. "module": "esnext",
  11. "moduleResolution": "node",
  12. "strict": true,
  13. "jsx": "preserve",
  14. "sourceMap": true,
  15. "resolveJsonModule": true,
  16. "isolatedModules": true,
  17. "esModuleInterop": true,
  18. "lib": ["esnext", "dom"],
  19. "skipLibCheck": true,
  20. // 用于ts解析 “@” 路径
  21. "paths": {
  22. "@/*": ["src/*"]
  23. }
  24. },
  25. "include": [
  26. "src/**/*.ts",
  27. "src/**/*.d.ts",
  28. "src/**/*.tsx",
  29. "src/**/*.vue",
  30. // 增加对注解文件的解析
  31. "./auto-imports.d.ts",
  32. "./components.d.ts"
  33. ],
  34. "references": [{ "path": "./tsconfig.node.json" }]
  35. }

8、安装与配置ESLint

:::info pnpm i eslint eslint-plugin-vue —save-dev
pnpm install @typescript-eslint/parser —save-dev
pnpm install @typescript-eslint/eslint-plugin —save-dev

::: 创建文件 .eslintrc.js

module.exports = {
  parser: 'vue-eslint-parser',

  parserOptions: {
    parser: '@typescript-eslint/parser',
    ecmaVersion: 2020,
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true,
    },
  },

  extends: [
    'plugin:vue/vue3-recommended',
    'plugin:@typescript-eslint/recommended',
    'prettier',
    'plugin:prettier/recommended',
  ],

  rules: {
    // override/add rules settings here, such as:
  },
};

创建忽略文件 .eslintignore

node_modules/
dist/
index.html

增加校验命令,修改 package.json

{
    ...
    "scripts": {
        ...
        "eslint:comment": "使用 ESLint 检查并自动修复 src 目录下所有扩展名为 .js 和 .vue 的文件",
        "eslint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src",
    }
    ...
}

9、配置prettierrc

:::info pnpm i prettier eslint-config-prettier eslint-plugin-prettier —save-dev ::: 新建 .prettierrc.js 文件

module.exports = {
  // 一行最多 80 字符
  printWidth: 80,
  // 使用 2 个空格缩进
  tabWidth: 2,
  // 不使用 tab 缩进,而使用空格
  useTabs: false,
  // 行尾需要有分号
  semi: true,
  // 使用单引号代替双引号
  singleQuote: true,
  // 对象的 key 仅在必要时用引号
  quoteProps: 'as-needed',
  // jsx 不使用单引号,而使用双引号
  jsxSingleQuote: false,
  // 末尾使用逗号
  trailingComma: 'all',
  // 大括号内的首尾需要空格 { foo: bar }
  bracketSpacing: true,
  // jsx 标签的反尖括号需要换行
  jsxBracketSameLine: false,
  // 箭头函数,只有一个参数的时候,也需要括号
  // arrowParens: 'always',
  // 每个文件格式化的范围是文件的全部内容
  rangeStart: 0,
  rangeEnd: Infinity,
  // 不需要写文件开头的 @prettier
  requirePragma: false,
  // 不需要自动在文件开头插入 @prettier
  insertPragma: false,
  // 使用默认的折行标准
  proseWrap: 'preserve',
  // 根据显示样式决定 html 要不要折行
  htmlWhitespaceSensitivity: 'css',
  // 换行符使用 lf
  endOfLine: 'auto',
};

二、TypeScript

参考 https://juejin.cn/post/6981728323051192357

// 定义变量
let text: string = "Hello World";
const num:number = 1;
let isNum:boolean = true;
let empty: null|undefined = null;

// 定义数组
const nums:number[] = [1,2,3];

// 某个位置的值可以是注解中的任何一个
const LOL: (string | number)[] = ["zed", 25, "darts"];

// 元组 每一项数据类型必须一致
const LOL: [string, string, number] = ["zed", "darts", 25];

// 定义对象
interface EventRecord {
  id:number;
  description:string;
  deviceId?:number;
  say()?: string; // say函数返回值为string
  [propname: string]: any; // 当前EventRecord可定义任意字符串类型的key
}

const EventList: EventRecord[] = [{ id: 1, description: '装置越限' }];
// or
const EventList: Array<EventRecord> = [{ id: 1, description: '装置越限' }];

// 函数
// 无返回值
function sayHello(): void {
  console.log("hello world");
}

function sayHello(): string {
  return "hello world";
}

// 参数
function sum(x: number, y: number): number {
    return x + y;
}

function handleEvent(eventList:EventRecord[]): void {
  ...
}

// 变量形式声明
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
    return x + y;
};

interface BaseRequest {
  code:number;
  data:any,
  msg:string
}
let getDate: () => Promise<BaseRequest> = function (): Promise<BaseRequest>  {
    return x + y;
};  


// 定义 enum枚举
// 初始值默认为 0
enum JiShiEnum {
  REDJ,
  BLUEJ,
  GREENJ,
}
// 设置初始值
enum JiShiEnum {
  REDJ = 8,
  BLUEJ,
  GREENJ,
}
const jishi: JiShiEnum = JiShiENUM.BLUE
console.log(jishi) // 9
// 字符串枚举,每个都需要声明
enum JiShiEnum {
  REDJ = "8号",
  BLUEJ = "9号",
  GREENJ = "10号",
}

二、Vue3开发

参考 https://juejin.cn/post/6887359442354962445#heading-27

Vue3   ElementPlus   Vite  TS 入门 - 图1

1、生命周期

:::info Vue2—————————vue3
beforeCreate -> setup()
created -> setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
activated -> onActivated
deactivated -> onDeactivated
errorCaptured -> onErrorCaptured :::

2、setup 与 ref、reactive

在setup中使用ref声明响应式变量,使其能在模板中使用

<template>
  <div>{{ count }}</div>
  <div>{{ state.name }}</div>
  <span v-for="item in state.items" :key="item">{{ item }}</span>
  <el-button @click="addCount">增加</el-button>
</template>
<script lang="ts" setup>
const count = ref(0);
// 定义对象 数组
let state = reactive({
  name: 'test',
  items: [1, 2, 3, 4, 5],
});
const addCount = () => {
  count.value += 1;
  state.name += count.value;
  state.items[2] += count.value;
};
</script>

3、computed

<template>
  <div>{{ count }}</div>

  <div>计算属性:{{ computedNum }}</div>
  <el-button @click="addCount">增加</el-button>
</template>
<script lang="ts" setup>
// 需要通过.value 访问
const count = ref(0);

const addCount = () => {
  count.value += 1;
};

const computedNum = computed({
  get: () => count.value * 100,
  set: (value) => count.value * value,
});
</script>

4、watch

<template>
  <div>{{ count }}</div>

  <div>age:{{ state.age }}</div>
  <el-button @click="addCount">增加</el-button>
</template>
<script lang="ts" setup>
interface Person {
  name: string;
  age: number;
}

// 需要通过.value 访问
const count = ref(0);

const state = reactive<Person>({ name: 'vue', age: 10 });

const addCount = () => {
  count.value += 1;
  state.age += 10;
};

watch(count, (newvalue, old) => {
  console.log('cuont', newvalue, old, count.value);
});

watch(
  // () => state,
  () => state.age,
  (age, preAge) => {
    console.log(age, preAge);
  },
  // 配置deep且直接监听state时 preAge的值与age的值一致
  // 为了完全侦听深度嵌套的对象和数组,可能需要对值进行深拷贝。
  /*  {
    deep: true,
  }, */
);

/*
参考 vue官方文档 https://v3.cn.vuejs.org/guide/reactivity-computed-watchers.html#侦听响应式对象
import _ from 'lodash';

const state = reactive({
  id: 1,
  attributes: {
    name: '',
  },
});

 watch(
  () => _.cloneDeep(state),
  (state, prevState) => {
    console.log(state.attributes.name, prevState.attributes.name)
  }
)

state.attributes.name = 'Alex' // 日志: "Alex" "" */
</script>

5、模块化

// hook/组件
import { ref } from 'vue';

const nowTime = ref('00:00:00');
const getNowTime = () => {
  const now = new Date();
  const hour = String(now.getHours()).padStart(2, '0');
  const minu =
    now.getMinutes() < 10 ? '0' + now.getMinutes() : now.getMinutes();
  const sec = now.getSeconds() < 10 ? '0' + now.getSeconds() : now.getSeconds();
  nowTime.value = hour + ':' + minu + ':' + sec;

  setTimeout(getNowTime, 1000);
};

export { nowTime, getNowTime };


// 在组件中使用

<template>
  <div>{{ nowTime }}</div>
  <el-button @click="getNowTime">开始</el-button>
</template>
<script lang="ts" setup>
import { nowTime, getNowTime } from '../hooks/useNowTime';
</script>

6、Teleport 组件

// modal组件 
<template>
  <div id="center">
    <h2>弹窗demo</h2>
  </div>
</template>
<script lang="ts" setup></script>
<style>
#center {
  width: 200px;
  height: 200px;
  border: 2px solid black;
  background: white;
  position: fixed;
  left: 50%;
  top: 50%;
  margin-left: -100px;
  margin-top: -100px;
  z-index: 100000000;
}
</style>


// 使用teleport组件包裹后,可以在使modal组件加载到想在的地方

<template>
  <div>122</div>
  <!-- 可以绑定 id  class  自定义属性 -->
  <!-- 相当于 将ModelDialog组件加载到id为model的dom容器中-->
  <teleport to="#modal">
    <ModelDialog />
  </teleport>
</template>
<script lang="ts" setup></script>