Record study record life
Vue学习笔记
Vue学习笔记

Vue学习笔记

项目构建

官网

Vue3 (vue3js.cn)

文档:简介 | Vue.js (vuejs.org)

配置环境

Vue官网

终端
LinuxMac上可以用自带的终端。
Windows上推荐用powershell或者cmdGit Bash有些指令不兼容。


安装Nodejs
安装地址


安装@vue/cli
打开Git Bash,执行:

npm i -g @vue/cli

如果执行后面的操作有bug,可能是最新版有问题,可以尝试安装早期版本,比如:npm i -g @vue/cli@4


启动vue自带的图形化项目管理界面

vue ui

常见问题1:Windows上运行vue,提示无法加载文件,表示用户权限不足。
解决方案:用管理员身份打开终端,输入set-ExecutionPolicy RemoteSigned,然后输入y

 

使用

Vue.createApp().mount('')//创建应用,将配置对象传入,mount进行挂载,和dom元素关联

CDN

你可以借助 script 标签直接通过 CDN 来使用 Vue:

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

使用全局构建版本

<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

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

<script>
  const { createApp } = Vue
  
  createApp({
    data() {
      return {
        message: 'Hello Vue!'
      }
    }
  }).mount('#app')
</script>

Vite

Vite 是一个轻量级的、速度极快的构建工具,对 Vue SFC 提供第一优先级支持。作者是尤雨溪,同时也是 Vue 的作者!

要使用 Vite 来创建一个 Vue 项目,非常简单:

npm init vue@latest
npm init vue@latest <project-name> -- -- template vue

这个命令会安装和执行 create-vue,它是 Vue 提供的官方脚手架工具。跟随命令行的提示继续操作即可。

  • 要学习更多关于 Vite 的知识,请查看 Vite 官方文档
  • 若要了解如何为一个 Vite 项目配置 Vue 相关的特殊行为,比如向 Vue 编译器传递相关选项,请查看 @vitejs/plugin-vue 的文档。

上面提到的两种在线演练场也支持将文件作为一个 Vite 项目下载。

vue cli

vue create projectname

基本概念

script部分

export default对象的属性:

  • name:组件的名称
  • components:存储<template>中用到的所有组件
  • props:存储父组件传递给子组件的数据
  • watch():当某个数据发生变化时触发
  • computed:动态计算某个数据
  • setup(props, context):初始化变量、函数
    • ref定义变量,可以用.value属性重新赋值
    • reactive定义对象,不可重新赋值
    • props存储父组件传递过来的数据
    • context.emit():触发父组件绑定的函数

template部分

  • <slot></slot>:存放父组件传过来的children
  • v-on:click@click属性:绑定事件
  • v-ifv-elsev-else-if属性:判断
  • v-for属性:循环,:key循环的每个元素需要有唯一的key
  • v-bind:或::绑定属性

style部分

  • <style>标签添加scope属性后,不同组件间的css不会相互影响。

第三方组件

  • view-router包:实现路由功能。
  • vuex:存储全局状态,全局唯一。
    • state: 存储所有数据,可以用modules属性划分成若干模块
    • getters:根据state中的值计算新的值
    • mutations:所有对state的修改操作都需要定义在这里,不支持异步,可以通过$store.commit()触发
    • actions:定义对state的复杂修改操作,支持异步,可以通过$store.dispatch()触发。注意不能直接修改state,只能通过mutations修改state
    • modules:定义state的子模块

模板语法

内容渲染指令

v-text

  • <p v-text='msg1'></>将数据源中的数据直接渲染到成为标签的内容
  • 缺点:v-text会直接覆盖掉标签内本来的值

{{}}

{{}} :插值表达式

插值表达式可以添加简单的JS表达式(加减乘除,三元表达式等)但是不能使用复杂的JS语句

姓名:{{msg1}}

最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 (即双大括号):

<span>Message: {{ msg }}</span>

双大括号标签会被替换为相应组件实例中 msg 属性的值。同时每次 msg 属性更改时它也会同步更新。

 

v-html

v-html:可以添加标签(html元素)

双大括号会将数据解释为纯文本,而不是 HTML。若想插入 HTML,你需要使用 v-html 指令

template

<p>Using text interpolation: {{ rawHtml }}</p>
<p>Using v-html directive: <span v-html="rawHtml"></span></p>

这里我们遇到了一个新的概念。这里看到的 v-html attribute 被称为一个指令。指令由 v- 作为前缀,表明它们是一些由 Vue 提供的特殊 attribute,你可能已经猜到了,它们将为渲染的 DOM 应用特殊的响应式行为。这里我们做的事情简单来说就是:在当前组件实例上,将此元素的 innerHTML 与 rawHtml 属性保持同步。

span 的内容将会被替换为 rawHtml 属性的值,插值为纯 HTML——数据绑定将会被忽略。注意,你不能使用 v-html 来拼接组合模板,因为 Vue 不是一个基于字符串的模板引擎。在使用 Vue 时,应当使用组件作为 UI 重用和组合的基本单元。

安全警告

在网站上动态渲染任意 HTML 是非常危险的,因为这非常容易造成 [XSS 漏洞](https://en.wikipedia.org/wiki/Cross-site_scripting)。请仅在内容安全可信时再使用 `v-html`,并且**永远不要**使用用户提供的 HTML 内容。

属性绑定指令

绑定 v-bind

双大括号不能在 HTML attributes 中使用。想要响应式地绑定一个 attribute,应该使用 v-bind 指令

<div v-bind:id="dynamicId"></div>

v-bind 指令指示 Vue 将元素的 id attribute 与组件的 dynamicId 属性保持一致。如果绑定的值是 null 或者 undefined,那么该 attribute 将会从渲染的元素上移除。

简写为“:”

因为 v-bind 非常常用,我们提供了特定的简写语法:

<div :id="dynamicId"></div>

开头为 : 的 attribute 可能和一般的 HTML attribute 看起来不太一样,但它的确是合法的 attribute 名称字符,并且所有支持 Vue 的浏览器都能正确解析它。此外,他们不会出现在最终渲染的 DOM 中。简写语法是可选的,但相信在你了解了它更多的用处后,你应该会更喜欢它。

接下来的指引中,我们都将在示例中使用简写语法,因为这是在实际开发中更常见的用法。

布尔型

布尔型 attribute 依据 true / false 值来决定 attribute 是否应该存在于该元素上。disabled 就是最常见的例子之一。

v-bind 在这种场景下的行为略有不同:

<button :disabled="isButtonDisabled">Button</button>

isButtonDisabled真值或一个空字符串 (即 <button disabled="">) 时,元素会包含这个 disabled attribute。而当其为其他假值时 attribute 将被忽略。

动态绑定多个值

如果你有像这样的一个包含多个 attribute 的 JavaScript 对象:

data() {
  return {
    objectOfAttrs: {
      id: 'container',
      class: 'wrapper'
    }
  }
}

通过不带参数的 v-bind,你可以将它们绑定到单个元素上:

template

<div v-bind="objectOfAttrs"></div>

事件绑定指令

v-on或@

<button @click="increment">{{ count }}</button>

v-on

  • 原生DOM对象去掉on即可 – v-on:click – v-on:input – v-on:keyup
  • 格式:v-on:事件名称=”事件处理函数的名称”
  • 简写:@事件名称=”事件处理函数的名称”
  • 绑定的事件可以用小括号进行传参,若无小括号则默认传触发的事件对象(MouseEvent{}),若在有参数的情况下,仍需获取事件对象,可使用$event符号获取事件对象。(@click=”add(n,$event)”)不过开发中不常用

事件修饰符(加在事件绑定后面)

@click.prenvent=”add”: 点击的同时,阻止默认行为(event.preventDefault() )

form表单提交会默认刷新网页,可以用@submit.prevent阻止

.stop:阻止事件冒泡

.capture: 以捕获模式触发当前的事件处理函数

.once: 绑定的事件只触发一次

.self: 只有在event.target是当前元素自身时触发事件处理函数

按键修饰符(仅用于键盘事件)

  • @keyup.enter=””: 仅按下回车键时触发
  • @keyup.esc=””: 仅按下esc时触发

双向绑定指令

  • 即双向数据绑定指令:v-model
  • 只有表单元素才能使用v-model,其他元素或无法呈现内容,或无法修改内容均无法使用该指令
  • 表单元素均可使用(可理解为带value属性的标签可用),如:
    • input输入框
      • type=”radio”
      • type=”checkbox”
      • type=”xxxx”
    • textarea文本域
    • select下拉菜单

v-model指令的修饰符

  • .number:自动将用户输入值转为数值类型
  • .trim:自动过滤用户输入的首尾空白字符
  • .lazy:在”change”时而非”input”时更新
    • 中间变化的时候不会同步到data数据源中,只会在最后输入完成后改变

条件渲染指令

v-show

v-show:为元素添加或删除display:none样式来隐藏或显示元素(频繁切换时建议使用)

v-show的用法与下面的v-if类似,用上次的代码,直接将if修改成show即可。

v-if

  • v-if和v-show
    • v-if:动态创建和删除元素(页面创建时即为false只有特殊情况才会让其显示时用v-if)
    • v-show:为元素添加或删除display:none样式来隐藏或显示元素(频繁切换时建议使用)
    • 以上仅为面试时回答,现在电脑对此消耗不大,绝大多数情况无需考虑性能直接使用v-if

与v-if配合使用的v-else-if()和v-else

   <div id="test">
        <div v-if="score<60">不及格</div>
        <div v-else-if="score<90">良好</div>
        <div v-else>优秀</div>
    </div>

 

v-for

data: {
	list: [1, 2, 3, 4, 5]
}


<div v-for="item in userList" v-bind:key="item.id">
    
    
<ul>
	<li v-for="(item,i) in list">索引:{{i}}---每一项:{{item}}</li>
</ul>
结果:
索引:0---每一项:1
索引:1---每一项:2
索引:2---每一项:3
索引:3---每一项:4

官方建议使用v-for时,后面一定要绑定一个:key=’item.id'(.vue文件中不绑key会直接报错)而且,尽量把id作为key的值

  • 可提升性能,防止列表状态紊乱
  • 官方对id的值类型有要求:字符串或数字
  • key值不允许重复
  • 使用索引号index没有用处,因为数组增删改查会改变索引号所对应的数据(如:在数组前面增删数据)

插槽<slot>

用于子元素占位,可在父元素中被替换

具名插槽

占位:

<slot name="slotname"></slot>

使用:

<template v-slot:slotname>替换的内容</template>

备用内容

在插槽没有提供内容是被渲染

作用域插槽

……


状态选项

data

用于声明组件初始响应式状态的函数。

  • 详细信息

    该函数应当返回一个普通 JavaScript 对象,Vue 会将它转换为响应式对象。实例创建后,可以通过 this.$data 访问该响应式对象。组件实例也代理了该数据对象上所有的属性,因此 this.a 等价于 this.$data.a

    所有会用到的顶层数据属性都应该提前在这个对象中声明。虽然理论上可以向 this.$data 添加新属性,但并不推荐这么做。如果一个属性的值在一开始还获取不到,应当先用 undefined 或是 null 值来占位,让 Vue 知道这个属性是存在的。

    _$ 开头的属性将不会被组件实例代理,因为它们可能和 Vue 的内置属性、API 方法冲突。你必须以 this.$data._property 的方式访问它们。

    推荐返回一个可能改变自身状态的对象,如浏览器 API 原生对象或是带原型的类实例等。理想情况下,返回的对象应是一个纯粹代表组件状态的普通对象。

  • 示例
    export default {
      data() {
        return { a: 1 }
      },
      created() {
        console.log(this.a) // 1
        console.log(this.$data) // { a: 1 }
      }
    }
    

    注意,如果你为 data 属性使用了一个箭头函数,则 this 将不会指向该组件实例,不过你仍然可以通过该函数的第一个参数来访问实例:

     

    data: (vm) => ({ a: vm.myProp })
    

methods

用于声明要混入到组件实例中的方法。

  • 详细信息

    声明的方法可以直接通过组件实例访问,或者在模板语法表达式中使用。所有的方法都会将它们的 this 上下文自动绑定为组件实例,即使在传递时也如此。

    在声明方法时避免使用箭头函数,因为它们不能通过 this 访问组件实例。

  • 示例
    export default {
      data() {
        return { a: 1 }
      },
      methods: {
        plus() {
          this.a++
        }
      },
      created() {
        this.plus()
        console.log(this.a) // => 2
      }
    }
    
  • 参考Event 处理

watch

用于声明在数据更改时调用的侦听回调。

为了便于阅读,对类型进行了简化。

  • 详细信息

    watch 选项期望接受一个对象,其中键是需要侦听的响应式组件实例属性 (例如,通过 datacomputed 声明的属性)——值是相应的回调函数。该回调函数接受被侦听源的新值和旧值。

    除了一个根级属性,键名也可以是一个简单的由点分隔的路径,例如 a.b.c。注意,这种用法不支持复杂表达式——仅支持由点分隔的路径。如果你需要侦听复杂的数据源,可以使用命令式的 $watch() API。

    值也可以是一个方法名称的字符串 (通过 methods 声明),或包含额外选项的对象。当使用对象语法时,回调函数应被声明在 handler 中。额外的选项包含:

    • immediate:在侦听器创建时立即触发回调。第一次调用时,旧值将为 undefined
    • deep:如果源是对象或数组,则强制深度遍历源,以便在深度变更时触发回调。详见深层侦听器
    • flush:调整回调的刷新时机。详见回调的触发时机watchEffect()
    • onTrack / onTrigger:调试侦听器的依赖关系。详见侦听器调试

    声明侦听器回调时避免使用箭头函数,因为它们将无法通过 this 访问组件实例。

  • 示例
    export default {
      data() {
        return {
          a: 1,
          b: 2,
          c: {
            d: 4
          },
          e: 5,
          f: 6
        }
      },
      watch: {
        // 侦听根级属性
        a(val, oldVal) {
          console.log(`new: ${val}, old: ${oldVal}`)
        },
        // 字符串方法名称
        b: 'someMethod',
        // 该回调将会在被侦听的对象的属性改变时调动,无论其被嵌套多深
        c: {
          handler(val, oldVal) {
            console.log('c changed')
          },
          deep: true
        },
        // 侦听单个嵌套属性:
        'c.d': function (val, oldVal) {
          // do something
        },
        // 该回调将会在侦听开始之后立即调用
        e: {
          handler(val, oldVal) {
            console.log('e changed')
          },
          immediate: true
        },
        // 你可以传入回调数组,它们将会被逐一调用
        f: [
          'handle1',
          function handle2(val, oldVal) {
            console.log('handle2 triggered')
          },
          {
            handler: function handle3(val, oldVal) {
              console.log('handle3 triggered')
            }
            /* ... */
          }
        ]
      },
      methods: {
        someMethod() {
          console.log('b changed')
        },
        handle1() {
          console.log('handle 1 triggered')
        }
      },
      created() {
        this.a = 3 // => new: 3, old: 1
      }
    }
    
  • 参考侦听器

 

export default {
    //组件的data选项是一个函数。Vue 会在创建新组件实例的过程中调用此函数。它应该返回一个对象,然后Vue 会通过响应性系统将其包裹起来,并以$data的形式存储在组件实例中。为方便起见,该对象的任何顶级property 也会直接通过组件实例暴露出来:
  data() {
    return {
      count: 0
    }
  },
    //要为组件添加方法,我们需要用到 methods 选项。其是一个包含所有方法的对象:
  methods: {
    increment() {
      this.count++
    }
  }
}

和组件实例上的其他属性一样,方法也可以在模板上被访问。在模板中它们常常被用作事件监听器:

<button @click="increment">{{ count }}</button>

computed

接受一个 getter 函数,返回一个只读的响应式 ref 对象。该 ref 通过 .value 暴露 getter 函数的返回值。它也可以接受一个带有 getset 函数的对象来创建一个可写的 ref 对象。

  • 示例
    data(){
        message:"safasddf"
    },
    computed:{//计算属性,只要依赖值不变,那么就不会重新计算(有缓存)---该示例的依赖值为message
    	reverseMsg=()=>{
    	return this.message.split('').reverse().join('')
    	}
    }
    

    创建一个只读的计算属性 ref:

    const count = ref(1)
    const plusOne = computed(() => count.value + 1)
    
    console.log(plusOne.value) // 2
    
    plusOne.value++ // 错误
    

    创建一个可写的计算属性 ref:(计算属性默认只有getter,在需要的时候可以提供一个setter)

    const count = ref(1)
    const plusOne = computed({
      get: () => count.value + 1,
      set: (val) => {
        count.value = val - 1
      }
    })
    
    plusOne.value = 1
    console.log(count.value) // 0
    

    调试:

    const plusOne = computed(() => count.value + 1, {
      onTrack(e) {
        debugger
      },
      onTrigger(e) {
        debugger
      }
    })
    
  • 参考:

 

组件传值

父组件调用子组件(子组件为小模块)

父子组件访问方式

  • 父组件访问子组件:$refs
  • 子组件访问父组件:$parent (尽量少用,最好使用props传值)
  • 子组件访问根组件:$root

父组件传子组件Props

子组件

props 需要使用 props 选项来定义:

export default {
  props: ['foo'],
  created() {
    // props 会暴露到 `this` 上
    console.log(this.foo)
  }
}

除了使用字符串数组来声明 prop 外,还可以使用对象的形式:

export default {
  props: {
    title: String,
    likes: Number
  }
}

对于以对象形式声明中的每个属性,key 是 prop 的名称,而值则是该 prop 预期类型的构造函数。比如,如果要求一个 prop 的值是 number 类型,则可使用 Number 构造函数作为其声明的值。

父组件

静态值形式的 props:

<BlogPost title="My journey with Vue" />

使用 v-bind 或缩写 : 来进行动态绑定的 props:

<!-- 根据一个变量的值动态传入 -->
<BlogPost :title='title' />
<BlogPost :title="post.title" />

<!-- 根据一个更复杂表达式的值动态传入 -->
<BlogPost :title="post.title + ' by ' + post.author.name" />

子组件传父组件

简单版本

子组件写入props

父组件

<template>
	<div>
    <Child ref="myid"></Child>
    </div>
</template>
<script>
export default{
    data(){
    	return {};
    },
    methods:{
	},
    mounted(){
        console.log(this.$refs.myid);
    }
}   
<script/>
    

自定义事件

子组件

通过$emit来触发事件

export default{
    data(){
    	return msg:"hello"
    },
    methods:{
		//在子组件中,通过$emit来触发事件
		sendParent:function(){
			//this.$emit('自定义事件的名称','发送的事件参数')
            this.$emit('injectMsg',this.msg)
	}
}

父组件

<template>
	<div>
    <Content @injectMsg="ChildMsg">
    </div>
</template>
export default{
    data(){
    	return {};
    },
    methods:{
		//在子组件中,通过$emit来触发事件
		getChildMsg:function(value){
			//接收到的值就是子组件传来的值
            console.log(value)
	}
}

组件跨级通信

Provide/Inject


生命周期

  • beforeCreate(在组件实例初始化完成之后立即调用。)
  • created(在组件实例处理完所有与状态相关的选项后调用。)
  • beforeMount(在组件被挂载之前调用。)
  • mounted(在组件被挂载之后调用。)
  • beforeUpdate(在组件即将因为一个响应式状态变更而更新其 DOM 树之前调用。)
  • updated(在组件因为一个响应式状态变更而更新其 DOM 树之后调用。)
  • beforeUnmount(在一个组件实例被卸载之前调用。)
  • unmounted(在一个组件实例被卸载之后调用。)
  • errorCaptured(在捕获了后代组件传递的错误时调用。)
  • enderTracked (在一个响应式依赖被组件的渲染作用追踪后调用。)
  • renderTriggered (在一个响应式依赖被组件触发了重新渲染之后调用。)
  • activated(若组件实例是<KeepAlive>缓存树的一部分,当组件被插入到 DOM 中时调用。)
  • deactivated(若组件实例是<KeepAlive>缓存树的一部分,当组件从 DOM 中被移除时调用。)
  • serverPrefetch (当组件实例在服务器上被渲染之前要完成的异步函数。)

img


setup()

介绍

vue3->组合式API:将同一个逻辑关注点相关代码收集在一起

setup() 在组件被创建之前执行

注意:setup中你应该避免使用this,因为它不会找到组件实例。setup的调用发生在dataproperty、computed propertymethods被解析之前,所以它们无法在setup中被获取。

setup选项是一个接收propscontext 的函数,我们将在之后进行讨论。此外。我们将setup返回的所有内容都暴露给组件的其余部分(计算属性、方法、生命周期钩了等等)以及组件的模板。

export default {
  setup(props, context) {
    // 透传 Attributes(非响应式的对象,等价于 $attrs)
    console.log(context.attrs)

    // 插槽(非响应式的对象,等价于 $slots)
    console.log(context.slots)

    // 触发事件(函数,等价于 $emit)
    console.log(context.emit)

    // 暴露公共属性(函数)
    console.log(context.expose)
      
      return{ }
  }
}

参数

props

setup函数中的第一个参数是props。正如在一个标准组件中所期望的那样, setup函数中的props是响应式的,当传入新的prop 时,它将被更新。

export default {
    props:{
        title: String
    },
    setup(props) {
   		console .log(props.title)
    }
}

但是,因为props是响应式的,你不能使用ES6解构,它会消除prop 的响应性。

如果需要解构prop,可以在setup函数中使用toRefs函数来完成此操作:

import { toRefs } from 'vue '
    setup( props){
        const { title } = toRefs (props)
        console.log(title.value)
}

 

context

传递给setup函数的第二个参数是contextcontext是一个普通JavaScript对象,暴露了其它可能在setup中有用的值:

第二个参数 context 包含三个属性:attrsemitslots。它们分别相当于组件实例的 $attrs$emit$slots 这几个属性。

export default {
setup(props,context){
    // Attribute(非响应式对象,等同于 $attrs)
        console.log( context.attrs)
    //插槽(非响应式对象,等同于$slots)
        console. log(context.slotE)
    //触发事件(方法,等同于 $emit)
        console.log( context.emit)
    //暴露公共property(函数)
        console.log ( context. expose))
}

 

响应式数据ref()

需要使用带ref的响应式变量

ref()接受一个内部值,返回一个响应式的、可更改的 ref 对象,此对象只有一个指向其内部值的属性 .value

ref函数使得任何响应式变量在任何地方起作用

import { ref } from 'vue'

export default {
  setup(props, context) {
    const count = ref(0)
    const increment = () => ++count.value

    return {count,increment}
  }
}


响应式对象reactive()

返回一个对象的响应式代理。

const obj = reactive(
    { 
     name:"张三",
     age:18
    }
)

watch、watchEffect

就像我们在组件中使用watch选项并在user property上设置侦听器一样,我们也可以使用从Vue导入的 watch函数执行相同的操作。它接受3个参数:

  • 一个想要侦听的响应式引用或 getter函数
  • 一个回调
  • 可选的配置选项

watchEffect可以监听到属性

import { ref,reactive, watch,watchEffect } from 'vue' 
export default{
    data(){
        return{}
    },
    setup(){
        const counter = ref(0)
        const user=reactive({
            name:"张三",
            age:18
    })
    //watch只能监听red响应式数据
        watch(counter,(newValue,oldValue) =>{
            console.log( "The new counter value is: ' + newValue)
            console.log( "The pld counter value is: ' + oldValue)
        })
    //watchEffect注意不需要指定监听的属性,自动收集依赖
        watchEffect() =>{
                console.log(user.name)
            })
            return{counter,user}
        },

}


computed

import { ref,reactive,computed } from 'vue' 
export default{
    data(){
        return{}
    },
    setup(){
        const msg=ref('helloworld')
        const reverseMsg=computed(()=>{
            return msg.value.split('').reverse().join(;)
        })
        return{msg}
    },

}


生命周期钩子

可以通过生命周期钩子前面加上“on”来访问组件生命周期钩子

<script >
import {onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted,onErrorCaptured,onRenderTracked,onRenderTriggered,onActivated,onDeactivated,onServerPrefetch} from 'vue';
 
export default {
    name: "index",
    props: {},
    components: {},
    setup(props, context) {
        //生命周期
        onBeforeMount(()=>{
          console.log('注册一个钩子,在组件被挂载之前被调用。onBeforeMount')
        })
        onMounted(()=>{
          console.log('注册一个回调函数,在组件挂载完成后执行。onMounted')
        })
        onBeforeUpdate(()=>{
          console.log('注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用。onBeforeUpdate')
        })
        onUpdated(()=>{
          console.log('注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用。onUpdated')
        })
        onBeforeUnmount(()=>{
          console.log('注册一个钩子,在组件实例被卸载之前调用。onBeforeUnmount')
        })
        onUnmounted(()=>{
          console.log('注册一个回调函数,在组件实例被卸载之后调用。onUnmounted')
        })
        onErrorCaptured(() => {
          console.log('注册一个钩子,在捕获了后代组件传递的错误时调用。onErrorCaptured')
        })
        onRenderTracked(() => {
          console.log('注册一个调试钩子,当组件渲染过程中追踪到响应式依赖时调用。onRenderTracked')
        })
        onRenderTriggered(() => {
          console.log('注册一个调试钩子,当响应式依赖的变更触发了组件渲染时调用。onRenderTriggered')
        })
        onActivated(() => {
          console.log('注册一个回调函数,若组件实例是 <KeepAlive> 缓存树的一部分,当组件被插入到 DOM 中时调用。onActivated')
        })
        onDeactivated(() => {
          console.log('注册一个回调函数,若组件实例是 <KeepAlive> 缓存树的一部分,当组件从 DOM 中被移除时调用。onDeactivated')
        })
        onServerPrefetch(() => {
          console.log('注册一个异步函数,在组件实例在服务器上被渲染之前调用。onServerPrefetch')
        })
        return { }
        },
})
</script>

Vue Router

安装

npm install vue-router@4

router-link

router-link 实现路由之间的跳转

将导航栏的链接href改为对应的路由

<a class="navbar-brand" href="/pk/">King Of Bots</a>

router-link标签—-前端渲染,可以实现点击后不刷新

<router-link class="navbar-brand" :to="{name:'home'}">King Of Bots</router-link>

router-view

router-view 当你的路由path 与访问的地址相符时,会将指定的组件替换该 router-view

App.vue中

<template>
  <NavBar/>
  <router-view />
</template>

动态路由

很多时候,我们需要将给定匹配模式的路由映射到同一个组件。例如,我们可能有一个 User 组件,它应该对所有用户进行渲染,但用户 ID 不同。在 Vue Router 中,我们可以在路径中使用一个动态字段来实现,我们称之为 路径参数

const User = {
  template: '<div>User</div>',
}

// 这些都会传递给 `createRouter`
const routes = [
  // 动态字段以冒号开始
  { path: '/users/:id', component: User },
]

现在像 /users/johnny/users/jolyne 这样的 URL 都会映射到同一个路由。

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

const User = {
  template: '<div>User {{ $route.params.id }}</div>',
}

嵌套路由

要将组件渲染到这个嵌套的 router-view 中,我们需要在路由中配置 children

const 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,
      },
    ],
  },
]

注意,以 / 开头的嵌套路径将被视为根路径。这允许你利用组件嵌套,而不必使用嵌套的 URL。

如你所见,children 配置只是另一个路由数组,就像 routes 本身一样。因此,你可以根据自己的需要,不断地嵌套视图。

此时,按照上面的配置,当你访问 /user/eduardo 时,在 Userrouter-view 里面什么都不会呈现,因为没有匹配到嵌套路由。也许你确实想在那里渲染一些东西。在这种情况下,你可以提供一个空的嵌套路径:

const routes = [
  {
    path: '/user/:id',
    component: User,
    children: [
      // 当 /user/:id 匹配成功
      // UserHome 将被渲染到 User 的 <router-view> 内部
      { path: '', component: UserHome },

      // ...其他子路由
    ],
  },
]

rouer配置

直接输入根地址重定向到pk

  {
    path: "/",
    name: "home",
    redirect:"/pk/"
  },

重定向到404

  {
    path: "/:catchAll(.*)",
    redirect:"/404/"
  }

动态路由

const routes =[
    //动态字段以冒号开始
	{ 
     path: '/users/:id', 
     component: User 
    },
    
]
import { createRouter, createWebHistory } from 'vue-router'
import PkIndexView from "../views/pk/PkIndexView"
import RecordIndexView from "../views/record/RecordIndexView"
import NotFound from "../views/error/NotFound"
const routes = [
  {
    path: "/",
    name: "home",
    redirect:"/pk/"
  },
  {
    path: "/pk/",
    name:"pk_index",
    component:PkIndexView,
  },
    {
    path: "/record/",
    name:"record_index",
    component:RecordIndexView,
  },
  {
    path: "/404/",
    name:"404",
    component:NotFound,
  },
  {
    path: "/:catchAll(.*)",
    redirect:"/404/"
  }

]

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

export default router

<hr< body=””></hr<>

不同的历史模式

history模式

hash 模式是用 createWebHashHistory() 创建的:

路径带#号

它在内部传递的实际 URL 之前使用了一个哈希字符(#)。由于这部分 URL 从未被发送到服务器,所以它不需要在服务器层面上进行任何特殊处理。不过,它在 SEO 中确实有不好的影响。如果你担心这个问题,可以使用 HTML5 模式。

http://192.168.85.1:3000/#/page

const router = createRouter({

  history: createWebHashHistory(),

  routes, 

})

HTML5 模式

createWebHistory() 创建 HTML5 模式,推荐使用这个模式:

路径不带#号,但需要后端进行配置(如nginx配置)

当使用这种历史模式时,URL 会看起来很 “正常”,例如 https://example.com/user/id。漂亮!

不过,问题来了。由于我们的应用是一个单页的客户端应用,如果没有适当的服务器配置,用户在浏览器中直接访问 https://example.com/user/id,就会得到一个 404 错误。这就丑了。

不用担心:要解决这个问题,你需要做的就是在你的服务器上添加一个简单的回退路由。如果 URL 不匹配任何静态资源,它应提供与你的应用程序中的 index.html 相同的页面。漂亮依旧!

http://192.168.85.1:3000/page

const router = createRouter({

  history: createWebHistory(),

  routes, 

})

服务器配置示例

注意:以下示例假定你正在从根目录提供服务。如果你部署到子目录,你应该使用Vue CLI 的 publicPath 配置和相关的路由器的 base 属性。你还需要调整下面的例子,以使用子目录而不是根目录(例如,将RewriteBase/ 替换为 RewriteBase/name-of-your-subfolder/)。

vue.config.js

module.exports = {
  publicPath: process.env.NODE_ENV === 'production'
    ? '/production-sub-path/'
    : '/'
}

Apache

<IfModule mod_negotiation.c>
  Options -MultiViews
</IfModule>

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.html [L]
</IfModule>

也可以使用 FallbackResource 代替 mod_rewrite

nginx

location / {
  try_files $uri $uri/ /index.html;
}

原生 Node.js

const http = require('http')
const fs = require('fs')
const httpPort = 80

http
  .createServer((req, res) => {
    fs.readFile('index.html', 'utf-8', (err, content) => {
      if (err) {
        console.log('We cannot open "index.html" file.')
      }

      res.writeHead(200, {
        'Content-Type': 'text/html; charset=utf-8',
      })

      res.end(content)
    })
  })
  .listen(httpPort, () => {
    console.log('Server listening on: http://localhost:%s', httpPort)
  })

动态路由

 


useRoute()

route是一个跳转的路由对象,每一个路由都会有一个route对象,是一个局部的对象,可以获取对应的name,path,params,query等:

matched: 数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象

name: 当前路径的名字,如果没有使用具名路径,则名字为空。

params: 对象,包含路由中的动态片段和全匹配片段的键值对

path: 字符串,等于当前路由对象的路径,会被解析为绝对路径,如 “/home/news”

query: 对象,包含路由中查询参数的键值对。 例如,对于 /home/news/detail/01?favorite=yes ,会得到$route.query.favorite => ‘yes’

<script>
import { useRoute } from 'vue-router'
import { computed } from 'vue'
export default {
    setup(){
        const route = useRoute();
        let route_name = computed(() => route.name);
        return {
            route_name
        }
    }
}

</script>

根据路由判断是否active,是否高亮

<router-link :class="route_name=='pk_index'?'nav-link active':'nav-link' " :to="{name:'pk_index'}">对战</router-link>

axios

安装

npm install axios

GET

<script>
import { axios } from 'axios'
export default {
    setup(){
        axios.get('127.0.0.1:5000/test').then(res)=>{
        	console.log(res);
        }
        return { }
    }
}

</script>

POST

<script>
import { axios } from 'axios'
export default {
    setup(){
        axios.post('127.0.0.1:5000/test',{
            date:"hhhhh",
            data2:"aaaaa"
        }).then(res)=>{
        	console.log(res);
        }
        return { }
    }
}

</script>

多并发请求

function getUserAccount() {
  return axios.get('/user/12345');
}

function getUserPermissions() {
  return axios.get('/user/12345/permissions');
}

Promise.all([getUserAccount(), getUserPermissions()])
  .then(function (results) {
    const acct = results[0];
    const perm = results[1];
  });

vite代理解决跨域

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  server: {
    port: 3000,
    open: true,
    // 配置代理
    proxy: {
      // 请求的路径前缀只要是 /testaxios 就会被拦截走这个代理
      '/testaxios': {
      /**
        *  请求的目标资源再经过替换成 /httphwm/getList 后,
        *  会加上 http://127.0.0.1:9693 这个前缀,
        *  最后请求的URL为: http://127.0.0.1:9693/httphwm/getList
        */
        target: 'http://127.0.0.1:9693',//被替换端的服务器地址
        ws: true,
        changeOrigin: true,
        // 拦截到的请求路径 testaxios/httphwm/getList,/testaxios会被替换成空
        rewrite: (path) => path.replace(/^\/testaxios/, ''),
      },
    },
  },


vuex

安装

npm install vuex@next --save

结构

vuex:存储全局状态,全局唯一。

  • state: 存储所有数据,可以用modules属性划分成若干模块
  • getters:根据state中的值计算新的值
  • mutations:所有对state的修改操作都需要定义在这里,不支持异步,可以通过$store.commit()触发
  • actions:定义对state的复杂修改操作,支持异步,可以通过$store.dispatch()触发。注意不能直接修改state,只能通过mutations修改state
  • modules:定义state的子模块

使用

安装 Vuex 之后,让我们来创建一个 store。创建过程直截了当——仅需要提供一个初始 state 对象和一些 mutation:

import { createApp } from 'vue'
import { createStore } from 'vuex'

// 创建一个新的 store 实例
const store = createStore({
  state () {
    return {
      count: 0
    }
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

const app = createApp({ /* 根组件 */ })

// 将 store 实例作为插件安装
app.use(store)

现在,你可以通过 store.state 来获取状态对象,并通过 store.commit 方法触发状态变更:

store.commit('increment')

console.log(store.state.count) // -> 1

在 Vue 组件中, 可以通过 this.$store 访问store实例。现在我们可以从组件的方法提交一个变更:

methods: {
  increment() {
    this.$store.commit('increment')
    console.log(this.$store.state.count)
  }
}

再次强调,我们通过提交 mutation 的方式,而非直接改变 store.state.count,是因为我们想要更明确地追踪到状态的变化。这个简单的约定能够让你的意图更加明显,这样你在阅读代码的时候能更容易地解读应用内部的状态改变。此外,这样也让我们有机会去实现一些能记录每次状态改变,保存状态快照的调试工具。有了它,我们甚至可以实现如时间穿梭般的调试体验。

由于 store 中的状态是响应式的,在组件中调用 store 中的状态简单到仅需要在计算属性中返回即可。触发变化也仅仅是在组件的 methods 中提交 mutation。

赞赏

微信赞赏 支付宝赞赏

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

2条评论