VUE2中文文档:语法基础笔记
声明式渲染
Vue.js 的核心是,可以采用简洁的模板语法来声明式的将数据渲染为 DOM:
<div id="app"> {{ message }} //模板语法 </div>
var app = new Vue({ //新建vue实例(响应式) el: '#app', //挂载到#app data: { message: 'Hello Vue!' //数据 } })
除了文本插值(text interpolation),我们还可以采用这样的方式绑定 DOM 元素属性:
<div id="app-2">
<span v-bind:title="message"> //v-bind:属性=“数据”
鼠标悬停此处几秒,
可以看到此处动态绑定的 title!
</span>
</div>
var app2 = new Vue({ el: '#app-2', data: { message: '页面加载于 ' + new Date().toLocaleString() } })
由组件组合而成的应用程序
在 Vue 中,一个组件本质上是一个被预先定义选项的 Vue 实例,在 Vue 中注册组件很简单:
// 定义一个被命名为 todo-item 的新组件 Vue.component('todo-item', { template: '<li>这是一个 todo 项</li>' })
现在你可以在另一个组件模板中组合使用它:
<ol> <!-- 创建一个 todo-item 组件的实例 --> <todo-item></todo-item> </ol>
将数据从父作用域传到子组件。让我们来修改下组件的定义,使它可以接受一个 prop:
var app7 = new Vue({ //vue实例挂载到app-7
el: '#app-7',
data: {
groceryList: [
{ id: 0, text: '蔬菜' },
{ id: 1, text: '奶酪' },
{ id: 2, text: '其他人类食物' }
]
}
})
<div id="app-7"> //组件模板app-7 <ol> <!-- 现在我们为每个 todo-item 提供了 todo 对象, 其中它的内容是动态的。 我们还需要为每个组件提供一个 "key", 这将在之后详细解释。 --> <todo-item v-for="item in groceryList" v-bind:todo="item" //将遍历的每个item对象传递给todo v-bind:key="item.id"> //由于vue对组件渲染是可复用的,如果不给每一个添加不同的key,后面循环的组件不会重新渲染 </todo-item> //而是直接复用第一次渲染的结果 </ol> </div>
Vue.component('todo-item', { props: ['todo'], //子组件用props接受todo template: '<li>{{ todo.text }}</li>' //子组件模板 })
下面正式开始:
Vue 实例
var vm = new Vue({ //创建 // 选项 })
Vue 应用程序由「一个使用 new Vue
创建的 Vue 根实例」、「嵌套的树结构(可选)」和「可复用的组件」组成。
data 和 methods
在创建 Vue 实例时,会将所有在 data
对象中找到的属性,都添加到 Vue 的响应式系统中。每当这些属性的值发生变化时,视图都会“及时响应”,并更新相应的新值。
每当 data 对象发生变化,都会触发视图重新渲染。值得注意的是,如果实例已经创建,那么只有那些 data
中的原本就已经存在的属性,才是响应式的。也就是说,如果在实例创建之后,添加一个新的属性,
例如:vm.b = ' hide';
然后,修改 b
不会触发任何视图更新。如果你已经提前知道,之后将会用到一个开始是空的或不存在的属性,你需要预先设置一些初始值。例如:
data: { newTodoText: '', visitCount: 0, hideCompletedTodos: false, todos: [], error: null }
唯一的例外是,使用 Object.freeze()
来防止已有属性被修改,这也意味着响应式系统无法追踪变化。
除了 data 属性, Vue 实例还暴露了一些有用的实例属性和方法。这些属性与方法都具有前缀 $
,以便与用户定义(user-defined)的属性有所区分。例如:
var data = { a: 1 } var vm = new Vue({ el: '#example', data: data }) vm.$data === data // => true 实例属性,获取实例data vm.$el === document.getElementById('example') // => true 获取挂载的DOM对象 // $watch 是一个实例方法 vm.$watch('a', function (newValue, oldValue) { // 此回调函数将在 `vm.a` 改变后调用 })
点击API文档,来获取实例属性(instance property)和方法(methods)的完整列表。
实例生命周期钩子函数
每个 Vue 实例在被创建之前,都要经过一系列的初始化过程 - 例如:
Vue 实例需要设置数据观察(set up data observation)、
编译模板(compile the template)、
在 DOM 挂载实例(mount the instance to the DOM),
数据变化时更新 DOM(update the DOM when data change)。
在这个过程中,Vue 实例还会调用执行一些生命周期钩子函数,这样用户能够在特定阶段添加自己的代码。
生命周期示意图
模板语法
所有 Vue.js 的模板都是有效的 HTML,能够被遵循规范的浏览器和 HTML 解析器解析。
在底层的实现上,Vue 将模板编译为可以生成 Virtual DOM 的 render 函数。结合响应式系统,在应用程序状态改变时,Vue 能够智能地找出重新渲染的最小数量的组件,并应用最少量的 DOM 操作。
如果你熟悉虚拟 DOM 的概念,并且倾向于使用原生 JavaScript,还可以不使用模板,而是直接编写 render 函数,具备可选的 JSX 语法支持。
插值(Interpolations)
文本(Text)
使用 “mustache” 语法(双花括号)的文本插值(text interpolation),也可以通过使用 v-once 指令,执行一次性插值,也就是说,在数据改变时,插值内容不会随之更新。但是请牢记,这也将影响到同一节点上的其他所有绑定。
原始 HTML(Raw HTML) (不建议使用)
属性(Attributes)
使用 JavaScript 表达式
有个限制是,每个绑定都只能包含单个表达式。
computed 属性和 watcher
你可以像绑定普通属性一样,将 computed 属性的数据,绑定(data-bind)到模板中的表达式上。Vue 能够意识到 computed属性
依赖于 实例的数据,也会在 示例数据修改后,更新所有依赖于 computed属性
的数据绑定。最恰到好处的部分是,我们是通过声明式来创建这种依赖关系:computed 属性的 getter 函数并无副作用(side effect),因此也更加易于测试和理解。
computed 缓存 vs method 方法
computed 属性会基于它所依赖的数据进行缓存。每个 computed 属性,只有在它所依赖的数据发生变化时,才会重新取值(re-evaluate)。这就意味着,只要 computed属性函数依赖的实例数据
没有发生变化,多次访问 computed 属性,将会立刻返回之前计算过的结果,而不必每次都重新执行函数。
为什么我们需要将依赖数据缓存起来?假设一种场景,我们有一个高性能开销(expensive)的 computed 属性 A,在 computed 属性的 getter 函数内部,需要遍历循环一个巨大数组,并进行大量计算。然后还有其他 computed 属性直接或间接依赖于 A。如果没有缓存,我们将不可避免地多次执行 A 的 getter 函数,这远多余实际需要执行的次数!然而在某些场景下,你可能不希望有缓存,请使用 method 方法替代。
computed 属性和 watch 属性
更推荐的方式是,使用 computed 属性,而不是命令式(imperative)的 watch
回调函数。例如:
computed版本
var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar' }, computed: { fullName: function () { return this.firstName + ' ' + this.lastName //需要一个返回值加入到实例数据或者修改实例数据 } //computed属性会检测其依赖的实例数据 } })
watch版本
var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar', fullName: 'Foo Bar' //watch属性,检测已经存在的属性数据,不会自动返回为新实例属性数据,所以要先声明 }, watch: { firstName: function (val) { //观测发生改变的属性对象,而不观测其依赖的实例数据,所以需要分为两个函数实现功能。 this.fullName = val + ' ' + this.lastName }, lastName: function (val) { this.fullName = this.firstName + ' ' + val } } })
对比 computed 属性实现的版本,以上代码是命令式和重复的。
computed 属性中设置 setter
computed 属性默认只设置 getter 函数,不过在需要时,还可以提供 setter 函数:
var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar', fullName: 'Foo Bar' }, computed: { fullName: { // getter 函数 get: function () { //get函数观测对象依赖,并返回变化 return this.firstName + ' ' + this.lastName }, // setter 函数 set: function (newValue) { //set函数观测对象,让你可以在computed中更改对象依赖的数据 var names = newValue.split(' ') this.firstName = names[0] this.lastName = names[names.length - 1] } } } })
现在当你运行 vm.fullName = 'John Doe'
,将会调用 setter,然后会对应更新 vm.firstName
和 vm.lastName
。
watcher
虽然在大多数情况下,更适合使用 computed 属性,然而有些时候,还是需要一个自定义 watcher。当你需要在数据变化响应时,执行异步操作,或高性能消耗的操作,自定义 watcher 的方式就会很有帮助。
<div id="watch-example"> <p> 问一个答案是 yes/no 的问题: <input v-model="question"> //v-modle会将input输入值传给question </p> <p>{{ answer }}</p> </div>
<!-- 对于 ajax 库(ajax libraries)和通用工具方法的集合(collections of general-purpose utility methods)来说, --> <!-- 由于已经存在大量与其相关的生态系统, --> <!-- 这也可以使你自由随意地选择自己最熟悉的。 --> <script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script> <script> var watchExampleVM = new Vue({ el: '#watch-example', data: { question: '', answer: '你要先提出问题,我才能给你答案!' }, watch: { //这种需求,不是更新观测对象(computed.get)或者更新其依赖(computed.set), 而是观测变化去做与数据无关的操作,computed无法实现 // 只要 question 发生改变,此函数就会执行 question: function (newQuestion, oldQuestion) { //不是很懂两个参数,但我想可能这是watch能监测到的改变之前和之后的对象, this.answer = '等待输入停止……' this.getAnswer() } }, methods: { // _.debounce 是由 lodash 提供的函数, // 在运行特别消耗性能的操作时, // 可以使用 _.debounce 来限制频率。 // 在下面这种场景中,我们需要限制访问 yesno.wtf/api 的频率, // 等到用户输入完毕之后,ajax 请求才会发出。 getAnswer: _.debounce( function () { if (this.question.indexOf('?') === -1) { this.answer = '问题通常需要包含一个中文问号。;-)' return } this.answer = '思考中……' var vm = this //ajax请求中的this可能没有指向实例,所以在请求之前先把实例对象this赋值给变量vm axios.get('https://yesno.wtf/api') .then(function (response) { vm.answer = _.capitalize(response.data.answer) }) .catch(function (error) { vm.answer = '错误!API 无法处理。' + error }) }, // 这是用户停止输入操作后所等待的毫秒数。 // (译者注:500毫秒之内,用户继续输入,则重新计时) 500 ) } }) </script>
class 和 style 绑定
与 HTML 的 class 绑定(Binding HTML Classes)
(内联、外部引用)这两种方式的渲染结果相同。我们还可以将 class 和 style 与某个 computed 属性绑定在一起,此 computed 属性所对应的 getter 函数需要返回一个对象。这是一种常用且强大的用法
<div v-bind:class="classObject"></div> //简写 :class="classObject"
data: { isActive: true, error: null }, computed: { //通过computed属性控制classObjecr对其它class的影响 classObject: function () { //如果classObject属性去除了,先前computed return的样式会恢复吗? return { active: this.isActive && !this.error, 'text-danger': this.error && this.error.type === 'fatal' } } }
我们可以向 v-bind:class
传入一个数组,来与 class 列表对应:
<div v-bind:class="[activeClass, errorClass]"></div>
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
//这里会直接添加 errorClass
,但是只有在 isActive
值是 truthy 时,才会添加 activeClass
。
<div v-bind:class="[{ active: isActive }, errorClass]"></div> //这里使用对象语法比三元运算更加简洁
在组件中使用
当你在自定义组件中使用 class
属性,这些 class 会被添加到组件的根元素上。根元素上已经存在的 class 不会被覆盖。
与内联 style 绑定(Binding Inline Styles)
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
v-bind:style
的对象语法,通常也会和 computed 属性结合使用,此 computed 属性所对应的 getter 函数需要返回一个对象。
<div v-bind:style="[baseStyles, overridingStyles]"></div>
从 2.3.0+ 起,你可以为每个 style 属性提供一个含有多个(前缀)值的数组,例如:
<div v-bind:style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
根据条件进行渲染
v-else
元素必须紧跟在 v-if
或 v-else-if
元素之后 - 否则无法识别它。
使用 key
控制元素是否可复用
Vue 会尽可能高效地渲染元素,通常会复用已渲染元素,而不是从头重新渲染。这样的实现方式,除了有助于使 Vue 变得非常快之外,还具有一些额外的优势。
但是这样有时并不符合实际需求,所以 Vue 为如下所述的情况提供了一种方式:“这两个元素是完全独立的 - 请不要复用它们”。那就是为它们添加一个具有不同值的 key
属性。
具有 v-show
的元素会始终渲染并保留在 DOM 中。v-show
只会切换元素的 display
这个 CSS 属性。
v-if
是“真实”的条件渲染,因为它会确保条件块(conditional block)在切换的过程中,完整地销毁(destroy)和重新创建(re-create)条件块内的事件监听器和子组件。
v-if
是惰性的(lazy):如果在初始渲染时条件为 false,它不会执行任何操作 - 在条件第一次变为 true 时,才开始渲染条件块。
通常来说,v-if
在切换时有更高的性能开销,而 v-show
在初始渲染时有更高的性能开销。因此,如果需要频繁切换,推荐使用 v-show
,如果条件在运行时改变的可能性较少,推荐使用 v-if
。
列表渲染
使用 v-for
遍历数组生成元素
在 v-for
代码块中,我们可以完全地访问父级作用域下的属性。v-for
还支持可选的第二个参数,作为当前项的索引。
使用 v-for
遍历对象
还可以提供第二个参数,作为对象的键名(key,然后第三个参数作为索引(index):
<div v-for="(value, key, index) in object"> {{ index }}. {{ key }}: {{ value }} </div>
在遍历一个对象时,是按照 Object.keys()
得出 key 的枚举顺序来遍历,无法保证在所有 JavaScript 引擎实现中完全一致。
推荐,在使用 v-for
时,尽可能提供一个 key
,除非迭代的 DOM 内容足够简单,或者你是故意依赖于默认行为来获得性能提升。
数组变化检测(Array Change Detection)
变化数组方法(Mutation Methods)
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
替换一个数组(Replacing an Array)
例如 filter()
, concat()
和 slice()
你可能会认为这将导致 Vue 丢弃现有 DOM 并重新渲染(re-render)整个列表 - 幸运的是,情况并非如此。Vue 实现了一些智能启发式方法(smart heuristic)来最大化 DOM 元素重用(reuse),因此将一个数组替换为包含重叠对象的另一个数组,会是一种非常高效的操作。
注意事项(Caveats)
由于 JavaScript 的限制,Vue 无法检测到以下数组变动:
- 当你使用索引直接设置一项时,例如
vm.items[indexOfItem] = newValue
- 当你修改数组长度时,例如
vm.items.length = newLength
为了解决第 1 个问题,以下方式都可以实现相同的效果,但是却可以通过响应式系统出发状态更新:
// Vue.set Vue.set(vm.items, indexOfItem, newValue) // Array.prototype.splice vm.items.splice(indexOfItem, 1, newValue) //你还可以使用 vm.$set 实例方法,这也是全局 Vue.set 方法的别名: vm.$set(vm.items, indexOfItem, newValue)
为了解决第 2 个问题,你可以使用 splice
:
vm.items.splice(newLength)
有时,你想要向已经存在的对象上添加一些新的属性,例如使用 Object.assign()
或 _.extend()
方法。在这种情况下,应该创建一个新的对象,这个对象同时具有两个对象的所有属性,因此,改为: //不是很理解
Object.assign(vm.userProfile, { age: 27, favoriteColor: 'Vue Green' }) //可以通过如下方式,添加新的响应式属性: vm.userProfile = Object.assign({}, vm.userProfile, { age: 27, favoriteColor: 'Vue Green' })
显示过滤/排序结果(Displaying Filtered/Sorted Results)
有时,我们想要显示一个数组过滤或排序后(filtered or sorted)的副本,而不是实际改变或重置原始数据。在这种情况下,可以创建一个返回过滤或排序数组的计算属性。
<li v-for="n in evenNumbers">{{ n }}</li>
data: { numbers: [ 1, 2, 3, 4, 5 ] }, computed: { evenNumbers: function () { return this.numbers.filter(function (number) { return number % 2 === 0 }) } }
在计算属性不适用的情况下(例如,在嵌套的 v-for
循环内),可以使用一个 method 方法: //不是很理解红色部分,参考上面循环,思考如果外部再套一层循环的computed结果
data: { numbers: [ 1, 2, 3, 4, 5 ] }, methods: { even: function (numbers) { return numbers.filter(function (number) { return number % 2 === 0 }) } }
使用 v-for
在整数值范围内迭代
<div> <span v-for="n in 10">{{ n }}</span> </div>
带有 v-if
的 v-for
当它们都处于同一节点时,v-for
的优先级高于 v-if
。这意味着,v-if
将分别在循环中的每次迭代上运行。当你只想将某些项渲染为节点时,这会非常有用,如下:
<li v-for="todo in todos" v-if="!todo.isComplete"> {{ todo }} </li>
如果你的意图与此相反,是根据条件跳过执行循环,可以将 v-if
放置于包裹元素上(或放置于 <template>
上)。例如:
<ul v-if="todos.length"> <li v-for="todo in todos"> {{ todo }} </li> </ul> <p v-else>No todos left!</p>
使用 v-for
遍历组件
现在,在 2.2.0+ 版本,当对组件使用 v-for
时,必须设置 key
属性。
无法自动向组件中传入数据,这是因为组件有自己的独立作用域。为了将组件外部的迭代数据传入组件,我们还需要额外使用 props:
<my-component v-for="(item, index) in items" v-bind:item="item" v-bind:index="index" v-bind:key="item.id" ></my-component>
没有通过 v-for 将 item
自动注入到组件中的原因是,一旦自动注入,就会使得组件与 v-for
指令的运行方式紧密耦合(tightly coupled)在一起。通过显式声明组件数据来源,可以使得组件可重用于其他场景。
<div id="todo-list-example"> <input v-model="newTodoText" v-on:keyup.enter="addNewTodo" placeholder="Add a todo" > <ul> <li is="todo-item" //注意is="todo-item"
属性。这在 DOM 模板中是必需的,因为在<ul>
中,只有<li>
是有效元素。这与调用<todo-item>
的实际结果相同,但是却可以解决浏览器潜在的解析错误。 v-for="(todo, index) in todos" v-bind:key="todo.id" v-bind:title="todo.title" v-on:remove="todos.splice(index, 1)" ></li> </ul> </div>
事件处理
监听事件
我们可以使用 v-on
指令监听 DOM 事件,并在事件被触发时执行一些 JavaScript 代码。
由于许多事件处理器的逻辑很复杂,所以把 JavaScript 代码都保存在 v-on
属性的值中是不可行的做法。这就是为什么 v-on
还可以接收要调用的方法名。
在行内语句的事件处理器中,有时我们也需要访问原始 DOM 事件对象。可以使用特殊的 $event
变量将它传递给一个方法:
<button v-on:click="warn('Form cannot be submitted yet.', $event)"> Submit </button> // ... methods: { warn: function (message, event) { // 现在,我们可以访问原始事件对象 if (event) event.preventDefault() alert(message) } }
事件修饰符(Event Modifiers)
尽管我们可以在 methods 中轻松实现这点,但更好的方式是:methods 只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
Vue.js 为 v-on
提供了事件修饰符。回想一下,修饰符是以点(.)开头的指令后缀来表示。
<!-- 停止点击事件冒泡 --> <a v-on:click.stop="doThis"></a> <!-- 提交事件不再重新载入页面 --> <form v-on:submit.prevent="onSubmit"></form> <!-- 修饰符可以链式调用 --> <a v-on:click.stop.prevent="doThat"></a> <!-- 只有修饰符 --> <form v-on:submit.prevent></form> <!-- 添加事件监听器时,使用事件捕获模式 --> <!-- 也就是说,内部元素触发的事件先在此处处理,然后才交给内部元素进行处理 --> //想不到实际运用场景 <div v-on:click.capture="doThis">...</div> <!-- 只有在 event.target 是元素自身时,才触发处理函数。 --> <!-- 也就是说,event.target 是子元素时,不触发处理函数 --> <div v-on:click.self="doThat">...</div>
<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>
<!-- 滚动事件的默认行为(滚动)将立即发生, -->
<!-- 而不是等待 `onScroll` 完成后才发生, -->
<!-- 以防在滚动事件的处理程序中含有 `event.preventDefault()` 调用 -->
<div v-on:scroll.passive="onScroll">...</div> //.passive
修饰符对于提高移动设备的性能尤其有用。
使用修饰符时的顺序会产生一些影响,因为相关的代码会以相同的顺序生成。所以,使用 v-on:click.prevent.self
会阻止所有点击,而 v-on:click.self.prevent
只阻止元素自身的点击。
不要将 .passive
和 .prevent
放在一起使用,因为 .prevent
将被忽略,并且浏览器可能会显示一条警告。记住,.passive
会向浏览器传达的意思是,你并不希望阻止事件的默认行为。
按键修饰符(Key Modifiers)
<!-- 和上面的示例相同 --> <input v-on:keyup.enter="submit"> <!-- 也可用于简写语法 --> <input @keyup.enter="submit">
这里列出所有的按键修饰符别名:
.enter
.tab
.delete
(捕获“删除”和“退格”按键).esc
.space
.up
.down
.left
.right
还可以自定义按键修饰符别名,通过全局 config.keyCodes
对象设置:
// 可以使用 `v-on:keyup.f1` Vue.config.keyCodes.f1 = 112
自动对应按键修饰符(Automatic Key Modifers)
也可以直接使用 KeyboardEvent.key
暴露出来的所有合法按键名作为修饰符,但是要将它们转换为串联式命名(kebab-case):
<input @keyup.page-down="onPageDown">
系统辅助按键(System Modifier Keys)
仅在以下修饰符对应的按键被按下时,才会触发鼠标或键盘事件监听器:
.ctrl
.alt
.shift
.meta
<!-- Alt + C --> <input @keyup.alt.67="clear"> <!-- Ctrl + Click --> <div @click.ctrl="doSomething">做一些操作</div>
.exact
修饰符(.exact Modifier)
.exact
修饰符可以控制触发事件所需的系统辅助按键的准确组合。
<!-- 如果 Alt 键或 Shift 键与 Ctrl 键同时按下,也会触发事件 --> <button @click.ctrl="onClick">A</button> <!-- 只在 Ctrl 按键按下,其他按键未按下时,触发事件 --> <button @click.ctrl.exact="onCtrlClick">A</button> <!-- 只在系统辅助按键按下时,触发事件 --> <button @click.exact="onClick">A</button>
鼠标按键修饰符(Mouse Button Modifiers)
.left
.right
.middle
这些修饰符会限制处理函数,仅响应特定的鼠标按键触发的事件。
表单 input 绑定
可以通过使用 v-model
指令,在表单 input 和 textarea 元素上创建双向数据绑定。v-model
指令可以根据 input 的 type 类型,自动地以正确的方式更新元素。虽然略显神奇,然而本质上 v-model
不过是「通过监听用户的 input 事件来更新数据」的语法糖,以及对一些边界情况做特殊处理。
v-model
会忽略所有表单元素中 value
, checked
或 selected
属性上初始设置的值,而总是将 Vue 实例中的 data 作为真实数据来源。因此你应该在 JavaScript 端的组件 data
选项中声明这些初始值,而不是 HTML 端。
与 value 属性绑定(待补充)
修饰符(modifiers)
.lazy
默认情况下,v-model
会在每次 input
事件触发之后,将数据同步至 input 元素中(除了上述提到的输入法组合文字时不会)。可以添加 lazy
修饰符,从而转为在触发 change
事件后同步。
.number
如果想要将用户的输入,自动转换为 Number 类型(译注:如果转换结果为 NaN 则返回字符串类型的输入值),可以在 v-model
之后添加一个 number
修饰符,来处理输入值:
这通常很有用,因为即使是在 type="number"
时,HTML 中 input 元素也总是返回一个字符串类型的值。
.trim
如果想要将用户的输入,自动过滤掉首尾空格,可以在 v-model
之后添加一个 trim
修饰符,来处理输入值: