ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
这个章节通过实例,基本完整的描述了vue.js的核心技术。是一个快速入门的教程; ## 一、申明式渲染 Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统: 1、普通文本插值及指令绑定渲染 ``` <div id="app"> {{ message }} <span v-bind:title="titleMsg"> 鼠标悬停几秒钟查看此处动态绑定的提示信息! </span> </div> var app = new Vue({ el: '#app', data: { message: 'Hello Vue!', titleMsg:'页面加载于 ' + new Date().toLocaleString() } }) ``` ![](https://img.kancloud.cn/0f/f4/0ff43d5eda5b8799b91e52919ff9b48f_699x73.png) 2、条件 ``` <div id="app"> <p v-if="seen">现在你看到我了</p> </div> var app = new Vue({ el: '#app', data: { seen: true } }) ``` ![](https://img.kancloud.cn/51/4f/514f52acbc6533960dbda57da39493a4_701x69.png) 3、循环 ``` <div id="app"> <ol> <li v-for="todo in todos"> {{ todo.text }} </li> </ol> </div> var app = new Vue({ el: '#app', data: { todos: [ { text: '学习 JavaScript' }, { text: '学习 Vue' }, { text: '整个牛项目' } ] } }) ``` ![](https://img.kancloud.cn/41/2b/412b74afb957d958893c112bcc7fed81_696x123.png) ## 二、用户输入 为了让用户和你的应用进行交互。 1、我们可以用 v-on 指令添加一个事件监听器,通过它调用在 Vue 实例中定义的方法: ``` <div id="app"> <p>{{ message }}</p> <button v-on:click="reverseMessage">反转消息</button> </div> var app = new Vue({ el: '#app', data: { message: 'Hello Vue.js!' }, methods: { reverseMessage: function () { this.message = this.message.split('').reverse().join('') } } }) ``` ![](https://img.kancloud.cn/a6/12/a6128fbf4ce4943d9709967ce48fb394_699x99.png) ![](https://img.kancloud.cn/f9/ee/f9eea61a08d44cc4a79b9ab7112a4847_700x95.png) 2、Vue 还提供了 v-model 指令,它能轻松实现表单输入和应用状态之间的双向绑定; ``` <div id="app"> <p>{{ message }}</p> <input v-model="message"> </div> var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } }) ``` ![](https://img.kancloud.cn/d8/09/d809ba81968177f96258247d76ca0c3a_699x106.png) ## 三、组件化 几乎任意类型的应用界面都可以抽象为一个组件树,组件是可复用的 Vue 实例; ![](https://img.kancloud.cn/59/f9/59f9c7516cbd2ed27137b3341b05c800_1406x544.png) 在 Vue 里,一个组件本质上是一个拥有预定义选项的一个 Vue 实例。 1、注册一个简单的组件 // 定义名为 todo-item 的新组件 ``` Vue.component('todo-item', { template: '<li>这是个待办项</li>' }) var app = new Vue(...) ``` 2、现在你可以用它构建另一个组件模板: ``` <ol> <!-- 创建一个 todo-item 组件的实例 --> <todo-item></todo-item> </ol> ``` 3、这样会为每个待办项渲染同样的文本,我们一般都需要能从父组件将数据传到子组件才对。修改一下该组件的定义,使之能够接受一个prop(子单元通过 prop 接口与父单元进行了良好的解耦): ``` Vue.component('todo-item', { // todo-item 组件现在接受一个 // "prop",类似于一个自定义 attribute。 // 这个 prop 名为 todo。 props: ['todo'], template: '<li>{{ todo.text }}</li>' }) ``` 现在,我们可以使用 v-bind 指令将待办项传到循环输出的每个组件中: ``` <div id="app-7"> <ol> <!-- 现在我们为每个 todo-item 提供 todo 对象 todo 对象是变量,即其内容可以是动态的。 基于采用v-for,所以需要为每个组件提供一个“key”. --> <todo-item v-for="item in groceryList" v-bind:todo="item" v-bind:key="item.id" ></todo-item> </ol> </div> ``` ``` Vue.component('todo-item', { props: ['todo'], template: '<li>{{ todo.text }}</li>' }) var app7 = new Vue({ el: '#app-7', data: { groceryList: [ { id: 0, text: '蔬菜' }, { id: 1, text: '奶酪' }, { id: 2, text: '随便其它什么人吃的东西' } ] } }) ``` ![](https://img.kancloud.cn/c5/30/c5303d159189b124c278603303301326_696x125.png) ## 四、Vue实例 每个 Vue 应用都是通过用`Vue`函数创建一个新的**Vue 实例**开始的,所有的 Vue 组件都是 Vue 实例,并且接受相同的选项对象 (一些根实例特有的选项除外); 1、数据与方法 当一个 Vue 实例被创建时,它将 data 对象中的所有的属性加入到 Vue 的响应式系统中。当这些属性的值发生改变时,视图将会产生“响应”,即匹配更新为新的值。 ``` // 我们的数据对象 var data = { a: 1 } // 该对象被加入到一个 Vue 实例中 var vm = new Vue({ data: data }) // 获得这个实例上的属性 // 返回源数据中对应的字段 vm.a == data.a // => true // 设置属性也会影响到原始数据 vm.a = 2 data.a // => 2 // ……反之亦然 data.a = 3 vm.a // => 3 ``` 当这些数据改变时,视图会进行重渲染。值得注意的是只有当实例被创建时就已经存在于 data 中的属性才是响应式的。也就是说如果你添加一个新的属性,比如: ``` vm.b = 'hi' ``` 那么对`b`的改动将不会触发任何视图的更新。因此,如果你知道你会在晚些时候需要一个属性,但是一开始它为空或不存在,那么你也需要定义它,并设置一些初始值。 2、实例生命周期钩子 每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。 比如 created 钩子可以用来在一个实例被创建之后执行代码: ``` new Vue({ data: { a: 1 }, created: function () { // `this` 指向 vm 实例 console.log('a is: ' + this.a) } }) // => "a is: 1" ``` 也有一些其它的钩子,在实例生命周期的不同阶段被调用,如 mounted、updated 和 destroyed。 >[danger] > 1、生命周期钩子的 this 上下文指向调用它的 Vue 实例。 > 2、不要在选项属性或回调上使用箭头函数,比如 created: () => console.log(this.a) 或 vm.$watch('a', newValue => this.myMethod())。因为箭头函数并没有 this,this 会作为变量一直向上级词法作用域查找,直至找到为止,经常导致 Uncaught TypeError: Cannot read property of undefined 或 Uncaught TypeError: this.myMethod is not a function 之类的错误。 > 3、生命周期图示 ![](https://img.kancloud.cn/d0/91/d0917d9b7c801ab541557b4547c95a71_1200x3039.png) ## 五、页面模板 Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。所有 Vue.js 的模板都是合法的 HTML ,所以能被遵循规范的浏览器和 HTML 解析器解析。在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。 1、插值 数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值: ``` <span>Message: {{ msg }}</span> ``` 2、指令 指令 (Directives) 是带有 v- 前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 (v-for 是例外情况)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。 | 元素 | 说明 | 实例| | --- | --- |--- | | 参数 | 一些指令能够接收一个“参数”,在指令名称之后以冒号表示。|\<a v-bind:href=url>...\</a>,在这里href是参数,告知v-bind指令将该元素的href的attribute 与表达式url的值绑定 | | 动态参数 |可以用方括号括起来的 JavaScript 表达式作为一个指令的参数| \<a v-bind:[attributeName]="url"> ... \</a>,这里的attributeName会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用 | | 修饰符 | 是以半角句号.指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。 |\<form v-on:submit.prevent="onSubmit">...\</form>| ## 六、计算属性 模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如: ``` <div id="example"> {{ message.split('').reverse().join('') }} </div> ``` 在这个地方,模板不再是简单的声明式逻辑。而是针对变量message做了一些算法处理,当你想要在模板中多次引用该字符串时,就比较麻烦。这里,1、vue可以采用计算属性来解决; ``` <div id="example"> <p>Original message: "{{ message }}"</p> <p>Computed reversed message: "{{ reversedMessage }}"</p> </div> var vm = new Vue({ el: '#example', data: { message: 'Hello' }, computed: { // 计算属性的 getter reversedMessage: function () { // `this` 指向 vm 实例 return this.message.split('').reverse().join('') } } }) //结果 Original message: "Hello" Computed reversed message: "olleH" ``` 2、组件方法实现 你可能已经注意到我们可以通过在表达式中调用方法来达到同样的效果: ``` <p>Reversed message: "{{ reversedMessage() }}"</p> ``` ``` // 在组件中 methods: { reversedMessage: function () { return this.message.split('').reverse().join('') } } ``` >[danger] 我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是**计算属性是基于它们的响应式依赖进行缓存的**。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要`message`还没有发生改变,多次访问`reversedMessage`计算属性会立即返回之前的计算结果,而不必再次执行函数。 计算属性默认只有 getter ,不过在需要时你也可以提供一个 setter : ``` // ... computed: { fullName: { // getter get: function () { return this.firstName + ' ' + this.lastName }, // setter set: function (newValue) { var names = newValue.split(' ') this.firstName = names[0] this.lastName = names[names.length - 1] } } } // ... ``` 现在再运行 vm.fullName = 'John Doe' 时,setter 会被调用,vm.firstName 和 vm.lastName 也会相应地被更新。 ## 七、侦听器 虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。 ``` <div id="watch-example"> <p> Ask a yes/no question: <input v-model="question"> </p> <p>{{ answer }}</p> </div> ``` ``` <!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 --> <!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 --> <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: 'I cannot give you an answer until you ask a question!' }, watch: { // 如果 `question` 发生改变,这个函数就会运行 question: function (newQuestion, oldQuestion) { this.answer = 'Waiting for you to stop typing...' this.debouncedGetAnswer() } }, created: function () { // `_.debounce` 是一个通过 Lodash 限制操作频率的函数。 // 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率 // AJAX 请求直到用户输入完毕才会发出。想要了解更多关于 // `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识, // 请参考:https://lodash.com/docs#debounce this.debouncedGetAnswer = _.debounce(this.getAnswer, 500) }, methods: { getAnswer: function () { if (this.question.indexOf('?') === -1) { this.answer = 'Questions usually contain a question mark. ;-)' return } this.answer = 'Thinking...' var vm = this axios.get('https://yesno.wtf/api') .then(function (response) { vm.answer = _.capitalize(response.data.answer) }) .catch(function (error) { vm.answer = 'Error! Could not reach the API. ' + error }) } } }) </script> ```