Vue进阶语法
【一】v-model
修饰符
v-model
:针对 input 标签实现数据双向绑定
| <body> |
| <div id="app"> |
| <h1>v-model进阶</h1> |
| <p>普通 <br> |
| <input type="text" v-model="name">--------->{{name}} |
| </p> |
| <p>lazy:当失去焦点更新数据 <br> |
| <input type="text" v-model.lazy="name1">--------->{{name1}} |
| </p> |
| <p>number:数字开头,只保留数字,后面的字母不保留;字母开头,都保留 <br> |
| <input type="text" v-model.number="name2">--------->{{name2}} |
| </p> |
| <p>trim:去除首尾的空格 <br> |
| <input type="text" v-model.trim="name3">--------->{{name3}} |
| </p> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| name: '', |
| name1: '', |
| name2: '', |
| name3: '', |
| }, |
| methods: {}, |
| computed: {} |
| }) |
| </script> |

【1】v-model.lazy
和 @change
的区别
v-model.lazy
和 @change
都是 Vue 中处理表单输入的方式,但它们有一些区别:
- v-model.lazy:
v-model.lazy
是 v-model
的一个修饰符,它会在输入框失去焦点时才更新绑定的数据。
- 这意味着,当用户在输入框中输入内容时,并不会立即更新数据,而是等到用户离开输入框(例如点击其他地方)时才会更新数据。
- @change:
@change
是一个事件监听器,它会在表单元素的值发生改变并且失去焦点时触发。
- 这意味着,当用户在输入框中输入内容后,并且离开输入框时,触发
change
事件,从而执行相应的事件处理函数。
- 因此,主要的区别在于数据更新的时机:
v-model.lazy
是在失去焦点时才更新数据。
@change
是在失去焦点时触发事件,需要手动处理数据更新。
【二】vue发送ajax请求
| |
| -1 jquery的ajax |
| -2 js原生的fetch |
| -3 第三方axios(最多) |
【1】三种请求方式各自的优劣势及特点
【1.1】jQuery 的 AJAX
jQuery’s Ajax-Related Methods | jQuery Learning Center
-
特点:
-
简单易用,可以快速地发送 AJAX 请求。
-
兼容性好,适用于各种浏览器。
-
提供丰富的方法和选项,便于处理各种情况。
-
优势:
-
API 简单,易于上手。
-
兼容性好,能够在各种浏览器中使用。
-
功能丰富,提供了丰富的回调函数和选项。
-
劣势:
【1.2】原生 JavaScript 的 Fetch
使用 Fetch - Web API | MDN (mozilla.org)
【1.3】第三方库 Axios
基本用例 | Axios中文文档 | Axios中文网 (axios-http.cn)
-
特点:
-
优势:
-
支持 Promise,便于处理异步操作。
-
提供了丰富的功能和选项,如拦截器、全局配置等。
-
支持取消请求,能够有效地管理并发请求。
-
劣势:
-
需要额外引入库,增加了项目的依赖。
-
相对于原生 Fetch,体积略大。
【2】使用
| <body> |
| <div id="app"> |
| <p> |
| 数据 <br> |
| name {{name}} |
| age {{age}} |
| </p> |
| <p> |
| <button @click="handleJquery">jquery发送ajax</button> |
| </p> |
| <p> |
| <button @click="handleFetch">fetch发送ajax</button> |
| </p> |
| <p> |
| <button @click="handleAxios">axios发送ajax</button> |
| </p> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| name: 'lea4ning', |
| age: 20 |
| }, |
| methods: { |
| handleJquery() { |
| $.ajax({ |
| |
| url: 'http://127.0.0.1:5000/', |
| |
| type: 'POST', |
| |
| data: JSON.stringify({name: this.name, age: this.age}), |
| |
| contentType: 'application/json', |
| |
| success: (res) => { |
| this.name = res.result.name |
| this.age = res.result.age |
| }, |
| |
| error: (exc) => { |
| console.log(exc) |
| } |
| }) |
| }, |
| handleFetch() { |
| fetch('http://127.0.0.1:5000/', { |
| |
| method: 'POST', |
| |
| body: JSON.stringify({name: this.name, age: this.age}), |
| |
| headers: {'Content-Type': 'application/json'}, |
| |
| redirect: "follow" |
| }).then(response => response.json()).then( |
| |
| data => { |
| console.log(data) |
| this.name = data.result.name |
| this.age = data.result.age |
| }).catch( |
| |
| exc => console.error(exc)) |
| }, |
| handleAxios() { |
| axios.post('http://127.0.0.1:5000/', |
| {name: this.name, age: this.age}).then(response => { |
| |
| console.log(response); |
| this.name = response.data.result.name |
| this.age = response.data.result.age |
| }) |
| .catch(function (error) { |
| |
| console.log(error); |
| }) |
| .finally(function () { |
| |
| }); |
| }, |
| }, |
| }) |
| </script> |
| |
| |
| from flask import Flask, jsonify, request |
| |
| from flask_cors import CORS |
| |
| app = Flask(__name__) |
| CORS(app) |
| |
| |
| @app.route('/', methods=['POST']) |
| def index(): |
| |
| print(request.data) |
| |
| print(request.json) |
| res = jsonify({ |
| 'code': 100, |
| 'msg': 'success', |
| 'result': { |
| 'name': 'lea4ning_true', |
| 'age': 99 |
| } |
| }) |
| return res |
| |
| |
| if __name__ == '__main__': |
| app.run() |
| |
【3】跨源资源共享(CORS)
跨源资源共享(CORS) - HTTP | MDN (mozilla.org)
15-跨域请求详解 - 刘清政 - 博客园 (cnblogs.com)
【3.1】flask为响应添加CORS响应头
【3.1.1】简单请求
| import json |
| |
| from flask import Flask, jsonify |
| |
| app = Flask(__name__) |
| |
| |
| @app.route('/') |
| def index(): |
| res = jsonify({ |
| 'code': 100, |
| 'msg': 'success', |
| 'result': { |
| 'name': 'lea4ning', |
| 'age': 20 |
| } |
| }) |
| |
| res.headers['Access-Control-Allow-Origin'] = '*' |
| return res |
【3.1.2】使用flask-cors 解决跨域问题
| from flask import Flask, jsonify, request |
| |
| from flask_cors import CORS |
| |
| app = Flask(__name__) |
| |
| CORS(app) |
| |
| |
| @app.route('/') |
| def index(): |
| res = jsonify({ |
| 'code': 100, |
| 'msg': 'success', |
| 'result': { |
| 'name': 'lea4ning_true', |
| 'age': 99 |
| } |
| }) |
| return res |
【3.2】Django为响应添加CORS响应头
【3.2.1】简单请求
| def test(request): |
| import json |
| obj=HttpResponse(json.dumps({'name':'lqz'})) |
| |
| return obj |
| from django.utils.deprecation import MiddlewareMixin |
| class CorsMiddleWare(MiddlewareMixin): |
| def process_response(self,request,response): |
| if request.method=="OPTIONS": |
| |
| response["Access-Control-Allow-Headers"]="Content-Type" |
| response["Access-Control-Allow-Origin"] = "*" |
| return response |
| pip install django-cors-headers |
| |
| |
| |
| INSTALLED_APPS = ( |
| ... |
| 'corsheaders', |
| ... |
| ) |
| |
| |
| |
| MIDDLEWARE = [ |
| ... |
| 'corsheaders.middleware.CorsMiddleware', |
| ... |
| ] |
| |
| |
| |
| CORS_ORIGIN_ALLOW_ALL = True |
| CORS_ALLOW_METHODS = ( |
| 'DELETE', |
| 'GET', |
| 'OPTIONS', |
| 'PATCH', |
| 'POST', |
| 'PUT', |
| 'VIEW', |
| ) |
| |
| CORS_ALLOW_HEADERS = ( |
| 'XMLHttpRequest', |
| 'X_FILENAME', |
| 'accept-encoding', |
| 'authorization', |
| 'content-type', |
| 'dnt', |
| 'origin', |
| 'user-agent', |
| 'x-csrftoken', |
| 'x-requested-with', |
| 'Pragma', |
| 'token' |
| ) |
| |
【三】计算属性和侦听器
计算属性和侦听器 — Vue.js (vuejs.org)
【1】计算属性
- 计算属性会根据它的依赖进行缓存。只有当相关依赖发生改变时,才会重新计算。这意味着只要依赖没有发生变化,多次访问计算属性会立即返回之前缓存的结果,不会重新计算。
- 普通方法在每次调用时都会重新计算。即使相同的数据和逻辑在多个地方调用,它也会重新执行。
【1.1】使用
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: {}, |
| methods: {}, |
| |
| computed: { |
| 函数名(){ |
| |
| |
| return '计算属性的返回值' |
| }; |
| now: function () { |
| |
| return Date.now() |
| } |
| } |
| }) |
| </script> |
【1.2】示例
| <body> |
| <div id="app"> |
| <h1>计算属性</h1> |
| <p> |
| firstName:{{firstName}} |
| </p> |
| <p> |
| lastName:{{lastName}} |
| </p> |
| <p> |
| fullName:{{fullName}} <br> |
| 修改firstName: <input type="text" v-model.lazy="firstName"> |
| </p> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| firstName: '赵', |
| lastName: 'xxx' |
| }, |
| methods: {}, |
| computed: { |
| fullName() { |
| console.log('计算属性fullName执行了') |
| // 只有当依赖的变量发生变化时,才重新计算 |
| return this.firstName + this.lastName |
| } |
| } |
| }) |
| </script> |

【1.3】进阶用法
- 计算属性默认只有 getter,不过在需要时你也可以提供一个 setter
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: {}, |
| methods: {}, |
| |
| computed: { |
| 计算属性:{ |
| |
| |
| get:function(){ |
| return '获取计算属性时执行' |
| }, |
| set(){ |
| |
| } |
| } |
| } |
| }) |
| </script> |
| <body> |
| <div id="app"> |
| <h1>计算属性</h1> |
| <p> |
| firstName:{{firstName}} |
| </p> |
| <p> |
| lastName:{{lastName}} |
| </p> |
| <p> |
| fullName:{{fullName}} <br> |
| 修改fullName: <input type="text" v-model="fullName" @focus="fullName=''"> |
| </p> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| firstName: '', |
| lastName: '' |
| }, |
| methods: {}, |
| computed: { |
| fullName: { |
| get: function(){ |
| console.log('计算属性的get被触发了') |
| |
| return this.firstName + this.lastName |
| }, |
| set(value) { |
| console.log('计算属性的set被触发了') |
| |
| this.firstName = value.slice(0, 1) |
| this.lastName = value.slice(1) |
| } |
| } |
| } |
| }) |
| </script> |

【2】侦听器
侦听器 — Vue.js (vuejs.org)
- 监听一个属性,只要这个属性发生变化,就执行函数
- 当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的
【2.1】使用
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: {}, |
| watch: { |
| |
| 属性名:function (newValue,oldValue) { |
| |
| |
| } |
| </script> |
【2.2】简单示例
| <body> |
| <div id="app"> |
| <p>{{num}}</p> |
| <p> |
| <button @click="handleNum">数字加1</button> |
| </p> |
| <p>变化了【{{count}}】次</p> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| num: 0, |
| |
| count: 0 |
| }, |
| watch: { |
| |
| num: function (newValue, oldValue) { |
| console.log('new', newValue) |
| console.log('old', oldValue) |
| console.log('==================') |
| |
| this.count++ |
| } |
| }, |
| methods: { |
| handleNum() { |
| this.num += 1 |
| } |
| } |
| }) |
| </script> |

【2.3】案例(来自vue官网)
_.debounce
是 Lodash库提供的方法之一。
它用于创建一个函数,该函数在连续调用之间至少间隔指定的毫秒数。
这种功能常用于延迟执行某个操作,例如在用户输入时延迟发送请求或执行某个耗时操作,以提高性能和用户体验
| <body> |
| <div id="app"> |
| <div id="watch-example"> |
| <p> |
| Ask a yes/no question: |
| <input v-model="question"> |
| </p> |
| <p>{{ answer }}</p> |
| </div> |
| </div> |
| </body> |
| |
| |
| <script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script> |
| <script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script> |
| <script> |
| var watchExampleVM = new Vue({ |
| el: '#app', |
| data: { |
| question: '', |
| answer: '我不能回答,直到你提出了问题!' |
| }, |
| watch: { |
| |
| question: function (newQuestion, oldQuestion) { |
| this.answer = 'Waiting for you to stop typing...' |
| this.debouncedGetAnswer() |
| } |
| }, |
| created: function () { |
| |
| |
| |
| |
| |
| this.debouncedGetAnswer = _.debounce(this.getAnswer, 500) |
| }, |
| methods: { |
| getAnswer: function () { |
| if (this.question.indexOf('?') === -1) { |
| this.answer = '问题通常包含一个【?】. ;-)' |
| return |
| } |
| this.answer = '思考中...' |
| var vm = this |
| axios.get('https://yesno.wtf/api') |
| .then(function (response) { |
| vm.answer = _.capitalize(response.data.answer) |
| }) |
| .catch(function (error) { |
| vm.answer = 'Error! Could not reach the API. ' + error |
| }) |
| } |
| } |
| }) |
| </script> |
【五】组件使用
-
局部组件:只能在当前页面中使用
-
全局组件:全局都可以用
【1】全局组件
【1.1】使用
| <body> |
| <div id="app"> |
| <h1>组件</h1> |
| <h2>子组件child</h2> |
| <child></child> |
| <h3>可以重复使用</h3> |
| <child></child> |
| <child></child> |
| </div> |
| </body> |
| <script> |
| |
| Vue.component('组件名', { |
| template: ` |
| <!-- 组件内容 --> |
| `, |
| data() { |
| |
| return {} |
| }, |
| methods: {}, |
| computed: {} |
| |
| }) |
| var vm = new Vue({ |
| |
| el: '#app', |
| data: {}, |
| methods: {}, |
| computed: {}, |
| }) |
| </script> |
【2】局部组件
【2.1】使用
| <body> |
| <div id="app"> |
| <h1>组件</h1> |
| <h2>子组件child</h2> |
| <child></child> |
| </div> |
| </body> |
| <script> |
| // Vue.component 创建全局组件 |
| Vue.component('child', { |
| template: ` |
| <div> |
| <p>child</p> |
| |
| <aaa></aaa> |
| </div> |
| |
| `, |
| data() { |
| return {} |
| }, |
| methods: {}, |
| computed: {}, |
| // 组件都可以有自己的data,methods,computed等属性 |
| components: { |
| 'aaa': { |
| template: ` |
| <div>局部组件,只能在所属的组件中使用</div>`, |
| data() { |
| return {} |
| }, |
| methods: {} |
| } |
| } |
| }) |
| var vm = new Vue({ |
| // 通过el挂载在页面上的,我们称为根组件 |
| el: '#app', |
| data: {}, |
| methods: {}, |
| computed: {}, |
| components: { |
| // 根组件中也可以注册局部组件 |
| } |
| }) |
| </script> |
【六】生命周期钩子函数
【1】组件生命周期

【2】生命周期钩子函数
钩子函数 |
描述 |
beforeCreate |
创建Vue实例之前调用 |
created |
创建Vue实例成功后调用(可以在此处发送异步请求后端数据) |
beforeMount |
渲染DOM之前调用 |
mounted |
渲染DOM之后调用 |
beforeUpdate |
重新渲染之前调用(数据更新等操作时,控制DOM重新渲染) |
updated |
重新渲染完成之后调用 |
beforeDestroy |
销毁之前调用 |
destroyed |
销毁之后调用 |
【3】案例
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>生命周期</title> |
| <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script> |
| </head> |
| <body> |
| <div id="box"> |
| <child v-if="isShow"></child> |
| <br> |
| <button @click="terminate">删除子组件</button> |
| <button @click="reborn">显示子组件</button> |
| </div> |
| </body> |
| <script> |
| Vue.component('child', { |
| template: ` |
| <div> |
| {{name}} |
| <button @click="name='Darker1'">更新数据1</button> |
| <button @click="name='Darker2'">更新数据2</button> |
| </div>`, |
| data() { |
| return { |
| name: 'Darker1', |
| } |
| }, |
| beforeCreate() { |
| console.group('当前状态:beforeCreate') |
| console.log('当前el状态:', this.$el) |
| console.log('当前data状态:', this.$data) |
| console.log('当前name状态:', this.name) |
| }, |
| created() { |
| console.group('当前状态:created') |
| console.log('当前el状态:', this.$el) |
| console.log('当前data状态:', this.$data) |
| console.log('当前name状态:', this.name) |
| }, |
| beforeMount() { |
| console.group('当前状态:beforeMount') |
| console.log('当前el状态:', this.$el) |
| console.log('当前data状态:', this.$data) |
| console.log('当前name状态:', this.name) |
| }, |
| mounted() { |
| console.group('当前状态:mounted') |
| console.log('当前el状态:', this.$el) |
| console.log('当前data状态:', this.$data) |
| console.log('当前name状态:', this.name) |
| }, |
| beforeUpdate() { |
| console.group('当前状态:beforeUpdate') |
| console.log('当前el状态:', this.$el) |
| console.log('当前data状态:', this.$data) |
| console.log('当前name状态:', this.name) |
| }, |
| updated() { |
| console.group('当前状态:updated') |
| console.log('当前el状态:', this.$el) |
| console.log('当前data状态:', this.$data) |
| console.log('当前name状态:', this.name) |
| }, |
| beforeDestroy() { |
| console.group('当前状态:beforeDestroy') |
| console.log('当前el状态:', this.$el) |
| console.log('当前data状态:', this.$data) |
| console.log('当前name状态:', this.name) |
| }, |
| destroyed() { |
| console.group('当前状态:destroyed') |
| console.log('当前el状态:', this.$el) |
| console.log('当前data状态:', this.$data) |
| console.log('当前name状态:', this.name) |
| }, |
| |
| |
| }) |
| let vm = new Vue({ |
| el: '#box', |
| data: { |
| isShow: true |
| }, |
| methods: { |
| terminate() { |
| this.isShow = false |
| }, |
| reborn() { |
| this.isShow = true |
| } |
| } |
| }) |
| </script> |
| </html> |
| |
【七】父组件与子组件通信
【1】父组件向子组件通信(自定义属性)
【1.1】基本使用
| <body> |
| <div id="app"> |
| <h1>根组件</h1> |
| |
| <child 自定义属性名="值"></child> |
| <child :自定义属性名="变量"></child> |
| <child v-bind:自定义属性名="变量"></child> |
| </div> |
| </body> |
| <script> |
| |
| Vue.component('child',{ |
| template:`<div>{{自定义属性名}}</div>`, |
| props:['自定义属性名'] |
| }) |
| var vm = new Vue({ |
| el: '#app', |
| components: { |
| child: { |
| |
| template: `<div></div>`, |
| data() {return {}}, |
| |
| props: ['自定义属性名'], |
| } |
| } |
| }) |
| </script> |
【1.2】示例
| <body> |
| <div id="app"> |
| <h1>根组件</h1> |
| <h2>根组件下的子组件child</h2> |
| <hr> |
| <p>自定义属性 【:属性名=变量】</p> |
| <child :datafromfather="value1"></child> |
| <hr> |
| <p>自定义属性 【v-bind:属性名=值】</p> |
| <child v-bind:data3="value2"></child> |
| <hr> |
| <p>自定义属性 【属性名=值】</p> |
| <child data2="value1"></child> |
| </div> |
| </body> |
| <script> |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| value1: 'data from father', |
| value2: '222', |
| }, |
| components: { |
| child: { |
| // 定义局部组件 |
| template: ` |
| <div> |
| <h1>child</h1> |
| <ul> |
| <template v-for="value in $props"> |
| |
| <li v-if="value !== undefined"> |
| 父组件传递的数据: {{ value }} |
| </li> |
| </template> |
| </ul> |
| </div> |
| `, |
| created() { |
| // 组件创建完毕后,父传子的数据就有了 |
| console.log(this.$props) |
| console.log(this.datafromfather, this.data2, this.data3) |
| }, |
| props: ['datafromfather', 'data2', 'data3'], // 使用数组接收父组件传递的数据 |
| } |
| } |
| }) |
| </script> |
【2】子组将向父组件通信(自定义事件)
【2.1】基本使用
| <body> |
| <div id="app"> |
| |
| <child @myevent="handleChild"></child> |
| <p>定义变量接收子组件传回的数据:{{value}}</p> |
| </div> |
| </body> |
| <script> |
| Vue.component('child', { |
| |
| template: ` |
| <div><button @click="handleSend">向父组件中传数据</button></div>`, |
| methods: { |
| handleSend(){ |
| this.$emit('自定义事件名',传递的参数) |
| } |
| } |
| }) |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| value: '', |
| }, |
| methods: { |
| |
| handleChild(value) { |
| this.value = value |
| } |
| }, |
| }) |
| </script> |
【2.2】示例
| <body> |
| <div id="app"> |
| |
| <child @myevent="handleChild"></child> |
| <p>子组件向父组件传递的数据:{{value}}</p> |
| </div> |
| </body> |
| <script> |
| Vue.component('child', { |
| template: ` |
| <div><button @click="handleSend">向父组件中传数据</button></div>`, |
| methods: { |
| handleSend(){ |
| |
| this.$emit('myevent','data from son') |
| |
| } |
| } |
| }) |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| value: '', |
| }, |
| methods: { |
| |
| handleChild(value) { |
| this.value = value |
| } |
| }, |
| }) |
| </script> |
【3】ref参数
- 当
ref
被用在普通 HTML 元素上时,它会注册对应的 DOM 元素
- 当
ref
被用在组件标签上时,它会注册对应的子组件实例
| <body> |
| <div id="app"> |
| <div ref="normalLabel">普通标签</div> |
| <div> |
| 组件标签 |
| <child ref="childLabel"></child> |
| </div> |
| <p> |
| <button @click="handlePrint">看控制台输出内容</button> |
| </p> |
| </div> |
| </body> |
| <script> |
| let normalComponent = { |
| template: ` |
| <div>组件</div> |
| `, |
| data() { |
| return {name: 'lea4ning'} |
| } |
| } |
| Vue.component('child', normalComponent) |
| var vm = new Vue({ |
| el: '#app', |
| methods: { |
| handlePrint() { |
| console.log(this.$refs) |
| console.log('dom对象', this.$refs.normalLabel) |
| |
| this.$refs.normalLabel.style.color='red' |
| console.log('组件对象', this.$refs.childLabel) |
| console.log('组件中的属性', this.$refs.childLabel.name) |
| } |
| } |
| }) |
| </script> |

【八】动态组件
- 使用component标签,并指定【is】属性即可实现组件间切换
【1】使用演示
| <component is='组件名'></component> |
| <body> |
| <div id="app"> |
| <h1>动态组件</h1> |
| <p>使用按钮进行切换,什么方式均可,改变component的is的值即可</p> |
| <p><button @click="current='child1'">child1</button></p> |
| <p><button @click="current='child2'">child2</button></p> |
| <div> |
| <p>使用component标签,并指定【is】属性</p> |
| <component :is="current"></component> |
| </div> |
| <hr> |
| <div> |
| <p>也可以直接写个组件名,但就没有使用动态组件的必要了</p> |
| <component is="child1"></component> |
| </div> |
| </div> |
| </body> |
| <script> |
| let normalComponent1 = { |
| template: ` |
| <div>组件1</div> |
| ` |
| } |
| let normalComponent2 = { |
| template: ` |
| <div>组件2</div> |
| ` |
| } |
| |
| Vue.component('child1', normalComponent1) |
| Vue.component('child2', normalComponent2) |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| current: '' |
| } |
| }) |
| </script> |
【2】使用keep-alive标签保持状态
| <keep-alive> |
| <component is='xxx'></component> |
| </keep-alive> |
| <body> |
| <div id="app"> |
| <h1>动态组件</h1> |
| <p> |
| <button @click="current='child1'">child1</button> |
| </p> |
| <p> |
| <button @click="current='child2'">child2</button> |
| </p> |
| <div> |
| <p>使用keep-alive标签包裹component标签即可实现状态保存</p> |
| <keep-alive> |
| <component :is="current"></component> |
| </keep-alive> |
| </div> |
| </div> |
| </body> |
| <script> |
| let normalComponent1 = { |
| template: ` |
| <div> |
| <p>组件1</p> |
| <input type="text"> |
| </div> |
| ` |
| } |
| let normalComponent2 = { |
| template: ` |
| <div> |
| <p>组件2</p> |
| <input type="text"> |
| </div> |
| ` |
| } |
| // 注册多个全局组件 |
| Vue.component('child1', normalComponent1) |
| Vue.component('child2', normalComponent2) |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| current: '' |
| } |
| }) |
| </script> |

【九】插槽
【1】5种插槽的基本模板
【1.1】默认插槽(Default Slot)
| <child-component> |
| <p>这是默认插槽的内容</p> |
| </child-component> |
| <div> |
| <slot></slot> |
| </div> |
【1.2】具名插槽(Named Slots):
| <div> |
| <child-component> |
| <template v-slot:header> |
| <h2>这是头部插槽的内容</h2> |
| </template> |
| <template v-slot:footer> |
| <p>这是底部插槽的内容</p> |
| </template> |
| </child-component> |
| </div> |
| <div> |
| <header> |
| <slot name="header"></slot> |
| </header> |
| <footer> |
| <slot name="footer"></slot> |
| </footer> |
| </div> |
【1.3】作用域插槽(Scoped Slots)
| <template> |
| <div> |
| <child-component v-slot:user="slotProps"> |
| <p>{{ slotProps.name }}</p> |
| <p>{{ slotProps.age }}</p> |
| </child-component> |
| </div> |
| </template> |
| html复制代码 |
子组件模板:
| <template> |
| <div> |
| <slot name="user" :name="user.name" :age="user.age"></slot> |
| </div> |
| </template> |
| |
| <script> |
| export default { |
| data() { |
| return { |
| user: { |
| name: 'John', |
| age: 30 |
| } |
| }; |
| } |
| }; |
| </script> |
| html复制代码 |
【1.4】作用域插槽的默认值(Scoped Slots with Default Value)
| <div> |
| <child-component> |
| <template #default="slotProps"> |
| <p v-if="slotProps">{{ slotProps }}</p> |
| <p v-else>默认值</p> |
| </template> |
| </child-component> |
| </div> |
| <template> |
| <div> |
| <slot :data="someData">默认值</slot> |
| </div> |
| </template> |
| |
| <script> |
| export default { |
| data() { |
| return { |
| someData: '这是来自子组件的数据' |
| }; |
| } |
| }; |
| </script> |
【1.5】动态插槽(Dynamic Slots)
| <template> |
| <div> |
| <child-component :slot-name="slotName"> |
| <p>动态插槽的内容</p> |
| </child-component> |
| </div> |
| </template> |
| |
| <script> |
| export default { |
| data() { |
| return { |
| slotName: 'header' |
| }; |
| } |
| }; |
| </script> |
| <template> |
| <div> |
| <slot :name="slotName"></slot> |
| </div> |
| </template> |
| |
| <script> |
| export default { |
| props: ['slotName'] |
| }; |
| </script> |
【2】实例展示
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>插槽</title> |
| <script src="../js/vue/vue.js"></script> |
| </head> |
| <body> |
| <div id="app"> |
| <h1>插槽</h1> |
| <h2>默认插槽</h2> |
| <child1><p>这是默认插槽的内容</p></child1> |
| <hr> |
| <h2>具名插槽</h2> |
| <child2> |
| <template v-slot:a>具名插槽:aaa</template> |
| <template v-slot:b>具名插槽:bbb</template> |
| </child2> |
| <hr> |
| <h2>作用域插槽</h2> |
| <child3 v-slot:person="slotProps"> |
| |
| <p>{{ slotProps.obj.name }}</p> |
| <p>{{ slotProps.obj.age }}</p> |
| </child3> |
| <hr> |
| <h2>作用域插槽的默认值</h2> |
| <child4 v-slot:default="slotProps"> |
| |
| |
| <p v-if="slotProps.age">{{ slotProps.age }}</p> |
| <p v-else>当slotProps没有数据,也就是组件中没有传递数据时,渲染默认值</p> |
| </child4> |
| <hr> |
| <h2>动态插槽</h2> |
| <child5 :slot-name="slotName"> |
| <p>动态插槽:根据slot-name动态渲染到指定插槽</p> |
| </child5> |
| |
| </div> |
| </body> |
| <script> |
| let normalComponent1 = { |
| template: ` |
| <div> |
| <h3>组件1</h3> |
| <slot></slot> |
| </div> |
| ` |
| } |
| let normalComponent2 = { |
| template: ` |
| <div> |
| <h3>组件2</h3> |
| <p>slot-a:<slot name="a"></slot></p> |
| <p>slot-b:<slot name="b"></slot></p> |
| </div> |
| ` |
| } |
| let normalComponent3 = { |
| template: ` |
| <div> |
| <h3>组件3</h3> |
| <slot name="person" :obj="user"></slot> |
| </div> |
| `, |
| data() { |
| return { |
| user: {name: 'lea4ning', age: 20} |
| } |
| } |
| } |
| let normalComponent4 = { |
| template: ` |
| <div> |
| <h3>组件4</h3> |
| <slot :age="isShow?user.age:undefined"></slot> |
| </div> |
| `, |
| data() { |
| return { |
| user: {name: 'lea4ning', age: 20}, |
| // 使用变量动态表示渲染默认值或数据 |
| isShow: true |
| } |
| } |
| } |
| let normalComponent5 = { |
| template: ` |
| <div> |
| <h3>组件5</h3> |
| <slot :name="slotName"></slot> |
| </div> |
| `, |
| props:['slotName'] |
| } |
| // 注册多个全局组件 |
| Vue.component('child1', normalComponent1) |
| Vue.component('child2', normalComponent2) |
| Vue.component('child3', normalComponent3) |
| Vue.component('child4', normalComponent4) |
| Vue.component('child5', normalComponent5) |
| var vm = new Vue({ |
| el: '#app', |
| data: { |
| slotName:'default' |
| } |
| }) |
| </script> |
| </html> |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了