【Vue】Vue开发实战之我的笔记(ch01-ch17)--20221103

参考https://blog.csdn.net/yfm120750310/article/details/111251300
参考https://pro.antdv.com/docs/getting-started
参考http://t.zoukankan.com/freely-p-10874297.html

04 | 第一个Vue程序

官方文档https://v2.cn.vuejs.org/v2/guide/conditional.html

4.0 总结

①插值 {{}} 其内只能使用表达式,不能用语句。微信小程序的原生组件也有这个数据绑定方式,来源是mustache模板引擎。
②v-bind:绑定动态的值
③v-if、v-else:可用来做条件渲染
④v-for:可用来列表循环渲染

4.1 变量默认用{{message}}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app">{{message}}</div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        var vm = new Vue({
            el:'#app',
            data:{
                message:'hello world'
            }
        })
    </script>
</body>
</html>

4.2 变量可在console中修改

vm.message=‘hello vue’
在这里插入图片描述

4.3 变量仅支持表达式,不支持指令语句

<div id="app">{{message}}{{message + message}}</div>

在这里插入图片描述

4.4 指令是个标志位,如v-bind

<body>
    <div id="app">{{message}}{{message + message}}
        <div v-bind:id="message"></div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        var vm = new Vue({
            el:'#app',
            data:{
                message:'hello world'
            }
        })
    </script>
</body>

在这里插入图片描述

4.5 v-if/v-else

<body>
    <div id="app">{{message}}{{message + message}}
        <div v-bind:id="message"></div>
        <ul>
            <li>
                <span v-if="!item.del">{{item.title}}</span>
                <span v-else style="text-decoration: line-through">{{item.title}}</span>
            </li>
        </ul>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        var vm = new Vue({
            el:'#app',
            data:{
                message:'hello world',
                item:{
                    title:'课程1',
                    del:false,
                }
            }
        })
    </script>
</body>

在这里插入图片描述

4.6 v-show显示与否,但挂在BOM节点上

<body>
    <div id="app">{{message}}{{message + message}}
        <div v-bind:id="message"></div>
        <ul>
            <li>
                <span v-if="!item.del">{{item.title}}</span>
                <span v-else style="text-decoration: line-through">{{item.title}}</span>
                <button v-show="!item.del">删除</button>
            </li>
        </ul>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        var vm = new Vue({
            el:'#app',
            data:{
                message:'hello world',
                item:{
                    title:'课程1',
                    del:false,
                }
            }
        })
    </script>
</body>

在这里插入图片描述

4.7 v-for循环

<body>
    <div id="app">{{message}}{{message + message}}
        <div v-bind:id="message"></div>
        <ul>
            <li v-for="item in list">
                <span v-if="!item.del">{{item.title}}</span>
                <span v-else style="text-decoration: line-through">{{item.title}}</span>
                <button v-show="!item.del">删除</button>
            </li>
        </ul>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        var vm = new Vue({
            el:'#app',
            data:{
                message:'hello world',
                list:[{
                    title:'课程1',
                    del:false,
                },
                {
                    title:'课程2',
                    del:true,
                },
                ]
            }
        })
    </script>
</body>

在这里插入图片描述

05 | 组件基础及组件注册

官方文档https://v2.cn.vuejs.org/v2/guide/components.html

总结

组件就是为了复用。
组件的名字必须是全局唯一的。

5.1 vue.component

vue.component需定义data、template、methods、props

        Vue.component('todo-item',{
        	props:{},
            template:``,
            data:function(){
                return {}
            },
            methods:{

            },

        })

5.2 建立todo-item组件

建立todo-item组件

<body>
    <div id="app">{{message}}{{message + message}}
        <div v-bind:id="message"></div>
        <ul>
            <!-- <li v-for="item in list">
                <span v-if="!item.del">{{item.title}}</span>
                <span v-else style="text-decoration: line-through">{{item.title}}</span>
                <button v-show="!item.del">删除</button>
            </li> -->
            <todo-item v-for="item in list" :title="item.title" :del="item.del"></todo-item>
        </ul>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        Vue.component('todo-item',{
            props:{
                title: String,
                del:{
                    type: Boolean,
                    default: false,
                },
            },
            template:`
            <li>
                <span v-if="!del">{{title}}</span>
                <span v-else style="text-decoration: line-through">{{title}}</span>
                <button v-show="!del">删除</button>
            </li>`,
            data:function(){
                return {}
            },
            methods:{

            },

        })
        var vm = new Vue({
            el:'#app',
            data:{
                message:'hello world',
                list:[{
                    title:'课程1',
                    del:false,
                },
                {
                    title:'课程2',
                    del:true,
                },
                ]
            }
        })
    </script>
</body>

在这里插入图片描述

5.3 建立todo-list组件

todo-list组件就是进一步复用。

<body>
    <div id="app">{{message}}{{message + message}}
        <div v-bind:id="message"></div>
        <!-- <ul>
            <todo-item v-for="item in list" :title="item.title" :del="item.del"></todo-item>
        </ul> -->
        <todo-list></todo-list>

    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        Vue.component('todo-item',{
            props:{
                title: String,
                del:{
                    type: Boolean,
                    default: false,
                },
            },
            template:`
            <li>
                <span v-if="!del">{{title}}</span>
                <span v-else style="text-decoration: line-through">{{title}}</span>
                <button v-show="!del">删除</button>
            </li>`,
            data:function(){
                return {}
            },
            methods:{
            },

        })
        Vue.component('todo-list',{
            template: `
            <ul>
                <todo-item v-for="item in list" :title="item.title" :del="item.del"></todo-item>
            </ul>
            `,
            data:function(){
                return {
                    list:[{
                    title:'课程1',
                    del:false,
                    },
                    {
                    title:'课程2',
                    del:true,
                    },
                    ]
                }
            }
        })
        var vm = new Vue({
            el:'#app',
            data:{
                message:'hello world',
            }
        })
    </script>
</body>

在这里插入图片描述

06 | Vue组件的核心概念:事件

官方文档https://v2.cn.vuejs.org/v2/guide/events.html

6.1 总结

总结:子组件通过事件声明的方式( $emit),将数据传递给父组件进行处理,父组件通过注册子组件声明的事件,并指定函数来完成接收并处理。

6.2 定义handleClick和handleDelete事件

  1. todo-item组件
    @click=“handleClick”
    methods中定义handleClick()方法
        Vue.component('todo-item',{
            props:{
                title: String,
                del:{
                    type: Boolean,
                    default: false,
                },
            },
            template:`
            <li>
                <span v-if="!del">{{title}}</span>
                <span v-else style="text-decoration: line-through">{{title}}</span>
                <button v-show="!del" @click="handleClick">删除</button>
            </li>`,
            data:function(){
                return {}
            },
            methods:{
                handleClick(){
                    console.log('点击删除按钮')
                    this.$emit('delete',this.title)
                }
            },

        })
  1. todo-list组件
    @click=“handleDelete”
    methods中定义handleDelete( )方法
        Vue.component('todo-list',{
            template: `
            <ul>
                <todo-item @delete="handleDelete" v-for="item in list" :title="item.title" :del="item.del"></todo-item>
            </ul>
            `,
            data:function(){
                return {
                    list:[{
                    title:'课程1',
                    del:false,
                    },
                    {
                    title:'课程2',
                    del:true,
                    },
                    ]
                }
            },
            methods:{
                handleDelete(val){
                    console.log('handleDelete', val)
                }
            },
        })
  1. 手动触发delete取到val
    this.$emit(‘delete’,this.title)

  2. 汇总代码
    4:10那段话记住
    在这里插入图片描述

<body>
    <div id="app">
        {{message}} {{message + message}}
        <div :id="message"></div>
        <!-- <ul>
            <todo-item v-for="item in list" :title="item.title" :del="item.del"></todo-item>
        </ul> -->
        <todo-list></todo-list>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        Vue.component('todo-item', {
            props: {
                title: String,
                del: {
                    type: Boolean,
                    default: false,
                },
            },
            template: `
            <li>
                <span v-if="!del">{{title}}</span>
                <span v-else style="text-decoration: line-through">{{title}}</span>
                <button v-show="!del" @click="handleClick">删除</button>
            </li>
          `,
            data: function() {
                return {}
            },
            methods: {
                handleClick(e) {
                    console.log('点击删除按钮')
                    this.$emit('delete', this.title)
                }
            },
        })
        Vue.component('todo-list', {
            template: `
            <ul>
              <todo-item @delete="handleDelete" v-for="item in list" :title="item.title" :del="item.del"></todo-item>
            </ul>
          `,
            data: function() {
                return {
                    list: [{
                        title: '课程1',
                        del: false
                    }, {
                        title: '课程2',
                        del: true
                    }],
                }
            },
            methods: {
                handleDelete(val) {
                    console.log('handleDelete', val)
                }
            }
        })
        var vm = new Vue({
            el: '#app',
            data: {
                message: 'hello world',

            }
        })
    </script>
</body>

07 | Vue组件的核心概:插槽

官方文档https://v2.cn.vuejs.org/v2/guide/components-slots.html

7.1 总结

插槽是一种传递复杂属性的API方式。
插槽(slot)是 vue 为组件的封装者提供的能力。允许开发者在封装组件时,把不确定的、希望由用户指定的部分定义为插槽。https://blog.csdn.net/qq_57587705/article/details/124527016

插槽有点像django模板中的变量{{}}。
插槽可以由默认值。

7.2 默认插槽

todo-item不要写死在todo-list里面。
< slot >< / slot >标签是为了放置<todo-item @delete="handleDelete" v-for="item in list" :title="item.title" :del="item.del"></todo-item>
如果没有< slot >< / slot >标签,< todo-item >< / todo-item >不知道挂在到哪个BOM上。

<body>
    <div id="app">{{message}}{{message + message}}
        <div v-bind:id="message"></div>
        <todo-list>
            <todo-item @delete="handleDelete" v-for="item in list" :title="item.title" :del="item.del"></todo-item>
        </todo-list>

    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        Vue.component('todo-item',{
            props:{
                title: String,
                del:{
                    type: Boolean,
                    default: false,
                },
            },
            template:`
            <li>
                <span v-if="!del">{{title}}</span>
                <span v-else style="text-decoration: line-through">{{title}}</span>
                <button v-show="!del" @click="handleClick">删除</button>
            </li>`,
            data:function(){
                return {}
            },
            methods:{
                handleClick(){
                    console.log('点击删除按钮')
                    this.$emit('delete',this.title)
                }
            },

        })
        Vue.component('todo-list',{
            template: `
            <ul>
                <slot></slot>
            </ul>
            `,
            data:function(){
                return {
                }
            },
        })
        var vm = new Vue({
            el:'#app',
            data:{
                message:'hello world',
                list:[{
                        title:'课程1',
                        del:false,
                    },
                    {
                        title:'课程2',
                        del:true,
                    },
                    ]
            },
            methods:{
                handleDelete(val){
                    console.log('handleDelete', val)
                }
            },
        })
    </script>
</body>

在这里插入图片描述

7.3 具名插槽(前后置图标)-Vue2.5语法

todo-list组件通过slot来放置todo-item,todo-item组件通过slot来放置【前置图标】和【后置图标】的span。

<body>
    <div id="app">{{message}}{{message + message}}
        <div v-bind:id="message"></div>
        <todo-list>
            <todo-item @delete="handleDelete" v-for="item in list" :title="item.title" :del="item.del">
                <span slot="pre-icon">前置图标</span>
                <span slot="suf-icon">后置图标</span>
            </todo-item>
        </todo-list>

    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        Vue.component('todo-item',{
            props:{
                title: String,
                del:{
                    type: Boolean,
                    default: false,
                },
            },
            template:`
            <li>
                <slot name="pre-icon"><slot>
                <span v-if="!del">{{title}}</span>
                <span v-else style="text-decoration: line-through">{{title}}</span>
                <slot name="suf-icon"><slot>
                <button v-show="!del" @click="handleClick">删除</button>
            </li>`,
            data:function(){
                return {}
            },
            methods:{
                handleClick(){
                    console.log('点击删除按钮')
                    this.$emit('delete',this.title)
                }
            },

        })
        Vue.component('todo-list',{
            template: `
            <ul>
                <slot></slot>
            </ul>
            `,
            data:function(){
                return {
                }
            },
        })
        var vm = new Vue({
            el:'#app',
            data:{
                message:'hello world',
                list:[{
                        title:'课程1',
                        del:false,
                    },
                    {
                        title:'课程2',
                        del:true,
                    },
                    ]
            },
            methods:{
                handleDelete(val){
                    console.log('handleDelete', val)
                }
            },
        })
    </script>
</body>

在这里插入图片描述

7.4 具名插槽(前后置图标)-Vue2.6之后的语法

插槽采取template的标签<template v-slot:suf-icon><span>后置图标</span></template>

<body>
    <div id="app">{{message}}{{message + message}}
        <div v-bind:id="message"></div>
        <todo-list>
            <todo-item @delete="handleDelete" v-for="item in list" :title="item.title" :del="item.del">
                <!-- <span slot="pre-icon">前置图标</span>
                <span slot="suf-icon">后置图标</span> -->
                <template v-slot:pre-icon>
                    <span>前置图标</span>
                </template>
                <template v-slot:suf-icon>
                    <span>后置图标</span>
                </template>
            </todo-item>
        </todo-list>

    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        Vue.component('todo-item',{
            props:{
                title: String,
                del:{
                    type: Boolean,
                    default: false,
                },
            },
            template:`
            <li>
                <slot name="pre-icon"></slot>
                <span v-if="!del">{{title}}</span>
                <span v-else style="text-decoration: line-through">{{title}}</span>
                <slot name="suf-icon"></slot>
                <button v-show="!del" @click="handleClick">删除</button>
            </li>`,
            data:function(){
                return {}
            },
            methods:{
                handleClick(){
                    console.log('点击删除按钮')
                    this.$emit('delete',this.title)
                }
            },

        })
        Vue.component('todo-list',{
            template: `
            <ul>
                <slot></slot>
            </ul>
            `,
            data:function(){
                return {
                }
            },
        })
        var vm = new Vue({
            el:'#app',
            data:{
                message:'hello world',
                list:[{
                        title:'课程1',
                        del:false,
                    },
                    {
                        title:'课程2',
                        del:true,
                    },
                    ]
            },
            methods:{
                handleDelete(val){
                    console.log('handleDelete', val)
                }
            },
        })
    </script>
</body>

7.5 作用域插槽

作用域插槽可以接收子组件传递的值。根据子组件传递值的不同,作用域插槽返回不同的内容。
value的传递过程:

  1. data: function() {
    return {
    value: Math.random()
    }
    },
  2. <slot name="pre-icon" :value="value"></slot>
  3. <template v-slot:pre-icon="{value}"><span>前置图标 {{value}}</span> </template>
<body>
    <div id="app">{{message}}{{message + message}}
        <div v-bind:id="message"></div>
        <todo-list>
            <todo-item @delete="handleDelete" v-for="item in list" :title="item.title" :del="item.del">
                <!-- <span slot="pre-icon">前置图标</span>
                <span slot="suf-icon">后置图标</span> -->
                <template v-slot:pre-icon="{value}">
                    <span>前置图标{{value}}</span>
                </template>
                <template v-slot:suf-icon="{value}">
                    <span>后置图标{{value}}</span>
                </template>
            </todo-item>
        </todo-list>

    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        Vue.component('todo-item',{
            props:{
                title: String,
                del:{
                    type: Boolean,
                    default: false,
                },
            },
            template:`
            <li>
                <slot name="pre-icon" :value="value"></slot>
                <span v-if="!del">{{title}}</span>
                <span v-else style="text-decoration: line-through">{{title}}</span>
                <slot name="suf-icon"></slot>
                <button v-show="!del" @click="handleClick">删除</button>
            </li>`,
            data:function(){
                return {
                    value: Math.random()
                }
            },
            methods:{
                handleClick(){
                    console.log('点击删除按钮')
                    this.$emit('delete',this.title)
                }
            },

        })
        Vue.component('todo-list',{
            template: `
            <ul>
                <slot></slot>
            </ul>
            `,
            data:function(){
                return {
                }
            },
        })
        var vm = new Vue({
            el:'#app',
            data:{
                message:'hello world',
                list:[{
                        title:'课程1',
                        del:false,
                    },
                    {
                        title:'课程2',
                        del:true,
                    },
                    ]
            },
            methods:{
                handleDelete(val){
                    console.log('handleDelete', val)
                }
            },
        })
    </script>
</body>

在这里插入图片描述

7.6 插槽默认值

<template v-slot:suf-icon="{value}"><span>后置图标{{value}}</span></template>删掉。
直接修改template的<slot name="suf-icon"></slot>,添加默认值。可以使插槽没有传入值时,使用默认值。

<body>
    <div id="app">{{message}}{{message + message}}
        <div v-bind:id="message"></div>
        <todo-list>
            <todo-item @delete="handleDelete" v-for="item in list" :title="item.title" :del="item.del">
                <!-- <span slot="pre-icon">前置图标</span>
                <span slot="suf-icon">后置图标</span> -->
                <template v-slot:pre-icon="{value}">
                    <span>前置图标{{value}}</span>
                </template>
            </todo-item>
        </todo-list>

    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <script>
        Vue.component('todo-item',{
            props:{
                title: String,
                del:{
                    type: Boolean,
                    default: false,
                },
            },
            template:`
            <li>
                <slot name="pre-icon" :value="value"></slot>
                <span v-if="!del">{{title}}</span>
                <span v-else style="text-decoration: line-through">{{title}}</span>
                <slot name="suf-icon">😄</slot>
                <button v-show="!del" @click="handleClick">删除</button>
            </li>`,
            data:function(){
                return {
                    value: Math.random()
                }
            },
            methods:{
                handleClick(){
                    console.log('点击删除按钮')
                    this.$emit('delete',this.title)
                }
            },

        })
        Vue.component('todo-list',{
            template: `
            <ul>
                <slot></slot>
            </ul>
            `,
            data:function(){
                return {
                }
            },
        })
        var vm = new Vue({
            el:'#app',
            data:{
                message:'hello world',
                list:[{
                        title:'课程1',
                        del:false,
                    },
                    {
                        title:'课程2',
                        del:true,
                    },
                    ]
            },
            methods:{
                handleDelete(val){
                    console.log('handleDelete', val)
                }
            },
        })
    </script>
</body>

在这里插入图片描述

08 | 理解单文件组件

官方文档https://v2.cn.vuejs.org/v2/guide/single-file-components.html

8.1 安装npm和vue-cli

  1. 安装npm请到官方网站下载Node的文件https://nodejs.org/en/
  2. npm install -g @vue/cli安装脚手架,如果失败则用npm install vue-cli -g --force
  3. npm install vue安装vue
  4. vue --version查看版本
  5. 淘宝镜像npm config set registry https://registry.npm.taobao.org --global

8.2 vue create vue-demo1

vue create vue-demo1

vue create vue-demo1

Vue CLI v5.0.8
✨  Creating project in C:\Users\Z13073219\vue-demo1.
⚙️  Installing CLI plugins. This might take a while...


added 847 packages in 2m
🚀  Invoking generators...
📦  Installing additional dependencies...


added 86 packages in 17s
⚓  Running completion hooks...

📄  Generating README.md...

🎉  Successfully created project vue-demo1.
👉  Get started with the following commands:

 $ cd vue-demo1
 $ npm run serve

8.3 运行服务

npm run serve
在这里插入图片描述

访问http://localhost:8080/可以打开默认网页。
在这里插入图片描述

8.4 main.js

main.js整列式写法,相当于index.html的el代码。

  • main.js
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')
  • index.html
        var vm = new Vue({
            el:'#app',
            data:{
                message:'hello world',
                list:[{
                        title:'课程1',
                        del:false,
                    },
                    {
                        title:'课程2',
                        del:true,
                    },
                    ]
            },
            methods:{
                handleDelete(val){
                    console.log('handleDelete', val)
                }
            },
        })

8.5 App.vue

main.js用import App from './App.vue',进入引入【App.vue】单文件组件。
将index.html中的el部分的data和methods,转化为App.vue的data和methods。
最大的区别是data在el中只是一个对象,在App.vue中是一个方法返回一个对象。

  • index.html
        var vm = new Vue({
            el:'#app',
            data:{
                message:'hello world',
                list:[{
                        title:'课程1',
                        del:false,
                    },
                    {
                        title:'课程2',
                        del:true,
                    },
                    ]
            },
            methods:{
                handleDelete(val){
                    console.log('handleDelete', val)
                }
            },
        })
  • App.vue增加data和methods
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    HelloWorld
  },
  data(){
      return {
      message:'hello world',
      list:[{
              title:'课程1',
              del:false,
          },
          {
              title:'课程2',
              del:true,
          },
          ]
  }
  },
  methods:{
      handleDelete(val){
          console.log('handleDelete', val)
      }
  },
}
</script>


<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

8.6 TodoItem.vue

将index.html中的todo-item组件,转化为TodoItem.vue。

  • index.html中的todo-item组件
        Vue.component('todo-item',{
            props:{
                title: String,
                del:{
                    type: Boolean,
                    default: false,
                },
            },
            template:`
            <li>
                <slot name="pre-icon" :value="value"></slot>
                <span v-if="!del">{{title}}</span>
                <span v-else style="text-decoration: line-through">{{title}}</span>
                <slot name="suf-icon">😄</slot>
                <button v-show="!del" @click="handleClick">删除</button>
            </li>`,
            data:function(){
                return {
                    value: Math.random()
                }
            },
            methods:{
                handleClick(){
                    console.log('点击删除按钮')
                    this.$emit('delete',this.title)
                }
            },

        })
  • TodoItem.vue直接拷贝index的todo-item组件内容,并把template置顶。
<template>
  <li>
      <slot name="pre-icon" :value="value"></slot>
      <span v-if="!del">{{title}}</span>
      <span v-else style="text-decoration: line-through">{{title}}</span>
      <slot name="suf-icon">😄</slot>
      <button v-show="!del" @click="handleClick">删除</button>
  </li>
</template>


<script>
export default {
    props:{
        title: String,
        del:{
            type: Boolean,
            default: false,
        },
    },
    data:function(){
        return {
            value: Math.random()
        }
    },
    methods:{
        handleClick(){
            console.log('点击删除按钮')
            this.$emit('delete',this.title)
        }
    },
}
</script>

8.7 TodoList.vue

将index.html中的todo-list组件,转化为TodoList.vue。

  • index.html中的todo-list组件
        Vue.component('todo-list',{
            template: `
            <ul>
                <slot></slot>
            </ul>
            `,
            data:function(){
                return {
                }
            },
        })
  • TodoList.vue
<template>
    <ul>
        <slot></slot>
    </ul>
</template>


<script>
export default {
    data:function(){
                return {
                }
            },
}
</script>

<style></style>

8.8 引入单文件组件

  • App.VUE的< script>增加下列import
    import TodoItem from ‘./components/TodoItem.vue’
    import TodoList from ‘./components/TodoList.vue’

8.9 debug:error Custom elements in iteration require ‘v-bind:key’ directives vue/valid-v-for

  • App.VUE template增加:key=“item.title”
<template>
    <div id="app">{{message}}{{message + message}}
        <div v-bind:id="message"></div>
        <todo-list>
            <todo-item @delete="handleDelete" v-for="item in list"  :title="item.title" :del="item.del">
                <!-- <span slot="pre-icon">前置图标</span>
                <span slot="suf-icon">后置图标</span> -->
                <template v-slot:pre-icon="{value}">
                    <span>前置图标{{value}}</span>
                </template>
            </todo-item>
        </todo-list>
    </div>
</template>

<script>
import TodoItem from './components/TodoItem.vue'
import TodoList from './components/TodoList.vue'

export default {
  name: 'App',
  components: {
    TodoItem,
    TodoList
  },
  data(){
      return {
      message:'hello world',
      list:[{
              title:'课程1',
              del:false,
          },
          {
              title:'课程2',
              del:true,
          },
          ]
  }
  },
  methods:{
      handleDelete(val){
          console.log('handleDelete', val)
      }
  },
}
</script>


<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

8.10 全局注册

  • main.js 中全局注册TodoList import TodoList from './components/TodoList.vue',并用Vue.component('todo-list',TodoList)
import Vue from 'vue'
import App from './App.vue'
import TodoList from './components/TodoList.vue'

Vue.component('todo-list',TodoList)
Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

8.11 当前组件的CSS不被污染

在单文件组件中的style中增加scoped属性。

<style scoped>
.red{
    color:red;
}
</style>
  • TodoItem.vue
<template>
  <li>
      <slot name="pre-icon" :value="value"></slot>
      <span class='red' v-if="!del">{{title}}</span>
      <span v-else style="text-decoration: line-through">{{title}}</span>
      <slot name="suf-icon">😄</slot>
      <button v-show="!del" @click="handleClick">删除</button>
  </li>
</template>


<script>
export default {
    props:{
        title: String,
        del:{
            type: Boolean,
            default: false,
        },
    },
    data:function(){
        return {
            value: Math.random()
        }
    },
    methods:{
        handleClick(){
            console.log('点击删除按钮')
            this.$emit('delete',this.title)
        }
    },
}
</script>



<style scoped>
.red{
    color:red;
}
</style>

在这里插入图片描述

09 | 双向绑定和单向数据流不冲突

9.1 双向绑定的意思

视图更新后,数据也会更新;数据更新后,视图也会更新。

9.2 v-model创建双向绑定。

<input v-model="message">

在这里插入图片描述

  • App.VUE中的template
<template>
    <div id="app">
        <input v-model="message">
        {{message}}{{message + message}}
        <div v-bind:id="message"></div>
        <todo-list>
            <todo-item @delete="handleDelete" v-for="item in list" :key="item.title" :title="item.title" :del="item.del">
                <!-- <span slot="pre-icon">前置图标</span>
                <span slot="suf-icon">后置图标</span> -->
                <template v-slot:pre-icon="{value}">
                    <span>前置图标{{value}}</span>
                </template>
            </todo-item>
        </todo-list>
    </div>
</template>

9.3 v-model仅仅是语法糖,可以通过:value来绑定值和@input来触发事件来更新值。

<input :value="message" @input="handleChange">


  methods:{
      handleChange(e){
          this.message = e.target.value
      },
  }

在这里插入图片描述

  • App.vue
<template>
    <div id="app">
        <input v-model="message">
        <input :value="message" @input="handleChange">
        {{message}}{{message + message}}
        <div v-bind:id="message"></div>
        <todo-list>
            <todo-item @delete="handleDelete" v-for="item in list" :key="item.title" :title="item.title" :del="item.del">
                <!-- <span slot="pre-icon">前置图标</span>
                <span slot="suf-icon">后置图标</span> -->
                <template v-slot:pre-icon="{value}">
                    <span>前置图标{{value}}</span>
                </template>
            </todo-item>
        </todo-list>
    </div>
</template>

<script>
import TodoItem from './components/TodoItem.vue'
import TodoList from './components/TodoList.vue'

export default {
  name: 'App',
  components: {
    TodoItem,
    TodoList
  },
  data(){
      return {
      message:'hello world',
      list:[{
              title:'课程1',
              del:false,
          },
          {
              title:'课程2',
              del:true,
          },
          ]
  }
  },
  methods:{
      handleChange(e){
          this.message = e.target.value
      },
      handleDelete(val){
          console.log('handleDelete', val)
      }
  },
}
</script>


<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

9.4 v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件。

在这里插入图片描述

9.5 .sync来绑定多个属性(略)

https://v2.cn.vuejs.org/v2/guide/components-custom-events.html#sync-%E4%BF%AE%E9%A5%B0%E7%AC%A6

10 | 理解虚拟DOM及key属性的作用

10.1 虚拟DOM==>如何移动节点

10.2 说明key属性的重要性,否则可能会报错

debug:error Custom elements in iteration require ‘v-bind:key’ directives vue/valid-v-for
做法1:App.VUE template增加:key=“item.title”
做法2:v-for=“(item,index) in list” :key=“index”。如果下属节点删除,可能导致index错乱。

11 | 如何触发组件的更新

11.1 组件更新

我们都知道vue是数据驱动的,只有在数据改变的时候,我们的视图才会改变。所以任何直接修改dom的行为都是在作死。
数据来源(单向的):

  • 来自父元素的属性props
  • 来自组件自身的状态data
  • 来自状态管理器,vuex,Vue.observable

11.2 状态data与属性props

  • 状态时组件自身的数据
  • 属性时来自父组件的数据
  • 状态的改变未必会触发更新
  • 属性的改变未必会触发更新

11.3 vue的响应式更新

用到的数据放在Watcher里面,如果没有用到就不放到Watcher里面。
如果Watcher用到了,下次更新时setter就会通知Watcher更新数据。
如果Watcher没有用到,即使setter更新了也不会通知Watcher。
在这里插入图片描述

11.4 代码说明:vue的响应式更新

1)非响应式更新
原因:name没有做响应式。响应式只存在info里面,还不存在info下面。
在这里插入图片描述

2)响应式更新
做法:name放到return里面;info提前声明number。

在这里插入图片描述

12 | 合理应用计算属性和侦听器

12.1 计算属性computed

1)作用

  • 减少模板中的计算逻辑
  • 数据缓存
  • 依赖固定的数据类型(响应式数据)

2)代码:ComputedDo.vue
message有变化时:而当我们在输入框中输入数据时,reversedMessage1和reversedMessage2会同时被执行。
message无变化时:我们通过this.$forceUpdate()刷新数据,我们点击按钮的时候,可以看到只有reversedMessage2方法被调用了。

<template>
    <div>
      <p>Reversed message1: "{{ reversedMessage1 }}"</p>
      <p>Reversed message2: "{{ reversedMessage2() }}"</p>
      <p>{{ now }}</p>
      <button @click="() => $forceUpdate()">forceUpdate</button>
      <br />
      <input v-model="message" />
    </div>
  </template>
  <script>
  export default {
    data() {
      return {
        message: "hello vue"
      };
    },
    computed: {
      // 计算属性的 getter
      reversedMessage1: function() {
        console.log("执行reversedMessage1");
        return this.message
          .split("")
          .reverse()
          .join("");
      },
      now: function() {
        return Date.now();
      }
    },
    methods: {
      reversedMessage2: function() {
        console.log("执行reversedMessage2");
        return this.message
          .split("")
          .reverse()
          .join("");
      }
    }
  };
  </script>
  

在这里插入图片描述

12.2 侦听器watch

1)作用

  • 更加灵活、通用
  • watch中可以执行任何逻辑,如函数节流,ajax异步获取数据,甚至操作DOM

2)代码:ComputedDo.vue
watch监听了a,当a有变化时,就会执行this.b.c += 1和console.log(“new: %s, old: %s”, val, oldVal)。
这里涉及到一个嵌套监听的概念,注意:如果在e中设置deep为false,那么对b.d的监听中更改e值不会涉及到e的handler事件的触发。

<template>
  <div>
    {{ $data }}
    <br />
    <button @click="() => (a += 1)">a+1</button>
  </div>
</template>
<script>
export default {
  data: function() {
    return {
      a: 1,
      b: { c: 2, d: 3 },
      e: {
        f: {
          g: 4
        }
      },
      h: []
    };
  },
  watch: {
    a: function(val, oldVal) {
      this.b.c += 1;
      console.log("new: %s, old: %s", val, oldVal);
    },
    "b.c": function(val, oldVal) {
      this.b.d += 1;
      console.log("new: %s, old: %s", val, oldVal);
    },
    "b.d": function(val, oldVal) {
      this.e.f.g += 1;
      console.log("new: %s, old: %s", val, oldVal);
    },
    e: {
      handler: function(val, oldVal) {
        this.h.push("😄");
        console.log("new: %s, old: %s", val, oldVal);
      },
      deep: true
    },
    h(val, oldVal) {
      console.log("new: %s, old: %s", val, oldVal);
    }
  }
};
</script>

在这里插入图片描述

12.3 computed vs watch

1)区别

  • computed 能做的,watch都能做,反之则不行
  • 能用computed的尽量用computed

2)功能相同,用computed API写的逻辑

<template>
  <div>
    {{ fullName }}

    <div>firstName: <input v-model="firstName" /></div>
    <div>lastName: <input v-model="lastName" /></div>
  </div>
</template>
<script>
export default {
  data: function() {
    return {
      firstName: "Foo",
      lastName: "Bar"
    };
  },
  computed: {
    fullName: function() {
      return this.firstName + " " + this.lastName;
    }
  },
  watch: {
    fullName: function(val, oldVal) {
      console.log("new: %s, old: %s", val, oldVal);
    }
  }
};
</script>

在这里插入图片描述

3)功能相同,用watch API写的逻辑

<template>
  <div>
    {{ fullName }}

    <div>firstName: <input v-model="firstName" /></div>
    <div>lastName: <input v-model="lastName" /></div>
  </div>
</template>
<script>
export default {
  data: function() {
    return {
      firstName: "Foo",
      lastName: "Bar",
      fullName: "Foo Bar"
    };
  },
  watch: {
    firstName: function(val) {
      this.fullName = val + " " + this.lastName;
    },
    lastName: function(val) {
      this.fullName = this.firstName + " " + val;
    }
  }
};
</script>

在这里插入图片描述

13 | 生命周期的应用场景和函数式组件

13.1 生命周期

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

13.2 函数式组件(难)

  1. 作用
  • fuctional: true
  • 无状态、无实例、没有this上下文、无生命周期
  • 是一个简单的方法

2)示例通过函数式组件实现临时变量的功能(虽然计算属性能帮我们搞定大部分的问题,但是计算属性更多的时针对一些被监听的变量的,有时候还是要用到临时变量。)
TempVar为一个函数式组件。

  • TempVar.js
export default {
  functional: true,
  render: (h, ctx) => {
    return ctx.scopedSlots.default && ctx.scopedSlots.default(ctx.props || {});
  }
};

  • index.vue
<template>
  <div>
    <a-tabs>
      <a-tab-pane key="clock" tab="时钟">
        <button @click="destroyClock = !destroyClock">
          {{ destroyClock ? "加载时钟" : "销毁时钟" }}
        </button>
        <Clock v-if="!destroyClock" />
      </a-tab-pane>
      <a-tab-pane key="Functional" tab="函数式组件">
        <Functional :name="name" />
        <TempVar
          :var1="`hello ${name}`"
          :var2="destroyClock ? 'hello vue' : 'hello world'"
        >
          <template v-slot="{ var1, var2 }">
            {{ var1 }}
            {{ var2 }}
          </template>
        </TempVar>
      </a-tab-pane>
    </a-tabs>
  </div>
</template>
<script>
import Clock from "./Clock";
import Functional from "./Functional";
import TempVar from "./TempVar";
export default {
  components: {
    Clock,
    Functional,
    TempVar
  },
  data() {
    return {
      destroyClock: false,
      name: "vue"
    };
  }
};
</script>

14 | 指令的本质是什么

14.1 内置指令

在这里插入图片描述
在这里插入图片描述

14.2 自定义指令

在这里插入图片描述

  • CustomerDirectives.vue
    这个自定义组件appendText被v-append-text引用。
    (vue-append: like v-html directive, but it can call javascript function)
<template>
  <div>
    <button @click="show = !show">
      销毁
    </button>
    <button v-if="show" v-append-text="`hello ${number}`" @click="number++">
      按钮
    </button>
  </div>
</template>
<script>
export default {
  directives: {
    appendText: {
      bind() {
        console.log("bind");
      },
      inserted(el, binding) {
        el.appendChild(document.createTextNode(binding.value));
        console.log("inserted", el, binding);
      },
      update() {
        console.log("update");
      },
      componentUpdated(el, binding) {
        el.removeChild(el.childNodes[el.childNodes.length - 1]);
        el.appendChild(document.createTextNode(binding.value));
        console.log("componentUpdated");
      },
      unbind() {
        console.log("unbind");
      }
    }
  },
  data() {
    return {
      number: 1,
      show: true
    };
  }
};
</script>

这个append-text指令实现的是往当前的结点文本内容之后插入内容的指令,看下运行效果图:
在这里插入图片描述

15 | 常用高级特性provide/inject(难)

15.1 provide/inject

https://blog.csdn.net/weixin_54757930/article/details/121761669
provider和inject是为了解决组件通信时层层需要传递的问题。
在这里插入图片描述

15.2 代码

1.上图展示了各个节点之间的层级,我们按照这个层级书写代码:
在这里插入图片描述

2.看下A节点的代码:A节点通过provide提供了一个theme属性。

<template>
  <div class="border">
    <h1>A 结点</h1>
    <button @click="() => changeColor()">改变color</button>
    <ChildrenB />
    <ChildrenC />
    <ChildrenD />
  </div>
</template>
<script>
import ChildrenB from "./ChildrenB";
import ChildrenC from "./ChildrenC";
import ChildrenD from "./ChildrenD";
export default {
  components: {
    ChildrenB,
    ChildrenC,
    ChildrenD
  },
  provide() {
    return {
      theme: {
        color: this.color
      }
    };
  },
  // provide() {
  //   return {
  //     theme: this
  //   };
  // },
  data() {
    return {
      color: "blue"
    };
  },
  methods: {
    changeColor(color) {
      if (color) {
        this.color = color;
      } else {
        this.color = this.color === "blue" ? "red" : "blue";
      }
    }
  }
};
</script>

3.E节点代码:E节点通过inject注入theme。

<template>
  <div class="border2">
    <h3 :style="{ color: theme.color }">E 结点</h3>
    <button @click="handleClick">改变color为green</button>
  </div>
</template>
<script>
export default {
  components: {},
  inject: {
    theme: {
      default: () => ({})
    }
  },
  methods: {
    handleClick() {
      if (this.theme.changeColor) {
        this.theme.changeColor("green");
      }
    }
  }
};
</script>

4.F节点(运用别名):

<template>
  <div class="border2">
    <h3 :style="{ color: theme1.color }">F 结点</h3>
  </div>
</template>
<script>
export default {
  components: {},
  inject: {
    theme1: {
      from: "theme",
      default: () => ({})
    }
  }
};
</script>

5.I节点(函数式组件):

<template functional>
  <div class="border2">
    <h3 :style="{ color: injections.theme.color }">I 结点</h3>
  </div>
</template>
<script>
export default {
  inject: {
    theme: {
      default: () => ({})
    }
  }
};
</script>

16 | 跨层级组件实例

16.1 ref获取组件实例

我们可以通过ref获取组件实例:
在这里插入图片描述
https://www.51sjk.com/b166b135671/
在这里插入图片描述

16.2 callback ref(难)

但是当我们层级多的时候就显得不合适了,递归查找的代码会繁琐,性能会低效。我们看一下callback ref的概念:
在这里插入图片描述
如果e节点更新以后能够调用a节点的钩子函数,主动通知a节点,那么层级的调用就显得容易很多了。
看一下实现的形式,依旧是沿用上一次的节点结构:

<template>
  <div class="border">
    <h1>A 结点</h1>
    <button @click="getEH3Ref">获取E h3 Ref</button>
    <ChildrenB />
    <ChildrenC />
    <ChildrenD />
  </div>
</template>
<script>
import ChildrenB from "./ChildrenB";
import ChildrenC from "./ChildrenC";
import ChildrenD from "./ChildrenD";
export default {
  components: {
    ChildrenB,
    ChildrenC,
    ChildrenD
  },
  provide() {
    return {
      setChildrenRef: (name, ref) => {
        this[name] = ref;
      },
      getChildrenRef: name => {
        return this[name];
      },
      getRef: () => {
        return this;
      }
    };
  },
  data() {
    return {
      color: "blue"
    };
  },
  methods: {
    getEH3Ref() {
      console.log(this.childrenE);
    }
  }
};
</script>

A节点通过provide提供主动获取通知的钩子函数。
子节点D:

<template>
  <div class="border1">
    <h2>D 结点</h2>
    <ChildrenG />
    <ChildrenH v-ant-ref="c => setChildrenRef('childrenH', c)" />
    <ChildrenI />
  </div>
</template>
<script>
import ChildrenG from "./ChildrenG";
import ChildrenH from "./ChildrenH";
import ChildrenI from "./ChildrenI";
export default {
  components: {
    ChildrenG,
    ChildrenH,
    ChildrenI
  },
  inject: {
    setChildrenRef: {
      default: () => {}
    }
  }
};
</script>

17 | template和JSX的对比以及它们的本质

17.1 template和JSX的对比

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

17.2 template和JSX的本质

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值