当前位置: 首页 > news >正文

vue2-基础核心

vue简介

动态构建用户界面的渐进式 JavaScript 框架

vue的特点:

  1. 遵循MVVM模式

  2. 采用组件化模式,提高代码复用率,让代码更好维护

  3. 声明式编码,无需直接操作DOM,提高开发效率,编码简洁、体积小,运行效率高

  4. 本身只关注UI,也可以引入其他三方库开发项目

  5. 使用虚拟DOM+优秀的Diff算法,尽量复用DOM节点

环境准备

1.导入vue

1、在页面以CDN包的形式导入

<!-- cdn链接 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16/dist/vue.js"></script><!--生产环境,推荐链接到一个明确的版本号和构建文件,以避免新版本造成的不可预期的破坏 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.16"></script>

2、下载Js源代码文件通过script导入

<script src="本地源代码路径"></script>

3、npm

在用 Vue 构建大型应用时推荐使用 npm 安装 。npm 能很好地和诸如webpack 或 Rollup 模块打包器配合使用

# 最新稳定版
npm install vue@next

4、脚手架工具(CLI)

Vue 提供了一个官方的 CLI,为单页面应用 (SPA) 快速搭建繁杂的脚手架。它为现代前端工作流提供了功能齐备的构建设置。只需要几分钟的时间就可以运行起来,并带有热重载、保存时 lint 校验,以及生产环境可用的构建版本

npm install -g @vue/cli

5、Vite

Vite 是尤雨溪新开发的一个 web 开发构建工具,由于其原生 ES 模块导入方式,可以实现闪电般的冷服务器启动,通过在终端中运行以下命令,可以使用 Vite 快速构建 Vue 项目

npm init vite@latest <project-name> --template vue
cd <project-name>
npm install
npm run dev

2.vue全局配置

Vue.config 是一个对象,包含 Vue 的全局配置,可以在启动应用之前修改

    // 阻止 vue 在启动时生成生产提示Vue.config.productionTip = false

vue实例

  • 想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象

  • 容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法

  • 容器里的代码被称为Vue模板

  • Vue实例和容器是一一对应的,一个vue实例只能和一个实例对应

  • 真实开发中只有一个Vue实例,并且会配合着组件一起使用

  • {{插值语法}}中可以写js表达式(1+1、Data.now()),且表达式可以自动读取到data中的所有属性

<body>
<!--创建ID为root的容器 -->
<div id="root"><!--  插值语法,关联创建的vue实例data对象中的name字段  -->
<h1>{{name}}</h1>
</div></body>
<script>// 阻止 vue 在启动时生成生产提示Vue.config.productionTip = false// 创建Vue实例,参数是一个配置对象new Vue({// el是element的缩写,el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串// 也可以使用el:document.getElementById("root")// 关联ID是root的容器el:"#root",// data是用于存储数据,数据供el指定的容器去使用data:{name:"vue"}})</script>

模版语法

Vue模板语法有2大类:

1、插值语法:

功能:用于解析标签体内容。

写法:{{xxx}},xxxjs表达式,且可以直接读取到data中的所有属性。

2、指令语法:

功能:用于解析标签(标签属性、标签体内容、绑定事件等等)

写法:Vue中有很多的指令且形式都是:v-xxxx:表达式, xxx是具体的指令,指令中同样写js表达式

<body>
<!--创建ID为root的容器 -->
<div id="root"><!--  插值语法--><h1>{{name}}</h1><!--指令语法--><!--   v-bind表示绑定,将data的url绑定给href,使用v-bind之后href的""就不再是字符串,而是js表达式  --><!--   等同于     <h2><a href="https://www.baidu.com">百度一下</a></h2>  --><h2><a v-bind:href="url">百度一下</a></h2><!--    v-bind简写形式,直接使用: --><h2><a :href="url">百度一下简写</a></h2></div></body>
<script>new Vue({el: "#root",data: {name: "vue",url: "https://www.baidu.com"}})</script>

数据绑定

Vue中有2种数据绑定的方式:

1、单向绑定(v-bind):数据只能从data流向页面。

2、双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data。

  • 双向绑定一般都应用在表单类元素(输入类元素)

  • v-model:value 可以简写为 v-model,因为v-model默认收集的就是value值

<body>
<div id="root">单向数据绑定: <input type="text" v-bind:value="name">单向数据绑定简写: <input type="text" :value="name"><br>双向数据绑定: <input type="text" v-model:value="info.category"><br>双向数据绑定简写: <input type="text" v-model="info.category"><!--    双向数据绑定-有一处地方数据发生变化,所有引用的地方都会发生变化。当数据模型中的数据发生变化时,与之绑定的视图会自动更新以反映新的数据状态;反之,当用户在视图层通过交互操作(如在输入框中输入内容、选择下拉菜单选项等)改变了数据,数据模型也会相应地更新--></div>
</body><script type="text/javascript">Vue.config.productionTip = falsenew Vue({el:'#root',data:{name:'vue',info:{category:"分类"}}})
</script>

vue实例创建的两种写法

1、第一种方式

	new Vue({// 第一种写法:创建实例的时候直接挂载对应的容器el:'#root',// 第一种写法:对象式data:{name:'vue'}})

2、第二种方式

    // 创建Vue实例const vm = new Vue({// 第二种方式:函数式,必须返回一个对象data:function (){// 此处的this是Vue实例对象return{name:"vue"}}})// 通过$mount方法绑定容器,$mount是vue原型上的方法,作用是将vue挂载指定容器vm.$mount("#root")

  • el使用哪种方式都可以,data的创建后续使用组件的时候,必须使用函数式

  • 由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例,而是Window

MVVM模型

  1. M:模型(Model) :data中的数据

  2. V:视图(View) :模板代码

  3. VM:视图模型(ViewModel):Vue实例data中所有的属性,最后都出现在了vm身上。vm身上所有的属性 及 Vue原型上所有属性,在Vue模板中都可以直接使用

Js的Object.defineProperty

用于在一个对象上定义一个新属性,或者修改一个已经存在的属性。它允许精确地添加或修改对象的属性,包括属性的值、可枚举性、可配置性和可读写性等

// 创建一个对象
let user = {name: "test",age: 18
}// 使用:Object.defineProperty(obj, prop, descriptor)
// 参数
//   obj:是要定义属性的目标对象。
//   prop:是要在目标对象上定义或修改的属性的名称。
//   descriptor:是一个对象,用于描述属性的各种特征。它可以包含以下键值对:
//              value:属性的值。例如,{ value: 42 }会将属性的值设置为 42。
//              writable:一个布尔值,表示属性是否可写。默认为false,则不能重新赋值。例如,{ writable: false, value: 'immutable' }定义了一个不可写的属性。
//              enumerable:一个布尔值,表示属性是否可枚举。默认为false,在使用for...in循环或者Object.keys()方法等列举对象属性时,该属性不会被列出
//              configurable:一个布尔值,表示属性是否可配置。默认为false,则不能删除该属性,并且除了value和writable之外的其他属性描述符(如enumerable和configurable)也不能被修改
//
//              getter和setter方法,读和写,和上述部分属性是互斥的// 向user对象中添加一个num属性,值是20
Object.defineProperty(user, "num", {value: 20,writable: true,enumerable: true,configurable: true,})let test = 10
// 向user对象中添加一个test属性
Object.defineProperty(user, "test", {// value/writable与get/set互斥// 当使用Object.defineProperty定义属性时,不能同时指定value(或writable)和get/set。// 原因是它们代表了两种不同的属性定义模式。value和writable用于定义一个简单的数据属性,其值是直接存储的,读取和修改操作是基本的赋值和取值// 而get和set定义的是访问器属性,通过自定义函数来控制属性的读取和写入逻辑,属性值可能存储在其他地方,不是简单地通过value来存储configurable: true,// 当读取user的test的属性的时候get(getter)函数就会被调用,返回值是num的值get: function () {console.log("test属性被调用")return test},// 当修改user的test的属性的时候set(setter)函数就会被调用,且会收到修改的值set: function (value) {console.log(`test属性被修改,值是${value}`)test = value}})

数据代理

数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)

数据代理概念

let user1 = {num:100}
let user2  = {}// 向user2中添加一个num属性
Object.defineProperty(user2,"num",{// 读取user2的num属性的时候,返回的是user1的numget() {return user1.num},// 修改user2的num属性的时候,修改的是user1的numset(value){user1.num = value}
})

vue中的数据代理

1、Vue中的数据代理:

通过vm对象来代理data对象中属性的操作(读/写)

2、Vue中数据代理的好处:

更加方便的操作data中的数据

3、基本原理:

通过Object.defineProperty()把data对象中所有属性添加到vm上。

为每一个添加到vm上的属性,都指定一个getter/setter。

在getter/setter内部去操作(读/写)data中对应的属性

1、原理示例

// 1. 创建vue实例的时候,data对象是存储在_data属性中
// 2. 使用Object.defineProperty向vue中添加_data对象中的属性,定义getter和setter方法
// 		定义的getter和setter方法,返回和修改的是_data中对应的属性,所以我们可以通过vue实例对象访问代理数据// 模拟vue的代理机制
// 模拟Vue实例(这里简化为一个普通对象来理解原理),实际的_data实现了数据劫持
const vm = {// 用于存储数据的属性,模拟Vue的_data属性_data: {},// 构造函数,模拟Vue实例创建过程constructor() {// 将传入的数据对象赋值给_data属性,类似Vue内部的处理this._data = {...dataObject };// 遍历_data中的所有属性,为它们创建数据代理for (let key in this._data) {Object.defineProperty(this, key, {get() {return this._data[key];},set(v) {this._data[key] = v;}});}}
}

2、使用代理数据

// 结合vue实现数据代理的原理,我们可以使用下面两种方式访问和修改数据const vm = new Vue({el: "#root",data: {name: "vue"}})// 通过_data对象console.log(vm._data.name)// console.log(vm.name)

事件处理

  1. 使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名;

  2. 事件的回调需要配置Vue选项参数的methods对象中,最终会在vm上;

  3. methods中配置的函数,不要用箭头函数!否则this就不是vm了;

  4. methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象;

基本使用

<div id="root"><!--    绑定点击事件-方式一--><button v-on:click="info">显示信息</button><!--    绑定点击事件-方式二--><button @click="info">显示信息2</button><!--    绑定点击事件-传参的回调函数 使用$event传递event对象,如果不需要使用event,可以不传,只传自定义参数--><button @click="info2($event,66666)">显示信息3</button><!-- 回调函数可以不传event对象,然后在函数内部可以直接使用--><button @click="info3">显示信息4</button></div>
</body><script type="text/javascript">const vm = new Vue({el: "#root",data: {name: "vue"},// 配置回调方法,回调方法最终也是存储在vm上,没有做数据代理methods: {// 回调方法info() {alert("hello world")},info2(event,number){console.log(this) // 此处的this是vm,如果使用箭头函数this是Windowalert(event.target + number.toString())},info3(e){console.log(e.target)}}})</script>

事件修饰符

  • prevent:阻止默认事件

  • stop:阻止事件冒泡

  • once:事件只触发一次

  • capture:使用事件的捕获模式

  • self:只有event.target是当前操作的元素时才触发事件

  • passive:事件的默认行为立即执行,无需等待事件回调执行完毕

    <!--    a标签配置点击的事件回调-触发info方法-点击事件的行为设置了prevent, --><!--    prevent-阻止默认事件,即点击超链接不会触发跳转效果 --><a href="https://www.baidu.com" @click.prevent="info">百度一下</a><a href="https://www.baidu.com" v-on:click.prevent="info">百度一下2</a><!--   一个div绑定了点击事件,内部有一个按钮绑定了点击事件,会触发事件冒泡 --><div @click="info" style="width: 100px;height: 100px;background-color: yellow"><!--    stop-阻止事件冒泡--><button @click.stop="info">点点</button></div><!--    按钮绑定了点击事件--><!--    once-事件只会触发一次 --><button @click.once="info">只有一次</button><!--    div绑定了点击事件--><!--使用事件的捕获模式,捕获阶段触发事件--><div @click.capture="info" style="width: 100px;height: 100px;background-color: yellow"><div @click style="width: 50px;height: 50px;background-color: gold"></div></div><!--     只有event.target是当前操作的元素时,才触发事件--><div @click.self="info" style="width: 100px;height: 100px;background-color: yellow"><button>按钮</button></div><!--    事件触发的时候立刻触发默认行为,不会等事件回调执行完 --><div style="height: 200px; overflow: auto" @scroll.passive="handleScroll">

键盘事件

键盘事件有:

  • keyup:按下键盘上的一个键并松开时触发

  • keydown:用户按下键盘上的键时触发

  • keypress:按下一个可打印字符(如字母、数字、标点符号等)的键并松开后触发

基本使用

<body>  
<!--    键盘事件-打印按下的按键--><input type="text" @keyup="info">
</body>
<script type="text/javascript">const vm = new Vue({el: "#root",data: {name: "vue"},// 配置回调方法methods: {// 回调方法info(e) {console.log(e.target.value)},}})</script>

键盘的按键别名

 <!--    vue常用的按键别名 --><!--    回车 => enter--><!--    删除 => delete (捕获“删除”和“退格”键)--><!--    退出 => esc--><!--    空格 => space--><!--    换行 => tab (特殊,必须配合keydown去使用)--><!--    上 => up--><!--    下 => down--><!--    左 => left--><!--    右 => right--><!--    键盘事件-按下按键后不会触发,需要按下回车键的时候才触发--><input type="text" @keyup.enter="info"><!--也可以通过键码触发对应的按键-不推荐-已弃用 --><input type="text" @keyup.20="info">

通过按键名字触发事件

<div id="root"><!--    Vue未提供别名的按键,可以使用按键原始的key值去绑定--><!--    在回调中通过event.target.key 和event.target.key分别获取按键名字和按键编码 --><!--    可以通过按键名字(event.key)获取键名,如果是大驼峰的名字,需要转换小写每个单词-隔开--><!--按下大小写的按键的时候触发- CapsLock === caps-lock --><input type="text" @keyup.caps-lock="info"></div>
</body><script type="text/javascript">const vm = new Vue({el: "#root",data: {name: "vue"},// 配置回调方法methods: {// 回调方法info(e) {console.log(e.key) // 键名  CapsLockconsole.log(e.keyCode) // 键码 20},}})

自定义定制键名

Vue.config.keyCodes.自定义键名 = 键码,可以去定制按键别名

   // 设置大小写的别名为daxiaoxieVue.config.keyCodes.daxiaoxie = 20const vm = new Vue({el: "#root",data: {name: "vue"},// 配置回调方法methods: {// 回调方法info(e) {console.log(e.key) // 键名  CapsLockconsole.log(e.keyCode) // 键码 20},}})

键盘的系统修饰键

系统修饰键(用法特殊):ctrl、alt、shift、meta

(1).配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。

(2).配合keydown使用:正常触发事件

计算属性

  • 定义:要用的属性不存在,要通过已有属性计算得来,即通过已有的属性计算生成一个新属性

  • 原理:底层借助了Objcet.defineproperty方法提供的getter和setter

  • 优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便

  • 计算属性最终会出现在vm上,直接读取使用即可

  • 如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变

基本使用

<body>
<div id="root">姓氏:<input type="text" v-model="firstName"><br>
名字:<input type="text" v-model="lastName"><br>
全名: <span>{{allName}}</span></div>
</body><script type="text/javascript">const vm = new Vue({el: "#root",data: {firstName: "vue",lastName:"html"},methods:{},// 配置计算属性computed:{// 定义一个allName计算属性allName:{// 当有人读取allName时,get就会被调用,返回值作为allName的返回值// 被调用时机:1.初次读取allName的时候 2.依赖的数据发生变化的时候// 初次计算后会将数据缓存,后续调用不用重复计算,除非有依赖数据发生变化get(){// this是vmreturn this.firstName + "-" + this.lastName},// set在allName被修改的时候调用set(v){// abc-bcd格式的字符串,通过-切割const arr = v.split("-")// 将切割的两个值分别赋值给两个属性,然后计算属性联动变化this.firstName = arr[0]this.lastName = arr[1]}}}})

简写方式

如果计算属性只读取,不修改的话,可以简写

<body>
<div id="root">姓氏:<input type="text" v-model="firstName"><br>名字:<input type="text" v-model="lastName"><br>全名: <span>{{allName}}</span></div>
</body><script type="text/javascript">const vm = new Vue({el: "#root",data: {firstName: "vue", lastName: "html"},methods: {},// 配置计算属性computed: {// 定义属性以函数的形式,该函数就是getter的作用allName() {// 直接返回计算后的属性return this.firstName + "-" + this.lastName}}})</script>

监视属性

  • 当被监视的属性变化时, 回调函数自动调用, 进行相关操作

  • 监视的属性必须存在,才能进行监视

  • 监视的两种写法:new Vue时传入watch配置通过vm.$watch监视

基本使用

1、第一种写法

    const vm = new Vue({el: "#root",data: {firstName: "vue", lastName: "html"},methods: {},// 配置计算属性computed: {allName() {return this.firstName + "-" + this.lastName}},// 配置监视属性watch:{// 要监视的属性名字firstName:{// 初始化的时候是否让handler调用一次,默认值是falseimmediate: true,// 当监视的属性发生变化的时候,handler调用handler(newValue,oldValue){console.log(`firstName被修改了`,newValue,oldValue)}}}})

2、第二种写法

  // 配置监视属性// 要监视的属性名字,配置对象vm.$watch("firstName",{immediate: true,handler(newValue,oldValue){console.log(`firstName被修改了`,newValue,oldValue)}})

深度监视

  • Vue中的watch默认不监测对象内部值的改变,只监视一层

  • 配置deep:true可以监测对象内部值改变,监视多层

  • Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以

  • 使用watch时根据数据的具体结构,决定是否采用深度监视

  • 当被监视的对象是一个复杂对象(如包含嵌套属性的对象)时,默认情况下,watch只会监视对象的引用是否发生变化。而深度监视则会递归地监视对象内部的每一个属性,一旦内部属性发生变化,就会触发监听器的回调函数

const vm = new Vue({el: "#root",data: {info: {a: 1,b: 2}},methods: {},// 配置计算属性computed: {},// 监视属性watch:{// 监视整个info,如果不深度监视,只会监视info对应的对象地址值引用有没有发生变化,而不会具体到里面的某个值info:{// 开启深度监视-监视多层结构中每个属性的变化deep:true,handler(){console.log("info-",this.info)}},// 监视多级结构中某个属性的变化-只监视info里面的a属性// 对象的key是字符串,所以需要写成字符串形式'info.a':{handler() {console.log("a",this.info.a)}}}})

监视属性简写

如果不需要深度监视和初始化时调用,可以简写

  // 监视属性watch:{// 监视info,以函数的形式,函数相当于handlerinfo(newV,oldV){console.log(newV,oldV)}}

  // 要监视的属性,回调函数,不允许写箭头函数vm.$watch("info",function (newV,oldV) {console.log(newV,oldV)})

计算属性和监视属性对比

computed和watch之间的区别:

  1. computed能完成的功能,watch都可以完成

  2. watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作

  3. 两个重要的小原则:所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象。所有不被Vue所管理的函数(定时器的回调函数、ajax的回调函数等、Promise的回调函数,最好写成箭头函数这样this的指向才是vm 或 组件实例对象

绑定样式

  • 在应用界面中, 某些元素的样式是变化的

  • class/style 绑定就是专门用来实现动态样式效果的技术

class绑定

使用:class进行绑定

<style>.box {width: 100px;height: 100px;}.box-color {background-color: yellow;}.box-border {border: 1px solid black;}</style><body>
<div id="root"><!-- 绑定class样式,ex是一个字符串,适用于样式不确定、需要动态指定--><!--    最终class和:class的对应类会拼成一个class 最终是 class="box box-color "--><div class="box" :class="ex">box</div><!--    绑定class样式,exArr是一个数组,适用于要使用的样式个数不确定、名字也不确定--><div class="box" :class="exArr">box</div><!--    绑定class样式,exObj是一个对象,适用于要绑定的样式个数确定、名字也确定,但要动态决定用不用--><div  :class="exObj">box</div></div>
</body><script type="text/javascript">const vm = new Vue({el: "#root",data: {ex: "box-color",exArr: ["box-color", "box-border"],exObj:{box:true,  // 根据true、false决定是否启动该样式// 有特殊字符的key值,使用下面两种写法都可以["box-color"]:true,"box-border":true}}})</script>

Style绑定

<body>
<div id="root"><!-- 绑定style样式--对象写法,单个动态样式 --><div :style="styleObj">box</div><!-- 绑定style样式--数组写法,多个动态样式 --><div :style="styleArr">box</div></div>
</body><script type="text/javascript">const vm = new Vue({el: "#root",data: {styleObj:{width:"100px",height:"100px",},styleArr: [{width:"100px",height:"100px",},{// background-color 这种属性 改为小驼峰写法// 也可以使用字符串key   "background-color"backgroundColor:"yellow"}]}})</script>

条件渲染
v-show

<div id="root"><!--    v-show可以控制显示、隐藏元素 --><!--    v-show并不会删除,只是页面不显示,相当于操作display: none;--><!--    适用于切换频率较高的场景--><!--  布尔值,true显示,false隐藏--><h1 v-show="false">title1</h1><!--也可以写表达式值是布尔值--><h1 v-show="2>1">title2</h1><!--   从vue data动态设置 --><h1 v-show="showIf">title3</h1></div>
</body><script type="text/javascript">const vm = new Vue({el: "#root",data: {showIf: true}})</script>

v-if

<body>
<div id="root"><!--    v-if会删除元素,而不是页面隐藏,适用于:切换频率较低的场景--><!--  布尔值,true显示,false隐藏--><h1 v-if="false">title1</h1><!--也可以写表达式值是布尔值--><h1 v-if="2>1">title2</h1><!--   从vue data动态设置 --><h1 v-if="showIf">title3</h1><!--    v-if可以和v-else-if、v-else一起使用,但要求结构不能被打断--><!--    如果 1和"1" 相等 显示title4 --><h1 v-if="num===1">title4</h1><!--    如果1大于2 显示title5--><h1 v-else-if="num>2">title5</h1><!--    否则显示title6,,4和5条件都不成立--><h1 v-else>title6</h1><!--  等同于if(num===1){}else if(num>2){}else {}--></div>
</body><script type="text/javascript">const vm = new Vue({el: "#root",data: {showIf: true,num:10}})</script>

			<!-- v-if只能和template的配合使用,不能和v-show配合使用 --><template v-if="num === 20"><h2>1</h2><h2>2</h2><h2>3</h2></template>

  • template用于定义一个模板片段,在这个模板片段中的内容在默认情况下不会被直接渲染到页面上,而是作为一种 “模板” 存储起来,以便在需要的时候通过 JavaScript 进行克隆或者其他操作来使用其中的内容。

  • 例如,在构建一个动态列表时,可以将列表项的模板定义在<template>标签中,然后根据数据动态地克隆和填充这个模板

列表渲染

基本渲染

<body>
<div id="root"><!--    遍历数组--><ul><!--    等同于  for (let u of users)--><!--        有多少条数据会填充多少条li--><!--         也可以使用 u in users--><!--         :key用来指定这个li节点的唯一标识 --><li v-for="u of users" :key="u.id">{{u.name}}---{{u.tag}}</li></ul><ul><!--index是下标--><li v-for="(u,index) of users" :key="u.id">{{u.name}}---{{u.tag}}---{{index}}</li></ul><!--    遍历对象 --><ul><li v-for="(value,key) of info" :key="key">{{key}} -- {{value}}</li></ul><!--    遍历字符串 --><ul><li v-for="(char,index) of strFor" :key="index">{{char}} -- {{index}}</li></ul><!-- 遍历指定次数 --><ul><!--        遍历5次,number是从1开始的值,index是下标 --><li v-for="(number,index) of 5" :key="index">{{number}}--{{index}}</li></ul>
</div>
</body><script type="text/javascript">const vm = new Vue({el: "#root",data: {users: [{id: 1, name: "vue", "tag": "js"},{id: 2, name: "python", "tag": "py"},{id: 3, name: "golang", "tag": "go"},],info: {name: "vue",count: 10,price: 1000},strFor: "helloworld"}})</script>

v-for key的原理和作用

虚拟DOM中key的作用:

key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM,随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:

  1. 旧虚拟DOM中找到了与新虚拟DOM相同的key:

    若虚拟DOM中内容没变, 直接使用之前的真实DOM!

    若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。

  2. 旧虚拟DOM中未找到与新虚拟DOM相同的key

    创建新的真实DOM,随后渲染到到页面

用index作为key可能会引发的问题:

  1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

  2. 如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题

image-20241114145515446

image-20241114145821110

开发中如何选择key:

  1. 最好使用每条数据的唯一标识作为key, 比如id、手机号等唯一值

  2. 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没问题的

  3. 如果不指定:key,vue默认是使用下标作为key的

列表过滤

使用watch实现

<body>
<div id="root"><!--    双向绑定过滤关键字--><input type="text" v-model="keyWord"><ul><!--       遍历模糊搜索过滤的数组 --><li v-for="u of filUsers" :key="u.id">{{u.name}}---{{u.tag}}</li></ul>
</div>
</body><script type="text/javascript">const vm = new Vue({el: "#root",data: {// 过滤的关键字keyWord: "",users: [{id: 1, name: "vue", "tag": "html", "price": 30},{id: 2, name: "python", "tag": "server", "price": 40},{id: 3, name: "golang", "tag": "server", "price": 50},],// 定义一个新数组,用于存储过滤后的数据filUsers: []},// 配置监视属性watch: {keyWord:{// 初始化时调用一下,过滤空串,初显全部数据immediate: true,handler(newValue) {// 从users的tag过滤 包含newValue的所有数据,重新赋值给this.usersthis.filUsers = this.users.filter((p) => {// 返回tag包含newValue的数据// indexOf 不包含 返回-1 空串 返回0return p.tag.indexOf(newValue) !== -1})}}}})</script>

用computed实现

    const vm = new Vue({el: "#root",data: {// 过滤的关键字keyWord: "",users: [{id: 1, name: "vue", "tag": "html", "price": 30},{id: 2, name: "python", "tag": "server", "price": 40},{id: 3, name: "golang", "tag": "server", "price": 50},],},// 配置计算属性computed:{// 计算属性依赖keyword,keyword发生变化的时候,触发计算出新的数组filUsers(){return this.users.filter((p)=>{return p.tag.indexOf(this.keyWord) !== -1})}}})

列表排序

<body>
<div id="root"><!--    双向绑定过滤关键字--><input type="text" v-model="keyWord"><!-- 按钮点击的时候 修改sortType的值 --><button @click="sortType = 2">price 升序</button><button @click="sortType = 1">price 降序</button><button @click="sortType = 0">原顺序</button><ul><!--       遍历模糊搜索过滤的数组 --><li v-for="u of filUsers" :key="u.id">{{u.name}}---{{u.tag}}--{{u.price}}</li></ul>
</div>
</body><script type="text/javascript">const vm = new Vue({el: "#root",data: {// 过滤的关键字keyWord: "",// 定义一个关键字,0表示原顺序  1表示降序 2表示升序sortType: 0,users: [{id: 1, name: "vue", "tag": "html", "price": 30},{id: 2, name: "python", "tag": "server", "price": 40},{id: 3, name: "golang", "tag": "server", "price": 50},],},// 配置计算属性computed: {// 计算属性依赖keyword,keyword发生变化的时候,触发计算出新的数组filUsers() {// 定义一个数组接收过滤后的数据const arr = this.users.filter((p) => {return p.tag.indexOf(this.keyWord) !== -1})// 先过滤 再排序// 判断是否需要排序if (this.sortType) {arr.sort((a, b) => {// 如果type===1 降序 否则升序return this.sortType === 1 ? b.price - a.price : a.price - b.price})}// 返回排序后的数组return arr}}})</script>

Vue监测数据原理
Vue监测对象

1、Vue监测对象的原理

当创建一个 Vue 实例时,Vue 会遍历data选项中的所有属性。对于每个属性,它使用Object.defineProperty方法来进行数据劫持。这个方法允许 Vue 重新定义属性的getset访问器

vm的数据代理,data数据给 _data前,先将数据加工,然后再赋值给_data,更好的实现响应式,对象中每个属性都有对应的getter和setter

// 模拟实现一个vm的数据监测function  Observer(obj){// 获取对象中所有的属性,生成一个数组const keys = Object.keys(obj)// 遍历所有属性keys.forEach((k)=>{// 将对应的属性添加到当前实例身上Object.defineProperty(this,k,{get(){// 返回对应的值return obj[k]},set(v){// 数据被修改,去解析模版,生成虚拟DOM比对,更新页面obj[k] = v}})})}let data = {name:"vue",tag:"js"}// 创建一个监视的实例对象,用于监视data中属性的变化const  obs = new Observer(data)// 准备一个的vm实例对象let vm = {}vm._data = data = obs// 可以通过vm._data去获取和修改属性

2、问题示例:点击按钮触发回调更新数据,第一种方法可以更新成功,第二个方法更新不成功(数据改了但是没有响应式)

    <button @click="updateInfo1" type="button">按钮</button><button @click="updateInfo2" type="button">按钮</button><ul><!--       遍历模糊搜索过滤的数组 --><li v-for="u of users" :key="u.id">{{u.name}}---{{u.tag}}--{{u.price}}</li></ul>
</div>
</body><script type="text/javascript">const vm = new Vue({el: "#root",data: {users: [{id: 1, name: "vue", "tag": "html", "price": 30},{id: 2, name: "python", "tag": "server", "price": 40},{id: 3, name: "golang", "tag": "server", "price": 50},],},// 回调methods:{updateInfo1(){// 可以更新成功this.users[0].name = "js"this.users[0].price = 40},updateInfo2(){// 数据更新成功,但是页面没有响应式更新,原因看监测数组原理this.users[0] =    {id: 1, name: "css", "tag": "html", "price": 40}}}})</script>

Vue.set()方法

如果我们想直接给vm或者_data上加一个新属性,通过vm.xxx = xxx 或者vm._data.xxx = xx是没有响应式的

Vue的数据的响应式是通过Object.defineProperty来实现的,每个属性都有对应的getter和setter,而直接添加属性是没有数据实现

Vue给我们提供了set和$set方法,给vm或者_data添加属性, 并且完成数据加工

  • Vue.set是全局的 API,它的调用方式是Vue.set(target, key, value)

    其中target是要添加属性的目标对象(响应式对象),key是要添加的属性名,value是属性的值

    这种调用方式不依赖于特定的 Vue 组件实例,适用于在组件外部或者一些工具函数等场景中使用

  • this.$set是 Vue 实例(组件)的一个实例方法,它只能在 Vue 组件内部使用

    通过this.$set来调用。this指向当前的 Vue 组件实例,调用方式为this.$set(target, key, value)

    参数含义与Vue.set相同。这种方式更符合在组件内部操作数据的习惯,因为它与组件实例紧密相关,可以方便地访问组件的this上下文,如组件的数据(data)、计算属性(computed)等

  • Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象 添加属性

    const vm = new Vue({el: "#root",data: {user:{name:"vue",info:{price:100}}},methods:{// 事件回调 给info天添加一个tag数学addInfo(){Vue.set(this.user.info,"tag1","html")this.$set(this.user.info,"tag2","html")}}})

Vue监测数组

vm上数组的数据,没有getter和setter

vm监测数组数据变化实现响应式,是通过调用Vue数组(非原生方法)方法实现的

  • Vue在数据原型上实现了数组的增删改查方法(push、pop等)

  • 内部先调用了原生数组的增删改查方法来实现数据变化

  • 然后解析模版实现响应式

通过数组索引修改数据,只是修改了数据,没有重新解析模版,所以页面不会跟随更新

  // 使用this.$set来更新数组元素,实现响应式更新this.$set(this.numbers, 0, 10);// 使用splice方法来更新数组元素,实现响应式更新this.numbers.splice(0, 1, 10);

vue收集表单数据和修饰符

<body>
<div id="root"><form><!--        有输入框相关的,v-Model收集的是value值,用户输入的东西就是value值--><!--.trim修饰符去除收集数据的首尾空格-->账号:<input type="text" v-model.trim="account"> <br/><br/>密码:<input type="password" v-model="password"> <br/><br/><!--  .number修饰符,用户输入vue收集过来会默认转换成number类型type="number" 是只能输入number,如果其他type 输入字符串“123”,修饰符收集过来会转换成number123如果输入123abc,修饰符收集过来是123-->年龄:<input type="number" v-model.number="age"> <br/><br/><!-- radio类型没有输入框    v-model收集的是value值,需要给标签配置value值   -->性别:男<input type="radio" name="sex" value="male" v-model="sex">女<input type="radio" name="sex" value="female" v-model="sex"> <br/><br/><!--checkbox类型如果没有配置value属性,v-model收集的就是标签的checked属性,true 或者false如果配置了value属性1. v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,布尔值)2. v-model的初始值是数组,那么收集的的就是value组成的数组初始值就是vue双向绑定给的初始值-->爱好:学习<input type="checkbox" v-model="hobby" value="study">打游戏<input type="checkbox" v-model="hobby" value="game">吃饭<input type="checkbox" v-model="hobby" value="eat"><br/><br/>所属校区<select v-model="city"><option value="">请选择校区</option><option value="beijing">北京</option><option value="shanghai">上海</option><option value="shenzhen">深圳</option><option value="wuhan">武汉</option></select><br/><br/>其他信息:<!--.lazy修饰符默认收集数据是实时的,每输入一个字符都会直接收集使用 .lazy修饰符,会在失去焦点的时候统一收集,而不是实时收集--><textarea v-model.lazy="other"></textarea> <br/><br/><input type="checkbox" v-model="agree">阅读并接受<a href="https://www.baidu.com">《用户协议》</a><button>提交</button></form>
</div>
</body><script type="text/javascript">const vm = new Vue({el: "#root",data: {account: "",password: "",age: 20, // 默认20sex: "male", // 默认选中 value为male// 复选:v-model初始值设置为数组,收集所选数据hobby: [],city: "beijing", // 默认选择北京other: "",agree: "" // 复选:不需要收集具体数,设置为字符串,收集checked状态}})</script>

过滤器

过滤器不会改变原本的数据, 是产生新的对应的数据

局部过滤器

<body>
<div id="root"><!--   要展示的属性 |  要使用的过滤器 --><!--   过滤器原理: 先读取time,把time当做参数传给过滤器,过滤器的返回值替换{{}}插值语法中的内容--><h2>{{time  | timeFormat }}</h2><!--    过滤器可以传递参数,第一个参数默认就是value,然后第二个参数需要使用形参接受--><h2>{{time  | timeFormat(true) }}</h2><!--    多个过滤器可以一起使用  多个过滤器使用 | 分割 , 插值 | 过滤器 |过滤器 ..--><!--    原理:先将time交给timeFormat处理,然后把timeFormat处理的结果交给testFilter,最后使用testFilter的值进行替换--><h2>{{time | timeFormat | testFilter}}</h2></div>
</body><script type="text/javascript">const vm = new Vue({el: "#root",data: {time: Date.now() // 当前时间的时间戳},// 配置局部过滤器,只有当前vue实例可以使用,其他实例不可以使用filters: {// 过滤器就是函数timeFormat(value, status) {if (status) {console.log(value)}// 将时间戳格式化 然后返回return dayjs(value).format("YYYY年MM月DD日 HH:mm:ss")},testFilter(value) {return value[0]}}})</script>

全局过滤器

  // 全局过滤器 必须在Vue实例创建之前配置Vue.filter("myFilter", function (value) {return value.slice(0, 4)})// 如果要多个全局过滤器,需要多声明Vue.filter("myFilter2", function (value) {return value.slice(0, 4)})const vm = new Vue({el: "#root",data: {time: Date.now() // 当前时间的时间戳},// 配置局部过滤器,只有当前vue实例可以使用,其他实例不可以使用filters: {// 过滤器就是函数timeFormat(value, status) {if (status) {console.log(value)}// 将时间戳格式化 然后返回return dayjs(value).format("YYYY年MM月DD日 HH:mm:ss")},testFilter(value) {return value[0]}}})

<!--在单向数据绑定的时候,也可以使用过滤器 --><input :value="time | timeFormat">

内置指令

v-bind : 单向绑定解析表达式, 可简写为 :xxxv-model : 双向数据绑定

v-for : 遍历数组/对象/字符串

v-on : 绑定事件监听, 可简写为@

v-if : 条件渲染(动态控制节点是否存存在)

v-else : 条件渲染(动态控制节点是否存存在)

v-show : 条件渲染 (动态控制节点是否展示)

v-text

作用:向其所在的节点中渲染文本内容。

与插值语法的区别:v-text会替换掉节点中的内容,插值语法则不会替换

<body>
<div id="root"><!-- 123vue--><h1>123{{name}}</h1><!-- vue  --><!--    v-text会拿到data里的值替换掉整个标签里的内容--><!-- v-text会将数据当成字符串渲染,不会解析标签,如果值是标签内容 也会当字符串显示--><h1 v-text="name">123</h1></div>
</body><script type="text/javascript">const vm = new Vue({el: "#root",data: {name: "vue"}})
</script>

v-html

向指定节点中渲染包含html结构的内容

v-html有安全性问题:

  1. 在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击

  2. 一定要在可信的内容上使用v-html,永不要用在用户提交的内容上

<div id="root"><!--<h1>666</h1>--><div>{{name}}</div><!--    <h1>666</h1>--><div v-text="name"></div><!--h1效果的666 ,v-html可以解析标签 ,如果标签中有其他内容,也会整个替换,和v-text一样--><div v-html="name"></div></div>
</body><script type="text/javascript">const vm = new Vue({el: "#root",data: {name: "<h1>666</h1>"}})
</script>

v-clock

本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题

<style>/*  当vue没有创建成功的时候,插值语法还没有值,标签有v-cloak属性,选中这些标签 将该元素隐藏 *//*  当vue创建成功的之后,标签的v-cloak都被删掉,这些元素就不会隐藏了 */[v-cloak] {display: none;}</style>
<body>
<div id="root"><!--     vue实例没有创建成功的时候,标签内v-cloak,当实例创建成功之后,会删除所有 所有标签内的v-cloak --><h1 v-cloak>{{name}}</h1><h1 v-cloak>{{name}}</h1></div>
</body><script type="text/javascript">const vm = new Vue({el: "#root",data: {name: "vue"}})
</script>

v-once

v-once所在节点在初次动态渲染后,就视为静态内容了。以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能

    <!--    使用v-once,只会读取一次num值,后续不会在更新--><h2 v-once>{{num}}</h2><!--     每点一次 按钮 num增加1 --><h2>当前的n值是:{{num}}</h2><button @click="num++">点我n+1</button>

v-pre

  • 跳过其所在节点的编译过程

  • 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译

  <!--  页面显示  {{name}}} 不会解析了 使用了插值语法、指令的地方,不使用 --><div v-pre>{{name}}</div><!--  对于不需要vue渲染的 可以使用该指令 --><div v-pre>内容</div>

自定义指令

指令对象式里面的方法、函数式的this都是Window

函数式

<div id="root"><div style="height: 100px" v-test="num" ></div></div>
</body><script type="text/javascript">const vm = new Vue({el: "#root",data: {num: 10},// 配置自定义指令directives:{// 函数形式写法-自定义一个test指令,函数名字不需要v-,但是使用的时候需要v-// element:使用该指令所绑定的 DOM 元素 比如 <div style=""></div>,真实DOM而非虚拟DOM// binding:一个对象,它包含了指令的相关信息//         def:包含了指令在生命周期不同阶段执行的函数,主要是bind和update//         update:当指令所绑定的数据发生变化时,update函数会被调用。它用于根据新的数据更新指令相关的操作。例如,如果指令是将数据绑定到元素的文本内容,update函数会更新文本内容以反映数据的变化//         expression:指令中使用的表达式,v-指令="xxx"中的xxx//         modifiers:一个对象,存储指令的修饰符。当指令在模板中有修饰符时,这个对象会记录相关信息//         name: 自定义指令的名称,这里是test//         rawName:自定义指令的原始名称,包含v-,这里是v-test//         value:指令的表达式绑定的值,通常是由expression计算得出或者直接从绑定的数据获取的,会在指令的逻辑中用于各种操作,比如更新元素的属性或者作为条件判断的依据// 指定触发时机 1.指令与元素成功绑定时(此时元素不一定已经放入页面) //              如果一些操作比如使用element获取焦点,如果元素还没放入页面,就会失败//              函数式存在指令和元素绑定成功但是vue还没将模版(html)渲染的问题//            2.指令所在的模板被重新解析时test(element,binding){console.log(binding)// element的对应的dom元素,向该元素写入数据, 从binding.value获取值,然后做处理element.innerText = binding.value *10}}})
</script>

对象式

<body>
<div id="root"><div style="height: 100px" v-test="num"></div><!--    指令名如果是多个单词,要使用v-xxx-xxx命名方式,不要用小驼峰命名--><input type="text" v-test-obj:value="num"></div>
</body><script type="text/javascript">const vm = new Vue({el: "#root",data: {num: 10},// 配置自定义指令directives: {test(element, binding) {// element的对应的dom元素,向该元素写入数据, 从binding.value获取值,然后做处理element.innerText = binding.value * 10},// 对象式-可以解决函数式的绑定元素&页面元素未放入的时机问题// bind和update方法大部分情况是一样的,函数式相当于实现了bind和update方法,没有inserted方法testObj: {//  调用时机-指令与元素成功绑定时bind(element, binding) {element.value = binding.value * 10},//  调用时机-指令所在元素被插入页面时调用inserted(element, binding) {// 获取input的焦点,触发时机是所在元素被插入页面时,所以dom的一些操作都会成功element.focus()},//  调用时机-指令所在的模版呗重新解析时调用update(element, binding) {element.value = binding.value * 10}},// 官方推荐直接使用 多个单词-连接的方式"test-object":{bind(element, binding) {},inserted(element, binding) {},update(element, binding) {}}}})
</script>

自定义指令-全局指令

在vue实例上配置的指令,都是局部指令,只能在当前vue实例使用

   // 在vue实例之前   // 指令名,配置对象Vue.directive("test",{}) // 指令名,回调函数Vue.directive("test1",function () {})

生命周期

image-20241118102140212

Vue 的生命周期是指 Vue组件从创建到销毁的整个过程中所经历的一系列阶段。在每个阶段,组件会触发相应的生命周期钩子函数,开发者可以通过在这些钩子函数中编写代码来实现特定的功能,例如初始化数据、发送网络请求、操作 DOM 元素、清理资源等

  • 又名:生命周期回调函数、生命周期函数、生命周期钩子。

  • Vue在关键时刻帮我们调用的一些特殊名称的函数

  • 生命周期函数的名字不可更改,但函数的具体内容是根据需求编写的

  • 生命周期函数中的this指向是vm 或 组件实例对象

    const vm = new Vue({el: "#root",data: {opacity: 1 // 设置透明度},// mounted是一个生命周期钩子函数// 当Vue完成模版解析并把初始的真实DOM放入页面后(挂载完毕)调用mountedmounted() {setInterval(()=>{this.opacity -= 0.01if(this.opacity <= 0)this.opacity = 1},16)}})

挂载流程

image-20241118102213359

    // new vue实例const vm = new Vue({el: "#root",data: {},/**   挂载流程   **///  1.初始化生命周期和事件之后,调用该方法,此数据代理还未开始,不能通过vm访问data、methodsbeforeCreate() {console.log("beforeCreate")},// 2.当数据检测、数据代理等初始化完成之后调用该方法,此时可以通过vm访问data、methods中的方法created() {console.log("created")},// 3.Vue开始解析模版,生成虚拟DOM(内存中),此时页面还不能显示解析好的内容//   判断new vue实例的时候有没有el选项,如果没有,就等待vm.$mount调用再后续执行//   判断new vue实例的时候是否有template选项,如果有的话,编译模版到render函数// 4.上方步骤之后,调用该方法,页面呈现的是未经Vue编译的DOM结构,所有对DOM的操作,此时都不奏效beforeMount() {console.log("beforeMount")},// 5.将内存中的虚拟DOM转为真实DOM插入页面// 6.当Vue完成模版解析并把初始的真实DOM放入页面后(挂载完毕)调用mounted//  此时对DOM的操作均有效,但是尽量不在此时操作DOM//  一般可以在这个时候开启定时器、发送请求、定向消息、绑定自定义事件等初始化操作mounted() {console.log("mounted")},/**   更新流程   **/// 1.当有数据改变的时候(更新之前),调用该方法//   此时数据是新的,但是页面还是旧的,页面还未和数据保持同步beforeUpdate() {console.log("beforeUpdate")},// 2.根据新数据,生成新的虚拟DOM,与旧的虚拟DOM进行比较,然后完成页面更新,Model->View的更新// 3. 更新完成之后,调用该方法,此时数据是新的,页面也是新的updated() {console.log("updated")},/**   销毁流程   **/// 当调用vm.$destroy()之后,触发销毁流程////// 1. 在组件即将被销毁之前调用的钩子函数。这个阶段组件仍然是完整的,还没有开始实际的销毁过程//     目前vm中的方法属性等都处于可以用状态,但是修改不会再生效,一般在此时关闭定时器、取消消息解绑自定义事件等收尾操作beforeDestroy() {console.log("beforeDestroy")},// 2. 完全销毁一个实例,清理它与其他实例的连接,解绑它的全部指令及监听//    vue不再管理页面,但是之前生成的数据还在// 3.在组件已经完全被销毁之后调用destroyed() {console.log("destroyed")}})

文章转载自:木子七

原文链接:https://www.cnblogs.com/Mickey-7/p/18552283

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

相关文章:

vue2-基础核心

vue简介 动态构建用户界面的渐进式 JavaScript 框架 vue的特点: 遵循MVVM模式 采用组件化模式&#xff0c;提高代码复用率&#xff0c;让代码更好维护 声明式编码&#xff0c;无需直接操作DOM&#xff0c;提高开发效率&#xff0c;编码简洁、体积小&#xff0c;运行效率高 本…...

使用 前端技术 创建 QR 码生成器 API1

前言 QR码&#xff08;Quick Response Code&#xff09;是一种二维码&#xff0c;于1994年开发。它能快速存储和识别数据&#xff0c;包含黑白方块图案&#xff0c;常用于扫描获取信息。QR码具有高容错性和快速读取的优点&#xff0c;广泛应用于广告、支付、物流等领域。通过扫…...

天云数据参编行业标准|《Maas模型服务协议要求》标准正式发布

随着各行业对大模型的应用需求日益增多&#xff0c;模型即服务&#xff08;MaaS&#xff09;发展迅速&#xff0c;MaaS将AI模型以服务的方式提供给用户&#xff0c;降低模型使用门槛。当前产业界已推出诸多MaaS产品&#xff0c;并集成和提供了大量模型服务&#xff0c;然而对于…...

观察者模式和订阅模式

观察者模式和订阅模式在概念上是相似的&#xff0c;它们都涉及到一个对象&#xff08;通常称为“主题”或“发布者”&#xff09;和多个依赖对象&#xff08;称为“观察者”或“订阅者”&#xff09;之间的关系。然而&#xff0c;尽管它们有相似之处&#xff0c;但在某些方面也…...

Mac设置java环境变量

Mac电脑中存在多个jdk版本,如何配置java环境变量为指定版本jdk? 一、查看所有已安装的 JDK 版本 /usr/libexec/java_home -V二、临时设置 export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)三、永久设置 如果需要永久使用指定版...

Sentinel服务保护

Sentinel是阿里巴巴开源的一款服务保护框架&#xff0c;目前已经加入SpringCloudAlibaba中。官方网站&#xff1a; home | Sentinel Sentinel 的使用可以分为两个部分: 核心库&#xff08;Jar包&#xff09;&#xff1a;不依赖任何框架/库&#xff0c;能够运行于 Java 8 及以…...

Linux内核USB2.0驱动框架分析--USB包

一&#xff0c; 包的组成 每个包都由SOP&#xff08;包起始域&#xff09;、SYNC&#xff08;同步域&#xff09;、Packet Content&#xff08;包内容&#xff09;、EOP&#xff08;包结束域&#xff09;四部分组成&#xff0c;其中SOP、SYNC、EOP为所有包共有的域&#xff0c…...

SpringCloud Gateway转发请求到同一个服务的不同端口

SpringCloud Gateway默认不支持将请求路由到一个服务的多个端口 本文将结合Gateway的处理流程&#xff0c;提供一些解决思路 需求背景 公司有一个IM项目&#xff0c;对外暴露了两个端口8081和8082&#xff0c;8081是springboot启动使用的端口&#xff0c;对外提供一些http接口…...

win10局域网加密共享设置

1、创建共享账户 我的电脑右键选择管理 选择本地用户和组 -> 用户 双击用户 在空白区域右键,新建用户 然后创建用户 点击创建后 2、设置网络 右下角网络右键...

论文阅读——Performance Evaluation of Passive Tag to Tag Communications(一)

文章目录 摘要一、互耦对监听器标签输入阻抗的影响A. 无限细偶极子互阻抗的理论研究B. 电细偶极子的情况&#xff1a;理论与模拟C. 印刷偶极子的情况&#xff1a;电磁模拟与测量 二、T2T 通信系统的性能评估总结 论文来源&#xff1a;https://ieeexplore.ieee.org/document/970…...

Docker Registry(镜像仓库)详解

Docker Registry&#xff08;镜像仓库&#xff09;详解 Docker Registry&#xff0c;即Docker镜像仓库&#xff0c;是Docker生态系统中一个至关重要的组件。它负责存储、管理和分发Docker镜像&#xff0c;为Docker容器提供镜像资源。本文将深入探讨Docker Registry的功能、结构…...

17. 【.NET 8 实战--孢子记账--从单体到微服务】--记账模块--主币种设置

记账模块是我们项目的核心模块&#xff0c;也是用户使用最多的模块&#xff0c;因此这个模块的东西比较多&#xff0c;我们要分为多个部分编写代码。 一、需求 币种设置的需求涉及到了我们前面编写的代码&#xff0c;我们来具体看一下需求。 编号需求说明1主币种设置1. 用户…...

ChatPaper.ai:通过阅读文献高效学习英语的新思路

前言 作为一名学习者&#xff0c;我们常常会遇到这样的困境&#xff1a;想提高英语水平&#xff0c;但单纯背单词缺乏语境&#xff1b;想读专业文献&#xff0c;却被英语障碍所困扰。今天我要分享一个工具 ChatPaper.ai&#xff0c;它让我找到了将英语学习和专业阅读完美结合的…...

.NET9 - 新功能体验(二)

书接上回&#xff0c;我们继续来聊聊.NET9和C#13带来的新变化。 01、新的泛型约束 allows ref struct 这是在 C# 13 中&#xff0c;引入的一项新的泛型约束功能&#xff0c;允许对泛型类型参数应用 ref struct 约束。 可能这样说不够直观&#xff0c;简单来说就是Span、ReadO…...

探索Python PDF处理的奥秘:pdfrw库揭秘

文章目录 探索Python PDF处理的奥秘&#xff1a;pdfrw库揭秘1. 背景&#xff1a;为何选择pdfrw&#xff1f;2. pdfrw是什么&#xff1f;3. 如何安装pdfrw&#xff1f;4. 五个简单的库函数使用方法4.1 读取PDF信息4.2 修改PDF元数据4.3 旋转PDF页面4.4 提取PDF中的图片4.5 合并P…...

网络爬虫——爬虫项目案例

本节将全面讲解如何通过实战爬虫项目解决复杂问题。结合最新技术和实际开发需求&#xff0c;案例将涵盖完整开发流程&#xff0c;包括需求分析、实现代码、优化方法和常见问题解决。力求实现高效、可扩展的爬虫项目架构&#xff0c;帮助开发者提升实战能力。 案例 1&#xff1a…...

JAVA中的Lamda表达式

JAVA中的Lamda表达式 Lambda 表达式的语法使用场景示例代码1.代替匿名内部类2. 带参数的 Lambda 表达式3. 与集合框架结合使用4. 使用 Stream 操作 总结 Java 的 Lambda 表达式是 Java 8 引入的一个新特性&#xff0c;用于简化代码&#xff0c;特别是在处理函数式编程时。Lambd…...

经典游戏:飞机大战游戏python设计与实现

《飞机大战》是一款经典的二维飞行射击游戏&#xff0c;其核心玩法是控制玩家飞机与敌机作战&#xff0c;通过击落敌机获取分数并尽量避免被敌机击中。根据提供的代码&#xff0c;飞机大战的设计和实现可以分为以下几个主要部分&#xff1a;游戏初始化、游戏界面设计、玩家控制…...

网络爬虫——常见问题与调试技巧

在开发网络爬虫的过程中&#xff0c;开发者常常会遇到各种问题&#xff0c;例如网页加载失败、数据提取错误、反爬机制限制等。以下内容将结合实际经验和技术方案&#xff0c;详细介绍解决常见错误的方法&#xff0c;以及如何高效调试和优化爬虫代码。 1. 爬虫过程中常见的错误…...

深入理解TensorFlow中的形状处理函数

摘要 在深度学习模型的构建过程中&#xff0c;张量&#xff08;Tensor&#xff09;的形状管理是一项至关重要的任务。特别是在使用TensorFlow等框架时&#xff0c;确保张量的形状符合预期是保证模型正确运行的基础。本文将详细介绍几个常用的形状处理函数&#xff0c;包括get_…...

macOS 无法安装第三方app,启用任何来源的方法

升级新版本 MacOS 后&#xff0c;安装下载的软件时&#xff0c;不能在 ”安全性与隐私” 中找不到 ”任何来源” 选项。 1. 允许展示任何来源 点击 启动器 (Launchpad) – 其他 (Other) – 终端 (Terminal)&#xff1a; 打开终端后&#xff0c;输入以下代码回车&#xff1a; …...

Leetcode148. 排序链表(HOT100)

链接 我写的错误代码&#xff1a; class Solution { public:ListNode* sortList(ListNode* head) {if (!head || !head->next)return head;ListNode* fast head;ListNode* slow head;while (fast&&fast->next) {fast fast->next->next;slow slow->…...

Linux线程_线程互斥_线程同步

一.线程互斥 1.进程线程间的互斥相关概念 临界资源&#xff1a;多线程执行流共享的资源就叫做临界资源临界区&#xff1a;每个线程内部&#xff0c;访问临界资源的代码&#xff0c;就叫做临界区互斥&#xff1a;任何时刻&#xff0c;互斥保证有且只有一个执行流进入临界区&…...

【Spiffo】环境配置:VScode+Windows开发环境

摘要&#xff1a; 在Linux下直接开发有时候不习惯快捷键和操作逻辑&#xff0c;用Windows的话其插件和工具都更齐全、方便&#xff0c;所以配置一个Windows的开发环境能一定程度提升效率。 思路&#xff1a; 自己本地网络内远程连接自己的虚拟机&#xff08;假定用的是虚拟机…...

DevExpress控件 基本使用

DevExpress控件 一、DevExpress简介 1、所有编辑器的公共功能 全部都可以绑定数据&#xff1b; 全部都可以独立使用或用于由 Developer Express 提供的容器控件 (XtraGrid、XtraVerticalGrid、XtraTreeList 和 XtraBars) 内的内置编辑&#xff1b; 全部都使用相同的样式、外…...

设计模式——装饰器模式

装饰器模式是结构型设计模式&#xff0c;在Python中有一个非常著名的装饰器wrapper&#xff0c;它的实现方法就是使用了该设计模式&#xff0c;装饰器可以修饰类也可以修饰函数。 从类的设计上说&#xff0c;他的本质是在不定义子类的情况下动态的给对象添加一些额外的功能。举…...

【编程题目】列表、元组及集合

一.列表的题目 题目1:列表反转与排序 描述:给定一个整数列表,首先反转该列表,然后对其进行升序排序。最后输出处理后的列表。输入:一个整数列表,例如 [3, 1, 4, 1, 5, 9]输出:处理后的列表,例如 [1, 1, 3, 4, 5, 9]示例:input_list = [3, 1, 4, 1, 5, 9] # 你的代码 …...

【大数据学习 | Spark-Core】RDD的缓存(cache and checkpoint)

1. 单应用缓存&#xff1a;cache 1.1 cache算子 cache算子能够缓存中间结果数据到各个executor中&#xff0c;后续的任务如果需要这部分数据就可以直接使用避免大量的重复执行和运算。 rdd 存储级别中默认使用的算子cache算子&#xff0c;cache算子的底层调用的是persist算子…...

自主研发,基于PHP+ vue2+element+ laravel8+ mysql5.7+ vscode开发的不良事件管理系统源码,不良事件管理系统源码

不良事件上报系统源码&#xff0c;不良事件管理系统源码&#xff0c;PHP源码 不良事件上报系统通过 “事前的人员知识培训管理和制度落地促进”、“事中的事件上报和跟进处理”、 以及 “事后的原因分析和工作持续优化”&#xff0c;结合预存上百套已正在使用的模板&#xff0…...

哈希表理解与底层模拟实现

内容摘要 本文内容包括红黑树和哈希表的性能比较逻辑分析及实现、哈希表的概念、哈希表映射关系建立的最常用的两种方法直接地址法和除留余数法介绍、介绍了哈希冲突的原因以及解决解决哈希冲突的方法、负载因子的概念、哈希表的扩容、开散列实现哈希表的思路及代码实现、闭散列…...

docker compose的安装和使用

1. Docker Compose 简介 Docker Compose 是一个工具&#xff0c;用于定义和运行多容器的 Docker 应用。通过编写一个 docker-compose.yml 文件&#xff0c;可以一次性启动所有容器&#xff0c;并且方便管理容器之间的依赖。 2. 安装 Docker Compose 前提条件 确保已安装 Do…...

17种Kubernetes安全检测工具详解

随着Kubernetes的广泛应用,确保其安全性就显得尤为重要。好在现有很多优秀的安全检测工具,可以帮助我们发现和修复Kubernetes集群中的安全隐患。本文将全面介绍17种常用的Kubernetes安全检测工具。 Kube-benchKube-hunterKubesec.ioTrivyKube-auditKube-secKube-vulnkube-scana…...

Python遥感开发之CGCS2000转换WGS84地理坐标系

Python遥感开发之CGCS2000转换WGS84地理坐标系 1 CGC2000坐标系介绍2 WGS84地理坐标系介绍3 代码实现CGCS2000转换WGS84地理坐标系 前言&#xff1a;主要借助pyproj实现从CGCS2000高斯-克吕格3度带&#xff08;EPSG:4547&#xff09;转换到WGS84地理坐标系&#xff08;EPSG:432…...

FAX动作文件优化脚本(MAX清理多余关键帧插件)

大较好,为大家介绍一个节省FBX容量的插件!只保留有用的动画轴向,其他不参与动画运动的清除! 一.插件目的:: 1.我们使用的U3D引擎产生的游戏资源包容量太大,故全方位优化动画资源; 2.在max曲线编辑器内,点取轴向太过麻烦,费事,直观清除帧大大提高效率。 如: 二:…...

Springboot集成ElasticSearch实现minio文件内容全文检索

一、docker安装Elasticsearch &#xff08;1&#xff09;springboot和Elasticsearch的版本对应关系如下&#xff0c;请看版本对应&#xff1a; 注意安装对应版本&#xff0c;否则可能会出现一些未知的错误。 &#xff08;2&#xff09;拉取镜像 docker pull elasticsearch:7…...

Python 中的 | 符号

Python 中的 | 符号 正文用法 1用法2 正文 今天遇到了一个符号 |&#xff0c;本文将对符号 | 的意思进行说明。 x: int 1 print(x) # 1上述代码中&#xff0c;:int 表示的是注释内容&#xff0c;这个在 python 中的注释 一文中我们已经进行了说明。 用法 1 有些时候我们会…...

网安基础知识|IDS入侵检测系统|IPS入侵防御系统|堡垒机|VPN|EDR|CC防御|云安全-VDC/VPC|安全服务

网安基础知识|IDS入侵检测系统|IPS入侵防御系统|堡垒机|VPN|EDR|CC防御|云安全-VDC/VPC|安全服务 IDS入侵检测系统 Intrusion Detection System 安全检测系统&#xff0c;通过监控网络流量、系统日志等信息&#xff0c;来检测系统中的安全漏洞、异常行为和入侵行为。 分为&am…...

记录elasticsearch-analysis-dynamic-synonym从8.15.0升级到8.16.0所遇到的问题

记录elasticsearch-analysis-dynamic-synonym从8.15.0升级到8.16.0所遇到的问题 一、打包步骤 步骤一、linux系统下执行elasticsearch-module中的build.sh脚本 步骤二、maven环境下elasticsearch-cluster-runner执行maven install命令安装到本地maven仓库。 步骤三、修改版…...

IDEA怎么定位java类所用maven依赖版本及引用位置

在实际开发中&#xff0c;我们可能会遇到需要搞清楚代码所用依赖版本号及引用位置的场景&#xff0c;便于排查问题&#xff0c;怎么通过IDEA实现呢&#xff1f; 可以在IDEA中打开项目&#xff0c;右键点击maven的pom.xml文件&#xff0c;或者在maven窗口下选中项目&#xff0c;…...

react native 安装好apk后无法打开

react native 打包好apk安装完成&#xff0c;没有打开app按钮&#xff0c; 在AndroidManifest.xml中 <intent-filter><action android:name"android.intent.action.MAIN" /><category android:name"android.intent.category.LAUNCHER" /&…...

HTML5 SVG

HTML5 SVG SVG(Scalable Vector Graphics)是一种基于XML的图像格式,用于在网页上创建矢量图形。与传统的位图图像(如PNG和JPEG)不同,SVG图像可以无限放大而不失真,因为它们是由直线和曲线定义的数学路径,而不是像素点。HTML5支持直接在网页中嵌入SVG,使得网页设计更加…...

【LeetCode每日一题】——485.最大连续 1 的个数

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时空频度】九【代码实现】十【提交结果】 一【题目类别】 数组 二【题目难度】 LeetCode 三【题目编号】 485.最大连续 1 的个数 四【题目描述】 给定…...

Python语言就业方向有哪些?

Python语言的就业方向非常广泛&#xff0c;涵盖了多个领域和职位&#xff0c;主要包括&#xff1a; Web开发 Python在Web开发领域有着广泛的应用&#xff0c;特别是通过框架如Django、Flask等。Python可以帮助开发者快速搭建网站&#xff0c;与各种前端技术集成&#xff0c;从而…...

SpringSecurity创建一个简单的自定义表单的认证应用

1、SpringSecurity 自定义表单 在 Spring Security 中创建自定义表单认证应用是一个常见的需求&#xff0c;特别是在需要自定义登录页面、认证逻辑或添加额外的表单字段时。以下是一个详细的步骤指南&#xff0c;帮助你创建一个自定义表单认证应用。 2、基于 SpringSecurity 的…...

wpf 事件转命令的方式

1&#xff0c;方式1 <StackPanel Background"Transparent"><StackPanel.InputBindings><KeyBinding Command"{Binding ChangeColorCommand}"CommandParameter"{Binding ElementNamecolorPicker, PathSelectedItem}"Key"{Bi…...

Docker 容器的初始化设置

虽然现在Conntainerd 大有取代Docker作为容器运行时的趋势&#xff0c;但是docker还是有自己的优势在。尤其是对于开发者来讲&#xff0c;使用Docker 比使用 containerd 方便很多&#xff0c;尤其是在Docker Desktop等工具的加持下。 本文主要面向Docker的初、中级学者&#xf…...

python里的数据结构

列表&#xff08;List&#xff09; 定义和特点&#xff1a; 列表是一种有序的可变序列&#xff0c;可包含不同类型的元素&#xff0c;如整数、字符串、列表等。可以通过索引访问和修改元素&#xff0c;索引从 0 开始。代码示例&#xff1a; my_list [1, 2, apple, [4, 5]] pr…...

Ros Noetic 20.04 跑通mpc_ros包保姆级教程

前言: 本文将简述mpc_ros包在noetic20.04中的安装,mpc是 一种跟踪、MPC_ROS 是一个基于ROS(Robot Operating System)的模型预测控制(Model Predictive Control,MPC)库。该项目旨在为机器人控制提供一个灵活且高效的MPC实现,使得开发者能够在ROS环境中轻松集成和使用MPC…...

MySQL 没有数据闪回?看 zCloud 如何补齐MySQL数据恢复能力

ENMOTECH 上一篇文章为大家介绍了某金融科技企业通过 zCloud 多元数据库智能管理平台的告警中心“警警”有条地管理告警并进行敏捷处置的实践案例。本篇跟大家继续分享该案例客户如何利用 zCloud 备份恢复模块下的Binlog解析功能补齐 MySQL 数据恢复能力&#xff0c;让运维人员…...

k8s集群加入node节点为ubuntu 22.04

文章目录 1.环境准备1.1 关闭无用服务1.2 环境和网络1.3 apt源1.4 系统优化 2. 装containerd3. 接入k8s集群3.1 kubelet、kubeadm、kubectl安装3.2 缺少一个镜像3.3 接入k8s集群 4. 一些相关问题 1.环境准备 rootcto-gpu-pro-n01:~# lsb_release -a No LSB modules are availa…...