1_Vue 基本使用

Vue 基本使用1

Vue 安装方式

  1. 直接下载源码然后通过路径引入

    开发版本:https://vuejs.org/js/vue.js
    
    生产版本:https://vuejs.org/js/vue.min.js
    
  2. 在线 cdn 引入的方式

    <script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
    
  3. 采用 npm 安装的方式

    // 现状: 都会采用npm的方式来进行正式项目开发
    npm install vue
    

    注意: Vue.js 不支持 IE8 及其以下版本


简单实例

步骤

  1. body 中,设置 Vue 管理的视图

  2. 引入vue.js

  3. 实例化 Vue 对象 new Vue()

  4. 设置 Vue 实例的选项:如el、data…

    new Vue({选项:值});

  5. 中通过 {{ }} 使用 data 中的数据

<p>实例一:声明式渲染</p>
<div id="app1">
    <p>{{str}}</p>
</div>

<script>
    var app = new Vue({
        el:'#app1',
        data:{
            str:'hello vue'
        }
    })
</script>

el、data、methods 介绍

el

  • 作用:当前 Vue 实例所管理的 html 视图
  • 值:通常是 id 选择器(或者是一个 dom 对象)
  • 注意: 不要让 el 所管理的视图是 html 或者 body !!!!
<script>
new Vue({ 
    // el: '#app' , id选择器
    // el: '.app', class选择器
    el: document.getElementById("#app") // dom对象
})
</script>

data

Vue 实例的 data (数据对象),是响应式数据(数据驱动视图)

  1. data 中的值 {数据名字:数据的初始值}

  2. data 中的数据 msg/count 可以在视图中通过 {{msg/count}} 访问数据

  3. data 中的数据也可以通过实例访问 vm.msg 或者 vm.$data.msg

  4. data 中的数据特点:响应式的数据->data 中的数据一旦发生变化->视图中使用该数据的位置就会发生变化

let vm = new Vue({ 
    el: "#app", 
    data: {
        msg: 'abc', 
        count: 100
        list: [1, 2, 3] 
	} 
})
vm.msg = 200
console.log(vm) 
console.log(vm.msg)
console.log(vm.$data.msg)

methods

  • methods 其值为一个对象
  • 可以直接通过 VM 实例访问这些方法,或者在指令表达式中使用
  • 方法中的 this 自动绑定为 Vue 实例
  • methods 中所有的方法 同样也被代理到了 Vue实例对象上,都可通过 this 访问
  • 注意: 不应该使用箭头函数来定义 method 函数。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例
   let vm = new Vue({
         el: "#app", 
         data:{
              name: "Hello world",
              name2: "Hello world2" 
        }, 
        
        methods: { 
            // 常规函数写法
            fn1:function(){
                 console.log(this.name)
                 this.fn2()
             },
            // es6 函数简写法
             fn2() {
                  console.log(this.name2)
                   } 
            }
    })

插值表达式 :{{}}

作用:会将绑定的数据实时的显示出来

形式::通过 {{ 插值表达式 }} 包裹的形式

用法:{{js表达式、三元运算符、方法调用等}}

  • {{ a }}
  • {{a == 10 }}
  • {{a > 10}}
  • {{a + b + c}}
  • {{a.split(’’).reverse().join(’’)}}
  • {{a > 0 ? “成功” : “失败”}}
// 错误写法
<!-- 这是语句,不是表达式 --> 
{{ var a = 1 }} 

<!-- 流控制也不会生效,请使用三元表达式 --> 
{{ if (ok) { return message } }}      
<!-- 方法调用 --> 
<!-- fn为methods中的方法 --> 
<p>{{ fn() }}</p>

指令

指令 (Directives) 是带有 v-前缀的特殊特性

指令特性的值预期是单个 JavaScript 表达式( v-for 是例外情况,稍后我们再讨论)

指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM

指令位置: 起始标签

系统指令:v-text 和 v-html

  • 很像 innerText 和 innerHTML

  • v-text:更新标签中的内容

  • v-text 和插值表达式的区别

    ​ v-text 更新 整个 标签中的内容

    ​ 插值表达式: 更新标签中局部的内容

  • v-html:更新标签中的内容/标签

  • 可以渲染内容中的 html 标签

  • 注意:尽量避免使用,容易造成危险 (XSS跨站脚本攻击)

<hr>
<p>实例五:v-text与v-html</p>
<div id="app5">
    <p v-text="msg">原始数据</p>
    <p v-html='msg1'>原始数据</p>
 </div>

 var f = new Vue({
        el: '#app5',
        data: {
            msg: 'v-text更新标签的内容[全部更新],{{局部更新}}',
            msg1: '<div>v-html更新内容与标签</div>'
        }
    })

系统指令:v-if 和 v-show

使用: v-if 和 v-show 后面跟着表达式的值是布尔值 ,布尔值来决定该元素显示隐藏

注意 :v-if 是直接决定元素 的 添加 或者删除 而 v-show 只是根据样式来决定 显示隐藏

  • v-if 有更高的切换开销
  • v-show 有更高的初始渲染开销

如果需要非常频繁地切换,则使用 v-show 较好

如果在运行时条件很少改变,则使用 v-if 较好


系统指令:v-on 绑定事件

使用

  • 第一种:v-on:事件名=“方法名”
  • 第二种:@事件名="方法名"的方式
// v-on:xx事件名='当触发xx事件时执行的语句'
<button v-on:click="fn">按钮</button> 

// v-on的简写方法
<button @click="fn">按钮</button>

修饰符

使用:@事件名.修饰符=“方法名”

  • .once - 只触发一次回调
  • .prevent - 调用 event.preventDefault() 阻止默认事件
// v-on修饰符 如 once: 只执行一次
<button @click.once="fn">只执行一次</button>

// v-on修饰符 如 prevent: 阻止默认事件 
<button @contextmenu.prevent="fn">阻止默认事件</button>

事件对象(扩展) :

第一种:方法名中采用 $event 的方式传形参

第二种:直接写事件名 默认第一个参数为 event 事件参数


系统指令:v-for(数组)

v-for 指令基于一个数组来渲染一个列表

v-for 语法: item in items 或者 item of items

其中 items 是源数据数组 而 item 则是被迭代的数组元素的别名

注意 : v-for 写的位置 应该是重复的标签上 不是其父级元素上 需要注意


系统指令:v-for(对象)

第一种用法:

// items 为对象 item为当前遍历属性对象的值
v-for="item in items"

第二种用法:

//item为当前遍历属性对象的值 key为当前属性名 index为当前索引的值
v-for="(item, key, index) in items"

系统指令:v-for(key属性)

场景: 列表数据变动会导致 视图列表重新更新 为了提升性能 方便更新 需要提供一个属性 key

// 使用v-for时 建议给每个元素设置一个key属性 (必须加上)
// key属性的值 要求是每个元素的唯一值 (唯一索引值)
// 好处:vue渲染页面标签 速度快

// 数组 
<li v-for="(v,i) in arr" :key="i">{{v}}</li> 

// 对象
<li v-for="(v,k,i) in json" :key="i">{{v}}-{{k}}</li>

全部练习代码 1:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!-- <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> -->
    <script src="./js/vue.js"></script>
</head>

<body>
    <p>实例一:声明式渲染</p>
    <div id="app1">
        <p>{{str}}</p>
        <input type="text" v-model="str">
    </div>

    <hr>
    <p>实例二:通过实例对象修改数据</p>
    <div id="app2">
        <p>{{msg}}</p>
        <p>{{list}}</p>
    </div>

    <hr>
    <p>实例三:定义method方法</p>
    <div id="app3">
        <p>{{msg3}}</p>
        <!-- <p>{{test2()}}\{{msg}}</p> -->
        //通常用法:在点击事件上添加一个方法
        <button v-on:click="test3">反转消息</button>
        //方式一
        <button @click="test4($event)">通过$event传入形参的方式得到事件对象</button>
        //方式二:不传参数,默认第一个就是事件对象
        <button @click="test5">事件对象</button>

        <button @click="test6($event,2,2)">方法传参</button>
    </div>

    <hr>
    <p>实例四:插值表达式</p>
    <div id="app4">
        <p>{{msg}}</p>
        <p>{{'原始值:' + msg}}</p>
        <p>{{msg + 10}}</p>
        <p>{{msg > 10}}</p>
        <p>{{msg>10?'是':'否'}}</p>
        <!-- <p>{{msg + 1 = 100}}</p> -->
    </div>

    <hr>
    <p>实例五:v-text与v-html</p>
    <div id="app5">
        <p v-text="msg">原始数据</p>
        <p v-html='msg1'>原始数据</p>
    </div>

    <hr>
    <p>实例六:v-if与v-show</p>
    <div id="app6">
        <p v-if='msg'>你好主人:v-if根据boolean结果显示[如果频繁切换,亲亲这边建议你使用v-show]</p>
        <!-- <div>{{modifyIf()}}</div> -->
        <p v-show='msg'>你好主人:v-show根据boolean结果决定是否隐藏标签属性进行显示[如果为false,将使用style="display: none;"]</p>
    </div>

    <hr>
    <p>实例七:v-on</p>
    <div id="app7">
        <p v-if='msg'>很开心见到你!</p>
        //绑定方式一:v-on:click='方法名':
        <button v-on:click='moIf'>点击显示v-if的内容</button>
        //绑定方式二:@click='方法名':
        <button @click='moIf'>点击切换</button>
        //属性一:once属性表示只可点击一次
        <button @click.once='moIf'>点击切换</button>
        //属性二:prevent属性表示阻止某个事件的发生
        <button @contextmenu.prevent='moIf'>阻止显示菜单</button>
    </div>
    <hr>
    <p>实例七:v-on</p>
    <div id="app7">

    </div>

</body>
<script>
    var app = new Vue({
        el: '#app1',
        data: {
            str: 'hello vue'
        }
    })

    var da = new Vue({
        el: '#app2',
        data: {
            msg: '数据1',
            list: [1, 2, 3, 4]
        }
    })

    //通过实例对象修改数据
    da.msg = '数据2'
    da.list[1] = 0
    console.log(da)
    //实例数据挂载到对象实例和data上
    console.log(da.msg)
    console.log(da.$data.msg)

    var m = new Vue({
        el: '#app3',
        data: {
            msg3: '原始数据'
        },
        methods: {
            //写法一:
            test1: function () {
                this.msg3 = '调用方法1改变'
            },
            //写法二:
            test2() {
                this.msg3 = '调用方法2改变'
            },
            test3() {
                this.msg3 = this.msg3.split('').reverse().join('')
            },
            test4(e) {
                console.log(e)
            },
            test5(e) {
                console.log(e)
            },
            test6(e, x1, x2) {
                console.log(e)
                this.msg3 = x1 + x2
            }
        }
    })
    //通过对象调用方法
    m.test1()

    var c = new Vue({
        el: '#app4',
        data: {
            msg: 10
        },
        methods: {
            test() {
                this.msg = 100
            }
        }
    })

    var f = new Vue({
        el: '#app5',
        data: {
            msg: 'v-text更新标签的内容[全部更新],{{局部更新}}',
            msg1: '<div>v-html更新内容与标签</div>'
        }
    })

    var s = new Vue({
        el: '#app6',
        data: {
            msg: true,
        },
        methods: {
            modifyIf() {
                this.msg = false
            }
        }
    })

    var se = new Vue({
        el: '#app7',
        data: {
            msg: false
        },
        methods: {
            moIf() {
                this.msg = !this.msg
            }
        }
    })

    var html = new Vue({
        el: '#ap',
        data: {
            msg: 'hah'
        }
    })
    let vm = new Vue({
         el: "#app", 
         data:{
              name: "Hello world",
              name2: "Hello world2" 
        }, 
        
        methods: { 
            // 常规函数写法
            fn1:function(){
                 console.log(this.name)
                 this.fn2()
             },
            // es6 函数简写法
             fn2() {
                  console.log(this.name2)
                   } 
            }
    })
</script>

</html>

总结:表单实践案例

功能点:

  1. 添加商品

  2. 删除商品

  3. 搜索商品

  4. 列表循环

  5. 数据不存在 显示不存在数据

  6. 时间格式

思路:

  1. 静态页面准备

  2. 实例化一个Vue

  3. 定义表格数据

  4. 采用 v-for 循环将静态内容切换为动态内容

  5. 采用 v-if 控制提示消息

代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #app {
            width: 600px;
            margin: 10px auto;
        }

        .tb {
            border-collapse: collapse;
            width: 100%;
        }

        .tb th {
            background-color: #0094ff;
            color: white;
        }

        .tb td,
        .tb th {
            padding: 5px;
            border: 1px solid black;
            text-align: center;
        }

        .add {
            padding: 5px;
            border: 1px solid black;
            margin-bottom: 10px;
        }
    </style>
    <script src="./js/vue.js"></script>
</head>
<body>
    <div id="app">
        <div class="add">
            品牌名称:     
            <input type="text" v-model="name">
            <input :disabled="name.length===0" @click="addItem" type="button" value="添加">
        </div>

        <div class="add">
            品牌名称:
            <input type="text" placeholder="请输入搜索条件">
        </div>

        <div>
            <table class="tb">
                <tr>
                    <th>编号</th>
                    <th>品牌名称</th>
                    <th>创立时间</th>
                    <th>操作</th>
                </tr>
                <tr v-for="(l,index) in list" :key="index">
                    <td>{{index+1}}</td>
                    <td>{{l.name}}</td>
                    <td>{{l.date}}</td>
                    <td>
                        <a href="#" @click.prevent="deleItem(index)">删除</a>
                    </td>
                </tr>
                <tr>
                    <td colspan="4" v-if='list.length == 0'>没有品牌数据</td>
                </tr>
            </table>
        </div>
    </div>
</body>
<script>
    var app = new Vue({
        el:'#app',
        data:{
            name:'', //绑定input输入
            list:[{name:'张三',date:'2020-01-01'},{name:'李四',date:'2020-02-02'} ]
        },
        methods:{
            //添加方法
            addItem(){
                //从数组前面添加
                this.list.unshift({
                    name:this.name,
                    date:new Date()
                })
                this.name=''
            },
            //删除方法
            deleItem(index){
                //询问是否删除
                if(confirm('是否删除')){
                    this.list.splice(index,1)
                }
            }
        }
    })
</script>

</html>

注意点:

  1. 为了防止添加数据时,输入的是空,使用:disabled="name.length===0"进行限制,只有输入框存在数据时,按钮才可用

  2. js 中往数组前面添加元素的方法为 unshift(),删除的方法为 splice(索引,数量)

  3. 使用 v-for 时加上 key 元素,为其增加唯一标识

  4. 点击删除时,使用 .prevent 属性

  5. js 的 confirm() 方法来询问是否这样做


功能补充1:使用过滤器完成日期格式处理
功能补充2:使用自定义指令完成自动获取焦点


Vue 基本使用2

系统指令:v-bind

作用: 绑定标签上的任何属性

场景: 当标签上的属性是变量/动态/需要改变的

用法:

// ID为数据对象中的变量值 
<p v-bind:id="ID"></p> 

// 简写 <p :id="ID"></p>

v-bind 绑定 class
v-bind 绑定 class (对象)

用法: 绑定 class 对象语法 :class="{ class名称: 布尔值}"

<p class="obox" :class="{obox:isBn}">内容</p> // isBn为 data选项中的属性

注意 : 绑定 class 和原生 class 会进行合并(但是不会合并重复的)


v-bind 绑定 class (数组)

绑定 class 数组语法 :class="[a,b]"

  • a、b 为 data 属性的 key
  • data 中 key 对应的 value 为 class 名字
<p :class="[a,b]">内容</p> 

data:{
	a:'obox',
    b:'left' 
}

v-bind 绑定 style
v-bind 绑定 style(对象)

语法 :style="{css 属性名:属性值}"

<p :style="{color:a,fontSize:b}"></p> 

//a、b为data属性的key
data: { 
    a: 'red', 
    b: '30px'
},

注意: css属性名 例如 font-size要写成 fontSize 以此类推 原有的 style 会覆盖


v-bind 绑定 style(数组)
// 语法: <div :style="[a,b]"></div> 

// a,b 为data的属性
data: {
	a: { color: "red" }, 
	b: { fontSize: "30px" } 
}

注意: 对象可以是多个属性的 集合 同样里面的css属性需要遵从小驼峰命名的规则


系统指令:v-model

作用: 表单元素的绑定

特点: 双向数据绑定

  • 数据发生变化可以更新到界面
  • 通过界面可以更改数据
  • v-model 绑定表单元素,会忽略所有表单元素的 value 、 checked 、 selected 特性的初始值
  • 表单元素会将 Vue 实例的 data 中的数据作为数据来源,所以应该在 data 选项中声明初始值
// 表单中设置value值没用 v-model会忽略
<input type="text" v-model="msg" value="zhang"> 
<p>{{msg}}</p>
// 在data中设置msg

data: { 
	msg: 'zhangsan' 
}

v-model:原理及实现

分析: 表单元素绑定的数据改变 => data数据发生改变=> 页面数据变化

<p>{{msg}}</p> 
<input type="text" :value="msg" @input="fn($event)">

data: {
	msg: 'abc' 
},
methods: {
	fn(e) { 
		//msg=最新的value
    	this.msg = e.target.value
        } 
}

v-model 绑定其他表单元素

表单元素: input textarea checkbox radio select

官方文档:

https://cn.vuejs.org/v2/guide/forms.html

系统指令:v-cloak

场景: 解决页面初次渲染时 页面模板闪屏现象

  1. 写入v-cloak 指令

  2. 在 style 里面加给 v-cloak 加上display: none;

注意: 避免多次写入标签 可以一次性 将 v-cloak 引用在实例视图上

<style> [v-cloak] {
	display: none;
}
</style>

<div id="app" v-cloak>
	{{msg}}
</div>

系统指令:v-once

作用: 指令所在元素只渲染一次,用于提高性能

<p v-once>{{msg}}</p>
<input type="text" v-model="msg">

全部练习代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./js/vue.js"></script>
    <link rel="stylesheet" type="text/css" href="./css/add.css" />
</head>

<body>
    <h1>v-bind绑定:Vue数据与任何属性,实现动态更新</h1>
    <div id="app1">
        //写法一:v-bind:属性名='数据'
        <a v-bind:href='url'>点击跳转到百度</a>
        //写法二::属性名='属性值'
        <img :src="imgUrl" width="30px;">

        <hr>
        <h2>与class绑定</h2>
        //与单个数据绑定
        <div :class="classUrl"></div>
        //与对象绑定:当a或者b为true时,才添加class
        <div class="bt" :class='{active:a,token:b}'></div>
        //与数组绑定
        <div class='bt' :class="[c,d]"></div>

        <hr>
        <h2>与style绑定</h2>
        //数据为对象形式
        <p :style="{color: e,fontSize: f}">对象</p>
        //数据为数组形式
        <h3 :style='[g,h]'>数组</p>
    </div>

    <hr>
    <h1>v-model实现数据与input的双向绑定,会忽略value值</h1>
    <div id="app2">
        <p>{{input}}</p>
        <input type="text" v-model='input' value="请.">

        //v-model原理分析
        <p>{{input1}}</p>
        <input type="text" :value="input1" @input='fn($event)'>
    </div>

    <hr>
    <p>文本处理</p>
    <div id='app3'>
        <p>Message is: {{ mes3 }}</p>
        <input v-model="mes3" placeholder="文本输入">
    </div>

    <hr>
    <p>多行文本</p>
    <div id='app4'>
        <p>Message is: {{ mes4 }}</p>
        <textarea v-model='mes4' placeholder="多行文本输入"></textarea>
        <!--不能这样输入: <textarea placeholder="请输入">{{mes4}}</textarea> -->
    </div>

    <hr>
    <p>单个复选框</p>
    <div id='app5'>
        <p>Message is: {{ mes5 }}</p>
        <input type="checkbox" id='yesorno' v-model='mes5'>
        <label for="yesorno">{{mes5}}</label>
    </div>

    <hr>
    <p>多个复选框</p>
    <div id='app6'>
        <input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
        <label for="jack">Jack</label>

        <input type="checkbox" id="john" value="John" v-model="checkedNames">
        <label for="john">John</label>

        <input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
        <label for="mike">Mike</label>
        <br>
        <span>Checked names: {{ checkedNames }}</span>
    </div>

    <hr>
    <p>单选按钮</p>
    <div id='app7'>
        <input type="radio" id="one" value="One" v-model="picked">
        <label for="one">One</label>
        <br>

        <input type="radio" id="two" value="Two" v-model="picked">
        <label for="two">Two</label>
        <br>

        <span>Picked: {{ picked }}</span>
    </div>

    <hr>
    <p>选择框[只能选择一个]</p>
    <div id='app8'>
        <select v-model="selected">
            <option disabled value="">请选择</option>
            <option>A</option>
            <option>B</option>
            <option>C</option>
        </select>
        <span>Selected: {{ selected }}</span>
    </div>

    <hr>
    <p>选择框[可以选择多个:添加multiple属性即可,并用数组接收选择的数据]</p>
    <div id='app9'>
        <select v-model="selected" multiple style="width: 50px;">
            <option>A</option>
            <option>B</option>
            <option>C</option>
        </select>
        <br>
        <span>Selected: {{ selected }}</span>
    </div>

    <hr>
    <p>选择框[v-for动态渲染:实现选择的是key,实质上是value值,同样也可以实现多选]</p>
    <div id='app10'>
        <select v-model="selected">
            <option v-for="option in options" v-bind:value="option.value">
                {{ option.text }}
            </option>
        </select>
        <span>Selected: {{ selected }}</span>
    </div>

    <hr>
    <p>值绑定</p>
    <div id='app11'>
        <p>picked:{{picked}}</p>
        单选按钮值绑定
        <input type="radio" v-model='picked' value="已选择">

        <p>picked1:{{picked1}}</p>
        复选框:值为true或者false
        <input type="checkbox" v-model='picked1'>

        <p>picked2:{{picked2}}</p>
        选择框:选择相应选项时,值为相对应的
        <select v-model='picked2'>
            <option :value="data_A">A</option>
            <option value="b">B</option>
            <option value="c">C</option>
        </select>

        <br>
        <p>toggle:{{toggle}}</p>
        复选框:[选中为yes,否则为no]
        <input type="checkbox" v-model="toggle" true-value="yes" false-value="no">

        <br>
        <p>a:{{a}}</p>
        <p>pick:{{pick}}</p>
        单选框:[选中后的值为data数据中的某个值]
        <input type="radio" v-model='pick' :value='a'>

        <br>
        <p>selected:{{selected}}</p>
        选择框
        <select v-model="selected">
            <!-- 内联对象字面量 -->
          <option v-bind:value="{ number: 1 }">1</option>
          <option v-bind:value="{ number: 2 }">2</option>
        </select>
    </div>

    <hr>
    <p>修饰符</p>
    <div id='app12'>
        <p>msg:{{msg}}</p>
        .lazy:通俗点讲就是等完全输入后再同步,而不是边输边同步
        <br>
        <input v-model.lazy='msg'>

        <br>
        .number:将输入值转化为数值[因为即使在 type="number" 时,HTML 输入元素的值也总会返回字符串]
        <br>
        <input v-model.number='msg1' type="number">

        <br>
        .trim:过滤用户输入的首尾空白字符
        <p>msg3:{{msg3.length}}</p>
        <input v-model='msg3' type="text">

    </div>


</body>
<script>
    var o = new Vue({
        el: '#app1',
        data: {
            url: 'https://www.baidu.com',
            imgUrl: './images/vue.jpg',
            classUrl: 'bt',
            a: true,
            b: true,
            c: 'ct',
            d: 'dt',
            e: 'red',
            f: '30px',
            g: { color: "black" },
            h: { fontSize: "30px" }
        }
    })
    var s = new Vue({
        el: '#app2',
        data: {
            input: '请输入你的名字',
            input1: 'v-model原理分析'
        },
        methods: {
            fn(e) {
                console.log(e)
                this.input1 = e.target.value
            }
        }
    })

    var t = new Vue({
        el: '#app3',
        data: {
            mes3: ''
        }
    })
    var f = new Vue({
        el: '#app4',
        data: {
            mes4: ''
        }
    })

    var s2 = new Vue({
        el: '#app5',
        data: {
            mes5: ''
        }
    })

    var s3 = new Vue({
        el: '#app6',
        data: {
            checkedNames: []
        }
    })

    var s4 = new Vue({
        el: '#app7',
        data: {
            picked: ''
        }
    })

    var s5 = new Vue({
        el: '#app8',
        data: {
            selected: ''
        }
    })

    var s6 = new Vue({
        el: '#app9',
        data: {
            selected: []
        }
    })

    var s7 = new Vue({
        el: '#app10',
        data: {
            selected: '',
            options: [
                { text: 'One', value: 'A' },
                { text: 'Two', value: 'B' },
                { text: 'Three', value: 'C' }
            ]
        }
    })

    var d = new Vue({
        el: '#app11',
        data: {
            data_A: '将Vue数据与选项动态绑定',
            picked: '',
            picked1: '',
            picked2: '',

            toggle:'',

            a:'谢谢你选择me',
            pick:'',

            selected:[]
        }
    })

    var d1 = new Vue({
        el: '#app12',
        data: {
            msg:'',
            msg1:'',
            msg3:''
        }
    })


</script>

</html>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./js/vue.js"></script>
    <link rel="stylesheet" type="text/css" href="./css/clock.css" />
</head>

<body>
    <p>v-cloak:全部编译完成后再显示,防止闪屏现象</p>
    <div id="app1" v-cloak>
        {{msg}}
    </div>

    <hr>
    <p>v-once:只渲染一次,用于优化更新性能[元素/组件及其所有的子节点将被视为静态内容并跳过]</p>
    <div id="app2">
        <!-- 单个元素 -->
        <span v-once>This will never change: {{msg}}</span>
        <input type="tetx" v-model='msg'>

        <!-- 有子元素 -->
        <div v-once>
            <h4>comment</h1>
                <p>{{msg}}</p>
        </div>

        <!-- `v-for` 指令-->
        <ul>
            <li v-for="i in list" v-once>{{i}}</li>
        </ul>

    </div>
</body>
<script>
    var a = new Vue({
        el: '#app1',
        data: {
            msg: '很hao看'
        }
    })

    var a1 = new Vue({
        el: '#app2',
        data: {
            msg: '初始值',
            list:['a','b','c']
        }
    })
</script>

</html>

过滤器

场景: 格式化 data 数据(日期格式/货币格式/大小写等)

分类: 本地(局部) 和全局 [ 全局是所有实例均可使用 ]

//本地:
filters: { 
	过滤器名字:function (value) {
    	return .... 
    }
}
//全局:
// 如何注册一个全局过滤器
Vue.filter("过滤器名字", function(value) { 
	return ......
	}
);

使用

// 过滤器应该被添加在尾部 每个过滤器用管道符分隔 
// 第一种用法在双花括号中 {{ 数据 | 过滤器名字 }}

// 第二种用法在 v-bind 中 :任何需要的地方都可以使用v-bind进行过滤得到数据
<div v-bind:id="数据 |过滤器名字 "></div>

基本用法实例

  1. 在创建 Vue 实例 之前 定义全局过滤器 Vue.filter()

  2. 在实例的 filter 选项中定义局部过滤器

  3. 在视图中通过 {{数据 | 过滤器名字}} 或者 v-bind 使用过滤器

// 如何注册一个全局过滤器
Vue.filter("过滤器名字", function(value) {
	return value.charAt(0).toUpperCase() + value.substr(1); 
}); 

// 如果注册一个局部过滤器 
filters: { 
	过滤器名字:function (value) {
    	return value.charAt(0).toUpperCase() + value.substr(1);
       }
}

过滤器:传参数和串联使用

过滤器可以传递参数,接收的第一个参数永远是前面传递过来的过滤值

过滤器也可以多个串行起来并排使用

// 多个过滤器用 | 分割
<p>{{count|a('元')|b}}</p>

// 定义过滤器 
filters:{ 
	// 第一个参数永远是前面传递过来的过滤值
    a:function(val,y){
    // val 是count值
    // y 是‘元’ 
	} 
}

注意点:
练习代码中实例中使用了 js的一些方法

  • charAt(0):获取数组第一个元素
  • toLowerCase():用于把字符串转换为小写
  • toUpperCase():将字符串小写字符转换为大写
  • slice():从已有的数组中返回选定的元素

任务 :实现一个过滤器,假如是数字 100 过滤完出来是100元人民币 :见全部代码实例


表格案例补充1:使用过滤器完成日期格式处理

步骤:格式化需要借助第三方插件

第三方插件 moment.js 官方下载地址:

http://momentjs.cn/
  1. 引入第三方格式化日期插件 moment.js

  2. 定义格式化日期过滤器

  3. 实现其格式化功能

  4. 使用过滤器

//全局过滤器:格式化日期
Vue.filter("formatDate",function(t){
        return moment(t).format('YYYY-MM-DD h:mm:ss a'); 
    })

使用:

<tr v-for="(l,index) in list" :key="index">
    <td>{{index+1}}</td>
    <td>{{l.name}}</td>
    <td>{{l.date|formatDate}}</td>
    <td>
    	<a href="#" @click.prevent="deleItem(index)">删除</a>
    </td>
</tr>

ref 属性:获取DOM

使用: 给元素定义 ref 属性, 然后通过 $refs.名称 来获取 dom 对象

<input type="text" ref="txt">// 定义ref
// 获取DOM的value值
methods: { 
	getVal() { 
	//获取dom 
	console.log(this.$refs.txt)
	}
}

任务: 通过ref功能,点击按钮时获取 inpu t的 value 值:见全部代码实例


自定义指令

使用场景: 需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令

分类: 全局指令和局部指令

全局自定义指令:

  1. 在创建 Vue 实例之前定义全局自定义指令 Vue.directive(参数1,参数2)

    第一个参数是指令名称

    第二参数是一个对象

    ​ 对象中要实现 inserted 方法

    ​ inserted 方法中的参数为当前指令 所在元素的 DOM 对象

// 1.注册一个自定义指令
Vue.directive( '指令名称' , { 
	inserted(参数){ 
	//参数为使用指令的DOM 
		//操作
 	} 
})

// 2.使用自定义指令 
<input type="text" v-指令名称>

实例: v-focus 加载页面完成时自动获取表单光标

// 示例(全局自动聚焦的自定义指令)
Vue.directive("focus", {
	inserted(el) {
    	el.focus(); 
    }
});

// 使用自定义指令 
<input type="text" v-focus>

局部自定义指令

//局部指令在vue实例内部定义
directives: { 
	"focus": {
    	inserted(dom) {
        	dom.focus();
          }
     }
}

// 调用
<input type="text" v-focus>

全部练习代码 2:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./js/vue.js"></script>
    <link rel="stylesheet" type="text/css" href="./css/clock.css" />
</head>

<body>
    <p>v-cloak:全部编译完成后再显示,防止闪屏现象</p>
    <div id="app1" v-cloak>
        {{msg}}
    </div>

    <hr>
    <p>v-once:只渲染一次,用于优化更新性能[元素/组件及其所有的子节点将被视为静态内容并跳过]</p>
    <div id="app2">
        <!-- 单个元素 -->
        <span v-once>This will never change: {{msg}}</span>
        <input type="tetx" v-model='msg'>

        <!-- 有子元素 -->
        <div v-once>
            <h4>comment</h1>
                <p>{{msg}}</p>
        </div>

        <!-- `v-for` 指令-->
        <ul>
            <li v-for="i in list" v-once>{{i}}</li>
        </ul>
    </div>

    <hr>
    <p>过滤器:全局和本地[同时存在时优先使用本地]</p>
    <p>使用方法一:{{属性名称 | 过滤器名称}}</p>
    <div id="app3">
        {{ msg | capitalize}}
    </div>

    <hr>
    <p>使用方法二:v-bind:id="属性名称 | 过滤器名称"</p>
    <div id="app4">
        <input type="text" v-bind:value='msg | capitalize'>
    </div>

    <hr>
    <p>过滤器的串联用法:{{ message | filterA | filterB }}</p>
    <div id="app5">
        {{money | funA('元') | funB('人命币')}}
    </div>

    <hr>
    <p>ref属性的使用</p>
    <div id="app6">
        <input type="text" ref='tx'>
        <button v-on:click='fun()'>提交</button>
    </div>

    <hr>
    <p>自定义指令:本地指令+全局指令[指令名称_操作的对象]</p>
    <div id="app7">
        <input type="text" v-focus1>
    </div>

</body>
<script>

    //全局过滤器
    Vue.filter('capitalize', function (value) {
        if (!value) return ''
        value = value.toString()
        return value.charAt(0).toUpperCase() + value.slice(1)
    })

    //自定义全局指令
    Vue.directive('focus1',{
        inserted:function(dom){
            dom.focus()
        }
    })


    var a = new Vue({
        el: '#app1',
        data: {
            msg: '安徽很hao看'
        }
    })

    var a1 = new Vue({
        el: '#app2',
        data: {
            msg: '初始值',
            list: ['a', 'b', 'c']
        }
    })

    var b1 = new Vue({
        el: '#app3',
        data: {
            msg: 'helloA'
        },
        //本地过滤器
        filters: {
            capitalize: function (value) {
                // if (!value) return ''
                // value = value.toString()
                return value.charAt(-1).toLowerCase() + value.slice(0)
            }
        }
    })

    var b2 = new Vue({
        el: '#app4',
        data: {
            msg: 'zbcd'
        }
    })

    var b3 = new Vue({
        el: '#app5',
        data: {
            money: 100
        },
        filters: {
            funA: function (v1, v2) {
                return v1 + v2
            },
            funB: function (v3, v4) {
                return v3 + v4
            }
        }
    })

    var c1 = new Vue({
        el: '#app6 ',
        data: {
            msg: ['a', 'b']
        },
        methods: {
            fun: function () {
                console.log(this.$refs.tx.value)
            }
        },
        //钩子函数:加载页面完成时自动获取表单光标
        mounted() {
            this.$refs.tx.focus()
        }

    })

    var c2 = new Vue({
        el: '#app7 ',
        data: {
            msg: ''
        },
        //自定义本地指令
        directives: {
            focus: {
                inserted: function (el) {
                    el.focus()
                }
            }
        }

    })


</script>

</html>

表格案例补充2:使用自定义指令完成自动获取焦点功能

定义:

//自定义全局指令:自动获取焦点
    Vue.directive('focus',{
        inserted:function(el){
            el.focus()
        }
    })

使用:

<input type="text" v-model="name" v-focus>

Vue 基本使用3

计算属性

场景: 当表达式过于复杂的情况下 可以采用计算属性 对于任何复杂逻辑都可以采用计算属性

基本使用

使用: 在 Vue 实例选项中 定义 computed:{ 计算属性名: 带返回值 的函数 }

示例: 通过计算属性实现字符串的翻转

  1. 定义数据对象
  2. 实现计算属性方法
  3. 使用计算属性
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./js/vue.js"></script>
    <script src="./js/axios.js"></script>
</head>

<body>
    <h4>计算属性:computed [ 不需要写(),必须return数据,与data数据进行关联 ]</h3>
    <div id="app1" >
        msg:{{msg}}
        <br>
        reversedMsg:{{reversedMsg}}
    </div>
</body>
<script>
    var d1 = new Vue({
        el: '#app1',
        data: {
            msg: 'hello'
        },
        computed:{
            reversedMsg(){
                return this.msg.split('').reverse().join('')
            }
        }
    })
</script>
</html>

计算属性 和 methods 方法的区别

  1. 计算属性不需要调用形式的写法 而 methods 方法必须采用 方法() 调用的形式

  2. 计算属性依赖 data 中的数据变化,如果 data 并没有发生变化,则计算属性则会取缓存的结果

  3. methods 不论 data 变化与否 只要调用 都会重新计算

注意: 当数据对象中 message发生变化时 计算属性也会重新计算计算=> 改变页面视图


表格案例补充3:用计算属性实现商品搜索

需求: 搜索框内容变化 => 列表内容变化

思路 :使用计算属性实现品牌搜索

1.定义品牌搜索的内容

2.定义计算属性

3.实现计算属性的筛选功能(筛选列表)

4.计算属性替换原有的列表数据

// 计算属性实现搜索功能:输入字符搜索时,显示相应内容
//用到了 数组的 filter 筛选功能 和 字符串的 startsWith 校验功能
        computed: {
            searchList() {
                return this.list.filter((v) => {
                    return v.name.startsWith(this.searchValue)
                })
            }
        }

Vue 中实现发送网络请求

原理 : Vue.js 中发送网络请求本质还是 ajax,我们可以使用插件方便操作

  1. vue-resource: Vue.js的插件,已经不维护,不推荐使用
  2. axios : 不是 vue 的插件 ,可以在任何地方使用,推荐

axios 官方文档地址:

http://www.axios-js.com/zh-cn/docs/

说明 : 既可以在 浏览器端 又可以在 node.js 中使用的发送 http 请求的库,支持Promise ,不支持 jsonp

axios 基本使用

用法:

// 基本用法 
axios.get(url).then((res) => {
	// 请求成功 会来到这 res响应体
}).catch((err) => {
    // 请求失败 会来到这 处理err对象
})
// 获取
axios.get('http://localhost:3000/brands').then().catch() 

// 删除
axios.delete('http://localhost:3000/brands/1').then().catch() 

// 添加
axios.post('http://localhost:3000/brands', {name: '小米', date: new Date()}).then().catch() 

// 修改
axios.put('http://localhost:3000/brands/1', {name: '小米', date: new Date()}).then().catch()

// get模糊搜索
axios.get("http://localhost:3000/brands?name_like=" + "aaa").then().catch()

json-server 的基本使用

快速创建一个接口,以供 ajax 请求

参考官方文档:

https://github.com/typicode/json-server

watch 基本使用

特性: 监听 data 数据变化时 自动触发函数

计算属性 和 watch 区别:

  • 计算属性 必须要有返回值 所以说不能写异步请求 因为有人用它的返回值(插值表达式)
  • watch 选项中可以写很多逻辑 不需要返回值 因为没有人用它的返回值
// 基本用法
data: {
	msg: 'abc'
},
watch: {
	// data中的属性msg发生改变时 自动触发该函数
    msg(newV, oldV) {
    	console.log(newV, oldV) 
    }
}

表格案例补充4:axios 列表实现增删改查 + 搜索

列表显示商品

目标: 将表格案例中列表数据实现用 axios 请求

思路: 使用 axios 请求列表

  1. 引入axios
  2. 在 mounted(相当于window.onload)函数中 发送请求获取数据
  3. 获取的数据赋值给 list 列表
//显示所有数据
showDate() {
    axios.get('http://localhost:3000/brands').then((rep) => {
        this.list = rep.data
        console.log(this.list)
    }).catch((error) => {
        console.log(error)
    })
},

//axios实现ajax请求,json-server模拟接口
// mounted函数 加载完 DOM 再执行的函数 相当于window.onload
mounted() {
	this.showDate()
}

删除商品

需求: 使用 axios 删除商品

1.删除方法中传入ID

2.删除方法中调用删除接口

3.删除完成后重新调用获取数据

deleteJson(index) {
    if (confirm('是否删除')) {
        axios.delete('http://localhost:3000/brands/' + (index + 1)).then((rep) => {
		// 重新调用拉取数据
            this.showDate()
        }).catch((error) => {
            console.log(error)
        })
    }
},

添加商品

需求: 使用 axios 添加商品

  1. 添加方法中调用新增接口
  2. 添加成功后重新拉取数据
  3. 清空输入框
	//添加指定数据
    addJson(name) {
        axios.post('http://localhost:3000/brands', {
            id: this.list.length + 1,
            name: this.name,
            date: new Date()
        }).then((rep) => {
            if (rep.status == 201) {
                this.showDate();// 重新拉取数据
                this.name = ""; // 清空文本框
            }
        }).catch((error) => {
            console.log(error)
        })
    }

axios 实现搜索功能

目标: 通过分析得出 计算属性中不能进行搜索功能的结论

分析 :计算属性=> 异步操作搜索=>返回结果 XXXXX 走不通

结论: 搜索功能不能采用 计算属性 进行解决 注意: 计算属性中一定是 同步操作 ,如果有 异步 操作,则该业务逻辑就会失败

解决办法: 当需要根据数据变化 进行相应业务操作,且该操作是 异步操作 时,计算属性不能再使用,可以使用侦听 属性watch

步骤:

  1. 监听搜索内容
  2. 在监听函数中 发送 axios 请求实现模糊搜索
  3. 把返回值赋值给 list 列表
watch:{
    //监听搜索值:一旦发生变化,就触发函数
    searchValue(newValue,oldValue){
        axios.get('http://localhost:3000/brands?name_like='+ newValue).then((rep)=>{
            this.list = rep.data
        }).catch((error) => {
            console.log(error)
        })
    }
},

表格案列完整代码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #app {
            width: 600px;
            margin: 10px auto;
        }

        .tb {
            border-collapse: collapse;
            width: 100%;
        }

        .tb th {
            background-color: #0094ff;
            color: white;
        }

        .tb td,
        .tb th {
            padding: 5px;
            border: 1px solid black;
            text-align: center;
        }

        .add {
            padding: 5px;
            border: 1px solid black;
            margin-bottom: 10px;
        }
    </style>
    <script src="./js/vue.js"></script>
    <script src="./js/moment.js"></script>
    <script src="./js/axios.js"></script>
</head>

<body>
    <div id="app">
        <div class="add">
            品牌名称:
            <input type="text" v-model="name" v-focus>
            <input :disabled="name.length===0" @click="addJson" type="button" value="添加">
        </div>

        <div class="add">
            品牌名称:
            <input type="text" placeholder="请输入搜索条件" v-model='searchValue'>
        </div>

        <div>
            <table class="tb">
                <tr>
                    <th>编号</th>
                    <th>品牌名称</th>
                    <th>创立时间</th>
                    <th>操作</th>
                </tr>
                <tr v-for="(l,index) in searchList" :key="index">
                    <td>{{index+1}}</td>
                    <td>{{l.name}}</td>
                    <td>{{l.date|formatDate}}</td>
                    <td>
                        <a href="#" @click.prevent="deleteJson(index)">删除</a>
                    </td>
                </tr>
                <tr>
                    <td colspan="4" v-if='searchList.length == 0'>没有品牌数据</td>
                </tr>
            </table>
        </div>
    </div>
</body>
<script>
    //全局过滤器:格式化日期
    Vue.filter("formatDate", function (t) {
        return moment(t).format('YYYY-MM-DD h:mm:ss a');
    })

    //自定义全局指令:自动获取焦点
    Vue.directive('focus', {
        inserted: function (el) {
            el.focus()
        }
    })

    var app = new Vue({
        el: '#app',
        data: {
            name: '', //绑定input输入
            list: [{ name: '张三', date: '2020-01-01' }, { name: '李四', date: '2020-02-02' }],
            searchValue: ''
        },
        methods: {
            //添加方法
            addItem() {
                //从数组前面添加
                this.list.unshift({
                    name: this.name,
                    date: new Date()
                })
                this.name = ''
            },
            //删除方法
            deleItem(index) {
                //询问是否删除
                if (confirm('是否删除')) {
                    this.list.splice(index, 1)
                }
            },

            //显示所有数据
            showDate() {
                axios.get('http://localhost:3000/brands').then((rep) => {
                    this.list = rep.data
                    console.log(this.list)
                }).catch((error) => {
                    console.log(error)
                })
            },
            //删除指定数据
            deleteJson(index) {
                if (confirm('是否删除')) {
                    axios.delete('http://localhost:3000/brands/' + (index + 1)).then((rep) => {
                        this.showDate()
                    }).catch((error) => {
                        console.log(error)
                    })
                }
            },
            //添加指定数据
            addJson(name) {
                axios.post('http://localhost:3000/brands', {
                    id: this.list.length + 1,
                    name: this.name,
                    date: new Date()
                }).then((rep) => {
                    if (rep.status == 201) {
                        this.showDate();// 重新拉取数据
                        this.name = ""; // 清空文本框
                    }
                }).catch((error) => {
                    console.log(error)
                })
            }
        },
        // 计算属性实现搜索功能:输入字符搜索时,显示相应内容
        computed: {
            searchList() {
                return this.list.filter((v) => {
                    return v.name.startsWith(this.searchValue)
                })
            }
        },
        watch:{
            //监听搜索值
            searchValue(newValue,oldValue){
                axios.get('http://localhost:3000/brands?name_like='+ newValue).then((rep)=>{
                    this.list = rep.data
                }).catch((error) => {
                    console.log(error)
                })
            }
        },

        //axios实现ajax请求,json-server模拟接口
        mounted() {
            this.showDate()
        }
    })
</script>

</html>

组件

场景: 重复的页面结构,数据,逻辑 都可以抽提成一个组件

特点: 简单 高效 不重复


组件和实例比较

相似之处: data/methods/computed/watch 等一应俱全

注意:

  • data 和 Vue 实例的区别为 组件中 data 为一个函数且需要返回一个对象
  • 组件没有 el 选项
  • template 代表其 页面结构 (有且只要一个根元素)

每个组件都是 独立 的 运行作用域、数据、逻辑没有任何关联


全局组件与局部组件

注册方式不同 应用范围不同

注意: 注意命名规范

​ 注册组件名称: 推荐 小写字母 加横向的结构


全局组件

步骤: 实现一个全局组件

  1. 定义一个全局组件
  2. 写入组件选项
  3. 使用组件
注意:
//data 中必须为一个返回对象的函数
// template 必须有且只有一个根元素

Vue.component("content-a", {
    template: `<div> {{count}} </div>`, 
	data() {
        return { 
			count: 1 
		};
    }
}); 

//使用
<content-a></content-a>

任务: 实现一个全局组件 完成 加减步进器:见全部代码实例


局部组件

步骤: 实现一个局部组件

  1. 在实例选项 compoents中 定义局部组件名字

  2. 在组件名字相对应的对象中定义选项(template、data()、…)

  3. 在实例视图中使用组件

components: { 
	"z-j": {
		template: `<div>我是z-j组件--{{msg}}</div>`,
		data() {
        	return {
            	msg: "abc" 
            	}
            }
      }
}      

//使用
<div id="app">
	<z-j></z-j>
</div>

组件嵌套

全局组件 嵌套 全局组件
Vue.component('all-a', {
        template: `
        <div>
            全局组件a
        </div>
         `
    })
    
Vue.component('all-b', {
        template: `
        <div>
            全局组件b
        </div>
         `
    })

Vue.component('all', {
        template: `
        <div>
           <all-a></all-a>
           <all-b></all-b>
        </div>
         `
    })
局部组件 嵌套 全局组件
let d4 = new Vue({
        el: '#app4',
        data: {
            count: 100
        },
        //自定义局部组件
        components: {
            'half-all': {
                template: `
                <div>
                    <all></all>
                </div>
                `
            }
        }
    })

注意: 组件嵌套和全局及局部组件没关系


组件通信

组件通信的几种情况:

组件嵌套 => 父子组件 => 父组件传递数据给子组件使用 => 组件之间的传值 => 也叫组件之间的通信

组件之间的通信根据关系的可以分为:

  1. 父子组件通信

    父组件到子组件

    子组件到父组件

  2. 兄弟组件通信


父子组件传值:props属性

父子组件的传值有多种方法, 兄弟组件的通信也有自己的写法

步骤:

  1. 子组件的 props 属性值是一个数组

  2. 数组中的值 绑定为子组件上的属性 用来接受父组件的传值

  3. 在子组件的 template中 就可以使用 绑定的属性 拿到父组件传递的值

<script>
// 调用组件
<div id="app-1">
        <person v-for='(p,index) in list' :key='index' :name='p.name' :age='p.age'></person>
</div>


// 子组件
Vue.component('person', {
        props: ['name', 'age'],
        template: '<h3>{{ name }}:{{age}}</h3>'
})
    
//父组件(根组件)
new Vue({
    el: '#app-1',
    data: {
        list: [{ 'name': '张三', 'age': 18 }, { 'name': '李四', 'age': 20 }]
    }
})

组件和模块的区别

模块:侧重于功能或者数据的封装

组件:包含了 template、style 和 script,而它的 script 可以由各种模块组成


完整练习代码 3:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./js/vue.js"></script>
</head>

<body>
    <h3>组件:本地组件与全局组件</h3>
    <div id="app1">
        <cp></cp>
        <component-step>
            </componemt-step>
    </div>

    <hr>
    <h4>使用全局组件:实现增减操作</h4>
    <div id="app2">
        <component-step>
            </componemt-step>
            <cp></cp>
    </div>

    <hr>
    <h4>全局组件嵌套全局组件</h4>
    <div id="app3">
        <all></all>
    </div>

    <hr>
    <h4>局部组件嵌套全局组件</h4>
    <div id="app4">
        <half-all></half-all>
    </div>

    <hr>
    <h4>组件之间的通信:父子组件使用props</h4>
    <div id="blog-post-demo">
        <blog-post v-for="post in posts" v-bind:key="post.id" v-bind:title="post.title"></blog-post>
    </div>

    <hr>
    <h4>组件之间的通信:动态绑定传值</h4>
    <div id="app-1">
        <person v-for='(p,index) in list' :key='index' :name='p.name' :age='p.age'></person>
    </div>
</body>
<script>

    //自定义全局组件
    Vue.component('component-step', {
        data() {
            return {
                count: 1
            }
        },
        methods: {
            add() {
                this.count++;
            },
            dec() {
                this.count--;
            }
        },
        template: `
        <div>
            <button @click='add'>增加</button>
            <p>{{count}}</p>
            <button v-on:click='dec'>减少</button>
        </div>
         `
    })
   
    Vue.component('all-a', {
        template: `
        <div>
            全局组件a
        </div>
         `
    })
    Vue.component('all-b', {
        template: `
        <div>
            全局组件b
        </div>
         `
    })
    Vue.component('all', {
        template: `
        <div>
           <all-a></all-a>
           <all-b></all-b>
        </div>
         `
    })

    Vue.component('blog-post', {
        props: ['title'],
        template: '<h3>{{ title }}</h3>'
    })

    Vue.component('person', {
        props: ['name', 'age'],
        template: '<h3>{{ name }}:{{age}}</h3>'
    })


    let d1 = new Vue({
        el: '#app1',
        data: {
            msg: ''
        },
        //自定义局部组件
        components: {
            'cp': {
                data() {
                    return {
                        name: 'Lisi'
                    }
                },
                template: `<div>{{name}}</div>`
            }
        }
    })

    let d2 = new Vue({
        el: '#app2',
        data: {
            msg: ''
        },
    })

    let d3 = new Vue({
        el: '#app3',
        data: {
            msg: ''
        },
    })

    let d4 = new Vue({
        el: '#app4',
        data: {
            count: 100
        },
        //自定义局部组件
        components: {
            'half-all': {
                template: `
                <div>
                    <all></all>
                </div>
                `
            }
        }
    })

    new Vue({
        el: '#blog-post-demo',
        data: {
            posts: [
                { id: 1, title: 'My journey with Vue!' },
                { id: 2, title: 'Blogging with Vue!' },
                { id: 3, title: 'Why Vue is so fun!' }
            ]
        }
    })

    new Vue({
        el: '#app-1',
        data: {
            list: [{ 'name': '张三', 'age': 18 }, { 'name': '李四', 'age': 20 }]
        }
    })


</script>

</html>

Vue 基本使用4

单页应用(简称SPA)

传统模式: 每个页面及其内容都需要从服务器一次次请求 如果网络差, 体验则会感觉很慢

SPA模式 :第一次 加载 会将所有的资源都请求到页面 模块之间切换 不会再请求服务器


SPA优点:

  1. 用户体验好,因为前端操作几乎感受不到网络的延迟
  2. 完全组件化开发 ,由于只有一个页面,所以原来属于一个个页面的工作被归类为一个个 组件

SPA缺点:

  1. 首屏 加载慢(可以只加载所需部分)
  2. 不利于 SEO ( 服务端渲染 可以解决)
  3. 开发难度高 (框架)

原理:

  • 可以通过页面地址的锚链接来实现 spa
  • hash(锚链接)位于链接地址 # 之后
  • hash 值的改变 不会触发 页面刷新
  • hash 值是 url 地址的一部分,会存储在页面地址上 我们可以获取到
  • 可以通过 事件监听 hash 值得改变
  • 拿到了 hash 值,就可以根据不同的 hash 值进行不同的 内容切换

Vue 路由

js 实现路由

需求:采用 hash 值改变 的特性来进行前端路由切换

步骤

  1. 实现导航结构(’#/a’)

  2. onhashchange 事件监听 hash 值的改变

  3. 获取 hash 值 根据值的不同 改变视图内容

<h3>js 实现前端路由原理</h3>
<a href="#/a">a</a>
<a href="#/b">b</a>
<a href="#/c">c</a>

<div id="app1"></div>


//js 根据路径参数hash值的不同显示不同的内容
var obx = document.getElementById('app1')
window.onhashchange = function () {
    let hash = location.hash
    console.log(hash)
    hash = hash.replace('#/', '')
    console.log(hash)
    switch (hash) {
        case "a":
            obx.innerText = 'A'
            break
        case 'b':
            obx.innerText = 'B'
            break
        case 'c':
            obx.innerText = 'C'
            break
        default:
            break
    }
}

注意:

  • onhashchange 事件在当前 URL 的锚部分(以 ‘#’ 号为开始) 发生改变时触发
  • location.hash:hash 属性是一个可读可写的字符串,该字符串是 URL 的锚部分(从 # 号开始的部分)
  • replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串

vue 实现 router 路由

  • Vue-Router 是 Vue.js 官方的路由管理器
  • 它和 Vue.js 的核心深度集成,让构建单页面应用变得简单
  • 实现根据不同的请求地址 而 显示不同的内容
  • 如果要使用 vue 开发项目,前端路由功能 必须使用 vue-router 来实现

路由详细使用参照官方文档:

https://router.vuejs.org/zh/installation.html

安装

三种安装方式:

  1. CDN
  2. 本地文件
  3. npm

注意: 本地文件引入 vue-router ,一定要先引入 vue.js,再引入vue-router


基本使用

  1. 导入 vue 和 vue-router

  2. 设置 HTML中的内容

  3. 实例化路由对象,配置路由规则

  4. 创建路由对应的组件

  5. 把 router 实例挂载到 vue 实例上


<p>Vue Router:路由的实现[ 类似a标签]</p>
<div id="app2">
    <p>
        <!-- 使用 router-link 组件来导航. -->
        <!-- 通过传入 `to` 属性指定链接. -->
        <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
        <router-link to="/foo">Go to Foo</router-link>
        <router-link to="/bar">Go to Bar</router-link>
    </p>

    <!-- 路由出口 -->
    <!-- 路由匹配到的组件将渲染在这里 -->
    <router-view></router-view>
</div>


<script>
    // 1. 定义 (路由) 组件。
    const Foo = {
        template: '<div>foo</div>'
    }
    const Bar = {
        template: '<div>bar</div>'
    }

    // 2. 定义路由 
    const routes = [
        { path: '/foo', component: Foo },
        { path: '/bar', component: Bar }
    ]

    // 3. 创建 router 实例,然后传 `routes` 配置
    const router = new VueRouter({
        routes // (缩写) 相当于 routes: routes
    })

    // 4. 创建和挂载根实例。
    // 记得要通过 router 配置参数注入路由,
    // 从而让整个应用都有路由功能
    const app = new Vue({
        el: '#app2',
        router
    })
</script>

动态路由

点击 列表页 跳转到 详情页 时,跳转的链接需要携带参数,会导致 path 不同

当 path 不同却需要对应同一个组件时,需要用到动态路由这一概念

步骤:

  1. 标签上传入不同的值
<router-link to="/item/8">小米</router-link>
<router-link to="/item/9">华为</router-link>

<router-view> </router-view>
  1. 路由规则中 尾部 添加动态参数 id
{ path: '/item/:id', component: Items }
  1. 在组件内部可以使用 $route.params 获取当前路由对象的动态参数
let Items = {
	template: '<div>我是商品详情页 {{ $route.params.id }}</div>', 
	mounted: {
		console.log(this.$route.params.id); 
	}
}

vue-router :to 属性赋值

to 有多种赋值方式

<!-- 常规跳转 -->
<!-- <router-link to="/aaa">aaa</router-link> -->

<!-- 变量 -->
<!-- <router-link :to="bbb">bbb</router-link> --> 

<!-- 根据对象name跳转 --> (注意:name值是字符串) 
<!-- <router-link :to="{name:'ccc'}">ccc</router-link> --> 

<!-- 根据对象path跳转 -->(注意:必须得加上/ 不然容易错乱) 
<!-- <router-link :to="{path:'/ddd'}">ddd</router-link> -->

<!-- 带参数的跳转 --> (注意获取参数route 不要写成router)
<!--<router-link :to="{name:'eee',params:{id:1}}">体育</router-link> -->

<router-view></router-view>

更多实例详见全部联系代码


vue-router:重定向

场景: 当希望某个页面被强制中转时 可采用 redirect 进行路由重定向设置

重定向也是通过 routes 配置来完成,下面例子是从 /a 重定向到 /b

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: '/b' }
  ]
})

重定向的目标也可以是一个命名的路由:

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: { name: 'foo' }}
  ]
})

更多实例见官网文档


vue-router:编程式导航

场景: 点击的时候路由实现跳转

除了使用 `` 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现

methods: {
        goPage() {
            this.$router.push({
                path: "/news"
            });
        }
    }

路由的激活样式

当前路由在导航中是拥有激活class样式的

审查导航元素,可以发现 激活样式

<a href="#/bj" class="router-link-exact-active router-link-active">北京</a>

可以根据 class 设置样式


嵌套路由

如果存在 组件嵌套 ,就需要提供多个视图容器

同时 router-link 和router-view 都可以添加类名、设定样式

步骤:

  1. 在原有的一级导航的 template 里面 配置 二级导航的 router-link 和 router-view

  2. 在相对应的一级导航路由配置选项 children 里面 配置 二级导航的路由和模板

path: '/music',
component: Music,
//子路由配置 在children选项
children: [{
	path: 'lx',
	component: Lx 
},...]

过度动画

参考官方文档:

https://cn.vuejs.org/v2/guide/transitions.html

基本用法就是给我们需要动画的标签外面嵌套 transition 标签 ,并且设置name属性

Vue 提供了 transition 的封装组件,列表中更新,移除,新增 情形中,可以给任何元素和组件添加 进入/ 离开 过渡

<transition name="fade">
	<div v-show="isShow" class="box"></div> 
</transition>

在进入/离开的过渡中,会有 6 个 class 切换。

  1. v-enter:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。
  2. v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
  3. v-enter-to2.1.8 版及以上定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。
  4. v-leave:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。
  5. v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
  6. v-leave-to2.1.8 版及以上定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。

在这里插入图片描述

对于这些在过渡中切换的类名来说,如果你使用一个没有名字的 <transition>,则 v- 是这些类名的默认前缀。如果你使用了 <transition name="my-transition">,那么 v-enter 会替换为 my-transition-enter


完整练习代码 4:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./js/vue.js"></script>
    <script src="./js/vue-router.js"></script>

    //自定义路由样式
    <style>
        .router-link-exact-active router-link-active {
            color: red;
        }

        .fade-enter-active,
        .fade-leave-active {
            transition: opacity .3s;
        }

        .fade-enter,
        .fade-leave-to

        /* .fade-leave-active below version 2.1.8 */
            {
            opacity: 0;
        }
    </style>

</head>

<body>
    <p>js 实现前端路由原理</p>
    <a href="#/a">a</a>
    <a href="#/b">b</a>
    <a href="#/c">c</a>

    <div id="app1"></div>

    <hr>
    <p>Vue Router:路由的实现[ 类似a标签]</p>
    <div id="app2">
        <p>
            <!-- 使用 router-link 组件来导航. -->
            <!-- 通过传入 `to` 属性指定链接. -->
            <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
            <router-link to="/foo">Go to Foo</router-link>
            <router-link to="/bar">Go to Bar</router-link>
        </p>
        <!-- 路由出口 -->
        <!-- 路由匹配到的组件将渲染在这里 -->
        <router-view></router-view>
    </div>

    <hr>
    <p>Vue Router动态路由:传递参数值</p>
    <div id="app3">
        <p>
            <router-link to="/home/1">传递参数1</router-link>
            <router-link to="/home/2">传递参数2</router-link>
        </p>
        <router-view></router-view>
    </div>

    <hr>
    <p>Vue Router动态路由:to属性的不同实现</p>
    <div id="app4">
        <p>
            <router-link :to="mes">点击to属性</router-link>
            <router-link :to="{name:'otherN'}">别名to方式</router-link>
            <router-link :to="{path:'/to1'}">根据path对象</router-link>
            <router-link :to="{name:'paras',params:{id:1}}">带别名和参数传递</router-link>
        </p>
        <router-view></router-view>
    </div>


    <hr>
    <p>Vue Router路由:实现重定向</p>
    <div id="app5">
        <p>
            <router-link to="/home">主页</router-link>
            <router-link to="/sec">第二页</router-link>
        </p>
        <router-view></router-view>
    </div>

    <hr>
    <p>Vue Router路由:编程式导航实现 [ 另外一种实现方式]</p>
    <div id="app6">
        <p>
            <button v-on:click='step()'>跳转</button>
        </p>
        <router-view></router-view>
    </div>

    <hr>
    <p> 路由激活样式,使用router-link后,
        在点击时,会增加class:router-link-exact-active router-link-active
        根据该class属性可以实现自定义
    </p>

    <hr>
    <p>嵌套路由</p>
    <div id="app7">
        <p>
            <router-link to='/user/profile'>点击使用嵌套路由</router-link>
        </p>
        <router-view></router-view>
    </div>

    <hr>
    <p>过渡动画</p>
    <div id="app8">
        <button v-on:click="show = !show">
            点击
        </button>
        <transition name="fade">
            <p v-if="show">hello</p>
        </transition>
    </div>

</body>
<script>
    //js 根据路径参数hash值的不同显示不同的内容
    var obx = document.getElementById('app1')
    window.onhashchange = function () {
        let hash = location.hash
        console.log(hash)
        hash = hash.replace('#/', '')
        console.log(hash)
        switch (hash) {
            case "a":
                obx.innerText = 'A'
                break
            case 'b':
                obx.innerText = 'B'
                break
            case 'c':
                obx.innerText = 'C'
                break
            default:
                break
        }
    }

    // 1. 定义 (路由) 组件。
    const Foo = {
        template: '<div>foo</div>'
    }
    const Bar = {
        template: '<div>bar</div>'
    }


    // 2. 定义路由 
    const routes = [
        { path: '/foo', component: Foo },
        { path: '/bar', component: Bar }
    ]

    // 3. 创建 router 实例,然后传 `routes` 配置
    const router = new VueRouter({
        routes // (缩写) 相当于 routes: routes
    })

    // 4. 创建和挂载根实例。
    // 记得要通过 router 配置参数注入路由,
    // 从而让整个应用都有路由功能
    const app = new Vue({
        el: '#app2',
        router
    })


    //动态路由
    const home = {
        template: `<div>我是家目录:{{$route.params.id}}</div>`,
        //页面加载完成时
        mounted() {
            console.log(this.$route.params.id)
        }
    }
    const router1 = new VueRouter({
        routes: [{
            path: '/home/:id',
            component: home
        }]
    })

    var a = new Vue({
        el: '#app3',
        router: router1
    })


    //to属性:①变量方式
    const toTo1 = {
        template: `<div>欢迎进入...1</div>`,
    }
    const toTo2 = {
        template: `<div>欢迎进入...2</div>`,
    }
    const toTo3 = {
        template: `<div>欢迎进入...3,参数值为:{{$route.params.id}}</div>`,
    }

    const routes2 = [{
        path: '/to1',
        component: toTo1
    },
    //②别名name方式
    {
        name: 'otherN',//先匹配别名
        path: '/to2',
        component: toTo2
    },

    //③别名name+参数方式
    {
        name: 'paras',//先匹配别名 
        path: '/to3/:id',//再识别参数
        component: toTo3
    },
    ]

    const router2 = new VueRouter({
        routes: routes2
    })
    var b = new Vue({
        el: '#app4',
        data: {
            mes: '/to1'
        },
        router: router2
    })


    //重定向
    //动态路由
    const home2 = {
        template: `<div>我是家目录</div>`,
    }
    const sec = {
        template: `<div>我是子目录</div>`,
    }
    const router5 = new VueRouter({
        routes: [{
            name: 'paras2',
            path: '/home',
            component: home2
        }, {
            path: '/sec',
            component: sec,
            //加上重定向
            // redirect:'/home'
            redirect: { name: 'paras2' }
        }
        ]
    })


    const pro = {
        template: `<div>编程时导航:{{$route.params.id}}</div>`,
    }

    const router6 = new VueRouter({
        routes: [{
            name: 'program',
            path: '/pro/:id',
            component: pro
        }
        ]
    })


    var b = new Vue({
        el: '#app6',
        methods: {
            step() {

                this.$router.push({
                    name: 'program',
                    params: { id: '1' }

                    //path与params一起使用,id参数会被自动忽略
                    // path:'/pro',
                    // params:{id:'1'}

                    //推荐使用:path+query组合
                    // path:'/pro',
                    // query:{id:'1'}

                })
            }
        },
        router: router6
    })


    //嵌套组件
    const User = {
        template: `
                <div class="user">
                    <h2>User {{ $route.params.id }}</h2>
                    <router-view></router-view>
                </div>
                `
    }

    const UserProfile = {
        template: `
                <div>
                    UserProfile
                </div>
                `
    }

    const UserPosts = {
        template: `
                <div>
                    UserPosts
                </div>
                `
    }

    const router7 = new VueRouter({
        routes: [
            {
                path: '/user/:id',
                component: User,
                children: [
                    {
                        // 当 /user/:id/profile 匹配成功,
                        // UserProfile 会被渲染在 User 的 <router-view> 中
                        path: 'profile',
                        component: UserProfile
                    },
                    {
                        // 当 /user/:id/posts 匹配成功
                        // UserPosts 会被渲染在 User 的 <router-view> 中
                        path: 'posts',
                        component: UserPosts
                    }
                ]
            }
        ]
    })

    new Vue({
        el: '#app7',
        router: router7
    })

    new Vue({
        el: '#app8',
        data: {
            show: true
        }
    })

</script>
</html>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值