单文件组件

Vue 单文件组件(又名 *.vue 文件,缩写为 SFC)是一种特殊的文件格式,它允许将 Vue 组件的模板、逻辑 样式封装在单个文件中。下面是 SFC 示例:

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

<template>
  <p class="greeting">{{ greeting }}</p>
</template>

<style>
.greeting {
  color: red;
  font-weight: bold;
}
</style>

Vue SFC 是经典的 HTML、CSS 与 JavaScript 三个经典组合的自然延伸。每个 *.vue 文件由三种类型的顶层代码块组成:<template><script><style>

  • <script> 部分是一个标准的 JavaScript 模块。它应该导出一个 Vue 组件定义作为其默认导出。

  • <template> 部分定义了组件的模板。

  • <style> 部分定义了与此组件关联的 CSS。

  • SFC 语法规范

工作原理

Vue SFC 是框架指定的文件格式,必须由 @vue/compiler-sfc 预编译为标准的 JavaScript 与 CSS。编译后的 SFC 是一个标准的 JavaScript(ES)模块

[!note]

我感觉就是一个一个的组件打包在一块,要用其他组件就引入就行了

如果要引入其他组件,用法如下:

import MyComponent from './MyComponent.vue'

export default {
  components: {
    MyComponent
  }
}

SFC 中的 <style> 标签通常在开发过程中作为原生 <style> 标签注入以支持热更新。对于生产环境,它们可以被提取并合并到单个 CSS 文件中

工具

在线演练场

你不需要在你的机器上安装任何东西来尝试 Vue 单文件。这里有很多在线演练场允许你在浏览器中运行:

在报告问题时也建议通过这些在线演练场来提供复现。

项目脚手架

Vue CLI 是 Vue 官方基于 webpack 的构建工具。可以通过 Vue CLI 进行使用:

创建后的项目配置可以参考:https://cli.vuejs.org/zh/config/

npm install -g @vue/cli
vue create hello-vue

image-20220613161821462

[!tip]

浏览一下创建的目录,结合之前的一些知识,一目了然

目录说明

git(隐藏文件)              =》git init
node_modules                 =》项目本地所有依赖的包文件
public                      =》本地服务的文件夹
        |index.html            =》主页
src                          =》工作目录
        |assets              =》资源文件(图片、css)
        |components       =》组件
        |App.Vue          =》跟组件
        |main.js          =》项目的全局配置
.gitignore                  =》不需要上传到仓库中的文件的配置
babel.config.js              =》有关bable的配置
package.json              =》项目基本配置说明
package-lock.json         =》版本范围
README.md                  =》说明文件

IDE支持

VSCode插件 + Volar扩展

多版本nodejs

因为我电脑有gitbook,需要nodejs版本为v10.24.1,但是vue cli有需要12以上的,所以需要多版本共存

参考:在 MacOS 上如何管理多个 Node 版本

# nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash

# node安装
nvm install node # "node" is an alias for the latest version
nvm install 6.14.4 # or 10.10.0, 8.9.1, etc
nvm install 14 # 长期维护版本

# 列出已安装的版本
nvm ls

# 设置当前node版本
nvm alias default stable # 最新稳定版
nvm alias default 14  # 指定版本

# 不同node版本切换
nvm use 14

路由

简单路由

如果你只需要非常简单的路由而不想引入一个功能完整的路由库,可以像这样动态渲染一个页面级的组件:

[!note]

简而言之,就是定义几个组件,然后在不同的路由的时候就切换到对应的组件,有点麻烦

<html>
<head>
  <script type="text/javascript" src="./vue.global.js"></script>
</head>

<body>
  <div id="test">
 </div>

    <script type="text/javascript">
      const { createApp, h } = Vue
      // 定义3个组件
      const NotFoundComponent = { template: '<p>Page not found</p>' }
      const HomeComponent = { template: '<p>Home page</p>' }
      const AboutComponent = { template: '<p>About page</p>' }

      // 定义路由
      const routes = {
      '/': HomeComponent,
      '/about': AboutComponent
    }
    // 定义根组件
    const SimpleRouter = {
      data: () => ({
        currentRoute: window.location.pathname
      }),

      computed: {
        CurrentComponent() {
          return routes[this.currentRoute] || NotFoundComponent
        }
      },

      render() {
        return h(this.CurrentComponent)
      }
    }

    createApp(SimpleRouter).mount('#test')
    </script>
</body>
</html>

官方路由

对于大多数单页面应用,都推荐使用官方支持的 vue-router 库。更多细节可以移步 vue-router 文档

  • Via CDN: <script src="https://unpkg.com/vue-router@4"></script>

  • In-browser playground on CodeSandbox

  • Add it to an existing Vue Project:

      npm install vue-router@4
    

当加入 Vue Router 时,我们需要做的就是将我们的组件映射到路由上,让 Vue Router 知道在哪里渲染它们

[!note]

官方教程没有用脚手架,是单独的,获取一个app应用,然后app.use(router);考虑到后期基本都是脚手架模式来开发,所以我这里就用脚手架的例子来记录

HTML部分说明

<div id="app">
  <h1>Hello App!</h1>
  <p>
    <!--使用 router-link 组件进行导航 -->
    <!--通过传递 `to` 来指定链接 -->
    <!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
    <router-link to="/">Go to Home</router-link>
    <router-link to="/about">Go to About</router-link>
  </p>
  <!-- 路由出口 -->
  <!-- 路由匹配到的组件将渲染在这里 -->
  <router-view></router-view>
</div>

[!tip]

用来跳转路由的,to属性是路由的位置

没有使用常规的 a 标签,而是使用一个自定义组件 router-link 来创建链接。这使得 Vue Router 可以在不重新加载页面的情况下更改 URL,处理 URL 的生成以及编码。

router-view

[!tip]

用来展示路由对应组件内容的

router-view 将显示与 url 对应的组件内容。你可以把它放在任何地方,以适应你的布局。

创建带有路由的脚手架项目

vue create hello-vue
cd hello-vue
vue add router

这样就是一个带有路由的脚手架项目了

image-20220614101522185

编写过程

我感觉主要有4步

  1. components中编写组件
  2. views中导入显示组件(简单的情况下可以和上面的合并成一个步骤)
  3. router/index.js中添加路由
  4. 对应的展示界面添加 <router-link to=>

动态路由

带参数的动态路由

就是动态参数映射到一个路由上,比如User/aaaUser/bbb都映射到User/:name上,再根据对应的用户名返回对应的结果;这种在路径中使用一个动态字段来实现,我们称之为 路径参数

路径参数 用冒号 : 表示。当一个路由被匹配时,它的 params 的值将在每个组件中以 this.$route.params 的形式暴露出来。因此,我们可以通过更新 User 的模板来呈现当前的用户 ID:

  • vue文件
<template>
  <div class="home">
    Now param {{ $route.params }} <br/>
    Now name {{ $route.params.name }}
  </div>
</template>

<script>
export default {
  name: 'HomeTest',
}
</script>
  • 路由
{
    path: '/test/:name',
    name: 'hometest',
    component: TestView
  }

访问 /test/aaa

image-20220614104759074

可以在同一个路由中设置有多个 路径参数,它们会映射到 $route.params 上的相应字段。例如:

匹配模式 匹配路径 $route.params
/users/:username /users/eduardo { username: 'eduardo' }
/users/:username/posts/:postId /users/eduardo/posts/123 { username: 'eduardo', postId: '123' }

除了 $route.params 之外,$route 对象还公开了其他有用的信息,如 $route.query(如果 URL 中存在参数)、$route.hash 等。你可以在 API 参考中查看完整的细节。

响应路由参数的变化

上面那个动态路由,每一次访问后,比如从aaabbb,都会重新渲染一次,相同的组件实例将被重复使用,造成了一定情况下资源的浪费;

所以有了另一种方法,不需要销毁了再创建,而是直接监听变化,这样效率会更高。不过,这也意味着组件的生命周期钩子不会被调用

要对同一个组件中参数的变化做出响应的话,你可以简单地 watch $route 对象上的任意属性,在上面的场景中,就是 $route.params

<template>
  <div class="home">
    Now param {{ $route.params }} <br/>
    Now name {{ $route.params.name }}
  </div>
</template>

<script>
export default {
  name: 'HomeTest',
  watch: {
    '$route'(to, from) {
      console.log(to)
      console.log(from)
    }
  }
}
</script>

[!warning]

触发的话,需要用鼠标点击跳转过去才会触发,直接改浏览器的URL地址是不会有效果的

捕获所有路由或 404 Not found 路由

如果我们想匹配任意路径,我们可以使用自定义的 路径参数 正则表达式,在 路径参数 后面的括号中加入 正则表达式 :

  • 路由JS
{
    path: '/:pathMatch(.*)*',
    name: '404',
    component: NotFound
  },
参数中定义正则

假如存在2个路由,/:orderId/:productName,两者会匹配完全相同的 URL,所以我们需要一种方法来区分它们(不考虑改变路由的情况)

情况分析:orderId只能是数字,productName可以是任何值

解决方法:

{
    path: '/test/:name',
    name: 'hometest',
    component: TestView
  },
  {
    path: '/test/:id(\\d+)',
    name: 'numberPath',
    component: HomeView
  },

如果访问/test/123那么就会使用组件HomeView

[!note]

确保转义反斜杠( \ ),就像我们对 \d (变成\\d)所做的那样,在 JavaScript 中实际传递字符串中的反斜杠字符。

匹配多部分路由

如果你需要匹配具有多个部分的路由,如 /first/second/third,你应该用 *(0 个或多个)和 +(1 个或多个)将参数标记为可重复:

const routes = [
  // /:chapters ->  匹配 /one, /one/two, /one/two/three, 等
  { path: '/:chapters+' },
  // /:chapters -> 匹配 /, /one, /one/two, /one/two/three, 等
  { path: '/:chapters*' },
]

也可以通过在右括号后添加它们与自定义正则结合使用

const routes = [
  // 仅匹配数字
  // 匹配 /1, /1/2, 等
  { path: '/:chapters(\\d+)+' },
  // 匹配 /, /1, /1/2, 等
  { path: '/:chapters(\\d+)*' },
]
严格匹配

默认情况下,所有路由是不区分大小写的,并且能匹配带有或不带有尾部斜线的路由。例如,路由 /users 将匹配 /users/users/、甚至 /Users/。这种行为可以通过 strictsensitive 选项来修改,它们可以既可以应用在整个全局路由上,又可以应用于当前路由上:

const router = createRouter({
  history: createWebHistory(),
  routes: [
    // 将匹配 /users/posva 而非:
    // - /users/posva/ 当 strict: true
    // - /Users/posva 当 sensitive: true
    { path: '/users/:id', sensitive: true },
    // 将匹配 /users, /Users, 以及 /users/42 而非 /users/ 或 /users/42/
    { path: '/users/:id?' },
  ]
  strict: true, // applies to all routes
})
可选参数

可以通过使用 ? 修饰符(0 个或 1 个)将一个参数标记为可选:

const routes = [
  // 匹配 /users 和 /users/posva
  { path: '/users/:userId?' },
  // 匹配 /users 和 /users/42
  { path: '/users/:userId(\\d+)?' },
]

请注意,* 在技术上也标志着一个参数是可选的,但 ? 参数不能重复。

嵌套路由

上面的都是定义好了所有的路由,可能一个user路由下面有多个其他的路由,比如user/infouser/name等,这样每一个都单独写一次/user会很麻烦,所以也就有了嵌套

  • TestView.vue文件
<template>
  <div class="home">
    Now param {{ $route.params }} <br/>
    Now name {{ $route.params.name }}
    <router-view></router-view>>
  </div>
</template>

<script>
export default {
  name: 'HomeTest',
}
</script>
  • 路由
{
    path: '/test/:name',
    name: 'hometest',
    component: TestView,
    children: [
      {
        path: 'child',
        name: 'home1',
        component: HomeView // HomeView会被渲染到TestView的 <router-view> 中
      },
    ]
  },
  • 效果

image-20220614115113543

编程式导航

导航到其他路径

在 Vue 实例中,你可以通过 $router 访问路由实例。因此你可以调用 this.$router.push

当你点击 <router-link> 时,内部会调用这个方法,所以点击 <router-link :to="..."> 相当于调用 router.push(...)

声明式 编程式
<router-link :to="..."> router.push(...)

简单举例如下:

<template>
  <div class="home">
    Now param {{ $route.params }} <br/>
    Now name {{ $route.params.name }}
    <a @click="test" href="#">123</a>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'HomeTest',
  methods: {
    test() {
      this.$router.push({ path: '/test/555' })
    }
  },
}
</script>

点击123那么就会跳转到/test/555

其他的一些常规用法:

// 字符串路径
router.push('/users/eduardo')

// 带有路径的对象
router.push({ path: '/users/eduardo' })

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

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

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

[!note]

如果提供了 pathparams 会被忽略,上述例子中的 query 并不属于这种情况。

由于属性 torouter.push 接受的对象种类相同,所以两者的规则完全相同。

替换当前位置

它的作用类似于 router.push,唯一不同的是,它在导航时不会向 history 添加新记录,正如它的名字所暗示的那样——它取代了当前的条目。

声明式 编程式
<router-link :to="..." replace> router.replace(...)

也可以直接在传递给 router.pushrouteLocation 中增加一个属性 replace: true

router.push({ path: '/home', replace: true })
// 相当于
router.replace({ path: '/home' })
前后跳转

就是对于历史记录的前进和后退,类似于 window.history.go(n)

例子

// 向前移动一条记录,与 router.forward() 相同
router.go(1)

// 返回一条记录,与 router.back() 相同
router.go(-1)

// 前进 3 条记录
router.go(3)

// 如果没有那么多记录,静默失败
router.go(-100)
router.go(100)

命名路由

前面跳转到一个路由,都是通过路径path来,比如/test/123,其实vue也提供了name参数来支持,优点如下:

  • 没有硬编码的 URL
  • params 的自动编码/解码。
  • 防止你在 url 中出现打字错误。
  • 绕过路径排序(如显示一个)

假设一个路由如下:

{
    path: '/test/:name',
    name: 'hometest',
    component: TestView,
  },

那么我们可以用如下的方式来:

<template>
  <nav>
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link> |
    <router-link :to="{name: 'hometest', params: {name: 123}}">Test</router-link>
  </nav>
  <router-view></router-view>
</template>

image-20220614122156107

router.push()一样

router.push({ name: 'hometest', params: { name: '123' } })

命名视图(多个组件)

有时候需要同级展示多个组件视图,而不是嵌套展示,但是通常只有一个<route-view>出口,这个时候命名视图派上用场了

<router-view></router-view>
<router-view name="HomeView"></router-view>

如上编写视图展示的地方,路由如下:

{
    path: '/test/:name',
    name: 'hometest',
    components: {
      default: TestView, 
      HomeView // HomeView: HomeView 的缩写
    }
  },

这样HomeView组件就会渲染到对应的地方了

image-20220614123552948

嵌套命名视图和嵌套路由类似,只要对应的组件有输出点即可。

重定向和别名

重定向

还是通过设置router来完成

{
    path: '/test/:name',
    name: 'hometest',
    components: {
      default: TestView, 
      HomeView // HomeView: HomeView 的缩写
    },
    redirect: "/"
  },

访问/test/xxx会直接跳转到/

同理,也可以重定向到一个命名的路由

{
    path: '/test/:name',
    name: 'hometest',
    components: {
      default: TestView, 
      HomeView // HomeView: HomeView 的缩写
    },
    redirect: {
      name: "home"
    }
  },

甚至返回一个方法

{
    path: '/test/:name',
    name: 'hometest',
    components: {
      default: TestView, 
      HomeView // HomeView: HomeView 的缩写
    },
    redirect: 
      to => {
        return {
          path: '/about',
          query: {
            q: to.params.name
          }
        }
      }
  },
别名

重定向是指当用户访问 /home 时,URL 会被 / 替换,然后匹配成 /

/ 别名为 /home,意味着当用户访问 /home 时,URL 仍然是 /home,但会被匹配为用户正在访问 /

{
    path: '/test/:name',
    name: 'hometest',
    components: {
      default: TestView, 
      HomeView // HomeView: HomeView 的缩写
    },
    alias: "/aaa/:name",
  },

如上,访问/aaa/xxx就相当于访问/test/xxx,且路由显示为/aaa/xxx

image-20220614140305505

props传参

之前在用组件嵌套的时候,通过props传参的,这里仍然可用

props 设置为 true 时,route.params 将被设置为组件的 props。

  • TestView.vue
<template>
  <div class="home">
    Now name {{ name }}
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'HomeTest',
  props: ['name'],
}
</script>
  • 路由
{
    path: '/bbb/:name',
    name: 'hometest2',
    component: TestView,
    props: true
  },
  • 效果

image-20220614141635080

API备忘参考

Copyright © d4m1ts 2023 all right reserved,powered by Gitbook该文章修订时间: 2022-06-18 10:44:41

results matching ""

    No results matching ""