Vue的使用

一、简介

一套用于构建用户界面的渐进式框架。

核心思想是数据驱动:数据映射图,数据的修改会自动引起试图的变化。

双向数据绑定:数据改变会引起试图的变化,试图的变化也会引起数据的变化,这就是双向数据绑定。

二如何理解Vue渐进式框架

Vue框架生态本身是包含很多东西的:

  1. 核心
    1. 声明式渲染
    2. 组件系统
  1. vue-router (路由)
  2. vuex (状态管理)
  3. 构建系统
    1. webpack
    2. vue-cli
    3. vite

一开始不必使用上框架的全部东西,根据需要自行选择即可。

安装

可百度Vue官网Vue.js

 Vue的基本使用

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <!-- 
    
    1. 定义一个挂载点
    2. 引入 Vue js
    3. 实例化 Vue

   -->

  <div id="app">
    <h1>Hello Vue</h1>
    <p>My name is {{ name }}</p>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script>
  <script>
    var vm = new Vue({
      el: '#app',

      data: {
        name: '张三'
      }
    })
  </script>
</body>
</html>

声明式渲染

<div id="app">
  {{ message }}
</div>

<script>
    var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})
</script>

 表达式渲染

   <h2>{{ msg + name }}</h2> //hello帅威
   <h2>{{ age + 10 }}</h2> //28
   <h2>{{ age > 18 ? '大于' : '不大于' }}</h2> //不大于
   <h2>{{ msg.split('').reverse().join('') }}</h2> //olleh

  <script>
    new Vue({
      // 配置对象  api 选项

      // 主要的两个 el  data
      el: '#app',
      data: {
        // 数据存放的位置 key: value
        msg: 'hello',
        name: '帅威',
        age: 18,
        sex: '男'
      }
    })

    // new Vue({
    //   el: '#root',
    //   data: {
    //     msg: 'world'
    //   }
    // })
  </script>

可以在#app:标签里面使用Vue 

小问题:在我们使用开饭版Vue是控制台会多出提示 这是因为我们是在开发模式中运行Vue

解决这个小提示

   <script>
    // 阻止提示
    Vue.config.productionTip = false
   </script>

Vue常用的指令

 什么是指令:

本质就是Vue自定义的标签属性

列:格式以 "v-" 开头的,例如:v-text v-html v-if v-for v-show v-bind

  作用:

当表达式的值改变时,将其产生的连带影响,响应式的作用于DOM。

指令:v-text

v-text: 将数据的值绑定到标签内,innerText

指令: v-html:

作用:将数据绑定到元素的内容上,innerHTML

区别:

能不能渲染html标签的区别。v-html可以,v-text不可以

注意事项:

1. 一般不要使用 v-html 。因为你不知道数据是什么内容,可能会产生 xss(跨站脚本攻击(也称为XSS)指利用网站漏洞从用户那里恶意盗取信息。) 攻击。

 2. 只有在你明确的知道这个数据是可以信任的时候,才去使用 v-html。

指令:v-bind

作用:将数据动态绑定到标签的属性上。

可以动态改变标签里面的属性值

v-bind 简写:

简写方式:

v-bind:xxx  => :xxx

问题:通过插值表达式的语法使用的时候,页面在初始化时有一个闪烁的效果

 现象的原因:

1. 网络问题,主要原因

2. 实例化也需要时间,次要原因

指令:v-cloak

作用:解决闪烁的问题

方法:

        1. 将元素使用 v-cloak 指令,这个指令不需要绑定数据

        2. 需要设置样式

          [v-cloak] {

            display: none

          }

 条件渲染

v-if

v-if="xxx" xxx 是一个数据,定义在 data 中的数据 key

v-else

1. 没有值,参考 v-cloak

2. 需要配套着  v-if v-else-if 去使用

3. 跟着 v-if 或者  v-else-if 的下一个兄弟元素

v-else-if

1. 有值,v-else-if="xxxx"

2. 需要配置着  v-if 使用

3. 跟着 v-if 或者  v-else-if 的下一个兄弟元素

v-show

1. 没有配套的,是个孤儿

2. v-show="xxx"

v-if 与 v-show的区别

 1. v-if 有配套的 v-else v-else-if。 而 v-show 没有

2. v-if 才是真正的条件渲染,条件为 true, 元素渲染,否则元素直接给干掉了

3. v-show 只是简单的控制的元素样式,display 来实现显示隐藏的。

4. 一般来说,当一个元素会频繁的切换显示隐藏的时候,推荐使用 v-show 。性能更好,节省了元素渲染的开销

5. 否则一般使用 v-if 。 当vue初始化完成之后,初始数据为 false 的时候性能更好。

6. v-if 可以配合 template 元素去使用,而 v-show 不可以。

因为 v-show 要控制元素,而template并没有生成dom元素

template 元素 模板

是 vue 实现的一个自带的 组件(自定义的标签)

作用:做包裹使用

特点:vue 渲染的时候,不会生成 真实的 dom 节点

 需要代码整洁高效,又需要不影响到现有的布局与样式时,就可以使用

v-for 循环

v-for="xx in yy"

yy 是数据,一般是个数组格式

xx 是循环这个 yy 的时候的每一项

v-for=”(item, index) in list”

list 是数据,一般是个数组格式

item 是数据中的每一个项目

index 是下标

v-for="(value, key, index) in obj" 对象格式

obj 是对象格式

value 是 key:value中的value

 key 是 key:value中的key

index 是 下标

key的使用

当使用 v-for 时,必须要跟着一个动态的 key 属性

1. key 不能写死,需要保证循环的每一项都是一个唯一的key

2. key 一般使用数据的 id ,不推荐使用 index 下标

v-for时,为什么需要使用key。 diff 算法

1. vue 在数组更新的时候,为了更高效的去复用之前已经渲染出来的 dom需要给他提供一个位置的标记,就是key

2. 为什么不推荐 index 下标呢,因为当使用下标的时候,没有高效一说

不用key: 没法根据特有的属性去判断是否需要重新创建标签。

当v-if 和v-for同时使用时

v-on 指令

v-on:xxx="yyy"

xxx 是指令的参数

click

 dbclick

...

yyy 是指令的值,这块一般是个函数

1. 可以直接对现有数据做操作   num++

2. 使用 methods 选项中定义的 函数

v-on简写: @

Vue组件注意事项

  1. 组件没有 el 选项。因为后续调用组件在哪里,这个组件的挂载点就是哪里
  2. 必须有 template 或者 render 选项,用来规定组件的模板内容
  3. data 选项必须是一个函数返回对象的形式
  4. 组件名不能是现有的 html 标签名,也不能是其他已经注册过的组件名
  5. 全局注册组件时,必须要放置在 new Vue 之前
  6. 组件名可以使用短横线写法与驼峰写法。但是调用组件时需要使用短横线写法。下列三种情况下可以不听这个规则
    1. 使用 template 字符串模板
    2. 使用 <script type="text/x-template"></script>
    3. 使用 .vue 后缀的单文件组件中时
  1. 组件的 template 模板,必须只能有一个根元素。
  2. prop的名字可以是用短横线与驼峰写法。但是调用组件时设置这个prop的时候,需要使用短横线写法。下列三种情况下可以不听这个规则
    1. 使用 template 字符串模板
    2. 使用 <script type="text/x-template"></script>
    3. 使用 .vue 后缀的单文件组件中时     
  1. prop是不允许修改,应为要遵循单向数据流这个规定
  2. 父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
  3. 调用组件时,写在组件标签内的内容,默认是不会被渲染的

事件修饰符

vue给我们的事件做了一些方便我们使用的小功能。比如快速阻止默认行为,快速阻止事件冒泡

v-on:xx事件.yyy  => .yyy 就是修饰符

.prevent  阻止默认行为    (常用)

.stop     阻止冒泡        (常用)

.capture  将事件触发模式,修改为捕获阶段,(不常用)

.once     一次,只触发一次事件,触发完之后就不生效了 (不常用)

.self     自身,触发事件的元素与绑定事件的元素 target 一致时,才触发(不常用)

.passive  明确告诉浏览器,我绝对不会去阻止你的默认行为。(常用于移动端长列表滚动时)

绑定按键键盘触发事件

1. 绑定键盘事件,使用keyup

2. 只有回车键keyup的才触发? 就需要用到按钮修饰符 .enter

v-on:xxx 绑定xxx事件

xxx:

onclick => click

onkeyup => keyup

onblur  => blur

onfoucs => foucs

....

计算属性

1. 写在哪里,computed 选项中

2. vue有个建议,不要将复杂的逻辑写到模板中(html)

3. 技术属性中使用到的数据,可以理解为是这个计算属性的依赖数据

计算属性的特点:

 1. 当依赖发生变化的时候,计算属性的函数会重新执行。效果是页面自动更新

2. 依赖数据没有发生变化,函数不会重新执行,有缓存。

计算属性一般情况下不能修改

可以通过 get可以获取属性

set可以设置属性

watch

作用:监听数据的变化,然后去干些事情

当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

生命周期

1. 在不同的阶段做一些事情。

2. vue会在一些阶段完成的时候,主动帮我们触发一些函数。

3. 这些函数就叫生命周期函数、生命周期回调函数、生命周期钩子函数

vue2.x 中有 8 个生命周期钩子函数

三个阶段

1. 挂载阶段

beforeCreate  

created         (常用)

        1. 初始数据请求

        beforeMount

        mounted         (常用)

  •                 1. 初始数据请求
  •                 2. 绑定全局事件
  •                 3. 定时器
  •                 4. 操作真实DOM (实在没办法的时候使用)

2. 更新阶段

beforeUpdate

updated

3. 销毁阶段

beforeDestroy   (常用)

  • 1. 收尾工作
  • 2. 解绑一些全局事件
  • 3. clear清理工作

destroyed

生命周期图例:

组件的使用

Vue中组件是可复用的 Vue 实例,且带有一个名字(组件名)。

因为组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 datacomputedwatchmethods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项

组件使用分三个步骤:

1. 定义组件

 Vue.extend

2. 注册组件

        - 全局注册:Vue.component(组件名称, 第一步中定义的组件)

3. 使用组件

        将第二步中的组件名称,拿到页面中当成自定义标签使用即可。

组件作用

  • 组件:用来实现局部功能效果的代码集合(html、css、js)。
  • 组件化:当应用中的页面功能都是组件的方式来编写的,那这个应用就可以叫做一个组件化的应用。
  • 他们的作用:
    1. 避免变量污染
    2. 提供代码的复用率
    3. 提高代码的可维护性
    4. 依赖关系清晰

组件的定义:

const person = Vue.extend({
  // options
  template: '<div>我是组件</div>'
})

// 简写方式,直接写对象
const person = {
  // options
  template: '<div>我是组件</div>'
}

组件定义的两种方式:

 1. template定义组件模板

template: `
        <div>
            <h1>姓名:张三</h1>
            <p>年龄:18</p>
        </div>
    `,

2.render 定义组件模板  createElmenet  document.createElmenet 

        1. render 需要定义成一个函数 

        2. render 函数会接受一个 createElement 的参数,这个参数也是一个函数

        3. 需要 return createElment 创建的虚拟元素(节点)     

        4. 这里的 createElmenet 返回是 虚拟DOM VDOM

        5. 每个节点叫做,虚拟节点 VNode

 return createElement("元素名称", "元素属性", "元素内容");

template与render的区别  

1. template 更人性化,render 使用太麻烦了。

2. 作用都一样

3. 我们推荐使用 template

4. 其实 我们使用 template Vue 内容会将 template 的内容转换成render

注意事项

  • 组件没有 el 选项。因为后续这个组件在那里调用就挂载在那里。
  • 必须提供 template 选项 或 render 选项,设置组件的模板内容。
  • Vue2.x 中,模板内容必须是单个根元素。
  • data 选项必须是一个函数,返回一个对象的格式。 因为组件是要复用的,避免 data 数据存在引用关系。

组件的注册

1.全局注册 - 使用 Vue.component 方法

Vue.component(componentName, {
  template: `组件模板内容`,
  data() {
    return {
      // 组件的data数据
    }
  }
})

2.局部注册 - 使用 components 配置项

new Vue({
  /** ... */
  components: {
    // key: value
    // 		key - 组件名
    //    value - 定义的组件
  },
});

注意事项

  • 组件名不能使用现有的 html 标签名,或已经注册的组件名。
  • 可以配置 name 选项,指定组件在开发者工具中显示的名字。
  • 全局注册组件时,必须要放置在 new Vue 之前。
  • 组件名可以使用 kebab-case(短横线写法)PascalCase(大驼峰写法)
    • 但是调用组件时需要使用短横线写法。下列三种情况下例外:
      • 使用 template 字符串模板
      • 使用 <script type="text/x-template"></script>
      • 使用 .vue 后缀的单文件组件中时

组件的使用

将注册好的组件名,当做自定义标签使用即可。

组件的prop

prop传递值

 1. 将组件当成函数来看

2. props 选项定义的是函数的形参

2.1 定义了的形参,可不可以不传递

3. data 选项定义的数据是函数自身的数据

注意事项

1. props 需要首先在 组件中通过 props 选项定义

2. props 选项支持 [] 简单格式写法。还有 {} 的写法

  ['name', 'age']
            {
                'name': String // String|Number|Function|Boolean|Object|Symbol|Date|Array
                'age': {
                    type: String // String|Number|Function|Boolean|Object|Symbol|Date|Array
                    required: true|false
                    default: ''
                }
            }

3. 设置 prop 默认值的时候,如果默认值是一个对象或者数组格式,不能直接写,需要使用 函数返回的方式。

4. prop的名字

4.1 name     单个单词

4.2 myName   小驼峰

注意: 当使用小驼峰时,模板中传递prop的时候,需要转换成 短横线写法。跟组件名的规则类似,同样在下列三种情况下可以不转换:

1. template 字符串

 2. <script type="text/x-template" id="xxx"></script>

3. 后缀名为 .vue 的单文件组件中

5. 组件接收到的prop不允许修改

1. 遵循单向数据流这个规则

如何修改prop

组件接受到 prop 不允许修改,但是可以找到数据提供方,让它去修改

触发自定义事件  原生 click input blur

 this.$emit(eventName, payload)

eventName - 自定义事件的名称: 比如 myClick

 payload   - 携带的一些参数

配置着 v-on 在 组件标签上 绑定这个自定义事件

自定义事件的 $event 这个变量的值,不是事件对象,而是触发这个事件时,传递的 payload

使用VueCLI脚手架

初始化脚手架

说明

  1. Vue 脚手架是 Vue 官方提供的标准化开发工具(开发平台)
  2. 最新的版本是 4.0.8
  3. 文档:Vue CLI

具体步骤

  1. 如果下载缓慢请配置 npm 淘宝镜像:npm config set registry http://registry.npm.taobao.org
  2. 全局安装@vue/cli:npm install -g @vue/cli
  3. 切换到你要创建项目的目录,然后使用命令创建项目:vue create xxxx
  4. 选择使用vue的版本
  5. 启动项目:npm run serve
  6. 暂停项目:Ctrl+C

脚手架的结构
 

.文件目录
├── node_modules 
├── public
│   ├── favicon.ico: 页签图标
│   └── index.html: 主页面
├── src
│   ├── assets: 存放静态资源
│   │   └── logo.png
│   │── component: 存放组件
│   │   └── HelloWorld.vue
│   │── App.vue: 汇总所有组件
│   └── main.js: 入口文件
├── .gitignore: git版本管制忽略的配置
├── babel.config.js: babel的配置文件
├── package.json: 应用包配置文件 
├── README.md: 应用描述文件
└── package-lock.json: 包版本控制文件

render函数

import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
    el:'#app',
    // 简写形式
	render: h => h(App),
    // 完整形式
	// render(createElement){
	//     return createElement(App)
	// }
})

总结:

1.vue.js 与 vue.runtime.xxx.js的区别:      

  1. vue.js 是完整版的 Vue,包含:核心功能+模板解析器
  2. vue.runtime.xxx.js 是运行版的 Vue,只包含核心功能,没有模板解析器

2.因为 vue.runtime.xxx.js 没有模板解析器,所以不能使用 template 配置项,需要使用 render函数接收到的createElement 函数去指定具体内容

非prop属性

 组件是个标签,标签就可以传递属性,如果传递的属性,在组件内部没有定义为prop.那么这些属性叫做非prop的属性, 非prop特性

非prop的属性有特点

1. 会继承到组件的根元素身上

2. 这些继承的效果是替换的. class与style例外

3. 这些属性可以在组件内部通过 $attrs 去获取到 class与style例外

4. 有些时候,可能不希望它自动继承到根元素身上. 在组件内, 配置inheritAttrs: false,

禁用非prop属性的继承

inheritAttrs: false, 

注意:class style除外

自定义事件

$attrs      接受到非prop特性,class与style除外 {}

$listeners  接受到绑定的事件 {}

v-model默认属性

  model: {
    prop: "checked", // v-model 的属性是 xxx  ,默认是 value
    event: "change", // v-model 的事件是 xxx, 默认是 input
  },

在组件上改变自定义组件为原生事件

 1. 组件上绑定的事件都是自定义事件,

2. 如果需要绑定原生事件,需要添加一个修饰符 .native (原生)

3. 加上 .native 绑定的原生事件,默认会绑定到组件的根元素身上。

 组件之间通信的方式,有哪些 (七大)

prop     父传递数据给子组件  

$emit    子组件触发自定义事件  

兄弟组件: 数据提升,提升到相同的父组件身上

v-model: 父子双向  

.sync 修饰符:父子双向

$refs    父子

$parent  子父

$root    根组件

 $children  所有子组件的集合[]

provide/inject  层级很深的时候

事件总线 EventBus

终极解决方案:状态管理器(Vuex)

案例:A组件要通知B组件

B组件在 created 中使用 eventBus 绑定一个事件

$on(eventName, () => {})

A组件在 合适的时机 使用 eventBus 触发B组件中绑

定的那个事件名 $emit(eventName)

        

 .sync 修饰符 是 v-bind 使用的,在满足某种条件的时候可以简写成 .sync 修饰符的形式

vue-router(vue路由器)

vue 的一个插件,专门用来实现基于 vue 的 单页面应用(SPA)。

1、单页面应用 与 多页面应用

1.SPA 单页面应用(Single Page Application)

概念:基于前端路由而起,整个网站只有一个HTML页面。通过监听地址栏中的变化,实现页面的局部内容更新,同时支持浏览器的前进与后退操作。

2.MPA 多页面应用(Multi Page Application)

概念:整个网站有多个HTML页面,页面跳转需要整页整体刷新。

2、路由的理解

1、什么是路由(route)

  1. 路由的本质就是一种映射关系(key - value)
  2. 根据不同的url请求,返回对应不同的资源。那么url地址和真实的资源之间就有一种对应的关系,这种对应的关系就是路由

2、什么是路由器(router)

  1. 管理路由的东西

3、路由的分类

后端路由

  1. 概念:根据不同的URL请求,返回不同的内容
  2. 本质:URL请求地址服务器资源之间的对应关系

前端路由

  1. 概念:监听URL地址的变化,显示不同的页面元素组件
  2. 本质:URL请求地址组件之间的对应关系

vue-router 的使用步骤

1. 项目必须安装 vue-router

2. 引入 vue-router 插件

3. 注册它 Vue.use(VueRouter)

4. 定义 路由规则

5. 实例化 路由对象

6. 将实例化的路由对象配置到 new Vue 的 router 选项上。

7. 在 App.vue 中放一个坑用于承载路由组件 <router-view>

// 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)

// 1. 定义 (路由) 组件。
// 可以从其他文件 import 进来
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }

// 2. 定义路由
// 每个路由应该映射一个组件。 其中"component" 可以是
// 通过 Vue.extend() 创建的组件构造器,
// 或者,只是一个组件配置对象。
// 我们晚点再讨论嵌套路由。
const routes = [
  { path: '/foo', component: Foo },
  { path: '/bar', component: Bar }
]

// 3. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({
  routes // (缩写) 相当于 routes: routes
})

// 4. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
const app = new Vue({
  router
}).$mount('#app')

// 现在,应用已经启动了!

// 不要忘了在页面中放置一个 <router-view /> 用来渲染路径匹配到的(路由)组件

路由组件

根路由打交道,且是某个path路径对应关系的组件,咱们就叫它路由组件

1. 不需要手动使用

2. 它会自动渲染到   路由视图组件(router-view)  中。

3. 在合适的时机,某个路由组件就会渲染到 router-view这个坑中

(路由组件)可以放在views文件夹中

注意:这个 views 文件夹,还可以换成 pages

vue-router 的安装

使用脚手架创建的老项目vue add router

新项目创建时:脚手架勾选上

导航的两种方式

声明式:  router-link 组件

使用的是 a 标签方式 \ router-link 组件方式 这两种方式可以叫做 声明式导航(声明式跳转\a标签跳转)

样式设置:

   router-link 高亮效果,借助与它自带的一个 router-link-active 类名 这个类名会自动添加或移除      

编程式 

用js控制跳转的方式,叫做 编程式导航(编程式跳转\js跳转)

 this.$router.push(增加)

      历史记录的默认模式, 追加模式
      [
        '#/',
        '#/home',
        '#/about'
      ]

this.$router.replace(替换)

      历史记录的替换模式
      [
        '#/',
        '#/home'   push
        '#/about'
      ]

默认模式: / -> home -> about

替换模式: / -> about

replace 模式

- 默认: 每次跳转都添加了一个历史记录

- replace: 当前的历史会被覆盖掉

替换会把之前访问的路径替换掉

    <!-- 
      声明式导航 replace
     -->
    <RouterLink to="/home">首页</RouterLink>
    <RouterLink to="/about" replace>关于页面</RouterLink>
    <!-- 
      编程式导航 replace
     -->
    <button @click="$router.push('/home')">首页</button>
    <button @click="$router.replace('/about')">关于页面</button>

命名路由

在路由规则集合中加上name

    声明式导航 使用 name 的跳转  :to="{ name: 'xxx' }"

    <RouterLink to="/home">首页</RouterLink>
    <RouterLink :to="{ name: 'abc' }">首页</RouterLink>
    <RouterLink :to="{ name: 'about' }">关于页面</RouterLink>

     编程式导航 使用 name 的跳转 { name: 'xxx' }

    <button @click="$router.push('/home')">首页</button>
    <button @click="$router.push({ name: 'abc' })">首页</button>
    <button @click="$router.replace({ name: 'about' })">关于页面</button>

定义路由规则时,多配置一个 name 选项, 后续跳转就可以直接应用这个 name 选项.

路由传递参数

 路由传参 1. 路径后面通过?号传递参数

<router-link :to="`/detail?id=${item.id}&name=${item.name}`">

路由传参 2. 对象形式,通过 query 属性传递

      <li v-for="item in goodList" :key="item.id">
        <!-- 
          路由传参 1. 路径后面通过?号传递参数
         -->
        <router-link :to="`/detail?id=${item.id}&name=${item.name}`">
          {{ item.name }}
        </router-link>

        <!-- 
          路由传参 2. 对象形式,通过 query 属性传递
         -->
        <router-link
          :to="{
            name: 'detail',
            query: {
              id: item.id,
              name: item.name,
            },
          }"
        >
          {{ item.name }}
        </router-link>

        <div @click="goDetail(item.id, item.name)">{{ item.name }}</div>
      </li>

路由传参 3. 字符串

        <router-link :to="`/detail/${item.id}/${item.name}`">

          {{ item.name }}

        </router-link>

路由传参 4 对象形式,通过 params 属性传递

        <router-link
          :to="{
            name: 'detail',
            params: {
              id: item.id,
              name: item.name
            },
          }"
        >
          {{ item.name }}
        </router-link>

动态路由匹配

- 占坑

- ?   - 可有可无

- *   - 放到最后一个,一般用来做404页面

通配符 * : 不管路径是什么,都可以匹配这条 * 的规则。

需要放置在最后一条规则,相当于兜底。

动态路由适用场景

比如写用户的信息页面时,页面的结构都一样,只是用户的名称不一样,这个时候就可以使用动态路由

路由规则的props

注意: props 需要你在组件中通过 props 选项先定义,不然这里传递过去的,组件中将是 $attrs 接收到的。

props 选项的 第一种使用方式  布尔值

props: true,

props 选项的 第二中使用方式 对象式, 静态传递属性给当前规则的组件

      props: {
        a: '10',
        b: '20',
        c: true,
        id: 100,
      },

props 选项的 第三种使用方式 函数式

      props: function (route) {
        console.log('====', route)
        return {
          // 期望属性 a 是变化的 id 而不是写死的 10
          // a: '10',
          // b: '20'

          // a: route.params.id

          // 满足页面需要的代码
          id: route.params.id,
          name: route.params.name
        }
      },

总结:

果参数传递了太多的话,为了省去 this.$route.params 或者 this.$route.query 的多次编写.

三种写法

  •  布尔写法 props: true  参数传递了什么,组件的$attrs中就有什么.如果组件配置了props选项,就在props中
  • 对象写法 props: {}    对象里面写什么,组件的$attrs中就有什么.如果组件配置了props选项,就在props中
  • 函数写法 props: function(route) { return {} } 返回的对象里面写什么,组件的$attrs中就有什么.如果组件配置了props选项,就在props中

一样不怎么使用,因为工作中,一般只需要一个id的参数,然后通过id调用接口获取更多的数据.

组件复用问题

当详情页面组件创建完成之后,拿到id去获取后端的接口数据

猜你喜欢案例时,组件会被复用,导致生命周期的钩子函数不会重复执行。带来的问题是:页面数据会有问题。

解决方案:

监听$route

  watch: {
    // 第一种方案:监听 $route
    $route() {
      this.getData();
    },
  },

二种方案:使用 beforeRouteUpdate 钩子函数 导航守卫

  beforeRouteUpdate(to, from, next) {
    console.log("to: ", to);
    console.log("from: ", from);
    // to -> 去哪里
    // from -> 从哪里来的
    // next -> 是一个函数,如果不执行这个函数,将不会放行....
    this.getData();
    next();
  },

总结:

/detail/1  => /detail/2   这时使用的都是 HelloDetail.vue 这个组件,为了高效,这个组件会被复用,也就是生命周期的钩子函数不会重复执行.

带来的问题是: ajax获取数据的代码不会被执行.页面会渲染出错误的内容

解决方案:

1. 监听 $route

2. 使用 beforeRouteUpdate 这个 导航守卫, to from next一定要执行 next()

路由的嵌套

实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件,例如:

 借助 vue-router,使用嵌套路由配置,就可以很简单地表达这种关系。

接着上节创建的 app:

<div id="app">
  <router-view></router-view>
</div>
const User = {
  template: '<div>User {{ $route.params.id }}</div>'
}

const router = new VueRouter({
  routes: [{ path: '/user/:id', component: User }]
})

这里的 <router-view> 是最顶层的出口,渲染最高级路由匹配到的组件。同样地,一个被渲染组件同样可以包含自己的嵌套 <router-view>。例如,在 User 组件的模板添加一个 <router-view>

const User = {
  template: `
    <div class="user">
      <h2>User {{ $route.params.id }}</h2>
      <router-view></router-view>
    </div>
  `
}

要在嵌套的出口中渲染组件,需要在 VueRouter 的参数中使用 children 配置:

const router = new VueRouter({
  routes: [
    {
      path: '/user/:id',
      component: User,
      children: [
        {
          // 当 /user/:id/profile 匹配成功,
          // UserProfile 会被渲染在 User 的 <router-view> 中
          path: 'profile',
          component: UserProfile
        },
        {
          // 当 /user/:id/posts 匹配成功
          // UserPosts 会被渲染在 User 的 <router-view> 中
          path: 'posts',
          component: UserPosts
        }
      ]
    }
  ]
})

要注意,以 / 开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径

你会发现,children 配置就是像 routes 配置一样的路由配置数组,所以呢,你可以嵌套多层路由。

重定向

重定向也是通过 routes 配置来完成

重定向的目标也可以是一个命名的路由

甚至是一个方法,动态返回重定向目标

注意导航守卫并没有应用在跳转路由上,而仅仅应用在其目标上。在下面这个例子中,为 /a 路由添加一个 beforeEnter 守卫并不会有任何效果。

路由模式有两种

1. hash 模式

  - 默认是 hash 模式

  - url 地址上有 # 号

  - 监听地址变化的原理是:hashChange 事件

  - 支持良好

2. history 模式

  - url 地址上没有 # 号

  - 监听地址变化的原理是:HTML5 中新增的 history 的 API 叫做 popstate 事件

  - 看上去美了点

  - 上线到服务器,需要运维配置一些信息。

?如何切换模式?

在 new VueRouter 的时候,传递 mode 选项

默认 hash

导航守卫

也称为路由守卫、路由跳转钩子函数

 在路由跳转的各个不同阶段会自动执行的一些函数。这些函数中可以处理一些逻辑 允许还是不允许。

主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。

注意:记住参数或查询的改变并不会触发进入/离开的导航守卫

可以通过观察 $route 对象来应对这些变化,或使用 beforeRouteUpdate 的组件内守卫。

全局前置守卫

你可以使用 router.beforeEach 注册一个全局前置守卫:

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // ...
})

当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中

每个守卫方法接收三个参数:

  • to: Route: 即将要进入的目标 路由对象

  • from: Route: 当前导航正要离开的路由

  • next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数

    • 1. next() 正常跳转到 to

    • 2. next(false) 阻止了跳转

    • 3. next(path || {})  跳转到别处

全局后置守卫

你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:

router.afterEach((to, from) => {
  // ...
})

路由独享守卫

你可以在路由配置上直接定义 beforeEnter 守卫:

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

组件内的守卫

最后,你可以在路由组件内直接定义以下路由导航守卫:

  • beforeRouteEnter
  • beforeRouteUpdate (2.2 新增)
  • beforeRouteLeave

const Foo = {
  template: `...`,
  beforeRouteEnter(to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

完整的导航解析流程

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

性能优化 - 路由懒加载

优化:把首页不需要用到的资源给拆出去,等用户访问到具体路由的时候,再去加载需要的资源文件。

命名视图

给 坑(router-view) 取名字

有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 sidebar (侧导航) 和 main (主内容) 两个视图,这个时候命名视图就派上用场了。你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default

<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>

一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用 components 配置 (带上 s):

const router = new VueRouter({
  routes: [
    {
      path: '/',
      components: {
        default: Foo,
        a: Bar,
        b: Baz
      }
    }
  ]
})

vue的两种版本:1.完整版 2.运行时版

完整版: 包含有 运行时版本 + 编译器功能

运行时版本: 只有 运行时版本

1. 不支持编译 template 选项

问题:

1. template 选项的使用,会报错

2. 当前项目,如何证实它就是用的 运行时版本呢

原因:

1. 当前项目中使用的时 运行时版本,没有编译器功能。不会将 template 字符串编译。

 2. node_modules 中 vue 的 package.json 中定义的module 字段的值

        - package.json 中 main 字段与 module 字段的区别

                - main:  给 commonjs 模块化系统使用的

                - module: 给 ES6 模块化系统使用的

Vuex

Vuex是什么

 一款 vue 官方团队提供的 插件 (使用时要Vue.use(xxx)). 用于处理数据共享的插件.

  Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式

1.作用:能处理 vue 项目中 数据共享的 事情

2. 版本问题 : 最新的版本是  vuex4.x

 3.xxx   vue 2.x

  4.xxx   vue 3.x

Vuex执行流程图

        

 流程原理梳理:

  1. 组件派发(dispatch)动作(action).

  2. 动作(action)提交(commit)突变(mutation)

  3. 突变(mutation)修改(update)数据(state)

  4. 数据(state)引起组件的重新渲染(render)

 简短流程

  1. 组件直接提交突变

  2. 突变修改数据(state)

  3. 数据(state)引起组件的重新渲染(render)

核心概念

 1. state        存放数据的位置

  2. mutations    存放突变的位置

  3. actions      存放动作的位置

store        仓库. 上述三个东西,都在仓库中. 且 store 还有一些api供组件使用

    store.state       拿仓库的数据

      store.dispatch()  派发动作

      store.commit()    提交突变

      ....

Vuex 必须在项目中使用吗?      

没必要

需要根据自己所写的项目需求来使用

如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式 (opens new window)就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。

vuex 的使用步骤

 安装

  1. 新项目  vue create xxx  时勾选上 vuex 即可.

  2. 老项目  vue add vuex 来安装. (不推荐) 需要保证代码是干净的.

  3. 老项目  npm install vuex@3  

1. 引入 vuex

import Vuex from 'vuex'

2. 注册这款插件

Vue.use(Vuex)

3. 实例化 Vuex 的仓库的实例对象

const store = new Vuex.Store({

  // 仓库的相关配置

  state: {},

  mutations: {},

  actions: {},

  getters: {},

  moudles: {},

})

4. 将 store 与 vue 实例关联起来

new Vue({

  store: store

})

仓库的数据有了,如何拿到页面(组件)中使用呢?

使用了vuex之后,并且相关的配置都ok之后,所有组件都能有一个 $store 对象,这个对象就是仓库的实例对象.

也就是 你  new Vuex.Store() 得到的那个东西

1. 直接使用 $store.state.xxx 去使用  (不推荐)

2. 使用 计算属性 去拿仓库的数据  (推荐)

3. 使用 vuex 中的 mapState 辅助函数 (推荐)

...

为什么组件中使用时,推荐使用计算属性而不要直接$store.state.xxx的方式

1. 用计算属性时,可以修改名字

2. 利用计算属性不能修改的特点, 避免直接通过 $store.state.xxx 的方式去修改它.

解决template模板下面有波浪号问题       

template 有波浪号。jsconfig.json文件夹里面,加: template 有波浪号。jsconfig.json文件夹里面,加:"jsx": "preserve",

Mutation

  mutations: {
    // key: value
    //    key - 突变的名字
    //    value(state, payload)   突变对应的处理函数
    //          state 是当前仓库store的state数据
    //          payload 提交这个突变时,传递过来的参数
    changeMsg(state, payload) {
      state.msg = payload;
    },
}

 action

 actions: {
    // key: value
    //    key      动作的名字
    //    value(context, payload)    动作对应的处理函数
    //          context 是当前仓库store的上下文对象,理解为这玩意里面有 state、commit、dispatch、...
    //              == $store state、rootState、getters、rootGetters、commit、dispath
    //          payload 派发这个动作时,传递过来的参数
    abc(context, payload) {
      console.log("abc ===", context);
      console.log("abc ===", payload);
    },
  },

什么要多出一个 action 流程

1. mutation 只能同步的修改 state 。不要使用异步的方式。

2. action 可以异步,异步之后,提交 mutation 来修改 state

getter

相对与对当前的 state 做一层 计算属性,基于当前的某个或多个state得到一个新的值,可以直接拿到组件中使用的。

Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。

Getter 接受 state 作为其第一个参数:

const store = createStore({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos (state) {
      return state.todos.filter(todo => todo.done)
    }
  }
})

Getter 会暴露为 store.getters 对象,你可以以属性的形式访问这些值:

store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]

Getter 也可以接受其他 getter 作为第二个参数:

getters: {
  // ...
  doneTodosCount (state, getters) {
    return getters.doneTodos.length
  }
}
store.getters.doneTodosCount // -> 1

我们可以很容易地在任何组件中使用它:

computed: {
  doneTodosCount () {
    return this.$store.getters.doneTodosCount
  }
}

 注意,getter 在通过属性访问时是作为 Vue 的响应式系统的一部分缓存其中的。

rootState getters第三个参数

rootState 是 根模块的 state 数据

module

当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割

const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = createStore({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

Vuex表单v-model的使用

双向绑定的计算属性

<input v-model="message">
// ...
computed: {
  message: {
    get () {
      return this.$store.state.obj.message
    },
    set (value) {
      this.$store.commit('updateMessage', value)
    }
  }
}

ui组件库 

市面上很大大公司或大团队提供的vue的组件集合.包含有很多组件,就叫组件库

 PC端

element-ui  饿了么团队

iview

移动端

vant        有赞

mintui

按需引入与完整引入的两种方式

1.完整引入

完整引入会将组件库中所有的代码都引入进项目. 带来的问题是会让我们项目的构建产物非常大.

可能我们这个项目只是有到了组件库中的一部分组件,完整引入没必要...

2.按需引入

按需引入只引入项目中需要使用到的组件,其他组件不用就不引入.能减少我们项目构建产物的大小.

修改组件库中的样式

1. 审查元素,找到它的类名,然后自己写样式去替换即可.

2. 如果要修改的元素不是组件的根元素,且当前我们的样式写了有 scoped 属性,那么覆盖的样式将不生效.

原因是: scoped 生成的样式上有属性选择器. 使用的第三方组件除了根元素外其它元素都没有添加上这个属性名(data-v-xxxx) ,所以样式不生效

解决方案是:

1. 多写一个 style 不加 scoped ,在这个 style 里面,专门写需要修改第三方组件的样式.        

2. 最好保险一点,自己加多一些类名的控制

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值