instruction

V-model two-way data binding instruction

  • v-modelIs a two-way data binding directive for form controls.
  • v-modelIs actuallySyntactic sugarSeparate encapsulation of different form types.
  • Of course Vue components are also supportedv-modelTwo-way data binding.
  • v-modelGreat help for form data entry!

input

1. Input implementation of two-way data binding, in fact, the use of input control value property, and listen for input events.

  • So let’s simulate oneinputv-model:
<template>
    <div>
      <span>V-model dual data binding</span>
      <div>Input data is: {{a1}}<input type="text" v-model="a1" />
      </div>
      
     <div>
      <span>Simulate input Textarea V-model</span>
      <div>Input binding is the data is: {{a2}}</div>
      <div>
        <input
          type="text"
          :value="a2"
          @input="handleSelfInput"
        />
      </div>
    </div>
    </div>
</template>

<script>
export default {
  name: "component-input-v-model-test".data() {
    return {
      a1: 0.a2: 222}; },methods: {
    handleChange() {
      debugger;
    },
    handleInput(args) {
      debugger;
    },
    handleSelfInput(value) {
      const target = value.target.value;
      this.a2 = target; ,}}};</script>
Copy the code

A1 is a normal V-Model that bidirectionally binds input data, and A2 is a V-Model that simulates value and input events. The principle is simple: use v-bind to dynamically bind a2 and then, when the input event is triggered, take the value from the event and overwrite A2 with the new value.

textarea

Textarea is the same as text:

<template>
  <div>
    <div>
      <div>Use v-model: {{textarea}}</div>
      <textarea v-model="textarea"></textarea>
    </div>
    <div>
      <div>Text-area v-model: {{textarea_self}}</div>
      <textarea :value="textarea_self" @input="onInput"></textarea>
    </div>
  </div>
</template>

<script>
export default {
  name: "v-model-v-text-area".data() {
    return {
      textarea: "".textarea_self: ""}; },methods: {
    onInput(e) {
      this.textarea_self = e.target.value; ,}}};</script>
Copy the code

radio

Radio button type = radio unlike input and Textarea, radio uses checked instead of value, and the event changes from text input to change

  • Form control: text -> radio/checkbox
  • Bind control properties: value -> Checked
  • Listen to the form event: input -> change

First of all, we need to understand that radio also needs a value, which is used to represent the current selection of radio. When writing radio, the content of radio is not written inside the radio element, but combined with the label label to expand the clickable range.

<template>
  <div>
    <div>
      <div>The default is V-model</div>
      <div>Binding data: {{radio}}<input type="radio" value="radio1" v-model="radio" />
        <input type="radio" value="radio2" v-model="radio" />
        <input type="radio" value="radio3" v-model="radio" />
      </div>
    </div>
    <div>
      <div>Simulate a radio V-model: {{checked}}</div>
      <div>
        <input
          type="radio"
          value="r1"
          :checked="checked === 'r1'"
          @change="onChange"
        />
        <input
          type="radio"
          value="r2"
          :checked="checked === 'r2'"
          @change="onChange"
        />
        <input
          type="radio"
          value="r3"
          :checked="checked === 'r3'"
          @change="onChange"
        />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "v-model-radio".data() {
    return {
      radio: "".checked: ' '}; },methods: {
    onChange(e) {
      this.checked = e.target.value; ,}}};</script>
Copy the code

Checkbox’s basic analog V-mode

<template>
  <div>
    <div>
      <div>Use checkbox: {{checkbox}}</div>
      <div>
        <input type="checkbox" value="c1" v-model="checkbox" />
        <input type="checkbox" value="c2" v-model="checkbox" />
        <input type="checkbox" value="c3" v-model="checkbox" />
      </div>
    </div>
    <div>
      <div>Checkbox {{checkbox_self}}</div>
      <div>
        <input
          type="checkbox"
          value="cc1"
          :checked="checkbox_self.indexOf('cc1') > -1 ? true : false"
          @change="onChange"
        />
        <input
          type="checkbox"
          value="cc2"
          :checked="checkbox_self.indexOf('cc2') > -1 ? true : false"
          @change="onChange"
        />
        <input
          type="checkbox"
          value="cc3"
          :checked="checkbox_self.indexOf('cc3') > -1 ? true : false"
          @change="onChange"
        />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "v-checkbox-v-demo".data() {
    return {
      checkbox: [].checkbox_self: [],}; },methods: {
    onChange(e) {
      const value = e.target.value;

      if (this.checkbox_self.indexOf(value) > -1) {
        this.checkbox_self = this.checkbox_self.filter((item) = >{ item ! == value; }); }else {
        this.checkbox_self = [...this.checkbox_self, value]; ,}}}};</script>
Copy the code

Simple simulation of the relationship between select and selected, where a single select is implemented

<template>
  <div>
    <div>
      <div>Select vmodel: {{selected}}</div>
      <div>
        <select name="my-select" id="" v-model="selected">
          <option value="s1">s111</option>
          <option value="s2">s222</option>
          <option value="s3">s333</option>
        </select>
      </div>
    </div>
    <div>
      <div>Simulate a single select V-model: {{selected_self}}</div>
      <div>
        <select name="ss1" id="" @change="onChange">
          <option value="v1" :selected="selected_self === 'V1'">V1</option>
          <option value="v2" :selected="selected_self === 'V2'">V2</option>
          <option value="v3" :selected="selected_self === 'V3'">V3</option>
        </select>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      selected: "s1".selected_self: "V2"}; },methods: {
    onChange(e) {
      const value = e.target.value;
      this.selected_self = value; ,}}};</script>
Copy the code

summary

Note that different form controls have different properties and event listeners, and the V-Model will handle them for different forms. But we also need to learn how to implement the V-Model ourselves, as vue directives may not be well supported in the render function or JSX.

Apply colours to a drawing

    1. Why is there a render function?
    1. Why is there JSX?
  1. When we write Vue, we mostly use HTML templates, but templates are not as flexible as javascript in terms of flexibility, even though they are simple and crude. We can see from the schematic diagram of Vue’s operating life cycle that Vue determines whether or not to contain templates. All templates are compiled into the Render function, so it’s important to learn what the render function does and how it works.
  2. JSX is an extension of JavaScipt that allows for the ability to write HTML in JavaScript, which makes JavaScript inherently template-like and makes render functions cleaner.

render

Render is a render function that creates the component we wrote and compiles it into browser-aware HTML, CSS, JS,

  • The render function takes createElement (which can be simply called h),
  • The return value of the render function is the element created after the createElement argument options is passed in
  • The options for createElement are as follows:

Location: the SRC/core/vdom/create – element. Js

export function createElement (context: Component, tag: any, data: any, children: any, normalizationType: any, alwaysNormalize: boolean) :VNode | Array<VNode> {
  if (Array.isArray(data) || isPrimitive(data)) {
    normalizationType = children
    children = data
    data = undefined
  }
  if (isTrue(alwaysNormalize)) {
    normalizationType = ALWAYS_NORMALIZE
  }
  return _createElement(context, tag, data, children, normalizationType)
}
Copy the code
export function _createElement (context: Component, tag? : string | Class<Component> |Function | Object, data? : VNodeData, children? : any, normalizationType? : number) :VNode | Array<VNode> {
  if(isDef(data) && isDef((data: any).__ob__)) { process.env.NODE_ENV ! = ='production' && warn(
      `Avoid using observed data object as vnode data: The ${JSON.stringify(data)}\n` +
      'Always create fresh vnode data objects in each render! ',
      context
    )
    return createEmptyVNode()
  }
  // object syntax in v-bind
  if (isDef(data) && isDef(data.is)) {
    tag = data.is
  }
  if(! tag) {// in case of component :is set to falsy value
    return createEmptyVNode()
  }
  // warn against non-primitive key
  if(process.env.NODE_ENV ! = ='production'&& isDef(data) && isDef(data.key) && ! isPrimitive(data.key) ) {if(! __WEEX__ || ! ('@binding' in data.key)) {
      warn(
        'Avoid using non-primitive value as key, ' +
        'use string/number value instead.',
        context
      )
    }
  }
  // support single function children as default scoped slot
  if (Array.isArray(children) &&
    typeof children[0= = ='function'
  ) {
    data = data || {}
    data.scopedSlots = { default: children[0] }
    children.length = 0
  }
  if (normalizationType === ALWAYS_NORMALIZE) {
    children = normalizeChildren(children)
  } else if (normalizationType === SIMPLE_NORMALIZE) {
    children = simpleNormalizeChildren(children)
  }
  let vnode, ns
  if (typeof tag === 'string') {
    let Ctor
    ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
    if (config.isReservedTag(tag)) {
      // platform built-in elements
      vnode = new VNode(
        config.parsePlatformTagName(tag), data, children,
        undefined.undefined, context
      )
    } else if((! data || ! data.pre) && isDef(Ctor = resolveAsset(context.$options,'components', tag))) {
      // component
      vnode = createComponent(Ctor, data, context, children, tag)
    } else {
      // unknown or unlisted namespaced elements
      // check at runtime because it may get assigned a namespace when its
      // parent normalizes children
      vnode = new VNode(
        tag, data, children,
        undefined.undefined, context
      )
    }
  } else {
    // direct component options / constructor
    vnode = createComponent(tag, data, context, children)
  }
  if (Array.isArray(vnode)) {
    return vnode
  } else if (isDef(vnode)) {
    if (isDef(ns)) applyNS(vnode, ns)
    if (isDef(data)) registerDeepBindings(data)
    return vnode
  } else {
    return createEmptyVNode()
  }
}
Copy the code
  • createComponent
export function createComponent (
  Ctor: Class<Component> | Function | Object | void, data: ? VNodeData, context: Component, children: ?Array<VNode>, tag? : string) :VNode | Array<VNode> | void {
  if (isUndef(Ctor)) {
    return
  }

  const baseCtor = context.$options._base

  // plain options object: turn it into a constructor
  if (isObject(Ctor)) {
    Ctor = baseCtor.extend(Ctor)
  }

  // if at this stage it's not a constructor or an async component factory,
  // reject.
  if (typeofCtor ! = ='function') {
    if(process.env.NODE_ENV ! = ='production') {
      warn(`Invalid Component definition: The ${String(Ctor)}`, context)
    }
    return
  }

  // async component
  let asyncFactory
  if (isUndef(Ctor.cid)) {
    asyncFactory = Ctor
    Ctor = resolveAsyncComponent(asyncFactory, baseCtor)
    if (Ctor === undefined) {
      // return a placeholder node for async component, which is rendered
      // as a comment node but preserves all the raw information for the node.
      // the information will be used for async server-rendering and hydration.
      return createAsyncPlaceholder(
        asyncFactory,
        data,
        context,
        children,
        tag
      )
    }
  }

  data = data || {}

  // resolve constructor options in case global mixins are applied after
  // component constructor creation
  resolveConstructorOptions(Ctor)

  // transform component v-model data into props & events
  if (isDef(data.model)) {
    transformModel(Ctor.options, data)
  }

  // extract props
  const propsData = extractPropsFromVNodeData(data, Ctor, tag)

  // functional component
  if (isTrue(Ctor.options.functional)) {
    return createFunctionalComponent(Ctor, propsData, data, context, children)
  }

  // extract listeners, since these needs to be treated as
  // child component listeners instead of DOM listeners
  const listeners = data.on
  // replace with listeners with .native modifier
  // so it gets processed during parent component patch.
  data.on = data.nativeOn

  if (isTrue(Ctor.options.abstract)) {
    // abstract components do not keep anything
    // other than props & listeners & slot

    // work around flow
    const slot = data.slot
    data = {}
    if (slot) {
      data.slot = slot
    }
  }

  // install component management hooks onto the placeholder node
  installComponentHooks(data)

  // return a placeholder vnode
  const name = Ctor.options.name || tag
  const vnode = new VNode(
    `vue-component-${Ctor.cid}${name ? ` -${name}` : ' '}`,
    data, undefined.undefined.undefined, context,
    { Ctor, propsData, listeners, tag, children },
    asyncFactory
  )

  // Weex specific: invoke recycle-list optimized @render function for
  // extracting cell-slot template.
  // https://github.com/Hanks10100/weex-native-directive/tree/master/component
  /* istanbul ignore if */
  if (__WEEX__ && isRecyclableComponent(vnode)) {
    return renderRecyclableComponentTemplate(vnode)
  }

  return vnode
}
Copy the code
  • VNode is a class that stores component information and DOM node information. This is actually a Vue component creation process. Vue components are eventually compiled into the Render function, which instantiates the VNode virtual node. Vue is essentially a VNode comparison and dom generation process. So render is the most important thing for developers to do when creating components.

jsx

github.com/vuejs/jsx is a plug-in for Babel,

  • The installation
npm install @vue/babel-preset-jsx @vue/babel-helper-vue-jsx-merge-props -D
Copy the code
  • Babel configuration file
{
  "presets": ["@vue/babel-preset-jsx"]}Copy the code
  • Vue grammar
    • content
    • attribute
    • slot
    • instruction
    • Function component

JSX in the slot.

Vm.$scopedSlots is particularly useful when developing a component using the render function.

  • The child component uses this.$scopedslots.user to bind a scoped slot
<script>
export default {
  name: 'Start',
  data () {
    return {
      a: '123'.b: '456'
    }
  },
  mounted () {
    console.log('children'.this.$scopedSlots.user)  // a function
  },
  render () {
    return (
      <div class="abc">
        <span>this is goods</span>
        {this.$slots.default}
        {this.$scopedSlots.user({
          a: this.a
        })}
      </div>)}}</script>

<style scoped>
.abc {
  background: rebeccapurple;
  color: #fff;
}
</style>
Copy the code
  • The parent component

The parent component does not use render + JSX scheme, but uses templates directly, because JSX’s Babel converter was written several years ago and has not been updated, there is no support for V-slot instructions with Vue.

<template>
  <div>
    <hello-world />
    <start v-slot:user="innerSlotData">
      {{innerSlotData}}
      <div>
        <span>this div is test => this.$slot.default</span>
        <span>123</span>
        <span>{{innerSlotData.a}}</span>
      </div>
    </start>
  </div>
</template>

<script>
import HelloWorld from '.. /components/HelloWorld'
import Start from '.. /components/Start'
export default {
  name: 'Home'.components: {
    HelloWorld,
    Start
  },
  mounted () {
    console.log(this.$scopedSlots.user) // undefined}}</script>

<style lang="scss" scoped>

</style>
Copy the code