本文最后更新于:2022-06-10T22:56:43+08:00
插值表达式
使用 v-cloak 能够解决 插值表达式闪烁的问题,[v-cloak] {display: none;}
。默认 v-text 是没有闪烁问题的,v-text 会覆盖元素中原本的内容,但是插值表达式 只会替换自己的这个占位符,不会把整个元素的内容清空
v-bind(缩写:)
v-on(缩写@)
v-model 只能用于表单元素
v-for
v-if
v-show
一般来说,v-if 有更高的切换消耗而 v-show 有更高的初始渲染消耗。因此,如果需要频繁切换,v-show 较好,如果运行时条件不太可能改变 v-if 较好
事件修饰符
- .stop 阻止冒泡
- .prevent 阻止默认事件
- .capture 添加事件侦听器时使用事件捕获模式
- .self 只当事件在该元素本身(比如不是子元素)触发时触发回调
- .once 事件只触发一次
在 Vue 中使用样式
使用 class 样式
- 数组
1
| <h1 :class="['red', 'thin']">这是一个邪恶的H1</h1>
|
- 数组中使用三元表达式
1
| <h1 :class="['red', 'thin', isactive?'active':'']">这是一个邪恶的H1</h1>
|
- 数组中嵌套对象
1
| <h1 :class="['red', 'thin', {'active': isactive}]">这是一个邪恶的H1</h1>
|
- 直接使用对象
1
| <h1 :class="{red:true, italic:true, active:true, thin:true}">这是一个邪恶的H1</h1>
|
使用内联样式
- 直接在元素上通过
:style
的形式,书写样式对象
1
| <h1 :style="{color: 'red', 'font-size': '40px'}">这是一个善良的H1</h1>
|
- 将样式对象,定义到
data
中,并直接引用到 :style
中
1 2 3
| data: { h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' } }
|
- 在元素中,通过属性绑定的形式,将样式对象应用到元素中:
1
| <h1 :style="h1StyleObj">这是一个善良的H1</h1>
|
- 在
:style
中通过数组,引用多个 data
上的样式对象
1 2 3 4
| data: { h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' }, h1StyleObj2: { fontStyle: 'italic' } }
|
- 在元素中,通过属性绑定的形式,将样式对象应用到元素中:
1
| <h1 :style="[h1StyleObj, h1StyleObj2]">这是一个善良的H1</h1>
|
Vue 指令之v-if
和v-show
一般来说,v-if 有更高的切换消耗而 v-show 有更高的初始渲染消耗。因此,如果需要频繁切换 v-show 较好,如果在运行时条件不大可能改变 v-if 较好。
过滤器
概念:Vue.js 允许你自定义过滤器,可被用作一些常见的文本格式化。过滤器可以用在两个地方:mustache 插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符指示;
私有过滤器
- HTML 元素:
1 2
| <td>{{item.ctime | dataFormat('yyyy-mm-dd')}}</td>
|
- 私有
filters
定义方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| filters: {
dataFormat(input, pattern = "") {
var dt = new Date(input);
var y = dt.getFullYear();
var m = (dt.getMonth() + 1).toString().padStart(2, '0');
var d = dt.getDate().toString().padStart(2, '0');
if (pattern.toLowerCase() === 'yyyy-mm-dd') {
return `${y}-${m}-${d}`;
} else {
var hh = dt.getHours().toString().padStart(2, '0');
var mm = dt.getMinutes().toString().padStart(2, '0');
var ss = dt.getSeconds().toString().padStart(2, '0');
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
}
}
}
|
使用 ES6 中的字符串新方法 String.prototype.padStart(maxLength, fillString=’’) 或 String.prototype.padEnd(maxLength, fillString=’’)来填充字符串;
全局过滤器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
Vue.filter('dataFormat', function (input, pattern = '') {
var dt = new Date(input);
var y = dt.getFullYear();
var m = (dt.getMonth() + 1).toString().padStart(2, '0');
var d = dt.getDate().toString().padStart(2, '0');
if (pattern.toLowerCase() === 'yyyy-mm-dd') {
return `${y}-${m}-${d}`;
} else {
var hh = dt.getHours().toString().padStart(2, '0');
var mm = dt.getMinutes().toString().padStart(2, '0');
var ss = dt.getSeconds().toString().padStart(2, '0');
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
}
});
|
注意:当有局部和全局两个名称相同的过滤器时候,会以就近原则进行调用,即:局部过滤器优先于全局过滤器被调用!
键盘修饰符以及自定义键盘修饰符
- 通过
Vue.config.keyCodes.名称 = 按键值
来自定义案件修饰符的别名:
1
| Vue.config.keyCodes.f2 = 113;
|
- 使用自定义的按键修饰符:
1
| <input type="text" v-model="name" @keyup.f2="add">
|
自定义指令
使用 Vue.directive()定义全局的指令,比如 v-focus
其中 参数 1:指令的名称,定义时不需要加 v-的前缀
使用的时候必须在指令名称前面加上 v-前缀来调用
参数 2:是一个对象,这个对象上有一些指令相关的函数,这些函数可以在特定的阶段执行相关的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Vue.dirctive('focus',{ bind:function(el){ }, inserted(el){ el.focus(); }, updated(el){ } }
|
私有指令的定义
1 2 3 4 5 6
| dirctives:{ 'fontsize':function (el,binding){ el.style.fontSize=binding.value } }
|
- 自定义指令的使用方式:
1
| <input type="text" v-model="searchName" v-focus v-color="'red'" v-font-weight="900">
|
实现筛选的方式显示过滤-排序结果:
- 筛选框绑定到 VM 实例中的
searchName
属性:
1 2 3 4
| <hr> 输入筛选名称:
<input type="text" v-model="searchName">
|
- 在使用
v-for
指令循环每一行数据的时候,不再直接 item in list
,而是 in
一个 过滤的 methods 方法,同时,把过滤条件searchName
传递进去:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <tbody>
<tr v-for="item in search(searchName)">
<td>{{item.id}}</td>
<td>{{item.name}}</td>
<td>{{item.ctime}}</td>
<td>
<a href="#" @click.prevent="del(item.id)">删除</a>
</td>
</tr>
</tbody>
|
search
过滤方法中,使用 数组的 filter
方法进行过滤:
1 2 3 4 5 6 7 8 9 10
| search(name) {
return this.list.filter(x => {
return x.name.indexOf(name) != -1;
});
}
|
JSONP 的实现原理
- 由于浏览器安全限制,不允许 AXAJ 访问协议不同、域名不同、端口号不同——不符合同源策略的。
- 可以通过动态创建 script 标签的形式,把 script 标签的 src 属性指向数据接口的地址。因为 script 标签不存在跨域限制,这种数据获取方式称之为 JSONP
具体实现过程
先在客户端定义一个回调方法,预定义对数据的操作;
再把这个回调方法的名称通过 URL 传参的形式提交到服务器的数据接口;
服务器数据接口组织好要发送给客户端的数据,再拿客户端传递过来的回调方法名称拼接出一个调用这个方法的字符串,发送给客户端解析执行;
客户端拿到服务器的返回的字符串之后,当作 script 脚本执行。
Node.js 实现一个 JSONP 的请求例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| const http = require('http'); const urlModule = require('url');
const server = http.createServer(); server.on('request', (req, res) => { const url = req.url;
var info = urlModule.parse(url, true);
if (info.pathname === '/getjsonp') { var cbName = info.query.callback; var data = { name: 'zs', age: 22, gender: '男', hobby: ['吃饭', '睡觉', '运动'] } var result = `${cbName}(${JSON.stringify(data)})`; res.end(result); } else { res.end('404'); } });
server.listen(3000, () => { console.log('server running at =http://127.0.0.1:3000'); });
|
使用过渡类名
- HTML 结构:
1 2 3 4 5 6 7
| <div id="app"> <input type="button" value="动起来" @click="myAnimate"> <transition name="fade"> <div v-show="isshow">动画哦</div> </transition> </div>
|
- VM 实例:
1 2 3 4 5 6 7 8 9 10 11 12 13
| var vm = new Vue({ el: '#app', data: { isshow: false }, methods: { myAnimate() { this.isshow = !this.isshow; } } });
|
- 定义两组类样式:
1 2 3 4 5 6 7 8 9 10 11 12 13
| .fade-enter-active, .fade-leave-active { transition: all 0.2s ease; position: absolute; }
.fade-enter, .fade-leave-to { opacity: 0; transform: translateX(100px); }
|
- 导入动画类库:
1
| <link rel="stylesheet" type="text/css" href="./lib/animate.css">
|
- 定义 transition 及属性:
1 2 3 4 5 6
| <transition enter-active-class="fadeInRight" leave-active-class="fadeOutRight" :duration="{ enter: 500, leave: 800 }"> <div class="animated" v-show="isshow">动画哦</div> </transition>
|
使用动画钩子函数
- 定义 transition 组件以及三个钩子函数:
1 2 3 4 5 6 7 8 9
| <div id="app"> <input type="button" value="切换动画" @click="isshow = !isshow"> <transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter"> <div v-if="isshow" class="show">OK</div> </transition> </div>
|
- 定义三个 methods 钩子方法:
1 2 3 4 5 6 7 8 9 10 11 12 13
| methods: { beforeEnter(el) { el.style.transform = 'translateX(500px)'; }, enter(el, done) { el.offsetWidth; el.style.transform = 'translateX(0px)'; done(); }, afterEnter(el) { this.isshow = !this.isshow; } }
|
- 定义动画过渡时长和样式:
1 2 3
| .show{ transition: all 0.4s ease; }
|
- 定义过渡样式:
1 2 3 4 5 6 7 8 9 10 11 12
| <style> .list-enter, .list-leave-to { opacity: 0; transform: translateY(10px); }
.list-enter-active, .list-leave-active { transition: all 0.3s ease; } </style>
|
- 定义 DOM 结构,其中,需要使用 transition-group 组件把 v-for 循环的列表包裹起来:
1 2 3 4 5 6 7
| <div id="app"> <input type="text" v-model="txt" @keyup.enter="add">
<transition-group tag="ul" name="list"> <li v-for="(item, i) in list" :key="i">{{item}}</li> </transition-group> </div>
|
- 定义 VM 中的结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var vm = new Vue({ el: '#app', data: { txt: '', list: [1, 2, 3, 4] }, methods: { add() { this.list.push(this.txt); this.txt = ''; } } });
|
列表的排序过渡
<transition-group>
组件还有一个特殊之处。不仅可以进入和离开动画,还可以改变定位。要使用这个新功能只需了解新增的 v-move
特性,它会在元素的改变定位的过程中应用。
v-move
和 v-leave-active
结合使用,能够让列表的过渡更加平缓柔和:
1 2 3 4 5 6
| .v-move{ transition: all 0.8s ease; } .v-leave-active{ position: absolute; }
|
定义 Vue 组件
什么是组件: 组件的出现,就是为了拆分 Vue 实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可;
组件化和模块化的不同:
- 模块化: 是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一;
- 组件化: 是从 UI 界面的角度进行划分的;前端的组件化,方便 UI 组件的重用;
全局组件定义的三种方式
- 使用 Vue.extend 配合 Vue.component 方法:
1 2 3 4
| var login = Vue.extend({ template: '<h1>登录</h1>' }); Vue.component('login', login);
|
- 直接使用 Vue.component 方法:
1 2 3
| Vue.component('register', { template: '<h1>注册</h1>' });
|
- 将模板字符串,定义到 script 标签种:
1 2 3
| <script id="tmpl" type="x-template"> <div><a href="#">登录</a> | <a href="#">注册</a></div> </script>
|
同时,需要使用 Vue.component 来定义组件:
1 2 3
| Vue.component('account', { template: '#tmpl' });
|
注意: 组件中的 DOM 结构,有且只能有唯一的根元素(Root Element)来进行包裹!
组件中展示数据和响应事件
- 在组件中,
data
需要被定义为一个方法,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13
| Vue.component('account', { template: '#tmpl', data() { return { msg: '大家好!' } }, methods:{ login(){ alert('点击了登录按钮'); } } });
|
- 在子组件中,如果将模板字符串,定义到了 script 标签中,那么,要访问子组件身上的
data
属性中的值,需要使用this
来访问;
- 组件可以有自己的 data 数据
- 组件的 data 和 实例的 data 有点不一样,实例中的 data 可以为一个对象,但是 组件中的 data 必须是一个方法
- 组件中的 data 除了必须为一个方法之外,这个方法内部,还必须返回一个对象才行;
- 组件中 的 data 数据,使用方式,和实例中的 data 使用方式完全一样!!!
组件切换
vue 提供了 component,来展示对应的名称组件
component 是一个占位符, :is 属性,可以用来指定要展示的组件的名称
1 2 3 4 5
| <div id="app"> <a href="" @click.prevent="componentId='login'">登录</a> <a href="" @click.prevent="componentId='register'">注册</a> <component :is="componentId"></component> </div>
|
当前学习了几个 Vue 提供的标签:
component, template, transition, transitionGroup
父组件向子组件传值
- 组件实例定义方式,注意:一定要使用
props
属性来定义父组件传递过来的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <script> var vm = new Vue({ el: '#app', data: { msg: '这是父组件中的消息' }, components: { son: { template: '<h1>这是子组件 --- {{finfo}}</h1>', props: ['finfo'] } } }); </script>
|
- 使用
v-bind
或简化指令,将数据传递到子组件中:
1 2 3
| <div id="app"> <son :finfo="msg"></son> </div>
|
子组件向父组件传值
- 原理:父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在调用方法的时候当作参数传递进去;
- 父组件将方法的引用传递给子组件,其中,
getMsg
是父组件中methods
中定义的方法名称,func
是子组件调用传递过来方法时候的方法名称
1
| <son @func="getMsg"></son>
|
- 子组件内部通过
this.$emit('方法名', 要传递的数据)
方式,来调用父组件中的方法,同时把数据传递给父组件使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <div id="app"> <!-- 引用父组件 --> <son @func="getMsg"></son>
<!-- 组件模板定义 --> <script type="x-template" id="son"> <div> <input type="button" value="向父组件传值" @click="sendMsg" /> </div> </script> </div>
<script> Vue.component('son', { template: '#son', methods: { sendMsg() { this.$emit('func', 'OK'); } } });
var vm = new Vue({ el: '#app', data: {}, methods: { getMsg(val){ alert(val); } } }); </script>
|
vue-router
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| const router = new VueRouter({ routes: [ { path: "/", redirect: "/home", meta: { title: "首页" } }, ], mode: "history", linkActiveClass: "active" });
router.beforeEach((to, from, next) => { document.title = to.matched[0].meta.title || "vuebox"; next(); });
router.afterEach((to, from) => { console.log("----------"); });
|
keep-alive
activated和deactivated只有该组件使用了keep-alive时才是有效的。
组件内路由,beforeRouteLeave(to,from,next){}
include
和exclude
插槽slot
vue2.x
1 2 3
| <div slot="item-icon"> <span>前置图标</span> </div>
|
vue3.x
1 2 3 4 5
| <template v-slot:pre-icon> <span> 前置图标 </span> </template>
|
1 2
| this.$router.push('/home/'+123) this.$router.push({path:'/home',query:{id:123}})
|