Vue2进阶语法

Vue进阶语法

【一】v-model修饰符

v-model:针对 input 标签实现数据双向绑定

# lazy:等待input框的数据绑定失去焦点之后再变化
# number:数字开头,只保留数字,后面的字母不保留;字母开头,都保留
# trim:去除首尾的空格
<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>

Vue-v-model进阶

【1】v-model.lazy@change的区别

  • v-model.lazy@change 都是 Vue 中处理表单输入的方式,但它们有一些区别:
    1. v-model.lazy
      • v-model.lazyv-model 的一个修饰符,它会在输入框失去焦点时才更新绑定的数据。
      • 这意味着,当用户在输入框中输入内容时,并不会立即更新数据,而是等到用户离开输入框(例如点击其他地方)时才会更新数据。
    2. @change
      • @change 是一个事件监听器,它会在表单元素的值发生改变并且失去焦点时触发。
      • 这意味着,当用户在输入框中输入内容后,并且离开输入框时,触发 change 事件,从而执行相应的事件处理函数。
  • 因此,主要的区别在于数据更新的时机:
    • v-model.lazy 是在失去焦点时才更新数据。
    • @change 是在失去焦点时触发事件,需要手动处理数据更新。

【二】vue发送ajax请求

# 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 简单,易于上手。

    • 兼容性好,能够在各种浏览器中使用。

    • 功能丰富,提供了丰富的回调函数和选项。

  • 劣势:

    • 依赖于整个 jQuery 库,如果项目中只使用了少量 jQuery 功能,会显得冗余。

    • jQuery 的体积相对较大,可能会影响页面加载速度。

【1.2】原生 JavaScript 的 Fetch

使用 Fetch - Web API | MDN (mozilla.org)

  • 特点:

    • 是原生的浏览器 API,不依赖于任何库。

    • 使用 Promise API 处理异步请求,代码结构清晰。

  • 优势:

    • 原生 API,不需要引入额外的库,减少了项目的依赖。

    • 支持 Promise,更好地处理异步操作。

  • 劣势:

    • 兼容性较差,一些老旧的浏览器不支持 Fetch API。

    • 功能相对简单,需要自己封装一些功能来满足需求。

【1.3】第三方库 Axios

基本用例 | Axios中文文档 | Axios中文网 (axios-http.cn)

  • 特点:

    • 基于 Promise,支持浏览器和 Node.js。

    • 提供了丰富的功能,如拦截器、取消请求等。

    • 在前端社区中广泛应用,有大量的文档和社区支持。

  • 优势:

    • 支持 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}),
                    // 指定 Content-Type 为 JSON
                    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/', {
                    // 请求方法 默认时get请求
                    method: 'POST',
                    // 携带在请求体中的数据
                    body: JSON.stringify({name: this.name, age: this.age}),
                    // 请求头
                    headers: {'Content-Type': 'application/json'},
                    // 如何处理重定向
                    redirect: "follow"
                }).then(response => response.json()).then(
                    // 固定格式第一次需要将 响应数据.json()
                    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>
# flask 后端

from flask import Flask, jsonify, request
# 第三方模块  # 去除cors跨域请求验证
from flask_cors import CORS  # pip install flask-cors

app = Flask(__name__)
CORS(app)


@app.route('/', methods=['POST'])
def index():
    # 查看请求携带的数据
    print(request.data)
    # 将请求携带的json数据转换成字典格式
    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)

  • 跨源资源共享CORS,或通俗地译为跨域资源共享)是一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其他(域、协议或端口),使得浏览器允许这些源访问加载自己的资源。

  • 出于安全性,浏览器限制脚本内发起的跨源 HTTP 请求。这意味着使用这些 API 的 Web 应用程序只能从加载应用程序的同一个域请求 HTTP 资源,除非响应报文包含了正确 CORS 响应头。

【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'}))
    # obj['Access-Control-Allow-Origin']='*'
    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
【3.2.2】使用django-cors-headers 解决跨域问题
pip install django-cors-headers
# settings.py

### 注册APP
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 中
        computed: {
            函数名(){
                // 调用data中的数据  // 当data中的数据发生变化时,计算属性也就会发生变化
                // 【注】只有在计算属性中调用了的变量发生变化时,才会跟着变化
                return '计算属性的返回值'
            };
            now: function () {
       			 // 该计算属性将不会发生变化,因为Date.now()并不是响应式依赖
    			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>

Vue-计算属性

【1.3】进阶用法

  • 计算属性默认只有 getter,不过在需要时你也可以提供一个 setter
<script>
    var vm = new Vue({
        el: '#app',
        data: {},
        methods: {},
        // 将计算属性写在 computed 中
        computed: {
            计算属性:{
                // 指定set需要,将计算属性写成对象形式  // 而不是函数形式
                // 对象中包含两个函数,get和set
                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被触发了')
                    // 当获取属性时,调用get
                    return this.firstName + this.lastName
                },
                set(value) {
                    console.log('计算属性的set被触发了')
                    // 当修改计算属性时,调用set
                    this.firstName = value.slice(0, 1)
                    this.lastName = value.slice(1)
                }
            }
        }
    })
</script>

Vue-计算属性进阶

【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: {
            // 如果 `count` 发生改变,这个函数就会运行
            num: function (newValue, oldValue) {
                console.log('new', newValue)
                console.log('old', oldValue)
                console.log('==================')
                // 变化次数+1
                this.count++
            }
        },
        methods: {
            handleNum() {
                this.num += 1
            }
        }
    })
</script>

Vue-侦听器

【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>
<!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 -->
<!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 -->
<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` 发生改变,这个函数就会运行
            question: function (newQuestion, oldQuestion) {
                this.answer = 'Waiting for you to stop typing...'
                this.debouncedGetAnswer()
            }
        },
        created: function () {
            // `_.debounce` 是一个通过 Lodash 限制操作频率的函数。
            // 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率
            // AJAX 请求直到用户输入完毕才会发出。想要了解更多关于
            // `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识,
            // 请参考:https://lodash.com/docs#debounce
            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 创建全局组件
    Vue.component('组件名', {
        template: `
          <!-- 组件内容 -->
        `,
        data() {
            // 除了根组件,其余组件的data均需要使用函数返回
            return {}
        },
        methods: {}, // 每一个组件有自己的方法
        computed: {} // 有自己的计算属性等
        // 组件都可以有自己的data,methods,computed等属性
    })
    var vm = new Vue({
        // 通过el挂载在页面上的,我们称为根组件
        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】组件生命周期

img

【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】数组中,传入与自定义属性名一致的字符串即可在子组件中使用 
                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">
                        <!-- 遍历$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">
	<!-- 【1】在子组件标签内定义自定义事件-->
    <child @myevent="handleChild"></child>
    <p>定义变量接收子组件传回的数据:{{value}}</p>
</div>
</body>
<script>
    Vue.component('child', {
        // 【4】this.$emit('自定义事件名',传递的参数) 向父组件发送数据
        template: `
          <div><button @click="handleSend">向父组件中传数据</button></div>`,
        methods: {
            handleSend(){
                this.$emit('自定义事件名',传递的参数)
            }
        }
    })
    var vm = new Vue({
        el: '#app',
        data: {
            value: '', // 【2】定义一个变量接收子组件传入的数据
        },
        methods: {
            // 【3】在父组件中定义方法接收并处理子组件的数据
            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')
                // this.$emit('自定义事件名',传递的参数)
            }
        }
    })
    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)
                // 操作dom对象
                this.$refs.normalLabel.style.color='red'
                console.log('组件对象', this.$refs.childLabel)
                console.log('组件中的属性', this.$refs.childLabel.name)
            }
        }
    })
</script>

image-20240507185828972

【八】动态组件

  • 使用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>

Vue-动态组件keep-alive保存状态

【九】插槽

【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">
        <!-- v-slot:slot名="接收数据的变量-->
        <p>{{ slotProps.obj.name }}</p>
        <p>{{ slotProps.obj.age }}</p>
    </child3>
    <hr>
    <h2>作用域插槽的默认值</h2>
    <child4 v-slot:default="slotProps">
        <!-- v-solt可以简写成【#】-->
        <!-- <child4 #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>
posted @ 2024-05-07 20:35  Lea4ning  阅读(17)  评论(0编辑  收藏  举报