Vue 基本使用1
Vue 安装方式
-
直接下载源码然后通过路径引入
开发版本:https://vuejs.org/js/vue.js 生产版本:https://vuejs.org/js/vue.min.js
-
在线 cdn 引入的方式
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
-
采用 npm 安装的方式
// 现状: 都会采用npm的方式来进行正式项目开发 npm install vue
注意: Vue.js 不支持 IE8 及其以下版本
简单实例
步骤 :
-
body 中,设置 Vue 管理的视图
-
引入vue.js
-
实例化 Vue 对象 new Vue()
-
设置 Vue 实例的选项:如el、data…
new Vue({选项:值});
-
在
中通过 {{ }} 使用 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 (数据对象),是响应式数据(数据驱动视图)
-
data 中的值 {数据名字:数据的初始值}
-
data 中的数据 msg/count 可以在视图中通过 {{msg/count}} 访问数据
-
data 中的数据也可以通过实例访问 vm.msg 或者 vm.$data.msg
-
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>
总结:表单实践案例
功能点:
-
添加商品
-
删除商品
-
搜索商品
-
列表循环
-
数据不存在 显示不存在数据
-
时间格式
思路:
-
静态页面准备
-
实例化一个Vue
-
定义表格数据
-
采用 v-for 循环将静态内容切换为动态内容
-
采用 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>
注意点:
-
为了防止添加数据时,输入的是空,使用
:disabled="name.length===0"
进行限制,只有输入框存在数据时,按钮才可用 -
js 中往数组前面添加元素的方法为
unshift()
,删除的方法为 splice(索引,数量) -
使用 v-for 时加上 key 元素,为其增加唯一标识
-
点击删除时,使用 .prevent 属性
-
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
场景: 解决页面初次渲染时 页面模板闪屏现象
-
写入v-cloak 指令
-
在 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>
基本用法实例 :
-
在创建 Vue 实例 之前 定义全局过滤器 Vue.filter()
-
在实例的 filter 选项中定义局部过滤器
-
在视图中通过 {{数据 | 过滤器名字}} 或者 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/
-
引入第三方格式化日期插件 moment.js
-
定义格式化日期过滤器
-
实现其格式化功能
-
使用过滤器
//全局过滤器:格式化日期
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 元素进行底层操作,这时候就会用到自定义指令
分类: 全局指令和局部指令
全局自定义指令:
-
在创建 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:{ 计算属性名: 带返回值 的函数 }
示例: 通过计算属性实现字符串的翻转
- 定义数据对象
- 实现计算属性方法
- 使用计算属性
<!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 方法的区别
-
计算属性不需要调用形式的写法 而 methods 方法必须采用 方法() 调用的形式
-
计算属性依赖 data 中的数据变化,如果 data 并没有发生变化,则计算属性则会取缓存的结果
-
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,我们可以使用插件方便操作
- vue-resource: Vue.js的插件,已经不维护,不推荐使用
- 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 请求列表
- 引入axios
- 在 mounted(相当于window.onload)函数中 发送请求获取数据
- 获取的数据赋值给 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 添加商品
- 添加方法中调用新增接口
- 添加成功后重新拉取数据
- 清空输入框
//添加指定数据
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
步骤:
- 监听搜索内容
- 在监听函数中 发送 axios 请求实现模糊搜索
- 把返回值赋值给 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 代表其 页面结构 (有且只要一个根元素)
每个组件都是 独立 的 运行作用域、数据、逻辑没有任何关联
全局组件与局部组件:
注册方式不同 应用范围不同
注意: 注意命名规范
注册组件名称: 推荐 小写字母 加横向的结构
全局组件
步骤: 实现一个全局组件
- 定义一个全局组件
- 写入组件选项
- 使用组件
注意:
//data 中必须为一个返回对象的函数
// template 必须有且只有一个根元素
Vue.component("content-a", {
template: `<div> {{count}} </div>`,
data() {
return {
count: 1
};
}
});
//使用
<content-a></content-a>
任务: 实现一个全局组件 完成 加减步进器:见全部代码实例
局部组件
步骤: 实现一个局部组件
-
在实例选项 compoents中 定义局部组件名字
-
在组件名字相对应的对象中定义选项(template、data()、…)
-
在实例视图中使用组件
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>
`
}
}
})
注意: 组件嵌套和全局及局部组件没关系
组件通信
组件通信的几种情况:
组件嵌套 => 父子组件 => 父组件传递数据给子组件使用 => 组件之间的传值 => 也叫组件之间的通信
组件之间的通信根据关系的可以分为:
-
父子组件通信
父组件到子组件
子组件到父组件
-
兄弟组件通信
父子组件传值:props属性
父子组件的传值有多种方法, 兄弟组件的通信也有自己的写法
步骤:
-
子组件的 props 属性值是一个数组
-
数组中的值 绑定为子组件上的属性 用来接受父组件的传值
-
在子组件的 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优点:
- 用户体验好,因为前端操作几乎感受不到网络的延迟
- 完全组件化开发 ,由于只有一个页面,所以原来属于一个个页面的工作被归类为一个个 组件
SPA缺点:
- 首屏 加载慢(可以只加载所需部分)
- 不利于 SEO ( 服务端渲染 可以解决)
- 开发难度高 (框架)
原理:
- 可以通过页面地址的锚链接来实现 spa
- hash(锚链接)位于链接地址 # 之后
- hash 值的改变 不会触发 页面刷新
- hash 值是 url 地址的一部分,会存储在页面地址上 我们可以获取到
- 可以通过 事件监听 hash 值得改变
- 拿到了 hash 值,就可以根据不同的 hash 值进行不同的 内容切换
Vue 路由
js 实现路由
需求:采用 hash 值改变 的特性来进行前端路由切换
步骤:
-
实现导航结构(’#/a’)
-
onhashchange 事件监听 hash 值的改变
-
获取 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
安装
三种安装方式:
- CDN
- 本地文件
- npm
注意: 本地文件引入 vue-router ,一定要先引入 vue.js,再引入vue-router
基本使用
-
导入 vue 和 vue-router
-
设置 HTML中的内容
-
实例化路由对象,配置路由规则
-
创建路由对应的组件
-
把 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 不同却需要对应同一个组件时,需要用到动态路由这一概念
步骤:
- 标签上传入不同的值
<router-link to="/item/8">小米</router-link>
<router-link to="/item/9">华为</router-link>
<router-view> </router-view>
- 路由规则中 尾部 添加动态参数 id
{ path: '/item/:id', component: Items }
- 在组件内部可以使用 $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 都可以添加类名、设定样式
步骤:
-
在原有的一级导航的 template 里面 配置 二级导航的 router-link 和 router-view
-
在相对应的一级导航路由配置选项 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 切换。
v-enter
:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。v-enter-active
:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。v-enter-to
:2.1.8 版及以上定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时v-enter
被移除),在过渡/动画完成之后移除。v-leave
:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。v-leave-active
:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。v-leave-to
:2.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>