2024-08-19

在Vue 3中,使用JSX/TSX时,你可以通过在setup函数中使用refreactive来创建响应式数据,并通过toRefs来暴露方法给父组件。以下是一个简单的例子:




import { defineComponent, ref, reactive, toRefs } from 'vue';
 
export default defineComponent({
  setup() {
    const count = ref(0);
    const state = reactive({
      message: 'Hello',
    });
 
    function increment() {
      count.value++;
    }
 
    function greet() {
      alert(state.message);
    }
 
    // 将响应式状态和方法通过toRefs暴露
    return {
      ...toRefs(state),
      count,
      increment,
      greet,
    };
  },
 
  // JSX 或 TSX 中的使用
  render() {
    return (
      <div>
        <p>{this.message}</p>
        <p>{this.count}</p>
        <button onClick={this.increment}>Increment</button>
        <button onClick={this.greet}>Greet</button>
      </div>
    );
  },
});

在这个例子中,countstate是响应式的,incrementgreet是在setup中定义的方法。通过toRefs将它们转换为响应式引用,这样就可以在JSX/TSX模板中直接使用它们了。

2024-08-19



// 定义一个函数,输出 "Hello, World!"
function sayHello(): void {
    console.log("Hello, World!");
}
 
// 调用函数
sayHello();

这段代码定义了一个简单的函数sayHello,它没有返回值(使用了void类型),并在函数内部使用了console.log来输出"Hello, World!"。然后调用了这个函数,以便执行并展示结果。这是学习TypeScript的一个基本例子,展示了如何定义一个简单的函数并运行它。

2024-08-19

在Vue 3项目中,我们可以使用Composition API来创建更加灵活和可复用的代码。以下是一个简单的Vue 3组件示例,它使用了refreactive来实现响应式数据,使用onMountedonUnmounted生命周期钩子,以及watch来响应数据的变化。




<template>
  <div>
    <h1>{{ message }}</h1>
    <button @click="incrementCounter">点击我</button>
    <p>点击次数: {{ counter }}</p>
  </div>
</template>
 
<script>
import { ref, reactive, onMounted, onUnmounted, watch } from 'vue';
 
export default {
  name: 'MyComponent',
  setup() {
    // 响应式数据
    const counter = ref(0);
    const message = reactive({ text: 'Hello Vue 3!' });
 
    // 方法
    function incrementCounter() {
      counter.value++;
    }
 
    // 生命周期钩子
    onMounted(() => {
      console.log('组件已挂载');
    });
 
    onUnmounted(() => {
      console.log('组件已卸载');
    });
 
    // 监听器
    watch(counter, (newValue, oldValue) => {
      if (newValue >= 5) {
        message.text = '你已经点击了足够次数!';
      }
    });
 
    // 暴露到模板
    return {
      counter,
      incrementCounter,
      message
    };
  }
};
</script>

这个组件包括了响应式数据(countermessage)、方法(incrementCounter)、生命周期钩子和watch监听器。它展示了如何在Vue 3项目中使用Composition API来更好地组织和管理代码逻辑。

2024-08-19

在TypeScript中,我们可以使用抽象类来定义一种模板,这个模板包含了一些抽象的方法和属性,继承了抽象类的子类需要实现这些抽象方法和属性。抽象类不能直接实例化,只能用于继承。

下面是一个使用抽象类的例子:




abstract class Animal {
    // 抽象属性,子类必须实现
    abstract name: string;
 
    // 抽象方法,子类必须实现
    abstract makeSound(): void;
 
    // 具体方法
    move(): void {
        console.log('Animals can move.');
    }
}
 
class Dog extends Animal {
    name: string;
 
    constructor(name: string) {
        super();
        this.name = name;
    }
 
    makeSound(): void {
        console.log('Woof! My name is ' + this.name);
    }
}
 
class Cat extends Animal {
    name: string;
 
    constructor(name: string) {
        super();
        this.name = name;
    }
 
    makeSound(): void {
        console.log('Meow! My name is ' + this.name);
    }
}
 
let myDog = new Dog('Rex');
let myCat = new Cat('Whiskers');
 
myDog.makeSound(); // Output: Woof! My name is Rex
myCat.makeSound(); // Output: Meow! My name is Whiskers

在这个例子中,Animal 是一个抽象类,它定义了 name 抽象属性和 makeSound 抽象方法。DogCat 类继承了 Animal 类,并且实现了所有抽象的属性和方法。这样,我们就可以在 DogCat 类中定义具体的行为,而不用在 Animal 类中重复实现相同的逻辑。

2024-08-19

JavaScript 中创建对象的方法有很多种,以下是其中的8种常见方法:

  1. 使用对象字面量:



let obj = {}; // 创建一个空对象
obj.name = 'John';
obj.age = 30;
  1. 使用 new 关键字:



let obj = new Object(); // 创建一个空对象
obj.name = 'John';
obj.age = 30;
  1. 工厂模式:



function createPerson(name, age) {
  let obj = new Object();
  obj.name = name;
  obj.age = age;
  return obj;
}
let person = createPerson('John', 30);
  1. 构造函数模式:



function Person(name, age) {
  this.name = name;
  this.age = age;
}
let person = new Person('John', 30);
  1. 原型模式:



function Person() {}
Person.prototype.name = 'John';
Person.prototype.age = 30;
let person = new Person();
  1. 组合使用构造函数和原型模式(常用):



function Person(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.greet = function() {
  return `Hello, my name is ${this.name}`;
};
let person = new Person('John', 30);
  1. 动态原型模式:



function Person(name, age) {
  this.name = name;
  this.age = age;
  if (typeof this.greet !== 'function') {
    Person.prototype.greet = function() {
      return `Hello, my name is ${this.name}`;
    };
  }
}
let person = new Person('John', 30);
  1. 类(ES6+):



class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
 
  greet() {
    return `Hello, my name is ${this.name}`;
  }
}
let person = new Person('John', 30);

这些方法可以根据你的具体需求和环境选择合适的方式来创建对象。

2024-08-19

以下是一个简化的TypeScript代码示例,用于实现贪吃蛇游戏中的蛇部分。这个示例只包含了蛇的基本属性和移动功能。




class Snake {
    body: number[][];  // 蛇的身体,使用二维数组表示位置
    direction: 'up' | 'down' | 'left' | 'right'; // 蛇的移动方向
 
    constructor() {
        this.body = [[2, 2], [1, 2], [0, 2]]; // 初始蛇的位置
        this.direction = 'right'; // 初始方向
    }
 
    move(food: number[]): void {
        const nextHead = this.getNextHead();
        if (this.isEating(food, nextHead)) {
            this.body.push(nextHead); // 吃到食物,蛇长大
        } else {
            this.body.push(nextHead); // 没吃食物,蛇前进
            this.body.shift(); // 蛇尾部移除
        }
    }
 
    getNextHead(): number[] {
        const [x, y] = this.body[this.body.length - 1];
        switch (this.direction) {
            case 'up': return [x - 1, y];
            case 'down': return [x + 1, y];
            case 'left': return [x, y - 1];
            case 'right': return [x, y + 1];
        }
    }
 
    isEating(food: number[], nextHead: number[]): boolean {
        return nextHead[0] === food[0] && nextHead[1] === food[1];
    }
 
    changeDirection(newDirection: 'up' | 'down' | 'left' | 'right'): void {
        const oppositeDirection = { 'up': 'down', 'down': 'up', 'left': 'right', 'right': 'left' };
        if (newDirection !== oppositeDirection[this.direction]) {
            this.direction = newDirection;
        }
    }
}
 
// 使用示例
const snake = new Snake();
const food = [1, 1]; // 假设食物在坐标(1, 1)
 
// 玩家按键,改变蛇的移动方向
snake.changeDirection('left');
 
// 游戏循环中调用snake.move(food)更新蛇的位置

这个代码实现了基本的贪吃蛇游戏中蛇的部分。它包括蛇的移动逻辑,包括蛇吃食物时的增长和没吃食物时的移动。这个示例没有实现游戏的其他部分,比如与屏幕边界的碰撞检测、蛇吃蛇本身的检测等。实际的游戏可能需要更复杂的逻辑来处理这些情况。

2024-08-19

在Vue 3中,Block是一个抽象的概念,它代表了一个可以被渲染的实体。在Vue 3的渲染系统中,Block可以是一个元素、组件或者是一段文本。Block是构建VNode树的基本单位。

在Vue 3的源码中,Block被定义在packages/runtime-core/src/vdom.ts文件中,它是一个使用Provide/Inject的服务。

以下是一个简化的Block定义示例:




import { provide, inject } from 'vue';
 
// 定义Block服务
export const BlockProviderSymbol = Symbol() as InjectionKey<VNode[]>;
 
// 在父组件中设置Block
export function setupBlock(props: { msg?: string }) {
  const blockChildren = ref<VNode[]>([]);
  provide(BlockProviderSymbol, blockChildren);
 
  // 可以根据需要对blockChildren进行操作
  // 例如,如果父组件接受一个msg属性,可以将其作为文本节点添加到blockChildren中
  if (props.msg) {
    blockChildren.value.push(createTextVNode(props.msg));
  }
 
  // 返回blockChildren以供注入
  return blockChildren;
}
 
// 在子组件中使用Block
export function useBlockContext() {
  return inject(BlockProviderSymbol, null);
}

在这个例子中,setupBlock函数提供了一个可注入的blockChildren VNode数组,可以在父组件中使用。useBlockContext函数用于在子组件中接收这个数组。如果父组件传递了一个msg属性,它会将这个文本作为一个新的VNode添加到blockChildren数组中。子组件可以通过调用useBlockContext来访问这个数组,并将其渲染出来。

这个例子展示了如何在Vue 3应用中创建一个Block服务,并在父子组件之间共享VNode数据。

2024-08-19

在TypeScript或JavaScript中,我们可以使用正则表达式来匹配除了预期之外的字符。这可以通过一个负向字符集来实现。

解法1:使用[^...]定义一个否定的字符集。




let str = "abc123";
let regExp = /[^0-9]/; // 匹配除了数字以外的任意字符
console.log(str.match(regExp)); // 输出:"abc"

解法2:使用[^...]定义一个否定的字符集,并使用量词?来使匹配为非贪婪匹配。




let str = "abc123";
let regExp = /[^0-9]+?/; // 匹配除了数字以外的字符,量词?使其为非贪婪匹配
console.log(str.match(regExp)); // 输出:undefined

解法3:使用(?!...)负向前瞻断言。




let str = "abc123";
let regExp = /.*(?!123)/; // 匹配不以"123"结尾的所有字符
console.log(str.match(regExp)); // 输出:"abc"

解法4:使用(?!...)负向前瞻断言,并使用量词?来使匹配为非贪婪匹配。




let str = "abc123";
let regExp = /.*?(?!123)/; // 匹配不以"123"结尾的字符,量词?使其为非贪婪匹配
console.log(str.match(regExp)); // 输出:undefined

以上就是在TypeScript或JavaScript中使用正则表达式来匹配除了预期之外的字符的几种方法。

2024-08-19

以下是针对Vue3+Vite+Ts项目中Axios的企业级封装以及本地存储的封装的示例代码:




// http.ts
import axios from 'axios';
import { ElMessage } from 'element-plus';
import { store } from '@/store/index';
 
// 创建axios实例
const service = axios.create({
  // API的base_url
  baseURL: import.meta.env.VITE_APP_BASE_API,
  // 请求超时时间
  timeout: 5000
});
 
// 请求拦截器
service.interceptors.request.use(
  config => {
    // 可以在这里添加请求头等信息
    if (store.state.user.token) {
      config.headers['Authorization'] = `Bearer ${store.state.user.token}`;
    }
    return config;
  },
  error => {
    // 请求错误处理
    console.log(error); // for debug
    Promise.reject(error);
  }
);
 
// 响应拦截器
service.interceptors.response.use(
  response => {
    const res = response.data;
    // 根据返回的状态码做相应处理,例如401未授权等
    return res;
  },
  error => {
    ElMessage({
      message: '服务器异常',
      type: 'error',
      duration: 5 * 1000
    });
    return Promise.reject(error);
  }
);
 
export default service;
 
// storage.ts
export const setLocal = <T>(key: string, value: T) => {
  localStorage.setItem(key, JSON.stringify(value));
};
 
export const getLocal = <T>(key: string): T | null => {
  const value = localStorage.getItem(key);
  if (value) {
    return JSON.parse(value);
  }
  return null;
};
 
export const removeLocal = (key: string) => {
  localStorage.removeItem(key);
};

在这个封装中,我们使用axios创建了一个实例,并对请求和响应进行了拦截处理。对于本地存储,我们提供了基本的设置、获取和删除操作,并使用泛型保证了存取数据的类型安全。

2024-08-19

在Vue 3中,ref是一个用于创建响应式引用对象的API。ref可以用于跟踪单个响应式值。refreactive对象的一个轻量版本,适用于跟踪单个值。

ref在Vue 3中的使用场景主要是在组件内部管理状态。

以下是一个使用ref的基本示例:




<template>
  <div>
    <input v-model="message" />
    <p>Message: {{ message }}</p>
  </div>
</template>
 
<script>
import { ref } from 'vue';
 
export default {
  setup() {
    const message = ref('');
    return { message };
  }
};
</script>

在这个例子中,我们创建了一个响应式的ref,它被初始化为空字符串。v-model用于双向绑定输入框的值到message

ref全家桶是指一系列相关的API,如toReftoRefscustomRef,它们用于在不同的场景下对ref进行扩展或自定义。

例如,toRef可以用来创建一个响应式引用,该引用是另一个响应式对象的特定属性:




import { reactive, toRef } from 'vue';
 
const state = reactive({
  foo: 'bar',
  baz: 'qux'
});
 
const fooRef = toRef(state, 'foo');
 
// 现在 fooRef 是一个响应式引用,它跟踪 state.foo 的变化

toRefs可以用来将reactive对象的属性转换为ref对象:




import { reactive, toRefs } from 'vue';
 
const state = reactive({
  foo: 'bar',
  baz: 'qux'
});
 
const stateRefs = toRefs(state);
 
// stateRefs 现在是一个带有 foo 和 baz 属性的对象,
// 每个属性都是指向 state 中相应属性的 ref

customRef可以用来创建自定义的ref,它可以定义在属性被读取和更改时执行的自定义行为:




import { customRef } from 'vue';
 
const myRef = customRef((track, trigger) => {
  return {
    get() {
      // 当值被读取时执行
      track();
      return theValue;
    },
    set(newValue) {
      // 当值被更改时执行
      theValue = newValue;
      trigger();
    }
  };
});
 
// 使用 myRef 就像使用普通 ref 一样

这些是ref全家桶的基本概念和使用方法。