Vue速成

发布于:2023-01-22 ⋅ 阅读:(448) ⋅ 点赞:(0)

vue基本概述

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。

Vue.js是一套构建用户界面的渐进式框架,采用自底向上增量开发的设计。Vue的核心库关注于视图(html),不仅易上手,还便于与第三方库或项目整合。

渐进式:一步一步,不是将所有的东西都学完才能使用。

自底向上设计:一种设计程序的过程和方法,就是先编写出基础程序段,然后在逐步扩大规范、补充和升级某些 功能,实际上是一种自底向上构造程序的过程。

Vue.js的核心是允许采用简洁式模板语法来声明的将数据渲染进DOM的系统

在使用数据之前需要先进性声明才可以使用

为什么要学习Vue?

简单,前端三大框架(Vue,React,angler)中是对小白最友好的框架

另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

Vue基本语法

插值表达式

使用{{ }}进行渲染data:{}里面的变量

也可以是函数,不过在data里面的方法,在插值表达式中需要补充大括号

例如

<div id="app">
    <div>{{ msg }}</div>
    <div>{{ getInfo() }}</div>
</div>
<script src="../vue.js"></script>
<script>
    new Vue({
        el: '#app',
        data: {
            msg: '枫枫知道',
            getInfo() {
                // 函数的返回值就是插值表达式中的结果
                return "床前明月光"
            }
        }
    })
</script>

在插值表达式中,支持运算符,以及一些方法

vue指令:
渲染文本,和插值表达式类似

v-text
<div v-text="msg"></div>
<div>{{ msg }}</div>

也是支持运算符的

<div v-text="2 > 4 ? '成立': '不成立'"></div>

v-html

渲染标签,如果字符串是含标签的特殊字符,那么vue会将它渲染成标签

如果是v-text或者是使用插值表达式,则会原样输出

注意:

v-html一般是用来渲染信任的文本,例如文章的详情内容等,最好不要用在用户提交的地方

容易造成XSS攻击

v-bind

可以给标签动态添加内容,也可以给动态的修改标签属性

不过在属性上的操作稍微和操作内容不太一样,我们使用v-bind指令

例如我想动态修改img标签的src属性,希望它去读取data里面的值

但是我们不能在属性中使用插值表达式

这样写src直接将我的内容原样输出了,所以我们需要使用v-bind,全称为动态属性

那么我可以这么做

<img v-bind:src="src" alt="">

当然,v-bind: 可以简写为 :

<img :src="src" alt="">

v-bind不仅可以用于HTML存在的属性,还可以应用在自定义属性上

例如

<p :id="'p' + '_01'" :data="msg" :还可以是中文="msg"></p>
实际结果:
<p id="p_01" data="haha" 还可以是中文="haha"></p>

在vue所有的指令中,都是支持表达式的

例如:

<div class="name">
    {{ a.length === 0 ? '没有数据' : a }}
    -{{ a.split('.')[1] }}
</div>

什么是表达式?

// 这是语句
var a = 1;
// 这是表达式
a.length === 0 ? '没有数据' : a

条件渲染

v-if和v-else的普通使用

v-if中的布尔值为true,则渲染这个div

如果if不成立,则渲染else中的代码块

<div id="app">
    <div>{{ num }}</div>
    <div id="if">
        <div v-if="num > 0.9">大于0.9的概率</div>
        <div v-else>v-if不满足显示我</div>
    </div>
</div>
<script src="../vue.js"></script>
<script>
    new Vue({
        el: '#app',
        data: {
            num: Math.random(), // 一个0-1之间的随机数字
        }
    })
</script>
v-else-if多条件语句
<div id="v-else-if">
    <div v-if="num > 0.9">大于0.9的概率</div>
    <div v-else-if="num > 0.6">大于0.6的概率</div>
    <div v-else-if="num > 0.4">大于0.4的概率</div>
    <div v-else-if="num > 0.2">大于0.2的概率</div>
    <div v-else>所有条件都不成立</div>
</div>

v-if=”flag”,为true的时候显示

为false的时候消失

<div id="app">
    <div class="name" v-if="flag">
        {{ name }}
    </div>
    <div v-else>
        if没匹配到就显示我
    </div>
</div>
<script>
    var vue = new Vue({
        el: '#app', // vue挂载的位置,不能是body
        data: {
            flag: false,
            name: '枫枫'
        }
    })
</script>
v-show与v-if的区别

v-if如果是false,不会渲染
v-show如果是false,不会显示 style=“display: none;”
体会一下v-if和v-show

列表渲染

主要分为遍历列表和遍历对象

遍历列表
lis: [
    '张三',
    '王伟',
    '张伟',
    '王五',
]

此时的item就是每个元素

<ul>
    <li v-for="item in lis" :key="item">
        {{ item }}
    </li>
</ul>

key要唯一!!!

如果需要遍历出每个元素的索引

则在遍历的时候指定index

<ul>
    <li v-for="(item, index) in lis" :key="item">
        {{ index }} -- {{ item }}
    </li>
</ul>
遍历对象
obj:{
    name: '张三',
    age: 21,
    addr: '北京市',
}

一个参数,就是遍历对象的值

二个参数,值和键

三个参数,值,键,索引

<ul>
    <li v-for="item in obj" :key="item">
        对象的值:{{ item }}
    </li>
</ul>
<ul>
    <li v-for="(item, key) in obj" :key="item">
        对象的值:{{ item }}
        对象的键:{{ key }}
    </li>
</ul>
<ul>
    <li v-for="(item, key, index) in obj" :key="item">
        对象的值:{{ item }}
        对象的键:{{ key }}
        变量的索引:{{ index }}
    </li>
</ul>

Vue事件

v-on:或者@

<div id="app">
    <div>
        {{ num }}
    </div>
    <div>
        <!--        执行的方法必须在methods中-->
        <button v-on:click="add">点我 +</button>
        <button @dblclick="num--">点我 -1</button>
    </div>
</div>

事件所执行的方法是要被定义在methods里面

methods: {
    add() {
        this.num ++
    }
}

参数问题

可以通过传参进行参数传递

<button v-on:click="add(10)">点我 +</button>
...
add(number) {
    this.num += number
}
// 此时的add函数就能接收到传递进来的参数值

默认参数

默认参数就是触发当前事件的事件对象

<button id="add" class="add_cls" v-on:click="add">点我 +</button>

一定是v-on:click=“add”,只写一个函数名,

而不是v-on:click=“add()”,这样获取不到事件对象

// 获取触发当前事件的标签
add(event) {
    // 触发当前事件的事件对象
    console.log(event)
    // 获取标签
    console.log(event.target)
    // 获取id,class
    console.log(event.target.id)
    console.log(event.target.className)
}

如果有参数,想接收事件对象

使用$event进行传递

<button id="add" class="add_cls" v-on:click="add(10, $event)">点我 +</button>
...
add(number, event) {
    this.num += number
    // 触发当前事件的事件对象
    console.log(event)
    // 获取标签
    console.log(event.target)
    // 获取id,class
    console.log(event.target.id)
    console.log(event.target.className)
}

图片轮播案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>图片轮播</title>
    <script src="../vue.js"></script>
</head>
<body>
<div id="app">
    <div>
        <img :src="img_list[index]" alt="">
    </div>
    <div>
        <button @click="prevImag(index, img_list.length)">上一张</button>
        <button @click="nextImag">下一张</button>
    </div>
</div>
<script>
    var vue = new Vue({
        el: '#app', // vue挂载的位置,不能是body
        data: {
            index: 0,
            img_list: [
                'http://h2.ioliu.cn/bing/SalzburgKrampus_ZH-CN7355658592_640x480.jpg?imageslim',
                'http://h2.ioliu.cn/bing/MistyTor_ZH-CN7520952555_640x480.jpg?imageslim',
                'http://h2.ioliu.cn/bing/Koenigsbourg_ZH-CN7675452866_640x480.jpg?imageslim',
                'http://h2.ioliu.cn/bing/CuvervilleIsland_ZH-CN9814166047_640x480.jpg?imageslim',
                'http://h2.ioliu.cn/bing/ElanValley_ZH-CN9533069637_640x480.jpg?imageslim',
                'http://h2.ioliu.cn/bing/WinterWaxwing_ZH-CN9435499385_640x480.jpg?imageslim',
            ]
        },
        methods: {
            // 下一张
            nextImag() {
                // 让索引加1
                // 判断是不是最后一张,如果是就回到第一张
                if (this.index === this.img_list.length - 1) {
                    this.index = 0
                    return
                }
                this.index++
            },
            // 上一张
            prevImag(index, len){
                // 我希望把索引和列表长度传递进来
                if (index === 0) {
                    this.index = len - 1
                    return
                }
                this.index --
            }
        }
    })
</script>
</body>
</html>

事件修饰符

在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。

为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。

<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
<!--阻止默认事件-->
<a href="http://www.fengfengzhidao.com" @click.prevent="my_alert">枫枫知道</a>

事件冒泡

<div class="parent" @click="log('外元素')">
    <div class="center" @click="log('中元素')">
        <div class="child" @click="log('里元素')">
        </div>
    </div>
</div>

点击里元素,执行顺序

点击中间的元素

被父元素包裹的元素,点击事件发送后会逐级将事件向上传递

<p>阻止事件冒泡</p>
<div class="parent" @click="log('外元素')">
    <div class="center" @click="log('中元素')">
        <div class="child" @click.stop="log('里元素')">
        </div>
    </div>
</div>

添加stop事件修饰符之后,点击里元素,会阻止事件继续向上传播

键盘事件

@keydown   键盘按下
@keyup     键盘回弹

按下回车触发

支持组合键

<input type="text" v-model="msg" @keyup.enter.ctrl="get_msg('上')">
<input type="text" v-model="msg" @keyup.enter="get_msg('上')">

按键修饰符

最常见的可能就是在一个输入框中,判断用户是否按下了回车键

<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">

一些必要的按键名称

.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right

组合按键

<!-- Alt + C -->
<input v-on:keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div v-on:click.ctrl="doSomething">Do something</div>

计算属性

调用的时候不用加括号(只是一个属性)
可以监听属性变化,属性变化,计算属性重新执行
并且有缓存(多个计算属性,只执行一次)
和methods的区别

属性变化,methods方法全部重新获取

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>计算属性</title>
    <script src="../vue.js"></script>
</head>
<body>
<div id="app">
    <div>{{ name }}</div>
    <div>{{ name.split('').reverse().join('') }}</div>
    <div>{{ name.length === 5 ? '五言绝句' : '七言绝句' }}</div>
    <div v-if="name.length === 5">五言绝句</div>
    <div v-else-if="name.length === 7">七言绝句</div>
    <div v-else>错了</div>
    <!--  计算属性  -->
    <div>{{ getName }}</div>
    <div>{{ getName }}</div>
    <div>{{ getName }}</div>
    <div>{{ getName }}</div>
    <div>{{ getName }}</div>
    <div>{{ getName }}</div>
    <div>{{ getName }}</div>
    <div>{{ get_data_name() }}</div>
    <div>{{ get_data_name() }}</div>
    <div>{{ get_data_name() }}</div>
    <div>{{ get_data_name() }}</div>
    <div>{{ get_data_name() }}</div>
    <button @click="getsub">
        点我
    </button>
    <span>{{ num }}</span>
    <button @click="set_name">计算属性</button>
</div>
<script>
    var vue = new Vue({
        el: '#app', // vue挂载的位置,不能是body
        data: {
            name: '床前明月光',
            num: 0
        },
        methods: {
            get_data_name() {
                console.log('属性方法')
                return this.name.split('').reverse().join('')
            },
            getsub(e) {
                this.num ++
            },
            set_name(){
                this.name += 'a'
            }
        },
        computed: {
            getName() {
                console.log('计算属性')
                return this.name.split('').reverse().join('')
            }
        }
    })
</script>
</body>
</html>

computed与watch,methods的区别

methods:可以放入函数,并且没有缓存

watch:

监听,当数据发送变化时,才会触发

可以得到现在的值和过去的值

还可以监听路由变化

和属性同名

自定义过滤器

创建一个过滤器

// 自定义过滤器
filters:{
    // 截取最后一个字符
    getLastChar(item){
        return item.substr(item.length-1,1)
    }
}

使用过滤器

<li v-for="item in student_list" :key="item.id">
    {{item.name|getLastChar }} -- {{ item.addr }}
</li>
时间过滤器
<div id="app">
    <div>
        当前时间:{{ now|get_date }}
    </div>
    <ul>
        <li v-for="item in date_list" :key="item.id">
            id:{{ item.id }} 时间:{{ item.date|get_date }}
        </li>
    </ul>
    <div>
        时间过滤
    </div>
    <ul>
        <li v-for="item in date_list" :key="item.id">
            id:{{ item.id }} 时间:{{ item.date|time_to_filter }}
        </li>
    </ul>
</div>
<script src="../vue.js"></script>
<script>
    function getDateDiff(datestamp) {
        var publishTime = datestamp / 1000,
            d_seconds,
            d_minutes,
            d_hours,
            d_days,
            timeNow = parseInt(new Date().getTime() / 1000),
            d,
            date = new Date(publishTime * 1000),
            Y = date.getFullYear(),
            M = date.getMonth() + 1,
            D = date.getDate(),
            H = date.getHours(),
            m = date.getMinutes(),
            s = date.getSeconds();
        //小于10的在前面补0
        if (M < 10) {
            M = '0' + M;
        }
        if (D < 10) {
            D = '0' + D;
        }
        if (H < 10) {
            H = '0' + H;
        }
        if (m < 10) {
            m = '0' + m;
        }
        if (s < 10) {
            s = '0' + s;
        }
        d = timeNow - publishTime;
        d_days = parseInt(d / 86400);
        d_hours = parseInt(d / 3600);
        d_minutes = parseInt(d / 60);
        d_seconds = parseInt(d);
        if (d_days > 0 && d_days < 30) {
            return d_days + '天前';
        } else if (d_days <= 0 && d_hours > 0) {
            return d_hours + '小时前';
        } else if (d_hours <= 0 && d_minutes > 0) {
            return d_minutes + '分钟前';
        } else if (d_seconds < 60) {
            if (d_seconds <= 0) {
                return '刚刚发表';
            } else {
                return d_seconds + '秒前';
            }
        } else if (d_days >= 30) {
            return Y + '-' + M + '-' + D + ' ' + H + ':' + m;
        }
    }
    var vue = new Vue({
        el: '#app',
        data: {
            now: '2021-12-8 22:01:11',
            date_list: [
                {id: 1, date: '2021-12-8 22:02:11'},
                {id: 2, date: '2021-12-6 22:02:21'},
                {id: 3, date: '2021-12-8 21:01:14'},
                {id: 4, date: '2021-12-5 20:12:11'},
            ]
        },
        filters: {
            // 年-月-日
            get_date(date_str) {
                // date,传递的参数
                // 字符串转时间对象
                let date = new Date(date_str)
                return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`
            },
            time_to_filter(date_str) {
                // 字符串->对象->时间戳
                // 和当前时间做差,算出相差的时间(多少秒,多少分)
                // 字符串转时间对象->时间戳
                let datestamp = new Date(date_str).getTime()
                return getDateDiff(datestamp)
            }
        }
    })
</script>

参数问题

自定义过滤器也是可以接受参数,它的书写语法是

<li v-for="item in date_list" :key="item.id">
    id:{{ item.id }} 时间:{{ item.date|time_to_filter('a', 'b') }}
</li>
...
filters: {
    ...
    time_to_filter(date_str, a, b) {
        // date_str  ->  '自身的值'
        // a  -> 'a'
        // b  -> 'b'
        return date_str
    }
}

过滤器方法的第一个参数就是调用者本身的值,第二个参数之后就是调用的实参

并且支持链式过滤器

<div>{{now|get_now|get_now1}}</div>

全局方法

需要将属性和方法挂载到Vue.prototype上

Vue.prototype.$com = "全局变量"
let global_method = ()=>{
    return "全局方法"
}
function add(){
    return "全局 add 方法"
}
Vue.prototype.$global_method = global_method
Vue.prototype.$add = add

使用

<div>
    {{ $global_method() }}
</div>
<div>
    {{ $add() }}
</div>

全局过滤器

// 全局过滤器
let global_filter = (item) => {
    return item + '--global'
}
// 注册全局过滤器   (过滤器名称,方法)
Vue.filter('global_filter', global_filter)

使用全局过滤器

<div>
    {{ $com|global_filter }}
</div>

局部组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>局部组件</title>
</head>
<body>
<div id="app">
    <App></App>  <!--使用子组件-->
</div>
<script src="../vue.js"></script>
<script>
    // app 组件 html css js
    const App = {
        data() {
            // 在组件中使用数据,需要 使用函数,返回值返回对象  data(){ return {} }
            return {msg: 'hello'}
        },
        template: `
          <div>
              <h3>我是app组件</h3>
              <p>{{ msg }}</p>
              <button @click="handleClick">按钮</button>
          </div>
        `,
        methods: {
            handleClick() {
                this.msg = 'world'
            }
        }
    }
    new Vue({
        el: '#app',
        data: {},
        components: {
            App  // 挂载子组件
        }
    })
</script>
</body>
</html>

全局组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>局部组件</title>
</head>
<body>
<div id="app">
    <App></App>  <!--使用子组件-->
</div>
<script src="../vue.js"></script>
<script>
    // app 组件 html css js
    // 只要创建组件,就可以在template中使用
    Vue.component('Vheader', {
        template: `
            <div>
                <span>我是导航组件</span>
            </div>
        `
    })
    Vue.component('Vfooter', {
        template: `
            <div>
                <span>我是底部组件</span>
            </div>
        `
    })
    Vue.component('Vsider', {
        data() {
            return {msg: '侧边栏'};
        },
        template: `
          <div>
              <span class="sider_title">我是侧边栏组件</span>
              <span>{{ msg }}</span>
          </div>
        `,
    })
    const Vbtn = {
        template: `
            <button>按钮</button>
        `
    }
    const App = {
        data() {
            // 在组件中使用数据,需要 使用函数,返回值返回对象  data(){ return {} }
            return {msg: 'hello'}
        },
        template: `
          <div>
          <Vheader></Vheader>
          <Vbtn></Vbtn>
          <Vbtn></Vbtn>
          <Vbtn></Vbtn>
          <Vsider></Vsider>
          <Vfooter></Vfooter>
          <h3>我是app组件</h3>
          <p>{{ msg }}</p>
          <button @click="handleClick">按钮</button>
          </div>
        `,
        components: {
          Vbtn  
        },
        methods: {
            handleClick() {
                this.msg = 'world'
            }
        }
    }
    new Vue({
        el: '#app',
        data: {},
        components: {
            App  // 挂载子组件
        }
    })
</script>
</body>
</html>

组件通信

父传子

父传子:通过props来进行通信

  1. 在自组件中声明props接收在父组件挂载的属性
  2. 可以在自组件的template在任意使用
    3, 在父组件绑定自定义的属性
    props传入一个对象
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: Array,
  author: Object,
  callback: Function,
  contactsPromise: Promise // or any other constructor
}
props: {
    data: {
        type: String,  // 类型
        required: true,  // 必填项
        default: '张三'  // 如果没传值,那么这个默认值就是它
    }
}
<div id="app">
    <App></App>  <!--使用子组件-->
</div>
<script src="../vue.js"></script>
<script>
    Vue.component('Child', {
        template: `
          <div>
          <h3>我是子组件</h3>
          <h4>{{ childData }}</h4>
          </div>
        `,
        props: ['childData']
    })
    const App = {
        data() {
            // 在组件中使用数据,需要 使用函数,返回值返回对象  data(){ return {} }
            return {msg: '我是父组件传递的值'}
        },
        template: `
          <div>
          <Child :childData="msg"></Child>
          <p>{{ msg }}</p>
          </div>
        `,
    }
    new Vue({
        el: '#app',
        data: {},
        components: {
            App  // 挂载子组件
        }
    })
</script>
created与mounted
created(){
    console.log(this.data, 1)
},
mounted(){
    console.log(this.data, 2)
}
子传父
  1. 在父组件中,自组件上绑定自定义事件
  2. 在自组件中,触发原生的事件,在事件函数通过this.$emit触发自定义的事件
    父组件
<comment1 v-on:eff="eff"></comment1>
components: {
    comment1
},
methods: {
    eff(data) {
        // data 是子组件传递的数据
        console.log('父组件被调用了', data)
    }
}

子组件

let comment1 = {
    template: `
      <div>
      <ul>
        <li>兰州拉面</li>
        <li>哈尔滨啤酒</li>
        <li>四川担担面</li>
        <button @click="add">点我触发父组件方法</button>
      </ul>
      </div>`,
    methods: {
        add() {
            this.$emit('eff', {name: '子组件传来的数据'})
        }
    }
}

平行组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>平行组件</title>
    <style>
        body {
            margin: 0;
        }
        #app {
            display: flex;
        }
        #app > div {
            width: 50%;
            height: 200px;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        #gouwuc {
            background-color: #3a8ee6;
        }
        #get {
            background-color: #b3d9d9;
        }
    </style>
</head>
<body>
<div id="app">
    <App1 id="gouwuc"></App1>  <!--使用子组件-->
    <App2 id="get"></App2>  <!--使用子组件-->
</div>
<script src="../vue.js"></script>
<script>
    let Bus = new Vue()
    let App1 = {
        data() {
            return {
                num: 100
            }
        },
        template: `
          <div>
          购物车 商品总数{{ num }}
          </div>`,
        mounted(){
            Bus.$on('add', (data)=>{
                this.num ++
            })
        }
    }
    let App2 = {
        data() {
            return {
                num: 100
            }
        },
        methods: {
            add() {
                Bus.$emit('add', {msg: '你的好兄弟又下单啦'})
            }
        },
        template: `
          <div>
          <button @click="add">加入购物车</button>
          </div>`
    }
    new Vue({
        el: '#app',
        data: {},
        components: {
            App1,
            App2,
        }
    })
</script>
</body>
</html>

多次嵌套取值

1. provide  提供变量  函数(){return {} },
2. inject  接收变量 inject: [],

匿名插槽

let base_layout = {
    data() {
        return {}
    },
    template: `
      <div>
      <header>
        <slot>我是页头</slot>
      </header>
      </div>`
}
new Vue({
    el: '#app',
    components: {
        base_layout
    }
})

在模板中把想要替换的东西放在标签中,这就是一个占位符

<base_layout>实际的内容</base_layout>

这样就会将标签中的内容去代替slot中的内容

如果slot都没有名字,那么如果有多个slot,就会全部进行替换

具名插槽

如果你的模板中需要有多个地方被替换

那么匿名插槽就不太合适了,我们给每个插槽都起一个别名

let base_layout = {
  data() {
      return {}
  },
  template: `
    <div>
    <header>
      <slot name="header">我是页头</slot>
    </header>
    <main>
      <slot name="main">我是内容</slot>
    </main>
    <footer>
      <slot name="footer">我是页脚</slot>
    </footer>
    </div>`
}

这样我们在进行替换的时候,指明要替换哪个slot就OK了

<base_layout>
  <template v-slot:header>
      头部
  </template>
  <template v-slot:main>
      身体
  </template>
  <template v-slot:footer>
      尾部
  </template>
</base_layout>

注意v-slot的写法

v-slot:header

v-slot: 可以简写为 #

<base_layout>
  <template v-slot:header>
      <child></child>
      <div>
          哈哈哈哈
      </div>
  </template>
  <template #main>
      身体
  </template>
  <template v-slot:footer>
      尾部
  </template>
</base_layout>

我们在替换的时候,是可以写任意标签的,也就是说,在替换的时候,你也可以将组件写入插槽中

<base_layout>
  <template v-slot:header>
      <child></child>
      <div>
          哈哈哈哈
      </div>
  </template>
  <template v-slot:main>
      身体
  </template>
  <template v-slot:footer>
      尾部
  </template>
</base_layout>

作用域插槽

let base_layout = {
  data() {
      return {
          user: {
              name: '枫枫',
              xin: '知道'
          }
      }
  },
  template: `
    <div>
    <main>
      <slot :user="user">{{ user.xin }}</slot>
    </main>
    </div>`
}

在子组件中,我在插槽里面显示的是user的xin,如果我想让它显示user的name应该怎么做呢

如果我们直接在父组件调用的时候这样写

<base_layout>
  {{ user.name }}
</base_layout>

这是错误的写法,因为我们的user是被定义再自组件中的,在父组件中就无法使用这个user,所以就会报错啦

那么我们就应该将这个user对象传递给父组件

在子组件中

<slot :user="user">{{ user.xin }}</slot>

通过动态属性的方式将user传递给父组件

在父组件中

<base_layout>
  <template v-slot:default="slotProps">
      {{ slotProps.user.name }}
  </template>
</base_layout>

接收传递来的user即可

slotProps可以是你自己定义的名字,它里面存储的是这样的

{ "user": { "name": "枫枫", "xin": "知道" } }

所以我们将slotProps中的user中的name传递给子组件,就可以做到数据动态替换

如果你的组件中,有且只有一个默认插槽,那么在替换的时候,是可以这么做的

<base_layout v-slot:default="slotProps">
  {{ slotProps }}
</base_layout>

但是,如果出现了具名插槽,这样会导致作用域混乱

我们应该这样使用

let base_1 = {
  data(){
      return {
          user: {
              name: '枫枫知道',
              age: 21
          }
      }
  },
  template: `
  <div>
  <header>
    我的名字是 <slot :user="user" name="name">{{ user.name }}</slot>,年龄是<slot :user="user" name="age">{{ user.age }}</slot>
  </header>
  </div>`
}
<base_1>
  <template v-slot:name>张子枫</template>
  <template v-slot:age="slotBase">{{ slotBase }}</template>
</base_1>

动态组件

元素是vue 里面的一个内置组件。
在里面使用 v-bind: is,可以实现动态组件的效果。

自定义指令

前面使用的v-if, v-show,以及v-for这些都是Vue为我们提供的内置指令

当然,我们也可以自己自定义一个指令

局部自定义指令

directives: {
focus: {
// 指令的定义
// 使用这个指令就会聚焦在输入框中
inserted: function (el) {
el.focus()
}
}
}
使用

<input type="text" v-focus>

在focus中有几个钩子函数,需要了解

bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。


网站公告

今日签到

点亮在社区的每一天
去签到