vue2.0 基础知识

128 阅读1分钟

简介

基本内容

  • 渐进式框架
  • 核心思想:数据驱动
  • 双向数据绑定

如何理解渐进式

Vue框架本身是分层的设计:

  1. 核心,视图渲染
  2. 组件化
  3. 单页面路由
  4. 状态管理
  5. 构建系统

一开始不必使用上框架的全部知识,根据需要自行选择即可。

双向数据绑定:

概念理解:数据的变化会引起页面的变化,反之亦然。主要使用与表单元素

指令

v-model

将数据与表单相关元素(input、select、textarea)绑定起来。实现双向数据绑定。

vue官网-表单控件绑定

image.png

<body>
    <!-- 
      v-model 其实是个语法糖(写起来方便,让我们甜甜的)

      <input type="text" v-model="msg" />

        =>

      <input type="text" v-bind:value="msg" v-on:input="msg = $event.target.value" />

      $event 是事件对象
      $event.target 就是触发这个事件的元素的DOM对象 input
      $event.target.value   input 的 value


      <input type="text" v-model.lazy="msg" />

        =>

      <input type="text" v-bind:value="msg" v-on:change="msg = $event.target.value" />

     -->

    <div id="app">
      <input
        type="text"
        v-bind:value="msg"
        v-on:input="msg = $event.target.value"
      />
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script>
    <script>
      var vm = new Vue({
        el: "#app",

        data: {
          msg: "Hello",
        },
      });
    </script>
  </body>
    <div id="app">
        <textarea :value="message" @input="message = $event.target.value"></textarea>
    </div>
    <script>
        new Vue({
            el: '#app',     
            data() {
                return {
                    message: 'sss'
                }
            },
        })  
    </script>

image.png

v-html、v-text

v-html: 会解析html元素 v-text: 不会解析html元素

v-show、v-if

v-show: 根据表达式的真假值,切换元素的display CSS属性 v-if: 通过表达式的值来控制元素是否渲染 v-show和v-if的效果基本一样

v-show 与 v-if 的区别

  • 1. v-if 是真正的条件渲染, 默认条件为false时元素不渲染,默认条件为true时元素渲染。条件变为false时元素会销毁,条件变为true时元素会重新创建。 而 v-show 只是简单的去控制元素的css的display属性,DOM一直存在。

    1. v-show 有更高的初始渲染开销(因为不管默认条件为真还是假,都会渲染出来) v-if 有更高的切换开销切换时做销毁与重建的操作) 如果需要频繁切换显示隐藏使用v-show更好,其余情况使用v-if就好。
    1. v-if 有 v-else v-else-if 去配套使用。 v-show 没有,它是孤零零的一个人
    1. v-if 能配合 template 元素使用,而 v-show 不能。

问题:为什么 v-show 不能与 template 去使用呢? 答案:template是不会渲染出来,v-show 是要去控制元素的css属性的。

v-cloak

vue 页面闪烁问题 现象描述:有时会在页面上出现插值表达式的语句,例如 {{ msg }} 。然后过一小会就没了

问题解释:vue 解析页面需要时间

解决方案:

一、不使用 插值表达式语法 。改用 v-text 。

二、推荐 使用 v-cloak 这个指令

  1. 将 v-cloak 写在挂载点元素上,它没有参数也没有表达式
  2. 设置 一个全局 css 属性选择器样式。样式代码如下
[v-cloak] {
  display: none
}

v-cloak 的特性:在vue实例化并且解析完成之后会自动删除掉这个属性

v-for

v-for 指令:主要就是用于做循环 一、循环数组

v-for="item in xxx"
item 循环的每一项
in   固定写法
xxx  要循环的数据

v-for="(item, index) in xxx"
item 循环的每一项
index 循环的下标
in   固定写法
xxx  要循环的数据

二、循环对象

v-for="item in xxx"
item 循环的xxx对象的value值
in   固定的写法
xxx  要循环的数据(对象)

v-for="(value, key, index) in xxx"
value value值
key   key值
index 下标值
in    固定的写法
xxx   要循环的数据(对象)

三、循环字符串

v-for="item in '张三'"

四、循环数字

v-for="item in 10"    =>  v-for="item in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"

数组更新检测

Vue 是数据驱动的,如果某个数据的类型是数组类型,在一些数组的操作下是可以更新页面的,还有一些数组的操作不会引起页面的更新.

下列的数组方法是可以引起页面更新的(变异方法): 官网说明

  • push() 尾部添加
  • pop() 尾部删除
  • shift() 头部删除
  • unshift() 头部添加
  • splice() 高级方法,删除、添加、替换
  • sort() 排序方法
  • reverse() 颠倒顺序 还有一些数组的操作可以引起页面更新。直接替换原数组 vm.fruits = ['Hello', 'World']

!!下面两种方式修改数组是不会引起页面的更新的:

  1. 直接通过下标的方式去修改数组: vm.fruits[1] = '我的天'
  2. 直接通过修改数组的length属性的方式:vm.fruits.length = num

?作业:去百度找一找这种修改不引起页面更新的原因是什么?

原因

解决上面两个情况的方案

  • 方案一
    1. 使用 Vue.set(target, index, value) 静态方法
    2. 使用 实例对象的 $set() 实例方法 vm.$set(target, index, value)

Vue.set() 与 vm.$set() 语法和作用都是一样的。随你选择使用哪种方式

Vue.set(target, index, value)
/*
target  要修改的数据源
index   要修改的下标
value   要修改成什么样
*/
  • 方案二

直接换成 splice() 方法来操作即可

vm.fruits.splice(1)

对象更新检测的问题

直接给对象新增一个属性是不会引起页面更新的。

解决方法使用 Vue.set() 或者 vm.$set() 即可

Vue.set(target, key, value)
/**
target 要修改的数据源
key     key
value   value
*/

动态class的几种写法

vue官网-class

一、v-bind:class="'hello'"

直接使用字符串 hello 做为类名

二、v-bind:class="hello"

使用 hello 这个数据的值作为类名

三、对象 v-bind:class="{ key1: value1, key2: value2 }"

根据 value1 这个数据是否为真值,来控制是否要加上 key1 这个类名

根据 value2 这个数据是否为真值,来控制是否要加上 key2 这个类名

四、数组 v-bind:class="[ value1, 'value2', value3, { key: value } ]"

使用 value1 这个数据的值来作为其中一个类名

使用 value2 这个字符串直接作为其中一个类名

使用 value3 这个数据的值赖作为其中一个类名

根据 value 这个数据的值是否为真值,来控制是否要加上 key 这个类名

    <div id="app">
      <p :class="hello">我真好看</p>
      <p :class="{ key1: true, key2: false }">我真的好看</p>
      <p :class="{ key1: key1Value, key2: key2Value }">我的人</p>

      <!-- box1, value2, box3, key -->
      <p :class="[ value1, 'value2', value3, { key: value } ]">我爱学习</p>
    </div>
    <script>
      var vm = new Vue({
        el: "#app",
        data: {
          hello: "box",
          key1Value: true,
          key2Value: false,
          value1: "box1",
          value2: "box2",
          value3: "box3",
          value: true,
        },
      });
    </script>

动态style的几种写法

vue官网-style

一、v-bind:style="color: red"

这种是有问题的 二、对象 v-bind:style="{ key1: value1, key2: value2, key3: 'value3' }" key1、key2、key3 都是css的属性名

value1 的值作为 key1 这个css属性名的值

value2 的值作为 key2 这个css属性名的值

value3 这个字符串会作为 key3 这个css属性名的值

css属性名如果是短横线写法,需要转换成驼峰写法: font-size => fontSize

三、数组 v-bind:style="[value1, value2, { color: myColor }]"

使用 value1 这个数据来控制 value1: {} √ value1: "fontSize: '20px'" ×

使用 value2 这个数据来控制

使用 myColor 这个数据来控制 color 属性的值

    <div id="app">
      <!-- <p :style="color: red">p1</p> -->
      <p :style="{ color: myColor, fontSize: mySize, fontWeight: 'bold' }">
        p2
      </p>
      <p :style="[ value1, value2, { color: myColor } ]">p3</p>
    </div>
    <script>
      var vm = new Vue({
        el: "#app",
        data: {
          myColor: "red",
          mySize: "20px",
          value1: {
            fontSize: "20px",
          },
          value2: {
            fontWeight: "bold",
          },
        },
      });
    </script>

watch

vue-watch

watch 配置选项:监听数据的变化。当数据发生变化的时候,可以去做一些额外的操作。

语法

 watch: {
          key1: value1,
          key2: value2
        }
/**
 key1、key2 是要监听的数据表达式
 value1、value2 是处理函数。string | function | object | Array
 处理函数会自动接收到两个参数分别是:newVal, oldVal
*/
var vm = new Vue({
        el: "#app",

        data: {
          a: 1,
          obj: {
            name: "张三",
            age: 18,
          },
        },

        watch: {
          // 1. 普通函数的写法
          // a: function (newVal, oldVal) {
          //   console.log("newVal: ", newVal);
          //   console.log("oldVal: ", oldVal);
          // },
          // 2. 对象函数的简写方式
          // a(newVal, oldVal) {
          //   console.log("newVal: ", newVal);
          //   console.log("oldVal: ", oldVal);
          // },
          // 3. 注意!!!不能使用箭头函数!!! methods 选项中也不要使用箭头函数
          // 箭头函数绑定了 父级作用域 的上下文,所以 this 将不会按期望指向Vue实例
          // a: (newVal, oldVal) => {
          //   console.log("newVal: ", newVal);
          //   console.log("oldVal: ", oldVal);
          //   console.log(this);
          // },
          // 4. string 写法。hello 是指向的 methods 中 hello 函数
          // a: "hello",
          // 5. object 写法
          // a: {
          //   // 一定要提供 handler 属性,属性值就是处理函数
          //   handler(newVal, oldVal) {
          //     console.log("newVal: ", newVal);
          //     console.log("oldVal: ", oldVal);
          //     console.log(this);
          //   },
          // },
          // 6. 为啥要有 object 写法,可以做更多监听配置,比如深度监听、默认触发监听函数一次
          // 深度监听: 对象中某个属性值发生变化默认是监听不到的
          // obj: {
          //   handler(newVal, oldVal) {
          //     console.log("newVal: ", newVal);
          //     console.log("oldVal: ", oldVal);
          //   },
          //   deep: true, // 深度监听
          //   immediate: true, // 默认触发一次监听
          // },
          // 7. 数组的写法 [string, function, { handler: function }, ...]
          // a: [
          //   "hello",
          //   function () {
          //     console.log("f2");
          //   },
          //   {
          //     handler: function () {
          //       console.log("f3");
          //     },
          //   },
          // ],
          // **key 的表达式写法** 直接去监听某个对象数据中的某个属性 obj.name

          "obj.name": function (newVal, oldVal) {
            console.log(newVal);
            console.log(oldVal);
          },
        },

        methods: {
          hello(newVal, oldVal) {
            console.log("hello");
            // console.log("newVal: ", newVal);
            // console.log("oldVal: ", oldVal);
            // console.log(this);
          },
        },
      });

注意!!!不能使用箭头函数!!! methods 选项中也不要使用箭头函数。因为箭头函数绑定了 父级作用域 的上下文,所以 this 将不会按期望指向Vue实例

    <div id="app">
        <input type="text" v-model="inputValue">
    </div>
    <script>
        new Vue({
            el: '#app',     
            data() {
                return {
                    inputValue: ''
                }
            },
            watch: {
                // inputValue: (newValue) => {
                //     console.log(this) // window
                // },
                inputValue: function (newValue) {
                    console.log(this) // Vue
                }
            }
        })  
    </script>

computed

vue-computed

computed 配置选项 (计算属性,衍生属性,派生属性):基于其余的数据来计算出一份新的数据

语法

computed: {
     key1: value1,
     key2: value2
}
/**
key1 、key2 就是计算属性。
value1、value2 是对应的计算属性的处理函数(计算方式)。是一个函数,必须有返回值。返回值就是这个计算属性的值
*/

function | object

示例一:
computed: {
    fullName: function () {
            return xxxx
    }
}
示例二:
computed: {
    fullName: {
            // 获取这个计算属性的值的函数
            get: function () {
              return xxxx
            },

            // 修改这个计算属性时触发的函数
            //  value 要修改的值
            set: function (value) {

            }
     }
}

计算属性的特点

    1. 计算属性也可以像data中的数据一样去使用
    1. 计算属性不允许手动去修改它的值,它的值是与他的依赖项有关的。只有依赖项发生变化,他的值才会发生变化
    1. 计算属性有缓存。默认计算出来之后,如果依赖项不发生变化,后续使用将一直使用的是缓存的数据
    1. 计算属性可以设置 setter 。但是就算有 setter 它也改变不了计算属性的值,真正能改变它的一直都是它的依赖项
    <div id="app">
      <p>firstName: {{ firstName }}</p>
      <p>lastName: {{ lastName }}</p>
      <p>fullName: {{ fullName }}</p>
      <p>fullName: {{ fullName }}</p>
      <p>fullName: {{ fullName }}</p>
      <p>fullName: {{ fullName }}</p>
      <p>fullName: {{ fullName }}</p>
      <p>fullName: {{ fullName }}</p>
    </div>
    <script>
      var vm = new Vue({
        el: "#app",

        data: {
          firstName: "张",
          lastName: "三",
        },

        computed: {
          // 一、普通函数的形式
          // fullName: function () {
          //   console.log("开始计算 fullName");
          //   // 返回一个值
          //   // fullName 的依赖项就是 firstName 与 lastName
          //   //         任何一个依赖项发生变化,这个计算属性就会得到变化
          //   return this.firstName + " " + this.lastName;
          // },
          // 二、对象的形式。 与 一 的功能一样
          // fullName: {
          //   get() {
          //     console.log("开始计算 fullName");
          //     return this.firstName + " " + this.lastName;
          //   },
          // },
          // 三、对象的形式并且允许修改
          fullName: {
            get() {
              console.log("开始计算 fullName");
              return this.firstName + " " + this.lastName;
            },

            set(value) {
              // 对 fullName 做赋值的时候,会触发 set 函数
              // vm.fullName = '李 四'
              console.log(value);

              // 间接的去改变它的依赖项,从而实现看上去 fullName 变了的效果
              let tmp = value.split(" ");
              this.firstName = tmp[0];
              this.lastName = tmp[1];
            },
          },
        },
      });
    </script>

动态参数

vue官网-动态参数

约束

  1. 避免使用大写字符(驼峰也不可以)
<div id="app">
  <button v-bind:[attribute]="value">{{ message }}</button>
  <button v-bind:[attributeName]="value">{{ message }}</button>
</div>
<script>
  new Vue({
    el: '#app',
    data() {
      return {
        message: '提交',
        value: 'submit',
        attribute: 'type',
        attributeName: 'type'
      }
    },
  })  
</script>

在 DOM 中使用模板时 (直接在一个 HTML 文件里撰写模板),还需要避免使用大写字符来命名键名,因为浏览器会把 attribute 名全部强制转为小写

  1. 动态参数的值的约束:只能是字符串类型或null,null表示移除绑定
<div id="app">
        <button v-bind:[attribute]="value">{{ message }}</button>
    </div>
    <script>
        new Vue({
            el: '#app',
            data() {
                return {
                    message: '提交',
                    value: 'submit',
                    attribute: true,
                    attributeName: 'type'
                }
            },
        })  
    </script>

  1. 表达式的约束

动态参数表达式有一些语法约束,因为某些字符,如空格和引号,放在 HTML attribute 名里是无效的

组件基础

data必须是函数的原因

官网原文:一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:

组件可以多次复用使用,如果data不使用函数,那么当其中一个组件更改data,其他组件的数据也会被修改

组件注册

全局组件

vue-全局组件

    Vue.component('my-com', {
            template: `
                <div>
                    <div>123</div>   
                </div>
            `
        })
        new Vue({
            el: '#app',
            data() {
                return {
                    message: 'sss'
                }
            }
        })  

局部组件

vue-局部组件

    Vue.component('my-com', {
            template: `
                <div>
                    <div>123</div>
                    <com1></com1>    
                </div>
            `,
            // 局部组件
            components: {
                com1: {
                    template: `<div>com1<div>`
                }
            }
        })
        new Vue({
            el: '#app',
            data() {
                return {
                    message: 'sss'
                }
            }
        })

组件名

    <!-- 
      在 template 选项中写模板内容,非常不方便,没有代码提示,也没有语法高亮。
      这时可以将 模板内容,放置到一个 script 标签中,要注意
        1. 给这个script的type属性设置为 text/x-template
        2. 给这个script设置一个id
      然后 组件的 template 选项,可以直接去使用 #xxId 即可
     -->
    <script id="appTmp" type="text/x-template">
      <div id="app">
        <com1></com1>

        <hello-world></hello-world>

        <helloWorld></helloWorld>
      </div>
    </script>
    <script>
      Vue.component("com1", {
        template: `
          <div>
            <div>我是一个 com1 com1 </div>
            <p>com1</p>
          </div>
        `,
      });
      Vue.component("helloWorld", {
        template: `
          <div>我是一个 Helo world 组件</div>
        `,
      });
      var vm = new Vue({
        el: "#app",
        template: "#appTmp",
      });
    </script>

prop

vue-prop

props 选项

将组件看成是一个函数,props 就是这个函数接收到的参数集合。

prop 就是 props 中的具体一个参数

prop大小写

HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名

prop类型

字符串数组

props: ['title', 'likes',....]

对象形式

可以给每个prop指定值的类型、默认值

类型验证

props: { 
// 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证) propA: Number, 
// 多个可能的类型 
propB: [String, Number], 
// 必填的字符串 
propC: { type: String, required: true }, 
// 带有默认值的数字 
propD: { type: Number, default: 100 }, 
// 带有默认值的对象 
propE: { 
    type: Object, 
    // 对象或数组默认值必须从一个工厂函数获取 
    default: function () { 
    return { message: 'hello' } 
    } 
}

传值的几种类型写法

vue-prop

单向数据流

vue-prop单项数据流

prop是不允许修改,应为要遵循单向数据流这个规定

非 prop 的 attribute (非 prop 的特性)

vue-非 prop 的 attribute

在调用组件时,设置的属性(attribute)如果这个组件中没有定义相应的prop。那么这个属性就叫做 非prop的attribute

    Vue.component('hello', {
        props: {
          name: String,
          age: Number
        },
        template: `<div>hello</div>`
    })
<hello name="张三" age="18" id="box" title="我的天" class="box1" style="color: red"></hello>
/*
    这里 hello 组件调用时。设置的属性有:name、age、id、title、class、style
    hello 组件中将 name、age 是规定为 prop 数据的。
    那么 这里 id 和 title 这两个属性就叫做非prop的特性
    注意:class 与 style 不是非prop的特性。他们两个是非常非常特殊的一个属性
*/

非prop的attribute 的特点

    1. 最主要的一个特点就是会继承到组件的根元素
    1. 并且是一种替换的操作

class与style是非常特殊的

    1. 他们有 非prop的attribute 的继承的特点
    1. 但是他们不是替换的操作,而是拼接操作

用处:调用别人的组件的时候,这个组件的源代码我不能修改,这时如果要修改样式,就可以在调用时设置自己的 class 或 style

<div id="app">
      <hello
        name="张三"
        :age="18"
        id="box"
        title="我的天"
        class="box1"
        style="color: red;"
      ></hello>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script>
    <script>
      Vue.component("hello", {
        props: {
          name: String,
          age: Number,
        },
        template: `<div id="hello" title="我的地" class="box2" style="color: green">hello</div>`,
      });

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

image.png

$attr、$props实例属性

$attrs 实例属性

$attrs 可以获取到组件接收到的非prop的特性。是一个对象 {key1: value1, key2: value2}

$props 实例属性

$props 可以获取到组件接收到的 prop 数据,是一个对象 {key1: value1, key2: value2}

    <div id="app">
      <hello
        id="box"
        title="hello"
        name="zhagnsan"
        :age="18"
        class="box"
        style="color: red;"
      ></hello>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script>
    <script>
      Vue.component("hello", {
        props: {
          name: String,
          age: Number,
        },
        template: `<div>hello</div>`,

        created() {
          console.log(this.$attrs);
          console.log(this.$props);
        },
      });

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

image.png

inheritAttrs

希望组件的根元素继承 attribute,可以在组件的选项中设置 inheritAttrs: false

    <div id="app">
        <base-input label="姓名" placeholder="请输入您的姓名"></base-input>
    </div>
    <script>
        Vue.component('baseInput', {
            props: {
                label: {
                    type: String,
                    default: ''
                }
            },
            template: `
                <div>
                    <span>{{label}}</span>
                    <input v-bind="$attrs"></input>    
                </div>
            `
        })
        new Vue({
            el: '#app',
            data() {
                return {
                    message: 'sss'
                }
            },
        })
    </script>

image.png

    Vue.component('baseInput', {
            inheritAttrs: false,
            props: {
                label: {
                    type: String,
                    default: ''
                }
            },
            template: `
                <div>
                    <span>{{label}}</span>
                    <input v-bind="$attrs"></input>    
                </div>
            `
        })
        new Vue({
            el: '#app',
            data() {
                return {
                    message: 'sss'
                }
            },
        })

image.png

inheritAttrs: false

基于 $attrs 和 inheritAttrs 来实现一些基础组件


      思考实现一个 input 组件

      1. label 可以配置
      2. type 可以配置
      3. placeholder 可以配置
      4. readonly 可以配置
      5. disabled 可以配置

      发现,原生的 input 框是有很多的属性可以去控制,比如:type、placeholder、readonly、disabled
      现在,将这些属性都设置为 prop 数据来进行控制的话,有点费劲。

      能不能解决这个费劲的问题:希望能够像下面这种去调用。但是不需要在组件中做这些prop的定义

        <base-input label="用户名" placeholder="请输入用户名"></base-input>
        <base-input
          label="密码"
          type="password"
          placeholder="请输入密码"
        ></base-input>
        <base-input label="地址" placeholder="请输入地址" disabled></base-input>

      这时,就可以使用 $attrs 与 inheritAttrs 来实现


      

做一个组件的时候需要考虑的事情:(!!!!)

    1. 组件要实现什么功能
    1. 组件中那些地方是应该使用数据来控制的,这个数据用什么数据来控制呢
      1. data 数据? 组件自身要对数据做修改的时候
      1. props 数据? 需要通过父组件去传递下来的时候
      1. computed 数据? 这个数据可以基于 现有的 data 、props、computed 做计算得来的时候
    <div id="app">
      <base-input label="用户名" placeholder="请输入用户名"></base-input>
      <base-input
        label="密码"
        type="password"
        placeholder="请输入密码"
      ></base-input>
      <base-input label="地址" placeholder="请输入地址" disabled></base-input>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script>
    <script>
      Vue.component("baseInput", {
        inheritAttrs: false,
        props: {
          label: {
            type: String,
            required: true,
          },
        },
        // 不使用prop数据,而是使用非prop特性数据
        // template: `
        //   <div>
        //     <label>
        //       <span>{{ label }}</span>
        //       <input
        //         :type="$attrs.type"
        //         :placeholder="$attrs.placeholder"
        //         :disabled="$attrs.disabled"
        //         :readonly="$attrs.readonly" />
        //     </label>
        //   </div>
        // `,
        // 一个一个的去绑定 $attrs 也挺费劲。这时可以使用 v-bind 直接绑定一个对象
        // v-bind="{key1: value1, key2: value2}" => v-bind:key1="value1" v-bind:key2="value2"
        template: `
          <div>
            <label>
              <span>{{ label }}</span>
              <input 
                v-bind="$attrs" />
            </label>
          </div>
        `,
        created() {
          console.log(this.$attrs);
        },
      });

      // Vue.component("baseInput", {
      //   props: {
      //     label: {
      //       type: String,
      //       required: true,
      //     },
      //     type: {
      //       type: String,
      //       default: "text",
      //     },
      //     placeholder: {
      //       type: String,
      //       default: "请输入",
      //     },
      //     readonly: {
      //       type: Boolean,
      //       default: false,
      //     },
      //     disabled: {
      //       type: Boolean,
      //       default: false,
      //     },
      //   },
      //   template: `
      //     <div>
      //       <label>
      //         <span>{{ label }}</span>
      //         <input :type="type" :placeholder="placeholder" :readonly="readonly" :disabled="disabled" />
      //       </label>
      //     </div>
      //   `,
      // });

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

v-bind="{key1: value1, key2: value2}" => v-bind:key1="value1" v-bind:key2="value2"


Elvmx