vue3核心笔记

本文最后更新于 2024年12月31日 晚上

前言

最近想系统性学习一下vue,在B站看到了技术蛋老师的教学视频https://github.com/eggtoopain/vue-router-4-tutorial,受益匪浅,蛋老师提供了各个章节源码,但是没有归纳笔记,因此本人按照视频目录粗略归纳了一下内容,以便快速回顾。

创建vue应用和插值表达式

  • 以script方式引入vue.js
    1
    <script src="https://unpkg.com/vue@3"></script>
  • 创建Vue应用
    1
    2
    3
    <script>
    Vue.createApp({data(){}, methods:{}, template:''}).mount('#app')
    </script>
  • 代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>vue3核心笔记</title>
    <script src="https://unpkg.com/vue@3"></script>
    </head>
    <body>
    <div id="app">
    <h1>{{ title }}</h1>
    </div>

    <script>
    Vue.createApp({
    data() {
    return {
    title: '零食清单'
    }
    }
    }).mount('#app');
    </script>
    </body>
    </html>

v-for,v-bind,v-model用法

  • v-for:循环元素
  • v-bind:单向绑定属性data->dom,“v-bind:”可以简写为“:”
  • v-model:双向绑定data<->dom
  • 代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
        <ul>
    <li v-for="food in foods">
    <img v-bind:src="food.image">
    <span>{{ food.name }}</span>
    <input type="checkbox" v-model="food.purchased">
    <span>{{ food.purchased }}</span>
    </li>
    </ul>
    <script>
    Vue.createApp({
    data() {
    return {
    title: '零食清单',
    foods: [
    { id: 1, name: '原味鱿鱼丝', image: './images/原味鱿鱼丝.png', purchased: false },
    { id: 2, name: '辣味鱿鱼丝', image: './images/辣味鱿鱼丝.png', purchased: false },
    { id: 3, name: '炭烧味鱿鱼丝', image: './images/炭烧味鱿鱼丝.png', purchased: false }
    ]
    }
    }
    }).mount('#app');
    </script>

key,v-show,computed用法

  • key:给每一个元素添加唯一标识,避免vue对元素进行重新渲染
  • v-show:根据条件显示元素,原理是根据CSS的display属性控制元素的显示与隐藏
  • computed:计算属性,依赖于data中的数据,可以缓存计算结果,提高渲染效率,适用于高频计算的属性
  • 代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    <div id="app">
    <section v-show="beforeBuy.length"><!-- beforeBuy.length为true时显示,长度为0时隐藏 -->
    <h2>未购零食</h2>
    <ul>
    <li v-for="food in beforeBuy" :key="food.id">
    <img :src="food.image">
    <span>{{ food.name }}</span>
    <input type="checkbox" v-model="food.purchased">
    </li>
    </ul>
    </section>

    <section v-show="afterBuy.length">
    <h2>已购零食</h2>
    <ul>
    <li v-for="food in afterBuy" :key="food.id">
    <img :src="food.image">
    <span>{{ food.name }}</span>
    <input type="checkbox" v-model="food.purchased">
    </li>
    </ul>
    </section>
    </div>

    <script>
    Vue.createApp({
    data() {
    return {
    foods: [
    { id: 1, name: '原味鱿鱼丝', image: './images/原味鱿鱼丝.png', purchased: false },
    { id: 2, name: '辣味鱿鱼丝', image: './images/辣味鱿鱼丝.png', purchased: false },
    { id: 3, name: '炭烧味鱿鱼丝', image: './images/炭烧味鱿鱼丝.png', purchased: false }
    ]
    }
    },
    computed: {
    beforeBuy() {//筛选purchased为false的元素
    return this.foods.filter(item => !item.purchased);
    },
    afterBuy() {//筛选purchased为true的元素
    return this.foods.filter(item => item.purchased);
    }
    }
    }).mount('#app');
    </script>

拆组件和引用组件

  • 拆组件:将一个大的组件拆分成多个小组件,提高代码的可维护性
  • 引用组件:在其他组件中引用其他组件,提高代码的复用性
  • 步骤
    • 创建组件:在单独的文件中定义组件,使用template标签定义组件的结构,如新建components/AppSection.js文件
      1
      2
      3
      4
      5
      export default {
      template: /*html*/ ``,//在反引号前面加上/*html*/,表示该字符串为html代码,vscode中使用es6-string-html插件来高亮关键字
      data() {},
      computed: {}
      }
    • 引用组件:在其他组件中引用组件,使用template标签引用组件,由于html不区分大小写,对于驼峰式命名的组件,需要使用kebab-case命名法,如AppSections.vue文件,在html引用时写成
      1
      2
      3
      4
      5
      6
      7
      8
      import AppSections from "./AppSections.js"

      export default {
      components: { AppSections },
      template: /*html*/ `
      <app-sections></app-sections>
      `
      }
      AppSection.js
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      export default {
      template: /*html*/ `
      <section v-show="beforeBuy.length">
      <h2>未购零食</h2>
      <ul>
      <li v-for="food in beforeBuy" :key="food.id">
      <img :src="food.image">
      <span>{{ food.name }}</span>
      <input type="checkbox" v-model="food.purchased">
      </li>
      </ul>
      </section>

      <section v-show="afterBuy.length">
      <h2>已购零食</h2>
      <ul>
      <li v-for="food in afterBuy" :key="food.id">
      <img :src="food.image">
      <span>{{ food.name }}</span>
      <input type="checkbox" v-model="food.purchased">
      </li>
      </ul>
      </section>
      `,
      data() {
      return {
      foods: [
      { id: 1, name: '原味鱿鱼丝', image: './images/原味鱿鱼丝.png', purchased: false },
      { id: 2, name: '辣味鱿鱼丝', image: './images/辣味鱿鱼丝.png', purchased: false },
      { id: 3, name: '炭烧味鱿鱼丝', image: './images/炭烧味鱿鱼丝.png', purchased: false }
      ]
      }
      },
      computed: {
      beforeBuy() {
      return this.foods.filter(item => !item.purchased);
      },
      afterBuy() {
      return this.foods.filter(item => item.purchased);
      }
      }
      }
      App.js
      1
      2
      3
      4
      5
      6
      7
      8
      import AppSections from "./AppSections.js"

      export default {
      components: { AppSections },
      template: /*html*/ `
      <app-sections></app-sections>
      `
      }
      index.html
      1
      2
      3
      4
      5
      6
      7
      <div id="app"></div>

      <script type="module">
      import App from './components/App.js';

      Vue.createApp(App).mount('#app');
      </script>

props和组件复用

  • 继续拆组件,把已购零食和未购零食拆分成两个组件,两个组件不同的是标题和数组,可以使用props来传递这两个数据
  • 用props传参的方式来写子组件,子组件中规定参数类型

子组件:AppSectionsList.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export default {
template: /*html*/ `
<section v-show="buyChild.length">
<h2>{{headline}}</h2>
<ul>
<li v-for="food in buyChild" :key="food.id">
<img :src="food.image">
<span>{{ food.name }}</span>
<input type="checkbox" v-model="food.purchased">
</li>
</ul>
</section>
`,
props: {
headline: String,
buyChild: Object
}
}
  • 父组件首先import子组件,然后在template中引用子组件,并传递props
  • 父组件的computed中定义filters对象,filters对象中包含两个数组,分别为未购零食和已购零食的数组
    父组件:AppSections.js
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    import AppSectionsList from "./AppSectionsList.js";

    export default {
    components: { AppSectionsList },
    template: /*html*/ `
    <app-sections-list
    headline="未购零食"
    :buyChild="filters.beforeBuy">
    </app-sections-list>
    <app-sections-list
    headline="已购零食"
    :buyChild="filters.afterBuy">
    </app-sections-list>
    `,
    data() {
    return {
    foods: [
    { id: 1, name: '原味鱿鱼丝', image: './images/原味鱿鱼丝.png', purchased: false },
    { id: 2, name: '辣味鱿鱼丝', image: './images/辣味鱿鱼丝.png', purchased: false },
    { id: 3, name: '炭烧味鱿鱼丝', image: './images/炭烧味鱿鱼丝.png', purchased: false }
    ]
    }
    },
    computed: {
    filters() {
    return {
    beforeBuy: this.foods.filter(item => !item.purchased),
    afterBuy: this.foods.filter(item => item.purchased)
    }
    }
    }
    }
    爷爷组件:App.js
    1
    2
    3
    4
    5
    6
    7
    8
    import AppSections from "./AppSections.js"

    export default {
    components: { AppSections },
    template: /*html*/ `
    <app-sections></app-sections>
    `
    }

v-on,methods,v-if,style和class绑定事件

  • v-on:绑定事件,可以简写为@
    @submit.prevent可以阻止表单的默认事件,可以在事件处理函数中执行自定义逻辑
  • v-if:真正的条件渲染,根据条件决定是否渲染元素,可以有v-else-if、v-else
    • v-show的原理是根据CSS的display属性控制元素的显示与隐藏,不会移除dom元素
  • :class={className: true}
    1
    2
    3
    4
    5
    6
    <form @submit.prevent="add">
    <input type="text" placeholder="输入爱吃的鱿鱼丝..." v-model="newFood" />
    <button type="submit" v-if="foods.length <=3">添加</button>
    <button type="submit" :class="{buttonColor: true}" v-else-if="foods.length > 3 && foods.length < 5">再加</button>
    <button type="submit" :class="{buttonColor: true}" v-else>继续加</button>
    </form>
    AppSections.js
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    import AppSectionsList from "./AppSectionsList.js";

    export default {
    components: { AppSectionsList },
    template: /*html*/ `
    <app-sections-list headline="未购零食" :buyChild="filters.beforeBuy"></app-sections-list>
    <app-sections-list headline="已购零食" :buyChild="filters.afterBuy"></app-sections-list>

    <form @submit.prevent="add">
    <input type="text" placeholder="输入爱吃的鱿鱼丝..." v-model="newFood" />
    <button type="submit" v-if="foods.length <=3">添加</button>
    <button type="submit" :class="{buttonColor: true}" v-else-if="foods.length > 3 && foods.length < 5">再加</button>
    <button type="submit" :class="{buttonColor: true}" v-else>继续加</button>
    </form>
    `,
    data() {
    return {
    foods: [
    { id: 1, name: '原味鱿鱼丝', image: './images/原味鱿鱼丝.png', purchased: false },
    { id: 2, name: '辣味鱿鱼丝', image: './images/辣味鱿鱼丝.png', purchased: false },
    { id: 3, name: '炭烧味鱿鱼丝', image: './images/炭烧味鱿鱼丝.png', purchased: false }
    ],
    newFood: '',
    }
    },
    methods: {
    add() {
    this.foods.push({
    id: this.foods.length + 1,
    name: this.newFood,
    image: '../images/鱿鱼丝.png',
    purchased: false
    });
    this.newFood = '';
    }
    },
    computed: {
    filters() {
    return {
    beforeBuy: this.foods.filter(item => !item.purchased),
    afterBuy: this.foods.filter(item => item.purchased)
    }
    }
    }
    }
    index.html
    1
    <link rel="stylesheet" href="./style.css">
    style.css
    1
    2
    3
    .buttonColor {
    background-color: aquamarine;
    }

$emit

  • $emit 是一个实例方法,用于在子组件中触发事件,使得父组件可以监听并响应这些事件。$emit 方法的第一个参数是事件名称,随后的参数是传递给事件处理函数的数据。
  • 子组件:
    • AppSectionsFrom.js
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      export default {
      template: /*html*/ `
      <form @submit.prevent="add">
      <input type="text" placeholder="输入爱吃的鱿鱼丝..." v-model="newFood" />
      <button type="submit">添加</button>
      </form>
      `,
      data() {
      return {
      newFood: ''
      }
      },
      methods: {
      add() {
      this.$emit('addFather', this.newFood);
      this.newFood = '';
      }
      }
      }
  • 父组件
    • AppSections.js
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      import AppSectionsList from "./AppSectionsList.js";
      import AppSectionsForm from "./AppSectionsForm.js";

      export default {
      components: { AppSectionsList, AppSectionsForm },
      template: /*html*/ `
      <app-sections-list headline="未购零食" :buyChild="filters.beforeBuy"></app-sections-list>
      <app-sections-list headline="已购零食" :buyChild="filters.afterBuy"></app-sections-list>

      <app-sections-form
      @addFather="fatherAdd"
      ></app-sections-form>
      `,
      data() {
      return {
      foods: [
      { id: 1, name: '原味鱿鱼丝', image: './images/原味鱿鱼丝.png', purchased: false },
      { id: 2, name: '辣味鱿鱼丝', image: './images/辣味鱿鱼丝.png', purchased: false },
      { id: 3, name: '炭烧味鱿鱼丝', image: './images/炭烧味鱿鱼丝.png', purchased: false }
      ]
      }
      },
      methods: {
      fatherAdd(youyusi) {//youyusi参数对应子组件的newFood
      this.foods.push({
      id: this.foods.length + 1,
      name: youyusi,
      image: '../images/鱿鱼丝.png',
      purchased: false
      });
      }
      },
      computed: {
      filters() {
      return {
      beforeBuy: this.foods.filter(item => !item.purchased),
      afterBuy: this.foods.filter(item => item.purchased)
      }
      }
      }
      }

vue项目组成逻辑、文件关系和作用

vue把页面理解为一个组件树,每个组件对应一个.vue文件,组件树的根节点是App.vue文件,App.vue文件中包含了整个页面的结构和逻辑。

SFC

Vue 单文件组件(Single File Component,简称 SFC)是 Vue.js 的一个核心特性,它允许开发者将组件的模板、逻辑和样式封装在一个单独的 .vue 文件中。这种文件结构使得组件的组织和复用变得更加简单和直观。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<template>
<div class="hello">
<h1>{{ greeting }} World</h1>
</div>
</template>

<script>
export default {
data() {
return {
greeting: 'Hello'
};
}
};
</script>

<style scoped>
.hello {
color: #42b983;
}
</style>

使用 SFC 的好处:

1.组件化:每个 SFC 都是一个独立的组件,可以包含自己的模板、逻辑和样式。

2.模块化:SFC 使得组件可以被轻松地导入和导出,便于模块化开发。

3.维护性:将相关的代码组织在一起,便于维护和理解。

4.开发效率:可以在一个文件中快速地编辑模板、逻辑和样式,提高开发效率。

npm

npm(node package manager)是一个开源的包管理工具,它可以帮助我们管理和发布代码。

生成 package.json 文件:

1
npm init -y

安装vue依赖包,会生成package-lock.json文件,该文件记录了当前项目依赖的具体版本号,以便于后续项目依赖的复现,依赖全都安装在node_modules目录下。

  • package.json文件:简易包管理文件
  • package-lock.json文件:详细包管理文件
    1
    npm i vue

webpack

webpack是一个模块打包工具,它可以将多个模块按照一定规则转换成浏览器可以识别的静态资源。

1
2
3
npm i - D webpack webpack-cli webpack-dev-server //-D表示开发环境依赖,并不放到生产环境中
npm i - D babel-loader @babel/core @babel/preset-env //防止浏览器不认识ES6语法,需要用babel把代码从高版本的ES6转为低版本的ES5
npm i - D vue-loader vue-template-compiler css-loader vue-style-loader html-webpack-plugin

创建webpack.config.js文件,配置webpack的入口和出口文件,以及loader:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader');

module.exports = {
entry: './src/main.js',
module: {
rules: [
{ test: /\.vue$/, use: 'vue-loader' },
{ test: /\.css$/, use: ['vue-style-loader', 'css-loader'] },
{ test: /\.js$/, use: 'babel-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new VueLoaderPlugin()
]
}

创建.babelrc文件,配置babel的转译规则:

1
2
3
moudle.exports = {
presets: ['@babel/preset-env']
}

webpack命令简化,回到package.json文件中,添加scripts命令:

1
2
3
4
"scripts": {
"dev": "webpack-dev-server --mode development",
"build": "webpack --mode production"
}

可以使用npm run dev命令启动webpack开发环境,使用npm run build命令打包生产环境代码。

git

1
git init //初始化git仓库

新建.gitignore文件,配置不需要提交的文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
.DS_Store
node_modules
dist
npm-debug.log*
yarn-debug.log*
yarn-error.log*
/*.log
.idea
.vscode
.env.local
.env.development.local
.env.test.local
.env.production.local

# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

# System files
Thumbs.db

链接到远程仓库

1
git remote add origin https://github.com/username/project.git

提交代码

1
2
3
git add . //添加所有文件
git commit -m "提交信息" //提交代码
git push origin master //推送代码到远程仓库的master分支,本地也需要有master分支,查看分支:git branch -a,查看远程分支:git branch -r

其他

1
2
3
4
5
6
git branch -m old_name new_name //本地分支改名
git checkout -b new_branch //创建新分支
git branch -D branch_name //删除本地分支
git checkout branch_name //切换分支
git fetch //只下载远程分支的最新更改,但不会自动合并到你的本地分支,需要手动合并。
git pull //下载远程分支的最新更改,并尝试将这些更改合并到你的本地分支。相当于 git fetch 和 git merge。

jsconfig.json

jsconfig.json文件是VS Code的配置文件,它可以帮助我们在VS Code中更好地开发JavaScript项目。

1
2
3
4
5
6
7
8
9
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]//使用@符号来表示src目录
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"]
}
}

vue-cli

vue-cli是一个脚手架工具,它可以帮助我们快速搭建基于Vue的项目结构。

创建一个新项目:

1
npm i -g @vue/cli //全局安装vue-cli
1
vue create my-project //创建新项目

目录结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
├──.git/
│ └── 版本控制相关文件
├── dist/
│ ├── css/
│ │ └── 样式文件
│ └── js/
│ ├── favicon.ico
│ └── index.html
├── node_modules/
│ └── 项目依赖的 Node.js 模块
├── public/
│ ├── favicon.ico
│ └── index.html
├── src/
│ ├── assets/
│ │ └── 静态资源文件,如图片、样式等
│ │ └── logo.png
│ ├── components/
│ │ └── Vue 组件目录
│ │ └── HelloWorld.vue
│ ├── App.vue
│ └── main.js
├──.gitignore
├── babel.config.js
├── jsconfig.json
├── package-lock.json
├── package.json
├── README.md
└── vue.config.js

vue-router客户端路由的核心基础知识

这里使用vite创建项目,使用vue-cli也差不多:

1
2
npm install -g create-vite
create-vite my-vue-vite-project --template vue

cd进入项目目录安装依赖:

1
2
npm install vue-router
npm install

新建router文件夹,创建index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { createRouter,createWebHistory } from 'vue-router'
//引入组件,一般放到views文件夹下,注意相对路径
import Home from '../views/Home.vue'
import About from '../views/About.vue'
//路由比较多的时候,使用对象的方式比较好管理,可以使router对象更加简洁
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
]
const router = createRouter(
{
history: createWebHistory(),
//createWebHistory:h5历史
//createHashHistory:路由前面多一个#号
routes
}
)
//导出router对象,使得main.js文件更加简洁
export default router

新建views文件夹,创建对应组件

1
2
3
4
5
<template>
<h1>
Home
</h1>
</template>
1
2
3
4
5
<template>
<h1>
About
</h1>
</template>

App.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<nav>
<a href="/">a标签:主页</a> |
<a href="/about">a标签:关于</a>
<br>
<router-link to="/">router-link:主页</router-link> |
<router-link to="/about">router-link:关于</router-link>
<br>点击a标签页面会有新的http请求,router-link不会。

</nav>
<router-view></router-view>
<!-- 指定路由组件显示的位置 -->
</template>

main.js

1
2
3
4
5
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from './router'//不需要写index.js,会自动识别
createApp(App).use(router).mount('#app')

进阶案例

路由由数据动态决定,不写死,比如:
准备一个data.json文件,内容如下:
要把每一条数据单独做一条路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
[
{
"id": 1,
"name": "鸡蛋",
"type": "chicken-egg",
"image": "chicken-egg.jpeg",
"description": "又名鸡卵、鸡子,是母鸡所产的卵",
"flavour": "味甘,性平,无毒(煮熟后)"
},
{
"id": 2,
"name": "鸭蛋",
"type": "duck-egg",
"image": "duck-egg.jpeg",
"description": "又名鸭子、鸭卵、太平、鸭春、青皮等,为鸭科动物家鸭的卵,受精卵可孵化成小鸭",
"flavour": "性涼、味甘"
},
{
"id": 3,
"name": "鹅蛋",
"type": "goose-egg",
"image": "goose-egg.jpeg",
"description": "家禽鹅生下的卵",
"flavour": "有些油"
},
{
"id": 4,
"name": "鹌鹑蛋",
"type": "quail-egg",
"image": "quail-egg.jpeg",
"description": "鵪鶉所產的卵,蛋殼表面帶有棕褐色斑點",
"flavour": "味甘、性平"
},
{
"id": 5,
"name": "笨蛋",
"type": "dumb-egg",
"image": "dumb-egg.jpeg",
"description": "我才不是笨蛋",
"flavour": "没吃过"
}
]

详细的逻辑请看注释****详细的逻辑请看注释
App.vue中使用v-for添加路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<template>
<nav>
<a href="/">a标签:主页</a> |
<a href="/about">a标签:关于</a>
<br>
<router-link to="/">router-link:主页</router-link> |
<router-link to="/about">router-link:关于</router-link>
<br>点击a标签页面会有新的http请求,router-link不会。
<br>下面是动态的路由:
<br>
<router-link
v-for="item in dataEggs"
:key="item.id"
:to="`/eggs/${item.type}`"
>
{{ item.name }} |
</router-link>

</nav>
<router-view></router-view>
<!-- 指定路由组件显示的位置 -->
<button @click="backward">退后</button>
<button @click="forward">前进</button>
</template>

<script>
import dataEggs from './data.json'
export default {
data() {
return {
dataEggs//ES6语法
}
},
methods: {
backward() {
this.$router.go(-1)//查看历史操作
},
forward() {
this.$router.go(1)
}
}
}
</script>

<style scoped>
.egg-active {
color: red;
}
</style>

router/index.js中配置路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { createRouter,createWebHistory } from 'vue-router'
//引入组件,一般放到views文件夹下,注意相对路径
import Home from '../views/Home.vue'
import About from '../views/About.vue'
// import Eggs from '../views/Eggs.vue'//引入路由组件
// import NotFound from '../views/NotFound.vue'//引入404页面

//路由比较多的时候,使用对象的方式比较好管理,可以使router对象更加简洁
const routes = [
{ path: '/', component: Home },
{ path: '/about', component: About },
// 动态路由:eggType是自定义属性名,跟data.json属性名无关
{ path: '/eggs/:eggType', component: () => import('../views/Eggs.vue') },//碰到/eggs路径的时候应用Eggs动态路由组件,细节参数eggType,不可以写成egg-type,js中不允许用-
// () => import('../views/Eggs.vue')这种写法是懒加载页面,只有当访问到该路由的时候才会加载,可以提高页面加载速度
// 也可以给路由添加别名 { path: '/eggs/:eggType', name: 'Eggs', component: () => import('../views/Eggs.vue') },
// 然后App.vue里可以用<router-link :to="{ name: 'Eggs', params: { eggType: item.type } }">去跳转到Eggs页面
{ path: '/eggs', redirect: '/eggs/chicken-egg' },//重定向
{ path: '/:pathMatch(.*)*', component: () => import('../views/NotFound.vue') },//匹配所有路径,如果都不匹配,就渲染NotFound组件

]
const router = createRouter(
{
history: createWebHistory(),
//createWebHistory:h5历史
//createHashHistory:路由前面多一个#号
routes,
linkActiveClass: 'egg-active'//路由激活的class,App.vue里定义的
}
)
//导出router对象,使得main.js文件更加简洁
export default router

Egg.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<template>
<div v-if="!dataEgg">
<h1>查无此蛋</h1>
</div>
<div v-else>
<h1>{{ dataEgg.name }}</h1>
<p>{{ dataEgg.description }}</p>
<p>{{ dataEgg.flavour }}</p>
<!-- <img :src="`../../src/assets/images/${dataEgg.image}`"> -->
</div>
</template>

<script>
import dataEggs from '../data.json'
export default {
computed: {
eggType() {
return this.$route.params.eggType
//this.$route 是一个包含当前路由信息的对象,它提供了许多有用的属性
},
dataEgg() {
return dataEggs.find(
dataEgg => dataEgg.type === this.eggType
)
}
}
}
</script>

<style scoped>
img {
width: 150px;
height: 150px;
}
</style>

其他:views/NotFound.vue

1
2
3
4
5
<template>
<h1>
404
</h1>
</template>

vue3核心笔记
https://xinhaojin.github.io/2024/12/31/vue3核心笔记/
作者
xinhaojin
发布于
2024年12月31日
更新于
2024年12月31日
许可协议