跳到主要内容

VueJS脚手架

单文件组件

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)模块

备注

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

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

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

提示

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

目录说明

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

路由

简单路由

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

备注

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

<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 知道在哪里渲染它们

备注

官方教程没有用脚手架,是单独的,获取一个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>
提示

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

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

router-view
提示

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

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>

注意

触发的话,需要用鼠标点击跳转过去才会触发,直接改浏览器的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

备注

确保转义反斜杠( \ ),就像我们对 \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' })
备注

如果提供了 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备忘参考