恬宝乖乖 發表於 2022-1-4 20:40:00

vue3+TypeScript+vue-router使用

<h1 id="简单使用">简单使用</h1>
<h2 id="创建项目">创建项目</h2>
<h3 id="vue-cli创建">vue-cli创建</h3>
<pre><code class="language-shell">$npm install -g @vue/cli
$vue --version
@vue/cli 4.5.15
$vue create my-project
</code></pre>
<p>然后的步骤:</p>
<ol>
<li>Please pick a preset<br>
选择 Manually select features</li>
<li>Check the features needed for your project<br>
选择上<code>TypeScript</code>,特别注意点空格是选择,点回车是下一步</li>
<li>Choose a version of Vue.js that you want to start the project with<br>
选择 3.x (Preview)</li>
<li>Use class-style component syntax<br>
直接回车</li>
<li>Use Babel alongside TypeScript<br>
直接回车</li>
<li>Pick a linter / formatter config<br>
直接回车</li>
<li>Use history mode for router?<br>
直接回车</li>
<li>Pick a linter / formatter config<br>
直接回车</li>
<li>Pick additional lint features<br>
直接回车</li>
<li>Where do you prefer placing config for Babel, ESLint, etc.?<br>
直接回车</li>
<li>Save this as a preset for future projects?<br>
直接回车</li>
</ol>
<p>文件结构:</p>
<pre><code class="language-bash">my-project
+--- babel.config.js
+--- package-lock.json
+--- package.json
+--- public
|   +--- favicon.ico
|   +--- index.html
+--- README.md
+--- src
|   +--- App.vue
|   +--- assets
|   |   +--- logo.png
|   +--- components
|   |   +--- HelloWorld.vue
|   +--- main.ts
|   +--- shims-vue.d.ts
+--- tsconfig.json
+--- node_modules
|   +--- ...
</code></pre>
<blockquote>
<p>入口文件为<code>src/main.ts</code></p>
</blockquote>
<h3 id="vite创建">vite创建</h3>
<p>执行以下命令创建项目</p>
<pre><code class="language-shell">$npm init vite-app &lt;project-name&gt;
$cd &lt;project-name&gt;
$npm install
$npm run dev
</code></pre>
<p>文件结构:</p>
<pre><code class="language-bash">project-name
+--- index.html
+--- package-lock.json
+--- package.json
+--- public
|   +--- favicon.ico
+--- src
|   +--- App.vue
|   +--- assets
|   |   +--- logo.png
|   +--- components
|   |   +--- HelloWorld.vue
|   +--- index.css
|   +--- main.js
+--- node_modules
|   +--- ...
</code></pre>
<blockquote>
<p>入口文件为<code>src/main.ts</code></p>
</blockquote>
<p><strong>注意: 由于使用vite方法创建的项目没有vue的声明文件</strong>, 所以需要我们自定义, 否则会报错.<br>
<code>src/shims-vue.d.ts</code></p>
<pre><code class="language-typescript">/* eslint-disable */
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent&lt;{}, {}, any&gt;
export default component
}
</code></pre>
<h2 id="安装vue-router">安装<code>vue-router</code></h2>
<pre><code class="language-shell">$npm install vue-router@4
</code></pre>
<p>至此, <code>package.json</code>如下:</p>
<pre><code class="language-json">{
"name": "my-project",
"version": "0.1.0",
"private": true,
"scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
},
"dependencies": {
    "core-js": "^3.6.5",
    "vue": "^3.0.0",
    "vue-router": "^4.0.12"
},
"devDependencies": {
    "@typescript-eslint/eslint-plugin": "^4.18.0",
    "@typescript-eslint/parser": "^4.18.0",
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-plugin-typescript": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "@vue/compiler-sfc": "^3.0.0",
    "@vue/eslint-config-typescript": "^7.0.0",
    "eslint": "^6.7.2",
    "eslint-plugin-vue": "^7.0.0",
    "typescript": "~4.1.5"
}
}
</code></pre>
<h2 id="创建修改组件">创建/修改组件</h2>
<ol>
<li>
<p>创建<code>src/router/index.ts</code></p>
<pre><code class="language-typescript">import { createRouter, createWebHashHistory } from "vue-router"

import Home from '../components/Home.vue'
import About from '../components/About.vue'
import User from '../components/User.vue'

const routes = [
        // router参数详细看下文
        {
                path: "/home",
                name: "home",
                component: Home
        },
        {
                path: "/about",
                name: "about",
                component: About
        },
        {
                path: "/user/:uid",// 动态参数
                name: "user",
                component: User
        }
]
export const router = createRouter({
        history: createWebHashHistory(),
        routes: routes
})
</code></pre>
</li>
<li>
<p>创建组件: <code>Home.vue</code> <code>About.vue</code> <code>User.vue</code><br>
<code>src/components/Home.vue</code></p>
<pre><code class="language-javascript">&lt;template&gt;
&lt;div&gt;home组件&lt;/div&gt;
&lt;/template&gt;

&lt;script lang="ts"&gt;
import { defineComponent } from "vue";

export default defineComponent({
name: "Home",
setup() {
        return {
          // 返回的数据
        };
},
});
&lt;/script&gt;
</code></pre>
<p><code>src/components/About.vue</code></p>
<pre><code class="language-javascript">&lt;template&gt;
&lt;div&gt;About组件&lt;/div&gt;
&lt;/template&gt;

&lt;script lang="ts"&gt;
import { defineComponent } from "vue";

export default defineComponent({
name: "About",
setup() {
        return {
          // 返回的数据
        };
},
});
&lt;/script&gt;

</code></pre>
<p><code>src/components/User.vue</code></p>
<pre><code class="language-javascript">&lt;template&gt;
&lt;div&gt;User组件&lt;/div&gt;
&lt;/template&gt;

&lt;script lang="ts"&gt;
import { defineComponent } from "vue";

export default defineComponent({
name: "User",
setup() {
        return {
          // 返回的数据
        };
},
});
&lt;/script&gt;
</code></pre>
</li>
<li>
<p>修改<code>App.vue</code></p>
<pre><code class="language-javascript">&lt;template&gt;
&lt;div&gt;{{ appMessage }}&lt;/div&gt;
&lt;!-- router-link会被渲染成a标签 --&gt;
&lt;router-link to="/home"&gt;home&lt;/router-link&gt;
&lt;router-link to="/about"&gt;about&lt;/router-link&gt;
&lt;router-link to="/user/lczmx"&gt;user&lt;/router-link&gt;

&lt;!-- 路由出口 --&gt;
&lt;!-- 路由匹配到的组件将渲染在这里 --&gt;
&lt;router-view&gt;&lt;/router-view&gt;
&lt;/template&gt;

&lt;script lang="ts"&gt;
import { defineComponent } from "vue";

export default defineComponent({
name: "App",
setup() {
        const appMessage = "App组件";
        return {
          // 返回的数据
          appMessage,
        };
},
});
&lt;/script&gt;
&lt;style&gt;
/* 添加样式 */
#app {
text-align: center;
margin-top: 50px;
}
a {
margin: 30px;
display: inline-block;
}
&lt;/style&gt;
</code></pre>
</li>
</ol>
<h2 id="修改入口ts">修改入口ts</h2>
<p>修改<code>src/main.ts</code>:</p>
<pre><code class="language-typescript">import { createApp } from 'vue'
import App from './App.vue'
import './index.css'

import { router } from './router'

// 创建应用 返回对应的实例对象
const app = createApp(App)

// 安装 vue-router 插件
app.use(router)
// 调用mount方法
app.mount('#app')
</code></pre>
<h2 id="启动vue">启动vue</h2>
<pre><code class="language-shell">$npm run serve

&gt; my-project@0.1.0 serve
&gt; vue-cli-service serve

INFOStarting development server...
98% after emitting CopyPlugin

DONECompiled successfully in 6387ms                                                                                             下午4:14:30

App running at:
- Local:   http://localhost:8080/
- Network: http://192.168.43.12:8080/

Note that the development build is not optimized.
To create a production build, run npm run build.

No issues found.
</code></pre>
<h2 id="在浏览器中访问">在浏览器中访问</h2>
<p>根据提示, 访问<code>http://localhost:8080/</code>, 如下图</p>
<p><img src="https://gitee.com/lczmx/Note/raw/master/pictures/vue3+TypeScript+vue-router%E4%BD%BF%E7%94%A8/%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8.gif" alt="简单使用" loading="lazy"></p>
<h2 id="文件结构图片">文件结构图片</h2>
<p><img src="https://gitee.com/lczmx/Note/raw/master/pictures/vue3+TypeScript+vue-router%E4%BD%BF%E7%94%A8/1639988588679.png" alt="文件结构" loading="lazy"></p>
<h1 id="综合使用">综合使用</h1>
<h2 id="动态参数">动态参数</h2>
<p>假如我们需要的路由是: <code>/user/lczmx</code>和<code>/user/jack</code>, 但是我们明显不可能为这两个路由定义两个不同的组件, 最好的方法就是使用动态参数:</p>
<pre><code class="language-javascript">const routes = [
// 动态段以冒号开始
{ path: '/users/:id', component: User },
// 使用正则表达式`()` 里面的东西会传给前面的pathMatch
// 值在route.params.pathMatch下
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
]
</code></pre>
<p>匹配时, 会将参数映射到<code>router</code>实例的<code>currentRoute.value.params</code>上</p>
<blockquote>
<p>注意vue2中: 由于在<code>setup</code>无法使用<code>this.$route</code>和<code>this.$router</code><br>
至于如何获取, 看我的另一篇博客: vue3获取当前路由 和 官网: Vue Router 和 组合式 API</p>
</blockquote>
<p>匹配列表</p>
<table>
<thead>
<tr>
<th>匹配模式</th>
<th>匹配路径</th>
<th>当前路由的参数</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>/users/:username</code></td>
<td><code>/users/eduardo</code></td>
<td><code>{ username: 'eduardo' }</code></td>
</tr>
<tr>
<td><code>/users/:username/posts/:postId</code></td>
<td><code>/users/eduardo/posts/123</code></td>
<td><code>{ username: 'eduardo', postId: '123' }</code></td>
</tr>
</tbody>
</table>
<p>在使用<strong>带有参数</strong>的路由时需要注意: 由于相同的组件实例将被重复使用,所以组件的生命周期钩子不会被调用<br>
但是我们可以对路由进行监听</p>
<h3 id="使用watch监听动态参数">使用watch监听动态参数</h3>
<p>修改<code>src/components/User.vue</code>:</p>
<pre><code class="language-javascript">&lt;template&gt;
&lt;div&gt;User组件&lt;/div&gt;
&lt;p&gt;当前用户: {{ uid }}&lt;/p&gt;

&lt;router-link to="/user/lczmx"&gt;lczmx&lt;/router-link&gt;
&lt;router-link to="/user/jack"&gt;jack&lt;/router-link&gt;
&lt;/template&gt;

&lt;script lang="ts"&gt;
import { defineComponent, watch, ref } from "vue";
import { useRouter } from "vue-router";

export default defineComponent({
name: "User",
setup() {
    const router = useRouter();
    const uid = ref(router.currentRoute.value.params.uid);
    watch(
      // 监听非响应式数据
      () =&gt; router.currentRoute.value,
      (val) =&gt; {
      // 修改uid
      uid.value = val.params.uid;
      }
    );
    return {
      // 返回的数据
      uid,
    };
},
});
&lt;/script&gt;
</code></pre>
<p><img src="https://gitee.com/lczmx/Note/raw/master/pictures/vue3+TypeScript+vue-router%E4%BD%BF%E7%94%A8/%E5%8A%A8%E6%80%81%E5%8F%82%E6%95%B0.gif" alt="监听动态参数" loading="lazy"></p>
<h3 id="使用组合api监听动态参数">使用组合API监听动态参数</h3>
<p>https://next.router.vuejs.org/zh/guide/advanced/composition-api.html</p>
<h2 id="重定向">重定向</h2>
<p>下面使用<code>router</code>的全部参数:</p>
<pre><code class="language-javascript">const routes = [
    {
      path: "/",
      // 写法1 写死url
      // redirect: "/home", // 访问 "/" 时 跳转到 "/home"

      // 写法2 跳转到对应的命名路由
      redirect: { name: "home" },

      // 写法3 定义一个方法
                // 该方法亦可以 返回一个相对路径
      /*
      redirect: to =&gt; {
            // 方法接收目标路由作为参数 "to"

            // return 重定向的字符串路径/路径对象
                       
                        // query指定参数
            return { path: '/home', query: { q: to.params.searchText } }
      },
      */
    },
    {
      path: "/home",
      name: "home",
      component: Home
    }
]

</code></pre>
<blockquote>
<p>注意, 重定向不会触发 导航守卫</p>
</blockquote>
<blockquote>
<p>另附官网的例子: Named Views - Vue Router 4 examples</p>
</blockquote>
<h2 id="命名与别名">命名与别名</h2>
<h3 id="命名路由">命名路由</h3>
<blockquote>
<p>给路由一个名称, 可以在其他路由中使用, 如: <code>redirect</code>和<code>router-link</code></p>
</blockquote>
<pre><code class="language-javascript">const routes = [
{
    path: '/user/:username',
    name: 'user',
    component: User
}
]

</code></pre>
<p>在<code>redirect</code>的使用如上文, 而<code>router-link</code>如下:</p>
<pre><code class="language-javascript">&lt;template&gt;
&lt;div&gt;User组件&lt;/div&gt;
&lt;p&gt;当前用户: {{ uid }}&lt;/p&gt;

&lt;router-link :to="{ name: 'user', params: { uid: 'lczmx' } }"
    &gt;lczmx&lt;/router-link
&gt;
&lt;router-link :to="{ name: 'user', params: { uid: 'jack' } }"
    &gt;jack&lt;/router-link
&gt;
&lt;/template&gt;
</code></pre>
<p>在<code>router.push</code>(<code>router</code>是<code>router</code>对象)中使用:</p>
<pre><code class="language-javascript">router.push({ name: 'user', params: { uid: 'lczmx' } })
</code></pre>
<h3 id="命名视图">命名视图</h3>
<blockquote>
<p>即, 我们可以<code>router-view</code>定义一个名字, 已达到实现可复用的效果<br>
我们可以使用这个功能实现 一个侧边栏等</p>
</blockquote>
<p>举个例子</p>
<ol>
<li>定义路由:<pre><code class="language-javascript">import { createRouter, createWebHashHistory } from "vue-router"

import Home from '../components/Home.vue'
import About from '../components/About.vue'
import User from '../components/User.vue'

const routes = [
        {
                path: "/",
                components: {
                        default: Home,// 默认用Home组件
                        a: About,// a用About组件
                        b: User,// b用User组件
                },

        },
        {
                path: "/home",
                components: {
                        default: About,   // 默认用About组件
                        a: Home,// a用Home组件
                        b: User,// b用User组件
                },

        },
]


export const router = createRouter({
        history: createWebHashHistory(),
        routes: routes
})
</code></pre>
</li>
<li>修改<code>App.vue</code><pre><code class="language-javascript">&lt;template&gt;
&lt;div&gt;{{ appMessage }}&lt;/div&gt;

&lt;!-- router-link会被渲染成a标签 --&gt;
&lt;router-link to="/"&gt;/&lt;/router-link&gt;
&lt;router-link to="/home"&gt;/home&lt;/router-link&gt;

&lt;!-- 路由出口 --&gt;
&lt;!-- 路由匹配到的组件将渲染在这里 --&gt;
&lt;!-- default --&gt;
&lt;router-view&gt;&lt;/router-view&gt;
&lt;router-view name="about"&gt;&lt;/router-view&gt;
&lt;router-view name="user"&gt;&lt;/router-view&gt;
&lt;/template&gt;

&lt;script lang="ts"&gt;
import { defineComponent } from "vue";

export default defineComponent({
name: "App",
setup() {
        const appMessage = "App组件";
        return {
          // 返回的数据
          appMessage,
        };
},
});
&lt;/script&gt;
&lt;style&gt;
/* 添加样式 */
#app {
text-align: center;
margin-top: 50px;
}
a {
margin: 30px;
display: inline-block;
}
&lt;/style&gt;
</code></pre>
</li>
<li>其他组件<br>
<code>About.vue</code>:<pre><code class="language-javascript">&lt;template&gt;
&lt;div&gt;about组件&lt;/div&gt;
&lt;/template&gt;
</code></pre>
<code>Home.vue</code>:<pre><code class="language-javascript">&lt;template&gt;
&lt;div&gt;home组件&lt;/div&gt;
&lt;/template&gt;
</code></pre>
<code>User.vue</code><pre><code class="language-javascript">&lt;template&gt;
&lt;div&gt;user组件&lt;/div&gt;
&lt;/template&gt;
</code></pre>
</li>
<li>启动服务并访问vue<br>
如图:<br>
<img src="https://gitee.com/lczmx/Note/raw/master/pictures/vue3+TypeScript+vue-router%E4%BD%BF%E7%94%A8/%E5%91%BD%E5%90%8D%E8%A7%86%E5%9B%BE.gif" alt="命名视图在浏览器中测试" loading="lazy"></li>
</ol>
<blockquote>
<p><strong>假如不指定视图名, 那么为<code>default</code></strong></p>
</blockquote>
<h3 id="别名">别名</h3>
<blockquote>
<p>可以实现 不同url 访问同一路由的效果</p>
</blockquote>
<pre><code class="language-javascript">const routes = [
    // 可以访问 "/home" 也可以访问 "/"
    // 且访问的路径不会改变
    {
      path: "/home",
      name: "home",
      component: Home,
      alias: "/"
    }
</code></pre>
<h2 id="嵌套路由">嵌套路由</h2>
<p>之前我们在<code>App.vue</code>中定义<code>router-view</code>, 让其他组件在哪里渲染<br>
但假如我们需要在其他组件中渲染的话, 就需要嵌套路由了</p>
<blockquote>
<p>使用<code>children</code>嵌套路由, 它的值是路由数据, 就好像外部的<code>router</code>那样定义</p>
</blockquote>
<p>例子:</p>
<ul>
<li>
<p><code>router.index.ts</code></p>
<pre><code class="language-javascript">import { createRouter, createWebHashHistory } from "vue-router"

import Home from '../components/Home.vue'
import About from '../components/About.vue'
import User from '../components/User.vue'
import UserHome from '../components/UserHome.vue'
import UserSettings from '../components/UserSettings.vue'
import UserProfile from '../components/UserProfile.vue'

const routes = [
        // 可以访问 "/home" 也可以访问 "/"
        // 且访问的路径不会改变
        {
                path: "/home",
                name: "home",
                component: Home,
                alias: "/"
        },
        {
                path: "/about",
                name: "about",
                component: About
        },
        {
                path: "/user/:uid",// 动态参数
                name: "user",
                component: User,// 内部有router-view渲染要嵌套的路由
                children: [
                        // 匹配形如 /user/lczmx 的url
                        { path: "", component: UserHome },

                        // 匹配形如 /user/lczmx/settings 的url
                        { path: "settings", component: UserSettings, name: "user-settings" },

                        // 匹配形如 /user/lczmx/profile 的url
                        { path: "profile", component: UserProfile, name: "user-profile" }
                ]
        }
]


export const router = createRouter({
        history: createWebHashHistory(),
        routes: routes
})

</code></pre>
<blockquote>
<p>注意: 假如<code>children</code>中没有<code>path: ""</code>的话, 那么访问<code>/user/lczmx</code>, 只能得到一个页面空白</p>
</blockquote>
</li>
<li>
<p><code>User.vue</code></p>
<pre><code class="language-javascript">&lt;template&gt;
&lt;div&gt;
        &lt;router-link :to="{ name: 'user-settings' }"&gt;settings&lt;/router-link&gt;
        &lt;router-link :to="{ name: 'user-profile' }"&gt;profile&lt;/router-link&gt;
&lt;/div&gt;

&lt;router-view&gt;&lt;/router-view&gt;
&lt;/template&gt;
</code></pre>
</li>
<li>
<p><code>UserHome.vue</code></p>
<pre><code class="language-javascript">&lt;template&gt;
&lt;div&gt;用户主页&lt;/div&gt;
&lt;/template&gt;
</code></pre>
</li>
<li>
<p><code>UserProfile.vue</code></p>
<pre><code class="language-javascript">&lt;template&gt;
&lt;div&gt;用户详细信息页面&lt;/div&gt;
&lt;/template&gt;

</code></pre>
</li>
<li>
<p><code>UserSettings.vue</code></p>
<pre><code class="language-javascript">&lt;template&gt;
&lt;div&gt;用户设置页面&lt;/div&gt;
&lt;/template&gt;
</code></pre>
</li>
<li>
<p>启动并访问<br>
在浏览器中测试:<br>
<img src="https://gitee.com/lczmx/Note/raw/master/pictures/vue3+TypeScript+vue-router%E4%BD%BF%E7%94%A8/%E5%B5%8C%E5%A5%97%E8%B7%AF%E7%94%B1%E6%B5%8B%E8%AF%95.gif" alt="嵌套路由测试" loading="lazy"></p>
</li>
</ul>
<h2 id="编程式路由">编程式路由</h2>
<p>即不通过a标签, 而是通过<code>js/ts</code>改变路由, 原理是<strong>向<code>history</code>栈添加一个新的记录</strong><br>
在vue3中, 有以下写法</p>
<pre><code class="language-javascript">&lt;template&gt;
&lt;div&gt;about组件&lt;/div&gt;
&lt;button @click="changeRouter"&gt;修改路由&lt;/button&gt;
&lt;/template&gt;


&lt;script lang="ts"&gt;
import { defineComponent } from "vue";

import { useRouter } from "vue-router";

export default defineComponent({
name: "About",
setup() {
    // 获得router对象
    const router = useRouter();

    const changeRouter = () =&gt; {
      /* 修改路由的例子 */

      // 1 字符串路径
      router.push("/users/lczmx");

      // 2 带有路径的对象
      router.push({ path: "/users/lczmx" });

      // 3 命名的路由,并加上参数,让路由建立 url
      router.push({ name: "user", params: { username: "lczmx" } });

      // 4 带查询参数,结果是 /register?plan=private
      router.push({ path: "/register", query: { plan: "private" } });

      // 5 带 hash,结果是 /about#team
      router.push({ path: "/about", hash: "#team" });

      // 6 我们可以手动建立 url,但我们必须自己处理编码
      const username = "lczmx";
      router.push(`/user/${username}`); // -&gt; /user/lczmx
      // 同样
      router.push({ path: `/user/${username}` }); // -&gt; /user/lczmx
      // 如果可能的话,使用 `name` 和 `params` 从自动 URL 编码中获益
      router.push({ name: "user", params: { username } }); // -&gt; /user/lczmx

      // 7 `params` 不能与 `path` 一起使用, 否则 `params` 将会被忽略
      router.push({ path: "/user", params: { username } }); // -&gt; /user

      // 8 replace为true 不向history 中添加
      router.push({ path: "/home", replace: true });
      // 等同于
      router.replace({ path: "/home" });

      // 9 横跨历史
      // 向前移动一条记录,与 router.forward() 相同
      router.go(1);
      // 返回一条记录,与router.back() 相同
      router.go(-1);
      // 前进 3 条记录
      router.go(3);
      // 如果没有那么多记录,静默失败
      router.go(-100);
      router.go(100);
    };
    return {
      // 返回的数据
      changeRouter,
    };
},
});
&lt;/script&gt;

&lt;style&gt;
button {
margin: 30px;
}
&lt;/style&gt;
</code></pre>
<blockquote>
<p>更多见vue-router4官网: Vue Router</p>
</blockquote>


</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自博客园,作者:403·Forbidden,转载请注明原文链接:https://www.cnblogs.com/lczmx/p/15763999.html</p><br><br>
来源:https://www.cnblogs.com/lczmx/p/15763999.html
頁: [1]
查看完整版本: vue3+TypeScript+vue-router使用