>[success] # v-model
1. 常见表单元素:`input(radio, text, address, email....)`、`select`、 `checkbox`、 `textarea`
2. 如果想在表单元素上进行双向绑定写法
~~~
<input
:value="text"
@input="event => text = event.target.value">
~~~
3. `vue` 也提供了`v-model` 这个语法糖可以更简单的进行在表单元素上创建双向数据绑定
~~~
<input v-model="text">
~~~
4. `v-model` 配合 `input `使用时候语法糖形式绑定事件也不同[源码参考](https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-dom/src/directives/vModel.ts#L52-L59)
* `text `和 `textarea `元素使用 `value property` 和 `input `事件;
* `checkbox `和 `radio `使用 `checked property` 和 `change `事件;
* `select `字段将 `value `作为 `prop `并将 `change `作为事件。
5. 组件也可以使用`v-model` 具体参考组件章节
>[info] ## 原生实现类vue 双向绑定
1. `v-model`配合`input`使用时候语法糖形式绑定事件也不同[源码参考](https://github1s.com/vuejs/core/blob/HEAD/packages/runtime-dom/src/directives/vModel.ts#L52-L59)因此本质上还是对dom 操作,使用原生做一个类似实现效果
>[danger] ##### input[text] -- onInput
![](https://img.kancloud.cn/f0/ab/f0ab9d612e3beea6115be206da566acf_449x70.png)
~~~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" />
<title>Document</title>
</head>
<body>
<div>
<div id="inputTextVal"></div>
<input id="text" value="" />
</div>
<script>
// 普通文本输入
;(function inputText() {
const textDom = document.getElementById('text')
textDom.addEventListener('input', function (e) {
document.getElementById('checkboxVal').innerText =
e.target.value
console.log(e.target.value)
})
})()
</script>
</body>
</html>
~~~
>[danger] ##### input[checkbox] -- onChange
1. `checkbox` 在原生中如果被选中会在节点上增加`checked` 属性,只要收集`name`属性标记相同一组`checkbox` 中`value`属性即可
![](https://img.kancloud.cn/05/46/0546afce7fa97a316890012d12f6ce68_466x112.png)
~~~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" />
<title>Document</title>
</head>
<body>
<div>
<div id="checkboxVal"></div>
<input type="checkbox" value="1" /> 足球
<input type="checkbox" value="2" /> 篮球
<input type="checkbox" value="3" /> 乒乓球
</div>
<script>
// checkbox 多选
;(function inputCheckbox() {
const ls = []
const checkList = document.querySelectorAll(
'input[type="checkbox"]'
)
// 绑定事件
function checkChange(callback) {
checkList.forEach((item) => {
item.addEventListener('change', function (e) {
if (e.target.checked) {
ls.push(e.target.value)
} else {
const index = ls.findIndex((val) => {
val === e.target.value
})
ls.splice(index, 1)
}
callback?.()
})
})
}
// 数据渲染页面
checkChange(() => {
document.getElementById('checkboxVal').innerText =
ls.toString()
})
})()
</script>
</body>
</html>
~~~
>[danger] ##### radio -- onChange
1. `radio` 需要配合 `name` 字段形成互斥,绑定`change` 事件获取 `value` 即可
![](https://img.kancloud.cn/6b/c2/6bc2e79b46019b066d797f5d90219883_226x78.png)
~~~
<!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" />
<title>Document</title>
</head>
<body>
<div>
<div id="radioVal"></div>
<input type="radio" name="gender" value="1" /> 男
<input type="radio" name="gender" value="2" /> 女
</div>
<script>
;(function inputRadio() {
const radioChecked= document.querySelector(
'input[type="radio"]'
)
// 绑定事件
function checkChange(callback) {
radioChecked.addEventListener('change', function (e) {
callback?.(e.target.value)
})
}
// 数据渲染页面
checkChange((val) => {
document.getElementById('radioVal').innerText = val
})
})()
</script>
</body>
</html>
~~~
>[danger] ##### select -- onChange
![](https://img.kancloud.cn/88/7e/887e42a6ab8f96586a8779989259700e_318x181.png)
~~~
<!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" />
<title>Document</title>
</head>
<body>
<div>
<div id="selectedVal"></div>
<select id="ss">
<option value="1">大连</option>
<option value="2">上海</option>
<option value="3">北京</option>
<option value="4" id="op1">天津</option>
</select>
</div>
<script>
;(function selected() {
const selected = document.querySelector('#ss')
// 绑定事件
selected.addEventListener('change', function (e) {
console.log(e.target.value)
const option = document.querySelector(
`option[value="${e.target.value}"]`
)
document.getElementById('selectedVal').innerText =
e.target.value + ',' + option.textContent
})
})()
</script>
</body>
</html>
~~~
>[info] ## v-model 和 原生属性
1. `v-model`会忽略任何表单元素上初始的`value`、`checked`或`selected`attribute。它将始终将当前绑定的 JavaScript 状态视为数据的正确来源。你应该在 JavaScript 中使用`data`选项来声明该初始值。理解为在上面原生案例可以知道其实通过`dom `原生提供的固定标记属性来获取选中值,当你在`vue `中使用` v-model`,这些属性将会被忽略,v-model 只会对自己数据源负责
vue自己`true-value`和`false-value` 也需要后续补充
>[danger] ##### 修饰符 - lazy
1. 默认情况下,`v-model`在进行双向绑定时,绑定的是`input`事件,那么会在每次内容输入后就将最新的值和绑定的属性进行同步
2. `v-model`后跟上lazy修饰符,那么会将绑定的事件切换为 `change `事件,只有在提交时才会触发
~~~
<!-- 在 "change" 事件后同步更新而不是 "input" -->
<input v-model.lazy="msg" />
~~~
>[danger] ##### 修饰符 - number
1. `v-model`绑定后的值总是`string`类型,`vue2`即使在我们设置`type`为`number`也是`string`类型,`vue3`在`number`修饰符会在输入框有`type="number"`时自动启用
2. 希望转换为数字类型,那么可以使用 `.number `修饰符
~~~
<input v-model.number="age" />
~~~
>[danger] ##### 修饰符 - .trim
1. 你想要默认自动去除用户输入内容中两端的空格,你可以在` v-model `后添加` .trim` 修饰
~~~
<input v-model.trim="msg" />
~~~
>[danger] ##### 官网给的提示
对于需要使用[IME](https://en.wikipedia.org/wiki/Input_method)的语言 (中文,日文和韩文等),你会发现`v-model`不会在 IME 输入还在拼字阶段时触发更新。如果你的确想在拼字阶段也触发更新,请直接使用自己的`input`事件监听器和`value`绑定而不要使用`v-model`。
>[danger] ##### 官网参考
[v-model](https://cn.vuejs.org/guide/essentials/forms.html#checkbox-2)
- 官网给的工具
- 声明vue2 和 vue3
- 指令速览
- Mustache -- 语法
- v-once -- 只渲染一次
- v-text -- 插入文本
- v-html -- 渲染html
- v-pre -- 显示原始的Mustache标签
- v-cloak -- 遮盖
- v-memo(新)-- 缓存指定值
- v-if/v-show -- 条件渲染
- v-for -- 循环
- v-bind -- 知识
- v-bind -- 修饰符
- v-on -- 点击事件
- v-model -- 双向绑定
- 其他基础知识速览
- 快速使用
- 常识知识点
- key -- 作用 (后续要更新)
- computed -- 计算属性
- watch -- 侦听
- 防抖和节流
- vue3 -- 生命周期
- vue-cli 和 vite 项目搭建方法
- vite -- 导入动态图片
- 组件
- 单文件组件 -- SFC
- 组件通信 -- porp
- 组件通信 -- $emit
- 组件通信 -- Provide / Inject
- 组件通信 -- 全局事件总线mitt库
- 插槽 -- slot
- 整体使用案例
- 动态组件 -- is
- keep-alive
- 分包 -- 异步组价
- mixin -- 混入
- v-model-- 组件
- 使用计算属性
- v-model -- 自定义修饰符
- Suspense -- 实验属性
- Teleport -- 指定挂载
- 组件实例 -- $ 属性
- Option API VS Composition API
- Setup -- 组合API 入口
- api -- reactive
- api -- ref
- 使用ref 和 reactive 场景
- api -- toRefs 和 toRef
- api -- readonly
- 判断性 -- API
- 功能性 -- API
- api -- computed
- api -- $ref 使用
- api -- 生命周期
- Provide 和 Inject
- watch
- watchEffect
- watch vs. watchEffect
- 简单使用composition Api
- 响应性语法糖
- css -- 功能
- 修改css -- :deep() 和 var
- Vue3.2 -- 语法
- ts -- vscode 配置
- attrs/emit/props/expose/slots -- 使用
- props -- defineProps
- props -- defineProps Ts
- emit -- defineEmits
- emit -- defineEmits Ts
- $ref -- defineExpose
- slots/attrs -- useSlots() 和 useAttrs()
- 自定义指令
- Vue -- 插件
- Vue2.x 和 Vue3.x 不同点
- $children -- 移除
- v-for 和 ref
- attribute 强制行为
- 按键修饰符
- v-if 和 v-for 优先级
- 组件使用 v-model -- 非兼容
- 组件
- h -- 函数
- jsx -- 编写
- Vue -- Router
- 了解路由和vue搭配
- vueRouter -- 简单实现
- 安装即使用
- 路由懒加载
- router-view
- router-link
- 路由匹配规则
- 404 页面配置
- 路由嵌套
- 路由组件传参
- 路由重定向和别名
- 路由跳转方法
- 命名路由
- 命名视图
- Composition API
- 路由守卫
- 路由元信息
- 路由其他方法 -- 添加/删除/获取
- 服务器配置映射
- 其他
- Vuex -- 状态管理
- Option Api -- VUEX
- composition API -- VUEX
- module -- VUEX
- 刷新后vuex 数据同步
- 小技巧
- Pinia -- 状态管理
- 开始使用
- pinia -- state
- pinia -- getter
- pinia -- action
- pinia -- 插件 ??
- Vue 源码解读
- 开发感悟
- 练手项目