Vue-Router

文章目录

一、使用步骤

1、安装

vue2

bash 复制代码
npm install vue-router@3

vue3

bash 复制代码
npm install vue-router@4

2、创建路由实例

  • **vue2:**使用new VueRouter,模式设置mode: 'history'
  • **vue3:**使用createRouter,模式设置history: createWebHistory()

vue2

js 复制代码
import Vue from 'vue';
import VueRouter from 'vue-router';
import Home from './components/Home.vue';
//import About from './components/About.vue';

Vue.use(VueRouter);
//路由懒加载
const routes = [
  { path: '/', component: Home },
  { path: '/about', component:resolve => require(['@/components/About.vue'],resolve)  }
];

const router = new VueRouter({
	mode: 'history',
  routes // short for `routes: routes`
});

export default router;

vue3

"history" => createWebHistory()

"hash" => createWebHashHistory()

"abstract" => createMemoryHistory()

js 复制代码
import { createRouter, createWebHistory } from 'vue-router';
import Home from './components/Home.vue';
import About from './components/About.vue';

const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About }
];

const router = createRouter({
  history: createWebHistory(),
  routes, // 这里不需要再写 routes: routes 了,直接传入数组即可
});

export default router;

路由的核心原理

就是通过 Hash 模式 或 History 模式 来监听 URL 的变化,从而实现视图组件的动态切换。

  • Hash 模式: 通过监听 window.onhashchange 事件来感知 URL 中 # 后面内容的变化。
  • History 模式: 基于 window.history.pushState window.history.replaceState API 来修改 URL,并通过 popstate 事件监听 URL 的变化。

3、在 Vue 应用中使用路由

Vue 2 和 Vue 3 (基本相同)。都需要创建一个路由器实例,并在根 Vue 实例中挂载它:

  • vue2: 使用new Vue创建应用实例,将router作为对象属性配置进去
  • vue3: 使用createApp创建应用实例,app.use(router)配置

vue2

js 复制代码
import Vue from 'vue';
import App from './App.vue';
import router from './router'; // 引入路由配置

new Vue({
  el: '#app',
  router, // 使用路由配置
  render: h => h(App)
});

vue3

js 复制代码
import { createApp } from 'vue';
import App from './App.vue';
import router from './router'; // 引入路由配置

const app = createApp(App); // 创建应用实例
app.use(router); // 使用路由配置
app.mount('#app'); // 挂载应用实例到 DOM 上

二、路由跳转

  • vue2: 只支持单一router-view;使用this.$router实现路由跳转;this.$route获取路由参数信息

  • vue3: 支持作用域插槽 v-slot="{ Component };使用useRouter()实现路由跳转 ; useRoute() 获取路由参数信息

    html 复制代码
    //vue2
    <router-view/>
    //vue3
    <!-- 使用 v-slot 获取当前组件,便于添加过渡动画 -->
    <router-view v-slot="{ Component }">
      <transition name="fade" mode="out-in">
        <component :is="Component" />
      </transition>
    </router-view>

vue2

在 Vue2 中,我们可以通过两种方式控制路由:声明式导航 和 编程式导航

  • 声明式导航 : 使用 组件生成可点击的链接,类似 HTML 的 标签,但不会引起页面刷新。

  • 编程式导航: 则通过 this.$router 提供的方法(如 push, replace, go)实现逻辑跳转,适用于按钮事件或条件判断场景。

    js 复制代码
    1) this.$router.push(path): 相当于点击路由链接(可以返回到当前路由界面)
    2) this.$router.replace(path): 用新路由替换当前路由(不可以返回到当前路由界面)
    3) this.$router.back(): 请求(返回)上一个记录路由
    4) this.$router.go(-1): 请求(返回)上一个记录路由
    5) this.$router.go(1): 请求下一个记录路由

同时,this.$route 对象提供了当前路由的详细信息(如路径、参数、查询等),可用于动态响应路由变化。

html 复制代码
<template>
  <div id="app">
    <nav>
      <!-- 声明式导航 -->
      <router-link to="/">Home</router-link>
      <router-link to="/about">About</router-link>
      <router-link :to="{ name: 'User', params: { id: 123 }}">User</router-link>

      <!-- 活动路由样式 -->
      <router-link to="/" exact-active-class="active">Home</router-link>
      <!-- 动态传参 -->
      <li v-for="m in messages" :key="m.id">
			<router-link :to="`/home/message/detail/${m.id}`">{{m.title}}</router-link>
			<button @click="pushShow(m.id)">push查看</button>
			<button @click="replaceShow(m.id)">replace查看</button>
		</li>
    </nav>

    <!-- 路由出口:匹配的组件将在此处渲染 -->
    <router-view/>
  </div>
</template>

<script>
export default {
  methods: {
    // 编程式导航
    goToAbout() {
      this.$router.push('/about')
    },
    goBack() {
      this.$router.go(-1) // 后退一页
    },
    replaceRoute() {
      this.$router.replace('/about') // 替换当前记录,无法后退
     //替换路由参数,并刷新路由
		window.location.reload()
    },
    pushShow (id) {
    	this.$router.push(`/home/message/detail/${id}`)
   	},
	
   	replaceShow(id) {
	     this.$router.replace(`/home/message/detail/${id}`)
   	}
  },

  // 访问路由信息
  computed: {
    currentRoute() {
      return this.$route // 包含 path, params, query, meta 等
    }
  }
}
</script>

vue3

提供了 useRouteruseRoute 两个组合式函数,可在 setup() 中直接调用

  • useRouter:useRouter()得到router路由实例,包含路由跳转方法(push、go、replace)
  • useRoute :useRoute()得到route路由对象,包含路由信息参数(path、params、hash、query)

使用步骤:

  • 引入:import { useRouter, useRoute } from 'vue-router'
  • 调用:useRouter();useRoute()
html 复制代码
<template>
  <div id="app">
    <nav>
      <router-link to="/">Home</router-link>
      <router-link to="/about">About</router-link>
    </nav>
    <!-- 使用 v-slot 获取当前组件,便于添加过渡动画 -->
    <router-view v-slot="{ Component }">
      <transition name="fade" mode="out-in">
        <component :is="Component" />
      </transition>
    </router-view>
  </div>
</template>

<script>
import { useRouter, useRoute } from 'vue-router'

export default {
  setup() {
    const router = useRouter()
    const route = useRoute()
    const goToAbout = () => {	router.push('/about')	}
    const goBack = () => {	router.go(-1)	}
    // 监听路由参数变化,实时获取新用户数据
    watch(
      () => route.params.id,
      (newId) => {
        fetchUserData(newId)
      },
      { immediate: true }
    )
    return {
      goToAbout,
      goBack,
      currentRoute: route
    }
  }
}
</script>

三、路由导航守卫

路由守卫是 Vue Router 的强大功能之一,允许你在导航过程中插入逻辑钩子,常用于权限校验、页面提示、数据预加载等场景。

  • 全局守卫:作用于所有路由切换
    • beforeEach:路由前置时触发(用于登录验证
    • afterEach:不接收 next 函数,不能阻止导航,常用于访问分析、更改页面标题、声明页面等辅助功能
    • beforeResolve :是获取数据或执行任何其他操作(进入所有页面后都执行的操作)的理想位置
  • 路由独享守卫:仅针对特定路由 ,直接定义在某个路由配置上,适用于管理员页面等特殊权限控制
    • beforeEnter:路由进入前(用于权限控制
    • afterEnter :路由离开时(用于清除或存储一些信息
  • 组件独享守卫:写在组件内部 ,用于处理组件自身的生命周期与路由交互
    • beforeRouteEnter:不能访问 this,因为此时组件还没有被创建,通过传一个回调给 next 来访问组件实例 next(vm=>{})
    • beforeRouteUpdate:在当前路由改变,但是该组件被复用时调用
    • beforeRouteLeave :用来预防用户在还未保存修改前突然离开。该守卫可以通过返回 false 来取消导航

它们的执行顺序如下:

全局前置守卫 (beforeEach)

路由独享守卫 (beforeEnter)

组件内守卫 (beforeRouteEnter)

全局解析守卫 (beforeResolve)

全局后置守卫 (afterEach)

区别

  • vue2: 路由导航守卫中必须调用 next() 才能继续导航 ,否则会被阻塞
  • vue3: 不再强制要求调用 next() ,而是通过返回值来决定导航行为 ,可以直接返回一个路径字符串、布尔值或 false 来中断导航。并且提供两个组合式 API,onBeforeRouteUpdateonBeforeRouteLeave

vue2

全局守卫

js 复制代码
//beforeEach:用于登录验证
router.beforeEach((to, from, next) => {
  const isAuthenticated = checkAuth()
  if (to.meta.requiresAuth && !isAuthenticated && to.name !== 'Login') {
    next('/login') // 未登录则跳转至登录页
  } else {
    next() // 放行
  }
})
//beforeResolve
router.beforeResolve((to, from, next) => {
  next()
})
//afterEach
router.afterEach((to, from) => {
  console.log(`页面跳转:${
     from.path} → ${
     to.path}`)
})

路由独享守卫:

将一个函数数组传递给 beforeEnter,用于不同的路由重用守卫

javascript 复制代码
// 清除 query 参数
function removeQueryParams(to) {
  if (Object.keys(to.query).length)
    return { path: to.path, query: {}, hash: to.hash }
}
// 清除 hash 值
function removeHash(to) {
  if (to.hash) return { path: to.path, query: to.query, hash: '' }
}
const routes = [
  {
    path: '/admin',
    component: Admin,
    beforeEnter: (to, from, next) => {
      if (!isAdmin()) {
        next('/403')
      } else {
        next()
      }
    },
    afterEnter:(){}
  },
  {
    path: '/users/:id',
    component: UserDetails,
    beforeEnter: [removeQueryParams, removeHash]
  }
]

组件独享守卫:

javascript 复制代码
const User = {
  template: `...`,
  beforeRouteEnter(to, from, next) {
    // 此时尚未创建组件实例,不能访问 this
    next(vm => {
      // 通过 vm 访问组件实例,可用于初始化数据
    })
  },
  beforeRouteUpdate(to, from, next) {
    // 当前组件复用时触发(如 /user/1 → /user/2)
    this.userData = null
    next()
  },
  beforeRouteLeave(to, from, next) {
    // 离开当前路由前询问用户
    const answer = window.confirm('确定要离开吗?')
    if (answer) {
      next()
    } else {
      next(false) // 阻止导航
    }
  }
}

vue3

最大的变化是:不再强制要求调用 next() ,而是通过返回值来决定导航行为

可以直接返回一个路径字符串、布尔值或 false 来中断导航。

提供两个组合式 APIonBeforeRouteUpdateonBeforeRouteLeave

全局守卫

javascript 复制代码
router.beforeEach((to, from) => {
  const isAuthenticated = checkAuth()
  // 检查是否需要认证
  if (to.meta.requiresAuth && !isAuthenticated) {
    const token = localStorage.getItem('token')
    if (!token) {
      return '/login'
    }
    // 检查用户权限
    const userRole = await getUserRole()
    if (to.meta.roles && !to.meta.roles.includes(userRole)) {
      return '/403'
    }
  }
  // 不需要 return 或 next(),默认放行
})

组合式 API 中的导航守卫

javascript 复制代码
<script setup lang="ts">
import { ref } from 'vue'
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'

const userData = ref()

onBeforeRouteUpdate(async (to, from) => {
  //仅当 id 更改时才获取用户信息
  if (to.params.id !== from.params.id) {
    userData.value = await fetchUser(to.params.id)
  }
  // 处理路由参数更新,例如刷新用户数据
    await refreshUserData(to.params.id)
})
onBeforeRouteLeave((to, from) => {
  const answer = window.confirm('确定要离开吗?')
  // 取消导航并停留在当前页面
  if (!answer) return false
})
</script>

四、路由传参

4 种传参方式:

  • params 动态路由参数作为路径一部分 ,如 /user/123,SEO 友好,适合资源标识
    • 必须使用name跳转
    • 刷新页面参数不会丢失(因为在 URL 中)
  • query 查询参数拼接在 URL 后面 ,如 ?id=123&name=tom,适合筛选、分页等可分享场景
    • 可以用 path 或 name 跳转
    • 刷新页面参数不会丢失
    • 适合传递非敏感、可分享的数据
  • props 解耦传参 :官方推荐,支持布尔、对象、函数三种模式 ,让组件与路由解耦,提高复用性
    • 组件可独立测试,不依赖 $route
    • 组件与路由解耦,提高复用性
    • 官方推荐
  • state 隐式传参 :Vue Router 4 新增,数据不显示在 URL ,适合临时敏感数据
    • 参数不显示在 URL 中
    • 刷新页面可能丢失(取决于实现方式)
    • 适合传递临时、敏感数据

Vue2 和 Vue3 的主要区别:

  • API 层面 :Vue3 使用 useRoute() 和 useRouter() 组合式函数,替代 this.$route
  • 重大变更 :Vue Router 4 移除了隐式 params 传参 ,必须在路由中定义动态参数或改用 query/state
  • 新增特性支持 history.state 传参,TypeScript 类型推导更完善

场景选择:

  • 详情页用 params
  • 筛选用 query
  • 组件复用用 props
  • 复杂数据用 Pinia 管理

params和query传参

javascript 复制代码
// 路由配置
const routes = [
  { path: '/user/:id', name: 'User', component: User }
]
// 传参方式1:params,必须使用 name 跳转
this.$router.push({ name: 'User', params: { id: 123 } })
// 或
this.$router.push('/user/123')
// 接收参数
this.$route.params.id

//传参方式2:query,path或name跳转都行
// 传参方式
this.$router.push({ path: '/user', query: { id: 123, name: 'tom' } })
// 接收参数
this.$route.query.id

props 解耦传参

javascript 复制代码
// 路由配置
const routes = [
  // 布尔模式:params 自动映射为 props
  { path: '/user/:id', component: User, props: true },
  // 对象模式:静态 props
  { path: '/user', component: User, props: { id: 123 } },
  // 函数模式:灵活处理
  { 
    path: '/user', 
    component: User, 
    props: route => ({ id: route.query.id, name: route.query.name })
  }
]
// 组件中接收
export default {
  props: ['id', 'name']
}

state 隐式传参

javascript 复制代码
// Vue2 写法(不推荐,刷新丢失)
this.$router.push({ name: 'User', params: { secret: 'data' } })
// 注意:如果路由没有定义 :secret,刷新后会丢失
 
// Vue3 + HTML5 History state
this.$router.push({ 
  path: '/user',
  state: { secret: 'hidden data' }  // Vue Router 4 新增
})
// 接收
history.state.secret

五、动态路由

区别

  • vue2: 使用router.addRoutes添加路由;通配符写法path: '*'
  • vue3: 使用router.addRoute添加路由;router.removeRoute删除路由;router.hasRoute判断路由是否存在;通配符写法path: '/:pathMatch(.*)*'

vue2

1、定义路由配置‌

javascript 复制代码
// router/index.js
import Vue from 'vue';
import Router from 'vue-router';
import Home from '../components/Home.vue';
Vue.use(Router);
const router = new Router({
  mode: 'history',
  routes: [
    { path: '/', component: Home },
    { path: '*', component: () => import('../components/NotFound.vue') } // 通配符路由
  ]
});
export default router;

2、动态添加路由‌,在main.js或相应的初始化文件中

javascript 复制代码
router.addRoutes([
  { path: '/about', component: () => import('../components/About.vue') },
  { path: '/user/:id', component: () => import('../components/UserProfile.vue') }
]);

vue3

1、定义路由配置‌

javascript 复制代码
// router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import Home from '../components/Home.vue';
import NotFound from '../components/NotFound.vue';
const routes = [
  { path: '/', component: Home },
  { path: '/:pathMatch(.*)*', component: NotFound } // 通配符路由
];
const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
});
export default router;

2、动态添加路由‌,在main.js或相应的初始化文件中

javascript 复制代码
// 在main.js或相应的初始化文件中
import { createApp } from 'vue';
import App from './App.vue';
import router from './router'; // 确保导入正确的router实例
import About from './components/About.vue'; // 示例组件导入,根据需要添加更多组件和路由定义。

const app = createApp(App);
app.use(router); // 使用router实例。注意:在Vue 3中,这是必须的。
app.mount('#app'); // 挂载应用。注意:在Vue 3中,这是必须的。

// 动态添加路由示例:在应用启动时或根据条件添加。例如,根据用户权限动态加载路由。
router.addRoute({ path: '/about/:id', component: () => import('@/components/About.vue') }); 
//删除路由
// 没有起名,name值为空时
const removeRoute = router.addRoute({ path: '/about/:id', component: () => import('@/components/About.vue') });
removeRoute()
//name命名时,命名为about
router.removeRoute('about')
//检查路由是否存在
router.hasRoute('about')

六、路由元信息meta

使用场景:

  • 权限控制: 在路由守卫中检查用户权限,根据 meta 中定义的权限信息决定是否允许访问某个路由。
  • 页面标题管理: 在应用中动态设置页面标题,可以将页面标题信息放在 meta 中,然后在全局前置守卫或组件中读取并设置。
  • 动态添加 CSS 类或样式: 根据 meta 中定义的样式信息,动态地给页面添加特定的 CSS 类或样式。
  • 跟踪分析: 在 meta 中添加用于分析的标签,比如来源页面、是否是营销活动页面等,用于跟踪用户行为。

区别

  • vue2: 在路由表中配置meta信息,在全局守卫router.beforeEach中判断及使用
  • vue3: 两种使用方法
    • 原生meta: 实现方式
      • 使用provide和inject
      • 使用Vue Router的beforeRouteEnter钩子或watch监听路由变化
    • vue-meta库 引入并使用

vue2

1、权限控制

javascript 复制代码
const routes = [
  {
    path: '/user',
    component: UserComponent,
    meta: { requiresAuth: true, title: '用户信息' },
  },
  {
    path: '/admin',
    component: AdminComponent,
    meta: { requiresAuth: true, title: '管理员面板', role: 'admin' },
  },
  {
    path: '/public',
    component: PublicComponent,
    meta: { title: '公共页面' },
  },
];

在全局守卫中判断

javascript 复制代码
router.beforeEach((to, from, next) => {
  // 假设从全局状态中获取用户角色
  const userRole = store.getters.userRole;
  // 检查路由是否需要认证
  if (to.matched.some(record => record.meta.requiresAuth)) {
    // 检查用户角色是否满足路由要求
    if (to.meta.requiresAuth && userRole !== 'admin') {
      // 如果用户角色不是管理员,则重定向到登录页面
      next({ name: 'login' });
    } else {
      // 用户角色匹配,允许访问
      next();
    }
  } else {
    // 不需要认证的路由,直接允许访问
    next();
  }
});

2、页面标题管理

javascript 复制代码
router.beforeEach((to, from, next) => {
  // 设置页面标题
  document.title = to.meta.title || '默认标题';
  next();
});

3、动态添加 CSS 类

javascript 复制代码
export default {
  setup() {
    const route = useRoute();
	// 根据 meta 中的样式信息动态添加 CSS 类
    const pageClass = computed(() => route.meta.pageClass || '');
    return { pageClass };
  },
};

模板中

html 复制代码
<div :class="pageClass">我是加了 pageClass 样式的div哦</div>

vue3

1、vue3自带Meta方法

  • 使用provide和inject
html 复制代码
//父组件

<template>
  <div>
    <h1>父组件</h1>
    <ChildComponent />
  </div>
</template>

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

export default {
  components: {
    ChildComponent
  },
  setup() {
    const metaInfo = ref({
      title: '页面标题',
      description: '页面描述'
    });

    provide('metaInfo', metaInfo);

    return { metaInfo };
  }
}
</script>
html 复制代码
//子组件

<template>
  <div>
    <h2>子组件</h2>
  </div>
</template>

<script>
import { inject, onMounted, onUnmounted } from 'vue';

export default {
  setup() {
    const metaInfo = inject('metaInfo');

    const updateMeta = () => {
      document.title = metaInfo.value.title;
      const metaDescription = document.querySelector('meta[name="description"]');
      if (metaDescription) {
        metaDescription.content = metaInfo.value.description;
      } else {
        const meta = document.createElement('meta');
        meta.name = "description";
        meta.content = metaInfo.value.description;
        document.getElementsByTagName('head')[0].appendChild(meta);
      }
    };

    onMounted(updateMeta);
    onUnmounted(() => {
      // 清除或恢复原始meta标签,如果需要的话。例如:恢复默认的document.title。
    });
  }
}
</script>
  • 使用Vue Router的beforeRouteEnter钩子watch监听路由变化
javascript 复制代码
//路由表中配置
const routes = [
  { 
    path: '/', 
    component: Home, 
    meta: { title: '首页', description: '这是首页的描述' } 
  },
  // 其他路由...
];
javascript 复制代码
//路由守卫中更新
import { useRouter } from 'vue-router';
import { watch, onBeforeMount, ref } from 'vue';
import { useTitle } from '@vueuse/core'; // 如果你使用vueuse包来管理标题的话。否则,你可以直接操作document.title。

export default {
  setup() {
    const router = useRouter();
    const title = useTitle(); // 使用vueuse的useTitle来管理标题。你也可以直接操作document.title。
    const description = ref(''); // 用于存储描述信息。如果不需要,可以忽略此行。
    const head = ref(document.head); // 获取head元素以便操作meta标签。如果不需要,可以忽略此行。但通常你需要它来动态添加或更新meta标签。
    const updateMeta = () => { 
    // 更新meta的方法。如果不需要,可以忽略此行。但通常你需要它来动态添加或更新meta标签。例如:添加或更新description。
    }

2、使用vue-meta

  • 安装
bash 复制代码
npm install vue-meta
yarn add vue-meta
  • 在入口文件中配置
javascript 复制代码
import { createApp } from 'vue'
import App from './App.vue'
// 引入配置
import { createMetaManager } from 'vue-meta'
const app = createApp(App)
//挂载配置
app.use(createMetaManager())
app.mount('#app')
  • 组件中使用,metaInfo选项来定义或修改页面的元数据;或者使用useMetaAPI
html 复制代码
<template>
  <div>Your Content Here</div>
</template>

<script>
export default {
  metaInfo: {
    // 用于SEO的标题
    title: '我的页面标题',
    // 用于SEO的描述
    meta: [
      { vmid: 'description', name: 'description', content: '这是页面的描述' }
    ]
  }
  //或者用useMeta API
  import { useMeta } from 'vue-meta'
	useMeta({
	  title: '我的页面标题',
	  meta: [
	    { vmid: 'description', name: 'description', content: '这是页面的描述' }
	  ]
	})
}
</script>

七、错误处理

javascript 复制代码
// 全局路由错误监听
router.onError((error) => {
  console.error('路由错误:', error)
})
// 导航失败处理
router.push('/some-path').catch(err => {
  if (err.name !== 'NavigationDuplicated') {
    console.error('导航失败:', err)
  }
})

八、滚动行为

目标:路由切换时页面如何滚动。比如,当跳转到新路由时,页面滚动到某个位置;切换路由时页面回到之前的滚动位置

当创建路由实例时,我们只需要提供一个 scrollBehavior 方法:

javascript 复制代码
const router = createRouter({
  history: createWebHashHistory(),
  routes: [...],
  scrollBehavior (to, from, savedPosition) {
    // return 期望滚动到哪个的位置
  }
})

使用场景

  • 滚动到固定距离
  • 滚动到元素位置
  • 滚动到锚点位置
  • 滚动到之前的位置
  • 延迟滚动
javascript 复制代码
const router = createRouter({
  history: createWebHashHistory(),
  routes: [...],
  scrollBehavior (to, from, savedPosition) {
    //滚动到固定距离
    return { top: 0 }
    //滚动到元素位置,如始终在元素 #main 上方滚动 10px
    return {
      // 也可以这么写
      // el: document.getElementById('main'),
      el: '#main',
      top: -10,
    }
    //滚动到锚点位置
    if (to.hash) {
      return {
        el: to.hash,
      }
    }
    //滚动到之前的位置(返回 savedPosition,在按下浏览器 后退/前进 按钮,或者调用 router.go() 方法时,页面会回到之前的滚动位置)
    if (savedPosition) {
      return savedPosition
    } else {
      return { top: 0 }
    }
    //延迟滚动
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve({ left: 0, top: 0 })
      }, 500)
    })
  }
})
相关推荐
肉肉不吃 肉2 小时前
什么是闭包
前端·javascript
窝子面2 小时前
十六、按钮组件
前端
天天向上10242 小时前
vue 页面内实现el-table和div自动滚动
前端·javascript·vue.js
前端老石人2 小时前
HTML文档元素与元数据详解
前端·html
wing982 小时前
用 AI 实现图片懒加载,这也太简单了!
前端·vue.js·图片资源
sigernet2 小时前
Claude Code 不再推荐 npm 安装:官方改为 Native Installer
前端·npm·node.js
lxh01132 小时前
函数防抖题解
前端·javascript·算法
颜酱2 小时前
环检测与拓扑排序:BFS/DFS双实现
javascript·后端·算法
我发现一个问题2 小时前
node+ts+koa全栈框架学习-1
前端