Go to comments

vue 集训营笔记

以前的开发,

我们关心的是 DOM 元素的处理,jQuery 只是简化的 DOM 操作


现在前端发展到单页面应用,

就是整个网站只有一个页面,或者是某一个功能块只有一个页面,这就是单页面应用程序


面对单页面应用程序,很多的数据、DOM元素全部要要 js 来处理,

这种情况用传统的开发就显得麻烦了,

vue 解决了这个问题,它能够在复杂的系统里面降低项目的复杂度


Vue 的特点

1. 渐进式,意思是 vue 的侵入性很少,因此使用 vue 和很多其他前端技术联用

2. 组件化,面对一个复杂的页面时,可以把页面划分为很多很多的区域,每一个区域做成一个组件

3. 响应式,指的是数据响应式,vue会监控数据的变化,当数据发生变化时自动重新渲染页面


渐进式的实现方式,

vue 只会控制指定的容器,一个 vue 实例控制一个容器,控制挂载区域的容器

<div>这些内容和vue共存</div>
<div id="app"></div>
<div>这些内容和vue共存</div>

<script>

  const app = new Vue(config);

  app.$mount("#app");

</script>


一、vue 的核心功能

创建 vue 工程有两种方式

1. 直接在页面上引用 vue.js

2. 使用脚手架(vue-cli)搭建工程


示例,

先引入 vue.js 文件,再引入我们自己写的 main.js

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>商品仓库管理</title>
<style>
  ul{list-style:none;padding:0;}
  span{display:inline-block;}
  .soldout{color: #008c8c;}
  .stock{color:#f40;width:30px;text-align:center;}
  [type="number"]{width:30px;}
</style>
</head>
<body>

  <div id="app">
    <!-- #app元素会被vue的模板替换掉 -->
  </div>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>
  <script src="./main.js"></script>

</body>
</html>


main.js

使用 vue 实现效果

// 模板
const template = `
<div>
  <h1>{{title}}</h1>
  <div>
    商品名称:<input type="text" v-model="newProducts.name">
    商品数量:<input type="number" v-model.number="newProducts.stock">
    <button @click="add">添加</button>
  </div>
  <ul>
    <li v-for="(item, index) in products" :key="index">
      <span style="width:70px;">{{item.name}}</span>
      <button @click="changeStock(item, item.stock-1)">-</button>
      <span v-if="item.stock>0" class="stock">{{item.stock}}</span>
      <i v-else>售罄</i>
      <input type="number" min="0" v-model="item.stock"/>
      <button @click="changeStock(item, +item.stock+1)">+</button>
      <button @click="remove(index)"> del </button>
    </li>
  </ul>
</div>`;

// 配置对象
const config = {
  template,
  el: "#app",
  data: {
    title: "商品和库存",
    products: [
      {name:"HuaWei", stock: 10},
      {name:"MI", stock: 8},
      {name:"iPhone", stock: 11}
    ],
    newProducts: {
      name: "",
      stock: 0
    }
  },
  methods: {
    changeStock(prod, newStock){
      if(newStock < 0){
        newStock = 0;
      }
      prod.stock = newStock;
    },
    remove(index){
      this.products.splice(index, 1);
    },
    add(){
      this.products.push(this.newProducts);
      this.newProducts = {
        name: "",
        stock: 0
      };
    },
  },
}

// 创建一个vue实例
const app = new Vue(config);


1、vue 实例

我们想要使用 vue

1. 先要通过  new Vue(config)  创建一个 vue 对象,这个对象就是 vue 的实例

2. 参数 config 是配置对象,配置对象就是配置 vue 里面具有有哪些功能,要做什么事情等各种配置


app 是 vue 实例,我们可以通过 vue 实例更改数据,数据的变化会导致界面重新渲染,这就是响应式

 app.title="修改标题" 


属性 title 在配置对象 config 的 data 里面,

 config.data.title = "修改title标题"  为什么不在 config.data 修改

 app.title = "修改标题"  而是在 vue 实例里面修改


打印 app

vue 实例里面有 title 属性,这个 title 属性怎么来的呢?


这涉及到响应式的原理

1. 通过  new Vue(config)  创建 vue 实例的时候,

    会遍历 data 配置里面的所有成员提升到 vue 实例里面,

2. vue 实例里面的属性,全是通过属性描述符 Object.definedPropty() 来创建的,

    读取属性要经过 get 函数,属性赋值的要经过 set 函数,这样做是为了实现响应式

3. 因此使用 app.title 赋值的时候,vue 就知道修改了数据,应该重新渲染了


Ps:

vue3 不在使用 Object.definedPropty() 方式的,使用 es6 里面的 proxy 对象代理


为什么在模板里面可以直接写 {{title}} ?

可以认为模板环境里面的 this 指向的就是“vue实例”,所以模板可以使用实例里面的东西


为什么实例里面有很多奇怪的属性名字?

由于配置里面的东西会提升到 vue 实例里面,为了防止命名冲突,vue 自身的成员名称前加上 $ 或 _

$ 开头是我们可以使用的

_ 开头是vue内部使用的


比如,data 里面有一个属性 children,

vue 实例里面自带了 $children 属性,

如果不加 $ 我们的 children 提升后就会把 $children 覆盖了


在我们创建 vue 实例的时候,会把下面的配置成员提升到 vue 实例里面

1. data 配置,提升是为了实现响应式

2. methods 配置,提升是为了在模板中方便使用

3. computed 计算属性

4. prop 属性


2、配置对象

对象里面的配置

配置的名子
template配置渲染的模板,类型是字符串,字符串写的是什么就渲染什么或者说就展示什么
el配置要控制的元素,写的是一个css选择器,就是控制哪个元素的渲染
data管理的数据,就是配置我们要控制的数据,该数据是响应式的
methods配置方法,方法中的this指向的是vue实例。不能使用箭头函数,会干扰this的绑定
render
computed


挂载的配置,挂载有两种方式

1. 通过 el: "#app" 进行配置

2. 使用 vue 实例中的 $mount 函数进行配置


模板的配置

1. 如果不配置 template 属性,可以把模板写到页面 #app 里面

2. 在 tempalte 配置中书写(常见)

3. 在 render 中手动用函数创建,render 函数的参数是一个创建虚拟 dom 的方法


render 函数的参数是一个函数,该函数帮我们创建元素,

比如,创建一个 h1 元素,我们配置的模板 template 失效了,页面变成 h1 元素了,也就是说真正起作用的是 render 配置

const template = `<div>
  <ul>
    <li v-for="(item, index) in products" :key="index">
      <span>{{item.name}}</span>
    </li>
  </ul>
</div>`;

const config = {
  template,
  el: "#app",
  data: {
    products: [
      {name:"HuaWei", stock: 10},
      {name:"MI", stock: 8},
      {name:"iPhone", stock: 11}
    ],
  },
  render(createElement){
    return createElement("h1", "hellow!!!");
  },
}

const app = new Vue(config);


我们没有写 render 配置,才会模板 template 配置里面读,然后帮我们生成 render

这也提醒我们,

render 函数的参数是一个创建虚拟 dom 对象的方法,createElement 方法创建的是虚拟 dom,

也就是说 template 匹配里面写的都不是真实的 dom,真实的 dom 里面是没有 v-for 等这些指令的


为什么需要虚拟 dom?

因为真实的 dom 操作特别慢,虚拟 dom 就是一个普通的 js 对象,


至少要知道,

template 模板里面不是真实的 dom,

template 模板里面的内容通过 createElement 生成虚拟 dom,


3、模板 template

大胡子语法:

在模板元素内部使用 {{js表达式}}


指令:

通常作为元素的属性存在,名称上以 v- 开头

指令
v-for表示循环生成元素
v-on用于注册事件,@语法糖,比如,input文本改变事件,不断打字就会不断的触发
v-if用于判断该元素是否生成,可以和 v-else 联用,或是 v-if-else 联着用
v-show用于判断该元素是否显示,不显示的时候 dispaly:none
v-bind用于绑定属性,如果属性来自于js表达式,语法糖 :
v-model语法糖,用于实现双向绑定,实际上是自动绑定了 :value 属性值,和注册了 @input 事件
v-html


注意,

vue2 只支持单个根元素,

否则会报错 Component template should contain exactly one root element.


手写商品的数据,可以显示并且具有响应式

// 模板
const template = `
<div>
  <h1>{{title}}</h1>
  <ul>
    <li>{{products[0].name}}</li>
    <li>{{products[1].name}}</li>
    <li>{{products[2].name}}</li>
  </ul>
</div>`;

// 配置对象
const config = {
  template,
  el: "#app",
  data: {
    title: "商品和库存",
    products: [
      {name:"HuaWei", stock: 10},
      {name:"MI", stock: 8},
      {name:"iPhone", stock: 11}
    ],
  },
}

// 创建一个vue实例
const app = new Vue(config);


 app.products[0] = {name: "锤子", stock: 1}  不能这样赋值(下面会说为什么不能

 app.products[0].name = "锤子"  可以修改 name 属性


v-for

一般使用 v-for 循环商品的数据

const template = `
<div>
  <h1>{{title}}</h1>
  <ul>
    <li v-for="(item, index) in products" :key="index">
      <span>{{item.name}}</span>
      <span>{{item.stock}}</span>
    </li>
  </ul>
</div>`;


 app.products.push({name: "锤子", stock: 1}) 

数据是有响应式的,增加一项更改了数据 products

vue 收到通知后就会重新渲染出新的数据


但是这里有一个疑问的,

为了响应式,products 提升到实例里面了,重新给 products 属性赋值时 vue 会收到通知,

但是这里没有给 app.products 重新赋值,我们是调用的是 js 数组的 push 方法,


vue 并不知道我们调用了数组的 push 方法,

所以,vue 重写了数组里面很多的方法,对比一下两个 push 不是一个方法(点击一下增加或减少库存)

 app.products.push === Array.prototype.push  返回 false


这个叫 vue 的数组变异,

所以对数组的各种操作 vue 都能收到通知


因为没有给 [0] 索引加上 definedPropty

 app.products[0] = {name: "红米手机", stock: 200} 

直接使用索引给数组赋值,虽然数据变了,但是vue收不到通知


 app.products[0].name = 123 

但是可以更改对象的属性是没问题的


v-on 事件的处理

我们不用写 dom 的操作,只关注数据就可以,

点击按钮改变数据 item.stock-- 变的非常的简单,数据是响应式的,自然而然生成界面

// 模板
const template = `
<div>
  <h1>{{title}}</h1>
  <ul>
    <li v-for="(item, index) in products" :key="index">
      <span>{{item.name}}</span>
      <button v-on:click="item.stock--">-</button>
      <span>{{item.stock}}</span>
      <button v-on:click="item.stock++">+</button>
    </li>
  </ul>
</div>`;


也可以写一个方法,

在模板里面直接写“方法的名称”就可以调用了

// 模板
const template = `
<div>
  <h1>{{title}}</h1>
  <ul>
    <li v-for="(item, index) in products" :key="index">
      <span>{{item.name}}</span>
      <button @click="changeStock(item, item.stock-1)">-</button>
      <span class="stock">{{item.stock}}</span>
      <button @click="changeStock(item, item.stock+1)">+</button>
    </li>
  </ul>
</div>`;

// 配置对象
const config = {
  template,
  el: "#app",
  data: {
    title: "商品和库存",
    products: [
      {name:"HuaWei", stock: 10},
      {name:"MI", stock: 8},
      {name:"iPhone", stock: 11}
    ],
  },
  methods:{
    changeStock(prod, newStock){
      if(newStock < 0){
        newStock = 0;
      }
      prod.stock = newStock;
    },
  },
}


app 实例里面不仅可以看到 products、title,

还可以看到 changeStock 方法,为了在模板中方便使用,methods 配置里面的方法也会提升到 vue 实例中,方法提升是为了可以在模板中调用


方法里面的 this 指向的是 vue 实例

methods: {
  changeStock(prod, newStock){
    console.log(this === app); //true
  },
},


元素的显示和隐藏

v-if 可以和 v-else 连着用

v-show 要写两个才能实现同样的效果

const template = `
<div>
  <h1>{{title}}</h1>
  <ul>
    <li v-for="(item, index) in products" :key="index">
      <span>{{item.name}}</span>
      <button @click="changeStock(item, item.stock-1)">-</button>
      <span v-show="item.stock>0" class="stock">{{item.stock}}</span>
      <i v-show="item.stock===0">售罄</i>
      <button @click="changeStock(item, item.stock+1)">+</button>
    </li>
  </ul>
</div>`;


v-if 和 v-show 的区别

v-show 条件不满足的时候 display: none

v-if 条件不满足,不生成元素


如果都是 span 元素,也可以写三目运算

<span>{{item.stock>0? item.stock : "售罄"}}</span>


文本框显示库存,

要设置 value 属性的值,value 的值来自于 js 表达式

不能这样写  value="item.stock" ,这样设置的 html 元素的属性,写什么就显示什么,

要使用 v-bind:value 绑定属性,也可以使用语法糖 :value="item.stock"

<input type="number" min="0" :value="item.stock"/>


改变文本框的数字,库存也要跟着变,

还要注册文本改变的 input 事件,只要打字就会不断触发该事件,

然后写一个事件函数处理


事件函数有两种调用方式,

1. 之前是调用的方式,可以传一些额外的参数,比如按钮上面的事件 <button @click="changeStock(item, item.stock-1)">

2. 现在直接写函数名 @input="handleInput",会自动把事件参数 e 带到事件函数里面


打印事件对象 e,

可以通过 e.target.value 拿到新的库存,然后赋值给对应的数据

// 模板
const template = `
<div>
  <h1>{{title}}</h1>
  <ul>
    <li v-for="(item, index) in products" :key="index">
      <span>{{item.name}}</span>
      <button @click="changeStock(item, item.stock-1)">-</button>
      <span v-show="item.stock>0" class="stock">{{item.stock}}</span>
      <i v-show="item.stock===0">售罄</i>
      <input type="number" min="0" @input="handleInput" :value="item.stock"/>
      <button @click="changeStock(item, item.stock+1)">+</button>
    </li>
  </ul>
</div>`;

// 配置对象
const config = {
  template,
  el: "#app",
  data: {
    title: "商品和库存",
    products: [
      {name:"HuaWei", stock: 10},
      {name:"MI", stock: 8},
      {name:"iPhone", stock: 11}
    ],
  },
  methods:{
    changeStock(prod, newStock){
      if(newStock < 0){
        newStock = 0;
      }
      prod.stock = newStock;
    },
    handleInput(e){
      console.log(e); // e.target.value拿到新的库存
    }
  },
}

// 创建一个vue实例
const app = new Vue(config);

不用直接写事件函数的名子的方式,使用事件对象 e 了


双向绑定

使用调用事件函数的方式,完成一个双向绑定

 <input type="number" min="0" @input="handleInput(item, $event)" :value="item.stock"/> 

1. 传两个参数,

    $event 表示是事件对象

    item 当前商品的数据

2. 拿到新的库存,然后赋值给对象的 stock 属性,数据是响应式的,数据一变 :value="item.stock" 绑定的 value 值也跟着变,这样就完成了双向绑定

// 模板
const template = `
<div>
  <h1>{{title}}</h1>
  <ul>
    <li v-for="(item, index) in products" :key="index">
      <span>{{item.name}}</span>
      <button @click="changeStock(item, item.stock-1)">-</button>
      <span v-show="item.stock>0" class="stock">{{item.stock}}</span>
      <i v-show="item.stock===0">售罄</i>
      <input type="number" min="0" @input="handleInput(item, $event)" :value="item.stock"/>
      <button @click="changeStock(item, item.stock+1)">+</button>
    </li>
  </ul>
</div>`;

// 配置对象
const config = {
  template,
  el: "#app",
  data: {
    title: "商品和库存",
    products: [
      {name:"HuaWei", stock: 10},
      {name:"MI", stock: 8},
      {name:"iPhone", stock: 11}
    ],
  },
  methods:{
    changeStock(prod, newStock){
      if(newStock < 0){
        newStock = 0;
      }
      prod.stock = newStock;
    },
    handleInput(item, event){
      // console.log(item, event);
      item.stock = +event.target.value;
    }
  },
}

// 创建一个vue实例
const app = new Vue(config);


也可以在行间完成双向绑定,

拿到 value 改变后的值,直接赋值给 item.stock 库存,对象的 stock 是响应式的,stock 变了表单的 value 也跟着变了

<input type="number" min="0"
 :value="item.stock"
 v-on:input="item.stock = $event.target.value"
/>


什么是双向绑定?

文本框的值来自于数据 :value="item.stock"

文本框一变化 $event.target.value 也会跟着变化

数据决定了文本框显示什么,文本框的操作决定了我们的数据是什么,这是是双向绑定

界面影响数据,数据也会影响界面


双向绑定可以使用简写 v-model,效果完全一样,它就是一个语法糖

绑定 :value 属性,

并自动注册 @input 事件

<input type="number" min="0" v-model="item.stock" />


删除一个商品

从数组里面移除一项,this 指向 app 对象,所以 methods 方法里面的函数不能用箭头函数

remove(index){
  this.products.splice(index, 1);
},


添加商品

按照以前的想法,获取 input 元素 value 的值,然后往 products 商品数组中加一项,

想法就错了,

vue 的一切全是数据,连添加里面的东西也是数据

1. data 里面写一个 newProducts 新商品对象

// 模板
const template = `
<div>
  <h1>{{title}}</h1>
  <div>
    商品名称:<input type="text" v-model="newProducts.name">
    商品数量:<input type="number" v-model.number="newProducts.stock">
    <button @click="add">添加</button>
  </div>
  <ul>
    <li v-for="(item, index) in products" :key="index">
      <span style="width:70px;">{{item.name}}</span>
      <button @click="changeStock(item, item.stock-1)">-</button>
      <span v-if="item.stock>0" class="stock">{{item.stock}}</span>
      <i v-else>售罄</i>
      <input type="number" min="0" v-model="item.stock"/>
      <button @click="changeStock(item, +item.stock+1)">+</button>
      <button @click="remove(index)"> del </button>
    </li>
  </ul>
</div>`;

// 配置对象
const config = {
  template,
  el: "#app",
  data: {
    title: "商品和库存",
    products: [
      {name:"HuaWei", stock: 10},
      {name:"XiaoMi", stock: 8},
      {name:"iPhone", stock: 11}
    ],
    newProducts: { // 每个商品都是一个对象,新商品不例外,也是一个对象
      name: "",
      stock: 0
    }
  },
  methods: {
    changeStock(prod, newStock){
      console.log(this === app); //true
      if(newStock < 0){
        newStock = 0;
      }
      prod.stock = newStock;
    },
    remove(index){
      this.products.splice(index, 1);
    },
    add(){
      this.products.push(this.newProducts);
      this.newProducts = {
        name: "",
        stock: 0
      };
    },
  },
}


v-html 指令

vue为了安全,会将元素内部的 {{插值}} 进行实体编码


什么是插值?

差值是在 div 元素内部,使用一个表达式 {{js...}} 算出来的结果


vue 会在 {{插值}} 的位置自动进行实体编码,因此我们看到 span 元素是编码后的结果(右键 Edit as HTML)

 <p>&lt;span style="color:#f40"&gt;带标签的内容&lt;/span&gt;</p> 

为了防止用户输入一些标签,进行注入攻击(xss攻击)


如果信任这个数据使用用 v-htlm 指令,一般用在富文本框提交的内容

<div v-html="html"></div> 相当于 div.innerHTML = '<p><span color="#f40">带标签的内容</span></p>'


4、计算属性 computed

通过 firstName 和 lastName 拼接全名

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="https://20180416-1252237835.cos.ap-beijing.myqcloud.com/common/vue.min.js"></script>
<title>计算属性</title>
</head>
<body>

<div id="app"></div>

<script>

const template = `<div>
    <p>姓:{{firstName }}</p>
    <p>名:{{lastName}}</p>
    <p>全名</p>
    <p>模板中写表达式:{{firstName + lastName}}</p>
    <p>调用方法:{{getFullName()}}</p>
  </div>`

const config = {
  template,
  el: "#app",
  data:{
    firstName: "莫",
    lastName: "尼卡"
  },
  methods:{
    getFullName(){
      console.log("方法调用了");
      return this.firstName + this.lastName;
    }
  }
}

var app = new Vue(config);

</script>
</body>
</html>


拼接全名有三种方法,

1.  {{firstName + lastName}}  

直接在大胡子语法里面写表达式的好处是,修改了姓 app.firstName = "Mo" 全名也跟着改了

因为数据是响应式的,修改后会重新渲染


2.  {{getFullName()}} 

但是如果计算很复杂,在 methods 配置里面写一个方法 getFullName 方法,然后在模板里面可以调用函数 


methods 配置里面的函数是没有响应式的,但是数据有响应式,

当修改了数据  app.firstName = "Mo" 模板要重新渲染,重新渲染就会重新调用 getFullName() 函数


3. 计算属性,在 computed 配置写一个 fullName 函数,在模板里面当做属性来调用

const template = `<div>
    <p>姓:{{firstName }}</p>
    <p>名:{{lastName}}</p>
    <p>全名</p>
    <p>模板中写表达式:{{firstName + lastName}}</p>
    <p>调用方法:{{getFullName()}}</p>
    <p>计算属性:{{fullName}}</p>
    <p>{{n}}</p>
    <button @click="n++"> 加 </button>
  </div>`

const config = {
  template,
  el: "#app",
  data:{
    firstName: "莫",
    lastName: "尼卡",
    n: 0
  },
  computed:{
    fullName(){
      console.log("属性重新计算了");
      return this.firstName + this.lastName;
    }
  },
  methods:{
    getFullName(){
      console.log("方法调用了");
      return this.firstName + this.lastName;
    }
  }
}

var app = new Vue(config);


关于计算属性 computed

1. 计算属性里面的配置会提升到 vue 实例中,

    因此,在模板里面可以直接当做“属性”使用, 使用时,实际上调用的是对应的方法

2. 通常情况下,计算属性里面都会使用“data”或来自其他的“计算属性”计算得到的数据(否则尽量不要用计算属性了)


计算属性与方法的区别(面试经常问的问题)

第一个区别:

vue 会检查计算属性的依赖,当依赖没有发生变化时,vue 会直接使用之前缓存的结果,而不会重新计算,这是为了提高效率


只要两个依赖的项不发生变化,就不会调用计算属性的函数,

1. 比如,点击按钮,数据变了 n++,数字变了肯定要重新渲染,

    计算属性只是一开始调用了一次,点击按钮重新渲染页面,但没有重新调用计算属性的函数

    因为计算属性依赖的两数据没有变,计算属性就不会重新计算  

2. 如果修改了依赖  app.firstName = "Moo" ,计算属性重新运行了

3. 而每次点击按钮,都会重新调用方法


所以,除非处理事件,否则能用计算属性尽量用计算属性,因为计算属性的效率更高


计算属性实现这一点的原理

1. 因为 data 里面的东西会提到 vue 实例里面,

    在实例里面这些提升的属性是用 definedPropty 定义的,读取属性的时候 get 函数会监听到

2. 于是当发现用计算属性 {{fullName}} 的时候,就知道调用了 computed 配置的 fullName 函数,

    这时候 vue 会做一张表进行缓存 fullName 属性

3. 在 get 函数里面,检查有没有调用这两个依赖...


第二个区别:

计算属性的读取函数,不能有参数(读取属性时,参数也没有意义)

方法可以有人任意的参数


第三个区别:

计算属性可以配置 get 和 set,分别用于读取和设置


如果需要设置计算属性

1. 计算属性就不是函数了,要配置为一个对象

2. 对象里面有 set 和 get 两个函数

3. set 函数需要一个参数 set(newVal),参数就是给计算属性赋的值

const template = `<div>
  <p>姓:{{firstName }}</p>
  <p>名:{{lastName}}</p>
  <p>计算属性拼接:{{fullName}}</p>
</div>`

const config = {
  template,
  el: "#app",
  data:{
    firstName: "莫",
    lastName: "尼卡",
  },
  computed:{
    fullName:{
      get(){
        console.log("属性重新计算");
        return this.firstName + this.lastName; // 读取属性的时候必须要返回
      },
      set(newVal){
        // console.log(newVal);
        // 因为计算属性是根据依赖生成的,改全名改的也是依赖
        this.firstName = newVal[0]; // 取第一个字符
        this.lastName = newVal.substr(1); // 从第2个字符开始截取
      }
    },
  },
}

var app = new Vue(config);


 fullName = "Moo尼卡"  赋值的时候实际调用 set 函数,并把新的值传进去了 fullName.set("Moo尼卡") 

 fullName  读属性候直接调用的是 get 函数,没有参数 fullName.get()


v-model="fullName" 双向绑定计算属性

文本框的内容变化,就会给计算属性重新赋值 app.fullName = "Mo尼卡"












Leave a comment 0 Comments.

Leave a Reply

换一张