查漏补缺方为上策!!两万六字总结vue的基本使用和高级特性,周边插件vuex和vue-router任你挑选

vue的基本使用和高级特性,周边插件vuex和vue-router

  • 一、vue的使用
    • 1、vue-cli
    • 2、基本使用
      • (1)模板(插值,指令)
      • (2)computed和watch
      • (3)class和style
      • (4)条件
      • (5)循环(列表)渲染
      • (6)事件
      • (7)表单
    • 3、组件
      • (1)props和$emit(适合父子组件间的通信)
      • (2)组件间通信 - 自定义事件
      • (3)组件生命周期
  • 二、vue的高级特性
    • 1、自定义 v-model
    • 2、$nextTick
      • (1)知识点
      • (2)例子展示🌰
    • 3、slot插槽
      • (1)插槽的作用
      • (2)三种插槽类型
        • 1)普通slot插槽
        • 2)作用域插槽slot-scope
        • 3)具名插槽
    • 4、动态、异步组件
      • (1)动态组件
        • 1)举个例子🌰
        • 2)动态组件的用法和应用场景
        • 3)演示
      • (2)异步组件
    • 5、keep-alive
      • (1)定义和应用场景
      • (2)举例🌰
    • 6、mixin
      • (1)mixin是什么
      • (2)mixin的问题
      • (3)举个栗子🌰
  • 三、vue的周边插件:vuex和vue-router
    • 1、vuex
      • (1)vuex基本概念
      • (2)用于vue组件中的API
      • (3)图例
    • 2、vue-router
      • (1)路由模式
      • (2)路由配置
  • 四、结束语

以下文章将讲解对 vue 的基本使用以及各种高级特性还有周边插件 vuexvue-router ,融合大量案例🌰和动图🕹️进行展示。可以把它当成是 vue 的入门宝库,有不懂的语法知识点时或许在这里可以寻找到你的答案并且通过例子运用起来。

废话不多说,下面来开始探索 vue 的奥秘吧🙆

一、vue的使用

1、vue-cli

vue 项目是基于 vue-cli 脚手架搭建的项目。当我们要创建一个项目时,首先要先全局安装 vue-cli 脚手架,命令行为:

npm i -g @vue/cli

在搭建完成项目以后,我们需要来了解 src 目录下各个文件夹和文件的用法。

├── assets 放置静态资源
├── components 放组件
├── router 定义路由的相关配置
├── views 视图
├── views 视图
├── app.vue 应用主组件
├── main.js 入口文件

2、基本使用

(1)模板(插值,指令)

1)插值、表达式

<template><div><p>文本插值 {{message}}</p><p>JS 表达式 {{ flag ? 'yes' : 'no' }} (只能是表达式,不能是 js 语句)</p></div>
</template><script>
export default {data() {return {message: 'I am Monday.',flag: true}}
}
</script>
//浏览器显示结果
// 文本插值 I am Monday.
// JS 表达式 yes

2)动态属性

<template><div><p :id="dynamicId">动态属性:{{dynamicId}}</p></div>
</template><script>
export default {data() {return {dynamicId: `id-${Date.now()}`}}
}
</script>//浏览器显示结果
//动态属性:id-1622339576875

3)v-html指令

<template><div><p v-html="rawHtml"><span>有 xss 风险</span><span>【注意】使用 v-html 之后,将会覆盖子元素</span></p></div>
</template><script>
export default {data() {return {rawHtml: '指令 - 原始 html <b>加粗</b> <i>斜体</i>',}}
}
</script>//浏览器显示结果
//指令 - 原始 html 加粗 斜体

值得注意的是, v-html 指令会有 xss 风险,且会覆盖子组件,要谨慎使用!!

(2)computed和watch

1)computed

computed 有缓存data 不变则不会重新计算。如下代码所示:

<template><div><p>num {{num}}</p><p>double1 {{double1}}</p>//用v-model一定要有get和set,否则会报错<input v-model="double2"/></div>
</template><script>
export default {data() {return {num: 20}},computed: {double1() {return this.num * 2},double2: {// 获取值get() {return this.num * 2},// 设置值set(val) {this.num = val/2}}}
}
</script>

此时浏览器的打印结果如图所示。

computed

大家可以看到,当 num 的值如果一直是 20 的值时,那么 double1double2get() 方法是不会重新计算的,它会被缓存下来,达到提高运算性能的效果。

2)watch

  • watch 监听基本数据类型时,可正常拿到 oldValval 的值。
  • watch 如果监听引用数据类型时,需要进行深度监听,且拿不到 oldVal 。因为指针相同,监听时,指针已经指向了新的 val

如下代码所示:

<template><div><input v-model="name"/><input v-model="info.city"/></div>
</template><script>
export default {data() {return {name: 'Monday',info: {city: 'FuZhou'}}},watch: {name(oldVal, val) {// eslint-disable-next-lineconsole.log('watch name', oldVal, val) // 值类型,即基本数据类型,可正常拿到 oldVal 和 val},info: {handler(oldVal, val) {// eslint-disable-next-lineconsole.log('watch info', oldVal, val) // 引用数据类型,拿不到 oldVal 。因为指针相同,此时已经指向了新的 val},deep: true // 深度监听}}
}
</script>

(3)class和style

  • 使用动态属性,即 v-bind 绑定;

  • 使用驼峰式写法。

如下代码所示:

<template><div><p :class="{ black: isBlack, yellow: isYellow }">使用 class</p><p :class="[black, yellow]">使用 class (数组)</p><p :style="styleData">使用 style</p></div>
</template><script>
export default {data() {return {isBlack: true,isYellow: true,black: 'black',yellow: 'yellow',styleData: {fontSize: '40px', // 转换为驼峰式color: 'red',backgroundColor: '#ccc' // 转换为驼峰式}}}
}
</script><style scoped>.black {background-color: #999;}.yellow {color: yellow;}
</style>

此时浏览器的显示效果如下。

class和style

(4)条件

  • v-ifv-else 的用法:可使用变量,也可以使用 === 表达式。
  • v-ifv-show 的区别?
    • v-if 会根据条件对元素进行渲染的控制,此处的控制渲染指的是将元素添加到 dom 中或移除 dom,所以会存在dom的增删。假设页面初始化时,条件判断结果为 false ,则不会将该元素添加到 dom 当中。
    • v-showv-if 不一样的是,不管条件是 true 还是 false ,都会将对应的元素添加到 dom 中,在条件为 false 时将元素的 css 属性display设置为none,所以我们只是肉眼看到元素从页面中消失了,但是它还存在于 dom 当中。
  • v-ifv-show 的使用场景?
    • v-if 适用于条件变化频率不高的时候,这样不会频繁的去对 DOM 进行增删操作;
    • v-show 适用于需要做频繁切换的时候,比如说A和B两个元素,需要一会显示 A ,一会显示 B ,这样就算比较频繁。如果去操作 v-if ,让 DOM 疯狂的增添和销毁是会非常耗费性能的,所以这个时候就应该用 v-showdisplay 属性原本就已经存放在 DOM 当中,只是对 display 属性进行修改修做即可。

具体使用方式如下代码所示:

<template><div><p v-if="type === 'a'">A</p><p v-else-if="type === 'b'">B</p><p v-else>other</p><p v-show="type === 'a'">A by v-show</p><p v-show="type === 'b'">B by v-show</p></div>
</template><script>
export default {data() {return {type: 'a'}}
}
</script>

(5)循环(列表)渲染

  • vue 中如何遍历对象? —— 使用 v-for

  • v-forv-if 不能一起使用。

  • key 的重要性。在对数据进行 v-for 遍历时,需要加上 key 值来保证数据的唯一性。同时, key 不能乱写,一般不要使用 random 或者 index ,而是绑定一个与该绑定数据相关的唯一值来确定。如果用 random 或者 index 时,在对数据进行删除或增添操作时,有可能会出现数据错乱等问题出现。

  • key 的作用。在对节点进行 diff 的过程中,判断两个节点是否为相同节点的一个很重要的条件就是 key 值是否相等,如果 key 值相等,则说明两个节点是相同节点,所以会尽可能的复用原有的 DOM 节点,减少不必要的 DOM 操作,提升系统性能。

下面用一段代码演示 v-for 遍历数组和遍历对象时的效果。

<template><div><p>遍历数组</p><ul><li v-for="(item, index) in listArr" :key="item.id">{{index}} - {{item.id}} - {{item.title}}</li></ul><p>遍历对象</p><ul ><li v-for="(val, key, index) in listObj" :key="key">{{index}} - {{key}} -  {{val.title}}</li></ul></div>
</template><script>
export default {data() {return {listArr: [{ id: 'a', title: '今天周一' }, // 在数据结构中,最好有 id ,目的是为了方便使用 key{ id: 'b', title: '今天周二' },{ id: 'c', title: '今天周三' }],listObj: {a: { title: '今天周一' },b: { title: '今天周二' },c: { title: '今天周三' },}}}
}
</script>

此时浏览器的显示效果如下所示:
v-for

(6)事件

1)event参数,自定义参数

下面通过绑定一个两个按钮事件,来观察 vue 中的事件,参数时怎么进行传递的,事件又会被绑定到哪里去?

<template><div><!-- <p>{{num}}</p> --><!-- event参数 --><button @click="increment1">+1</button><!-- 自定义参数 --> <button @click="increment2(2, $event)">+2</button><div>递增值:{{num}}</div></div>
</template><script>
export default {data() {return {num: 0}},methods: {increment1(event) {console.log('event', event, event.__proto__.constructor) // 是原生的 event 对象console.log(event.target) //vue中的event,放在什么元素下,就会被挂载在什么元素下console.log(event.currentTarget) // 注意,事件是被注册到当前元素的,和 React 不一样this.num++// 1. event 是原生的// 2. 事件被挂载到当前元素// 和 DOM 事件一样},increment2(val, event) {console.log(event.target)this.num = this.num + val}}
}
</script>

大家可以看到,上面的 increment1 方法,没有传递参数,则它可以直接使用 event 来触发当前对象;而 increment2 方法中,通过传递了一个 2 的参数,我们可以通过 $event 的方式,把 2 传递进来,之后同样用 event 触发原生对象。

同时,当触发到当前元素时,事件会被挂载到当前元素,和 DOM 事件一样。

此时浏览器的打印效果如下:

$event

2)自定义事件

上面的演示中, vue 事件是直接挂载到 vue 上面进行监听的。而下面的代码中,我们可以看到,如果我们需要自定义一个事件,而不进行直接挂载,则可以通过 addEventListener 来进行注册。

但值得注意的是,用 vue 绑定的事件,组件销毁时会自动进行解绑。但是呢,如果是自己定义的事件需要自己再进行手动销毁!!这一点需要特别注意,不然写代码过程很容易因为没有销毁热导致后面引发的一系列 bug 出现。

<template><div></div>
</template><script>
export default {data() {return {num: 0}},methods: {loadHandler() {// do some thing}},mounted() {window.addEventListener('load', this.loadHandler)},beforeDestroy() {//【注意】用 vue 绑定的事件,组建销毁时会自动被解绑// 但如果是,自己绑定的事件,需要自己销毁!!!window.removeEventListener('load', this.loadHandler)}
}
</script>

3)事件修饰符

对于 vue 来说,经常用到的修饰符有下面6种事件修饰符3种按键修饰符

<!-- 事件修饰符 --><!-- 阻止单击事件继续传播 -->
<a v-on:stop="doThis"></a><!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form><!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a><!-- 只有修饰符 -->
<form v-on:submit.prevent></form><!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div><!-- 只当在event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
<!-- 按键修饰符 --><!-- 即使Alt 或 Shift 被一同按下时也会触发 -->
<button @click.ctrl="onClick">A</button><!-- 有且只有ctrl被按下时候才会触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button><!-- 没有任何系统修饰符被按下的时候才触发 -->
<button @click,exact="onClick">A</button>

(7)表单

在vue项目中,主要使用 v-model 指令在表单 inputtextareacheckboxradioselect 等元素上创建双向数据绑定

对于 v-model 来说,常见的修饰符有 trimlazynumbertrim 表示去除输入内容前后的空格, lazy 表示需要按回车键数据才会进行创建, number 表示把用户输入的值转换为 number 类型,如果先输入字符,则整串数据都会被当成字符串 string 类型看待,如果先输入数字,后输入字母,则后面的字母无法输入到输入框当中。

接下来我们用代码演示一遍常见的表单。

<template><div><p>输入框: {{name}} {{age}}</p><!-- .trim去除前后空格 --><input type="text" v-model.trim="name"/><!-- .lazy需要按回车键数据才会起作用 --><input type="text" v-model.lazy="name"/><!-- .number表示把用户输入的值转为number类型 --><input type="text" v-model.number="age"/><hr><p>多行文本: {{intro}}</p><textarea v-model="intro"></textarea><!-- 注意,<textarea>{{intro}}</textarea> 是不允许的!!! --><hr><p>复选框 {{checked}}</p><input type="checkbox" v-model="checked"/><hr><p>多个复选框 {{checkedNames}}</p><input type="checkbox" id="jack" value="Jack" v-model="checkedNames"><label for="jack">Jack</label><input type="checkbox" id="john" value="John" v-model="checkedNames"><label for="john">John</label><input type="checkbox" id="mike" value="Mike" v-model="checkedNames"><label for="mike">Mike</label><hr><p>单选 {{gender}}</p><input type="radio" id="male" value="male" v-model="gender"/><label for="male"></label><input type="radio" id="female" value="female" v-model="gender"/><label for="female"></label><hr><p>下拉列表选择 {{selected}}</p><select v-model="selected"><option disabled value="">请选择</option><option>A</option><option>B</option><option>C</option></select><hr><p>下拉列表选择(多选) {{selectedList}}</p><select v-model="selectedList" multiple><option disabled value="">请选择</option><option>A</option><option>B</option><option>C</option></select></div>
</template><script>
export default {data() {return {name: 'Monday',age: 18,intro: '自我介绍',checked: true,checkedNames: [],gender: 'female',selected: '',selectedList: []}}
}
</script>

此时浏览器的显示效果如下:

表单

3、组件

(1)props和$emit(适合父子组件间的通信)

传递形式通信方式
$emit子组件向父组件传递数据
props父组件的数据需要通过props把数据传给子组件,props的取值可以是数组也可以是对象

(2)组件间通信 - 自定义事件

除了父子组件通信外,一般还有兄弟间的组件通信。对于兄弟间的组件通信来说, 兄弟1 可以通过 event.$on 来绑定自定义事件,绑定完成之后,需要再 beforeDestroy 的生命周期里面用 event.$off及时销毁自定义事件, 防止内存泄漏发生。之后 兄弟2 通过 event.$emit 来调用 兄弟1 的自定义事件。

下面我们结合父子组件通信和兄弟组件通信的知识点,来实现一个添加和删除元素的功能。具体代码如下:

父组件index.vue:

<template><div><!-- 当前使用是父组件,背后的组件是子组件,即input.vue和List.vue是子组件 --><Input @add="addHandler"/><List :list="list" @delete="deleteHandler"/></div>
</template><script>
import Input from './Input'
import List from './List'export default {components: {Input,List},data() {return {list: [{id: 'id-1',title: '标题1'},{id: 'id-2',title: '标题2'}]}},methods: {addHandler(title) {this.list.push({id: `id-${Date.now()}`,title})},deleteHandler(id) {// 过滤出不等于当前删除内容的元素this.list = this.list.filter(item => item.id !== id)}}
}
</script>

子组件List.vue:

<template><div><ul><li v-for="item in list" :key="item.id">{{item.title}}<button @click="deleteItem(item.id)">删除</button></li></ul></div>
</template><script>
import event from './event'export default {// props: ['list']props: {// prop 类型和默认值list: {type: Array,default() {return []}}},data() {return {}},methods: {deleteItem(id) {this.$emit('delete', id)},addTitleHandler(title) {console.log('on add title', title)}},created() {},mounted() {// 绑定自定义事件//event是通过实例化一个vue对象,来调用vue中本身具有的自定义事件能力event.$on('onAddTitle', this.addTitleHandler)}beforeDestroy() {// 及时销毁,否则可能造成内存泄露event.$off('onAddTitle', this.addTitleHandler)}
}
</script>

子组件Input.vue:

<template><div><input type="text" v-model="title"/><button @click="addTitle">add</button></div>
</template><script>
import event from './event'export default {data() {return {title: ''}},methods: {addTitle() {// 调用父组件的事件this.$emit('add', this.title)// 调用自定义事件,调用List.vue中自定义的事件event.$emit('onAddTitle', this.title)this.title = ''}}
}
</script>

event.vue:

import Vue from 'vue'export default new Vue()

此时浏览器的显示效果如下:

props-$emit

(3)组件生命周期

1)组件生命周期(单个组件)

一般来说,组件生命周期的执行顺序为:挂载阶段 → 更新阶段 → 销毁阶段。下面给出常用组件生命周期的解析。

生命周期钩子介绍
beforeCreate在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。
created页面还没有渲染,但是vue的实例已经初始化结束。
beforeMount在挂载开始之前被调用:相关的 render 函数首次被调用。
mounted页面已经渲染完毕。
beforeUpdate数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
updated由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。
activatedkeep-alive 组件激活时调用。
deactivatedkeep-alive 组件停用时调用。
beforeDestroy实例销毁之前调用。在这一步,实例仍然完全可用。常用场景有: 自定义事件的绑定要解除、setTimeout等定时任务需要销毁、自己绑定的window或者document事件需要销毁。
destroyedVue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

2)组件生命周期(父子组件)

加载渲染过程

  父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted

子组件更新过程

  父beforeUpdate->子beforeUpdate->子updated->父updated

父组件更新过程

  父beforeUpdate->父updated

销毁过程

  父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

二、vue的高级特性

vue的高级特性主要有以下6种:

  • 自定义 v-model
  • $nextTick
  • slot
  • 动态、异步组件
  • keep-alive
  • mixin

下面对这6种高级特性进行一一讲解。

1、自定义 v-model

在文章的前半部分我们有讲到了 v-model 在表单中的应用,那么接下来我们将动手来实现一个 v-model

第一步,我们先定义一个子组件,名字叫 CustomVModel.vue ,具体代码如下:

<template><!-- $emit是子组件往父组件传递数据 --><input type="text":value="text1"@input="$emit('change1', $event.target.value)"><!--1. 上面的 input 使用了 :value 来绑定数据,而不是使用 v-model2. 上面的 change1 和 model.event 要对应起来3. 上面的 text1 与下面props的 text1 属性对应起来-->
</template><script>
export default {model: {prop: 'text1', // 对应下面 props 的 text1event: 'change1'},props: {text1: String,default() {return ''}}
}
</script>

第二步,我们在父组件中使用上面的这个子组件:

<template><div><p>vue 高级特性</p><hr><!-- 自定义 v-model --><p>{{name}}</p><CustomVModel v-model="name"/></div>
</template><script>
import CustomVModel from './CustomVModel'export default {components: {CustomVModel},data() {return {name: 'Monday'}}
}
</script>

通过上面的代码我们可以发现,通过绑定 value 属性和 input 事件这两个语法糖,最终实现数据的双向绑定。

此时我们看下浏览器的显示效果。

自定义v-model

通过上图我们自己发现,结果跟实际的 v-model 结果是一样的。至此,我们就实现了自定义的 v-model ,以此来操作数据的双向绑定

2、$nextTick

(1)知识点

先来了解三个知识点:

  • Vue异步渲染
  • data 发生改变之后, DOM 不会立刻进行渲染;
  • $nextTick 会在 DOM 渲染之后被触发,以获取最新 DOM 节点。

(2)例子展示🌰

假设我们现在要实现一个功能,当我们点击按钮时,打印出列表的项数。这个时候我们大多人可能会这么操作。

<template><div id="app"><!-- ref的设置时为了方便后续可以用来:取节点的DOM元素 --><ul ref="ul1"><li v-for="(item, index) in list" :key="index">{{item}}</li></ul><button @click="addItem">添加一项</button></div>
</template><script>
export default {name: 'app',data() {return {list: ['a', 'b', 'c']}},methods: {addItem() {this.list.push(`${Date.now()}`)this.list.push(`${Date.now()}`)this.list.push(`${Date.now()}`)// 获取 DOM 元素const ulElem = this.$refs.ul1console.log( ulElem.childNodes.length )}}
}
</script>

此时浏览器的显示效果如下。

nextTick

细心的小伙伴已经发现,浏览器并没有按照我们所想的打印。当页面上的列表显示 6项 内容时,此时控制台只打印 3项 ;当显示 9项 时,此时控制台直接只打印 6项

那这究竟时为什么呢?

其实,当我们点击的那一刻, data 发生变化,但是 DOM 并不会立刻进行渲染。所以等到我们点击完成的时候,获取的元素还是原来触发的内容,而不会增添上新的内容。

那我们所期望的是,当点击之后立刻触发 DOM 渲染并拿到最新的值。这个时候就需要用到 nextTick 。具体代码如下:

<script>
export default {name: 'app',data() {return {list: ['a', 'b', 'c']}},methods: {addItem() {this.list.push(`${Date.now()}`)this.list.push(`${Date.now()}`)this.list.push(`${Date.now()}`)// 1. 异步渲染,$nextTick 待 DOM 渲染完再回调,//    即NextTick函数会在多次data修改完并且全部DOM渲染完再触发,仅在最后触发一次// 2. 页面渲染时会将 data 的修改做整合this.$nextTick(() => {// 获取 DOM 元素const ulElem = this.$refs.ul1console.log( ulElem.childNodes.length )})}}
}
</script>

我们通过给获取DOM元素的代码外面再嵌套一层 $nextTick 函数,来达到我们想要的效果。在此过程中,当我们点击结束后,data的值发生变化,此时 $nextTick 会等待DOM全部渲染完成之后再进行回调。

最终浏览器的打印效果如下:

nextTick2

3、slot插槽

(1)插槽的作用

让用户可以拓展组件,去更好地复用组件和对其做定制化处理

(2)三种插槽类型

1)普通slot插槽

slot的基本使用方法是,父组件往子组件中插入一段内容。

我们来演示一下。

假设我们现在有一个子组件,名字叫SlotDemo.vue,它的代码如下。

<template><a :href="url"><slot></slot></a>
</template><script>
export default {props: ['url'], data() {return {}}
}
</script>

我们可能希望这个 <a> 标签内绝大多数情况下都渲染文本 Sunday ,但是有时候却希望渲染文本不是 Sunday ,而是其他内容,那该怎么实现呢?

我们就可以将 Sunday 作为后备内容,这个时候把它放在 <slot> 标签内:

<a :href="url"><slot>Sunday</slot></a>

现在,我定义一个父级组件,名字叫 index.vue ,并且在父级组件中引用上面的 SlotDemo 组件,具体代码如下:

<template><div><p>vue 高级特性</p><hr><SlotDemo :url="website.url"></SlotDemo></div>
</template><script>
import SlotDemo from './SlotDemo'export default {components: {SlotDemo},data() {return {name: 'Monday',website: {url: 'https://blog.csdn.net/weixin_44803753',title: 'Monday',subTitle: '穿梭于前端开发的学习永动机'}}}
}
</script>

这个时候后备内容 Sunday 就会被渲染出来。此时浏览器显示效果如下:

后备内容Sunday

那么如果想要把这个后备内容渲染成我们想要的内容,而不是 Sunday 呢,这个时候我们应该这么做:

<SlotDemo :url="website.url">{{website.title}}
</SlotDemo>

我们可以通过在父组件 index.vue 中引用的子组件 SlotDemo.vue 里面,插入我们想要的内容。这个时候子组件 SlotDemo.vue后备内容 Sunday 就会渲染成我们提供的新内容,以此来达到我们想要的目的。

此时浏览器的打印效果如下:

后备内容Monday

2)作用域插槽slot-scope

vue官方的说法给到的解释是:

  • 父组件模板的所有东西都会在父级作用域内编译;
  • 子组件模板的所有东西都会在子级作用域内编译。

这样看起来好像有点难懂,接下来我们用一个例子来了解作用域插槽想要解决的问题究竟是什么!

首先,我们先定义一个子组件,名字叫 ScopedSlotDemo.vue具体代码如下所示:

<template><a><slot>{{website.subTitle}} <!-- 默认值显示 subTitle ,即父组件不传内容时 --></slot></a>
</template><script>
export default {props: ['url'],data() {return {website: {url: 'http://tinymce.ax-z.cn/',title: 'tinymce',subTitle: '一款易用、且功能强大的富文本编辑器'}}}
}
</script>

通过以上代码可以分析,这个时候我们想要把 slot 里面想要呈现的内容绑定下方的data数据,于是通过 {{website.subTitle}} 引用。


接下来我们定义一个父组件,名字叫 index.vue ,父组件代码如下:

<template><div><p>vue 高级特性</p><hr><ScopedSlotDemo>{{website.title}}</ScopedSlotDemo></div>
</template>
<script>
import ScopedSlotDemo from './ScopedSlotDemo'export default {components: {ScopedSlotDemo},data() {return {name: 'Monday'}}
}
</script>

我们希望父组件不显示子组件的后备内容 subTitle ,而是显示子组件的另外一个 data 属性 ➡ title ,于是我们按照默认绑定的操作,直接在父组件进行 {{website.title}} 的绑定。此时我们运行一下:

作用域插槽报错

阿欧!这是什么东西呢?为什么会报错呢?

这就谈到了我们开头说的定义了。在vue当中,父组件模板的所有东西只会在父级作用域内编译;子组件模板的所有东西只会在子级作用域内编译。

所以,我们在父组件当中引用子组件的内容,当然是要报错的。这就相当于,一个父亲称呼他的儿子为爸爸一样,怎么可能嘛对吧!

在vue中,父级组件只能访问父级的数据,而子级组件也只能访问子级的数据,不能跃级访问。

因此,有了这个问题的出现,我们引用了作用域插槽来解决这个问题。怎么做呢?来看下面内容。


我们需要在父级作用域 index.vue 中为我们要绑定的元素设置一个 v-slot ,这个 v-slot 用来定义我们提供的插槽 prop 的名字。

index.vue:

<ScopedSlotDemo>    <template v-slot="slotProps">		{{slotProps.slotData.title}}    </template>
</ScopedSlotDemo>

那么父级定义完只是第一步,接下来我们要让子级 ScopedSlotDemo.vue 的插槽内容在父级当中可用,于是我们需要在子级的插槽中绑定一个自定义事件,并且指向我们需要的数据,如下代码所示:

ScopedSlotDemo.vue:

<slot v-bind:slotData="website">	{{website.subTitle}} 
</slot>

从上面我们可以看到,我们通过绑定一个自定义事件 v-bind:slotData ,并把它指向我们想要的数据 website 。指向之后,我们在父级的插槽当中,通过 slopProps.slotData ,就可以访问到自己子级 website 的数据,这样,就达到最终我们想要的效果。

最终浏览器访问结果:

作用域插槽成功访问子级数据


最后贴上完整的父子级代码,大家可以根据以上讲解进行演示。

子级 ScopedSlotDemo.vue:

<template><a><slot>{{website.subTitle}} <!-- 默认值显示 subTitle ,即父组件不传内容时 --></slot></a>
</template><script>
export default {props: ['url'],data() {return {website: {url: 'http://tinymce.ax-z.cn/',title: 'tinymce',subTitle: '一款易用、且功能强大的富文本编辑器'}}}
}
</script>

父级 index.vue:

<template><div><p>vue 高级特性</p><hr><ScopedSlotDemo><template v-slot="slotProps">{{slotProps.slotData.title}}</template></ScopedSlotDemo></div>
</template><script>
import ScopedSlotDemo from './ScopedSlotDemo'export default {components: {ScopedSlotDemo},data() {return {name: 'Monday',website: {url: 'https://blog.csdn.net/weixin_44803753',title: 'Monday',subTitle: '穿梭于前端开发的学习永动机'}}}
}
</script>

3)具名插槽

具名插槽的定义: 将父组件中的内容插入指定的子组件位置中。

接下来来看一下具名插槽是怎么使用的?

有时候我们一个组件里需要多个插槽。比如,现在我们有一个子组件,名字叫 named.vue ,具体代码如下:

<template><div><header><!-- 我们希望把标题放这里 --></header><main><!-- 我们希望把主体内容放这里 --></main><footer><!-- 我们希望把页脚放这里 --></footer></div>
</template>

大家可以看到,上面的代码中,我们希望在 headermainfooter 当中放入对应模块的内容进去,这个时候就需要使用多个插槽来解决。


那怎么处理呢? 对于这样的情况, <slot> 元素有一个特殊的属性: name ,这个属性可以用来定义额外的插槽。我们通过使用 name 属性来对子组件 named.vue 的插槽加上属性名。具体做法如下:

<template><div><header><slot name="header"></slot></header><main><!-- 如果一个<slot>不带name属性的话,那么它的name默认为default --><slot></slot></main><footer><slot name="footer"></slot></footer></div>
</template>

值得注意的是,如果一个 <slot> 不带 name 属性的话,那么它的 name 默认为 default


子级组件插入完插槽之后,接下来我们来定义一个父组件 index.vue 。在向子级组件的具名插槽提供内容的时候,我们可以在父级组件的 <template> 元素上使用 v-slot 指令,并以参数的形式传递具名插槽的名称。

index.vue:

<template><div><template v-slot:header><h1>这里是页面标题</h1></template><p>页面主题内容第一页</p><p>页面主题内容第二页</p><template v-slot:footer><p>这里是页脚</p></template></div>
</template>

现在 <template> 元素中的所有内容都将会被传入到子组件相应的插槽当中,且任何没有被包裹在带有 v-slot<template> 中的内容都会被视为默认插槽的内容。

如果你希望更加明确一点的话,那就把主体内容包裹上一个插槽,并设置 name="default"

<template v-slot:default><p>页面主题内容第一页</p><p>页面主题内容第二页</p>
</template>

这样看起来或许会更优雅一点。


最终打印结果如下:

这里是页面标题页面主题内容第一页
页面主题内容第二页这里是页脚

讲完具名插槽,我们来对它的使用方法进行一个总结归纳。

具名插槽的使用语法:

  • 子组件定义 slot 时,在标签上加上 name='xxx' 属性,不写 name 则默认 name=default
  • 父组件将想插入的部分最外部的 div 上加上 v-slot="xxx" 属性, xxx 对应name的值。
  • v-slot: 可以简写为 # ,如 v-slot:header 相当于 #header

都说一个萝卜一个坑,具名插槽通过将父组件中的内容一个一个对应地插入到指定的子组件位置当中,把坑填上了,萝卜也就长出来了。

4、动态、异步组件

(1)动态组件

1)举个例子🌰

比如我们在做某个新闻网站的文章详情页,那这个时候文章详情页显示的内容是非常多样化的。有可能是文本、视频和图片类型,也有可能是文本和图片类型,这个时候我么就没有办法确定这个组件想要的是什么类型的内容,于是我们据需要用到动态组件。

2)动态组件的用法和应用场景

  • 用法: :is = "component-name" 用法;
  • 应用场景: 需要根据 数据 进行动态渲染的场景,即组件类型不确定的场景。

3)演示

演示1:

我们定义一个子组件,名字叫 Dynamic.vue ,具体代码如下:

<template><div><p>动态组件子组件</p></div>
</template><script>export default {data() {reuturn{}}
}
</script>

接下来定义一个父组件,名字叫 index.vue 。我们希望地引入子组件 Dynamic.vue 。因此,我们可以这样做:

<template><div><p>vue 高级特性</p><hr><!-- 动态组件 --><component :is="DynamicName"/></div>
</template><script>
import Dynamic from './Dynamic'
export default {components: {Dynamic},data() {return {DynamicName: "Dynamic"}}
}
</script>

这个时候我们可以发现,通过在 data 中绑定组件的名字,之后在视图上用 is="ComponentName" 的形式进行引用,达到对组件进行动态绑定的效果。


演示2:

假设我们要给某一个页面上的一组数据进行动态绑定,这个时候我们可以这么做:

<template><div><!-- 动态组件 --><div v-for="(val, key) in newsData" :key = "key"><component :is = "val.type"/></div></div>
</template><script>export default {data() {return {newsData:{1:{type:'text'},2:{type:'text'},3:{type:'image'}}}}
}
</script>

(2)异步组件

平常我们在开发中,通常用的是 import()函数 来引入一个组件,但是import虽然好用,可是大家有没有想过,如果当我们要引入一个比较大的组件时,也用 import 去引入的话,它会进行同步加载操作,这样子对性能优化用户体验来说是非常不友好的。

因此,我们要引入异步组件来解决 import 同步加载的问题。

我们来举个例子:

<template><div><!-- 异步组件 --><FormDemo v-if="showFormDemo"/><button @click="showFormDemo = true">show form demo</button></div>
</template><script>
export default {components: {FormDemo: () => import('./FormDemo')},data() {return {showFormDemo: false}}
}
</script>

通过上面的代码,我们可以了解到,通过控制 showFormDemo 的布尔值,来决定 FormDemo 组件在什么时候引入。因此,在button点击之前, FormDemo 的判断条件 showFormDemofalse ,这个时候组件不会被加载。只有当 button 点击之后, FormDemo 的判断条件 showFormDemo 变为true,这个时候才按需将组件的内容加载出来。

通过以上分析,大家应该可以了解到, import 是同步加载,不管页面需不需要这个组件,都会在页面加载时就加载出来。

而如果将组件直接注册在 components 中,通过布尔值数据来控制组件是否加载,而不会从一开始就加载,这样的方式来加载组件,从性能上会好非常多。


因此,我们对异步组件做出以下总结:

  • 不适合用 import() 函数;
  • 按需加载,异步方式加载大组件;
  • 异步加载,即什么时候用什么时候加载,什么时候不用,就永远不会加载。

5、keep-alive

(1)定义和应用场景

keep-alive ,从字面意思来讲就是,保持它存活着,保持它不死亡。

我们先抛出 keep-alive定义和应用场景:

  • keep-alive 是一个缓存组件;
  • 应用于频繁切换的场景,用 keep-alive 就不需要重复渲染;
  • keep-alivevue 中常见的性能优化方法。

(2)举例🌰

了解完定义和应用场景,我们来开始举例子看看这个缓存组件,到底是何方神圣,让人们在开发时都对之有所热爱。

首先,我们先来定义一个父组件,名字叫 keepAlive.vue ,具体代码如下:

keepAlive.vue:

<template><div><button @click="changeState('A')">button A</button><button @click="changeState('B')">button B</button><button @click="changeState('C')">button C</button><KeepAliveStageA v-if="state === 'A'"/> <KeepAliveStageB v-if="state === 'B'"/><KeepAliveStageC v-if="state === 'C'"/></div>
</template><script>
import KeepAliveStageA from './KeepAliveStateA'
import KeepAliveStageB from './KeepAliveStateB'
import KeepAliveStageC from './KeepAliveStateC'export default {components: {KeepAliveStageA,KeepAliveStageB,KeepAliveStageC},data() {return {state: 'A'}},methods: {changeState(state) {this.state = state}}
}
</script>

我们再来定义三个子组件,名字分别为 keepAliveStateA.vuekeepAliveStateB.vuekeepAliveStateC.vue 。 具体代码如下:

keepAliveStateA.vue:

<template><p>state A</p>
</template><script>
export default {mounted() {console.log('A mounted')},destroyed() {console.log('A destroyed')}
}
</script>

keepAliveStateB.vue:

<template><p>state B</p>
</template><script>
export default {mounted() {console.log('B mounted')},destroyed() {console.log('B destroyed')}
}
</script>

keepAliveStateC.vue:

<template><p>state C</p>
</template><script>
export default {mounted() {console.log('C mounted')},destroyed() {console.log('C destroyed')}
}
</script>

此时我们来看下浏览器的运行效果:

没有keep-alive组件时

通过以上动图可以发现,我们初始化时是状态 A ,之后当我们每次点击按钮时,编译器都会对这个组件进行销毁,再创建我们点击的新组件。这样反复的创建销毁,创建销毁,对用户的体验似乎是不太好的。因为用户每点击一个东西就得重新下载,如果遇到代码量很多的页面呢?那用户可能会从此把这个网站拉入黑名单了。

因此,我们可以使用vue中的 keep-alive ,来解决这个问题。我们想要达到的目的很简单,就是把用户每次点击后的内容给缓存下来,之后用户每次点击时就可以直接从缓存中拉取资源直接显示,而无需重新加载。

那么,怎么做呢?

很简单,我们只需要在父组件 keepAlive.vue 中引入的三个子组件外面,再包上一个缓存组件 keep-alive , 这样,就能达到我们想要的目的了。

<keep-alive> <KeepAliveStageA v-if="state === 'A'"/><KeepAliveStageB v-if="state === 'B'"/><KeepAliveStageC v-if="state === 'C'"/>
</keep-alive>

我们来看下此时浏览器的打印效果:

有keep-alive组件时

我们可以发现,有keep-alive时,浏览器直接把子组件的内容缓存下来了,当我们再次点击它时,就不会再进行重复的创建和销毁,直接从缓存中拉取资源,并加载到我们的页面中来。

6、mixin

(1)mixin是什么

我们先来了解下 mixin 的定义:

  • 多个组件有相同的逻辑,抽离出来,当作 mixin 来使用。
  • mixin 并不是完美的解决方案,会存在一些小问题的出现。
  • Vue 3 提出的 Composition API 旨在解决这些问题。这里不对 Vue 3Composition API 进行细讲,将在后面的文章中做介绍。

(2)mixin的问题

mixin虽说可以用来抽离相同逻辑的组件,但是总会存在各种各样的问题。比如:

  • 变量来源不明确,不利于阅读。
  • 多个 mixin 可能会造成命名冲突
  • mixin 和组件可能出现多对多的关系,复杂度较高。

(3)举个栗子🌰

我们先来定义一个组件,名字叫 MixinDemo.vue ,具体代码如下:

<template><div><p>{{name}}: {{intro}}: {{city}}</p><button @click="showName">显示姓名</button></div>
</template><script>
import myMixin from './mixin'export default {mixins: [myMixin], // 可以添加多个,会自动合并起来data() {return {name: 'Monday',intro: '热爱前端的学习永动机'}},methods: {},mounted() {console.log('component mounted', this.name)}
}
</script>

再定义一个 js 文件,名字叫 mixin.js具体代码如下:

export default {data() {return {city: '广东'}},methods: {showName() {console.log(this.name)}},mounted() {console.log('mixin mounted', this.name)}
}

此时浏览器的演示效果如下:

mixin

我们来解析下上面这波操作。假设此时 js 文件的代码是多个组件中抽离出来的比较相似的代码部分,那我们我们对它进行抽离后并注册到 MixinDemo.vue 当中,用 mixins: [myMixin] 的方式进行引入。此时,我们在 MixinDemo.vue 组件中就可以访问到 mixin.js 里面的内容,这样就完成了一波逻辑复用。

mixin看着是挺好用,但是它会存在各种各样的问题发生。这就谈到我们第二点中所说的。

我们来对问题进行一一剖析。

第一个,变量来源不明确,不利于阅读。 比如我们的第一块代码,里面有一个按钮绑定了showName的方法,此时showName的方法放在了mixin里面,而没有放在该页面中。假如我们定义了多个mixin,那我们得对每个mixin一个一个地去找showName这个方法,因为我们也不知道showName来自于哪里,只能这样干。那这样子的话得有多麻烦,非常的影响开发效率。

第二个,多个 mixin 可能会造成命名冲突。 比如说当出现多个 mixin 时,里面的 data 绑定的属性一模一样,那这个时候就有可能出现覆盖的情况,也很不利于我们找错。

第三个, mixin 和组件可能出现多对多的关系,复杂度较高。 我们在开发过程中,有可能会遇到一个组件引入多个 mixin ,也有可能多个组件引入一个 mixin ,这样就是一个多对多的关系。这有一天如果某个组件想要改个属性值,但是它只能动 mixin 。这一动,其他组件就都被影响到了,很容易导致了剪不断理还乱的尴尬局面。

所以, mixin 有它的好处所在,也有它的问题所在。而 vue3 中的 composition API 就旨在解决这个问题。这里先给大家抛个概念, composition API 将在后面的文章中进行讲解。

三、vue的周边插件:vuex和vue-router

1、vuex

(1)vuex基本概念

  • state:单一状态树,用一个对象来包含全部的应用层级状态
  • getters: vuex 允许我们在 store 中定义 getter ,就像计算属性一样, getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生改变时,才会被重新计算。
  • action:异步逻辑封装;
  • mutation:更改状态的唯一方法,并且这个过程是同步的

(2)用于vue组件中的API

  • dispatch
  • commit
  • mapState
  • mapGetters
  • mapActions
  • mapMutations

(3)图例

vuex

对于 vuex 来说,先理解上面这几个概念,后面还会有文章讲解关于 vuex 的实际应用。

2、vue-router

(1)路由模式

1)hash 模式(默认)

hash模式,如 http://abc.com/#/user/10 。早期的前端路由就是基于 location.hash 来实现的。其实原理很简单, location.hash 的值就是 url# 后面的内容。比如下面这个网站,它的 location.hash 就是 #abc

https://www.baidu.com#abc

2)H5 history模式

history 模式,如 http://abc.com/user/20 ;与 hash 模式不同的是,这种模式需要 server 端支持,因此无特殊需求可选择前者

HTML5 提供了 History API 来实现 URL 的变化。其中最主要的 API 有以下两个。 history.pushState()history.replaceState() 。这两个 API 可以在不刷新的情况下,操作浏览器的历史记录。唯一不用的是,前者是新增一个记录,后者是直接替换当前的历史记录如下所示:

window.history.pushState(null, null, path);
window.history.replaceState(null, null, path);

同时,对于 history 来说,如果刷新时,服务器没有响应的资源,会报错 404

(2)路由配置

1)动态路由

动态路径参数,以 冒号 : 开头进行路由配置。如下所示:

const User = {//获取参数如 3 5template: '<div>User {{$route.params.id}}</div>'
}const router = new VueRouter({routes:[//动态路径参数 以冒号开头。能命中`/user/3` `/user/5` 等格式的路由{path: 'user/:id', component: User}]
})

2)懒加载

懒加载即对应一个 import() 函数,然后去导入一个组件。跟我们前面主题二的第4小点讲过的异步组件类似,当我们去访问一级二级等各种级别的的路由时,就会异步的去把对应的组件加载出来。不会出现一下子全部同时加载的问题,而是该加载时再加载,可以理解为我需要你的时候你再出现就好了示例代码如下所示:

export default new VueRouter({routes:[{path:'/',component: () => import('./../compoents/Navigator')},{path:'/detail',component: () => import('./../components/Detail')}]
})

四、结束语

以上文章总结了 vue 的基本使用以及 vue 的高级特性,还有概括了 vue 的周边插件 vuexvue-router 的一些常见知识,融合大量案例进行讲解。

前端在做 vue 的项目中,总是脱离不开以上文章所涉及到的内容,唯一的区别在于用的多和用的少的问题。

希望通过以上文章的讲解,小伙伴们能有所收获🥂

  • 关注公众号 星期一研究室 ,不定期分享学习干货,学习路上不迷路~
  • 如果这篇文章对你有用,记得点个赞加个关注再走哦~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/307996.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据库单表千万行 LIKE 搜索优化手记

我们经常在数据库中使用 LIKE 操作符来完成对数据的模糊搜索&#xff0c;LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式。如果需要查找客户表中所有姓氏是“张”的数据&#xff0c;可以使用下面的 SQL 语句&#xff1a;SELECT * FROM Customer WHERE Name LIKE 张%如果需要…

Mybatis第一个程序

一:整体流程 这个流程可以根据官方文档进行编写 mybatis的官方文档 二:实现 1:创建一个表 CREATE DATABASE mybatis;CREATE TABLE user(id INT NOT NULL PRIMARY KEY,name VARCHAR(30) NOT NULL DEFAULT ,pwd VARCHAR(20) NOT NULL DEFAULT )ENGINE INNODB;INSERT I…

基于.NetCore3.1系列 —— 日志记录之初识Serilog

前言对内置日志系统的整体实现进行了介绍之后&#xff0c;可以通过使用内置记录器来实现日志的输出路径。而在实际项目开发中&#xff0c;使用第三方日志框架&#xff08;如&#xff1a;Log4Net、NLog、Loggr、Serilog、Sentry 等&#xff09;来记录也是非常多的。首先一般基础…

手把手教你剖析vue响应式原理,监听数据不再迷茫

Object.defineProperty实现vue响应式原理一、组件化基础1、“很久以前”的组件化&#xff08;1&#xff09;asp jsp php 时代&#xff08;2&#xff09;nodejs2、数据驱动视图&#xff08;MVVM&#xff0c;setState&#xff09;&#xff08;1&#xff09;数据驱动视图 - Vue MV…

leetcode:203. 移除链表元素(两种方法)

一:题目 二:上码 1:方法一&#xff1a;(虚拟一个首结点) class Solution { public:ListNode* removeElements(ListNode* head, int val) {//1.虚拟一个头结点 这样就不用单独处理了ListNode * virtuals new ListNode(0);//给其开辟个空间并且赋初值virtuals->next head…

面试中的网红虚拟DOM,你知多少呢?深入解读diff算法

深入浅出虚拟DOM和diff算法一、虚拟DOM&#xff08;Vitual DOM&#xff09;1、虚拟DOM&#xff08;Vitual DOM&#xff09;和diff的关系2、真实DOM的渲染过程3、虚拟DOM是什么&#xff1f;4、解决方案 - vdom&#xff08;1&#xff09;问题引出&#xff08;2&#xff09;vdom如…

Blazor带我重玩前端(六)

本文主要讨论Blazor事件内容&#xff0c;由于blazor事件部分很多&#xff0c;所以会分成上下两篇&#xff0c;本文为第二篇。双向绑定概述如图所示当点击单项绑定的时候&#xff0c;MyOnewayComponent里的属性值会发生变化&#xff0c;这种变化是单项的&#xff0c;仅仅只是本地…

leetcode707:设计链表(增删差)

一:题目 二:上码 class MyLinkedList { public://定义链表节点结构体struct LinkedNode {int val;LinkedNode* next;LinkedNode(int val):val(val), next(nullptr){}};// 初始化链表MyLinkedList() {node new LinkedNode(0); // 这里定义的头结点 是一个虚拟头结点&#xff0…

深入探究.Net Core Configuration读取配置的优先级

前言在之前的文章.Net Core Configuration源码探究一文中我们曾解读过Configuration的工作原理&#xff0c;也.Net Core Configuration Etcd数据源一文中探讨过为Configuration自定义数据源需要哪些操作。由于Configuration配置系统也是.Net Core的核心&#xff0c;其中也包含了…

TypeScript,从0到入门带你进入类型的世界

从0到入门进入TS的世界一、什么是TypeScript&#xff1f;1、编程语言的类型2、TypeScript究竟是什么&#xff1f;二、为什么要学习TypeScript&#xff1f;1、程序更容易理解2、效率更高3、更少的错误4、非常好的包容性5、一点小缺点三、typescript入门1、如何安装TypeScript2、…

编写第一个 .NET 微服务

介绍本文的目的是&#xff1a;通过创建一个返回列表的简单服务&#xff0c;并在 Docker 容器中运行该服务&#xff0c;让您熟悉使用 .NET 创建微服务的构建过程。安装 .NET SDK要开始构建 .NET 应用程序&#xff0c;首先下载并安装 .NET Core SDK&#xff08;软件开发工具包&am…

模板编译template的背后,究竟发生了什么事?带你了解template的纸短情长

解析模板编译template的背后发生了什么一、&#x1f4d1;初识模板编译1、vue组件中使用render代替template2、模板编译总结二、✏️感受模板编译的美1、with语法&#xff08;1&#xff09;例子展示&#x1f330;&#xff08;2&#xff09;知识点归纳三、&#x1f4c8;编译模板1…

leetcode24. 两两交换链表中的节点(思路+解析)

一:题目 二:思路 思路: 1.分析题意 这是相邻结点进行交换 如果是4个结点 那么1和2交换 3和4交换 如果是3个结点 那么就1和2进行交换 3不动 2.这里我们定义一个虚拟头节点方便操作&#xff0c;我们只需三步实现结点的交换 <1>:让虚拟结点指向第二个结点(进行交换的结点我…

把Autofac玩的和java Spring一样6

大家好&#xff0c;今天来介绍我开源的一个autofac.Annotation项目 源码&#xff1a;https://github.com/yuzd/Autofac.Annotation本项目是autofa的一个扩展组件&#xff0c;autofac是一个老牌的DI容器框架 &#xff0c;支持netframework和netcoreAnnotdation是注解的意思&…

『软件测试5』测开岗只要求会黑白盒测试?NO!还要学会性能测试!

浅谈软件测试中的性能测试一、&#x1f92a;性能测试概念1、为什么要有性能测试&#xff1f;2、性能测试是什么&#xff1f;3、性能测试的目的二、&#x1f910;性能测试指标1、响应时间2、吞吐量3、并发用户数4、TPS(Transaction Per Second)5、点击率6、资源利用率三、&#…

CLR的简单理解

CLR加载程序生成进程&#xff0c;一个进程中可以存在多个线程&#xff0c;当创建一个线程时&#xff0c;会分配1Mb的空间&#xff0c;也就是线程的栈空间&#xff0c;对应jvm的虚拟机堆栈&#xff0c;是线程执行过程中用到的工作内存。这片内存用于方法传递实参&#xff0c;并存…

『软件测试6』bug一两是小事,但安全漏洞是大事!

详解软件测试中的安全测试一、&#x1f4bf;安全测试概念1、安全测试概述2、安全测试与软件生命周期的关系3、常规测试与安全测试的不同&#xff08;1&#xff09;测试目标不同&#xff08;2&#xff09;假设条件不同&#xff08;3&#xff09;思考域不同&#xff08;4&#xf…

我们真的需要JWT吗?

JWT&#xff08;JSON Web Token&#xff09;是目前最流行的认证方案之一。博客园、各种技术公众号隔三差五就会推一篇JWT相关的文章&#xff0c;真的多如牛毛。但我对JWT有点困惑&#xff0c;今天写出来跟大家探讨探讨&#xff0c;不要喷哈。JWT原理本文默认读者已经对JWT有所了…

leetcode面试题 02.07. 链表相交

一:题目 二:思路 1.这道题我们是需要找到一个结点&#xff0c;并且从这个结点往后的结点都相等 2.我们需要将两个链表 右对齐 3.然后将长链表的指针移动到和短链表头结点相同的位置 4.接下来就是比较指针&#xff0c;当一个指针相同也就意味着往后的结点的数值也相等 三:上码…

详解队列在前端的应用,深剖JS中的事件循环Eventloop,再了解微任务和宏任务

队列在前端中的应用一、队列是什么二、应用场景三、前端与队列&#xff1a;事件循环与任务队列1、event loop2、JS如何执行3、event loop过程4、 DOM 事件和 event loop5、event loop 总结四、宏任务和微任务1、引例2、宏任务和微任务&#xff08;1&#xff09;常用的宏任务和微…