Vue.js学习笔记-基础部分+完整实现代码

下载安装搭建环境

  • 可以选npm安装,或者简单下载一个开发版的vue.js文件
  • 浏览器打开加载有vue的文档时,控制台提示可以安装vue控制台插件,于是去chrome商店安装,Firefox的插件是通过模拟chrome插件来辅助安装的。但是第一次点击Vue Devtools提示“vue.js is not detected”,咦,原来我忘记勾选“允许访问文件地址”,勾选后就可以正常运行了。

第一个demo

Hello Vue!
关键词:模板语法、声明式

这里有个小bug,JSFiddle Hello World例子中,并没有为new Vue赋值变量,于是教程中接下来说的,“修改app.message”就没法实现,认真看教程中代码可以看到它为其赋值命名为app,下一个例子命名为app-2,命名后就可以在控制台采用“名称.属性”的形式访问修改内容了。

第二个demo

新指令:v-bind
用法

  • html——v-bind:title="message"
  • js——data:{message:'内容'}

新指令:v-if
用法:根据值的真假决定是否渲染DOM内容
原理:Vue可以绑定DOM结构到数据


新指令:v-for
用法

  • html——
  • {{todo.text}}
  • js——data:{todos:[{text:"内容1"},{text:"内容2"},{text:"内容3"}]}

原理:v-for是一个包装过的for in循环,对应的data内需要有一个数组


新指令:v-on
用法

  • html——绑定事件----v-on:click="事件名字"
  • js——methods:{事件名字:function(){处理内容}}

新指令:v-model
用法:

  • html——
  • js——data:{message:"hello vue"}

原理:v-model可实现表单输入和应用状态的双向绑定,修改表单内容,message对应值也会修改。

组件化应用构建

  • 注册组件
Vue.component('todo-item',{
      template:'
  • 这是一个待办项
  • ' })
    • 使用

    但是这样实现,会为每个元素绑定相同文本,Vue可以将数据从父作用域传到子组件。

    • 注册组件
    Vue.component('todo-item', {
      props: ['todo'],
      template: '
  • {{ todo.text }}
  • ' })
    • 创建元素并v-bind绑定数据到一个对象
    • 创建对象
    var app7 = new Vue({
      el: '#app-7',
      data: {
        groceryList: [
          { text: '蔬菜' },
          { text: '奶酪' },
          { text: '随便其他什么人吃的东西' }
        ]
      }
    })
    
    • props是作为一个接口存在的。
    • Vue组件非常类似于自定义元素,但是有一些纯自定义元素不具备的重要功能,包括跨组件数据流,自定义事件通信和构建工具集成。

    Vue实例

    • Vue 构造器
    • Vue.extend({})可以扩展Vue构造器
    • 每个Vue实例都会代理其data对象里面的所有属性,即实例.属性===data.属性
    • 实例引用内部属性和方法需要加前缀$
    vm.$el===document.getElementById('exzample');
    
    • 实例声明周期与钩子
      created是实例被创建后调用,还有mounted、updated、destroyed,钩子的this指向调用它的实例

    模板语法

    • 文本:双大括号的文本插值(默认是响应式的,对应属性的值变化,此处内容会更新)
    Message:{{ msg}}
    

    也可以通过v-once执行一次性插值,不再更新内容,但是这会影响这个节点的所有数据绑定

    This will never change:{{message}}
    
    • 纯HTML:用v-html可以输出真正的html
    var app8=new Vue({ el:'#app-8', data:{ rawHtml:'
  • 纯html
  • ' } }) //输出:● 纯html

    但是要注意只对可信内容使用html插值,不然容易导致xss攻击

    • 属性:html属性不能用双大括号语法,需要用v-bind。
    悬停几秒看信息
    • 所有的数据绑定都可以用js表达式
    {{ number + 1 }}
    {{ ok ? 'YES' : 'NO' }}
    {{ message.split('').reverse().join('') }}
    

    然而,语句和流控制是不行滴,三元表达式是可以的

    {{var a = 1 }}
    {{if(ok){return message}}}
    
    • 指令:带有v-前缀的特殊属性,它的值预期是单一js表达式,指定的职责是,当表达式的值改变时相应的将某些行为应用到DOM上。
    • 部分指令接收一个参数,在指令后以冒号指明,比如v-bind:href="url"
      还比如v-on:click="方法名"绑定一个事件
    • 修饰符,以半角句号指明的特殊后缀,指出一个指令应该以特殊方式绑定,.prevent 修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault():
    • 过滤器:大括号语法和v-bind表达式,用于文本转换
    {{message|format}}
    var app9=new Vue({ el:'#app-9', data:{ message:'hello,Filters!' }, filters:{ format:function(value){ value=value.toLowerCase() return value.charAt(0).toUpperCase()+value.slice(1) } } })

    过滤器可以串联,可以接受参数

    • 缩写:v-bind缩写为空,后接冒号
    
    
    

    v-on缩写为@,并省略了后面的冒号

    
    
    var app01=new Vue({ el:'#app-01', data:{ isActive:true } }) //div class="active"
    • v-bind:class可与普通class属性共存,并可以传入更多属性来动态切换:
    • 还可以绑定为一个对象:
    var app02=new Vue({ el:'#app-02', data:{ classObject:{ active:true, 'text-danger':false } } })
    • 还可以结合计算属性,加入函数
    computed: {
      classObject: function () {
        return {
          active: this.isActive && !this.error,
          'text-danger': this.error && this.error.type === 'fatal',
        }
      }
    }
    

    总结:只要冒号后的数据形式为‘名称:布尔值’就可以

    • 可以传入数组
      数组语法涉及逻辑判断很少,可以理解为对象语法的组合版。
    data: { activeClass: 'active', errorClass: 'text-danger' }
    • 可以用在组件上
    
    

    和内置元素的使用差不多

    对style样式增强绑定:
    由于默认格式里就包括一对引号,要注意单双引号的区分,不然会出错

    1111

    var app03=new Vue({ el:'#app-03', data:{ activeColor:'red', fontSize:30 } })

    条件渲染

    • v-if/v-else

    Yes

    No

    var app04=new Vue({ el:'#app-04', data:{ ok:false } })
    • template可以作为一个包装元素,包裹多个需要同一个v-if的元素

    No

    • 还有一个v-else-if作为链式使用判断
    • template包装起来,用v-if和v-else判断的组件,默认是复用的,不想复用,给各自添加一个不同名称的key就可以了。
      复用的如下:
    var app05=new Vue({ el:'#app-05', data:{ loginType:'username' }, methods:{ toggle:function(){ this.loginType==='username'?this.loginType='email':this.loginType='username' } } })

    不复用的如下:

    • v-show始终都会渲染,简单的切换display属性,不支持template语法,也不支持v-else

    222

    v-show vs v-if
    v-show适合频繁切换,v-if适合条件不太改变的情况

    列表渲染

    • 基本用法:v-for = "item in items",items是源数组,item是数组元素迭代的别名
    • {{item.message}}
    var app06=new Vue({ el:'#app-06', data:{ items:[ {message:'Foo'}, {message:'Bar'} ] } })
    • v-for支持索引参数,但是是从0开始滴(改为{{index+1}}就是从1开始了),并且拥有对父作用域的完全访问权限,可以引用其他属性,比如例子中的parentMessage
    • {{parentMessage}}-{{index}}-{{item.message}}
    • 还可以用of替代in作为分隔符,更接近js迭代器
    • 支持template组合语句单元
    • 对象迭代
      v-for = "value in object"——object是一个拥有多个属性的对象,不再是数组。
      不同于数组迭代,对象迭代是三个参数,分别为迭代内容,迭代键值和参数:(item,key ,index)顺序固定
    • {{item}}-{{key}}-{{index}}
    • v-for也可做简单的数字循环:
    • {{n}}

    但是同样需要建立Vue对象,

    var app09=new Vue({
            el:'#app-09'
        })
    
    • ‘就地复用’的选择:与v-if相同,添加唯一key属性值可以不复用,v-for中最好绑定一个唯一id
    • {{item.message}}-{{index}}-{{item.id}}

    这里我绑了元素的index在id上

    • 包含一组观察数组的变异方法:
    • push()——数组末端添加新项(返回新长度)
    • pop()——删除数组最后一项(返回元素值)
    • shift()——删除数组第一项(返回元素值)
    • unshift()——数组头部添加新项(返回新长度)
    • splice()——添加或删除项目
    • sort()
    • reverse()
    • 包含几个非变异数组方法(变异是指改变原数组):
    • filter()
    • concat()
    • slice()
    • 数组操作方法的局限:
    • 不能通过索引值直接设置一个项:
    vm.items[indexOfItem] = newValue
    
    • 但是可以用set方法设置:
    Vue.set(example1.items,indexOfItem,newValue)
    
    • 或者用万能的splice:
    example1.items.splice(indexOfItem,1,newValue)
    

    注意这里的第二个参数为"1",表示原地替换原元素

    • 不能直接修改数组长度:
    vm.items.length = newLength
    

    但是依旧可以用万能的splice:

    example1.items.splice(newLength)
    第二个参数为删除个数,不填第二个参数时表示删除到末尾
    

    此处翻阅了犀牛书和高程,高程中没提到splice()省略第二个参数的情况,犀牛书提到了,省略第二个参数,从起始点到结尾的所有元素都将被删除。查了ecma-262的文档,原文是:

    5  If the number of actual arguments is 0, then
         a. Let insertCount be 0.
         b. Let actualDeleteCount be 0.
    6  Else if the number of actual arguments is 1, then 
         a. Let insertCount be 0. 
         b. Let actualDeleteCount be len ‑ actualStart.
    
    • 显示过滤、排序结果
    • 使用计算属性computed
    • {{n}}
    var app11=new Vue({ el:'#app-11', data:{ numbers:[1,2,3,4,5] }, computed:{ evenNumbers:function(){ return this.numbers.filter(function(number){ return number%2 === 0 }) } } })
    • 使用方法,methods——区别很小,引用时需要传入参数
    • {{n}}
    var app11=new Vue({ el:'#app-11', data:{ numbers:[1,2,3,4,5] }, methods:{ evenNumbers:function(){ return this.numbers.filter(function(number){ return number%2 === 0 }) } } })

    事件处理器

    • 最简单的把处理代码写在v-on:click里:

    这个按钮被点击了{{counter}}次

    var app12=new Vue({ el:"#app-12", data:{ counter:0 } })
    • 命名一个方法,把方法名写在v-on:click后
    • 可以传参数
    var app13=new Vue({ el:'#app-13', data:{ message:'new event!' }, methods:{ say:function(m){ alert(m) } } })
    • 可以访问原生DOM事件,用特殊变量$event传入
    var app14=new Vue({ el:'#app-14', methods:{ mod:function(message,e){ if(e)e.preventDefault() alert(message) } } })

    代码中点击button会触发一个click事件,把event作为参数传入,就可以对这个事件进行操作

    • 事件修饰符
      从方法中拆分出了事件处理过程给指令,表达更直观。
    • .stop——阻止冒泡
    • .prevent——?
    • .capture——捕获事件
    • .self——该元素本身(而不是子元素)时触发回调
    • .once——点击事件只会触发一次
    • 按键修饰符:用于监听键盘事件
    
    var app1=new Vue({
       el:'#app-1',
       methods:{
           submit:function(){
               alert('success')
           }
       }
    })
    

    keyup.13是"enter"
    还有别名:

    
    
    
    
    • .enter
    • .tab
    • .delete
    • .esc
    • .space
    • .up
    • .down
    • .left
    • .right
    • .ctrl
    • .alt
    • .shift
    • .meta
    • 还可以自定义别名:
    Vue.config.keyCodes.f2=113
    

    表单控件绑定

    v-model本质上是语法糖,监听输入事件以更新数据

    • 基础用法:

    Message is:{{message}}

    var app2=new Vue({ el:'#app-2', data:{ message:'' } })

    多行文本同理

    • 单选框很方便
    var app3=new Vue({ el:'#app-3', data:{ checked:false } })

    这里,checked预设值如果设为空,则开始时label没有内容,如果设为true,则选框会默认选上,所以最好只设为false

    • 复选框可共用v-model

    checked names: {{ checkedNames }}
    var app4=new Vue({ el:'#app-4', data:{ checkedNames:[] } })
    • 单选按钮
    • 单选列表
    selected:{{selected}}
    var app06=new Vue({ el:'#app-6', data:{ selected:"" } })

    此处遇到一小问题,建立option时,emmet的补全加了属性value='',如果不改动的话,默认option的值是为空的,选中无效,需要删除这个属性或者显式填值

    • 多选列表,在select处添加属性,multiple="multiple"就可以了。多选时按住ctrl或command键才可以多选
    • 动态选项值
    selected:{{selected}}
    var app06=new Vue({ el:'#app-6', data:{ selected:'A', options:[ {text:'One',value:'A'}, {text:'Two',value:'B'}, {text:'Tree',value:'C'} ] } })

    此例子将选项内容与选项值区分成两组数据,而selected显示的是value的内容,同上一个小问题相同,option不显式设置值时,option标签内容即为值,显式设置后,value值会覆盖标签内容被发送。

    • 动态value值
      v-model绑定的value通常是静态字符串,通过v-bind可以动态绑定变量到value上,语法是v-bind:true-value/v-bind:false-value
    var app07=new Vue({ el:'#app-7', data:{ toggle:'false', a:'true', b:'false' } })

    例子中绑定了true-value到a变量,false-value到b变量
    单选框同理:

    
    
    • 修饰符
    • .lazy——默认情况下v-model载input事件中同步输入框的值与数据,添加一个.lazy修饰符后,转变为在change事件中同步,问题是,change事件是神马?存疑
    
    
    • .number自动将输入值转为Number类型
    
    

    这个例子玄机很多:
    输入字母e/E情况特殊,会被理解为指数标志,不同设定效果不同:
    1. 只设置type为number:字母输入无效;e后为有效数字时会正常显示,比如:

    输入:123abc456,
    框内显示为:123456,值显示为123456
    、、、
    输入:123e12,
    框内显示为:123e12  值显示为:123e12
    

    但是超过一定数值会显示为空:

    (很大很大一个数字)显示为:
    123e1234 值显示为:
    

    然而负指数会正确显示:

    123e-1234 值显示为 123e-1234
    
       2. 只设置v-model为number时:
    
    输入:123abc456,
    框内显示为123abc456,失去焦点时更新为123,
    值显示为123
    、、、
    输入:abc123,
    框内显示为abc123,失去焦点没变化,
    值显示为abc123,说明判定失效啦
    、、、
    123e12 值显示为:123000000000000
    ——同时输入框内会被更新成123000000000000
    

    指数e超过一定数值会显示为Infinity

    123e1234值显示为:Infinity
    ——同时输入框内会被更新成Infinity
    

    在一个范围内的负指数会被格式化后显示,
    超过一定值的负指数会被显示为0:

    123e-123  值显示为:1.23e-121
    123e-1234 值显示为:0
    ——同时输入框内会被更新
    
       3. 设置type为number并且v-model为number时:
    
    输入123abc456,
    框内显示为:123456,值显示为123456
    、、、
    输入abc123,
    框内显示为:123,值显示为123
    、、、
    123e12 值显示为:123000000000000
    ——同时输入框内会被更新成123000000000000
    指数大于20位会转化成指数形式
    

    超过一定数值显示为空:

    123e1234 值显示为:
    

    在一个范围内的负指数会被格式化后显示,
    超过一定值的负指数会被显示为0:

    123e-123  值显示为:1.23e-121
    123e-1234 值显示为:0
    ——同时输入框内会被更新
    

    总结:控制数字输入的主要依赖type="number",v-model.number是将输入值转化为Number类型,但是表现不太稳定。

    • .trim自动过滤用户输入的首尾空格:
    空空abc空空def空空  ——>  abc空空def
    

    组件

    • 基础语法:
    Vue.component('my-component',{ template:'
    A custom component!
    ' }) var app01=new Vue({ el:'#app-01' })

    要确保,先注册组件,再初始化实例,调换前后顺序会出错

    • 局部注册:把组件用components注册在实例中,注意components为复数形式,好处是限制了作用域
    var app01=new Vue({ el:'#app-01', components:{ 'my-component':{ template:'
    A custom component!
    ' } } })

    注意此处相当于先实例再在实例中注册组件,因此可以判断,注册组件行为不能落后于实例化,但是可以包含在实例的同时进行。

    • DOM模板解析——当把模板加载到一些已存在的元素上时,可能会受到HTML的一些限制,因为像一些ul/ol/table/select限制了能被它包裹的元素,而一些如option的元素只能出现在某些元素内部,因此,有时自定义组件会失效而出错:
    ...

    需要使用特殊的is属性:

    这相当于把组件伪装成一个tr ,查看浏览器文档结构解析为:

    A custom component
    ——(组件内容)

    tbody问题
    此处出现的tbody是html解析自动补全的,tbody功能为,划分表格结构,默认情况下是一个tbody,也可手动建立多个并行的tbody,这影响表格分别渲染及切分(很长的表格换页的时候断在哪里就是切分)

    • 限制不适用情况没看懂(存疑):

      • //载入一个验证插件





        //此处有一个合计

        Total: ${{total}}



        //组件部分
        Vue.component('currency-input',{
        template:'

        //如果有label,则显示,否则不显示

        $ v-on:focus="selectAll" v-on:blur="formatValue">
        ',
        props:{
        //对参数进行验证
        value:{
        type:Number,
        default:0
        },
        label:{
        type:String,
        default:''
        }
        },
        //生命周期钩子,当组件被挂载到实例时会触发
        mounted:function(){
        this.formatValue()
        },
        methods:{
        updateValue:function(value){
        var result=currencyValidator.parse(value,this.value)
        if(result.warning){
        this.$refs.input.value=result.value
        }
        this.$emit('input',result.value)
        },
        formatValue:function(){
        this.$refs.input.value =currencyValidator.format(this.value)
        },
        //做setTimeout是由于safari此处有bug,不考虑兼容的话不必如此
        selectAll:function(event){
        setTimeout(function(){
        event.target.select()
        },0)
        }
        }
        })

        - 非父子组件间的通信(简单场景)——创建一个空的Vue实例作为连接:
        

        var bus = new Vue()
        //在A组件内
        bus.$emit('id-selected', 1)
        //在B组件内
        bus.$on('id-selected', function (id) {
        // ...
        })

        - slot 内容分发
         - 单个slot:子组件只有一个没有名字的slot时,父组件的整个内容片断会插入到slot所在的DOM位置
         - 具名slot:
        

        子模板












        父模板

        这里可能是一个页面标题


        主要内容的一个段落。


        另一个主要段落。


        这里有一些联系信息



        渲染结果


        这里可能是一个页面标题




        主要内容的一个段落。


        另一个主要段落。




        这里有一些联系信息



         - 作用域插槽:子组件中把数据传递到插槽slot,父元素用一个template scope="props" 获取数据,然后就可以使用了
        

        Vue.component('child',{
        template:'

        '
        })
        var app03=new Vue({
        el:'#app-03',
        components:{
        'parent-component':{
        template:'



        '
        }
        }
        })
        //显示为:
        hello from parent hello from child

         - 更复杂的列表组件的例子:
        

        //需要把mylist传递进parent-list

        Vue.component('child-list',{
        props:['mylist'],
        template:'

        '
        })
        var app04=new Vue({
        el:'#app-04',
        data:{
        mylist:[
        {text:'蔬菜'},
        {text:'水果'},
        {text:'肉'}
        ]
        },
        components:{
        'parent-list':{
        props:['mylist'],
        //此处也需要把mylist传递进child-list
        template:'

        '
        }
        }
        })
        显示为:

        • 蔬菜
        • 水果
        - 渲染一个元组件为动态组件,根据is的值来决定渲染哪个组件
        


        var app06=new Vue({
        el:'#app-06',
        data:{
        currentView:'posts'
        },
        components:{
        'home':{
        template:'home'
        },
        'posts':{
        template:'posts'
        },
        'archive':{
        template:'archive'
        }
        }
        })
        //输入为posts

        - keep-alive保留状态或避免重新渲染
        

        - 可复用组件,三个接口:
         - props
         - events
         - slots
        
        

        :foo="baz"
        :bar="qux"
        @event-a="doThis"
        @event-b="doThat"


        Hello!


        - 子组件索引:使用ref指定一个索引id
        




        var parent = new Vue({el:'#parent'})
        var child = parent.$refs.profile

        当ref与v-for一起使用时,ref是一个数组或对象,包含相应的子组件,是非响应式的,应避免在模板或计算属性中使用
        - 异步组件,接受一个工厂函数,可以动态解析组件的定义:
        
        • 可以搭配webpack的代码分割功能
        • 可以使用webpack2 +es2015返回一个promise resolve函数
        • 组件命名约定,三种都可以:kebab-case,camelCase,TitleCase
          但是,html模板中,只能使用kebab-case
          字符串模板中,三种都可以
        • 递归组件,组件可以递归的调用自己
          (这里的雷是,不能在实例内部创建组件,因为递归时会查找不到子组件,需要定义全局组件)
        Vue.component('recursion-component',{ props:['count'], name:'count', template:'
        {{count}}
        ' }) var app07=new Vue({ el:'#app-07' }) //输出是:0 1 2 3 4 5 6 7 8 9 10
        • 组件间的循环引用——注册为全局组件时没有问题,但是使用webpack或者browerify用requiring/importing组件的话,会报错,需要用生命周期钩子中注册它的优先级:
        beforeCreate:function(){
        this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue')
        }
        

        此处(存疑,以后用了webpack再说)
        文件目录树的例子:

        Vue.component('tree-folder',{ template:'

        {{folder.name}}

        ', props:['folder'] }) Vue.component('tree-folder-contents',{ template:'
        • \ \ {{child.name}}\
        ', props:['children'] }) var app08=new Vue({ el:'#app-08', data:{ folder:{ name:'总目录', children:[ {name:"二层目录1"}, {name:"二层目录2", children:[ {name:"三层目录1"}, {name:"三层目录1"} ] }, {name:'二层目录3'} ] } } }) //输出: 总目录 - 二层目录1 - 二层目录2 - 三层目录1 - 三层目录2 - 二层目录3
        • 内联模板:一个组件有inline-template属性时,组件会把它的内容当做它的模板,而不是当做分发内容。应对于很简单的模板,因为有作用域问题
        啦啦啦
        \\js部分
        Vue.component('parent-com',{
          template:'
        this is part of parents
        ' }) \\默认情况下,“啦啦啦”作为分发内容,不会显示,而是显示this is part of parents. \\如果改为: 啦啦啦 \\则显示为“啦啦啦”,原设定的template被覆盖了
        • 另一种定义模板的方式:x-template
        \\先单独一个js,类型为type="text/x-template",并且需要有id \\然后在另一个js里创建组件,只不过模板可以直接引用这个id: Vue.component('hello-world', { template: '#hello-world-template' }) \\别忘了创建实例(掩面) var app10=new Vue({ el:'#app-10' })
        • 对低开销的静态组件使用v-once只渲染一次,下次时直接应用缓存
        Vue.component('terms-of-service', {
          template: '\
            
        \

        Terms of Service

        \ ... a lot of static content ...\
        \ ' })

        基础部分结束

    你可能感兴趣的:(Vue.js学习笔记-基础部分+完整实现代码)