精华内容
下载资源
问答
  • 01. 什么是 VueVue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架,是当下很火的一个 JavaScript MVVM 库,是以 数据驱动和组件化 的思想构建的。MVVM 模式简述下图不仅概括了 MVVM 模式 (Model...

    752f69631b95fd4622161f71051b0f86.png

    3d550ae8a8e644a71b0d11b0fb7b87a6.png

    01. 什么是 Vue

    3869f87e3b89c90a4e96777686c24302.png

    Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架,是当下很火的一个 JavaScript MVVM 库,是以 数据驱动和组件化 的思想构建的。

    MVVM 模式简述

    下图不仅概括了 MVVM 模式 (Model-View-ViewModel),还描述了在 Vue.js 中 ViewModel 是如何和 View 以及 Model 进行交互的。

    24cd74e8d9e3cae6e84ec2e29644b7cb.png

    ViewModel 是 Vue.js 的核心,它是一个 Vue 实例。Vue 实例是作用于某一个 HTML 元素上的,这个元素可以是 HTML 的 body 元素,也可以是指定了 id 的某个元素。

    当创建了 ViewModel 后,双向绑定是如何达成的呢?

    首先,我们将上图中的 DOM ListenersData Bindings 看作两个工具,它们是实现双向绑定的关键。

    • 从 View 侧看,ViewModel 中的 DOM Listeners 工具会帮我们监测页面上DOM元素的变化,如果有变化,则更改Model中的数据;
    • 从 Model 侧看,当我们更新 Model 中的数据时,Data Bindings 工具会帮我们更新页面中的 DOM 元素。

    库和框架的区别

    2e129bc24be558ed1c7e9213c77a6ca1.png

    在这里我们需要稍微注意一下前端 库(Library)框架(Framework) 的区别,它们的本质都是某人编写的,用于解决常见问题的 可复用代码 的集合。

    比如,你有一个处理字符串的程序,你为了保持代码的 DRY (Don't Repeat Yourself),你编写了如下可复用的功能代码:

    function getWords(str) {
       const words = str.split(' ');
       return words;
    }
    function createSentence(words) {
       const sentence = words.join(' ');
       return sentence;
    }
    

    恭喜你,你创建了一个 JavaScript 库!

    如果我们用 「构建房子」 来类比 「构建应用」 的话,那么 使用库 就像是 去宜家购物 一样,我已经有了一个家,现在我需要挑选自己喜欢的一些家具,以达到我自己满意的状态,这一切 都在我的控制范围之内;而 使用框架 就会像是已经有了一个 清装房,在已经规划好的蓝图和选择之中,我们的一些想法会显得十分地有限。

    Vue.js 本身只是一个 JavaScript 库,包括 React 也一样,只不过平时我们所说的 Vue 框架,是指包含 Router/ Vuex 等一系列组件之后融合的 一整套解决方案

    更加详细的解释如下:
    • 「库」 是一个封装好的特定的集合,提供给开发者使用,而且是特定于某一方面的集合(方法和函数),库没有控制权,控制权完全在于使用者本身;
    • 「框架」 顾名思义是一套架构,会基于自身的特点向用户提供一套比较完整的解决方案,如果使用者选定了一套框架,那么就需要根据框架本身做出一定的适应。

    02. 为什么使用 Vue?

    8ecd5afba972654a66c748577ddfab41.png

    说实话我个人非常喜欢 Vue。在我大学刚尝试学习 HTML + CSS + JavaScript 和 Bootstrap 融合之后,我就接触了 Vue,它对我来说这样的「前端小白」来说,几乎没有什么开发的门槛,很平滑地就得以过渡到 Vue 的使用中去。

    典型的 .vue 文件可以简单成如下的样子:(vue-tutorial/typical-case.html)

    <template>
        <!-- HTML 代码 -->
    </template>
    
    <script>
        // JavaScript 代码
    </script>
    
    <style>
        /* css 代码 */
    </style>

    bc11225f4d59b22c6a9005dbcd5f23e6.png
    Vue 从一开始的定位就是尽可能的降低前端开发的门槛,让更多的人能够更快地上手开发。———— 尤雨溪

    理由一:易上手、学习曲线平滑

    就像上面的典型 .vue 文件的展示一样,在 Vue 中,一切都很自然,例如我们使用 Vue 来构建我们的 Hello World 程序:(vue-tutorial/hello-vue.html)

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Hello Vue!</title>
    </head>
    <body>
    <div id="app">
        {{ message }}
    </div>
    
    <!-- 开发环境版本,包含了有帮助的命令行警告 -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
        // 创建一个 Vue 实例或者说是 VieModel 实例
        var app = new Vue({
            el: '#app',
            data: {
                message: 'Hello Vue!'
            }
        })
    </script>
    </body>
    </html>

    可以看到几乎没有多余的部分,只是在创建 Vue 实例时,把 idapp 的对象 (此处为一个 div) 绑定到了 Vue 实例中而已。

    理由二:文档友好

    由于 Vue 是国人编写的,所以在官网中有完整的中文文档可供开发者参考,并且借由尤大大出色的文笔,非常地清晰易懂,相信看过的朋友会和我有一样的感受:

    ae8f32e93c4920dec24afcd69f5a44da.png

    理由三:MVVM 天然的双向绑定

    Vue.js 是一个提供了 MVVM 风格的双向数据绑定的 JavaScript 库,这就让我们能够专注于 View 层的开发,这种轻量级的框架让前端开发更加高效、便捷。

    例如,我们使用 v-model 来简单改造一下我们的 hello-vue.html 文件让它编程一个简单的双向绑定示例:(vue-tutorial/v-model-demo.html)

    <div id="app">
      <p>{{ message }}</p>
      <input type="text" v-model="message" />
    </div>

    message 绑定到文本框,当更改文本框的值的时候, <p>{{ message }}</p> 中的内容也会被更新:

    d17215a2147e3f7b4b218fa310c169c1.png

    反过来如果我们更改 message 的值的话,文本框的值也会被更新,我们可以在控制台中尝试一下:

    48a01d02a8c30f15187883bab80df9c6.png

    03. Vue 常用指令

    20dd8b27956e1f3967ac79fa3abe48f5.png

    上面我们已经实际体验了一个 Vue 的指令 v-model 了,在 Vue 中,指令都带有 v- 前缀,以表示它们是 Vue 提供的特殊的 attribute,它们会在渲染 DOM 时进行特殊的响应式行为。

    Vue 内置了一些常用的指令,接下来我们将依次来介绍:

    • v-ifv-else 条件渲染指令;
    • v-show 条件展示指令;
    • v-for 列表渲染指令
    • v-bind 条件绑定指令;
    • v-on 事件处理指令;

    v-if 和 v-else 条件渲染指令

    v-if 指令

    v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。例如:(vue-tutorial/v-if-demo)

    <p v-if="seen">现在你看到我了!</p>
    var app = new Vue({
        el: '#app',
        data: {
            seen: true
        }
    })

    页面会正确的显示「现在你看到我了!」这几个字。

    v-else 指令

    你也可以使用 v-else 来添加一个 "else 块" 来表达条件不满足时应该渲染的模块:

    <p v-if="seen">现在你看到我了!</p>
    <p v-else>Oh no!</p>
    var app = new Vue({
        el: '#app',
        data: {
            seen: false
        }
    })

    此时条件 seen 不满足,页面就会显示「Oh no!」的字样。

    v-else-if 指令

    这是 2.1.0 版本新增的指令,充当 v-if 的 "else-if 块",可以用来连续判断条件:

    <div v-if="type === 'A'">
      A
    </div>
    <div v-else-if="type === 'B'">
      B
    </div>
    <div v-else-if="type === 'C'">
      C
    </div>
    <div v-else>
      Not A/B/C
    </div>

    类似于 v-elsev-else-if 也必须紧跟在带 v-if 或者 v-else-if 的元素之后。

    v-show 条件展示指令

    另一个用于根据条件展示元素的选项是 v-show 指令。用法大致一样:

    <h1 v-show="ok">Hello!</h1>

    不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的 CSS 属性 display (条件不满足则把元素 display 属性设置为 none),而 v-if 则在条件不满足时直接不渲染出对象。

    v-if 与 v-show

    v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。

    v-if 也是 惰性 的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

    相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。

    一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。

    v-for 列表渲染指令

    我们可以用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。

    <ul id="example-1">
      <li v-for="item in items">
        {{ item.message }}
      </li>
    </ul>
    var example1 = new Vue({
      el: '#example-1',
      data: {
        items: [
          { message: 'Foo' },
          { message: 'Bar' }
        ]
      }
    })

    结果:

    • Foo
    • Bar
    注意:永远不要把 v-if 和 v-for 同时用在同一个元素上。因为当 Vue 处理指令时, v-forv-if 拥有更高的优先级,所以会导致错误,详细的技术细节可以 戳这里

    v-bind 条件绑定指令

    我们可以传给 v-bind:class 一个对象,以动态地切换 class:(也可以用缩写 : 来替代 v-bind 指令)

    <div v-bind:class="{ active: isActive }"></div>

    上面的语法表示 active 这个 class 存在与否将取决于数据属性 isActivetruthiness

    你可以在对象中传入更多属性来动态切换多个 class。此外,v-bind:class 指令也可以与普通的 class 属性共存。当有如下模板:

    <div
      class="static"
      v-bind:class="{ active: isActive, 'text-danger': hasError }"
    ></div>

    和如下 data:

    data: {
      isActive: true,
      hasError: false
    }
    

    结果渲染为:

    <div class="static active"></div>

    isActive 或者 hasError 变化时,class 列表将相应地更新。例如,如果 hasError 的值为 true,class 列表将变为 "static active text-danger"

    v-on 事件处理指令

    可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。(也可以用缩写 @ 来替代 v-on 指令)

    示例:

    <div id="example-1">
      <button v-on:click="counter += 1">Add 1</button>
      <p>The button above has been clicked {{ counter }} times.</p>
    </div>
    var example1 = new Vue({
      el: '#example-1',
      data: {
        counter: 0
      }
    })

    结果:

    4d6ca2a1e83d544d7e3fe5c7cff30086.png

    v-on 的事件修饰符

    在事件处理程序中调用 event.preventDefault()event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。

    为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。

    • .stop
    • .prevent
    • .capture
    • .self
    • .once
    • .passive
    <!-- 阻止单击事件继续传播 -->
    <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 是当前元素自身时触发处理函数 -->
    <!-- 即事件不是从内部元素触发的 -->
    <div v-on:click.self="doThat">...</div>
    另外事件处理还可以支持 按键码 (某一个键按下)系统修饰符 (键盘鼠标按下),可以参看 官方教程

    04. Todo-List 示例

    38ed6f42d7a8f2f9ae525423f3a25a71.png

    上面我们了解了一些基本的指令了,接下来我们实际动动手,来搭建一个简单的 TodoList demo 小程序。

    第一步:明确需求

    TodoList 想必大家都很熟悉,使用来记录我们接下来要做的一些事情的程序,最基本的功能有增加和删除:

    d25f0effe31c8143a07cbc2f0dd2e019.png

    很简单,可以看出我们只需要一个输入框 (用来记录将要保存的数据),一个按钮 (用来添加数据),和一个集合 (用来保存数据) 就差不多可以了,上手!

    第二步:创建好需要的 data

    先来创建好我们需要的数据 data:

    data: {
        todos: [{
                id: nextTodoId++,
                text: '写代码'
            },
            {
                id: nextTodoId++,
                text: '还是写代码'
            }
        ],
        newTodoText: ""
    }
    

    这里多定义了 id 属性是为了方便我们的删除操作。

    第三步:创建好对应的 HTML

    没有任何布局,就直接定义好我们所需要的组件就好了:

    <input type="text" v-model="newTodoText" />
    <button @click="addItem">添加</button>
    <ul>
        <li v-for="item in todos">
            <span>{{ item.text }}</span>
            <span><button @click="removeItem(item.id)">del</button></span>
        </li>
    </ul>

    没有任何的特别,只是里面包含了两个我们 未定义 的方法:addItemremoveItem 而已。

    第三步:定义并实现方法

    Vue 中的方法需要定义在 Vue 实例的 methods 关键字下面:

    methods: {
        addItem(key) {
            this.todos.push({
                id: nextTodoId,
                text: this.newTodoText
            })
            this.newTodoText = ""
        },
        removeItem(id) {
            this.todos = this.todos.filter(todo => {
                return todo.id !== id
            })
        }
    }
    

    这里数组的更新需要用到 push,另外删除时我们使用了一个 lambda 表达式来完成,删除时传入了一个要删除元素的 id,然后从数组中挑选出所有 不等于 这个 id 的元素重新赋值给原数组,这样就相当于是删除了元素了。

    展开全文
  • 作者:chinamasters 来源:segmentfault最近在深入研究vue源码,把学习过程中,看到的一些好玩的的函数方法收集起来做分享,希望对大家对深入学习js有所帮助。如果大家都能一眼看懂这些函数,说明技术还是不错的哦。...

    作者:chinamasters 来源:segmentfault

    最近在深入研究vue源码,把学习过程中,看到的一些好玩的的函数方法收集起来做分享,希望对大家对深入学习js有所帮助。如果大家都能一眼看懂这些函数,说明技术还是不错的哦。

    88af409ea64a34f970a8d18e43e04463.png

    1. 数据类型判断

    Object.prototype.toString.call()返回的数据格式为 [object Object]类型,然后用slice截取第8位到倒一位,得到结果为 Object

    var _toString = Object.prototype.toString; function toRawType (value) {   return _toString.call(value).slice(8, -1) } 

    运行结果测试

    toRawType({}) //  Object  toRawType([])  // Array     toRawType(true) // Boolean toRawType(undefined) // Undefined toRawType(null) // Null toRawType(function(){}) // Function 

    2. 利用闭包构造map缓存数据

    vue中判断我们写的组件名是不是html内置标签的时候,如果用数组类遍历那么将要循环很多次获取结果,如果把数组转为对象,把标签名设置为对象的key,那么不用依次遍历查找,只需要查找一次就能获取结果,提高了查找效率。

    function makeMap (str, expectsLowerCase) {     // 构建闭包集合map     var map = Object.create(null);     var list = str.split(',');     for (var i = 0; i < list.length; i++) {       map[list[i]] = true;     }     return expectsLowerCase       ? function (val) { return map[val.toLowerCase()]; }       : function (val) { return map[val]; } } // 利用闭包,每次判断是否是内置标签只需调用isHTMLTag var isHTMLTag = makeMap('html,body,base,head,link,meta,style,title') console.log('res', isHTMLTag('body')) // true 

    3. 二维数组扁平化

    vue中_createElement格式化传入的children的时候用到了simpleNormalizeChildren函数,原来是为了拍平数组,使二维数组扁平化,类似lodash中的flatten方法。

    // 先看lodash中的flatten _.flatten([1, [2, [3, [4]], 5]]) // 得到结果为  [1, 2, [3, [4]], 5]  // vue中 function simpleNormalizeChildren (children) {   for (var i = 0; i < children.length; i++) {     if (Array.isArray(children[i])) {       return Array.prototype.concat.apply([], children)     }   }   return children }  // es6中 等价于 function simpleNormalizeChildren (children) {    return [].concat(...children) } 

    4. 方法拦截

    vue中利用Object.defineProperty收集依赖,从而触发更新视图,但是数组却无法监测到数据的变化,但是为什么数组在使用push pop等方法的时候可以触发页面更新呢,那是因为vue内部拦截了这些方法。

    // 重写push等方法,然后再把原型指回原方法   var ARRAY_METHOD = [ 'push', 'pop', 'shift', 'unshift', 'reverse',  'sort', 'splice' ];   var array_methods = Object.create(Array.prototype);   ARRAY_METHOD.forEach(method => {     array_methods[method] = function () {       // 拦截方法       console.log('调用的是拦截的 ' + method + ' 方法,进行依赖收集');       return Array.prototype[method].apply(this, arguments);     }   }); 

    运行结果测试

    var arr = [1,2,3] arr.__proto__ = array_methods // 改变arr的原型 arr.unshift(6) // 打印结果: 调用的是拦截的 unshift 方法,进行依赖收集 

    5. 继承的实现

    vue中调用Vue.extend实例化组件,Vue.extend就是VueComponent构造函数,而VueComponent利用Object.create继承Vue,所以在平常开发中Vue 和 Vue.extend区别不是很大。这边主要学习用es5原生方法实现继承的,当然了,es6中 class类直接用extends继承。

    // 继承方法   function inheritPrototype(Son, Father) {    var prototype = Object.create(Father.prototype)    prototype.constructor = Son    // 把Father.prototype赋值给 Son.prototype    Son.prototype = prototype  }  function Father(name) {    this.name = name    this.arr = [1,2,3]  }  Father.prototype.getName = function() {    console.log(this.name)  }  function Son(name, age) {    Father.call(this, name)    this.age = age  }  inheritPrototype(Son, Father)  Son.prototype.getAge = function() {    console.log(this.age)  } 

    运行结果测试

    var son1 = new Son("AAA", 23) son1.getName()            //AAA son1.getAge()             //23 son1.arr.push(4)           console.log(son1.arr)     //1,2,3,4  var son2 = new Son("BBB", 24) son2.getName()            //BBB son2.getAge()             //24 console.log(son2.arr)     //1,2,3 

    6. 执行一次

    once 方法相对比较简单,直接利用闭包实现就好了

    function once (fn) {   var called = false;   return function () {     if (!called) {       called = true;       fn.apply(this, arguments);     }   } } 

    7. 浅拷贝

    简单的深拷贝我们可以用 JSON.stringify() 来实现,不过vue源码中的looseEqual 浅拷贝写的也很有意思,先类型判断再递归调用,总体也不难,学一下思路。

    function looseEqual (a, b) {   if (a === b) { return true }   var isObjectisObjectA = isObject(a);   var isObjectisObjectB = isObject(b);   if (isObjectA && isObjectB) {     try {       var isArrayA = Array.isArray(a);       var isArrayB = Array.isArray(b);       if (isArrayA && isArrayB) {         return a.length === b.length && a.every(function (e, i) {           return looseEqual(e, b[i])         })       } else if (!isArrayA && !isArrayB) {         var keysA = Object.keys(a);         var keysB = Object.keys(b);         return keysA.length === keysB.length && keysA.every(function (key) {           return looseEqual(a[key], b[key])         })       } else {         /* istanbul ignore next */         return false       }     } catch (e) {       /* istanbul ignore next */       return false     }   } else if (!isObjectA && !isObjectB) {     return String(a) === String(b)   } else {     return false   } } function isObject (obj) {   return obj !== null && typeof obj === 'object' } 
    展开全文
  • 下面先看看我们的需求列表组件quiList.vue本节我们主要要完成这样一个列表功能,每一行的列表是一个组件,列表内可能出现按钮组件或者箭头组件,点击按钮组件可以自定义事件,同时可以根据不同的参数来决定当前列表...

    接下来我们会详细分析下如何完成由多个组件组成一个复用组件的开发流程。

    下面先看看我们的需求

    列表组件quiList.vue

    本节我们主要要完成这样一个列表功能,每一行的列表是一个组件,列表内可能出现按钮组件或者箭头组件,点击按钮组件可以自定义事件,同时可以根据不同的参数来决定当前列表是带按钮的列表or带箭头的列表。

    38c7cd96f4338b7a070b59eaa68704d2.png

    首先看看quiList.vue

    //quiList.vue

    {{tipsText}}

    上面的知识点基本上就是我们之前学过的,只不过记住quiList本身是一个组件,而在这个组件里面,我们又引入了按钮组件quiButton,也就是组件内引用组件,实际上就是组件的嵌套,注意到这里:msg=msg的使用,这里冒号表示绑定的是一个变量msg,然后这个属性通过props暴露出去(本身在按钮中就暴露了msg给列表组件使用),借用下面一张图理解下:

    e5b0432378c3ca9e198674da712c30b8.png

    至于点击事件,也是我们之前学习过的事件的绑定。现在引入一个新问题,是否有一个参数,可以决定列表组件的右侧是放置按钮组件呢?还是箭头组件。

    动态组件

    Vue中提供了一些特定关键字:is和特定的结构来生成动态组件,让我们修改下script里面的内容先:

    首先我们先Import多一个箭头组件,在components中添加一个自定义标签‘qui-arrow’,注意到我们多了一个currentView的自定义属性,默认值是qui-btn,现在再看看template标签里面写什么:

    {{tipsText}}

    我们把qui-btn标签去掉了,取而代之的是一个component标签,这是Vue自带的一个标签,可以把它当作一个容器,这个容器可以用来装按钮,也可以用来装箭头。决定这个容器装的是哪个组件的关键代码在于:is="currentView",当currentView的值为qui-btn的时候,这个容器就是按钮组件,当它是qui-arrow的时候,就是箭头组件。而我们刚才给这个变量定义的默认值是qui-btn。

    keep-alive关键字保持这个组件在内存中是常驻的,由于动态组件可能需要动态切换,这样保持组件活跃可以减少组件变化时候的内存消耗。

    可以看到我们的component上还保留着按钮的点击事件和msg信息,这些没有关系,只要箭头组件中不出现同样的变量就不会发生冲突。

    使用列表组件的时候,只需要给暴露出来的currentView指定一个值,就可以决定右侧是按钮还是箭头了。注意最后一个qui-list上有一个ref的属性,这个属性代表组件集合,当页面中有很多组件的时候,可以通过几种方法来获取对应的某个组件的信息:

    console.log(this.$children[0].msg);//通过数组获取

    console.log(this.$refs.child1.msg);//通过对象集合获取

    其实关于动态组件,不一定要用:is+component来实现,在Vue中有一个指令叫做v-if / v-else / v-else-if,统称判断指令,配合展示指令v-show,可以根据指定的值来决定对应的组件是否应该展示,另外这种做法我不展示了,就当做一个作业吧,有兴趣的还是建议实战一下,毕竟我们也只是教大家入门学习,后面还是希望大家能够自己去扩展学习。

    生命周期

    这里简单讲一下什么是组件的生命周期,上面我们通过refs来获取组件对象的信息,那么在什么时候或者说哪个时机点去做这件事呢,组件从引用到调用到销毁(比较少操作)有以下几个关键回调函数:

    所以要想使用refs的内容,就需要在组件写入dom之后才能开始调用哦!

    我还需要学什么

    目前为止,我们三篇文章已经学了大部分的关于组件和路由的知识,当然这并不是Vue的全部,只是相对于其他的知识点,这些可以算是一个垫脚石,看懂了这些,对后面其他API的应用,帮助很大。下面我列举一些其他的,后续大家可以去官网查看资料的一些关键点,其实都不难,只要有一些小小的项目demo实践,你会发现Vue也不过如此。

    过渡

    过渡其实就是CSS3动画,transition这些,只是写CSS3变成好像在写JS一样,有点类似于greenSock的一些思想。

    指令

    目前为止我们学习了一些常用指令,像v-on,v-bind,v-for,还有几个常用的像刚才提到的判断指令和v-show指令,还有v-model指令(主要用于input等表单组件)。当知道指令作用的时候,学习起来其实并不难。

    Render

    渲染这个方法是我觉得应该用心去学习的,它可以方便我们写出更好的面向对象的组件,而学习它的成本在于这个接口更接近于原生JS代码的使用。如果有需要,后续也可以写一篇关于Render的文章。

    总结

    三篇系列文暂时在这里告一段落,有些童靴可能到这里还是觉得没有学会Vue,对不起,可能是我的标题太夸张了,也可能因为我的例子还不够清晰,文笔也还不好理解。不过没关系,回顾我们的学习历程,你可以按照这个知识点的学习过程,去找更多的文章,毕竟“熟读唐诗三百首,不会作诗也会吟”嘛。当然,学习过程中我们自己更多的练习和尝试才能锻炼巩固知识。至于浅入之后是浅出还是深出,还是要靠大家自己去定义了!

    展开全文
  • 响应式是Vue的最大特色之一。如果你不知道幕后情况,它也是最神秘的地方之一。例如,为什么它不能用于对象和数组,而不能用于诸如 localStorage 之类的其他东西?让我们回答这个问题,在解决这个问题时,让Vue响应式...

    5ad3a503fd9e1e7e5b14d7a001a6d1ab.png

    响应式是Vue的最大特色之一。如果你不知道幕后情况,它也是最神秘的地方之一。例如,为什么它不能用于对象和数组,而不能用于诸如 localStorage 之类的其他东西?

    让我们回答这个问题,在解决这个问题时,让Vue响应式与 localStorage 一起使用。

    如果运行以下代码,则会看到计数器显示为静态值,并且不会像我们期望的那样发生变化,这是因为setInterval在 localStorage 中更改了该值。

    new Vue({
      el: "#counter",
      data: () => ({
        counter: localStorage.getItem("counter")
      }),
      computed: {
        even() {
          return this.counter % 2 == 0;
        }
      },
      template: `<div>
        <div>Counter: {{ counter }}</div>
        <div>Counter is {{ even ? 'even' : 'odd' }}</div>
      </div>`
    });
    // some-other-file.js
    setInterval(() => {
      const counter = localStorage.getItem("counter");
      localStorage.setItem("counter", +counter + 1);
    }, 1000);
    

    尽管Vue实例中的 counter 属性是响应式的,但它不会因为我们更改了它在 localStorage 中的来源而更改。

    有多种解决方案,最好的也许是使用Vuex,并保持存储值与 localStorage 同步。但如果我们需要像本例中那样简单的东西呢?我们要深入了解一下Vue的响应式系统是如何工作的。

    Vue 中的响应式

    当Vue初始化组件实例时,它将观察data选项。这意味着它将遍历数据中的所有属性,并使用 Object.defineProperty 将它们转换为getter/setter。通过为每个属性设置自定义设置器,Vue可以知道属性何时发生更改,并且可以通知需要对更改做出反应的依赖者。它如何知道哪些依赖者依赖于一个属性?通过接入getters,它可以在计算的属性、观察者函数或渲染函数访问数据属性时进行注册。

    // core/instance/state.js
    function initData () {
      // ...
      observe(data)
    }
    // core/observer/index.js
    export function observe (value) {
      // ...
      new Observer(value)
      // ...
    }
    
    export class Observer {
      // ...
      constructor (value) {
        // ...
        this.walk(value)
      }
    
      walk (obj) {
        const keys = Object.keys(obj)
        for (let i = 0; i < keys.length; i++) {
          defineReactive(obj, keys[i])
        }
      }
    } 
    

    export function defineReactive (obj, key, ...) {
      const dep = new Dep()
      // ...
      Object.defineProperty(obj, key, {
        // ...
        get() {
          // ...
          dep.depend()
          // ...
        },
        set(newVal) {
          // ...
          dep.notify()
        }
      })
    }
    

    所以,为什么 localStorage 不响应?因为它不是具有属性的对象

    但是等一下,我们也不能用数组定义getter和setter,但Vue中的数组仍然是反应式的。这是因为数组在Vue中是一种特殊情况。为了拥有响应式的数组,Vue在后台重写了数组方法,并与Vue的响应式系统进行了修补。

    我们可以对 localStorage 做类似的事情吗?

    覆盖localStorage函数

    首先尝试通过覆盖localStorage方法来修复最初的示例,以跟踪哪些组件实例请求了localStorage项目。

    // LocalStorage项目键与依赖它的Vue实例列表之间的映射。
    const storeItemSubscribers = {};
    
    const getItem = window.localStorage.getItem;
    localStorage.getItem = (key, target) => {
      console.info("Getting", key);
    
      // 收集依赖的Vue实例
      if (!storeItemSubscribers[key]) storeItemSubscribers[key] = [];
      if (target) storeItemSubscribers[key].push(target);
    
      // 调用原始函数 
      return getItem.call(localStorage, key);
    };
    
    const setItem = window.localStorage.setItem;
    localStorage.setItem = (key, value) => {
      console.info("Setting", key, value);
    
      // 更新相关Vue实例中的值
      if (storeItemSubscribers[key]) {
        storeItemSubscribers[key].forEach((dep) => {
          if (dep.hasOwnProperty(key)) dep[key] = value;
        });
      }
    
      // 调用原始函数 
      setItem.call(localStorage, key, value);
    };
    new Vue({
      el: "#counter",
      data: function() {
        return {
          counter: localStorage.getItem("counter", this) // 我们现在需要传递“this”
        }
      },
      computed: {
        even() {
          return this.counter % 2 == 0;
        }
      },
      template: `<div>
        <div>Counter: {{ counter }}</div>
        <div>Counter is {{ even ? 'even' : 'odd' }}</div>
      </div>`
    });
    setInterval(() => {
      const counter = localStorage.getItem("counter");
      localStorage.setItem("counter", +counter + 1);
    }, 1000);
    

    在这个例子中,我们重新定义了 getItemsetItem,以便收集和通知依赖 localStorage 项目的组件。在新的 getItem 中,我们注意到哪个组件请求了哪个项目,在 setItems 中,我们联系所有请求该项目的组件,并重写它们的数据属性。

    为了使上面的代码工作,我们必须向 getItem 传递一个对组件实例的引用,这就改变了它的函数签名。我们也不能再使用箭头函数了,因为否则我们就不会有正确的 this 值。

    如果我们想做得更好,就必须更深入地挖掘。例如,我们如何在不显式传递依赖者的情况下跟踪它们?

    Vue如何收集依赖关系

    为了获得启发,我们可以回到Vue的响应式系统。我们之前曾看到,访问数据属性时,数据属性的 getter 将使调用者订阅该属性的进一步更改。但是它怎么知道是谁做的调用呢?当我们得到一个数据属性时,它的 getter 函数没有任何关于调用者是谁的输入。Getter函数没有输入,它怎么知道谁要注册为依赖者呢?

    每个数据属性维护一个需要在Dep类中进行响应的依赖项列表。如果我们在此类中进行更深入的研究,可以看到只要在注册依赖项时就已经在静态目标变量中定义了依赖项。这个目标是由一个非常神秘的Watche类确定的。实际上,当数据属性更改时,将实际通知这些观察程序,并且它们将启动组件的重新渲染或计算属性的重新计算。

    但是,他们又是谁?

    当Vue使 data 选项可观察时,它还会为每个计算出的属性函数以及所有watch函数(不应与Watcher类混为一谈)以及每个组件实例的render函数创建watcher。观察者就像这些函数的伴侣。他们主要做两件事:

    1. 当它们被创建时,它们会评估函数。这将触发依赖关系的集合。
    2. 当他们被通知他们所依赖的一个值发生变化时,他们会重新运行他们的函数。这将最终重新计算一个计算出的属性或重新渲染整个组件。

    在观察者调用其负责的函数之前,有一个重要的步骤发生了:他们将自己设置为Dep类中静态变量的目标。这样可以确保在访问响应式数据属性时将它们注册为从属。

    追踪谁调用了localStorage

    我们无法完全做到这一点,因为我们无法使用Vue的内部机制。但是,我们可以使用Vue的想法,即观察者可以在调用其负责的函数之前,将目标设置为静态属性。我们能否在调用 localStorage 之前设置对组件实例的引用?

    如果我们假设在设置 data 选项时调用了 localStorage,则可以将其插入 beforeCreatecreated 中。这两个挂钩在初始化data选项之前和之后都会被触发,因此我们可以设置一个目标变量,然后清除该变量,并引用当前组件实例(我们可以在生命周期挂钩中访问该实例)。然后,在我们的自定义获取器中,我们可以将该目标注册为依赖项。

    我们要做的最后一点是使这些生命周期挂钩成为我们所有组件的一部分,我们可以通过整个项目的全局混合来做到这一点。

    // LocalStorage项目键与依赖它的Vue实例列表之间的映射
    const storeItemSubscribers = {};
    
    // 当前正在初始化的Vue实例
    let target = undefined;
    
    const getItem = window.localStorage.getItem;
    localStorage.getItem = (key) => {
      console.info("Getting", key);
    
      // 收集依赖的Vue实例
      if (!storeItemSubscribers[key]) storeItemSubscribers[key] = [];
      if (target) storeItemSubscribers[key].push(target);
    
      // 调用原始函数 
      return getItem.call(localStorage, key);
    };
    
    const setItem = window.localStorage.setItem;
    localStorage.setItem = (key, value) => {
      console.info("Setting", key, value);
    
      // 更新相关Vue实例中的值
      if (storeItemSubscribers[key]) {
        storeItemSubscribers[key].forEach((dep) => {
          if (dep.hasOwnProperty(key)) dep[key] = value;
        });
      }
    
      // 调用原始函数 
      setItem.call(localStorage, key, value);
    };
    
    Vue.mixin({
      beforeCreate() {
        console.log("beforeCreate", this._uid);
        target = this;
      },
      created() {
        console.log("created", this._uid);
        target = undefined;
      }
    });
    

    现在,当我们运行第一个示例时,我们将获得一个计数器,该计数器每秒增加一个数字。

    new Vue({
      el: "#counter",
      data: () => ({
        counter: localStorage.getItem("counter")
      }),
      computed: {
        even() {
          return this.counter % 2 == 0;
        }
      },
      template: `<div class="component">
        <div>Counter: {{ counter }}</div>
        <div>Counter is {{ even ? 'even' : 'odd' }}</div>
      </div>`
    });
    setInterval(() => {
      const counter = localStorage.getItem("counter");
      localStorage.setItem("counter", +counter + 1);
    }, 1000);
    

    我们的思想实验结束

    当我们解决了最初的问题时,请记住这主要是一个思想实验。它缺少一些功能,例如处理已删除的项目和未安装的组件实例。它还具有一些限制,例如组件实例的属性名称需要与存储在 localStorage 中的项目相同的名称。就是说,主要目标是更好地了解Vue响应式在幕后的工作方式并充分利用这一点,因此,我希望你能从所有这些事情中受益。


    来源:https://css-tricks.com,作者:roberto,翻译:公众号《前端全栈开发者》

    展开全文
  • vue数组去重

    2021-04-13 17:21:35
    vue数组去重 最近在开发中遇到了需要对数组进行去重操作,在这里记录一下基本的去重方法 数组去重 对于简单的数组进行去重,可以使用Set集合,基本上可以对所有数据类型进行去重 test() { const arr = [true, 1, '...
  • 主要介绍了vue数组对象排序的实现代码,这里整理了详细的代码,非常具有实用价值,需要的朋友可以参考下
  • vue数组拼接

    千次阅读 2020-06-22 09:46:03
    例如目前有一组需求,后端传过来的数组里面包含经度,纬度两个属性。我们在前端展示的时候需要把他们放在一个表单 直接上图
  • vue 数组倒序

    千次阅读 2020-08-22 10:43:24
    uniapp 数据倒序主要方法 Array.reverse
  • VUE 数组更新

    2017-12-15 16:41:00
    1、数据方法分类: (1)原数组改变 push pop unshift shift reverse sort splice (2)原数组未变,生成新数组 slice concat filter ma...
  • vue 数组中添加对象

    千次阅读 2021-01-19 16:23:09
    //定义个一个数组 GridLayout: [] //数组中添加一个对象 this.GridLayout.push({ 'content': [ { 'span': 24, 'url': 'www.1.com', }, ] },)
  • vue数组去重2种方法

    万次阅读 2019-09-29 10:10:56
    //去重后的数组 // var flag; // for (var i in that.new_Positions){ // flag = true; // for (var j in that.resultArr) { // if (that.resultArr[j] == that.new_Positions[i]) { // flag = false; // ...
  • vue 数组对象排序

    2020-12-25 14:19:55
    <div id="app"> <ul> <li v-for="(stu,index) in students1">{{stu}}</li> </ul> </div> data:{ students:[ {nam
  • vue 数组按时间排序

    2021-08-10 10:49:41
    <template> <div> <div @click="sort()">时间排序</div> </div> </template> <script> export default { name: 'TimeSort', components: {}, ... time:'2
  • vue 数组排序、对象排序

    万次阅读 2019-01-17 11:06:48
    &lt;div class="sort"&gt; &...数组排序&lt;/h3&gt; &lt;div&gt;&lt;span&gt;排序后&lt;/span&gt;{{sortAry}}&lt;/div&gt; &lt
  • vue 数组分数组 封装

    2020-03-02 10:32:57
    数组分数组 花里胡哨,为毛一篇一篇分开写? 标题就能看出这篇文章是啥 也打算写个集合,但集合找过来找过去 自己受不了 啊哈哈 开撸 group(array, length) { let index = 0; let list = []; while(index < ...
  • vue数组进行分组

    千次阅读 2017-09-17 16:24:00
    数组进行分组使用switch方法 <template> <v-layout> <v-card contextual-style="dark" v-if="show"> <span slot="header"> 一级主页面 </span> <div s...
  • vue 数组 unshift push shift pop

    千次阅读 2019-02-18 22:51:18
     <script src="vue.js">    {{v.content}}     cols="30" rows="10"></textarea><br>  ('end')">发表到最后  ('prev')">发表到前面  ...
  • <code>//在vue中定义数组对像list data:{ list:[ {inpuoff:true,msg:"切割图片"}, {inpuoff:false,msg:"编写页面代码"}, {inpuoff:false,msg:"编写JS代码"} ]} //定义方法 methods:{...
  • 思路:定义一个新数组,并存放原数组的第一个元素,然后将元素组一一和新数组的元素对比,若不同则存放在新数组中。 function unique(arr) { let newArr = [arr[0]]; for (let i = 1; i < arr.length; i++) { ...
  • 作者 | 爱编程的小和尚责编 | 王晓曼出品 | CSDN博客学...VUE,主要实现 VUE 数据响应式 (数据劫持结合发布者-订阅者)、数组的变异方法、编译指令,数据的双向绑定的功能。本文需要有一定 VUE 基础,并不适合新手学...
  •  数组.push(元素) 参数 描述 newelement1 必需。要添加到数组的第一个元素。 newelement2 可选。要添加到数组的第二个元素。 newelementX 可选。可添加多个元素。 2、unshift() 头部添加  ...
  • 由于 JavaScript 的限制,Vue不能检测数组和对象的变化,在我们改变数组和对象的数据时,页面没有办法响应式改变。因此,我们要采用一些特殊的方法来让页面响应数组和对象的改变。 数组 Vue 不能检测以下数组的变动...
  • ![图片说明](https://img-ask.csdn.net/upload/201909/27/1569567291_372909.jpg) ![图片说明]... ... <option v-for="c in cList" :value="c.p">{{c.pName}}</option>
  • 一、在一个for循环里我们只想获取选择的... 逻辑部分: 这样就可以获取在一个for循环里面的选择到的数据。...二、 二维数组任意位置删除方法: productList:[ ] 第一层数组 productItem:[ ] 第二层数组 ...
  • vue 数组中嵌套的对象添加新属性--页面更新:https://www.jianshu.com/p/8f0e5bb13735 转载于:https://www.cnblogs.com/bydzhangxiaowei/p/10115433.html
  • vue数组的查找与筛选

    千次阅读 2019-05-22 17:39:19
    1.从接口获取到的数据传到vuex里,实现方法: 找到获取列表的接口: if (result.code === 1) { this.workerList = result.data // 将工人id存进全局 ...includes方法:判断一个数组是否包含指定的值
  • VUE 数组对象 和 数字排序

    千次阅读 2018-07-05 10:36:06
    普通数组的排序先看代码:&lt;div class="app"&gt; &lt;h1&gt;v-for实例&lt;/h1&gt; &lt;hr&gt; &lt;ol&gt; &lt;li v-for="number in numbers"...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 11,540
精华内容 4,616
关键字:

vue数组转集合

vue 订阅