vue 计算和监听属性+组件通信+动态组件+插槽

计算属性和监听属性

计算属性

在computed对象中写函数,这个函数可以当作属性使用。

并且该属性仅在它的相关依赖发生改变时才会重新求值,即该函数在相关依赖不被改变时是不会执行的。

<!--    写在插值语法内-->
    <input type="text" v-model="myText"> ------------>{{myText.substr(0,1).toUpperCase()+myText.substr(1)}}
    <br>
<!--    写在methods中-->
    <input type="text" v-model="myText2"> ------------>{{getText()}}
    <br>
<!--    写在computed中-->
    <input type="text" v-model="myText3"> ------------>{{getName}}
</div>


<script>
    new Vue({
        el:'#app',
        data:{
            myText:'',
            myText2:'',
            myText3:''
        },
        methods:{
            // 页面每次发生改变都会执行一次
            getText() {
                console.log('getText')
                return this.myText2.substr(0,1).toUpperCase()+this.myText2.substr(1)
            }
        },
        computed:{
            // 只有myText3发生改变时才会执行
            getName(){
                console.log('getName')
                return this.myText3.substr(0,1).toUpperCase()+this.myText3.substr(1)
            }
        }
    })
</script>

重写过滤案例

<div id="app">
    <h1>过滤</h1>
    <input type="text" v-model="myText">
    <hr>
    <p v-for="item in newList">{{item}}</p>

</div>


<script>
    new Vue({
        el:'#app',
        data:{
            myText:'',
            dataList: ['a', 'at', 'atom', 'be', 'beyond', 'cs', 'csrf'],
        },
        computed:{
            newList(){
                // filter()把传入的函数作用于每一个元素,然后根据返回值是true和false决定保留还是丢掉该元素。
                // indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。如果没有找到匹配的字符串则返回 -1。
                return this.dataList.filter(item=>{
                    return item.indexOf(this.myText) >= 0
                })
            }
        }
    })
</script>

监听属性

在watch对象中写函数,函数名使用data中的变量名,当这个变量发生变化,就会触发该函数的执行。

<div id="app">
    <input type="text" v-model="myText"> ---->{{myText}}
</div>


<script>
    new Vue({
        el:'#app',
        data:{
            myText:''
        },
        watch:{
            myText(val){
                console.log('执行',val)
            }
        }
    })
</script>

组件化开发之局部和全局组件

组件用于扩展 HTML 元素,封装可重用的代码,目的是复用

例如:有一个轮播,可以在很多页面中使用,一个轮播有js,css,html

组件把js,css,html放到一起,有逻辑,有样式,有html

组件的定义

<div id="app">
    <h1>全局组件</h1>
    <Child></Child>
    <hr>
    <h1>局部组件</h1>
    <rain></rain>
</div>


<script>
    // 全局组件
    // 这些组件是全局注册的。也就是说它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中
    Vue.component('Child', {
        template: `
          <div>
          <h1>我是一个组件--{{ myText }}</h1>
          <button @click="handleClick">点击</button>
          <br>
          <input type="text" v-model="myText">
          <jason></jason>
          </div>
        `,
        data(){
            return{
                myText:'',
            }
        },
        methods:{
            handleClick(){
                alert('xxx')
            }
        },
        // 局部组件,写在Vue实例或者组件实例中
        // 局部组件
        components:{
            jason:{
                template: `
                  <div>
                  <h1>我是局部组件--jason</h1>
                  </div>
                `,
            }
        }
    })
    new Vue({
        el:'#app',
        data:{},
        // 局部组件
        components: {
            rain:{
                template:`
                  <div>
                  <h1>我是局部组件</h1>
                  </div>
                `,
            }
        }
    })
</script>

1.自定义组件需要有一个root element(根元素),一般包裹在一个div中

2.父子组件的data,methods是无法共享

3.组件可以有data,methods,computed....,但是data 必须是一个函数

组件间通信

父传子(自定义属性)

在组件中添加props,以字典形式接受需要的数据和类型

<div id="app">
    输入需要传到子组件的数据:
    <br>
    <input type="text" v-model="name">
    <Child :myname="name"></Child>
</div>


<script>
    // 全局组件----子
    Vue.component('Child', {
        template: `
          <div>
          <h1>自定义属性--{{ myname }}</h1>
          <br>
          </div>
        `,
        // 自定义属性
        props:{
            'myname':String
        }
    })
    // Vue实例(根组件)----父
    new Vue({
        el:'#app',
        data:{
            name:''
        },
    })
</script>

父传子(属性验证)

父组件传入的属性必选是子组件props中所规定的属性类型,否则会报错

<div id="app">
    输入需要传到子组件的数据:
    <br>
<!--    输入字符类型-->
    <input type="text" v-model="name">
<!--    输入数值类型-->
    <input type="text" v-model="age">
    <Child :myname="name" :myage="age"></Child>
</div>


<script>
    // 全局组件----子
    Vue.component('Child', {
        template: `
          <div>
          <h1>自定义属性(字符串)--{{ myname }}</h1>
          <h1>自定义属性(数值)--{{ myage }}</h1>
          <br>
          </div>
        `,
        props:{
            'myname':String,
            'myage':Number
        }
    })
    // Vue实例(根组件)----父
    new Vue({
        el:'#app',
        data:{
            name:'',
            age:null
        },
    })
</script>

子传父(自定义事件)

子组件可以使用$emit触发父组件的自定义监听,在父组件中调用子组件时调用包含$emit的事件,就可以接受到来自子组件的值

<div id="app">
<!--    调用了包含$emit的自定义事件myevent传值,并且执行handleEvent函数接受值-->
    <Child @myevent="handleEvent"></Child>
    <br>
    子组件传来的数据:{{name}}
</div>

<script>
    // 全局组件----子
    Vue.component('Child', {
        template: `
          <div>
          <button @click="handleClick">点击向父组件传数据</button>
          <br>
          <input type="text" v-model="myText">
          </div>
        `,
        data(){
            return{
                myText:'',
            }
        },
        methods:{
            // 定义myevent事件的函数
            handleClick(){
                this.$emit('myevent',this.myText)
            }
        },
    })

    // Vue实例(根组件)----父
    new Vue({
        el:'#app',
        data:{
            name:''
        },
        methods: {
            // 接收值的函数
            handleEvent(myText){
                console.log('接受到了'+ myText)
                this.name = myText
            }
        }
    })
</script>

ref属性

ref放在标签上,拿到的是原生节点
ref放在组件上,拿到的是组件对象
通过这种方式实现子传父(this.$refs['ref得到的组件对象'].子组件的属性)
通过这种方式实现父传子(调用子组件方法传参数)

<div id="app">
<!--    ref放在标签上,拿到的是原生节点-->
    <p ref="ppp"></p>
    <h1>从子组件获得的值--{{name}}</h1>
    <button @click="handleClick">数据传输</button>
    <br>
    <input type="text" v-model="name">
    <hr>
<!--    ref放在组件上,拿到的是组件对象-->
    <Child ref="ccc"></Child>
</div>


<script>
    // 全局组件----子
    Vue.component('Child', {
        template: `
          <div>
          <h1>从父组件获得的值--{{ name }}</h1>
          <input type="text" v-model="name">
          <br>
          </div>
        `,
        data(){
            return{
                name:''
            }
        },
        methods: {
            name1(aa){
                this.name = aa
            }
        }
    })
    // Vue实例(跟组件)----父
    new Vue({
        el:'#app',
        data:{
            name:''
        },
        methods:{
            handleClick(){
                // 通过这种方式实现父传子(调用子组件方法传参数)
                this.$refs['ccc'].name1('传参')
                // 父传子(调用子组件属性传参数)
                this.$refs['ccc'].name = this.name
                // 通过这种方式实现子传父(this.$refs['ref得到的组件对象'].子组件的属性)
                this.name = this.$refs['ccc'].name
            }
        }
    })
</script>

数据总线

不同层级的不通组件通信

数据总线

vuex(状态管理器)

cookie,localStorage,sessionStorage

<div id="app">
    <child1></child1>
    <hr>
    <child2></child2>
</div>


<script>
    // 1 定义一个数据总线
    var bus = new Vue()  //new一个vue的实例,就是中央事件总线
    // 2 定义两个全局组件 一个用于发生数据 一个用于储存数据
    Vue.component('child1', {
        template: `
          <div>
          <input type="text" v-model="myText">
          <button @click="handleClick">点我</button>
          </div>`,
        data() {
            return {
                myText: ''
            }
        },
        methods: {
            handleClick() {
                bus.$emit('suibian',this.myText)
            }
        }
    })

    Vue.component('child2', {
        template: `
          <div>
          接受到的数据是:{{ myText }}
          </div>`,
        data() {
            return {
                myText: ''
            }
        },
        mounted(){
            // 等着,监听,如果谁触发我,我就执行
            // $on() 监听当前实例上的自定义事件
            bus.$on('suibian',(name)=>{
                this.myText=name
            })
        }
    })

    var vm = new Vue({
        el: '#app',
        data: {},
    })
</script>

动态组件

通过component配合is属性,决定显示的组件是哪个

keep-alive 保证组件切换走后不被销毁

<div id="app">
    <ul>
        <li @click="who='home'">首页</li>
        <li @click="who='goods'">商品</li>
        <li @click="who='order'">订单</li>
    </ul>
<!--    keep-alive保证组件切换走后不被销毁-->
    <keep-alive>
<!--        component + is决定显示的组件是哪个-->
        <component :is="who">
        </component>
    </keep-alive>

</div>


<script>
    Vue.component('home', {
        template: `
          <div>
          首页
          </div>`,
    })
    Vue.component('goods', {
        template: `
          <div>
          商品
          <input type="text">
          </div>`,
    })
    Vue.component('order', {
        template: `
          <div>
          订单
          </div>`,
    })

    var vm = new Vue({
        el: '#app',
        data: {
            // 默认显示home组件
            who: 'home'
        },
    })
</script>

slot插槽 (内容分发)

a. 单个slot 
b. 具名slot
混合父组件的内容与子组件自己的模板-->内容分发
父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译。
<div id="app">
<home>
<!--    父组件模板的内容-->
    <div><button>美女</button></div>
</home>
</div>


<script>
    Vue.component('home', {
        template: `
          <div>
          <input type="text">
          <hr>
          <!--插槽-->
          <slot></slot>
          <!--子组件模板的内容-->
          <button>点我</button>

          </div>`,
    })

    var vm = new Vue({
        el: '#app',
        data: {
        },
    })
</script>

具名插槽

可以指定插入的位置

<div id="app">
<home>
<!--    具名插槽a的父组件内容-->
    <div slot="a">我是div</div>
<!--    具名插槽b的父组件内容-->
    <img src="https://tva1.sinaimg.cn/large/00831rSTly1gd1u0jw182j30u00u043b.jpg" alt="" slot="b">
</home>
</div>


<script>
    Vue.component('home', {
        template: `
          <div>
          <input type="text">
          <hr>
          <!--具名插槽a-->
          <slot name="a"></slot>
          navbar
          <!--具名插槽b-->
          <slot name="b"></slot>
          <button>点我</button>
          </div>`,
    })

    var vm = new Vue({
        el: '#app',
        data: {
        },
    })
</script>
posted @ 2022-06-29 22:49  Rain_Kz  阅读(129)  评论(0编辑  收藏  举报