[Vue] Vue scaffold

1 Vue scaffold

1.1 Vue scaffold installation

Vue CLI is a Vue based JS for rapid development of the complete system.

Install the latest version of Vue cli

npm install -g @vue/cli

Install Vue cli 3 X and above

npm install  '@vue-cli@3.x.x' -g

Check that the installation was successful

vue -V  

Create project

vue create xxx

Depending on the project requirements, you can choose vue2 and vue3

Run project

npm run serve

1.2 project example

Template project structure

├── node_modules 
├── public
│   ├── favicon.ico: Tab Icon
│   └── index.html: Main page
├── src
│   ├── assets: Storing static resources
│   │   └── logo.png
│   │── component: Store components
│   │   └── School.vue
│   │── App.vue: Summarize all components
│   │── main.js: Entry file
├── .gitignore: git Configuration ignored by versioning
├── babel.config.js: babel Configuration file for
├── package.json: App package profile 
├── README.md: Application description file
├── package-lock.json: Package version control file
├── vue.config.js: vue Optional profile

Code display

index.html main page

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8" />
    <!--Give Way IE The browser renders the page at the highest rendering level  -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <!-- Open the ideal viewport of the mobile terminal -->
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    <!-- Configuration tab Icon -->
    <link rel="icon" href="<%= BASE_URL %>favicon.ico" />
    <!-- Configure page title -->
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <!-- When the browser does not support js Time noscript The elements in the are rendered -->
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    
    <!-- container -->
    <div id="app"></div>
     <!-- Not used here App.vue vue Scaffold default App.vue Compile here -->
  </body>
</html>

main.js entry file

/*
 This file is the entry file of the whole project 
*/

//Introduce Vue
import Vue from 'vue';
//Import parent component App
import App from './App.vue';
//Turn off vue's production prompt
Vue.config.productionTip = false;

//Create Vue instance object
new Vue({
  //Mount dom element: this instance is a #app tag service
  el: '#app',
  //Create App templates and put App components into containers
  render: h => h(App),
});

App.vue parent component

<template>
  <!-- A component template must contain only one root element,This root element is the starting point of traversal -->
  <div>
    <img src="./assets/logo.png" alt="log" />
    <!-- Use subcomponents -->
    <School></School>
    <Student></Student>
  </div>
</template>

<script>
//Introducing sub components
import School from "./components/School.vue";
import Student from "./components/Student.vue";
export default {
   name: "App",//Can not write
  //Register subcomponents
  components: { Student, School },
};
</script>

<style>
</style>

School.vue sub assembly

<template>
  <!-- Component template --> 
  <div class="demo">
    <h2>School Name:{{ name }}</h2>
    <h2>School address:{{ address }}</h2>
    <button @click="showName">Click me to prompt the school name</button>
  </div>
</template>

<script>
// Component interaction (data, method related code)
export default {
  //Vue is omitted here extend()
  //Component name
  name: "School",
  data() {
    return {
      name: "Shang Silicon Valley",
      address: "Beijing",
    };
  },
  methods: {
    showName() {
      alert(this.name);
    },
  },
};
</script>

<style>
/* Component style */
.demo {
  background-color: pink;
}
</style>

Student.vue sub assembly

<template>
  <div>
    <h2>Student Name:{{ name }}</h2>
    <h2>School age:{{ age }}</h2>
  </div>
</template>

<script>
export default {
  //name: "Student",
  data() {
    return {
      name: "Zhang San",
      age: 18,
    };
  },
};
</script>

<style>
</style>

Project error reporting

Error reason: the code with irregular name is regarded as an error during eslint syntax check
Solution:

  • Change the component name to conform to Vue's recommended double hump or - connection naming specification, such as StudentName or student name
  • Modify the configuration item and close the eslint syntax check
    1. Find (or create) Vue in the root directory of the project config. JS file
    2. Add the following contents to the file, then save the file and recompile it
module.exports = {
  lintOnSave: false, //Close the eslint check
};

About different versions of Vue
vue.js and Vue runtime. xxx. JS (running version introduced in main. JS):
vue.js is the full version of Vue, including: core functions + template parser.
vue.runtime.xxx.js is the running version of Vue, which only contains: core functions; There is no template parser.
Because Vue runtime. xxx. JS has no template parser, so you can't use the configuration item template. You need to use the createElement function received by the render function to specify the specific content. Like template, the render function creates html templates

vue.config.js configuration file
Use Vue inspect > output JS can view the default configuration of Vue scaffold.
Use Vue config. JS can customize the scaffold. For details, see: https://cli.vuejs.org/zh

2. Small knowledge of Vue components

2.1 ref attribute

ref is used to register reference information (id substitutes) for elements or subcomponents. It is used to obtain real DOM elements on html tags or component instance objects (vc) on component tags
Usage:

//Marking
<h1 ref="xxx">.....</h1>or <School ref="xxx"></School>
//obtain
this.$refs.xxx
<template>
  <div>
    <h1 v-text="msg" ref="title"></h1>
    <button ref="btn" @click="showDOM">Click on the top of my output DOM element</button>
    <School ref="sch" />
  </div>
</template>

<script>
//Introducing School components
import School from "./components/School";

export default {
  name: "App",
  components: { School },
  data() {
    return {
      msg: "Welcome to study Vue!",
    };
  },
  methods: {
    showDOM() {
      console.log(this.$refs.title); //Real DOM elements
      console.log(this.$refs.btn); //Real DOM elements
      console.log(this.$refs.sch); //Instance object of School component (vc)
    },
  },
};
</script>

2.2 props configuration item

props configuration item: let the component receive the data transmitted from the outside (prop attribute)

Parent component: transferring data

//name="xxx" is the data passed
<Student name="xxx"/>  

Sub component: receiving data

//The first method (receive only) is the most commonly used
props:['name']
//Second method (restriction type)

props:{name:String}
//The third method (restriction type, restriction necessity, specifying default value)
props:{
	name:{
	type:String, //type
	required:true, //necessity
	default:'Zhang San' //Default value
	}
}

Note: props is read-only. Vue bottom layer will monitor your modification of props. If it is modified, it will issue a warning. If the business requirements really need to be modified, please copy the contents of props into data, and then modify the data in data.

App.vue parent component

<template>
  <div>
    <!-- Data transmission -->
    <Student name='Li Si' sex='female'  :age='18'></Student>
  </div>
</template>

<script>
//Introducing sub components

import Student from "./components/Student.vue";
export default {
   name: "App",
   components: { Student },
};
</script>

<style>
</style>

Student.vue sub assembly

<template>
  <div>
    <h2>Student Name:{{ name }}</h2>
    <h2>Student gender:{{ sex }}</h2>
    <h2>Student age:{{ myAge + 1 }}</h2>
    <button @click="updateAge">Age of modification attempts received</button>
  </div>
</template>

<script>
export default {
  name: "Student",
  data() {
    return {
    //If the business requirements really need to be modified, please copy the contents of props into data, and then modify the data in data
      myAge: this.age,
    };
  },
  methods: {
    updateAge() {
      this.myAge++;
    },
  },
  
  //receive data 
  
  //1. Simple declaration of receipt
 props:['name','age','sex']
 
  //2. Limit the type of data while receiving
  /* props:{
			name:String,
			age:Number,
			sex:String
		} */
		
  //3. At the same time of receiving data: type restriction + default value designation + necessity restriction
  /*props: {
    name: {
      type: String, //name The type of is a string
      required: true, //name be necessary
    },
    age: {
      type: Number,
      default: 23, //Default value
    },
    sex: {
      type: String,
      required: true,
    },
  },*/
};
</script>

2.3 mixin

Mixin: the configuration shared by multiple components can be extracted into a mixed object

Usage:

Step 1: define mixing

{
    data(){....},
    methods:{....}
    ....
}

mixin.js

//Define blend
export const mixin = {
  methods: {
    showName() {
      alert(this.name);
    },
  },
};

Step 2: use blending

Global blending: Vue mixin(xxx)

main.js

//Introduce Vue
import Vue from 'vue'
//Introduce App
import App from './App.vue'
import {mixin} from './mixin'
//Global blending
Vue.mixin(mixin)

//Create vm
new Vue({
	el:'#app',
	render: h => h(App)
})

Local mixing: mixins: ['xxx']

School.vue sub assembly

<template>
  <div class="demo">
    <h2 @click="showName">School Name:{{ name }}</h2>
    <h2>School address:{{ address }}</h2>
  </div>
</template>

<script>
import { mixin } from "../mixin";
export default {
  data() {
    return {
      name: "Shang Silicon Valley",
      address: "Beijing",
    };
  },
  mixins: [mixin],
};
</script>

<style>
</style>

Student.vue sub assembly

<template>
  <div>
    <h2 @click="showName">Student Name:{{ name }}</h2>
    <h2>Student gender:{{ sex }}</h2>
  </div>
</template>
<script>
 import {mixin} from '../mixin'
export default {
  name: "Student",
  data() {
    return {
      name: "Zhang San",
      sex: "male",
    };
  },
  //Local mixing
 mixins:[mixin]
};
</script>

2.4 Vue plug-in

Vue plug-in: used to enhance Vue

Essence: an object containing the install method. The first parameter of install is Vue, and the second parameter is the data passed by the plug-in user.

Define plug-ins

object.install = function (Vue, options) {
    // 1. Add global filter
    Vue.filter(....)
    // 2. Add global instruction
    Vue.directive(....)
    // 3. Configure global mixing (integration)
    Vue.mixin(....)
    // 4. Add instance method
    Vue.prototype.myMethod = function () {...}
    Vue.prototype.myProperty = xxxx
}

Using plug-ins

Vue.use()

example

plugins.js

export default {
  install(Vue) {
    //Global filter
    Vue.filter("mySlice", function (value) {
      return value.slice(0, 4);
    });
    //Add a method to the Vue prototype (both vm and vc can be used)
    Vue.prototype.hello = () => {
      alert("How do you do");
    };
  },
};

main.js

//Introduce Vue
import Vue from "vue";
//Introduce App
import App from "./App.vue";
//Introducing plug-ins
import plugins from "./plugins";
//Using plug-ins
Vue.use(plugins);
//Create vm
new Vue({
  el: "#app",
  render: (h) => h(App),
});

School. Used in Vue subcomponents

<template>
  <div>
    <h2>School Name:{{ name | mySlice }}</h2>
    <h2>School address:{{ address }}</h2>
    <button @click="test">Let me test one hello method</button>
  </div>
</template>

<script>
export default {
  name: "School",
  data() {
    return {
      name: "Shang Silicon Valley atguigu",
      address: "Beijing",
    };
  },
  methods: {
    test() {
      this.hello();
    },
  },
};
</script>

2.5 scoped style

scoped style: make the style take effect locally to prevent conflicts.
Writing method:

<style scoped>
</style>

2.6 nextTick

Syntax: this$ Nexttick (callback function)
Function: execute the specified callback after the next DOM update.
When to use: when you need to perform some operations based on the updated new DOM after changing the data, it should be executed in the callback function specified by nextTick.

3. Data interaction between components

Pass value from parent to child: child component sets props + parent component sets v-bind attribute binding

Pass value from child to parent: the child component's $emit + parent component sets the v-on event binding

Communication between sibling components and any component: transfer data through the event car, create an empty global Vue object EventBus, send it with $emit and receive it with $on

3.1 pass value props from parent component to child component

1. The child component receives the value passed by the parent component through props

props data transfer principle: one-way data flow, only parent to child

v-bind does not support the use of hump identification. For example, cUser should be changed to c-User.

Vue.component('menu-item',{
    //props accepts the data passed by the parent component
	props: ['title'],
	template: '<div>{{ title }}</div>'
})

2. The parent component passes the value to the child component through the attribute binding v-bind

//Traditional way
<menu-item title="Data from parent component"></menu-item>
//Dynamic binding
<menu-item :title="title"></menu-item>
 //07 - pass value from parent component to child component - props naming rules html
<!DOCTYPE html>
 <html lang="en">

 <head>
   <meta charset="UTF-8">
   <title></title>
 </head>

 <body>
   <div id="app">
     <div>{{pmsg}}</div>
	//Traditional way
     <menu-item title='Value from parent component'></menu-item>
	//Dynamic binding
     <menu-item :title='ptitle' content='hello'></menu-item>
   </div>
   <script type='text/javascript' src='js/vue.js'></script>
   <script type='text/javascript'>
     //Sub component (relative to root component)
     Vue.component('menu-item', {
       //props accepts the data passed by the parent component
       props: ['title', 'content'],
       data: function () {
         return {
           msg: 'Data of the subcomponent itself'
         }
       },
       template: `
     <div>{{msg + '-----' +title + '-----' +content}}</div>
     `
     })
     //Vue instances are root components
     const app = new Vue({
       el: "#app",
       data: {
         pmsg: "Content in parent component",
         ptitle: 'Dynamically bound values'
       }
     })
   </script>
 </body>

 </html>

3.props attribute name rule

  • Hump form is used in props, and dash form is needed in html

  • There is no such restriction in string templates

    Vue.component('menu-item', {
    	// It's humped in JavaScript
    	props: ['menuTitle'],
    	template: '<div>{{ menuTitle }}</div>'
    })
    //It is dashed in html
    <menu-item menu-title="nihao"></menu-item>
    

4.props attribute value type

  • String string

  • Value Number

  • Boolean

  • Array array

  • Object object

     <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Document</title>
    </head>
    <body>
      <div id="app">
        <div>{{pmsg}}</div>
        <menu-item :pstr='pstr' :pnum='12' pboo='true' :parr='parr' :pobj='pobj'></menu-item>
      </div>
      <script type="text/javascript" src="js/vue.js"></script>
      <script type="text/javascript">
        Vue.component('menu-item', {
          props: ['pstr','pnum','pboo','parr','pobj'],
          template: `
            <div>
              <div>{{pstr}}</div>
              <div>{{12 + pnum}}</div>
              <div>{{typeof pboo}}</div>
              <ul>
                <li :key='index' v-for='(item,index) in parr'>{{item}}</li>
              </ul>
                <span>{{pobj.name}}</span>
                <span>{{pobj.age}}</span>
              </div>
            </div>
          `
        });
        var vm = new Vue({
          el: '#app',
          data: {
            pmsg: 'Content in parent component',
            pstr: 'hello',
            parr: ['apple','orange','banana'],
            pobj: {
              name: 'lisi',
              age: 12
            }
          }
        });
      </script>
    </body>
    </html>
    

3.2 the child component passes the value of $emit to the parent component

1. The child component passes information to the parent component through the custom event $emit

The child component passes values to the parent component, using the custom event $emit$ Emit (custom event name)

<button @click='$emit("enlarge-text")'>Expand parent component font</button>

2. Listen for events of child components through v-on in parent components

<menu-item @enlarge-text='handle'></menu-item>
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title></title>
</head>

<body>
  <!-- whole app Parent component -->
  <div id="app">
    <div :style='{fontSize:fontsize+"px"}'>{{msg}}</div>
    <!-- Listen for child component events in the parent component -->
    <menu-item @enlarge-text='handle'></menu-item>
  </div>
  <script type='text/javascript' src='js/vue.js'></script>
  <script type='text/javascript'>
    //Click the button of child component to control the font size of parent component
    //Create subcomponents
    Vue.component('menu-item', {
      template: `
<button @click='$emit("enlarge-text")'>Expand parent component font</button>
`
    })
    const app = new Vue({
      el: "#app",
      data: {
        msg: 'Content in parent component',
        fontsize: 10
      },
      methods: {
        handle: function () {
          this.fontsize += 5;
        }
      }
    })
  </script>
</body>

</html>

3. The child component passes information to the parent component through custom events and carries additional values

5 is an additional value

<button @click='$emit("enlarge-text", 5)'>Increase font size in parent component</button>

4. The parent component listens to the events of the child component

Accept additional values via $event

<menu-item  @enlarge-text='handle($event)'></menu-item>
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title></title>
</head>

<body>
  <div id="app">
    <div :style='{fontSize:fontsize+"px"}'>{{msg}}</div>
    <menu-item @enlarge-text='handle($event)'></menu-item>
  </div>
  <script type='text/javascript' src='js/vue.js'></script>
  <script type='text/javascript'>
    Vue.component('menu-item', {
      template: `
<button @click='$emit("enlarge-text",5)'>Expand parent component font</button>
`
    })
    const app = new Vue({
      el: "#app",
      data: {
        msg: 'Content in parent component',
        fontsize: 10
      },
      methods: {
        handle: function (val) {
          this.fontsize += val;
        }
      }
    })
  </script>
</body>

</html>

3.3 value transfer between non parent and child components


1. The data transfer between any components needs the help of the event car, and the data is transferred through the event car
2. Create an instance of Vue so that all components share the same event mechanism.
3. Pass the data side and trigger eventBus through an event e m i t ( square method name , pass Deliver of number according to ) . 4 , meet collect number according to square , through too m o u n t e d ( ) touch hair e v e n t B u s . emit (method name, passed data). 4. The data receiving party triggers eventBus through mounted() {} emit (method name, passed data). 4. The data receiving party triggers eventbus through mounted() On (method name, function (parameter to receive data) {use the data of this component to receive the passed data}). At this time, the this in the function has changed, and the arrow function can be used.

First, create a separate js file bus js, the content is as follows

import Vue from 'vue'
export default new Vue

Parent components are as follows:

<template>
     <components-a></components-a>
     <components-b></components-b>
</template>

Sub assembly a is as follows:

<template>
      <div>
           <button @click="abtn">A Button</button>
      </div>
</template>
<script>
import  from '../../js/bus.js'
export default {
      name: 'components-a',
      data () {
        return {
                'msg':"I'm a component A"
        }
      },
      methods:{
           abtn:function(){
                  let  _this = this;
                   //The $emit method triggers an event
                   bus .$emit("myFun", _this.msg)  
           }
      }
}
</script>

Sub assembly b is as follows:

<template>
     <div>
         <div>{{btext}}</div>
     </div>
</template>
<script>
import bus from '../../js/bus.js'
export default {
   name: 'components-b',
   data () {
        return {
           'btext':"I am B Component content"
        }
   },
   created:function(){
       this.bbtn();
   },
   methods:{
       bbtn:function(){
            bus .$on("myFun",(message)=>{   
            //It's better to use the arrow function here, otherwise there's a problem with this pointing
                 this.btext = message      
            })
       }
    }
}
</script>

In this way, click the button in component A, and component B can obtain the value from component A.

4. Component slot

Anonymous slots only exist < slot > < / slot >
There can be multiple < slot name = "up" > < / slot > named slots
Scope slot:
Slot usage: the child component defines the slot position, and the parent component defines the slot content, which controls the display and hiding of the content

4.1 function of component slot

Compile scope: everything in the parent component template will be compiled in the parent scope; Everything in the subcomponent template is compiled within the child scope.
The purpose of the slot is to make our original device more scalable. For example, the USB of the computer can be inserted into the USB stick.
The component slot is a built-in component of Vue. In order to make our encapsulated components more extensible, users can decide what to display inside the component.
Function of component slot: the parent component passes content to the child component

4.2 basic usage of component slot

In the sub component, a slot can be opened for the sub component by using the special element < slot >.

What is inserted into this slot depends on how the parent component is used.

How to package properly?

Extract commonalities and retain differences. The best packaging method is to extract commonness into components and reserve different as slots.

1. Slot position

Vue.component('alert-box', {
	template: `
		<div class="demo-alert-box">
			<strong>Error!</strong>
			<slot></slot>
		</div>
	`
})

2. Slot content

<alert-box>Something had happened.</alert-box>
//12 - basic usage of slot html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <alert-box>have bug happen</alert-box>
    <alert-box>There is a warning</alert-box>
    <alert-box></alert-box>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">

    Vue.component('alert-box', {
      template: `
        <div>
          <strong>ERROR:</strong>
          <slot>Default content</slot>
        </div>
      `
    });
    var vm = new Vue({
      el: '#app',
      data: {
        
      }
    });
  </script>
</body>
</html>

4.3 named slot usage

1. Slot definition

<div class="container">
	<header>
		<slot name="header"></slot>
	</header>
	<main>
		<slot></slot>
	</main>
	<footer>
		<slot name="footer"></slot>
	</footer>
</div>

2. Slot content

//Match according to the name, and the unmatched ones are placed in the default slot
<base-layout>
	<h1 slot="header">Title Content</h1>
	<p>Main contents 1</p>
	<p>Main content 2</p>
	<p slot="footer">Bottom content</p>
</base-layout>
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>

<body>
  <div id="app">
    <base-layout>
      <template slot='header'>
        <p>Title Information 1</p>
        <p>Title Information 2</p>
      </template>
      <p>Main contents 1</p>
      <p>Main content 2</p>
      <template slot='footer'>
        <p>Bottom message 1</p>
        <p>Bottom message 2</p>
      </template>
    </base-layout>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    Vue.component('base-layout', {
      template: `
        <div>
          <header>
            <slot name='header'></slot>
          </header>
          <main>
            <slot></slot>
          </main>
          <footer>
            <slot name='footer'></slot>
          </footer>
        </div>
      `
    });
    var vm = new Vue({
      el: '#app',
      data: {

      }
    });
  </script>
</body>

</html>

4.4 scope slot

Application scenario: the parent component processes the content of the child component

1. Slot definition

<ul>
	<li v-for= "item in list" v-bind:key= "item.id" >
		<slot v-bind:item="item">
			{{item.name}}
		</slot>
	</li>
</ul>

2. Slot content

<fruit-list v-bind:list= "list">
	<template slot-scope="slotProps">
		<strong v-if="slotProps.item.current">
			{{ slotProps.item.text }}
		</strong>
	</template>
</fruit-list>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<style type="text/css">
  .current {
    color: orange;
  }
</style>
<body>
  <div id="app">
    <fruit-list :list='list'>
      <template slot-scope='slotProps'>
        <strong v-if='slotProps.info.id==3' class="current">{{slotProps.info.name}}</strong>
        <span v-else>{{slotProps.info.name}}</span>
      </template>
    </fruit-list>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    Vue.component('fruit-list', {
      props: ['list'],
      template: `
        <div>
          <li :key='item.id' v-for='item in list'>
            <slot :info='item'>{{item.name}}</slot>
          </li>
        </div>
      `
    });
    var vm = new Vue({
      el: '#app',
      data: {
        list: [{
          id: 1,
          name: 'apple'
        },{
          id: 2,
          name: 'orange'
        },{
          id: 3,
          name: 'banana'
        }]
      }
    });
  </script>
</body>
</html>

5. Component based case: shopping cart

Case requirements analysis: realize business requirements in a component-based manner

Component division according to business functions

① Title component (display text)

② List component (list display, commodity quantity change, commodity deletion)

③ Settlement component (calculate total goods)

Function realization steps:

  • Achieve overall layout and style effect
  • Separate functional components
  • Combine all sub components to form an overall structure
  • Realize the work of each component one by one
    • Title Component
    • List component
    • Settlement component
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <!-- introduce css style -->
  <link rel="stylesheet" type="text/css" href="Shopping cart case.css" />
</head>

<body>
  <div id="app">
    <div class="container">
      <my-cart></my-cart>
    </div>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    //Title Component
    var CartTitle = {
      //Pass user name data
      props: ['uname'],
      template: `
        <div class="title">{{uname}}Commodity</div>
      `
    }
    //List component
    var CartList = {
      props: ['list'],
      //List display
        //List name
      //Change of commodity quantity
        //Quantity of goods minus
        //Render data lose focus
        //Commodity quantity plus
      //Product deletion
      template: `
        <div>
          <div :key='item.id' v-for='item in list' class="item">
            <img :src="item.img"/>
            <div class="name">{{item.name}}</div>
            <div class="change">             
              <a href="" @click.prevent='sub(item.id)'>-</a>             
              <input type="text" class="num" :value='item.num' @blur='changeNum(item.id, $event)'/>             
              <a href="" @click.prevent='add(item.id)'>+</a>
            </div>            
            <div class="del" @click='del(item.id)'>×</div>
          </div>
        </div>
      `,
      methods: {
        //Commodity quantity input field modification
        changeNum: function (id, event) {
          this.$emit('change-num', {
            id: id,
            type: 'change',
            num: event.target.value //Latest value of current input field
          });
        },
        //Quantity of goods minus
        add: function (id) {
          this.$emit('change-num', {
            id: id,
            type: 'add'
          });
        },
        //Commodity quantity plus
        sub: function (id) {
          this.$emit('change-num', {
            id: id,
            type: 'sub'
          });
        },
        //Product deletion
        del: function (id) {
          // Pass id to parent component
          this.$emit('cart-del', id);
        }
      }
    }
    //Settlement component
    var CartTotal = {
      props: ['list'],
      template: `
        <div class="total">
          <span>Total price:{{total}}</span>
          <button>settlement</button>
        </div>
      `,
      computed: {
        total: function () {
          // Calculate the total price of goods
          var t = 0;
          this.list.forEach(item => {
            t += item.price * item.num;
          });
          return t;
        }
      }
    }
    Vue.component('my-cart', {
      data: function () {
        return {
          uname: 'Zhang San',
          list: [{
            id: 1,
            name: 'TCL Color TV',
            price: 1000,
            num: 1,
            img: 'img/a.jpg'
          }, {
            id: 2,
            name: 'Set top box',
            price: 1000,
            num: 1,
            img: 'img/b.jpg'
          }, {
            id: 3,
            name: 'Haier refrigerator',
            price: 1000,
            num: 1,
            img: 'img/c.jpg'
          }, {
            id: 4,
            name: 'Mi phones',
            price: 1000,
            num: 1,
            img: 'img/d.jpg'
          }, {
            id: 5,
            name: 'PPTV television',
            price: 1000,
            num: 1,
            img: 'img/e.jpg'
          }]
        }
      },
      template: `
        <div class='cart'>
          <cart-title :uname='uname'></cart-title>
          <cart-list :list='list' @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list>
          <cart-total :list='list'></cart-total>
        </div>
      `,
      components: {
        'cart-title': CartTitle,
        'cart-list': CartList,
        'cart-total': CartTotal
      },
      methods: {
        changeNum: function (val) {
          // Change of commodity quantity
          //There are three cases: change of commodity quantity input field, increase of commodity quantity and decrease of commodity quantity
          if (val.type == 'change') {
            // According to the data passed by the sub component, the corresponding data in the new list
            this.list.some(item => {
              if (item.id == val.id) {
                item.num = val.num;
                // Terminate traversal
                return true;
              }
            });
          } else if (val.type == 'sub') {
            // Minus one operation
            this.list.some(item => {
              if (item.id == val.id) {
                item.num -= 1;
                // Terminate traversal
                return true;
              }
            });
          } else if (val.type == 'add') {
            // Add one operation
            this.list.some(item => {
              if (item.id == val.id) {
                item.num += 1;
                // Terminate traversal
                return true;
              }
            });
          }
        },
        //List deletion function
        delCart: function (id) {
          // Delete the corresponding data in the list according to the id
          // 1. Find the index of the data corresponding to the id
          var index = this.list.findIndex(item => {
            return item.id == id;
          });
          // 2. Delete corresponding data according to index
          this.list.splice(index, 1);
        }
      }
    });
    var vm = new Vue({
      el: '#app',
    });
  </script>
</body>

</html>

Tags: Front-end Vue.js Vue-cli

Posted by monkeypaw201 on Thu, 14 Apr 2022 10:19:06 +0930