✨「前端进阶」带你丝滑过渡Vue2到Vue3、React

前言

​ 相应有很多前端er工作中长期接触Vue2,在面对技术栈切换到Vue3或React常常无从下手,可能平时零星接触一些知识点,但缺乏项目的实战,担心自己不能够hold住项目,下面这边将结合实战中常用的场景来帮助大家更好地理解"框架工具",从Vue2丝滑过渡技术栈!

​ 首先,先讲讲这三个框架的一些显著特点

  • Vue2: 模版语法、选项式API、组件内包含this指向、mixin实现逻辑复用
  • Vue3: 紧凑式API、组件无this指向(提供实例概念)、reactive + hooks式逻辑复用、 更好地结合TS
  • React: 单向数据流、jsx、tsx语法、函数式组件、useState + hooks式逻辑复用

下面开始讲讲三个框架在各应用场景下的使用:

1、组件间通信

父组件传数据到子组件

1、Vue2:父组件通过属性传递参数,子组件采用props接收

xml 复制代码
<!-- 父组件 -->
<template>
  <div>
    <child-component :message="parentMessage"></child-component>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      parentMessage: 'Hello from parent component'
    };
  }
};
</script>


<!-- 子组件 -->
<template>
  <div>
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  props: ['message']
};
</script>

2、Vue3:父组件同样属性传递数据,子组件接收中props 需要使用 defineProps() 这个宏函数来进行声明

xml 复制代码
<!-- 父组件 -->
<template>
  <div>
    <child-component :message="parentMessage"></child-component>
  </div>
</template>

<script>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  setup() {
    const parentMessage = ref('Hello from parent component');

    return {
      parentMessage
    };
  }
};
</script>



<!-- 子组件 -->
<script setup>
const props = defineProps({
  message: {
    type: String,
    required: true
  }
})
</script>

<template>
  <div>{{ message }}</div>
  <div>{{ props.message }}</div>
</template>

注:defineProps 、defineEmits 、 defineExpose 和 withDefaults 这四个宏函数只能在 <script setup> 中使用。他们不需要导入,会随着 <script setup> 的处理过程中一起被编译。

3、React:父组件通过属性传递参数,子组件采用props接收

javascript 复制代码
<!-- 父组件 -->
import React from 'react';
import ChildComponent from './ChildComponent';

const ParentComponent = () => {
  const parentMessage = 'Hello from parent component';

  return (
    <div>
      <ChildComponent message={parentMessage} />
    </div>
  );
}

export default ParentComponent;

<!-- 子组件 -->
import React from 'react';

const ChildComponent = (props) => {
  const { message } = props;

  return (
    <div>
      <p>{message}</p>
    </div>
  );
}

export default ChildComponent;

子组件传数据到父组件

1、Vue2:子组件向父组件传值通常通过自定义事件的方式实现。首先,在子组件中使用$emit方法触发一个自定义事件,并传递需要传递给父组件的值。然后,在父组件中监听该自定义事件,并在对应的方法中获取传递的值

xml 复制代码
<!-- 子组件 -->
<template>
  <button @click="sendValue">传递值给父组件</button>
</template>

<script>
export default {
  methods: {
    sendValue() {
      this.$emit('custom-event', '传递的值');
    }
  }
}
</script>


<!-- 父组件 -->
<template>
  <div>
    <p>接收到的值:{{ receivedValue }}</p>
    <child-component @custom-event="handleEvent"></child-component>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      receivedValue: ''
    };
  },
  methods: {
    handleEvent(value) {
      this.receivedValue = value;
    }
  }
}
</script>

2、Vue3:子组件向父组件传值的方式略有不同,采用defineEmits

xml 复制代码
<!-- 子组件 -->
<script setup>
const emit = defineEmits(['someEvent'])
function onClick() {
  emit('someEvent', 'child message')
}
</script>

<template>
  <button @click="onClick">点击</button>
</template>

<!-- 父组件 -->
<script setup>
import ChildView from './ChildView.vue'

function someEvent(value) {
  console.log(value) // child message
}
</script>

<template>
  <ChildView @some-event="someEvent" />
</template>

3、React:父组件通过props向子组件传递一个回调函数的方式,并由子组件在需要时调用该回调函数,并将需要传递给父组件的值作为参数传递

javascript 复制代码
<!-- 父组件 -->
import React, { useState } from 'react';
import ChildComponent from './ChildComponent';

function ParentComponent() {
  const [receivedValue, setReceivedValue] = useState('');

  const handleValue = (value) => {
    setReceivedValue(value);
  }

  return (
    <div>
      <p>接收到的值:{receivedValue}</p>
      <ChildComponent onSendValue={handleValue} />
    </div>
  );
}

export default ParentComponent;


<!-- 子组件 -->
import React from 'react';

function ChildComponent(props) {
  const sendValue = () => {
    props.onSendValue('传递的值');
  }

  return (
    <button onClick={sendValue}>传递值给父组件</button>
  );
}

export default ChildComponent;

跨层级组件传递

1、Vue2:跨层级的组件之间传递参数,通常会使用provideinject来实现。provide用于在父组件中提供数据,而inject用于在子组件中注入并使用这些数据

xml 复制代码
<!-- 父组件 -->
<template>
  <div>
    <p>父组件提供的值:{{ providedValue }}</p>
    <child-component></child-component>
  </div>
</template>

<script>
export default {
  data() {
    return {
      providedValue: '跨层级传递的参数'
    };
  },
  provide() {
    return {
      providedValue: this.providedValue
    };
  }
}
</script>



<!-- 子组件 -->
<template>
  <div>
    <p>从父组件注入的值:{{ injectedValue }}</p>
  </div>
</template>

<script>
export default {
  inject: ['providedValue'],
  computed: {
    injectedValue() {
      return this.providedValue;
    }
  }
}
</script>

2、Vue3:类似使用provideinject来实现

xml 复制代码
<!-- 父组件 -->
<template>
  <div>
    <p>父组件提供的值:{{ providedValue }}</p>
    <child-component />
  </div>
</template>

<script setup>
import { provide, ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

const providedValue = ref('跨组件传递的参数');

provide('providedValue', providedValue);
</script>



<!-- 子组件 -->
<template>
  <div>
    <p>从父组件传递的值:{{ injectedValue }}</p>
  </div>
</template>

<script setup>
import { inject, defineEmits } from 'vue';

const injectedValue = inject('providedValue');
</script>

3、React:使用上下文(Context)传值,通过创建上下文对象,将数据传递给所有的子组件。子组件通过useContext钩子或Consumer组件来获取上下文中的值。上下文可以在整个组件树中共享数据。

xml 复制代码
<!-- 父组件 -->
const MyContext = React.createContext();
function ParentComponent() {
  const sharedValue = "跨组件传递的值";

  return (
    <MyContext.Provider value={sharedValue}>
      <ChildComponent />
    </MyContext.Provider>
  );
}


<!-- 子组件 -->
function ChildComponent() {
  const sharedValue = React.useContext(MyContext);

  return <p>从父组件传递的值:{sharedValue}</p>;
}

2、计算属性

1、Vue2:在 Vue 2 中,计算属性是通过定义一个具有 get 方法的函数来创建的。这个函数会被设置为计算属性的 getter

javascript 复制代码
// Vue 2
export default {
  data() {
    return {
      firstName: 'John',
      lastName: 'Doe',
    };
  },
  computed: {
    fullName() {
      return this.firstName + ' ' + this.lastName;
    },
  },
};

<!-- 使用计算属性 -->
<template>
  <div>
    <p>Full Name: {{ fullName }}</p>
  </div>
</template>

2、Vue3:可以使用 computed 函数来创建类似于计算属性的行为

javascript 复制代码
import { ref, computed } from 'vue';

export default {
  setup() {
    const firstName = ref('John');
    const lastName = ref('Doe');
    
    const fullName = computed(() => {
      return firstName.value + ' ' + lastName.value;
    });
    
    return {
      firstName,
      lastName,
      fullName,
    };
  },
};

<!-- 使用计算属性 -->
<template>
  <div>
    <p>Full Name: {{ fullName }}</p>
  </div>
</template>

3、React:没有直接等同于 Vue 中的计算属性的概念,但它可以使用 useMemo 钩子函数,避免在每次组件渲染时都重新计算某个值,你可以使用 React 的 useMemo 钩子函数。useMemo 接收一个回调函数和依赖项数组,并返回计算后的值。只有在依赖项发生变化时,才会重新计算。

javascript 复制代码
import { useMemo } from 'react';

function MyComponent({ firstName, lastName }) {
  const fullName = useMemo(() => {
    return firstName + ' ' + lastName;
  }, [firstName, lastName]);

  return <p>Full Name: {fullName}</p>;
}

3、Watch

1、Vue2:在 Vue 2 中,你可以通过在组件的 watch 选项中定义一个或多个观察表达式来观察数据变化,并在回调函数中对变化做出相应的处理。

javascript 复制代码
// Vue 2
export default {
  data() {
    return {
      firstName: 'John',
      lastName: 'Doe',
    };
  },
  watch: {
    firstName(newFirstName, oldFirstName) {
      console.log('firstName changed', newFirstName, oldFirstName);
    },
    lastName(newLastName, oldLastName) {
      console.log('lastName changed', newLastName, oldLastName);
    },
  },
};

2、Vue3:在 Vue 3 中,watch 函数已被重构为更加灵活的 watchEffectwatch 函数的组合

  • watchEffect:会立即执行传入的回调函数,并自动追踪其内部所使用的响应式依赖,并在依赖发生改变时重新运行回调函数。

    javascript 复制代码
    import { watchEffect } from 'vue';
    
    export default {
      setup() {
        const firstName = ref('John');
    
        watchEffect(() => {
          console.log('firstName changed', firstName.value);
        });
    
        return {
          firstName,
        };
      },
    };
  • watch:可以通过设置 watch 函数来手动监视特定的响应式依赖项,并在其发生改变时执行回调函数

    javascript 复制代码
    import { watch, ref } from 'vue';
    
    export default {
      setup() {
        const firstName = ref('John');
        const lastName = ref('Doe');
    
        watch([firstName, lastName], ([newFirstName, newLastName], [oldFirstName, oldLastName]) => {
          console.log('firstName or lastName changed', newFirstName, newLastName, oldFirstName, oldLastName);
        });
    
        return {
          firstName,
          lastName,
        };
     },

3、React:在函数组件中,你可以使用 React 的 useEffect 钩子函数来监听特定的状态或属性,并在其发生变化时执行相应的操作

javascript 复制代码
import { useState, useEffect } from 'react';

function MyComponent() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log('count changed', count);
    // 执行其他操作...
  }, [count]);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

上述示例中,通过传递一个依赖项数组 [count] 给 useEffect,它会在 count 发生变化时执行回调函数。

4、路由使用

​ 1、Vue2:通常是使用 this.$routerthis.$route 来进行路由的跳转和参数获取

xml 复制代码
<script>

function onClick() {
  this.$router.push({
    path: '/about',
    query: {
      msg: 'hello vue3!'
    }
  })
}
const route = this.$route
console.log(route.query.msg) // hello vue3!
</script>

​ 2、Vue3:以使用 vue-router 提供的 useRouter 方法,来进行路由跳转

xml 复制代码
<script setup>
import { useRouter, useRoute } from 'vue-router'

const router = useRouter()
function onClick() {
  router.push({
    path: '/about',
    query: {
      msg: 'hello vue3!'
    }
  })
}
const route = useRoute()
console.log(route.query.msg) // hello vue3!
</script>

​ 3、React:使用React Router库进行路由跳转和获取路由对象,通过调用history对象的push方法,可以进行编程式导航跳转,获取路由对象:可以通过useParamsuseLocationuseHistory等React Router提供的钩子函数来获取路由对象

javascript 复制代码
ex1:

import { useHistory } from 'react-router-dom';

function MyComponent() {
  const history = useHistory();

  const handleClick = () => {
    history.push('/path');
  };

  return (
    <div>
      <button onClick={handleClick}>Go to Path</button>
    </div>
  );
}

ex2:
import { useParams } from 'react-router-dom';

function MyComponent() {
  const { id } = useParams();

  // 使用路由参数id进行特定操作

  return (
    <div>
      <h1>Post {id}</h1>
    </div>
  );
}

ex3:
import { useLocation } from 'react-router-dom';

function MyComponent() {
  const location = useLocation();

  // 使用location对象获取当前URL、查询参数等信息

  return (
    <div>
      <p>Current URL: {location.pathname}</p>
      <p>Query Params: {location.search}</p>
    </div>
  );
}

5、上下文对象

1、Vue2:Vue2存在this这个上下文对象,其数据、方法等都可以用this.xx来获取,这边就不举例。

2、Vue3:Vue3 的 setup 中无法使用 this 这个上下文对象,它可以通过 getCurrentInstance 方法获取上下文对象。

xml 复制代码
<script setup>
import { getCurrentInstance } from 'vue'

// 以下两种方法都可以获取到上下文对象
const { ctx } = getCurrentInstance()
const { proxy }  = getCurrentInstance()
</script>

注:ctx 只能在开发环境使用,生成环境为 undefined 。 推荐使用 proxy ,在开发环境和生产环境都可以使用

3、React:在React中,组件没有类似于Vue 3的实例引用的概念

6、插槽

1、Vue2:有两种类型的插槽:具名插槽和作用域插槽

  • 具名插槽:可以在父组件中使用 <slot> 元素,并通过 name 属性来指定插槽的名称。子组件中使用 slot 元素并设置 slot 属性的值为对应的插槽名称,父组件中内容会被插入到对应的插槽中。

    xml 复制代码
    <!-- 父组件 -->
    <template>
      <div>
        <slot name="header"></slot>
        <slot></slot>
        <slot name="footer"></slot>
      </div>
    </template>
    
    <!-- 使用父组件 -->
    <template>
      <my-component>
        <template v-slot:header>
          <!-- 插入到名称为 "header" 的插槽中 -->
          <h1>Header Slot Content</h1>
        </template>
    
        <p>Default Slot Content</p>
        
        <template v-slot:footer>
          <!-- 插入到名称为 "footer" 的插槽中 -->
          <footer>Footer Slot Content</footer>
        </template>
      </my-component>
    </template>
  • 作用域插槽:作用域插槽允许父组件向子组件传递数据。可以在父组件中使用带有参数的 <slot> 元素,并在子组件中使用具有相同参数的 <template> 元素

    xml 复制代码
    <!-- 父组件 -->
    <template>
      <div>
        <slot :item="data"></slot>
      </div>
    </template>
    
    <!-- 使用父组件 -->
    <template>
      <my-component>
        <template v-slot="{ item }">
          <!-- 使用传递的数据 -->
          <p>{{ item }}</p>
        </template>
      </my-component>
    </template>

2、Vue3:插槽的概念发生了变化,被合并为一个更通用的组合式 API:<slot> 元素被替换为 v-slot 指令,并且可以直接在组件标签上使用。使用 v-slot 指令时,可以通过参数语法或缩写语法来命名插槽

xml 复制代码
ex1:

<!-- 父组件 -->
<template>
  <div>
    <slot name="header"></slot>
    <slot></slot>
    <slot name="footer"></slot>
  </div>
</template>

<!-- 使用父组件 -->
<template>
  <my-component>
    <template v-slot:header>
      <!-- 插入到名称为 "header" 的插槽中 -->
      <h1>Header Slot Content</h1>
    </template>

    <p>Default Slot Content</p>
    
    <template v-slot:footer>
      <!-- 插入到名称为 "footer" 的插槽中 -->
      <footer>Footer Slot Content</footer>
    </template>
  </my-component>
</template>

ex2:(作用域插槽)


<!-- 父组件 -->
<template>
  <div>
    <slot :item="data"></slot>
  </div>
</template>

<!-- 使用父组件 -->
<template>
  <my-component>
    <template v-slot:default="slotProps">
      <!-- 使用传递的数据 -->
      <p>{{ slotProps.item }}</p>
    </template>
  </my-component>
</template>

3、React:通过组件的props来实现的,有两种常见的插槽使用方式

  • 使用Props作为插槽:父组件可以通过子组件的props将内容传递给子组件进行渲染

    javascript 复制代码
    // 父组件
    function ParentComponent() {
      return (
        <div>
          <ChildComponent>
            {/* 子组件插槽内容 */}
            <h1>插槽内容</h1>
          </ChildComponent>
        </div>
      );
    }
    
    // 子组件
    function ChildComponent(props) {
      return (
        <div>
          {/* 渲染插槽内容 */}
          {props.children}
        </div>
      );
    }
  • 使用组件作为插槽:父组件可以通过自定义组件作为插槽,在子组件中使用该组件进行插槽渲染。

    javascript 复制代码
    // 父组件
    function ParentComponent() {
      return (
        <div>
          <ChildComponent header={<h1>Header Slot Content</h1>} footer={<footer>Footer Slot Content</footer>}>
            {/* 默认插槽内容 */}
            <p>Default Slot Content</p>
          </ChildComponent>
        </div>
      );
    }
    
    // 子组件
    function ChildComponent(props) {
      return (
        <div>
          {/* 渲染插槽内容 */}
          {props.header}
          {props.children}
          {props.footer}
        </div>
      );
    }

7、生命周期

1、Vue2:生命周期方法在组件实例创建、挂载和销毁时被调用,使用 beforeCreate, created, beforeMount, mounted, beforeUpdate, updated, beforeDestroy, destroyed等生命周期方法。

2、Vue3:使用 setup 函数来替代大部分的生命周期方法,它在组件的 beforeCreate 钩子之前调用,可以在 setup 函数中执行组件初始化的逻辑。使用 onBeforeMountonMountedonBeforeUpdateonUpdatedonBeforeUnmountonUnmounted 等新的生命周期方法。还引入了两个全局的生命周期方法 onRenderTrackedonRenderTriggered,用于跟踪和调试渲染过程。

3、React:在 React 中,组件的生命周期可以分为三个阶段:挂载阶段、更新阶段和卸载阶段。

arduino 复制代码
1、挂载阶段(Mounting):
constructor:在组件被创建时调用,用于初始化组件的状态和绑定事件处理程序。
static getDerivedStateFromProps:在组件实例化和接收到新的 props 时调用,用于根据新的 props 更新组件状态。
render:必选的生命周期方法,返回组件的 JSX 元素并渲染到页面上。
componentDidMount:在组件被渲染到页面后调用,可以进行异步数据的获取、订阅事件等操作。


2、更新阶段(Updating):
static getDerivedStateFromProps:同挂载阶段中的用法,根据新的 props 更新组件状态。
shouldComponentUpdate:在组件更新之前调用,可以根据新的 props 或 state 决定是否需要重新渲染组件。
render:重新渲染组件。
getSnapshotBeforeUpdate:在组件更新之前被调用,可以获取之前的 DOM 信息,返回值将作为第三个参数传递给 componentDidUpdate 方法。
componentDidUpdate:在组件更新完成后调用,可以进行 DOM 操作、发起网络请求等。


3、卸载阶段(Unmounting):
componentWillUnmount:在组件被卸载之前调用,可以进行清理工作,如取消订阅、清除定时器等。

8、逻辑复用

1、Vue2:使用 Mixins 来实现逻辑的复用,但在多个 Mixin 之间可能存在命名冲突和相互依赖的问题

javascript 复制代码
// 定义一个 Mixin 对象
var myMixin = {
  data: function () {
    return {
      message: 'Hello, world!'
    }
  },
  methods: {
    showMessage: function () {
      alert(this.message)
    }
  }
}

// 组件引入 Mixin
var myComponent = Vue.extend({
  mixins: [myMixin],
  created: function () {
    console.log(this.message) // 输出 "Hello, world!"
  }
})

2、Vue3:引入了 Composition API,通过函数式组合的方式来实现逻辑的复用,可以更好地组织和封装代码

javascript 复制代码
import { ref, computed } from 'vue'

// 定义一个逻辑
function useCounter() {
  const count = ref(0)
  
  function increment() {
    count.value++
  }
  
  function decrement() {
    count.value--
  }
  
  const doubledCount = computed(() => count.value * 2)
  
  return {
    count,
    doubleCount,
    increment,
    decrement
  }
}

// 组件引入逻辑
export default {
  setup() {
    const { count, doubleCount, increment, decrement } = useCounter()
    
    return {
      count,
      doubleCount,
      increment,
      decrement
    }
  }
}

3、React:可以使用 Hooks 来实现逻辑的复用。Hooks 实际上是一个函数,用于增强函数组件的功能,使之具有类似于类组件的生命周期、状态管理等功能。(Hooks 只能用于函数组件和自定义 Hooks 中,类组件中不可使用)

javascript 复制代码
import React, { useState, useEffect } from 'react'

// 定义一个逻辑
function useCounter() {
  const [count, setCount] = useState(0)
  
  function increment() {
    setCount(count + 1)
  }
  
  function decrement() {
    setCount(count - 1)
  }
  
  useEffect(() => {
    console.log('count has changed:', count)
  }, [count])
  
  return {
    count,
    increment,
    decrement
  }
}

// 组件引入逻辑
function MyComponent() {
  const { count, increment, decrement } = useCounter()
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  )
}

9、全局API

1、Vue2:Vue2 中的全局属性或全局方法,是在构造函数 Vue 的原型对象上进行添加,如:Vue.prototype.$axios = axios,通过this.$axios进行调用。

2、Vue3:在 app 实例上添加:

arduino 复制代码
// main.js
app.config.globalProperties.$axios = axios

在组件中使用:

xml 复制代码
<script setup>
import { getCurrentInstance } from 'vue'

const { proxy } = getCurrentInstance()
proxy.$axios.get('http://...')
</script>

3、React:在 React 中,可以通过将全局 API 注册到应用的上下文中,以便在整个应用的任何地方使用。一种常见的方式是使用 Context API 或第三方库,如 react-helmet。

javascript 复制代码
1、创建一个新的文件,例如 api.js,用于定义全局 API:
import React, { createContext, useContext } from 'react';

// 创建一个上下文对象
const GlobalAPIContext = createContext();

// 提供一个自定义 Hook 用于获取全局 API
export function useGlobalAPI() {
  return useContext(GlobalAPIContext);
}

// 全局 API 提供者组件
export function GlobalAPIProvider({ children }) {
  // 定义全局 API 的值和方法
  const globalAPI = {
    // 在这里定义你的全局 API
    showMessage: (message) => {
      alert(message);
    },
    // ...
  };

  return (
    // 使用上下文 Provider 包装子组件,并传递全局 API
    <GlobalAPIContext.Provider value={globalAPI}>
      {children}
    </GlobalAPIContext.Provider>
  );
}

2、在根组件中使用全局 API 提供者,并将应用的内容包裹在其中
import React from 'react';
import { GlobalAPIProvider } from './api.js';

function App() {
  return (
    // 使用 GlobalAPIProvider 包裹应用的内容
    <GlobalAPIProvider>
      {/* 应用的其他组件 */}
    </GlobalAPIProvider>
  );
}

export default App;

3、在需要使用全局 API 的组件中,使用 useGlobalAPI 自定义 Hook 获取全局 API:
import React from 'react';
import { useGlobalAPI } from './api.js';

function MyComponent() {
  // 使用 useGlobalAPI 自定义 Hook 获取全局 API
  const globalAPI = useGlobalAPI();

  // 调用全局 API 的方法
  const handleClick = () => {
    globalAPI.showMessage('Hello, World!');
  };

  return (
    <button onClick={handleClick}>Show Message</button>
  );
}

export default MyComponent;
相关推荐
你的人类朋友9 分钟前
说说git的变基
前端·git·后端
姑苏洛言12 分钟前
网页作品惊艳亮相!这个浪浪山小妖怪网站太治愈了!
前端
字节逆旅19 分钟前
nvm 安装pnpm的异常解决
前端·npm
Jerry34 分钟前
Compose 从 View 系统迁移
前端
IT码农-爱吃辣条1 小时前
Three.js 初级教程大全
开发语言·javascript·three.js
GIS之路1 小时前
2025年 两院院士 增选有效候选人名单公布
前端
四岁半儿1 小时前
vue,H5车牌弹框定制键盘包括新能源车牌
前端·vue.js
烛阴1 小时前
告别繁琐的类型注解:TypeScript 类型推断完全指南
前端·javascript·typescript
gnip1 小时前
工程项目中.env 文件原理
前端·javascript
JefferyXZF2 小时前
Next.js Server Actions 详解: 无缝衔接前后端的革命性技术(八)
前端·全栈·next.js