Interviewer: What is the difference between MVVM and MVC?

Think about it for a minute.

For the specific explanation of the above interview questions, please move here, this article is not overstated. To begin, here are some tips that you may or may not have used. Whatever it is, I hope you get something out of it. Writing and knowledge is limited, wrong place, please leave a message as if they are correct!

Set multiple types to the props property

This technique is often used when developing components for greater fault tolerance and more user-friendly code:

export default {
  props: {
    width: {
      type: [String.Number].default: '100px'
    }
    // Or so
    // width: [String, Number]}}Copy the code

For example, if a

exposes a width attribute, we can pass either 100px or 100:

<! -- my-button.vue -->
<template>
  <button :style="computedWidth">{{ computedWidth }}</button>
</template>

<script>
  export default {
    props: {
      width: [String.Number]},computed: {
      computedWidth () {
        let o = {}
        if (typeof this.width === 'string') o.width = this.width
        if (typeof this.width === 'number') o.width = this.width + 'px'
        return o
      }
    }
  }
</script>
Copy the code

Use:

<! -- Used in other components -->
<template>
  <my-button width="100px"></my-button>
  <! -- or -->
  <my-button :width="100"></my-button>
</template>
Copy the code

How do I prompt Element upload before deleting it?

When colleagues do upload and delete, add a prompt to the user “Delete? Notice that even if you click cancel, the upload list will still be deleted. View the document if in ‘

Disable the browser Auto Complete behavior

Sometimes we enter the account password to log in the background system, the browser will pop up whether to save your login information. We’re going to hit Save, because the next time we log in it’s going to auto-fill and it’s going to add to our user experience, which is nice.

But sometimes, when we are developing a certain block (such as new user), click on the new user button and the popup appears, unfortunately, in the account and password fields the browser fills in for us. But that’s not what we want. Therefore, my effective solution is as follows:

  • Set up the<el-input/>Read-only mode
  • infocusRemove read-only mode from the event

The code is as follows:

<el-input
  type="text"
  v-model="addData.loginName"
  readonly
  @focus="handleFocusEvent"
/>
Copy the code
.methods: {
  handleFocusEvent(event) {
    event.target && event.target.removeAttribute('readonly')}}Copy the code

However, ElementUI’s built-in auto-complete=”off” doesn’t seem to work.

stop<el-form>Default commit behavior

Sometimes a page refresh is triggered by default when entering enter in the text box using the Ele. me component

. We can address its default behavior by adding the following code:

<el-form @submit.native.prevent>.</el-form>
Copy the code

use<el-scrollbar>component

Element does not officially provide this component, but it does exist in the source code. I wrote a Demo of how to use it, but you can see the examples here.

Based on business combinationel-tableThe rows and columns in

Recently, WHEN I was working on a project, I came across a requirement to display the role information when there are multiple assigned roles under the same account ID. This brings us to the merge method provided by el-Table :span-method. But it has requirements for the background data format:

  • If the data returned in the background is an array nested within an array, you need to take the array out of the array in order
// If the data returned in the background is as follows
{
  data: [{id: 1.appkey: 1.name: 'a'.list: [{ id: 11.appkey: 1.name: 'a-1'}, {id: 12.appkey: 1.name: 'a-2'} {}]id: 2.appkey: 2.name: 'b'.list: [{ id: 21.appkey: 2.name: 'b-1'}, {id: 22.appkey: 2.name: 'b-2'}}}]]// This should look like this
{
  data: [{id: 1.appkey: 1.name: 'a' },
    { id: 11.appkey: 1.name: 'a-1' },
    { id: 12.appkey: 1.name: 'a-2' },
    { id: 2.appkey: 2.name: 'b' },
    { id: 21.appkey: 2.name: 'b-1' },
    { id: 22.appkey: 2.name: 'b-2'}}]Copy the code

The following is the specific processing process:

<template>
  <el-table 
    :data="formattedList" 
    :span-method="handleColspanMethod"
  >.</el-table>
</template>
<script>
import Api from '@/api/assign'
export default {
  data() {
    return {
      list: [].// The data returned by the background
      formattedList: [].// Formatted data
      spanArr: [].// Save the column data to be merged}},created() {
    this.getList()
  },
  methods: {
    getList() {
      Api.fetchList().then(response= > {
        this.list = response.data.data
        // Format the data
        this.formattedList = this.formatArray(this.list)
        // get the merge location
        this.getSpanArr()
      })
    },
    /** * Format Array * {Array} sources * {Array} arrayed Array * return Returns the formatted Array */
    formatArray: function f(sources, arrayed) {
      if(! sources)return []
      
      const arr = arrayed || []
      const len = sources.length

      for (let i = 0; i < len; i++) {
        const childs = sources[i].list || []
        arr.push(sources[i])
        if (childs.length > 0) {
          f(sources[i].list, arr)
        }
      }
      return arr
    },
    /** * get merged location information */
    getSpanArr() {
      // Reset the spanArr because the stats change when the page is turned
      // If the previous data is not cleared, other pages will be affected
      this.spanArr = []
      const data = this.formattedList
      if(! data || data.length <=0) return
      / / traverse
      for (let i = 0; i < data.length; i++) {
        if (i === 0) {
          this.spanArr.push(1)
          // This is a row index
          this.pos = 0
        } else {
          // If the appkey of the current object is equal to that of the previous object
          // Indicates that a merge is required
          if (data[i].appkey === data[i - 1].appkey) {
            this.spanArr[this.pos] += 1
            this.spanArr.push(0)}else {
            this.spanArr.push(1)
            this.pos = i
          }
        }
      }
    },
    /** * handles cross-row merge */
    handleColspanMethod({ row, column, rowIndex, columnIndex}) {
      if (columnIndex < 2) {
        const _spa = this.spanArr[rowIndex]
        const _row = _spa ? _spa : 0
        const _col = _row > 0 ? 1 : 0
        return {
          rowspan: _row,
          colspan: _col
        }
      }
    }
  }
}
</script>
Copy the code

Single file style extraction

If both A. vue and B. vue use the following style:

.btn {
  color: silver} // omit long styles...Copy the code

Consider extracting styles into stylename.scss/CSS, for example:

./components/common.scss

.btn {
  color: silver} // omit long stylesCopy the code

Then it is introduced in two files, namely:

<template>.</template>
<script>.</script>
<style lang="scss" src="./components/common.scss" scoped/>
Copy the code

Does it save a lot of code? Go and try it!

To solvevue-template-adminSingle file background image production path 404 problem after packaging

  • findbuild/utils.js
  • And then findgenerateLoadersmethods
  • inif(options.extract){... }, namely:
if (options.extract) {
  // Solve the problem of packing background image path
  loaders.push({
    loader: MiniCssExtractPlugin.loader,
    options: {
      publicPath: '.. /.. / '}})}else{... }Copy the code

Remember, never write your CSS like this:

background: url("/new/static/images/assets/loginbackground.png");
Copy the code

Because the new folder is configured by the background students to help you, it can be modified at any time. One place is ok, but multiple places will be painful to change.

dataInitialize the

Initialize props before data, so we can use this to initialize some data for data.

export default {
  data () {
    return {
      buttonSize: this.size
    }
  },
 props: {
   size: String}}Copy the code

In addition to the above, the child component’s data function can also have arguments that are the current instance object. So we can use this to make some judgments of our own. For example, rewrite the code above:

export default {
  data (vm) {
    return {
      buttonSize: vm.size
    }
  },
 props: {
   size: String}}Copy the code

template

When making v-if judgments, we can place the judgment criteria on the template component, and the final render will not contain the

<template>
  <div class="box">
    <template v-if="isVal">
      <h2>.</h2>
    </template>
    <template v-else>
      <h2>.</h2>
    </template>
  </div>
</template>
Copy the code

V-for also works.

Lifecycle hook

A lifecycle hook can be an array type, and the functions in the array are executed sequentially.

export default{...created: [
   function one () {
     console.log(1)},function two () {
     console.log(2)}]... }Copy the code

It doesn’t help. Just know. In fact, lifecycle hooks can also be applied to DOM elements. To take advantage of this, we can initialize the child’s lifecycle hooks using methods in the parent component:

<! -- Child.vue -->
<template>
  <h3>I'm child!</h3>
</template>

<! -- Parent.vue -->
<template>
 <child @hook:created="handleChildCreated"></child>
</template>

<script>
   import Child from './child.vue'
   export default {
     components: [ Child ],
     methods: {
       handleChildCreated () {
         console.log('handle child created... ')}}}</script>
Copy the code

The other hooks are the same.

v-for

We often make the mistake of doing this when iterating over a number with v-for, for example:

Use v-for and v-if on the same element:

<template>
  <ul class="items">
    <! -- Only active users can display -->
    <li 
      v-for="(user, index) in users" 
      v-if="user.isActive" 
      :key="user.id">
      {{ user.name }}
    </li>
  </ul>
</template>
Copy the code

Because v-for and V-if can be used on the same element, the official recommendation is to filter the calculated attributes before traversing them. So usually development is not recommended to use together, know that there is this thing can, do not know when the interview. For why it is not recommended to use them together, see Avoiding -v-if- and -v-for- together.

hybrid

If multiple components share some props, data, methods, etc., you can pull them out and put them in a mixins mixer. For example, in a user management list.

Paging mixer:

// paging-mixin.vue
export default {
  props: {
    pageSize: 1.pageLength: 10.currentPage: 1
    total: 20
  },
  methods: {
    /** ** ** /
    prevPage (page) {
      ...
    },
    /** ** next page */
    nextPage (page) {
      ...
    }
    /** * jumps to the current page */currentPage (page) { ... }}}Copy the code

Users.vue:

<template>
  <div class="user-model">
    <my-table :data="users"></my-table>
    <my-paging
      :page-length="pageLength"
      :page-size="pageSize"
      :current-page="currentPage"
      :total="total">
    </my-paging>
  </div>
</template>

<script>
  import PagingMixin from '.. /mixins/paging-mixin.vue'
  export default {
    mixins: [PagingMixin],
    data () {
      return {
        users: [].pageLength: 10.pageSize: 1.currentPage: 1.total: 20}}}</script>
Copy the code

Don’t need to write props and methods for every page.

Render function

Here is a simple template template:

<template>
  <div class="box">
    <h2>title</h2>
    this is content
  </div>
</template>
Copy the code

We rewrite the above code with a render function:

export default {
  render (h) {
    let _c = h
    return _c('div', 
      { class: 'box'}, 
      [_c('h2', {}, 'title'), 'this is content'])}}Copy the code

In fact, Vue compiles a template into a render function, which you can view in real time using an online tool. The above template template is compiled into the following rendering function:

let render = function () {
  return _c('div',
    {staticClass:"box"},
    [_c('h2', [_v("title")]), _v("this is content")])}Copy the code

Is it similar? As officially stated, the render function is closer to the compiler than template. If you were to use a flow chart, it would look something like this:

Template ↓ Precompile tool (vue-loader + vue-template-compile) ↓ render ↓ resolve vnodeCopy the code

See the Vue declaration cycle diagram for details.

Render function:

  • Development component library, Element source code with render
  • Encapsulate some high-level components. A component nested within a component is a higher-order component, provided that it meets the following three components:props,event,slot
  • Used to deal with some complex logical decisions. If we have a component that has a lot in itv-ifUsing a template is not appropriate and can be handled easily with rendering functions

errorCaptured

Called when an error from a descendant component is caught. This is useful sometimes when we want to collect error logs without exposing errors to the browser console. Here’s an example:

Child.vue

<template>
  <! -- omit some irrelevant code -->
</template>
<script>
  export default {
    mounted () {
      // Intentionally miswrite console
      consol.log('There will be an error! ')}}</script>
Copy the code

Parent.vue

<template>
  <child></child>
</template>
<script>
  import Child from './Child.vue'
  export default {
    components: [ Child ],
    /** * Three parameters are received: the error object, the component instance where the error occurred, and a string containing information about the source of the error. * This hook can return false to prevent further propagation of the error. * /
    errorCaptured (err, vm, info) {
      console.log(err)
      // -> ReferenceError: consle is not defined ...
      console.log(vm)
      // -> {_uid: 1, _isVue: true, $options: {... }, _renderProxy: o, _self: o... }
      console.log(info)
      // -> `mounted hook`
      // Tell us that this error occurred in the VM component mounted hook
      
      // Prevent the error from propagating upward
      return false}}</script>
Copy the code

For more on errorCaptured, go to errorCaptured.

v-once

Create low-overhead static components with V-once. Rendering normal HTML elements is very fast in Vue, but sometimes you may have a component that contains a lot of static content. In this case, you can add the V-once feature to the root element to ensure that the content is evaluated only once and then cached, like this:

<template>
  <div class="box" v-once>
    <h2>User agreement</h2>. a lot of static content ...</div>
</template>
Copy the code

Render elements and components only once. In subsequent re-rendering, the element/component and all its child nodes are treated as static and skipped. This can be used to optimize update performance. For more information about V-Once, please go to the official website ->.

slot-scope

Scope slot. The [email protected] version was formerly called scope, and later versions have replaced it with slot-scope. This is the same as slot-scope, except that scope can only be used for

Those of you who have used Element know that when we use

we see code like this:

[email protected] version:

<el-table-column label="Operation">
  <template scope="scope">
  <el-button
    size="small"
    @click="handleEdit(scope.$index, scope.row)">The editor</el-button>
  <el-button
    size="small"
    type="danger"
    @click="handleDelete(scope.$index, scope.row)">delete</el-button>
  </template>
</el-table-column>
Copy the code

However, versions after 2.0 have been replaced with slot-scope.

[email protected]:

<el-table-column label="Operation">
  <template slot-scope="scope">
    <el-button
      size="mini"
      @click="handleEdit(scope.$index, scope.row)">The editor</el-button>
    <el-button
      size="mini"
      type="danger"
      @click="handleDelete(scope.$index, scope.row)">delete</el-button>
  </template>
</el-table-column>
Copy the code

To put it bluntly, slot-scope is a callback to a function. I’ll give you the result, and you can do whatever you want with it:

function getUserById (url, data, callback) {
  $.ajax({
    url,
    data,
    success: function (result) {
      callback(result)
    }
  })
}

/ / use
getUserById('/users', { id: 1 }, function (response) {
  // Get the data and start working on your page logic
})
Copy the code

Let’s briefly simulate how slot-scope is used inside the

component.

Emulated

components:

// Define the template
let template = ` <ul class="table"> <li v-for="(item, index) in data" :key="index"> <! I want the data to be handled by the caller --> <! <slot :row="item"> <! - when the user didn't write anything, the default value will display - > {{item. The name}} < / slot > < / li > < / ul > `
Vue.component('el-table', {
  template,
  props: {
    data: Array.default: []}})Copy the code

Use the

component where you need it:

HTML:

<div id="app">
  <el-table :data="userData">
    <! -- You can use template -->
    <! -- 'scope' is also a variable name, which is arbitrary, such as foo, bar -->
    <template slot-scope="scope">
      <! Row = scope. Row = scope. Row = scope.
      <! -- 'scope.row' returns an 'item' object -->
      <template v-if="scope.row.isActived">
        <span class="red">{{ scope.row.name }}</span>
      </template>
      <template v-else>
        {{ scope.row.name }}
      </template>
    </template>
  </el-table>
</div>
Copy the code

JavaScript:

new Vue({
  el: '#app'.data: {
    userData: [{id: 1.name: 'Joe'.isActived: false},
      {id: 2.name: 'bill'.isActived: false},
      {id: 1.name: 'Cathy'.isActived: true},
      {id: 1.name: 'Daisy'.isActived: false},]}})Copy the code

CSS:

.red {
  color: red
}
Copy the code

You can click here hard to see the effect above! Finally, we use the render function to refactor the above code:

JavaScript:

/ / ` < el - table > ` components
Vue.component('el-table', {
  name: 'ElTable'.render: function (h) {
    return h('div', { 
      class: 'el-table'
    }, this.$slots.default)
  },
  props: {
    data: Array}})// `<el-table-column>`
Vue.component('el-table-column', {
  name: 'ElTableColumn'.render: function (h) {
    // Define an array of li elements
    let lis = [], 
       // Get the data array from the parent component
       data = this.$parent.data
    // Iterate over the array, which is the 'V -for' above, to generate the '
  • ' label
  • // 'this.$scopedslots. default' gets the slot-scope applied to the slot. // Pass '{row: item}' to the scope variable above data.forEach((item, index) = > { let liEl = h('li', { key: item.id }, [ this.$scopedSlots.default({ row: item }) ]) // Store the generated li tag in the array lis.push(liEl) }) return h('ul', { class: 'el-table-column' }, lis) } }) Copy the code

    Use it like this on your page:

    HTMl:

    <div id="app">
      <el-table :data="list">
        <el-table-column>
          <template slot-scope="scope">
            <span class="red" v-if="scope.row.actived">{{ scope.row.name }}</span>
            <span v-else>{{ scope.row.name }}</span>
          </template>
        </el-table-column>
      </el-table>
    </div>
    Copy the code

    JavaScript:

    new Vue({
      el: '#app'.data: {
        list: [{id: 1.name: 'Joe'.actived: false },
          { id: 1.name: 'bill'.actived: false },
          { id: 1.name: 'Cathy'.actived: true },
          { id: 1.name: 'Daisy'.actived: false},]}})Copy the code

    You can click here hard to see the effect above!

  • This is necessary because we sometimes use components that we didn’t develop ourselves, such as

    above.
  • At the end!

    Long winded so much, hope to see the students more or less a little harvest it. Incorrect place also please leave a message to correct, grateful. As the saying goes,Three people will have my teacher!We hope that more Vue partners can get together to exchange technology! Here is a Q group I maintain, welcome to scan code into the group oh, let us exchange learning together. You can also add my personal wechat account: G911214255, remarksThe Denver nuggetsCan.