Wang's blog Wang's blog
首页
  • 前端文章

    • HTML教程
    • CSS
    • JavaScript
  • 前端框架

    • Vue
    • React
    • VuePress
    • Electron
  • 后端技术

    • Npm
    • Node
    • TypeScript
  • 编程规范

    • 规范
  • 我的笔记
  • Git
  • GitHub
  • VSCode
  • Mac工具
  • 数据库
  • Google
  • 服务器
  • Python爬虫
  • 前端教程
更多
收藏
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Wang Mings

跟随大神,成为大神!
首页
  • 前端文章

    • HTML教程
    • CSS
    • JavaScript
  • 前端框架

    • Vue
    • React
    • VuePress
    • Electron
  • 后端技术

    • Npm
    • Node
    • TypeScript
  • 编程规范

    • 规范
  • 我的笔记
  • Git
  • GitHub
  • VSCode
  • Mac工具
  • 数据库
  • Google
  • 服务器
  • Python爬虫
  • 前端教程
更多
收藏
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Python爬虫

  • 前端教程

    • 团队规范

    • Project

    • JS

    • NodeJS

    • Vue

      • 个人理解Vue和React区别
      • Vue高级用法
      • Vue2.x源码分析 - 框架结构
      • Vue2.x源码分析 - 模版编译以及挂载
      • 虚拟dom算法库 - snabbdom
      • Vue2.x源码分析 - Virtual DOM实现
      • Vue2.x源码分析 - 事件系统
      • Vue2.x源码分析 - 组件系统
      • Vue2.x源码分析 - Vue.nextTick
      • Vue2.x源码分析 - inject provide
      • Vue2.x源码分析 - 解析Template模板
      • Vue2.x源码分析 - 响应式原理
      • Vue2.x源码分析 - v-model
        • [#](#普通元素input) 普通元素input
        • [#](#组件) 组件
          • [#](#编译阶段) 编译阶段
          • [#](#patch阶段) patch阶段
      • Vue CLI3 插件系统原理
      • Vue Loader v15 源码解析
      • Vue3 设计思想
      • Vue3 RFCS导读
      • Vue3 响应式原理 - Ref Reactive Effect源码分析
      • Vue3 API 源码解析
      • 为何Vue3 Proxy 更快
      • Vue核心原理 - 笔记
    • React

    • 效率工具

    • 读书笔记

  • 教程
  • 前端教程
  • Vue
wangmings
2022-07-19
目录

Vue2.x源码分析 - v-model

# # Vue2.x源码分析 - v-model

v-model 即可以作用在普通表单元素上,又可以作用在组件上,它其实是一个语法糖。

# # 普通元素input

input 实现 v-model 的精髓,通过修改 AST 元素,给 el 添加一个 prop,相当于我们在 input 上动态绑定了 value,又给 el 添加了事件处理,相当于在 input 上绑定了 input 事件。等于动态增加prop + DOM绑定事件语法糖实现。

    // src/platforms/web/compiler/directives/model.js
    addProp(el, 'value', `(${value})`)
    addHandler(el, event, code, null, true)
    
1
2
3
4
    <input v-model="message" />
    <input
      v-bind:value="message"
      v-on:input="message=$event.target.value" />
    
1
2
3
4
5
    // 最终生成的 render 代码
    with(this) {
      return _c('div',[_c('input',{
        directives:[{
          name:"model",
          rawName:"v-model",
          value:(message),
          expression:"message"
        }],
        attrs:{"placeholder":"edit me"},
        domProps:{"value":(message)},
        on:{"input":function($event){
          if($event.target.composing)
            return;
          message=$event.target.value
        }}}),_c('p',[_v("Message is: "+_s(message))])
        ])
    }
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# # 组件

组件v-model也是语法糖,最终也是如上面input普通元素,增加prop以及设置data.on事件。但组件事件走的是另外一套逻辑Vue2.x源码分析 - 事件系统。所以组件在编译阶段,不会直接设置data.on,而是给到中转变量el.model;在patch阶段的createComponent时,才把el.model转换为prop以及data.on(这时的data.on走的是组件事件的$on/$emit)。

# # 编译阶段

    // src/compiler/directives/model.js
    export function genComponentModel (
      el: ASTElement,
      value: string,
      modifiers: ?ASTModifiers
    ): ?boolean {
        // 处理number,trim修饰符
      const { number, trim } = modifiers || {}
      const baseValueExpression = '$$v'
      let valueExpression = baseValueExpression
      if (trim) {
        valueExpression =
          `(typeof ${baseValueExpression} === 'string'` +
            `? ${baseValueExpression}.trim()` +
            `: ${baseValueExpression})`
      }
      if (number) {
        valueExpression = `_n(${valueExpression})`
      }
    
      // 生成el.model,在genData生成render函数有用到
      const assignment = genAssignmentCode(value, valueExpression)
      el.model = {
        value: `(${value})`,
        expression: `"${value}"`,
        callback: `function (${baseValueExpression}) {${assignment}}`
      }
    }
    
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
    // 生成的render函数
    with(this){
      return _c('div',[_c('child',{
        model:{
          value:(message),
          callback:function ($$v) {
            message=$$v
          },
          expression:"message"
        }
      })])
    }
    
1
2
3
4
5
6
7
8
9
10
11
12
13

# # patch阶段

    // src/core/vdom/create-component.js
    if (isDef(data.model)) {
       transformModel(Ctor.options, data)
    }
    
    function transformModel (options, data: any) {
      // api中的options.model可配置
      const prop = (options.model && options.model.prop) || 'value'
      const event = (options.model && options.model.event) || 'input';
    
      // 设置组件prop和事件
      (data.props || (data.props = {}))[prop] = data.model.value
      const on = data.on || (data.on = {})
      if (isDef(on[event])) {
        on[event] = [data.model.callback].concat(on[event])
      } else {
        on[event] = data.model.callback
      }
    }
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    // 最终VNode.data如下,接下来就是组件的事件处理
    data.props = {
      value: (message),
    }
    data.on = {
      input: function ($$v) {
        message=$$v
      }
    }
    
1
2
3
4
5
6
7
8
9
10
编辑 (opens new window)
Vue2.x源码分析 - 响应式原理
Vue CLI3 插件系统原理

← Vue2.x源码分析 - 响应式原理 Vue CLI3 插件系统原理→

最近更新
01
theme-vdoing-blog博客静态编译问题
09-16
02
搜索引擎
07-19
03
友情链接
07-19
更多文章>
Theme by Vdoing | Copyright © 2019-2022 Evan Xu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式